@opice/harness 0.6.1 → 0.8.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/README.md +4 -0
- package/dist/dsn.d.ts +8 -6
- package/dist/dsn.d.ts.map +1 -1
- package/dist/dsn.js +4 -3
- package/dist/dsn.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/reporter.d.ts +35 -3
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +110 -19
- package/dist/reporter.js.map +1 -1
- package/dist/scenario.d.ts +13 -0
- package/dist/scenario.d.ts.map +1 -1
- package/dist/scenario.js +82 -1
- package/dist/scenario.js.map +1 -1
- package/dist/tier.d.ts +44 -0
- package/dist/tier.d.ts.map +1 -0
- package/dist/tier.js +38 -0
- package/dist/tier.js.map +1 -0
- package/package.json +1 -1
- package/src/dsn.ts +12 -9
- package/src/index.ts +12 -1
- package/src/reporter.ts +149 -21
- package/src/scenario.ts +101 -1
- package/src/tier.ts +67 -0
package/README.md
CHANGED
|
@@ -149,4 +149,8 @@ export const setup: BrowserSetup = async (context) => {
|
|
|
149
149
|
`never`. Outside CI, reporting is opt-in so iterating with bare `bun test` doesn't stream
|
|
150
150
|
half-finished runs onto the shared dashboard. CI-detected runs are tagged `ci`, opted-in
|
|
151
151
|
local runs `local`.
|
|
152
|
+
- `OPICE_REPORT_STRICT` — `1`/`true` to **fail the run** if reporting to the platform fails
|
|
153
|
+
(default best-effort: a reporting failure is logged loudly but never reddens the test). Use
|
|
154
|
+
it to catch a misconfigured token / unreachable endpoint that would otherwise leave CI green
|
|
155
|
+
while nothing reaches the dashboard. `opice test --fail-on-report-error` sets this for you.
|
|
152
156
|
```
|
package/dist/dsn.d.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* An opice DSN packs everything a project needs to report into one string:
|
|
3
3
|
*
|
|
4
|
-
* OPICE_DSN=https://<
|
|
4
|
+
* OPICE_DSN=https://<clientId>:<clientSecret>@<host>/<slug>
|
|
5
5
|
*
|
|
6
|
-
* The
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* The DSN is a Cloudflare Access SERVICE TOKEN: the client id + secret ride in the
|
|
7
|
+
* userinfo (sent as the `CF-Access-Client-Id` / `CF-Access-Client-Secret` headers),
|
|
8
|
+
* the host is the platform endpoint, and the first path segment is the project slug.
|
|
9
|
+
* It's the single value the dashboard hands you to drop into `.env`; the individual
|
|
10
|
+
* `OPICE_*` vars still win when set, so a DSN is purely a convenience fallback.
|
|
10
11
|
*/
|
|
11
12
|
export interface OpiceDsn {
|
|
12
|
-
|
|
13
|
+
clientId: string;
|
|
14
|
+
clientSecret: string;
|
|
13
15
|
endpoint: string;
|
|
14
16
|
project: string;
|
|
15
17
|
}
|
package/dist/dsn.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dsn.d.ts","sourceRoot":"","sources":["../src/dsn.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"dsn.d.ts","sourceRoot":"","sources":["../src/dsn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,QAAQ;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,CAa7E"}
|
package/dist/dsn.js
CHANGED
|
@@ -8,10 +8,11 @@ export function parseOpiceDsn(raw) {
|
|
|
8
8
|
catch {
|
|
9
9
|
return null;
|
|
10
10
|
}
|
|
11
|
-
const
|
|
11
|
+
const clientId = decodeURIComponent(url.username);
|
|
12
|
+
const clientSecret = decodeURIComponent(url.password);
|
|
12
13
|
const project = url.pathname.replace(/^\/+/, '').split('/')[0] ?? '';
|
|
13
|
-
if (!
|
|
14
|
+
if (!clientId || !clientSecret || !project)
|
|
14
15
|
return null;
|
|
15
|
-
return {
|
|
16
|
+
return { clientId, clientSecret, endpoint: `${url.protocol}//${url.host}`, project };
|
|
16
17
|
}
|
|
17
18
|
//# sourceMappingURL=dsn.js.map
|
package/dist/dsn.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dsn.js","sourceRoot":"","sources":["../src/dsn.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dsn.js","sourceRoot":"","sources":["../src/dsn.ts"],"names":[],"mappings":"AAkBA,MAAM,UAAU,aAAa,CAAC,GAA8B;IAC3D,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,IAAI,GAAQ,CAAA;IACZ,IAAI,CAAC;QACJ,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;IACnB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;IACD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACjD,MAAM,YAAY,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACrD,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IACpE,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAY,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IACvD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAA;AACrF,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,10 @@ export { back, currentPath, currentUrl, forward, open, reload } from './navigati
|
|
|
4
4
|
export { getPage, getContext } from './context.js';
|
|
5
5
|
export { browserTest, DEFAULT_WALKTHROUGH_TIMEOUT_MS, invariant, step } from './scenario.js';
|
|
6
6
|
export type { BrowserTestMeta, StepContract } from './scenario.js';
|
|
7
|
+
export { DEFAULT_SCENARIO_TIER, DEFAULT_SELECTED_TIER, isTierSkipped, normalizeTier, parseSelectedTier, resolveSelectedTier, TIER_ORDER, } from './tier.js';
|
|
8
|
+
export type { SelectedTier, Tier } from './tier.js';
|
|
7
9
|
export { getReporter, setReporter, configureFromEnv } from './reporter.js';
|
|
8
|
-
export type { Reporter, ReporterConfig, StepEvent, ScenarioStart, ScenarioFinish } from './reporter.js';
|
|
10
|
+
export type { Reporter, ReporterConfig, StepEvent, ScenarioStart, ScenarioSkip, ScenarioFinish } from './reporter.js';
|
|
9
11
|
export { parseOpiceDsn } from './dsn.js';
|
|
10
12
|
export type { OpiceDsn } from './dsn.js';
|
|
11
13
|
export { command, call, runCommand, makeCtx, loadUserCommands, findUserCommandsFile, z } from './command.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEzD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEtF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,WAAW,EAAE,8BAA8B,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAC5F,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAElE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC1E,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEzD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEtF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,WAAW,EAAE,8BAA8B,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAC5F,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAElE,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,UAAU,GACV,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEnD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC1E,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAErH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAC5G,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC7D,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAI9C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAGzC,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { byLabel, byRole, byText } from './accessible.js';
|
|
|
3
3
|
export { back, currentPath, currentUrl, forward, open, reload } from './navigation.js';
|
|
4
4
|
export { getPage, getContext } from './context.js';
|
|
5
5
|
export { browserTest, DEFAULT_WALKTHROUGH_TIMEOUT_MS, invariant, step } from './scenario.js';
|
|
6
|
+
export { DEFAULT_SCENARIO_TIER, DEFAULT_SELECTED_TIER, isTierSkipped, normalizeTier, parseSelectedTier, resolveSelectedTier, TIER_ORDER, } from './tier.js';
|
|
6
7
|
export { getReporter, setReporter, configureFromEnv } from './reporter.js';
|
|
7
8
|
export { parseOpiceDsn } from './dsn.js';
|
|
8
9
|
export { command, call, runCommand, makeCtx, loadUserCommands, findUserCommandsFile, z } from './command.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEzD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEtF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,WAAW,EAAE,8BAA8B,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAG5F,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAG1E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAG5G,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAG7D,iFAAiF;AACjF,uEAAuE;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEzD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEtF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,WAAW,EAAE,8BAA8B,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAG5F,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,UAAU,GACV,MAAM,WAAW,CAAA;AAGlB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAG1E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAG5G,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAG7D,iFAAiF;AACjF,uEAAuE;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/reporter.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* The CLI handles end-of-run finalization: the reporter writes a
|
|
9
9
|
* handoff file under $TMPDIR with the runId and credentials, the
|
|
10
10
|
* `opice test` wrapper picks it up after `bun test` exits and POSTs
|
|
11
|
-
* /api/v1
|
|
11
|
+
* /api/v1/<slug>/runs/<id>/finish so the dashboard sees the run as completed.
|
|
12
12
|
*
|
|
13
13
|
* When env vars aren't configured, the reporter falls back to a no-op so
|
|
14
14
|
* harness behavior matches the bindx prototype.
|
|
@@ -16,12 +16,22 @@
|
|
|
16
16
|
export interface ReporterConfig {
|
|
17
17
|
endpoint: string;
|
|
18
18
|
projectId: string;
|
|
19
|
-
|
|
19
|
+
/** Service-token credentials (the OPICE_DSN userinfo / OPICE_CLIENT_ID+SECRET). */
|
|
20
|
+
clientId: string;
|
|
21
|
+
clientSecret: string;
|
|
20
22
|
branch?: string;
|
|
21
23
|
commit?: string;
|
|
22
24
|
/** 'ci' for runs from automation, 'local' for opted-in dev runs. */
|
|
23
25
|
source?: 'ci' | 'local';
|
|
26
|
+
/**
|
|
27
|
+
* The tier this run SELECTED (from `OPICE_TIER`) — recorded on the run so the
|
|
28
|
+
* dashboard can explain why scenarios were skipped. Omitted when no tier
|
|
29
|
+
* filter was set (the run ran everything).
|
|
30
|
+
*/
|
|
31
|
+
tier?: string;
|
|
24
32
|
}
|
|
33
|
+
/** Whether strict reporting is active (see {@link strictReporting}). */
|
|
34
|
+
export declare function isStrictReporting(): boolean;
|
|
25
35
|
export interface StepEvent {
|
|
26
36
|
scenarioId: string;
|
|
27
37
|
/**
|
|
@@ -72,6 +82,16 @@ export interface ScenarioStart {
|
|
|
72
82
|
seeds?: string[];
|
|
73
83
|
/** Identities / roles the scenario acts as. */
|
|
74
84
|
roles?: string[];
|
|
85
|
+
/** Declared tier (critical | standard | extended) — when it runs. */
|
|
86
|
+
tier?: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A scenario the tier filter excluded from this run — registered for the record
|
|
90
|
+
* but never executed. Reported `skipped`, carrying a `reason` (which tier it
|
|
91
|
+
* declared vs the selected one) so the dashboard can explain the absence.
|
|
92
|
+
*/
|
|
93
|
+
export interface ScenarioSkip extends ScenarioStart {
|
|
94
|
+
reason?: string;
|
|
75
95
|
}
|
|
76
96
|
export interface ScenarioFinish {
|
|
77
97
|
scenarioId: string;
|
|
@@ -85,14 +105,26 @@ export interface ScenarioFinish {
|
|
|
85
105
|
}
|
|
86
106
|
export interface Reporter {
|
|
87
107
|
startScenario(input: ScenarioStart): Promise<string>;
|
|
108
|
+
/** Record a scenario the tier filter skipped (created already-finished as `skipped`). */
|
|
109
|
+
skipScenario(input: ScenarioSkip): Promise<void>;
|
|
88
110
|
recordStep(event: StepEvent): Promise<void>;
|
|
89
111
|
finishScenario(input: ScenarioFinish): Promise<void>;
|
|
90
112
|
flush(): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* True if any report to the platform failed (network error or non-2xx). Used
|
|
115
|
+
* by the harness to fail the run under strict reporting — see
|
|
116
|
+
* {@link isStrictReporting}. Always false for the no-op reporter.
|
|
117
|
+
*/
|
|
118
|
+
hadFailures(): boolean;
|
|
91
119
|
}
|
|
92
120
|
export declare const HANDOFF_DIR: string;
|
|
93
121
|
export interface RunHandoff {
|
|
94
122
|
endpoint: string;
|
|
95
|
-
|
|
123
|
+
/** Project slug — the CLI builds /api/v1/<project>/runs/<id>/finish from it. */
|
|
124
|
+
project: string;
|
|
125
|
+
/** Service-token credentials so the CLI can POST /finish with the CF-Access-Client-* headers. */
|
|
126
|
+
clientId: string;
|
|
127
|
+
clientSecret: string;
|
|
96
128
|
runId: string;
|
|
97
129
|
}
|
|
98
130
|
export declare function getReporter(): Reporter;
|
package/dist/reporter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"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"}
|
package/dist/reporter.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* The CLI handles end-of-run finalization: the reporter writes a
|
|
9
9
|
* handoff file under $TMPDIR with the runId and credentials, the
|
|
10
10
|
* `opice test` wrapper picks it up after `bun test` exits and POSTs
|
|
11
|
-
* /api/v1
|
|
11
|
+
* /api/v1/<slug>/runs/<id>/finish so the dashboard sees the run as completed.
|
|
12
12
|
*
|
|
13
13
|
* When env vars aren't configured, the reporter falls back to a no-op so
|
|
14
14
|
* harness behavior matches the bindx prototype.
|
|
@@ -18,17 +18,38 @@ import { mkdirSync, writeFileSync } from 'node:fs';
|
|
|
18
18
|
import { tmpdir } from 'node:os';
|
|
19
19
|
import path from 'node:path';
|
|
20
20
|
import { parseOpiceDsn } from './dsn.js';
|
|
21
|
+
import { resolveSelectedTier } from './tier.js';
|
|
21
22
|
/** Per-request cap, so a hung connection can't stall a scenario's afterAll. */
|
|
22
23
|
const REQUEST_TIMEOUT_MS = 10_000;
|
|
23
24
|
/** Total cap on `flush()` waiting for pending step uploads (afterAll-bounded). */
|
|
24
25
|
const FLUSH_BUDGET_MS = 15_000;
|
|
26
|
+
/**
|
|
27
|
+
* Strict reporting policy, resolved once from the env in {@link configureFromEnv}.
|
|
28
|
+
*
|
|
29
|
+
* Reporting is best-effort by design — a flaky uplink or a dashboard outage must
|
|
30
|
+
* never redden an otherwise-green test run. But that decoupling hides a real
|
|
31
|
+
* failure mode: a misconfigured token or an unreachable endpoint means the run
|
|
32
|
+
* is silently NOT recorded, while CI stays green. Strict mode (opt in via
|
|
33
|
+
* `OPICE_REPORT_STRICT` / `opice test --fail-on-report-error`) makes that loud —
|
|
34
|
+
* any reporting failure fails the run (the harness throws from a scenario's
|
|
35
|
+
* `afterAll`; the CLI escalates a failed `POST /finish` to a non-zero exit).
|
|
36
|
+
*/
|
|
37
|
+
let strictReporting = false;
|
|
38
|
+
/** Whether strict reporting is active (see {@link strictReporting}). */
|
|
39
|
+
export function isStrictReporting() {
|
|
40
|
+
return strictReporting;
|
|
41
|
+
}
|
|
25
42
|
class NoopReporter {
|
|
26
43
|
async startScenario(input) {
|
|
27
44
|
return `noop-${input.name}-${Date.now()}`;
|
|
28
45
|
}
|
|
46
|
+
async skipScenario(_input) { }
|
|
29
47
|
async recordStep(_event) { }
|
|
30
48
|
async finishScenario(_input) { }
|
|
31
49
|
async flush() { }
|
|
50
|
+
hadFailures() {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
32
53
|
}
|
|
33
54
|
export const HANDOFF_DIR = path.join(tmpdir(), 'opice-handoffs');
|
|
34
55
|
function handoffPath(pid = process.pid) {
|
|
@@ -39,9 +60,14 @@ class HttpReporter {
|
|
|
39
60
|
runIdPromise = null;
|
|
40
61
|
pending = new Set();
|
|
41
62
|
warnedUnreachable = false;
|
|
63
|
+
/** Count of failed reports (network error or non-2xx). Drives strict mode. */
|
|
64
|
+
failures = 0;
|
|
42
65
|
constructor(config) {
|
|
43
66
|
this.config = config;
|
|
44
67
|
}
|
|
68
|
+
hadFailures() {
|
|
69
|
+
return this.failures > 0;
|
|
70
|
+
}
|
|
45
71
|
async ensureRun() {
|
|
46
72
|
if (!this.runIdPromise) {
|
|
47
73
|
this.runIdPromise = this.startRun();
|
|
@@ -49,17 +75,24 @@ class HttpReporter {
|
|
|
49
75
|
return this.runIdPromise;
|
|
50
76
|
}
|
|
51
77
|
async startRun() {
|
|
52
|
-
const response = await this.fetch('POST',
|
|
78
|
+
const response = await this.fetch('POST', `/api/v1/${this.config.projectId}/runs`, {
|
|
53
79
|
branch: this.config.branch,
|
|
54
80
|
commit: this.config.commit,
|
|
55
81
|
source: this.config.source,
|
|
82
|
+
tier: this.config.tier,
|
|
56
83
|
});
|
|
57
84
|
const runId = response['runId'];
|
|
58
85
|
// Synchronous write so the CLI can pick this up even if the test
|
|
59
86
|
// process exits abruptly (process.on('exit') runs sync).
|
|
60
87
|
try {
|
|
61
88
|
mkdirSync(HANDOFF_DIR, { recursive: true });
|
|
62
|
-
const handoff = {
|
|
89
|
+
const handoff = {
|
|
90
|
+
endpoint: this.config.endpoint,
|
|
91
|
+
project: this.config.projectId,
|
|
92
|
+
clientId: this.config.clientId,
|
|
93
|
+
clientSecret: this.config.clientSecret,
|
|
94
|
+
runId,
|
|
95
|
+
};
|
|
63
96
|
writeFileSync(handoffPath(), JSON.stringify(handoff), 'utf-8');
|
|
64
97
|
}
|
|
65
98
|
catch {
|
|
@@ -69,16 +102,33 @@ class HttpReporter {
|
|
|
69
102
|
}
|
|
70
103
|
async startScenario(input) {
|
|
71
104
|
const runId = await this.ensureRun();
|
|
72
|
-
const response = await this.fetch('POST', `/api/v1/runs/${runId}/scenarios`, {
|
|
105
|
+
const response = await this.fetch('POST', `/api/v1/${this.config.projectId}/runs/${runId}/scenarios`, {
|
|
73
106
|
name: input.name,
|
|
74
107
|
hash: input.hash,
|
|
75
108
|
testFile: input.testFile,
|
|
76
109
|
feature: input.feature,
|
|
77
110
|
seeds: input.seeds,
|
|
78
111
|
roles: input.roles,
|
|
112
|
+
tier: input.tier,
|
|
79
113
|
});
|
|
80
114
|
return response['scenarioId'];
|
|
81
115
|
}
|
|
116
|
+
async skipScenario(input) {
|
|
117
|
+
const runId = await this.ensureRun();
|
|
118
|
+
// A skipped scenario is created already-finished on the platform — no
|
|
119
|
+
// steps follow, so we don't keep the returned id.
|
|
120
|
+
await this.fetch('POST', `/api/v1/${this.config.projectId}/runs/${runId}/scenarios`, {
|
|
121
|
+
name: input.name,
|
|
122
|
+
hash: input.hash,
|
|
123
|
+
testFile: input.testFile,
|
|
124
|
+
feature: input.feature,
|
|
125
|
+
seeds: input.seeds,
|
|
126
|
+
roles: input.roles,
|
|
127
|
+
tier: input.tier,
|
|
128
|
+
skipped: true,
|
|
129
|
+
reason: input.reason,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
82
132
|
recordStep(event) {
|
|
83
133
|
// Track synchronously so flush() awaits the entire pipeline (including
|
|
84
134
|
// encodeScreenshot's fs.readFile and the upload), not just whatever
|
|
@@ -92,7 +142,7 @@ class HttpReporter {
|
|
|
92
142
|
const screenshot = event.screenshotPath
|
|
93
143
|
? await this.encodeScreenshot(event.screenshotPath)
|
|
94
144
|
: undefined;
|
|
95
|
-
await this.fetch('POST', `/api/v1/runs/${runId}/scenarios/${event.scenarioId}/steps`, {
|
|
145
|
+
await this.fetch('POST', `/api/v1/${this.config.projectId}/runs/${runId}/scenarios/${event.scenarioId}/steps`, {
|
|
96
146
|
attempt: event.attempt,
|
|
97
147
|
sequence: event.sequence,
|
|
98
148
|
kind: event.kind,
|
|
@@ -110,7 +160,7 @@ class HttpReporter {
|
|
|
110
160
|
const runId = await this.ensureRun();
|
|
111
161
|
// Awaited inline so the scenario status is committed before the
|
|
112
162
|
// bun:test afterAll returns.
|
|
113
|
-
await this.fetch('PATCH', `/api/v1/runs/${runId}/scenarios/${input.scenarioId}`, {
|
|
163
|
+
await this.fetch('PATCH', `/api/v1/${this.config.projectId}/runs/${runId}/scenarios/${input.scenarioId}`, {
|
|
114
164
|
status: input.status,
|
|
115
165
|
durationMs: input.durationMs,
|
|
116
166
|
attempts: input.attempts,
|
|
@@ -146,7 +196,9 @@ class HttpReporter {
|
|
|
146
196
|
response = await fetch(this.config.endpoint + path, {
|
|
147
197
|
method,
|
|
148
198
|
headers: {
|
|
149
|
-
|
|
199
|
+
// Cloudflare Access service-token pair — validated at the edge, never the origin.
|
|
200
|
+
'cf-access-client-id': this.config.clientId,
|
|
201
|
+
'cf-access-client-secret': this.config.clientSecret,
|
|
150
202
|
'content-type': 'application/json',
|
|
151
203
|
},
|
|
152
204
|
body: body == null ? undefined : JSON.stringify(body),
|
|
@@ -159,25 +211,33 @@ class HttpReporter {
|
|
|
159
211
|
// DOM and routes fetch through a same-origin policy). Callers swallow
|
|
160
212
|
// reporter errors so the test still runs, so this is the one place the
|
|
161
213
|
// failure is visible — make it loud and actionable.
|
|
162
|
-
this.
|
|
214
|
+
this.noteFailure(`${method} ${path}`, err instanceof Error ? err.message : String(err));
|
|
163
215
|
throw err;
|
|
164
216
|
}
|
|
165
217
|
if (!response.ok) {
|
|
166
218
|
const detail = `${response.status} ${await response.text()}`.trim();
|
|
167
|
-
this.
|
|
219
|
+
this.noteFailure(`${method} ${path}`, detail);
|
|
168
220
|
throw new Error(`opice reporter ${method} ${path} failed: ${detail}`);
|
|
169
221
|
}
|
|
170
222
|
return (await response.json());
|
|
171
223
|
}
|
|
172
224
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
225
|
+
* Record a reporting failure and surface it. Callers swallow reporter errors
|
|
226
|
+
* so the test still runs (reporting is best-effort), which makes this the one
|
|
227
|
+
* place a failure is visible — so every failure is logged to stderr (a
|
|
228
|
+
* configured reporter that can't reach the platform means the run is silently
|
|
229
|
+
* NOT recorded, the most confusing failure mode in onboarding: the test
|
|
230
|
+
* passes but nothing shows on the dashboard). The first failure prints the
|
|
231
|
+
* full hint with the usual culprits; the rest a concise one-liner so a
|
|
232
|
+
* recurring failure is visible without flooding the log. Counts toward
|
|
233
|
+
* {@link hadFailures}, which strict mode fails the run on.
|
|
177
234
|
*/
|
|
178
|
-
|
|
179
|
-
|
|
235
|
+
noteFailure(call, detail) {
|
|
236
|
+
this.failures++;
|
|
237
|
+
if (this.warnedUnreachable) {
|
|
238
|
+
console.error(`[opice] reporter error (${call}): ${detail} — this report was NOT recorded.`);
|
|
180
239
|
return;
|
|
240
|
+
}
|
|
181
241
|
this.warnedUnreachable = true;
|
|
182
242
|
console.error(`[opice] reporter could not reach the platform (${call}: ${detail}). `
|
|
183
243
|
+ `This run will NOT be recorded on the dashboard.\n`
|
|
@@ -185,7 +245,8 @@ class HttpReporter {
|
|
|
185
245
|
+ `[opice] - the test runner's global setup installs a DOM (happy-dom/jsdom) or mocks\n`
|
|
186
246
|
+ `[opice] fetch, so the cross-origin POST is blocked (look for "Cross-Origin Request\n`
|
|
187
247
|
+ `[opice] Blocked" / an OPTIONS … 401). Scope that setup so it skips the e2e dir.\n`
|
|
188
|
-
+ `[opice] - a missing / expired OPICE_DSN api key (401), or an unreachable endpoint
|
|
248
|
+
+ `[opice] - a missing / expired OPICE_DSN api key (401), or an unreachable endpoint.\n`
|
|
249
|
+
+ `[opice] (set OPICE_REPORT_STRICT=1 / opice test --fail-on-report-error to fail the run on this.)`);
|
|
189
250
|
}
|
|
190
251
|
}
|
|
191
252
|
let active = new NoopReporter();
|
|
@@ -195,13 +256,37 @@ export function getReporter() {
|
|
|
195
256
|
export function setReporter(reporter) {
|
|
196
257
|
active = reporter;
|
|
197
258
|
}
|
|
259
|
+
function isTruthy(value) {
|
|
260
|
+
if (!value)
|
|
261
|
+
return false;
|
|
262
|
+
const v = value.toLowerCase();
|
|
263
|
+
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Strict reporting is requested but the reporter is a no-op — it can never fail,
|
|
267
|
+
* so strict mode has nothing to enforce. Warn rather than silently ignoring it:
|
|
268
|
+
* the user asked for "fail if reporting fails" and is instead getting no
|
|
269
|
+
* reporting at all, which strict can't catch.
|
|
270
|
+
*/
|
|
271
|
+
function warnStrictNoop(why) {
|
|
272
|
+
console.error(`[opice] OPICE_REPORT_STRICT is set but ${why} — strict reporting has no effect `
|
|
273
|
+
+ `(there is nothing to report, so nothing can fail).`);
|
|
274
|
+
}
|
|
198
275
|
export function configureFromEnv(env = process.env) {
|
|
276
|
+
// Strict reporting: fail the run if any report to the platform fails. Opt-in
|
|
277
|
+
// (default best-effort is locked design), resolved once here for the whole
|
|
278
|
+
// process. The CLI's `--fail-on-report-error` sets OPICE_REPORT_STRICT in the
|
|
279
|
+
// child env, so a bare `bun test` honours it too.
|
|
280
|
+
strictReporting = isTruthy(env['OPICE_REPORT_STRICT']);
|
|
199
281
|
// Individual vars win; OPICE_DSN fills any gaps (see dsn.ts).
|
|
200
282
|
const dsn = parseOpiceDsn(env['OPICE_DSN']);
|
|
201
283
|
const endpoint = env['OPICE_ENDPOINT'] ?? dsn?.endpoint;
|
|
202
284
|
const projectId = env['OPICE_PROJECT'] ?? dsn?.project;
|
|
203
|
-
const
|
|
204
|
-
|
|
285
|
+
const clientId = env['OPICE_CLIENT_ID'] ?? dsn?.clientId;
|
|
286
|
+
const clientSecret = env['OPICE_CLIENT_SECRET'] ?? dsn?.clientSecret;
|
|
287
|
+
if (!endpoint || !projectId || !clientId || !clientSecret) {
|
|
288
|
+
if (strictReporting)
|
|
289
|
+
warnStrictNoop('reporter credentials are not configured (no OPICE_DSN / OPICE_* vars)');
|
|
205
290
|
return new NoopReporter();
|
|
206
291
|
}
|
|
207
292
|
// Reporting is opt-in outside CI. A local `bun test` while authoring would
|
|
@@ -213,15 +298,21 @@ export function configureFromEnv(env = process.env) {
|
|
|
213
298
|
const mode = (env['OPICE_REPORT'] ?? 'auto').toLowerCase();
|
|
214
299
|
const shouldReport = mode === 'never' ? false : mode === 'always' ? true : isCI;
|
|
215
300
|
if (!shouldReport) {
|
|
301
|
+
if (strictReporting)
|
|
302
|
+
warnStrictNoop(`reporting is disabled here (OPICE_REPORT=${mode}, CI=${isCI})`);
|
|
216
303
|
return new NoopReporter();
|
|
217
304
|
}
|
|
218
305
|
const reporter = new HttpReporter({
|
|
219
306
|
endpoint,
|
|
220
307
|
projectId,
|
|
221
|
-
|
|
308
|
+
clientId,
|
|
309
|
+
clientSecret,
|
|
222
310
|
branch: env['OPICE_BRANCH'] ?? env['GITHUB_REF_NAME'],
|
|
223
311
|
commit: env['OPICE_COMMIT'] ?? env['GITHUB_SHA'],
|
|
224
312
|
source: isCI ? 'ci' : 'local',
|
|
313
|
+
// Record the selected tier only when one was explicitly requested — a run
|
|
314
|
+
// with no OPICE_TIER ran everything and carries no tier filter.
|
|
315
|
+
tier: env['OPICE_TIER'] ? resolveSelectedTier(env) : undefined,
|
|
225
316
|
});
|
|
226
317
|
setReporter(reporter);
|
|
227
318
|
return reporter;
|
package/dist/reporter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"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"}
|
package/dist/scenario.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Tier } from './tier.js';
|
|
1
2
|
/**
|
|
2
3
|
* Scenario metadata — the **first** argument to `browserTest`.
|
|
3
4
|
*
|
|
@@ -23,6 +24,18 @@ export interface BrowserTestMeta {
|
|
|
23
24
|
hash?: string;
|
|
24
25
|
/** Feature / requirement id this scenario covers (e.g. `'F-SML-03a'`). */
|
|
25
26
|
feature?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Test tier — *when* this scenario runs (critical < standard < extended).
|
|
29
|
+
* A run selects a tier via `OPICE_TIER` / `opice test --tier`; selection is a
|
|
30
|
+
* threshold (running `standard` runs critical + standard). A scenario above
|
|
31
|
+
* the selected tier is **skipped**: reported as `skipped` (so it still shows
|
|
32
|
+
* on the dashboard) but never opens a browser. Defaults to `standard`.
|
|
33
|
+
*
|
|
34
|
+
* critical — the must-pass core, every push
|
|
35
|
+
* standard — the normal suite (default), PRs / merges
|
|
36
|
+
* extended — slow / edge / expensive, nightly or on demand
|
|
37
|
+
*/
|
|
38
|
+
tier?: Tier;
|
|
26
39
|
/**
|
|
27
40
|
* Seeds that must be loaded for this scenario to run — machine-checkable
|
|
28
41
|
* preconditions, not prose. e.g. `['initial-data', 'crm-master-data']`.
|
package/dist/scenario.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenario.d.ts","sourceRoot":"","sources":["../src/scenario.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scenario.d.ts","sourceRoot":"","sources":["../src/scenario.ts"],"names":[],"mappings":"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"}
|