-
Notifications
You must be signed in to change notification settings - Fork 256
Expand file tree
/
Copy pathapp-deploy.spec.ts
More file actions
160 lines (149 loc) · 6.84 KB
/
app-deploy.spec.ts
File metadata and controls
160 lines (149 loc) · 6.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import {appTestFixture as test, createApp, deployApp, versionsList, configLink} from '../setup/app.js'
import {teardownAll} from '../setup/teardown.js'
import {TEST_TIMEOUT} from '../setup/constants.js'
import {requireEnv} from '../setup/env.js'
import {expect} from '@playwright/test'
import * as fs from 'fs'
// eslint-disable-next-line no-restricted-imports
import * as path from 'path'
/**
* Test A — full deploy lifecycle (QA checklist: Apps section, deploy flow).
*
* 1. `app init` Create primary app (React Router + JavaScript)
* 2. `app deploy --version v1` Deploy with a version tag
* 3. `app versions list` Verify the primary tag is active and no other
* version is stuck active
* 4. `app config link` from primary dir → creates a brand-new secondary app
* interactively (answers org → "Create new?" → app name; config name
* prompt is skipped via `--config secondary`)
* 5. `app deploy --config secondary` Deploy from primary dir to secondary app
* 6. `app versions list --config secondary` Verify the secondary deploy hit
* the secondary app (not a silent fallback to primary) and the primary
* tag does not leak into secondary's list
*
* Test body is pure CLI; teardown uses the dev dashboard to delete both apps.
*/
interface VersionLine {
versionTag?: string | null
status: string
}
/**
* Asserts a `versions list --json` result shows:
* - `expectedTag` is present and `active`
* - no other version is stuck `active`
* - (if `forbiddenTag` provided) `forbiddenTag` does not appear at all
*
* The last check guards cross-app leakage: a version we expect to live on one
* app should never appear in another app's version list.
*/
function assertActiveVersion(opts: {
result: {stdout: string; stderr: string; exitCode: number}
expectedTag: string
step: string
forbiddenTag?: string
}) {
const {result, expectedTag, step, forbiddenTag} = opts
const output = result.stdout + result.stderr
expect(result.exitCode, `${step} - versions list failed:\n${output}`).toBe(0)
const versions = JSON.parse(result.stdout) as VersionLine[]
const entry = versions.find((version) => version.versionTag === expectedTag)
expect(entry, `${step} - version tag "${expectedTag}" not found in:\n${result.stdout}`).toBeDefined()
expect(entry?.status, `${step} - expected "${expectedTag}" to be active, got "${entry?.status}"`).toBe('active')
const otherActive = versions.filter((version) => version.versionTag !== expectedTag && version.status === 'active')
expect(otherActive, `${step} - unexpected other active versions: ${JSON.stringify(otherActive)}`).toHaveLength(0)
if (forbiddenTag) {
const tags = versions.map((version) => version.versionTag)
expect(tags, `${step} - tag "${forbiddenTag}" unexpectedly found in list`).not.toContain(forbiddenTag)
}
}
test.describe('App deploy', () => {
test('init, deploy, versions list, config link, deploy to secondary', async ({cli, env, browserPage}) => {
test.setTimeout(TEST_TIMEOUT.long)
requireEnv(env, 'orgId')
const parentDir = fs.mkdtempSync(path.join(env.tempDir, 'app-'))
const appName = `E2E-deploy1-${Date.now()}`
const secondaryAppName = `E2E-deploy2-${Date.now()}`
try {
// Step 1: Create primary app (React Router template)
const initResult = await createApp({
cli,
parentDir,
name: appName,
template: 'reactRouter',
flavor: 'javascript',
packageManager: 'pnpm',
orgId: env.orgId,
})
expect(initResult.exitCode, `Step 1 - primary app init failed:\n${initResult.stderr}`).toBe(0)
const appDir = initResult.appDir
// Step 2: Deploy with a tagged version
const versionTag = `E2E-v1-${Date.now()}`
const deployResult = await deployApp({cli, appDir, version: versionTag, message: 'E2E A primary deployment'})
const deployOutput = deployResult.stdout + deployResult.stderr
expect(deployResult.exitCode, `Step 2 - deploy failed:\n${deployOutput}`).toBe(0)
// Step 3: Verify the primary tag is active and no other version is stuck active.
const listResult = await versionsList({cli, appDir})
assertActiveVersion({result: listResult, expectedTag: versionTag, step: 'Step 3'})
// Step 4: Config link from primary dir → creates a brand-new secondary app
// interactively (org → "Create new?" → "App name"). The "Configuration file
// name" prompt is skipped via `--config secondary`.
const secondaryConfig = 'secondary'
const linkResult = await configLink({
cli,
appDir,
appName: secondaryAppName,
orgId: env.orgId,
configName: secondaryConfig,
})
const linkOutput = linkResult.stdout + linkResult.stderr
expect(linkResult.exitCode, `Step 4 - config link failed:\n${linkOutput}`).toBe(0)
expect(linkOutput, `Step 4 - missing "is now linked to \\"${secondaryAppName}\\""`).toContain(
`is now linked to "${secondaryAppName}"`,
)
const secondaryTomlPath = path.join(appDir, `shopify.app.${secondaryConfig}.toml`)
expect(
fs.existsSync(secondaryTomlPath),
`Step 4 - expected ${secondaryTomlPath} to exist after config link`,
).toBe(true)
// Step 5: Deploy from primary dir to secondary app via --config secondary
const secondaryVersionTag = `E2E-v2-${Date.now()}`
const secondaryDeployResult = await deployApp({
cli,
appDir,
config: secondaryConfig,
version: secondaryVersionTag,
message: 'E2E A secondary deployment',
})
const secondaryDeployOutput = secondaryDeployResult.stdout + secondaryDeployResult.stderr
expect(secondaryDeployResult.exitCode, `Step 5 - secondary deploy failed:\n${secondaryDeployOutput}`).toBe(0)
// Step 6: Verify the secondary deploy hit the secondary app (not a silent
// fallback to primary). Checks the secondary tag is active, no other
// version is stuck active, and the primary tag doesn't leak into secondary.
const secondaryListResult = await versionsList({cli, appDir, config: secondaryConfig})
assertActiveVersion({
result: secondaryListResult,
expectedTag: secondaryVersionTag,
step: 'Step 6',
forbiddenTag: versionTag,
})
} finally {
// E2E_SKIP_TEARDOWN=1 skips teardown for debugging. Run cleanup scripts afterward.
if (!process.env.E2E_SKIP_TEARDOWN) {
fs.rmSync(parentDir, {recursive: true, force: true})
// Neither app was installed on a store — delete the apps only (no uninstall)
await teardownAll({
browserPage,
appName,
orgId: env.orgId,
workerIndex: env.workerIndex,
})
await teardownAll({
browserPage,
appName: secondaryAppName,
orgId: env.orgId,
workerIndex: env.workerIndex,
})
}
}
})
})