@itmetsam/samurai-framework 0.9.0 → 0.12.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 +4 -5
- package/dist/assert/assertion-error.d.ts +1 -1
- package/dist/assert/expect.d.ts +5 -50
- package/dist/assert/expect.js +5 -182
- package/dist/assert/locator-assertions.d.ts +22 -0
- package/dist/assert/locator-assertions.js +61 -0
- package/dist/assert/pending-assertions.d.ts +7 -0
- package/dist/assert/pending-assertions.js +40 -0
- package/dist/assert/text-match.d.ts +4 -0
- package/dist/assert/text-match.js +11 -0
- package/dist/assert/value-assertions.d.ts +23 -0
- package/dist/assert/value-assertions.js +82 -0
- package/dist/browser/browser-context.d.ts +0 -1
- package/dist/browser/browser-context.js +13 -5
- package/dist/browser/browser-finder.js +5 -9
- package/dist/browser/browser.d.ts +3 -31
- package/dist/browser/browser.js +36 -219
- package/dist/browser/chrome-launch.d.ts +14 -1
- package/dist/browser/chrome-launch.js +44 -1
- package/dist/browser/dialog-handling.d.ts +32 -0
- package/dist/browser/dialog-handling.js +85 -0
- package/dist/browser/document-watcher.d.ts +44 -0
- package/dist/browser/document-watcher.js +160 -0
- package/dist/browser/download-folders.d.ts +0 -1
- package/dist/browser/download-folders.js +7 -15
- package/dist/browser/download.js +5 -10
- package/dist/browser/downloads-dir.js +2 -2
- package/dist/browser/emit.d.ts +7 -0
- package/dist/browser/emit.js +16 -0
- package/dist/browser/emulation.d.ts +1 -1
- package/dist/browser/emulation.js +54 -18
- package/dist/browser/file-chooser.js +4 -11
- package/dist/browser/history.d.ts +6 -0
- package/dist/browser/history.js +89 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/init-script.d.ts +2 -0
- package/dist/browser/init-script.js +16 -0
- package/dist/browser/launch-firefox.d.ts +20 -0
- package/dist/browser/launch-firefox.js +66 -0
- package/dist/browser/launch-flags.d.ts +18 -0
- package/dist/browser/launch-flags.js +100 -0
- package/dist/browser/lazy.d.ts +21 -0
- package/dist/browser/lazy.js +44 -0
- package/dist/browser/page-events.d.ts +44 -0
- package/dist/browser/page-events.js +58 -0
- package/dist/browser/page-logs.js +2 -9
- package/dist/browser/page-navigation.d.ts +58 -0
- package/dist/browser/page-navigation.js +108 -0
- package/dist/browser/page-network.d.ts +69 -0
- package/dist/browser/page-network.js +163 -0
- package/dist/browser/page-settings.d.ts +35 -0
- package/dist/browser/page-settings.js +51 -0
- package/dist/browser/page-transfers.d.ts +36 -0
- package/dist/browser/page-transfers.js +50 -0
- package/dist/browser/page.d.ts +39 -154
- package/dist/browser/page.js +134 -758
- package/dist/browser/pdf.d.ts +1 -1
- package/dist/browser/session.d.ts +16 -0
- package/dist/browser/session.js +32 -0
- package/dist/browser/viewport.d.ts +19 -0
- package/dist/browser/viewport.js +72 -0
- package/dist/browser/warm-up.d.ts +1 -1
- package/dist/browser/warm-up.js +2 -1
- package/dist/browsers/index.d.ts +1 -1
- package/dist/browsers/index.js +1 -1
- package/dist/browsers/pins.d.ts +0 -2
- package/dist/browsers/pins.js +0 -2
- package/dist/browsers/store.d.ts +0 -2
- package/dist/browsers/store.js +1 -2
- package/dist/cli/args.d.ts +19 -7
- package/dist/cli/args.js +18 -9
- package/dist/cli/browsers.d.ts +1 -2
- package/dist/cli/browsers.js +2 -2
- package/dist/cli/console-reporter.d.ts +4 -0
- package/dist/cli/console-reporter.js +15 -7
- package/dist/cli/flow-reporter.js +2 -1
- package/dist/cli/flow.d.ts +1 -2
- package/dist/cli/flow.js +25 -22
- package/dist/cli/init.js +3 -8
- package/dist/cli/io.d.ts +10 -0
- package/dist/cli/io.js +1 -0
- package/dist/cli/main.d.ts +3 -10
- package/dist/cli/main.js +11 -137
- package/dist/cli/paint.d.ts +2 -0
- package/dist/cli/paint.js +4 -0
- package/dist/cli/record.d.ts +4 -0
- package/dist/cli/record.js +93 -0
- package/dist/cli/usage.d.ts +1 -0
- package/dist/cli/usage.js +58 -0
- package/dist/cli/version.d.ts +2 -0
- package/dist/cli/version.js +7 -0
- package/dist/config/browsers.d.ts +11 -0
- package/dist/config/browsers.js +32 -0
- package/dist/config/config.js +8 -1
- package/dist/config/mask.d.ts +1 -1
- package/dist/config/mask.js +7 -2
- package/dist/config/run-settings.d.ts +1 -2
- package/dist/config/run-settings.js +1 -6
- package/dist/config/timeouts.d.ts +2 -0
- package/dist/config/timeouts.js +13 -0
- package/dist/config/workers.d.ts +14 -0
- package/dist/config/workers.js +29 -0
- package/dist/flows/run/branch-context.d.ts +14 -0
- package/dist/flows/run/branch-context.js +35 -0
- package/dist/flows/run/handlers/condition.d.ts +1 -1
- package/dist/flows/run/handlers/end.d.ts +1 -1
- package/dist/flows/run/handlers/group.d.ts +1 -1
- package/dist/flows/run/handlers/index.d.ts +2 -2
- package/dist/flows/run/handlers/parallel.d.ts +1 -1
- package/dist/flows/run/handlers/start.d.ts +1 -1
- package/dist/flows/run/handlers/support.d.ts +3 -24
- package/dist/flows/run/handlers/support.js +1 -1
- package/dist/flows/run/handlers/test.d.ts +1 -1
- package/dist/flows/run/handlers/types.d.ts +27 -0
- package/dist/flows/run/handlers/types.js +1 -0
- package/dist/flows/run/index.d.ts +1 -1
- package/dist/flows/run/prepare-flow.d.ts +14 -0
- package/dist/flows/run/prepare-flow.js +44 -0
- package/dist/flows/run/project.d.ts +33 -2
- package/dist/flows/run/project.js +139 -49
- package/dist/flows/run/run.d.ts +8 -4
- package/dist/flows/run/run.js +30 -194
- package/dist/flows/run/walker.d.ts +56 -0
- package/dist/flows/run/walker.js +225 -0
- package/dist/flows/steps/catalog.d.ts +1 -1
- package/dist/flows/steps/handler.d.ts +1 -1
- package/dist/flows/steps/set.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/locator/actionability.d.ts +33 -0
- package/dist/locator/actionability.js +104 -0
- package/dist/locator/chain-resolver.d.ts +20 -0
- package/dist/locator/chain-resolver.js +102 -0
- package/dist/locator/document-swap.d.ts +1 -0
- package/dist/locator/document-swap.js +11 -0
- package/dist/locator/element-state.d.ts +3 -1
- package/dist/locator/element-state.js +11 -3
- package/dist/locator/file-actions.d.ts +35 -0
- package/dist/locator/file-actions.js +55 -0
- package/dist/{script/helpers.js → locator/helper-realm.js} +2 -2
- package/dist/locator/input-actions.d.ts +18 -0
- package/dist/locator/input-actions.js +80 -0
- package/dist/locator/label-locator.d.ts +1 -1
- package/dist/locator/label-locator.js +5 -12
- package/dist/locator/locator.d.ts +14 -37
- package/dist/locator/locator.js +68 -424
- package/dist/locator/page-scripts.d.ts +16 -0
- package/dist/locator/page-scripts.js +54 -0
- package/dist/locator/page-search.d.ts +10 -0
- package/dist/locator/page-search.js +19 -0
- package/dist/locator/selector-errors.d.ts +0 -4
- package/dist/locator/selector-errors.js +0 -7
- package/dist/locator/selector.d.ts +14 -13
- package/dist/locator/selector.js +10 -16
- package/dist/locator/text-locator.d.ts +1 -1
- package/dist/locator/text-locator.js +5 -9
- package/dist/logger/index.d.ts +6 -0
- package/dist/logger/index.js +14 -7
- package/dist/network/bidi-wire.d.ts +10 -0
- package/dist/network/bidi-wire.js +38 -0
- package/dist/network/intercept.d.ts +30 -0
- package/dist/network/intercept.js +130 -0
- package/dist/network/network-tracker.d.ts +10 -47
- package/dist/network/network-tracker.js +34 -68
- package/dist/network/network-types.d.ts +33 -0
- package/dist/network/network-types.js +1 -0
- package/dist/network/network-wait.d.ts +7 -2
- package/dist/network/network-wait.js +14 -4
- package/dist/network/response.d.ts +1 -1
- package/dist/network/response.js +1 -1
- package/dist/network/route.d.ts +92 -0
- package/dist/network/route.js +142 -0
- package/dist/network/router.d.ts +6 -91
- package/dist/network/router.js +25 -258
- package/dist/recorder/expectation.d.ts +5 -0
- package/dist/recorder/expectation.js +20 -0
- package/dist/recorder/files-recorder.d.ts +30 -0
- package/dist/recorder/files-recorder.js +88 -0
- package/dist/recorder/normalise.d.ts +17 -6
- package/dist/recorder/normalise.js +96 -91
- package/dist/recorder/options.d.ts +56 -0
- package/dist/recorder/options.js +1 -0
- package/dist/recorder/page-messages.d.ts +35 -0
- package/dist/recorder/page-messages.js +26 -0
- package/dist/recorder/ranking.js +1 -0
- package/dist/recorder/realms.d.ts +6 -0
- package/dist/recorder/realms.js +13 -0
- package/dist/recorder/recorder.d.ts +10 -74
- package/dist/recorder/recorder.js +39 -199
- package/dist/recorder/session-browser.d.ts +22 -0
- package/dist/recorder/session-browser.js +21 -0
- package/dist/recorder/session-secrets.d.ts +8 -0
- package/dist/recorder/session-secrets.js +45 -0
- package/dist/recorder/session.d.ts +7 -13
- package/dist/recorder/session.js +22 -117
- package/dist/recorder/spec-source.d.ts +39 -0
- package/dist/recorder/spec-source.js +84 -0
- package/dist/recorder/toolbar/action-guard.d.ts +21 -0
- package/dist/recorder/toolbar/action-guard.js +43 -0
- package/dist/recorder/toolbar/actions/assert.d.ts +4 -0
- package/dist/recorder/toolbar/actions/assert.js +52 -0
- package/dist/recorder/toolbar/actions/insert.d.ts +6 -0
- package/dist/recorder/toolbar/actions/insert.js +93 -0
- package/dist/recorder/toolbar/actions/pick.d.ts +8 -0
- package/dist/recorder/toolbar/actions/pick.js +26 -0
- package/dist/recorder/toolbar/actions/reply.d.ts +7 -0
- package/dist/recorder/toolbar/actions/reply.js +10 -0
- package/dist/recorder/toolbar/controller-fixture.d.ts +47 -0
- package/dist/recorder/toolbar/controller-fixture.js +129 -0
- package/dist/recorder/toolbar/controller.d.ts +12 -78
- package/dist/recorder/toolbar/controller.js +35 -247
- package/dist/recorder/toolbar/inspect.d.ts +1 -3
- package/dist/recorder/toolbar/inspect.js +2 -2
- package/dist/recorder/toolbar/page-scope.d.ts +19 -0
- package/dist/recorder/toolbar/page-scope.js +43 -0
- package/dist/recorder/toolbar/protocol.d.ts +2 -1
- package/dist/recorder/toolbar/source/attach.d.ts +2 -0
- package/dist/recorder/toolbar/source/attach.js +41 -0
- package/dist/recorder/toolbar/source/dom.d.ts +2 -0
- package/dist/recorder/toolbar/source/dom.js +47 -0
- package/dist/recorder/toolbar/source/drag.d.ts +2 -0
- package/dist/recorder/toolbar/source/drag.js +32 -0
- package/dist/recorder/toolbar/source/events-page.d.ts +2 -0
- package/dist/recorder/toolbar/source/events-page.js +67 -0
- package/dist/recorder/toolbar/source/events-toolbar.d.ts +2 -0
- package/dist/recorder/toolbar/source/events-toolbar.js +35 -0
- package/dist/recorder/toolbar/source/fields.d.ts +2 -0
- package/dist/recorder/toolbar/source/fields.js +44 -0
- package/dist/recorder/toolbar/source/handlers.d.ts +2 -0
- package/dist/recorder/toolbar/source/handlers.js +134 -0
- package/dist/recorder/toolbar/source/names.d.ts +2 -0
- package/dist/recorder/toolbar/source/names.js +12 -0
- package/dist/recorder/toolbar/source/picking.d.ts +2 -0
- package/dist/recorder/toolbar/source/picking.js +72 -0
- package/dist/recorder/toolbar/source/placement.d.ts +2 -0
- package/dist/recorder/toolbar/source/placement.js +45 -0
- package/dist/recorder/toolbar/source/recorder-side.d.ts +2 -0
- package/dist/recorder/toolbar/source/recorder-side.js +54 -0
- package/dist/recorder/toolbar/source/setup.d.ts +2 -0
- package/dist/recorder/toolbar/source/setup.js +50 -0
- package/dist/recorder/toolbar/source/views-panels.d.ts +2 -0
- package/dist/recorder/toolbar/source/views-panels.js +150 -0
- package/dist/recorder/toolbar/source/views-rail.d.ts +2 -0
- package/dist/recorder/toolbar/source/views-rail.js +40 -0
- package/dist/recorder/toolbar/source/views-render.d.ts +2 -0
- package/dist/recorder/toolbar/source/views-render.js +48 -0
- package/dist/recorder/toolbar/source.js +30 -856
- package/dist/recorder/toolbar/types.d.ts +70 -0
- package/dist/recorder/toolbar/types.js +1 -0
- package/dist/recorder/toolbar-bridge.d.ts +43 -0
- package/dist/recorder/toolbar-bridge.js +106 -0
- package/dist/runner/browsers.d.ts +10 -0
- package/dist/runner/browsers.js +12 -0
- package/dist/runner/groups.d.ts +5 -1
- package/dist/runner/groups.js +9 -2
- package/dist/runner/options.d.ts +31 -0
- package/dist/runner/options.js +1 -0
- package/dist/runner/pooled-run.d.ts +43 -0
- package/dist/runner/pooled-run.js +75 -0
- package/dist/runner/ports.d.ts +2 -0
- package/dist/runner/ports.js +2 -0
- package/dist/runner/registry.d.ts +1 -1
- package/dist/runner/registry.js +1 -1
- package/dist/runner/reporter.d.ts +40 -11
- package/dist/runner/reporter.js +69 -16
- package/dist/runner/run-test.d.ts +16 -0
- package/dist/runner/run-test.js +178 -0
- package/dist/runner/run.d.ts +5 -2
- package/dist/runner/run.js +28 -28
- package/dist/runner/select.d.ts +24 -0
- package/dist/runner/select.js +77 -0
- package/dist/runner/test-dir.d.ts +13 -0
- package/dist/runner/test-dir.js +27 -0
- package/dist/runner/test-runner.d.ts +27 -50
- package/dist/runner/test-runner.js +102 -264
- package/dist/runner/worker-pool.d.ts +67 -0
- package/dist/runner/worker-pool.js +275 -0
- package/dist/runner/worker-process.d.ts +6 -0
- package/dist/runner/worker-process.js +54 -0
- package/dist/runner/worker-protocol.d.ts +67 -0
- package/dist/runner/worker-protocol.js +1 -0
- package/dist/runner/worker-session.d.ts +7 -0
- package/dist/runner/worker-session.js +60 -0
- package/dist/runner/worker.d.ts +26 -0
- package/dist/runner/worker.js +121 -0
- package/dist/runner/workers-option.d.ts +15 -0
- package/dist/runner/workers-option.js +22 -0
- package/dist/script/call-function.d.ts +1 -9
- package/dist/script/call-function.js +2 -19
- package/dist/script/element-handle.d.ts +1 -2
- package/dist/script/element-handle.js +1 -2
- package/dist/script/serialize.d.ts +3 -12
- package/dist/script/serialize.js +53 -62
- package/dist/steps/parse/call.d.ts +3 -0
- package/dist/steps/parse/call.js +81 -0
- package/dist/steps/parse/expectation.d.ts +5 -0
- package/dist/steps/parse/expectation.js +71 -0
- package/dist/steps/parse/file-entry.d.ts +4 -0
- package/dist/steps/parse/file-entry.js +74 -0
- package/dist/steps/parse/literals.d.ts +4 -0
- package/dist/steps/parse/literals.js +28 -0
- package/dist/steps/parse/step.d.ts +4 -0
- package/dist/steps/parse/step.js +81 -0
- package/dist/steps/parse.js +2 -323
- package/dist/transport/bidi-connection.d.ts +4 -2
- package/dist/transport/bidi-connection.js +9 -43
- package/dist/transport/log-redaction.d.ts +13 -0
- package/dist/transport/log-redaction.js +51 -0
- package/dist/transport/subscriptions.js +18 -13
- package/dist/transport/unsupported.d.ts +7 -0
- package/dist/transport/unsupported.js +13 -0
- package/dist/types/bidi-modules/browser.d.ts +1 -12
- package/dist/types/bidi-modules/browsing-context.d.ts +8 -18
- package/dist/types/bidi-modules/emulation.d.ts +29 -21
- package/dist/types/bidi-modules/input.d.ts +1 -9
- package/dist/types/bidi-modules/network.d.ts +29 -24
- package/dist/types/bidi-modules/script.d.ts +0 -9
- package/dist/types/bidi-modules/session.d.ts +0 -8
- package/dist/types/bidi-modules/storage.d.ts +0 -5
- package/dist/types/bidi.d.ts +7 -37
- package/dist/types/config.d.ts +17 -3
- package/dist/types/test.d.ts +15 -2
- package/dist/wait/wait-until.d.ts +1 -5
- package/dist/wait/wait-until.js +7 -16
- package/package.json +3 -2
- package/dist/config/default.d.ts +0 -2
- package/dist/config/default.js +0 -3
- package/dist/types/bidi-modules/web-extensions.d.ts +0 -9
- package/dist/types/bidi-protocols/command.d.ts +0 -7
- package/dist/types/bidi-protocols/event.d.ts +0 -17
- /package/dist/{script/helpers.d.ts → locator/helper-realm.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ test("Visitor can sign in", async ({ page, env, secrets }) => {
|
|
|
19
19
|
- **Auto-waiting.** Actions wait until the element is attached, visible, stable, enabled and not covered. Assertions retry until they pass or time out.
|
|
20
20
|
- **Semantic locators.** By role, label, text, test id, CSS or XPath, with ordered fallbacks.
|
|
21
21
|
- **Environments and secrets.** One test file runs against `dev`, `staging` or `production`; secrets come from the environment or a `.env.<name>` file and are masked in logs and reports.
|
|
22
|
+
- **Parallel runs.** Tests run side by side in worker processes (`--workers`, `workers` in the config; half the CPUs by default), and a flow's Parallel node runs its branches at the same time (see [Running tests and reports](docs/running-and-reports.md#parallel-runs)).
|
|
22
23
|
- **Network control.** Mock, block or modify requests; wait for requests, responses and network idle; read response bodies.
|
|
23
24
|
- **Flows.** Chain tests and groups into a graph with conditions, variables, waits and HTTP requests, and run it from the command line or CI (see [Flows](docs/flows.md)).
|
|
24
25
|
- **Recorder building blocks.** Specs are parsed into steps and edited back with minimal diffs, and an engine records what a person does in the browser. A toolbar in the recorded page adds assertions, variables and secrets, waits, screenshots and pause (see [Steps and the recorder](docs/recorder.md)). The Electron app that ties them together lives in a separate repository (see the [roadmap](docs/roadmap.md)).
|
|
@@ -57,7 +58,7 @@ bun install
|
|
|
57
58
|
|
|
58
59
|
### 3. Write a test
|
|
59
60
|
|
|
60
|
-
Tests live in the folder `srcDir` points to (`./src/tests` in `samurai.config.ts`) and end in `.spec.ts`.
|
|
61
|
+
Tests live in the folder `srcDir` points to (`./src/tests` in `samurai.config.ts`) and end in `.spec.ts`. This repository has one as `src/tests/example.spec.ts`:
|
|
61
62
|
|
|
62
63
|
```ts
|
|
63
64
|
import { expect, test } from "../api.js";
|
|
@@ -82,7 +83,7 @@ import { defineConfig } from "./src/config/config.js";
|
|
|
82
83
|
|
|
83
84
|
export default defineConfig({
|
|
84
85
|
srcDir: "./src/tests",
|
|
85
|
-
|
|
86
|
+
browsers: ["firefox"],
|
|
86
87
|
timeout: 30000,
|
|
87
88
|
environments: {
|
|
88
89
|
staging: { baseURL: "https://staging.example.com" },
|
|
@@ -106,8 +107,6 @@ bun run dev --timeout 60000 # per-test timeout in ms; also --expect-timeout
|
|
|
106
107
|
|
|
107
108
|
A browser window opens for each test and closes when the test ends (`--headless` hides it; on CI it is the default, see [Configuration](docs/configuration.md#headless)). Each test's result is printed as it finishes, followed by a summary, and the full report is written to `result/report.json`. The process exits with code 0 when every test passed, 1 otherwise and 2 for a usage or configuration error. Framework logs go to stderr and `logs/`.
|
|
108
109
|
|
|
109
|
-
> `src/tests/index.spec.ts` is an example that submits a real contact form. Delete it or replace it before running everything.
|
|
110
|
-
|
|
111
110
|
### Flows (optional)
|
|
112
111
|
|
|
113
112
|
A project can also keep flows in `flows/*.flow.json` (edited in the Samurai app) that chain tests and groups with conditions and variables. `samurai flow check` validates them and `samurai flow run <flow>` runs them, with a report in `result/flows/`. See [Flows](docs/flows.md).
|
|
@@ -142,7 +141,7 @@ Read it in a test as `secrets.TEST_PASSWORD`. Real environment variables win ove
|
|
|
142
141
|
| [Contributing](docs/contributing.md) | Project layout, running the framework's own tests, conventions |
|
|
143
142
|
| [Roadmap](docs/roadmap.md) | What is built and what is still open |
|
|
144
143
|
|
|
145
|
-
The [documentation index](docs/README.md) lists every guide
|
|
144
|
+
The [documentation index](docs/README.md) lists every guide by topic.
|
|
146
145
|
|
|
147
146
|
## Scripts
|
|
148
147
|
|
|
@@ -11,7 +11,7 @@ export interface AssertionErrorDetails {
|
|
|
11
11
|
matcher: string;
|
|
12
12
|
expected: unknown;
|
|
13
13
|
actual: unknown;
|
|
14
|
-
/**
|
|
14
|
+
/** Description of the asserted locator, only for locator assertions */
|
|
15
15
|
locator?: string | undefined;
|
|
16
16
|
}
|
|
17
17
|
export declare class AssertionError extends Error {
|
package/dist/assert/expect.d.ts
CHANGED
|
@@ -1,54 +1,9 @@
|
|
|
1
1
|
import Locator from "../locator/locator.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export declare function takePendingAssertions(): string[];
|
|
8
|
-
export interface LocatorAssertionOptions {
|
|
9
|
-
/** Time (ms) to keep retrying. Defaults to config `expect.timeout`, then 5000 */
|
|
10
|
-
timeout?: number;
|
|
11
|
-
}
|
|
12
|
-
export declare class ValueAssertions<T> {
|
|
13
|
-
private readonly actual;
|
|
14
|
-
private readonly negated;
|
|
15
|
-
constructor(actual: T, negated?: boolean);
|
|
16
|
-
/** Inverts the next matcher */
|
|
17
|
-
get not(): ValueAssertions<T>;
|
|
18
|
-
private assert;
|
|
19
|
-
private compare;
|
|
20
|
-
/** Strict identity (`Object.is`) */
|
|
21
|
-
toBe(expected: unknown): void;
|
|
22
|
-
/** Deep structural equality */
|
|
23
|
-
toEqual(expected: unknown): void;
|
|
24
|
-
toBeTruthy(): void;
|
|
25
|
-
toBeFalsy(): void;
|
|
26
|
-
/** Array includes `item`, or string contains substring `item` */
|
|
27
|
-
toContain(item: unknown): void;
|
|
28
|
-
/** String contains `pattern` (string) or matches it (RegExp) */
|
|
29
|
-
toMatch(pattern: string | RegExp): void;
|
|
30
|
-
toBeGreaterThan(expected: number | bigint): void;
|
|
31
|
-
toBeGreaterThanOrEqual(expected: number | bigint): void;
|
|
32
|
-
toBeLessThan(expected: number | bigint): void;
|
|
33
|
-
toBeLessThanOrEqual(expected: number | bigint): void;
|
|
34
|
-
}
|
|
35
|
-
export declare class LocatorAssertions {
|
|
36
|
-
private readonly locator;
|
|
37
|
-
private readonly negated;
|
|
38
|
-
constructor(locator: Locator, negated?: boolean);
|
|
39
|
-
/** Inverts the next matcher: it retries until the condition is false */
|
|
40
|
-
get not(): LocatorAssertions;
|
|
41
|
-
/** Re-reads the element until `check` passes or the timeout expires */
|
|
42
|
-
private assertEventually;
|
|
43
|
-
toBeVisible(options?: LocatorAssertionOptions): Promise<void>;
|
|
44
|
-
/** Trimmed text content equals `expected` (string) or matches it (RegExp) */
|
|
45
|
-
toHaveText(expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
46
|
-
/** Text content contains `expected` (string) or matches it (RegExp) */
|
|
47
|
-
toContainText(expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
48
|
-
toHaveValue(expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
49
|
-
toHaveAttribute(name: string, expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
50
|
-
toHaveCount(expected: number, options?: LocatorAssertionOptions): Promise<void>;
|
|
51
|
-
}
|
|
2
|
+
import { LocatorAssertions } from "./locator-assertions.js";
|
|
3
|
+
import { ValueAssertions } from "./value-assertions.js";
|
|
4
|
+
export { takePendingAssertions } from "./pending-assertions.js";
|
|
5
|
+
export { LocatorAssertions } from "./locator-assertions.js";
|
|
6
|
+
export { ValueAssertions } from "./value-assertions.js";
|
|
52
7
|
/** Auto-retrying assertions on a page element */
|
|
53
8
|
export declare function expect(locator: Locator): LocatorAssertions;
|
|
54
9
|
/** Synchronous assertions on a plain value */
|
package/dist/assert/expect.js
CHANGED
|
@@ -1,186 +1,9 @@
|
|
|
1
|
-
import { isDeepStrictEqual } from "node:util";
|
|
2
|
-
import { AssertionError } from "./assertion-error.js";
|
|
3
1
|
import Locator from "../locator/locator.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const pendingAssertions = new Map();
|
|
10
|
-
/**
|
|
11
|
-
* Returns the matcher names of locator assertions that were never awaited and are
|
|
12
|
-
* still running or already failed, and stops tracking them. The runner calls this
|
|
13
|
-
* after a test to detect a missing `await`.
|
|
14
|
-
*/
|
|
15
|
-
export function takePendingAssertions() {
|
|
16
|
-
const names = [...pendingAssertions.values()];
|
|
17
|
-
pendingAssertions.clear();
|
|
18
|
-
return names;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Promise that stops being pending as soon as anyone subscribes to it. `await`,
|
|
22
|
-
* `.catch`, `.finally`, `Promise.all` and `assert.rejects` all go through `then`
|
|
23
|
-
* (`await` calls it because the constructor is not the native Promise).
|
|
24
|
-
*/
|
|
25
|
-
class TrackedAssertion extends Promise {
|
|
26
|
-
/** Promises derived through `then` are plain promises */
|
|
27
|
-
static get [Symbol.species]() {
|
|
28
|
-
return Promise;
|
|
29
|
-
}
|
|
30
|
-
then(onfulfilled, onrejected) {
|
|
31
|
-
pendingAssertions.delete(this);
|
|
32
|
-
return super.then(onfulfilled, onrejected);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function track(matcher, assertion) {
|
|
36
|
-
const tracked = new TrackedAssertion((resolve, reject) => {
|
|
37
|
-
assertion.then(resolve, reject);
|
|
38
|
-
});
|
|
39
|
-
pendingAssertions.set(tracked, matcher);
|
|
40
|
-
// The native then does not count as awaiting: a pass stops tracking, a failure stays
|
|
41
|
-
// tracked. Handling the rejection keeps an un-awaited failure from being unhandled.
|
|
42
|
-
Promise.prototype.then.call(tracked, () => pendingAssertions.delete(tracked), () => { });
|
|
43
|
-
return tracked;
|
|
44
|
-
}
|
|
45
|
-
function matchesText(actual, expected, mode) {
|
|
46
|
-
// search() ignores lastIndex, so global regexps behave the same on every call
|
|
47
|
-
if (expected instanceof RegExp)
|
|
48
|
-
return actual.search(expected) !== -1;
|
|
49
|
-
return mode === "exact" ? actual === expected : actual.includes(expected);
|
|
50
|
-
}
|
|
51
|
-
function isNumeric(value) {
|
|
52
|
-
return typeof value === "number" || typeof value === "bigint";
|
|
53
|
-
}
|
|
54
|
-
export class ValueAssertions {
|
|
55
|
-
constructor(actual, negated = false) {
|
|
56
|
-
this.actual = actual;
|
|
57
|
-
this.negated = negated;
|
|
58
|
-
}
|
|
59
|
-
/** Inverts the next matcher */
|
|
60
|
-
get not() {
|
|
61
|
-
return new ValueAssertions(this.actual, !this.negated);
|
|
62
|
-
}
|
|
63
|
-
assert(pass, matcher, expected) {
|
|
64
|
-
if (pass !== this.negated)
|
|
65
|
-
return;
|
|
66
|
-
throw new AssertionError({
|
|
67
|
-
matcher: this.negated ? `not.${matcher}` : matcher,
|
|
68
|
-
expected,
|
|
69
|
-
actual: this.actual,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
compare(matcher, expected, check) {
|
|
73
|
-
const actual = this.actual;
|
|
74
|
-
if (!isNumeric(actual) || !isNumeric(expected)) {
|
|
75
|
-
throw new TypeError(`${matcher} expects numbers, received ${typeof actual} and ${typeof expected}`);
|
|
76
|
-
}
|
|
77
|
-
this.assert(check(actual, expected), matcher, expected);
|
|
78
|
-
}
|
|
79
|
-
/** Strict identity (`Object.is`) */
|
|
80
|
-
toBe(expected) {
|
|
81
|
-
this.assert(Object.is(this.actual, expected), "toBe", expected);
|
|
82
|
-
}
|
|
83
|
-
/** Deep structural equality */
|
|
84
|
-
toEqual(expected) {
|
|
85
|
-
this.assert(isDeepStrictEqual(this.actual, expected), "toEqual", expected);
|
|
86
|
-
}
|
|
87
|
-
toBeTruthy() {
|
|
88
|
-
this.assert(Boolean(this.actual), "toBeTruthy", "truthy");
|
|
89
|
-
}
|
|
90
|
-
toBeFalsy() {
|
|
91
|
-
this.assert(!this.actual, "toBeFalsy", "falsy");
|
|
92
|
-
}
|
|
93
|
-
/** Array includes `item`, or string contains substring `item` */
|
|
94
|
-
toContain(item) {
|
|
95
|
-
const actual = this.actual;
|
|
96
|
-
if (typeof actual === "string") {
|
|
97
|
-
if (typeof item !== "string") {
|
|
98
|
-
throw new TypeError(`toContain on a string expects a string, received ${typeof item}`);
|
|
99
|
-
}
|
|
100
|
-
this.assert(actual.includes(item), "toContain", item);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (Array.isArray(actual)) {
|
|
104
|
-
this.assert(actual.includes(item), "toContain", item);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
throw new TypeError(`toContain expects an array or string, received ${typeof actual}`);
|
|
108
|
-
}
|
|
109
|
-
/** String contains `pattern` (string) or matches it (RegExp) */
|
|
110
|
-
toMatch(pattern) {
|
|
111
|
-
const actual = this.actual;
|
|
112
|
-
if (typeof actual !== "string") {
|
|
113
|
-
throw new TypeError(`toMatch expects a string, received ${typeof actual}`);
|
|
114
|
-
}
|
|
115
|
-
this.assert(matchesText(actual, pattern, "contains"), "toMatch", pattern);
|
|
116
|
-
}
|
|
117
|
-
toBeGreaterThan(expected) {
|
|
118
|
-
this.compare("toBeGreaterThan", expected, (a, b) => a > b);
|
|
119
|
-
}
|
|
120
|
-
toBeGreaterThanOrEqual(expected) {
|
|
121
|
-
this.compare("toBeGreaterThanOrEqual", expected, (a, b) => a >= b);
|
|
122
|
-
}
|
|
123
|
-
toBeLessThan(expected) {
|
|
124
|
-
this.compare("toBeLessThan", expected, (a, b) => a < b);
|
|
125
|
-
}
|
|
126
|
-
toBeLessThanOrEqual(expected) {
|
|
127
|
-
this.compare("toBeLessThanOrEqual", expected, (a, b) => a <= b);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
export class LocatorAssertions {
|
|
131
|
-
constructor(locator, negated = false) {
|
|
132
|
-
this.locator = locator;
|
|
133
|
-
this.negated = negated;
|
|
134
|
-
}
|
|
135
|
-
/** Inverts the next matcher: it retries until the condition is false */
|
|
136
|
-
get not() {
|
|
137
|
-
return new LocatorAssertions(this.locator, !this.negated);
|
|
138
|
-
}
|
|
139
|
-
/** Re-reads the element until `check` passes or the timeout expires */
|
|
140
|
-
assertEventually(matcher, expected, read, check, options) {
|
|
141
|
-
const name = this.negated ? `not.${matcher}` : matcher;
|
|
142
|
-
return track(name, (async () => {
|
|
143
|
-
const timeout = await resolveTimeout(options?.timeout);
|
|
144
|
-
try {
|
|
145
|
-
await waitUntil(read, (actual) => check(actual) !== this.negated, { timeout });
|
|
146
|
-
}
|
|
147
|
-
catch (err) {
|
|
148
|
-
if (!(err instanceof WaitTimeoutError))
|
|
149
|
-
throw err;
|
|
150
|
-
// Reads never resolve to undefined, so an undefined `last` means no read completed
|
|
151
|
-
if (err.last === undefined) {
|
|
152
|
-
throw new Error(`expect(locator).${name}: could not read ${this.locator.selector} within ${timeout}ms`);
|
|
153
|
-
}
|
|
154
|
-
throw new AssertionError({
|
|
155
|
-
matcher: name,
|
|
156
|
-
expected,
|
|
157
|
-
actual: err.last,
|
|
158
|
-
locator: this.locator.selector,
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
})());
|
|
162
|
-
}
|
|
163
|
-
toBeVisible(options) {
|
|
164
|
-
return this.assertEventually("toBeVisible", true, () => this.locator.isVisible(), (visible) => visible, options);
|
|
165
|
-
}
|
|
166
|
-
/** Trimmed text content equals `expected` (string) or matches it (RegExp) */
|
|
167
|
-
toHaveText(expected, options) {
|
|
168
|
-
return this.assertEventually("toHaveText", expected, () => this.locator.textContent(), (text) => text !== null && matchesText(text, expected, "exact"), options);
|
|
169
|
-
}
|
|
170
|
-
/** Text content contains `expected` (string) or matches it (RegExp) */
|
|
171
|
-
toContainText(expected, options) {
|
|
172
|
-
return this.assertEventually("toContainText", expected, () => this.locator.textContent(), (text) => text !== null && matchesText(text, expected, "contains"), options);
|
|
173
|
-
}
|
|
174
|
-
toHaveValue(expected, options) {
|
|
175
|
-
return this.assertEventually("toHaveValue", expected, () => this.locator.inputValue(), (value) => value !== null && matchesText(value, expected, "exact"), options);
|
|
176
|
-
}
|
|
177
|
-
toHaveAttribute(name, expected, options) {
|
|
178
|
-
return this.assertEventually("toHaveAttribute", expected, () => this.locator.getAttribute(name), (value) => value !== null && matchesText(value, expected, "exact"), options);
|
|
179
|
-
}
|
|
180
|
-
toHaveCount(expected, options) {
|
|
181
|
-
return this.assertEventually("toHaveCount", expected, () => this.locator.count(), (count) => count === expected, options);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
2
|
+
import { LocatorAssertions } from "./locator-assertions.js";
|
|
3
|
+
import { ValueAssertions } from "./value-assertions.js";
|
|
4
|
+
export { takePendingAssertions } from "./pending-assertions.js";
|
|
5
|
+
export { LocatorAssertions } from "./locator-assertions.js";
|
|
6
|
+
export { ValueAssertions } from "./value-assertions.js";
|
|
184
7
|
export function expect(value) {
|
|
185
8
|
return value instanceof Locator
|
|
186
9
|
? new LocatorAssertions(value)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type Locator from "../locator/locator.js";
|
|
2
|
+
export interface LocatorAssertionOptions {
|
|
3
|
+
/** Time (ms) to keep retrying. Defaults to config `expect.timeout`, then 5000 */
|
|
4
|
+
timeout?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class LocatorAssertions {
|
|
7
|
+
private readonly locator;
|
|
8
|
+
private readonly negated;
|
|
9
|
+
constructor(locator: Locator, negated?: boolean);
|
|
10
|
+
/** Inverts the next matcher: it retries until the condition is false */
|
|
11
|
+
get not(): LocatorAssertions;
|
|
12
|
+
/** Re-reads the element until `check` passes or the timeout expires */
|
|
13
|
+
private assertEventually;
|
|
14
|
+
toBeVisible(options?: LocatorAssertionOptions): Promise<void>;
|
|
15
|
+
/** Trimmed text content equals `expected` (string) or matches it (RegExp) */
|
|
16
|
+
toHaveText(expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
17
|
+
/** Text content contains `expected` (string) or matches it (RegExp) */
|
|
18
|
+
toContainText(expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
19
|
+
toHaveValue(expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
20
|
+
toHaveAttribute(name: string, expected: string | RegExp, options?: LocatorAssertionOptions): Promise<void>;
|
|
21
|
+
toHaveCount(expected: number, options?: LocatorAssertionOptions): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { resolveTimeout } from "../config/timeouts.js";
|
|
2
|
+
import { waitUntil, WaitTimeoutError } from "../wait/wait-until.js";
|
|
3
|
+
import { AssertionError } from "./assertion-error.js";
|
|
4
|
+
import { track } from "./pending-assertions.js";
|
|
5
|
+
import { textCheck } from "./text-match.js";
|
|
6
|
+
export class LocatorAssertions {
|
|
7
|
+
constructor(locator, negated = false) {
|
|
8
|
+
this.locator = locator;
|
|
9
|
+
this.negated = negated;
|
|
10
|
+
}
|
|
11
|
+
/** Inverts the next matcher: it retries until the condition is false */
|
|
12
|
+
get not() {
|
|
13
|
+
return new LocatorAssertions(this.locator, !this.negated);
|
|
14
|
+
}
|
|
15
|
+
/** Re-reads the element until `check` passes or the timeout expires */
|
|
16
|
+
assertEventually(matcher, expected, read, check, options) {
|
|
17
|
+
const name = this.negated ? `not.${matcher}` : matcher;
|
|
18
|
+
return track(name, (async () => {
|
|
19
|
+
const timeout = await resolveTimeout(options?.timeout);
|
|
20
|
+
try {
|
|
21
|
+
await waitUntil(read, (actual) => check(actual) !== this.negated, {
|
|
22
|
+
timeout,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
if (!(err instanceof WaitTimeoutError))
|
|
27
|
+
throw err;
|
|
28
|
+
// Reads never resolve to undefined, so an undefined `last` means no read completed
|
|
29
|
+
if (err.last === undefined) {
|
|
30
|
+
throw new Error(`expect(locator).${name}: could not read ${this.locator.selector} within ${timeout}ms`);
|
|
31
|
+
}
|
|
32
|
+
throw new AssertionError({
|
|
33
|
+
matcher: name,
|
|
34
|
+
expected,
|
|
35
|
+
actual: err.last,
|
|
36
|
+
locator: this.locator.selector,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
})());
|
|
40
|
+
}
|
|
41
|
+
toBeVisible(options) {
|
|
42
|
+
return this.assertEventually("toBeVisible", true, () => this.locator.isVisible(), (visible) => visible, options);
|
|
43
|
+
}
|
|
44
|
+
/** Trimmed text content equals `expected` (string) or matches it (RegExp) */
|
|
45
|
+
toHaveText(expected, options) {
|
|
46
|
+
return this.assertEventually("toHaveText", expected, () => this.locator.textContent(), textCheck(expected, "exact"), options);
|
|
47
|
+
}
|
|
48
|
+
/** Text content contains `expected` (string) or matches it (RegExp) */
|
|
49
|
+
toContainText(expected, options) {
|
|
50
|
+
return this.assertEventually("toContainText", expected, () => this.locator.textContent(), textCheck(expected, "contains"), options);
|
|
51
|
+
}
|
|
52
|
+
toHaveValue(expected, options) {
|
|
53
|
+
return this.assertEventually("toHaveValue", expected, () => this.locator.inputValue(), textCheck(expected, "exact"), options);
|
|
54
|
+
}
|
|
55
|
+
toHaveAttribute(name, expected, options) {
|
|
56
|
+
return this.assertEventually("toHaveAttribute", expected, () => this.locator.getAttribute(name), textCheck(expected, "exact"), options);
|
|
57
|
+
}
|
|
58
|
+
toHaveCount(expected, options) {
|
|
59
|
+
return this.assertEventually("toHaveCount", expected, () => this.locator.count(), (count) => count === expected, options);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the matcher names of locator assertions that were never awaited and are
|
|
3
|
+
* still running or already failed, and stops tracking them. The runner calls this
|
|
4
|
+
* after a test to detect a missing `await`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function takePendingAssertions(): string[];
|
|
7
|
+
export declare function track(matcher: string, assertion: Promise<void>): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locator assertions nobody has awaited yet that are still running or have failed,
|
|
3
|
+
* mapped to their matcher name
|
|
4
|
+
*/
|
|
5
|
+
const pendingAssertions = new Map();
|
|
6
|
+
/**
|
|
7
|
+
* Returns the matcher names of locator assertions that were never awaited and are
|
|
8
|
+
* still running or already failed, and stops tracking them. The runner calls this
|
|
9
|
+
* after a test to detect a missing `await`.
|
|
10
|
+
*/
|
|
11
|
+
export function takePendingAssertions() {
|
|
12
|
+
const names = [...pendingAssertions.values()];
|
|
13
|
+
pendingAssertions.clear();
|
|
14
|
+
return names;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Promise that stops being pending as soon as anyone subscribes to it. `await`,
|
|
18
|
+
* `.catch`, `.finally`, `Promise.all` and `assert.rejects` all go through `then`
|
|
19
|
+
* (`await` calls it because the constructor is not the native Promise).
|
|
20
|
+
*/
|
|
21
|
+
class TrackedAssertion extends Promise {
|
|
22
|
+
/** Promises derived through `then` are plain promises */
|
|
23
|
+
static get [Symbol.species]() {
|
|
24
|
+
return Promise;
|
|
25
|
+
}
|
|
26
|
+
then(onfulfilled, onrejected) {
|
|
27
|
+
pendingAssertions.delete(this);
|
|
28
|
+
return super.then(onfulfilled, onrejected);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function track(matcher, assertion) {
|
|
32
|
+
const tracked = new TrackedAssertion((resolve, reject) => {
|
|
33
|
+
assertion.then(resolve, reject);
|
|
34
|
+
});
|
|
35
|
+
pendingAssertions.set(tracked, matcher);
|
|
36
|
+
// The native then does not count as awaiting: a pass stops tracking, a failure stays
|
|
37
|
+
// tracked. Handling the rejection keeps an un-awaited failure from being unhandled.
|
|
38
|
+
Promise.prototype.then.call(tracked, () => pendingAssertions.delete(tracked), () => { });
|
|
39
|
+
return tracked;
|
|
40
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Whether `actual` equals (`exact`) or contains (`contains`) `expected`, or matches it when it is a RegExp */
|
|
2
|
+
export declare function matchesText(actual: string, expected: string | RegExp, mode: "exact" | "contains"): boolean;
|
|
3
|
+
/** A check for a read that may find nothing (`null`): true when the text is there and matches */
|
|
4
|
+
export declare function textCheck(expected: string | RegExp, mode: "exact" | "contains"): (actual: string | null) => boolean;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Whether `actual` equals (`exact`) or contains (`contains`) `expected`, or matches it when it is a RegExp */
|
|
2
|
+
export function matchesText(actual, expected, mode) {
|
|
3
|
+
// search() ignores lastIndex, so global regexps behave the same on every call
|
|
4
|
+
if (expected instanceof RegExp)
|
|
5
|
+
return actual.search(expected) !== -1;
|
|
6
|
+
return mode === "exact" ? actual === expected : actual.includes(expected);
|
|
7
|
+
}
|
|
8
|
+
/** A check for a read that may find nothing (`null`): true when the text is there and matches */
|
|
9
|
+
export function textCheck(expected, mode) {
|
|
10
|
+
return (actual) => actual !== null && matchesText(actual, expected, mode);
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare class ValueAssertions<T> {
|
|
2
|
+
private readonly actual;
|
|
3
|
+
private readonly negated;
|
|
4
|
+
constructor(actual: T, negated?: boolean);
|
|
5
|
+
/** Inverts the next matcher */
|
|
6
|
+
get not(): ValueAssertions<T>;
|
|
7
|
+
private assert;
|
|
8
|
+
private compare;
|
|
9
|
+
/** Strict identity (`Object.is`) */
|
|
10
|
+
toBe(expected: unknown): void;
|
|
11
|
+
/** Deep structural equality */
|
|
12
|
+
toEqual(expected: unknown): void;
|
|
13
|
+
toBeTruthy(): void;
|
|
14
|
+
toBeFalsy(): void;
|
|
15
|
+
/** Array includes `item`, or string contains substring `item` */
|
|
16
|
+
toContain(item: unknown): void;
|
|
17
|
+
/** String contains `pattern` (string) or matches it (RegExp) */
|
|
18
|
+
toMatch(pattern: string | RegExp): void;
|
|
19
|
+
toBeGreaterThan(expected: number | bigint): void;
|
|
20
|
+
toBeGreaterThanOrEqual(expected: number | bigint): void;
|
|
21
|
+
toBeLessThan(expected: number | bigint): void;
|
|
22
|
+
toBeLessThanOrEqual(expected: number | bigint): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { AssertionError } from "./assertion-error.js";
|
|
3
|
+
import { matchesText } from "./text-match.js";
|
|
4
|
+
function isNumeric(value) {
|
|
5
|
+
return typeof value === "number" || typeof value === "bigint";
|
|
6
|
+
}
|
|
7
|
+
export class ValueAssertions {
|
|
8
|
+
constructor(actual, negated = false) {
|
|
9
|
+
this.actual = actual;
|
|
10
|
+
this.negated = negated;
|
|
11
|
+
}
|
|
12
|
+
/** Inverts the next matcher */
|
|
13
|
+
get not() {
|
|
14
|
+
return new ValueAssertions(this.actual, !this.negated);
|
|
15
|
+
}
|
|
16
|
+
assert(pass, matcher, expected) {
|
|
17
|
+
if (pass !== this.negated)
|
|
18
|
+
return;
|
|
19
|
+
throw new AssertionError({
|
|
20
|
+
matcher: this.negated ? `not.${matcher}` : matcher,
|
|
21
|
+
expected,
|
|
22
|
+
actual: this.actual,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
compare(matcher, expected, check) {
|
|
26
|
+
const actual = this.actual;
|
|
27
|
+
if (!isNumeric(actual) || !isNumeric(expected)) {
|
|
28
|
+
throw new TypeError(`${matcher} expects numbers, received ${typeof actual} and ${typeof expected}`);
|
|
29
|
+
}
|
|
30
|
+
this.assert(check(actual, expected), matcher, expected);
|
|
31
|
+
}
|
|
32
|
+
/** Strict identity (`Object.is`) */
|
|
33
|
+
toBe(expected) {
|
|
34
|
+
this.assert(Object.is(this.actual, expected), "toBe", expected);
|
|
35
|
+
}
|
|
36
|
+
/** Deep structural equality */
|
|
37
|
+
toEqual(expected) {
|
|
38
|
+
this.assert(isDeepStrictEqual(this.actual, expected), "toEqual", expected);
|
|
39
|
+
}
|
|
40
|
+
toBeTruthy() {
|
|
41
|
+
this.assert(Boolean(this.actual), "toBeTruthy", "truthy");
|
|
42
|
+
}
|
|
43
|
+
toBeFalsy() {
|
|
44
|
+
this.assert(!this.actual, "toBeFalsy", "falsy");
|
|
45
|
+
}
|
|
46
|
+
/** Array includes `item`, or string contains substring `item` */
|
|
47
|
+
toContain(item) {
|
|
48
|
+
const actual = this.actual;
|
|
49
|
+
if (typeof actual === "string") {
|
|
50
|
+
if (typeof item !== "string") {
|
|
51
|
+
throw new TypeError(`toContain on a string expects a string, received ${typeof item}`);
|
|
52
|
+
}
|
|
53
|
+
this.assert(actual.includes(item), "toContain", item);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(actual)) {
|
|
57
|
+
this.assert(actual.includes(item), "toContain", item);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
throw new TypeError(`toContain expects an array or string, received ${typeof actual}`);
|
|
61
|
+
}
|
|
62
|
+
/** String contains `pattern` (string) or matches it (RegExp) */
|
|
63
|
+
toMatch(pattern) {
|
|
64
|
+
const actual = this.actual;
|
|
65
|
+
if (typeof actual !== "string") {
|
|
66
|
+
throw new TypeError(`toMatch expects a string, received ${typeof actual}`);
|
|
67
|
+
}
|
|
68
|
+
this.assert(matchesText(actual, pattern, "contains"), "toMatch", pattern);
|
|
69
|
+
}
|
|
70
|
+
toBeGreaterThan(expected) {
|
|
71
|
+
this.compare("toBeGreaterThan", expected, (a, b) => a > b);
|
|
72
|
+
}
|
|
73
|
+
toBeGreaterThanOrEqual(expected) {
|
|
74
|
+
this.compare("toBeGreaterThanOrEqual", expected, (a, b) => a >= b);
|
|
75
|
+
}
|
|
76
|
+
toBeLessThan(expected) {
|
|
77
|
+
this.compare("toBeLessThan", expected, (a, b) => a < b);
|
|
78
|
+
}
|
|
79
|
+
toBeLessThanOrEqual(expected) {
|
|
80
|
+
this.compare("toBeLessThanOrEqual", expected, (a, b) => a <= b);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -2,7 +2,6 @@ import type { BiDiConnector } from "../transport/bidi-connection.js";
|
|
|
2
2
|
import type { SameSite } from "../types/bidi-modules/network.js";
|
|
3
3
|
import { type EmulationOptions } from "./emulation.js";
|
|
4
4
|
import type Page from "./page.js";
|
|
5
|
-
export type { SameSite } from "../types/bidi-modules/network.js";
|
|
6
5
|
/** A cookie as `context.cookies()` returns it */
|
|
7
6
|
export interface Cookie {
|
|
8
7
|
name: string;
|
|
@@ -21,7 +21,10 @@ export class BrowserContext {
|
|
|
21
21
|
/** Opens a tab in this context */
|
|
22
22
|
async newPage() {
|
|
23
23
|
this.assertOpen();
|
|
24
|
-
return this.host.newPage({
|
|
24
|
+
return this.host.newPage({
|
|
25
|
+
type: "tab",
|
|
26
|
+
...(!this.isDefault && { userContext: this.id }),
|
|
27
|
+
});
|
|
25
28
|
}
|
|
26
29
|
/** The pages opened in this context that are still open */
|
|
27
30
|
pages() {
|
|
@@ -38,7 +41,10 @@ export class BrowserContext {
|
|
|
38
41
|
this.assertOpen();
|
|
39
42
|
const prepared = cookies.map(toBiDiCookie);
|
|
40
43
|
for (const cookie of prepared) {
|
|
41
|
-
await this.host.connector.send("storage.setCookie", {
|
|
44
|
+
await this.host.connector.send("storage.setCookie", {
|
|
45
|
+
cookie,
|
|
46
|
+
partition: this.partition(),
|
|
47
|
+
});
|
|
42
48
|
}
|
|
43
49
|
}
|
|
44
50
|
/** The cookies of this context, optionally only those matching `filter` */
|
|
@@ -84,11 +90,13 @@ export class BrowserContext {
|
|
|
84
90
|
/** Removes the context; the browser closes its pages. The default context cannot be closed */
|
|
85
91
|
async close() {
|
|
86
92
|
if (this.isDefault)
|
|
87
|
-
throw new Error("cannot close the default context");
|
|
93
|
+
throw new Error("close(): cannot close the default context");
|
|
88
94
|
if (this.isClosed)
|
|
89
95
|
return;
|
|
90
96
|
try {
|
|
91
|
-
await this.host.connector.send("browser.removeUserContext", {
|
|
97
|
+
await this.host.connector.send("browser.removeUserContext", {
|
|
98
|
+
userContext: this.id,
|
|
99
|
+
});
|
|
92
100
|
}
|
|
93
101
|
catch (err) {
|
|
94
102
|
// Already gone: the goal is met
|
|
@@ -118,7 +126,7 @@ function toBiDiCookie(input) {
|
|
|
118
126
|
secure ??= url.protocol === "https:";
|
|
119
127
|
}
|
|
120
128
|
if (domain === undefined)
|
|
121
|
-
throw new TypeError(`cookie "${input.name}" needs a domain or a url`);
|
|
129
|
+
throw new TypeError(`addCookies(): cookie "${input.name}" needs a domain or a url`);
|
|
122
130
|
return {
|
|
123
131
|
name: input.name,
|
|
124
132
|
value: { type: "string", value: input.value },
|