@ait-co/devtools 0.1.137 → 0.1.138
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/{attach-orchestrator-Cjdhir2U.js → attach-orchestrator-6kz_ZdWL.js} +5 -3
- package/dist/{attach-orchestrator-Cjdhir2U.js.map → attach-orchestrator-6kz_ZdWL.js.map} +1 -1
- package/dist/{attach-orchestrator-CE0S09YU.js → attach-orchestrator-BEiz3wAx.js} +5 -3
- package/dist/{attach-orchestrator-CMoDuG2A.js.map → attach-orchestrator-BEiz3wAx.js.map} +1 -1
- package/dist/{attach-orchestrator-CMoDuG2A.js → attach-orchestrator-BG8TGJOZ.js} +5 -3
- package/dist/{attach-orchestrator-CE0S09YU.js.map → attach-orchestrator-BG8TGJOZ.js.map} +1 -1
- package/dist/{bundle-BKqyhEK9.d.ts → bundle-C796JIwG.d.ts} +65 -2
- package/dist/bundle-C796JIwG.d.ts.map +1 -0
- package/dist/{capture-ltuV0gZa.d.ts → capture-DsP525OZ.d.ts} +1 -1
- package/dist/{capture-ltuV0gZa.d.ts.map → capture-DsP525OZ.d.ts.map} +1 -1
- package/dist/{cdp-connection-BYE9meXe.d.ts → cdp-connection-rP1WdnH5.d.ts} +1 -1
- package/dist/{cdp-connection-BYE9meXe.d.ts.map → cdp-connection-rP1WdnH5.d.ts.map} +1 -1
- package/dist/{cell-D1y4shoV.js → cell-BMlFh08P.js} +2 -2
- package/dist/cell-BMlFh08P.js.map +1 -0
- package/dist/{cell-DHA578lX.js → cell-Dtgtusog.js} +90 -16
- package/dist/cell-Dtgtusog.js.map +1 -0
- package/dist/{cell-BIdSQb9T.js → cell-KsRXFdDs.js} +122 -16
- package/dist/cell-KsRXFdDs.js.map +1 -0
- package/dist/{debug-server-CBK7dn8O.js → debug-server-6c22qSWa.js} +132 -9
- package/dist/debug-server-6c22qSWa.js.map +1 -0
- package/dist/{debug-server-BQplCzf8.js → debug-server-BQHbaKPT.js} +134 -11
- package/dist/debug-server-BQHbaKPT.js.map +1 -0
- package/dist/{debug-server-D2wpPK3_.js → debug-server-DWat7d-9.js} +82 -6
- package/dist/debug-server-DWat7d-9.js.map +1 -0
- package/dist/devtools-opener-3Drge_RJ.js +75 -0
- package/dist/devtools-opener-3Drge_RJ.js.map +1 -0
- package/dist/devtools-opener-CJpEsXXQ.js +76 -0
- package/dist/devtools-opener-CJpEsXXQ.js.map +1 -0
- package/dist/devtools-opener-CxtryS8c.js +75 -0
- package/dist/devtools-opener-CxtryS8c.js.map +1 -0
- package/dist/in-app/auto.js +59 -12
- package/dist/in-app/auto.js.map +1 -1
- package/dist/in-app/index.d.ts +21 -2
- package/dist/in-app/index.d.ts.map +1 -1
- package/dist/in-app/index.js +60 -13
- package/dist/in-app/index.js.map +1 -1
- package/dist/mcp/cli.js +299 -60
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mock/index.d.ts +64 -1
- package/dist/mock/index.d.ts.map +1 -1
- package/dist/mock/index.js +221 -14
- package/dist/mock/index.js.map +1 -1
- package/dist/panel/index.js +178 -23
- package/dist/panel/index.js.map +1 -1
- package/dist/{pool-CmfvXnwh.d.ts → pool-CrP5CPvU.d.ts} +3 -3
- package/dist/{pool-CmfvXnwh.d.ts.map → pool-CrP5CPvU.d.ts.map} +1 -1
- package/dist/{qr-http-server-ZkT6F7is.js → qr-http-server-D1hpoyBF.js} +1 -1
- package/dist/{qr-http-server-ZkT6F7is.js.map → qr-http-server-D1hpoyBF.js.map} +1 -1
- package/dist/qr-http-server-D4rGz8M7.js +1640 -0
- package/dist/qr-http-server-D4rGz8M7.js.map +1 -0
- package/dist/{qr-http-server-C7q8Gn04.js → qr-http-server-DIjCV7h_.js} +1 -1
- package/dist/{qr-http-server-C7q8Gn04.js.map → qr-http-server-DIjCV7h_.js.map} +1 -1
- package/dist/{relay-factory-DVzpBRxz.js → relay-factory-C1X3G3WY.js} +71 -15
- package/dist/relay-factory-C1X3G3WY.js.map +1 -0
- package/dist/{relay-secret-store-CLEGyHou.js → relay-secret-store-Bmyleu0A.js} +1 -1
- package/dist/{relay-secret-store-CLEGyHou.js.map → relay-secret-store-Bmyleu0A.js.map} +1 -1
- package/dist/{relay-secret-store-DhzAnnj-.js → relay-secret-store-CQenfcSL.js} +2 -2
- package/dist/{relay-secret-store-DhzAnnj-.js.map → relay-secret-store-CQenfcSL.js.map} +1 -1
- package/dist/{relay-secret-store-BcVrWwTq.js → relay-secret-store-DKxs7zwq.js} +1 -1
- package/dist/{relay-secret-store-BcVrWwTq.js.map → relay-secret-store-DKxs7zwq.js.map} +1 -1
- package/dist/{relay-secret-store-DGduVJhs.js → relay-secret-store-WJ8EGkIl.js} +1 -1
- package/dist/{relay-secret-store-DGduVJhs.js.map → relay-secret-store-WJ8EGkIl.js.map} +1 -1
- package/dist/{relay-url-store-CKW8RQzf.js → relay-url-store-BR2XodiO.js} +2 -2
- package/dist/{relay-url-store-CKW8RQzf.js.map → relay-url-store-BR2XodiO.js.map} +1 -1
- package/dist/{relay-url-store-CdA58fgw.js → relay-url-store-CH63fVCm.js} +2 -2
- package/dist/{relay-url-store-B0X8TsGr.js.map → relay-url-store-CH63fVCm.js.map} +1 -1
- package/dist/{relay-url-store-CwKT7i04.js → relay-url-store-DaY1QPes.js} +2 -2
- package/dist/{relay-url-store-CwKT7i04.js.map → relay-url-store-DaY1QPes.js.map} +1 -1
- package/dist/{relay-url-store-B0X8TsGr.js → relay-url-store-xmUuTjXA.js} +2 -2
- package/dist/{relay-url-store-CdA58fgw.js.map → relay-url-store-xmUuTjXA.js.map} +1 -1
- package/dist/{relay-worker-6fy1eaIo.js → relay-worker-BUXI0K0b.js} +8 -4
- package/dist/{relay-worker-6fy1eaIo.js.map → relay-worker-BUXI0K0b.js.map} +1 -1
- package/dist/{relay-worker-DJnZkXza.d.ts → relay-worker-DcyboK43.d.ts} +30 -5
- package/dist/relay-worker-DcyboK43.d.ts.map +1 -0
- package/dist/{runtime-DfHHZms2.d.ts → runtime-BiigOuvb.d.ts} +30 -3
- package/dist/runtime-BiigOuvb.d.ts.map +1 -0
- package/dist/test-runner/bin.js +376 -38
- package/dist/test-runner/bin.js.map +1 -1
- package/dist/test-runner/bundle.d.ts +2 -2
- package/dist/test-runner/bundle.js +66 -13
- package/dist/test-runner/bundle.js.map +1 -1
- package/dist/test-runner/capture.d.ts +1 -1
- package/dist/test-runner/config.d.ts +58 -5
- package/dist/test-runner/config.d.ts.map +1 -1
- package/dist/test-runner/config.js +1 -1
- package/dist/test-runner/method-pace.d.ts +82 -0
- package/dist/test-runner/method-pace.d.ts.map +1 -0
- package/dist/test-runner/method-pace.js +120 -0
- package/dist/test-runner/method-pace.js.map +1 -0
- package/dist/test-runner/pool.d.ts +1 -1
- package/dist/test-runner/pool.js +1 -1
- package/dist/test-runner/relay-factory.d.ts +57 -4
- package/dist/test-runner/relay-factory.d.ts.map +1 -1
- package/dist/test-runner/relay-factory.js +70 -14
- package/dist/test-runner/relay-factory.js.map +1 -1
- package/dist/test-runner/relay-worker.d.ts +1 -1
- package/dist/test-runner/relay-worker.js +1 -1
- package/dist/test-runner/report.d.ts +3 -3
- package/dist/test-runner/rpc.d.ts +2 -2
- package/dist/test-runner/runtime.d.ts +2 -2
- package/dist/test-runner/runtime.js +83 -6
- package/dist/test-runner/runtime.js.map +1 -1
- package/dist/test-runner/task-graph.d.ts +1 -1
- package/dist/throttle-DKKzX1qC.js +59 -0
- package/dist/throttle-DKKzX1qC.js.map +1 -0
- package/dist/totp-Dwft0Kz7.js +3 -0
- package/package.json +1 -1
- package/dist/bundle-BKqyhEK9.d.ts.map +0 -1
- package/dist/cell-BIdSQb9T.js.map +0 -1
- package/dist/cell-D1y4shoV.js.map +0 -1
- package/dist/cell-DHA578lX.js.map +0 -1
- package/dist/debug-server-BQplCzf8.js.map +0 -1
- package/dist/debug-server-CBK7dn8O.js.map +0 -1
- package/dist/debug-server-D2wpPK3_.js.map +0 -1
- package/dist/relay-factory-DVzpBRxz.js.map +0 -1
- package/dist/relay-worker-DJnZkXza.d.ts.map +0 -1
- package/dist/runtime-DfHHZms2.d.ts.map +0 -1
- package/dist/totp-D1pulXLa.js +0 -3
package/dist/test-runner/bin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { m as logInfo } from "../attach-orchestrator-
|
|
2
|
+
import { m as logInfo } from "../attach-orchestrator-BEiz3wAx.js";
|
|
3
3
|
import { r as generateTotp } from "../totp-CZLLKfOC.js";
|
|
4
|
-
import {
|
|
4
|
+
import { r as runPermissionPreflight, t as PERMISSION_PREFLIGHT_TIMEOUT_MS } from "../cell-KsRXFdDs.js";
|
|
5
5
|
import * as path$1 from "node:path";
|
|
6
6
|
import path, { basename, isAbsolute, resolve } from "node:path";
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
@@ -93,12 +93,13 @@ function partitionManualTests(files) {
|
|
|
93
93
|
//#endregion
|
|
94
94
|
//#region src/test-runner/relay-factory.ts
|
|
95
95
|
/**
|
|
96
|
-
* Builds a {@link RelayConnectionFactory} that opens a standalone
|
|
97
|
-
* connection
|
|
96
|
+
* Builds a {@link RelayConnectionFactory} that opens a standalone relay
|
|
97
|
+
* connection for env 3 (intoss-private scheme, default) or env 2 (AITC Sandbox
|
|
98
|
+
* PWA launcher deep-link, when `opts.attachLauncher` is set — devtools#774).
|
|
98
99
|
*
|
|
99
100
|
* `open()` performs the full attach lifecycle and BLOCKS while a human scans
|
|
100
|
-
* the rendered QR with their phone — there is no way around the manual scan
|
|
101
|
-
*
|
|
101
|
+
* the rendered QR with their phone — there is no way around the manual scan.
|
|
102
|
+
* By default the wait is UNBOUNDED (`opts.timeoutMs` omitted): the
|
|
102
103
|
* runner stays up until the user stops it (Ctrl-C/SIGTERM), since QR-scan is
|
|
103
104
|
* a human-paced action with no sound default bound (devtools#735). Passing an
|
|
104
105
|
* explicit `timeoutMs` opts into the old bounded behavior (CI/headless
|
|
@@ -115,14 +116,17 @@ function createRelayConnectionFactory(opts) {
|
|
|
115
116
|
const headless = opts.headless === true;
|
|
116
117
|
let family;
|
|
117
118
|
let qrServer;
|
|
119
|
+
let attachWatcher;
|
|
118
120
|
let phase = "active";
|
|
119
121
|
let manualPrompt = null;
|
|
120
122
|
return {
|
|
121
123
|
async open() {
|
|
122
|
-
const { prepareAttach, renderAndMaybeWait, mintAttachUrl } = await import("../attach-orchestrator-
|
|
123
|
-
const { injectDebugIndicator, injectGlobals } = await import("../cell-
|
|
124
|
-
const { loadRelaySecretReadOnly } = await import("../relay-secret-store-
|
|
125
|
-
const { bootRelayFamily, buildRelayVerifyAuth } = await import("../debug-server-
|
|
124
|
+
const { prepareAttach, renderAndMaybeWait, mintAttachUrl } = await import("../attach-orchestrator-BEiz3wAx.js").then((n) => n.i);
|
|
125
|
+
const { injectDebugIndicator, injectGlobals } = await import("../cell-KsRXFdDs.js").then((n) => n.n);
|
|
126
|
+
const { loadRelaySecretReadOnly } = await import("../relay-secret-store-Bmyleu0A.js");
|
|
127
|
+
const { bootRelayFamily, buildRelayVerifyAuth, startAttachWatcher } = await import("../debug-server-DWat7d-9.js");
|
|
128
|
+
const { buildChiiInspectorUrl } = await import("../devtools-opener-CJpEsXXQ.js");
|
|
129
|
+
const { generateTotp } = await import("../totp-CZLLKfOC.js").then((n) => n.i);
|
|
126
130
|
await loadRelaySecretReadOnly({ projectRoot });
|
|
127
131
|
let resolveTunnelUp;
|
|
128
132
|
const tunnelReady = new Promise((resolve) => {
|
|
@@ -152,29 +156,73 @@ function createRelayConnectionFactory(opts) {
|
|
|
152
156
|
canOpenBrowser: () => !headless
|
|
153
157
|
};
|
|
154
158
|
try {
|
|
155
|
-
const { startQrHttpServer } = await import("../qr-http-server-
|
|
159
|
+
const { startQrHttpServer } = await import("../qr-http-server-DIjCV7h_.js");
|
|
156
160
|
const getDashboardState = () => ({
|
|
157
161
|
tunnel: attachDeps.getTunnelStatus(),
|
|
158
|
-
pages:
|
|
162
|
+
pages: booted.connection.listTargets().map((t) => ({
|
|
163
|
+
id: t.id,
|
|
164
|
+
url: t.url
|
|
165
|
+
})),
|
|
159
166
|
attachUrl: lastAttachParts ? mintAttachUrl(attachDeps, lastAttachParts) : null,
|
|
160
|
-
mode: "relay-dev",
|
|
167
|
+
mode: opts.attachLauncher === true ? "relay-mobile" : "relay-dev",
|
|
161
168
|
phase,
|
|
162
169
|
manualPrompt
|
|
163
170
|
});
|
|
164
|
-
|
|
171
|
+
const getDirectInspectorUrl = () => {
|
|
172
|
+
if (!booted.relayHttpUrl) return {
|
|
173
|
+
ok: false,
|
|
174
|
+
reason: "relayDown"
|
|
175
|
+
};
|
|
176
|
+
const targets = booted.connection.listTargets();
|
|
177
|
+
if (targets.length === 0) return {
|
|
178
|
+
ok: false,
|
|
179
|
+
reason: "noTarget"
|
|
180
|
+
};
|
|
181
|
+
const totpSecret = process.env.AIT_DEBUG_TOTP_SECRET;
|
|
182
|
+
if (!totpSecret) return {
|
|
183
|
+
ok: false,
|
|
184
|
+
reason: "totpUnavailable"
|
|
185
|
+
};
|
|
186
|
+
const url = buildChiiInspectorUrl(booted.relayHttpUrl, targets[0].id, () => generateTotp(totpSecret, Date.now()));
|
|
187
|
+
if (url === null) return {
|
|
188
|
+
ok: false,
|
|
189
|
+
reason: "totpUnavailable"
|
|
190
|
+
};
|
|
191
|
+
return {
|
|
192
|
+
ok: true,
|
|
193
|
+
url
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
qrServer = await startQrHttpServer(getDashboardState, {
|
|
197
|
+
dashboardPort: opts.dashboardPort,
|
|
198
|
+
getDirectInspectorUrl
|
|
199
|
+
});
|
|
165
200
|
attachDeps.qrHttpServer = qrServer;
|
|
166
201
|
attachDeps.onAttachUrlBuilt = (parts) => {
|
|
167
202
|
lastAttachParts = parts;
|
|
168
203
|
qrServer?.notifyStateChange();
|
|
169
204
|
};
|
|
205
|
+
attachWatcher = startAttachWatcher(booted.connection, void 0, 1e3, () => qrServer?.notifyStateChange(), () => qrServer?.notifyStateChange());
|
|
170
206
|
process.stderr.write(`devtools-test: QR dashboard: http://127.0.0.1:${qrServer.port}/\n`);
|
|
171
207
|
} catch {}
|
|
172
208
|
const TUNNEL_BOOT_TIMEOUT_MS = 15e3;
|
|
173
209
|
await Promise.race([tunnelReady, new Promise((resolve) => setTimeout(resolve, TUNNEL_BOOT_TIMEOUT_MS))]);
|
|
174
|
-
|
|
210
|
+
let prep;
|
|
211
|
+
if (opts.attachLauncher === true) {
|
|
212
|
+
const priorTunnelBaseUrl = process.env.AIT_TUNNEL_BASE_URL;
|
|
213
|
+
process.env.AIT_TUNNEL_BASE_URL = opts.appUrl ?? "";
|
|
214
|
+
try {
|
|
215
|
+
prep = await prepareAttach(attachDeps, "relay-mobile", {}, booted.connection);
|
|
216
|
+
} finally {
|
|
217
|
+
if (priorTunnelBaseUrl === void 0) delete process.env.AIT_TUNNEL_BASE_URL;
|
|
218
|
+
else process.env.AIT_TUNNEL_BASE_URL = priorTunnelBaseUrl;
|
|
219
|
+
}
|
|
220
|
+
} else prep = await prepareAttach(attachDeps, "relay-dev", { scheme_url: opts.schemeUrl }, booted.connection);
|
|
175
221
|
if (!prep.ok) {
|
|
176
222
|
booted.stop();
|
|
177
223
|
family = void 0;
|
|
224
|
+
attachWatcher?.stop();
|
|
225
|
+
attachWatcher = void 0;
|
|
178
226
|
await qrServer?.close();
|
|
179
227
|
qrServer = void 0;
|
|
180
228
|
throw new Error("createRelayConnectionFactory: attach preparation failed — check the scheme_url and that the relay tunnel is up");
|
|
@@ -183,6 +231,8 @@ function createRelayConnectionFactory(opts) {
|
|
|
183
231
|
if (waitResult.isError) {
|
|
184
232
|
booted.stop();
|
|
185
233
|
family = void 0;
|
|
234
|
+
attachWatcher?.stop();
|
|
235
|
+
attachWatcher = void 0;
|
|
186
236
|
await qrServer?.close();
|
|
187
237
|
qrServer = void 0;
|
|
188
238
|
const timeoutSec = Math.round(timeoutMs / 1e3);
|
|
@@ -204,6 +254,8 @@ function createRelayConnectionFactory(opts) {
|
|
|
204
254
|
if (lastEnableError !== void 0) {
|
|
205
255
|
booted.stop();
|
|
206
256
|
family = void 0;
|
|
257
|
+
attachWatcher?.stop();
|
|
258
|
+
attachWatcher = void 0;
|
|
207
259
|
await qrServer?.close();
|
|
208
260
|
qrServer = void 0;
|
|
209
261
|
throw new Error(`createRelayConnectionFactory: page did not become ready after ${PAGE_READY_RETRIES} attempts (${Math.round(PAGE_READY_RETRIES * PAGE_READY_RETRY_DELAY_MS / 1e3)}s) — the mini-app page may have disconnected before enableDomains() could open the CDP websocket`);
|
|
@@ -211,6 +263,8 @@ function createRelayConnectionFactory(opts) {
|
|
|
211
263
|
await injectDebugIndicator(booted.connection);
|
|
212
264
|
if (opts.cell !== void 0) await injectGlobals(booted.connection, { __AIT_CELL__: opts.cell });
|
|
213
265
|
if (opts.stubBlocking === true) await injectGlobals(booted.connection, { __AIT_STUB_BLOCKING__: true });
|
|
266
|
+
if (opts.paceMs !== void 0 && opts.paceMs > 0) await injectGlobals(booted.connection, { __AIT_PACE_MS__: opts.paceMs });
|
|
267
|
+
if (opts.paceMethodMs !== void 0 && opts.paceMethodMs > 0) await injectGlobals(booted.connection, { __AIT_PACE_METHOD_MS__: opts.paceMethodMs });
|
|
214
268
|
return booted.connection;
|
|
215
269
|
},
|
|
216
270
|
onSessionPhase(next) {
|
|
@@ -227,11 +281,13 @@ function createRelayConnectionFactory(opts) {
|
|
|
227
281
|
qrServer?.notifyStateChange();
|
|
228
282
|
}
|
|
229
283
|
try {
|
|
230
|
-
const { injectDebugIndicator } = await import("../cell-
|
|
284
|
+
const { injectDebugIndicator } = await import("../cell-KsRXFdDs.js").then((n) => n.n);
|
|
231
285
|
await injectDebugIndicator(connection, { state: "disconnected" });
|
|
232
286
|
} catch {}
|
|
233
287
|
family?.stop();
|
|
234
288
|
family = void 0;
|
|
289
|
+
attachWatcher?.stop();
|
|
290
|
+
attachWatcher = void 0;
|
|
235
291
|
await qrServer?.close();
|
|
236
292
|
qrServer = void 0;
|
|
237
293
|
}
|
|
@@ -917,7 +973,63 @@ const SDK_IMPORT_FILTER = new RegExp(`^${SDK_PACKAGE.replace(/[.*+?^${}()|[\]\\]
|
|
|
917
973
|
* `--stub-blocking` flag). Default off => zero behavior change: every existing
|
|
918
974
|
* (non-stub) test run resolves the exact same `window.__sdk` reference it did
|
|
919
975
|
* before this plugin learned about stubbing.
|
|
976
|
+
*
|
|
977
|
+
* Per-method pacing (devtools#769): BEFORE the stub wrap, `window.__sdk` is
|
|
978
|
+
* run through `wrapWithMethodPacing`, which enforces a minimum interval
|
|
979
|
+
* between calls to the SAME named SDK function (`getPaceMethodMs()` reads the
|
|
980
|
+
* `__AIT_PACE_METHOD_MS__` page global — see `method-pace.ts`). Composition
|
|
981
|
+
* order is pacing INSIDE the stub (`wrapSdkWithStub(wrapWithMethodPacing(sdk,
|
|
982
|
+
* gap), enabled)`): a stubbed name resolves instantly from
|
|
983
|
+
* `bridge-stub.ts`'s fixtures without ever reaching the paced wrapper, and
|
|
984
|
+
* only calls that fall through to the real bridge — the ones that can
|
|
985
|
+
* actually trigger `APP_BRIDGE_THROTTLED` — pay the pacing cost. `gapMs <= 0`
|
|
986
|
+
* (default, no `--pace-method` / non-2.x cell) is a no-op fast path — see
|
|
987
|
+
* `method-pace.ts#wrapWithMethodPacing`'s doc for the full contract.
|
|
920
988
|
*/
|
|
989
|
+
/**
|
|
990
|
+
* Builds the virtual CommonJS-style module contents the `sdk-redirect`
|
|
991
|
+
* esbuild plugin loads for every `@apps-in-toss/web-framework` import.
|
|
992
|
+
*
|
|
993
|
+
* Pulled out to a pure, exported function (rather than inlined at the
|
|
994
|
+
* `build.onLoad` call site) so unit tests can assert the exact composition
|
|
995
|
+
* order (pacing wraps the raw SDK; the stub wraps the paced result) as a
|
|
996
|
+
* string-level contract, independent of esbuild — esbuild cannot run inside
|
|
997
|
+
* this repo's jsdom vitest environment (see `bundleTestFile`'s module doc's
|
|
998
|
+
* lazy-import note), so a real bundling round-trip is out of unit-test reach
|
|
999
|
+
* here; `wrapSdkWithStub`/`wrapWithMethodPacing`'s own composed-call behavior
|
|
1000
|
+
* is covered directly (see `bundle.test.ts`).
|
|
1001
|
+
*
|
|
1002
|
+
* Generates a virtual CommonJS-style module so that esbuild does NOT perform
|
|
1003
|
+
* strict named-export matching. When `format:'iife'` bundles a CJS module, it
|
|
1004
|
+
* wraps it with its own `__toCommonJS` helper and satisfies named imports via
|
|
1005
|
+
* property access on the `module.exports` object — which is our Proxy. This
|
|
1006
|
+
* means `import { getPlatformOS } from '...'` becomes
|
|
1007
|
+
* `__proxy.getPlatformOS` at runtime, which correctly reads from
|
|
1008
|
+
* `window.__sdk`.
|
|
1009
|
+
*
|
|
1010
|
+
* The bridge-stub and method-pace imports are STATIC imports of the real
|
|
1011
|
+
* `bridge-stub.ts`/`method-pace.ts` modules (not inlined JS) so esbuild
|
|
1012
|
+
* bundles each once and the fixture registry / pacing registry have a single
|
|
1013
|
+
* source of truth shared with their own unit tests.
|
|
1014
|
+
*
|
|
1015
|
+
* Exported for unit testing.
|
|
1016
|
+
*/
|
|
1017
|
+
function buildSdkRedirectModuleContents() {
|
|
1018
|
+
return `
|
|
1019
|
+
import { wrapSdkWithStub, isStubBlockingEnabled } from ${JSON.stringify(getBridgeStubPath())};
|
|
1020
|
+
import { wrapWithMethodPacing, getPaceMethodMs } from ${JSON.stringify(getMethodPacePath())};
|
|
1021
|
+
var __rawSdk = (typeof window !== 'undefined' && window.__sdk)
|
|
1022
|
+
? window.__sdk
|
|
1023
|
+
: new Proxy({}, {
|
|
1024
|
+
get: function(_t, p) {
|
|
1025
|
+
throw new Error('window.__sdk is not installed — run in a dog-food build. Missing: ' + String(p));
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
var __pacedSdk = wrapWithMethodPacing(__rawSdk, getPaceMethodMs());
|
|
1029
|
+
var __proxy = wrapSdkWithStub(__pacedSdk, isStubBlockingEnabled());
|
|
1030
|
+
module.exports = __proxy;
|
|
1031
|
+
`;
|
|
1032
|
+
}
|
|
921
1033
|
function sdkRedirectPlugin() {
|
|
922
1034
|
return {
|
|
923
1035
|
name: "sdk-redirect",
|
|
@@ -930,18 +1042,7 @@ function sdkRedirectPlugin() {
|
|
|
930
1042
|
filter: /.*/,
|
|
931
1043
|
namespace: "sdk-redirect"
|
|
932
1044
|
}, () => ({
|
|
933
|
-
contents:
|
|
934
|
-
import { wrapSdkWithStub, isStubBlockingEnabled } from ${JSON.stringify(getBridgeStubPath())};
|
|
935
|
-
var __rawSdk = (typeof window !== 'undefined' && window.__sdk)
|
|
936
|
-
? window.__sdk
|
|
937
|
-
: new Proxy({}, {
|
|
938
|
-
get: function(_t, p) {
|
|
939
|
-
throw new Error('window.__sdk is not installed — run in a dog-food build. Missing: ' + String(p));
|
|
940
|
-
}
|
|
941
|
-
});
|
|
942
|
-
var __proxy = wrapSdkWithStub(__rawSdk, isStubBlockingEnabled());
|
|
943
|
-
module.exports = __proxy;
|
|
944
|
-
`,
|
|
1045
|
+
contents: buildSdkRedirectModuleContents(),
|
|
945
1046
|
loader: "js",
|
|
946
1047
|
resolveDir: process.cwd()
|
|
947
1048
|
}));
|
|
@@ -1142,6 +1243,14 @@ function getBridgeStubPath() {
|
|
|
1142
1243
|
return getPageSideModulePath("bridge-stub");
|
|
1143
1244
|
}
|
|
1144
1245
|
/**
|
|
1246
|
+
* Absolute path to the per-method pacing wrapper (devtools#769) —
|
|
1247
|
+
* `wrapWithMethodPacing`/`getPaceMethodMs`, statically imported by the
|
|
1248
|
+
* `sdk-redirect` virtual module.
|
|
1249
|
+
*/
|
|
1250
|
+
function getMethodPacePath() {
|
|
1251
|
+
return getPageSideModulePath("method-pace");
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1145
1254
|
* Bundles `absPath` into a single IIFE string suitable for `Runtime.evaluate`.
|
|
1146
1255
|
*
|
|
1147
1256
|
* The IIFE installs `window.__testBundle` (or the custom `globalName`) with:
|
|
@@ -1415,13 +1524,17 @@ async function runTestFilesOverRelay(connection, files, opts) {
|
|
|
1415
1524
|
let manualIndex = 0;
|
|
1416
1525
|
const stubBlockingFiles = opts?.stubBlockingFiles;
|
|
1417
1526
|
let preflightPermissions;
|
|
1418
|
-
if (files.length > 0) preflightPermissions = await runPermissionPreflight(connection);
|
|
1527
|
+
if (files.length > 0) preflightPermissions = await runPermissionPreflight(connection, PERMISSION_PREFLIGHT_TIMEOUT_MS, opts?.preflightSdkLine !== "3.x");
|
|
1528
|
+
const paceMs = opts?.paceMs;
|
|
1529
|
+
let ranFirstFile = false;
|
|
1419
1530
|
try {
|
|
1420
1531
|
for (const file of files) {
|
|
1421
1532
|
if (pendingReconnectCheck) {
|
|
1422
1533
|
await attemptReconnect("next-file");
|
|
1423
1534
|
pendingReconnectCheck = false;
|
|
1424
1535
|
}
|
|
1536
|
+
if (paceMs !== void 0 && paceMs > 0 && ranFirstFile) await new Promise((resolve) => setTimeout(resolve, paceMs));
|
|
1537
|
+
ranFirstFile = true;
|
|
1425
1538
|
const isStubbed = stubBlockingFiles?.has(file) === true;
|
|
1426
1539
|
const isManual = !isStubbed && manualFiles?.has(file) === true;
|
|
1427
1540
|
if (isManual) {
|
|
@@ -1835,7 +1948,21 @@ USAGE
|
|
|
1835
1948
|
|
|
1836
1949
|
OPTIONS
|
|
1837
1950
|
--scheme-url <url> intoss-private:// URL from \`ait deploy --scheme-only\`
|
|
1838
|
-
(required for standalone relay attach / env3)
|
|
1951
|
+
(required for standalone relay attach / env3). Unused
|
|
1952
|
+
and ignored when --attach-launcher is set.
|
|
1953
|
+
--attach-launcher Attach over the env-2 AITC Sandbox PWA launcher
|
|
1954
|
+
(real-device WebKit) instead of the env-3 intoss
|
|
1955
|
+
scheme deep-link. The relay/QR/dashboard/attach-wait
|
|
1956
|
+
are identical; only the QR is a launcher deep-link.
|
|
1957
|
+
Requires --app-url. The SDK still hits the mock in
|
|
1958
|
+
env 2, so this measures the mock + standard Web API
|
|
1959
|
+
layer's engine-attributable behavior (env1↔env2
|
|
1960
|
+
equivalence), NOT native-bridge fidelity (that is env3).
|
|
1961
|
+
--app-url <url> The consumer dev server's HTTP tunnel URL (e.g. the
|
|
1962
|
+
*.trycloudflare.com URL from \`pnpm dev:phone:cdp\`)
|
|
1963
|
+
that the launcher PWA frames. REQUIRED with
|
|
1964
|
+
--attach-launcher; ignored otherwise. SECRET: a tunnel
|
|
1965
|
+
host — never printed; it rides only inside the QR.
|
|
1839
1966
|
--timeout <ms> Per-file evaluate timeout in ms (default: 60000).
|
|
1840
1967
|
Controls how long a single test file is allowed to run
|
|
1841
1968
|
before it is considered hung. Does NOT affect how long
|
|
@@ -1847,7 +1974,10 @@ OPTIONS
|
|
|
1847
1974
|
Pass a value to bound the wait for CI/headless runs.
|
|
1848
1975
|
--cell-sdk-line <line> SDK line to inject as __AIT_CELL__.sdkLine (2.x|3.x)
|
|
1849
1976
|
--cell-platform <plat> Platform to inject as __AIT_CELL__.platform
|
|
1850
|
-
(mock|ios|android, default: AIT_CELL_PLATFORM
|
|
1977
|
+
(mock|ios|android|ios-pwa, default: AIT_CELL_PLATFORM
|
|
1978
|
+
env). Use ios-pwa for env-2 (--attach-launcher) runs
|
|
1979
|
+
so env1(mock@desktop) and env2(mock@WebKit) captures
|
|
1980
|
+
stay distinguishable. An unknown value is rejected.
|
|
1851
1981
|
--report-dir <dir> Persist a runner-agnostic report + captures to <dir>
|
|
1852
1982
|
(report: <sdkLine>.<platform>.json; captures:
|
|
1853
1983
|
<dir>/.ait-capture/<category>.<sdkLine>.<platform>.json).
|
|
@@ -1862,6 +1992,28 @@ OPTIONS
|
|
|
1862
1992
|
--headless Disable browser auto-open (text QR only)
|
|
1863
1993
|
--project-root <dir> Project root for .ait_relay secret lookup
|
|
1864
1994
|
(default: current working directory)
|
|
1995
|
+
--pace <ms> Minimum delay in ms between test-to-test AND
|
|
1996
|
+
file-to-file bridge calls (default: 0, i.e. no
|
|
1997
|
+
added delay — today's behavior byte-for-byte).
|
|
1998
|
+
Falls back to the AIT_PACE env var when omitted
|
|
1999
|
+
(--pace takes precedence over the env var when both
|
|
2000
|
+
are given). Use on a 2.x cell scan when the native
|
|
2001
|
+
per-method bridge rate limit (APP_BRIDGE_THROTTLED,
|
|
2002
|
+
devtools#767) is rejecting rapid same-method calls —
|
|
2003
|
+
3.x cells are unaffected by that limiter and do not
|
|
2004
|
+
need this flag.
|
|
2005
|
+
--pace-method <ms> Minimum delay in ms BETWEEN calls to the SAME named
|
|
2006
|
+
SDK function — paces a same-method burst WITHIN a
|
|
2007
|
+
single test body (e.g. a clipboard happy-path loop
|
|
2008
|
+
calling setClipboardText/getClipboardText 8 times
|
|
2009
|
+
back to back), which --pace's test/file spacing
|
|
2010
|
+
cannot reach (devtools#769). Falls back to the
|
|
2011
|
+
AIT_PACE_METHOD env var when omitted (--pace-method
|
|
2012
|
+
takes precedence over the env var when both are
|
|
2013
|
+
given). Default: 250ms when --cell-sdk-line is 2.x
|
|
2014
|
+
(unset --cell-sdk-line also defaults to 2.x — see
|
|
2015
|
+
--cell-sdk-line), 0 (no added delay) otherwise. Pass
|
|
2016
|
+
--pace-method 0 to opt out even on a 2.x cell.
|
|
1865
2017
|
--manual-blocking Run manual-tagged test files (*.manual.ait.test.ts)
|
|
1866
2018
|
LAST, after all regular files, with a human present.
|
|
1867
2019
|
Before each manual file, the QR dashboard is pushed
|
|
@@ -1914,7 +2066,7 @@ DESCRIPTION
|
|
|
1914
2066
|
The test files run against the live relay connection started by this process;
|
|
1915
2067
|
no separate MCP daemon is required.
|
|
1916
2068
|
|
|
1917
|
-
EXAMPLE
|
|
2069
|
+
EXAMPLE (env 3 — intoss-private scheme)
|
|
1918
2070
|
devtools-test 'src/**/*.ait.test.ts' \\
|
|
1919
2071
|
--scheme-url "intoss-private://..." \\
|
|
1920
2072
|
--cell-sdk-line 3.x \\
|
|
@@ -1922,6 +2074,14 @@ EXAMPLE
|
|
|
1922
2074
|
--report-dir .ait-report \\
|
|
1923
2075
|
--timeout 60000
|
|
1924
2076
|
|
|
2077
|
+
EXAMPLE (env 2 — AITC Sandbox PWA launcher)
|
|
2078
|
+
devtools-test 'src/**/*.ait.test.ts' \\
|
|
2079
|
+
--attach-launcher \\
|
|
2080
|
+
--app-url "https://<subdomain>.trycloudflare.com" \\
|
|
2081
|
+
--cell-sdk-line 3.x \\
|
|
2082
|
+
--cell-platform ios-pwa \\
|
|
2083
|
+
--report-dir .ait-report
|
|
2084
|
+
|
|
1925
2085
|
`.trimStart();
|
|
1926
2086
|
/**
|
|
1927
2087
|
* Grace period (ms) for the exit backstop armed around Step 6 teardown
|
|
@@ -1970,6 +2130,144 @@ function resolveDashboardPort(raw) {
|
|
|
1970
2130
|
return port;
|
|
1971
2131
|
}
|
|
1972
2132
|
/**
|
|
2133
|
+
* Rewrites a lone `--pace <value>` or `--pace-method <value>` pair from space
|
|
2134
|
+
* syntax to `=` syntax (`--pace=<value>` / `--pace-method=<value>`) when
|
|
2135
|
+
* `<value>` starts with `-` (devtools#768 review; extended to `--pace-method`
|
|
2136
|
+
* in devtools#769).
|
|
2137
|
+
*
|
|
2138
|
+
* Node's `util.parseArgs` treats a `type: 'string'` option's value as
|
|
2139
|
+
* "ambiguous" and throws its OWN parser error whenever that value starts with
|
|
2140
|
+
* a dash and was passed via the space form (`--pace -1`) — it never reaches
|
|
2141
|
+
* this module's `resolvePace`/`resolvePaceMethod`, so the friendly
|
|
2142
|
+
* `'--pace must be a non-negative integer'` message (and its unit-tested path)
|
|
2143
|
+
* was unreachable from the real CLI entry point for the single most natural
|
|
2144
|
+
* way a user would try a negative value, even though every other flag in
|
|
2145
|
+
* {@link USAGE} is documented in space syntax.
|
|
2146
|
+
*
|
|
2147
|
+
* Scoped narrowly to `--pace`/`--pace=...` and `--pace-method`/
|
|
2148
|
+
* `--pace-method=...` tokens only — every other flag is untouched, and
|
|
2149
|
+
* positionals/other options keep flowing through `parseArgs` unchanged. Only
|
|
2150
|
+
* rewrites the space form; `--pace=-1`/`--pace-method=-1` already parse fine
|
|
2151
|
+
* and are passed through untouched.
|
|
2152
|
+
*
|
|
2153
|
+
* Exported for unit testing.
|
|
2154
|
+
*/
|
|
2155
|
+
function normalizePaceArgv(argv) {
|
|
2156
|
+
const out = [];
|
|
2157
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2158
|
+
const arg = argv[i];
|
|
2159
|
+
const next = argv[i + 1];
|
|
2160
|
+
if ((arg === "--pace" || arg === "--pace-method") && next !== void 0 && next.startsWith("-") && next !== "--") {
|
|
2161
|
+
out.push(`${arg}=${next}`);
|
|
2162
|
+
i++;
|
|
2163
|
+
continue;
|
|
2164
|
+
}
|
|
2165
|
+
out.push(arg);
|
|
2166
|
+
}
|
|
2167
|
+
return out;
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* Parses `--pace` (falling back to the `AIT_PACE` env var when the flag is
|
|
2171
|
+
* omitted) into a validated millisecond delay (devtools#767).
|
|
2172
|
+
*
|
|
2173
|
+
* Opt-in, zero-diff-when-absent: BOTH omitted resolves to `0` — no pacing —
|
|
2174
|
+
* which is byte-for-byte today's behavior (`runtime.ts` treats `0`/absent
|
|
2175
|
+
* `__AIT_PACE_MS__` identically, and `relay-worker.ts`'s file-to-file gap is
|
|
2176
|
+
* skipped entirely when the resolved value is 0). `--pace` takes precedence
|
|
2177
|
+
* over `AIT_PACE` when both are present, mirroring `--cell-platform` /
|
|
2178
|
+
* `AIT_CELL_PLATFORM`'s existing flag-over-env precedent.
|
|
2179
|
+
*
|
|
2180
|
+
* Returns an error string on invalid input (non-integer or negative), or the
|
|
2181
|
+
* resolved delay (`>= 0`) on success. Exported for unit testing.
|
|
2182
|
+
*/
|
|
2183
|
+
function resolvePace(rawFlag, rawEnv) {
|
|
2184
|
+
const raw = rawFlag ?? rawEnv;
|
|
2185
|
+
if (raw === void 0) return 0;
|
|
2186
|
+
const ms = parseInt(raw, 10);
|
|
2187
|
+
if (Number.isNaN(ms) || ms < 0) return "--pace must be a non-negative integer";
|
|
2188
|
+
return ms;
|
|
2189
|
+
}
|
|
2190
|
+
/**
|
|
2191
|
+
* Parses `--pace-method` (falling back to the `AIT_PACE_METHOD` env var when
|
|
2192
|
+
* the flag is omitted) into a validated millisecond per-method minimum
|
|
2193
|
+
* interval (devtools#769).
|
|
2194
|
+
*
|
|
2195
|
+
* Precedence, highest first: `--pace-method` flag > `AIT_PACE_METHOD` env >
|
|
2196
|
+
* sdkLine-aware default (`{@link DEFAULT_PACE_METHOD_MS_2X}` for a 2.x cell,
|
|
2197
|
+
* `0` otherwise) — an EXPLICIT flag or env value always wins, including
|
|
2198
|
+
* `--pace-method 0` to opt out on a 2.x cell. Mirrors `resolvePace`'s
|
|
2199
|
+
* flag-over-env precedent, with the sdkLine default spliced in only when
|
|
2200
|
+
* BOTH are absent.
|
|
2201
|
+
*
|
|
2202
|
+
* Returns an error string on invalid input (non-integer or negative), or the
|
|
2203
|
+
* resolved delay (`>= 0`) on success. Exported for unit testing.
|
|
2204
|
+
*
|
|
2205
|
+
* @param rawFlag - The raw `--pace-method` string value, if passed.
|
|
2206
|
+
* @param rawEnv - The raw `AIT_PACE_METHOD` env value, if set.
|
|
2207
|
+
* @param resolvedCellSdkLine - The effective `cell.sdkLine` value (already
|
|
2208
|
+
* defaulted to '2.x' by the caller when `--cell-sdk-line` was omitted — see
|
|
2209
|
+
* `main()`'s `cell` construction) — used ONLY to pick the sdkLine-aware
|
|
2210
|
+
* default when both `rawFlag` and `rawEnv` are absent.
|
|
2211
|
+
*/
|
|
2212
|
+
function resolvePaceMethod(rawFlag, rawEnv, resolvedCellSdkLine) {
|
|
2213
|
+
const raw = rawFlag ?? rawEnv;
|
|
2214
|
+
if (raw === void 0) return resolvedCellSdkLine === "2.x" ? 250 : 0;
|
|
2215
|
+
const ms = parseInt(raw, 10);
|
|
2216
|
+
if (Number.isNaN(ms) || ms < 0) return "--pace-method must be a non-negative integer";
|
|
2217
|
+
return ms;
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* The set of `__AIT_CELL__.platform` values the runner accepts (devtools#774).
|
|
2221
|
+
*
|
|
2222
|
+
* `mock`/`ios`/`android` mirror sdk-example `aitCapture`'s existing `Platform`
|
|
2223
|
+
* union; `ios-pwa` is the new env-2 axis value (issue #774) — env 1 is
|
|
2224
|
+
* mock@desktop-Chromium and env 2 is mock@real-device-WebKit, so the two share
|
|
2225
|
+
* `sdkLine`/`ios` but must be DISTINGUISHABLE in the capture cell to diff
|
|
2226
|
+
* engine-attributable differences. `ios-pwa` names "mock SDK, real-device iOS
|
|
2227
|
+
* WebKit via the launcher PWA" — it is NOT `ios` (which is env 3's native
|
|
2228
|
+
* bridge). The consuming `aitCapture` type extension is a separate sdk-example
|
|
2229
|
+
* PR (out of scope here); the runner only needs to inject + validate the label.
|
|
2230
|
+
*
|
|
2231
|
+
* Kept as a validated allowlist (rather than a free string) purely as a
|
|
2232
|
+
* typo guard: `platform` flows into report/capture FILENAMES
|
|
2233
|
+
* (`<sdkLine>.<platform>.json`), so a silent typo would fork the artifact set
|
|
2234
|
+
* and break offline diffing. This is the first validation on `--cell-platform`
|
|
2235
|
+
* — before #774 it was an unvalidated free string defaulting to `mock`.
|
|
2236
|
+
*/
|
|
2237
|
+
const ALLOWED_CELL_PLATFORMS = [
|
|
2238
|
+
"mock",
|
|
2239
|
+
"ios",
|
|
2240
|
+
"android",
|
|
2241
|
+
"ios-pwa"
|
|
2242
|
+
];
|
|
2243
|
+
/**
|
|
2244
|
+
* Validates a resolved `--cell-platform` value against {@link ALLOWED_CELL_PLATFORMS}.
|
|
2245
|
+
*
|
|
2246
|
+
* `undefined` (flag + `AIT_CELL_PLATFORM` env both absent) passes through as
|
|
2247
|
+
* `{ ok: true, value: undefined }` — the caller applies its own `'mock'`
|
|
2248
|
+
* default, so an omitted platform is never an error. A present-but-unknown
|
|
2249
|
+
* value returns `{ ok: false, error }` naming the allowed set.
|
|
2250
|
+
*
|
|
2251
|
+
* Uses a tagged result (not the `string`-is-error convention of
|
|
2252
|
+
* `resolveDashboardPort`/`resolvePace`) because BOTH the success value and the
|
|
2253
|
+
* error message are strings here — a bare `string` return would be ambiguous.
|
|
2254
|
+
* Exported for unit testing.
|
|
2255
|
+
*/
|
|
2256
|
+
function resolveCellPlatform(raw) {
|
|
2257
|
+
if (raw === void 0) return {
|
|
2258
|
+
ok: true,
|
|
2259
|
+
value: void 0
|
|
2260
|
+
};
|
|
2261
|
+
if (ALLOWED_CELL_PLATFORMS.includes(raw)) return {
|
|
2262
|
+
ok: true,
|
|
2263
|
+
value: raw
|
|
2264
|
+
};
|
|
2265
|
+
return {
|
|
2266
|
+
ok: false,
|
|
2267
|
+
error: `--cell-platform must be one of ${ALLOWED_CELL_PLATFORMS.join("|")} (got "${raw}")`
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
/**
|
|
1973
2271
|
* Renders per-file result lines and the aggregate totals line to a string.
|
|
1974
2272
|
*
|
|
1975
2273
|
* Each file gets one line:
|
|
@@ -2038,8 +2336,8 @@ function shouldSuppressQr(noQrFlag) {
|
|
|
2038
2336
|
*
|
|
2039
2337
|
* 1. Parse args: globs, --timeout (per-file evaluate), --attach-timeout (QR
|
|
2040
2338
|
* scan wait), --cell-sdk-line, --cell-platform, --scheme-url (required for
|
|
2041
|
-
* env3)
|
|
2042
|
-
* --project-root.
|
|
2339
|
+
* env3) OR --attach-launcher + --app-url (env2, devtools#774), --report-dir,
|
|
2340
|
+
* --dashboard-port, --no-qr-stdout, --headless, --project-root.
|
|
2043
2341
|
* 2. Discover test files; exit 1 if none.
|
|
2044
2342
|
* 3. factory.open() — boot relay → render QR (suppressed on non-interactive
|
|
2045
2343
|
* stdout) → wait for phone (up to attachTimeoutMs) → inject cell →
|
|
@@ -2060,7 +2358,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2060
2358
|
let parsed;
|
|
2061
2359
|
try {
|
|
2062
2360
|
parsed = parseArgs({
|
|
2063
|
-
args: argv,
|
|
2361
|
+
args: normalizePaceArgv(argv),
|
|
2064
2362
|
options: {
|
|
2065
2363
|
help: {
|
|
2066
2364
|
type: "boolean",
|
|
@@ -2069,6 +2367,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2069
2367
|
timeout: { type: "string" },
|
|
2070
2368
|
"attach-timeout": { type: "string" },
|
|
2071
2369
|
"scheme-url": { type: "string" },
|
|
2370
|
+
"attach-launcher": { type: "boolean" },
|
|
2371
|
+
"app-url": { type: "string" },
|
|
2072
2372
|
"cell-sdk-line": { type: "string" },
|
|
2073
2373
|
"cell-platform": { type: "string" },
|
|
2074
2374
|
"report-dir": { type: "string" },
|
|
@@ -2076,6 +2376,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2076
2376
|
"no-qr-stdout": { type: "boolean" },
|
|
2077
2377
|
headless: { type: "boolean" },
|
|
2078
2378
|
"project-root": { type: "string" },
|
|
2379
|
+
pace: { type: "string" },
|
|
2380
|
+
"pace-method": { type: "string" },
|
|
2079
2381
|
"manual-blocking": { type: "boolean" },
|
|
2080
2382
|
"stub-blocking": { type: "boolean" }
|
|
2081
2383
|
},
|
|
@@ -2098,9 +2400,17 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2098
2400
|
return;
|
|
2099
2401
|
}
|
|
2100
2402
|
const { evaluateTimeoutMs, attachTimeoutMs } = timeouts;
|
|
2403
|
+
const attachLauncher = vals["attach-launcher"] === true;
|
|
2101
2404
|
const schemeUrl = typeof vals["scheme-url"] === "string" ? vals["scheme-url"] : "";
|
|
2102
|
-
|
|
2103
|
-
|
|
2405
|
+
const appUrl = typeof vals["app-url"] === "string" ? vals["app-url"] : "";
|
|
2406
|
+
if (attachLauncher) {
|
|
2407
|
+
if (appUrl === "") {
|
|
2408
|
+
process.stderr.write("devtools-test: --app-url is required with --attach-launcher (env 2).\n Pass the consumer dev server's tunnel URL (e.g. the HTTP *.trycloudflare.com\n URL printed by `pnpm dev:phone:cdp`). --scheme-url is not used in this mode.\n");
|
|
2409
|
+
process.exitCode = 1;
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2412
|
+
} else if (schemeUrl === "") {
|
|
2413
|
+
process.stderr.write("devtools-test: --scheme-url is required for standalone relay attach.\n Pass the intoss-private:// URL from `ait deploy --scheme-only`.\n (For env 2 / AITC Sandbox PWA, use --attach-launcher --app-url <tunnel-url> instead.)\n");
|
|
2104
2414
|
process.exitCode = 1;
|
|
2105
2415
|
return;
|
|
2106
2416
|
}
|
|
@@ -2113,16 +2423,34 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2113
2423
|
process.exitCode = 1;
|
|
2114
2424
|
return;
|
|
2115
2425
|
}
|
|
2426
|
+
const paceMs = resolvePace(typeof vals.pace === "string" ? vals.pace : void 0, process.env.AIT_PACE);
|
|
2427
|
+
if (typeof paceMs === "string") {
|
|
2428
|
+
process.stderr.write(`devtools-test: ${paceMs}\n`);
|
|
2429
|
+
process.exitCode = 1;
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2116
2432
|
const suppressQr = shouldSuppressQr(vals["no-qr-stdout"] === true);
|
|
2117
2433
|
const stubBlocking = vals["stub-blocking"] === true;
|
|
2118
2434
|
const manualBlocking = vals["manual-blocking"] === true || stubBlocking;
|
|
2119
2435
|
const cellSdkLine = typeof vals["cell-sdk-line"] === "string" ? vals["cell-sdk-line"] : void 0;
|
|
2120
|
-
const
|
|
2436
|
+
const cellPlatformResult = resolveCellPlatform(typeof vals["cell-platform"] === "string" ? vals["cell-platform"] : process.env.AIT_CELL_PLATFORM);
|
|
2437
|
+
if (!cellPlatformResult.ok) {
|
|
2438
|
+
process.stderr.write(`devtools-test: ${cellPlatformResult.error}\n`);
|
|
2439
|
+
process.exitCode = 1;
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2442
|
+
const cellPlatform = cellPlatformResult.value;
|
|
2121
2443
|
const hasCell = cellSdkLine !== void 0 || cellPlatform !== void 0;
|
|
2122
2444
|
const cell = {
|
|
2123
2445
|
sdkLine: cellSdkLine ?? "2.x",
|
|
2124
2446
|
platform: cellPlatform ?? "mock"
|
|
2125
2447
|
};
|
|
2448
|
+
const paceMethodMs = resolvePaceMethod(typeof vals["pace-method"] === "string" ? vals["pace-method"] : void 0, process.env.AIT_PACE_METHOD, cell.sdkLine);
|
|
2449
|
+
if (typeof paceMethodMs === "string") {
|
|
2450
|
+
process.stderr.write(`devtools-test: ${paceMethodMs}\n`);
|
|
2451
|
+
process.exitCode = 1;
|
|
2452
|
+
return;
|
|
2453
|
+
}
|
|
2126
2454
|
const globs = parsed.positionals;
|
|
2127
2455
|
if (globs.length === 0) {
|
|
2128
2456
|
process.stderr.write(`devtools-test: at least one glob pattern is required\n`);
|
|
@@ -2142,14 +2470,22 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2142
2470
|
process.stderr.write(manualBlocking && manual.length > 0 ? `devtools-test: found ${regular.length} regular + ${manual.length} manual (${MANUAL_TEST_SUFFIX}) test file(s)\n` : `devtools-test: found ${files.length} test file(s)\n`);
|
|
2143
2471
|
if (hasCell) process.stderr.write(`devtools-test: injecting __AIT_CELL__ = ${JSON.stringify(cell)}\n`);
|
|
2144
2472
|
if (stubBlocking) process.stderr.write("devtools-test: --stub-blocking enabled — blocking-UI calls in manual-tagged files will be answered from fixtures (devtools#740), not forwarded to native UI\n");
|
|
2473
|
+
if (paceMs > 0) process.stderr.write(`devtools-test: --pace ${paceMs}ms enabled (devtools#767)\n`);
|
|
2474
|
+
if (paceMethodMs > 0) process.stderr.write(`devtools-test: --pace-method ${paceMethodMs}ms enabled (devtools#769)\n`);
|
|
2145
2475
|
const factory = createRelayConnectionFactory({
|
|
2146
2476
|
schemeUrl,
|
|
2477
|
+
...attachLauncher ? {
|
|
2478
|
+
attachLauncher: true,
|
|
2479
|
+
appUrl
|
|
2480
|
+
} : {},
|
|
2147
2481
|
projectRoot,
|
|
2148
2482
|
...attachTimeoutMs !== void 0 ? { timeoutMs: attachTimeoutMs } : {},
|
|
2149
2483
|
...dashboardPort !== void 0 ? { dashboardPort } : {},
|
|
2150
2484
|
headless,
|
|
2151
2485
|
cell: hasCell ? cell : void 0,
|
|
2152
2486
|
stubBlocking,
|
|
2487
|
+
paceMs,
|
|
2488
|
+
paceMethodMs,
|
|
2153
2489
|
onQrContent: (chunks) => {
|
|
2154
2490
|
if (suppressQr) {
|
|
2155
2491
|
process.stdout.write("QR suppressed (non-interactive)\n");
|
|
@@ -2173,6 +2509,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2173
2509
|
timeoutMs: evaluateTimeoutMs,
|
|
2174
2510
|
printSummary: true,
|
|
2175
2511
|
collectCaptures: reportDir !== void 0,
|
|
2512
|
+
paceMs,
|
|
2513
|
+
preflightSdkLine: cell.sdkLine,
|
|
2176
2514
|
manualFiles: manualBlocking && manualFileSet.size > 0 ? manualFileSet : void 0,
|
|
2177
2515
|
stubBlockingFiles: stubBlocking && manualFileSet.size > 0 ? manualFileSet : void 0,
|
|
2178
2516
|
onManualFile: (file, index, total) => {
|