doc-detective 4.12.1 → 4.14.0
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/common/src/schemas/schemas.json +6759 -1997
- package/dist/common/src/types/generated/config_v3.d.ts +4 -0
- package/dist/common/src/types/generated/config_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/record_v3.d.ts +4 -0
- package/dist/common/src/types/generated/record_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/report_v3.d.ts +4 -0
- package/dist/common/src/types/generated/report_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/resolvedTests_v3.d.ts +8 -0
- package/dist/common/src/types/generated/resolvedTests_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/runBrowserScript_v3.d.ts +48 -0
- package/dist/common/src/types/generated/runBrowserScript_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/runBrowserScript_v3.js +7 -0
- package/dist/common/src/types/generated/runBrowserScript_v3.js.map +1 -0
- package/dist/common/src/types/generated/spec_v3.d.ts +4 -0
- package/dist/common/src/types/generated/spec_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/step_v3.d.ts +215 -75
- package/dist/common/src/types/generated/step_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/stopRecord_v3.d.ts +20 -2
- package/dist/common/src/types/generated/stopRecord_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/test_v3.d.ts +506 -222
- package/dist/common/src/types/generated/test_v3.d.ts.map +1 -1
- package/dist/core/resolveTests.d.ts.map +1 -1
- package/dist/core/resolveTests.js +2 -13
- package/dist/core/resolveTests.js.map +1 -1
- package/dist/core/tests/ffmpegRecorder.d.ts +11 -2
- package/dist/core/tests/ffmpegRecorder.d.ts.map +1 -1
- package/dist/core/tests/ffmpegRecorder.js +155 -8
- package/dist/core/tests/ffmpegRecorder.js.map +1 -1
- package/dist/core/tests/findElement.d.ts.map +1 -1
- package/dist/core/tests/findElement.js +2 -1
- package/dist/core/tests/findElement.js.map +1 -1
- package/dist/core/tests/runBrowserScript.d.ts +7 -0
- package/dist/core/tests/runBrowserScript.d.ts.map +1 -0
- package/dist/core/tests/runBrowserScript.js +133 -0
- package/dist/core/tests/runBrowserScript.js.map +1 -0
- package/dist/core/tests/saveScreenshot.d.ts.map +1 -1
- package/dist/core/tests/saveScreenshot.js +26 -9
- package/dist/core/tests/saveScreenshot.js.map +1 -1
- package/dist/core/tests/startRecording.d.ts.map +1 -1
- package/dist/core/tests/startRecording.js +36 -2
- package/dist/core/tests/startRecording.js.map +1 -1
- package/dist/core/tests/stopRecording.d.ts.map +1 -1
- package/dist/core/tests/stopRecording.js +51 -16
- package/dist/core/tests/stopRecording.js.map +1 -1
- package/dist/core/tests/typeKeys.d.ts.map +1 -1
- package/dist/core/tests/typeKeys.js +3 -2
- package/dist/core/tests/typeKeys.js.map +1 -1
- package/dist/core/tests.d.ts +12 -1
- package/dist/core/tests.d.ts.map +1 -1
- package/dist/core/tests.js +198 -47
- package/dist/core/tests.js.map +1 -1
- package/dist/core/utils.d.ts +25 -1
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/core/utils.js +68 -9
- package/dist/core/utils.js.map +1 -1
- package/dist/debug/provenance.d.ts.map +1 -1
- package/dist/debug/provenance.js +6 -0
- package/dist/debug/provenance.js.map +1 -1
- package/dist/hints/hints.d.ts.map +1 -1
- package/dist/hints/hints.js +40 -0
- package/dist/hints/hints.js.map +1 -1
- package/dist/index.cjs +6446 -1341
- package/dist/runtime/browserStepKeys.d.ts +19 -0
- package/dist/runtime/browserStepKeys.d.ts.map +1 -0
- package/dist/runtime/browserStepKeys.js +33 -0
- package/dist/runtime/browserStepKeys.js.map +1 -0
- package/dist/runtime/inferRuntimeNeeds.d.ts.map +1 -1
- package/dist/runtime/inferRuntimeNeeds.js +4 -19
- package/dist/runtime/inferRuntimeNeeds.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +7 -0
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import { log } from "../utils.js";
|
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import fs from "node:fs";
|
|
6
|
-
import { getFfmpegPath } from "./ffmpegRecorder.js";
|
|
6
|
+
import { getFfmpegPath, selectRecordingToStop, stopRecordTargetName, } from "./ffmpegRecorder.js";
|
|
7
7
|
export { stopRecording };
|
|
8
8
|
async function stopRecording({ config, step, driver }) {
|
|
9
9
|
let result = {
|
|
@@ -19,15 +19,48 @@ async function stopRecording({ config, step, driver }) {
|
|
|
19
19
|
result.description = `Invalid step definition: ${isValidStep.errors}`;
|
|
20
20
|
return result;
|
|
21
21
|
}
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
22
|
+
// `stopRecord: false` is an explicit no-op (mirrors `record: false`): it must
|
|
23
|
+
// not stop anything. Runtime dispatch keys on property presence, so guard the
|
|
24
|
+
// value here rather than in the schema (the boolean form stays valid).
|
|
25
|
+
if (step.stopRecord === false) {
|
|
26
|
+
result.status = "SKIPPED";
|
|
27
|
+
result.description = "Recording stop is disabled (stopRecord: false).";
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
// Resolve which active recording to stop. Recordings are per-context (they
|
|
31
|
+
// live on driver.state.recordings), so concurrent contexts can't see each
|
|
32
|
+
// other's recordings. `__stopAny` (set by end-of-context cleanup) lets a
|
|
33
|
+
// generic stop also drain the synthetic autoRecord recording.
|
|
34
|
+
const recordings = Array.isArray(driver?.state?.recordings)
|
|
35
|
+
? driver.state.recordings
|
|
36
|
+
: [];
|
|
37
|
+
const recording = selectRecordingToStop(recordings, step.stopRecord, {
|
|
38
|
+
includeSynthetic: step?.__stopAny === true,
|
|
39
|
+
});
|
|
26
40
|
if (!recording) {
|
|
27
41
|
result.status = "SKIPPED";
|
|
28
|
-
|
|
42
|
+
const target = stopRecordTargetName(step.stopRecord);
|
|
43
|
+
if (target !== undefined) {
|
|
44
|
+
result.description = `No active recording named '${target}'.`;
|
|
45
|
+
}
|
|
46
|
+
else if (recordings.length > 0) {
|
|
47
|
+
// An untargeted stop found only the synthetic autoRecord recording, which
|
|
48
|
+
// it deliberately skips — say so rather than the misleading "isn't started".
|
|
49
|
+
result.description = `No user-stoppable recording is active; an automatic (autoRecord) recording is still running and stops at the end of the context.`;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
result.description = `Recording isn't started.`;
|
|
53
|
+
}
|
|
29
54
|
return result;
|
|
30
55
|
}
|
|
56
|
+
// Remove this specific handle from the active set regardless of how the stop
|
|
57
|
+
// below resolves — a failed stop must not leave a dead handle that the
|
|
58
|
+
// end-of-context cleanup would retry forever.
|
|
59
|
+
const dropHandle = () => {
|
|
60
|
+
const idx = recordings.indexOf(recording);
|
|
61
|
+
if (idx !== -1)
|
|
62
|
+
recordings.splice(idx, 1);
|
|
63
|
+
};
|
|
31
64
|
try {
|
|
32
65
|
if (recording.type === "MediaRecorder") {
|
|
33
66
|
// Browser engine.
|
|
@@ -47,7 +80,7 @@ async function stopRecording({ config, step, driver }) {
|
|
|
47
80
|
if (remainingHandles.length > 0) {
|
|
48
81
|
await driver.switchToWindow(remainingHandles[0]);
|
|
49
82
|
}
|
|
50
|
-
|
|
83
|
+
dropHandle();
|
|
51
84
|
return result;
|
|
52
85
|
}
|
|
53
86
|
// Stop recording
|
|
@@ -64,7 +97,7 @@ async function stopRecording({ config, step, driver }) {
|
|
|
64
97
|
result.description = "Recording download timed out.";
|
|
65
98
|
// Clear the state so the auto-stop in runContext doesn't re-invoke
|
|
66
99
|
// a doomed second stop (the recorder was already told to stop).
|
|
67
|
-
|
|
100
|
+
dropHandle();
|
|
68
101
|
return result;
|
|
69
102
|
}
|
|
70
103
|
// Close recording tab and switch back to the original content tab
|
|
@@ -81,7 +114,7 @@ async function stopRecording({ config, step, driver }) {
|
|
|
81
114
|
targetPath: recording.targetPath,
|
|
82
115
|
deleteSource: true,
|
|
83
116
|
});
|
|
84
|
-
|
|
117
|
+
dropHandle();
|
|
85
118
|
}
|
|
86
119
|
else if (recording.type === "ffmpeg") {
|
|
87
120
|
// ffmpeg engine. Stop the capture gracefully (write "q" to stdin so the
|
|
@@ -134,16 +167,16 @@ async function stopRecording({ config, step, driver }) {
|
|
|
134
167
|
deleteSource: true,
|
|
135
168
|
crop: recording.crop,
|
|
136
169
|
});
|
|
137
|
-
|
|
170
|
+
dropHandle();
|
|
138
171
|
}
|
|
139
172
|
}
|
|
140
173
|
catch (error) {
|
|
141
174
|
// Couldn't stop recording
|
|
142
175
|
result.status = "FAIL";
|
|
143
176
|
result.description = `Couldn't stop recording. ${error}`;
|
|
144
|
-
//
|
|
177
|
+
// Drop the handle so the auto-stop in runContext doesn't re-invoke a
|
|
145
178
|
// doomed second stop.
|
|
146
|
-
|
|
179
|
+
dropHandle();
|
|
147
180
|
return result;
|
|
148
181
|
}
|
|
149
182
|
// PASS
|
|
@@ -207,9 +240,11 @@ async function transcode({ config, sourcePath, targetPath, deleteSource, crop, }
|
|
|
207
240
|
.on("error", reject);
|
|
208
241
|
});
|
|
209
242
|
}
|
|
210
|
-
// Wait for a file to exist and stop growing (size unchanged across
|
|
211
|
-
// ~500ms apart), up to `maxSeconds`. Returns true once
|
|
212
|
-
// timeout. Guards against transcoding a download that's still
|
|
243
|
+
// Wait for a file to exist and stop growing (size unchanged across three reads
|
|
244
|
+
// ~500ms apart, i.e. ~1s of stability), up to `maxSeconds`. Returns true once
|
|
245
|
+
// stable, false on timeout. Guards against transcoding a download that's still
|
|
246
|
+
// being written — concurrent recordings make a mid-write size plateau (the OS
|
|
247
|
+
// flushing in chunks) more likely, so a single agreeing read isn't enough.
|
|
213
248
|
async function waitForStableFile(filePath, maxSeconds) {
|
|
214
249
|
let lastSize = -1;
|
|
215
250
|
let stableReads = 0;
|
|
@@ -226,7 +261,7 @@ async function waitForStableFile(filePath, maxSeconds) {
|
|
|
226
261
|
// before writing data, and transcoding an empty file fails.
|
|
227
262
|
if (size > 0 && size === lastSize) {
|
|
228
263
|
stableReads++;
|
|
229
|
-
if (stableReads >=
|
|
264
|
+
if (stableReads >= 2)
|
|
230
265
|
return true;
|
|
231
266
|
}
|
|
232
267
|
else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stopRecording.js","sourceRoot":"","sources":["../../../src/core/tests/stopRecording.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,
|
|
1
|
+
{"version":3,"file":"stopRecording.js","sourceRoot":"","sources":["../../../src/core/tests/stopRecording.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeKeys.d.ts","sourceRoot":"","sources":["../../../src/core/tests/typeKeys.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"typeKeys.d.ts","sourceRoot":"","sources":["../../../src/core/tests/typeKeys.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,QAAQ,EAAE,CAAC;AAqFpB,iBAAe,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE;;;GA+IxF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { validate } from "../../common/src/validate.js";
|
|
2
2
|
import { findElementByCriteria, } from "./findStrategies.js";
|
|
3
3
|
import { loadHeavyDep } from "../../runtime/loader.js";
|
|
4
|
+
import { isRecordingActive } from "./ffmpegRecorder.js";
|
|
4
5
|
export { typeKeys };
|
|
5
6
|
let _specialKeyMap = null;
|
|
6
7
|
async function getSpecialKeyMap(ctx = {}) {
|
|
@@ -143,7 +144,7 @@ async function typeKeys({ config, step, driver }) {
|
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
// Split into array of strings, each containing a single key
|
|
146
|
-
if (driver
|
|
147
|
+
if (isRecordingActive(driver)) {
|
|
147
148
|
let keys = [];
|
|
148
149
|
step.type.keys.forEach((key) => {
|
|
149
150
|
if (key.startsWith("$") && key.endsWith("$")) {
|
|
@@ -186,7 +187,7 @@ async function typeKeys({ config, step, driver }) {
|
|
|
186
187
|
}
|
|
187
188
|
// Run action
|
|
188
189
|
try {
|
|
189
|
-
if (driver
|
|
190
|
+
if (isRecordingActive(driver)) {
|
|
190
191
|
// Type keys one at a time
|
|
191
192
|
for (let i = 0; i < step.type.keys.length; i++) {
|
|
192
193
|
await driver.keys(step.type.keys[i]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeKeys.js","sourceRoot":"","sources":["../../../src/core/tests/typeKeys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EACL,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"typeKeys.js","sourceRoot":"","sources":["../../../src/core/tests/typeKeys.ts"],"names":[],"mappings":"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"}
|
package/dist/core/tests.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type BrowserAssetName } from "../runtime/browsers.js";
|
|
2
|
-
export { runSpecs, runViaApi, getRunner, ensureChromeAvailable, ensureContextBrowserInstalled, combinationKey, warmUpDecision, selectWarmUpTargets, getDriverCapabilities, getDefaultBrowser, isSupportedContext, resolveAutoScreenshot, };
|
|
2
|
+
export { runSpecs, runViaApi, getRunner, ensureChromeAvailable, ensureContextBrowserInstalled, combinationKey, warmUpDecision, selectWarmUpTargets, getDriverCapabilities, getDefaultBrowser, isSupportedContext, resolveAutoScreenshot, resolveAutoRecord, buildAutoRecordStep, };
|
|
3
3
|
/**
|
|
4
4
|
* Stable identity for a "context combination" — the platform + browser pairing
|
|
5
5
|
* that determines whether a driver session can be created. The runner memoizes
|
|
@@ -76,6 +76,17 @@ declare function resolveAutoScreenshot({ config, spec, test, }: {
|
|
|
76
76
|
spec: any;
|
|
77
77
|
test: any;
|
|
78
78
|
}): boolean;
|
|
79
|
+
declare function resolveAutoRecord({ config, spec, test, }: {
|
|
80
|
+
config: any;
|
|
81
|
+
spec: any;
|
|
82
|
+
test: any;
|
|
83
|
+
}): boolean;
|
|
84
|
+
declare function buildAutoRecordStep({ config, spec, test, context, }: {
|
|
85
|
+
config: any;
|
|
86
|
+
spec: any;
|
|
87
|
+
test: any;
|
|
88
|
+
context: any;
|
|
89
|
+
}): any | null;
|
|
79
90
|
declare function runStep({ config, context, step, driver, metaValues, options, }: {
|
|
80
91
|
config?: any;
|
|
81
92
|
context?: any;
|
package/dist/core/tests.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tests.d.ts","sourceRoot":"","sources":["../../src/core/tests.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"tests.d.ts","sourceRoot":"","sources":["../../src/core/tests.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAoEhC,OAAO,EACL,QAAQ,EACR,SAAS,EACT,SAAS,EACT,qBAAqB,EACrB,6BAA6B,EAC7B,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,GACpB,CAAC;AASF;;;;;;;;GAQG;AACH,iBAAS,cAAc,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAI5C;AAED;;;;;;GAMG;AACH,iBAAS,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAE7E;AAGD,iBAAS,qBAAqB,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;IAAE,aAAa,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,GAAG,GAAG,CA2HrH;AAiBD,iBAAS,kBAAkB,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;IAAE,OAAO,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,GAAG,EAAE,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE,WAwBpG;AAED,iBAAS,iBAAiB,CAAC,EAAE,aAAa,EAAE,EAAE;IAAE,aAAa,EAAE,GAAG,CAAA;CAAE,OAUnE;AA8CD,iBAAe,SAAS,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,MAAW,EAAE,EAAE;IAAE,aAAa,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAwIhI;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,iBAAe,QAAQ,CAAC,EAAE,aAAa,EAAE,EAAE;IAAE,aAAa,EAAE,GAAG,CAAA;CAAE,gBAgfhE;AAED;;;;;;;;;GASG;AACH,iBAAS,mBAAmB,CAC1B,IAAI,EAAE,GAAG,EAAE,EACX,aAAa,EAAE,GAAG,GACjB,KAAK,CAAC;IAAE,OAAO,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAyBxC;AA8ID,iBAAS,qBAAqB,CAAC,EAC7B,MAAM,EACN,IAAI,EACJ,IAAI,GACL,EAAE;IACD,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC;IACV,IAAI,EAAE,GAAG,CAAC;CACX,GAAG,OAAO,CAIV;AAID,iBAAS,iBAAiB,CAAC,EACzB,MAAM,EACN,IAAI,EACJ,IAAI,GACL,EAAE;IACD,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC;IACV,IAAI,EAAE,GAAG,CAAC;CACX,GAAG,OAAO,CAEV;AASD,iBAAS,mBAAmB,CAAC,EAC3B,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,OAAO,GACR,EAAE;IACD,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,GAAG,CAAC;IACV,IAAI,EAAE,GAAG,CAAC;IACV,OAAO,EAAE,GAAG,CAAC;CACd,GAAG,GAAG,GAAG,IAAI,CA0Bb;AAinBD,iBAAe,OAAO,CAAC,EACrB,MAAW,EACX,OAAY,EACZ,IAAI,EACJ,MAAM,EACN,UAAe,EACf,OAAY,GACb,EAAE;IACD,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,IAAI,EAAE,GAAG,CAAC;IACV,MAAM,EAAE,GAAG,CAAC;IACZ,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,OAAO,CAAC,EAAE,GAAG,CAAC;CACf,GAAG,OAAO,CAAC,GAAG,CAAC,CAuHf;AA+KD;;;;;;;;;;;GAWG;AACH,iBAAe,qBAAqB,CAClC,MAAM,EAAE,GAAG,EACX,IAAI,EAAE;IACJ,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACxC,SAAS,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,UAAU,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IAClC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACzD,GACA,OAAO,CAAC,GAAG,EAAE,CAAC,CAmChB;AAED;;;;;;;;;;;GAWG;AACH,iBAAe,6BAA6B,CAAC,EAC3C,WAAW,EACX,MAAM,EACN,eAAe,EACf,IAAI,GACL,EAAE;IACD,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,GAAG,CAAC;IACZ,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,gBAAgB,CAAC,CAAC;IACxE,IAAI,EAAE;QACJ,aAAa,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;QACvE,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;KACzD,CAAC;CACH,GAAG,OAAO,CAAC,WAAW,GAAG,QAAQ,GAAG,gBAAgB,CAAC,CAsCrD;AAED,iBAAe,SAAS,CAAC,OAAO,GAAE,GAAQ;;;;;GAoHzC"}
|
package/dist/core/tests.js
CHANGED
|
@@ -7,6 +7,8 @@ import kill from "tree-kill";
|
|
|
7
7
|
// to a non-runtime identifier.
|
|
8
8
|
import { loadHeavyDep, resolveHeavyDepPath } from "../runtime/loader.js";
|
|
9
9
|
import { requiredBrowserAssets, ensureBrowserInstalled, } from "../runtime/browsers.js";
|
|
10
|
+
// Single source of truth for browser/driver-requiring step keys.
|
|
11
|
+
import { BROWSER_STEP_KEYS as driverActions } from "../runtime/browserStepKeys.js";
|
|
10
12
|
import os from "node:os";
|
|
11
13
|
import { log, replaceEnvs, selectSpecsForRun, findFreePort, runConcurrent, rollUpResults, createAppiumPool, getRunOutputDir, runArchivesArtifacts, sanitizeFilesystemName, } from "./utils.js";
|
|
12
14
|
import axios from "axios";
|
|
@@ -20,13 +22,14 @@ import { wait } from "./tests/wait.js";
|
|
|
20
22
|
import { saveScreenshot } from "./tests/saveScreenshot.js";
|
|
21
23
|
import { startRecording } from "./tests/startRecording.js";
|
|
22
24
|
import { stopRecording } from "./tests/stopRecording.js";
|
|
23
|
-
import { browserCaptureTitle, browserDownloadDir, coerceRecordContextBrowser, jobIsFfmpegRecording, computeEffectiveConcurrency, checkSystemBinary, xvfbDisplay, startXvfb, XVFB_SCREEN_SIZE, } from "./tests/ffmpegRecorder.js";
|
|
25
|
+
import { browserCaptureTitle, browserDownloadDir, coerceRecordContextBrowser, jobIsFfmpegRecording, computeEffectiveConcurrency, checkSystemBinary, xvfbDisplay, startXvfb, XVFB_SCREEN_SIZE, isRecordingActive, recordStepName, detectRecordingNameConflict, } from "./tests/ffmpegRecorder.js";
|
|
24
26
|
import { loadVariables } from "./tests/loadVariables.js";
|
|
25
27
|
import { saveCookie } from "./tests/saveCookie.js";
|
|
26
28
|
import { loadCookie } from "./tests/loadCookie.js";
|
|
27
29
|
import { httpRequest } from "./tests/httpRequest.js";
|
|
28
30
|
import { clickElement } from "./tests/click.js";
|
|
29
31
|
import { runCode } from "./tests/runCode.js";
|
|
32
|
+
import { runBrowserScript } from "./tests/runBrowserScript.js";
|
|
30
33
|
import { dragAndDropElement } from "./tests/dragAndDrop.js";
|
|
31
34
|
import path from "node:path";
|
|
32
35
|
import { spawn } from "node:child_process";
|
|
@@ -40,23 +43,10 @@ import http from "node:http";
|
|
|
40
43
|
import https from "node:https";
|
|
41
44
|
import { fileURLToPath } from "node:url";
|
|
42
45
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
43
|
-
export { runSpecs, runViaApi, getRunner, ensureChromeAvailable, ensureContextBrowserInstalled, combinationKey, warmUpDecision, selectWarmUpTargets, getDriverCapabilities, getDefaultBrowser, isSupportedContext, resolveAutoScreenshot, };
|
|
46
|
+
export { runSpecs, runViaApi, getRunner, ensureChromeAvailable, ensureContextBrowserInstalled, combinationKey, warmUpDecision, selectWarmUpTargets, getDriverCapabilities, getDefaultBrowser, isSupportedContext, resolveAutoScreenshot, resolveAutoRecord, buildAutoRecordStep, };
|
|
44
47
|
// exports.appiumStart = appiumStart;
|
|
45
48
|
// exports.appiumIsReady = appiumIsReady;
|
|
46
49
|
// exports.driverStart = driverStart;
|
|
47
|
-
// Doc Detective actions that require a driver.
|
|
48
|
-
const driverActions = [
|
|
49
|
-
"click",
|
|
50
|
-
"dragAndDrop",
|
|
51
|
-
"stopRecord",
|
|
52
|
-
"find",
|
|
53
|
-
"goTo",
|
|
54
|
-
"loadCookie",
|
|
55
|
-
"record",
|
|
56
|
-
"saveCookie",
|
|
57
|
-
"screenshot",
|
|
58
|
-
"type",
|
|
59
|
-
];
|
|
60
50
|
// Browser names getDriverCapabilities knows how to build caps for. `safari` is
|
|
61
51
|
// rewritten to `webkit` during context resolution, so both appear here.
|
|
62
52
|
const KNOWN_BROWSERS = ["firefox", "chrome", "safari", "webkit"];
|
|
@@ -519,6 +509,10 @@ async function runSpecs({ resolvedTests }) {
|
|
|
519
509
|
// matches input order, no matter what order concurrent contexts finish in.
|
|
520
510
|
log(config, "info", "Running test specs.");
|
|
521
511
|
const jobs = [];
|
|
512
|
+
// Set when at least one context gets a synthetic autoRecord (ffmpeg) step.
|
|
513
|
+
// It opts the run into overlapping ffmpeg captures (parallel anyway) rather
|
|
514
|
+
// than the safe-serial default reserved for explicit-only ffmpeg recordings.
|
|
515
|
+
let autoRecordInjected = false;
|
|
522
516
|
for (const spec of specs) {
|
|
523
517
|
log(config, "debug", `SPEC: ${spec.specId}`);
|
|
524
518
|
// Create-if-missing: specIds (and testIds) aren't guaranteed unique
|
|
@@ -543,6 +537,22 @@ async function runSpecs({ resolvedTests }) {
|
|
|
543
537
|
contexts: new Array(test.contexts.length),
|
|
544
538
|
};
|
|
545
539
|
specReport.tests.push(testReport);
|
|
540
|
+
// Preflight: a `record` step that reuses a recording `name` while one is
|
|
541
|
+
// still active makes a later `stopRecord: "<name>"` ambiguous. Catch it
|
|
542
|
+
// statically and skip the whole test (across all its contexts) with a
|
|
543
|
+
// warning, rather than failing mid-run. Scan every context's authored
|
|
544
|
+
// steps (runOn overrides can differ) and skip if any conflicts.
|
|
545
|
+
let recordingNameConflict = null;
|
|
546
|
+
for (const c of test.contexts) {
|
|
547
|
+
const conflict = detectRecordingNameConflict(c?.steps);
|
|
548
|
+
if (conflict) {
|
|
549
|
+
recordingNameConflict = conflict;
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (recordingNameConflict) {
|
|
554
|
+
log(config, "warning", `Skipping test '${test.testId}': recording name '${recordingNameConflict}' is reused while a recording with that name is still active. Names must be unique among recordings that overlap in time.`);
|
|
555
|
+
}
|
|
546
556
|
// Track contextIds within this test so the deterministic fallback below
|
|
547
557
|
// can suffix collisions, mirroring resolveTests' deriveContextId.
|
|
548
558
|
const usedContextIds = new Set(test.contexts.map((c) => c.contextId).filter(Boolean));
|
|
@@ -566,6 +576,35 @@ async function runSpecs({ resolvedTests }) {
|
|
|
566
576
|
usedContextIds.add(id);
|
|
567
577
|
context.contextId = id;
|
|
568
578
|
}
|
|
579
|
+
// Preflight conflict: record a SKIPPED context and don't enqueue a job.
|
|
580
|
+
if (recordingNameConflict) {
|
|
581
|
+
testReport.contexts[slot] = {
|
|
582
|
+
contextId: context.contextId,
|
|
583
|
+
platform: context.platform,
|
|
584
|
+
browser: context.browser,
|
|
585
|
+
result: "SKIPPED",
|
|
586
|
+
resultDescription: `Skipped — recording name '${recordingNameConflict}' is reused while still active; names must be unique among overlapping recordings.`,
|
|
587
|
+
steps: [],
|
|
588
|
+
};
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
// autoRecord: prepend a synthetic full-context ffmpeg recording step
|
|
592
|
+
// (even when the author also has explicit record steps — overlapping is
|
|
593
|
+
// intended). Done before the concurrency calc below so the synthetic
|
|
594
|
+
// ffmpeg recording is counted by jobIsFfmpegRecording.
|
|
595
|
+
if (resolveAutoRecord({ config, spec, test })) {
|
|
596
|
+
const autoStep = buildAutoRecordStep({ config, spec, test, context });
|
|
597
|
+
if (autoStep) {
|
|
598
|
+
// Idempotent: strip any prior synthetic step before prepending, so a
|
|
599
|
+
// resolved context that's reused (e.g. runSpecs invoked twice) can't
|
|
600
|
+
// accumulate duplicate autoRecord captures targeting the same file.
|
|
601
|
+
const authored = Array.isArray(context.steps)
|
|
602
|
+
? context.steps.filter((s) => !s?.__autoRecord)
|
|
603
|
+
: [];
|
|
604
|
+
context.steps = [autoStep, ...authored];
|
|
605
|
+
autoRecordInjected = true;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
569
608
|
// Auto-resolution: when a record step has no explicit engine and the
|
|
570
609
|
// user never chose a browser, prefer the concurrency-safe browser
|
|
571
610
|
// engine by coercing to headed Chrome (when available). Done here,
|
|
@@ -595,17 +634,34 @@ async function runSpecs({ resolvedTests }) {
|
|
|
595
634
|
if (anyFfmpegRecording && process.platform === "linux") {
|
|
596
635
|
xvfbAvailable = await checkSystemBinary("Xvfb");
|
|
597
636
|
}
|
|
637
|
+
// "Parallel anyway" is an autoRecord-only opt-in: only bypass the forced-serial
|
|
638
|
+
// safeguard when every ffmpeg recording in the run is a synthetic autoRecord
|
|
639
|
+
// capture. If any explicit (author-written) record step would run as ffmpeg,
|
|
640
|
+
// keep the safe serial default so those recordings aren't silently
|
|
641
|
+
// parallelized (which would clobber each other on a shared display).
|
|
642
|
+
const hasExplicitFfmpegRecording = jobs.some((job) => jobIsFfmpegRecording({
|
|
643
|
+
context: {
|
|
644
|
+
...job.context,
|
|
645
|
+
steps: Array.isArray(job.context?.steps)
|
|
646
|
+
? job.context.steps.filter((s) => !s?.__autoRecord)
|
|
647
|
+
: [],
|
|
648
|
+
},
|
|
649
|
+
}));
|
|
598
650
|
const concurrency = computeEffectiveConcurrency({
|
|
599
651
|
requestedLimit: limit,
|
|
600
652
|
jobs,
|
|
601
653
|
platform: process.platform,
|
|
602
654
|
xvfbAvailable,
|
|
655
|
+
allowOverlappingCaptures: autoRecordInjected && !hasExplicitFfmpegRecording,
|
|
603
656
|
});
|
|
604
657
|
limit = concurrency.limit;
|
|
605
658
|
if (concurrency.forcedSerial) {
|
|
606
659
|
log(config, "warning", "Recording with the ffmpeg engine needs exclusive use of the display, so this run is executing serially (concurrentRunners=1). To record concurrently, use the Chrome browser engine (record: { engine: \"browser\" }) or, on Linux, install Xvfb.");
|
|
607
660
|
report.recordingForcedSerial = true;
|
|
608
661
|
}
|
|
662
|
+
else if (concurrency.overlappingCaptures && limit > 1) {
|
|
663
|
+
log(config, "warning", "autoRecord is running ffmpeg recordings concurrently on a shared display, so the captured videos will overlap (each context records the whole screen). For isolated concurrent recordings, run on Linux with Xvfb installed.");
|
|
664
|
+
}
|
|
609
665
|
// Start one Appium server per concurrent runner that will actually use a
|
|
610
666
|
// driver (capped at the number of driver contexts). Each server owns a
|
|
611
667
|
// distinct port, so parallel contexts never create sessions on the same
|
|
@@ -957,6 +1013,38 @@ async function warmUpContexts({ jobs, config, runnerDetails, appiumPool, install
|
|
|
957
1013
|
function resolveAutoScreenshot({ config, spec, test, }) {
|
|
958
1014
|
return Boolean(test?.autoScreenshot ?? spec?.autoScreenshot ?? config?.autoScreenshot);
|
|
959
1015
|
}
|
|
1016
|
+
// Effective autoRecord setting for a test: same precedence as autoScreenshot
|
|
1017
|
+
// (test > spec > config; unset levels defer down the chain).
|
|
1018
|
+
function resolveAutoRecord({ config, spec, test, }) {
|
|
1019
|
+
return Boolean(test?.autoRecord ?? spec?.autoRecord ?? config?.autoRecord);
|
|
1020
|
+
}
|
|
1021
|
+
// Build the synthetic full-context recording step for an autoRecord run, or
|
|
1022
|
+
// null when the context shouldn't be recorded (no driver-required authored
|
|
1023
|
+
// steps). Always uses the ffmpeg engine, and a deterministic path under the
|
|
1024
|
+
// run's artifact folder (recordings/<specId>/<testId>/<contextId>.mp4) so the
|
|
1025
|
+
// same context lands on the same relative path every run for comparison. The
|
|
1026
|
+
// `__autoRecord` marker tags the started handle as synthetic so an untargeted
|
|
1027
|
+
// user `stopRecord` won't end it (only end-of-context cleanup does).
|
|
1028
|
+
function buildAutoRecordStep({ config, spec, test, context, }) {
|
|
1029
|
+
if (!isDriverRequired({ test: context }))
|
|
1030
|
+
return null;
|
|
1031
|
+
// Compute the path only — runSpecs reserves the run folder up front when
|
|
1032
|
+
// runArchivesArtifacts is true (autoRecord counts), so creating it here would
|
|
1033
|
+
// be redundant. (startRecording also mkdirs the recording's target dir up
|
|
1034
|
+
// front, so an enabled autoRecord run reserves the folder regardless — same as
|
|
1035
|
+
// autoScreenshot.)
|
|
1036
|
+
const runDir = getRunOutputDir(config, { create: false });
|
|
1037
|
+
const fileName = `${capPathSegment(sanitizeFilesystemName(String(context.contextId ?? ""), "context"))}.mp4`;
|
|
1038
|
+
const recordPath = path.join(runDir, "recordings", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), capPathSegment(sanitizeFilesystemName(String(test.testId ?? ""), "test")), fileName);
|
|
1039
|
+
return {
|
|
1040
|
+
record: { path: recordPath, overwrite: "true", engine: "ffmpeg" },
|
|
1041
|
+
description: "Automatic full-context recording",
|
|
1042
|
+
stepId: `${sanitizeFilesystemName(String(test.testId ?? ""), "test")}~autorecord`,
|
|
1043
|
+
// Internal marker — the runStep record dispatch flags the started handle as
|
|
1044
|
+
// synthetic so it survives untargeted stopRecord and is swept by cleanup.
|
|
1045
|
+
__autoRecord: true,
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
960
1048
|
// Directory/file segments built from IDs are capped so deeply nested doc
|
|
961
1049
|
// trees can't push the full screenshot path past Windows' MAX_PATH. Keep the
|
|
962
1050
|
// tail — content hashes live at the end of generated IDs.
|
|
@@ -1345,35 +1433,27 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
|
|
|
1345
1433
|
stepExecutionFailed = true;
|
|
1346
1434
|
}
|
|
1347
1435
|
}
|
|
1348
|
-
//
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
description: "Stopping recording",
|
|
1353
|
-
stepId: randomUUID(),
|
|
1354
|
-
};
|
|
1355
|
-
const stepResult = await runStep({
|
|
1356
|
-
config: config,
|
|
1357
|
-
context: context,
|
|
1358
|
-
step: stopRecordStep,
|
|
1359
|
-
driver: driver,
|
|
1360
|
-
options: {
|
|
1361
|
-
openApiDefinitions: context.openApi || [],
|
|
1362
|
-
},
|
|
1363
|
-
});
|
|
1364
|
-
stepResult.result = stepResult.status;
|
|
1365
|
-
stepResult.resultDescription = stepResult.description;
|
|
1366
|
-
delete stepResult.status;
|
|
1367
|
-
delete stepResult.description;
|
|
1368
|
-
// Add step result to report
|
|
1369
|
-
const stepReport = {
|
|
1370
|
-
...stopRecordStep,
|
|
1371
|
-
...stepResult,
|
|
1372
|
-
};
|
|
1373
|
-
contextReport.steps.push(stepReport);
|
|
1374
|
-
}
|
|
1436
|
+
// Stop every recording still active at the end of the context (the
|
|
1437
|
+
// synthetic autoRecord capture plus any explicit record steps the author
|
|
1438
|
+
// didn't stop). Each produces an ordered stopRecord step report.
|
|
1439
|
+
await stopAllRecordings({ config, context, driver, contextReport });
|
|
1375
1440
|
}
|
|
1376
1441
|
finally {
|
|
1442
|
+
// Safety net: if the context threw before the normal sweep above, recordings
|
|
1443
|
+
// are still active. Stop them now — while the driver session is still alive
|
|
1444
|
+
// (the deleteSession below would otherwise kill the browser/ffmpeg capture
|
|
1445
|
+
// and leak the process). On the normal path the set is already empty, so
|
|
1446
|
+
// this is a no-op. Best-effort: a stop failure here must not mask the
|
|
1447
|
+
// original error.
|
|
1448
|
+
try {
|
|
1449
|
+
// On the normal path the step loop above already drained every recording,
|
|
1450
|
+
// so this is a no-op; it only does work when the context threw before the
|
|
1451
|
+
// in-loop sweep, finalizing recordings before deleteSession kills them.
|
|
1452
|
+
await stopAllRecordings({ config, context, driver, contextReport });
|
|
1453
|
+
}
|
|
1454
|
+
catch (error) {
|
|
1455
|
+
clog("error", `Failed to stop recordings during cleanup: ${error?.message ?? error}`);
|
|
1456
|
+
}
|
|
1377
1457
|
// Close driver. In a finally so an unexpected throw can't leak a session
|
|
1378
1458
|
// while sibling contexts keep running.
|
|
1379
1459
|
if (driver) {
|
|
@@ -1394,6 +1474,55 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
|
|
|
1394
1474
|
contextReport.result = rollUpResults(contextReport.steps);
|
|
1395
1475
|
return contextReport;
|
|
1396
1476
|
}
|
|
1477
|
+
// Stop every recording still active on the driver, pushing an ordered
|
|
1478
|
+
// stopRecord step report for each. Drains the per-context stack (LIFO) by
|
|
1479
|
+
// synthesizing `{ stopRecord: true, __stopAny: true }` steps — `__stopAny`
|
|
1480
|
+
// lets the generic stop also end the synthetic autoRecord recording (a plain
|
|
1481
|
+
// user `stopRecord: true` deliberately skips it). Each stop removes its handle
|
|
1482
|
+
// even on failure, so the loop always makes progress; an explicit iteration
|
|
1483
|
+
// cap is a belt-and-suspenders guard against a handle that somehow refuses to
|
|
1484
|
+
// drop. Each synthesized stop is isolated so one failure doesn't abort the
|
|
1485
|
+
// rest. Safe to call when nothing is active (no-op).
|
|
1486
|
+
async function stopAllRecordings({ config, context, driver, contextReport, }) {
|
|
1487
|
+
if (!Array.isArray(driver?.state?.recordings))
|
|
1488
|
+
return;
|
|
1489
|
+
let guard = driver.state.recordings.length + 1;
|
|
1490
|
+
while (driver.state.recordings.length > 0 && guard-- > 0) {
|
|
1491
|
+
const stopRecordStep = {
|
|
1492
|
+
stopRecord: true,
|
|
1493
|
+
__stopAny: true,
|
|
1494
|
+
description: "Stopping recording",
|
|
1495
|
+
stepId: randomUUID(),
|
|
1496
|
+
};
|
|
1497
|
+
try {
|
|
1498
|
+
const stepResult = await runStep({
|
|
1499
|
+
config,
|
|
1500
|
+
context,
|
|
1501
|
+
step: stopRecordStep,
|
|
1502
|
+
driver,
|
|
1503
|
+
options: { openApiDefinitions: context.openApi || [] },
|
|
1504
|
+
});
|
|
1505
|
+
stepResult.result = stepResult.status;
|
|
1506
|
+
stepResult.resultDescription = stepResult.description;
|
|
1507
|
+
delete stepResult.status;
|
|
1508
|
+
delete stepResult.description;
|
|
1509
|
+
// Don't leak the internal routing marker into the report.
|
|
1510
|
+
delete stopRecordStep.__stopAny;
|
|
1511
|
+
contextReport.steps.push({ ...stopRecordStep, ...stepResult });
|
|
1512
|
+
}
|
|
1513
|
+
catch (error) {
|
|
1514
|
+
// A throw from runStep would otherwise strand the remaining handles.
|
|
1515
|
+
// Drop the top handle so the loop can't spin, and record the failure.
|
|
1516
|
+
delete stopRecordStep.__stopAny;
|
|
1517
|
+
driver.state.recordings.pop();
|
|
1518
|
+
contextReport.steps.push({
|
|
1519
|
+
...stopRecordStep,
|
|
1520
|
+
result: "FAIL",
|
|
1521
|
+
resultDescription: `Couldn't stop recording. ${error?.message ?? error}`,
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1397
1526
|
// Run a specific step
|
|
1398
1527
|
async function runStep({ config = {}, context = {}, step, driver, metaValues = {}, options = {}, }) {
|
|
1399
1528
|
let actionResult;
|
|
@@ -1456,11 +1585,30 @@ async function runStep({ config = {}, context = {}, step, driver, metaValues = {
|
|
|
1456
1585
|
step: step,
|
|
1457
1586
|
driver: driver,
|
|
1458
1587
|
});
|
|
1459
|
-
|
|
1588
|
+
// Push the started recording onto the per-context stack so several can
|
|
1589
|
+
// overlap. Carry the step's `id`/`name` so a later stopRecord can target
|
|
1590
|
+
// it (by name) and end-of-context cleanup can identify the synthetic one.
|
|
1591
|
+
if (actionResult.recording) {
|
|
1592
|
+
if (!Array.isArray(driver.state.recordings))
|
|
1593
|
+
driver.state.recordings = [];
|
|
1594
|
+
const handle = actionResult.recording;
|
|
1595
|
+
handle.id = handle.id ?? randomUUID();
|
|
1596
|
+
handle.name = handle.name ?? recordStepName(step.record);
|
|
1597
|
+
if (step.__autoRecord)
|
|
1598
|
+
handle.synthetic = true;
|
|
1599
|
+
driver.state.recordings.push(handle);
|
|
1600
|
+
}
|
|
1460
1601
|
}
|
|
1461
1602
|
else if (typeof step.runCode !== "undefined") {
|
|
1462
1603
|
actionResult = await runCode({ config: config, step: step });
|
|
1463
1604
|
}
|
|
1605
|
+
else if (typeof step.runBrowserScript !== "undefined") {
|
|
1606
|
+
actionResult = await runBrowserScript({
|
|
1607
|
+
config: config,
|
|
1608
|
+
step: step,
|
|
1609
|
+
driver: driver,
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1464
1612
|
else if (typeof step.runShell !== "undefined") {
|
|
1465
1613
|
actionResult = await runShell({ config: config, step: step });
|
|
1466
1614
|
}
|
|
@@ -1488,7 +1636,7 @@ async function runStep({ config = {}, context = {}, step, driver, metaValues = {
|
|
|
1488
1636
|
};
|
|
1489
1637
|
}
|
|
1490
1638
|
// If recording, wait until browser is loaded, then instantiate cursor
|
|
1491
|
-
if (driver
|
|
1639
|
+
if (isRecordingActive(driver)) {
|
|
1492
1640
|
const currentUrl = await driver.getUrl();
|
|
1493
1641
|
if (currentUrl !== driver.state.url) {
|
|
1494
1642
|
driver.state.url = currentUrl;
|
|
@@ -1599,9 +1747,12 @@ async function driverStart(capabilities, port, maxAttempts = 4, ctx = {}) {
|
|
|
1599
1747
|
connectionRetryTimeout: 120000, // 2 minutes
|
|
1600
1748
|
waitforTimeout: 120000, // 2 minutes
|
|
1601
1749
|
});
|
|
1602
|
-
// Per-context mutable state. `
|
|
1603
|
-
// so concurrent contexts can't clobber each other's recordings.
|
|
1604
|
-
|
|
1750
|
+
// Per-context mutable state. `recordings` lives here (not on config)
|
|
1751
|
+
// so concurrent contexts can't clobber each other's recordings. It is a
|
|
1752
|
+
// stack of active recording handles (LIFO) so several can overlap within
|
|
1753
|
+
// one context (e.g. an autoRecord full-context capture plus an explicit
|
|
1754
|
+
// record/stopRecord sub-section).
|
|
1755
|
+
driver.state = { url: "", x: null, y: null, recordings: [] };
|
|
1605
1756
|
return driver;
|
|
1606
1757
|
}
|
|
1607
1758
|
catch (err) {
|