@solidxai/core 0.1.15 → 0.1.16-beta.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/CHANGELOG.md +1293 -0
- package/dist/commands/run-tests.command.d.ts +3 -1
- package/dist/commands/run-tests.command.d.ts.map +1 -1
- package/dist/commands/run-tests.command.js +6 -2
- package/dist/commands/run-tests.command.js.map +1 -1
- package/dist/controllers/test-run.controller.d.ts +12 -0
- package/dist/controllers/test-run.controller.d.ts.map +1 -0
- package/dist/controllers/test-run.controller.js +54 -0
- package/dist/controllers/test-run.controller.js.map +1 -0
- package/dist/dtos/test-run-request.dto.d.ts +35 -0
- package/dist/dtos/test-run-request.dto.d.ts.map +1 -0
- package/dist/dtos/test-run-request.dto.js +120 -0
- package/dist/dtos/test-run-request.dto.js.map +1 -0
- package/dist/guards/access-token.guard.d.ts +5 -1
- package/dist/guards/access-token.guard.d.ts.map +1 -1
- package/dist/guards/access-token.guard.js +20 -1
- package/dist/guards/access-token.guard.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/active-user-data.interface.d.ts +1 -0
- package/dist/interfaces/active-user-data.interface.d.ts.map +1 -1
- package/dist/interfaces/active-user-data.interface.js.map +1 -1
- package/dist/jobs/database/test-run-queue-options-database.d.ts +8 -0
- package/dist/jobs/database/test-run-queue-options-database.d.ts.map +1 -0
- package/dist/jobs/database/test-run-queue-options-database.js +10 -0
- package/dist/jobs/database/test-run-queue-options-database.js.map +1 -0
- package/dist/jobs/database/test-run-queue-publisher-database.service.d.ts +12 -0
- package/dist/jobs/database/test-run-queue-publisher-database.service.d.ts.map +1 -0
- package/dist/jobs/database/test-run-queue-publisher-database.service.js +39 -0
- package/dist/jobs/database/test-run-queue-publisher-database.service.js.map +1 -0
- package/dist/jobs/database/test-run-queue-subscriber-database.service.d.ts +26 -0
- package/dist/jobs/database/test-run-queue-subscriber-database.service.d.ts.map +1 -0
- package/dist/jobs/database/test-run-queue-subscriber-database.service.js +120 -0
- package/dist/jobs/database/test-run-queue-subscriber-database.service.js.map +1 -0
- package/dist/resources/images/solid-core-menu-icon.png +0 -0
- package/dist/seeders/seed-data/solid-core-metadata.json +4 -2
- package/dist/services/access-token-denylist.service.d.ts +16 -0
- package/dist/services/access-token-denylist.service.d.ts.map +1 -0
- package/dist/services/access-token-denylist.service.js +74 -0
- package/dist/services/access-token-denylist.service.js.map +1 -0
- package/dist/services/authentication.service.d.ts +9 -1
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +39 -4
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/import-transaction.service.d.ts +1 -1
- package/dist/services/import-transaction.service.d.ts.map +1 -1
- package/dist/services/import-transaction.service.js +13 -23
- package/dist/services/import-transaction.service.js.map +1 -1
- package/dist/services/module-metadata.service.js +4 -4
- package/dist/services/module-metadata.service.js.map +1 -1
- package/dist/services/settings/default-settings-provider.service.d.ts +20 -0
- package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
- package/dist/services/settings/default-settings-provider.service.js +11 -0
- package/dist/services/settings/default-settings-provider.service.js.map +1 -1
- package/dist/services/workflow-secret.service.d.ts +3 -0
- package/dist/services/workflow-secret.service.d.ts.map +1 -1
- package/dist/services/workflow-secret.service.js +25 -0
- package/dist/services/workflow-secret.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +9 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/testing/adapters/ui/browser-provisioner.d.ts +5 -0
- package/dist/testing/adapters/ui/browser-provisioner.d.ts.map +1 -0
- package/dist/testing/adapters/ui/browser-provisioner.js +102 -0
- package/dist/testing/adapters/ui/browser-provisioner.js.map +1 -0
- package/dist/testing/adapters/ui/playwright-adapter.d.ts +40 -1
- package/dist/testing/adapters/ui/playwright-adapter.d.ts.map +1 -1
- package/dist/testing/adapters/ui/playwright-adapter.js +185 -3
- package/dist/testing/adapters/ui/playwright-adapter.js.map +1 -1
- package/dist/testing/adapters/ui/ui.types.d.ts +6 -0
- package/dist/testing/adapters/ui/ui.types.d.ts.map +1 -1
- package/dist/testing/adapters/ui/ui.types.js.map +1 -1
- package/dist/testing/contracts/runtime-context.types.d.ts +2 -0
- package/dist/testing/contracts/runtime-context.types.d.ts.map +1 -1
- package/dist/testing/contracts/runtime-context.types.js.map +1 -1
- package/dist/testing/core/interpolation.d.ts +1 -0
- package/dist/testing/core/interpolation.d.ts.map +1 -1
- package/dist/testing/core/interpolation.js +28 -68
- package/dist/testing/core/interpolation.js.map +1 -1
- package/dist/testing/core/path-resolution.spec.d.ts +2 -0
- package/dist/testing/core/path-resolution.spec.d.ts.map +1 -0
- package/dist/testing/core/path-resolution.spec.js +106 -0
- package/dist/testing/core/path-resolution.spec.js.map +1 -0
- package/dist/testing/core/path-segments.d.ts +2 -0
- package/dist/testing/core/path-segments.d.ts.map +1 -0
- package/dist/testing/core/path-segments.js +70 -0
- package/dist/testing/core/path-segments.js.map +1 -0
- package/dist/testing/core/resource-store.d.ts.map +1 -1
- package/dist/testing/core/resource-store.js +5 -2
- package/dist/testing/core/resource-store.js.map +1 -1
- package/dist/testing/core/testing-engine.d.ts.map +1 -1
- package/dist/testing/core/testing-engine.js +16 -0
- package/dist/testing/core/testing-engine.js.map +1 -1
- package/dist/testing/reporter/artifact-sink.d.ts +14 -0
- package/dist/testing/reporter/artifact-sink.d.ts.map +1 -0
- package/dist/testing/reporter/artifact-sink.js +3 -0
- package/dist/testing/reporter/artifact-sink.js.map +1 -0
- package/dist/testing/reporter/console-reporter.d.ts.map +1 -1
- package/dist/testing/reporter/console-reporter.js +7 -0
- package/dist/testing/reporter/console-reporter.js.map +1 -1
- package/dist/testing/reporter/file-service-artifact-sink.d.ts +22 -0
- package/dist/testing/reporter/file-service-artifact-sink.d.ts.map +1 -0
- package/dist/testing/reporter/file-service-artifact-sink.js +31 -0
- package/dist/testing/reporter/file-service-artifact-sink.js.map +1 -0
- package/dist/testing/reporter/lifecycle-events.types.d.ts +67 -0
- package/dist/testing/reporter/lifecycle-events.types.d.ts.map +1 -0
- package/dist/testing/reporter/lifecycle-events.types.js +3 -0
- package/dist/testing/reporter/lifecycle-events.types.js.map +1 -0
- package/dist/testing/reporter/lifecycle-webhook-reporter.d.ts +77 -0
- package/dist/testing/reporter/lifecycle-webhook-reporter.d.ts.map +1 -0
- package/dist/testing/reporter/lifecycle-webhook-reporter.js +275 -0
- package/dist/testing/reporter/lifecycle-webhook-reporter.js.map +1 -0
- package/dist/testing/reporter/progress-reporter.d.ts +3 -3
- package/dist/testing/reporter/progress-reporter.d.ts.map +1 -1
- package/dist/testing/reporter/progress-reporter.js.map +1 -1
- package/dist/testing/reporter/reporter.types.d.ts +13 -1
- package/dist/testing/reporter/reporter.types.d.ts.map +1 -1
- package/dist/testing/reporter/reporter.types.js.map +1 -1
- package/dist/testing/runner/read-scenarios-file.d.ts +9 -0
- package/dist/testing/runner/read-scenarios-file.d.ts.map +1 -0
- package/dist/testing/runner/read-scenarios-file.js +56 -0
- package/dist/testing/runner/read-scenarios-file.js.map +1 -0
- package/dist/testing/runner/run-from-metadata.d.ts +7 -2
- package/dist/testing/runner/run-from-metadata.d.ts.map +1 -1
- package/dist/testing/runner/run-from-metadata.js +57 -6
- package/dist/testing/runner/run-from-metadata.js.map +1 -1
- package/dist/testing/steps/ui/navigation.step.d.ts.map +1 -1
- package/dist/testing/steps/ui/navigation.step.js +12 -12
- package/dist/testing/steps/ui/navigation.step.js.map +1 -1
- package/package.json +4 -2
- package/postman/instant-logout.postman_collection.json +1493 -0
- package/resources/images/solid-core-menu-icon.png +0 -0
- package/src/commands/run-tests.command.ts +3 -0
- package/src/controllers/test-run.controller.ts +44 -0
- package/src/dtos/test-run-request.dto.ts +130 -0
- package/src/guards/access-token.guard.ts +48 -0
- package/src/index.ts +21 -1
- package/src/interfaces/active-user-data.interface.ts +8 -0
- package/src/jobs/database/test-run-queue-options-database.ts +9 -0
- package/src/jobs/database/test-run-queue-publisher-database.service.ts +24 -0
- package/src/jobs/database/test-run-queue-subscriber-database.service.ts +122 -0
- package/src/seeders/seed-data/solid-core-metadata.json +4 -2
- package/src/services/access-token-denylist.service.ts +145 -0
- package/src/services/authentication.service.ts +118 -2
- package/src/services/import-transaction.service.ts +19 -36
- package/src/services/module-metadata.service.ts +4 -4
- package/src/services/settings/default-settings-provider.service.ts +12 -0
- package/src/services/workflow-secret.service.ts +61 -2
- package/src/solid-core.module.ts +11 -0
- package/src/testing/README.md +44 -1
- package/src/testing/adapters/ui/browser-provisioner.ts +115 -0
- package/src/testing/adapters/ui/playwright-adapter.ts +255 -5
- package/src/testing/adapters/ui/ui.types.ts +14 -0
- package/src/testing/contracts/runtime-context.types.ts +6 -0
- package/src/testing/core/interpolation.ts +39 -69
- package/src/testing/core/path-resolution.spec.ts +133 -0
- package/src/testing/core/path-segments.ts +81 -0
- package/src/testing/core/resource-store.ts +8 -2
- package/src/testing/core/testing-engine.ts +22 -0
- package/src/testing/reporter/artifact-sink.ts +16 -0
- package/src/testing/reporter/console-reporter.ts +9 -0
- package/src/testing/reporter/file-service-artifact-sink.ts +46 -0
- package/src/testing/reporter/lifecycle-events.types.ts +103 -0
- package/src/testing/reporter/lifecycle-webhook-reporter.ts +385 -0
- package/src/testing/reporter/progress-reporter.ts +3 -1
- package/src/testing/reporter/reporter.types.ts +10 -1
- package/src/testing/runner/read-scenarios-file.ts +44 -0
- package/src/testing/runner/run-from-metadata.ts +102 -9
- package/src/testing/steps/ui/navigation.step.ts +24 -20
- package/dist/services/access-token-storage.service.d.ts +0 -1
- package/dist/services/access-token-storage.service.d.ts.map +0 -1
- package/dist/services/access-token-storage.service.js +0 -1
- package/dist/services/access-token-storage.service.js.map +0 -1
- package/src/services/access-token-storage.service.ts +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
|
|
3
|
+
import type { ScenarioSpec, TestingDataRecord } from "../contracts/testing-metadata.types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Result of reading a scenario JSON file. `ok:false` means the file was missing,
|
|
7
|
+
* unreadable, or contained invalid JSON — never a thrown error.
|
|
8
|
+
*/
|
|
9
|
+
export interface ReadScenariosResult {
|
|
10
|
+
scenarios: ScenarioSpec[];
|
|
11
|
+
data?: TestingDataRecord[];
|
|
12
|
+
ok: boolean;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Read + parse an application's scenario file `{ testing: { scenarios, data } }` from an
|
|
18
|
+
* absolute path, on the WORKER tier (which shares the filesystem with the caller in the
|
|
19
|
+
* default single-process deployment). Tolerant by design — mirrors
|
|
20
|
+
* ScenarioFileService.resolvePayload in testingHub — so a concurrent editor half-writing
|
|
21
|
+
* the file, a deleted file (TOCTOU between existsSync and readFileSync), or invalid JSON
|
|
22
|
+
* returns `{ ok:false }` instead of throwing. The worker fails the run loudly on `ok:false`.
|
|
23
|
+
*
|
|
24
|
+
* Lives in core (not testingHub) because the worker is a core component and must not import
|
|
25
|
+
* testingHub code.
|
|
26
|
+
*/
|
|
27
|
+
export function readScenariosFile(absPath: string): ReadScenariosResult {
|
|
28
|
+
if (!absPath || !fs.existsSync(absPath)) {
|
|
29
|
+
return { scenarios: [], ok: false, error: "file_not_found" };
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const parsed = JSON.parse(fs.readFileSync(absPath, "utf-8"));
|
|
33
|
+
const rawScenarios =
|
|
34
|
+
parsed?.testing?.scenarios ??
|
|
35
|
+
parsed?.scenarios ??
|
|
36
|
+
(Array.isArray(parsed) ? parsed : []);
|
|
37
|
+
const scenarios: ScenarioSpec[] = Array.isArray(rawScenarios) ? rawScenarios : [];
|
|
38
|
+
const rawData = parsed?.testing?.data ?? parsed?.data;
|
|
39
|
+
const data: TestingDataRecord[] | undefined = Array.isArray(rawData) ? rawData : undefined;
|
|
40
|
+
return { scenarios, data, ok: true };
|
|
41
|
+
} catch (err) {
|
|
42
|
+
return { scenarios: [], ok: false, error: (err as Error)?.message || "read_error" };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Reporter } from "../reporter/reporter.types";
|
|
2
|
-
import type { TestingMetadata, TestingDataRecord } from "../contracts/testing-metadata.types";
|
|
2
|
+
import type { TestingMetadata, TestingDataRecord, ScenarioSpec } from "../contracts/testing-metadata.types";
|
|
3
3
|
import type { ApiAdapterOptions } from "../adapters/api/api.types";
|
|
4
4
|
import type { PlaywrightAdapterOptions } from "../adapters/ui/ui.types";
|
|
5
5
|
import { ApiAdapter } from "../adapters/api/api-adapter";
|
|
@@ -13,7 +13,9 @@ import { StepRegistry } from "../core/step-registry";
|
|
|
13
13
|
import { SpecRegistry } from "../core/spec-registry";
|
|
14
14
|
import { TestingEngine } from "../core/testing-engine";
|
|
15
15
|
import { filterScenarios } from "./scenario-filter";
|
|
16
|
+
import { collectSecretKeys } from "../core/interpolation";
|
|
16
17
|
import { ensureUiStarted, scenarioNeedsUi } from "./lifecycle";
|
|
18
|
+
import { ensureChromiumInstalled } from "../adapters/ui/browser-provisioner";
|
|
17
19
|
import { ConsoleReporter } from "../reporter/console-reporter";
|
|
18
20
|
|
|
19
21
|
|
|
@@ -31,7 +33,11 @@ function buildTestDataIndex(data?: TestingDataRecord[]): Record<string, Record<s
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export type RunnerOptions = {
|
|
34
|
-
metadata
|
|
36
|
+
metadata?: TestingMetadata;
|
|
37
|
+
scenarios?: ScenarioSpec[];
|
|
38
|
+
data?: TestingDataRecord[];
|
|
39
|
+
externalRunId?: string;
|
|
40
|
+
env?: Record<string, string>;
|
|
35
41
|
scenarioIds?: string[];
|
|
36
42
|
includeTags?: string[];
|
|
37
43
|
skipScenarioIds?: string[];
|
|
@@ -41,8 +47,59 @@ export type RunnerOptions = {
|
|
|
41
47
|
defaults?: { timeoutMs?: number; retries?: number };
|
|
42
48
|
options?: { printApiLogs?: boolean };
|
|
43
49
|
specs?: (registry: SpecRegistry) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Resolves `${secret:...}` references against the secret store. Injected by the
|
|
52
|
+
* caller so this runner stays free of database and DI coupling. Required only if a
|
|
53
|
+
* scenario actually references a secret.
|
|
54
|
+
*/
|
|
55
|
+
resolveSecrets?: (keys: string[]) => Promise<Record<string, any>>;
|
|
44
56
|
};
|
|
45
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Resolves every secret referenced by the scenarios about to run, in one call, and
|
|
60
|
+
* returns a per-scenario view of the result.
|
|
61
|
+
*
|
|
62
|
+
* Partitioned deliberately: ctxBase is shared across scenarios, so handing every
|
|
63
|
+
* scenario the whole map would let each read secrets belonging to the others.
|
|
64
|
+
*/
|
|
65
|
+
async function resolveSecretsByScenario(
|
|
66
|
+
scenarios: ScenarioSpec[],
|
|
67
|
+
resolveSecrets?: RunnerOptions["resolveSecrets"],
|
|
68
|
+
): Promise<Map<string, Record<string, any>>> {
|
|
69
|
+
const keysByScenario = new Map<string, string[]>();
|
|
70
|
+
const allKeys = new Set<string>();
|
|
71
|
+
|
|
72
|
+
for (const scenario of scenarios) {
|
|
73
|
+
const keys = collectSecretKeys(scenario);
|
|
74
|
+
if (keys.length === 0) continue;
|
|
75
|
+
keysByScenario.set(scenario.id, keys);
|
|
76
|
+
keys.forEach((key) => allKeys.add(key));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (allKeys.size === 0) return new Map();
|
|
80
|
+
|
|
81
|
+
if (!resolveSecrets) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Scenarios reference secrets (${[...allKeys].join(", ")}) but no secret resolver was provided to the runner.`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Resolved up front so a missing secret fails the run before any scenario executes.
|
|
88
|
+
const resolved = await resolveSecrets([...allKeys]);
|
|
89
|
+
|
|
90
|
+
const byScenario = new Map<string, Record<string, any>>();
|
|
91
|
+
for (const [scenarioId, keys] of keysByScenario) {
|
|
92
|
+
byScenario.set(
|
|
93
|
+
scenarioId,
|
|
94
|
+
keys.reduce((acc, key) => {
|
|
95
|
+
acc[key] = resolved[key];
|
|
96
|
+
return acc;
|
|
97
|
+
}, {} as Record<string, any>),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return byScenario;
|
|
101
|
+
}
|
|
102
|
+
|
|
46
103
|
export async function runFromMetadata(opts: RunnerOptions): Promise<void> {
|
|
47
104
|
const startedAt = Date.now();
|
|
48
105
|
const registry = new StepRegistry();
|
|
@@ -53,37 +110,58 @@ export async function runFromMetadata(opts: RunnerOptions): Promise<void> {
|
|
|
53
110
|
registerTestSteps(registry);
|
|
54
111
|
|
|
55
112
|
const engine = new TestingEngine(registry, opts.defaults);
|
|
56
|
-
const
|
|
113
|
+
const allScenarios = opts.scenarios ?? opts.metadata?.testing?.scenarios ?? [];
|
|
114
|
+
const scenarios = filterScenarios(allScenarios, {
|
|
57
115
|
scenarioIds: opts.scenarioIds,
|
|
58
116
|
includeTags: opts.includeTags,
|
|
59
117
|
skipScenarioIds: opts.skipScenarioIds,
|
|
60
118
|
});
|
|
61
119
|
|
|
62
120
|
const specRegistry = new SpecRegistry();
|
|
63
|
-
const testData = buildTestDataIndex(opts.metadata
|
|
121
|
+
const testData = buildTestDataIndex(opts.data ?? opts.metadata?.testing?.data);
|
|
64
122
|
if (opts.specs) {
|
|
65
123
|
opts.specs(specRegistry);
|
|
66
124
|
}
|
|
67
125
|
|
|
126
|
+
// Fetch the browser before the reporter starts drawing its footer, so the download
|
|
127
|
+
// gets a clean terminal and a missing browser fails before any scenario runs.
|
|
128
|
+
if (scenarios.some(scenarioNeedsUi)) {
|
|
129
|
+
await ensureChromiumInstalled();
|
|
130
|
+
}
|
|
131
|
+
|
|
68
132
|
const resources = new SimpleResourceStore();
|
|
69
|
-
const reporter = opts.reporter ?? new ConsoleReporter();
|
|
70
|
-
reporter.onRunStart?.({ total: scenarios.length });
|
|
133
|
+
const reporter: Reporter = opts.reporter ?? new ConsoleReporter();
|
|
71
134
|
const api = new ApiAdapter(opts.api);
|
|
72
135
|
const { PlaywrightAdapter } = await import("../adapters/ui/playwright-adapter");
|
|
73
136
|
const ui = new PlaywrightAdapter(opts.ui);
|
|
74
|
-
const ctxBase = { resources, reporter, api, ui, specRegistry, testData, options: opts.options };
|
|
137
|
+
const ctxBase = { resources, reporter, api, ui, specRegistry, testData, env: opts.env, options: opts.options };
|
|
75
138
|
const uiStarted = { value: false };
|
|
76
139
|
let passed = 0;
|
|
77
140
|
let failed = 0;
|
|
78
141
|
let runError: unknown;
|
|
79
142
|
|
|
143
|
+
reporter.onRunStart?.({
|
|
144
|
+
total: scenarios.length,
|
|
145
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
146
|
+
scenarioIds: scenarios.map((s) => s.id),
|
|
147
|
+
scenarios: scenarios.map((s) => ({ id: s.id, name: s.name, type: s.type })),
|
|
148
|
+
});
|
|
149
|
+
|
|
80
150
|
try {
|
|
151
|
+
// One query for the whole run, after onRunStart so a failure here still reports
|
|
152
|
+
// run.end. Keys that do not resolve are not fatal — they surface at the step that
|
|
153
|
+
// references them, where the reporter can name the token and locate the failure.
|
|
154
|
+
const secretsByScenario = await resolveSecretsByScenario(scenarios, opts.resolveSecrets);
|
|
155
|
+
|
|
81
156
|
for (const scenario of scenarios) {
|
|
82
157
|
if (scenarioNeedsUi(scenario)) {
|
|
83
158
|
await ensureUiStarted(ctxBase, uiStarted);
|
|
84
159
|
}
|
|
85
160
|
try {
|
|
86
|
-
await engine.runScenario(scenario,
|
|
161
|
+
await engine.runScenario(scenario, {
|
|
162
|
+
...ctxBase,
|
|
163
|
+
secrets: secretsByScenario.get(scenario.id),
|
|
164
|
+
});
|
|
87
165
|
passed += 1;
|
|
88
166
|
} catch (error) {
|
|
89
167
|
failed += 1;
|
|
@@ -91,6 +169,11 @@ export async function runFromMetadata(opts: RunnerOptions): Promise<void> {
|
|
|
91
169
|
throw error;
|
|
92
170
|
}
|
|
93
171
|
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
// Also catches a setup failure before the loop (unreachable database, absent
|
|
174
|
+
// encryption key), which would otherwise leave runError unset and report ok: true.
|
|
175
|
+
runError = error;
|
|
176
|
+
throw error;
|
|
94
177
|
} finally {
|
|
95
178
|
reporter.onRunEnd?.({
|
|
96
179
|
ok: !runError,
|
|
@@ -100,7 +183,17 @@ export async function runFromMetadata(opts: RunnerOptions): Promise<void> {
|
|
|
100
183
|
durationMs: Date.now() - startedAt,
|
|
101
184
|
});
|
|
102
185
|
if (uiStarted.value) {
|
|
103
|
-
|
|
186
|
+
// Only KEEP the whole-run video when the run FAILED — passing runs discard it unread.
|
|
187
|
+
const keepVideo = !!runError;
|
|
188
|
+
await ui.stop({ keepVideo });
|
|
189
|
+
// Video bytes are only finalized after ui.stop(); attach it as a run-level artifact so
|
|
190
|
+
// the reporter uploads it and references it on run.end (queued before flushPending runs).
|
|
191
|
+
if (keepVideo) {
|
|
192
|
+
const video = ui.getRunVideo?.();
|
|
193
|
+
if (video && reporter.attachRunArtifact) {
|
|
194
|
+
reporter.attachRunArtifact({ name: video.name, contentType: video.contentType, data: video.data });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
104
197
|
}
|
|
105
198
|
}
|
|
106
199
|
}
|
|
@@ -3,7 +3,7 @@ import type { OpStep } from "../../contracts/testing-metadata.types";
|
|
|
3
3
|
import { StepRegistry } from "../../core/step-registry";
|
|
4
4
|
|
|
5
5
|
type GotoInput = { url: string; timeoutMs?: number };
|
|
6
|
-
type ExpectUrlInput = { equals?: string; contains?: string };
|
|
6
|
+
type ExpectUrlInput = { equals?: string; contains?: string; timeoutMs?: number };
|
|
7
7
|
|
|
8
8
|
function requirePage(ctx: TestContext, op: string) {
|
|
9
9
|
if (!ctx.ui || !ctx.ui.page) {
|
|
@@ -28,28 +28,32 @@ export function registerNavigationSteps(registry: StepRegistry): void {
|
|
|
28
28
|
registry.register("ui.expectUrl", async (ctx: TestContext, step: OpStep) => {
|
|
29
29
|
const page = requirePage(ctx, "ui.expectUrl");
|
|
30
30
|
const input = (step.with ?? {}) as ExpectUrlInput;
|
|
31
|
-
const current = page.url();
|
|
32
31
|
|
|
33
|
-
if (input.equals
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
return;
|
|
32
|
+
if (input.equals === undefined && input.contains === undefined) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
'Missing "equals" or "contains" in step.with for op "ui.expectUrl"',
|
|
35
|
+
);
|
|
40
36
|
}
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
38
|
+
const expectation =
|
|
39
|
+
input.equals !== undefined
|
|
40
|
+
? `equal "${input.equals}"`
|
|
41
|
+
: `contain "${input.contains}"`;
|
|
50
42
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
// Waits for the URL rather than sampling it, so an assertion placed straight
|
|
44
|
+
// after a click does not race the navigation.
|
|
45
|
+
try {
|
|
46
|
+
await page.waitForURL(
|
|
47
|
+
(url) =>
|
|
48
|
+
input.equals !== undefined
|
|
49
|
+
? url.toString() === input.equals
|
|
50
|
+
: url.toString().includes(String(input.contains)),
|
|
51
|
+
{ timeout: ctx.ui?.resolveNavigationTimeout(input.timeoutMs) },
|
|
52
|
+
);
|
|
53
|
+
} catch {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Expected URL to ${expectation} but got "${page.url()}"`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
54
58
|
});
|
|
55
59
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=access-token-storage.service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"access-token-storage.service.d.ts","sourceRoot":"","sources":["../../src/services/access-token-storage.service.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=access-token-storage.service.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"access-token-storage.service.js","sourceRoot":"","sources":["../../src/services/access-token-storage.service.ts"],"names":[],"mappings":"","sourcesContent":[""]}
|
|
File without changes
|