Skip to content

Commit d74c18a

Browse files
committed
add unit tests for init command
1 parent 0b79a0f commit d74c18a

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import Init from './init.js'
2+
import initPrompt from '../../prompts/init/init.js'
3+
import initService from '../../services/init/init.js'
4+
import {selectDeveloperPlatformClient} from '../../utilities/developer-platform-client.js'
5+
import {selectOrg} from '../../services/context.js'
6+
import {appNamePrompt, createAsNewAppPrompt} from '../../prompts/dev.js'
7+
import {validateFlavorValue, validateTemplateValue} from '../../services/init/validate.js'
8+
import {testAppLinked, testDeveloperPlatformClient, testOrganization} from '../../models/app/app.test-data.js'
9+
import {describe, expect, test, vi} from 'vitest'
10+
import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output'
11+
import {generateRandomNameForSubdirectory} from '@shopify/cli-kit/node/fs'
12+
import {inferPackageManager} from '@shopify/cli-kit/node/node-package-manager'
13+
14+
vi.mock('../../prompts/init/init.js')
15+
vi.mock('../../services/init/init.js')
16+
vi.mock('../../utilities/developer-platform-client.js')
17+
vi.mock('../../services/context.js')
18+
vi.mock('../../prompts/dev.js')
19+
vi.mock('../../services/dev/fetch.js')
20+
vi.mock('../../services/init/validate.js')
21+
vi.mock('@shopify/cli-kit/node/is-global')
22+
vi.mock('@shopify/cli-kit/node/fs')
23+
vi.mock('@shopify/cli-kit/node/node-package-manager')
24+
vi.mock('@shopify/cli-kit/node/metadata')
25+
26+
describe('Init command', () => {
27+
test('runs init command with default flags', async () => {
28+
// Given
29+
const mockOrganization = testOrganization()
30+
const mockDeveloperPlatformClient = testDeveloperPlatformClient()
31+
const mockApp = testAppLinked()
32+
33+
mockAndCaptureOutput()
34+
vi.mocked(validateTemplateValue).mockReturnValue(undefined)
35+
vi.mocked(validateFlavorValue).mockReturnValue(undefined)
36+
vi.mocked(inferPackageManager).mockReturnValue('npm')
37+
vi.mocked(generateRandomNameForSubdirectory).mockResolvedValue('test-app')
38+
vi.mocked(selectDeveloperPlatformClient).mockReturnValue(mockDeveloperPlatformClient)
39+
vi.mocked(selectOrg).mockResolvedValue(mockOrganization)
40+
41+
// Mock the orgAndApps method on the developer platform client
42+
vi.mocked(mockDeveloperPlatformClient.orgAndApps).mockResolvedValue({
43+
organization: mockOrganization,
44+
apps: [],
45+
hasMorePages: false,
46+
})
47+
48+
vi.mocked(initPrompt).mockResolvedValue({
49+
template: 'https://github.com/Shopify/shopify-app-template-remix',
50+
templateType: 'remix',
51+
globalCLIResult: {install: false, alreadyInstalled: false},
52+
})
53+
vi.mocked(createAsNewAppPrompt).mockResolvedValue(true)
54+
vi.mocked(appNamePrompt).mockResolvedValue('test-app')
55+
vi.mocked(initService).mockResolvedValue({app: mockApp})
56+
57+
// When
58+
await Init.run([])
59+
60+
// Then
61+
expect(initService).toHaveBeenCalledWith(
62+
expect.objectContaining({
63+
name: 'test-app',
64+
packageManager: 'npm',
65+
}),
66+
)
67+
})
68+
69+
test('runs init command without prompts when organization-id, name, and template flags are provided', async () => {
70+
// Given
71+
const mockOrganization = testOrganization()
72+
const mockDeveloperPlatformClient = testDeveloperPlatformClient()
73+
const mockApp = testAppLinked()
74+
75+
mockAndCaptureOutput()
76+
vi.mocked(validateTemplateValue).mockReturnValue(undefined)
77+
vi.mocked(validateFlavorValue).mockReturnValue(undefined)
78+
vi.mocked(inferPackageManager).mockReturnValue('npm')
79+
vi.mocked(selectDeveloperPlatformClient).mockReturnValue(mockDeveloperPlatformClient)
80+
81+
// Mock fetchOrganizations to return the organization
82+
const {fetchOrganizations} = await import('../../services/dev/fetch.js')
83+
vi.mocked(fetchOrganizations).mockResolvedValue([mockOrganization])
84+
85+
// Mock the orgAndApps method on the developer platform client
86+
vi.mocked(mockDeveloperPlatformClient.orgAndApps).mockResolvedValue({
87+
organization: mockOrganization,
88+
apps: [],
89+
hasMorePages: false,
90+
})
91+
92+
vi.mocked(initPrompt).mockResolvedValue({
93+
template: 'https://github.com/Shopify/shopify-app-template-remix',
94+
templateType: 'remix',
95+
globalCLIResult: {install: false, alreadyInstalled: false},
96+
})
97+
vi.mocked(initService).mockResolvedValue({app: mockApp})
98+
99+
// When
100+
await Init.run(['--organization-id', mockOrganization.id, '--name', 'my-app', '--template', 'remix'])
101+
102+
// Then
103+
// Verify that prompt functions were NOT called
104+
// Any other interactive prompts would also cause the test to fail with an AbortError
105+
expect(selectOrg).not.toHaveBeenCalled()
106+
expect(createAsNewAppPrompt).not.toHaveBeenCalled()
107+
expect(appNamePrompt).not.toHaveBeenCalled()
108+
109+
// Verify the command completed successfully
110+
expect(initService).toHaveBeenCalledWith(
111+
expect.objectContaining({
112+
name: 'my-app',
113+
packageManager: 'npm',
114+
template: 'https://github.com/Shopify/shopify-app-template-remix',
115+
}),
116+
)
117+
})
118+
119+
test('fails with clear error message when invalid organization-id is provided', async () => {
120+
// Given
121+
const validOrg = testOrganization()
122+
const mockDeveloperPlatformClient = testDeveloperPlatformClient()
123+
124+
// Suppress stderr output for this error test
125+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
126+
127+
try {
128+
const outputMock = mockAndCaptureOutput()
129+
vi.mocked(validateTemplateValue).mockReturnValue(undefined)
130+
vi.mocked(validateFlavorValue).mockReturnValue(undefined)
131+
vi.mocked(inferPackageManager).mockReturnValue('npm')
132+
vi.mocked(selectDeveloperPlatformClient).mockReturnValue(mockDeveloperPlatformClient)
133+
134+
// Mock fetchOrganizations to return only the valid organization
135+
const {fetchOrganizations} = await import('../../services/dev/fetch.js')
136+
vi.mocked(fetchOrganizations).mockResolvedValue([validOrg])
137+
138+
vi.mocked(initPrompt).mockResolvedValue({
139+
template: 'https://github.com/Shopify/shopify-app-template-remix',
140+
templateType: 'remix',
141+
globalCLIResult: {install: false, alreadyInstalled: false},
142+
})
143+
144+
// When/Then
145+
// The command throws an AbortError which is caught by oclif's error handler
146+
// This causes process.exit(1) which vitest intercepts
147+
await expect(
148+
Init.run(['--organization-id', 'invalid-org-id', '--name', 'my-app', '--template', 'remix']),
149+
).rejects.toThrow('process.exit unexpectedly called with "1"')
150+
151+
// Verify the error message was displayed
152+
expect(outputMock.error()).toContain('Organization with ID invalid-org-id not found')
153+
154+
// Verify initService was never called since validation failed
155+
expect(initService).not.toHaveBeenCalled()
156+
} finally {
157+
// Always restore console.error, even if the test fails
158+
consoleErrorSpy.mockRestore()
159+
}
160+
})
161+
})

0 commit comments

Comments
 (0)