Skip to content

Commit e839a00

Browse files
committed
QA to E2E: app basic flow (no extensions)
1 parent 0318e7b commit e839a00

3 files changed

Lines changed: 126 additions & 0 deletions

File tree

packages/e2e/setup/auth.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export const authFixture = cliFixture.extend<{}, {authLogin: void}>({
2222
return
2323
}
2424

25+
process.stdout.write('[e2e] Authenticating automatically — no action required.\n')
26+
2527
// Clear any existing session
2628
await execa('node', [executables.cli, 'auth', 'logout'], {
2729
env: env.processEnv,

packages/e2e/setup/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const cliFixture = envFixture.extend<{cli: CLIProcess}>({
5252
const execaOpts: ExecaOptions = {
5353
cwd: opts.cwd,
5454
env: {...env.processEnv, ...opts.env},
55+
extendEnv: false,
5556
timeout,
5657
reject: false,
5758
}
@@ -75,6 +76,7 @@ export const cliFixture = envFixture.extend<{cli: CLIProcess}>({
7576
const execaOpts: ExecaOptions = {
7677
cwd: opts.cwd,
7778
env: {...env.processEnv, ...opts.env},
79+
extendEnv: false,
7880
timeout,
7981
reject: false,
8082
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import {appScaffoldFixture as test} from '../setup/app.js'
2+
import {requireEnv} from '../setup/env.js'
3+
import {expect} from '@playwright/test'
4+
import {writeFileSync} from 'node:fs'
5+
import {join} from 'node:path'
6+
7+
test.describe('App basic flow (no extensions)', () => {
8+
test('init, dev, execute, quit, clean, deploy, versions, config link, deploy to secondary', async ({
9+
appScaffold,
10+
cli,
11+
env,
12+
}) => {
13+
// Full flow: init + dev (3 min) + deploy + config link + secondary deploy — needs 10 min
14+
test.setTimeout(10 * 60 * 1000)
15+
16+
requireEnv(env, 'clientId', 'storeFqdn', 'secondaryClientId')
17+
18+
// Step 1: Create a React Router app
19+
const initResult = await appScaffold.init({
20+
template: 'reactRouter',
21+
flavor: 'javascript',
22+
packageManager: 'npm',
23+
})
24+
expect(initResult.exitCode, `app init failed:\n${initResult.stdout}\n${initResult.stderr}`).toBe(0)
25+
26+
// Step 2: Start dev server via PTY
27+
// Unset CI so keyboard shortcuts are enabled in the Dev UI
28+
const dev = await cli.spawn(['app', 'dev', '--path', appScaffold.appDir], {env: {CI: ''}})
29+
try {
30+
await dev.waitForOutput('Ready, watching for changes in your app', 3 * 60 * 1000)
31+
32+
// Step 3: Run a GraphQL query while the dev server is running
33+
const executeResult = await cli.exec(
34+
['app', 'execute', '--query', 'query { shop { name } }', '--path', appScaffold.appDir],
35+
{timeout: 60 * 1000},
36+
)
37+
const executeOutput = executeResult.stdout + executeResult.stderr
38+
expect(executeResult.exitCode, `app execute failed:\n${executeOutput}`).toBe(0)
39+
expect(executeOutput).toContain('shop')
40+
41+
// Step 4: Press q to quit the dev server
42+
dev.sendKey('q')
43+
const devExitCode = await dev.waitForExit(30_000)
44+
expect(devExitCode).toBe(0)
45+
} finally {
46+
// Step 5: Always clean up the dev preview, even if the test fails
47+
dev.kill()
48+
const cleanResult = await cli.exec(['app', 'dev', 'clean', '--path', appScaffold.appDir])
49+
const cleanOutput = cleanResult.stdout + cleanResult.stderr
50+
expect(cleanResult.exitCode, `dev clean failed:\n${cleanOutput}`).toBe(0)
51+
expect(cleanOutput).toContain('Dev preview stopped')
52+
}
53+
54+
// Step 6: Deploy the primary app
55+
const versionTag = `e2e-v-${Date.now()}`
56+
const deployResult = await cli.exec(
57+
[
58+
'app',
59+
'deploy',
60+
'--path',
61+
appScaffold.appDir,
62+
'--force',
63+
'--version',
64+
versionTag,
65+
'--message',
66+
'E2E basic flow deployment',
67+
],
68+
{timeout: 5 * 60 * 1000},
69+
)
70+
const deployOutput = deployResult.stdout + deployResult.stderr
71+
expect(deployResult.exitCode, `deploy failed:\n${deployOutput}`).toBe(0)
72+
73+
// Step 7: List versions and verify our tag appears
74+
const listResult = await cli.exec(['app', 'versions', 'list', '--path', appScaffold.appDir, '--json'], {
75+
timeout: 60 * 1000,
76+
})
77+
const listOutput = listResult.stdout + listResult.stderr
78+
expect(listResult.exitCode, `versions list failed:\n${listOutput}`).toBe(0)
79+
expect(listOutput).toContain(versionTag)
80+
81+
// Step 8: Config link to the secondary app
82+
// Pre-create a minimal TOML stub so getTomls() finds the secondary client ID and skips
83+
// the interactive "Configuration file name" prompt entirely. This avoids PTY timing races
84+
// where the Enter key arrives before ink has fully initialized the text prompt, which
85+
// causes renderTextPrompt to return '' → filenameFromName('') = 'shopify.app.toml' →
86+
// that file already exists → overwrite confirmation prompt hangs.
87+
// (--config and --client-id are mutually exclusive flags, so we can't pass both directly.)
88+
writeFileSync(
89+
join(appScaffold.appDir, 'shopify.app.secondary.toml'),
90+
`client_id = "${env.secondaryClientId}"\n`,
91+
)
92+
93+
const configLink = await cli.spawn(
94+
['app', 'config', 'link', '--path', appScaffold.appDir, '--client-id', env.secondaryClientId],
95+
{env: {CI: '', SHOPIFY_FLAG_CLIENT_ID: undefined}},
96+
)
97+
await configLink.waitForOutput('is now linked to', 2 * 60 * 1000)
98+
const configLinkExitCode = await configLink.waitForExit(30_000)
99+
expect(configLinkExitCode, `config link failed:\n${configLink.getOutput()}`).toBe(0)
100+
101+
// Step 9: Deploy to the secondary app using the linked config file
102+
const secondaryVersionTag = `e2e-secondary-v-${Date.now()}`
103+
const secondaryDeployResult = await cli.exec(
104+
[
105+
'app',
106+
'deploy',
107+
'--path',
108+
appScaffold.appDir,
109+
'--config',
110+
'secondary',
111+
'--force',
112+
'--version',
113+
secondaryVersionTag,
114+
'--message',
115+
'E2E secondary app deployment',
116+
],
117+
{timeout: 5 * 60 * 1000, env: {SHOPIFY_FLAG_CLIENT_ID: undefined}},
118+
)
119+
const secondaryDeployOutput = secondaryDeployResult.stdout + secondaryDeployResult.stderr
120+
expect(secondaryDeployResult.exitCode, `secondary deploy failed:\n${secondaryDeployOutput}`).toBe(0)
121+
})
122+
})

0 commit comments

Comments
 (0)