@grest-ts/testkit 0.0.6 → 0.0.7

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 (46) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +413 -413
  3. package/dist/src/runner/isolated-loader.mjs +91 -91
  4. package/dist/src/runner/worker-loader.mjs +49 -49
  5. package/dist/tsconfig.publish.tsbuildinfo +1 -1
  6. package/package.json +12 -12
  7. package/src/GGBundleTest.ts +89 -89
  8. package/src/GGTest.ts +318 -318
  9. package/src/GGTestContext.ts +74 -74
  10. package/src/GGTestRunner.ts +308 -308
  11. package/src/GGTestRuntime.ts +265 -265
  12. package/src/GGTestRuntimeWorker.ts +159 -159
  13. package/src/GGTestSharedRef.ts +116 -116
  14. package/src/GGTestkitExtensionsDiscovery.ts +26 -26
  15. package/src/IGGLocalDiscoveryServer.ts +16 -16
  16. package/src/callOn/GGCallOnSelector.ts +61 -61
  17. package/src/callOn/GGContractClass.implement.ts +43 -43
  18. package/src/callOn/GGTestActionForLocatorOnCall.ts +134 -134
  19. package/src/callOn/TestableIPC.ts +81 -81
  20. package/src/callOn/callOn.ts +224 -224
  21. package/src/callOn/registerOnCallHandler.ts +123 -123
  22. package/src/index-node.ts +64 -64
  23. package/src/mockable/GGMockable.ts +22 -22
  24. package/src/mockable/GGMockableCall.ts +45 -45
  25. package/src/mockable/GGMockableIPC.ts +20 -20
  26. package/src/mockable/GGMockableInterceptor.ts +44 -44
  27. package/src/mockable/GGMockableInterceptorsServer.ts +69 -69
  28. package/src/mockable/mockable.ts +71 -71
  29. package/src/runner/InlineRunner.ts +47 -47
  30. package/src/runner/IsolatedRunner.ts +179 -179
  31. package/src/runner/RuntimeRunner.ts +15 -15
  32. package/src/runner/WorkerRunner.ts +179 -179
  33. package/src/runner/isolated-loader.mjs +91 -91
  34. package/src/runner/worker-loader.mjs +49 -49
  35. package/src/testers/GGCallInterceptor.ts +224 -224
  36. package/src/testers/GGMockWith.ts +92 -92
  37. package/src/testers/GGSpyWith.ts +115 -115
  38. package/src/testers/GGTestAction.ts +332 -332
  39. package/src/testers/GGTestComponent.ts +16 -16
  40. package/src/testers/GGTestSelector.ts +223 -223
  41. package/src/testers/IGGTestInterceptor.ts +10 -10
  42. package/src/testers/IGGTestWith.ts +15 -15
  43. package/src/testers/RuntimeSelector.ts +151 -151
  44. package/src/utils/GGExpectations.ts +78 -78
  45. package/src/utils/GGTestError.ts +36 -36
  46. package/src/utils/captureStack.ts +53 -53
@@ -1,16 +1,16 @@
1
- import type {GGTestRunner} from "../GGTestRunner";
2
-
3
- /**
4
- * Interface for test components that can be registered with GGTestRunner.
5
- * Components must accept GGTestRunner as their constructor argument.
6
- */
7
- export interface GGTestComponent {
8
- start?(): Promise<void>;
9
- afterEach?(): Promise<void>;
10
- teardown?(): Promise<void>;
11
- }
12
-
13
- /**
14
- * Constructor type for components. All components must accept GGTestRunner as constructor argument.
15
- */
16
- export type GGTestComponentType<T extends GGTestComponent> = new (runner: GGTestRunner) => T;
1
+ import type {GGTestRunner} from "../GGTestRunner";
2
+
3
+ /**
4
+ * Interface for test components that can be registered with GGTestRunner.
5
+ * Components must accept GGTestRunner as their constructor argument.
6
+ */
7
+ export interface GGTestComponent {
8
+ start?(): Promise<void>;
9
+ afterEach?(): Promise<void>;
10
+ teardown?(): Promise<void>;
11
+ }
12
+
13
+ /**
14
+ * Constructor type for components. All components must accept GGTestRunner as constructor argument.
15
+ */
16
+ export type GGTestComponentType<T extends GGTestComponent> = new (runner: GGTestRunner) => T;
@@ -1,223 +1,223 @@
1
- import {RuntimeConstructor, RuntimeResult, Selector, SelectorExtensions, ObjectResult, StartResult, RuntimeInput} from "./RuntimeSelector";
2
- import {GGTestRuntime} from "../GGTestRuntime";
3
- import type {GGTestRunner} from "../GGTestRunner";
4
-
5
- /**
6
- * Internal implementation class for Selector.
7
- * Works with GGTestRuntime instances directly.
8
- */
9
- export class GGTestSelector<T extends RuntimeConstructor[]> {
10
-
11
- private static readonly extensions = new Map<string, typeof GGTestSelectorExtension>();
12
-
13
- public readonly runtimes: GGTestRuntime[];
14
- private readonly extensionCache = new Map<string, unknown>();
15
-
16
- constructor(runtimes: GGTestRuntime[]) {
17
- this.runtimes = runtimes;
18
- }
19
-
20
- public get length(): number {
21
- return this.runtimes.length;
22
- }
23
-
24
- public static addExtension(factory: typeof GGTestSelectorExtension & { PROPERTY_NAME: string }): void {
25
- if (this.extensions.has(factory.PROPERTY_NAME)) {
26
- throw new Error("Extension with name '" + factory.PROPERTY_NAME + "' is already registered!")
27
- }
28
- this.extensions.set(factory.PROPERTY_NAME, factory);
29
- }
30
-
31
- public static hasExtension(name: string): boolean {
32
- return this.extensions.has(name);
33
- }
34
-
35
- public getExtension<K extends keyof SelectorExtensions<T>>(name: K): SelectorExtensions<T>[K] {
36
- const cached = this.extensionCache.get(name as string);
37
- if (cached !== undefined) {
38
- return cached as SelectorExtensions<T>[K];
39
- }
40
-
41
- const extension = GGTestSelector.extensions.get(name);
42
- if (!extension) {
43
- throw new Error(`Extension '${String(name)}' is not registered. ` +
44
- `Make sure the module providing this extension is imported.`);
45
- }
46
-
47
- const extensionInstance = new extension(this.runtimes);
48
- this.extensionCache.set(name as string, extensionInstance);
49
- return extensionInstance as SelectorExtensions<T>[K];
50
- }
51
-
52
- /**
53
- * Stop all runtimes in this selector.
54
- */
55
- public async stop(): Promise<void> {
56
- for (const runtime of this.runtimes) {
57
- await runtime.stop();
58
- }
59
- }
60
-
61
- /**
62
- * Shutdown all runtimes in this selector.
63
- */
64
- public async shutdown(): Promise<void> {
65
- for (const runtime of this.runtimes) {
66
- await runtime.shutdown();
67
- }
68
- }
69
- }
70
-
71
- export class GGTestSelectorExtension {
72
-
73
- protected readonly runner: GGTestRunner
74
-
75
- constructor(protected readonly runtimes: GGTestRuntime[]) {
76
- this.runner = this.runtimes[0]?.runner
77
- }
78
-
79
- protected async forEachParallel(callback: (runtime: GGTestRuntime) => Promise<void>): Promise<void> {
80
- const results = await Promise.allSettled(
81
- this.runtimes.map(runtime => callback(runtime))
82
- );
83
- const errors = results
84
- .filter((r): r is PromiseRejectedResult => r.status === 'rejected')
85
- .map(r => r.reason);
86
-
87
- if (errors.length > 0) {
88
- throw new AggregateError(errors, `${errors.length} runtime(s) failed`);
89
- }
90
- }
91
- }
92
-
93
- /**
94
- * Create a proxied Selector that supports:
95
- * - Indexed access: selector[0]
96
- * - Extension access: selector.config, selector.logs
97
- * - Lifecycle methods: selector.stop(), selector.shutdown()
98
- * - Standard properties: selector.runtimes, selector.length
99
- */
100
- export function createSelector<T extends RuntimeConstructor[]>(runtimes: GGTestRuntime[]): Selector<T> {
101
- const impl = new GGTestSelector<T>(runtimes);
102
-
103
- return new Proxy(impl as unknown as Selector<T>, {
104
- get(target, prop) {
105
- const implTarget = target as unknown as GGTestSelector<T>;
106
-
107
- // Handle numeric index access: selector[0]
108
- if (typeof prop === 'string' && /^\d+$/.test(prop)) {
109
- const index = parseInt(prop, 10);
110
- if (index >= 0 && index < implTarget.runtimes.length) {
111
- return createSelector([implTarget.runtimes[index]]);
112
- }
113
- return undefined;
114
- }
115
-
116
- // Handle extension access: selector.config, selector.logs, etc.
117
- if (typeof prop === 'string' && GGTestSelector.hasExtension(prop)) {
118
- return implTarget.getExtension(prop as keyof SelectorExtensions<T>);
119
- }
120
-
121
- // Handle methods and properties from impl
122
- return Reflect.get(implTarget, prop);
123
- }
124
- }) as Selector<T>;
125
- }
126
-
127
- // ============================================================================
128
- // Factory functions for different input shapes
129
- // ============================================================================
130
-
131
- /**
132
- * Create result based on input shape.
133
- * - Single runtime → Selector
134
- * - Array of runtimes → Selector
135
- * - Object → ObjectResult with named selectors
136
- */
137
- export function createStartResult<T extends RuntimeInput>(
138
- input: T,
139
- runtimes: GGTestRuntime[]
140
- ): StartResult<T> {
141
- // Object input: { main: MainRuntime, sub: SubRuntime }
142
- if (isObjectInput(input)) {
143
- return createObjectResult(input, runtimes) as StartResult<T>;
144
- }
145
-
146
- // Array or single runtime → Selector
147
- return createSelector(runtimes) as StartResult<T>;
148
- }
149
-
150
- /**
151
- * Check if input is an object (not a constructor or array).
152
- */
153
- function isObjectInput(input: RuntimeInput): input is Record<string, RuntimeConstructor | RuntimeConstructor[]> {
154
- return typeof input === 'object' && !Array.isArray(input) && !('NAME' in input);
155
- }
156
-
157
- /**
158
- * Create ObjectResult for object input.
159
- */
160
- function createObjectResult<T extends Record<string, RuntimeConstructor | RuntimeConstructor[]>>(
161
- input: T,
162
- runtimes: GGTestRuntime[]
163
- ): ObjectResult<T> {
164
- const result: Record<string, Selector<any>> = {};
165
-
166
- // Group runtimes by the key they were registered under
167
- // We need to track which runtimes belong to which key
168
- for (const [key, value] of Object.entries(input)) {
169
- const constructors = Array.isArray(value) ? value : [value];
170
- const names = constructors.map(c => c.NAME);
171
- const keyRuntimes = runtimes.filter(r => names.includes(r.name));
172
- result[key] = createSelector(keyRuntimes);
173
- }
174
-
175
- // Add stop/shutdown that affects all runtimes
176
- (result as any).stop = async () => {
177
- for (const runtime of runtimes) {
178
- await runtime.stop();
179
- }
180
- };
181
-
182
- (result as any).shutdown = async () => {
183
- for (const runtime of runtimes) {
184
- await runtime.shutdown();
185
- }
186
- };
187
-
188
- return result as ObjectResult<T>;
189
- }
190
-
191
- // ============================================================================
192
- // Legacy factory (for backwards compatibility)
193
- // ============================================================================
194
-
195
- /**
196
- * @deprecated Use createStartResult instead.
197
- * Create a RuntimeResult for accessing runtimes via get() method.
198
- */
199
- export function createRuntimeSelector<T extends RuntimeConstructor[]>(runtimes: GGTestRuntime[]): RuntimeResult<T> {
200
- return {
201
- get(runtimeConstructor: RuntimeConstructor): Selector<any> {
202
- const name = runtimeConstructor.NAME;
203
- const filtered = runtimes.filter(r => r.name === name);
204
- return createSelector(filtered);
205
- },
206
-
207
- all(): Selector<any> {
208
- return createSelector(runtimes);
209
- },
210
-
211
- async stop(): Promise<void> {
212
- for (const runtime of runtimes) {
213
- await runtime.stop();
214
- }
215
- },
216
-
217
- async shutdown(): Promise<void> {
218
- for (const runtime of runtimes) {
219
- await runtime.shutdown();
220
- }
221
- }
222
- } as RuntimeResult<T>;
223
- }
1
+ import {RuntimeConstructor, RuntimeResult, Selector, SelectorExtensions, ObjectResult, StartResult, RuntimeInput} from "./RuntimeSelector";
2
+ import {GGTestRuntime} from "../GGTestRuntime";
3
+ import type {GGTestRunner} from "../GGTestRunner";
4
+
5
+ /**
6
+ * Internal implementation class for Selector.
7
+ * Works with GGTestRuntime instances directly.
8
+ */
9
+ export class GGTestSelector<T extends RuntimeConstructor[]> {
10
+
11
+ private static readonly extensions = new Map<string, typeof GGTestSelectorExtension>();
12
+
13
+ public readonly runtimes: GGTestRuntime[];
14
+ private readonly extensionCache = new Map<string, unknown>();
15
+
16
+ constructor(runtimes: GGTestRuntime[]) {
17
+ this.runtimes = runtimes;
18
+ }
19
+
20
+ public get length(): number {
21
+ return this.runtimes.length;
22
+ }
23
+
24
+ public static addExtension(factory: typeof GGTestSelectorExtension & { PROPERTY_NAME: string }): void {
25
+ if (this.extensions.has(factory.PROPERTY_NAME)) {
26
+ throw new Error("Extension with name '" + factory.PROPERTY_NAME + "' is already registered!")
27
+ }
28
+ this.extensions.set(factory.PROPERTY_NAME, factory);
29
+ }
30
+
31
+ public static hasExtension(name: string): boolean {
32
+ return this.extensions.has(name);
33
+ }
34
+
35
+ public getExtension<K extends keyof SelectorExtensions<T>>(name: K): SelectorExtensions<T>[K] {
36
+ const cached = this.extensionCache.get(name as string);
37
+ if (cached !== undefined) {
38
+ return cached as SelectorExtensions<T>[K];
39
+ }
40
+
41
+ const extension = GGTestSelector.extensions.get(name);
42
+ if (!extension) {
43
+ throw new Error(`Extension '${String(name)}' is not registered. ` +
44
+ `Make sure the module providing this extension is imported.`);
45
+ }
46
+
47
+ const extensionInstance = new extension(this.runtimes);
48
+ this.extensionCache.set(name as string, extensionInstance);
49
+ return extensionInstance as SelectorExtensions<T>[K];
50
+ }
51
+
52
+ /**
53
+ * Stop all runtimes in this selector.
54
+ */
55
+ public async stop(): Promise<void> {
56
+ for (const runtime of this.runtimes) {
57
+ await runtime.stop();
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Shutdown all runtimes in this selector.
63
+ */
64
+ public async shutdown(): Promise<void> {
65
+ for (const runtime of this.runtimes) {
66
+ await runtime.shutdown();
67
+ }
68
+ }
69
+ }
70
+
71
+ export class GGTestSelectorExtension {
72
+
73
+ protected readonly runner: GGTestRunner
74
+
75
+ constructor(protected readonly runtimes: GGTestRuntime[]) {
76
+ this.runner = this.runtimes[0]?.runner
77
+ }
78
+
79
+ protected async forEachParallel(callback: (runtime: GGTestRuntime) => Promise<void>): Promise<void> {
80
+ const results = await Promise.allSettled(
81
+ this.runtimes.map(runtime => callback(runtime))
82
+ );
83
+ const errors = results
84
+ .filter((r): r is PromiseRejectedResult => r.status === 'rejected')
85
+ .map(r => r.reason);
86
+
87
+ if (errors.length > 0) {
88
+ throw new AggregateError(errors, `${errors.length} runtime(s) failed`);
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Create a proxied Selector that supports:
95
+ * - Indexed access: selector[0]
96
+ * - Extension access: selector.config, selector.logs
97
+ * - Lifecycle methods: selector.stop(), selector.shutdown()
98
+ * - Standard properties: selector.runtimes, selector.length
99
+ */
100
+ export function createSelector<T extends RuntimeConstructor[]>(runtimes: GGTestRuntime[]): Selector<T> {
101
+ const impl = new GGTestSelector<T>(runtimes);
102
+
103
+ return new Proxy(impl as unknown as Selector<T>, {
104
+ get(target, prop) {
105
+ const implTarget = target as unknown as GGTestSelector<T>;
106
+
107
+ // Handle numeric index access: selector[0]
108
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
109
+ const index = parseInt(prop, 10);
110
+ if (index >= 0 && index < implTarget.runtimes.length) {
111
+ return createSelector([implTarget.runtimes[index]]);
112
+ }
113
+ return undefined;
114
+ }
115
+
116
+ // Handle extension access: selector.config, selector.logs, etc.
117
+ if (typeof prop === 'string' && GGTestSelector.hasExtension(prop)) {
118
+ return implTarget.getExtension(prop as keyof SelectorExtensions<T>);
119
+ }
120
+
121
+ // Handle methods and properties from impl
122
+ return Reflect.get(implTarget, prop);
123
+ }
124
+ }) as Selector<T>;
125
+ }
126
+
127
+ // ============================================================================
128
+ // Factory functions for different input shapes
129
+ // ============================================================================
130
+
131
+ /**
132
+ * Create result based on input shape.
133
+ * - Single runtime → Selector
134
+ * - Array of runtimes → Selector
135
+ * - Object → ObjectResult with named selectors
136
+ */
137
+ export function createStartResult<T extends RuntimeInput>(
138
+ input: T,
139
+ runtimes: GGTestRuntime[]
140
+ ): StartResult<T> {
141
+ // Object input: { main: MainRuntime, sub: SubRuntime }
142
+ if (isObjectInput(input)) {
143
+ return createObjectResult(input, runtimes) as StartResult<T>;
144
+ }
145
+
146
+ // Array or single runtime → Selector
147
+ return createSelector(runtimes) as StartResult<T>;
148
+ }
149
+
150
+ /**
151
+ * Check if input is an object (not a constructor or array).
152
+ */
153
+ function isObjectInput(input: RuntimeInput): input is Record<string, RuntimeConstructor | RuntimeConstructor[]> {
154
+ return typeof input === 'object' && !Array.isArray(input) && !('NAME' in input);
155
+ }
156
+
157
+ /**
158
+ * Create ObjectResult for object input.
159
+ */
160
+ function createObjectResult<T extends Record<string, RuntimeConstructor | RuntimeConstructor[]>>(
161
+ input: T,
162
+ runtimes: GGTestRuntime[]
163
+ ): ObjectResult<T> {
164
+ const result: Record<string, Selector<any>> = {};
165
+
166
+ // Group runtimes by the key they were registered under
167
+ // We need to track which runtimes belong to which key
168
+ for (const [key, value] of Object.entries(input)) {
169
+ const constructors = Array.isArray(value) ? value : [value];
170
+ const names = constructors.map(c => c.NAME);
171
+ const keyRuntimes = runtimes.filter(r => names.includes(r.name));
172
+ result[key] = createSelector(keyRuntimes);
173
+ }
174
+
175
+ // Add stop/shutdown that affects all runtimes
176
+ (result as any).stop = async () => {
177
+ for (const runtime of runtimes) {
178
+ await runtime.stop();
179
+ }
180
+ };
181
+
182
+ (result as any).shutdown = async () => {
183
+ for (const runtime of runtimes) {
184
+ await runtime.shutdown();
185
+ }
186
+ };
187
+
188
+ return result as ObjectResult<T>;
189
+ }
190
+
191
+ // ============================================================================
192
+ // Legacy factory (for backwards compatibility)
193
+ // ============================================================================
194
+
195
+ /**
196
+ * @deprecated Use createStartResult instead.
197
+ * Create a RuntimeResult for accessing runtimes via get() method.
198
+ */
199
+ export function createRuntimeSelector<T extends RuntimeConstructor[]>(runtimes: GGTestRuntime[]): RuntimeResult<T> {
200
+ return {
201
+ get(runtimeConstructor: RuntimeConstructor): Selector<any> {
202
+ const name = runtimeConstructor.NAME;
203
+ const filtered = runtimes.filter(r => r.name === name);
204
+ return createSelector(filtered);
205
+ },
206
+
207
+ all(): Selector<any> {
208
+ return createSelector(runtimes);
209
+ },
210
+
211
+ async stop(): Promise<void> {
212
+ for (const runtime of runtimes) {
213
+ await runtime.stop();
214
+ }
215
+ },
216
+
217
+ async shutdown(): Promise<void> {
218
+ for (const runtime of runtimes) {
219
+ await runtime.shutdown();
220
+ }
221
+ }
222
+ } as RuntimeResult<T>;
223
+ }
@@ -1,11 +1,11 @@
1
- export interface IGGTestInterceptor {
2
- register(): void | Promise<void>;
3
-
4
- unregister(): void | Promise<void>;
5
-
6
- validate(): void | Promise<void>;
7
-
8
- getMockValidationError(): Error | undefined;
9
-
10
- isCalled(): boolean;
1
+ export interface IGGTestInterceptor {
2
+ register(): void | Promise<void>;
3
+
4
+ unregister(): void | Promise<void>;
5
+
6
+ validate(): void | Promise<void>;
7
+
8
+ getMockValidationError(): Error | undefined;
9
+
10
+ isCalled(): boolean;
11
11
  }
@@ -1,15 +1,15 @@
1
- import {IGGTestInterceptor} from "./IGGTestInterceptor";
2
-
3
- export interface IGGTestWith {
4
- createInterceptor(): IGGTestInterceptor;
5
-
6
- /**
7
- * Returns true if this expectation requires async processing and must be used with `.waitFor()`.
8
- * When true, using with `.with()` will throw an error guiding the developer to use `.waitFor()`.
9
- *
10
- * Example: SQS message processing is async (happens after HTTP response), so SQS
11
- * interceptors require `.waitFor()`.
12
- */
13
- requiresWaitFor?(): boolean;
14
- }
15
-
1
+ import {IGGTestInterceptor} from "./IGGTestInterceptor";
2
+
3
+ export interface IGGTestWith {
4
+ createInterceptor(): IGGTestInterceptor;
5
+
6
+ /**
7
+ * Returns true if this expectation requires async processing and must be used with `.waitFor()`.
8
+ * When true, using with `.with()` will throw an error guiding the developer to use `.waitFor()`.
9
+ *
10
+ * Example: SQS message processing is async (happens after HTTP response), so SQS
11
+ * interceptors require `.waitFor()`.
12
+ */
13
+ requiresWaitFor?(): boolean;
14
+ }
15
+