@nwire/test-kit 0.10.0 → 0.11.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/bdd.d.ts +111 -29
- package/dist/bdd.js +114 -46
- package/dist/harness-extensions.d.ts +1 -1
- package/dist/harness-extensions.js +5 -4
- package/dist/harness.d.ts +13 -11
- package/dist/harness.js +14 -11
- package/dist/test-kit.d.ts +1 -1
- package/dist/test-kit.js +1 -1
- package/package.json +9 -9
package/dist/bdd.d.ts
CHANGED
|
@@ -1,43 +1,125 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* BDD
|
|
3
|
-
*
|
|
2
|
+
* BDD support — thin re-exports of `@amiceli/vitest-cucumber` so test-kit
|
|
3
|
+
* users have a single dependency for harness + BDD primitives, plus a
|
|
4
|
+
* `bddHarness({ buildApp })` factory that handles the Background-runs-
|
|
5
|
+
* before-BeforeEachScenario lifecycle quirk so step files don't have to.
|
|
4
6
|
*
|
|
5
|
-
*
|
|
7
|
+
* import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
|
|
8
|
+
* import { bddHarness } from "@nwire/test-kit";
|
|
6
9
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* import submissionsApp from "./submissions.app";
|
|
10
|
+
* describeFeature(feature, ({ Scenario, Background, AfterEachScenario }) => {
|
|
11
|
+
* const ctx = bddHarness({ buildApp });
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
* ctx.background(async () => { ctx.harness = await harness({ app: submissionsApp }); });
|
|
13
|
-
* ctx.afterScenario(async () => ctx.harness?.stop());
|
|
13
|
+
* AfterEachScenario(async () => { await ctx.reset(); });
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Background(({ Given, And }) => {
|
|
16
|
+
* Given("the platform is up", async () => { await ctx.boot(); });
|
|
17
|
+
* And("a seed exists", async () => {
|
|
18
|
+
* await ctx.boot();
|
|
19
|
+
* await ctx.scoped().dispatch(...);
|
|
20
|
+
* });
|
|
17
21
|
* });
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
22
|
+
*
|
|
23
|
+
* Scenario("does the thing", ({ When, Then }) => {
|
|
24
|
+
* When("we dispatch", async () => { await ctx.scoped().dispatch(...); });
|
|
25
|
+
* Then("we see the result", async () => {
|
|
26
|
+
* expect(ctx.harness.telemetry.count(...)).toBeGreaterThan(0);
|
|
27
|
+
* });
|
|
21
28
|
* });
|
|
22
29
|
* });
|
|
23
30
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
31
|
+
* Lifecycle quirk: in @amiceli/vitest-cucumber@4, `BeforeEachScenario`
|
|
32
|
+
* fires in the scenario's `beforeAll`, but **Background** steps live in
|
|
33
|
+
* a separate describe that runs BEFORE the scenario's beforeAll. The
|
|
34
|
+
* lazy-init pattern (call `ctx.boot()` at the head of every step) keeps
|
|
35
|
+
* Background + Scenario steps wired to the SAME fresh harness within a
|
|
36
|
+
* scenario, and `AfterEachScenario(() => ctx.reset())` releases it
|
|
37
|
+
* between scenarios.
|
|
38
|
+
*
|
|
39
|
+
* Step-text reuse: vitest-cucumber binds registrations to feature steps
|
|
40
|
+
* by the registration text. If a feature uses the same step pattern
|
|
41
|
+
* twice in one scope (Background or Scenario), each occurrence still
|
|
42
|
+
* binds to the FIRST matching registration — the second occurrence
|
|
43
|
+
* reports as "missing". Give each occurrence unique wording in the
|
|
44
|
+
* .feature file.
|
|
45
|
+
*
|
|
46
|
+
* `@amiceli/vitest-cucumber` is a peer dependency — install it in the
|
|
47
|
+
* consuming project: `pnpm add -D @amiceli/vitest-cucumber`.
|
|
48
|
+
*/
|
|
49
|
+
import type { App } from "@nwire/app";
|
|
50
|
+
import { type Harness } from "./harness.js";
|
|
51
|
+
import { type ScopedHarness, type UserLike } from "./harness-extensions.js";
|
|
52
|
+
/**
|
|
53
|
+
* Re-export of vitest-cucumber's primary API surface. The package is a
|
|
54
|
+
* peer dep — we resolve it lazily so test-kit consumers that don't run
|
|
55
|
+
* BDD tests aren't forced to install it.
|
|
56
|
+
*/
|
|
57
|
+
export declare function loadBddRunner(): Promise<typeof import("@amiceli/vitest-cucumber")>;
|
|
58
|
+
/**
|
|
59
|
+
* Marker type — `BddContext` was a planning placeholder in 0.10. The
|
|
60
|
+
* real API is `describeFeature` / `loadFeature` from
|
|
61
|
+
* `@amiceli/vitest-cucumber`, used directly in step files. This export
|
|
62
|
+
* is kept for back-compat of the test-kit barrel; consumers should
|
|
63
|
+
* import `describeFeature` / `loadFeature` directly from
|
|
64
|
+
* `@amiceli/vitest-cucumber`.
|
|
26
65
|
*/
|
|
27
|
-
import type { Harness } from "./harness.js";
|
|
28
66
|
export interface BddContext {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
67
|
+
readonly harness?: Harness;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Convenience pass-through — equivalent to importing `loadFeature`
|
|
71
|
+
* directly from `@amiceli/vitest-cucumber` but routed through the
|
|
72
|
+
* peer-dep loader so the error message is friendlier if the package
|
|
73
|
+
* is missing.
|
|
74
|
+
*/
|
|
75
|
+
export declare function feature(path: string): Promise<Awaited<ReturnType<typeof import("@amiceli/vitest-cucumber").loadFeature>>>;
|
|
76
|
+
export interface BddHarnessOptions {
|
|
77
|
+
readonly buildApp: () => App;
|
|
78
|
+
/** Default user pinned via `asUser` when no scenario overrides it. */
|
|
79
|
+
readonly defaultUser?: UserLike;
|
|
80
|
+
/** Tenant prefix used to namespace each scenario's state. Default "t". */
|
|
81
|
+
readonly tenantPrefix?: string;
|
|
82
|
+
}
|
|
83
|
+
export interface BddHarnessCtx {
|
|
84
|
+
/** Current harness, or `null` before the first `boot()` in a scenario. */
|
|
85
|
+
readonly harness: Harness | null;
|
|
86
|
+
/** The pinned user for `scoped()`. Step files may swap it per scenario. */
|
|
87
|
+
user: UserLike;
|
|
88
|
+
/** Tenant string assigned to the current scenario. */
|
|
89
|
+
readonly tenant: string;
|
|
90
|
+
/**
|
|
91
|
+
* Boot the harness if it isn't already. Call at the head of every
|
|
92
|
+
* Background and Scenario step so Background's first invocation wins
|
|
93
|
+
* regardless of vitest-cucumber's hook ordering.
|
|
94
|
+
*/
|
|
95
|
+
boot(): Promise<Harness>;
|
|
96
|
+
/**
|
|
97
|
+
* Scoped dispatcher pinned to `user` + `tenant`. Call after `boot()`.
|
|
98
|
+
* Throws if invoked before any `boot()` ran in the current scenario.
|
|
99
|
+
*/
|
|
100
|
+
scoped(overrides?: Partial<UserLike>): ScopedHarness;
|
|
101
|
+
/**
|
|
102
|
+
* Tear down the current harness. Call from `AfterEachScenario` so the
|
|
103
|
+
* next scenario starts fresh.
|
|
104
|
+
*/
|
|
105
|
+
reset(): Promise<void>;
|
|
37
106
|
}
|
|
38
107
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
108
|
+
* Construct a BDD lifecycle helper for a feature.
|
|
109
|
+
*
|
|
110
|
+
* const ctx = bddHarness({ buildApp });
|
|
111
|
+
*
|
|
112
|
+
* AfterEachScenario(() => ctx.reset());
|
|
113
|
+
*
|
|
114
|
+
* Background(({ Given }) => {
|
|
115
|
+
* Given("the platform is up", () => ctx.boot());
|
|
116
|
+
* });
|
|
117
|
+
*
|
|
118
|
+
* Scenario("does X", ({ When, Then }) => {
|
|
119
|
+
* When("dispatch happens", async () => {
|
|
120
|
+
* await ctx.boot();
|
|
121
|
+
* await ctx.scoped().dispatch(...);
|
|
122
|
+
* });
|
|
123
|
+
* });
|
|
42
124
|
*/
|
|
43
|
-
export declare function
|
|
125
|
+
export declare function bddHarness(options: BddHarnessOptions): BddHarnessCtx;
|
package/dist/bdd.js
CHANGED
|
@@ -1,66 +1,134 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* BDD
|
|
3
|
-
*
|
|
2
|
+
* BDD support — thin re-exports of `@amiceli/vitest-cucumber` so test-kit
|
|
3
|
+
* users have a single dependency for harness + BDD primitives, plus a
|
|
4
|
+
* `bddHarness({ buildApp })` factory that handles the Background-runs-
|
|
5
|
+
* before-BeforeEachScenario lifecycle quirk so step files don't have to.
|
|
4
6
|
*
|
|
5
|
-
*
|
|
7
|
+
* import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
|
|
8
|
+
* import { bddHarness } from "@nwire/test-kit";
|
|
6
9
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* import submissionsApp from "./submissions.app";
|
|
10
|
+
* describeFeature(feature, ({ Scenario, Background, AfterEachScenario }) => {
|
|
11
|
+
* const ctx = bddHarness({ buildApp });
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
* ctx.background(async () => { ctx.harness = await harness({ app: submissionsApp }); });
|
|
13
|
-
* ctx.afterScenario(async () => ctx.harness?.stop());
|
|
13
|
+
* AfterEachScenario(async () => { await ctx.reset(); });
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Background(({ Given, And }) => {
|
|
16
|
+
* Given("the platform is up", async () => { await ctx.boot(); });
|
|
17
|
+
* And("a seed exists", async () => {
|
|
18
|
+
* await ctx.boot();
|
|
19
|
+
* await ctx.scoped().dispatch(...);
|
|
20
|
+
* });
|
|
17
21
|
* });
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
22
|
+
*
|
|
23
|
+
* Scenario("does the thing", ({ When, Then }) => {
|
|
24
|
+
* When("we dispatch", async () => { await ctx.scoped().dispatch(...); });
|
|
25
|
+
* Then("we see the result", async () => {
|
|
26
|
+
* expect(ctx.harness.telemetry.count(...)).toBeGreaterThan(0);
|
|
27
|
+
* });
|
|
21
28
|
* });
|
|
22
29
|
* });
|
|
23
30
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
31
|
+
* Lifecycle quirk: in @amiceli/vitest-cucumber@4, `BeforeEachScenario`
|
|
32
|
+
* fires in the scenario's `beforeAll`, but **Background** steps live in
|
|
33
|
+
* a separate describe that runs BEFORE the scenario's beforeAll. The
|
|
34
|
+
* lazy-init pattern (call `ctx.boot()` at the head of every step) keeps
|
|
35
|
+
* Background + Scenario steps wired to the SAME fresh harness within a
|
|
36
|
+
* scenario, and `AfterEachScenario(() => ctx.reset())` releases it
|
|
37
|
+
* between scenarios.
|
|
38
|
+
*
|
|
39
|
+
* Step-text reuse: vitest-cucumber binds registrations to feature steps
|
|
40
|
+
* by the registration text. If a feature uses the same step pattern
|
|
41
|
+
* twice in one scope (Background or Scenario), each occurrence still
|
|
42
|
+
* binds to the FIRST matching registration — the second occurrence
|
|
43
|
+
* reports as "missing". Give each occurrence unique wording in the
|
|
44
|
+
* .feature file.
|
|
45
|
+
*
|
|
46
|
+
* `@amiceli/vitest-cucumber` is a peer dependency — install it in the
|
|
47
|
+
* consuming project: `pnpm add -D @amiceli/vitest-cucumber`.
|
|
26
48
|
*/
|
|
49
|
+
import { harness } from "./harness.js";
|
|
50
|
+
import { asUser } from "./harness-extensions.js";
|
|
27
51
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
52
|
+
* Re-export of vitest-cucumber's primary API surface. The package is a
|
|
53
|
+
* peer dep — we resolve it lazily so test-kit consumers that don't run
|
|
54
|
+
* BDD tests aren't forced to install it.
|
|
31
55
|
*/
|
|
32
|
-
export async function
|
|
33
|
-
let cucumber;
|
|
56
|
+
export async function loadBddRunner() {
|
|
34
57
|
try {
|
|
35
|
-
|
|
58
|
+
return await import("@amiceli/vitest-cucumber");
|
|
36
59
|
}
|
|
37
60
|
catch {
|
|
38
|
-
throw new Error("@nwire/test-kit
|
|
61
|
+
throw new Error("@nwire/test-kit BDD helpers require '@amiceli/vitest-cucumber' as a peer dependency. " +
|
|
39
62
|
"Install it: pnpm add -D @amiceli/vitest-cucumber");
|
|
40
63
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Convenience pass-through — equivalent to importing `loadFeature`
|
|
67
|
+
* directly from `@amiceli/vitest-cucumber` but routed through the
|
|
68
|
+
* peer-dep loader so the error message is friendlier if the package
|
|
69
|
+
* is missing.
|
|
70
|
+
*/
|
|
71
|
+
export async function feature(path) {
|
|
72
|
+
const cucumber = await loadBddRunner();
|
|
73
|
+
return cucumber.loadFeature(path);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Construct a BDD lifecycle helper for a feature.
|
|
77
|
+
*
|
|
78
|
+
* const ctx = bddHarness({ buildApp });
|
|
79
|
+
*
|
|
80
|
+
* AfterEachScenario(() => ctx.reset());
|
|
81
|
+
*
|
|
82
|
+
* Background(({ Given }) => {
|
|
83
|
+
* Given("the platform is up", () => ctx.boot());
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
* Scenario("does X", ({ When, Then }) => {
|
|
87
|
+
* When("dispatch happens", async () => {
|
|
88
|
+
* await ctx.boot();
|
|
89
|
+
* await ctx.scoped().dispatch(...);
|
|
90
|
+
* });
|
|
91
|
+
* });
|
|
92
|
+
*/
|
|
93
|
+
export function bddHarness(options) {
|
|
94
|
+
let current = null;
|
|
95
|
+
let scenarioCounter = 0;
|
|
96
|
+
let tenant = `${options.tenantPrefix ?? "t"}-0`;
|
|
97
|
+
let user = options.defaultUser ?? { id: "bdd", tenant };
|
|
50
98
|
const ctx = {
|
|
51
|
-
harness
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
99
|
+
get harness() {
|
|
100
|
+
return current;
|
|
101
|
+
},
|
|
102
|
+
get user() {
|
|
103
|
+
return user;
|
|
104
|
+
},
|
|
105
|
+
set user(next) {
|
|
106
|
+
user = next;
|
|
107
|
+
},
|
|
108
|
+
get tenant() {
|
|
109
|
+
return tenant;
|
|
110
|
+
},
|
|
111
|
+
async boot() {
|
|
112
|
+
if (current)
|
|
113
|
+
return current;
|
|
114
|
+
const app = options.buildApp();
|
|
115
|
+
current = await harness({ app });
|
|
116
|
+
tenant = `${options.tenantPrefix ?? "t"}-${++scenarioCounter}`;
|
|
117
|
+
user = options.defaultUser ? { ...options.defaultUser, tenant } : { id: "bdd", tenant };
|
|
118
|
+
return current;
|
|
119
|
+
},
|
|
120
|
+
scoped(overrides) {
|
|
121
|
+
if (!current) {
|
|
122
|
+
throw new Error("bddHarness.scoped: boot the harness first via ctx.boot() before scoped() runs.");
|
|
123
|
+
}
|
|
124
|
+
return asUser(current, { ...user, ...overrides, tenant });
|
|
125
|
+
},
|
|
126
|
+
async reset() {
|
|
127
|
+
if (current) {
|
|
128
|
+
await current.stop();
|
|
129
|
+
current = null;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
58
132
|
};
|
|
59
|
-
|
|
60
|
-
// The complete wiring is left as a follow-up: each step pattern needs to
|
|
61
|
-
// register with the cucumber adapter's per-version API. Today this
|
|
62
|
-
// function validates the user's intent and surfaces a helpful error if
|
|
63
|
-
// the BDD dependency is missing — full step-pattern wiring lands in a
|
|
64
|
-
// follow-up turn when we exercise a real .feature file against the
|
|
65
|
-
// RealWorld migration.
|
|
133
|
+
return ctx;
|
|
66
134
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* They live in a separate file so the in-memory `harness` core stays small
|
|
19
19
|
* and the optional deps (`@nwire/auth`, `@nwire/rbac`, `koa`) stay opt-in.
|
|
20
20
|
*/
|
|
21
|
-
import type
|
|
21
|
+
import { type ActionDefinition } from "@nwire/forge";
|
|
22
22
|
import type { ZodTypeAny } from "@nwire/messages";
|
|
23
23
|
import type { z } from "zod";
|
|
24
24
|
import type { Harness } from "./harness.js";
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* and the optional deps (`@nwire/auth`, `@nwire/rbac`, `koa`) stay opt-in.
|
|
20
20
|
*/
|
|
21
21
|
import supertest from "supertest";
|
|
22
|
+
import { forgeDispatcher } from "@nwire/forge";
|
|
22
23
|
/**
|
|
23
24
|
* Return a thin proxy that pins `envelope.user` (and `envelope.userId`,
|
|
24
25
|
* `envelope.tenant` derived from the user) on every dispatch/query.
|
|
@@ -32,11 +33,11 @@ export function asUser(harness, user) {
|
|
|
32
33
|
// `user` too, which is on MessageEnvelope but not on the public dispatch
|
|
33
34
|
// overload. Going through runtime.dispatch with a hand-seeded envelope
|
|
34
35
|
// keeps the type contract honest without widening the public Harness API.
|
|
35
|
-
const
|
|
36
|
+
const dispatcher = forgeDispatcher(harness.app);
|
|
36
37
|
return {
|
|
37
38
|
async dispatch(action, input, envelope) {
|
|
38
39
|
if (!user)
|
|
39
|
-
return
|
|
40
|
+
return dispatcher.dispatch(action, input);
|
|
40
41
|
const { seedEnvelope } = await import("@nwire/envelope");
|
|
41
42
|
const seeded = seedEnvelope({
|
|
42
43
|
tenant: envelope?.tenant ?? user.tenant,
|
|
@@ -44,10 +45,10 @@ export function asUser(harness, user) {
|
|
|
44
45
|
user,
|
|
45
46
|
correlationId: envelope?.correlationId,
|
|
46
47
|
});
|
|
47
|
-
return
|
|
48
|
+
return dispatcher.dispatch(action, input, seeded);
|
|
48
49
|
},
|
|
49
50
|
async query(queryDef, input, tenant) {
|
|
50
|
-
return
|
|
51
|
+
return dispatcher.query(queryDef.name, input, tenant ?? user?.tenant ?? "");
|
|
51
52
|
},
|
|
52
53
|
};
|
|
53
54
|
}
|
package/dist/harness.d.ts
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `harness(app)` — wrap an already-constructed
|
|
3
|
-
*
|
|
2
|
+
* `harness(app)` — wrap an already-constructed App with a light test handle:
|
|
3
|
+
* dispatch helpers, idle-wait, and a buffered telemetry probe.
|
|
4
4
|
*
|
|
5
|
-
* const app =
|
|
6
|
-
*
|
|
7
|
-
* await
|
|
8
|
-
* await h.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* const app = createApp({ appName: "submissions",
|
|
6
|
+
* plugins: [createForgePlugin({ handlers, actors })] });
|
|
7
|
+
* const h = await harness({ app });
|
|
8
|
+
* await h.dispatch(submitAnswer, { … });
|
|
9
|
+
* await h.idle();
|
|
10
|
+
* expect(h.telemetry.count("event.published")).toBeGreaterThan(0);
|
|
11
|
+
* await h.stop();
|
|
11
12
|
*/
|
|
12
|
-
import type {
|
|
13
|
+
import type { App } from "@nwire/app";
|
|
14
|
+
import type { ActionDefinition } from "@nwire/forge";
|
|
13
15
|
import type { z } from "zod";
|
|
14
16
|
import type { ZodTypeAny } from "@nwire/messages";
|
|
15
17
|
import { TelemetryProbe } from "./telemetry-probe.js";
|
|
16
18
|
export interface HarnessOptions {
|
|
17
|
-
readonly app:
|
|
19
|
+
readonly app: App;
|
|
18
20
|
}
|
|
19
21
|
export interface Harness {
|
|
20
|
-
readonly app:
|
|
22
|
+
readonly app: App;
|
|
21
23
|
readonly telemetry: TelemetryProbe;
|
|
22
24
|
dispatch<TSchema extends ZodTypeAny>(action: ActionDefinition<TSchema>, input: z.input<TSchema>, envelope?: {
|
|
23
25
|
tenant?: string;
|
package/dist/harness.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `harness(app)` — wrap an already-constructed
|
|
3
|
-
*
|
|
2
|
+
* `harness(app)` — wrap an already-constructed App with a light test handle:
|
|
3
|
+
* dispatch helpers, idle-wait, and a buffered telemetry probe.
|
|
4
4
|
*
|
|
5
|
-
* const app =
|
|
6
|
-
*
|
|
7
|
-
* await
|
|
8
|
-
* await h.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* const app = createApp({ appName: "submissions",
|
|
6
|
+
* plugins: [createForgePlugin({ handlers, actors })] });
|
|
7
|
+
* const h = await harness({ app });
|
|
8
|
+
* await h.dispatch(submitAnswer, { … });
|
|
9
|
+
* await h.idle();
|
|
10
|
+
* expect(h.telemetry.count("event.published")).toBeGreaterThan(0);
|
|
11
|
+
* await h.stop();
|
|
11
12
|
*/
|
|
13
|
+
import { forgeDispatcher } from "@nwire/forge";
|
|
12
14
|
import { TelemetryProbe } from "./telemetry-probe.js";
|
|
13
15
|
export async function harness(options) {
|
|
14
16
|
const app = options.app;
|
|
15
17
|
const probe = new TelemetryProbe();
|
|
16
18
|
const unsubscribe = app.runtime.onTelemetry((rec) => probe.record(rec));
|
|
17
19
|
await app.start();
|
|
20
|
+
const dispatcher = forgeDispatcher(app);
|
|
18
21
|
let pending = 0;
|
|
19
22
|
let lastRecordAt = Date.now();
|
|
20
23
|
app.runtime.onTelemetry(() => {
|
|
@@ -33,16 +36,16 @@ export async function harness(options) {
|
|
|
33
36
|
userId: envelope.userId,
|
|
34
37
|
correlationId: envelope.correlationId,
|
|
35
38
|
});
|
|
36
|
-
return await
|
|
39
|
+
return await dispatcher.dispatch(action, input, seeded);
|
|
37
40
|
}
|
|
38
|
-
return await
|
|
41
|
+
return await dispatcher.dispatch(action, input);
|
|
39
42
|
}
|
|
40
43
|
finally {
|
|
41
44
|
pending--;
|
|
42
45
|
}
|
|
43
46
|
},
|
|
44
47
|
async query(queryDef, input, tenant = "") {
|
|
45
|
-
return
|
|
48
|
+
return dispatcher.query(queryDef.name, input, tenant);
|
|
46
49
|
},
|
|
47
50
|
async idle(graceMs = 25, timeoutMs = 5000) {
|
|
48
51
|
const deadline = Date.now() + timeoutMs;
|
package/dist/test-kit.d.ts
CHANGED
|
@@ -20,6 +20,6 @@ export { asUser, simulateRequest, checkPolicy, onLog, type UserLike, type Scoped
|
|
|
20
20
|
export { TelemetryProbe, type TelemetryFilter } from "./telemetry-probe.js";
|
|
21
21
|
export { dockerCompose, PRESETS as DOCKER_COMPOSE_PRESETS, type ComposeStack, type ComposePreset, } from "./docker-compose.js";
|
|
22
22
|
export { isReachable } from "./is-reachable.js";
|
|
23
|
-
export { feature, type BddContext } from "./bdd.js";
|
|
23
|
+
export { feature, loadBddRunner, bddHarness, type BddContext, type BddHarnessOptions, type BddHarnessCtx, } from "./bdd.js";
|
|
24
24
|
export { factory, sequence, type Factory, type SequenceCounter } from "./zod-fixture-factory.js";
|
|
25
25
|
export { createTestApp, bootTestApp, type BootedTestApp } from "./supertest-app-helper.js";
|
package/dist/test-kit.js
CHANGED
|
@@ -20,7 +20,7 @@ export { asUser, simulateRequest, checkPolicy, onLog, } from "./harness-extensio
|
|
|
20
20
|
export { TelemetryProbe } from "./telemetry-probe.js";
|
|
21
21
|
export { dockerCompose, PRESETS as DOCKER_COMPOSE_PRESETS, } from "./docker-compose.js";
|
|
22
22
|
export { isReachable } from "./is-reachable.js";
|
|
23
|
-
export { feature } from "./bdd.js";
|
|
23
|
+
export { feature, loadBddRunner, bddHarness, } from "./bdd.js";
|
|
24
24
|
// Pre-existing helpers
|
|
25
25
|
export { factory, sequence } from "./zod-fixture-factory.js";
|
|
26
26
|
export { createTestApp, bootTestApp } from "./supertest-app-helper.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nwire/test-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Shared test helpers and zod-driven fixture factories",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fixtures",
|
|
@@ -28,20 +28,20 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"supertest": "^7.2.2",
|
|
30
30
|
"zod": "^4.0.0",
|
|
31
|
-
"@nwire/envelope": "0.
|
|
32
|
-
"@nwire/forge": "0.
|
|
33
|
-
"@nwire/logger": "0.
|
|
34
|
-
"@nwire/messages": "0.
|
|
31
|
+
"@nwire/envelope": "0.11.0",
|
|
32
|
+
"@nwire/forge": "0.11.0",
|
|
33
|
+
"@nwire/logger": "0.11.0",
|
|
34
|
+
"@nwire/messages": "0.11.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/koa": "^2.15.0",
|
|
38
38
|
"@types/node": "^22.19.9",
|
|
39
39
|
"@types/supertest": "^6.0.3",
|
|
40
40
|
"typescript": "^5.9.3",
|
|
41
|
-
"@nwire/app": "0.
|
|
42
|
-
"@nwire/endpoint": "0.
|
|
43
|
-
"@nwire/wires": "0.
|
|
44
|
-
"@nwire/koa": "0.
|
|
41
|
+
"@nwire/app": "0.11.0",
|
|
42
|
+
"@nwire/endpoint": "0.11.0",
|
|
43
|
+
"@nwire/wires": "0.11.0",
|
|
44
|
+
"@nwire/koa": "0.11.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@amiceli/vitest-cucumber": "^4.0.0",
|