@lingxia/test 0.17.0 → 0.19.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/clock.d.ts +30 -0
- package/dist/clock.d.ts.map +1 -0
- package/dist/clock.js +99 -0
- package/dist/clock.js.map +1 -0
- package/dist/coverage.d.ts +25 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +92 -0
- package/dist/coverage.js.map +1 -0
- package/dist/deadline.d.ts +82 -0
- package/dist/deadline.d.ts.map +1 -0
- package/dist/deadline.js +184 -0
- package/dist/deadline.js.map +1 -0
- package/dist/dialogs.d.ts +28 -0
- package/dist/dialogs.d.ts.map +1 -0
- package/dist/dialogs.js +74 -0
- package/dist/dialogs.js.map +1 -0
- package/dist/equal.d.ts +28 -0
- package/dist/equal.d.ts.map +1 -0
- package/dist/equal.js +149 -0
- package/dist/equal.js.map +1 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +37 -0
- package/dist/errors.js.map +1 -0
- package/dist/expect.d.ts +35 -7
- package/dist/expect.d.ts.map +1 -1
- package/dist/expect.js +216 -68
- package/dist/expect.js.map +1 -1
- package/dist/fixture.d.ts +205 -40
- package/dist/fixture.d.ts.map +1 -1
- package/dist/fixture.js +1178 -156
- package/dist/fixture.js.map +1 -1
- package/dist/format.d.ts +2 -0
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +78 -0
- package/dist/format.js.map +1 -1
- package/dist/host-types.d.ts +65 -0
- package/dist/host-types.d.ts.map +1 -0
- package/dist/host-types.js +2 -0
- package/dist/host-types.js.map +1 -0
- package/dist/host.d.ts +20 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +34 -2
- package/dist/host.js.map +1 -1
- package/dist/ids.d.ts +16 -0
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +38 -3
- package/dist/ids.js.map +1 -1
- package/dist/index.d.ts +5 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/json-result.d.ts +3 -0
- package/dist/json-result.d.ts.map +1 -0
- package/dist/json-result.js +62 -0
- package/dist/json-result.js.map +1 -0
- package/dist/junit.d.ts +1 -1
- package/dist/junit.d.ts.map +1 -1
- package/dist/junit.js +40 -5
- package/dist/junit.js.map +1 -1
- package/dist/locator.d.ts +116 -7
- package/dist/locator.d.ts.map +1 -1
- package/dist/locator.js +444 -58
- package/dist/locator.js.map +1 -1
- package/dist/mock.d.ts +43 -0
- package/dist/mock.d.ts.map +1 -0
- package/dist/mock.js +162 -0
- package/dist/mock.js.map +1 -0
- package/dist/network.d.ts +68 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +287 -0
- package/dist/network.js.map +1 -0
- package/dist/openapi.d.ts +127 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +414 -0
- package/dist/openapi.js.map +1 -0
- package/dist/pending.d.ts +84 -0
- package/dist/pending.d.ts.map +1 -0
- package/dist/pending.js +311 -0
- package/dist/pending.js.map +1 -0
- package/dist/redact.d.ts +41 -0
- package/dist/redact.d.ts.map +1 -0
- package/dist/redact.js +168 -0
- package/dist/redact.js.map +1 -0
- package/dist/remote.d.ts +32 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +117 -0
- package/dist/remote.js.map +1 -0
- package/dist/report-extras.d.ts +16 -0
- package/dist/report-extras.d.ts.map +1 -0
- package/dist/report-extras.js +125 -0
- package/dist/report-extras.js.map +1 -0
- package/dist/report-types.d.ts +428 -0
- package/dist/report-types.d.ts.map +1 -0
- package/dist/report-types.js +6 -0
- package/dist/report-types.js.map +1 -0
- package/dist/report.d.ts +15 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +196 -19
- package/dist/report.js.map +1 -1
- package/dist/runner.d.ts +13 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +11 -0
- package/dist/runner.js.map +1 -0
- package/dist/runtime.d.ts +55 -4
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1500 -139
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +29 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +463 -0
- package/dist/schema.js.map +1 -0
- package/dist/spec-api.d.ts +10 -2
- package/dist/spec-api.d.ts.map +1 -1
- package/dist/tags.d.ts +26 -0
- package/dist/tags.d.ts.map +1 -0
- package/dist/tags.js +78 -0
- package/dist/tags.js.map +1 -0
- package/dist/types.d.ts +863 -168
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +18 -5
- package/schemas/lxdev.schema.json +50 -0
- package/schemas/mock-config.schema.json +21 -0
- package/schemas/scenario.schema.json +180 -0
package/dist/mock.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { LxAppDriver, Scenario, ScenarioCall, ScenarioInput } from "@lingxia/types/automation";
|
|
2
|
+
import { type NetworkHost } from "./network.js";
|
|
3
|
+
import type { TestScenario } from "./types.js";
|
|
4
|
+
import type { ScenarioReport } from "./report-types.js";
|
|
5
|
+
/**
|
|
6
|
+
* The scenario one spec installed with `t.scenario.use()`. Installing
|
|
7
|
+
* another replaces it (the host does the same for its run), and the runner
|
|
8
|
+
* removes it when the spec ends.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ScenarioScope {
|
|
11
|
+
current: {
|
|
12
|
+
raw: Scenario;
|
|
13
|
+
label: string;
|
|
14
|
+
} | undefined;
|
|
15
|
+
failure: unknown;
|
|
16
|
+
track(raw: Scenario, label: string): void;
|
|
17
|
+
/** Remove the scenario; one already removed counts as removed. */
|
|
18
|
+
reclaim(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* What a failed spec reports about its scenario: each rule's hits and the
|
|
21
|
+
* Function calls the companion saw. Never throws.
|
|
22
|
+
*/
|
|
23
|
+
report(budgetMs: number): Promise<{
|
|
24
|
+
scenario: ScenarioReport;
|
|
25
|
+
functionCalls: ScenarioCall[];
|
|
26
|
+
} | undefined>;
|
|
27
|
+
}
|
|
28
|
+
/** `name:variant`, `scenario` standing in for a missing name. */
|
|
29
|
+
export declare function scenarioLabel(definition: ScenarioInput, variant: string | undefined): string;
|
|
30
|
+
/**
|
|
31
|
+
* `t.scenario.use(file, variant?)`: the host installs the scenario for the
|
|
32
|
+
* run, on top of the mock selection; the fixture removes it when the spec
|
|
33
|
+
* ends and traces every call.
|
|
34
|
+
*/
|
|
35
|
+
export declare function installScenario(resolve: () => LxAppDriver, host: NetworkHost, scope: ScenarioScope, definition: ScenarioInput, variant?: string): Promise<TestScenario>;
|
|
36
|
+
/**
|
|
37
|
+
* Each spec starts with fresh mock handler state: the app evaluates
|
|
38
|
+
* `mocks/index.ts` again on its next call. Returns why a companion could
|
|
39
|
+
* not do the same for the run's Functions, once per run (`reasons` holds
|
|
40
|
+
* what was already said).
|
|
41
|
+
*/
|
|
42
|
+
export declare function resetMocks(driver: LxAppDriver, reasons: Set<string>): Promise<string | undefined>;
|
|
43
|
+
//# sourceMappingURL=mock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../src/mock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,YAAY,EAEZ,aAAa,EAEd,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAA6D,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3G,OAAO,KAAK,EAAsB,YAAY,EAAsB,MAAM,YAAY,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD;;;;GAIG;AACH,qBAAa,aAAa;IACxB,OAAO,EAAE;QAAE,GAAG,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACtD,OAAO,EAAE,OAAO,CAAC;IAEjB,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAIzC,kEAAkE;IAC5D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAa9B;;;OAGG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,cAAc,CAAC;QAAC,aAAa,EAAE,YAAY,EAAE,CAAA;KAAE,GAAG,SAAS,CAAC;CA6BjH;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI5F;AAsBD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,WAAW,EAC1B,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,aAAa,EACzB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC,CA2CvB;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAWvG"}
|
package/dist/mock.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { truncate } from "./format.js";
|
|
2
|
+
import { errorText, scenarioCall, waitForNextCall } from "./network.js";
|
|
3
|
+
import { runnerSetTimeout } from "./pending.js";
|
|
4
|
+
/**
|
|
5
|
+
* The scenario one spec installed with `t.scenario.use()`. Installing
|
|
6
|
+
* another replaces it (the host does the same for its run), and the runner
|
|
7
|
+
* removes it when the spec ends.
|
|
8
|
+
*/
|
|
9
|
+
export class ScenarioScope {
|
|
10
|
+
track(raw, label) {
|
|
11
|
+
this.current = { raw, label };
|
|
12
|
+
}
|
|
13
|
+
/** Remove the scenario; one already removed counts as removed. */
|
|
14
|
+
async reclaim() {
|
|
15
|
+
if (this.failure !== undefined)
|
|
16
|
+
throw this.failure;
|
|
17
|
+
const current = this.current;
|
|
18
|
+
if (!current)
|
|
19
|
+
return;
|
|
20
|
+
// Raw handle: the fixture is closed by now.
|
|
21
|
+
try {
|
|
22
|
+
await current.raw.unroute();
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
throw new Error(`mock scenario ${current.label} was not removed: ${errorText(error)}`);
|
|
26
|
+
}
|
|
27
|
+
if (this.current === current)
|
|
28
|
+
this.current = undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* What a failed spec reports about its scenario: each rule's hits and the
|
|
32
|
+
* Function calls the companion saw. Never throws.
|
|
33
|
+
*/
|
|
34
|
+
async report(budgetMs) {
|
|
35
|
+
const current = this.current;
|
|
36
|
+
if (!current)
|
|
37
|
+
return undefined;
|
|
38
|
+
let rules = [];
|
|
39
|
+
let calls = [];
|
|
40
|
+
try {
|
|
41
|
+
rules = [...current.raw.rules];
|
|
42
|
+
calls = await Promise.race([
|
|
43
|
+
current.raw.calls(),
|
|
44
|
+
new Promise((resolve) => { runnerSetTimeout(() => resolve([]), budgetMs); }),
|
|
45
|
+
]);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Evidence never replaces the failure.
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
scenario: {
|
|
52
|
+
label: current.label,
|
|
53
|
+
name: current.raw.name,
|
|
54
|
+
variant: current.raw.variant,
|
|
55
|
+
rules: rules.map((rule) => ({
|
|
56
|
+
index: rule.index,
|
|
57
|
+
target: rule.target,
|
|
58
|
+
kind: rule.kind,
|
|
59
|
+
hits: rule.hits ?? calls.filter((call) => call.rule === rule.index).length,
|
|
60
|
+
})),
|
|
61
|
+
},
|
|
62
|
+
functionCalls: calls.filter((call) => call.kind === "function"),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** `name:variant`, `scenario` standing in for a missing name. */
|
|
67
|
+
export function scenarioLabel(definition, variant) {
|
|
68
|
+
const record = definition;
|
|
69
|
+
const name = typeof record.name === "string" ? record.name : "scenario";
|
|
70
|
+
return variant ? `${name}:${variant}` : name;
|
|
71
|
+
}
|
|
72
|
+
function describeScenario(definition, variant) {
|
|
73
|
+
const record = definition;
|
|
74
|
+
const shared = Array.isArray(record.rules) ? record.rules.length : 0;
|
|
75
|
+
const own = variant && Array.isArray(record.variants?.[variant]?.rules) ? record.variants[variant].rules.length : 0;
|
|
76
|
+
const count = shared + own;
|
|
77
|
+
return truncate(`${scenarioLabel(definition, variant)} (${count} rule${count === 1 ? "" : "s"})`, 80);
|
|
78
|
+
}
|
|
79
|
+
function describeTarget(target) {
|
|
80
|
+
if ("http" in target)
|
|
81
|
+
return target.http;
|
|
82
|
+
if ("function" in target)
|
|
83
|
+
return `function ${target.function}`;
|
|
84
|
+
return `rule ${target.rule}`;
|
|
85
|
+
}
|
|
86
|
+
function validTarget(target) {
|
|
87
|
+
if (!target || typeof target !== "object")
|
|
88
|
+
return false;
|
|
89
|
+
const record = target;
|
|
90
|
+
return typeof record.http === "string" || typeof record.function === "string" || typeof record.rule === "number";
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* `t.scenario.use(file, variant?)`: the host installs the scenario for the
|
|
94
|
+
* run, on top of the mock selection; the fixture removes it when the spec
|
|
95
|
+
* ends and traces every call.
|
|
96
|
+
*/
|
|
97
|
+
export function installScenario(resolve, host, scope, definition, variant) {
|
|
98
|
+
return host.act("mock.use", describeScenario(definition, variant), async () => {
|
|
99
|
+
let raw;
|
|
100
|
+
try {
|
|
101
|
+
raw = variant === undefined
|
|
102
|
+
? await resolve().mock.use(definition)
|
|
103
|
+
: await resolve().mock.use(definition, variant);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (error?.code === "E_SCENARIO_STATE_UNKNOWN")
|
|
107
|
+
scope.failure = error;
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
const label = scenarioLabel(definition, variant);
|
|
111
|
+
scope.track(raw, label);
|
|
112
|
+
const read = async (filter) => (await raw.calls(filter)).map(scenarioCall);
|
|
113
|
+
const cursors = new Map();
|
|
114
|
+
const wrapped = {
|
|
115
|
+
get name() { return raw.name; },
|
|
116
|
+
get variant() { return raw.variant; },
|
|
117
|
+
get rules() { return raw.rules; },
|
|
118
|
+
calls: (filter) => host.act("scenario.calls", filter ? JSON.stringify(filter) : label, () => read(filter)),
|
|
119
|
+
waitForCall: (target, options) => {
|
|
120
|
+
if (!validTarget(target)) {
|
|
121
|
+
throw new TypeError("scenario.waitForCall needs { http: 'METHOD url' }, { function: 'name' } or { rule: n }");
|
|
122
|
+
}
|
|
123
|
+
const key = JSON.stringify(target);
|
|
124
|
+
let cursor = cursors.get(key);
|
|
125
|
+
if (!cursor)
|
|
126
|
+
cursors.set(key, cursor = { after: 0, taken: 0 });
|
|
127
|
+
const after = async (seq) => {
|
|
128
|
+
const window = await raw.callsAfter(target, seq);
|
|
129
|
+
return { calls: window.calls.map(scenarioCall), droppedThrough: window.droppedThrough };
|
|
130
|
+
};
|
|
131
|
+
return waitForNextCall(host, "scenario.waitForCall", `scenario ${label}: ${describeTarget(target)}`, after, () => read(), cursor, options);
|
|
132
|
+
},
|
|
133
|
+
remove: () => host.act("scenario.remove", label, async () => {
|
|
134
|
+
await raw.unroute();
|
|
135
|
+
if (scope.current?.raw === raw)
|
|
136
|
+
scope.current = undefined;
|
|
137
|
+
}),
|
|
138
|
+
};
|
|
139
|
+
return wrapped;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Each spec starts with fresh mock handler state: the app evaluates
|
|
144
|
+
* `mocks/index.ts` again on its next call. Returns why a companion could
|
|
145
|
+
* not do the same for the run's Functions, once per run (`reasons` holds
|
|
146
|
+
* what was already said).
|
|
147
|
+
*/
|
|
148
|
+
export async function resetMocks(driver, reasons) {
|
|
149
|
+
const mock = driver.mock;
|
|
150
|
+
// A host that predates mocks has nothing to reset.
|
|
151
|
+
if (!mock || typeof mock.reset !== "function")
|
|
152
|
+
return undefined;
|
|
153
|
+
const result = await mock.reset();
|
|
154
|
+
const reason = result.function && result.function.reset === false
|
|
155
|
+
? result.function.reason ?? "the companion cannot start its handler state over"
|
|
156
|
+
: undefined;
|
|
157
|
+
if (reason === undefined || reasons.has(reason))
|
|
158
|
+
return undefined;
|
|
159
|
+
reasons.add(reason);
|
|
160
|
+
return `mock handler state of the Functions is not reset per spec: ${reason}`;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=mock.js.map
|
package/dist/mock.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock.js","sourceRoot":"","sources":["../src/mock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { NetworkDriver, NetworkRoute, NetworkRouteRequest, ScenarioCall } from "@lingxia/types/automation";
|
|
2
|
+
import type { NetworkCall, TestNetwork, WaitForCallOptions } from "./types.js";
|
|
3
|
+
/** The fixture surface the network, scenario and clock wrappers need. */
|
|
4
|
+
export interface NetworkHost {
|
|
5
|
+
act<T>(name: string, detail: string, op: () => T | Promise<T>): Promise<T>;
|
|
6
|
+
/** Milliseconds left in the spec (or cleanup) budget. */
|
|
7
|
+
budgetRoom(): number;
|
|
8
|
+
/** Stop recording actions while a wait polls; `resumeActions` undoes it. */
|
|
9
|
+
silenceActions(): void;
|
|
10
|
+
resumeActions(): void;
|
|
11
|
+
/** A note for the run's diagnostics, beside the trace. */
|
|
12
|
+
diagnostic(phase: string, message: string): void | Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Routes one spec installed, across every `t.app` / `t.automation.lxapp()`
|
|
16
|
+
* wrapper it created. The runner removes them when the spec ends — whether
|
|
17
|
+
* or not its body settled, apart from the spec's own cleanup — so a route
|
|
18
|
+
* never bleeds into the next spec of the same run.
|
|
19
|
+
*/
|
|
20
|
+
export declare class NetworkScope {
|
|
21
|
+
readonly routes: Map<number, NetworkRoute>;
|
|
22
|
+
track(route: NetworkRoute): void;
|
|
23
|
+
/**
|
|
24
|
+
* Remove every tracked route; a route already gone (expired through
|
|
25
|
+
* `times`, removed by the spec) counts as removed. Rejects naming the
|
|
26
|
+
* routes whose removal failed.
|
|
27
|
+
*/
|
|
28
|
+
reclaim(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export declare function errorText(error: unknown): string;
|
|
31
|
+
/** One request a route handled, as a `NetworkCall`. */
|
|
32
|
+
export declare function routeCall(request: NetworkRouteRequest): NetworkCall;
|
|
33
|
+
/** One call that reached a scenario, as a `NetworkCall`. */
|
|
34
|
+
export declare function scenarioCall(call: ScenarioCall): NetworkCall;
|
|
35
|
+
/** `GET https://h/x → 200 (rule 2)`, for failure messages. */
|
|
36
|
+
export declare function describeCall(call: NetworkCall): string;
|
|
37
|
+
/** Calls after a cursor, and how far the log they come from was trimmed. */
|
|
38
|
+
export interface CallWindowRead {
|
|
39
|
+
calls: NetworkCall[];
|
|
40
|
+
/** The highest `seq` the log dropped (0 when none). */
|
|
41
|
+
droppedThrough: number;
|
|
42
|
+
}
|
|
43
|
+
/** Where one handle's (or scenario target's) `waitForCall` stands. */
|
|
44
|
+
export interface CallCursor {
|
|
45
|
+
/** `seq` of the last call handed out; 0 before the first. */
|
|
46
|
+
after: number;
|
|
47
|
+
/** Calls handed out so far. */
|
|
48
|
+
taken: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Poll `read(cursor.after)` until it lists a call and hand out the oldest.
|
|
52
|
+
* Each handle (and scenario target) keeps its own cursor, so successive
|
|
53
|
+
* waits return successive calls, including ones made before the wait
|
|
54
|
+
* started. The host logs are bounded; when they dropped calls past the
|
|
55
|
+
* cursor, the next call cannot be told and the wait says so instead of
|
|
56
|
+
* handing out a later one.
|
|
57
|
+
*
|
|
58
|
+
* Every read and pause shares one deadline: a read that never returns
|
|
59
|
+
* fails the wait on time, and one that returns after the deadline does not
|
|
60
|
+
* count. The recent calls a timeout lists get a small slice of their own.
|
|
61
|
+
*/
|
|
62
|
+
export declare function waitForNextCall(host: NetworkHost, verb: string, what: string, read: (after: number) => Promise<CallWindowRead>, recent: () => Promise<NetworkCall[]>, cursor: CallCursor, options?: WaitForCallOptions): Promise<NetworkCall>;
|
|
63
|
+
/**
|
|
64
|
+
* `resolve` reads the raw driver lazily, inside each traced call, so an app
|
|
65
|
+
* that closed since `t.app` was read fails that call, not the read.
|
|
66
|
+
*/
|
|
67
|
+
export declare function wrapNetwork(resolve: () => NetworkDriver, host: NetworkHost, scope: NetworkScope): TestNetwork;
|
|
68
|
+
//# sourceMappingURL=network.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EAGZ,mBAAmB,EACnB,YAAY,EACb,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAa,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAI1F,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3E,yDAAyD;IACzD,UAAU,IAAI,MAAM,CAAC;IACrB,4EAA4E;IAC5E,cAAc,IAAI,IAAI,CAAC;IACvB,aAAa,IAAI,IAAI,CAAC;IACtB,0DAA0D;IAC1D,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED;;;;;GAKG;AACH,qBAAa,YAAY;IACvB,QAAQ,CAAC,MAAM,4BAAmC;IAElD,KAAK,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAIhC;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAa/B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEhD;AAYD,uDAAuD;AACvD,wBAAgB,SAAS,CAAC,OAAO,EAAE,mBAAmB,GAAG,WAAW,CAanE;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CA0B5D;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAMtD;AAUD,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,EAChD,MAAM,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,EACpC,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,WAAW,CAAC,CAkEtB;AAyBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,GAAG,WAAW,CAgC7G"}
|
package/dist/network.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { ActionDeadline, TimeoutError } from "./deadline.js";
|
|
2
|
+
import { truncate } from "./format.js";
|
|
3
|
+
import { callerLocation, displayLocation } from "./ids.js";
|
|
4
|
+
import { DEFAULT_ACTION_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS } from "./version.js";
|
|
5
|
+
import { runnerClearTimeout, runnerSetTimeout } from "./pending.js";
|
|
6
|
+
/**
|
|
7
|
+
* Routes one spec installed, across every `t.app` / `t.automation.lxapp()`
|
|
8
|
+
* wrapper it created. The runner removes them when the spec ends — whether
|
|
9
|
+
* or not its body settled, apart from the spec's own cleanup — so a route
|
|
10
|
+
* never bleeds into the next spec of the same run.
|
|
11
|
+
*/
|
|
12
|
+
export class NetworkScope {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.routes = new Map();
|
|
15
|
+
}
|
|
16
|
+
track(route) {
|
|
17
|
+
this.routes.set(route.id, route);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Remove every tracked route; a route already gone (expired through
|
|
21
|
+
* `times`, removed by the spec) counts as removed. Rejects naming the
|
|
22
|
+
* routes whose removal failed.
|
|
23
|
+
*/
|
|
24
|
+
async reclaim() {
|
|
25
|
+
const failed = [];
|
|
26
|
+
for (const [id, route] of [...this.routes]) {
|
|
27
|
+
// Raw handle: the fixture is closed by now.
|
|
28
|
+
try {
|
|
29
|
+
await route.unroute();
|
|
30
|
+
this.routes.delete(id);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
failed.push(`${route.pattern}: ${errorText(error)}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (failed.length > 0)
|
|
37
|
+
throw new Error(`network routes were not removed (${failed.join("; ")})`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function errorText(error) {
|
|
41
|
+
return String(error?.message ?? error);
|
|
42
|
+
}
|
|
43
|
+
/** A request body as the spec reads it: parsed when it is JSON. */
|
|
44
|
+
function parsedBody(body) {
|
|
45
|
+
if (body === null)
|
|
46
|
+
return null;
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(body);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return body;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** One request a route handled, as a `NetworkCall`. */
|
|
55
|
+
export function routeCall(request) {
|
|
56
|
+
return {
|
|
57
|
+
seq: request.seq,
|
|
58
|
+
time: request.timestamp,
|
|
59
|
+
kind: "http",
|
|
60
|
+
method: request.method,
|
|
61
|
+
url: request.url,
|
|
62
|
+
status: request.status,
|
|
63
|
+
body: parsedBody(request.body),
|
|
64
|
+
headers: request.headers,
|
|
65
|
+
// A plain `continue` let the real backend answer.
|
|
66
|
+
answeredBy: request.action === "continue" ? "real" : "route",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** One call that reached a scenario, as a `NetworkCall`. */
|
|
70
|
+
export function scenarioCall(call) {
|
|
71
|
+
const answeredBy = call.rule !== null
|
|
72
|
+
? "rule"
|
|
73
|
+
: call.answeredBy.startsWith("route")
|
|
74
|
+
? "route"
|
|
75
|
+
: call.answeredBy.startsWith("mock") || call.answeredBy === "function mock"
|
|
76
|
+
? "mock"
|
|
77
|
+
: call.answeredBy === "function real"
|
|
78
|
+
? "real"
|
|
79
|
+
: call.kind === "function"
|
|
80
|
+
? "companion"
|
|
81
|
+
: "real";
|
|
82
|
+
const out = { seq: call.seq, time: call.time, kind: call.kind, answeredBy };
|
|
83
|
+
if (call.kind === "function") {
|
|
84
|
+
out.function = call.function;
|
|
85
|
+
if (call.args !== undefined)
|
|
86
|
+
out.body = call.args;
|
|
87
|
+
if (call.outcome !== undefined)
|
|
88
|
+
out.outcome = call.outcome;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
out.method = call.method;
|
|
92
|
+
out.url = call.url;
|
|
93
|
+
out.status = call.status ?? null;
|
|
94
|
+
if (call.body !== undefined)
|
|
95
|
+
out.body = call.body;
|
|
96
|
+
}
|
|
97
|
+
if (call.rule !== null)
|
|
98
|
+
out.rule = call.rule;
|
|
99
|
+
if (call.noMatch)
|
|
100
|
+
out.noMatch = call.noMatch;
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/** `GET https://h/x → 200 (rule 2)`, for failure messages. */
|
|
104
|
+
export function describeCall(call) {
|
|
105
|
+
const by = call.answeredBy === "rule" && call.rule !== undefined ? `rule ${call.rule}` : call.answeredBy;
|
|
106
|
+
const head = call.kind === "function"
|
|
107
|
+
? `function ${call.function ?? "?"} → ${call.outcome ?? "no answer"}`
|
|
108
|
+
: `${call.method ?? "?"} ${call.url ?? "?"} → ${call.status ?? "no answer"}`;
|
|
109
|
+
return `${head} (${by})${call.noMatch ? `; ${call.noMatch}` : ""}`;
|
|
110
|
+
}
|
|
111
|
+
/** How many recent calls a `waitForCall` timeout lists. */
|
|
112
|
+
const LISTED_CALLS = 10;
|
|
113
|
+
/**
|
|
114
|
+
* The most a `waitForCall` timeout spends reading recent calls for its
|
|
115
|
+
* message, past its own timeout and never past the spec's budget.
|
|
116
|
+
*/
|
|
117
|
+
const EVIDENCE_MS = 100;
|
|
118
|
+
/**
|
|
119
|
+
* Poll `read(cursor.after)` until it lists a call and hand out the oldest.
|
|
120
|
+
* Each handle (and scenario target) keeps its own cursor, so successive
|
|
121
|
+
* waits return successive calls, including ones made before the wait
|
|
122
|
+
* started. The host logs are bounded; when they dropped calls past the
|
|
123
|
+
* cursor, the next call cannot be told and the wait says so instead of
|
|
124
|
+
* handing out a later one.
|
|
125
|
+
*
|
|
126
|
+
* Every read and pause shares one deadline: a read that never returns
|
|
127
|
+
* fails the wait on time, and one that returns after the deadline does not
|
|
128
|
+
* count. The recent calls a timeout lists get a small slice of their own.
|
|
129
|
+
*/
|
|
130
|
+
export function waitForNextCall(host, verb, what, read, recent, cursor, options = {}) {
|
|
131
|
+
const location = callerLocation();
|
|
132
|
+
const at = `at ${displayLocation(location.file, location.line, location.column)}`;
|
|
133
|
+
const requested = options.timeout ?? DEFAULT_ACTION_TIMEOUT_MS;
|
|
134
|
+
const interval = options.interval ?? DEFAULT_POLL_INTERVAL_MS;
|
|
135
|
+
if (!Number.isFinite(requested) || requested <= 0 || !Number.isFinite(interval) || interval <= 0) {
|
|
136
|
+
throw new TypeError("waitForCall timeout and interval must be positive finite numbers");
|
|
137
|
+
}
|
|
138
|
+
return host.act(verb, what, async () => {
|
|
139
|
+
const deadline = new ActionDeadline(requested, host.budgetRoom());
|
|
140
|
+
let stalled = false;
|
|
141
|
+
host.silenceActions();
|
|
142
|
+
try {
|
|
143
|
+
for (;;) {
|
|
144
|
+
if (deadline.expired())
|
|
145
|
+
break;
|
|
146
|
+
let window;
|
|
147
|
+
try {
|
|
148
|
+
window = await deadline.call(`${verb} reading calls`, () => read(cursor.after), () => at);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
if (!(error instanceof TimeoutError) || !deadline.expired())
|
|
152
|
+
throw error;
|
|
153
|
+
stalled = true;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
// A read that came back after the deadline does not count.
|
|
157
|
+
if (deadline.expired())
|
|
158
|
+
break;
|
|
159
|
+
if (window.droppedThrough > cursor.after) {
|
|
160
|
+
const lost = window.droppedThrough - cursor.after;
|
|
161
|
+
const skippedFrom = cursor.after;
|
|
162
|
+
const range = lost === 1 ? `seq ${window.droppedThrough}` : `seq ${skippedFrom + 1}–${window.droppedThrough}`;
|
|
163
|
+
cursor.after = window.droppedThrough;
|
|
164
|
+
throw new Error([
|
|
165
|
+
`${what}: calls after ${skippedFrom === 0 ? "the start" : `the last one waitForCall returned (seq ${skippedFrom})`} ` +
|
|
166
|
+
`were dropped from a bounded call log before they were read (${range}, ` +
|
|
167
|
+
`${lost} ${lost === 1 ? "entry" : "entries"} of that log), so the next call cannot be told.`,
|
|
168
|
+
"Wait for calls as they happen, or make fewer calls between waits; the next waitForCall resumes after the gap.",
|
|
169
|
+
at,
|
|
170
|
+
].join("\n"));
|
|
171
|
+
}
|
|
172
|
+
const next = window.calls.find((call) => call.seq > cursor.after);
|
|
173
|
+
if (next) {
|
|
174
|
+
cursor.after = next.seq;
|
|
175
|
+
cursor.taken += 1;
|
|
176
|
+
return next;
|
|
177
|
+
}
|
|
178
|
+
const pause = Math.min(interval, deadline.remaining());
|
|
179
|
+
if (pause <= 0)
|
|
180
|
+
break;
|
|
181
|
+
await new Promise((resolve) => { runnerSetTimeout(resolve, pause); });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
host.resumeActions();
|
|
186
|
+
}
|
|
187
|
+
const elapsed = deadline.elapsed();
|
|
188
|
+
// A read that stalled says the driver is not answering; asking it for
|
|
189
|
+
// recent calls would only spend more time.
|
|
190
|
+
const evidence = stalled
|
|
191
|
+
? "The last read of its calls had not returned when the time ran out, so recent calls were not read."
|
|
192
|
+
: await recentCalls(recent, Math.min(EVIDENCE_MS, Math.floor(host.budgetRoom())));
|
|
193
|
+
const seen = cursor.taken;
|
|
194
|
+
throw new TimeoutError([
|
|
195
|
+
`${what}: no new call within ${elapsed}ms` +
|
|
196
|
+
(seen > 0 ? ` (${seen} earlier ${seen === 1 ? "call was" : "calls were"} already returned by waitForCall).` : "."),
|
|
197
|
+
evidence,
|
|
198
|
+
deadline.clampNote(),
|
|
199
|
+
at,
|
|
200
|
+
].filter(Boolean).join("\n"));
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/** The lines listing recent calls for a timeout, read within `ms`. */
|
|
204
|
+
async function recentCalls(recent, ms) {
|
|
205
|
+
if (ms < 1)
|
|
206
|
+
return "Recent calls were not read: the spec's budget is spent.";
|
|
207
|
+
let handle;
|
|
208
|
+
const late = new Promise((resolve) => { handle = runnerSetTimeout(() => resolve("late"), ms); });
|
|
209
|
+
let listed;
|
|
210
|
+
try {
|
|
211
|
+
const task = Promise.resolve().then(recent);
|
|
212
|
+
task.catch(() => { });
|
|
213
|
+
listed = await Promise.race([task, late]);
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// The listing explains the timeout; it never replaces it.
|
|
217
|
+
return "Recent calls could not be read.";
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
runnerClearTimeout(handle);
|
|
221
|
+
}
|
|
222
|
+
if (listed === "late")
|
|
223
|
+
return `Recent calls were not read: the read took longer than ${ms}ms.`;
|
|
224
|
+
const tail = listed.slice(-LISTED_CALLS);
|
|
225
|
+
return tail.length > 0
|
|
226
|
+
? `Recent calls:\n${tail.map((call) => ` ${describeCall(call)}`).join("\n")}`
|
|
227
|
+
: "No call reached it.";
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* `resolve` reads the raw driver lazily, inside each traced call, so an app
|
|
231
|
+
* that closed since `t.app` was read fails that call, not the read.
|
|
232
|
+
*/
|
|
233
|
+
export function wrapNetwork(resolve, host, scope) {
|
|
234
|
+
const driver = resolve;
|
|
235
|
+
const wrapRoute = (route) => {
|
|
236
|
+
const cursor = { after: 0, taken: 0 };
|
|
237
|
+
const calls = async () => (await route.requests()).map(routeCall);
|
|
238
|
+
const after = async (seq) => {
|
|
239
|
+
const window = await route.requestsAfter(seq);
|
|
240
|
+
return { calls: window.requests.map(routeCall), droppedThrough: window.droppedThrough };
|
|
241
|
+
};
|
|
242
|
+
return {
|
|
243
|
+
get id() { return route.id; },
|
|
244
|
+
get pattern() { return route.pattern; },
|
|
245
|
+
remove: () => host.act("network.remove", route.pattern, async () => { await route.unroute(); }),
|
|
246
|
+
calls: () => host.act("network.calls", route.pattern, calls),
|
|
247
|
+
waitForCall: (options) => waitForNextCall(host, "network.waitForCall", `route ${route.pattern}`, after, calls, cursor, options),
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
return {
|
|
251
|
+
route: (pattern, handler) => host.act("network.route", describeRoute(pattern, handler), async () => {
|
|
252
|
+
const route = await driver().route(pattern, handler);
|
|
253
|
+
scope.track(route);
|
|
254
|
+
return wrapRoute(route);
|
|
255
|
+
}),
|
|
256
|
+
// Spec-scoped: the host log spans the whole run.
|
|
257
|
+
calls: () => host.act("network.calls", "", async () => (await driver().requests())
|
|
258
|
+
.filter((entry) => scope.routes.has(entry.routeId))
|
|
259
|
+
.map(routeCall)),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function describePattern(pattern) {
|
|
263
|
+
if (typeof pattern === "string")
|
|
264
|
+
return pattern;
|
|
265
|
+
if (pattern instanceof RegExp)
|
|
266
|
+
return String(pattern);
|
|
267
|
+
const url = pattern.url instanceof RegExp ? String(pattern.url) : pattern.url;
|
|
268
|
+
return pattern.method ? `${pattern.method.toUpperCase()} ${url}` : url;
|
|
269
|
+
}
|
|
270
|
+
function describeHandler(handler) {
|
|
271
|
+
if (handler.sequence !== undefined)
|
|
272
|
+
return `sequence of ${handler.sequence.length}`;
|
|
273
|
+
if (handler.sse !== undefined)
|
|
274
|
+
return `sse (${handler.sse.length} items)`;
|
|
275
|
+
if (handler.abort !== undefined)
|
|
276
|
+
return `abort ${String(handler.abort)}`;
|
|
277
|
+
if (handler.hang !== undefined)
|
|
278
|
+
return "hang";
|
|
279
|
+
if (handler.continue !== undefined)
|
|
280
|
+
return handler.patchJson !== undefined ? "continue + patchJson" : "continue";
|
|
281
|
+
const status = String(handler.status ?? 200);
|
|
282
|
+
return handler.delay ? `${status} after ${handler.delay}ms` : status;
|
|
283
|
+
}
|
|
284
|
+
function describeRoute(pattern, handler) {
|
|
285
|
+
return truncate(`${describePattern(pattern)} → ${describeHandler(handler)}`, 80);
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=network.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"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"}
|