@@ -3,9 +3,15 @@ import initPrompt from '../../prompts/init/init.js'
33import initService from '../../services/init/init.js'
44import { selectDeveloperPlatformClient } from '../../utilities/developer-platform-client.js'
55import { selectOrg } from '../../services/context.js'
6- import { appNamePrompt , createAsNewAppPrompt } from '../../prompts/dev.js'
6+ import { fetchOrgFromId , NoOrgError } from '../../services/dev/fetch.js'
7+ import { appNamePrompt , createAsNewAppPrompt , selectAppPrompt } from '../../prompts/dev.js'
78import { validateFlavorValue , validateTemplateValue } from '../../services/init/validate.js'
8- import { testAppLinked , testDeveloperPlatformClient , testOrganization } from '../../models/app/app.test-data.js'
9+ import {
10+ testAppLinked ,
11+ testDeveloperPlatformClient ,
12+ testOrganization ,
13+ testOrganizationApp ,
14+ } from '../../models/app/app.test-data.js'
915import { describe , expect , test , vi } from 'vitest'
1016import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output'
1117import { generateRandomNameForSubdirectory } from '@shopify/cli-kit/node/fs'
@@ -15,6 +21,13 @@ vi.mock('../../prompts/init/init.js')
1521vi . mock ( '../../services/init/init.js' )
1622vi . mock ( '../../utilities/developer-platform-client.js' )
1723vi . mock ( '../../services/context.js' )
24+ vi . mock ( '../../services/dev/fetch.js' , async ( importOriginal ) => {
25+ const actual = await importOriginal < typeof import ( '../../services/dev/fetch.js' ) > ( )
26+ return {
27+ ...actual ,
28+ fetchOrgFromId : vi . fn ( ) ,
29+ }
30+ } )
1831vi . mock ( '../../prompts/dev.js' )
1932vi . mock ( '../../services/init/validate.js' )
2033vi . mock ( '@shopify/cli-kit/node/fs' )
@@ -75,8 +88,8 @@ describe('Init command', () => {
7588 vi . mocked ( inferPackageManager ) . mockReturnValue ( 'npm' )
7689 vi . mocked ( selectDeveloperPlatformClient ) . mockReturnValue ( mockDeveloperPlatformClient )
7790
78- // Mock orgFromId to return the organization
79- vi . mocked ( mockDeveloperPlatformClient . orgFromId ) . mockResolvedValue ( mockOrganization )
91+ // Mock fetchOrgFromId to return the organization
92+ vi . mocked ( fetchOrgFromId ) . mockResolvedValue ( mockOrganization )
8093
8194 // Mock the orgAndApps method on the developer platform client
8295 vi . mocked ( mockDeveloperPlatformClient . orgAndApps ) . mockResolvedValue ( {
@@ -114,7 +127,6 @@ describe('Init command', () => {
114127
115128 test ( 'fails with clear error message when invalid organization-id is provided' , async ( ) => {
116129 // Given
117- const validOrg = testOrganization ( )
118130 const mockDeveloperPlatformClient = testDeveloperPlatformClient ( )
119131
120132 // Suppress stderr output for this error test
@@ -127,8 +139,10 @@ describe('Init command', () => {
127139 vi . mocked ( inferPackageManager ) . mockReturnValue ( 'npm' )
128140 vi . mocked ( selectDeveloperPlatformClient ) . mockReturnValue ( mockDeveloperPlatformClient )
129141
130- // Mock orgFromId to return undefined for invalid organization
131- vi . mocked ( mockDeveloperPlatformClient . orgFromId ) . mockResolvedValue ( undefined )
142+ // Mock fetchOrgFromId to throw NoOrgError for invalid organization
143+ vi . mocked ( fetchOrgFromId ) . mockRejectedValue (
144+ new NoOrgError ( { type : 'UserAccount' , email : 'test@example.com' } , 'invalid-org-id' ) ,
145+ )
132146
133147 vi . mocked ( initPrompt ) . mockResolvedValue ( {
134148 template : 'https://github.com/Shopify/shopify-app-template-remix' ,
@@ -144,7 +158,7 @@ describe('Init command', () => {
144158 ) . rejects . toThrow ( 'process.exit unexpectedly called with "1"' )
145159
146160 // Verify the error message was displayed
147- expect ( outputMock . error ( ) ) . toContain ( 'Organization with ID invalid-org-id not found' )
161+ expect ( outputMock . error ( ) ) . toContain ( 'No Organization found' )
148162
149163 // Verify initService was never called since validation failed
150164 expect ( initService ) . not . toHaveBeenCalled ( )
@@ -153,4 +167,54 @@ describe('Init command', () => {
153167 consoleErrorSpy . mockRestore ( )
154168 }
155169 } )
170+
171+ test ( 'skips app selection prompts when organization has existing apps but --name flag is provided' , async ( ) => {
172+ // Given
173+ const mockOrganization = testOrganization ( )
174+ const mockDeveloperPlatformClient = testDeveloperPlatformClient ( )
175+ const mockApp = testAppLinked ( )
176+ const existingApp = testOrganizationApp ( )
177+
178+ mockAndCaptureOutput ( )
179+ vi . mocked ( validateTemplateValue ) . mockReturnValue ( undefined )
180+ vi . mocked ( validateFlavorValue ) . mockReturnValue ( undefined )
181+ vi . mocked ( inferPackageManager ) . mockReturnValue ( 'npm' )
182+ vi . mocked ( selectDeveloperPlatformClient ) . mockReturnValue ( mockDeveloperPlatformClient )
183+
184+ // Mock fetchOrgFromId to return the organization
185+ vi . mocked ( fetchOrgFromId ) . mockResolvedValue ( mockOrganization )
186+
187+ // Mock the orgAndApps method to return existing apps
188+ vi . mocked ( mockDeveloperPlatformClient . orgAndApps ) . mockResolvedValue ( {
189+ organization : mockOrganization ,
190+ apps : [ existingApp ] ,
191+ hasMorePages : false ,
192+ } )
193+
194+ vi . mocked ( initPrompt ) . mockResolvedValue ( {
195+ template : 'https://github.com/Shopify/shopify-app-template-remix' ,
196+ templateType : 'remix' ,
197+ globalCLIResult : { install : false , alreadyInstalled : false } ,
198+ } )
199+ vi . mocked ( initService ) . mockResolvedValue ( { app : mockApp } )
200+
201+ // When
202+ await Init . run ( [ '--organization-id' , mockOrganization . id , '--name' , 'my-new-app' , '--template' , 'remix' ] )
203+
204+ // Then
205+ // Verify that app selection prompts were NOT called even though org has existing apps
206+ expect ( selectOrg ) . not . toHaveBeenCalled ( )
207+ expect ( createAsNewAppPrompt ) . not . toHaveBeenCalled ( )
208+ expect ( selectAppPrompt ) . not . toHaveBeenCalled ( )
209+ expect ( appNamePrompt ) . not . toHaveBeenCalled ( )
210+
211+ // Verify the command completed successfully with the provided name
212+ expect ( initService ) . toHaveBeenCalledWith (
213+ expect . objectContaining ( {
214+ name : 'my-new-app' ,
215+ packageManager : 'npm' ,
216+ template : 'https://github.com/Shopify/shopify-app-template-remix' ,
217+ } ) ,
218+ )
219+ } )
156220} )
0 commit comments