@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/dist/runner/reporter.js
CHANGED
|
@@ -2,14 +2,15 @@ import { mkdir, writeFile } from "fs/promises";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { projectDir } from "../config/config.js";
|
|
4
4
|
import { maskDeep } from "../config/mask.js";
|
|
5
|
-
/** Results are keyed by file
|
|
6
|
-
function keyOf(test) {
|
|
7
|
-
return `${test.file}::${test.name}`;
|
|
5
|
+
/** Results are keyed by file, full title and browser, so equal titles in different files or describe blocks, and the same test in two browsers, stay apart */
|
|
6
|
+
function keyOf(test, browser) {
|
|
7
|
+
return `${test.file}::${test.name}::${browser}`;
|
|
8
8
|
}
|
|
9
9
|
export default class TestReporter {
|
|
10
10
|
constructor(options) {
|
|
11
11
|
this.testResults = new Map();
|
|
12
12
|
this.environment = options.environment;
|
|
13
|
+
this.browsers = [...options.browsers];
|
|
13
14
|
this.output =
|
|
14
15
|
options.output ?? path.join(projectDir(), "result/report.json");
|
|
15
16
|
this.listener = options.onEvent;
|
|
@@ -22,26 +23,46 @@ export default class TestReporter {
|
|
|
22
23
|
// A listener must not break the run
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
/** Lists the tests of the run in the order the report shows them, whichever order they end in */
|
|
27
|
+
setOrder(tests) {
|
|
28
|
+
this.order = tests.map(({ test, browser }) => keyOf(test, browser));
|
|
29
|
+
}
|
|
30
|
+
onStart(total, workers = 1) {
|
|
31
|
+
const t = performance.mark("run-start");
|
|
27
32
|
this.summary = {
|
|
28
33
|
duration: 0,
|
|
29
34
|
status: "failed",
|
|
30
35
|
startTime: t.startTime,
|
|
31
36
|
environment: this.environment,
|
|
37
|
+
browsers: this.browsers,
|
|
32
38
|
tests: [],
|
|
33
39
|
};
|
|
34
|
-
this.emit({
|
|
40
|
+
this.emit({
|
|
41
|
+
type: "run-start",
|
|
42
|
+
environment: this.environment,
|
|
43
|
+
total,
|
|
44
|
+
browsers: this.browsers,
|
|
45
|
+
...(workers > 1 && { workers }),
|
|
46
|
+
});
|
|
35
47
|
}
|
|
36
48
|
async onEnd() {
|
|
37
|
-
performance.mark(
|
|
38
|
-
const duration = performance.measure("test-duration",
|
|
49
|
+
performance.mark("run-end");
|
|
50
|
+
const duration = performance.measure("test-duration", "run-start", "run-end").duration;
|
|
51
|
+
const tests = this.order
|
|
52
|
+
? [
|
|
53
|
+
...this.order.flatMap((key) => this.testResults.get(key) ?? []),
|
|
54
|
+
...[...this.testResults]
|
|
55
|
+
.filter(([key]) => !this.order.includes(key))
|
|
56
|
+
.map(([, result]) => result),
|
|
57
|
+
]
|
|
58
|
+
: [...this.testResults.values()];
|
|
39
59
|
this.summary = {
|
|
40
60
|
...this.summary,
|
|
41
61
|
duration,
|
|
42
62
|
environment: this.environment,
|
|
43
|
-
|
|
44
|
-
|
|
63
|
+
browsers: this.browsers,
|
|
64
|
+
tests,
|
|
65
|
+
status: tests.every((r) => r.status === "success")
|
|
45
66
|
? "success"
|
|
46
67
|
: "failed",
|
|
47
68
|
};
|
|
@@ -54,19 +75,45 @@ export default class TestReporter {
|
|
|
54
75
|
this.emit({ type: "run-end", summary: this.summary });
|
|
55
76
|
return this.summary;
|
|
56
77
|
}
|
|
57
|
-
onTestStart(test) {
|
|
58
|
-
const key = keyOf(test);
|
|
78
|
+
onTestStart(test, browser) {
|
|
79
|
+
const key = keyOf(test, browser);
|
|
59
80
|
const t = performance.mark(`${key}-start`);
|
|
60
81
|
this.testResults.set(key, {
|
|
61
82
|
name: test.name,
|
|
62
83
|
file: test.file,
|
|
84
|
+
browser,
|
|
63
85
|
status: "started",
|
|
64
86
|
startTime: t.startTime,
|
|
65
87
|
});
|
|
66
|
-
this.emit({
|
|
88
|
+
this.emit({
|
|
89
|
+
type: "test-start",
|
|
90
|
+
name: test.name,
|
|
91
|
+
file: test.file,
|
|
92
|
+
browser,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Ends a test that ran in a worker: the worker built the result (masked, with its own duration), this keeps the
|
|
97
|
+
* start time from this process's clock, as the worker's clock has another origin
|
|
98
|
+
*/
|
|
99
|
+
onRemoteEnd(test, browser, result) {
|
|
100
|
+
const key = keyOf(test, browser);
|
|
101
|
+
const started = this.testResults.get(key);
|
|
102
|
+
const ended = {
|
|
103
|
+
...result,
|
|
104
|
+
...(started && { startTime: started.startTime }),
|
|
105
|
+
};
|
|
106
|
+
this.testResults.set(key, ended);
|
|
107
|
+
this.emit({
|
|
108
|
+
type: "test-end",
|
|
109
|
+
name: test.name,
|
|
110
|
+
file: test.file,
|
|
111
|
+
browser,
|
|
112
|
+
result: ended,
|
|
113
|
+
});
|
|
67
114
|
}
|
|
68
|
-
onTestEnd(test, error, extras) {
|
|
69
|
-
const key = keyOf(test);
|
|
115
|
+
onTestEnd(test, browser, error, extras) {
|
|
116
|
+
const key = keyOf(test, browser);
|
|
70
117
|
performance.mark(`${key}-finish`);
|
|
71
118
|
const duration = performance.measure("test-duration", `${key}-start`, `${key}-finish`).duration;
|
|
72
119
|
const result = maskDeep({
|
|
@@ -79,6 +126,12 @@ export default class TestReporter {
|
|
|
79
126
|
...(extras?.screenshots?.length && { screenshots: extras.screenshots }),
|
|
80
127
|
});
|
|
81
128
|
this.testResults.set(key, result);
|
|
82
|
-
this.emit({
|
|
129
|
+
this.emit({
|
|
130
|
+
type: "test-end",
|
|
131
|
+
name: test.name,
|
|
132
|
+
file: test.file,
|
|
133
|
+
browser,
|
|
134
|
+
result,
|
|
135
|
+
});
|
|
83
136
|
}
|
|
84
137
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SupportedBrowser } from "../types/browser.js";
|
|
2
|
+
import type { RegisteredTestCase } from "../types/test.js";
|
|
3
|
+
import type { RunnerOptions } from "./options.js";
|
|
4
|
+
import type { PreparedRun } from "./prepare-run.js";
|
|
5
|
+
import type { TestSink } from "./reporter.js";
|
|
6
|
+
/** What a worker gives its tests that the main process does not: its own port and profile, and its number */
|
|
7
|
+
export interface WorkerResources {
|
|
8
|
+
/** The browser's debugging port. @default `options.port`, else DEFAULT_PORT */
|
|
9
|
+
port?: number;
|
|
10
|
+
/** The browser's profile folder. @default the browser's own */
|
|
11
|
+
profileDir?: string;
|
|
12
|
+
/** The `workerIndex` fixture. @default 0 */
|
|
13
|
+
workerIndex?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Runs one test in one browser and reports it: launch, run the body, judge the page's logs, close the browser */
|
|
16
|
+
export declare function runOneTest(test: RegisteredTestCase, browserName: SupportedBrowser, run: PreparedRun, options: RunnerOptions, srcDir: string, reporter: TestSink, resources?: WorkerResources): Promise<void>;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { takePendingAssertions } from "../assert/expect.js";
|
|
3
|
+
import { Browser } from "../browser/browser.js";
|
|
4
|
+
import { Evidence } from "../browser/evidence.js";
|
|
5
|
+
import { projectDir, readConfig } from "../config/config.js";
|
|
6
|
+
import { createSecretsFixture } from "../config/secrets.js";
|
|
7
|
+
import { createEnvFixture } from "../config/variables.js";
|
|
8
|
+
import logger from "../logger/index.js";
|
|
9
|
+
import { ConnectionClosedError } from "../transport/bidi-connection.js";
|
|
10
|
+
import { judgePageLogs } from "./page-logs-report.js";
|
|
11
|
+
import { DEFAULT_PORT } from "./ports.js";
|
|
12
|
+
import { testDirOf } from "./test-dir.js";
|
|
13
|
+
import { toTestError } from "./test-error.js";
|
|
14
|
+
/**
|
|
15
|
+
* The state of one test run. A test ends once, whichever of finishing, failing, timing out, an abort or a
|
|
16
|
+
* closed browser comes first: that one is reported and the rest are ignored.
|
|
17
|
+
*/
|
|
18
|
+
class TestOutcome {
|
|
19
|
+
constructor(reporter, test, browser) {
|
|
20
|
+
this.reporter = reporter;
|
|
21
|
+
this.test = test;
|
|
22
|
+
this.browser = browser;
|
|
23
|
+
/** The test has been reported: later calls to `finish` do nothing */
|
|
24
|
+
this.ended = false;
|
|
25
|
+
/** The runner itself is closing the browser: its exit is expected and is not a failure */
|
|
26
|
+
this.closing = false;
|
|
27
|
+
/** A person closed the browser, or it crashed, while the test ran */
|
|
28
|
+
this.browserClosed = false;
|
|
29
|
+
}
|
|
30
|
+
get isEnded() {
|
|
31
|
+
return this.ended;
|
|
32
|
+
}
|
|
33
|
+
finish(error, logs) {
|
|
34
|
+
if (this.ended)
|
|
35
|
+
return;
|
|
36
|
+
this.ended = true;
|
|
37
|
+
this.reporter.onTestEnd(this.test, this.browser, error, {
|
|
38
|
+
...logs,
|
|
39
|
+
screenshots: this.evidence?.screenshots,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Runs one test in one browser and reports it: launch, run the body, judge the page's logs, close the browser */
|
|
44
|
+
export async function runOneTest(test, browserName, run, options, srcDir, reporter, resources = {}) {
|
|
45
|
+
logger.verbose("Starting test: %s in %s", test.name, browserName);
|
|
46
|
+
reporter.onTestStart(test, browserName);
|
|
47
|
+
const outcome = new TestOutcome(reporter, test, browserName);
|
|
48
|
+
const { settings, secrets } = run;
|
|
49
|
+
const timeout = settings.timeout;
|
|
50
|
+
const { reportPath } = options;
|
|
51
|
+
const resultDir = typeof reportPath === "string"
|
|
52
|
+
? path.dirname(reportPath)
|
|
53
|
+
: path.join(projectDir(), "result");
|
|
54
|
+
let browser;
|
|
55
|
+
let page;
|
|
56
|
+
let timeoutTimer;
|
|
57
|
+
let onRunAborted;
|
|
58
|
+
const timeoutController = new AbortController();
|
|
59
|
+
let browserGone = () => { };
|
|
60
|
+
/** Resolves when the time is up or the run is aborted; the test is reported failed then */
|
|
61
|
+
const limit = new Promise((resolve) => {
|
|
62
|
+
timeoutTimer = setTimeout(() => {
|
|
63
|
+
outcome.finish({
|
|
64
|
+
message: `Test timed out after ${timeout / 1000} seconds`,
|
|
65
|
+
type: "timeout",
|
|
66
|
+
});
|
|
67
|
+
timeoutController.abort(new Error("Test timed out"));
|
|
68
|
+
resolve();
|
|
69
|
+
}, timeout);
|
|
70
|
+
onRunAborted = () => {
|
|
71
|
+
if (timeoutController.signal.aborted)
|
|
72
|
+
return;
|
|
73
|
+
outcome.finish({ message: "Run aborted", type: "error" });
|
|
74
|
+
timeoutController.abort(new Error("Run aborted"));
|
|
75
|
+
resolve();
|
|
76
|
+
};
|
|
77
|
+
options.signal?.addEventListener("abort", onRunAborted, { once: true });
|
|
78
|
+
});
|
|
79
|
+
/** Resolves when the browser exits on its own while the test runs */
|
|
80
|
+
const gone = new Promise((resolve) => (browserGone = resolve));
|
|
81
|
+
/** Launches the browser, runs the test body and reports how it went */
|
|
82
|
+
const runBody = async () => {
|
|
83
|
+
try {
|
|
84
|
+
const launched = await Browser.launch(browserName, {
|
|
85
|
+
port: resources.port ?? options.port ?? DEFAULT_PORT,
|
|
86
|
+
...(resources.profileDir && { profileDir: resources.profileDir }),
|
|
87
|
+
headless: options.headless ?? settings.headless,
|
|
88
|
+
}, timeoutController.signal);
|
|
89
|
+
browser = launched.browser;
|
|
90
|
+
// A person closing the window, or the browser crashing: end the test now instead of waiting for a
|
|
91
|
+
// command to time out
|
|
92
|
+
void launched.browser.exited.then(() => {
|
|
93
|
+
if (outcome.isEnded || outcome.closing)
|
|
94
|
+
return;
|
|
95
|
+
outcome.browserClosed = true;
|
|
96
|
+
outcome.finish({
|
|
97
|
+
message: "The browser was closed while the test was running",
|
|
98
|
+
type: "error",
|
|
99
|
+
});
|
|
100
|
+
timeoutController.abort(new Error("Browser closed"));
|
|
101
|
+
browserGone();
|
|
102
|
+
});
|
|
103
|
+
page = launched.page;
|
|
104
|
+
outcome.evidence = new Evidence(resultDir, `${browserName}/${testDirOf(test, srcDir)}`);
|
|
105
|
+
launched.page.setEvidence(outcome.evidence);
|
|
106
|
+
const fixtures = {
|
|
107
|
+
page: launched.page,
|
|
108
|
+
browser: launched.browser,
|
|
109
|
+
browserName,
|
|
110
|
+
workerIndex: resources.workerIndex ?? 0,
|
|
111
|
+
env: createEnvFixture(settings.environment, settings.variables),
|
|
112
|
+
secrets: createSecretsFixture(settings.environment, secrets),
|
|
113
|
+
};
|
|
114
|
+
await test.function(fixtures);
|
|
115
|
+
// Already reported as timed out
|
|
116
|
+
if (timeoutController.signal.aborted)
|
|
117
|
+
return;
|
|
118
|
+
const unawaited = takePendingAssertions();
|
|
119
|
+
if (unawaited.length > 0) {
|
|
120
|
+
outcome.finish({
|
|
121
|
+
message: unawaited
|
|
122
|
+
.map((matcher) => `expect(locator).${matcher}() was not awaited`)
|
|
123
|
+
.join("\n"),
|
|
124
|
+
type: "assertion",
|
|
125
|
+
}, await judgeLogs(page, true));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const verdict = await judgeLogs(page, false);
|
|
129
|
+
outcome.finish(verdict.error, verdict);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
if (timeoutController.signal.aborted)
|
|
133
|
+
return;
|
|
134
|
+
outcome.finish(outcome.browserClosed || err instanceof ConnectionClosedError
|
|
135
|
+
? {
|
|
136
|
+
message: "The browser was closed while the test was running",
|
|
137
|
+
type: "error",
|
|
138
|
+
}
|
|
139
|
+
: toTestError(err), await judgeLogs(page, true));
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
await Promise.race([limit, gone, runBody()]);
|
|
143
|
+
clearTimeout(timeoutTimer);
|
|
144
|
+
if (onRunAborted)
|
|
145
|
+
options.signal?.removeEventListener("abort", onRunAborted);
|
|
146
|
+
takePendingAssertions();
|
|
147
|
+
logger.verbose("Closing browser for test: %s", test.name);
|
|
148
|
+
outcome.closing = true;
|
|
149
|
+
await browser?.close();
|
|
150
|
+
}
|
|
151
|
+
/** Reads the page's log once the test body is done and applies config `logs` (see judgePageLogs) */
|
|
152
|
+
async function judgeLogs(page, testFailed) {
|
|
153
|
+
if (!page)
|
|
154
|
+
return {};
|
|
155
|
+
try {
|
|
156
|
+
// Events and command replies share one ordered connection: after this, everything logged so far is in
|
|
157
|
+
await page.syncLogs();
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
logger.debug("Could not sync the page's logs", { err });
|
|
161
|
+
}
|
|
162
|
+
let config;
|
|
163
|
+
try {
|
|
164
|
+
config = await readConfig("logs");
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
logger.debug("Could not read logs config, using defaults", { err });
|
|
168
|
+
}
|
|
169
|
+
return judgePageLogs({
|
|
170
|
+
config,
|
|
171
|
+
entries: page.getLogs(),
|
|
172
|
+
errors: page.pageErrors(),
|
|
173
|
+
dropped: page.logsDropped,
|
|
174
|
+
testFailed,
|
|
175
|
+
allowPageErrors: page.pageErrorsAllowed,
|
|
176
|
+
routeErrors: page.routeErrors(),
|
|
177
|
+
});
|
|
178
|
+
}
|
package/dist/runner/run.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { TestSummary } from "../types/test.js";
|
|
2
2
|
import { type ProjectOptions } from "./project.js";
|
|
3
3
|
import type { ResolvedGroup } from "./groups.js";
|
|
4
|
-
import {
|
|
4
|
+
import type { RunnerOptions } from "./options.js";
|
|
5
5
|
export interface RunTestsOptions extends RunnerOptions, ProjectOptions {
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
@@ -24,8 +24,11 @@ export interface FoundTest {
|
|
|
24
24
|
export declare function listTests(options?: RunTestsOptions): Promise<FoundTest[]>;
|
|
25
25
|
/** Imports the project's specs without running anything and lists the config's groups with the tests each holds */
|
|
26
26
|
export declare function listGroups(options?: RunTestsOptions): Promise<ResolvedGroup[]>;
|
|
27
|
+
export { BROWSER_NAMES, checkBrowserList } from "../config/browsers.js";
|
|
28
|
+
export { parseWorkers } from "../config/workers.js";
|
|
27
29
|
export type { RunEvent } from "./reporter.js";
|
|
28
30
|
export type { TestError, TestResult, TestSummary } from "../types/test.js";
|
|
29
31
|
export type { ResolvedGroup } from "./groups.js";
|
|
30
|
-
export type { RunnerOptions } from "./
|
|
32
|
+
export type { RunnerOptions } from "./options.js";
|
|
31
33
|
export type { ProjectOptions } from "./project.js";
|
|
34
|
+
export { DEFAULT_PORT } from "./ports.js";
|
package/dist/runner/run.js
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
import { withProject } from "./project.js";
|
|
2
|
-
import TestRunner
|
|
2
|
+
import TestRunner from "./test-runner.js";
|
|
3
|
+
/**
|
|
4
|
+
* Sets the project up (config, environment, secrets), builds a runner from the rest of the options and hands both to
|
|
5
|
+
* `work`. `headless` stays with the runner options on purpose: the runner reads it, and the project's run settings
|
|
6
|
+
* (`withProject`'s own `headless`) are left to the config.
|
|
7
|
+
*/
|
|
8
|
+
function inProject(options, work) {
|
|
9
|
+
const { projectDir, config, dataDir, environment, timeout, expectTimeout, ...runner } = options;
|
|
10
|
+
return withProject(
|
|
11
|
+
// The cast is for `exactOptionalPropertyTypes`: these may be undefined, which the project options leave out
|
|
12
|
+
{
|
|
13
|
+
projectDir,
|
|
14
|
+
config,
|
|
15
|
+
dataDir,
|
|
16
|
+
environment,
|
|
17
|
+
timeout,
|
|
18
|
+
expectTimeout,
|
|
19
|
+
}, async (run) => work(await TestRunner.init(run, runner, {
|
|
20
|
+
inlineConfig: config !== undefined,
|
|
21
|
+
...(dataDir !== undefined && { dataDir }),
|
|
22
|
+
})));
|
|
23
|
+
}
|
|
3
24
|
/**
|
|
4
25
|
* Runs the project's tests, one after the other, and resolves with the summary (also written to the report).
|
|
5
26
|
* Spec files are TypeScript: the process must be able to import them (run under `tsx`, or with Node's own
|
|
@@ -10,37 +31,16 @@ import TestRunner, {} from "./test-runner.js";
|
|
|
10
31
|
* onEvent: (event) => console.log(event.type) });
|
|
11
32
|
*/
|
|
12
33
|
export async function runTests(options = {}) {
|
|
13
|
-
|
|
14
|
-
return withProject({
|
|
15
|
-
projectDir,
|
|
16
|
-
config,
|
|
17
|
-
dataDir,
|
|
18
|
-
environment,
|
|
19
|
-
timeout,
|
|
20
|
-
expectTimeout,
|
|
21
|
-
}, async (run) => (await TestRunner.init(run, undefined, runner)).start());
|
|
34
|
+
return inProject(options, (runner) => runner.start());
|
|
22
35
|
}
|
|
23
36
|
/** Imports the project's specs without running anything and lists the tests the options select */
|
|
24
37
|
export async function listTests(options = {}) {
|
|
25
|
-
|
|
26
|
-
return withProject({
|
|
27
|
-
projectDir,
|
|
28
|
-
config,
|
|
29
|
-
dataDir,
|
|
30
|
-
environment,
|
|
31
|
-
timeout,
|
|
32
|
-
expectTimeout,
|
|
33
|
-
}, async (run) => (await (await TestRunner.init(run, undefined, runner)).register()).map(({ name, file }) => ({ name, file })));
|
|
38
|
+
return inProject(options, async (runner) => (await runner.register()).map(({ name, file }) => ({ name, file })));
|
|
34
39
|
}
|
|
35
40
|
/** Imports the project's specs without running anything and lists the config's groups with the tests each holds */
|
|
36
41
|
export async function listGroups(options = {}) {
|
|
37
|
-
|
|
38
|
-
return withProject({
|
|
39
|
-
projectDir,
|
|
40
|
-
config,
|
|
41
|
-
dataDir,
|
|
42
|
-
environment,
|
|
43
|
-
timeout,
|
|
44
|
-
expectTimeout,
|
|
45
|
-
}, async (run) => (await TestRunner.init(run, undefined, runner)).groups());
|
|
42
|
+
return inProject(options, (runner) => runner.groups());
|
|
46
43
|
}
|
|
44
|
+
export { BROWSER_NAMES, checkBrowserList } from "../config/browsers.js";
|
|
45
|
+
export { parseWorkers } from "../config/workers.js";
|
|
46
|
+
export { DEFAULT_PORT } from "./ports.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SupportedBrowser } from "../types/browser.js";
|
|
2
|
+
import type { RegisteredTestCase } from "../types/test.js";
|
|
3
|
+
import { type ResolvedGroup } from "./groups.js";
|
|
4
|
+
import type { RunnerOptions } from "./options.js";
|
|
5
|
+
/** The specs a run imported, with the config's groups resolved against them */
|
|
6
|
+
export interface LoadedSpecs {
|
|
7
|
+
all: RegisteredTestCase[];
|
|
8
|
+
groups: ResolvedGroup[];
|
|
9
|
+
/** The real `srcDir` the groups were resolved against */
|
|
10
|
+
srcDir: string;
|
|
11
|
+
}
|
|
12
|
+
/** Whether a test with this full name is selected by the options */
|
|
13
|
+
export declare function isSelected(name: string, { grep, testNames }: Pick<RunnerOptions, "grep" | "testNames">): boolean;
|
|
14
|
+
/** Id of a registered test: its spec file relative to `srcDir`, then its name */
|
|
15
|
+
export declare function idOf(test: RegisteredTestCase, srcDir: string): string;
|
|
16
|
+
/** Imports the spec files once and resolves the config's groups against what they registered */
|
|
17
|
+
export declare function loadSpecs(testFiles: string[]): Promise<LoadedSpecs>;
|
|
18
|
+
/** The tests the options select: by `group`, `testNames` and `grep` */
|
|
19
|
+
export declare function selectTests({ all, groups, srcDir }: LoadedSpecs, options: RunnerOptions): RegisteredTestCase[];
|
|
20
|
+
/**
|
|
21
|
+
* The browsers the run uses: the options' (`--browser`), else the selected group's, else the project's, else
|
|
22
|
+
* Firefox
|
|
23
|
+
*/
|
|
24
|
+
export declare function browsersOfRun(groups: ResolvedGroup[], options: RunnerOptions): Promise<SupportedBrowser[]>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { pathToFileURL } from "url";
|
|
3
|
+
import { loadConfig } from "../config/config.js";
|
|
4
|
+
import logger from "../logger/index.js";
|
|
5
|
+
import { browsersFor } from "./browsers.js";
|
|
6
|
+
import { findGroup, resolveGroups, testId, toPosix, } from "./groups.js";
|
|
7
|
+
import { assertModuleProject } from "./module-type.js";
|
|
8
|
+
import { clearRegistry, registeredTests } from "./registry.js";
|
|
9
|
+
import { realDirs } from "./test-dir.js";
|
|
10
|
+
/** Whether a test with this full name is selected by the options */
|
|
11
|
+
export function isSelected(name, { grep, testNames }) {
|
|
12
|
+
if (testNames && !testNames.includes(name))
|
|
13
|
+
return false;
|
|
14
|
+
if (grep === undefined)
|
|
15
|
+
return true;
|
|
16
|
+
if (typeof grep === "string")
|
|
17
|
+
return name.includes(grep);
|
|
18
|
+
grep.lastIndex = 0;
|
|
19
|
+
return grep.test(name);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Imports the spec files and returns every test they register. A fresh registry and a fresh import of each
|
|
23
|
+
* file, so a second run in this process registers its tests again (and sees edited specs, for files loaded as
|
|
24
|
+
* ES modules; CommonJS files stay cached until the process ends)
|
|
25
|
+
*/
|
|
26
|
+
async function registerAll(testFiles) {
|
|
27
|
+
clearRegistry();
|
|
28
|
+
const stamp = `?run=${Date.now().toString(36)}`;
|
|
29
|
+
for (const file of testFiles)
|
|
30
|
+
assertModuleProject(file);
|
|
31
|
+
for (const file of testFiles) {
|
|
32
|
+
logger.verbose("Trying to register file: %s", file);
|
|
33
|
+
await import(pathToFileURL(file).href + stamp);
|
|
34
|
+
}
|
|
35
|
+
return [...registeredTests()];
|
|
36
|
+
}
|
|
37
|
+
/** Id of a registered test: its spec file relative to `srcDir`, then its name */
|
|
38
|
+
export function idOf(test, srcDir) {
|
|
39
|
+
return testId(toPosix(path.relative(srcDir, test.file)), test.name);
|
|
40
|
+
}
|
|
41
|
+
/** The config's groups, resolved against the tests the specs register */
|
|
42
|
+
async function resolveAll(tests) {
|
|
43
|
+
const config = await loadConfig();
|
|
44
|
+
const { dir, srcDir } = realDirs(config.srcDir);
|
|
45
|
+
const groups = resolveGroups(config, tests.map(({ file, name }) => ({
|
|
46
|
+
file: toPosix(path.relative(srcDir, file)),
|
|
47
|
+
name,
|
|
48
|
+
})), dir, srcDir);
|
|
49
|
+
return { groups, srcDir };
|
|
50
|
+
}
|
|
51
|
+
/** Imports the spec files once and resolves the config's groups against what they registered */
|
|
52
|
+
export async function loadSpecs(testFiles) {
|
|
53
|
+
const all = await registerAll(testFiles);
|
|
54
|
+
return { all, ...(await resolveAll(all)) };
|
|
55
|
+
}
|
|
56
|
+
/** The tests the options select: by `group`, `testNames` and `grep` */
|
|
57
|
+
export function selectTests({ all, groups, srcDir }, options) {
|
|
58
|
+
const { group } = options;
|
|
59
|
+
let inGroup = () => true;
|
|
60
|
+
if (group !== undefined) {
|
|
61
|
+
const members = new Set(findGroup(groups, group).testIds);
|
|
62
|
+
inGroup = (test) => members.has(idOf(test, srcDir));
|
|
63
|
+
}
|
|
64
|
+
return all.filter((test) => inGroup(test) && isSelected(test.name, options));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The browsers the run uses: the options' (`--browser`), else the selected group's, else the project's, else
|
|
68
|
+
* Firefox
|
|
69
|
+
*/
|
|
70
|
+
export async function browsersOfRun(groups, options) {
|
|
71
|
+
const { browsers: flag, group } = options;
|
|
72
|
+
const config = await loadConfig();
|
|
73
|
+
const ofGroup = group !== undefined && !(flag && flag.length > 0)
|
|
74
|
+
? findGroup(groups, group).browsers
|
|
75
|
+
: undefined;
|
|
76
|
+
return browsersFor({ flag, group: ofGroup, project: config.browsers });
|
|
77
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RegisteredTestCase } from "../types/test.js";
|
|
2
|
+
/** The project and source folders as real paths: registered test files come from stack traces, which resolve symlinks */
|
|
3
|
+
export declare function realDirs(configuredSrcDir: string | undefined): {
|
|
4
|
+
dir: string;
|
|
5
|
+
srcDir: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* The folder of a test's screenshots below `screenshots/`: its spec file without extension, then its cleaned
|
|
9
|
+
* name. A spec outside srcDir loses the `..` (and a drive or root) its relative path starts with, so it stays
|
|
10
|
+
* below `screenshots/`.
|
|
11
|
+
* @internal Exported for tests
|
|
12
|
+
*/
|
|
13
|
+
export declare function testDirOf(test: RegisteredTestCase, srcDir: string): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { projectDir } from "../config/config.js";
|
|
3
|
+
import { cleanName } from "../browser/evidence.js";
|
|
4
|
+
import { DEFAULT_SRC_DIR, realPath, toPosix } from "./groups.js";
|
|
5
|
+
/** The project and source folders as real paths: registered test files come from stack traces, which resolve symlinks */
|
|
6
|
+
export function realDirs(configuredSrcDir) {
|
|
7
|
+
const dir = realPath(projectDir());
|
|
8
|
+
const srcDir = realPath(path.resolve(dir, configuredSrcDir ?? DEFAULT_SRC_DIR));
|
|
9
|
+
return { dir, srcDir };
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The folder of a test's screenshots below `screenshots/`: its spec file without extension, then its cleaned
|
|
13
|
+
* name. A spec outside srcDir loses the `..` (and a drive or root) its relative path starts with, so it stays
|
|
14
|
+
* below `screenshots/`.
|
|
15
|
+
* @internal Exported for tests
|
|
16
|
+
*/
|
|
17
|
+
export function testDirOf(test, srcDir) {
|
|
18
|
+
const file = toPosix(path.relative(srcDir, test.file))
|
|
19
|
+
.replace(/\.[cm]?[jt]sx?$/, "")
|
|
20
|
+
.split("/")
|
|
21
|
+
.filter((segment) => segment !== "" &&
|
|
22
|
+
segment !== "." &&
|
|
23
|
+
segment !== ".." &&
|
|
24
|
+
!/^[A-Za-z]:$/.test(segment))
|
|
25
|
+
.join("/");
|
|
26
|
+
return `${file}/${cleanName(test.name)}`;
|
|
27
|
+
}
|
|
@@ -1,63 +1,40 @@
|
|
|
1
1
|
import type { RegisteredTestCase } from "../types/test.js";
|
|
2
|
-
import type { PreparedRun } from "./prepare-run.js";
|
|
3
|
-
import { type RunEvent } from "./reporter.js";
|
|
4
|
-
import type { SamuraiGroup } from "../types/config.js";
|
|
5
2
|
import type { ResolvedGroup } from "./groups.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
reportPath?: string | false;
|
|
22
|
-
/** Receives start and end of the run and of every test, as they happen */
|
|
23
|
-
onEvent?: (event: RunEvent) => void;
|
|
24
|
-
/** Aborting stops the running test (its browser is closed), skips the rest and reports the run as failed */
|
|
25
|
-
signal?: AbortSignal;
|
|
3
|
+
import type { RunnerOptions } from "./options.js";
|
|
4
|
+
import type { PreparedRun } from "./prepare-run.js";
|
|
5
|
+
import { type PooledRunner } from "./pooled-run.js";
|
|
6
|
+
import { type PoolOptions } from "./worker-pool.js";
|
|
7
|
+
export type { RunnerOptions } from "./options.js";
|
|
8
|
+
/** What a runner knows about the project it runs in, besides the options of the run */
|
|
9
|
+
export interface ProjectInfo {
|
|
10
|
+
/** The config was given as an object, not read from `samurai.config.ts`: a worker process cannot get it */
|
|
11
|
+
inlineConfig?: boolean;
|
|
12
|
+
/** Where the framework keeps browser profiles; a worker needs the same folder */
|
|
13
|
+
dataDir?: string;
|
|
14
|
+
/** Makes the pool of a run with more than one worker; tests give a fake */
|
|
15
|
+
createPool?: (options: PoolOptions) => PooledRunner & {
|
|
16
|
+
dispose(): Promise<void>;
|
|
17
|
+
};
|
|
26
18
|
}
|
|
27
|
-
/**
|
|
28
|
-
export declare function isSelected(name: string, { grep, testNames }: Pick<RunnerOptions, "grep" | "testNames">): boolean;
|
|
19
|
+
/** Finds the spec files, picks the tests and browsers of a run and runs each test in each browser */
|
|
29
20
|
export default class TestRunner {
|
|
30
21
|
private testFiles;
|
|
31
22
|
private run;
|
|
32
|
-
private group?;
|
|
33
23
|
private options;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
static init(run: PreparedRun, group?: SamuraiGroup, options?: RunnerOptions): Promise<TestRunner>;
|
|
38
|
-
/**
|
|
39
|
-
* Imports the spec files and returns every test they register. A fresh registry and a fresh import of each
|
|
40
|
-
* file, so a second run in this process registers its tests again (and sees edited specs, for files loaded as
|
|
41
|
-
* ES modules; CommonJS files stay cached until the process ends)
|
|
42
|
-
*/
|
|
43
|
-
private registerAll;
|
|
44
|
-
/** Id of a registered test: its spec file relative to `srcDir`, then its name */
|
|
45
|
-
private idOf;
|
|
46
|
-
/** The config's groups, resolved against the tests the specs register */
|
|
47
|
-
private resolveAll;
|
|
24
|
+
private project;
|
|
25
|
+
constructor(testFiles: string[], run: PreparedRun, options?: RunnerOptions, project?: ProjectInfo);
|
|
26
|
+
static init(run: PreparedRun, options?: RunnerOptions, project?: ProjectInfo): Promise<TestRunner>;
|
|
48
27
|
/** The groups of the config with the tests each holds; see `listGroups` */
|
|
49
28
|
groups(): Promise<ResolvedGroup[]>;
|
|
50
29
|
/** Imports the spec files and returns the tests the options select: by `group`, `testNames` and `grep` */
|
|
51
30
|
register(): Promise<RegisteredTestCase[]>;
|
|
52
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Runs the selected tests in every browser of the run, one browser after the other, and returns the summary that
|
|
33
|
+
* was also written to the report. With more than one worker the tests run at the same time in worker processes.
|
|
34
|
+
*/
|
|
53
35
|
start(): Promise<import("../types/test.js").TestSummary>;
|
|
54
|
-
|
|
36
|
+
/** Whether anything but the default says how many workers to use */
|
|
37
|
+
private workersAsked;
|
|
38
|
+
/** The pool of a run: up to `workers` worker processes that read the same project and settings as this run */
|
|
39
|
+
private createPool;
|
|
55
40
|
}
|
|
56
|
-
/**
|
|
57
|
-
* The folder of a test's screenshots below `screenshots/`: its spec file without extension, then its cleaned
|
|
58
|
-
* name. A spec outside srcDir loses the `..` (and a drive or root) its relative path starts with, so it stays
|
|
59
|
-
* below `screenshots/`.
|
|
60
|
-
* @internal Exported for tests
|
|
61
|
-
*/
|
|
62
|
-
export declare function testDirOf(test: RegisteredTestCase, srcDir: string): string;
|
|
63
|
-
export { describe, test } from "./registry.js";
|