@heal-dev/heal-playwright-tracer 1.0.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/LICENSE +661 -0
- package/README.md +245 -0
- package/dist/application/babel-playwright-tracer-plugin/index.d.ts +16 -0
- package/dist/application/babel-playwright-tracer-plugin/index.js +111 -0
- package/dist/application/heal-config/index.d.ts +8 -0
- package/dist/application/heal-config/index.js +22 -0
- package/dist/application/heal-config/registry.d.ts +37 -0
- package/dist/application/heal-config/registry.js +64 -0
- package/dist/application/heal-config/types.d.ts +73 -0
- package/dist/application/heal-config/types.js +7 -0
- package/dist/application/playwright-fixture/index.d.ts +14 -0
- package/dist/application/playwright-fixture/index.js +234 -0
- package/dist/application/trace-event-recorder-runtime/index.d.ts +15 -0
- package/dist/application/trace-event-recorder-runtime/index.js +68 -0
- package/dist/domain/code-hook-injector/service/meta-fields/enclosing-scope-labeler.d.ts +12 -0
- package/dist/domain/code-hook-injector/service/meta-fields/enclosing-scope-labeler.js +53 -0
- package/dist/domain/code-hook-injector/service/meta-fields/leading-comment-extractor.d.ts +14 -0
- package/dist/domain/code-hook-injector/service/meta-fields/leading-comment-extractor.js +20 -0
- package/dist/domain/code-hook-injector/service/meta-fields/relative-file-path.d.ts +6 -0
- package/dist/domain/code-hook-injector/service/meta-fields/relative-file-path.js +57 -0
- package/dist/domain/code-hook-injector/service/meta-fields/source-snippet-extractor.d.ts +12 -0
- package/dist/domain/code-hook-injector/service/meta-fields/source-snippet-extractor.js +28 -0
- package/dist/domain/code-hook-injector/service/playwright-import-rewriter.d.ts +10 -0
- package/dist/domain/code-hook-injector/service/playwright-import-rewriter.js +21 -0
- package/dist/domain/code-hook-injector/service/statement-analysis/cjs-artifact-detector.d.ts +14 -0
- package/dist/domain/code-hook-injector/service/statement-analysis/cjs-artifact-detector.js +30 -0
- package/dist/domain/code-hook-injector/service/statement-analysis/for-head-declaration-detector.d.ts +10 -0
- package/dist/domain/code-hook-injector/service/statement-analysis/for-head-declaration-detector.js +18 -0
- package/dist/domain/code-hook-injector/service/statement-analysis/leaf-statement-classifier.d.ts +15 -0
- package/dist/domain/code-hook-injector/service/statement-analysis/leaf-statement-classifier.js +66 -0
- package/dist/domain/code-hook-injector/service/statement-analysis/non-wrappable-statement.d.ts +11 -0
- package/dist/domain/code-hook-injector/service/statement-analysis/non-wrappable-statement.js +20 -0
- package/dist/domain/code-hook-injector/service/trace-hook/enter-meta-literal.d.ts +20 -0
- package/dist/domain/code-hook-injector/service/trace-hook/enter-meta-literal.js +34 -0
- package/dist/domain/code-hook-injector/service/trace-hook/global-trace-call.d.ts +10 -0
- package/dist/domain/code-hook-injector/service/trace-hook/global-trace-call.js +16 -0
- package/dist/domain/code-hook-injector/service/trace-hook/try-finally-wrapper.d.ts +18 -0
- package/dist/domain/code-hook-injector/service/trace-hook/try-finally-wrapper.js +44 -0
- package/dist/domain/code-hook-injector/service/trace-hook/variable-declaration-hoister.d.ts +20 -0
- package/dist/domain/code-hook-injector/service/trace-hook/variable-declaration-hoister.js +27 -0
- package/dist/domain/code-hook-injector/service/traced-file-matcher.d.ts +10 -0
- package/dist/domain/code-hook-injector/service/traced-file-matcher.js +26 -0
- package/dist/domain/trace-event-recorder/model/enter-meta.d.ts +24 -0
- package/dist/domain/trace-event-recorder/model/enter-meta.js +7 -0
- package/dist/domain/trace-event-recorder/model/global-names.d.ts +8 -0
- package/dist/domain/trace-event-recorder/model/global-names.js +20 -0
- package/dist/domain/trace-event-recorder/model/serialized-error.d.ts +16 -0
- package/dist/domain/trace-event-recorder/model/serialized-error.js +7 -0
- package/dist/domain/trace-event-recorder/model/statement-trace-schema.d.ts +171 -0
- package/dist/domain/trace-event-recorder/model/statement-trace-schema.js +33 -0
- package/dist/domain/trace-event-recorder/model/trace-schema.d.ts +114 -0
- package/dist/domain/trace-event-recorder/model/trace-schema.js +16 -0
- package/dist/domain/trace-event-recorder/port/clock.d.ts +9 -0
- package/dist/domain/trace-event-recorder/port/clock.js +7 -0
- package/dist/domain/trace-event-recorder/port/heal-trace-exporter.d.ts +11 -0
- package/dist/domain/trace-event-recorder/port/heal-trace-exporter.js +7 -0
- package/dist/domain/trace-event-recorder/port/system-info-provider.d.ts +18 -0
- package/dist/domain/trace-event-recorder/port/system-info-provider.js +7 -0
- package/dist/domain/trace-event-recorder/port/trace-event-consumer.d.ts +11 -0
- package/dist/domain/trace-event-recorder/port/trace-event-consumer.js +7 -0
- package/dist/domain/trace-event-recorder/service/active-enter-stack.d.ts +15 -0
- package/dist/domain/trace-event-recorder/service/active-enter-stack.js +34 -0
- package/dist/domain/trace-event-recorder/service/event-builders/enter-event-builder.d.ts +8 -0
- package/dist/domain/trace-event-recorder/service/event-builders/enter-event-builder.js +37 -0
- package/dist/domain/trace-event-recorder/service/event-builders/meta-event-builder.d.ts +7 -0
- package/dist/domain/trace-event-recorder/service/event-builders/meta-event-builder.js +19 -0
- package/dist/domain/trace-event-recorder/service/event-builders/ok-event-builder.d.ts +7 -0
- package/dist/domain/trace-event-recorder/service/event-builders/ok-event-builder.js +27 -0
- package/dist/domain/trace-event-recorder/service/event-builders/throw-event-builder.d.ts +7 -0
- package/dist/domain/trace-event-recorder/service/event-builders/throw-event-builder.js +23 -0
- package/dist/domain/trace-event-recorder/service/exporters/composite-heal-trace-exporter.d.ts +12 -0
- package/dist/domain/trace-event-recorder/service/exporters/composite-heal-trace-exporter.js +32 -0
- package/dist/domain/trace-event-recorder/service/index.d.ts +10 -0
- package/dist/domain/trace-event-recorder/service/index.js +15 -0
- package/dist/domain/trace-event-recorder/service/projectors/index.d.ts +6 -0
- package/dist/domain/trace-event-recorder/service/projectors/index.js +10 -0
- package/dist/domain/trace-event-recorder/service/projectors/statement-projector.d.ts +26 -0
- package/dist/domain/trace-event-recorder/service/projectors/statement-projector.js +183 -0
- package/dist/domain/trace-event-recorder/service/serializers/error-serializer.d.ts +8 -0
- package/dist/domain/trace-event-recorder/service/serializers/error-serializer.js +49 -0
- package/dist/domain/trace-event-recorder/service/serializers/variable-snapshot-serializer.d.ts +7 -0
- package/dist/domain/trace-event-recorder/service/serializers/variable-snapshot-serializer.js +102 -0
- package/dist/domain/trace-event-recorder/service/trace-event-recorder-state.d.ts +19 -0
- package/dist/domain/trace-event-recorder/service/trace-event-recorder-state.js +7 -0
- package/dist/domain/trace-event-recorder/service/trace-event-recorder.d.ts +56 -0
- package/dist/domain/trace-event-recorder/service/trace-event-recorder.js +80 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +43 -0
- package/dist/infrastructure/ndjson-exporter-adapter/index.d.ts +6 -0
- package/dist/infrastructure/ndjson-exporter-adapter/index.js +10 -0
- package/dist/infrastructure/ndjson-exporter-adapter/ndjson-exporter.d.ts +13 -0
- package/dist/infrastructure/ndjson-exporter-adapter/ndjson-exporter.js +77 -0
- package/dist/infrastructure/perf-hooks-clock-adapter/index.d.ts +6 -0
- package/dist/infrastructure/perf-hooks-clock-adapter/index.js +10 -0
- package/dist/infrastructure/perf-hooks-clock-adapter/perf-hooks-clock.d.ts +11 -0
- package/dist/infrastructure/perf-hooks-clock-adapter/perf-hooks-clock.js +22 -0
- package/dist/infrastructure/playwright-locator-screenshot-adapter/assertion-wrapper.d.ts +6 -0
- package/dist/infrastructure/playwright-locator-screenshot-adapter/assertion-wrapper.js +109 -0
- package/dist/infrastructure/playwright-locator-screenshot-adapter/index.d.ts +9 -0
- package/dist/infrastructure/playwright-locator-screenshot-adapter/index.js +21 -0
- package/dist/infrastructure/playwright-locator-screenshot-adapter/locator-patch.d.ts +11 -0
- package/dist/infrastructure/playwright-locator-screenshot-adapter/locator-patch.js +79 -0
- package/dist/infrastructure/playwright-locator-screenshot-adapter/overlay-helpers.d.ts +15 -0
- package/dist/infrastructure/playwright-locator-screenshot-adapter/overlay-helpers.js +33 -0
- package/dist/infrastructure/playwright-locator-screenshot-adapter/screenshot-capture-session.d.ts +26 -0
- package/dist/infrastructure/playwright-locator-screenshot-adapter/screenshot-capture-session.js +125 -0
- package/dist/infrastructure/playwright-step-tracking-adapter/index.d.ts +7 -0
- package/dist/infrastructure/playwright-step-tracking-adapter/index.js +10 -0
- package/dist/infrastructure/playwright-step-tracking-adapter/playwright-step-tracking-adapter.d.ts +14 -0
- package/dist/infrastructure/playwright-step-tracking-adapter/playwright-step-tracking-adapter.js +51 -0
- package/dist/infrastructure/playwright-test-context-adapter/heal-tag-prefix.d.ts +25 -0
- package/dist/infrastructure/playwright-test-context-adapter/heal-tag-prefix.js +28 -0
- package/dist/infrastructure/playwright-test-context-adapter/index.d.ts +8 -0
- package/dist/infrastructure/playwright-test-context-adapter/index.js +12 -0
- package/dist/infrastructure/playwright-test-context-adapter/playwright-test-context-adapter.d.ts +19 -0
- package/dist/infrastructure/playwright-test-context-adapter/playwright-test-context-adapter.js +43 -0
- package/dist/infrastructure/stdout-capture-adapter/index.d.ts +7 -0
- package/dist/infrastructure/stdout-capture-adapter/index.js +10 -0
- package/dist/infrastructure/stdout-capture-adapter/stdout-capture-session.d.ts +20 -0
- package/dist/infrastructure/stdout-capture-adapter/stdout-capture-session.js +47 -0
- package/dist/infrastructure/system-info-adapter/index.d.ts +6 -0
- package/dist/infrastructure/system-info-adapter/index.js +10 -0
- package/dist/infrastructure/system-info-adapter/system-info-adapter.d.ts +12 -0
- package/dist/infrastructure/system-info-adapter/system-info-adapter.js +83 -0
- package/package.json +95 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright: (c) Myia SAS 2026.
|
|
4
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
5
|
+
* Please see the LICENSE file at the root of this repository
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.NdjsonExporter = void 0;
|
|
42
|
+
// NdjsonExporter — HealTraceExporter adapter that appends one JSON record
|
|
43
|
+
// per line to a file on disk.
|
|
44
|
+
//
|
|
45
|
+
// Designed to be crash-safe: every `write()` calls `fs.writeSync()`
|
|
46
|
+
// so the record is on disk before the call returns. If the test
|
|
47
|
+
// process is SIGKILL'd one instruction later the file still
|
|
48
|
+
// contains every record written up to that point. This is the
|
|
49
|
+
// durable record of truth; the live agent path (AgentHttpExporter) is
|
|
50
|
+
// best-effort and may lose its tail on crash.
|
|
51
|
+
//
|
|
52
|
+
// The file descriptor is opened in the constructor and closed by
|
|
53
|
+
// `close()`. Callers MUST call `close()` at test teardown; omitting
|
|
54
|
+
// it leaks the fd but does not lose data (writes were already
|
|
55
|
+
// flushed line-by-line).
|
|
56
|
+
const fs = __importStar(require("fs"));
|
|
57
|
+
class NdjsonExporter {
|
|
58
|
+
constructor(filePath) {
|
|
59
|
+
this.closed = false;
|
|
60
|
+
// O_APPEND so concurrent appenders (not expected, but cheap) are
|
|
61
|
+
// safe and so truncation on reopen never happens.
|
|
62
|
+
this.fd = fs.openSync(filePath, 'a');
|
|
63
|
+
}
|
|
64
|
+
write(record) {
|
|
65
|
+
if (this.closed)
|
|
66
|
+
return;
|
|
67
|
+
fs.writeSync(this.fd, JSON.stringify(record) + '\n');
|
|
68
|
+
}
|
|
69
|
+
close() {
|
|
70
|
+
if (this.closed)
|
|
71
|
+
return Promise.resolve();
|
|
72
|
+
this.closed = true;
|
|
73
|
+
fs.closeSync(this.fd);
|
|
74
|
+
return Promise.resolve();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.NdjsonExporter = NdjsonExporter;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright: (c) Myia SAS 2026.
|
|
4
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
5
|
+
* Please see the LICENSE file at the root of this repository
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.PerfHooksClock = void 0;
|
|
9
|
+
var perf_hooks_clock_1 = require("./perf-hooks-clock");
|
|
10
|
+
Object.defineProperty(exports, "PerfHooksClock", { enumerable: true, get: function () { return perf_hooks_clock_1.PerfHooksClock; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright: (c) Myia SAS 2026.
|
|
3
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
4
|
+
* Please see the LICENSE file at the root of this repository
|
|
5
|
+
*/
|
|
6
|
+
import type { Clock } from '../../domain/trace-event-recorder/port/clock';
|
|
7
|
+
export declare class PerfHooksClock implements Clock {
|
|
8
|
+
private readonly performance;
|
|
9
|
+
now(): number;
|
|
10
|
+
wallNow(): number;
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright: (c) Myia SAS 2026.
|
|
4
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
5
|
+
* Please see the LICENSE file at the root of this repository
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.PerfHooksClock = void 0;
|
|
9
|
+
class PerfHooksClock {
|
|
10
|
+
constructor() {
|
|
11
|
+
// Lazy require so pulling this adapter in doesn't eagerly load
|
|
12
|
+
// perf_hooks in environments that don't need it.
|
|
13
|
+
this.performance = require('perf_hooks').performance;
|
|
14
|
+
}
|
|
15
|
+
now() {
|
|
16
|
+
return this.performance.now();
|
|
17
|
+
}
|
|
18
|
+
wallNow() {
|
|
19
|
+
return Date.now();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.PerfHooksClock = PerfHooksClock;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright: (c) Myia SAS 2026.
|
|
3
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
4
|
+
* Please see the LICENSE file at the root of this repository
|
|
5
|
+
*/
|
|
6
|
+
export declare function wrapExpect<E extends (...args: unknown[]) => unknown>(origExpect: E): E;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright: (c) Myia SAS 2026.
|
|
4
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
5
|
+
* Please see the LICENSE file at the root of this repository
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.wrapExpect = wrapExpect;
|
|
9
|
+
const overlay_helpers_1 = require("./overlay-helpers");
|
|
10
|
+
const locator_patch_1 = require("./locator-patch");
|
|
11
|
+
// Duck-type: a Playwright Locator always has both `.boundingBox` and
|
|
12
|
+
// `.page` as functions. FrameLocator / ElementHandle / Page won't match.
|
|
13
|
+
// We don't use `instanceof` because the Locator class is created lazily
|
|
14
|
+
// inside Playwright and isn't reliably exported for runtime checks.
|
|
15
|
+
function isLocator(target) {
|
|
16
|
+
if (!target || typeof target !== 'object')
|
|
17
|
+
return false;
|
|
18
|
+
const obj = target;
|
|
19
|
+
return typeof obj.boundingBox === 'function' && typeof obj.page === 'function';
|
|
20
|
+
}
|
|
21
|
+
// Assertion methods in Playwright all start with `to` (toBeVisible,
|
|
22
|
+
// toHaveText, …). Every other property we see on the assertion
|
|
23
|
+
// instance — `not`, symbol keys, internal fields — we leave alone.
|
|
24
|
+
function isAssertionMethodName(prop) {
|
|
25
|
+
return typeof prop === 'string' && prop.startsWith('to');
|
|
26
|
+
}
|
|
27
|
+
function wrapAssertion(assertion, locator) {
|
|
28
|
+
return new Proxy(assertion, {
|
|
29
|
+
get(target, prop, _receiver) {
|
|
30
|
+
// Read against `target` (not the proxy) so getters like `.not`
|
|
31
|
+
// see the original `this` and don't recurse through the proxy.
|
|
32
|
+
const value = Reflect.get(target, prop, target);
|
|
33
|
+
if (prop === 'not' && value && typeof value === 'object') {
|
|
34
|
+
return wrapAssertion(value, locator);
|
|
35
|
+
}
|
|
36
|
+
if (isAssertionMethodName(prop) && typeof value === 'function') {
|
|
37
|
+
return async function patchedAssertion(...args) {
|
|
38
|
+
const page = typeof locator.page === 'function'
|
|
39
|
+
? locator.page()
|
|
40
|
+
: null;
|
|
41
|
+
let drawnNodeId = null;
|
|
42
|
+
const session = (0, locator_patch_1.getActiveCaptureSession)();
|
|
43
|
+
if (page && session) {
|
|
44
|
+
try {
|
|
45
|
+
drawnNodeId = await session.captureWithHighlight(page, locator, `assert-${prop}`);
|
|
46
|
+
}
|
|
47
|
+
catch (_) {
|
|
48
|
+
// Capture is best-effort: never block the assertion.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
return await value.apply(target, args);
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
if (drawnNodeId && page) {
|
|
56
|
+
try {
|
|
57
|
+
await (0, overlay_helpers_1.removeOverlay)(page, drawnNodeId);
|
|
58
|
+
}
|
|
59
|
+
catch (_) {
|
|
60
|
+
// Page gone — nothing to clean.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Build a callable wrapping `origFn(target, ...)`-shaped functions —
|
|
71
|
+
// used for both `expect` and `expect.soft`.
|
|
72
|
+
function wrapExpectCallable(origFn) {
|
|
73
|
+
const wrapped = function wrappedExpect(target, ...rest) {
|
|
74
|
+
const assertion = origFn.call(this, target, ...rest);
|
|
75
|
+
if (isLocator(target) && assertion && typeof assertion === 'object') {
|
|
76
|
+
return wrapAssertion(assertion, target);
|
|
77
|
+
}
|
|
78
|
+
return assertion;
|
|
79
|
+
};
|
|
80
|
+
return wrapped;
|
|
81
|
+
}
|
|
82
|
+
function wrapExpect(origExpect) {
|
|
83
|
+
const wrapped = wrapExpectCallable(origExpect);
|
|
84
|
+
// Copy static properties across so `wrapped.soft`, `wrapped.poll`,
|
|
85
|
+
// `wrapped.configure`, `wrapped.extend`, etc. remain callable. `soft`
|
|
86
|
+
// shares the `(target, ...)` contract so we wrap it the same way the
|
|
87
|
+
// top-level expect is wrapped; everything else is forwarded as-is.
|
|
88
|
+
for (const key of Reflect.ownKeys(origExpect)) {
|
|
89
|
+
if (key === 'length' || key === 'name' || key === 'prototype')
|
|
90
|
+
continue;
|
|
91
|
+
const descriptor = Object.getOwnPropertyDescriptor(origExpect, key);
|
|
92
|
+
if (!descriptor)
|
|
93
|
+
continue;
|
|
94
|
+
if (key === 'soft' &&
|
|
95
|
+
'value' in descriptor &&
|
|
96
|
+
typeof descriptor.value === 'function') {
|
|
97
|
+
const softFn = descriptor.value;
|
|
98
|
+
descriptor.value = wrapExpectCallable(softFn);
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
Object.defineProperty(wrapped, key, descriptor);
|
|
102
|
+
}
|
|
103
|
+
catch (_) {
|
|
104
|
+
// Some descriptors may be non-configurable on the target; skip
|
|
105
|
+
// them — the caller can still reach them via the original expect.
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return wrapped;
|
|
109
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright: (c) Myia SAS 2026.
|
|
3
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
4
|
+
* Please see the LICENSE file at the root of this repository
|
|
5
|
+
*/
|
|
6
|
+
import type { Page } from 'playwright';
|
|
7
|
+
export declare function startLocatorScreenshotCapture(samplePage: Page, outputDir: string, onScreenshotWritten: (filename: string) => void): () => void;
|
|
8
|
+
export { wrapExpect } from './assertion-wrapper';
|
|
9
|
+
export { ScreenshotCaptureSession } from './screenshot-capture-session';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright: (c) Myia SAS 2026.
|
|
4
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
5
|
+
* Please see the LICENSE file at the root of this repository
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.ScreenshotCaptureSession = exports.wrapExpect = void 0;
|
|
9
|
+
exports.startLocatorScreenshotCapture = startLocatorScreenshotCapture;
|
|
10
|
+
const locator_patch_1 = require("./locator-patch");
|
|
11
|
+
const screenshot_capture_session_1 = require("./screenshot-capture-session");
|
|
12
|
+
function startLocatorScreenshotCapture(samplePage, outputDir, onScreenshotWritten) {
|
|
13
|
+
(0, locator_patch_1.ensureLocatorPrototypePatched)(samplePage);
|
|
14
|
+
const session = new screenshot_capture_session_1.ScreenshotCaptureSession(outputDir, onScreenshotWritten);
|
|
15
|
+
(0, locator_patch_1.setActiveCaptureSession)(session);
|
|
16
|
+
return () => (0, locator_patch_1.setActiveCaptureSession)(null);
|
|
17
|
+
}
|
|
18
|
+
var assertion_wrapper_1 = require("./assertion-wrapper");
|
|
19
|
+
Object.defineProperty(exports, "wrapExpect", { enumerable: true, get: function () { return assertion_wrapper_1.wrapExpect; } });
|
|
20
|
+
var screenshot_capture_session_2 = require("./screenshot-capture-session");
|
|
21
|
+
Object.defineProperty(exports, "ScreenshotCaptureSession", { enumerable: true, get: function () { return screenshot_capture_session_2.ScreenshotCaptureSession; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright: (c) Myia SAS 2026.
|
|
3
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
4
|
+
* Please see the LICENSE file at the root of this repository
|
|
5
|
+
*/
|
|
6
|
+
import type { Page } from 'playwright';
|
|
7
|
+
import type { ScreenshotCaptureSession } from './screenshot-capture-session';
|
|
8
|
+
export declare const HIGHLIGHTED_LOCATOR_ACTIONS: string[];
|
|
9
|
+
export declare function setActiveCaptureSession(session: ScreenshotCaptureSession | null): void;
|
|
10
|
+
export declare function getActiveCaptureSession(): ScreenshotCaptureSession | null;
|
|
11
|
+
export declare function ensureLocatorPrototypePatched(samplePage: Page): void;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright: (c) Myia SAS 2026.
|
|
4
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
5
|
+
* Please see the LICENSE file at the root of this repository
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.HIGHLIGHTED_LOCATOR_ACTIONS = void 0;
|
|
9
|
+
exports.setActiveCaptureSession = setActiveCaptureSession;
|
|
10
|
+
exports.getActiveCaptureSession = getActiveCaptureSession;
|
|
11
|
+
exports.ensureLocatorPrototypePatched = ensureLocatorPrototypePatched;
|
|
12
|
+
const overlay_helpers_1 = require("./overlay-helpers");
|
|
13
|
+
// Locator action methods that will be highlighted and screenshotted.
|
|
14
|
+
// User-facing actions only — not queries, not waits, not assertions.
|
|
15
|
+
// If Playwright adds new action methods, append them here.
|
|
16
|
+
exports.HIGHLIGHTED_LOCATOR_ACTIONS = [
|
|
17
|
+
'click',
|
|
18
|
+
'dblclick',
|
|
19
|
+
'tap',
|
|
20
|
+
'fill',
|
|
21
|
+
'clear',
|
|
22
|
+
'hover',
|
|
23
|
+
'press',
|
|
24
|
+
'pressSequentially',
|
|
25
|
+
'type',
|
|
26
|
+
'check',
|
|
27
|
+
'uncheck',
|
|
28
|
+
'setChecked',
|
|
29
|
+
'focus',
|
|
30
|
+
'blur',
|
|
31
|
+
'selectOption',
|
|
32
|
+
'selectText',
|
|
33
|
+
'setInputFiles',
|
|
34
|
+
'dragTo',
|
|
35
|
+
'scrollIntoViewIfNeeded',
|
|
36
|
+
];
|
|
37
|
+
const PATCHED = Symbol.for('heal-playwright-tracer.locator-patched');
|
|
38
|
+
// Active-session registry. The fixture sets this at test start and
|
|
39
|
+
// clears it at teardown; the patched methods read it on every call.
|
|
40
|
+
let activeSession = null;
|
|
41
|
+
function setActiveCaptureSession(session) {
|
|
42
|
+
activeSession = session;
|
|
43
|
+
}
|
|
44
|
+
function getActiveCaptureSession() {
|
|
45
|
+
return activeSession;
|
|
46
|
+
}
|
|
47
|
+
// Idempotent proto patch. Call once per process with any Page-like
|
|
48
|
+
// object that produces a Locator via `locator('body')` — we grab the
|
|
49
|
+
// prototype from it and overwrite every action method.
|
|
50
|
+
function ensureLocatorPrototypePatched(samplePage) {
|
|
51
|
+
const proto = Object.getPrototypeOf(samplePage.locator('body'));
|
|
52
|
+
if (!proto || proto[PATCHED])
|
|
53
|
+
return;
|
|
54
|
+
proto[PATCHED] = true;
|
|
55
|
+
for (const name of exports.HIGHLIGHTED_LOCATOR_ACTIONS) {
|
|
56
|
+
const orig = proto[name];
|
|
57
|
+
if (typeof orig !== 'function')
|
|
58
|
+
continue;
|
|
59
|
+
proto[name] = async function patched(...args) {
|
|
60
|
+
const self = this;
|
|
61
|
+
const pg = typeof self.page === 'function' ? self.page() : null;
|
|
62
|
+
const session = activeSession;
|
|
63
|
+
const drawnNodeId = pg && session ? await session.captureWithHighlight(pg, self, name) : null;
|
|
64
|
+
try {
|
|
65
|
+
return await orig.apply(self, args);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
if (drawnNodeId && pg) {
|
|
69
|
+
try {
|
|
70
|
+
await (0, overlay_helpers_1.removeOverlay)(pg, drawnNodeId);
|
|
71
|
+
}
|
|
72
|
+
catch (_) {
|
|
73
|
+
// Page closed / navigated / element detached — nothing to clean.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright: (c) Myia SAS 2026.
|
|
3
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
4
|
+
* Please see the LICENSE file at the root of this repository
|
|
5
|
+
*/
|
|
6
|
+
import type { Page } from 'playwright';
|
|
7
|
+
interface Box {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function drawOverlay(page: Page, nodeId: string, box: Box): Promise<void>;
|
|
14
|
+
export declare function removeOverlay(page: Page, nodeId: string): Promise<void>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright: (c) Myia SAS 2026.
|
|
4
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
5
|
+
* Please see the LICENSE file at the root of this repository
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.drawOverlay = drawOverlay;
|
|
9
|
+
exports.removeOverlay = removeOverlay;
|
|
10
|
+
const DRAW_BORDER_SIZE = 4;
|
|
11
|
+
const DRAW_COLOR = 'magenta';
|
|
12
|
+
async function drawOverlay(page, nodeId, box) {
|
|
13
|
+
await page.evaluate((params) => {
|
|
14
|
+
const node = document.createElement('canvas');
|
|
15
|
+
node.id = params.nodeId;
|
|
16
|
+
node.style.pointerEvents = 'none';
|
|
17
|
+
node.style.position = 'absolute';
|
|
18
|
+
node.style.left = params.box.x + window.scrollX + 'px';
|
|
19
|
+
node.style.top = params.box.y + window.scrollY + 'px';
|
|
20
|
+
node.style.width = params.box.width + 'px';
|
|
21
|
+
node.style.height = params.box.height + 'px';
|
|
22
|
+
node.style.border = params.borderSize + 'px solid ' + params.color;
|
|
23
|
+
node.style.zIndex = '2147483647';
|
|
24
|
+
document.body.appendChild(node);
|
|
25
|
+
}, { nodeId, box, borderSize: DRAW_BORDER_SIZE, color: DRAW_COLOR });
|
|
26
|
+
}
|
|
27
|
+
async function removeOverlay(page, nodeId) {
|
|
28
|
+
await page.evaluate((id) => {
|
|
29
|
+
const el = document.getElementById(id);
|
|
30
|
+
if (el)
|
|
31
|
+
el.remove();
|
|
32
|
+
}, nodeId);
|
|
33
|
+
}
|
package/dist/infrastructure/playwright-locator-screenshot-adapter/screenshot-capture-session.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright: (c) Myia SAS 2026.
|
|
3
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
4
|
+
* Please see the LICENSE file at the root of this repository
|
|
5
|
+
*/
|
|
6
|
+
import type { Page } from 'playwright';
|
|
7
|
+
interface Box {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}
|
|
13
|
+
interface CapturableTarget {
|
|
14
|
+
boundingBox?: () => Promise<Box | null>;
|
|
15
|
+
}
|
|
16
|
+
export declare class ScreenshotCaptureSession {
|
|
17
|
+
private readonly outputDir;
|
|
18
|
+
private readonly onScreenshotWritten;
|
|
19
|
+
private seq;
|
|
20
|
+
private readonly cdpSessionCache;
|
|
21
|
+
constructor(outputDir: string, onScreenshotWritten: (filename: string) => void);
|
|
22
|
+
captureWithHighlight(page: Page, target: CapturableTarget, actionName: string): Promise<string | null>;
|
|
23
|
+
private takeScreenshot;
|
|
24
|
+
private getCDPSession;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
package/dist/infrastructure/playwright-locator-screenshot-adapter/screenshot-capture-session.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright: (c) Myia SAS 2026.
|
|
4
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
5
|
+
* Please see the LICENSE file at the root of this repository
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.ScreenshotCaptureSession = void 0;
|
|
42
|
+
// ScreenshotCaptureSession — one test's worth of locator-screenshot
|
|
43
|
+
// capture: output directory, monotonic per-test sequence, CDP cache,
|
|
44
|
+
// and the callback that stamps the filename onto the active
|
|
45
|
+
// statement. The fixture creates one at test start and drops it at
|
|
46
|
+
// test teardown (via `setActiveCaptureSession`).
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const overlay_helpers_1 = require("./overlay-helpers");
|
|
50
|
+
class ScreenshotCaptureSession {
|
|
51
|
+
constructor(outputDir, onScreenshotWritten) {
|
|
52
|
+
this.outputDir = outputDir;
|
|
53
|
+
this.onScreenshotWritten = onScreenshotWritten;
|
|
54
|
+
this.seq = 0;
|
|
55
|
+
// Per-page CDP session cache. `page.screenshot()` in headed mode forces
|
|
56
|
+
// the configured viewport via `Emulation.setDeviceMetricsOverride`,
|
|
57
|
+
// which visibly resizes the OS window on every capture. Going through
|
|
58
|
+
// a raw CDP `Page.captureScreenshot` skips that override entirely. On
|
|
59
|
+
// Firefox/WebKit `newCDPSession` throws; we cache `null` so we stop
|
|
60
|
+
// retrying and fall back to `page.screenshot`.
|
|
61
|
+
this.cdpSessionCache = new WeakMap();
|
|
62
|
+
}
|
|
63
|
+
// Draw → screenshot → stamp. Returns the overlay node id if one is
|
|
64
|
+
// drawn (caller must call `removeOverlay` in finally), or null if
|
|
65
|
+
// nothing was drawn.
|
|
66
|
+
async captureWithHighlight(page, target, actionName) {
|
|
67
|
+
if (typeof target.boundingBox !== 'function')
|
|
68
|
+
return null;
|
|
69
|
+
let box;
|
|
70
|
+
try {
|
|
71
|
+
box = await target.boundingBox();
|
|
72
|
+
}
|
|
73
|
+
catch (_) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
if (!box)
|
|
77
|
+
return null;
|
|
78
|
+
const seq = ++this.seq;
|
|
79
|
+
const nodeId = `_heal_draw_area_tracer_${seq}`;
|
|
80
|
+
try {
|
|
81
|
+
await (0, overlay_helpers_1.drawOverlay)(page, nodeId, box);
|
|
82
|
+
}
|
|
83
|
+
catch (_) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const filename = `highlight-${seq}-${actionName}.png`;
|
|
88
|
+
const fullPath = path.join(this.outputDir, filename);
|
|
89
|
+
await this.takeScreenshot(page, fullPath);
|
|
90
|
+
this.onScreenshotWritten(filename);
|
|
91
|
+
}
|
|
92
|
+
catch (_) {
|
|
93
|
+
// Overlay is drawn; caller must still clean it up in its finally.
|
|
94
|
+
}
|
|
95
|
+
return nodeId;
|
|
96
|
+
}
|
|
97
|
+
async takeScreenshot(page, fullPath) {
|
|
98
|
+
const cdp = await this.getCDPSession(page);
|
|
99
|
+
if (cdp) {
|
|
100
|
+
const { data } = await cdp.send('Page.captureScreenshot', { format: 'png' });
|
|
101
|
+
await fs.promises.writeFile(fullPath, Buffer.from(data, 'base64'));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
await page.screenshot({ path: fullPath });
|
|
105
|
+
}
|
|
106
|
+
async getCDPSession(page) {
|
|
107
|
+
if (this.cdpSessionCache.has(page))
|
|
108
|
+
return this.cdpSessionCache.get(page) ?? null;
|
|
109
|
+
try {
|
|
110
|
+
const ctx = typeof page.context === 'function' ? page.context() : null;
|
|
111
|
+
if (!ctx || typeof ctx.newCDPSession !== 'function') {
|
|
112
|
+
this.cdpSessionCache.set(page, null);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const session = await ctx.newCDPSession(page);
|
|
116
|
+
this.cdpSessionCache.set(page, session);
|
|
117
|
+
return session;
|
|
118
|
+
}
|
|
119
|
+
catch (_) {
|
|
120
|
+
this.cdpSessionCache.set(page, null);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.ScreenshotCaptureSession = ScreenshotCaptureSession;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright: (c) Myia SAS 2026.
|
|
3
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
4
|
+
* Please see the LICENSE file at the root of this repository
|
|
5
|
+
*/
|
|
6
|
+
export { PlaywrightStepTrackingAdapter } from './playwright-step-tracking-adapter';
|
|
7
|
+
export type { StepHooks } from './playwright-step-tracking-adapter';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright: (c) Myia SAS 2026.
|
|
4
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
5
|
+
* Please see the LICENSE file at the root of this repository
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.PlaywrightStepTrackingAdapter = void 0;
|
|
9
|
+
var playwright_step_tracking_adapter_1 = require("./playwright-step-tracking-adapter");
|
|
10
|
+
Object.defineProperty(exports, "PlaywrightStepTrackingAdapter", { enumerable: true, get: function () { return playwright_step_tracking_adapter_1.PlaywrightStepTrackingAdapter; } });
|
package/dist/infrastructure/playwright-step-tracking-adapter/playwright-step-tracking-adapter.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright: (c) Myia SAS 2026.
|
|
3
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
4
|
+
* Please see the LICENSE file at the root of this repository
|
|
5
|
+
*/
|
|
6
|
+
export interface StepHooks {
|
|
7
|
+
pushStep(name: string): void;
|
|
8
|
+
popStep(): void;
|
|
9
|
+
}
|
|
10
|
+
export declare class PlaywrightStepTrackingAdapter {
|
|
11
|
+
private readonly hooks;
|
|
12
|
+
constructor(hooks: StepHooks);
|
|
13
|
+
patch(base: unknown): void;
|
|
14
|
+
}
|
package/dist/infrastructure/playwright-step-tracking-adapter/playwright-step-tracking-adapter.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright: (c) Myia SAS 2026.
|
|
4
|
+
* This file and its contents are licensed under the AGPLv3 License.
|
|
5
|
+
* Please see the LICENSE file at the root of this repository
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.PlaywrightStepTrackingAdapter = void 0;
|
|
9
|
+
// Feature: thread test.step titles onto the runtime step stack.
|
|
10
|
+
//
|
|
11
|
+
// `test.step("foo", async () => {...})` becomes:
|
|
12
|
+
// runtime.pushStep("foo");
|
|
13
|
+
// try { await body(); } finally { runtime.popStep(); }
|
|
14
|
+
//
|
|
15
|
+
// Every enter event emitted inside the body then carries `step: "foo"`
|
|
16
|
+
// and `stepPath: [...]` so the resulting trace can be grouped by step.
|
|
17
|
+
//
|
|
18
|
+
// Idempotent — the patch runs once per process and guards against
|
|
19
|
+
// double-wrapping via a Symbol marker placed on the patched target.
|
|
20
|
+
//
|
|
21
|
+
// The runtime hooks are passed in by the composition root (the
|
|
22
|
+
// fixture) — this adapter is pure Playwright plumbing and must not
|
|
23
|
+
// reach back into application or domain modules.
|
|
24
|
+
const PATCHED = Symbol.for('heal-playwright-tracer.step-tracking.patched');
|
|
25
|
+
class PlaywrightStepTrackingAdapter {
|
|
26
|
+
constructor(hooks) {
|
|
27
|
+
this.hooks = hooks;
|
|
28
|
+
}
|
|
29
|
+
patch(base) {
|
|
30
|
+
const target = base;
|
|
31
|
+
if (target[PATCHED])
|
|
32
|
+
return;
|
|
33
|
+
const origStep = target.step;
|
|
34
|
+
if (typeof origStep !== 'function')
|
|
35
|
+
return;
|
|
36
|
+
target[PATCHED] = true;
|
|
37
|
+
const hooks = this.hooks;
|
|
38
|
+
target.step = function patchedStep(title, body, opts) {
|
|
39
|
+
return origStep.call(this, title, async (...args) => {
|
|
40
|
+
hooks.pushStep(title);
|
|
41
|
+
try {
|
|
42
|
+
return await body(...args);
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
hooks.popStep();
|
|
46
|
+
}
|
|
47
|
+
}, opts);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.PlaywrightStepTrackingAdapter = PlaywrightStepTrackingAdapter;
|