Skip to content

Commit e35703a

Browse files
committed
E2E: QA - app basic flow
1 parent 14613f3 commit e35703a

1 file changed

Lines changed: 158 additions & 0 deletions

File tree

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

0 commit comments

Comments
 (0)