nativeproof 0.10.1 → 0.10.2
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 +14 -0
- package/dist/app.d.ts +22 -17
- package/dist/config.d.ts +4 -3
- package/dist/config.js +10 -0
- package/dist/evidence.d.ts +10 -0
- package/dist/evidence.js +9 -0
- package/dist/harness.d.ts +8 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ All notable changes to NativeProof are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/) and the project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## 0.10.2
|
|
8
|
+
|
|
9
|
+
Generic mock typing and built-in evidence-on-failure.
|
|
10
|
+
|
|
11
|
+
**Added**
|
|
12
|
+
|
|
13
|
+
- `defineApp` (and `createHarness` / `defineConfig`) are now generic over the mock type. An app
|
|
14
|
+
whose mock extends the base contract gets that concrete type through screens, login/join,
|
|
15
|
+
teardown, `onFailure` and the session context — no casts. Existing single-type-arg uses are
|
|
16
|
+
unchanged (the mock type defaults to `MockBackend`).
|
|
17
|
+
- The synthesised runner config captures evidence on a failed behaviour out of the box — a
|
|
18
|
+
screenshot + redacted page source named after the spec — so apps need no hand-written
|
|
19
|
+
`afterTest`. Best-effort: a capture error never masks the real failure.
|
|
20
|
+
|
|
7
21
|
## 0.10.1
|
|
8
22
|
|
|
9
23
|
Cross-platform locator resolution fixes.
|
package/dist/app.d.ts
CHANGED
|
@@ -10,31 +10,36 @@ import type { MockBackend } from "./mock.js";
|
|
|
10
10
|
* framework core imports nothing from the app; the app describes itself here once.
|
|
11
11
|
* `app.session(role)` turns that into a {@link ScenarioFixture} the `test` facade runs.
|
|
12
12
|
*/
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The device handles every session provides before app screens are layered on.
|
|
15
|
+
* Generic over the mock type `M` (default {@link MockBackend}) so an app with a richer
|
|
16
|
+
* mock — extra socket/presence controls beyond the base contract — gets `mock` typed as
|
|
17
|
+
* that richer type throughout, with no casts. Existing one-arg uses keep the base mock.
|
|
18
|
+
*/
|
|
19
|
+
export interface DeviceContext<M extends MockBackend = MockBackend> {
|
|
15
20
|
driver: Driver;
|
|
16
|
-
mock:
|
|
21
|
+
mock: M;
|
|
17
22
|
}
|
|
18
23
|
/** A screen-object factory: given the device context, build that screen's locators/actions. */
|
|
19
|
-
export type ScreenFactory<S> = (context: DeviceContext) => S;
|
|
20
|
-
export type ScreenFactories = Record<string, ScreenFactory<unknown>>;
|
|
24
|
+
export type ScreenFactory<S, M extends MockBackend = MockBackend> = (context: DeviceContext<M>) => S;
|
|
25
|
+
export type ScreenFactories<M extends MockBackend = MockBackend> = Record<string, ScreenFactory<unknown, M>>;
|
|
21
26
|
/** Context passed to the login/join flows. */
|
|
22
|
-
export type FlowContext = DeviceContext & {
|
|
27
|
+
export type FlowContext<M extends MockBackend = MockBackend> = DeviceContext<M> & {
|
|
23
28
|
role: string;
|
|
24
29
|
};
|
|
25
|
-
export interface AppDefinition<S extends ScreenFactories> {
|
|
30
|
+
export interface AppDefinition<S extends ScreenFactories<M>, M extends MockBackend = MockBackend> {
|
|
26
31
|
/** Acquire the device/driver (e.g. wdioDriver()). */
|
|
27
32
|
driver: () => Driver | Promise<Driver>;
|
|
28
|
-
/** Start the app's mock backend. */
|
|
29
|
-
mock: () =>
|
|
33
|
+
/** Start the app's mock backend (its concrete type `M` flows through the whole session). */
|
|
34
|
+
mock: () => M | Promise<M>;
|
|
30
35
|
/** App-specific secret patterns to keep out of evidence (injected, never baked into the core). */
|
|
31
36
|
secrets?: readonly RegExp[];
|
|
32
37
|
/** App-specific evidence-redaction patterns. */
|
|
33
38
|
redact?: readonly RegExp[];
|
|
34
39
|
/** Reach a logged-in state for the role. */
|
|
35
|
-
login?: (context: FlowContext) => Promise<void>;
|
|
40
|
+
login?: (context: FlowContext<M>) => Promise<void>;
|
|
36
41
|
/** Enter the role's main surface. */
|
|
37
|
-
join?: (context: FlowContext) => Promise<void>;
|
|
42
|
+
join?: (context: FlowContext<M>) => Promise<void>;
|
|
38
43
|
/** Screen-object factories, bound to the device context. */
|
|
39
44
|
screens: S;
|
|
40
45
|
/**
|
|
@@ -43,20 +48,20 @@ export interface AppDefinition<S extends ScreenFactories> {
|
|
|
43
48
|
* so its background sockets are gone before `deleteSession`. The mock is still stopped
|
|
44
49
|
* even if this throws.
|
|
45
50
|
*/
|
|
46
|
-
teardown?: (context: SessionContext<S>) => Promise<void> | void;
|
|
51
|
+
teardown?: (context: SessionContext<S, M>) => Promise<void> | void;
|
|
47
52
|
/**
|
|
48
53
|
* Invoked when a behaviour throws, before the failure propagates — wire on-failure
|
|
49
54
|
* evidence here (e.g. `captureState(...)`) so capture lives in one place, not in every
|
|
50
55
|
* behaviour. Its own errors are swallowed so they never mask the real failure.
|
|
51
56
|
*/
|
|
52
|
-
onFailure?: (context: SessionContext<S>, info: FailureInfo) => Promise<void> | void;
|
|
57
|
+
onFailure?: (context: SessionContext<S, M>, info: FailureInfo) => Promise<void> | void;
|
|
53
58
|
}
|
|
54
59
|
/** The fixture context a session injects: the device handles plus each app screen. */
|
|
55
|
-
export type SessionContext<S extends ScreenFactories> = DeviceContext & {
|
|
60
|
+
export type SessionContext<S extends ScreenFactories<M>, M extends MockBackend = MockBackend> = DeviceContext<M> & {
|
|
56
61
|
[K in keyof S]: ReturnType<S[K]>;
|
|
57
62
|
};
|
|
58
|
-
export interface App<S extends ScreenFactories> {
|
|
63
|
+
export interface App<S extends ScreenFactories<M>, M extends MockBackend = MockBackend> {
|
|
59
64
|
/** A scenario fixture that provisions a logged-in, joined session for `role`. */
|
|
60
|
-
session(role?: string): ScenarioFixture<SessionContext<S>>;
|
|
65
|
+
session(role?: string): ScenarioFixture<SessionContext<S, M>>;
|
|
61
66
|
}
|
|
62
|
-
export declare function defineApp<S extends ScreenFactories>(definition: AppDefinition<S>): App<S>;
|
|
67
|
+
export declare function defineApp<S extends ScreenFactories<M>, M extends MockBackend = MockBackend>(definition: AppDefinition<S, M>): App<S, M>;
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { App, ScreenFactories } from "./app.js";
|
|
2
|
+
import type { MockBackend } from "./mock.js";
|
|
2
3
|
/**
|
|
3
4
|
* The Playwright-style config: one `nativeproof.config.ts` declares the app, the device
|
|
4
5
|
* projects, and where the tests live. The `nativeproof` CLI auto-discovers it and
|
|
@@ -43,12 +44,12 @@ export interface RunnerConfig {
|
|
|
43
44
|
/** Per-test timeout in ms (default 240000). */
|
|
44
45
|
mochaTimeout?: number;
|
|
45
46
|
}
|
|
46
|
-
export interface NativeProofConfig<S extends ScreenFactories = ScreenFactories> extends RunnerConfig {
|
|
47
|
+
export interface NativeProofConfig<S extends ScreenFactories<M> = ScreenFactories, M extends MockBackend = MockBackend> extends RunnerConfig {
|
|
47
48
|
/** The app under test (from `defineApp`). */
|
|
48
|
-
app: App<S>;
|
|
49
|
+
app: App<S, M>;
|
|
49
50
|
}
|
|
50
51
|
/** Identity helper for typed config + editor autocomplete (mirrors Playwright's `defineConfig`). */
|
|
51
|
-
export declare function defineConfig<S extends ScreenFactories>(config: NativeProofConfig<S>): NativeProofConfig<S>;
|
|
52
|
+
export declare function defineConfig<S extends ScreenFactories<M> = ScreenFactories, M extends MockBackend = MockBackend>(config: NativeProofConfig<S, M>): NativeProofConfig<S, M>;
|
|
52
53
|
/** Selection inputs (from the CLI / env) used to resolve the active project + connection. */
|
|
53
54
|
export interface RunnerEnv {
|
|
54
55
|
platform?: string;
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { captureState, failureEvidenceName } from "./evidence.js";
|
|
3
4
|
/** Identity helper for typed config + editor autocomplete (mirrors Playwright's `defineConfig`). */
|
|
4
5
|
export function defineConfig(config) {
|
|
5
6
|
return config;
|
|
@@ -43,6 +44,15 @@ export function buildWdioConfig(config, env = {}, cwd = process.cwd()) {
|
|
|
43
44
|
framework: "mocha",
|
|
44
45
|
reporters: ["spec"],
|
|
45
46
|
mochaOpts: { ui: "bdd", timeout: config.mochaTimeout ?? 240_000 },
|
|
47
|
+
// Evidence on failure, out of the box: on a failed behaviour, snapshot a screenshot +
|
|
48
|
+
// redacted page source into the artifact dir, named after the spec. Best-effort — a
|
|
49
|
+
// capture error never masks the real failure — and consumers get it without writing
|
|
50
|
+
// their own afterTest hook.
|
|
51
|
+
afterTest: async (test, _context, result) => {
|
|
52
|
+
if (result.passed)
|
|
53
|
+
return;
|
|
54
|
+
await captureState(failureEvidenceName(test)).catch(() => { });
|
|
55
|
+
},
|
|
46
56
|
};
|
|
47
57
|
}
|
|
48
58
|
const CONFIG_NAMES = [
|
package/dist/evidence.d.ts
CHANGED
|
@@ -3,3 +3,13 @@ export declare function captureText(filename: string, contents: string): Promise
|
|
|
3
3
|
export declare function captureScreenshot(filename: string): Promise<string>;
|
|
4
4
|
/** Capture a screenshot + redacted source pair under one prefix; returns the source. */
|
|
5
5
|
export declare function captureState(prefix: string): Promise<string>;
|
|
6
|
+
/**
|
|
7
|
+
* A filesystem-safe evidence prefix for a failed behaviour — `failure-<describe>-<test>`
|
|
8
|
+
* with runs of non-word characters collapsed to `_` and capped at 120 chars. Used by the
|
|
9
|
+
* runner's built-in on-failure capture so a failing spec leaves a screenshot + source pair
|
|
10
|
+
* named after it, with no per-spec wiring.
|
|
11
|
+
*/
|
|
12
|
+
export declare function failureEvidenceName(test: {
|
|
13
|
+
parent: string;
|
|
14
|
+
title: string;
|
|
15
|
+
}): string;
|
package/dist/evidence.js
CHANGED
|
@@ -42,3 +42,12 @@ export async function captureState(prefix) {
|
|
|
42
42
|
await captureText(`${prefix}.xml`, source);
|
|
43
43
|
return source;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* A filesystem-safe evidence prefix for a failed behaviour — `failure-<describe>-<test>`
|
|
47
|
+
* with runs of non-word characters collapsed to `_` and capped at 120 chars. Used by the
|
|
48
|
+
* runner's built-in on-failure capture so a failing spec leaves a screenshot + source pair
|
|
49
|
+
* named after it, with no per-spec wiring.
|
|
50
|
+
*/
|
|
51
|
+
export function failureEvidenceName(test) {
|
|
52
|
+
return `failure-${test.parent}-${test.title}`.replace(/[^\w.-]+/g, "_").slice(0, 120);
|
|
53
|
+
}
|
package/dist/harness.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { App, ScreenFactories, SessionContext } from "./app.js";
|
|
2
2
|
import { expect } from "./expect.js";
|
|
3
|
+
import type { MockBackend } from "./mock.js";
|
|
3
4
|
/**
|
|
4
5
|
* `createHarness(app)` — the Playwright `@playwright/test` pattern.
|
|
5
6
|
*
|
|
@@ -18,19 +19,19 @@ import { expect } from "./expect.js";
|
|
|
18
19
|
* });
|
|
19
20
|
* ```
|
|
20
21
|
*/
|
|
21
|
-
export interface HarnessTest<S extends ScreenFactories> {
|
|
22
|
-
(name: string, body: (context: SessionContext<S>) => void | Promise<void>): void;
|
|
22
|
+
export interface HarnessTest<S extends ScreenFactories<M>, M extends MockBackend = MockBackend> {
|
|
23
|
+
(name: string, body: (context: SessionContext<S, M>) => void | Promise<void>): void;
|
|
23
24
|
/** Open a scenario block for the default role. */
|
|
24
25
|
describe(title: string, body: () => void): void;
|
|
25
26
|
/** Open a scenario block for a specific role (e.g. "member" / "guest"). */
|
|
26
27
|
describe(title: string, role: string, body: () => void): void;
|
|
27
28
|
/** Run before each behaviour in the open scenario, with the session context injected. */
|
|
28
|
-
beforeEach(body: (context: SessionContext<S>) => void | Promise<void>): void;
|
|
29
|
+
beforeEach(body: (context: SessionContext<S, M>) => void | Promise<void>): void;
|
|
29
30
|
/** Run after each behaviour in the open scenario, with the session context injected. */
|
|
30
|
-
afterEach(body: (context: SessionContext<S>) => void | Promise<void>): void;
|
|
31
|
+
afterEach(body: (context: SessionContext<S, M>) => void | Promise<void>): void;
|
|
31
32
|
}
|
|
32
|
-
export interface Harness<S extends ScreenFactories> {
|
|
33
|
-
test: HarnessTest<S>;
|
|
33
|
+
export interface Harness<S extends ScreenFactories<M>, M extends MockBackend = MockBackend> {
|
|
34
|
+
test: HarnessTest<S, M>;
|
|
34
35
|
expect: typeof expect;
|
|
35
36
|
}
|
|
36
|
-
export declare function createHarness<S extends ScreenFactories>(app: App<S>): Harness<S>;
|
|
37
|
+
export declare function createHarness<S extends ScreenFactories<M>, M extends MockBackend = MockBackend>(app: App<S, M>): Harness<S, M>;
|
package/package.json
CHANGED