@@ -31,7 +31,7 @@ import {MinimalAppIdentifiers} from '../../models/organization.js'
3131import { CreateAssetUrl } from '../../api/graphql/app-management/generated/create-asset-url.js'
3232import { SourceExtension } from '../../api/graphql/app-management/generated/types.js'
3333import { ListOrganizations } from '../../api/graphql/business-platform-destinations/generated/organizations.js'
34- import { describe , expect , test , vi } from 'vitest'
34+ import { describe , expect , test , vi , beforeEach } from 'vitest'
3535import { CLI_KIT_VERSION } from '@shopify/cli-kit/common/version'
3636import { fetch } from '@shopify/cli-kit/node/http'
3737import {
@@ -49,6 +49,11 @@ vi.mock('@shopify/cli-kit/node/api/business-platform')
4949vi . mock ( '@shopify/cli-kit/node/api/app-management' )
5050vi . mock ( '@shopify/cli-kit/node/api/webhooks' )
5151
52+ beforeEach ( ( ) => {
53+ // Reset the singleton instance before each test
54+ AppManagementClient . resetInstance ( )
55+ } )
56+
5257const extensionA = await testUIExtension ( { uid : 'extension-a-uuid' } )
5358const extensionB = await testUIExtension ( { uid : 'extension-b-uuid' } )
5459const extensionC = await testUIExtension ( { uid : 'extension-c-uuid' } )
@@ -160,7 +165,7 @@ describe('templateSpecifications', () => {
160165 vi . mocked ( businessPlatformOrganizationsRequest ) . mockResolvedValueOnce ( mockedFetchFlagsResponse )
161166
162167 // When
163- const client = new AppManagementClient ( )
168+ const client = AppManagementClient . getInstance ( )
164169 client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
165170 const { templates : got } = await client . templateSpecifications ( orgApp )
166171 const gotLabels = got . map ( ( template ) => template . name )
@@ -188,7 +193,7 @@ describe('templateSpecifications', () => {
188193 vi . mocked ( businessPlatformOrganizationsRequest ) . mockResolvedValueOnce ( mockedFetchFlagsResponse )
189194
190195 // When
191- const client = new AppManagementClient ( )
196+ const client = AppManagementClient . getInstance ( )
192197 client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
193198 const { templates : got } = await client . templateSpecifications ( orgApp )
194199 const gotLabels = got . map ( ( template ) => template . name )
@@ -236,7 +241,7 @@ describe('templateSpecifications', () => {
236241 vi . mocked ( businessPlatformOrganizationsRequest ) . mockResolvedValueOnce ( mockedFetchFlagsResponse )
237242
238243 // When
239- const client = new AppManagementClient ( )
244+ const client = AppManagementClient . getInstance ( )
240245 client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
241246 const { groupOrder} = await client . templateSpecifications ( orgApp )
242247
@@ -249,7 +254,7 @@ describe('templateSpecifications', () => {
249254 vi . mocked ( fetch ) . mockRejectedValueOnce ( new Error ( 'Failed to fetch' ) )
250255
251256 // When
252- const client = new AppManagementClient ( )
257+ const client = AppManagementClient . getInstance ( )
253258 const got = client . templateSpecifications ( testOrganizationApp ( ) )
254259
255260 // Then
@@ -346,7 +351,7 @@ describe('searching for apps', () => {
346351 vi . mocked ( appManagementRequestDoc ) . mockResolvedValueOnce ( mockedFetchAppsResponse )
347352
348353 // When
349- const client = new AppManagementClient ( )
354+ const client = AppManagementClient . getInstance ( )
350355 client . token = ( ) => Promise . resolve ( 'token' )
351356 const got = await client . appsForOrg ( orgId , query )
352357
@@ -377,7 +382,7 @@ describe('searching for apps', () => {
377382 vi . mocked ( appManagementRequestDoc ) . mockResolvedValueOnce ( { } )
378383
379384 // When
380- const client = new AppManagementClient ( )
385+ const client = AppManagementClient . getInstance ( )
381386 client . token = ( ) => Promise . resolve ( 'token' )
382387
383388 // Then
@@ -388,7 +393,7 @@ describe('searching for apps', () => {
388393describe ( 'createApp' , ( ) => {
389394 test ( 'fetches latest stable API version for webhooks module' , async ( ) => {
390395 // Given
391- const client = new AppManagementClient ( )
396+ const client = AppManagementClient . getInstance ( )
392397 const org = testOrganization ( )
393398 const mockedApiVersionResult : PublicApiVersionsQuery = {
394399 publicApiVersions : [ { handle : '2024-07' } , { handle : '2024-10' } , { handle : '2025-01' } , { handle : 'unstable' } ] ,
@@ -442,7 +447,7 @@ describe('createApp', () => {
442447 test ( 'creates app successfully and returns expected app structure' , async ( ) => {
443448 // Given
444449 const appName = 'app-name'
445- const client = new AppManagementClient ( )
450+ const client = AppManagementClient . getInstance ( )
446451 const org = testOrganization ( )
447452 const expectedApp = {
448453 id : '1' ,
@@ -485,7 +490,7 @@ describe('createApp', () => {
485490
486491 test ( 'sets embedded to true in app home module' , async ( ) => {
487492 // Given
488- const client = new AppManagementClient ( )
493+ const client = AppManagementClient . getInstance ( )
489494 const org = testOrganization ( )
490495 vi . mocked ( webhooksRequestDoc ) . mockResolvedValueOnce ( {
491496 publicApiVersions : [ { handle : '2024-07' } , { handle : '2024-10' } , { handle : '2025-01' } , { handle : 'unstable' } ] ,
@@ -540,7 +545,7 @@ describe('apiVersions', () => {
540545 vi . mocked ( webhooksRequestDoc ) . mockResolvedValueOnce ( mockedResponse )
541546
542547 // When
543- const client = new AppManagementClient ( )
548+ const client = AppManagementClient . getInstance ( )
544549 client . token = ( ) => Promise . resolve ( 'token' )
545550 const apiVersions = await client . apiVersions ( orgId )
546551
@@ -558,7 +563,7 @@ describe('topics', () => {
558563 vi . mocked ( webhooksRequestDoc ) . mockResolvedValueOnce ( mockedResponse )
559564
560565 // When
561- const client = new AppManagementClient ( )
566+ const client = AppManagementClient . getInstance ( )
562567 client . token = ( ) => Promise . resolve ( 'token' )
563568 const topics = await client . topics ( { api_version : '2024-07' } , orgId )
564569
@@ -574,7 +579,7 @@ describe('topics', () => {
574579 vi . mocked ( webhooksRequestDoc ) . mockResolvedValueOnce ( mockedResponse )
575580
576581 // When
577- const client = new AppManagementClient ( )
582+ const client = AppManagementClient . getInstance ( )
578583 client . token = ( ) => Promise . resolve ( 'token' )
579584 const topics = await client . topics ( { api_version : 'invalid' } , orgId )
580585
@@ -615,7 +620,7 @@ describe('sendSampleWebhook', () => {
615620 vi . mocked ( webhooksRequestDoc ) . mockResolvedValueOnce ( mockedResponse )
616621
617622 // When
618- const client = new AppManagementClient ( )
623+ const client = AppManagementClient . getInstance ( )
619624 client . token = ( ) => Promise . resolve ( token )
620625 const result = await client . sendSampleWebhook ( input , orgId )
621626
@@ -664,7 +669,7 @@ describe('sendSampleWebhook', () => {
664669 vi . mocked ( webhooksRequestDoc ) . mockResolvedValueOnce ( mockedResponse )
665670
666671 // When
667- const client = new AppManagementClient ( )
672+ const client = AppManagementClient . getInstance ( )
668673 client . token = ( ) => Promise . resolve ( token )
669674 const result = await client . sendSampleWebhook ( input , orgId )
670675
@@ -704,7 +709,7 @@ describe('sendSampleWebhook', () => {
704709 vi . mocked ( webhooksRequestDoc ) . mockResolvedValueOnce ( mockedResponse )
705710
706711 // When
707- const client = new AppManagementClient ( )
712+ const client = AppManagementClient . getInstance ( )
708713 client . token = ( ) => Promise . resolve ( 'token' )
709714 const result = await client . sendSampleWebhook ( input , orgId )
710715
@@ -718,7 +723,7 @@ describe('sendSampleWebhook', () => {
718723
719724describe ( 'deploy' , ( ) => {
720725 // Given
721- const client = new AppManagementClient ( )
726+ const client = AppManagementClient . getInstance ( )
722727 client . token = ( ) => Promise . resolve ( 'token' )
723728
724729 test ( 'creates version with correct metadata and modules' , async ( ) => {
@@ -933,7 +938,7 @@ describe('deploy', () => {
933938 test ( 'queries for versions list' , async ( ) => {
934939 // Given
935940 const appId = 'gid://shopify/App/123'
936- const client = new AppManagementClient ( )
941+ const client = AppManagementClient . getInstance ( )
937942 client . token = ( ) => Promise . resolve ( 'token' )
938943 const mockResponse : AppVersionsQuery = {
939944 app : {
@@ -1036,7 +1041,7 @@ describe('AppManagementClient', () => {
10361041 describe ( 'generateSignedUploadUrl' , ( ) => {
10371042 test ( 'passes Brotli format for uploads' , async ( ) => {
10381043 // Given
1039- const client = new AppManagementClient ( )
1044+ const client = AppManagementClient . getInstance ( )
10401045 const mockResponse = {
10411046 appRequestSourceUploadUrl : {
10421047 sourceUploadUrl : 'https://example.com/upload-url' ,
@@ -1079,7 +1084,7 @@ describe('AppManagementClient', () => {
10791084 describe ( 'bundleFormat' , ( ) => {
10801085 test ( 'returns br for Brotli compression format' , ( ) => {
10811086 // Given
1082- const client = new AppManagementClient ( )
1087+ const client = AppManagementClient . getInstance ( )
10831088
10841089 // Then
10851090 expect ( client . bundleFormat ) . toBe ( 'br' )
@@ -1094,7 +1099,7 @@ describe('ensureUserAccessToStore', () => {
10941099 const store = testOrganizationStore ( { shopId : '456' } )
10951100 const token = 'business-platform-token'
10961101
1097- const client = new AppManagementClient ( )
1102+ const client = AppManagementClient . getInstance ( )
10981103 client . businessPlatformToken = ( ) => Promise . resolve ( token )
10991104
11001105 const mockResponse = {
@@ -1127,7 +1132,7 @@ describe('ensureUserAccessToStore', () => {
11271132 // Given
11281133 const store = testOrganizationStore ( { } )
11291134 store . provisionable = false
1130- const client = new AppManagementClient ( )
1135+ const client = AppManagementClient . getInstance ( )
11311136
11321137 // When
11331138 await client . ensureUserAccessToStore ( '123' , store )
@@ -1138,7 +1143,7 @@ describe('ensureUserAccessToStore', () => {
11381143
11391144 test ( 'handles failure' , async ( ) => {
11401145 const store = testOrganizationStore ( { } )
1141- const client = new AppManagementClient ( )
1146+ const client = AppManagementClient . getInstance ( )
11421147 client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
11431148
11441149 const mockResponse = {
@@ -1156,7 +1161,7 @@ describe('ensureUserAccessToStore', () => {
11561161} )
11571162
11581163describe ( 'appExtensionRegistrations' , ( ) => {
1159- const client = new AppManagementClient ( )
1164+ const client = AppManagementClient . getInstance ( )
11601165 const organizationId = 'org123'
11611166 const apiKey = 'api-key-123'
11621167 const appId = 'app-id-123'
@@ -1534,7 +1539,7 @@ describe('appExtensionRegistrations', () => {
15341539describe ( 'organizations' , ( ) => {
15351540 test ( 'returns empty array when currentUserAccount is null' , async ( ) => {
15361541 // Given
1537- const client = new AppManagementClient ( )
1542+ const client = AppManagementClient . getInstance ( )
15381543 client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
15391544
15401545 vi . mocked ( businessPlatformRequestDoc ) . mockResolvedValueOnce ( {
@@ -1556,7 +1561,7 @@ describe('organizations', () => {
15561561
15571562 test ( 'returns organizations with unique names' , async ( ) => {
15581563 // Given
1559- const client = new AppManagementClient ( )
1564+ const client = AppManagementClient . getInstance ( )
15601565 client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
15611566 const mockResponse = {
15621567 currentUserAccount : {
@@ -1585,7 +1590,7 @@ describe('organizations', () => {
15851590
15861591 test ( 'appends ID to businessName when organizations have duplicate names' , async ( ) => {
15871592 // Given
1588- const client = new AppManagementClient ( )
1593+ const client = AppManagementClient . getInstance ( )
15891594 client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
15901595 const mockResponse = {
15911596 currentUserAccount : {
@@ -1632,7 +1637,7 @@ describe('organizations', () => {
16321637
16331638 test ( 'returns empty array when organizationsWithAccessToDestination is empty' , async ( ) => {
16341639 // Given
1635- const client = new AppManagementClient ( )
1640+ const client = AppManagementClient . getInstance ( )
16361641 client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
16371642 const mockResponse = {
16381643 currentUserAccount : {
@@ -1651,3 +1656,26 @@ describe('organizations', () => {
16511656 expect ( result ) . toEqual ( [ ] )
16521657 } )
16531658} )
1659+
1660+ describe ( 'singleton pattern' , ( ) => {
1661+ test ( 'getInstance returns the same instance' , ( ) => {
1662+ // Given/When
1663+ const instance1 = AppManagementClient . getInstance ( )
1664+ const instance2 = AppManagementClient . getInstance ( )
1665+
1666+ // Then
1667+ expect ( instance1 ) . toBe ( instance2 )
1668+ } )
1669+
1670+ test ( 'resetInstance allows creating a new instance' , ( ) => {
1671+ // Given
1672+ const instance1 = AppManagementClient . getInstance ( )
1673+
1674+ // When
1675+ AppManagementClient . resetInstance ( )
1676+ const instance2 = AppManagementClient . getInstance ( )
1677+
1678+ // Then
1679+ expect ( instance1 ) . not . toBe ( instance2 )
1680+ } )
1681+ } )
0 commit comments