@essential-apps/shopify-test-runner 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/guestVnc.d.ts +31 -0
- package/dist/lib/guestVnc.d.ts.map +1 -0
- package/dist/lib/guestVnc.js +111 -0
- package/dist/lib/guestVnc.js.map +1 -0
- package/dist/probes/runProbe.js +0 -0
- package/dist/scripts/addStore.js +0 -0
- package/dist/scripts/buildImage.d.ts +3 -0
- package/dist/scripts/buildImage.d.ts.map +1 -0
- package/dist/scripts/{buildDockerImage.js → buildImage.js} +12 -10
- package/dist/scripts/buildImage.js.map +1 -0
- package/dist/scripts/captureAuth.js +0 -0
- package/dist/scripts/captureContracts.js +0 -0
- package/dist/scripts/captureRestContracts.js +0 -0
- package/dist/scripts/captureSharedContracts.d.ts +3 -0
- package/dist/scripts/captureSharedContracts.d.ts.map +1 -0
- package/dist/scripts/captureSharedContracts.js +209 -0
- package/dist/scripts/captureSharedContracts.js.map +1 -0
- package/dist/scripts/checkOperationCoverage.js +0 -0
- package/dist/scripts/cleanupStores.js +0 -0
- package/dist/scripts/createStores.js +0 -0
- package/dist/scripts/deployAppVersion.js +0 -0
- package/dist/scripts/devOnlineBackend.js +0 -0
- package/dist/scripts/installApp.js +0 -0
- package/dist/scripts/listStores.js +0 -0
- package/dist/scripts/runOffline.js +83 -1
- package/dist/scripts/runOffline.js.map +1 -1
- package/dist/scripts/runOfflineFullTests.js +82 -6
- package/dist/scripts/runOfflineFullTests.js.map +1 -1
- package/dist/scripts/runTests.js +0 -0
- package/dist/scripts/runVm.js +0 -0
- package/dist/scripts/runVmAuth.js +0 -0
- package/dist/scripts/setupTestDb.js +0 -0
- package/dist/scripts/verifyContracts.js +20 -29
- package/dist/scripts/verifyContracts.js.map +1 -1
- package/dist/scripts/verifyRestContracts.js +17 -30
- package/dist/scripts/verifyRestContracts.js.map +1 -1
- package/docker/Dockerfile.vm +1 -0
- package/package.json +11 -9
- package/src/lib/guestVnc.ts +147 -0
- package/src/scripts/{buildDockerImage.ts → buildImage.ts} +11 -9
- package/src/scripts/captureSharedContracts.ts +228 -0
- package/src/scripts/runOffline.ts +87 -1
- package/src/scripts/runOfflineFullTests.ts +94 -6
- package/src/scripts/verifyContracts.ts +22 -38
- package/src/scripts/verifyRestContracts.ts +23 -42
- package/dist/edge/nodeShim.d.ts +0 -2
- package/dist/edge/nodeShim.d.ts.map +0 -1
- package/dist/edge/nodeShim.js +0 -217
- package/dist/edge/nodeShim.js.map +0 -1
- package/dist/scripts/_probeSourceUrl.d.ts +0 -3
- package/dist/scripts/_probeSourceUrl.d.ts.map +0 -1
- package/dist/scripts/_probeSourceUrl.js +0 -119
- package/dist/scripts/_probeSourceUrl.js.map +0 -1
- package/dist/scripts/buildDockerImage.d.ts +0 -3
- package/dist/scripts/buildDockerImage.d.ts.map +0 -1
- package/dist/scripts/buildDockerImage.js.map +0 -1
- package/dist/scripts/devE2eBackend.d.ts +0 -3
- package/dist/scripts/devE2eBackend.d.ts.map +0 -1
- package/dist/scripts/devE2eBackend.js +0 -117
- package/dist/scripts/devE2eBackend.js.map +0 -1
- package/dist/scripts/runDocker.d.ts +0 -3
- package/dist/scripts/runDocker.d.ts.map +0 -1
- package/dist/scripts/runDocker.js +0 -88
- package/dist/scripts/runDocker.js.map +0 -1
- package/dist/scripts/runDockerAuth.d.ts +0 -3
- package/dist/scripts/runDockerAuth.d.ts.map +0 -1
- package/dist/scripts/runDockerAuth.js +0 -108
- package/dist/scripts/runDockerAuth.js.map +0 -1
- package/dist/scripts/runDockerOffline.d.ts +0 -3
- package/dist/scripts/runDockerOffline.d.ts.map +0 -1
- package/dist/scripts/runDockerOffline.js +0 -129
- package/dist/scripts/runDockerOffline.js.map +0 -1
- package/dist/scripts/runDockerOfflineExplore.d.ts +0 -3
- package/dist/scripts/runDockerOfflineExplore.d.ts.map +0 -1
- package/dist/scripts/runDockerOfflineExplore.js +0 -116
- package/dist/scripts/runDockerOfflineExplore.js.map +0 -1
- package/dist/scripts/runIsolatedDockerOffline.d.ts +0 -3
- package/dist/scripts/runIsolatedDockerOffline.d.ts.map +0 -1
- package/dist/scripts/runIsolatedDockerOffline.js +0 -351
- package/dist/scripts/runIsolatedDockerOffline.js.map +0 -1
- package/dist/scripts/runOfflineE2e.d.ts +0 -3
- package/dist/scripts/runOfflineE2e.d.ts.map +0 -1
- package/dist/scripts/runOfflineE2e.js +0 -408
- package/dist/scripts/runOfflineE2e.js.map +0 -1
- package/dist/scripts/runSupermachine.d.ts +0 -3
- package/dist/scripts/runSupermachine.d.ts.map +0 -1
- package/dist/scripts/runSupermachine.js +0 -474
- package/dist/scripts/runSupermachine.js.map +0 -1
- package/dist/scripts/runSupermachineAuth.d.ts +0 -3
- package/dist/scripts/runSupermachineAuth.d.ts.map +0 -1
- package/dist/scripts/runSupermachineAuth.js +0 -454
- package/dist/scripts/runSupermachineAuth.js.map +0 -1
- package/dist/vite/offlineConfig.d.ts +0 -34
- package/dist/vite/offlineConfig.d.ts.map +0 -1
- package/dist/vite/offlineConfig.js +0 -61
- package/dist/vite/offlineConfig.js.map +0 -1
- package/src/scripts/runDockerAuth.ts +0 -120
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifyRestContracts.js","sourceRoot":"","sources":["../../src/scripts/verifyRestContracts.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"verifyRestContracts.js","sourceRoot":"","sources":["../../src/scripts/verifyRestContracts.ts"],"names":[],"mappings":"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"}
|
package/docker/Dockerfile.vm
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@essential-apps/shopify-test-runner",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "Orchestration scripts (container, auth capture, install) and Playwright config preset for Essential Apps' Shopify test suites. Internal use only.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
"./contracts/normalize-html": {
|
|
22
22
|
"types": "./dist/contracts/normalizeHtml.d.ts",
|
|
23
23
|
"import": "./dist/contracts/normalizeHtml.js"
|
|
24
|
-
}
|
|
24
|
+
},
|
|
25
|
+
"./package.json": "./package.json"
|
|
25
26
|
},
|
|
26
27
|
"bin": {
|
|
27
28
|
"shopify-test-run-online": "./dist/scripts/runVm.js",
|
|
28
29
|
"shopify-test-run-offline": "./dist/scripts/runOffline.js",
|
|
29
30
|
"shopify-test-run-tests": "./dist/scripts/runTests.js",
|
|
30
31
|
"shopify-test-capture-online-auth": "./dist/scripts/runVmAuth.js",
|
|
31
|
-
"shopify-test-capture-online-auth-libkrun": "./dist/scripts/runDockerAuth.js",
|
|
32
32
|
"shopify-test-capture-auth": "./dist/scripts/captureAuth.js",
|
|
33
33
|
"shopify-test-deploy-app-version": "./dist/scripts/deployAppVersion.js",
|
|
34
34
|
"shopify-test-install-app": "./dist/scripts/installApp.js",
|
|
@@ -40,9 +40,10 @@
|
|
|
40
40
|
"shopify-test-dev-backend": "./dist/scripts/devOnlineBackend.js",
|
|
41
41
|
"shopify-test-run-offline-full-tests": "./dist/scripts/runOfflineFullTests.js",
|
|
42
42
|
"shopify-test-probe": "./dist/probes/runProbe.js",
|
|
43
|
-
"shopify-test-build-image": "./dist/scripts/
|
|
43
|
+
"shopify-test-build-image": "./dist/scripts/buildImage.js",
|
|
44
44
|
"shopify-test-check-operation-coverage": "./dist/scripts/checkOperationCoverage.js",
|
|
45
45
|
"shopify-test-capture-contracts": "./dist/scripts/captureContracts.js",
|
|
46
|
+
"shopify-test-capture-shared-contracts": "./dist/scripts/captureSharedContracts.js",
|
|
46
47
|
"shopify-test-verify-contracts": "./dist/scripts/verifyContracts.js",
|
|
47
48
|
"shopify-test-capture-rest-contracts": "./dist/scripts/captureRestContracts.js",
|
|
48
49
|
"shopify-test-verify-rest-contracts": "./dist/scripts/verifyRestContracts.js"
|
|
@@ -57,11 +58,12 @@
|
|
|
57
58
|
"clean": "rm -rf dist"
|
|
58
59
|
},
|
|
59
60
|
"dependencies": {
|
|
60
|
-
"@essential-apps/shopify-test-core": "^1.0.
|
|
61
|
-
"@essential-apps/shopify-test-
|
|
62
|
-
"@essential-apps/shopify-test-
|
|
63
|
-
"@essential-apps/shopify-test-
|
|
64
|
-
"@essential-apps/shopify-test-
|
|
61
|
+
"@essential-apps/shopify-test-core": "^1.0.13",
|
|
62
|
+
"@essential-apps/shopify-test-contracts": "^1.0.13",
|
|
63
|
+
"@essential-apps/shopify-test-mock-admin": "^1.0.13",
|
|
64
|
+
"@essential-apps/shopify-test-shopify-api": "^1.0.13",
|
|
65
|
+
"@essential-apps/shopify-test-storefront": "^1.0.13",
|
|
66
|
+
"@essential-apps/shopify-test-themes": "^1.0.13",
|
|
65
67
|
"@playwright/test": "^1.49.0",
|
|
66
68
|
"@types/node": "^20.19.40",
|
|
67
69
|
"@types/tar-stream": "^3.1.4",
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expose a guest VM's Xvfb display over VNC to the host.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the interactive VM flows that need a developer to *see*
|
|
5
|
+
* what a headed Chromium is doing inside the microVM:
|
|
6
|
+
* - `runVmAuth.ts` — one-time Shopify login capture.
|
|
7
|
+
* - `runOffline.ts` (explore mode) — click through the offline mock
|
|
8
|
+
* stack (admin app + storefront) by hand.
|
|
9
|
+
*
|
|
10
|
+
* The sequence (extracted so both flows share one implementation):
|
|
11
|
+
* 1. (Re)start Xvfb :99 with access control off, then x11vnc bound
|
|
12
|
+
* to 0.0.0.0:5900 inside the guest.
|
|
13
|
+
* 2. Forward host 127.0.0.1:5900 → guest :5900 via `vm.exposeTcp`.
|
|
14
|
+
* 3. On macOS, open Screen Sharing at vnc://localhost:5900 with the
|
|
15
|
+
* password prefilled. Elsewhere, print connect instructions.
|
|
16
|
+
*
|
|
17
|
+
* Returns the TCP forwarder handle so the caller can keep it alive
|
|
18
|
+
* for the session and close it on teardown.
|
|
19
|
+
*/
|
|
20
|
+
import { spawn as nodeSpawn } from 'node:child_process';
|
|
21
|
+
import { platform as osPlatform } from 'node:os';
|
|
22
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
23
|
+
|
|
24
|
+
/** Minimal structural view of the VM handle this helper needs. */
|
|
25
|
+
export interface VncCapableVm {
|
|
26
|
+
exec(opts: {
|
|
27
|
+
argv: string[];
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
}): Promise<{ exitCode: number; stdout: Buffer; stderr: Buffer }>;
|
|
30
|
+
exposeTcp(hostPort: number, guestPort: number): Promise<unknown>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const DEFAULT_VNC_PORT = 5900;
|
|
34
|
+
|
|
35
|
+
export interface ExposeGuestVncOptions {
|
|
36
|
+
/** VNC auth password. Default: TEST_ONLINE_VNC_PASSWORD ?? 'test'. */
|
|
37
|
+
vncPassword?: string;
|
|
38
|
+
/** Host+guest VNC port. Default: 5900. */
|
|
39
|
+
port?: number;
|
|
40
|
+
/** Xvfb virtual screen geometry. Default: '1600x1000x24'. */
|
|
41
|
+
screen?: string;
|
|
42
|
+
/** Log prefix, e.g. '[runOffline]'. Default: '[guestVnc]'. */
|
|
43
|
+
logPrefix?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Start x11vnc in the guest, bridge it to the host, and (on macOS)
|
|
48
|
+
* open Screen Sharing. Throws if Xvfb or x11vnc fail to come up.
|
|
49
|
+
*
|
|
50
|
+
* @returns the forwarder handle (keep it referenced; close on exit).
|
|
51
|
+
*/
|
|
52
|
+
export async function exposeGuestVnc(
|
|
53
|
+
vm: VncCapableVm,
|
|
54
|
+
opts: ExposeGuestVncOptions = {},
|
|
55
|
+
): Promise<unknown> {
|
|
56
|
+
const vncPassword =
|
|
57
|
+
opts.vncPassword ?? process.env['TEST_ONLINE_VNC_PASSWORD'] ?? 'test';
|
|
58
|
+
const port = opts.port ?? DEFAULT_VNC_PORT;
|
|
59
|
+
const screen = opts.screen ?? '1600x1000x24';
|
|
60
|
+
const log = opts.logPrefix ?? '[guestVnc]';
|
|
61
|
+
|
|
62
|
+
// 1) (Re)start Xvfb :99 + x11vnc inside the guest. We restart Xvfb
|
|
63
|
+
// with -ac (access control off) so x11vnc — launched from this
|
|
64
|
+
// fresh exec shell, without the entrypoint's Xauthority — can
|
|
65
|
+
// attach without a cookie. Background via shell `&` + nohup so
|
|
66
|
+
// both survive this exec returning.
|
|
67
|
+
console.error(`${log} starting x11vnc on guest :${port}…`);
|
|
68
|
+
const vncStart = await vm.exec({
|
|
69
|
+
argv: [
|
|
70
|
+
'bash',
|
|
71
|
+
'-c',
|
|
72
|
+
`
|
|
73
|
+
# No set -e: pkill/rm of absent things exit nonzero legitimately.
|
|
74
|
+
# Match Xvfb by exact name (-x), NOT -f: -f matches this script's
|
|
75
|
+
# own argv (the heredoc contains "Xvfb") and would kill our shell.
|
|
76
|
+
echo "[xvfb] restarting Xvfb :99 ${screen} -ac …"
|
|
77
|
+
pkill -x Xvfb 2>/dev/null || true
|
|
78
|
+
rm -f /tmp/.X11-unix/X99 /tmp/.X99-lock
|
|
79
|
+
sleep 0.3
|
|
80
|
+
nohup Xvfb :99 -screen 0 ${screen} -nolisten tcp -ac \
|
|
81
|
+
>/var/log/xvfb.log 2>&1 &
|
|
82
|
+
for _ in $(seq 1 50); do
|
|
83
|
+
[ -e /tmp/.X11-unix/X99 ] && break
|
|
84
|
+
sleep 0.1
|
|
85
|
+
done
|
|
86
|
+
if [ ! -e /tmp/.X11-unix/X99 ]; then
|
|
87
|
+
echo "[xvfb] FAILED to bind /tmp/.X11-unix/X99 within 5s"
|
|
88
|
+
cat /var/log/xvfb.log
|
|
89
|
+
exit 1
|
|
90
|
+
fi
|
|
91
|
+
echo "[xvfb] up."
|
|
92
|
+
|
|
93
|
+
mkdir -p /root/.vnc
|
|
94
|
+
x11vnc -storepasswd "${vncPassword}" /root/.vnc/passwd >/dev/null 2>&1
|
|
95
|
+
echo "[x11vnc] starting on :${port}…"
|
|
96
|
+
nohup x11vnc -display :99 -forever -shared \
|
|
97
|
+
-rfbauth /root/.vnc/passwd -rfbport ${port} -quiet \
|
|
98
|
+
>/var/log/x11vnc.log 2>&1 &
|
|
99
|
+
echo "[x11vnc] pid $!"
|
|
100
|
+
# Confirm the listener bound. netstat (net-tools) is in the image;
|
|
101
|
+
# bash /dev/tcp is unreliable on jammy (device sometimes disabled).
|
|
102
|
+
for _ in $(seq 1 30); do
|
|
103
|
+
if netstat -ltn 2>/dev/null | grep -q ":${port} "; then
|
|
104
|
+
exit 0
|
|
105
|
+
fi
|
|
106
|
+
sleep 0.1
|
|
107
|
+
done
|
|
108
|
+
echo "x11vnc didn't bind :${port} within 3s"
|
|
109
|
+
netstat -ltn 2>&1 || echo "(netstat unavailable)"
|
|
110
|
+
cat /var/log/x11vnc.log
|
|
111
|
+
exit 1
|
|
112
|
+
`,
|
|
113
|
+
],
|
|
114
|
+
timeoutMs: 15_000,
|
|
115
|
+
});
|
|
116
|
+
if (vncStart.exitCode !== 0) {
|
|
117
|
+
console.error(
|
|
118
|
+
`${log} x11vnc failed:`,
|
|
119
|
+
vncStart.stdout.toString(),
|
|
120
|
+
vncStart.stderr.toString(),
|
|
121
|
+
);
|
|
122
|
+
throw new Error('x11vnc startup failed');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 2) Bridge host 127.0.0.1:port → guest :port.
|
|
126
|
+
const forwarder = await vm.exposeTcp(port, port);
|
|
127
|
+
console.error(`${log} forwarding host 127.0.0.1:${port} → guest :${port}`);
|
|
128
|
+
|
|
129
|
+
// 3) Open a viewer (macOS Screen Sharing), password prefilled.
|
|
130
|
+
if (osPlatform() === 'darwin') {
|
|
131
|
+
// Tiny delay so the accept loop is warm before the client dials,
|
|
132
|
+
// else Screen Sharing intermittently shows "connection refused".
|
|
133
|
+
await sleep(500);
|
|
134
|
+
const vncUrl = `vnc://:${encodeURIComponent(vncPassword)}@localhost:${port}`;
|
|
135
|
+
console.error(
|
|
136
|
+
`${log} opening macOS Screen Sharing → vnc://localhost:${port} (password prefilled)`,
|
|
137
|
+
);
|
|
138
|
+
console.error(`${log} (if nothing opens: open '${vncUrl}')`);
|
|
139
|
+
nodeSpawn('open', [vncUrl], { stdio: 'ignore', detached: true }).unref();
|
|
140
|
+
} else {
|
|
141
|
+
console.error(
|
|
142
|
+
`${log} connect any VNC viewer to localhost:${port} (password: ${vncPassword}).`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return forwarder;
|
|
147
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Build the canonical `shopify-test` image
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Build the canonical `shopify-test` VM image via Apple's `container
|
|
4
|
+
* build` (NOT docker — we don't use docker for any test path). The
|
|
5
|
+
* image spec is the bundled `docker/Dockerfile`; Dockerfile syntax is
|
|
6
|
+
* what `container build` consumes, and the directory name is
|
|
7
|
+
* historical. supermachine restores microVMs from the built image.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
9
|
+
* Run directly:
|
|
8
10
|
*
|
|
9
|
-
*
|
|
11
|
+
* node --import tsx packages/runner/src/scripts/buildImage.ts
|
|
10
12
|
*
|
|
11
|
-
*
|
|
13
|
+
* or via the exposed bin `shopify-test-build-image`.
|
|
12
14
|
*
|
|
13
15
|
* The image is amd64-only (Rosetta on Apple Silicon) — see
|
|
14
16
|
* docker/README.md for why.
|
|
@@ -22,7 +24,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
22
24
|
// inspecting `container images` sees the same name they `npm install`.
|
|
23
25
|
const DEFAULT_TAG = 'essential-apps/shopify-test:latest';
|
|
24
26
|
|
|
25
|
-
function
|
|
27
|
+
function imageContextDir(): string {
|
|
26
28
|
// This file lives at packages/runner/src/scripts/ (or dist/scripts/
|
|
27
29
|
// after build). Walk up + sideways to docker/.
|
|
28
30
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -42,10 +44,10 @@ function dockerContextDir(): string {
|
|
|
42
44
|
|
|
43
45
|
async function main(): Promise<void> {
|
|
44
46
|
const tag = process.env['SHOPIFY_TEST_IMAGE_TAG'] ?? DEFAULT_TAG;
|
|
45
|
-
const context =
|
|
47
|
+
const context = imageContextDir();
|
|
46
48
|
|
|
47
49
|
console.log('────────────────────────────────────────────────────────────');
|
|
48
|
-
console.log(' shopify-test
|
|
50
|
+
console.log(' shopify-test VM image build (Apple `container build`)');
|
|
49
51
|
console.log('────────────────────────────────────────────────────────────');
|
|
50
52
|
console.log(` Tag : ${tag}`);
|
|
51
53
|
console.log(` Context : ${context}`);
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Regenerate the SHARED contract goldens in
|
|
4
|
+
* `@essential-apps/shopify-test-contracts/fixtures` from the in-process
|
|
5
|
+
* offline mock — the "replayable-variable capture" generator.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists (the design the centralization landed on):
|
|
8
|
+
* A verify golden must be *mock-replayable* — `verify-contracts`
|
|
9
|
+
* replays its `source` + `variables` against the offline mock and
|
|
10
|
+
* asserts the response matches. A conformance capture (scrubbed LIVE
|
|
11
|
+
* variables like `ownerId: <NUMERIC_ID>`, a richer field selection)
|
|
12
|
+
* is NOT replayable: the mock can't resolve the placeholders. So the
|
|
13
|
+
* two are different artifacts.
|
|
14
|
+
*
|
|
15
|
+
* The clean split:
|
|
16
|
+
* - The verify golden is captured FROM THE MOCK with deterministic
|
|
17
|
+
* seed variables — replayable by construction. That's this script.
|
|
18
|
+
* - `@essential-apps/shopify-test-conformance` separately CERTIFIES
|
|
19
|
+
* the mock matches live Shopify (schema SDL + shape diffs). A
|
|
20
|
+
* golden that's mock-replayable AND mock-certified-vs-live is
|
|
21
|
+
* trustworthy end to end.
|
|
22
|
+
*
|
|
23
|
+
* This script keeps each golden's `operationName` / `source` /
|
|
24
|
+
* `variables` (so the *request* contract is stable) and refreshes only
|
|
25
|
+
* the `response` to the current mock output. Idempotent: unchanged
|
|
26
|
+
* responses aren't rewritten.
|
|
27
|
+
*
|
|
28
|
+
* node --import tsx packages/runner/src/scripts/captureSharedContracts.ts
|
|
29
|
+
* # or the bin: shopify-test-capture-shared-contracts
|
|
30
|
+
*/
|
|
31
|
+
import { readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
32
|
+
import { resolve } from 'node:path';
|
|
33
|
+
import {
|
|
34
|
+
createAdminApi,
|
|
35
|
+
createStorefrontApi,
|
|
36
|
+
} from '@essential-apps/shopify-test-shopify-api';
|
|
37
|
+
import { ShopState } from '@essential-apps/shopify-test-storefront';
|
|
38
|
+
import {
|
|
39
|
+
fixturesDir,
|
|
40
|
+
type OperationContract,
|
|
41
|
+
type RestContract,
|
|
42
|
+
} from '@essential-apps/shopify-test-contracts/operation-contract';
|
|
43
|
+
import { normaliseResponse } from '../contracts/normalize.js';
|
|
44
|
+
|
|
45
|
+
const ADMIN_API_VERSION = '2025-07';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Structural equality under the same normalisation `verify` applies
|
|
49
|
+
* (gids → `<ID>`, timestamps → `<DATETIME>`, …). We rewrite a golden
|
|
50
|
+
* only on genuine structural drift — not when a volatile-but-equivalent
|
|
51
|
+
* value (a fresh timestamp/id) changed — so the generator is idempotent.
|
|
52
|
+
*/
|
|
53
|
+
function sameShape(a: unknown, b: unknown): boolean {
|
|
54
|
+
return (
|
|
55
|
+
JSON.stringify(normaliseResponse(a)) === JSON.stringify(normaliseResponse(b))
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Deterministic GraphQL seed. MUST stay in lockstep with
|
|
61
|
+
* `verifyContracts.ts` / `captureContracts.ts` `buildSeededState` — the
|
|
62
|
+
* goldens are replayed against this exact state at verify time.
|
|
63
|
+
*/
|
|
64
|
+
function buildGraphqlState(): ShopState {
|
|
65
|
+
const state = new ShopState({
|
|
66
|
+
shop: {
|
|
67
|
+
domain: 'test-shop.myshopify.com',
|
|
68
|
+
permanent_domain: 'test-shop.myshopify.com',
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
state.addProduct({
|
|
72
|
+
id: 900_000_001,
|
|
73
|
+
handle: 'sample-product',
|
|
74
|
+
title: 'Sample Product',
|
|
75
|
+
description: 'Used by contract capture as a deterministic fixture.',
|
|
76
|
+
price: 1000,
|
|
77
|
+
vendor: 'Sample Vendor',
|
|
78
|
+
type: 'Sample',
|
|
79
|
+
variants: [
|
|
80
|
+
{
|
|
81
|
+
id: 900_010_001,
|
|
82
|
+
title: 'Default Title',
|
|
83
|
+
price: 1000,
|
|
84
|
+
available: true,
|
|
85
|
+
sku: 'SAMPLE-1',
|
|
86
|
+
inventory_quantity: 100,
|
|
87
|
+
selected_options: ['Default Title'],
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
tags: [],
|
|
91
|
+
});
|
|
92
|
+
state.addCollection({
|
|
93
|
+
id: 900_020_001,
|
|
94
|
+
handle: 'sample-collection',
|
|
95
|
+
title: 'Sample Collection',
|
|
96
|
+
productHandles: ['sample-product'],
|
|
97
|
+
});
|
|
98
|
+
return state;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** REST seed — matches `verifyRestContracts.ts` `buildSeededState`. */
|
|
102
|
+
function buildRestState(): ShopState {
|
|
103
|
+
return new ShopState({
|
|
104
|
+
shop: {
|
|
105
|
+
domain: 'test-shop.myshopify.com',
|
|
106
|
+
permanent_domain: 'test-shop.myshopify.com',
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function fillPath(template: string, params: Record<string, string>): string {
|
|
112
|
+
return template.replace(/\{([^}]+)\}/g, (_, key: string) => {
|
|
113
|
+
if (!(key in params)) {
|
|
114
|
+
throw new Error(`path template references {${key}} but no value provided`);
|
|
115
|
+
}
|
|
116
|
+
return encodeURIComponent(params[key] ?? '');
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function appendQuery(path: string, query?: Record<string, string>): string {
|
|
120
|
+
if (!query || Object.keys(query).length === 0) return path;
|
|
121
|
+
const params = new URLSearchParams();
|
|
122
|
+
for (const [k, v] of Object.entries(query)) params.set(k, v);
|
|
123
|
+
return `${path}${path.includes('?') ? '&' : '?'}${params.toString()}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Flat `<op>.json` goldens only (skip manifests + store-keyed captures). */
|
|
127
|
+
function flatGoldens(dir: string): string[] {
|
|
128
|
+
let entries: string[];
|
|
129
|
+
try {
|
|
130
|
+
entries = readdirSync(dir);
|
|
131
|
+
} catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
return entries.filter(
|
|
135
|
+
(f) =>
|
|
136
|
+
f.endsWith('.json') &&
|
|
137
|
+
!f.endsWith('.manifest.json') &&
|
|
138
|
+
!f.slice(0, -'.json'.length).includes('.'),
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function regenGraphql(api: 'admin' | 'storefront'): Promise<number> {
|
|
143
|
+
const dir = resolve(fixturesDir(), api);
|
|
144
|
+
const state = buildGraphqlState();
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Hono types are loose
|
|
146
|
+
const app: any =
|
|
147
|
+
api === 'admin' ? createAdminApi({ state }) : createStorefrontApi({ state });
|
|
148
|
+
const endpoint =
|
|
149
|
+
api === 'admin'
|
|
150
|
+
? '/admin/api/2025-07/graphql.json'
|
|
151
|
+
: '/api/2025-07/graphql.json';
|
|
152
|
+
let changed = 0;
|
|
153
|
+
for (const f of flatGoldens(dir)) {
|
|
154
|
+
const p = resolve(dir, f);
|
|
155
|
+
const g = JSON.parse(readFileSync(p, 'utf8')) as OperationContract;
|
|
156
|
+
if (g.warning) continue;
|
|
157
|
+
const resp = await app.request(endpoint, {
|
|
158
|
+
method: 'POST',
|
|
159
|
+
headers: {
|
|
160
|
+
'Content-Type': 'application/json',
|
|
161
|
+
'X-Shopify-Access-Token': 'mock-access-token',
|
|
162
|
+
},
|
|
163
|
+
body: JSON.stringify({ query: g.source, variables: g.variables }),
|
|
164
|
+
});
|
|
165
|
+
const actual = await resp.json();
|
|
166
|
+
if (!sameShape(g.response, actual)) {
|
|
167
|
+
g.response = actual;
|
|
168
|
+
writeFileSync(p, JSON.stringify(g, null, 2) + '\n');
|
|
169
|
+
console.log(` updated ${api}/${f}`);
|
|
170
|
+
changed++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return changed;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function regenRest(): Promise<number> {
|
|
177
|
+
const dir = resolve(fixturesDir(), 'admin-rest');
|
|
178
|
+
const state = buildRestState();
|
|
179
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Hono types are loose
|
|
180
|
+
const app: any = createAdminApi({ state });
|
|
181
|
+
let changed = 0;
|
|
182
|
+
for (const f of flatGoldens(dir)) {
|
|
183
|
+
const p = resolve(dir, f);
|
|
184
|
+
const c = JSON.parse(readFileSync(p, 'utf8')) as RestContract;
|
|
185
|
+
if (c.warning) continue;
|
|
186
|
+
const finalPath = appendQuery(
|
|
187
|
+
fillPath(c.path, { version: ADMIN_API_VERSION, ...(c.pathParams ?? {}) }),
|
|
188
|
+
c.query,
|
|
189
|
+
);
|
|
190
|
+
const init: RequestInit = {
|
|
191
|
+
method: c.method,
|
|
192
|
+
headers: {
|
|
193
|
+
'Content-Type': 'application/json',
|
|
194
|
+
'X-Shopify-Access-Token': 'mock-access-token',
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
if (c.body !== undefined) init.body = JSON.stringify(c.body);
|
|
198
|
+
const resp: Response = await app.request(finalPath, init);
|
|
199
|
+
let body: unknown = null;
|
|
200
|
+
try {
|
|
201
|
+
body = await resp.json();
|
|
202
|
+
} catch {
|
|
203
|
+
/* empty / non-JSON */
|
|
204
|
+
}
|
|
205
|
+
const next = { status: resp.status, body };
|
|
206
|
+
if (c.response.status !== next.status || !sameShape(c.response.body, next.body)) {
|
|
207
|
+
c.response = next;
|
|
208
|
+
writeFileSync(p, JSON.stringify(c, null, 2) + '\n');
|
|
209
|
+
console.log(` updated admin-rest/${f}`);
|
|
210
|
+
changed++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return changed;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function main(): Promise<void> {
|
|
217
|
+
console.log('[capture-shared-contracts] regenerating shared goldens from the offline mock…');
|
|
218
|
+
let total = 0;
|
|
219
|
+
total += await regenGraphql('admin');
|
|
220
|
+
total += await regenGraphql('storefront');
|
|
221
|
+
total += await regenRest();
|
|
222
|
+
console.log(`[capture-shared-contracts] done — ${total} golden(s) refreshed.`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
main().catch((err) => {
|
|
226
|
+
console.error(err);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
});
|
|
@@ -56,6 +56,7 @@ import { dirname, resolve } from 'node:path';
|
|
|
56
56
|
import { fileURLToPath } from 'node:url';
|
|
57
57
|
import { pickApp, prepareOciArchive, envFileArgs, computeBuildHash } from '@essential-apps/shopify-test-core';
|
|
58
58
|
import { buildExtensions } from '../lib/buildExtensions.js';
|
|
59
|
+
import { exposeGuestVnc } from '../lib/guestVnc.js';
|
|
59
60
|
|
|
60
61
|
const repoRoot = process.cwd();
|
|
61
62
|
|
|
@@ -338,6 +339,27 @@ async function main(): Promise<void> {
|
|
|
338
339
|
}
|
|
339
340
|
})();
|
|
340
341
|
const runtimeTag = `sm${runtimeVersion.replace(/[^0-9a-z]+/gi, '')}`;
|
|
342
|
+
|
|
343
|
+
// Fold THIS runner package's own version into the snapshot key. The
|
|
344
|
+
// warm snapshot bakes the in-VM `npm install` (which installs the
|
|
345
|
+
// orchestrator + fixtures this same shopify-test version ships). Those
|
|
346
|
+
// run INSIDE the VM, so a version bump that changes orchestrator/
|
|
347
|
+
// fixture behaviour (e.g. screen-video support) would otherwise
|
|
348
|
+
// warm-restore a STALE in-VM install — the consumer updates the
|
|
349
|
+
// package but keeps running the old in-VM code until they manually
|
|
350
|
+
// `rm -rf ~/.cache/<app>-test`. Keying on the runner version makes a
|
|
351
|
+
// bump auto-rebake (cold install of the new version) — same
|
|
352
|
+
// no-manual-bust philosophy as manifestHash + runtimeTag.
|
|
353
|
+
const runnerVersion = ((): string => {
|
|
354
|
+
try {
|
|
355
|
+
return (
|
|
356
|
+
createRequire(import.meta.url)('../../package.json') as { version: string }
|
|
357
|
+
).version;
|
|
358
|
+
} catch {
|
|
359
|
+
return 'unknown';
|
|
360
|
+
}
|
|
361
|
+
})();
|
|
362
|
+
const runnerTag = `r${runnerVersion.replace(/[^0-9a-z]+/gi, '')}`;
|
|
341
363
|
// Scope the snapshot to the consuming app. supermachine keys WARM
|
|
342
364
|
// snapshots by (image + warmupTag), but the image is shared across
|
|
343
365
|
// apps and the rest of the tag is identical for any two apps vendoring
|
|
@@ -347,7 +369,7 @@ async function main(): Promise<void> {
|
|
|
347
369
|
// identity (observed: essential-upsell booting with essential-seo's
|
|
348
370
|
// DB). The cache paths are already appName-namespaced; this aligns the
|
|
349
371
|
// snapshot key with them.
|
|
350
|
-
const warmupTag = `offline-v13-${appName}-${runtimeTag}-${manifestHash}`;
|
|
372
|
+
const warmupTag = `offline-v13-${appName}-${runtimeTag}-${runnerTag}-${manifestHash}`;
|
|
351
373
|
|
|
352
374
|
// Dynamic import — @supermachine/core is heavy and only needed
|
|
353
375
|
// when actually running tests, not for `--help` etc.
|
|
@@ -508,6 +530,20 @@ async function main(): Promise<void> {
|
|
|
508
530
|
// teammates can run `PLAYWRIGHT_UPDATE_SNAPSHOTS=all npm run
|
|
509
531
|
// test:offline` after intentional UI changes.
|
|
510
532
|
'PLAYWRIGHT_UPDATE_SNAPSHOTS',
|
|
533
|
+
// Opt-in full-screen recording: run headed under Xvfb and capture
|
|
534
|
+
// the display with ffmpeg (see videoMounts above + the
|
|
535
|
+
// orchestrator's Xvfb startup + the offlineFullStack screen-video
|
|
536
|
+
// fixture).
|
|
537
|
+
'TEST_OFFLINE_VIDEO',
|
|
538
|
+
// Offline runs headed-under-Xvfb by default; opt out to headless.
|
|
539
|
+
'TEST_OFFLINE_HEADLESS',
|
|
540
|
+
// Interactive explore mode: orchestrator skips Playwright and
|
|
541
|
+
// instead opens a headed Chromium (admin + storefront tabs) on
|
|
542
|
+
// the guest Xvfb display, which we surface over VNC (see the
|
|
543
|
+
// explore branch below).
|
|
544
|
+
'TEST_OFFLINE_EXPLORE',
|
|
545
|
+
'TEST_OFFLINE_EXPLORE_URL',
|
|
546
|
+
'TEST_OFFLINE_EXPLORE_NO_DEVTOOLS',
|
|
511
547
|
'CI',
|
|
512
548
|
'DEBUG',
|
|
513
549
|
]) {
|
|
@@ -580,6 +616,56 @@ async function main(): Promise<void> {
|
|
|
580
616
|
forwardEnv['PRISMA_QUERY_ENGINE_LIBRARY'] =
|
|
581
617
|
'/workspace/node_modules/.prisma/client/libquery_engine-linux-arm64-openssl-3.0.x.so.node';
|
|
582
618
|
|
|
619
|
+
// ── Explore mode (interactive, VNC) ───────────────────────────
|
|
620
|
+
// `TEST_OFFLINE_EXPLORE=true` boots the full offline mock stack
|
|
621
|
+
// and hands the developer a real browser inside the VM instead of
|
|
622
|
+
// running Playwright. We surface the guest's Xvfb display over VNC
|
|
623
|
+
// (supermachine `vm.exposeTcp`, NOT docker --publish) so they can
|
|
624
|
+
// click through the admin app + storefront against the mock stack.
|
|
625
|
+
// The orchestrator opens both tabs (see runOfflineFullTests explore
|
|
626
|
+
// branch) and holds until the browser window is closed / Ctrl-C.
|
|
627
|
+
if (process.env['TEST_OFFLINE_EXPLORE'] === 'true') {
|
|
628
|
+
let forwarder: { stop?: () => Promise<void> | void } | undefined;
|
|
629
|
+
try {
|
|
630
|
+
forwarder = (await exposeGuestVnc(vm, {
|
|
631
|
+
logPrefix: '[runOffline]',
|
|
632
|
+
})) as { stop?: () => Promise<void> | void };
|
|
633
|
+
|
|
634
|
+
console.error('[runOffline] launching explore session…');
|
|
635
|
+
const exploreProc = await vm.spawn({
|
|
636
|
+
argv: ['sh', '-c', `
|
|
637
|
+
cd /workspace
|
|
638
|
+
export PATH=/workspace/node_modules/.bin:$PATH
|
|
639
|
+
export DISPLAY=:99
|
|
640
|
+
node ${envFileArgs(repoRoot)} node_modules/@essential-apps/shopify-test-runner/dist/scripts/runOfflineFullTests.js
|
|
641
|
+
`],
|
|
642
|
+
env: forwardEnv,
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
// Stream guest output verbatim with NO stall/hard timeout — an
|
|
646
|
+
// explore session legitimately sits idle for as long as the
|
|
647
|
+
// developer is poking around. It ends when they close the
|
|
648
|
+
// browser (orchestrator exits) or Ctrl-C here.
|
|
649
|
+
const pump = (async () => {
|
|
650
|
+
while (true) {
|
|
651
|
+
const out = await exploreProc.readStdout(64 * 1024);
|
|
652
|
+
if (out.length === 0) {
|
|
653
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
process.stdout.write(out);
|
|
657
|
+
}
|
|
658
|
+
})();
|
|
659
|
+
pump.catch(() => {});
|
|
660
|
+
|
|
661
|
+
const wait = await exploreProc.wait();
|
|
662
|
+
process.exitCode = wait.exitCode ?? 0;
|
|
663
|
+
return;
|
|
664
|
+
} finally {
|
|
665
|
+
if (forwarder?.stop) await Promise.resolve(forwarder.stop()).catch(() => {});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
583
669
|
// Optional --grep forwarding for fast iteration on a single
|
|
584
670
|
// scenario or spec without writing a one-off bench script.
|
|
585
671
|
const grep = process.env['PLAYWRIGHT_GREP'];
|