@itmetsam/samurai-framework 0.17.0 → 0.18.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/capabilities.d.ts +2 -0
- package/dist/capabilities.js +2 -0
- package/dist/flows/run/handlers/support.js +1 -1
- package/dist/flows/run/handlers/types.d.ts +1 -1
- package/dist/flows/run/node-pool.d.ts +1 -1
- package/dist/flows/run/node-pool.js +7 -2
- package/dist/flows/run/project-options.d.ts +5 -1
- package/dist/flows/run/project.js +8 -8
- package/dist/flows/run/run.d.ts +2 -0
- package/dist/flows/run/run.js +2 -1
- package/dist/runner/worker-protocol.d.ts +2 -0
- package/dist/runner/worker-session.js +3 -3
- package/dist/runner/worker.d.ts +3 -1
- package/dist/runner/worker.js +1 -1
- package/package.json +1 -1
package/dist/capabilities.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export declare const CAPABILITIES: {
|
|
|
31
31
|
readonly recorderStartStatus: true;
|
|
32
32
|
/** `outputDir` on `runTests` and `runFlowInProject`: a folder per run for the report and the screenshots. */
|
|
33
33
|
readonly outputDir: true;
|
|
34
|
+
/** `runFlowInProject`'s `outputDir` gives each node its own folder, `nodes/<node id>/`. */
|
|
35
|
+
readonly flowNodeFolders: true;
|
|
34
36
|
};
|
|
35
37
|
/** The shape of {@link CAPABILITIES}. */
|
|
36
38
|
export type Capabilities = typeof CAPABILITIES;
|
package/dist/capabilities.js
CHANGED
|
@@ -31,4 +31,6 @@ export const CAPABILITIES = {
|
|
|
31
31
|
recorderStartStatus: true,
|
|
32
32
|
/** `outputDir` on `runTests` and `runFlowInProject`: a folder per run for the report and the screenshots. */
|
|
33
33
|
outputDir: true,
|
|
34
|
+
/** `runFlowInProject`'s `outputDir` gives each node its own folder, `nodes/<node id>/`. */
|
|
35
|
+
flowNodeFolders: true,
|
|
34
36
|
};
|
|
@@ -7,7 +7,7 @@ export const fail = (e) => ({
|
|
|
7
7
|
export async function runNodeTests(node, run, list) {
|
|
8
8
|
let result;
|
|
9
9
|
try {
|
|
10
|
-
result = await run.runTests(list, (e) => run.onTestEvent(node.id, e), run.nodeBrowsers?.[node.id]);
|
|
10
|
+
result = await run.runTests(node.id, list, (e) => run.onTestEvent(node.id, e), run.nodeBrowsers?.[node.id]);
|
|
11
11
|
}
|
|
12
12
|
catch (e) {
|
|
13
13
|
return fail(e);
|
|
@@ -14,7 +14,7 @@ export interface HandlerRun {
|
|
|
14
14
|
/** The browsers of the nodes that have their own (a Group node's group); the others use the project's */
|
|
15
15
|
nodeBrowsers?: Record<string, SupportedBrowser[]>;
|
|
16
16
|
signal: AbortSignal;
|
|
17
|
-
runTests: (tests: NodeTest[], onEvent: (e: RunEvent) => void, browsers?: SupportedBrowser[]) => Promise<"passed" | "failed" | "cancelled">;
|
|
17
|
+
runTests: (nodeId: string, tests: NodeTest[], onEvent: (e: RunEvent) => void, browsers?: SupportedBrowser[]) => Promise<"passed" | "failed" | "cancelled">;
|
|
18
18
|
sleep: (ms: number, signal: AbortSignal) => Promise<void>;
|
|
19
19
|
onTestEvent: (nodeId: string, e: RunEvent) => void;
|
|
20
20
|
/** Definitions of kinds core doesn't have (steps), for expression scopes */
|
|
@@ -4,6 +4,6 @@ import type { WorkItem } from "../../runner/worker-protocol.js";
|
|
|
4
4
|
import type { SupportedBrowser } from "../../types/browser.js";
|
|
5
5
|
import type { NodeTest } from "./events.js";
|
|
6
6
|
/** The work items of a node: each test in each browser, the browsers one after the other */
|
|
7
|
-
export declare function itemsOf(tests: NodeTest[], browsers: readonly SupportedBrowser[]): WorkItem[];
|
|
7
|
+
export declare function itemsOf(tests: NodeTest[], browsers: readonly SupportedBrowser[], outputDir?: string): WorkItem[];
|
|
8
8
|
/** Runs the tests of a node on the pool and reports them as a run would */
|
|
9
9
|
export declare function runOnPool(pool: Pick<WorkerPool, "run">, items: WorkItem[], onEvent: (event: RunEvent) => void, signal: AbortSignal): Promise<"passed" | "failed" | "cancelled">;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/** The work items of a node: each test in each browser, the browsers one after the other */
|
|
2
|
-
export function itemsOf(tests, browsers) {
|
|
2
|
+
export function itemsOf(tests, browsers, outputDir) {
|
|
3
3
|
return browsers
|
|
4
|
-
.flatMap((browser) => tests.map((t) => ({
|
|
4
|
+
.flatMap((browser) => tests.map((t) => ({
|
|
5
|
+
file: t.file,
|
|
6
|
+
testName: t.name,
|
|
7
|
+
browser,
|
|
8
|
+
...(outputDir !== undefined && { outputDir }),
|
|
9
|
+
})))
|
|
5
10
|
.map((item, index) => ({ ...item, index }));
|
|
6
11
|
}
|
|
7
12
|
/** Runs the tests of a node on the pool and reports them as a run would */
|
|
@@ -20,7 +20,11 @@ export interface RunFlowInProjectOptions extends ProjectOptions {
|
|
|
20
20
|
/** `"fail"` (default): a flow with a node that can't run is refused. `"skip"`: those nodes are skipped. */
|
|
21
21
|
unsupported?: "fail" | "skip";
|
|
22
22
|
port?: number;
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The folder for the tests' named screenshots, instead of `<project>/result` (see `runTests`). Each node writes to
|
|
25
|
+
* `nodes/<node id>/` inside it, so a test that runs in two nodes keeps both sets; screenshot paths in a node's
|
|
26
|
+
* results are relative to that folder. Relative to the project folder
|
|
27
|
+
*/
|
|
24
28
|
outputDir?: string;
|
|
25
29
|
/**
|
|
26
30
|
* How many worker processes the tests of the whole run share: a number, or a percentage like `"50%"`. Unset
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { cleanName } from "../../browser/evidence.js";
|
|
2
3
|
import { DEFAULT_SRC_DIR } from "../../config/defaults.js";
|
|
3
4
|
import { projectConfig } from "../../config/config.js";
|
|
4
5
|
import { checkFlow, withKeys, } from "../core/index.js";
|
|
@@ -176,9 +177,6 @@ async function walkFlowInProject(options) {
|
|
|
176
177
|
...(options.headless !== undefined && {
|
|
177
178
|
headless: options.headless,
|
|
178
179
|
}),
|
|
179
|
-
...(options.outputDir !== undefined && {
|
|
180
|
-
outputDir: options.outputDir,
|
|
181
|
-
}),
|
|
182
180
|
files,
|
|
183
181
|
basePort: options.port ?? DEFAULT_PORT,
|
|
184
182
|
...(retries > 0 && { retries }),
|
|
@@ -197,9 +195,13 @@ async function walkFlowInProject(options) {
|
|
|
197
195
|
unsupported,
|
|
198
196
|
handlers: { ...HANDLERS, ...steps.handlers },
|
|
199
197
|
nodes: steps.nodes,
|
|
200
|
-
runTests: async ({ tests, onEvent, signal, browsers }) => {
|
|
198
|
+
runTests: async ({ nodeId, tests, onEvent, signal, browsers }) => {
|
|
199
|
+
// Each node gets a folder of its own: the same test in two nodes would overwrite its screenshots
|
|
200
|
+
const outputDir = options.outputDir !== undefined
|
|
201
|
+
? path.join(options.outputDir, "nodes", cleanName(nodeId))
|
|
202
|
+
: undefined;
|
|
201
203
|
if (pool)
|
|
202
|
-
return runOnPool(pool, itemsOf(tests, browsers ?? browsersFor({ project: config.browsers })), onEvent, signal);
|
|
204
|
+
return runOnPool(pool, itemsOf(tests, browsers ?? browsersFor({ project: config.browsers }), outputDir), onEvent, signal);
|
|
203
205
|
const summary = await run({
|
|
204
206
|
projectDir,
|
|
205
207
|
config,
|
|
@@ -217,9 +219,7 @@ async function walkFlowInProject(options) {
|
|
|
217
219
|
files: [...new Set(tests.map((t) => t.file))],
|
|
218
220
|
testNames: tests.map((t) => t.name),
|
|
219
221
|
reportPath: false,
|
|
220
|
-
...(
|
|
221
|
-
outputDir: options.outputDir,
|
|
222
|
-
}),
|
|
222
|
+
...(outputDir !== undefined && { outputDir }),
|
|
223
223
|
signal,
|
|
224
224
|
onEvent,
|
|
225
225
|
});
|
package/dist/flows/run/run.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface RunFlowOptions {
|
|
|
20
20
|
signal?: AbortSignal;
|
|
21
21
|
onEvent?: (event: FlowEvent) => void;
|
|
22
22
|
runTests: (input: {
|
|
23
|
+
/** The node the tests are for: a node runs at most once in a run */
|
|
24
|
+
nodeId: string;
|
|
23
25
|
tests: NodeTest[];
|
|
24
26
|
onEvent: (e: RunEvent) => void;
|
|
25
27
|
signal: AbortSignal;
|
package/dist/flows/run/run.js
CHANGED
|
@@ -34,7 +34,8 @@ export async function runFlow(options) {
|
|
|
34
34
|
...(options.nodeBrowsers && { nodeBrowsers: options.nodeBrowsers }),
|
|
35
35
|
signal,
|
|
36
36
|
sleep: options.sleep ?? defaultSleep,
|
|
37
|
-
runTests: (tests, onTestEvent, browsers) => options.runTests({
|
|
37
|
+
runTests: (nodeId, tests, onTestEvent, browsers) => options.runTests({
|
|
38
|
+
nodeId,
|
|
38
39
|
tests,
|
|
39
40
|
onEvent: onTestEvent,
|
|
40
41
|
signal,
|
|
@@ -11,6 +11,8 @@ export interface WorkItem {
|
|
|
11
11
|
browser: SupportedBrowser;
|
|
12
12
|
/** Items with the same key never run at the same time */
|
|
13
13
|
serialKey?: string;
|
|
14
|
+
/** Where this test's screenshots go, instead of the run's `outputDir` (a flow gives each node its own) */
|
|
15
|
+
outputDir?: string;
|
|
14
16
|
}
|
|
15
17
|
/** What a worker needs to start: the project and the settings that are the same for every test */
|
|
16
18
|
export interface WorkerInit {
|
|
@@ -36,7 +36,7 @@ export function openSession(init) {
|
|
|
36
36
|
});
|
|
37
37
|
resolve({
|
|
38
38
|
tests: loaded.all,
|
|
39
|
-
async runTest(test, browser, signal, onEvent) {
|
|
39
|
+
async runTest(test, browser, signal, onEvent, perTest = {}) {
|
|
40
40
|
const reporter = new TestReporter({
|
|
41
41
|
environment: run.settings.environment,
|
|
42
42
|
browsers: [browser],
|
|
@@ -48,8 +48,8 @@ export function openSession(init) {
|
|
|
48
48
|
...(init.reportPath !== undefined && {
|
|
49
49
|
reportPath: init.reportPath,
|
|
50
50
|
}),
|
|
51
|
-
...(init.outputDir !== undefined && {
|
|
52
|
-
outputDir: init.outputDir,
|
|
51
|
+
...((perTest.outputDir ?? init.outputDir) !== undefined && {
|
|
52
|
+
outputDir: perTest.outputDir ?? init.outputDir,
|
|
53
53
|
}),
|
|
54
54
|
port,
|
|
55
55
|
signal,
|
package/dist/runner/worker.d.ts
CHANGED
|
@@ -6,7 +6,9 @@ import type { FromWorker, ToWorker, WorkerInit } from "./worker-protocol.js";
|
|
|
6
6
|
export interface WorkerSession {
|
|
7
7
|
tests: readonly RegisteredTestCase[];
|
|
8
8
|
/** Runs one test; `onEvent` gets the reporter's events for it. Aborting `signal` ends the test as aborted */
|
|
9
|
-
runTest(test: RegisteredTestCase, browser: SupportedBrowser, signal: AbortSignal, onEvent: (event: RunEvent) => void
|
|
9
|
+
runTest(test: RegisteredTestCase, browser: SupportedBrowser, signal: AbortSignal, onEvent: (event: RunEvent) => void, options?: {
|
|
10
|
+
outputDir?: string;
|
|
11
|
+
}): Promise<void>;
|
|
10
12
|
/** Ends the project's session */
|
|
11
13
|
close(): Promise<void>;
|
|
12
14
|
}
|
package/dist/runner/worker.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itmetsam/samurai-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Browser test framework over WebDriver BiDi: auto-waiting locators, environments and secrets, a recorder, and a command line.",
|
|
5
5
|
"main": "dist/api.js",
|
|
6
6
|
"exports": {
|