@empiricalrun/playwright-utils 0.62.1 → 0.63.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/CHANGELOG.md +26 -0
- package/dist/api-worker-client.d.ts +0 -13
- package/dist/api-worker-client.d.ts.map +1 -1
- package/dist/api-worker-client.js +0 -15
- package/dist/auth/google.d.ts.map +1 -1
- package/dist/auth/google.js +0 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +0 -9
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +0 -8
- package/dist/generated/api-worker-openapi.d.ts +3031 -1420
- package/dist/generated/api-worker-openapi.d.ts.map +1 -1
- package/dist/overlay-tests/cache.spec.js +31 -17
- package/dist/overlay-tests/click.spec.js +2 -25
- package/dist/overlay-tests/dismiss-mechanics.spec.d.ts +2 -0
- package/dist/overlay-tests/dismiss-mechanics.spec.d.ts.map +1 -0
- package/dist/overlay-tests/dismiss-mechanics.spec.js +59 -0
- package/dist/overlay-tests/dropdown.spec.d.ts +2 -0
- package/dist/overlay-tests/dropdown.spec.d.ts.map +1 -0
- package/dist/overlay-tests/dropdown.spec.js +53 -0
- package/dist/overlay-tests/fixtures.d.ts +3 -1
- package/dist/overlay-tests/fixtures.d.ts.map +1 -1
- package/dist/overlay-tests/fixtures.js +15 -0
- package/dist/overlay-tests/typesafe-hit-check.spec.d.ts +2 -0
- package/dist/overlay-tests/typesafe-hit-check.spec.d.ts.map +1 -0
- package/dist/overlay-tests/typesafe-hit-check.spec.js +57 -0
- package/dist/overlay-tests/typesafe.spec.d.ts +2 -0
- package/dist/overlay-tests/typesafe.spec.d.ts.map +1 -0
- package/dist/overlay-tests/typesafe.spec.js +86 -0
- package/dist/playwright-extensions.d.ts.map +1 -1
- package/dist/playwright-extensions.js +0 -1
- package/dist/reporter/attachment-cleanup-test-reporter.d.ts +0 -6
- package/dist/reporter/attachment-cleanup-test-reporter.d.ts.map +1 -1
- package/dist/reporter/attachment-cleanup-test-reporter.js +0 -6
- package/dist/reporter/attachment-cleanup.d.ts +0 -33
- package/dist/reporter/attachment-cleanup.d.ts.map +1 -1
- package/dist/reporter/attachment-cleanup.js +2 -51
- package/dist/reporter/empirical-reporter.d.ts.map +1 -1
- package/dist/reporter/empirical-reporter.js +0 -9
- package/dist/reporter/harness.d.ts +0 -62
- package/dist/reporter/harness.d.ts.map +1 -1
- package/dist/reporter/harness.js +1 -53
- package/dist/reporter/ibr-utils.d.ts +0 -5
- package/dist/reporter/ibr-utils.d.ts.map +1 -1
- package/dist/reporter/ibr-utils.js +0 -5
- package/dist/reporter/incremental-blob-reporter.d.ts +0 -17
- package/dist/reporter/incremental-blob-reporter.d.ts.map +1 -1
- package/dist/reporter/incremental-blob-reporter.js +5 -35
- package/dist/reporter/lifecycle-events.d.ts.map +1 -1
- package/dist/reporter/lifecycle-events.js +0 -9
- package/dist/reporter/parity-utils.d.ts +0 -30
- package/dist/reporter/parity-utils.d.ts.map +1 -1
- package/dist/reporter/parity-utils.js +4 -28
- package/dist/reporter/reporter-state.d.ts +0 -12
- package/dist/reporter/reporter-state.d.ts.map +1 -1
- package/dist/reporter/reporter-state.js +0 -12
- package/dist/reporter/test-case-sync.d.ts.map +1 -1
- package/dist/reporter/test-case-sync.js +0 -12
- package/dist/reporter/uploader.d.ts.map +1 -1
- package/dist/reporter/uploader.js +0 -6
- package/dist/reporter/util.d.ts +0 -33
- package/dist/reporter/util.d.ts.map +1 -1
- package/dist/reporter/util.js +0 -37
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +1 -10
- package/dist/test/coverage.d.ts.map +1 -1
- package/dist/test/coverage.js +1 -3
- package/dist/test/expect/visual.d.ts.map +1 -1
- package/dist/test/expect/visual.js +1 -9
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +4 -21
- package/dist/test/scripts/agent-capabilities.js +2 -2
- package/dist/test/scripts/index.d.ts +2 -2
- package/dist/test/scripts/index.d.ts.map +1 -1
- package/dist/test/scripts/index.js +3 -16
- package/dist/test/scripts/pw-locator-patch/actions/click.d.ts.map +1 -0
- package/dist/test/scripts/pw-locator-patch/{highlight → actions}/click.js +8 -6
- package/dist/test/scripts/pw-locator-patch/actions/hover.d.ts.map +1 -0
- package/dist/test/scripts/pw-locator-patch/{highlight → actions}/hover.js +0 -3
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/cache.d.ts +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/cache.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/cache.js +9 -13
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.d.ts +3 -0
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.js +14 -12
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/prompt.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/prompt.js +0 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/typesafe.d.ts +38 -0
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/typesafe.d.ts.map +1 -0
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/typesafe.js +159 -0
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/utils.d.ts +3 -0
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/utils.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/utils.js +13 -5
- package/dist/test/scripts/pw-locator-patch/utils/index.d.ts +0 -1
- package/dist/test/scripts/pw-locator-patch/utils/index.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/utils/index.js +0 -18
- package/dist/test/types.d.ts +2 -5
- package/dist/test/types.d.ts.map +1 -1
- package/dist/test/video-labels.d.ts.map +1 -1
- package/dist/test/video-labels.js +0 -1
- package/dist/webhook.d.ts.map +1 -1
- package/dist/webhook.js +0 -3
- package/docs/fixtures.md +4 -5
- package/package.json +6 -6
- package/playwright.config.ts +0 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/upstream-oracle/ibr-adapter.js +0 -23
- package/upstream-oracle/run.mjs +1 -69
- package/dist/test/scripts/locator-highlights.d.ts +0 -4
- package/dist/test/scripts/locator-highlights.d.ts.map +0 -1
- package/dist/test/scripts/locator-highlights.js +0 -28
- package/dist/test/scripts/pw-locator-patch/highlight/click.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/expect.d.ts +0 -5
- package/dist/test/scripts/pw-locator-patch/highlight/expect.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/expect.js +0 -23
- package/dist/test/scripts/pw-locator-patch/highlight/hover.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/inner-text.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/inner-text.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/inner-text.js +0 -30
- package/dist/test/scripts/pw-locator-patch/highlight/input-value.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/input-value.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/input-value.js +0 -30
- package/dist/test/scripts/pw-locator-patch/highlight/is-checked.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/is-checked.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/is-checked.js +0 -30
- package/dist/test/scripts/pw-locator-patch/highlight/is-disabled.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/is-disabled.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/is-disabled.js +0 -30
- package/dist/test/scripts/pw-locator-patch/highlight/is-editable.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/is-editable.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/is-editable.js +0 -29
- package/dist/test/scripts/pw-locator-patch/highlight/text-content.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/text-content.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/text-content.js +0 -30
- /package/dist/test/scripts/pw-locator-patch/{highlight → actions}/click.d.ts +0 -0
- /package/dist/test/scripts/pw-locator-patch/{highlight → actions}/hover.d.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test/scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test/scripts/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,EAAE,aAAoB,EAAE,iBAAwB,EAAE,GAAE,cAAmB,QAYxE"}
|
|
@@ -3,26 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.injectLocatorHighlightScripts = injectLocatorHighlightScripts;
|
|
4
4
|
const logger_1 = require("../../logger");
|
|
5
5
|
const agent_capabilities_1 = require("./agent-capabilities");
|
|
6
|
-
const locator_highlights_1 = require("./locator-highlights");
|
|
7
6
|
const locator_vision_1 = require("./locator-vision");
|
|
8
|
-
|
|
9
|
-
locatorHighlighter: true,
|
|
10
|
-
visionMethods: true,
|
|
11
|
-
agentCapabilities: true,
|
|
12
|
-
};
|
|
13
|
-
function injectLocatorHighlightScripts(page, testFn, options = defaultOptions) {
|
|
14
|
-
const opts = {
|
|
15
|
-
...defaultOptions,
|
|
16
|
-
...options,
|
|
17
|
-
};
|
|
7
|
+
function injectLocatorHighlightScripts(page, testFn, { visionMethods = true, agentCapabilities = true } = {}) {
|
|
18
8
|
try {
|
|
19
|
-
if (
|
|
20
|
-
(0, locator_highlights_1.addLocatorHighlights)(page, testFn);
|
|
21
|
-
}
|
|
22
|
-
if (opts.visionMethods) {
|
|
9
|
+
if (visionMethods) {
|
|
23
10
|
(0, locator_vision_1.addLocatorVisionMethods)(page);
|
|
24
11
|
}
|
|
25
|
-
if (
|
|
12
|
+
if (agentCapabilities) {
|
|
26
13
|
(0, agent_capabilities_1.addAgentCapabilities)(page, testFn);
|
|
27
14
|
}
|
|
28
15
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"click.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/actions/click.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAWrC,wBAAgB,UAAU,CACxB,YAAY,EAAE,QAAQ,GAAG;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,EAC/C,MAAM,EAAE,MAAM,QA+Df"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.patchClick = patchClick;
|
|
4
|
+
const cua_1 = require("@empiricalrun/cua");
|
|
4
5
|
const dismiss_overlays_1 = require("../dismiss-overlays");
|
|
5
6
|
const utils_1 = require("../dismiss-overlays/utils");
|
|
6
7
|
const utils_2 = require("../utils");
|
|
7
|
-
// Static flag to track if click has been patched
|
|
8
8
|
const isClickPatched = new WeakMap();
|
|
9
9
|
function patchClick(LocatorClass, testFn) {
|
|
10
10
|
if (isClickPatched.get(LocatorClass)) {
|
|
@@ -16,6 +16,13 @@ function patchClick(LocatorClass, testFn) {
|
|
|
16
16
|
LocatorClass.prototype.click = async function (options) {
|
|
17
17
|
const stepName = `locator.click(${(0, utils_2.description)(this)})`;
|
|
18
18
|
return await testFn.step(stepName, async () => {
|
|
19
|
+
if (options?.isExecutingForOverlayDismiss &&
|
|
20
|
+
(0, dismiss_overlays_1.overlayClickMode)() === "dispatch" &&
|
|
21
|
+
!(await this.evaluate(cua_1.needsFocus, undefined, {
|
|
22
|
+
timeout: options.timeout,
|
|
23
|
+
}))) {
|
|
24
|
+
return await this.dispatchEvent("click");
|
|
25
|
+
}
|
|
19
26
|
let overlayAttemptsRemaining = 2;
|
|
20
27
|
let originalError;
|
|
21
28
|
const overlayDismiss = new dismiss_overlays_1.OverlayDismissAgent(this._frame._page);
|
|
@@ -23,19 +30,15 @@ function patchClick(LocatorClass, testFn) {
|
|
|
23
30
|
overlayAttemptsRemaining--;
|
|
24
31
|
try {
|
|
25
32
|
let result = await originalClick.apply(this, [options]);
|
|
26
|
-
// If click is successful, we can save overlay dismissal code to cache and return
|
|
27
33
|
await overlayDismiss.saveToCache();
|
|
28
34
|
return result;
|
|
29
35
|
}
|
|
30
36
|
catch (error) {
|
|
31
37
|
originalError = error;
|
|
32
38
|
if (originalError instanceof Error) {
|
|
33
|
-
// Overwrite the stack trace to show the original click (remove patch)
|
|
34
39
|
Error.captureStackTrace(originalError, LocatorClass.prototype.click);
|
|
35
40
|
}
|
|
36
41
|
if (options?.isExecutingForOverlayDismiss) {
|
|
37
|
-
// While executing dismiss code from cache, we attach this custom option
|
|
38
|
-
// which ensures that the overlay dismissal will not trigger recursively
|
|
39
42
|
throw originalError;
|
|
40
43
|
}
|
|
41
44
|
if (!(0, dismiss_overlays_1.isErrorSupported)(originalError.message)) {
|
|
@@ -45,7 +48,6 @@ function patchClick(LocatorClass, testFn) {
|
|
|
45
48
|
throw originalError;
|
|
46
49
|
}
|
|
47
50
|
await overlayDismiss.run(originalError, reporter, testFn);
|
|
48
|
-
// Dismissal attempt finished (implicitly successful if no error thrown)
|
|
49
51
|
reporter(`Overlay dismissal attempted, retrying original action.`);
|
|
50
52
|
}
|
|
51
53
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/actions/hover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAOrC,wBAAgB,UAAU,CACxB,YAAY,EAAE,QAAQ,GAAG;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,EAC/C,MAAM,EAAE,MAAM,QAgDf"}
|
|
@@ -21,7 +21,6 @@ function patchHover(LocatorClass, testFn) {
|
|
|
21
21
|
overlayAttemptsRemaining--;
|
|
22
22
|
try {
|
|
23
23
|
let result = await originalHover.apply(this, [options]);
|
|
24
|
-
// If hover is successful, we can save overlay dismissal code to cache and return
|
|
25
24
|
await overlayDismiss.saveToCache();
|
|
26
25
|
return result;
|
|
27
26
|
}
|
|
@@ -31,8 +30,6 @@ function patchHover(LocatorClass, testFn) {
|
|
|
31
30
|
Error.captureStackTrace(originalError, LocatorClass.prototype.hover);
|
|
32
31
|
}
|
|
33
32
|
if (options?.isExecutingForOverlayDismiss) {
|
|
34
|
-
// While executing dismiss code from cache, we attach this custom option
|
|
35
|
-
// which ensures that the overlay dismissal will not trigger recursively
|
|
36
33
|
throw originalError;
|
|
37
34
|
}
|
|
38
35
|
if (!(0, dismiss_overlays_1.isErrorSupported)(originalError.message)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAM7C,
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAM7C,wBAAgB,SAAS,WAKxB;AAMD,wBAAsB,cAAc,CAAC,EACnC,GAAG,EACH,IAAI,EACJ,IAAI,GACL,EAAE;IACD,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd,iBAgCA;AAED,wBAAsB,gBAAgB,CAAC,EACrC,IAAI,EACJ,GAAG,EACH,IAAI,GACL,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAiChC;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,UAY5C"}
|
|
@@ -3,24 +3,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.cacheFile = cacheFile;
|
|
7
7
|
exports.setCodeToCache = setCodeToCache;
|
|
8
8
|
exports.executeFromCache = executeFromCache;
|
|
9
9
|
exports.addOverlayOption = addOverlayOption;
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
|
-
|
|
12
|
+
function cacheFile() {
|
|
13
|
+
return (process.env.EMPIRICALRUN_OVERLAY_CACHE_FILE ??
|
|
14
|
+
path_1.default.join(process.cwd(), ".empiricalrun", "overlay-cache.json"));
|
|
15
|
+
}
|
|
13
16
|
function normalizeString(str) {
|
|
14
|
-
// Replace all whitespace sequences with a single space
|
|
15
17
|
return str.replace(/\s+/g, " ").trim();
|
|
16
18
|
}
|
|
17
19
|
async function setCodeToCache({ dom, text, code, }) {
|
|
18
20
|
if (!dom && !text) {
|
|
19
|
-
// At least one of the two is required
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
22
23
|
if (process.env.TEST_GEN_TOKEN) {
|
|
23
|
-
// No caching for test gen sessions (we don't want steps to get committed)
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
const obj = {
|
|
@@ -35,7 +35,7 @@ async function setCodeToCache({ dom, text, code, }) {
|
|
|
35
35
|
data: [],
|
|
36
36
|
};
|
|
37
37
|
try {
|
|
38
|
-
cache = JSON.parse(fs_1.default.readFileSync(
|
|
38
|
+
cache = JSON.parse(fs_1.default.readFileSync(cacheFile(), "utf8"));
|
|
39
39
|
cache.data.push(obj);
|
|
40
40
|
}
|
|
41
41
|
catch {
|
|
@@ -44,19 +44,18 @@ async function setCodeToCache({ dom, text, code, }) {
|
|
|
44
44
|
data: [obj],
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
const dir = path_1.default.dirname(
|
|
47
|
+
const dir = path_1.default.dirname(cacheFile());
|
|
48
48
|
if (!fs_1.default.existsSync(dir)) {
|
|
49
49
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
50
50
|
}
|
|
51
|
-
fs_1.default.writeFileSync(
|
|
51
|
+
fs_1.default.writeFileSync(cacheFile(), JSON.stringify(cache, null, 2));
|
|
52
52
|
}
|
|
53
53
|
async function executeFromCache({ page, dom, text, }) {
|
|
54
54
|
if (!dom && !text) {
|
|
55
|
-
// At least one of the two is required
|
|
56
55
|
return { success: false };
|
|
57
56
|
}
|
|
58
57
|
try {
|
|
59
|
-
const cache = JSON.parse(fs_1.default.readFileSync(
|
|
58
|
+
const cache = JSON.parse(fs_1.default.readFileSync(cacheFile(), "utf8"));
|
|
60
59
|
const match = cache.data.find((c) => {
|
|
61
60
|
return (c.element.dom === (dom || "") &&
|
|
62
61
|
c.element.textContent === normalizeString(text || ""));
|
|
@@ -66,7 +65,6 @@ async function executeFromCache({ page, dom, text, }) {
|
|
|
66
65
|
}
|
|
67
66
|
try {
|
|
68
67
|
console.log(`Executing from cache: "${match.code}" for element: ${dom}`);
|
|
69
|
-
// Ref: https://davidwalsh.name/async-function-class
|
|
70
68
|
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
|
|
71
69
|
const codeToExecute = addOverlayOption(match.code);
|
|
72
70
|
const exec = new AsyncFunction("page", codeToExecute);
|
|
@@ -85,12 +83,10 @@ async function executeFromCache({ page, dom, text, }) {
|
|
|
85
83
|
function addOverlayOption(code) {
|
|
86
84
|
return code.replace(/\.(click|hover)(\s*\(\s*)(\{[^}]*\})?(\s*\))(;)?/g, (match, action, openParen, options, closeParen, semicolon) => {
|
|
87
85
|
if (options) {
|
|
88
|
-
// If options exist, add isExecutingForOverlayDismiss to them
|
|
89
86
|
const optionsObj = options.replace(/\s*}\s*$/, "");
|
|
90
87
|
return `.${action}${openParen}${optionsObj}, isExecutingForOverlayDismiss: true }${closeParen}${semicolon || ""}`;
|
|
91
88
|
}
|
|
92
89
|
else {
|
|
93
|
-
// If no options, add them with isExecutingForOverlayDismiss
|
|
94
90
|
return `.${action}${openParen}{ isExecutingForOverlayDismiss: true }${closeParen}${semicolon || ""}`;
|
|
95
91
|
}
|
|
96
92
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { type ClickMode } from "@empiricalrun/cua";
|
|
1
2
|
import { Page } from "@playwright/test";
|
|
2
3
|
import type { TestFn } from "../../types";
|
|
3
4
|
import type { OverlayElement } from "./types";
|
|
5
|
+
export declare function overlayClickMode(): ClickMode;
|
|
4
6
|
export declare function isErrorSupported(errorMessage: string | undefined): boolean;
|
|
5
7
|
export declare class OverlayDismissAgent {
|
|
6
8
|
private readonly page;
|
|
@@ -8,6 +10,7 @@ export declare class OverlayDismissAgent {
|
|
|
8
10
|
constructor(page: Page);
|
|
9
11
|
saveToCache(): Promise<void>;
|
|
10
12
|
run(originalError: any, reporter: (description: string) => void, testFn: TestFn): Promise<void>;
|
|
13
|
+
private dismiss;
|
|
11
14
|
private runAgent;
|
|
12
15
|
}
|
|
13
16
|
export declare function extractInterceptingElement(errorMessage: string): OverlayElement | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAEf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAIxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAW9C,wBAAgB,gBAAgB,IAAI,SAAS,CAI5C;AAED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,WAKhE;AAED,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,iBAAiB,CAIhB;IAET,YAA6B,IAAI,EAAE,IAAI,EAAI;IAErC,WAAW,kBAIhB;IAEK,GAAG,CACP,aAAa,EAAE,GAAG,EAClB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAUf;YAEa,OAAO;YAwDP,QAAQ;CAkCvB;AAED,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,GACnB,cAAc,GAAG,SAAS,CAiC5B"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OverlayDismissAgent = void 0;
|
|
4
|
+
exports.overlayClickMode = overlayClickMode;
|
|
4
5
|
exports.isErrorSupported = isErrorSupported;
|
|
5
6
|
exports.extractInterceptingElement = extractInterceptingElement;
|
|
6
7
|
const cua_1 = require("@empiricalrun/cua");
|
|
@@ -10,6 +11,11 @@ const cache_1 = require("./cache");
|
|
|
10
11
|
const prompt_1 = require("./prompt");
|
|
11
12
|
const utils_1 = require("./utils");
|
|
12
13
|
const ERROR_SUBSTRING_INTERCEPTION = "intercepts pointer events";
|
|
14
|
+
function overlayClickMode() {
|
|
15
|
+
return process.env.EMPIRICALRUN_OVERLAY_CLICK_MODE === "dispatch"
|
|
16
|
+
? "dispatch"
|
|
17
|
+
: "mouse";
|
|
18
|
+
}
|
|
13
19
|
function isErrorSupported(errorMessage) {
|
|
14
20
|
if (!errorMessage) {
|
|
15
21
|
return false;
|
|
@@ -28,14 +34,15 @@ class OverlayDismissAgent {
|
|
|
28
34
|
}
|
|
29
35
|
}
|
|
30
36
|
async run(originalError, reporter, testFn) {
|
|
31
|
-
const startTime = Date.now();
|
|
32
37
|
let element;
|
|
33
38
|
try {
|
|
34
39
|
element = extractInterceptingElement(originalError.message);
|
|
35
40
|
}
|
|
36
|
-
catch {
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
catch { }
|
|
42
|
+
await testFn.step((0, utils_1.dismissOverlayStepTitle)(element), () => this.dismiss(element, originalError, reporter, testFn), { box: true });
|
|
43
|
+
}
|
|
44
|
+
async dismiss(element, originalError, reporter, testFn) {
|
|
45
|
+
const startTime = Date.now();
|
|
39
46
|
const description = (0, utils_1.overlayDescription)(element) ?? "unknown";
|
|
40
47
|
const testName = testFn.info().title;
|
|
41
48
|
reporter(`Attempting to auto-dismiss overlay: ${description}`);
|
|
@@ -85,7 +92,7 @@ class OverlayDismissAgent {
|
|
|
85
92
|
async runAgent(element, text, testFn) {
|
|
86
93
|
const task = await (0, prompt_1.getTask)(this.page, element, text);
|
|
87
94
|
const trace = (0, utils_1.traceThis)(task, testFn);
|
|
88
|
-
const result = await (0, cua_1.createTestUsingComputerUseAgent)({
|
|
95
|
+
const result = await testFn.step(utils_1.DISMISSAL_AGENT_STEP, () => (0, cua_1.createTestUsingComputerUseAgent)({
|
|
89
96
|
page: this.page,
|
|
90
97
|
task,
|
|
91
98
|
trace,
|
|
@@ -96,15 +103,10 @@ class OverlayDismissAgent {
|
|
|
96
103
|
// only once in a particular test run.
|
|
97
104
|
// Ran into this here: https://empirical.run/quizizz-tests/sessions/2825
|
|
98
105
|
prefersElementFromPointCodegen: true,
|
|
99
|
-
// Overlay dismissal doesn't need page navigation — the overlay is
|
|
100
|
-
// already on the current page. Including page_goto causes the model
|
|
101
|
-
// to waste turns trying to navigate instead of clicking dismiss.
|
|
102
106
|
includePageGotoTool: false,
|
|
103
|
-
// Route the model call through api-worker (Cloudflare AI Gateway) on the
|
|
104
|
-
// project API key, so test-run containers carry no OpenAI key. Undefined
|
|
105
|
-
// on a dev machine or in a pi sandbox — see the helper.
|
|
106
107
|
openAIClientConfig: (0, api_worker_client_1.llmProxyOpenAIConfig)(),
|
|
107
|
-
|
|
108
|
+
clickMode: overlayClickMode(),
|
|
109
|
+
}));
|
|
108
110
|
this.cachingCandidates.push({
|
|
109
111
|
dom: element?.interceptor,
|
|
110
112
|
text,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC,wBAAsB,OAAO,CAC3B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,cAAc,GAAG,SAAS,EACnC,IAAI,EAAE,MAAM,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC,wBAAsB,OAAO,CAC3B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,cAAc,GAAG,SAAS,EACnC,IAAI,EAAE,MAAM,GAAG,SAAS,mBAqCzB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ElementHandle, Page } from "playwright-core";
|
|
2
|
+
type Box = {
|
|
3
|
+
top: number;
|
|
4
|
+
bottom: number;
|
|
5
|
+
left: number;
|
|
6
|
+
right: number;
|
|
7
|
+
};
|
|
8
|
+
export type TypeSafeDismissStep = {
|
|
9
|
+
popupProbability: number;
|
|
10
|
+
box: Box;
|
|
11
|
+
boxConfidence: number;
|
|
12
|
+
clicked: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
} | undefined;
|
|
16
|
+
hitCheck: HitCheck | undefined;
|
|
17
|
+
latencyMs: number;
|
|
18
|
+
inputTokens: number;
|
|
19
|
+
outputTokens: number;
|
|
20
|
+
};
|
|
21
|
+
type Point = {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
};
|
|
25
|
+
type HitCheck = "ok" | "no_blocker" | "outside_overlay" | "not_clickable";
|
|
26
|
+
export type TypeSafeStopReason = "popup_gone" | "low_box_confidence" | Exclude<HitCheck, "ok"> | "no_visual_change" | "max_steps";
|
|
27
|
+
export type TypeSafeDismissResult = {
|
|
28
|
+
dismissed: boolean;
|
|
29
|
+
stopReason: TypeSafeStopReason;
|
|
30
|
+
steps: TypeSafeDismissStep[];
|
|
31
|
+
};
|
|
32
|
+
export declare function dismissWithTypeSafe(page: Page, overlay: {
|
|
33
|
+
text: string | undefined;
|
|
34
|
+
target: ElementHandle<Element>;
|
|
35
|
+
}): Promise<TypeSafeDismissResult>;
|
|
36
|
+
export declare function checkHit(target: ElementHandle<Element>, point: Point): Promise<"no_blocker" | "not_clickable" | "ok" | "outside_overlay">;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=typesafe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typesafe.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/typesafe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAQ3D,KAAK,GAAG,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAWxE,MAAM,MAAM,mBAAmB,GAAG;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,GAAG,EAAE,GAAG,CAAC;IACT,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAC9C,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,KAAK,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC,KAAK,QAAQ,GAAG,IAAI,GAAG,YAAY,GAAG,iBAAiB,GAAG,eAAe,CAAC;AAE1E,MAAM,MAAM,kBAAkB,GAC1B,YAAY,GACZ,oBAAoB,GACpB,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,GACvB,kBAAkB,GAClB,WAAW,CAAC;AAEhB,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAC9B,CAAC;AAEF,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;CAAE,GACpE,OAAO,CAAC,qBAAqB,CAAC,CAoDhC;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,sEAyDpE"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dismissWithTypeSafe = dismissWithTypeSafe;
|
|
4
|
+
exports.checkHit = checkHit;
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const SYSTEM_ONE_URL = "https://api.typesafe.ai/v1/systemone";
|
|
7
|
+
const MODEL = "jev-preview";
|
|
8
|
+
const MAX_STEPS = 4;
|
|
9
|
+
const MIN_POPUP_PROBABILITY = 0.5;
|
|
10
|
+
const MIN_BOX_CONFIDENCE = 0.3;
|
|
11
|
+
async function dismissWithTypeSafe(page, overlay) {
|
|
12
|
+
const steps = [];
|
|
13
|
+
const stop = (stopReason) => ({
|
|
14
|
+
dismissed: stopReason === "popup_gone" && steps.length > 1,
|
|
15
|
+
stopReason,
|
|
16
|
+
steps,
|
|
17
|
+
});
|
|
18
|
+
let previousScreenshot;
|
|
19
|
+
for (let i = 0; i < MAX_STEPS; i++) {
|
|
20
|
+
const viewport = page.viewportSize();
|
|
21
|
+
if (!viewport) {
|
|
22
|
+
throw new Error("Viewport size is required for TypeSafe dismissal");
|
|
23
|
+
}
|
|
24
|
+
const screenshot = await page.screenshot({ type: "png" });
|
|
25
|
+
if (previousScreenshot?.equals(screenshot)) {
|
|
26
|
+
return stop("no_visual_change");
|
|
27
|
+
}
|
|
28
|
+
previousScreenshot = screenshot;
|
|
29
|
+
const startedAt = Date.now();
|
|
30
|
+
const response = await askSystemOne(screenshot, overlay.text);
|
|
31
|
+
const { popup_visible, dismiss_control } = response.answers;
|
|
32
|
+
const step = {
|
|
33
|
+
popupProbability: popup_visible.noul,
|
|
34
|
+
box: dismiss_control.box,
|
|
35
|
+
boxConfidence: dismiss_control.confidence,
|
|
36
|
+
clicked: undefined,
|
|
37
|
+
hitCheck: undefined,
|
|
38
|
+
latencyMs: Date.now() - startedAt,
|
|
39
|
+
inputTokens: response.usage.input_tokens,
|
|
40
|
+
outputTokens: response.usage.output_tokens,
|
|
41
|
+
};
|
|
42
|
+
steps.push(step);
|
|
43
|
+
if (popup_visible.noul < MIN_POPUP_PROBABILITY) {
|
|
44
|
+
return stop("popup_gone");
|
|
45
|
+
}
|
|
46
|
+
if (dismiss_control.confidence < MIN_BOX_CONFIDENCE) {
|
|
47
|
+
return stop("low_box_confidence");
|
|
48
|
+
}
|
|
49
|
+
const { box } = dismiss_control;
|
|
50
|
+
const point = {
|
|
51
|
+
x: Math.round(((box.left + box.right) / 2) * viewport.width),
|
|
52
|
+
y: Math.round(((box.top + box.bottom) / 2) * viewport.height),
|
|
53
|
+
};
|
|
54
|
+
step.hitCheck = await checkHit(overlay.target, point);
|
|
55
|
+
if (step.hitCheck !== "ok") {
|
|
56
|
+
return stop(step.hitCheck);
|
|
57
|
+
}
|
|
58
|
+
step.clicked = point;
|
|
59
|
+
await page.mouse.click(point.x, point.y);
|
|
60
|
+
await page.waitForTimeout(300);
|
|
61
|
+
}
|
|
62
|
+
return stop("max_steps");
|
|
63
|
+
}
|
|
64
|
+
function checkHit(target, point) {
|
|
65
|
+
return target.evaluate((targetEl, { x, y }) => {
|
|
66
|
+
const deepHit = (px, py) => {
|
|
67
|
+
let el = document.elementFromPoint(px, py);
|
|
68
|
+
while (el?.shadowRoot) {
|
|
69
|
+
const inner = el.shadowRoot.elementFromPoint(px, py);
|
|
70
|
+
if (!inner || inner === el) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
el = inner;
|
|
74
|
+
}
|
|
75
|
+
return el;
|
|
76
|
+
};
|
|
77
|
+
const parentOf = (el) => el.parentNode instanceof ShadowRoot
|
|
78
|
+
? el.parentNode.host
|
|
79
|
+
: el.parentElement;
|
|
80
|
+
const isAncestorOrSelf = (ancestor, el) => {
|
|
81
|
+
for (let n = el; n; n = parentOf(n)) {
|
|
82
|
+
if (n === ancestor) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
};
|
|
88
|
+
const rect = targetEl.getBoundingClientRect();
|
|
89
|
+
const blocker = deepHit(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
90
|
+
if (!blocker || isAncestorOrSelf(targetEl, blocker)) {
|
|
91
|
+
return "no_blocker";
|
|
92
|
+
}
|
|
93
|
+
let overlayRoot = blocker;
|
|
94
|
+
for (let p = parentOf(overlayRoot); p && !isAncestorOrSelf(p, targetEl); p = parentOf(p)) {
|
|
95
|
+
overlayRoot = p;
|
|
96
|
+
}
|
|
97
|
+
const hit = deepHit(x, y);
|
|
98
|
+
if (!hit || !isAncestorOrSelf(overlayRoot, hit)) {
|
|
99
|
+
return "outside_overlay";
|
|
100
|
+
}
|
|
101
|
+
const interactive = 'button, a[href], input, select, label, summary, iframe, [role=button], [role=link], [role=checkbox], [role=menuitem], [role=tab], [onclick], [tabindex]:not([tabindex="-1"])';
|
|
102
|
+
for (let n = hit; n; n = parentOf(n)) {
|
|
103
|
+
if (n.matches(interactive) || getComputedStyle(n).cursor === "pointer") {
|
|
104
|
+
return "ok";
|
|
105
|
+
}
|
|
106
|
+
if (n === overlayRoot) {
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return "not_clickable";
|
|
111
|
+
}, point);
|
|
112
|
+
}
|
|
113
|
+
async function askSystemOne(screenshot, overlayText) {
|
|
114
|
+
const apiKey = process.env.TYPESAFE_API_KEY;
|
|
115
|
+
if (!apiKey) {
|
|
116
|
+
throw new Error("TYPESAFE_API_KEY is not set");
|
|
117
|
+
}
|
|
118
|
+
const reference = (0, node_crypto_1.randomUUID)();
|
|
119
|
+
const popup = overlayText
|
|
120
|
+
? `the popup that contains the text "${overlayText.trim().slice(0, 300)}"`
|
|
121
|
+
: "the popup";
|
|
122
|
+
const body = {
|
|
123
|
+
model: MODEL,
|
|
124
|
+
state: {
|
|
125
|
+
screenshot: reference,
|
|
126
|
+
context: "A browser test was blocked because a popup (modal, toast, survey, banner or product tour) covers the element it wants to click.",
|
|
127
|
+
},
|
|
128
|
+
assets: {
|
|
129
|
+
[reference]: {
|
|
130
|
+
type: "image",
|
|
131
|
+
format: "png",
|
|
132
|
+
data_base64: screenshot.toString("base64"),
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
questions: {
|
|
136
|
+
popup_visible: {
|
|
137
|
+
type: "noul",
|
|
138
|
+
instructions: `Is ${popup} still visible in \`screenshot\`?`,
|
|
139
|
+
},
|
|
140
|
+
dismiss_control: {
|
|
141
|
+
type: "box",
|
|
142
|
+
instructions: `the single control in \`screenshot\` to click next to dismiss ${popup}: a close (×) button, or a Close, Skip, No thanks, Got it, Accept, Agree or Continue button. Prefer the option that accepts or continues over Decline or Cancel. If a required checkbox must be ticked first, the checkbox. Never the page's own controls behind the popup.`,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
const response = await fetch(SYSTEM_ONE_URL, {
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: {
|
|
149
|
+
Authorization: `Bearer ${apiKey}`,
|
|
150
|
+
"Content-Type": "application/json",
|
|
151
|
+
"TypeSafe-Danger-Zone-Images": "true",
|
|
152
|
+
},
|
|
153
|
+
body: JSON.stringify(body),
|
|
154
|
+
});
|
|
155
|
+
if (!response.ok) {
|
|
156
|
+
throw new Error(`TypeSafe ${response.status}: ${await response.text()}`);
|
|
157
|
+
}
|
|
158
|
+
return (await response.json());
|
|
159
|
+
}
|
|
@@ -2,6 +2,9 @@ import type { Page } from "@playwright/test";
|
|
|
2
2
|
import type { TestFn } from "../../types";
|
|
3
3
|
import type { OverlayElement } from "./types";
|
|
4
4
|
export declare function overlayDescription(element: OverlayElement | undefined): string | undefined;
|
|
5
|
+
export declare const DISMISS_OVERLAY_STEP = "Dismiss overlay";
|
|
6
|
+
export declare const DISMISSAL_AGENT_STEP = "Run overlay dismissal agent";
|
|
7
|
+
export declare function dismissOverlayStepTitle(element: OverlayElement | undefined): string;
|
|
5
8
|
export declare function isProductFruitsOverlay(element: OverlayElement | undefined): boolean | undefined;
|
|
6
9
|
export declare function textContent(pageRef: Page, element: OverlayElement | undefined): Promise<string | undefined>;
|
|
7
10
|
export declare function annotateForReport(testFn: TestFn, description: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,sBASrE;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,uBAKzE;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,IAAI,EACb,OAAO,EAAE,cAAc,GAAG,SAAS,+
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,sBASrE;AAED,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AACtD,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAElE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,UAO1E;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,uBAKzE;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,IAAI,EACb,OAAO,EAAE,cAAc,GAAG,SAAS,+BAgDpC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAMpE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,uDAiBrD"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DISMISSAL_AGENT_STEP = exports.DISMISS_OVERLAY_STEP = void 0;
|
|
3
4
|
exports.overlayDescription = overlayDescription;
|
|
5
|
+
exports.dismissOverlayStepTitle = dismissOverlayStepTitle;
|
|
4
6
|
exports.isProductFruitsOverlay = isProductFruitsOverlay;
|
|
5
7
|
exports.textContent = textContent;
|
|
6
8
|
exports.annotateForReport = annotateForReport;
|
|
@@ -17,6 +19,15 @@ function overlayDescription(element) {
|
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
21
|
}
|
|
22
|
+
exports.DISMISS_OVERLAY_STEP = "Dismiss overlay";
|
|
23
|
+
exports.DISMISSAL_AGENT_STEP = "Run overlay dismissal agent";
|
|
24
|
+
function dismissOverlayStepTitle(element) {
|
|
25
|
+
const root = element?.parent ?? element?.interceptor ?? "";
|
|
26
|
+
const name = /aria-label="([^"]+)"/.exec(root)?.[1] ??
|
|
27
|
+
/role="([^"]+)"/.exec(root)?.[1] ??
|
|
28
|
+
/<([a-z][a-z0-9-]*)/i.exec(root)?.[1];
|
|
29
|
+
return name ? `${exports.DISMISS_OVERLAY_STEP} "${name}"` : exports.DISMISS_OVERLAY_STEP;
|
|
30
|
+
}
|
|
20
31
|
function isProductFruitsOverlay(element) {
|
|
21
32
|
return (element?.parent?.includes("productfruits--container") ||
|
|
22
33
|
element?.interceptor.includes("productfruits--container"));
|
|
@@ -29,7 +40,7 @@ async function textContent(pageRef, element) {
|
|
|
29
40
|
let endTag;
|
|
30
41
|
const match = startTag.match(/^<\s*([^\s>]+)/);
|
|
31
42
|
if (match) {
|
|
32
|
-
const tagName = match[1];
|
|
43
|
+
const tagName = match[1];
|
|
33
44
|
endTag = `</${tagName}>`;
|
|
34
45
|
}
|
|
35
46
|
if (!startTag || !endTag) {
|
|
@@ -41,18 +52,15 @@ async function textContent(pageRef, element) {
|
|
|
41
52
|
const parser = new DOMParser();
|
|
42
53
|
const doc = parser.parseFromString(htmlString, "text/html");
|
|
43
54
|
const tempEl = doc.body.firstElementChild;
|
|
44
|
-
let selector = tempEl.tagName.toLowerCase();
|
|
45
|
-
// If there's an id, add it (id is unique and very specific)
|
|
55
|
+
let selector = tempEl.tagName.toLowerCase();
|
|
46
56
|
if (tempEl.id) {
|
|
47
57
|
selector += `#${CSS.escape(tempEl.id)}`;
|
|
48
58
|
}
|
|
49
|
-
// Add classes individually, escaping each one
|
|
50
59
|
if (tempEl.classList.length > 0) {
|
|
51
60
|
Array.from(tempEl.classList).forEach((cls) => {
|
|
52
61
|
selector += `.${CSS.escape(cls)}`;
|
|
53
62
|
});
|
|
54
63
|
}
|
|
55
|
-
// Add other attributes, being careful with escaping
|
|
56
64
|
Array.from(tempEl.attributes).forEach((attr) => {
|
|
57
65
|
if (attr.name === "id" || attr.name === "class") {
|
|
58
66
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,UA0B3C"}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.description = description;
|
|
4
|
-
exports.scrollIntoView = scrollIntoView;
|
|
5
|
-
const logger_1 = require("../../../../logger");
|
|
6
|
-
const constants_1 = require("../../../constants");
|
|
7
4
|
function description(locator) {
|
|
8
5
|
const fallbackDescription = locator.toString();
|
|
9
6
|
try {
|
|
@@ -25,18 +22,3 @@ function description(locator) {
|
|
|
25
22
|
return fallbackDescription;
|
|
26
23
|
}
|
|
27
24
|
}
|
|
28
|
-
async function scrollIntoView(locator) {
|
|
29
|
-
try {
|
|
30
|
-
await locator.evaluate((node) => {
|
|
31
|
-
if (node.scrollIntoViewIfNeeded) {
|
|
32
|
-
node.scrollIntoViewIfNeeded();
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
node.scrollIntoView();
|
|
36
|
-
}
|
|
37
|
-
}, undefined, { timeout: constants_1.DEFAULT_SCRIPT_EXECUTION_TIMEOUT });
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
logger_1.logger.debug("Failed to scroll into view for locator method");
|
|
41
|
-
}
|
|
42
|
-
}
|
package/dist/test/types.d.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
export type
|
|
2
|
-
/** Show Playwright's action annotations (cursor, target box, title) in videos */
|
|
3
|
-
mousePointerHighlighter?: boolean;
|
|
4
|
-
locatorHighlighter?: boolean;
|
|
1
|
+
export type PageScriptOpts = {
|
|
5
2
|
visionMethods?: boolean;
|
|
6
3
|
agentCapabilities?: boolean;
|
|
7
4
|
};
|
|
8
|
-
export type BaseTestFixtureOpts =
|
|
5
|
+
export type BaseTestFixtureOpts = PageScriptOpts & {
|
|
9
6
|
collectCoverage?: boolean;
|
|
10
7
|
};
|
|
11
8
|
//# sourceMappingURL=types.d.ts.map
|