Skip to content

Commit b31e3b2

Browse files
committed
E2E: QA - app basic flow
1 parent a91fa51 commit b31e3b2

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/* eslint-disable no-restricted-imports */
2+
import {
3+
appTestFixture as test,
4+
createApp,
5+
extractClientId,
6+
deployApp,
7+
versionsList,
8+
configLink,
9+
teardownApp,
10+
} from '../setup/app.js'
11+
import {CLI_TIMEOUT, TEST_TIMEOUT} from '../setup/constants.js'
12+
import {requireEnv} from '../setup/env.js'
13+
import {expect} from '@playwright/test'
14+
import * as fs from 'fs'
15+
import * as path from 'path'
16+
17+
/**
18+
* App basic flow — from scratch (QA checklist: Apps section, no extensions).
19+
*
20+
* Exercises the full app lifecycle end-to-end:
21+
* 1. Create a new app from the reactRouter template
22+
* 2. Start dev server
23+
* 3. Run a GraphQL query via app execute
24+
* 4. Quit dev server with q
25+
* 5. Clean dev preview
26+
* 6. Deploy with a version tag
27+
* 7. Verify version in versions list
28+
* 8. Create a secondary app, config link to it
29+
* 9. Deploy to the secondary app
30+
*/
31+
test.describe('App basic flow — from scratch', () => {
32+
test('init, dev, execute, quit, clean, deploy, versions, config link, deploy to secondary', async ({
33+
cli,
34+
env,
35+
browserPage,
36+
}) => {
37+
test.setTimeout(TEST_TIMEOUT.long)
38+
requireEnv(env, 'orgId', 'storeFqdn')
39+
40+
const parentDir = fs.mkdtempSync(path.join(env.tempDir, 'app-'))
41+
const appName = `E2E-basic-${Date.now()}`
42+
let secondaryParentDir = ''
43+
const secondaryAppName = `E2E-basic2-${Date.now()}`
44+
45+
try {
46+
// Step 1: Create a new app
47+
const initResult = await createApp({
48+
cli,
49+
parentDir,
50+
name: appName,
51+
template: 'reactRouter',
52+
flavor: 'typescript',
53+
packageManager: 'npm',
54+
orgId: env.orgId,
55+
})
56+
expect(initResult.exitCode, `Step 1 - app init failed:\n${initResult.stderr}`).toBe(0)
57+
const appDir = initResult.appDir
58+
59+
// Step 2: Start dev server (CI='' enables keyboard shortcuts)
60+
const dev = await cli.spawn(['app', 'dev', '--path', appDir], {env: {CI: ''}})
61+
try {
62+
await dev.waitForOutput('Ready, watching for changes in your app', CLI_TIMEOUT.medium)
63+
64+
// Step 3: Run a GraphQL query
65+
const executeResult = await cli.exec(
66+
['app', 'execute', '--query', 'query { shop { name } }', '--path', appDir],
67+
{timeout: CLI_TIMEOUT.short},
68+
)
69+
const executeOutput = executeResult.stdout + executeResult.stderr
70+
expect(executeResult.exitCode, `Step 3 - app execute failed:\n${executeOutput}`).toBe(0)
71+
expect(executeOutput, 'Step 3 - app execute: response missing "shop" field').toContain('shop')
72+
73+
// Step 4: Quit dev server
74+
dev.sendKey('q')
75+
const devExitCode = await dev.waitForExit(CLI_TIMEOUT.short)
76+
expect(devExitCode, 'Step 4 - app dev quit failed').toBe(0)
77+
} finally {
78+
dev.kill()
79+
}
80+
81+
// Step 5: Clean dev preview
82+
const cleanResult = await cli.exec(['app', 'dev', 'clean', '--path', appDir])
83+
const cleanOutput = cleanResult.stdout + cleanResult.stderr
84+
expect(cleanResult.exitCode, `Step 5 - app dev clean failed:\n${cleanOutput}`).toBe(0)
85+
expect(cleanOutput, 'Step 5 - missing "Dev preview stopped"').toContain('Dev preview stopped')
86+
87+
// Step 6: Deploy with a version tag
88+
const versionTag = `E2E-v1-${Date.now()}`
89+
const deployResult = await deployApp({
90+
cli,
91+
appDir,
92+
version: versionTag,
93+
message: 'E2E basic flow deployment',
94+
})
95+
expect(deployResult.exitCode, `Step 6 - app deploy failed:\n${deployResult.stderr}`).toBe(0)
96+
97+
// Step 7: Verify version in list
98+
const listResult = await versionsList({cli, appDir})
99+
const listOutput = listResult.stdout + listResult.stderr
100+
expect(listResult.exitCode, `Step 7 - versions list failed:\n${listOutput}`).toBe(0)
101+
expect(listOutput, `Step 7 - version tag "${versionTag}" not found`).toContain(versionTag)
102+
103+
// Step 8: Create a secondary app and config link to it
104+
secondaryParentDir = fs.mkdtempSync(path.join(env.tempDir, 'app-'))
105+
const secondaryInit = await createApp({
106+
cli,
107+
parentDir: secondaryParentDir,
108+
name: secondaryAppName,
109+
template: 'reactRouter',
110+
flavor: 'typescript',
111+
packageManager: 'npm',
112+
orgId: env.orgId,
113+
})
114+
expect(secondaryInit.exitCode, `Step 8a - secondary app init failed:\n${secondaryInit.stderr}`).toBe(0)
115+
116+
const secondaryClientId = extractClientId(secondaryInit.appDir)
117+
118+
// Write a TOML stub so config link skips the "Configuration file name" prompt
119+
fs.writeFileSync(path.join(appDir, 'shopify.app.secondary.toml'), `client_id = "${secondaryClientId}"\n`)
120+
121+
const linkResult = await configLink({cli, appDir, clientId: secondaryClientId})
122+
const linkOutput = linkResult.stdout + linkResult.stderr
123+
expect(linkResult.exitCode, `Step 8b - config link failed:\n${linkOutput}`).toBe(0)
124+
expect(linkOutput, 'Step 8b - missing "is now linked to"').toContain('is now linked to')
125+
126+
// Step 9: Deploy to the secondary app
127+
const tomlFiles = fs
128+
.readdirSync(appDir)
129+
.filter(
130+
(file: string) => file.startsWith('shopify.app.') && file.endsWith('.toml') && file !== 'shopify.app.toml',
131+
)
132+
const secondaryConfig = tomlFiles[0]?.replace('shopify.app.', '').replace('.toml', '') ?? 'secondary'
133+
const secondaryVersionTag = `E2E-v2-${Date.now()}`
134+
const secondaryDeployResult = await deployApp({
135+
cli,
136+
appDir,
137+
config: secondaryConfig,
138+
version: secondaryVersionTag,
139+
message: 'E2E secondary deployment',
140+
})
141+
expect(secondaryDeployResult.exitCode, `Step 9 - secondary deploy failed:\n${secondaryDeployResult.stderr}`).toBe(
142+
0,
143+
)
144+
} finally {
145+
// E2E_SKIP_CLEANUP=1 skips cleanup for debugging. Run `pnpm test:e2e-cleanup` afterward.
146+
if (!process.env.E2E_SKIP_CLEANUP) {
147+
fs.rmSync(parentDir, {recursive: true, force: true})
148+
if (secondaryParentDir) fs.rmSync(secondaryParentDir, {recursive: true, force: true})
149+
await teardownApp({browserPage, appName, email: process.env.E2E_ACCOUNT_EMAIL, orgId: env.orgId})
150+
await teardownApp({
151+
browserPage,
152+
appName: secondaryAppName,
153+
email: process.env.E2E_ACCOUNT_EMAIL,
154+
orgId: env.orgId,
155+
})
156+
}
157+
}
158+
})
159+
})

0 commit comments

Comments
 (0)