@bitmagic/cli 0.1.13 → 0.1.15
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/README.md +18 -1
- package/dist/cli.d.ts +44 -2
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/build.d.ts +6 -1
- package/dist/commands/build.js +14 -4
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/check.d.ts +6 -1
- package/dist/commands/check.js +14 -2
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/forge.d.ts +4 -0
- package/dist/commands/forge.js +24 -9
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/generate.d.ts +1 -1
- package/dist/commands/generate.js +42 -13
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +28 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.d.ts +4 -0
- package/dist/commands/login.js +15 -6
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts +4 -0
- package/dist/commands/logout.js +8 -1
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/publish.d.ts +10 -1
- package/dist/commands/publish.js +23 -8
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/upgrade.d.ts +7 -1
- package/dist/commands/upgrade.js +30 -14
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/commands/verify.d.ts +4 -0
- package/dist/commands/verify.js +28 -6
- package/dist/commands/verify.js.map +1 -1
- package/dist/commands/whoami.d.ts +4 -0
- package/dist/commands/whoami.js +9 -2
- package/dist/commands/whoami.js.map +1 -1
- package/dist/output.d.ts +36 -0
- package/dist/output.js +25 -0
- package/dist/output.js.map +1 -0
- package/dist/verify/browser.d.ts +22 -0
- package/dist/verify/browser.js +85 -1
- package/dist/verify/browser.js.map +1 -1
- package/dist/verify/classify.d.ts +12 -0
- package/dist/verify/classify.js +22 -0
- package/dist/verify/classify.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/verify.js
CHANGED
|
@@ -5,11 +5,12 @@ import { spawn, spawnSync } from 'child_process';
|
|
|
5
5
|
import { CliError } from '../errors.js';
|
|
6
6
|
import { loadProjectContext, projectBin } from '../project/context.js';
|
|
7
7
|
import { reserveCorsAllowedPort, CORS_ALLOWED_PORT_MIN, CORS_ALLOWED_PORT_MAX } from '../local-port.js';
|
|
8
|
-
import { runHeadlessCheck } from '../verify/browser.js';
|
|
8
|
+
import { runHeadlessCheck, DEFAULT_START_INTERACTION } from '../verify/browser.js';
|
|
9
9
|
import { classifyVerifyRun } from '../verify/classify.js';
|
|
10
10
|
import { waitForServer } from '../verify/wait-for-server.js';
|
|
11
11
|
import { computeFingerprint } from '../publish/fingerprint.js';
|
|
12
12
|
import { writeVerifyRecord, decideVerifyOutcome, recordAndGateVerify } from '../verify/result.js';
|
|
13
|
+
import { createOutput } from '../output.js';
|
|
13
14
|
const DEFAULT_SETTLE_MS = 15_000;
|
|
14
15
|
/**
|
|
15
16
|
* A free port INSIDE the CORS-allowed window, never an OS-assigned ephemeral one.
|
|
@@ -65,13 +66,18 @@ export const verifyCommand = defineCommand({
|
|
|
65
66
|
meta: { name: 'verify', description: 'Boot the game in a real browser and report what broke.' },
|
|
66
67
|
args: {
|
|
67
68
|
timeout: { type: 'string', description: `Settle time in ms (default ${DEFAULT_SETTLE_MS}).` },
|
|
69
|
+
json: {
|
|
70
|
+
type: 'boolean',
|
|
71
|
+
description: 'Print the result as JSON on stdout; all progress goes to stderr.',
|
|
72
|
+
},
|
|
68
73
|
},
|
|
69
74
|
async run({ args }) {
|
|
70
75
|
const settleMs = parseTimeoutArg(args.timeout);
|
|
71
76
|
const context = loadProjectContext();
|
|
72
77
|
const tsc = projectBin(context.root, 'tsc');
|
|
73
78
|
const vite = projectBin(context.root, 'vite');
|
|
74
|
-
|
|
79
|
+
const buildOutput = createOutput(args.json === true);
|
|
80
|
+
buildOutput.info('Building…');
|
|
75
81
|
const build = spawnSync(tsc, [], { cwd: context.root, stdio: 'inherit' });
|
|
76
82
|
if (build.error) {
|
|
77
83
|
throw new CliError(`Could not run the TypeScript compiler: ${build.error.message}`);
|
|
@@ -99,21 +105,37 @@ export const verifyCommand = defineCommand({
|
|
|
99
105
|
waitForServer(port, 30_000),
|
|
100
106
|
watchForEarlyExit(server, () => viteStderr),
|
|
101
107
|
]);
|
|
102
|
-
// ?autostart=1
|
|
103
|
-
//
|
|
108
|
+
// No ?autostart=1: verify clicks the engine Play button like a real player would
|
|
109
|
+
// (startInteraction), which both proves the menu came up AND that gameplay starts from it.
|
|
110
|
+
// Autostart bypassed the menu — a game whose Play flow was broken could still pass.
|
|
104
111
|
const observations = await runHeadlessCheck({
|
|
105
|
-
url: `http://localhost:${port}
|
|
112
|
+
url: `http://localhost:${port}/`,
|
|
106
113
|
screenshotPath,
|
|
107
114
|
consolePath,
|
|
108
115
|
settleMs,
|
|
116
|
+
startInteraction: DEFAULT_START_INTERACTION,
|
|
109
117
|
});
|
|
110
118
|
const result = classifyVerifyRun(observations);
|
|
111
119
|
const outcome = decideVerifyOutcome(result, computeFingerprint(context.root), new Date().toISOString());
|
|
120
|
+
const output = createOutput(args.json === true);
|
|
112
121
|
recordAndGateVerify(artifactDir, outcome, { consolePath, screenshotPath }, {
|
|
113
122
|
write: writeVerifyRecord,
|
|
114
|
-
|
|
123
|
+
// Under --json both streams are prose and belong on stderr; the result document below
|
|
124
|
+
// is the only thing stdout carries.
|
|
125
|
+
info: (line) => output.info(line),
|
|
115
126
|
error: (line) => console.error(line),
|
|
116
127
|
});
|
|
128
|
+
// Only reached when the run PASSED — recordAndGateVerify throws on failure, and the
|
|
129
|
+
// wrapper then emits the error document. Both paths leave stdout parseable.
|
|
130
|
+
output.result({
|
|
131
|
+
ok: true,
|
|
132
|
+
failures: outcome.record.failures,
|
|
133
|
+
warnings: outcome.record.warnings,
|
|
134
|
+
fingerprint: outcome.record.fingerprint,
|
|
135
|
+
artifactDir,
|
|
136
|
+
consolePath,
|
|
137
|
+
screenshotPath,
|
|
138
|
+
});
|
|
117
139
|
}
|
|
118
140
|
finally {
|
|
119
141
|
server?.kill('SIGTERM');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAqB,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACxG,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,4 +8,8 @@ export declare const whoamiCommand: import("citty").CommandDef<{
|
|
|
8
8
|
readonly type: "string";
|
|
9
9
|
readonly description: "Environment to query (dev, prod, local).";
|
|
10
10
|
};
|
|
11
|
+
readonly json: {
|
|
12
|
+
readonly type: "boolean";
|
|
13
|
+
readonly description: "Print the result as JSON on stdout; all progress goes to stderr.";
|
|
14
|
+
};
|
|
11
15
|
}>;
|
package/dist/commands/whoami.js
CHANGED
|
@@ -3,6 +3,7 @@ import { resolveEnvironment, resolveAuth0ClientId } from '../config/environments
|
|
|
3
3
|
import { readDefaultEnvironment } from '../config/credentials.js';
|
|
4
4
|
import { getAccessToken } from '../auth/session.js';
|
|
5
5
|
import { apiGet } from '../http/client.js';
|
|
6
|
+
import { createOutput } from '../output.js';
|
|
6
7
|
const deviceFlowDeps = { fetch: globalThis.fetch, sleep: (ms) => new Promise((r) => setTimeout(r, ms)) };
|
|
7
8
|
export async function runWhoami(explicitEnv) {
|
|
8
9
|
const environment = resolveEnvironment({
|
|
@@ -19,11 +20,17 @@ export const whoamiCommand = defineCommand({
|
|
|
19
20
|
meta: { name: 'whoami', description: 'Show the identity the CLI is authenticated as.' },
|
|
20
21
|
args: {
|
|
21
22
|
env: { type: 'string', description: 'Environment to query (dev, prod, local).' },
|
|
23
|
+
json: {
|
|
24
|
+
type: 'boolean',
|
|
25
|
+
description: 'Print the result as JSON on stdout; all progress goes to stderr.',
|
|
26
|
+
},
|
|
22
27
|
},
|
|
23
28
|
async run({ args }) {
|
|
29
|
+
const output = createOutput(args.json === true);
|
|
24
30
|
const me = await runWhoami(args.env);
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
output.info(`user: ${me.userId}`);
|
|
32
|
+
output.info(`roles: ${me.roles.length > 0 ? me.roles.join(', ') : '(none)'}`);
|
|
33
|
+
output.result({ ok: true, userId: me.userId, roles: me.roles });
|
|
27
34
|
},
|
|
28
35
|
});
|
|
29
36
|
//# sourceMappingURL=whoami.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAO5C,MAAM,cAAc,GAAG,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;AAEvH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,WAAoB;IAClD,MAAM,WAAW,GAAG,kBAAkB,CAAC;QACrC,QAAQ,EAAE,WAAW;QACrB,aAAa,EAAE,sBAAsB,EAAE;KACxC,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,WAAW,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;IAC1E,OAAO,MAAM,CAAiB,WAAW,EAAE,oBAAoB,EAAE,KAAK,EAAE;QACtE,KAAK,EAAE,UAAU,CAAC,KAAK;KACxB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;IACzC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gDAAgD,EAAE;IACvF,IAAI,EAAE;QACJ,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,0CAA0C,EAAE;QAChF,IAAI,EAAE;YACJ,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,kEAAkE;SAChF;KACF;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAChB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAChD,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9E,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;IAClE,CAAC;CACF,CAAC,CAAC"}
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a command's words go, and where its result goes.
|
|
3
|
+
*
|
|
4
|
+
* The lane's stated convention (pro-lane design, "Conventions that make this agent-drivable") is
|
|
5
|
+
* **progress to stderr, result JSON to stdout**, so `bitmagic forge --json > result.json` works
|
|
6
|
+
* while the agent still watches progress on a minutes-long pipeline.
|
|
7
|
+
*
|
|
8
|
+
* That only holds if EVERY human-facing line goes through `info`. A stray `console.log` in
|
|
9
|
+
* `--json` mode lands on stdout beside the JSON document and corrupts it — which is not a
|
|
10
|
+
* hypothetical: the engine-upgrade nudge did exactly that before this existed, printing through
|
|
11
|
+
* the same `log` seam as the result line.
|
|
12
|
+
*/
|
|
13
|
+
export interface Output {
|
|
14
|
+
/** Progress, warnings, prose. stdout normally; stderr under `--json`. */
|
|
15
|
+
info(message: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* The machine-readable result. Prints only under `--json`, as a single line on stdout.
|
|
18
|
+
*
|
|
19
|
+
* In human mode this is a no-op: the command has already said the same thing through `info`,
|
|
20
|
+
* in a form a person reads.
|
|
21
|
+
*/
|
|
22
|
+
result(value: unknown): void;
|
|
23
|
+
}
|
|
24
|
+
export declare function createOutput(json: boolean): Output;
|
|
25
|
+
/**
|
|
26
|
+
* The failure shape, so an agent can branch on one parse rather than on prose.
|
|
27
|
+
*
|
|
28
|
+
* Emitted on stdout like any other result: under `--json` the contract is "stdout is exactly one
|
|
29
|
+
* JSON document describing what happened", and a failure is as much an outcome as a success. The
|
|
30
|
+
* exit code is still authoritative and unchanged — this only makes the reason machine-readable.
|
|
31
|
+
*/
|
|
32
|
+
export declare function jsonErrorResult(message: string, exitCode: number): {
|
|
33
|
+
ok: false;
|
|
34
|
+
exitCode: number;
|
|
35
|
+
error: string;
|
|
36
|
+
};
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function createOutput(json) {
|
|
2
|
+
return {
|
|
3
|
+
info: (message) => {
|
|
4
|
+
if (json)
|
|
5
|
+
console.error(message);
|
|
6
|
+
else
|
|
7
|
+
console.log(message);
|
|
8
|
+
},
|
|
9
|
+
result: (value) => {
|
|
10
|
+
if (json)
|
|
11
|
+
console.log(JSON.stringify(value));
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The failure shape, so an agent can branch on one parse rather than on prose.
|
|
17
|
+
*
|
|
18
|
+
* Emitted on stdout like any other result: under `--json` the contract is "stdout is exactly one
|
|
19
|
+
* JSON document describing what happened", and a failure is as much an outcome as a success. The
|
|
20
|
+
* exit code is still authoritative and unchanged — this only makes the reason machine-readable.
|
|
21
|
+
*/
|
|
22
|
+
export function jsonErrorResult(message, exitCode) {
|
|
23
|
+
return { ok: false, exitCode, error: message };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAwBA,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,OAAO;QACL,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;YAChB,IAAI,IAAI;gBAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;;gBAC5B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QACD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAChB,IAAI,IAAI;gBAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/C,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,QAAgB;IAC/D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACjD,CAAC"}
|
package/dist/verify/browser.d.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import type { VerifyObservations } from './classify.js';
|
|
2
|
+
export interface StartInteractionOptions {
|
|
3
|
+
/** Selector for the engine Play button. Its label is localizable and per-game overridable, so
|
|
4
|
+
* never match by text. */
|
|
5
|
+
playButtonSelector: string;
|
|
6
|
+
/** Console line the engine logs when `handleGameStart()` runs — every start path (button
|
|
7
|
+
* click, auto-starting genres, custom start UIs calling `engine.startGame()`) produces it. */
|
|
8
|
+
startedConsoleMarker: string;
|
|
9
|
+
/** How long the Play button (or an auto-start) gets to show up after page load. */
|
|
10
|
+
buttonTimeoutMs: number;
|
|
11
|
+
/** How long after clicking the button the start marker gets to appear. */
|
|
12
|
+
postClickStartTimeoutMs: number;
|
|
13
|
+
}
|
|
14
|
+
export declare const DEFAULT_START_INTERACTION: StartInteractionOptions;
|
|
2
15
|
export interface HeadlessCheckOptions {
|
|
3
16
|
url: string;
|
|
4
17
|
screenshotPath: string;
|
|
@@ -15,6 +28,15 @@ export interface HeadlessCheckOptions {
|
|
|
15
28
|
* classifies as "the game did not start" even when it rendered perfectly.
|
|
16
29
|
*/
|
|
17
30
|
probeFrameUrlIncludes?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Start gameplay the way a player does — wait for the engine Play button and click it —
|
|
33
|
+
* before settling, and report what happened in `VerifyObservations.start`.
|
|
34
|
+
*
|
|
35
|
+
* Only `bitmagic verify` sets this. `publish`'s smoke check loads the game in an iframe where
|
|
36
|
+
* these top-level selectors would not reach, and its job is "does the published bundle come
|
|
37
|
+
* up", not "does gameplay start".
|
|
38
|
+
*/
|
|
39
|
+
startInteraction?: StartInteractionOptions;
|
|
18
40
|
}
|
|
19
41
|
/**
|
|
20
42
|
* Drives the creator's installed Chrome. `channel: 'chrome'` deliberately uses the system
|
package/dist/verify/browser.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import { chromium } from 'playwright-core';
|
|
3
3
|
import { CliError } from '../errors.js';
|
|
4
|
+
export const DEFAULT_START_INTERACTION = {
|
|
5
|
+
playButtonSelector: '#play-button, .hud-play-button',
|
|
6
|
+
startedConsoleMarker: '🎮 Starting game...',
|
|
7
|
+
buttonTimeoutMs: 30_000,
|
|
8
|
+
postClickStartTimeoutMs: 10_000,
|
|
9
|
+
};
|
|
4
10
|
/**
|
|
5
11
|
* Drives the creator's installed Chrome. `channel: 'chrome'` deliberately uses the system
|
|
6
12
|
* browser rather than a downloaded one, so installing the CLI stays a small, fast npm install.
|
|
@@ -41,6 +47,9 @@ export async function runHeadlessCheck(options) {
|
|
|
41
47
|
const detail = error instanceof Error ? error.message : String(error);
|
|
42
48
|
throw new CliError(`The page never finished loading at ${options.url}: ${detail}`);
|
|
43
49
|
}
|
|
50
|
+
const interaction = options.startInteraction === undefined
|
|
51
|
+
? null
|
|
52
|
+
: await performStartInteraction(page, consoleLines, options.startInteraction);
|
|
44
53
|
await page.waitForTimeout(options.settleMs);
|
|
45
54
|
// Same probe either way; only the frame it runs in differs. A missing child frame leaves
|
|
46
55
|
// `target` null, which classifies as "no canvas" — correct, because a harness whose iframe
|
|
@@ -68,7 +77,17 @@ export async function runHeadlessCheck(options) {
|
|
|
68
77
|
});
|
|
69
78
|
const screenshotBuffer = await page.screenshot();
|
|
70
79
|
fs.writeFileSync(options.screenshotPath, screenshotBuffer);
|
|
71
|
-
|
|
80
|
+
let start;
|
|
81
|
+
if (options.startInteraction !== undefined && interaction !== null) {
|
|
82
|
+
start = {
|
|
83
|
+
...interaction,
|
|
84
|
+
// Recomputed from the full console capture rather than taken from the interaction step,
|
|
85
|
+
// so a start that only lands during the settle (slow world load) still counts.
|
|
86
|
+
gameStartDetected: hasStartMarker(consoleLines, options.startInteraction.startedConsoleMarker),
|
|
87
|
+
menuVisibleAfterSettle: await isMenuVisible(page),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return { consoleLines, pageErrors, canvas, screenshotBytes: screenshotBuffer.length, start };
|
|
72
91
|
}
|
|
73
92
|
finally {
|
|
74
93
|
// Written on success and failure alike, including when the try block above threw before
|
|
@@ -78,4 +97,69 @@ export async function runHeadlessCheck(options) {
|
|
|
78
97
|
await browser.close();
|
|
79
98
|
}
|
|
80
99
|
}
|
|
100
|
+
const START_POLL_MS = 250;
|
|
101
|
+
function hasStartMarker(consoleLines, marker) {
|
|
102
|
+
return consoleLines.some((line) => line.includes(marker));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Start gameplay the way a player does: wait for the engine Play button and click it. The button
|
|
106
|
+
* only becomes visible once the world has finished loading, so "button appeared" doubles as a
|
|
107
|
+
* load-complete signal. Games without the button (auto-starting genres, custom start UIs) are
|
|
108
|
+
* accepted through the start marker instead.
|
|
109
|
+
*
|
|
110
|
+
* Never throws for a game that misbehaves — the run continues to settle/probe/screenshot so a
|
|
111
|
+
* failing game still leaves its menu screenshot and console log as artifacts, and classification
|
|
112
|
+
* decides pass/fail from what is recorded here.
|
|
113
|
+
*/
|
|
114
|
+
async function performStartInteraction(page, consoleLines, options) {
|
|
115
|
+
const button = page.locator(options.playButtonSelector).first();
|
|
116
|
+
let playButtonSeen = false;
|
|
117
|
+
let playButtonClicked = false;
|
|
118
|
+
// One poll loop instead of racing a locator.waitFor() against a console watcher: the marker
|
|
119
|
+
// lands in `consoleLines` (already captured by the page handlers), and isVisible() never
|
|
120
|
+
// throws on a missing element, so there are no promises left dangling when either side wins.
|
|
121
|
+
const buttonDeadline = Date.now() + options.buttonTimeoutMs;
|
|
122
|
+
while (Date.now() < buttonDeadline) {
|
|
123
|
+
if (hasStartMarker(consoleLines, options.startedConsoleMarker)) {
|
|
124
|
+
return { playButtonSeen, playButtonClicked };
|
|
125
|
+
}
|
|
126
|
+
if (await button.isVisible()) {
|
|
127
|
+
playButtonSeen = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
await page.waitForTimeout(START_POLL_MS);
|
|
131
|
+
}
|
|
132
|
+
if (playButtonSeen) {
|
|
133
|
+
try {
|
|
134
|
+
await button.click({ timeout: 5_000 });
|
|
135
|
+
playButtonClicked = true;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Visible but unclickable (detached mid-fade, covered by another element) — recorded as
|
|
139
|
+
// seen-but-not-clicked; the game can still pass if it started some other way.
|
|
140
|
+
}
|
|
141
|
+
const startDeadline = Date.now() + options.postClickStartTimeoutMs;
|
|
142
|
+
while (Date.now() < startDeadline && !hasStartMarker(consoleLines, options.startedConsoleMarker)) {
|
|
143
|
+
await page.waitForTimeout(START_POLL_MS);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { playButtonSeen, playButtonClicked };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Whether the engine's start menu overlay is still displayed. After a successful start the
|
|
150
|
+
* StartScreen fades out and sets `display: none`; a menu still up at the end of the run means
|
|
151
|
+
* the game bounced back to (or never left) the menu, whatever the console claimed.
|
|
152
|
+
*/
|
|
153
|
+
async function isMenuVisible(page) {
|
|
154
|
+
return page.evaluate(() => {
|
|
155
|
+
// Runs inside the browser, where `document`/`window` are real globals — see the canvas probe
|
|
156
|
+
// above for why eslint needs telling.
|
|
157
|
+
// eslint-disable-next-line no-undef
|
|
158
|
+
const menu = document.querySelector('#game-menu');
|
|
159
|
+
if (!(menu instanceof HTMLElement))
|
|
160
|
+
return false;
|
|
161
|
+
// eslint-disable-next-line no-undef
|
|
162
|
+
return window.getComputedStyle(menu).display !== 'none';
|
|
163
|
+
});
|
|
164
|
+
}
|
|
81
165
|
//# sourceMappingURL=browser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/verify/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/verify/browser.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,15 @@ export interface CanvasObservation {
|
|
|
13
13
|
bufferWidth: number;
|
|
14
14
|
bufferHeight: number;
|
|
15
15
|
}
|
|
16
|
+
/** What the start interaction (waiting for and clicking the engine Play button) observed. */
|
|
17
|
+
export interface StartObservations {
|
|
18
|
+
playButtonSeen: boolean;
|
|
19
|
+
playButtonClicked: boolean;
|
|
20
|
+
/** The engine's game-start console line appeared at any point during the run. */
|
|
21
|
+
gameStartDetected: boolean;
|
|
22
|
+
/** The start menu overlay was still displayed after settling. */
|
|
23
|
+
menuVisibleAfterSettle: boolean;
|
|
24
|
+
}
|
|
16
25
|
export interface VerifyObservations {
|
|
17
26
|
/** Console lines, each prefixed with its type, e.g. `error: …`. */
|
|
18
27
|
consoleLines: string[];
|
|
@@ -20,6 +29,9 @@ export interface VerifyObservations {
|
|
|
20
29
|
canvas: CanvasObservation | null;
|
|
21
30
|
/** Byte length of the PNG screenshot taken after settling. */
|
|
22
31
|
screenshotBytes: number;
|
|
32
|
+
/** Only present when the run was asked to perform the start interaction (`bitmagic verify`);
|
|
33
|
+
* `publish`'s smoke check never sets it and classifies exactly as before. */
|
|
34
|
+
start?: StartObservations;
|
|
23
35
|
}
|
|
24
36
|
export interface VerifyResult {
|
|
25
37
|
ok: boolean;
|
package/dist/verify/classify.js
CHANGED
|
@@ -53,6 +53,28 @@ export function classifyVerifyRun(observations) {
|
|
|
53
53
|
for (const error of observations.pageErrors) {
|
|
54
54
|
failures.push(`Uncaught error on the page: ${error}`);
|
|
55
55
|
}
|
|
56
|
+
const { start } = observations;
|
|
57
|
+
if (start) {
|
|
58
|
+
if (!start.gameStartDetected) {
|
|
59
|
+
// Auto-starting genres and custom start UIs never show the button, so its absence alone is
|
|
60
|
+
// fine — what is never fine is a run that ends without gameplay having started at all.
|
|
61
|
+
if (start.playButtonClicked) {
|
|
62
|
+
failures.push('The Play button was clicked but the game never started — the start handler most '
|
|
63
|
+
+ 'likely threw.');
|
|
64
|
+
}
|
|
65
|
+
else if (start.playButtonSeen) {
|
|
66
|
+
failures.push('The Play button appeared but could not be clicked, and the game never started.');
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
failures.push('The game never started — no Play button appeared and no game start was detected.');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (start.menuVisibleAfterSettle) {
|
|
73
|
+
// Started-then-back-to-menu is a mobile-fullscreen bounce the engine allows; it should not
|
|
74
|
+
// happen in a headless desktop run, but the start DID happen, so surface rather than fail.
|
|
75
|
+
warnings.push('The game started but the start menu was still visible when the run ended.');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
56
78
|
const { canvas } = observations;
|
|
57
79
|
if (!canvas) {
|
|
58
80
|
failures.push('No <canvas> was rendered inside #game-container — the game did not start.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/verify/classify.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/verify/classify.ts"],"names":[],"mappings":"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"}
|