@tracelane/wdio 0.1.0-alpha.1
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/NOTICE +19 -0
- package/README.md +138 -0
- package/dist/framework-result.d.ts +21 -0
- package/dist/framework-result.d.ts.map +1 -0
- package/dist/framework-result.js +87 -0
- package/dist/framework-result.js.map +1 -0
- package/dist/hooks.d.ts +34 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +56 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/inpage-bundle.d.ts +9 -0
- package/dist/inpage-bundle.d.ts.map +1 -0
- package/dist/inpage-bundle.js +47 -0
- package/dist/inpage-bundle.js.map +1 -0
- package/dist/network-capture.d.ts +25 -0
- package/dist/network-capture.d.ts.map +1 -0
- package/dist/network-capture.js +56 -0
- package/dist/network-capture.js.map +1 -0
- package/dist/options.d.ts +36 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +4 -0
- package/dist/options.js.map +1 -0
- package/dist/report-writer.d.ts +36 -0
- package/dist/report-writer.d.ts.map +1 -0
- package/dist/report-writer.js +56 -0
- package/dist/report-writer.js.map +1 -0
- package/dist/rrweb-bundle.js +141 -0
- package/dist/service.d.ts +44 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +73 -0
- package/dist/service.js.map +1 -0
- package/dist/test-identity.d.ts +23 -0
- package/dist/test-identity.d.ts.map +1 -0
- package/dist/test-identity.js +15 -0
- package/dist/test-identity.js.map +1 -0
- package/dist/tracelane-session.d.ts +79 -0
- package/dist/tracelane-session.d.ts.map +1 -0
- package/dist/tracelane-session.js +189 -0
- package/dist/tracelane-session.js.map +1 -0
- package/dist/wdio-executor.d.ts +27 -0
- package/dist/wdio-executor.d.ts.map +1 -0
- package/dist/wdio-executor.js +39 -0
- package/dist/wdio-executor.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Frameworks, Services } from '@wdio/types';
|
|
2
|
+
import type { TraceLaneOptions } from './options.js';
|
|
3
|
+
/**
|
|
4
|
+
* The tracelane WebdriverIO Service. Implements the worker + launcher hooks from
|
|
5
|
+
* `Services.ServiceInstance`; extra Cucumber hooks (`beforeScenario` /
|
|
6
|
+
* `afterScenario`) are declared as plain methods since `@wdio/types` doesn't put
|
|
7
|
+
* them on `ServiceInstance` (they only exist at runtime under the Cucumber
|
|
8
|
+
* framework).
|
|
9
|
+
*/
|
|
10
|
+
export default class TraceLaneService implements Services.ServiceInstance {
|
|
11
|
+
private readonly session;
|
|
12
|
+
/**
|
|
13
|
+
* WDIO instantiates the Service with `(options, capabilities, config)`. We
|
|
14
|
+
* read `config.framework` so the result-shape switch (P1 PRD §A.2) picks the
|
|
15
|
+
* right normalization, and `config.outputDir`-adjacent options come from the
|
|
16
|
+
* `options` arg.
|
|
17
|
+
*/
|
|
18
|
+
constructor(options?: TraceLaneOptions, _capabilities?: unknown, config?: {
|
|
19
|
+
framework?: string;
|
|
20
|
+
});
|
|
21
|
+
/** Refine the framework + worker id once the session is initializing. */
|
|
22
|
+
beforeSession(_config: unknown, _capabilities: unknown, _specs: string[], cid?: string): void;
|
|
23
|
+
/** Worker hook: stash the live browser + build the recorder. */
|
|
24
|
+
before(_capabilities: unknown, _specs: string[], browser: WebdriverIO.Browser): Promise<void>;
|
|
25
|
+
/** No-op in v1; present for the documented hook surface (ADR-0004). */
|
|
26
|
+
beforeSuite(_suite: Frameworks.Suite): void;
|
|
27
|
+
/** Mocha/Jasmine: start capture and remember the test identity. */
|
|
28
|
+
beforeTest(test: Frameworks.Test, _context: unknown): Promise<void>;
|
|
29
|
+
/** Mocha/Jasmine: decide + write the report from the per-test result. */
|
|
30
|
+
afterTest(_test: Frameworks.Test, _context: unknown, result: Frameworks.TestResult): Promise<void>;
|
|
31
|
+
/** Cucumber: scenario start (runtime-only hook; mirrors `beforeTest`). */
|
|
32
|
+
beforeScenario(world: unknown, _context?: unknown): Promise<void>;
|
|
33
|
+
/** Cucumber: scenario end (runtime-only hook; mirrors `afterTest`). */
|
|
34
|
+
afterScenario(world: unknown, result?: unknown): Promise<void>;
|
|
35
|
+
/** Re-inject the recorder after a `url(...)` navigation (ADR-0006). */
|
|
36
|
+
beforeCommand(commandName: string, args: unknown[]): Promise<void>;
|
|
37
|
+
/** No-op in v1; present for the documented hook surface (ADR-0004). */
|
|
38
|
+
afterSuite(_suite: Frameworks.Suite): void;
|
|
39
|
+
/** Worker teardown: stop the drain poll so no timer leaks. */
|
|
40
|
+
after(_result: number, _capabilities: unknown, _specs: string[]): Promise<void>;
|
|
41
|
+
/** Launcher hook: nothing to aggregate in v1 (reports are per-test files). */
|
|
42
|
+
onComplete(_exitCode: number, _config: unknown, _capabilities: unknown, _results: unknown): void;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKrD;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,YAAW,QAAQ,CAAC,eAAe;IACvE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAE3C;;;;;OAKG;gBAED,OAAO,GAAE,gBAAqB,EAC9B,aAAa,CAAC,EAAE,OAAO,EACvB,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;IAKjC,yEAAyE;IACzE,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAI7F,gEAAgE;IAC1D,MAAM,CACV,aAAa,EAAE,OAAO,EACtB,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,WAAW,CAAC,OAAO,GAC3B,OAAO,CAAC,IAAI,CAAC;IAIhB,uEAAuE;IACvE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,GAAG,IAAI;IAE3C,mEAAmE;IAC7D,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzE,yEAAyE;IACnE,SAAS,CACb,KAAK,EAAE,UAAU,CAAC,IAAI,EACtB,QAAQ,EAAE,OAAO,EACjB,MAAM,EAAE,UAAU,CAAC,UAAU,GAC5B,OAAO,CAAC,IAAI,CAAC;IAIhB,0EAA0E;IACpE,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvE,uEAAuE;IACjE,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE,uEAAuE;IACjE,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxE,uEAAuE;IACvE,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,GAAG,IAAI;IAE1C,8DAA8D;IACxD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrF,8EAA8E;IAC9E,UAAU,CACR,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,OAAO,EACtB,QAAQ,EAAE,OAAO,GAChB,IAAI;CACR"}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// TraceLaneService — the WebdriverIO Service (Task 2.14 / ADR-0004).
|
|
2
|
+
//
|
|
3
|
+
// Registered in `wdio.conf.ts` as `services: [[TraceLaneService, options]]`
|
|
4
|
+
// (P1 PRD §M.1). A Service — not a Reporter — because only the Service hook
|
|
5
|
+
// surface gives a live `browser` in worker hooks, which tracelane needs to
|
|
6
|
+
// inject rrweb, drain the in-page buffer, and attach CDP (ADR-0004).
|
|
7
|
+
//
|
|
8
|
+
// All real work is delegated to a single TraceLaneSession so the Service and the
|
|
9
|
+
// `traceLaneHooks` factory (hooks.ts) share one implementation.
|
|
10
|
+
import { scenarioIdentity, testIdentity } from './test-identity.js';
|
|
11
|
+
import { TraceLaneSession } from './tracelane-session.js';
|
|
12
|
+
/**
|
|
13
|
+
* The tracelane WebdriverIO Service. Implements the worker + launcher hooks from
|
|
14
|
+
* `Services.ServiceInstance`; extra Cucumber hooks (`beforeScenario` /
|
|
15
|
+
* `afterScenario`) are declared as plain methods since `@wdio/types` doesn't put
|
|
16
|
+
* them on `ServiceInstance` (they only exist at runtime under the Cucumber
|
|
17
|
+
* framework).
|
|
18
|
+
*/
|
|
19
|
+
export default class TraceLaneService {
|
|
20
|
+
session;
|
|
21
|
+
/**
|
|
22
|
+
* WDIO instantiates the Service with `(options, capabilities, config)`. We
|
|
23
|
+
* read `config.framework` so the result-shape switch (P1 PRD §A.2) picks the
|
|
24
|
+
* right normalization, and `config.outputDir`-adjacent options come from the
|
|
25
|
+
* `options` arg.
|
|
26
|
+
*/
|
|
27
|
+
constructor(options = {}, _capabilities, config) {
|
|
28
|
+
this.session = new TraceLaneSession(options, config?.framework);
|
|
29
|
+
}
|
|
30
|
+
/** Refine the framework + worker id once the session is initializing. */
|
|
31
|
+
beforeSession(_config, _capabilities, _specs, cid) {
|
|
32
|
+
this.session.setCid(cid);
|
|
33
|
+
}
|
|
34
|
+
/** Worker hook: stash the live browser + build the recorder. */
|
|
35
|
+
async before(_capabilities, _specs, browser) {
|
|
36
|
+
await this.session.onBefore(browser);
|
|
37
|
+
}
|
|
38
|
+
/** No-op in v1; present for the documented hook surface (ADR-0004). */
|
|
39
|
+
beforeSuite(_suite) { }
|
|
40
|
+
/** Mocha/Jasmine: start capture and remember the test identity. */
|
|
41
|
+
async beforeTest(test, _context) {
|
|
42
|
+
const { title, spec } = testIdentity(test);
|
|
43
|
+
await this.session.onBeforeTest(title, spec);
|
|
44
|
+
}
|
|
45
|
+
/** Mocha/Jasmine: decide + write the report from the per-test result. */
|
|
46
|
+
async afterTest(_test, _context, result) {
|
|
47
|
+
await this.session.onAfterTest(result);
|
|
48
|
+
}
|
|
49
|
+
/** Cucumber: scenario start (runtime-only hook; mirrors `beforeTest`). */
|
|
50
|
+
async beforeScenario(world, _context) {
|
|
51
|
+
const { title, spec } = scenarioIdentity(world);
|
|
52
|
+
await this.session.onBeforeTest(title, spec);
|
|
53
|
+
}
|
|
54
|
+
/** Cucumber: scenario end (runtime-only hook; mirrors `afterTest`). */
|
|
55
|
+
async afterScenario(world, result) {
|
|
56
|
+
await this.session.onAfterTest(world, result);
|
|
57
|
+
}
|
|
58
|
+
/** Re-inject the recorder after a `url(...)` navigation (ADR-0006). */
|
|
59
|
+
async beforeCommand(commandName, args) {
|
|
60
|
+
if (commandName === 'url' && typeof args[0] === 'string') {
|
|
61
|
+
await this.session.onUrl(args[0]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** No-op in v1; present for the documented hook surface (ADR-0004). */
|
|
65
|
+
afterSuite(_suite) { }
|
|
66
|
+
/** Worker teardown: stop the drain poll so no timer leaks. */
|
|
67
|
+
async after(_result, _capabilities, _specs) {
|
|
68
|
+
await this.session.onAfter();
|
|
69
|
+
}
|
|
70
|
+
/** Launcher hook: nothing to aggregate in v1 (reports are per-test files). */
|
|
71
|
+
onComplete(_exitCode, _config, _capabilities, _results) { }
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,2EAA2E;AAC3E,qEAAqE;AACrE,EAAE;AACF,iFAAiF;AACjF,gEAAgE;AAIhE,OAAO,EAAiB,gBAAgB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IAClB,OAAO,CAAmB;IAE3C;;;;;OAKG;IACH,YACE,UAA4B,EAAE,EAC9B,aAAuB,EACvB,MAA+B;QAE/B,IAAI,CAAC,OAAO,GAAG,IAAI,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAClE,CAAC;IAED,yEAAyE;IACzE,aAAa,CAAC,OAAgB,EAAE,aAAsB,EAAE,MAAgB,EAAE,GAAY;QACpF,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,gEAAgE;IAChE,KAAK,CAAC,MAAM,CACV,aAAsB,EACtB,MAAgB,EAChB,OAA4B;QAE5B,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAiC,CAAC,CAAC;IACjE,CAAC;IAED,uEAAuE;IACvE,WAAW,CAAC,MAAwB,IAAS,CAAC;IAE9C,mEAAmE;IACnE,KAAK,CAAC,UAAU,CAAC,IAAqB,EAAE,QAAiB;QACvD,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,IAAgB,CAAC,CAAC;QACvD,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,yEAAyE;IACzE,KAAK,CAAC,SAAS,CACb,KAAsB,EACtB,QAAiB,EACjB,MAA6B;QAE7B,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,0EAA0E;IAC1E,KAAK,CAAC,cAAc,CAAC,KAAc,EAAE,QAAkB;QACrD,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,aAAa,CAAC,KAAc,EAAE,MAAgB;QAClD,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAChD,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,aAAa,CAAC,WAAmB,EAAE,IAAe;QACtD,IAAI,WAAW,KAAK,KAAK,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;YACzD,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,UAAU,CAAC,MAAwB,IAAS,CAAC;IAE7C,8DAA8D;IAC9D,KAAK,CAAC,KAAK,CAAC,OAAe,EAAE,aAAsB,EAAE,MAAgB;QACnE,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED,8EAA8E;IAC9E,UAAU,CACR,SAAiB,EACjB,OAAgB,EAChB,aAAsB,EACtB,QAAiB,IACV,CAAC;CACX"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** A WDIO `Test`/`Suite`-shaped object — we only read `title` / `fullTitle` / `file`. */
|
|
2
|
+
export interface TestLike {
|
|
3
|
+
title?: string;
|
|
4
|
+
fullTitle?: string;
|
|
5
|
+
file?: string;
|
|
6
|
+
}
|
|
7
|
+
/** The identity the session needs: a display title and (when known) a spec path. */
|
|
8
|
+
export interface TestIdentity {
|
|
9
|
+
title: string;
|
|
10
|
+
spec?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Prefer the fully-qualified title; fall back to the leaf title, then a placeholder. */
|
|
13
|
+
export declare function testIdentity(test: TestLike): TestIdentity;
|
|
14
|
+
/** A Cucumber `World`-shaped object — we read its pickle's `name` / `uri`. */
|
|
15
|
+
export interface WorldLike {
|
|
16
|
+
pickle?: {
|
|
17
|
+
name?: string;
|
|
18
|
+
uri?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** Pull the scenario title + feature path out of a Cucumber `World` pickle. */
|
|
22
|
+
export declare function scenarioIdentity(world: unknown): TestIdentity;
|
|
23
|
+
//# sourceMappingURL=test-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-identity.d.ts","sourceRoot":"","sources":["../src/test-identity.ts"],"names":[],"mappings":"AAIA,yFAAyF;AACzF,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,YAAY,CAGzD;AAED,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAI7D"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Shared helper for pulling a test's human title + spec path out of a WDIO
|
|
2
|
+
// `Test`/`Suite` object. Used by both the Service (service.ts) and the hook
|
|
3
|
+
// factory (hooks.ts) so the extraction lives in exactly one place.
|
|
4
|
+
/** Prefer the fully-qualified title; fall back to the leaf title, then a placeholder. */
|
|
5
|
+
export function testIdentity(test) {
|
|
6
|
+
const title = test.fullTitle ?? test.title ?? 'unknown test';
|
|
7
|
+
return test.file ? { title, spec: test.file } : { title };
|
|
8
|
+
}
|
|
9
|
+
/** Pull the scenario title + feature path out of a Cucumber `World` pickle. */
|
|
10
|
+
export function scenarioIdentity(world) {
|
|
11
|
+
const pickle = world.pickle;
|
|
12
|
+
const title = pickle?.name ?? 'unknown scenario';
|
|
13
|
+
return pickle?.uri ? { title, spec: pickle.uri } : { title };
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=test-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-identity.js","sourceRoot":"","sources":["../src/test-identity.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,4EAA4E;AAC5E,mEAAmE;AAenE,yFAAyF;AACzF,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,IAAI,cAAc,CAAC;IAC7D,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;AAC5D,CAAC;AAOD,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,MAAM,MAAM,GAAI,KAAmB,CAAC,MAAM,CAAC;IAC3C,MAAM,KAAK,GAAG,MAAM,EAAE,IAAI,IAAI,kBAAkB,CAAC;IACjD,OAAO,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type TraceLaneOptions } from './options.js';
|
|
2
|
+
import { type WdioBrowser } from './wdio-executor.js';
|
|
3
|
+
/** Minimal capability shape we read for the report header (browser name/version). */
|
|
4
|
+
interface BrowserCapabilities {
|
|
5
|
+
browserName?: string;
|
|
6
|
+
browserVersion?: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
}
|
|
9
|
+
/** The browser fields the session reads beyond the BrowserExecutor surface. */
|
|
10
|
+
type SessionBrowser = WdioBrowser & {
|
|
11
|
+
capabilities?: BrowserCapabilities;
|
|
12
|
+
sessionId?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare class TraceLaneSession {
|
|
15
|
+
private readonly options;
|
|
16
|
+
private readonly outDir;
|
|
17
|
+
private readonly captureRrweb;
|
|
18
|
+
private readonly captureNetwork;
|
|
19
|
+
private readonly captureConsole;
|
|
20
|
+
private framework;
|
|
21
|
+
private cid;
|
|
22
|
+
private browser;
|
|
23
|
+
private recorder;
|
|
24
|
+
private current;
|
|
25
|
+
private networkAttached;
|
|
26
|
+
/** Set once CDP attach has failed, to stop the per-test retry storm (#2). */
|
|
27
|
+
private networkUnavailable;
|
|
28
|
+
constructor(options?: TraceLaneOptions, framework?: string, cid?: string);
|
|
29
|
+
/** Tell the session which framework is running (from `beforeSession`/config). */
|
|
30
|
+
setFramework(framework: string | undefined): void;
|
|
31
|
+
/** Tell the session its worker capability id (for parallel-safe filenames). */
|
|
32
|
+
setCid(cid: string | undefined): void;
|
|
33
|
+
/**
|
|
34
|
+
* `before` hook: stash the live browser. The recorder is created lazily
|
|
35
|
+
* per-test in `onBeforeTest` (not here), so no recorder lingers between tests
|
|
36
|
+
* and worker teardown never drains an unstarted recorder (#5).
|
|
37
|
+
*/
|
|
38
|
+
onBefore(browser: SessionBrowser): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the console-plugin options: when `capture.console === false`, pass
|
|
41
|
+
* `{ level: [] }` so the rrweb console plugin patches no `console.*` methods
|
|
42
|
+
* and installs no error/rejection listeners — i.e. console capture is off
|
|
43
|
+
* (#1). Otherwise forward any user-supplied `consolePluginOptions` (the core
|
|
44
|
+
* recorder applies its defaults when none are given).
|
|
45
|
+
*/
|
|
46
|
+
private resolveConsolePluginOptions;
|
|
47
|
+
/** Build a fresh recorder for the live browser (one per test; #5). */
|
|
48
|
+
private createRecorderForCurrentTest;
|
|
49
|
+
/** `beforeTest`/`beforeScenario`: record the test identity and start capture. */
|
|
50
|
+
onBeforeTest(title: string, spec?: string): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Attach CDP network capture once per session. After the first failure (no
|
|
53
|
+
* devtools-service / non-Chrome / Selenium Grid), set a sentinel so every
|
|
54
|
+
* subsequent test skips the attempt instead of retrying forever, and warn
|
|
55
|
+
* exactly once (#2).
|
|
56
|
+
*/
|
|
57
|
+
private maybeAttachNetworkCapture;
|
|
58
|
+
/** WDIO `beforeCommand('url', ...)`: re-inject the recorder after navigation. */
|
|
59
|
+
onUrl(url: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* `afterTest`/`afterScenario`: normalize the framework result, ask the
|
|
62
|
+
* recorder for the report decision (ADR-0005), and write the HTML on a build.
|
|
63
|
+
* Returns the path written, or undefined when no report was produced. Drops
|
|
64
|
+
* the recorder afterward so the next test starts fresh and teardown has
|
|
65
|
+
* nothing to drain (#5).
|
|
66
|
+
*/
|
|
67
|
+
onAfterTest(resultA: unknown, resultB?: unknown): Promise<string | undefined>;
|
|
68
|
+
/**
|
|
69
|
+
* `afterSuite`/`after`: stop the active recorder so no poll timer leaks past
|
|
70
|
+
* the worker. In the normal flow `onAfterTest` already finalized + dropped the
|
|
71
|
+
* recorder, so this is a no-op; it only fires if a test started but never
|
|
72
|
+
* reached `afterTest` (#5 — never drains an unstarted recorder).
|
|
73
|
+
*/
|
|
74
|
+
onAfter(): Promise<void>;
|
|
75
|
+
/** Compose the report metadata from the current test + browser capabilities. */
|
|
76
|
+
private buildMeta;
|
|
77
|
+
}
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=tracelane-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracelane-session.d.ts","sourceRoot":"","sources":["../src/tracelane-session.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEtE,OAAO,EAAE,KAAK,WAAW,EAAsB,MAAM,oBAAoB,CAAC;AAQ1E,qFAAqF;AACrF,UAAU,mBAAmB;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+EAA+E;AAC/E,KAAK,cAAc,GAAG,WAAW,GAAG;IAClC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAU;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,GAAG,CAAqB;IAEhC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,eAAe,CAAS;IAChC,6EAA6E;IAC7E,OAAO,CAAC,kBAAkB,CAAS;gBAEvB,OAAO,GAAE,gBAAqB,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM;IAW5E,iFAAiF;IACjF,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIjD,+EAA+E;IAC/E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIrC;;;;OAIG;IACG,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD;;;;;;OAMG;IACH,OAAO,CAAC,2BAA2B;IAKnC,sEAAsE;IACtE,OAAO,CAAC,4BAA4B;IAqBpC,iFAAiF;IAC3E,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/D;;;;;OAKG;YACW,yBAAyB;IAevC,iFAAiF;IAC3E,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvC;;;;;;OAMG;IACG,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAuBnF;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B,gFAAgF;IAChF,OAAO,CAAC,SAAS;CAclB"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// The shared per-worker capture session.
|
|
2
|
+
//
|
|
3
|
+
// Both surfaces — the `TraceLaneService` class (Task 2.14) and the
|
|
4
|
+
// `traceLaneHooks` factory (Task 2.15) — delegate to one `TraceLaneSession` so
|
|
5
|
+
// the capture/inject/drain/report logic lives in exactly one place (ADR-0004:
|
|
6
|
+
// "the same logic exported as plain hook functions"). The session owns the
|
|
7
|
+
// recorder, the current-test metadata, and the report-write decision.
|
|
8
|
+
import { createRecorder, } from '@tracelane/core';
|
|
9
|
+
import { normalizeResult } from './framework-result.js';
|
|
10
|
+
import { loadRrwebBundle } from './inpage-bundle.js';
|
|
11
|
+
import { attachNetworkCapture } from './network-capture.js';
|
|
12
|
+
import { DEFAULT_OUT_DIR } from './options.js';
|
|
13
|
+
import { writeReport } from './report-writer.js';
|
|
14
|
+
import { createWdioExecutor } from './wdio-executor.js';
|
|
15
|
+
export class TraceLaneSession {
|
|
16
|
+
options;
|
|
17
|
+
outDir;
|
|
18
|
+
captureRrweb;
|
|
19
|
+
captureNetwork;
|
|
20
|
+
captureConsole;
|
|
21
|
+
framework;
|
|
22
|
+
cid;
|
|
23
|
+
browser;
|
|
24
|
+
recorder;
|
|
25
|
+
current;
|
|
26
|
+
networkAttached = false;
|
|
27
|
+
/** Set once CDP attach has failed, to stop the per-test retry storm (#2). */
|
|
28
|
+
networkUnavailable = false;
|
|
29
|
+
constructor(options = {}, framework, cid) {
|
|
30
|
+
this.options = options;
|
|
31
|
+
this.outDir = options.outDir ?? DEFAULT_OUT_DIR;
|
|
32
|
+
// Capture channels default on (P1 PRD §M.1).
|
|
33
|
+
this.captureRrweb = options.capture?.rrweb !== false;
|
|
34
|
+
this.captureNetwork = options.capture?.network !== false;
|
|
35
|
+
this.captureConsole = options.capture?.console !== false;
|
|
36
|
+
this.framework = framework;
|
|
37
|
+
this.cid = cid;
|
|
38
|
+
}
|
|
39
|
+
/** Tell the session which framework is running (from `beforeSession`/config). */
|
|
40
|
+
setFramework(framework) {
|
|
41
|
+
if (framework)
|
|
42
|
+
this.framework = framework;
|
|
43
|
+
}
|
|
44
|
+
/** Tell the session its worker capability id (for parallel-safe filenames). */
|
|
45
|
+
setCid(cid) {
|
|
46
|
+
if (cid)
|
|
47
|
+
this.cid = cid;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* `before` hook: stash the live browser. The recorder is created lazily
|
|
51
|
+
* per-test in `onBeforeTest` (not here), so no recorder lingers between tests
|
|
52
|
+
* and worker teardown never drains an unstarted recorder (#5).
|
|
53
|
+
*/
|
|
54
|
+
async onBefore(browser) {
|
|
55
|
+
this.browser = browser;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolve the console-plugin options: when `capture.console === false`, pass
|
|
59
|
+
* `{ level: [] }` so the rrweb console plugin patches no `console.*` methods
|
|
60
|
+
* and installs no error/rejection listeners — i.e. console capture is off
|
|
61
|
+
* (#1). Otherwise forward any user-supplied `consolePluginOptions` (the core
|
|
62
|
+
* recorder applies its defaults when none are given).
|
|
63
|
+
*/
|
|
64
|
+
resolveConsolePluginOptions() {
|
|
65
|
+
if (!this.captureConsole)
|
|
66
|
+
return { level: [] };
|
|
67
|
+
return this.options.consolePluginOptions;
|
|
68
|
+
}
|
|
69
|
+
/** Build a fresh recorder for the live browser (one per test; #5). */
|
|
70
|
+
createRecorderForCurrentTest(browser) {
|
|
71
|
+
const recorderOptions = {
|
|
72
|
+
executor: createWdioExecutor(browser),
|
|
73
|
+
rrwebBundle: loadRrwebBundle(),
|
|
74
|
+
};
|
|
75
|
+
if (this.options.drainIntervalMs !== undefined) {
|
|
76
|
+
recorderOptions.drainIntervalMs = this.options.drainIntervalMs;
|
|
77
|
+
}
|
|
78
|
+
if (this.options.cooldownMs !== undefined) {
|
|
79
|
+
recorderOptions.cooldownMs = this.options.cooldownMs;
|
|
80
|
+
}
|
|
81
|
+
const consolePluginOptions = this.resolveConsolePluginOptions();
|
|
82
|
+
if (consolePluginOptions !== undefined) {
|
|
83
|
+
recorderOptions.consolePluginOptions = consolePluginOptions;
|
|
84
|
+
}
|
|
85
|
+
if (this.options.mode !== undefined) {
|
|
86
|
+
recorderOptions.mode = this.options.mode;
|
|
87
|
+
}
|
|
88
|
+
return createRecorder(recorderOptions);
|
|
89
|
+
}
|
|
90
|
+
/** `beforeTest`/`beforeScenario`: record the test identity and start capture. */
|
|
91
|
+
async onBeforeTest(title, spec) {
|
|
92
|
+
this.current = spec ? { title, spec } : { title };
|
|
93
|
+
if (!this.captureRrweb || !this.browser)
|
|
94
|
+
return;
|
|
95
|
+
// Create + start a fresh recorder for this test (#5). A passing test in
|
|
96
|
+
// `failed` mode discards its buffer at finalize; no recorder survives the
|
|
97
|
+
// test, so teardown never re-drains.
|
|
98
|
+
this.recorder = this.createRecorderForCurrentTest(this.browser);
|
|
99
|
+
await this.recorder.start();
|
|
100
|
+
await this.maybeAttachNetworkCapture();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Attach CDP network capture once per session. After the first failure (no
|
|
104
|
+
* devtools-service / non-Chrome / Selenium Grid), set a sentinel so every
|
|
105
|
+
* subsequent test skips the attempt instead of retrying forever, and warn
|
|
106
|
+
* exactly once (#2).
|
|
107
|
+
*/
|
|
108
|
+
async maybeAttachNetworkCapture() {
|
|
109
|
+
if (!this.captureNetwork || this.networkAttached || this.networkUnavailable)
|
|
110
|
+
return;
|
|
111
|
+
if (!this.browser)
|
|
112
|
+
return;
|
|
113
|
+
try {
|
|
114
|
+
await attachNetworkCapture(createWdioExecutor(this.browser));
|
|
115
|
+
this.networkAttached = true;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Give up for the rest of the session and say so once.
|
|
119
|
+
this.networkUnavailable = true;
|
|
120
|
+
console.warn('[tracelane/wdio] network capture unavailable (CDP not attached); degrading to rrweb+console only.');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** WDIO `beforeCommand('url', ...)`: re-inject the recorder after navigation. */
|
|
124
|
+
async onUrl(url) {
|
|
125
|
+
if (!this.recorder)
|
|
126
|
+
return;
|
|
127
|
+
await this.recorder.reinject(url);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* `afterTest`/`afterScenario`: normalize the framework result, ask the
|
|
131
|
+
* recorder for the report decision (ADR-0005), and write the HTML on a build.
|
|
132
|
+
* Returns the path written, or undefined when no report was produced. Drops
|
|
133
|
+
* the recorder afterward so the next test starts fresh and teardown has
|
|
134
|
+
* nothing to drain (#5).
|
|
135
|
+
*/
|
|
136
|
+
async onAfterTest(resultA, resultB) {
|
|
137
|
+
const normalized = normalizeResult(this.framework, resultA, resultB);
|
|
138
|
+
const recorder = this.recorder;
|
|
139
|
+
this.recorder = undefined;
|
|
140
|
+
if (!recorder) {
|
|
141
|
+
this.current = undefined;
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
const { shouldBuildReport, events } = await recorder.finalize({
|
|
145
|
+
passed: normalized.passed,
|
|
146
|
+
});
|
|
147
|
+
if (!shouldBuildReport) {
|
|
148
|
+
this.current = undefined;
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
const meta = this.buildMeta(normalized);
|
|
152
|
+
const path = writeReport({ outDir: this.outDir, cid: this.cid, events, meta });
|
|
153
|
+
this.current = undefined;
|
|
154
|
+
return path;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* `afterSuite`/`after`: stop the active recorder so no poll timer leaks past
|
|
158
|
+
* the worker. In the normal flow `onAfterTest` already finalized + dropped the
|
|
159
|
+
* recorder, so this is a no-op; it only fires if a test started but never
|
|
160
|
+
* reached `afterTest` (#5 — never drains an unstarted recorder).
|
|
161
|
+
*/
|
|
162
|
+
async onAfter() {
|
|
163
|
+
if (this.recorder) {
|
|
164
|
+
await this.recorder.stop();
|
|
165
|
+
this.recorder = undefined;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Compose the report metadata from the current test + browser capabilities. */
|
|
169
|
+
buildMeta(normalized) {
|
|
170
|
+
const caps = this.browser?.capabilities;
|
|
171
|
+
const meta = {
|
|
172
|
+
title: this.current?.title ?? 'unknown test',
|
|
173
|
+
status: normalized.status,
|
|
174
|
+
};
|
|
175
|
+
if (this.current?.spec)
|
|
176
|
+
meta.spec = this.current.spec;
|
|
177
|
+
if (normalized.error !== undefined)
|
|
178
|
+
meta.error = normalized.error;
|
|
179
|
+
if (normalized.durationMs !== undefined)
|
|
180
|
+
meta.durationMs = normalized.durationMs;
|
|
181
|
+
if (caps?.browserName)
|
|
182
|
+
meta.browserName = caps.browserName;
|
|
183
|
+
const browserVersion = caps?.browserVersion ?? caps?.version;
|
|
184
|
+
if (browserVersion)
|
|
185
|
+
meta.browserVersion = browserVersion;
|
|
186
|
+
return meta;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=tracelane-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracelane-session.js","sourceRoot":"","sources":["../src/tracelane-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BrowserExecutor } from '@tracelane/core';
|
|
2
|
+
/**
|
|
3
|
+
* The structural subset of a WebdriverIO `browser` the adapter needs.
|
|
4
|
+
*
|
|
5
|
+
* `execute` / `executeAsync` are core commands; `cdp` and CDP-event `on(...)`
|
|
6
|
+
* are contributed by `@wdio/devtools-service` and are therefore typed loosely
|
|
7
|
+
* here (the base `WebdriverIO.Browser` type doesn't declare `cdp`, and its `on`
|
|
8
|
+
* is constrained to the Bidi event map). We accept this minimal shape so the
|
|
9
|
+
* adapter stays decoupled from the devtools-service type augmentation, which the
|
|
10
|
+
* user installs separately (P1 PRD §A.5).
|
|
11
|
+
*/
|
|
12
|
+
export interface WdioBrowser {
|
|
13
|
+
execute<T>(fn: (...args: unknown[]) => T, ...args: unknown[]): Promise<T>;
|
|
14
|
+
executeAsync<T>(fn: (...args: unknown[]) => void, ...args: unknown[]): Promise<T>;
|
|
15
|
+
cdp?(domain: string, command: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
16
|
+
on(event: string, handler: (param: never) => void): unknown;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Wrap a WDIO `browser` as a {@link BrowserExecutor}.
|
|
20
|
+
*
|
|
21
|
+
* The mapping is 1:1 for `execute` / `executeAsync` / `on`. For `cdp` we throw a
|
|
22
|
+
* clear error if the running session has no `cdp` command — that means
|
|
23
|
+
* `@wdio/devtools-service` wasn't registered (or the driver/vendor doesn't
|
|
24
|
+
* expose CDP), which is the most common network-capture misconfiguration.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createWdioExecutor(browser: WdioBrowser): BrowserExecutor;
|
|
27
|
+
//# sourceMappingURL=wdio-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wdio-executor.d.ts","sourceRoot":"","sources":["../src/wdio-executor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1E,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClF,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1F,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC;CAC7D;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,eAAe,CA6BxE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Adapts a WebdriverIO `browser` object to @tracelane/core's BrowserExecutor
|
|
2
|
+
// (ADR-0004). @tracelane/core only ever talks to a BrowserExecutor — never to a
|
|
3
|
+
// concrete framework driver — so this is the single seam between WDIO and the
|
|
4
|
+
// recorder engine.
|
|
5
|
+
/**
|
|
6
|
+
* Wrap a WDIO `browser` as a {@link BrowserExecutor}.
|
|
7
|
+
*
|
|
8
|
+
* The mapping is 1:1 for `execute` / `executeAsync` / `on`. For `cdp` we throw a
|
|
9
|
+
* clear error if the running session has no `cdp` command — that means
|
|
10
|
+
* `@wdio/devtools-service` wasn't registered (or the driver/vendor doesn't
|
|
11
|
+
* expose CDP), which is the most common network-capture misconfiguration.
|
|
12
|
+
*/
|
|
13
|
+
export function createWdioExecutor(browser) {
|
|
14
|
+
return {
|
|
15
|
+
execute(fn, ...args) {
|
|
16
|
+
return browser.execute(fn, ...args);
|
|
17
|
+
},
|
|
18
|
+
executeAsync(fn, ...args) {
|
|
19
|
+
return browser.executeAsync(fn, ...args);
|
|
20
|
+
},
|
|
21
|
+
cdp(domain, command, params) {
|
|
22
|
+
if (typeof browser.cdp !== 'function') {
|
|
23
|
+
return Promise.reject(new Error("@tracelane/wdio: browser.cdp is unavailable. Register '@wdio/devtools-service' " +
|
|
24
|
+
'in your wdio.conf services to enable CDP network capture (P1 PRD §A.5), ' +
|
|
25
|
+
'or disable it via capture.network = false.'));
|
|
26
|
+
}
|
|
27
|
+
// `params` is optional on BrowserExecutor; only forward it when provided so
|
|
28
|
+
// we match `browser.cdp(domain, command)` arity when there are no params.
|
|
29
|
+
return params === undefined
|
|
30
|
+
? browser.cdp(domain, command)
|
|
31
|
+
: browser.cdp(domain, command, params);
|
|
32
|
+
},
|
|
33
|
+
on(event, handler) {
|
|
34
|
+
// WDIO's `on` returns the browser for chaining; BrowserExecutor.on is void.
|
|
35
|
+
browser.on(event, handler);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=wdio-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wdio-executor.js","sourceRoot":"","sources":["../src/wdio-executor.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,gFAAgF;AAChF,8EAA8E;AAC9E,mBAAmB;AAqBnB;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAoB;IACrD,OAAO;QACL,OAAO,CAAI,EAA6B,EAAE,GAAG,IAAe;YAC1D,OAAO,OAAO,CAAC,OAAO,CAAI,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QACzC,CAAC;QACD,YAAY,CAAI,EAAgC,EAAE,GAAG,IAAe;YAClE,OAAO,OAAO,CAAC,YAAY,CAAI,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QAC9C,CAAC;QACD,GAAG,CAAC,MAAc,EAAE,OAAe,EAAE,MAAgC;YACnE,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;gBACtC,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,KAAK,CACP,iFAAiF;oBAC/E,0EAA0E;oBAC1E,4CAA4C,CAC/C,CACF,CAAC;YACJ,CAAC;YACD,4EAA4E;YAC5E,0EAA0E;YAC1E,OAAO,MAAM,KAAK,SAAS;gBACzB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;gBAC9B,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QACD,EAAE,CAAC,KAAa,EAAE,OAAkC;YAClD,4EAA4E;YAC5E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAiC,CAAC,CAAC;QACvD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tracelane/wdio",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "WebdriverIO Service that records rrweb sessions and writes a self-contained HTML report on failed tests. The user-facing tracelane integration for WDIO.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./hooks": {
|
|
15
|
+
"types": "./dist/hooks.d.ts",
|
|
16
|
+
"import": "./dist/hooks.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": ["dist", "NOTICE", "README.md"],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json && node scripts/build-rrweb-bundle.mjs",
|
|
22
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
23
|
+
"test": "vitest run --passWithNoTests",
|
|
24
|
+
"test:e2e": "node scripts/build-rrweb-bundle.mjs && node ./e2e/run.mjs"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@tracelane/core": "workspace:*",
|
|
28
|
+
"@tracelane/report": "workspace:*"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@wdio/types": "^9.0.0",
|
|
32
|
+
"webdriverio": "^9.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@cubenest/rrweb-core": "workspace:*",
|
|
36
|
+
"@wdio/cli": "^9.0.0",
|
|
37
|
+
"@wdio/globals": "^9.0.0",
|
|
38
|
+
"@wdio/local-runner": "^9.0.0",
|
|
39
|
+
"@wdio/mocha-framework": "^9.0.0",
|
|
40
|
+
"@wdio/spec-reporter": "^9.0.0",
|
|
41
|
+
"@wdio/types": "^9.0.0",
|
|
42
|
+
"esbuild": "^0.28.0",
|
|
43
|
+
"tsx": "^4.19.0",
|
|
44
|
+
"webdriverio": "^9.0.0"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"provenance": true
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/Cubenest/rrweb-stack.git",
|
|
53
|
+
"directory": "packages/tracelane-wdio"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/Cubenest/rrweb-stack/tree/main/packages/tracelane-wdio#readme"
|
|
56
|
+
}
|