@itmetsam/samurai-framework 0.1.0 → 0.2.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/README.md +129 -0
- package/bin/samurai.mjs +31 -0
- package/dist/api.js +3 -0
- package/dist/assert/assertion-error.d.ts +23 -0
- package/dist/assert/assertion-error.js +45 -0
- package/dist/assert/expect.d.ts +55 -0
- package/dist/assert/expect.js +188 -0
- package/dist/browser/browser-context.d.ts +91 -0
- package/dist/browser/browser-context.js +146 -0
- package/dist/browser/browser-finder.d.ts +2 -0
- package/dist/browser/browser-finder.js +95 -0
- package/dist/browser/browser.d.ts +95 -0
- package/dist/browser/browser.js +372 -0
- package/dist/browser/context-tree.d.ts +23 -0
- package/dist/browser/context-tree.js +60 -0
- package/dist/browser/dialog.d.ts +37 -0
- package/dist/browser/dialog.js +61 -0
- package/dist/browser/download.d.ts +71 -0
- package/dist/browser/download.js +137 -0
- package/dist/browser/downloads-dir.d.ts +8 -0
- package/dist/browser/downloads-dir.js +38 -0
- package/dist/browser/emulation.d.ts +61 -0
- package/dist/browser/emulation.js +144 -0
- package/dist/browser/file-chooser.d.ts +39 -0
- package/dist/browser/file-chooser.js +86 -0
- package/dist/browser/file-paths.d.ts +4 -0
- package/dist/browser/file-paths.js +18 -0
- package/dist/browser/index.d.ts +4 -0
- package/dist/browser/index.js +4 -0
- package/dist/browser/navigation-error.d.ts +13 -0
- package/dist/browser/navigation-error.js +19 -0
- package/dist/browser/page-logs.d.ts +86 -0
- package/dist/browser/page-logs.js +149 -0
- package/dist/browser/page-wait.d.ts +9 -0
- package/dist/browser/page-wait.js +17 -0
- package/dist/browser/page.d.ts +370 -0
- package/dist/browser/page.js +1038 -0
- package/dist/browser/pdf.d.ts +72 -0
- package/dist/browser/pdf.js +37 -0
- package/dist/browser/screenshot.d.ts +29 -0
- package/dist/browser/screenshot.js +42 -0
- package/dist/cli/args.d.ts +35 -0
- package/dist/cli/args.js +69 -0
- package/dist/cli/cli.cli-test.d.ts +1 -0
- package/dist/cli/cli.cli-test.js +170 -0
- package/dist/cli/console-reporter.d.ts +15 -0
- package/dist/cli/console-reporter.js +52 -0
- package/dist/cli/init.d.ts +7 -0
- package/dist/cli/init.js +61 -0
- package/dist/cli/main.d.ts +12 -0
- package/dist/cli/main.js +156 -0
- package/dist/config/config.d.ts +24 -0
- package/dist/config/config.js +42 -0
- package/dist/config/default.d.ts +2 -0
- package/dist/config/default.js +3 -0
- package/dist/config/mask.d.ts +11 -0
- package/dist/config/mask.js +85 -0
- package/dist/config/run-settings.d.ts +28 -0
- package/dist/config/run-settings.js +61 -0
- package/dist/config/secrets.d.ts +10 -0
- package/dist/config/secrets.js +67 -0
- package/dist/config/variables.d.ts +11 -0
- package/dist/config/variables.js +26 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -0
- package/dist/locator/action-timeout-error.d.ts +25 -0
- package/dist/locator/action-timeout-error.js +35 -0
- package/dist/locator/element-state.d.ts +44 -0
- package/dist/locator/element-state.js +109 -0
- package/dist/locator/label-locator.d.ts +13 -0
- package/dist/locator/label-locator.js +49 -0
- package/dist/locator/locator.d.ts +168 -0
- package/dist/locator/locator.js +591 -0
- package/dist/locator/selector-errors.d.ts +9 -0
- package/dist/locator/selector-errors.js +15 -0
- package/dist/locator/selector.d.ts +60 -0
- package/dist/locator/selector.js +113 -0
- package/dist/locator/text-locator.d.ts +13 -0
- package/dist/logger/index.d.ts +2 -0
- package/dist/logger/index.js +30 -0
- package/dist/network/data-collector.d.ts +26 -0
- package/dist/network/data-collector.js +77 -0
- package/dist/network/network-tracker.d.ts +94 -0
- package/dist/network/network-tracker.js +198 -0
- package/dist/network/network-wait.d.ts +16 -0
- package/dist/network/network-wait.js +24 -0
- package/dist/network/response.d.ts +22 -0
- package/dist/network/response.js +42 -0
- package/dist/network/router.d.ts +120 -0
- package/dist/network/router.js +343 -0
- package/dist/network/url-match.d.ts +8 -0
- package/dist/network/url-match.js +38 -0
- package/dist/recorder/apply.d.ts +3 -0
- package/dist/recorder/apply.js +10 -0
- package/dist/recorder/capture.d.ts +18 -0
- package/dist/recorder/capture.js +198 -0
- package/dist/recorder/index.d.ts +3 -0
- package/dist/recorder/index.js +3 -0
- package/dist/recorder/normalise.d.ts +68 -0
- package/dist/recorder/normalise.js +104 -0
- package/dist/recorder/ranking.d.ts +9 -0
- package/dist/recorder/ranking.js +21 -0
- package/dist/recorder/recorder.d.ts +52 -0
- package/dist/recorder/recorder.js +143 -0
- package/dist/recorder/session.d.ts +49 -0
- package/dist/recorder/session.js +113 -0
- package/dist/runner/module-type.d.ts +6 -0
- package/dist/runner/module-type.js +29 -0
- package/dist/runner/page-logs-report.d.ts +40 -0
- package/dist/runner/page-logs-report.js +44 -0
- package/dist/runner/prepare-run.d.ts +14 -0
- package/dist/runner/prepare-run.js +20 -0
- package/dist/runner/project.d.ts +17 -0
- package/dist/runner/project.js +33 -0
- package/dist/runner/registry.d.ts +7 -0
- package/dist/runner/registry.js +40 -0
- package/dist/runner/reporter.d.ts +43 -0
- package/dist/runner/reporter.js +83 -0
- package/dist/runner/run.d.ts +27 -0
- package/dist/runner/run.js +34 -0
- package/dist/runner/test-error.d.ts +3 -0
- package/dist/runner/test-error.js +18 -0
- package/dist/runner/test-runner.d.ts +44 -0
- package/dist/runner/test-runner.js +205 -0
- package/dist/script/call-function.d.ts +34 -0
- package/dist/script/call-function.js +71 -0
- package/dist/script/element-handle.d.ts +10 -0
- package/{src/script/element-handle.ts → dist/script/element-handle.js} +4 -4
- package/dist/script/helpers.d.ts +28 -0
- package/dist/script/helpers.js +50 -0
- package/dist/script/preload.d.ts +31 -0
- package/dist/script/preload.js +71 -0
- package/dist/script/serialize.d.ts +29 -0
- package/dist/script/serialize.js +207 -0
- package/dist/steps/build.d.ts +5 -0
- package/dist/steps/build.js +38 -0
- package/dist/steps/edit.d.ts +30 -0
- package/dist/steps/edit.js +80 -0
- package/dist/steps/emit.d.ts +6 -0
- package/dist/steps/emit.js +73 -0
- package/dist/steps/index.d.ts +5 -0
- package/dist/steps/index.js +4 -0
- package/dist/steps/model.d.ts +103 -0
- package/dist/steps/model.js +1 -0
- package/dist/steps/parse.d.ts +3 -0
- package/dist/steps/parse.js +272 -0
- package/dist/transport/bidi-connection.d.ts +46 -0
- package/dist/transport/bidi-connection.js +176 -0
- package/dist/transport/bidi-error.d.ts +10 -0
- package/dist/transport/bidi-error.js +10 -0
- package/dist/transport/subscriptions.d.ts +36 -0
- package/dist/transport/subscriptions.js +81 -0
- package/{src → dist}/types/test.d.ts +10 -4
- package/dist/wait/wait-until.d.ts +20 -0
- package/dist/wait/wait-until.js +63 -0
- package/package.json +44 -7
- package/.claude/settings.local.json +0 -7
- package/.env.example +0 -3
- package/.github/actions/setup/action.yml +0 -16
- package/.github/workflows/ci.yml +0 -71
- package/.trunk/configs/.markdownlint.yaml +0 -2
- package/.trunk/configs/.yamllint.yaml +0 -7
- package/.trunk/configs/svgo.config.mjs +0 -14
- package/.trunk/trunk.yaml +0 -34
- package/docs/superpowers/bidi-roadmap.md +0 -60
- package/docs/superpowers/plans/2026-09-29-auto-wait.md +0 -1827
- package/docs/superpowers/plans/2026-09-29-expect.md +0 -1350
- package/docs/superpowers/plans/2026-10-01-ci.md +0 -350
- package/docs/superpowers/plans/2026-10-01-test-api-alignment.md +0 -1821
- package/docs/superpowers/recorder-roadmap.md +0 -52
- package/docs/superpowers/specs/2026-09-29-auto-wait-design.md +0 -185
- package/docs/superpowers/specs/2026-09-29-bidi-foundation-design.md +0 -130
- package/docs/superpowers/specs/2026-09-29-contexts-and-storage-design.md +0 -88
- package/docs/superpowers/specs/2026-09-29-downloads-and-file-dialogs-design.md +0 -94
- package/docs/superpowers/specs/2026-09-29-emulation-design.md +0 -88
- package/docs/superpowers/specs/2026-09-29-expect-design.md +0 -130
- package/docs/superpowers/specs/2026-09-29-locate-nodes-design.md +0 -90
- package/docs/superpowers/specs/2026-09-29-logs-and-errors-design.md +0 -87
- package/docs/superpowers/specs/2026-09-29-navigation-waits-design.md +0 -83
- package/docs/superpowers/specs/2026-09-29-network-data-design.md +0 -78
- package/docs/superpowers/specs/2026-09-29-network-interception-design.md +0 -91
- package/docs/superpowers/specs/2026-09-29-network-tracking-design.md +0 -86
- package/docs/superpowers/specs/2026-09-29-page-api-design.md +0 -90
- package/docs/superpowers/specs/2026-09-29-preload-scripts-design.md +0 -82
- package/docs/superpowers/specs/2026-09-29-script-call-function-design.md +0 -107
- package/docs/superpowers/specs/2026-09-29-set-files-design.md +0 -62
- package/docs/superpowers/specs/2026-09-29-user-prompts-design.md +0 -75
- package/docs/superpowers/specs/2026-10-01-ci-design.md +0 -106
- package/docs/superpowers/specs/2026-10-01-test-api-alignment-design.md +0 -241
- package/public/logo-dark.svg +0 -236
- package/public/logo-light.svg +0 -262
- package/samurai.config.ts +0 -7
- package/src/assert/assertion-error.test.ts +0 -55
- package/src/assert/assertion-error.ts +0 -62
- package/src/assert/expect-locator.test.ts +0 -217
- package/src/assert/expect.test.ts +0 -73
- package/src/assert/expect.ts +0 -299
- package/src/browser/browser-context.browser-test.ts +0 -201
- package/src/browser/browser-context.test.ts +0 -407
- package/src/browser/browser-context.ts +0 -208
- package/src/browser/browser-finder.ts +0 -110
- package/src/browser/browser.test.ts +0 -88
- package/src/browser/browser.ts +0 -515
- package/src/browser/context-tree.test.ts +0 -86
- package/src/browser/context-tree.ts +0 -70
- package/src/browser/dialog.browser-test.ts +0 -144
- package/src/browser/dialog.test.ts +0 -322
- package/src/browser/dialog.ts +0 -83
- package/src/browser/download.test.ts +0 -161
- package/src/browser/download.ts +0 -172
- package/src/browser/downloads-dir.test.ts +0 -75
- package/src/browser/downloads-dir.ts +0 -39
- package/src/browser/emulation.browser-test.ts +0 -186
- package/src/browser/emulation.test.ts +0 -200
- package/src/browser/emulation.ts +0 -204
- package/src/browser/file-chooser.test.ts +0 -142
- package/src/browser/file-chooser.ts +0 -99
- package/src/browser/file-paths.ts +0 -18
- package/src/browser/goto.browser-test.ts +0 -44
- package/src/browser/navigation-error.ts +0 -26
- package/src/browser/navigation.browser-test.ts +0 -172
- package/src/browser/page-api.browser-test.ts +0 -228
- package/src/browser/page-logs.browser-test.ts +0 -119
- package/src/browser/page-logs.test.ts +0 -212
- package/src/browser/page-logs.ts +0 -208
- package/src/browser/page-wait.ts +0 -19
- package/src/browser/page.test.ts +0 -1567
- package/src/browser/page.ts +0 -1176
- package/src/browser/pdf.ts +0 -62
- package/src/browser/screenshot.ts +0 -65
- package/src/browser/transfers.browser-test.ts +0 -180
- package/src/config/config.ts +0 -33
- package/src/config/default.ts +0 -5
- package/src/config/fixtures.test.ts +0 -137
- package/src/config/mask.test.ts +0 -143
- package/src/config/mask.ts +0 -87
- package/src/config/run-settings.test.ts +0 -163
- package/src/config/run-settings.ts +0 -114
- package/src/config/secrets.ts +0 -70
- package/src/config/variables.ts +0 -31
- package/src/index.ts +0 -10
- package/src/locator/action-timeout-error.test.ts +0 -83
- package/src/locator/action-timeout-error.ts +0 -72
- package/src/locator/element-state.test.ts +0 -181
- package/src/locator/element-state.ts +0 -159
- package/src/locator/locate-nodes.browser-test.ts +0 -169
- package/src/locator/locator-actions.test.ts +0 -355
- package/src/locator/locator-chain.test.ts +0 -292
- package/src/locator/locator.browser-test.ts +0 -137
- package/src/locator/locator.test.ts +0 -179
- package/src/locator/locator.ts +0 -656
- package/src/locator/selector-errors.ts +0 -18
- package/src/locator/selector.test.ts +0 -124
- package/src/locator/selector.ts +0 -107
- package/src/locator/set-files.browser-test.ts +0 -141
- package/src/locator/set-files.test.ts +0 -191
- package/src/locator/text-locator.test.ts +0 -118
- package/src/logger/index.test.ts +0 -28
- package/src/logger/index.ts +0 -40
- package/src/network/data-collector.test.ts +0 -111
- package/src/network/data-collector.ts +0 -92
- package/src/network/network-data.browser-test.ts +0 -192
- package/src/network/network-tracker.browser-test.ts +0 -151
- package/src/network/network-tracker.test.ts +0 -307
- package/src/network/network-tracker.ts +0 -286
- package/src/network/network-wait.ts +0 -28
- package/src/network/response.test.ts +0 -93
- package/src/network/response.ts +0 -58
- package/src/network/router.browser-test.ts +0 -260
- package/src/network/router.test.ts +0 -528
- package/src/network/router.ts +0 -420
- package/src/network/url-match.test.ts +0 -69
- package/src/network/url-match.ts +0 -41
- package/src/runner/page-logs-report.test.ts +0 -144
- package/src/runner/page-logs-report.ts +0 -82
- package/src/runner/prepare-run.test.ts +0 -33
- package/src/runner/prepare-run.ts +0 -36
- package/src/runner/registry.test.ts +0 -59
- package/src/runner/registry.ts +0 -46
- package/src/runner/reporter.test.ts +0 -73
- package/src/runner/reporter.ts +0 -109
- package/src/runner/test-error.test.ts +0 -39
- package/src/runner/test-error.ts +0 -20
- package/src/runner/test-runner.ts +0 -184
- package/src/script/call-function.browser-test.ts +0 -112
- package/src/script/call-function.test.ts +0 -174
- package/src/script/call-function.ts +0 -113
- package/src/script/helpers.test.ts +0 -116
- package/src/script/helpers.ts +0 -68
- package/src/script/preload.browser-test.ts +0 -144
- package/src/script/preload.test.ts +0 -180
- package/src/script/preload.ts +0 -102
- package/src/script/serialize.test.ts +0 -182
- package/src/script/serialize.ts +0 -203
- package/src/testing/browser-fixture.ts +0 -18
- package/src/testing/fake-websocket.ts +0 -72
- package/src/testing/stub-connector.ts +0 -154
- package/src/tests/index.spec.ts +0 -30
- package/src/transport/bidi-connection.test.ts +0 -155
- package/src/transport/bidi-connection.ts +0 -256
- package/src/transport/bidi-error.ts +0 -23
- package/src/transport/foundation.browser-test.ts +0 -73
- package/src/transport/subscriptions.test.ts +0 -140
- package/src/transport/subscriptions.ts +0 -110
- package/src/types/bidi-commands.test-d.ts +0 -55
- package/src/wait/wait-until.test.ts +0 -117
- package/src/wait/wait-until.ts +0 -86
- package/tsconfig.json +0 -44
- /package/{src/api.ts → dist/api.d.ts} +0 -0
- /package/{src/locator/text-locator.ts → dist/locator/text-locator.js} +0 -0
- /package/{src → dist}/types/bidi-modules/browser.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/browsing-context.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/emulation.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/input.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/log.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/network.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/permissions.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/script.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/session.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/storage.d.ts +0 -0
- /package/{src → dist}/types/bidi-modules/web-extensions.d.ts +0 -0
- /package/{src → dist}/types/bidi-protocols/command.d.ts +0 -0
- /package/{src → dist}/types/bidi-protocols/error.d.ts +0 -0
- /package/{src → dist}/types/bidi-protocols/event.d.ts +0 -0
- /package/{src → dist}/types/bidi.d.ts +0 -0
- /package/{src → dist}/types/browser.d.ts +0 -0
- /package/{src → dist}/types/config.d.ts +0 -0
- /package/{src → dist}/types/env.d.ts +0 -0
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { after, before, test } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { Browser } from "../browser/browser.js";
|
|
4
|
-
import type Page from "../browser/page.js";
|
|
5
|
-
import { setContent } from "../testing/browser-fixture.js";
|
|
6
|
-
import { ActionTimeoutError } from "./action-timeout-error.js";
|
|
7
|
-
|
|
8
|
-
let browser: Browser;
|
|
9
|
-
let page: Page;
|
|
10
|
-
|
|
11
|
-
before(async () => {
|
|
12
|
-
({ browser, page } = await Browser.launch("firefox", { port: 9231, headless: true }));
|
|
13
|
-
await page.navigateTo("about:blank", "complete");
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
after(async () => {
|
|
17
|
-
await browser?.close();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
/** Records on the button whether a click was a real (trusted) pointer click */
|
|
21
|
-
const RECORD_CLICKS = `
|
|
22
|
-
const b = document.getElementById("b");
|
|
23
|
-
b.addEventListener("click", (event) => { b.dataset.clicked = event.isTrusted ? "trusted" : "synthetic"; });`;
|
|
24
|
-
|
|
25
|
-
const OPTIONS = { timeout: 15000 };
|
|
26
|
-
|
|
27
|
-
test("click waits for a button that becomes enabled", OPTIONS, async () => {
|
|
28
|
-
await setContent(
|
|
29
|
-
page,
|
|
30
|
-
`<button id="b" disabled>Go</button>`,
|
|
31
|
-
`${RECORD_CLICKS} setTimeout(() => { b.disabled = false; }, 300);`,
|
|
32
|
-
);
|
|
33
|
-
const button = page.locator("button[@id='b']");
|
|
34
|
-
await button.click({ timeout: 3000 });
|
|
35
|
-
assert.equal(await button.getAttribute("data-clicked"), "trusted");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("click waits for a covering overlay to disappear", OPTIONS, async () => {
|
|
39
|
-
await setContent(
|
|
40
|
-
page,
|
|
41
|
-
`<button id="b">Go</button><div id="overlay" style="position:fixed;inset:0;background:rgba(0,0,0,0.5)"></div>`,
|
|
42
|
-
`${RECORD_CLICKS} setTimeout(() => document.getElementById("overlay").remove(), 300);`,
|
|
43
|
-
);
|
|
44
|
-
const button = page.locator("button[@id='b']");
|
|
45
|
-
await button.click({ timeout: 3000 });
|
|
46
|
-
assert.equal(await button.getAttribute("data-clicked"), "trusted");
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("click waits for a moving button to stop", OPTIONS, async () => {
|
|
50
|
-
await setContent(
|
|
51
|
-
page,
|
|
52
|
-
`<button id="b" style="position:absolute;left:0;top:0;transition:left 300ms linear">Go</button>`,
|
|
53
|
-
`${RECORD_CLICKS} requestAnimationFrame(() => requestAnimationFrame(() => { b.style.left = "300px"; }));`,
|
|
54
|
-
);
|
|
55
|
-
const button = page.locator("button[@id='b']");
|
|
56
|
-
await button.click({ timeout: 3000 });
|
|
57
|
-
assert.equal(await button.getAttribute("data-clicked"), "trusted");
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("click scrolls a button below the fold into view", OPTIONS, async () => {
|
|
61
|
-
await setContent(page, `<div style="height:3000px"></div><button id="b">Go</button>`, RECORD_CLICKS);
|
|
62
|
-
const button = page.locator("button[@id='b']");
|
|
63
|
-
await button.click({ timeout: 3000 });
|
|
64
|
-
assert.equal(await button.getAttribute("data-clicked"), "trusted");
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("click works on an SVG element", OPTIONS, async () => {
|
|
68
|
-
await setContent(
|
|
69
|
-
page,
|
|
70
|
-
`<svg id="icon" width="40" height="40"><rect width="40" height="40"></rect></svg>`,
|
|
71
|
-
`const icon = document.getElementById("icon");
|
|
72
|
-
icon.addEventListener("click", (event) => icon.setAttribute("data-clicked", event.isTrusted ? "trusted" : "synthetic"));`,
|
|
73
|
-
);
|
|
74
|
-
// SVG elements are not in the HTML namespace, so a bare //svg does not match them in XPath
|
|
75
|
-
const icon = page.locator("*[local-name()='svg' and @id='icon']");
|
|
76
|
-
await icon.click({ timeout: 3000 });
|
|
77
|
-
assert.equal(await icon.getAttribute("data-clicked"), "trusted");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("fill waits for a readonly field to become editable and replaces its value", OPTIONS, async () => {
|
|
81
|
-
await setContent(
|
|
82
|
-
page,
|
|
83
|
-
`<input id="i" readonly value="old">`,
|
|
84
|
-
`setTimeout(() => { document.getElementById("i").readOnly = false; }, 300);`,
|
|
85
|
-
);
|
|
86
|
-
const input = page.locator("input[@id='i']");
|
|
87
|
-
await input.fill("typed", { timeout: 3000 });
|
|
88
|
-
assert.equal(await input.inputValue(), "typed");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("force clicks an element that is not considered visible", OPTIONS, async () => {
|
|
92
|
-
await setContent(page, `<button id="b" style="opacity:0">Go</button>`, RECORD_CLICKS);
|
|
93
|
-
const button = page.locator("button[@id='b']");
|
|
94
|
-
await assert.rejects(button.click({ timeout: 300 }), /visible ✗/);
|
|
95
|
-
await button.click({ force: true, timeout: 3000 });
|
|
96
|
-
assert.equal(await button.getAttribute("data-clicked"), "trusted");
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("waitFor hidden resolves when the element is removed; visible times out when it stays hidden", OPTIONS, async () => {
|
|
100
|
-
await setContent(
|
|
101
|
-
page,
|
|
102
|
-
`<div id="s">Loading</div>`,
|
|
103
|
-
`setTimeout(() => document.getElementById("s").remove(), 300);`,
|
|
104
|
-
);
|
|
105
|
-
await page.locator("div[@id='s']").waitFor({ state: "hidden", timeout: 3000 });
|
|
106
|
-
|
|
107
|
-
await setContent(page, `<div id="s" style="display:none">Loading</div>`);
|
|
108
|
-
await assert.rejects(
|
|
109
|
-
page.locator("div[@id='s']").waitFor({ timeout: 300 }),
|
|
110
|
-
(err) =>
|
|
111
|
-
err instanceof ActionTimeoutError &&
|
|
112
|
-
err.message === "waitFor(): //div[@id='s'] did not become visible within 300ms",
|
|
113
|
-
);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
test("a disabled button times out with the check summary", OPTIONS, async () => {
|
|
117
|
-
await setContent(page, `<button id="b" disabled>Go</button>`);
|
|
118
|
-
await assert.rejects(
|
|
119
|
-
page.locator("button[@id='b']").click({ timeout: 500 }),
|
|
120
|
-
(err) =>
|
|
121
|
-
err instanceof ActionTimeoutError &&
|
|
122
|
-
err.message ===
|
|
123
|
-
"click(): //button[@id='b'] was not actionable within 500ms\n" +
|
|
124
|
-
" attached ✓ visible ✓ stable ✓ enabled ✗ hit target —",
|
|
125
|
-
);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test("a covered button times out naming the covering element", OPTIONS, async () => {
|
|
129
|
-
await setContent(
|
|
130
|
-
page,
|
|
131
|
-
`<button id="b">Go</button><div id="cover" class="overlay" style="position:fixed;inset:0"></div>`,
|
|
132
|
-
);
|
|
133
|
-
await assert.rejects(
|
|
134
|
-
page.locator("button[@id='b']").click({ timeout: 500 }),
|
|
135
|
-
/hit target ✗ \(covered by div#cover\.overlay\)/,
|
|
136
|
-
);
|
|
137
|
-
});
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { test } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import Locator from "./locator.js";
|
|
4
|
-
import { remote, stubConnector, type StubResponse } from "../testing/stub-connector.js";
|
|
5
|
-
|
|
6
|
-
function locatorWith(xpath: string, ...responses: StubResponse[]) {
|
|
7
|
-
const stub = stubConnector(...responses);
|
|
8
|
-
return { locator: new Locator(xpath, stub.connector, "ctx"), ...stub };
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Fails if the evaluated script is not valid JavaScript (compiles only, never runs) */
|
|
12
|
-
function assertCompiles(script: string) {
|
|
13
|
-
assert.doesNotThrow(() => new Function(script), script);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Element state for an element that is ready for any action */
|
|
17
|
-
const ready: StubResponse = remote({
|
|
18
|
-
attached: true,
|
|
19
|
-
visible: true,
|
|
20
|
-
enabled: true,
|
|
21
|
-
editable: true,
|
|
22
|
-
box: { x: 0, y: 0, width: 100, height: 20 },
|
|
23
|
-
hitTarget: "self",
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("selector prefixes relative xpaths with // and keeps quotes as written", () => {
|
|
27
|
-
const selectorOf = (xpath: string) => locatorWith(xpath, { type: "null" }).locator.selector;
|
|
28
|
-
assert.equal(selectorOf("h1"), "//h1");
|
|
29
|
-
assert.equal(selectorOf("//div"), "//div");
|
|
30
|
-
assert.equal(selectorOf("a[text()='Start']"), "//a[text()='Start']");
|
|
31
|
-
assert.equal(selectorOf("/html/body"), "/html/body");
|
|
32
|
-
assert.equal(selectorOf("(//li)[2]"), "(//li)[2]");
|
|
33
|
-
assert.equal(selectorOf(".//span"), ".//span");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
/** The BiDi locators sent, in order */
|
|
37
|
-
function locates(sent: Array<{ method: string; params: unknown }>) {
|
|
38
|
-
return sent
|
|
39
|
-
.filter(({ method }) => method === "browsingContext.locateNodes")
|
|
40
|
-
.map(({ params }) => (params as { locator: unknown }).locator);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
test("the xpath reaches the browser in the locate command, byte for byte, never in a script", async () => {
|
|
44
|
-
const xpath = `//a[text()="It's \\ a\nnew line"]`;
|
|
45
|
-
const { locator, sent, calls } = locatorWith(xpath, { type: "string", value: "x" }, ready);
|
|
46
|
-
await locator.textContent();
|
|
47
|
-
await locator.click({ timeout: 3000 });
|
|
48
|
-
assert.deepEqual(locates(sent)[0], { type: "xpath", value: xpath });
|
|
49
|
-
for (const call of calls) {
|
|
50
|
-
assert.ok(!call.functionDeclaration.includes("It's"), "the xpath must not be embedded in the source");
|
|
51
|
-
assertCompiles(call.functionDeclaration);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test("scripts receive the located element as their first argument", async () => {
|
|
56
|
-
const { locator, calls } = locatorWith("h1", { type: "string", value: "x" });
|
|
57
|
-
await locator.textContent();
|
|
58
|
-
assert.deepEqual(calls[0]!.args[0], { sharedId: "stub-node-0" });
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("locating asks for references only", async () => {
|
|
62
|
-
const { locator, sent } = locatorWith("h1", { type: "null" });
|
|
63
|
-
await locator.textContent();
|
|
64
|
-
const params = sent[0]!.params as { serializationOptions: unknown; maxNodeCount: unknown; context: string };
|
|
65
|
-
assert.deepEqual(params.serializationOptions, { maxDomDepth: 0 });
|
|
66
|
-
assert.equal(params.maxNodeCount, 1);
|
|
67
|
-
assert.equal(params.context, "ctx");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("all() returns one locator per current match", async () => {
|
|
71
|
-
const { locator, nodeCounts, sent } = locatorWith("li", { type: "null" });
|
|
72
|
-
nodeCounts(3);
|
|
73
|
-
const items = await locator.all();
|
|
74
|
-
assert.deepEqual(
|
|
75
|
-
items.map((item) => item.selector),
|
|
76
|
-
["(//li)[1]", "(//li)[2]", "(//li)[3]"],
|
|
77
|
-
);
|
|
78
|
-
assert.equal((sent[0]!.params as { maxNodeCount?: number }).maxNodeCount, undefined);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("all() returns an empty array when nothing matches", async () => {
|
|
82
|
-
const { locator, nodeCounts } = locatorWith("li", { type: "null" });
|
|
83
|
-
nodeCounts(0);
|
|
84
|
-
assert.deepEqual(await locator.all(), []);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("actions on an all() item target that item", async () => {
|
|
88
|
-
const stub = stubConnector(ready);
|
|
89
|
-
stub.nodeCounts(2, 1);
|
|
90
|
-
const items = await new Locator("li", stub.connector, "ctx").all();
|
|
91
|
-
await items[1]!.click({ timeout: 3000 });
|
|
92
|
-
assert.deepEqual(locates(stub.sent)[1], { type: "xpath", value: "(//li)[2]" });
|
|
93
|
-
assert.equal(stub.sent.filter(({ method }) => method === "input.performActions").length, 1);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
/** Methods and key values sent by fill(), in order */
|
|
97
|
-
function fillTrace(sent: Array<{ method: string; params: unknown }>, expressions: string[]) {
|
|
98
|
-
let evaluateIndex = 0;
|
|
99
|
-
return sent
|
|
100
|
-
.filter(({ method }) => method !== "browsingContext.locateNodes")
|
|
101
|
-
.map(({ method, params }) => {
|
|
102
|
-
if (method === "script.callFunction") return `evaluate:${expressions[evaluateIndex++]!.includes(".select()") ? "select" : "other"}`;
|
|
103
|
-
const [source] = (params as { actions: Array<{ type: string; actions: Array<{ type: string; value?: string }> }> }).actions;
|
|
104
|
-
if (source!.type === "key") return `keys:${source!.actions.map((a) => a.value).join("")}`;
|
|
105
|
-
return source!.type;
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
test("fill() selects the existing value before typing so it is replaced", async () => {
|
|
110
|
-
const { locator, sent, expressions } = locatorWith("input", ready, { type: "undefined" });
|
|
111
|
-
await locator.fill("abc", { timeout: 3000 });
|
|
112
|
-
assert.deepEqual(fillTrace(sent, expressions), [
|
|
113
|
-
"evaluate:other",
|
|
114
|
-
"pointer",
|
|
115
|
-
"none",
|
|
116
|
-
"evaluate:select",
|
|
117
|
-
"keys:aabbcc",
|
|
118
|
-
]);
|
|
119
|
-
for (const expression of expressions) assertCompiles(expression);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test('fill("") clears the field with Backspace', async () => {
|
|
123
|
-
const { locator, sent, expressions } = locatorWith("input", ready, { type: "undefined" });
|
|
124
|
-
await locator.fill("", { timeout: 3000 });
|
|
125
|
-
assert.deepEqual(fillTrace(sent, expressions), [
|
|
126
|
-
"evaluate:other",
|
|
127
|
-
"pointer",
|
|
128
|
-
"none",
|
|
129
|
-
"evaluate:select",
|
|
130
|
-
"keys:",
|
|
131
|
-
]);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test("textContent returns the string, or null when the element is missing", async () => {
|
|
135
|
-
const found = locatorWith("h1", { type: "string", value: "Contact" });
|
|
136
|
-
assert.equal(await found.locator.textContent(), "Contact");
|
|
137
|
-
assert.deepEqual(locates(found.sent)[0], { type: "xpath", value: "//h1" });
|
|
138
|
-
|
|
139
|
-
const missing = locatorWith("h1", { type: "null" });
|
|
140
|
-
assert.equal(await missing.locator.textContent(), null);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test("inputValue returns the value, or null", async () => {
|
|
144
|
-
assert.equal(
|
|
145
|
-
await locatorWith("input", { type: "string", value: "test" }).locator.inputValue(),
|
|
146
|
-
"test",
|
|
147
|
-
);
|
|
148
|
-
assert.equal(await locatorWith("input", { type: "null" }).locator.inputValue(), null);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test("getAttribute passes the attribute name as an argument", async () => {
|
|
152
|
-
const { locator, calls } = locatorWith("a", { type: "string", value: "/home" });
|
|
153
|
-
assert.equal(await locator.getAttribute("href"), "/home");
|
|
154
|
-
assert.deepEqual(calls[0]!.args, [{ sharedId: "stub-node-0" }, "href"]);
|
|
155
|
-
assert.match(calls[0]!.functionDeclaration, /el\.getAttribute\(name\)/);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
test("isVisible returns the boolean result", async () => {
|
|
159
|
-
assert.equal(await locatorWith("div", { type: "boolean", value: true }).locator.isVisible(), true);
|
|
160
|
-
assert.equal(await locatorWith("div", { type: "boolean", value: false }).locator.isVisible(), false);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test("count is the number of located nodes, found without running any script", async () => {
|
|
164
|
-
const { locator, nodeCounts, expressions, sent } = locatorWith("li", { type: "null" });
|
|
165
|
-
nodeCounts(3);
|
|
166
|
-
assert.equal(await locator.count(), 3);
|
|
167
|
-
assert.equal(expressions.length, 0);
|
|
168
|
-
assert.deepEqual(locates(sent), [{ type: "xpath", value: "//li" }]);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test("reads throw on an evaluate exception", async () => {
|
|
172
|
-
const { locator } = locatorWith("h1[", { exception: "SyntaxError: bad xpath" });
|
|
173
|
-
await assert.rejects(locator.textContent(), /Failed to locate element/);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test("reads reject unexpected result types", async () => {
|
|
177
|
-
const { locator } = locatorWith("h1", { type: "number", value: 1 });
|
|
178
|
-
await assert.rejects(locator.textContent(), /Expected a string or null/);
|
|
179
|
-
});
|