@itmetsam/samurai-framework 0.21.4 → 0.22.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/dist/api.d.ts +8 -1
- package/dist/api.js +1 -0
- package/dist/browser/browser-context.d.ts +18 -6
- package/dist/browser/browser-context.js +25 -7
- package/dist/browser/browser.d.ts +11 -2
- package/dist/browser/browser.js +31 -8
- package/dist/browser/cdp.d.ts +11 -0
- package/dist/browser/cdp.js +28 -0
- package/dist/browser/chrome-launch.d.ts +4 -0
- package/dist/browser/chrome-launch.js +11 -3
- package/dist/browser/context-cookie-store.d.ts +17 -0
- package/dist/browser/context-cookie-store.js +90 -0
- package/dist/browser/index.d.ts +8 -1
- package/dist/browser/index.js +5 -1
- package/dist/browser/keyboard.d.ts +35 -0
- package/dist/browser/keyboard.js +83 -0
- package/dist/browser/launch-firefox.js +1 -1
- package/dist/browser/launch-flags.d.ts +4 -1
- package/dist/browser/launch-flags.js +18 -5
- package/dist/browser/mouse.d.ts +42 -0
- package/dist/browser/mouse.js +66 -0
- package/dist/browser/page-navigation.d.ts +0 -2
- package/dist/browser/page-navigation.js +1 -6
- package/dist/browser/page-settings.d.ts +1 -1
- package/dist/browser/page.d.ts +20 -6
- package/dist/browser/page.js +24 -8
- package/dist/browser/session-capabilities.d.ts +10 -0
- package/dist/browser/session-capabilities.js +11 -0
- package/dist/browser/session.d.ts +2 -1
- package/dist/browser/session.js +3 -3
- package/dist/browser/storage-state.d.ts +4 -0
- package/dist/browser/storage-state.js +17 -0
- package/dist/browser/use-applier.d.ts +38 -0
- package/dist/browser/use-applier.js +121 -0
- package/dist/browser/use-commands.d.ts +36 -0
- package/dist/browser/use-commands.js +120 -0
- package/dist/browser/viewport.d.ts +3 -2
- package/dist/browser/viewport.js +15 -2
- package/dist/cli/args.d.ts +8 -4
- package/dist/cli/args.js +8 -5
- package/dist/cli/console-reporter.js +5 -1
- package/dist/cli/main.js +1 -0
- package/dist/cli/usage.d.ts +1 -1
- package/dist/cli/usage.js +4 -1
- package/dist/config/base-url.d.ts +4 -0
- package/dist/config/base-url.js +6 -0
- package/dist/config/config.js +8 -0
- package/dist/config/fully-parallel.d.ts +3 -0
- package/dist/config/fully-parallel.js +6 -0
- package/dist/config/group-use.d.ts +8 -0
- package/dist/config/group-use.js +41 -0
- package/dist/config/resolved-config.d.ts +2 -0
- package/dist/config/resolved-config.js +3 -0
- package/dist/config/storage-state-file.d.ts +3 -0
- package/dist/config/storage-state-file.js +20 -0
- package/dist/config/use-options.d.ts +18 -0
- package/dist/config/use-options.js +184 -0
- package/dist/config/web-server-options.d.ts +7 -0
- package/dist/config/web-server-options.js +111 -0
- package/dist/locator/check-actions.js +1 -1
- package/dist/locator/drag-actions.d.ts +17 -0
- package/dist/locator/drag-actions.js +80 -0
- package/dist/locator/element-actions.d.ts +10 -4
- package/dist/locator/element-actions.js +23 -16
- package/dist/locator/element-state.d.ts +5 -0
- package/dist/locator/element-state.js +2 -1
- package/dist/locator/event-actions.d.ts +10 -0
- package/dist/locator/event-actions.js +81 -0
- package/dist/locator/input-actions.d.ts +2 -11
- package/dist/locator/input-actions.js +3 -56
- package/dist/locator/key-actions.d.ts +11 -0
- package/dist/locator/key-actions.js +52 -0
- package/dist/locator/key-map.d.ts +16 -0
- package/dist/locator/key-map.js +76 -0
- package/dist/locator/locator.d.ts +35 -5
- package/dist/locator/locator.js +70 -16
- package/dist/locator/page-scripts.d.ts +2 -0
- package/dist/locator/page-scripts.js +7 -0
- package/dist/locator/pointer-actions.d.ts +44 -0
- package/dist/locator/pointer-actions.js +117 -0
- package/dist/locator/pointer-element-actions.d.ts +21 -0
- package/dist/locator/pointer-element-actions.js +42 -0
- package/dist/logger/warn-once.d.ts +4 -0
- package/dist/logger/warn-once.js +13 -0
- package/dist/request/api-request-context.d.ts +65 -0
- package/dist/request/api-request-context.js +198 -0
- package/dist/request/api-response.d.ts +34 -0
- package/dist/request/api-response.js +57 -0
- package/dist/request/cookie-jar.d.ts +48 -0
- package/dist/request/cookie-jar.js +164 -0
- package/dist/request/http-transport.d.ts +31 -0
- package/dist/request/http-transport.js +113 -0
- package/dist/request/redirect.d.ts +10 -0
- package/dist/request/redirect.js +21 -0
- package/dist/request/request-body.d.ts +27 -0
- package/dist/request/request-body.js +71 -0
- package/dist/request/request.d.ts +16 -0
- package/dist/request/request.js +20 -0
- package/dist/runner/all-hooks.d.ts +7 -1
- package/dist/runner/all-hooks.js +17 -1
- package/dist/runner/attachments.d.ts +16 -0
- package/dist/runner/attachments.js +60 -0
- package/dist/runner/fixture-args.d.ts +15 -0
- package/dist/runner/fixture-args.js +95 -0
- package/dist/runner/fixture-defs.d.ts +22 -0
- package/dist/runner/fixture-defs.js +104 -0
- package/dist/runner/fixture-scope.d.ts +33 -0
- package/dist/runner/fixture-scope.js +156 -0
- package/dist/runner/fixtures.d.ts +37 -0
- package/dist/runner/fixtures.js +40 -0
- package/dist/runner/global-hooks.d.ts +13 -0
- package/dist/runner/global-hooks.js +66 -0
- package/dist/runner/hooks.d.ts +6 -1
- package/dist/runner/hooks.js +15 -3
- package/dist/runner/options.d.ts +13 -2
- package/dist/runner/output-tail.d.ts +14 -0
- package/dist/runner/output-tail.js +30 -0
- package/dist/runner/pool-schedule.d.ts +22 -0
- package/dist/runner/pool-schedule.js +55 -0
- package/dist/runner/pool-units.d.ts +34 -0
- package/dist/runner/pool-units.js +85 -0
- package/dist/runner/pooled-run.d.ts +7 -5
- package/dist/runner/pooled-run.js +19 -9
- package/dist/runner/registry.d.ts +3 -39
- package/dist/runner/registry.js +99 -21
- package/dist/runner/reporter.d.ts +17 -1
- package/dist/runner/reporter.js +15 -2
- package/dist/runner/request-fixture.d.ts +14 -0
- package/dist/runner/request-fixture.js +25 -0
- package/dist/runner/run-hooks.d.ts +16 -0
- package/dist/runner/run-hooks.js +52 -0
- package/dist/runner/run-test.d.ts +1 -3
- package/dist/runner/run-test.js +98 -35
- package/dist/runner/run.d.ts +1 -0
- package/dist/runner/run.js +1 -0
- package/dist/runner/select.d.ts +15 -4
- package/dist/runner/select.js +38 -12
- package/dist/runner/serial-run.d.ts +23 -0
- package/dist/runner/serial-run.js +88 -0
- package/dist/runner/serial.d.ts +34 -0
- package/dist/runner/serial.js +63 -0
- package/dist/runner/shard.d.ts +22 -0
- package/dist/runner/shard.js +73 -0
- package/dist/runner/steps.d.ts +13 -0
- package/dist/runner/steps.js +67 -0
- package/dist/runner/suite.d.ts +8 -3
- package/dist/runner/suite.js +12 -3
- package/dist/runner/test-annotations.d.ts +16 -0
- package/dist/runner/test-annotations.js +47 -0
- package/dist/runner/test-function.d.ts +115 -0
- package/dist/runner/test-function.js +1 -0
- package/dist/runner/test-info.d.ts +104 -0
- package/dist/runner/test-info.js +170 -0
- package/dist/runner/test-runner.d.ts +1 -0
- package/dist/runner/test-runner.js +64 -19
- package/dist/runner/test-timeout.d.ts +22 -0
- package/dist/runner/test-timeout.js +45 -0
- package/dist/runner/use-options.d.ts +7 -0
- package/dist/runner/use-options.js +25 -0
- package/dist/runner/wait-for-url.d.ts +20 -0
- package/dist/runner/wait-for-url.js +65 -0
- package/dist/runner/web-server.d.ts +16 -0
- package/dist/runner/web-server.js +165 -0
- package/dist/runner/worker-pool.d.ts +13 -5
- package/dist/runner/worker-pool.js +85 -51
- package/dist/runner/worker-protocol.d.ts +8 -0
- package/dist/runner/worker-session.js +5 -2
- package/dist/runner/worker.d.ts +2 -0
- package/dist/runner/worker.js +7 -1
- package/dist/script/element-handle.d.ts +1 -1
- package/dist/script/element-handle.js +1 -1
- package/dist/types/config.d.ts +86 -2
- package/dist/types/extend.d.ts +30 -0
- package/dist/types/test.d.ts +138 -0
- package/dist/types/use.d.ts +85 -0
- package/package.json +1 -1
- package/dist/browser/emulation.d.ts +0 -61
- package/dist/browser/emulation.js +0 -180
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { TestError, TestResult } from "../types/test.js";
|
|
2
|
+
import type { WorkItem } from "./worker-protocol.js";
|
|
3
|
+
/** The error a failed result carries, as the next result lists it in `previousAttempts` */
|
|
4
|
+
export declare function errorOf(result: TestResult): TestError;
|
|
5
|
+
/** The result of a test of a serial block that did not run, because an earlier test of the block failed */
|
|
6
|
+
export declare function serialSkipped(item: WorkItem): TestResult;
|
|
7
|
+
/** What the pool keeps of a queued item: the item, and whatever else it needs to find its place again */
|
|
8
|
+
interface Held<E> {
|
|
9
|
+
entry: E;
|
|
10
|
+
result: TestResult;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A serial block as the pool runs it. With retries the results are held until the block's last attempt is known,
|
|
14
|
+
* so a test ends once, saying which attempt that was; without, the pool reports them as they come and uses this
|
|
15
|
+
* only to count the attempts.
|
|
16
|
+
*/
|
|
17
|
+
export declare class UnitState<E extends {
|
|
18
|
+
item: WorkItem;
|
|
19
|
+
}> {
|
|
20
|
+
readonly call: unknown;
|
|
21
|
+
/** Which attempt at the block this is, counting from 0 */
|
|
22
|
+
attempt: number;
|
|
23
|
+
private held;
|
|
24
|
+
private readonly previous;
|
|
25
|
+
constructor(call: unknown);
|
|
26
|
+
hold(entry: E, result: TestResult): void;
|
|
27
|
+
/** The block failed and runs again: forgets this attempt's results, remembers the error and returns the entries to queue again, in order */
|
|
28
|
+
restart(failed: E, result: TestResult): E[];
|
|
29
|
+
/** The held results of the last attempt, each saying which attempt it was and what failed before; they are not held any more */
|
|
30
|
+
release(): Held<E>[];
|
|
31
|
+
}
|
|
32
|
+
/** The result of an item that ended without one: the worker died, or the run was aborted */
|
|
33
|
+
export declare function failedResult(item: WorkItem, message: string, startTime?: number): TestResult;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { SERIAL_SKIP_REASON } from "./serial.js";
|
|
2
|
+
/** The error a failed result carries, as the next result lists it in `previousAttempts` */
|
|
3
|
+
export function errorOf(result) {
|
|
4
|
+
const { message, type, stack, expected, actual } = result;
|
|
5
|
+
return {
|
|
6
|
+
message: message ?? "The test failed",
|
|
7
|
+
type: type ?? "error",
|
|
8
|
+
...(stack !== undefined && { stack }),
|
|
9
|
+
...(expected !== undefined && { expected }),
|
|
10
|
+
...(actual !== undefined && { actual }),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** The result of a test of a serial block that did not run, because an earlier test of the block failed */
|
|
14
|
+
export function serialSkipped(item) {
|
|
15
|
+
return {
|
|
16
|
+
name: item.testName,
|
|
17
|
+
file: item.file,
|
|
18
|
+
browser: item.browser,
|
|
19
|
+
status: "skipped",
|
|
20
|
+
startTime: Date.now(),
|
|
21
|
+
duration: 0,
|
|
22
|
+
annotation: "skip",
|
|
23
|
+
reason: SERIAL_SKIP_REASON,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A serial block as the pool runs it. With retries the results are held until the block's last attempt is known,
|
|
28
|
+
* so a test ends once, saying which attempt that was; without, the pool reports them as they come and uses this
|
|
29
|
+
* only to count the attempts.
|
|
30
|
+
*/
|
|
31
|
+
export class UnitState {
|
|
32
|
+
constructor(call) {
|
|
33
|
+
this.call = call;
|
|
34
|
+
/** Which attempt at the block this is, counting from 0 */
|
|
35
|
+
this.attempt = 0;
|
|
36
|
+
this.held = [];
|
|
37
|
+
this.previous = new Map();
|
|
38
|
+
}
|
|
39
|
+
hold(entry, result) {
|
|
40
|
+
this.held.push({ entry, result });
|
|
41
|
+
}
|
|
42
|
+
/** The block failed and runs again: forgets this attempt's results, remembers the error and returns the entries to queue again, in order */
|
|
43
|
+
restart(failed, result) {
|
|
44
|
+
const entries = this.held.map(({ entry }) => entry);
|
|
45
|
+
this.held = [];
|
|
46
|
+
const errors = this.previous.get(failed) ?? [];
|
|
47
|
+
errors.push(errorOf(result));
|
|
48
|
+
this.previous.set(failed, errors);
|
|
49
|
+
this.attempt++;
|
|
50
|
+
return entries;
|
|
51
|
+
}
|
|
52
|
+
/** The held results of the last attempt, each saying which attempt it was and what failed before; they are not held any more */
|
|
53
|
+
release() {
|
|
54
|
+
const released = this.held.map(({ entry, result }) => {
|
|
55
|
+
const before = this.previous.get(entry) ?? [];
|
|
56
|
+
return {
|
|
57
|
+
entry,
|
|
58
|
+
result: {
|
|
59
|
+
...result,
|
|
60
|
+
...(this.attempt > 0 && {
|
|
61
|
+
retry: this.attempt,
|
|
62
|
+
previousAttempts: [...before],
|
|
63
|
+
...(result.status === "success" &&
|
|
64
|
+
before.length > 0 && { flaky: true }),
|
|
65
|
+
}),
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
this.held = [];
|
|
70
|
+
return released;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** The result of an item that ended without one: the worker died, or the run was aborted */
|
|
74
|
+
export function failedResult(item, message, startTime = Date.now()) {
|
|
75
|
+
return {
|
|
76
|
+
name: item.testName,
|
|
77
|
+
file: item.file,
|
|
78
|
+
browser: item.browser,
|
|
79
|
+
status: "failed",
|
|
80
|
+
type: "error",
|
|
81
|
+
message,
|
|
82
|
+
startTime,
|
|
83
|
+
duration: Date.now() - startTime,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -3,6 +3,7 @@ import type { SamuraiGroup } from "../types/config.js";
|
|
|
3
3
|
import type { RegisteredTestCase, TestResult } from "../types/test.js";
|
|
4
4
|
import type { ResolvedGroup } from "./groups.js";
|
|
5
5
|
import type TestReporter from "./reporter.js";
|
|
6
|
+
import type { RunUnit } from "./serial.js";
|
|
6
7
|
import type { RunHooks } from "./worker-pool.js";
|
|
7
8
|
import type { WorkItem } from "./worker-protocol.js";
|
|
8
9
|
/** What a run needs from a worker pool: `WorkerPool` itself, or a fake in tests */
|
|
@@ -16,10 +17,11 @@ export interface PlannedTest {
|
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
19
|
* Lists the run: every selected test in the first browser, then every selected test in the next one. The
|
|
19
|
-
* position in the list is the item's `index`. `serialKey` says which group makes a test run alone
|
|
20
|
-
* run's input values, go
|
|
20
|
+
* position in the list is the item's `index`. `serialKey` says which group makes a test run alone, `unitOf` which
|
|
21
|
+
* tests must run one after the other on one worker (a serial block, a file). `inputs`, the run's input values, go
|
|
22
|
+
* with every item. `runsIn` says which browsers a test runs in (all of them unless a group names its own).
|
|
21
23
|
*/
|
|
22
|
-
export declare function planItems(selected: RegisteredTestCase[], browsers: SupportedBrowser[], serialKey: (test: RegisteredTestCase) => string | undefined, inputs?: Record<string, string
|
|
24
|
+
export declare function planItems(selected: RegisteredTestCase[], browsers: SupportedBrowser[], serialKey: (test: RegisteredTestCase) => string | undefined, inputs?: Record<string, string>, unitOf?: (test: RegisteredTestCase) => RunUnit | undefined, runsIn?: (test: RegisteredTestCase, browser: SupportedBrowser) => boolean): {
|
|
23
25
|
items: WorkItem[];
|
|
24
26
|
planned: PlannedTest[];
|
|
25
27
|
};
|
|
@@ -29,8 +31,8 @@ export declare function planItems(selected: RegisteredTestCase[], browsers: Supp
|
|
|
29
31
|
*/
|
|
30
32
|
export declare function serialKeyOf(groups: ResolvedGroup[], configGroups: SamuraiGroup[] | undefined, srcDir: string): (test: RegisteredTestCase) => string | undefined;
|
|
31
33
|
/**
|
|
32
|
-
* How many items can run at the same time: each item alone counts one, and the items of one
|
|
33
|
-
* count one. More workers than this would only sit idle.
|
|
34
|
+
* How many items can run at the same time: each item alone counts one, and the items of one unit, or of one serial
|
|
35
|
+
* key, together count one. More workers than this would only sit idle.
|
|
34
36
|
*/
|
|
35
37
|
export declare function concurrency(items: WorkItem[]): number;
|
|
36
38
|
/**
|
|
@@ -2,23 +2,31 @@ import { idOf } from "./select.js";
|
|
|
2
2
|
import { staticSkip } from "./skip.js";
|
|
3
3
|
/**
|
|
4
4
|
* Lists the run: every selected test in the first browser, then every selected test in the next one. The
|
|
5
|
-
* position in the list is the item's `index`. `serialKey` says which group makes a test run alone
|
|
6
|
-
* run's input values, go
|
|
5
|
+
* position in the list is the item's `index`. `serialKey` says which group makes a test run alone, `unitOf` which
|
|
6
|
+
* tests must run one after the other on one worker (a serial block, a file). `inputs`, the run's input values, go
|
|
7
|
+
* with every item. `runsIn` says which browsers a test runs in (all of them unless a group names its own).
|
|
7
8
|
*/
|
|
8
|
-
export function planItems(selected, browsers, serialKey, inputs) {
|
|
9
|
-
const planned = browsers.flatMap((browser) => selected
|
|
9
|
+
export function planItems(selected, browsers, serialKey, inputs, unitOf = () => undefined, runsIn = () => true) {
|
|
10
|
+
const planned = browsers.flatMap((browser) => selected
|
|
11
|
+
.filter((test) => runsIn(test, browser))
|
|
12
|
+
.map((test) => ({ test, browser })));
|
|
10
13
|
// A test that does not run (skip, fixme) is in the plan but not in the queue; `index` is its place in the plan
|
|
11
14
|
const items = planned.flatMap(({ test, browser }, index) => {
|
|
12
15
|
if (staticSkip(test))
|
|
13
16
|
return [];
|
|
14
17
|
const key = serialKey(test);
|
|
18
|
+
const unit = unitOf(test);
|
|
15
19
|
return [
|
|
16
20
|
{
|
|
17
21
|
index,
|
|
18
22
|
file: test.file,
|
|
19
23
|
testName: test.name,
|
|
20
24
|
browser,
|
|
25
|
+
...(test.group !== undefined && { group: test.group }),
|
|
21
26
|
...(key !== undefined && { serialKey: key }),
|
|
27
|
+
// One unit per browser: the same block in two browsers is two runs
|
|
28
|
+
...(unit && { unit: `${unit.key}@${browser}` }),
|
|
29
|
+
...(unit?.serial && { serial: true }),
|
|
22
30
|
...(inputs !== undefined && { inputs }),
|
|
23
31
|
},
|
|
24
32
|
];
|
|
@@ -41,17 +49,19 @@ export function serialKeyOf(groups, configGroups, srcDir) {
|
|
|
41
49
|
return (test) => keys.get(idOf(test, srcDir));
|
|
42
50
|
}
|
|
43
51
|
/**
|
|
44
|
-
* How many items can run at the same time: each item alone counts one, and the items of one
|
|
45
|
-
* count one. More workers than this would only sit idle.
|
|
52
|
+
* How many items can run at the same time: each item alone counts one, and the items of one unit, or of one serial
|
|
53
|
+
* key, together count one. More workers than this would only sit idle.
|
|
46
54
|
*/
|
|
47
55
|
export function concurrency(items) {
|
|
48
56
|
const keys = new Set();
|
|
49
57
|
let free = 0;
|
|
50
58
|
for (const item of items) {
|
|
51
|
-
if (item.
|
|
52
|
-
|
|
59
|
+
if (item.unit !== undefined)
|
|
60
|
+
keys.add(`unit:${item.unit}`);
|
|
61
|
+
else if (item.serialKey !== undefined)
|
|
62
|
+
keys.add(`key:${item.serialKey}`);
|
|
53
63
|
else
|
|
54
|
-
|
|
64
|
+
free++;
|
|
55
65
|
}
|
|
56
66
|
return free + keys.size;
|
|
57
67
|
}
|
|
@@ -1,41 +1,5 @@
|
|
|
1
|
-
import type { AllHook, EachHook, RegisteredTestCase
|
|
2
|
-
type
|
|
3
|
-
/** `test(title, fn)`, `test.skip(title, fn)`, `test.skip(condition, reason)` and the other forms */
|
|
4
|
-
export interface TestFunction {
|
|
5
|
-
(title: string, fn: Body): void;
|
|
6
|
-
(title: string, options: TestOptions, fn: Body): void;
|
|
7
|
-
/** Registers a test that is not run */
|
|
8
|
-
skip: SkipFunction;
|
|
9
|
-
/** Registers a test that is not run because it is known to be broken */
|
|
10
|
-
fixme: SkipFunction;
|
|
11
|
-
/** Runs only this test (and the other `only` ones) */
|
|
12
|
-
only: {
|
|
13
|
-
(title: string, fn: Body): void;
|
|
14
|
-
(title: string, options: TestOptions, fn: Body): void;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* With a title and a body it registers a skipped test, `{ reason }` saying why; with a condition it skips the
|
|
19
|
-
* running test when the condition is true
|
|
20
|
-
*/
|
|
21
|
-
interface SkipFunction {
|
|
22
|
-
(title: string, fn: Body): void;
|
|
23
|
-
(title: string, options: TestOptions, fn: Body): void;
|
|
24
|
-
(condition: boolean, reason?: string): void;
|
|
25
|
-
}
|
|
26
|
-
/** `describe.skip` and `describe.fixme`: every test inside gets the modifier, and `{ reason }` says why */
|
|
27
|
-
interface DescribeSkipFunction {
|
|
28
|
-
(title: string, fn: () => void): void;
|
|
29
|
-
(title: string, details: TestDetails, fn: () => void): void;
|
|
30
|
-
}
|
|
31
|
-
/** `describe(title, fn)` and the same with a modifier on every test inside */
|
|
32
|
-
export interface DescribeFunction {
|
|
33
|
-
(title: string, fn: () => void): void;
|
|
34
|
-
skip: DescribeSkipFunction;
|
|
35
|
-
fixme: DescribeSkipFunction;
|
|
36
|
-
only(title: string, fn: () => void): void;
|
|
37
|
-
}
|
|
38
|
-
export declare const test: TestFunction;
|
|
1
|
+
import type { AllHook, EachHook, RegisteredTestCase } from "../types/test.js";
|
|
2
|
+
import type { DescribeFunction, TestFunction } from "./test-function.js";
|
|
39
3
|
export declare const describe: DescribeFunction;
|
|
40
4
|
/** Runs before each test of the `describe` block it is written in (of the file, at the top level) */
|
|
41
5
|
export declare function beforeEach(hook: EachHook): void;
|
|
@@ -45,7 +9,7 @@ export declare function afterEach(hook: EachHook): void;
|
|
|
45
9
|
export declare function beforeAll(hook: AllHook): void;
|
|
46
10
|
/** Runs once per worker, when the worker is done, for every `describe` block it ran a `beforeAll` for */
|
|
47
11
|
export declare function afterAll(hook: AllHook): void;
|
|
12
|
+
export declare const test: TestFunction;
|
|
48
13
|
export declare function registeredTests(): readonly RegisteredTestCase[];
|
|
49
14
|
/** Forgets every registered test: a run registers its specs afresh */
|
|
50
15
|
export declare function clearRegistry(): void;
|
|
51
|
-
export {};
|
package/dist/runner/registry.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import logger from "../logger/index.js";
|
|
3
|
-
import {
|
|
3
|
+
import { extendLayers } from "./fixture-defs.js";
|
|
4
|
+
import { validateUseOptions } from "../config/use-options.js";
|
|
5
|
+
import { currentStepLog, currentTestInfo, infoFor, skipFromCondition, } from "./test-info.js";
|
|
6
|
+
import { runStep } from "./steps.js";
|
|
7
|
+
import { markTests } from "./test-annotations.js";
|
|
4
8
|
import { validateTestIO } from "./test-io.js";
|
|
5
|
-
import { addHook, currentSuite, enterSuite, leaveSuite, modifierOf, resetSuites, } from "./suite.js";
|
|
9
|
+
import { addHook, addUse, currentSuite, enterSuite, leaveSuite, modifierOf, resetSuites, } from "./suite.js";
|
|
10
|
+
import { configureSuite } from "./serial.js";
|
|
6
11
|
const registered = [];
|
|
7
12
|
/** Runs `fn` as the `describe` block `title`, under `modifier` when there is one. `fn` runs immediately and must be synchronous. */
|
|
8
|
-
function openDescribe(file, title, fn, modifier, reason) {
|
|
9
|
-
enterSuite(file, title, modifier, reason);
|
|
13
|
+
function openDescribe(file, title, fn, modifier, reason, mode) {
|
|
14
|
+
enterSuite(file, title, modifier, reason, mode);
|
|
10
15
|
try {
|
|
11
16
|
const result = fn();
|
|
12
17
|
if (result instanceof Promise) {
|
|
@@ -18,8 +23,8 @@ function openDescribe(file, title, fn, modifier, reason) {
|
|
|
18
23
|
leaveSuite();
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
|
-
function add(file, title, fn, modifier, extras = {}) {
|
|
22
|
-
const { reason, inputs, outputs } = extras;
|
|
26
|
+
function add({ file, line, column }, title, fn, modifier, extras = {}) {
|
|
27
|
+
const { reason, inputs, outputs, extension, expectedFail } = extras;
|
|
23
28
|
const suite = currentSuite(file);
|
|
24
29
|
const titlePath = [...titlesOf(suite), title];
|
|
25
30
|
const name = titlePath.join(" > ");
|
|
@@ -28,10 +33,13 @@ function add(file, title, fn, modifier, extras = {}) {
|
|
|
28
33
|
titlePath,
|
|
29
34
|
function: fn,
|
|
30
35
|
file,
|
|
36
|
+
...(line !== undefined && { line, column }),
|
|
31
37
|
suite,
|
|
32
38
|
...modifierOf({ modifier, ...(reason !== undefined && { reason }) }, suite),
|
|
33
39
|
...(inputs && { inputs }),
|
|
34
40
|
...(outputs && { outputs }),
|
|
41
|
+
...(extension && { extension }),
|
|
42
|
+
...(expectedFail && { expectedFail }),
|
|
35
43
|
});
|
|
36
44
|
logger.debug("Registered test %s", name);
|
|
37
45
|
}
|
|
@@ -58,28 +66,59 @@ function bodyAndOptions(call, title, second, third) {
|
|
|
58
66
|
};
|
|
59
67
|
}
|
|
60
68
|
/** `test()` and `test.only()`: a reason only makes sense with skip and fixme */
|
|
61
|
-
function registering(call, modifier) {
|
|
69
|
+
function registering(call, modifier, extension) {
|
|
62
70
|
return ((title, second, third) => {
|
|
63
71
|
const { fn, reason, ...io } = bodyAndOptions(call, title, second, third);
|
|
64
72
|
if (reason !== undefined)
|
|
65
73
|
throw new Error(`${call}(): reason only applies to skip and fixme`);
|
|
66
74
|
// Frames: Error, callerFile, this function, the spec
|
|
67
|
-
add(
|
|
75
|
+
add(callerLocation(), title, fn, modifier, {
|
|
76
|
+
...io,
|
|
77
|
+
...(extension && { extension }),
|
|
78
|
+
});
|
|
68
79
|
});
|
|
69
80
|
}
|
|
70
81
|
/** `test.skip` and `test.fixme`: a title and a body register a test with the modifier; a condition skips the running test */
|
|
71
|
-
function skipping(annotation) {
|
|
82
|
+
function skipping(annotation, extension) {
|
|
72
83
|
return ((first, second, third) => {
|
|
73
84
|
if (typeof first === "string") {
|
|
74
85
|
const { fn, ...extras } = bodyAndOptions(`test.${annotation}`, first, second, third);
|
|
75
86
|
// Frames: Error, callerFile, this function, the spec
|
|
76
|
-
add(
|
|
87
|
+
add(callerLocation(), first, fn, annotation, {
|
|
88
|
+
...extras,
|
|
89
|
+
...(extension && { extension }),
|
|
90
|
+
});
|
|
77
91
|
return;
|
|
78
92
|
}
|
|
79
|
-
|
|
80
|
-
throw new SkipSignal(annotation, second);
|
|
93
|
+
skipFromCondition(annotation, first, second);
|
|
81
94
|
});
|
|
82
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* `test.fail`: a title and a body register a test that is expected to fail; otherwise the running test (or, in a
|
|
98
|
+
* `describe` block, every test in it) is marked
|
|
99
|
+
*/
|
|
100
|
+
function failing(extension) {
|
|
101
|
+
return ((first, second, third) => {
|
|
102
|
+
if (typeof first !== "string") {
|
|
103
|
+
// Frames: Error, callerFile, this function, the spec
|
|
104
|
+
const file = callerFile();
|
|
105
|
+
return markTests("fail", () => currentSuite(file), first, second);
|
|
106
|
+
}
|
|
107
|
+
const { fn, reason, ...io } = bodyAndOptions("test.fail", first, second, third);
|
|
108
|
+
// Frames: Error, callerFile, this function, the spec
|
|
109
|
+
add(callerLocation(), first, fn, undefined, {
|
|
110
|
+
...io,
|
|
111
|
+
expectedFail: { ...(reason !== undefined && { description: reason }) },
|
|
112
|
+
...(extension && { extension }),
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/** `test.slow`: triples the timeout of the running test, or of every test of the `describe` block it is written in */
|
|
117
|
+
const slow = ((condition, reason) => {
|
|
118
|
+
// Frames: Error, callerFile, this function, the spec
|
|
119
|
+
const file = callerFile();
|
|
120
|
+
markTests("slow", () => currentSuite(file), condition, reason);
|
|
121
|
+
});
|
|
83
122
|
/** `describe.skip` and `describe.fixme`, with or without `{ reason }` */
|
|
84
123
|
function describeSkipping(annotation) {
|
|
85
124
|
return ((title, second, third) => {
|
|
@@ -87,15 +126,13 @@ function describeSkipping(annotation) {
|
|
|
87
126
|
openDescribe(callerFile(), title, fn, annotation, reason);
|
|
88
127
|
});
|
|
89
128
|
}
|
|
90
|
-
export const test = Object.assign(registering("test"), {
|
|
91
|
-
skip: skipping("skip"),
|
|
92
|
-
fixme: skipping("fixme"),
|
|
93
|
-
only: registering("test.only", "only"),
|
|
94
|
-
});
|
|
95
129
|
export const describe = Object.assign((title, fn) => openDescribe(callerFile(), title, fn), {
|
|
96
130
|
skip: describeSkipping("skip"),
|
|
97
131
|
fixme: describeSkipping("fixme"),
|
|
98
132
|
only: (title, fn) => openDescribe(callerFile(), title, fn, "only"),
|
|
133
|
+
configure: (options) => configureSuite(currentSuite(callerFile()), options),
|
|
134
|
+
serial: (title, fn) => openDescribe(callerFile(), title, fn, undefined, undefined, "serial"),
|
|
135
|
+
parallel: (title, fn) => openDescribe(callerFile(), title, fn, undefined, undefined, "parallel"),
|
|
99
136
|
});
|
|
100
137
|
/** Runs before each test of the `describe` block it is written in (of the file, at the top level) */
|
|
101
138
|
export function beforeEach(hook) {
|
|
@@ -113,6 +150,34 @@ export function beforeAll(hook) {
|
|
|
113
150
|
export function afterAll(hook) {
|
|
114
151
|
addHook(currentSuite(callerFile()), "afterAll", hook);
|
|
115
152
|
}
|
|
153
|
+
/** `test.use(options)`: checked now, applied when each test of the scope runs */
|
|
154
|
+
function use(options) {
|
|
155
|
+
addUse(currentSuite(callerFile()), validateUseOptions(options));
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* A `test` that registers its tests with the custom fixtures `extension`. The aliases are the same functions as
|
|
159
|
+
* the named exports: they find the spec file from the stack, so they must not be wrapped.
|
|
160
|
+
*/
|
|
161
|
+
function makeTest(extension) {
|
|
162
|
+
return Object.assign(registering("test", undefined, extension), {
|
|
163
|
+
skip: skipping("skip", extension),
|
|
164
|
+
fixme: skipping("fixme", extension),
|
|
165
|
+
fail: failing(extension),
|
|
166
|
+
slow,
|
|
167
|
+
setTimeout: (ms) => infoFor("setTimeout()").setTimeout(ms),
|
|
168
|
+
step: (title, body, options) => runStep(currentStepLog(), title, body, options),
|
|
169
|
+
only: registering("test.only", "only", extension),
|
|
170
|
+
info: currentTestInfo,
|
|
171
|
+
use,
|
|
172
|
+
describe,
|
|
173
|
+
beforeEach,
|
|
174
|
+
afterEach,
|
|
175
|
+
beforeAll,
|
|
176
|
+
afterAll,
|
|
177
|
+
extend: (fixtures) => makeTest(extendLayers(extension, fixtures)),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
export const test = makeTest();
|
|
116
181
|
export function registeredTests() {
|
|
117
182
|
return registered;
|
|
118
183
|
}
|
|
@@ -121,10 +186,23 @@ export function clearRegistry() {
|
|
|
121
186
|
registered.length = 0;
|
|
122
187
|
resetSuites();
|
|
123
188
|
}
|
|
124
|
-
/**
|
|
125
|
-
function
|
|
189
|
+
/** The place of the caller: stack lines are Error, callerLocation, the entry point, the caller */
|
|
190
|
+
function callerLocation() {
|
|
126
191
|
const caller = new Error().stack?.split("\n")[3] ?? "";
|
|
127
|
-
|
|
192
|
+
return parseFrame(caller);
|
|
193
|
+
}
|
|
194
|
+
/** File of the code that called the entry point (the same frames as `callerLocation`) */
|
|
195
|
+
function callerFile() {
|
|
196
|
+
return parseFrame(new Error().stack?.split("\n")[3] ?? "").file;
|
|
197
|
+
}
|
|
198
|
+
function parseFrame(frame) {
|
|
199
|
+
const match = frame.match(/\((.+?):(\d+):(\d+)\)$/) ??
|
|
200
|
+
frame.match(/at (.+?):(\d+):(\d+)$/);
|
|
128
201
|
const location = match?.[1] ?? "";
|
|
129
|
-
|
|
202
|
+
const file = location.startsWith("file://")
|
|
203
|
+
? fileURLToPath(location)
|
|
204
|
+
: location;
|
|
205
|
+
return match
|
|
206
|
+
? { file, line: Number(match[2]), column: Number(match[3]) }
|
|
207
|
+
: { file };
|
|
130
208
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { RegisteredTestCase, TestError, TestLogEntry, TestResult, TestRetries, TestSummary } from "../types/test.js";
|
|
1
|
+
import type { TestAnnotation, TestStepEntry, TestAttachmentEntry, RegisteredTestCase, TestError, TestLogEntry, TestResult, TestRetries, TestSummary } from "../types/test.js";
|
|
2
2
|
import type { SupportedBrowser } from "../types/browser.js";
|
|
3
|
+
import type { Shard } from "./shard.js";
|
|
3
4
|
/** What a run reports while it happens */
|
|
4
5
|
export type RunEvent = {
|
|
5
6
|
type: "run-start";
|
|
@@ -9,11 +10,15 @@ export type RunEvent = {
|
|
|
9
10
|
browsers: SupportedBrowser[];
|
|
10
11
|
/** How many tests run at the same time; absent when they run one after the other */
|
|
11
12
|
workers?: number;
|
|
13
|
+
/** The slice of the selected tests this run holds, when it is a shard */
|
|
14
|
+
shard?: Shard;
|
|
12
15
|
} | {
|
|
13
16
|
type: "test-start";
|
|
14
17
|
name: string;
|
|
15
18
|
file: string;
|
|
16
19
|
browser: SupportedBrowser;
|
|
20
|
+
/** The group the test runs for, when the run selected groups; absent otherwise */
|
|
21
|
+
group?: string;
|
|
17
22
|
/** The attempt, counting from 1, when the test is run again after a failure; absent for the first */
|
|
18
23
|
retry?: number;
|
|
19
24
|
} | {
|
|
@@ -21,6 +26,8 @@ export type RunEvent = {
|
|
|
21
26
|
name: string;
|
|
22
27
|
file: string;
|
|
23
28
|
browser: SupportedBrowser;
|
|
29
|
+
/** The group the test ran for, when the run selected groups; absent otherwise */
|
|
30
|
+
group?: string;
|
|
24
31
|
result: TestResult;
|
|
25
32
|
} | {
|
|
26
33
|
type: "run-end";
|
|
@@ -33,6 +40,8 @@ export interface ReporterOptions {
|
|
|
33
40
|
browsers: SupportedBrowser[];
|
|
34
41
|
/** Where the report is written. @default <cwd>/result/report.json */
|
|
35
42
|
output?: string | false;
|
|
43
|
+
/** The slice of the selected tests this run holds; recorded in the report */
|
|
44
|
+
shard?: Shard;
|
|
36
45
|
/** Called with every event, in order. A throwing listener is ignored */
|
|
37
46
|
onEvent?: (event: RunEvent) => void;
|
|
38
47
|
}
|
|
@@ -60,6 +69,12 @@ export interface TestEndExtras extends TestRetries {
|
|
|
60
69
|
inputs?: Record<string, string>;
|
|
61
70
|
/** The test's saved outputs, when it declares any */
|
|
62
71
|
outputs?: Record<string, string>;
|
|
72
|
+
/** Files the test attached with `testInfo.attach`, relative to the result folder */
|
|
73
|
+
attachments?: TestAttachmentEntry[];
|
|
74
|
+
/** The notes the test pushed to `testInfo.annotations` */
|
|
75
|
+
annotations?: TestAnnotation[];
|
|
76
|
+
/** The steps the test ran with `test.step` */
|
|
77
|
+
steps?: TestStepEntry[];
|
|
63
78
|
}
|
|
64
79
|
export default class TestReporter implements TestSink {
|
|
65
80
|
private testResults;
|
|
@@ -68,6 +83,7 @@ export default class TestReporter implements TestSink {
|
|
|
68
83
|
private browsers;
|
|
69
84
|
private output;
|
|
70
85
|
private listener;
|
|
86
|
+
private shard;
|
|
71
87
|
/** Keys of the run's tests in the order the report lists them; unset: the order they started in */
|
|
72
88
|
private order;
|
|
73
89
|
constructor(options: ReporterOptions);
|
package/dist/runner/reporter.js
CHANGED
|
@@ -2,10 +2,12 @@ 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, full title and
|
|
5
|
+
/** Results are keyed by file, full title, browser and group, so equal titles in different files or describe blocks, and the same test in two browsers or for two groups, stay apart */
|
|
6
6
|
function keyOf(test, browser) {
|
|
7
|
-
return `${test.file}::${test.name}::${browser}`;
|
|
7
|
+
return `${test.file}::${test.name}::${browser}${test.group === undefined ? "" : `::${test.group}`}`;
|
|
8
8
|
}
|
|
9
|
+
/** The `group` of a test's report entry and events: nothing when no group was selected */
|
|
10
|
+
const groupOf = (test) => test.group === undefined ? {} : { group: test.group };
|
|
9
11
|
export default class TestReporter {
|
|
10
12
|
constructor(options) {
|
|
11
13
|
this.testResults = new Map();
|
|
@@ -14,6 +16,7 @@ export default class TestReporter {
|
|
|
14
16
|
this.output =
|
|
15
17
|
options.output ?? path.join(projectDir(), "result/report.json");
|
|
16
18
|
this.listener = options.onEvent;
|
|
19
|
+
this.shard = options.shard;
|
|
17
20
|
}
|
|
18
21
|
emit(event) {
|
|
19
22
|
try {
|
|
@@ -43,6 +46,7 @@ export default class TestReporter {
|
|
|
43
46
|
total,
|
|
44
47
|
browsers: this.browsers,
|
|
45
48
|
...(workers > 1 && { workers }),
|
|
49
|
+
...(this.shard && { shard: this.shard }),
|
|
46
50
|
});
|
|
47
51
|
}
|
|
48
52
|
async onEnd() {
|
|
@@ -61,6 +65,7 @@ export default class TestReporter {
|
|
|
61
65
|
duration,
|
|
62
66
|
environment: this.environment,
|
|
63
67
|
browsers: this.browsers,
|
|
68
|
+
...(this.shard && { shard: this.shard }),
|
|
64
69
|
tests,
|
|
65
70
|
status: tests.every((r) => r.status === "success" || r.status === "skipped")
|
|
66
71
|
? "success"
|
|
@@ -82,6 +87,7 @@ export default class TestReporter {
|
|
|
82
87
|
name: test.name,
|
|
83
88
|
file: test.file,
|
|
84
89
|
browser,
|
|
90
|
+
...groupOf(test),
|
|
85
91
|
status: "started",
|
|
86
92
|
startTime: t.startTime,
|
|
87
93
|
});
|
|
@@ -90,6 +96,7 @@ export default class TestReporter {
|
|
|
90
96
|
name: test.name,
|
|
91
97
|
file: test.file,
|
|
92
98
|
browser,
|
|
99
|
+
...groupOf(test),
|
|
93
100
|
...(retry ? { retry } : {}),
|
|
94
101
|
});
|
|
95
102
|
}
|
|
@@ -111,6 +118,7 @@ export default class TestReporter {
|
|
|
111
118
|
name: test.name,
|
|
112
119
|
file: test.file,
|
|
113
120
|
browser,
|
|
121
|
+
...groupOf(test),
|
|
114
122
|
result,
|
|
115
123
|
});
|
|
116
124
|
}
|
|
@@ -131,6 +139,7 @@ export default class TestReporter {
|
|
|
131
139
|
name: test.name,
|
|
132
140
|
file: test.file,
|
|
133
141
|
browser,
|
|
142
|
+
...groupOf(test),
|
|
134
143
|
result: ended,
|
|
135
144
|
});
|
|
136
145
|
}
|
|
@@ -148,6 +157,9 @@ export default class TestReporter {
|
|
|
148
157
|
...(extras?.screenshots?.length && { screenshots: extras.screenshots }),
|
|
149
158
|
...(extras?.inputs && { inputs: extras.inputs }),
|
|
150
159
|
...(extras?.outputs && { outputs: extras.outputs }),
|
|
160
|
+
...(extras?.attachments && { attachments: extras.attachments }),
|
|
161
|
+
...(extras?.annotations && { annotations: extras.annotations }),
|
|
162
|
+
...(extras?.steps && { steps: extras.steps }),
|
|
151
163
|
...(extras?.retry && { retry: extras.retry }),
|
|
152
164
|
...(extras?.flaky && { flaky: true }),
|
|
153
165
|
...(extras?.previousAttempts?.length && {
|
|
@@ -160,6 +172,7 @@ export default class TestReporter {
|
|
|
160
172
|
name: test.name,
|
|
161
173
|
file: test.file,
|
|
162
174
|
browser,
|
|
175
|
+
...groupOf(test),
|
|
163
176
|
result,
|
|
164
177
|
});
|
|
165
178
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { APIRequestContext } from "../request/api-request-context.js";
|
|
2
|
+
import type { UseOptions } from "../types/use.js";
|
|
3
|
+
/**
|
|
4
|
+
* The `request` fixture of one test: an `APIRequestContext` of its own (its own cookies), made when the test first
|
|
5
|
+
* reads it, from the run's `baseURL` and the `use` options, and disposed when the test is over
|
|
6
|
+
*/
|
|
7
|
+
export declare class RequestFixture {
|
|
8
|
+
private readonly baseURL;
|
|
9
|
+
private readonly use;
|
|
10
|
+
private context;
|
|
11
|
+
constructor(baseURL: string | undefined, use: UseOptions);
|
|
12
|
+
get(): APIRequestContext;
|
|
13
|
+
dispose(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createRequestContext } from "../request/request.js";
|
|
2
|
+
/**
|
|
3
|
+
* The `request` fixture of one test: an `APIRequestContext` of its own (its own cookies), made when the test first
|
|
4
|
+
* reads it, from the run's `baseURL` and the `use` options, and disposed when the test is over
|
|
5
|
+
*/
|
|
6
|
+
export class RequestFixture {
|
|
7
|
+
constructor(baseURL, use) {
|
|
8
|
+
this.baseURL = baseURL;
|
|
9
|
+
this.use = use;
|
|
10
|
+
}
|
|
11
|
+
get() {
|
|
12
|
+
const { use } = this;
|
|
13
|
+
return (this.context ??= createRequestContext({
|
|
14
|
+
baseURL: this.baseURL,
|
|
15
|
+
extraHTTPHeaders: use.extraHTTPHeaders,
|
|
16
|
+
ignoreHTTPSErrors: use.acceptInsecureCerts,
|
|
17
|
+
...(use.storageState !== undefined && {
|
|
18
|
+
storageState: use.storageState,
|
|
19
|
+
}),
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
async dispose() {
|
|
23
|
+
await this.context?.dispose();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RunSettings } from "../config/run-settings.js";
|
|
2
|
+
import type { SamuraiTestConfig } from "../types/config.js";
|
|
3
|
+
/**
|
|
4
|
+
* Runs `work` between the config's run-level steps: web servers start, then `globalSetup`, then `work`, then
|
|
5
|
+
* `globalTeardown`, then the servers stop. The teardown runs when the work or the setup throws (not when a server
|
|
6
|
+
* failed to start, since nothing was set up), and the servers are stopped whatever happens. When the config has no
|
|
7
|
+
* `baseURL` for the run, the first server's address becomes `settings.baseURL`.
|
|
8
|
+
* Errors: a failed start is `webServer(): ...`, a failed setup `globalSetup(): ...`; the work's own error wins over
|
|
9
|
+
* a teardown error, which is logged then.
|
|
10
|
+
*/
|
|
11
|
+
export declare function withRunHooks<T>(run: {
|
|
12
|
+
config: SamuraiTestConfig;
|
|
13
|
+
settings: RunSettings;
|
|
14
|
+
projectDir: string;
|
|
15
|
+
signal?: AbortSignal | undefined;
|
|
16
|
+
}, work: () => Promise<T>): Promise<T>;
|