@zudojs/testing 1.1.2 → 1.2.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.
Files changed (96) hide show
  1. package/README.md +138 -2
  2. package/dist/assertions/httpAssertions.core.d.ts +2 -2
  3. package/dist/httpTestClient/httpTestClient.cookieJar.d.ts +30 -0
  4. package/dist/httpTestClient/httpTestClient.cookieJar.js +107 -0
  5. package/dist/httpTestClient/httpTestClient.factory.d.ts +25 -0
  6. package/dist/httpTestClient/httpTestClient.factory.js +84 -0
  7. package/dist/httpTestClient/httpTestClient.target.d.ts +16 -0
  8. package/dist/httpTestClient/httpTestClient.target.js +80 -0
  9. package/dist/httpTestClient/httpTestClient.type.d.ts +74 -0
  10. package/dist/httpTestClient/httpTestClient.type.js +5 -0
  11. package/dist/httpTestClient/httpTestRequest/httpTestRequest.core.d.ts +10 -0
  12. package/dist/httpTestClient/httpTestRequest/httpTestRequest.core.js +97 -0
  13. package/dist/httpTestClient/httpTestRequest/httpTestRequest.encode.d.ts +26 -0
  14. package/dist/httpTestClient/httpTestRequest/httpTestRequest.encode.js +73 -0
  15. package/dist/httpTestClient/httpTestRequest/httpTestRequest.execute.d.ts +26 -0
  16. package/dist/httpTestClient/httpTestRequest/httpTestRequest.execute.js +58 -0
  17. package/dist/httpTestClient/httpTestRequest/httpTestRequest.type.d.ts +53 -0
  18. package/dist/httpTestClient/httpTestRequest/httpTestRequest.type.js +5 -0
  19. package/dist/httpTestClient/httpTestRequest/index.d.ts +13 -0
  20. package/dist/httpTestClient/httpTestRequest/index.js +10 -0
  21. package/dist/httpTestClient/httpTestResponse/httpTestResponse.core.d.ts +15 -0
  22. package/dist/httpTestClient/httpTestResponse/httpTestResponse.core.js +71 -0
  23. package/dist/httpTestClient/httpTestResponse/httpTestResponse.expect.d.ts +19 -0
  24. package/dist/httpTestClient/httpTestResponse/httpTestResponse.expect.js +74 -0
  25. package/dist/httpTestClient/httpTestResponse/httpTestResponse.match.d.ts +15 -0
  26. package/dist/httpTestClient/httpTestResponse/httpTestResponse.match.js +52 -0
  27. package/dist/httpTestClient/httpTestResponse/httpTestResponse.type.d.ts +44 -0
  28. package/dist/httpTestClient/httpTestResponse/httpTestResponse.type.js +5 -0
  29. package/dist/httpTestClient/httpTestResponse/index.d.ts +12 -0
  30. package/dist/httpTestClient/httpTestResponse/index.js +10 -0
  31. package/dist/httpTestClient/httpTestTransport/httpTestTransport.fetch.d.ts +15 -0
  32. package/dist/httpTestClient/httpTestTransport/httpTestTransport.fetch.js +65 -0
  33. package/dist/httpTestClient/httpTestTransport/httpTestTransport.node.d.ts +18 -0
  34. package/dist/httpTestClient/httpTestTransport/httpTestTransport.node.js +94 -0
  35. package/dist/httpTestClient/httpTestTransport/httpTestTransport.server.d.ts +23 -0
  36. package/dist/httpTestClient/httpTestTransport/httpTestTransport.server.js +76 -0
  37. package/dist/httpTestClient/httpTestTransport/httpTestTransport.type.d.ts +46 -0
  38. package/dist/httpTestClient/httpTestTransport/httpTestTransport.type.js +9 -0
  39. package/dist/httpTestClient/httpTestTransport/httpTestTransport.zudo.d.ts +25 -0
  40. package/dist/httpTestClient/httpTestTransport/httpTestTransport.zudo.js +71 -0
  41. package/dist/httpTestClient/httpTestTransport/index.d.ts +15 -0
  42. package/dist/httpTestClient/httpTestTransport/index.js +13 -0
  43. package/dist/httpTestClient/index.d.ts +18 -0
  44. package/dist/httpTestClient/index.js +13 -0
  45. package/dist/httpTesting/httpRequest/httpRequest.builder.d.ts +13 -0
  46. package/dist/httpTesting/httpRequest/httpRequest.builder.js +64 -0
  47. package/dist/httpTesting/httpRequest/httpRequest.factory.d.ts +17 -0
  48. package/dist/httpTesting/httpRequest/httpRequest.factory.js +25 -0
  49. package/dist/httpTesting/{httpRequest.core.d.ts → httpRequest/httpRequest.type.d.ts} +6 -21
  50. package/dist/httpTesting/httpRequest/httpRequest.type.js +5 -0
  51. package/dist/httpTesting/httpRequest/index.d.ts +10 -0
  52. package/dist/httpTesting/httpRequest/index.js +9 -0
  53. package/dist/httpTesting/{httpResponse.helpers.d.ts → httpResponse/httpResponse.helpers.d.ts} +1 -1
  54. package/dist/httpTesting/{httpResponse.type.d.ts → httpResponse/httpResponse.type.d.ts} +1 -1
  55. package/dist/httpTesting/httpResponse/index.d.ts +10 -0
  56. package/dist/httpTesting/httpResponse/index.js +9 -0
  57. package/dist/httpTesting/index.d.ts +6 -6
  58. package/dist/httpTesting/index.js +4 -4
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.js +1 -0
  61. package/dist/mocking/spy.core.d.ts +6 -1
  62. package/dist/mocking/spy.core.js +1 -3
  63. package/dist/mocking/stub.core.d.ts +3 -1
  64. package/dist/mocking/stub.core.js +15 -2
  65. package/dist/testApplication/index.d.ts +1 -1
  66. package/dist/testApplication/index.js +1 -1
  67. package/dist/testApplication/testApplication.core.d.ts +35 -5
  68. package/dist/testApplication/testApplication.core.js +18 -3
  69. package/dist/testEventBus/index.d.ts +2 -2
  70. package/dist/testEventBus/index.js +1 -1
  71. package/dist/testEventBus/testEventBus.core.d.ts +12 -45
  72. package/dist/testEventBus/testEventBus.core.js +55 -46
  73. package/dist/testEventBus/testEventBus.type.d.ts +40 -0
  74. package/dist/testEventBus/testEventBus.type.js +2 -0
  75. package/dist/testMessageBus/index.d.ts +1 -1
  76. package/dist/testMessageBus/index.js +1 -1
  77. package/dist/testMessageBus/testMessageBus.core.d.ts +20 -24
  78. package/dist/testMessageBus/testMessageBus.core.js +45 -47
  79. package/dist/testQueue/index.d.ts +2 -2
  80. package/dist/testQueue/index.js +1 -1
  81. package/dist/testQueue/testQueue.core.d.ts +19 -38
  82. package/dist/testQueue/testQueue.core.js +34 -30
  83. package/dist/testQueue/testQueue.type.d.ts +30 -0
  84. package/dist/testQueue/testQueue.type.js +2 -0
  85. package/dist/testQueue/testQueue.view.d.ts +37 -0
  86. package/dist/testQueue/testQueue.view.js +44 -0
  87. package/dist/testStorage/testStorageMemory.core.d.ts +6 -1
  88. package/dist/testStorage/testStorageMemory.core.js +13 -3
  89. package/package.json +17 -17
  90. package/dist/httpTesting/httpRequest.core.js +0 -114
  91. package/dist/httpTesting/httpStatusCode.type.d.ts +0 -10
  92. package/dist/httpTesting/httpStatusCode.type.js +0 -9
  93. /package/dist/httpTesting/{httpResponse.core.d.ts → httpResponse/httpResponse.core.d.ts} +0 -0
  94. /package/dist/httpTesting/{httpResponse.core.js → httpResponse/httpResponse.core.js} +0 -0
  95. /package/dist/httpTesting/{httpResponse.helpers.js → httpResponse/httpResponse.helpers.js} +0 -0
  96. /package/dist/httpTesting/{httpResponse.type.js → httpResponse/httpResponse.type.js} +0 -0
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @zudojs/testing — one-call HTTP request factory.
3
+ */
4
+ /**
5
+ * Creates a simple test HTTP request without the builder pattern.
6
+ *
7
+ * `query` and `params` are copied, so mutating the object passed in after the
8
+ * call does not change the request (the builder already behaved this way).
9
+ *
10
+ * @param method - HTTP method.
11
+ * @param path - Request path.
12
+ * @param options - Optional headers, query, params, and body.
13
+ * @returns A TestHTTPRequest instance.
14
+ */
15
+ export function createHTTPRequest(method, path, options = {}) {
16
+ return {
17
+ method,
18
+ path,
19
+ headers: new Headers(options.headers),
20
+ query: { ...options.query },
21
+ body: options.body,
22
+ params: { ...options.params },
23
+ };
24
+ }
25
+ //# sourceMappingURL=httpRequest.factory.js.map
@@ -1,9 +1,7 @@
1
1
  /**
2
- * HTTP request builder for testing.
3
- *
4
- * Provides a fluent API for constructing test HTTP requests.
2
+ * @zudojs/testing — HTTP request test double types.
5
3
  */
6
- /** HTTP method type. */
4
+ /** HTTP method accepted by the request builders and the test client. */
7
5
  export type HTTPMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
8
6
  /**
9
7
  * A test HTTP request.
@@ -46,24 +44,11 @@ export interface HTTPRequestBuilder {
46
44
  withBody: (body: unknown) => HTTPRequestBuilder;
47
45
  build: () => TestHTTPRequest;
48
46
  }
49
- /**
50
- * Creates a new HTTP request builder.
51
- *
52
- * @returns An HTTPRequestBuilder instance.
53
- */
54
- export declare function createTestHTTPRequest(): HTTPRequestBuilder;
55
- /**
56
- * Creates a simple test HTTP request without the builder pattern.
57
- *
58
- * @param method - HTTP method.
59
- * @param path - Request path.
60
- * @param options - Optional headers, query, and body.
61
- * @returns A TestHTTPRequest instance.
62
- */
63
- export declare function createHTTPRequest(method: HTTPMethod, path: string, options?: {
47
+ /** Options accepted by `createHTTPRequest`. */
48
+ export interface HTTPRequestOptions {
64
49
  readonly headers?: Headers | Record<string, string>;
65
50
  readonly query?: Record<string, string>;
66
51
  readonly body?: unknown;
67
52
  readonly params?: Record<string, string>;
68
- }): TestHTTPRequest;
69
- //# sourceMappingURL=httpRequest.core.d.ts.map
53
+ }
54
+ //# sourceMappingURL=httpRequest.type.d.ts.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @zudojs/testing — HTTP request test double types.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=httpRequest.type.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @zudojs/testing — HTTP request test doubles.
3
+ *
4
+ * A fluent request builder and a one-call factory. Built requests can be sent
5
+ * to a real app with `createHttpTestClient(app).request(built)`.
6
+ */
7
+ export { createTestHTTPRequest } from "./httpRequest.builder.js";
8
+ export { createHTTPRequest } from "./httpRequest.factory.js";
9
+ export type { HTTPMethod, HTTPRequestBuilder, HTTPRequestOptions, TestHTTPRequest, } from "./httpRequest.type.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @zudojs/testing — HTTP request test doubles.
3
+ *
4
+ * A fluent request builder and a one-call factory. Built requests can be sent
5
+ * to a real app with `createHttpTestClient(app).request(built)`.
6
+ */
7
+ export { createTestHTTPRequest } from "./httpRequest.builder.js";
8
+ export { createHTTPRequest } from "./httpRequest.factory.js";
9
+ //# sourceMappingURL=index.js.map
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Convenience functions for creating common test HTTP responses.
5
5
  */
6
- import type { HTTPStatusCode } from "./httpStatusCode.type.js";
6
+ import type { HTTPStatusCode } from "@zudojs/http";
7
7
  import type { TestHTTPResponse } from "./httpResponse.type.js";
8
8
  /**
9
9
  * Creates a simple test HTTP response without the builder pattern.
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Types for test HTTP responses and response builders.
5
5
  */
6
- import type { HTTPStatusCode } from "./httpStatusCode.type.js";
6
+ import type { HTTPStatusCode } from "@zudojs/http";
7
7
  /** A test HTTP response. */
8
8
  export interface TestHTTPResponse {
9
9
  readonly status: HTTPStatusCode;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @zudojs/testing — HTTP response test doubles.
3
+ *
4
+ * A fluent response builder plus one-line helpers for the common responses
5
+ * (`jsonResponse`, `notFoundResponse`, ...).
6
+ */
7
+ export { createTestHTTPResponse } from "./httpResponse.core.js";
8
+ export type { HTTPResponseBuilder, TestHTTPResponse, } from "./httpResponse.type.js";
9
+ export { createHTTPResponse, jsonResponse, createdResponse, noContentResponse, badRequestResponse, notFoundResponse, serverErrorResponse, } from "./httpResponse.helpers.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @zudojs/testing — HTTP response test doubles.
3
+ *
4
+ * A fluent response builder plus one-line helpers for the common responses
5
+ * (`jsonResponse`, `notFoundResponse`, ...).
6
+ */
7
+ export { createTestHTTPResponse } from "./httpResponse.core.js";
8
+ export { createHTTPResponse, jsonResponse, createdResponse, noContentResponse, badRequestResponse, notFoundResponse, serverErrorResponse, } from "./httpResponse.helpers.js";
9
+ //# sourceMappingURL=index.js.map
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * @zudojs/testing — HTTP testing helpers.
3
3
  *
4
- * Request and response builders for HTTP testing.
4
+ * Request and response builders for HTTP testing. To drive a running app,
5
+ * see `createHttpTestClient` in `httpTestClient/`.
5
6
  */
6
- export { createTestHTTPRequest, createHTTPRequest, } from "./httpRequest.core.js";
7
- export type { HTTPRequestBuilder, TestHTTPRequest, } from "./httpRequest.core.js";
8
- export { createTestHTTPResponse } from "./httpResponse.core.js";
9
- export type { HTTPResponseBuilder, TestHTTPResponse, } from "./httpResponse.type.js";
10
- export { createHTTPResponse, jsonResponse, createdResponse, noContentResponse, badRequestResponse, notFoundResponse, serverErrorResponse, } from "./httpResponse.helpers.js";
7
+ export { createTestHTTPRequest, createHTTPRequest, } from "./httpRequest/index.js";
8
+ export type { HTTPRequestBuilder, TestHTTPRequest, } from "./httpRequest/index.js";
9
+ export { createTestHTTPResponse, createHTTPResponse, jsonResponse, createdResponse, noContentResponse, badRequestResponse, notFoundResponse, serverErrorResponse, } from "./httpResponse/index.js";
10
+ export type { HTTPResponseBuilder, TestHTTPResponse, } from "./httpResponse/index.js";
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * @zudojs/testing — HTTP testing helpers.
3
3
  *
4
- * Request and response builders for HTTP testing.
4
+ * Request and response builders for HTTP testing. To drive a running app,
5
+ * see `createHttpTestClient` in `httpTestClient/`.
5
6
  */
6
- export { createTestHTTPRequest, createHTTPRequest, } from "./httpRequest.core.js";
7
- export { createTestHTTPResponse } from "./httpResponse.core.js";
8
- export { createHTTPResponse, jsonResponse, createdResponse, noContentResponse, badRequestResponse, notFoundResponse, serverErrorResponse, } from "./httpResponse.helpers.js";
7
+ export { createTestHTTPRequest, createHTTPRequest, } from "./httpRequest/index.js";
8
+ export { createTestHTTPResponse, createHTTPResponse, jsonResponse, createdResponse, noContentResponse, badRequestResponse, notFoundResponse, serverErrorResponse, } from "./httpResponse/index.js";
9
9
  //# sourceMappingURL=index.js.map
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export * from "./testEventBus/index.js";
15
15
  export * from "./testMessageBus/index.js";
16
16
  export * from "./testQueue/index.js";
17
17
  export * from "./httpTesting/index.js";
18
+ export * from "./httpTestClient/index.js";
18
19
  export * from "./mocking/index.js";
19
20
  export * from "./fixtures/index.js";
20
21
  export * from "./assertions/index.js";
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ export * from "./testEventBus/index.js";
15
15
  export * from "./testMessageBus/index.js";
16
16
  export * from "./testQueue/index.js";
17
17
  export * from "./httpTesting/index.js";
18
+ export * from "./httpTestClient/index.js";
18
19
  export * from "./mocking/index.js";
19
20
  export * from "./fixtures/index.js";
20
21
  export * from "./assertions/index.js";
@@ -49,7 +49,11 @@ export interface SpyMethod<TObj, TMethod extends keyof TObj> {
49
49
  readonly results: readonly unknown[];
50
50
  readonly errors: readonly unknown[];
51
51
  readonly callCount: number;
52
- /** Reinstates the original method exactly as it was found. */
52
+ /**
53
+ * Reinstates the original method exactly as it was found. The recorded
54
+ * `calls`, `results` and `errors` are kept, so they can be asserted after
55
+ * restoring; calls made after `restore()` are not recorded.
56
+ */
53
57
  restore: () => void;
54
58
  }
55
59
  /**
@@ -69,6 +73,7 @@ export interface SpyMethod<TObj, TMethod extends keyof TObj> {
69
73
  * expect(spy.calls).toHaveLength(1);
70
74
  *
71
75
  * spy.restore();
76
+ * expect(spy.calls).toHaveLength(1); // still recorded after restore
72
77
  * ```
73
78
  */
74
79
  export declare function createSpyMethod<TObj, TMethod extends keyof TObj>(object: TObj, property: TMethod): SpyMethod<TObj, TMethod>;
@@ -77,6 +77,7 @@ export function createSpyFn(fn) {
77
77
  * expect(spy.calls).toHaveLength(1);
78
78
  *
79
79
  * spy.restore();
80
+ * expect(spy.calls).toHaveLength(1); // still recorded after restore
80
81
  * ```
81
82
  */
82
83
  export function createSpyMethod(object, property) {
@@ -120,9 +121,6 @@ export function createSpyMethod(object, property) {
120
121
  target[key] = original;
121
122
  else
122
123
  delete target[key];
123
- calls.length = 0;
124
- results.length = 0;
125
- errors.length = 0;
126
124
  },
127
125
  };
128
126
  }
@@ -7,7 +7,9 @@
7
7
  * Creates a stub object from an interface.
8
8
  *
9
9
  * All methods return undefined by default. Override specific methods
10
- * by passing an overrides object.
10
+ * by passing an overrides object. The overrides are the stub's own
11
+ * enumerable properties, so `Object.keys(stub)`, `{ ...stub }` and
12
+ * `expect.objectContaining` see them; unstubbed no-ops are not own keys.
11
13
  *
12
14
  * @typeParam T - The interface type to stub.
13
15
  * @param overrides - Optional method implementations.
@@ -8,12 +8,14 @@
8
8
  *
9
9
  * `then` is the important one: answering it makes the stub a thenable, so
10
10
  * `await stub` calls it expecting a promise callback. The fake never resolves,
11
- * and the test hangs until it times out with no indication why.
11
+ * and the test hangs until it times out with no indication why. Likewise
12
+ * `asymmetricMatch` makes Vitest's `equals` treat the stub as a matcher.
12
13
  */
13
14
  const NON_CALLABLE_KEYS = new Set([
14
15
  "then",
15
16
  "catch",
16
17
  "finally",
18
+ "asymmetricMatch",
17
19
  Symbol.toPrimitive,
18
20
  Symbol.iterator,
19
21
  Symbol.asyncIterator,
@@ -23,7 +25,9 @@ const NON_CALLABLE_KEYS = new Set([
23
25
  * Creates a stub object from an interface.
24
26
  *
25
27
  * All methods return undefined by default. Override specific methods
26
- * by passing an overrides object.
28
+ * by passing an overrides object. The overrides are the stub's own
29
+ * enumerable properties, so `Object.keys(stub)`, `{ ...stub }` and
30
+ * `expect.objectContaining` see them; unstubbed no-ops are not own keys.
27
31
  *
28
32
  * @typeParam T - The interface type to stub.
29
33
  * @param overrides - Optional method implementations.
@@ -69,6 +73,15 @@ export function createStub(overrides = {}) {
69
73
  has(_target, prop) {
70
74
  return !NON_CALLABLE_KEYS.has(prop);
71
75
  },
76
+ ownKeys() {
77
+ return Reflect.ownKeys(overrides);
78
+ },
79
+ getOwnPropertyDescriptor(_target, prop) {
80
+ const descriptor = Reflect.getOwnPropertyDescriptor(overrides, prop);
81
+ return descriptor === undefined
82
+ ? undefined
83
+ : { ...descriptor, configurable: true };
84
+ },
72
85
  });
73
86
  }
74
87
  /**
@@ -3,6 +3,6 @@
3
3
  *
4
4
  * Simplified test application context creation.
5
5
  */
6
- export { createTestApplication } from "./testApplication.core.js";
6
+ export { createTestApplication, DEFAULT_TEST_APPLICATION_TIME, } from "./testApplication.core.js";
7
7
  export type { TestApplication, TestApplicationOptions, } from "./testApplication.core.js";
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * Simplified test application context creation.
5
5
  */
6
- export { createTestApplication } from "./testApplication.core.js";
6
+ export { createTestApplication, DEFAULT_TEST_APPLICATION_TIME, } from "./testApplication.core.js";
7
7
  //# sourceMappingURL=index.js.map
@@ -6,25 +6,47 @@
6
6
  */
7
7
  import type { Container } from "@zudojs/container";
8
8
  import type { Logger } from "@zudojs/logger";
9
+ import type { SpyLogger } from "../spyLogger/spyLogger.type.js";
9
10
  import type { CleanupManager } from "../cleanupManager/cleanupManager.core.js";
10
11
  import type { TestClock } from "../testClock/testClock.core.js";
12
+ /**
13
+ * The instant a test application's default clock starts at:
14
+ * 2026-01-01T00:00:00.000Z. Fixed, so a test that reads `app.clock.now`
15
+ * gets the same answer on every run.
16
+ */
17
+ export declare const DEFAULT_TEST_APPLICATION_TIME: number;
11
18
  /**
12
19
  * Options for creating a test application.
13
20
  */
14
- export interface TestApplicationOptions {
21
+ export interface TestApplicationOptions<TLogger extends Logger = Logger> {
15
22
  readonly name?: string;
16
23
  readonly container?: Container;
17
- readonly logger?: Logger;
24
+ /**
25
+ * The application's logger. Defaults to a silent `createSpyLogger(name)`
26
+ * that records every line in `app.logger.calls`. Pass a real logger
27
+ * (`createLogger({ name })` from `@zudojs/logger`) to print instead.
28
+ */
29
+ readonly logger?: TLogger;
30
+ /**
31
+ * The application's clock. Defaults to a test clock pinned at
32
+ * `startTime`.
33
+ */
18
34
  readonly clock?: TestClock;
35
+ /**
36
+ * Where the default clock starts. Defaults to
37
+ * {@link DEFAULT_TEST_APPLICATION_TIME}; pass `Date.now()` to start at
38
+ * the wall-clock time. Ignored when `clock` is given.
39
+ */
40
+ readonly startTime?: Date | string | number;
19
41
  readonly cleanup?: CleanupManager;
20
42
  }
21
43
  /**
22
44
  * A test application context.
23
45
  */
24
- export interface TestApplication {
46
+ export interface TestApplication<TLogger extends Logger = Logger> {
25
47
  readonly name: string;
26
48
  readonly container: Container;
27
- readonly logger: Logger;
49
+ readonly logger: TLogger;
28
50
  readonly clock: TestClock;
29
51
  readonly cleanup: CleanupManager;
30
52
  dispose: () => Promise<void>;
@@ -32,6 +54,11 @@ export interface TestApplication {
32
54
  /**
33
55
  * Creates a test application context.
34
56
  *
57
+ * Deterministic and quiet by default: the logger is a recording
58
+ * `SpyLogger` that prints nothing, and the clock is a test clock pinned at
59
+ * {@link DEFAULT_TEST_APPLICATION_TIME}. Pass `logger` or `clock` (or
60
+ * `startTime`) to use something else.
61
+ *
35
62
  * @param options - Test application options.
36
63
  * @returns A TestApplication instance.
37
64
  *
@@ -42,8 +69,11 @@ export interface TestApplication {
42
69
  * app.container.registerValue(token, implementation);
43
70
  * const service = app.container.resolve(token);
44
71
  *
72
+ * app.logger.calls; // what the code under test logged
73
+ * app.clock.now.toISOString(); // "2026-01-01T00:00:00.000Z"
74
+ *
45
75
  * await app.dispose();
46
76
  * ```
47
77
  */
48
- export declare function createTestApplication(options?: TestApplicationOptions): TestApplication;
78
+ export declare function createTestApplication<TLogger extends Logger = SpyLogger>(options?: TestApplicationOptions<TLogger>): TestApplication<TLogger>;
49
79
  //# sourceMappingURL=testApplication.core.d.ts.map
@@ -5,12 +5,23 @@
5
5
  * contexts without the full Runtime lifecycle.
6
6
  */
7
7
  import { createStartedContainer } from "@zudojs/container";
8
- import { createLogger } from "@zudojs/logger";
9
8
  import { createCleanupManager } from "../cleanupManager/cleanupManager.core.js";
10
9
  import { createTestClock } from "../testClock/testClock.core.js";
10
+ import { createSpyLogger } from "../spyLogger/spyLogger.core.js";
11
+ /**
12
+ * The instant a test application's default clock starts at:
13
+ * 2026-01-01T00:00:00.000Z. Fixed, so a test that reads `app.clock.now`
14
+ * gets the same answer on every run.
15
+ */
16
+ export const DEFAULT_TEST_APPLICATION_TIME = Date.UTC(2026, 0, 1);
11
17
  /**
12
18
  * Creates a test application context.
13
19
  *
20
+ * Deterministic and quiet by default: the logger is a recording
21
+ * `SpyLogger` that prints nothing, and the clock is a test clock pinned at
22
+ * {@link DEFAULT_TEST_APPLICATION_TIME}. Pass `logger` or `clock` (or
23
+ * `startTime`) to use something else.
24
+ *
14
25
  * @param options - Test application options.
15
26
  * @returns A TestApplication instance.
16
27
  *
@@ -21,14 +32,18 @@ import { createTestClock } from "../testClock/testClock.core.js";
21
32
  * app.container.registerValue(token, implementation);
22
33
  * const service = app.container.resolve(token);
23
34
  *
35
+ * app.logger.calls; // what the code under test logged
36
+ * app.clock.now.toISOString(); // "2026-01-01T00:00:00.000Z"
37
+ *
24
38
  * await app.dispose();
25
39
  * ```
26
40
  */
27
41
  export function createTestApplication(options = {}) {
28
42
  const name = options.name ?? "test-app";
29
43
  const container = options.container ?? createStartedContainer();
30
- const logger = options.logger ?? createLogger({ name });
31
- const clock = options.clock ?? createTestClock();
44
+ const logger = options.logger ?? createSpyLogger(name);
45
+ const clock = options.clock ??
46
+ createTestClock(options.startTime ?? DEFAULT_TEST_APPLICATION_TIME);
32
47
  const cleanup = options.cleanup ?? createCleanupManager();
33
48
  // Cleanups run in reverse registration order, so the logger is registered
34
49
  // first and therefore closed last: anything that logs while the container
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Test event bus helpers.
3
3
  *
4
- * EventBus with recording and assertion support.
4
+ * A real EventBus that records every publication.
5
5
  */
6
6
  export { createTestEventBus } from "./testEventBus.core.js";
7
- export type { RecordedEvent, TestEventBus } from "./testEventBus.core.js";
7
+ export type { RecordedEvent, TestEventBus } from "./testEventBus.type.js";
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Test event bus helpers.
3
3
  *
4
- * EventBus with recording and assertion support.
4
+ * A real EventBus that records every publication.
5
5
  */
6
6
  export { createTestEventBus } from "./testEventBus.core.js";
7
7
  //# sourceMappingURL=index.js.map
@@ -1,60 +1,27 @@
1
1
  /**
2
2
  * Test event bus helpers.
3
3
  *
4
- * Wraps the real EventBus with recording and assertion support.
4
+ * A real EventBus that records every publication, whichever method it
5
+ * came through.
5
6
  */
6
- import { EventBus } from "@zudojs/events";
7
- import type { Event, EventInput, EventBusOptions, EventPublishResult } from "@zudojs/events";
7
+ import type { EventBusOptions } from "@zudojs/events";
8
+ import type { TestEventBus } from "./testEventBus.type.js";
9
+ export type { RecordedEvent, TestEventBus } from "./testEventBus.type.js";
8
10
  /**
9
- * A recorded event publication.
10
- */
11
- export interface RecordedEvent<TPayload = unknown> {
12
- readonly event: Event<TPayload>;
13
- readonly result: EventPublishResult<Event<TPayload>>;
14
- readonly timestamp: Date;
15
- }
16
- /**
17
- * A test event bus with recording capabilities.
18
- */
19
- export interface TestEventBus {
20
- readonly bus: EventBus;
21
- /**
22
- * All recorded publications.
23
- */
24
- readonly published: readonly RecordedEvent[];
25
- /**
26
- * Publish an event and record the result.
27
- */
28
- publish: <TPayload>(input: EventInput<TPayload>) => Promise<EventPublishResult<Event<TPayload>>>;
29
- /**
30
- * Find published events by type.
31
- */
32
- findByType: (type: string) => readonly RecordedEvent[];
33
- /**
34
- * Clear recorded publications.
35
- */
36
- clear: () => void;
37
- /**
38
- * Dispose the event bus.
39
- */
40
- dispose: () => void;
41
- }
42
- /**
43
- * Creates a test event bus with recording.
11
+ * Creates a started test event bus that records every publication.
44
12
  *
45
13
  * @param options - EventBus options.
46
- * @returns A TestEventBus instance.
14
+ * @returns A TestEventBus, which is itself an `EventBus`.
47
15
  *
48
16
  * @example
49
17
  * ```ts
50
- * const testBus = createTestEventBus();
51
- *
52
- * await testBus.publish({ type: "user.created", payload: { id: "123" } });
18
+ * const events = createTestEventBus();
19
+ * const service = new UserService(events); // takes an EventBus
53
20
  *
54
- * expect(testBus.published).toHaveLength(1);
55
- * expect(testBus.findByType("user.created")).toHaveLength(1);
21
+ * await service.register("ann"); // calls events.publishEvent(...)
56
22
  *
57
- * testBus.dispose();
23
+ * expect(events.findByType("user.created")).toHaveLength(1);
24
+ * events.dispose();
58
25
  * ```
59
26
  */
60
27
  export declare function createTestEventBus(options?: EventBusOptions): TestEventBus;
@@ -1,64 +1,73 @@
1
1
  /**
2
2
  * Test event bus helpers.
3
3
  *
4
- * Wraps the real EventBus with recording and assertion support.
4
+ * A real EventBus that records every publication, whichever method it
5
+ * came through.
5
6
  */
6
- import { EventBus } from "@zudojs/events";
7
- import { randomUUID } from "node:crypto";
7
+ import { EventBus, isEvent } from "@zudojs/events";
8
8
  /**
9
- * Creates a test event bus with recording.
9
+ * EventBus subclass that records at the publish entry points every other
10
+ * path (`emit`, a bus handed to code under test) funnels into.
11
+ */
12
+ class RecordingEventBus extends EventBus {
13
+ recorded = [];
14
+ constructor(options) {
15
+ super(options);
16
+ // Bound, so `const { publish, clear } = createTestEventBus()` keeps
17
+ // working as it did with the earlier closure-based wrapper.
18
+ this.publish = this.publish.bind(this);
19
+ this.publishEvent = this.publishEvent.bind(this);
20
+ this.emit = this.emit.bind(this);
21
+ this.findByType = this.findByType.bind(this);
22
+ this.clear = this.clear.bind(this);
23
+ this.dispose = this.dispose.bind(this);
24
+ }
25
+ get bus() {
26
+ return this;
27
+ }
28
+ get published() {
29
+ return [...this.recorded];
30
+ }
31
+ async publish(input, options = {}) {
32
+ const result = isEvent(input)
33
+ ? await super.publish(input, options)
34
+ : await super.publishEvent(input, options);
35
+ return this.record(result);
36
+ }
37
+ async publishEvent(input, options = {}) {
38
+ return this.record(await super.publishEvent(input, options));
39
+ }
40
+ findByType(type) {
41
+ return this.recorded.filter((entry) => entry.event.type === type);
42
+ }
43
+ clear() {
44
+ this.recorded.length = 0;
45
+ }
46
+ record(result) {
47
+ this.recorded.push(Object.freeze({ event: result.event, result, timestamp: new Date() }));
48
+ return result;
49
+ }
50
+ }
51
+ /**
52
+ * Creates a started test event bus that records every publication.
10
53
  *
11
54
  * @param options - EventBus options.
12
- * @returns A TestEventBus instance.
55
+ * @returns A TestEventBus, which is itself an `EventBus`.
13
56
  *
14
57
  * @example
15
58
  * ```ts
16
- * const testBus = createTestEventBus();
17
- *
18
- * await testBus.publish({ type: "user.created", payload: { id: "123" } });
59
+ * const events = createTestEventBus();
60
+ * const service = new UserService(events); // takes an EventBus
19
61
  *
20
- * expect(testBus.published).toHaveLength(1);
21
- * expect(testBus.findByType("user.created")).toHaveLength(1);
62
+ * await service.register("ann"); // calls events.publishEvent(...)
22
63
  *
23
- * testBus.dispose();
64
+ * expect(events.findByType("user.created")).toHaveLength(1);
65
+ * events.dispose();
24
66
  * ```
25
67
  */
26
68
  export function createTestEventBus(options = {}) {
27
- const bus = new EventBus(options);
28
- const published = [];
69
+ const bus = new RecordingEventBus(options);
29
70
  bus.start();
30
- const publish = async (input) => {
31
- const event = {
32
- id: `evt_${randomUUID()}`,
33
- type: input.type,
34
- payload: input.payload,
35
- timestamp: new Date(),
36
- ...(input.metadata ? { metadata: input.metadata } : {}),
37
- };
38
- const result = await bus.publish(event);
39
- published.push({
40
- event: result.event,
41
- result,
42
- timestamp: new Date(),
43
- });
44
- return result;
45
- };
46
- const findByType = (type) => published.filter((e) => e.event.type === type);
47
- const clear = () => {
48
- published.length = 0;
49
- };
50
- const dispose = () => {
51
- bus.dispose();
52
- };
53
- return {
54
- bus,
55
- get published() {
56
- return [...published];
57
- },
58
- publish,
59
- findByType,
60
- clear,
61
- dispose,
62
- };
71
+ return bus;
63
72
  }
64
73
  //# sourceMappingURL=testEventBus.core.js.map