@routecraft/testing 0.3.0-canary.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/README.md +71 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @routecraft/testing
|
|
2
|
+
|
|
3
|
+
Test utilities for RouteCraft routes. Use with [Vitest](https://vitest.dev) to run route lifecycles and assert on logs and errors.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @routecraft/testing
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
or
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add -D @routecraft/testing
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Install as a **devDependency** and ensure `vitest` (>=4) and `@routecraft/routecraft` are available.
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
23
|
+
import { testContext, type TestContext } from "@routecraft/testing";
|
|
24
|
+
import { craft, simple, log } from "@routecraft/routecraft";
|
|
25
|
+
|
|
26
|
+
describe("my route", () => {
|
|
27
|
+
let t: TestContext;
|
|
28
|
+
|
|
29
|
+
afterEach(async () => {
|
|
30
|
+
if (t) await t.stop();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("runs and logs", async () => {
|
|
34
|
+
const route = craft()
|
|
35
|
+
.id("example")
|
|
36
|
+
.from(simple("hello"))
|
|
37
|
+
.to(log());
|
|
38
|
+
|
|
39
|
+
t = await testContext().routes(route).build();
|
|
40
|
+
await t.test();
|
|
41
|
+
|
|
42
|
+
expect(t.logger.info).toHaveBeenCalled();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
- **`testContext()`** — Returns a builder. Call `.routes(...).build()` to get a `TestContext`.
|
|
50
|
+
- **`TestContext`** — Wrapper around `CraftContext` with:
|
|
51
|
+
- **`ctx`** — The underlying context.
|
|
52
|
+
- **`logger`** — A spy logger (Vitest `vi.fn()` methods) for asserting on log calls.
|
|
53
|
+
- **`errors`** — Collected route errors.
|
|
54
|
+
- **`test()`** — Runs start → wait for routes ready → drain → stop. Assert after `await t.test()`.
|
|
55
|
+
- **`stop()`** / **`drain()`** — Lifecycle helpers.
|
|
56
|
+
- **`TestContextOptions`** — Options (e.g. `routesReadyTimeoutMs`).
|
|
57
|
+
- **`SpyLogger`** — Type for the spy logger on `t.logger`.
|
|
58
|
+
|
|
59
|
+
## Documentation
|
|
60
|
+
|
|
61
|
+
For testing patterns and examples, see [routecraft.dev](https://routecraft.dev).
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
Apache-2.0
|
|
66
|
+
|
|
67
|
+
## Links
|
|
68
|
+
|
|
69
|
+
- [Documentation](https://routecraft.dev)
|
|
70
|
+
- [GitHub Repository](https://github.com/routecraftjs/routecraft)
|
|
71
|
+
- [Issue Tracker](https://github.com/routecraftjs/routecraft/issues)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var vitest=require('vitest'),routecraft=require('@routecraft/routecraft');var v=200;function L(){let r={info:vitest.vi.fn(),debug:vitest.vi.fn(),warn:vitest.vi.fn(),error:vitest.vi.fn(),trace:vitest.vi.fn(),fatal:vitest.vi.fn(),child:vitest.vi.fn()};return r.child.mockImplementation(()=>r),r}function x(){let r=vitest.vi.fn(),e=vitest.vi.fn(),t={info:r,debug:r,warn:r,error:r,trace:r,fatal:r,child:e};return e.mockImplementation(()=>t),t}var f=class{ctx;logger;errors=[];routesReadyTimeoutMs;restoreLoggerChild;constructor(e,t){this.ctx=e,this.logger=t?.spyLogger??x(),t?.restoreLoggerChild&&(this.restoreLoggerChild=t.restoreLoggerChild),this.routesReadyTimeoutMs=t?.routesReadyTimeoutMs??v,e.on("error",n=>{let i=n.details.error;this.errors.push(routecraft.isRouteCraftError(i)?i:routecraft.error("RC9901",i));});}async test(){let e=this.ctx,t=e.getRoutes().length,n=t===0?Promise.resolve():new Promise((y,l)=>{let p=0,s=false,a=setTimeout(()=>{s||(d(),l(new Error("Timeout waiting for routes to start")));},this.routesReadyTimeoutMs),c=e.on("routeStarted",()=>{s||(p++,p>=t&&(d(),y()));}),h=e.on("error",m=>{s||(d(),l(m.details.error));});function d(){s||(s=true,c(),h(),a!==void 0&&(clearTimeout(a),a=void 0));}}),i=e.start();try{await n,await e.drain();}finally{await e.stop(),await i,this.restoreLoggerChild?.();}}drain(){return this.ctx.drain()}stop(){return this.ctx.stop()}},g=class{builder=new routecraft.ContextBuilder;routesReadyTimeoutMs;routesReadyTimeout(e){return this.routesReadyTimeoutMs=e,this}with(e){return this.builder.with(e),this}on(e,t){return this.builder.on(e,t),this}store(e,t){return this.builder.store(e,t),this}routes(e){return this.builder.routes(e),this}async build(){let e=L(),t=routecraft.logger.child.bind(routecraft.logger);routecraft.logger.child=vitest.vi.fn(()=>e);let n=await this.builder.build(),i={...this.routesReadyTimeoutMs!==void 0?{routesReadyTimeoutMs:this.routesReadyTimeoutMs}:{},spyLogger:e,restoreLoggerChild:()=>{routecraft.logger.child=t;}};return new f(n,i)}};function E(){return new g}exports.TestContext=f;exports.TestContextBuilder=g;exports.testContext=E;//# sourceMappingURL=index.cjs.map
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["DEFAULT_ROUTES_READY_TIMEOUT_MS","createSpyLogger","spy","vi","createNoopSpyLogger","noop","childFn","noopLogger","TestContext","ctx","options","payload","err","isRouteCraftError","rcError","total","allReady","resolve","reject","ready","settled","timeoutId","cleanup","offRouteStarted","offError","started","TestContextBuilder","ContextBuilder","ms","config","event","handler","key","value","routes","spyLogger","originalChild","logger","testContext"],"mappings":"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","file":"index.cjs","sourcesContent":["import { vi } from \"vitest\";\nimport type {\n CraftContext,\n CraftConfig,\n StoreRegistry,\n} from \"@routecraft/routecraft\";\nimport {\n ContextBuilder,\n isRouteCraftError,\n RouteCraftError,\n error as rcError,\n logger,\n} from \"@routecraft/routecraft\";\nimport type { EventName, EventHandler } from \"@routecraft/routecraft\";\nimport type { RouteDefinition, RouteBuilder } from \"@routecraft/routecraft\";\n\nconst DEFAULT_ROUTES_READY_TIMEOUT_MS = 200;\n\nfunction createSpyLogger(): SpyLogger {\n const spy: SpyLogger = {\n info: vi.fn(),\n debug: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n trace: vi.fn(),\n fatal: vi.fn(),\n child: vi.fn(),\n };\n spy.child.mockImplementation(() => spy);\n return spy;\n}\n\nfunction createNoopSpyLogger(): SpyLogger {\n const noop = vi.fn();\n const childFn = vi.fn();\n const noopLogger: SpyLogger = {\n info: noop,\n debug: noop,\n warn: noop,\n error: noop,\n trace: noop,\n fatal: noop,\n child: childFn,\n };\n childFn.mockImplementation(() => noopLogger);\n return noopLogger;\n}\n\nexport interface TestContextOptions {\n /** Timeout in ms for waiting for all routes to emit routeStarted. Default 200. */\n routesReadyTimeoutMs?: number;\n}\n\n/**\n * Spy logger with vi.fn() methods for assertions (e.g. expect(t.logger.info).toHaveBeenCalledWith(...)).\n */\nexport type SpyLogger = {\n info: ReturnType<typeof vi.fn>;\n debug: ReturnType<typeof vi.fn>;\n warn: ReturnType<typeof vi.fn>;\n error: ReturnType<typeof vi.fn>;\n trace: ReturnType<typeof vi.fn>;\n fatal: ReturnType<typeof vi.fn>;\n child: ReturnType<typeof vi.fn>;\n};\n\n/**\n * Test-friendly wrapper around CraftContext. Runs the real context but manages\n * lifecycle (start → wait routes ready → drain → stop) and collects errors.\n * t.logger is a spy logger (vi.fn() methods) for asserting on log calls.\n */\nexport class TestContext {\n readonly ctx: CraftContext;\n /** Spy logger; e.g. expect(t.logger.info).toHaveBeenCalledWith(...) */\n readonly logger: SpyLogger;\n readonly errors: RouteCraftError[] = [];\n private readonly routesReadyTimeoutMs: number;\n\n private restoreLoggerChild?: () => void;\n\n constructor(\n ctx: CraftContext,\n options?: TestContextOptions & {\n spyLogger?: SpyLogger;\n restoreLoggerChild?: () => void;\n },\n ) {\n this.ctx = ctx;\n this.logger = options?.spyLogger ?? createNoopSpyLogger();\n if (options?.restoreLoggerChild)\n this.restoreLoggerChild = options.restoreLoggerChild;\n this.routesReadyTimeoutMs =\n options?.routesReadyTimeoutMs ?? DEFAULT_ROUTES_READY_TIMEOUT_MS;\n ctx.on(\"error\", (payload) => {\n const err = payload.details.error;\n this.errors.push(\n isRouteCraftError(err)\n ? (err as RouteCraftError)\n : rcError(\"RC9901\", err),\n );\n });\n }\n\n /**\n * Start context, wait for all routes ready, drain in-flight, then stop.\n * Assert after this returns (mocks, t.errors, t.ctx.getStore() all valid).\n */\n async test(): Promise<void> {\n const ctx = this.ctx;\n const total = ctx.getRoutes().length;\n const allReady =\n total === 0\n ? Promise.resolve()\n : new Promise<void>((resolve, reject) => {\n let ready = 0;\n let settled = false;\n let timeoutId: ReturnType<typeof setTimeout> | undefined =\n setTimeout(() => {\n if (settled) return;\n cleanup();\n reject(new Error(\"Timeout waiting for routes to start\"));\n }, this.routesReadyTimeoutMs);\n\n const offRouteStarted = ctx.on(\"routeStarted\", () => {\n if (settled) return;\n ready++;\n if (ready >= total) {\n cleanup();\n resolve();\n }\n });\n const offError = ctx.on(\"error\", (payload) => {\n if (settled) return;\n cleanup();\n reject(payload.details.error);\n });\n\n function cleanup(): void {\n if (settled) return;\n settled = true;\n offRouteStarted();\n offError();\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n }\n });\n const started = ctx.start();\n try {\n await allReady;\n await ctx.drain();\n } finally {\n await ctx.stop();\n await started;\n this.restoreLoggerChild?.();\n }\n }\n\n drain(): Promise<void> {\n return this.ctx.drain();\n }\n\n stop(): Promise<void> {\n return this.ctx.stop();\n }\n}\n\n/**\n * Builder that returns TestContext instead of CraftContext.\n * Same API as ContextBuilder (routes, on, with, store).\n */\nexport class TestContextBuilder {\n private builder = new ContextBuilder();\n private routesReadyTimeoutMs: number | undefined;\n\n /** Override timeout for waiting for routes to start (ms). Used by tests that assert timeout behavior. */\n routesReadyTimeout(ms: number): this {\n this.routesReadyTimeoutMs = ms;\n return this;\n }\n\n with(config: CraftConfig): this {\n this.builder.with(config);\n return this;\n }\n\n on<K extends EventName>(event: K, handler: EventHandler<K>): this {\n this.builder.on(event, handler);\n return this;\n }\n\n store<K extends keyof StoreRegistry>(key: K, value: StoreRegistry[K]): this {\n this.builder.store(key, value);\n return this;\n }\n\n routes(\n routes:\n | RouteDefinition[]\n | RouteBuilder<unknown>[]\n | RouteDefinition\n | RouteBuilder<unknown>,\n ): this {\n this.builder.routes(routes);\n return this;\n }\n\n async build(): Promise<TestContext> {\n const spyLogger = createSpyLogger();\n const originalChild = logger.child.bind(logger);\n logger.child = vi.fn(\n () => spyLogger as unknown as ReturnType<typeof logger.child>,\n ) as typeof logger.child;\n const ctx = await this.builder.build();\n const options: TestContextOptions & {\n spyLogger: SpyLogger;\n restoreLoggerChild: () => void;\n } = {\n ...(this.routesReadyTimeoutMs !== undefined\n ? { routesReadyTimeoutMs: this.routesReadyTimeoutMs }\n : {}),\n spyLogger,\n restoreLoggerChild: () => {\n logger.child = originalChild;\n },\n };\n return new TestContext(ctx, options);\n }\n}\n\n/**\n * Create a test context builder. Use .routes(...).build(), await the result, then await t.test().\n *\n * @example\n * const builder = testContext();\n * const t = await builder.routes(myRoutes).build();\n * await t.test();\n */\nexport function testContext(): TestContextBuilder {\n return new TestContextBuilder();\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import { CraftContext, RouteCraftError, CraftConfig, EventName, EventHandler, StoreRegistry, RouteDefinition, RouteBuilder } from '@routecraft/routecraft';
|
|
3
|
+
|
|
4
|
+
interface TestContextOptions {
|
|
5
|
+
/** Timeout in ms for waiting for all routes to emit routeStarted. Default 200. */
|
|
6
|
+
routesReadyTimeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Spy logger with vi.fn() methods for assertions (e.g. expect(t.logger.info).toHaveBeenCalledWith(...)).
|
|
10
|
+
*/
|
|
11
|
+
type SpyLogger = {
|
|
12
|
+
info: ReturnType<typeof vi.fn>;
|
|
13
|
+
debug: ReturnType<typeof vi.fn>;
|
|
14
|
+
warn: ReturnType<typeof vi.fn>;
|
|
15
|
+
error: ReturnType<typeof vi.fn>;
|
|
16
|
+
trace: ReturnType<typeof vi.fn>;
|
|
17
|
+
fatal: ReturnType<typeof vi.fn>;
|
|
18
|
+
child: ReturnType<typeof vi.fn>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Test-friendly wrapper around CraftContext. Runs the real context but manages
|
|
22
|
+
* lifecycle (start → wait routes ready → drain → stop) and collects errors.
|
|
23
|
+
* t.logger is a spy logger (vi.fn() methods) for asserting on log calls.
|
|
24
|
+
*/
|
|
25
|
+
declare class TestContext {
|
|
26
|
+
readonly ctx: CraftContext;
|
|
27
|
+
/** Spy logger; e.g. expect(t.logger.info).toHaveBeenCalledWith(...) */
|
|
28
|
+
readonly logger: SpyLogger;
|
|
29
|
+
readonly errors: RouteCraftError[];
|
|
30
|
+
private readonly routesReadyTimeoutMs;
|
|
31
|
+
private restoreLoggerChild?;
|
|
32
|
+
constructor(ctx: CraftContext, options?: TestContextOptions & {
|
|
33
|
+
spyLogger?: SpyLogger;
|
|
34
|
+
restoreLoggerChild?: () => void;
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Start context, wait for all routes ready, drain in-flight, then stop.
|
|
38
|
+
* Assert after this returns (mocks, t.errors, t.ctx.getStore() all valid).
|
|
39
|
+
*/
|
|
40
|
+
test(): Promise<void>;
|
|
41
|
+
drain(): Promise<void>;
|
|
42
|
+
stop(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Builder that returns TestContext instead of CraftContext.
|
|
46
|
+
* Same API as ContextBuilder (routes, on, with, store).
|
|
47
|
+
*/
|
|
48
|
+
declare class TestContextBuilder {
|
|
49
|
+
private builder;
|
|
50
|
+
private routesReadyTimeoutMs;
|
|
51
|
+
/** Override timeout for waiting for routes to start (ms). Used by tests that assert timeout behavior. */
|
|
52
|
+
routesReadyTimeout(ms: number): this;
|
|
53
|
+
with(config: CraftConfig): this;
|
|
54
|
+
on<K extends EventName>(event: K, handler: EventHandler<K>): this;
|
|
55
|
+
store<K extends keyof StoreRegistry>(key: K, value: StoreRegistry[K]): this;
|
|
56
|
+
routes(routes: RouteDefinition[] | RouteBuilder<unknown>[] | RouteDefinition | RouteBuilder<unknown>): this;
|
|
57
|
+
build(): Promise<TestContext>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create a test context builder. Use .routes(...).build(), await the result, then await t.test().
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* const builder = testContext();
|
|
64
|
+
* const t = await builder.routes(myRoutes).build();
|
|
65
|
+
* await t.test();
|
|
66
|
+
*/
|
|
67
|
+
declare function testContext(): TestContextBuilder;
|
|
68
|
+
|
|
69
|
+
export { type SpyLogger, TestContext, TestContextBuilder, type TestContextOptions, testContext };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import { CraftContext, RouteCraftError, CraftConfig, EventName, EventHandler, StoreRegistry, RouteDefinition, RouteBuilder } from '@routecraft/routecraft';
|
|
3
|
+
|
|
4
|
+
interface TestContextOptions {
|
|
5
|
+
/** Timeout in ms for waiting for all routes to emit routeStarted. Default 200. */
|
|
6
|
+
routesReadyTimeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Spy logger with vi.fn() methods for assertions (e.g. expect(t.logger.info).toHaveBeenCalledWith(...)).
|
|
10
|
+
*/
|
|
11
|
+
type SpyLogger = {
|
|
12
|
+
info: ReturnType<typeof vi.fn>;
|
|
13
|
+
debug: ReturnType<typeof vi.fn>;
|
|
14
|
+
warn: ReturnType<typeof vi.fn>;
|
|
15
|
+
error: ReturnType<typeof vi.fn>;
|
|
16
|
+
trace: ReturnType<typeof vi.fn>;
|
|
17
|
+
fatal: ReturnType<typeof vi.fn>;
|
|
18
|
+
child: ReturnType<typeof vi.fn>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Test-friendly wrapper around CraftContext. Runs the real context but manages
|
|
22
|
+
* lifecycle (start → wait routes ready → drain → stop) and collects errors.
|
|
23
|
+
* t.logger is a spy logger (vi.fn() methods) for asserting on log calls.
|
|
24
|
+
*/
|
|
25
|
+
declare class TestContext {
|
|
26
|
+
readonly ctx: CraftContext;
|
|
27
|
+
/** Spy logger; e.g. expect(t.logger.info).toHaveBeenCalledWith(...) */
|
|
28
|
+
readonly logger: SpyLogger;
|
|
29
|
+
readonly errors: RouteCraftError[];
|
|
30
|
+
private readonly routesReadyTimeoutMs;
|
|
31
|
+
private restoreLoggerChild?;
|
|
32
|
+
constructor(ctx: CraftContext, options?: TestContextOptions & {
|
|
33
|
+
spyLogger?: SpyLogger;
|
|
34
|
+
restoreLoggerChild?: () => void;
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Start context, wait for all routes ready, drain in-flight, then stop.
|
|
38
|
+
* Assert after this returns (mocks, t.errors, t.ctx.getStore() all valid).
|
|
39
|
+
*/
|
|
40
|
+
test(): Promise<void>;
|
|
41
|
+
drain(): Promise<void>;
|
|
42
|
+
stop(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Builder that returns TestContext instead of CraftContext.
|
|
46
|
+
* Same API as ContextBuilder (routes, on, with, store).
|
|
47
|
+
*/
|
|
48
|
+
declare class TestContextBuilder {
|
|
49
|
+
private builder;
|
|
50
|
+
private routesReadyTimeoutMs;
|
|
51
|
+
/** Override timeout for waiting for routes to start (ms). Used by tests that assert timeout behavior. */
|
|
52
|
+
routesReadyTimeout(ms: number): this;
|
|
53
|
+
with(config: CraftConfig): this;
|
|
54
|
+
on<K extends EventName>(event: K, handler: EventHandler<K>): this;
|
|
55
|
+
store<K extends keyof StoreRegistry>(key: K, value: StoreRegistry[K]): this;
|
|
56
|
+
routes(routes: RouteDefinition[] | RouteBuilder<unknown>[] | RouteDefinition | RouteBuilder<unknown>): this;
|
|
57
|
+
build(): Promise<TestContext>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create a test context builder. Use .routes(...).build(), await the result, then await t.test().
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* const builder = testContext();
|
|
64
|
+
* const t = await builder.routes(myRoutes).build();
|
|
65
|
+
* await t.test();
|
|
66
|
+
*/
|
|
67
|
+
declare function testContext(): TestContextBuilder;
|
|
68
|
+
|
|
69
|
+
export { type SpyLogger, TestContext, TestContextBuilder, type TestContextOptions, testContext };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {vi}from'vitest';import {isRouteCraftError,error,ContextBuilder,logger}from'@routecraft/routecraft';var v=200;function L(){let r={info:vi.fn(),debug:vi.fn(),warn:vi.fn(),error:vi.fn(),trace:vi.fn(),fatal:vi.fn(),child:vi.fn()};return r.child.mockImplementation(()=>r),r}function x(){let r=vi.fn(),e=vi.fn(),t={info:r,debug:r,warn:r,error:r,trace:r,fatal:r,child:e};return e.mockImplementation(()=>t),t}var f=class{ctx;logger;errors=[];routesReadyTimeoutMs;restoreLoggerChild;constructor(e,t){this.ctx=e,this.logger=t?.spyLogger??x(),t?.restoreLoggerChild&&(this.restoreLoggerChild=t.restoreLoggerChild),this.routesReadyTimeoutMs=t?.routesReadyTimeoutMs??v,e.on("error",n=>{let i=n.details.error;this.errors.push(isRouteCraftError(i)?i:error("RC9901",i));});}async test(){let e=this.ctx,t=e.getRoutes().length,n=t===0?Promise.resolve():new Promise((y,l)=>{let p=0,s=false,a=setTimeout(()=>{s||(d(),l(new Error("Timeout waiting for routes to start")));},this.routesReadyTimeoutMs),c=e.on("routeStarted",()=>{s||(p++,p>=t&&(d(),y()));}),h=e.on("error",m=>{s||(d(),l(m.details.error));});function d(){s||(s=true,c(),h(),a!==void 0&&(clearTimeout(a),a=void 0));}}),i=e.start();try{await n,await e.drain();}finally{await e.stop(),await i,this.restoreLoggerChild?.();}}drain(){return this.ctx.drain()}stop(){return this.ctx.stop()}},g=class{builder=new ContextBuilder;routesReadyTimeoutMs;routesReadyTimeout(e){return this.routesReadyTimeoutMs=e,this}with(e){return this.builder.with(e),this}on(e,t){return this.builder.on(e,t),this}store(e,t){return this.builder.store(e,t),this}routes(e){return this.builder.routes(e),this}async build(){let e=L(),t=logger.child.bind(logger);logger.child=vi.fn(()=>e);let n=await this.builder.build(),i={...this.routesReadyTimeoutMs!==void 0?{routesReadyTimeoutMs:this.routesReadyTimeoutMs}:{},spyLogger:e,restoreLoggerChild:()=>{logger.child=t;}};return new f(n,i)}};function E(){return new g}export{f as TestContext,g as TestContextBuilder,E as testContext};//# sourceMappingURL=index.js.map
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["DEFAULT_ROUTES_READY_TIMEOUT_MS","createSpyLogger","spy","vi","createNoopSpyLogger","noop","childFn","noopLogger","TestContext","ctx","options","payload","err","isRouteCraftError","rcError","total","allReady","resolve","reject","ready","settled","timeoutId","cleanup","offRouteStarted","offError","started","TestContextBuilder","ContextBuilder","ms","config","event","handler","key","value","routes","spyLogger","originalChild","logger","testContext"],"mappings":"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","file":"index.js","sourcesContent":["import { vi } from \"vitest\";\nimport type {\n CraftContext,\n CraftConfig,\n StoreRegistry,\n} from \"@routecraft/routecraft\";\nimport {\n ContextBuilder,\n isRouteCraftError,\n RouteCraftError,\n error as rcError,\n logger,\n} from \"@routecraft/routecraft\";\nimport type { EventName, EventHandler } from \"@routecraft/routecraft\";\nimport type { RouteDefinition, RouteBuilder } from \"@routecraft/routecraft\";\n\nconst DEFAULT_ROUTES_READY_TIMEOUT_MS = 200;\n\nfunction createSpyLogger(): SpyLogger {\n const spy: SpyLogger = {\n info: vi.fn(),\n debug: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n trace: vi.fn(),\n fatal: vi.fn(),\n child: vi.fn(),\n };\n spy.child.mockImplementation(() => spy);\n return spy;\n}\n\nfunction createNoopSpyLogger(): SpyLogger {\n const noop = vi.fn();\n const childFn = vi.fn();\n const noopLogger: SpyLogger = {\n info: noop,\n debug: noop,\n warn: noop,\n error: noop,\n trace: noop,\n fatal: noop,\n child: childFn,\n };\n childFn.mockImplementation(() => noopLogger);\n return noopLogger;\n}\n\nexport interface TestContextOptions {\n /** Timeout in ms for waiting for all routes to emit routeStarted. Default 200. */\n routesReadyTimeoutMs?: number;\n}\n\n/**\n * Spy logger with vi.fn() methods for assertions (e.g. expect(t.logger.info).toHaveBeenCalledWith(...)).\n */\nexport type SpyLogger = {\n info: ReturnType<typeof vi.fn>;\n debug: ReturnType<typeof vi.fn>;\n warn: ReturnType<typeof vi.fn>;\n error: ReturnType<typeof vi.fn>;\n trace: ReturnType<typeof vi.fn>;\n fatal: ReturnType<typeof vi.fn>;\n child: ReturnType<typeof vi.fn>;\n};\n\n/**\n * Test-friendly wrapper around CraftContext. Runs the real context but manages\n * lifecycle (start → wait routes ready → drain → stop) and collects errors.\n * t.logger is a spy logger (vi.fn() methods) for asserting on log calls.\n */\nexport class TestContext {\n readonly ctx: CraftContext;\n /** Spy logger; e.g. expect(t.logger.info).toHaveBeenCalledWith(...) */\n readonly logger: SpyLogger;\n readonly errors: RouteCraftError[] = [];\n private readonly routesReadyTimeoutMs: number;\n\n private restoreLoggerChild?: () => void;\n\n constructor(\n ctx: CraftContext,\n options?: TestContextOptions & {\n spyLogger?: SpyLogger;\n restoreLoggerChild?: () => void;\n },\n ) {\n this.ctx = ctx;\n this.logger = options?.spyLogger ?? createNoopSpyLogger();\n if (options?.restoreLoggerChild)\n this.restoreLoggerChild = options.restoreLoggerChild;\n this.routesReadyTimeoutMs =\n options?.routesReadyTimeoutMs ?? DEFAULT_ROUTES_READY_TIMEOUT_MS;\n ctx.on(\"error\", (payload) => {\n const err = payload.details.error;\n this.errors.push(\n isRouteCraftError(err)\n ? (err as RouteCraftError)\n : rcError(\"RC9901\", err),\n );\n });\n }\n\n /**\n * Start context, wait for all routes ready, drain in-flight, then stop.\n * Assert after this returns (mocks, t.errors, t.ctx.getStore() all valid).\n */\n async test(): Promise<void> {\n const ctx = this.ctx;\n const total = ctx.getRoutes().length;\n const allReady =\n total === 0\n ? Promise.resolve()\n : new Promise<void>((resolve, reject) => {\n let ready = 0;\n let settled = false;\n let timeoutId: ReturnType<typeof setTimeout> | undefined =\n setTimeout(() => {\n if (settled) return;\n cleanup();\n reject(new Error(\"Timeout waiting for routes to start\"));\n }, this.routesReadyTimeoutMs);\n\n const offRouteStarted = ctx.on(\"routeStarted\", () => {\n if (settled) return;\n ready++;\n if (ready >= total) {\n cleanup();\n resolve();\n }\n });\n const offError = ctx.on(\"error\", (payload) => {\n if (settled) return;\n cleanup();\n reject(payload.details.error);\n });\n\n function cleanup(): void {\n if (settled) return;\n settled = true;\n offRouteStarted();\n offError();\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n }\n });\n const started = ctx.start();\n try {\n await allReady;\n await ctx.drain();\n } finally {\n await ctx.stop();\n await started;\n this.restoreLoggerChild?.();\n }\n }\n\n drain(): Promise<void> {\n return this.ctx.drain();\n }\n\n stop(): Promise<void> {\n return this.ctx.stop();\n }\n}\n\n/**\n * Builder that returns TestContext instead of CraftContext.\n * Same API as ContextBuilder (routes, on, with, store).\n */\nexport class TestContextBuilder {\n private builder = new ContextBuilder();\n private routesReadyTimeoutMs: number | undefined;\n\n /** Override timeout for waiting for routes to start (ms). Used by tests that assert timeout behavior. */\n routesReadyTimeout(ms: number): this {\n this.routesReadyTimeoutMs = ms;\n return this;\n }\n\n with(config: CraftConfig): this {\n this.builder.with(config);\n return this;\n }\n\n on<K extends EventName>(event: K, handler: EventHandler<K>): this {\n this.builder.on(event, handler);\n return this;\n }\n\n store<K extends keyof StoreRegistry>(key: K, value: StoreRegistry[K]): this {\n this.builder.store(key, value);\n return this;\n }\n\n routes(\n routes:\n | RouteDefinition[]\n | RouteBuilder<unknown>[]\n | RouteDefinition\n | RouteBuilder<unknown>,\n ): this {\n this.builder.routes(routes);\n return this;\n }\n\n async build(): Promise<TestContext> {\n const spyLogger = createSpyLogger();\n const originalChild = logger.child.bind(logger);\n logger.child = vi.fn(\n () => spyLogger as unknown as ReturnType<typeof logger.child>,\n ) as typeof logger.child;\n const ctx = await this.builder.build();\n const options: TestContextOptions & {\n spyLogger: SpyLogger;\n restoreLoggerChild: () => void;\n } = {\n ...(this.routesReadyTimeoutMs !== undefined\n ? { routesReadyTimeoutMs: this.routesReadyTimeoutMs }\n : {}),\n spyLogger,\n restoreLoggerChild: () => {\n logger.child = originalChild;\n },\n };\n return new TestContext(ctx, options);\n }\n}\n\n/**\n * Create a test context builder. Use .routes(...).build(), await the result, then await t.test().\n *\n * @example\n * const builder = testContext();\n * const t = await builder.routes(myRoutes).build();\n * await t.test();\n */\nexport function testContext(): TestContextBuilder {\n return new TestContextBuilder();\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@routecraft/testing",
|
|
3
|
+
"version": "0.3.0-canary.1",
|
|
4
|
+
"description": "Test utilities for RouteCraft routes",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
21
|
+
"test": "vitest",
|
|
22
|
+
"test:coverage": "vitest --coverage",
|
|
23
|
+
"prepublishOnly": "pnpm run build"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@routecraft/routecraft": "^0.3.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"vitest": "^4.0.18"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@routecraft/routecraft": "*",
|
|
33
|
+
"vitest": ">=4"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"routecraft",
|
|
37
|
+
"testing",
|
|
38
|
+
"vitest",
|
|
39
|
+
"test-context"
|
|
40
|
+
],
|
|
41
|
+
"author": "routecraftjs",
|
|
42
|
+
"license": "Apache-2.0",
|
|
43
|
+
"homepage": "https://routecraft.dev",
|
|
44
|
+
"bugs": "https://github.com/routecraftjs/routecraft/issues",
|
|
45
|
+
"repository": "https://github.com/routecraftjs/routecraft",
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|