@schwabyio/gta 0.13.0 → 0.14.1
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 +178 -1
- package/dist/FUNCTIONS.md +6 -3
- package/dist/SPEC.md +289 -111
- package/dist/THIRD_PARTY_NOTICES.txt +26 -0
- package/dist/chunks/chunk-4JUUUUWB.js +234 -0
- package/dist/chunks/chunk-4W4TFJ53.js +577 -0
- package/dist/chunks/{chunk-ABBBM5UZ.js → chunk-S563G22M.js} +8812 -1953
- package/dist/gta.js +1036 -998
- package/dist/index.d.ts +3 -0
- package/dist/index.js +14 -0
- package/dist/playwright.d.ts +37 -0
- package/dist/playwright.js +178 -0
- package/dist/types.d.ts +246 -0
- package/dist/worker.js +1 -1
- package/package.json +25 -2
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __gtaCreateRequire } from 'node:module';
|
|
3
|
+
const require = __gtaCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
openProject
|
|
6
|
+
} from "./chunks/chunk-4JUUUUWB.js";
|
|
7
|
+
import "./chunks/chunk-4W4TFJ53.js";
|
|
8
|
+
import "./chunks/chunk-S563G22M.js";
|
|
9
|
+
|
|
10
|
+
// src/library/index.ts
|
|
11
|
+
var openProject2 = openProject;
|
|
12
|
+
export {
|
|
13
|
+
openProject2 as openProject
|
|
14
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { expect } from '@playwright/test';
|
|
2
|
+
import type { PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType } from '@playwright/test';
|
|
3
|
+
import type { GravityProject, OpenOptions, RunOptions, RunOutcome, UseOptions, VarValue } from './types.js';
|
|
4
|
+
export interface GravityOptions extends OpenOptions {
|
|
5
|
+
/** The project folder, relative to the Playwright config's folder. Absent: that folder. */
|
|
6
|
+
project?: string;
|
|
7
|
+
}
|
|
8
|
+
/** The `gravity` option, for `defineConfig<{}, GravityConfig>`. */
|
|
9
|
+
export interface GravityConfig {
|
|
10
|
+
gravity: GravityOptions;
|
|
11
|
+
}
|
|
12
|
+
export interface GtaRunOptions extends Omit<RunOptions, 'signal'> {
|
|
13
|
+
/** On a failure, go on with the test and fail it at the end, as `expect.soft` does. */
|
|
14
|
+
soft?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface GtaUseOptions extends Omit<UseOptions, 'signal'> {
|
|
17
|
+
/** On a failure, go on with the test and fail it at the end, as `expect.soft` does. */
|
|
18
|
+
soft?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** The `gta` fixture. */
|
|
21
|
+
export interface Gta {
|
|
22
|
+
/** The project, opened once for each worker. */
|
|
23
|
+
readonly project: GravityProject;
|
|
24
|
+
/** `project.run`, reported as a step, failing the test when the run fails. */
|
|
25
|
+
run(collection: string, options?: GtaRunOptions): Promise<RunOutcome>;
|
|
26
|
+
/** `project.use`, reported as a step, failing the test when the run fails. */
|
|
27
|
+
use(name: string, params?: Record<string, VarValue>, options?: GtaUseOptions): Promise<RunOutcome>;
|
|
28
|
+
}
|
|
29
|
+
export interface GravityFixtures {
|
|
30
|
+
gta: Gta;
|
|
31
|
+
}
|
|
32
|
+
export interface GravityWorkerFixtures extends GravityConfig {
|
|
33
|
+
gravityProject: GravityProject;
|
|
34
|
+
}
|
|
35
|
+
export type * from './types.js';
|
|
36
|
+
export { expect };
|
|
37
|
+
export declare const test: TestType<PlaywrightTestArgs & PlaywrightTestOptions & GravityFixtures, PlaywrightWorkerArgs & PlaywrightWorkerOptions & GravityWorkerFixtures>;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __gtaCreateRequire } from 'node:module';
|
|
3
|
+
const require = __gtaCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
openProject
|
|
6
|
+
} from "./chunks/chunk-4JUUUUWB.js";
|
|
7
|
+
import "./chunks/chunk-4W4TFJ53.js";
|
|
8
|
+
import {
|
|
9
|
+
iterationName,
|
|
10
|
+
resultName
|
|
11
|
+
} from "./chunks/chunk-S563G22M.js";
|
|
12
|
+
|
|
13
|
+
// src/library/playwright.ts
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { expect, test as base } from "@playwright/test";
|
|
17
|
+
var test = base.extend({
|
|
18
|
+
gravity: [{}, { scope: "worker", option: true }],
|
|
19
|
+
// Once for each worker: settings read, and an environment's flag command run, once.
|
|
20
|
+
gravityProject: [
|
|
21
|
+
async ({ gravity }, use, workerInfo) => {
|
|
22
|
+
const { project = ".", ...options } = gravity;
|
|
23
|
+
const { configFile, rootDir } = workerInfo.config;
|
|
24
|
+
const from = configFile ? path.dirname(configFile) : rootDir;
|
|
25
|
+
await use(await openProject(path.resolve(from, project), options));
|
|
26
|
+
},
|
|
27
|
+
{ scope: "worker", title: "open the Gravity project" }
|
|
28
|
+
],
|
|
29
|
+
gta: async ({ gravityProject }, use) => {
|
|
30
|
+
const runs = { ending: new AbortController(), running: /* @__PURE__ */ new Set() };
|
|
31
|
+
await use(gtaFor(gravityProject, runs));
|
|
32
|
+
runs.ending.abort();
|
|
33
|
+
await Promise.allSettled(runs.running);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
var checks = expect.extend({
|
|
37
|
+
toHavePassed(outcome, title, caller) {
|
|
38
|
+
const message = messageOf(title, outcome);
|
|
39
|
+
if (outcome.passed) return { pass: true, name: "toHavePassed", message: () => message };
|
|
40
|
+
const error = new Error(message);
|
|
41
|
+
if (caller) {
|
|
42
|
+
error.stack = `Error: ${message}
|
|
43
|
+
at ${caller.file}:${caller.line}:${caller.column}`;
|
|
44
|
+
}
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
function messageOf(title, outcome) {
|
|
49
|
+
const { summary, error } = outcome;
|
|
50
|
+
const failed = summary.failed + summary.errored;
|
|
51
|
+
const headline = error ? `${title}: ${error}` : `${title}: ${failed} of ${summary.total} ${summary.total === 1 ? "step" : "steps"} failed`;
|
|
52
|
+
const details = outcome.failures.split("\n").slice(1).filter((line) => !error || line.trim() !== `\u2717 ${error}`);
|
|
53
|
+
return details.length > 0 ? `${headline}
|
|
54
|
+
|
|
55
|
+
${details.join("\n")}` : headline;
|
|
56
|
+
}
|
|
57
|
+
function gtaFor(project, runs) {
|
|
58
|
+
return {
|
|
59
|
+
project,
|
|
60
|
+
run: (collection, { soft, onResult, ...options } = {}) => reported(
|
|
61
|
+
`gta ${collection}`,
|
|
62
|
+
{ soft, onResult },
|
|
63
|
+
runs,
|
|
64
|
+
(hooks) => project.run(collection, { ...options, ...hooks })
|
|
65
|
+
),
|
|
66
|
+
use: (set, params, { soft, onResult, ...options } = {}) => reported(
|
|
67
|
+
`gta use ${set}`,
|
|
68
|
+
{ soft, onResult },
|
|
69
|
+
runs,
|
|
70
|
+
(hooks) => project.use(set, params, { ...options, ...hooks })
|
|
71
|
+
)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function reported(title, options, runs, start) {
|
|
75
|
+
const location = callerLocation();
|
|
76
|
+
return base.step(
|
|
77
|
+
title,
|
|
78
|
+
async () => {
|
|
79
|
+
let reporting = Promise.resolve();
|
|
80
|
+
const run = start({
|
|
81
|
+
signal: runs.ending.signal,
|
|
82
|
+
onResult: (result, step) => {
|
|
83
|
+
options.onResult?.(result, step);
|
|
84
|
+
reporting = reporting.then(() => reportResult(result, step));
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
runs.running.add(run);
|
|
88
|
+
const outcome = await run.finally(() => runs.running.delete(run));
|
|
89
|
+
await reporting;
|
|
90
|
+
if (!outcome.passed && !runs.ending.signal.aborted) {
|
|
91
|
+
if (options.soft) checks.soft(outcome).toHavePassed(title, location);
|
|
92
|
+
else checks(outcome).toHavePassed(title, location);
|
|
93
|
+
}
|
|
94
|
+
return outcome;
|
|
95
|
+
},
|
|
96
|
+
location ? { location } : {}
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
async function reportResult(result, step) {
|
|
100
|
+
await base.step(
|
|
101
|
+
titleOf(result, step),
|
|
102
|
+
async (info) => {
|
|
103
|
+
if (result.status !== "skipped" && result.request.url !== "") {
|
|
104
|
+
await info.attach("request and response", { body: exchangeOf(result) });
|
|
105
|
+
}
|
|
106
|
+
if (result.status === "skipped") info.skip(true, result.skipped?.reason);
|
|
107
|
+
if (result.status === "fail" || result.status === "error") {
|
|
108
|
+
throw new Error(problemsOf(result));
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{ location: { file: step.file, line: step.line ?? 1, column: 1 } }
|
|
112
|
+
).catch(() => {
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function titleOf(result, step) {
|
|
116
|
+
const parts = [iterationName(resultName(result), step)];
|
|
117
|
+
if (result.response) parts.push(String(result.response.status));
|
|
118
|
+
else if (result.status === "skipped") parts.push("skipped");
|
|
119
|
+
if (result.status !== "skipped") parts.push(`${Math.round(result.durationMs)} ms`);
|
|
120
|
+
return parts.join(" \xB7 ");
|
|
121
|
+
}
|
|
122
|
+
function problemsOf(result) {
|
|
123
|
+
const lines = [];
|
|
124
|
+
if (result.error) lines.push(`${result.error.phase} error: ${result.error.message}`);
|
|
125
|
+
for (const check of result.assertions) {
|
|
126
|
+
if (check.status !== "fail") continue;
|
|
127
|
+
lines.push(`${check.name}${check.message ? `: ${check.message}` : ""}`);
|
|
128
|
+
if (check.expected !== void 0) lines.push(` expected ${check.expected}`);
|
|
129
|
+
if (check.actual !== void 0) lines.push(` actual ${check.actual}`);
|
|
130
|
+
}
|
|
131
|
+
return lines.join("\n") || "failed";
|
|
132
|
+
}
|
|
133
|
+
var BODY_LIMIT = 256 * 1024;
|
|
134
|
+
var clip = (body) => body.length > BODY_LIMIT ? `${body.slice(0, BODY_LIMIT)}
|
|
135
|
+
\u2026 (${body.length - BODY_LIMIT} more characters)` : body;
|
|
136
|
+
function exchangeOf(result) {
|
|
137
|
+
const { request, response } = result;
|
|
138
|
+
const lines = [`${request.method} ${request.url}`];
|
|
139
|
+
for (const header of request.headers) lines.push(`${header.name}: ${header.value}`);
|
|
140
|
+
if (request.body) lines.push("", clip(request.body));
|
|
141
|
+
lines.push("");
|
|
142
|
+
if (response) {
|
|
143
|
+
lines.push(
|
|
144
|
+
`${response.status} ${response.statusText} \xB7 ${Math.round(response.timings.totalMs)} ms \xB7 ${response.sizeBytes} bytes`
|
|
145
|
+
);
|
|
146
|
+
for (const header of response.headers) lines.push(`${header.name}: ${header.value}`);
|
|
147
|
+
if (response.bodyEncoding === "base64")
|
|
148
|
+
lines.push("", `[binary body, ${response.sizeBytes} bytes]`);
|
|
149
|
+
else if (response.body) lines.push("", clip(response.body));
|
|
150
|
+
} else {
|
|
151
|
+
lines.push(result.error ? `No response: ${result.error.message}` : "No response");
|
|
152
|
+
}
|
|
153
|
+
if (result.assertions.length > 0) {
|
|
154
|
+
lines.push("", "Checks:");
|
|
155
|
+
for (const check of result.assertions) {
|
|
156
|
+
lines.push(`${check.status === "pass" ? "\u2713" : "\u2715"} ${check.name}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return `${lines.join("\n")}
|
|
160
|
+
`;
|
|
161
|
+
}
|
|
162
|
+
var ours = path.dirname(fileURLToPath(import.meta.url));
|
|
163
|
+
function callerLocation() {
|
|
164
|
+
for (const frame of (new Error().stack ?? "").split("\n").slice(1)) {
|
|
165
|
+
const match = /^\s*at (?:.*? \()?(.+?):(\d+):(\d+)\)?\s*$/.exec(frame);
|
|
166
|
+
if (!match) continue;
|
|
167
|
+
const file = match[1].startsWith("file://") ? fileURLToPath(match[1]) : match[1];
|
|
168
|
+
if (file.startsWith("node:") || !path.isAbsolute(file)) continue;
|
|
169
|
+
if (file.startsWith(ours + path.sep) || path.dirname(file) === ours) continue;
|
|
170
|
+
if (file.includes(`${path.sep}node_modules${path.sep}`)) continue;
|
|
171
|
+
return { file, line: Number(match[2]), column: Number(match[3]) };
|
|
172
|
+
}
|
|
173
|
+
return void 0;
|
|
174
|
+
}
|
|
175
|
+
export {
|
|
176
|
+
expect,
|
|
177
|
+
test
|
|
178
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The types of gta's library, `@schwabyio/gta`: a project's collections and
|
|
3
|
+
* reusable requests run from code, such as a Playwright test.
|
|
4
|
+
*
|
|
5
|
+
* Written out here rather than taken from the core, so the published `.d.ts`
|
|
6
|
+
* files stand alone: the core's are zod's inferences, and neither ships.
|
|
7
|
+
* `project.ts` fails to compile when the two drift apart.
|
|
8
|
+
*/
|
|
9
|
+
/** A variable's value: plain data only (SPEC.md §4). */
|
|
10
|
+
export type VarValue = string | number | boolean | null;
|
|
11
|
+
/** A feature flag's value (SPEC.md §2.9). */
|
|
12
|
+
export type FlagValue = string | number | boolean;
|
|
13
|
+
export interface HeaderEntry {
|
|
14
|
+
name: string;
|
|
15
|
+
value: string;
|
|
16
|
+
}
|
|
17
|
+
export interface SentRequest {
|
|
18
|
+
method: string;
|
|
19
|
+
/** After variables are resolved. */
|
|
20
|
+
url: string;
|
|
21
|
+
headers: HeaderEntry[];
|
|
22
|
+
body: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** How an event stream was read (SPEC.md §2.3). */
|
|
25
|
+
export interface EventStreamRead {
|
|
26
|
+
endedBy: 'close' | 'maxEvents' | 'streamTimeout' | 'untilEvent' | 'limit' | 'stopped' | 'held';
|
|
27
|
+
/** Milliseconds from the response headers to each event, in order. */
|
|
28
|
+
at: number[];
|
|
29
|
+
/** For a step that opens or reads a connection (SPEC.md §2.11). */
|
|
30
|
+
connection?: {
|
|
31
|
+
name: string;
|
|
32
|
+
open: boolean;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export interface ReceivedResponse {
|
|
36
|
+
status: number;
|
|
37
|
+
statusText: string;
|
|
38
|
+
/** Where the response came from, after any redirects. */
|
|
39
|
+
url: string;
|
|
40
|
+
headers: HeaderEntry[];
|
|
41
|
+
body: string;
|
|
42
|
+
bodyKind: 'json' | 'xml' | 'html' | 'text' | 'binary' | 'empty' | 'events';
|
|
43
|
+
/** `base64` when `body` is the bytes in base64: a binary body that is not text, an image's. */
|
|
44
|
+
bodyEncoding?: 'base64';
|
|
45
|
+
sizeBytes: number;
|
|
46
|
+
redirectCount: number;
|
|
47
|
+
timings: {
|
|
48
|
+
/** Epoch milliseconds. */
|
|
49
|
+
startedAt: number;
|
|
50
|
+
ttfbMs: number;
|
|
51
|
+
totalMs: number;
|
|
52
|
+
};
|
|
53
|
+
stream?: EventStreamRead;
|
|
54
|
+
}
|
|
55
|
+
/** Whose script something came from. */
|
|
56
|
+
export type ScriptOwner = 'endpoint-file' | 'endpoint' | 'base' | 'collection' | 'set' | 'step' | 'use';
|
|
57
|
+
/** Where a script made a check: whose script, and its line there, from 1. */
|
|
58
|
+
export interface ScriptSource {
|
|
59
|
+
script: ScriptOwner;
|
|
60
|
+
line: number;
|
|
61
|
+
/** For a check a check file made: that file, and its line. */
|
|
62
|
+
check?: {
|
|
63
|
+
file: string;
|
|
64
|
+
line: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** One check a step's tests made. */
|
|
68
|
+
export interface AssertionResult {
|
|
69
|
+
name: string;
|
|
70
|
+
status: 'pass' | 'fail';
|
|
71
|
+
message?: string;
|
|
72
|
+
target?: 'status' | 'header' | 'body' | 'strict' | 'custom';
|
|
73
|
+
/** The header name, or the body path as written. */
|
|
74
|
+
path?: string;
|
|
75
|
+
expected?: string;
|
|
76
|
+
actual?: string;
|
|
77
|
+
/** Strict validation only: body paths nothing checked. */
|
|
78
|
+
unasserted?: string[];
|
|
79
|
+
/** A body check only: the paths it vouched for. */
|
|
80
|
+
covered?: string[];
|
|
81
|
+
source?: ScriptSource;
|
|
82
|
+
}
|
|
83
|
+
export interface RunError {
|
|
84
|
+
phase: 'use' | 'flags' | 'forEach' | 'interpolate' | 'body' | 'pre-request' | 'http' | 'connection' | 'tests';
|
|
85
|
+
message: string;
|
|
86
|
+
code?: string;
|
|
87
|
+
stack?: string;
|
|
88
|
+
/** For a script error: its line in that script, from 1. */
|
|
89
|
+
line?: number;
|
|
90
|
+
script?: ScriptOwner;
|
|
91
|
+
}
|
|
92
|
+
/** One request a run sent, and what came of it: the same result gta's JSON report holds. */
|
|
93
|
+
export interface RunResult {
|
|
94
|
+
item: {
|
|
95
|
+
path: string | null;
|
|
96
|
+
name: string;
|
|
97
|
+
seq: number | null;
|
|
98
|
+
};
|
|
99
|
+
request: SentRequest;
|
|
100
|
+
response: ReceivedResponse | null;
|
|
101
|
+
assertions: AssertionResult[];
|
|
102
|
+
/** Body paths the tests ignored (`gta.ignoreResponseBodyProperty`). */
|
|
103
|
+
ignored?: Array<{
|
|
104
|
+
path: string;
|
|
105
|
+
source?: ScriptSource;
|
|
106
|
+
}>;
|
|
107
|
+
/** `gta.sortResponseBodyArrays` as applied. */
|
|
108
|
+
sortedBy?: string[];
|
|
109
|
+
/** What the step's scripts wrote with `console`. */
|
|
110
|
+
logs?: Array<{
|
|
111
|
+
level: 'log' | 'info' | 'warn' | 'error';
|
|
112
|
+
message: string;
|
|
113
|
+
phase: 'pre-request' | 'tests';
|
|
114
|
+
}>;
|
|
115
|
+
error: RunError | null;
|
|
116
|
+
status: 'pass' | 'fail' | 'error' | 'skipped';
|
|
117
|
+
/** Why a skipped step did not run. */
|
|
118
|
+
skipped?: {
|
|
119
|
+
reason: string;
|
|
120
|
+
};
|
|
121
|
+
durationMs: number;
|
|
122
|
+
/**
|
|
123
|
+
* For a request a use step ran: the reusable requests as `use` named them,
|
|
124
|
+
* the use step's name, and which of their steps, from 0.
|
|
125
|
+
*/
|
|
126
|
+
use?: {
|
|
127
|
+
set: string;
|
|
128
|
+
name?: string;
|
|
129
|
+
child: number;
|
|
130
|
+
of: number;
|
|
131
|
+
};
|
|
132
|
+
/** For a setup or teardown step. */
|
|
133
|
+
stage?: 'setup' | 'teardown';
|
|
134
|
+
/** For a step with `forEach`: which item, from 0. */
|
|
135
|
+
forEach?: {
|
|
136
|
+
index: number;
|
|
137
|
+
of: number;
|
|
138
|
+
item: string;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** A run's totals, and every result. */
|
|
142
|
+
export interface CollectionRunSummary {
|
|
143
|
+
total: number;
|
|
144
|
+
passed: number;
|
|
145
|
+
failed: number;
|
|
146
|
+
errored: number;
|
|
147
|
+
/** Skipped by a flag or a script, and never attempted after a bail or a cancel. */
|
|
148
|
+
skipped: number;
|
|
149
|
+
durationMs: number;
|
|
150
|
+
results: RunResult[];
|
|
151
|
+
}
|
|
152
|
+
/** Where a result's step is written, for a person to go and look. */
|
|
153
|
+
export interface StepRef {
|
|
154
|
+
/** The file, absolute: the collection's, or for `use`, the reusable requests file's. */
|
|
155
|
+
file: string;
|
|
156
|
+
/** The step's line in it, from 1; null when it cannot be told. */
|
|
157
|
+
line: number | null;
|
|
158
|
+
/** The step's place in its list, from 0. */
|
|
159
|
+
index: number;
|
|
160
|
+
/** For a setup or teardown step, which list it is in. */
|
|
161
|
+
stage?: 'setup' | 'teardown';
|
|
162
|
+
/** For a collection with a data file: the row it ran with, from 1. */
|
|
163
|
+
iteration?: {
|
|
164
|
+
index: number;
|
|
165
|
+
of: number;
|
|
166
|
+
label: string | null;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
export interface OpenOptions {
|
|
170
|
+
/** The environment to run against, by name. Absent: `environmentType` from settings.yml. */
|
|
171
|
+
environment?: string;
|
|
172
|
+
/** Feature flag values, over the environment's and `GTA_FLAG_*` (SPEC.md §2.9). */
|
|
173
|
+
flags?: Record<string, FlagValue>;
|
|
174
|
+
/** Stop a run at its first failing step. Absent: `bail` from settings.yml. */
|
|
175
|
+
bail?: boolean;
|
|
176
|
+
/** Milliseconds a run may take before it is stopped. Absent: `timeoutCollection` from settings.yml. */
|
|
177
|
+
timeoutCollection?: number;
|
|
178
|
+
}
|
|
179
|
+
export interface RunOptions {
|
|
180
|
+
/**
|
|
181
|
+
* The steps to run: a step's name, as reports show it, or its place in
|
|
182
|
+
* `steps:` from 1. They run in the file's order. Absent: every step. Setup
|
|
183
|
+
* and teardown run either way.
|
|
184
|
+
*/
|
|
185
|
+
steps?: ReadonlyArray<string | number>;
|
|
186
|
+
/** Values to start with, over the environment and under a data row. */
|
|
187
|
+
vars?: Record<string, VarValue>;
|
|
188
|
+
/** Stop at the first failing step. Absent: the project's `bail`. */
|
|
189
|
+
bail?: boolean;
|
|
190
|
+
signal?: AbortSignal;
|
|
191
|
+
/** As each request finishes, with the step it came from. */
|
|
192
|
+
onResult?: (result: RunResult, step: StepRef) => void;
|
|
193
|
+
}
|
|
194
|
+
export type UseOptions = Omit<RunOptions, 'steps'>;
|
|
195
|
+
export interface RunOutcome {
|
|
196
|
+
/** Nothing failed or errored, and the run finished. */
|
|
197
|
+
passed: boolean;
|
|
198
|
+
/** The collection's id, or the reusable requests as `use` named them. */
|
|
199
|
+
id: string;
|
|
200
|
+
/** Its file, absolute. */
|
|
201
|
+
file: string;
|
|
202
|
+
/** Why it did not run, or did not finish: a file that will not load, a timeout, a cancel. */
|
|
203
|
+
error: string | null;
|
|
204
|
+
/** Every request's result, in order, secrets hidden. */
|
|
205
|
+
summary: CollectionRunSummary;
|
|
206
|
+
/** Where each result's step is: one per result, in the same order. */
|
|
207
|
+
steps: StepRef[];
|
|
208
|
+
/**
|
|
209
|
+
* Every value the run set or captured, by name: `gta.set`, and checks that
|
|
210
|
+
* save what they read. The last one set wins. Not redacted.
|
|
211
|
+
*/
|
|
212
|
+
values: Record<string, VarValue>;
|
|
213
|
+
/** What went wrong, as gta prints it under Failures; empty when it passed. */
|
|
214
|
+
failures: string;
|
|
215
|
+
}
|
|
216
|
+
/** A project, open: its settings read, its feature flags resolved. */
|
|
217
|
+
export interface GravityProject {
|
|
218
|
+
/** The project folder, absolute. */
|
|
219
|
+
readonly root: string;
|
|
220
|
+
/** `name` from project.yml, or the folder's name. */
|
|
221
|
+
readonly name: string;
|
|
222
|
+
/** The environment runs use; null for none. */
|
|
223
|
+
readonly environment: string | null;
|
|
224
|
+
/** The environments it has. */
|
|
225
|
+
readonly environments: string[];
|
|
226
|
+
readonly flags: Record<string, FlagValue>;
|
|
227
|
+
/** Its collections, by id. */
|
|
228
|
+
readonly collections: string[];
|
|
229
|
+
/** Problems with the project's layout that stop nothing, such as a folder too deep. */
|
|
230
|
+
readonly warnings: string[];
|
|
231
|
+
/**
|
|
232
|
+
* Run a collection, named by its id or its place in `collections/`
|
|
233
|
+
* (`checkout/sessions`): setup, its steps once for each data row, then
|
|
234
|
+
* teardown, as gta runs it. Resolves however the steps fare; rejects only
|
|
235
|
+
* for a name nothing answers to.
|
|
236
|
+
*/
|
|
237
|
+
run(collection: string, options?: RunOptions): Promise<RunOutcome>;
|
|
238
|
+
/**
|
|
239
|
+
* Run reusable requests as a use step would (SPEC.md §2.5): `login` is
|
|
240
|
+
* `requests/login.yml`, in the project or its global project. A param left
|
|
241
|
+
* out takes its default.
|
|
242
|
+
*/
|
|
243
|
+
use(name: string, params?: Record<string, VarValue>, options?: UseOptions): Promise<RunOutcome>;
|
|
244
|
+
}
|
|
245
|
+
/** Open the project in `folder`, the one holding `collections/`. */
|
|
246
|
+
export type OpenProjectFunction = (folder: string, options?: OpenOptions) => Promise<GravityProject>;
|
package/dist/worker.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createRequire as __gtaCreateRequire } from 'node:module';
|
|
|
3
3
|
const require = __gtaCreateRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
runJob
|
|
6
|
-
} from "./chunks/chunk-
|
|
6
|
+
} from "./chunks/chunk-S563G22M.js";
|
|
7
7
|
|
|
8
8
|
// src/worker.ts
|
|
9
9
|
import { parentPort, workerData } from "node:worker_threads";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schwabyio/gta",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "gta, the Gravity Test Automation command-line runner: HTTP API tests written as YAML files, run from a terminal or CI.",
|
|
6
6
|
"keywords": [
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"cli",
|
|
14
14
|
"yaml",
|
|
15
15
|
"junit",
|
|
16
|
-
"ci"
|
|
16
|
+
"ci",
|
|
17
|
+
"playwright"
|
|
17
18
|
],
|
|
18
19
|
"license": "MIT",
|
|
19
20
|
"homepage": "https://github.com/schwabyio/gravity#readme",
|
|
@@ -28,6 +29,18 @@
|
|
|
28
29
|
"bin": {
|
|
29
30
|
"gta": "dist/gta.js"
|
|
30
31
|
},
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./playwright": {
|
|
38
|
+
"types": "./dist/playwright.d.ts",
|
|
39
|
+
"default": "./dist/playwright.js"
|
|
40
|
+
},
|
|
41
|
+
"./package.json": "./package.json"
|
|
42
|
+
},
|
|
43
|
+
"types": "./dist/index.d.ts",
|
|
31
44
|
"files": [
|
|
32
45
|
"dist"
|
|
33
46
|
],
|
|
@@ -37,14 +50,24 @@
|
|
|
37
50
|
"publishConfig": {
|
|
38
51
|
"access": "public"
|
|
39
52
|
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@playwright/test": ">=1.51.0"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"@playwright/test": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
},
|
|
40
61
|
"scripts": {
|
|
41
62
|
"build": "node build.mjs",
|
|
42
63
|
"prepack": "node build.mjs",
|
|
43
64
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
44
65
|
},
|
|
45
66
|
"devDependencies": {
|
|
67
|
+
"@playwright/test": "^1.63.0",
|
|
46
68
|
"@schwabyio/gravity-core": "*",
|
|
47
69
|
"esbuild": "0.28.2",
|
|
70
|
+
"typescript": "5.9.3",
|
|
48
71
|
"yaml": "2.9.1",
|
|
49
72
|
"zod": "4.6.5"
|
|
50
73
|
}
|