@grest-ts/testkit 0.0.5 → 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.
- package/LICENSE +21 -21
- package/README.md +418 -413
- package/dist/src/runner/isolated-loader.mjs +91 -91
- package/dist/src/runner/worker-loader.mjs +49 -49
- package/dist/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/src/GGBundleTest.ts +89 -89
- package/src/GGTest.ts +318 -318
- package/src/GGTestContext.ts +74 -74
- package/src/GGTestRunner.ts +308 -308
- package/src/GGTestRuntime.ts +265 -265
- package/src/GGTestRuntimeWorker.ts +159 -159
- package/src/GGTestSharedRef.ts +116 -116
- package/src/GGTestkitExtensionsDiscovery.ts +26 -26
- package/src/IGGLocalDiscoveryServer.ts +16 -16
- package/src/callOn/GGCallOnSelector.ts +61 -61
- package/src/callOn/GGContractClass.implement.ts +43 -43
- package/src/callOn/GGTestActionForLocatorOnCall.ts +134 -134
- package/src/callOn/TestableIPC.ts +81 -81
- package/src/callOn/callOn.ts +224 -224
- package/src/callOn/registerOnCallHandler.ts +123 -123
- package/src/index-node.ts +64 -64
- package/src/mockable/GGMockable.ts +22 -22
- package/src/mockable/GGMockableCall.ts +45 -45
- package/src/mockable/GGMockableIPC.ts +20 -20
- package/src/mockable/GGMockableInterceptor.ts +44 -44
- package/src/mockable/GGMockableInterceptorsServer.ts +69 -69
- package/src/mockable/mockable.ts +71 -71
- package/src/runner/InlineRunner.ts +47 -47
- package/src/runner/IsolatedRunner.ts +179 -179
- package/src/runner/RuntimeRunner.ts +15 -15
- package/src/runner/WorkerRunner.ts +179 -179
- package/src/runner/isolated-loader.mjs +91 -91
- package/src/runner/worker-loader.mjs +49 -49
- package/src/testers/GGCallInterceptor.ts +224 -224
- package/src/testers/GGMockWith.ts +92 -92
- package/src/testers/GGSpyWith.ts +115 -115
- package/src/testers/GGTestAction.ts +332 -332
- package/src/testers/GGTestComponent.ts +16 -16
- package/src/testers/GGTestSelector.ts +223 -223
- package/src/testers/IGGTestInterceptor.ts +10 -10
- package/src/testers/IGGTestWith.ts +15 -15
- package/src/testers/RuntimeSelector.ts +151 -151
- package/src/utils/GGExpectations.ts +78 -78
- package/src/utils/GGTestError.ts +36 -36
- package/src/utils/captureStack.ts +53 -53
|
@@ -1,151 +1,151 @@
|
|
|
1
|
-
import type {GGTestRuntime} from "../GGTestRuntime";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Constructor type for a runtime class that has a static NAME property.
|
|
5
|
-
* This enforces that all runtimes used with the selector system define NAME.
|
|
6
|
-
*
|
|
7
|
-
* Note: We use `unknown` instead of `GGRuntime` to avoid circular dependency
|
|
8
|
-
* between @grest-ts/testkit and @grest-ts/runtime.
|
|
9
|
-
*/
|
|
10
|
-
export interface RuntimeConstructor<N extends string = string> {
|
|
11
|
-
new(): unknown;
|
|
12
|
-
|
|
13
|
-
/** Name of the runtime - must be unique across all runtimes */
|
|
14
|
-
readonly NAME: N;
|
|
15
|
-
/** Source path set by GGRuntime.cli() - used for worker/isolated modes */
|
|
16
|
-
readonly SOURCE_MODULE_URL?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Extension interface for selector capabilities.
|
|
21
|
-
* Modules augment this interface to add their accessors (config, logs, metrics, etc.)
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* // In @grest-ts/config module:
|
|
25
|
-
* declare module '@grest-ts/testkit' {
|
|
26
|
-
* interface SelectorExtensions<T extends RuntimeConstructor[]> {
|
|
27
|
-
* config: GGTestConfigAccessor<T>;
|
|
28
|
-
* }
|
|
29
|
-
* }
|
|
30
|
-
*/
|
|
31
|
-
export interface SelectorExtensions<T extends RuntimeConstructor[]> {
|
|
32
|
-
// Base interface - modules add properties via declaration merging
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Selector for a group of runtime instances.
|
|
37
|
-
* Provides access to selected runtimes and their extension accessors.
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* const t = GGTest.startWorker(MainRuntime);
|
|
41
|
-
* t.logs.cursor();
|
|
42
|
-
* t[0].logs.cursor();
|
|
43
|
-
* await t.stop();
|
|
44
|
-
*/
|
|
45
|
-
export interface Selector<T extends RuntimeConstructor[]> extends SelectorExtensions<T> {
|
|
46
|
-
/**
|
|
47
|
-
* Get the underlying runtime instances
|
|
48
|
-
*/
|
|
49
|
-
readonly runtimes: GGTestRuntime[];
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Access individual instance by index
|
|
53
|
-
*/
|
|
54
|
-
[index: number]: Selector<[T[number]]>;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Number of selected instances
|
|
58
|
-
*/
|
|
59
|
-
readonly length: number;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Stop runtimes (teardown services but keep IPC alive for log retrieval).
|
|
63
|
-
* After stop(), you can still call commands like log retrieval.
|
|
64
|
-
*/
|
|
65
|
-
stop(): Promise<void>;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Fully shutdown runtimes (terminate workers/processes).
|
|
69
|
-
* After shutdown(), no commands can be sent.
|
|
70
|
-
*/
|
|
71
|
-
shutdown(): Promise<void>;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ============================================================================
|
|
75
|
-
// Input types for startWorker/startInline/startIsolated
|
|
76
|
-
// ============================================================================
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Input can be a single runtime, array of runtimes, or object mapping names to runtimes.
|
|
80
|
-
*/
|
|
81
|
-
export type RuntimeInput =
|
|
82
|
-
| RuntimeConstructor
|
|
83
|
-
| RuntimeConstructor[]
|
|
84
|
-
| Record<string, RuntimeConstructor | RuntimeConstructor[]>;
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Normalize a single runtime or array to always be an array.
|
|
88
|
-
*/
|
|
89
|
-
type NormalizeToArray<T> = T extends RuntimeConstructor[] ? T : [T];
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Result type for object input: each key maps to a Selector.
|
|
93
|
-
*/
|
|
94
|
-
export type ObjectResult<T extends Record<string, RuntimeConstructor | RuntimeConstructor[]>> = {
|
|
95
|
-
[K in keyof T]: Selector<NormalizeToArray<T[K]>>;
|
|
96
|
-
} & {
|
|
97
|
-
/**
|
|
98
|
-
* Stop all runtimes in this group.
|
|
99
|
-
*/
|
|
100
|
-
stop(): Promise<void>;
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Shutdown all runtimes in this group.
|
|
104
|
-
*/
|
|
105
|
-
shutdown(): Promise<void>;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Result type based on input shape:
|
|
110
|
-
* - Single runtime → Selector
|
|
111
|
-
* - Array of runtimes → Selector
|
|
112
|
-
* - Object → ObjectResult with named selectors
|
|
113
|
-
*/
|
|
114
|
-
export type StartResult<T extends RuntimeInput> =
|
|
115
|
-
T extends Record<string, RuntimeConstructor | RuntimeConstructor[]>
|
|
116
|
-
? ObjectResult<T>
|
|
117
|
-
: T extends RuntimeConstructor[]
|
|
118
|
-
? Selector<T>
|
|
119
|
-
: T extends RuntimeConstructor
|
|
120
|
-
? Selector<[T]>
|
|
121
|
-
: never;
|
|
122
|
-
|
|
123
|
-
// ============================================================================
|
|
124
|
-
// Legacy RuntimeResult (for backwards compatibility with .get())
|
|
125
|
-
// ============================================================================
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* @deprecated Use the new startWorker overloads instead.
|
|
129
|
-
* Legacy result type that provides .get() method.
|
|
130
|
-
*/
|
|
131
|
-
export interface RuntimeResult<T extends RuntimeConstructor[]> {
|
|
132
|
-
/**
|
|
133
|
-
* Get selector for a specific runtime type
|
|
134
|
-
*/
|
|
135
|
-
get<R extends T[number]>(runtime: R): Selector<[R]>;
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Get selector for all runtimes
|
|
139
|
-
*/
|
|
140
|
-
all(): Selector<T>;
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Stop all runtimes.
|
|
144
|
-
*/
|
|
145
|
-
stop(): Promise<void>;
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Shutdown all runtimes.
|
|
149
|
-
*/
|
|
150
|
-
shutdown(): Promise<void>;
|
|
151
|
-
}
|
|
1
|
+
import type {GGTestRuntime} from "../GGTestRuntime";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Constructor type for a runtime class that has a static NAME property.
|
|
5
|
+
* This enforces that all runtimes used with the selector system define NAME.
|
|
6
|
+
*
|
|
7
|
+
* Note: We use `unknown` instead of `GGRuntime` to avoid circular dependency
|
|
8
|
+
* between @grest-ts/testkit and @grest-ts/runtime.
|
|
9
|
+
*/
|
|
10
|
+
export interface RuntimeConstructor<N extends string = string> {
|
|
11
|
+
new(): unknown;
|
|
12
|
+
|
|
13
|
+
/** Name of the runtime - must be unique across all runtimes */
|
|
14
|
+
readonly NAME: N;
|
|
15
|
+
/** Source path set by GGRuntime.cli() - used for worker/isolated modes */
|
|
16
|
+
readonly SOURCE_MODULE_URL?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Extension interface for selector capabilities.
|
|
21
|
+
* Modules augment this interface to add their accessors (config, logs, metrics, etc.)
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // In @grest-ts/config module:
|
|
25
|
+
* declare module '@grest-ts/testkit' {
|
|
26
|
+
* interface SelectorExtensions<T extends RuntimeConstructor[]> {
|
|
27
|
+
* config: GGTestConfigAccessor<T>;
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
export interface SelectorExtensions<T extends RuntimeConstructor[]> {
|
|
32
|
+
// Base interface - modules add properties via declaration merging
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Selector for a group of runtime instances.
|
|
37
|
+
* Provides access to selected runtimes and their extension accessors.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* const t = GGTest.startWorker(MainRuntime);
|
|
41
|
+
* t.logs.cursor();
|
|
42
|
+
* t[0].logs.cursor();
|
|
43
|
+
* await t.stop();
|
|
44
|
+
*/
|
|
45
|
+
export interface Selector<T extends RuntimeConstructor[]> extends SelectorExtensions<T> {
|
|
46
|
+
/**
|
|
47
|
+
* Get the underlying runtime instances
|
|
48
|
+
*/
|
|
49
|
+
readonly runtimes: GGTestRuntime[];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Access individual instance by index
|
|
53
|
+
*/
|
|
54
|
+
[index: number]: Selector<[T[number]]>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Number of selected instances
|
|
58
|
+
*/
|
|
59
|
+
readonly length: number;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Stop runtimes (teardown services but keep IPC alive for log retrieval).
|
|
63
|
+
* After stop(), you can still call commands like log retrieval.
|
|
64
|
+
*/
|
|
65
|
+
stop(): Promise<void>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Fully shutdown runtimes (terminate workers/processes).
|
|
69
|
+
* After shutdown(), no commands can be sent.
|
|
70
|
+
*/
|
|
71
|
+
shutdown(): Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// Input types for startWorker/startInline/startIsolated
|
|
76
|
+
// ============================================================================
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Input can be a single runtime, array of runtimes, or object mapping names to runtimes.
|
|
80
|
+
*/
|
|
81
|
+
export type RuntimeInput =
|
|
82
|
+
| RuntimeConstructor
|
|
83
|
+
| RuntimeConstructor[]
|
|
84
|
+
| Record<string, RuntimeConstructor | RuntimeConstructor[]>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Normalize a single runtime or array to always be an array.
|
|
88
|
+
*/
|
|
89
|
+
type NormalizeToArray<T> = T extends RuntimeConstructor[] ? T : [T];
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Result type for object input: each key maps to a Selector.
|
|
93
|
+
*/
|
|
94
|
+
export type ObjectResult<T extends Record<string, RuntimeConstructor | RuntimeConstructor[]>> = {
|
|
95
|
+
[K in keyof T]: Selector<NormalizeToArray<T[K]>>;
|
|
96
|
+
} & {
|
|
97
|
+
/**
|
|
98
|
+
* Stop all runtimes in this group.
|
|
99
|
+
*/
|
|
100
|
+
stop(): Promise<void>;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Shutdown all runtimes in this group.
|
|
104
|
+
*/
|
|
105
|
+
shutdown(): Promise<void>;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Result type based on input shape:
|
|
110
|
+
* - Single runtime → Selector
|
|
111
|
+
* - Array of runtimes → Selector
|
|
112
|
+
* - Object → ObjectResult with named selectors
|
|
113
|
+
*/
|
|
114
|
+
export type StartResult<T extends RuntimeInput> =
|
|
115
|
+
T extends Record<string, RuntimeConstructor | RuntimeConstructor[]>
|
|
116
|
+
? ObjectResult<T>
|
|
117
|
+
: T extends RuntimeConstructor[]
|
|
118
|
+
? Selector<T>
|
|
119
|
+
: T extends RuntimeConstructor
|
|
120
|
+
? Selector<[T]>
|
|
121
|
+
: never;
|
|
122
|
+
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// Legacy RuntimeResult (for backwards compatibility with .get())
|
|
125
|
+
// ============================================================================
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @deprecated Use the new startWorker overloads instead.
|
|
129
|
+
* Legacy result type that provides .get() method.
|
|
130
|
+
*/
|
|
131
|
+
export interface RuntimeResult<T extends RuntimeConstructor[]> {
|
|
132
|
+
/**
|
|
133
|
+
* Get selector for a specific runtime type
|
|
134
|
+
*/
|
|
135
|
+
get<R extends T[number]>(runtime: R): Selector<[R]>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get selector for all runtimes
|
|
139
|
+
*/
|
|
140
|
+
all(): Selector<T>;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Stop all runtimes.
|
|
144
|
+
*/
|
|
145
|
+
stop(): Promise<void>;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Shutdown all runtimes.
|
|
149
|
+
*/
|
|
150
|
+
shutdown(): Promise<void>;
|
|
151
|
+
}
|
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
import {Raw} from "@grest-ts/schema";
|
|
2
|
-
import {updateErrorStack} from "./captureStack";
|
|
3
|
-
|
|
4
|
-
interface Expectation<Data> {
|
|
5
|
-
err: Error;
|
|
6
|
-
execute: (input: Data) => void;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Collection of expectations to validate data.
|
|
11
|
-
* Pure validation utility - does not know about HTTP response structure.
|
|
12
|
-
*/
|
|
13
|
-
export class GGExpectations<Data> {
|
|
14
|
-
|
|
15
|
-
private readonly expectations: Expectation<Data>[] = []
|
|
16
|
-
|
|
17
|
-
public check(data: Data) {
|
|
18
|
-
this.expectations.forEach(expectation => {
|
|
19
|
-
try {
|
|
20
|
-
expectation.execute(data)
|
|
21
|
-
} catch (e: any) {
|
|
22
|
-
throw updateErrorStack(e, expectation.err)
|
|
23
|
-
}
|
|
24
|
-
})
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public flush() {
|
|
28
|
-
this.expectations.length = 0;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
private add(execute: (input: Data) => void): void {
|
|
32
|
-
this.expectations.push({
|
|
33
|
-
err: new Error(),
|
|
34
|
-
execute: execute
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public toEqual(expectedData: Data): void {
|
|
39
|
-
return this.add((input) => expect(input).toEqual(expectedData))
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
public toMatchObject(expectedData: Data): void {
|
|
43
|
-
return this.add((input) => expect(input).toMatchObject(expectedData as any))
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public toBeUndefined(): void {
|
|
47
|
-
return this.add((input) => expect(input).toBeUndefined())
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public toHaveLength(length: number) {
|
|
51
|
-
return this.add((input: any) => {
|
|
52
|
-
if (input?.length === undefined) {
|
|
53
|
-
throw new Error("expect.length is not defined on: " + JSON.stringify(input, null, 2));
|
|
54
|
-
}
|
|
55
|
-
expect(input.length).toEqual(length)
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public arrayToContain<Item extends Data extends Array<infer R> ? R : never>(...items: Partial<Raw<Item>>[]) {
|
|
60
|
-
return this.add((input: any) => {
|
|
61
|
-
expect(Array.isArray(input)).toBe(true)
|
|
62
|
-
expect(input).toEqual(
|
|
63
|
-
expect.arrayContaining(
|
|
64
|
-
items.map(item => expect.objectContaining(item as any))
|
|
65
|
-
)
|
|
66
|
-
);
|
|
67
|
-
})
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public arrayToContainEqual<Item extends Data extends Array<infer R> ? R : never>(...items: Partial<Raw<Item>>[]) {
|
|
71
|
-
return this.add((input: any) => {
|
|
72
|
-
expect(Array.isArray(input)).toBe(true)
|
|
73
|
-
for (const item of items) {
|
|
74
|
-
expect(input).toContainEqual(item);
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
}
|
|
78
|
-
}
|
|
1
|
+
import {Raw} from "@grest-ts/schema";
|
|
2
|
+
import {updateErrorStack} from "./captureStack";
|
|
3
|
+
|
|
4
|
+
interface Expectation<Data> {
|
|
5
|
+
err: Error;
|
|
6
|
+
execute: (input: Data) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Collection of expectations to validate data.
|
|
11
|
+
* Pure validation utility - does not know about HTTP response structure.
|
|
12
|
+
*/
|
|
13
|
+
export class GGExpectations<Data> {
|
|
14
|
+
|
|
15
|
+
private readonly expectations: Expectation<Data>[] = []
|
|
16
|
+
|
|
17
|
+
public check(data: Data) {
|
|
18
|
+
this.expectations.forEach(expectation => {
|
|
19
|
+
try {
|
|
20
|
+
expectation.execute(data)
|
|
21
|
+
} catch (e: any) {
|
|
22
|
+
throw updateErrorStack(e, expectation.err)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public flush() {
|
|
28
|
+
this.expectations.length = 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private add(execute: (input: Data) => void): void {
|
|
32
|
+
this.expectations.push({
|
|
33
|
+
err: new Error(),
|
|
34
|
+
execute: execute
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public toEqual(expectedData: Data): void {
|
|
39
|
+
return this.add((input) => expect(input).toEqual(expectedData))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public toMatchObject(expectedData: Data): void {
|
|
43
|
+
return this.add((input) => expect(input).toMatchObject(expectedData as any))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public toBeUndefined(): void {
|
|
47
|
+
return this.add((input) => expect(input).toBeUndefined())
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public toHaveLength(length: number) {
|
|
51
|
+
return this.add((input: any) => {
|
|
52
|
+
if (input?.length === undefined) {
|
|
53
|
+
throw new Error("expect.length is not defined on: " + JSON.stringify(input, null, 2));
|
|
54
|
+
}
|
|
55
|
+
expect(input.length).toEqual(length)
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public arrayToContain<Item extends Data extends Array<infer R> ? R : never>(...items: Partial<Raw<Item>>[]) {
|
|
60
|
+
return this.add((input: any) => {
|
|
61
|
+
expect(Array.isArray(input)).toBe(true)
|
|
62
|
+
expect(input).toEqual(
|
|
63
|
+
expect.arrayContaining(
|
|
64
|
+
items.map(item => expect.objectContaining(item as any))
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public arrayToContainEqual<Item extends Data extends Array<infer R> ? R : never>(...items: Partial<Raw<Item>>[]) {
|
|
71
|
+
return this.add((input: any) => {
|
|
72
|
+
expect(Array.isArray(input)).toBe(true)
|
|
73
|
+
for (const item of items) {
|
|
74
|
+
expect(input).toContainEqual(item);
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
package/src/utils/GGTestError.ts
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import {LOG_COLORS} from "@grest-ts/logger-console";
|
|
2
|
-
|
|
3
|
-
export class GGTestError extends Error {
|
|
4
|
-
|
|
5
|
-
constructor(data: CreateErrorInput) {
|
|
6
|
-
|
|
7
|
-
const tab = (n: number, str: string) => {
|
|
8
|
-
return str.split("\n").join("\n" + ("\t".repeat(n)));
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const fixValue = (value: any) => {
|
|
12
|
-
if (value instanceof Error) {
|
|
13
|
-
return value.stack
|
|
14
|
-
} else if (typeof value === "object") {
|
|
15
|
-
return JSON.stringify(value, null, 2)
|
|
16
|
-
} else {
|
|
17
|
-
return String(value);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
super("Error: " + (data.context ? data.context + " " : "") + data.test + "\n" +
|
|
22
|
-
"\nExpected: " + LOG_COLORS.green + tab(1, fixValue(data.expected)) + LOG_COLORS.reset +
|
|
23
|
-
"\nReceived: " + tab(1, fixValue(data.received)) +
|
|
24
|
-
(data.extra ? "\n\t" + tab(1, data.extra) : "") +
|
|
25
|
-
(data.sourceFile ? "\n\t" + tab(1, data.sourceFile) : "") +
|
|
26
|
-
"\n");
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface CreateErrorInput {
|
|
31
|
-
context?: string;
|
|
32
|
-
test: string,
|
|
33
|
-
expected: any,
|
|
34
|
-
received: any,
|
|
35
|
-
extra?: any
|
|
36
|
-
sourceFile?: string;
|
|
1
|
+
import {LOG_COLORS} from "@grest-ts/logger-console";
|
|
2
|
+
|
|
3
|
+
export class GGTestError extends Error {
|
|
4
|
+
|
|
5
|
+
constructor(data: CreateErrorInput) {
|
|
6
|
+
|
|
7
|
+
const tab = (n: number, str: string) => {
|
|
8
|
+
return str.split("\n").join("\n" + ("\t".repeat(n)));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const fixValue = (value: any) => {
|
|
12
|
+
if (value instanceof Error) {
|
|
13
|
+
return value.stack
|
|
14
|
+
} else if (typeof value === "object") {
|
|
15
|
+
return JSON.stringify(value, null, 2)
|
|
16
|
+
} else {
|
|
17
|
+
return String(value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
super("Error: " + (data.context ? data.context + " " : "") + data.test + "\n" +
|
|
22
|
+
"\nExpected: " + LOG_COLORS.green + tab(1, fixValue(data.expected)) + LOG_COLORS.reset +
|
|
23
|
+
"\nReceived: " + tab(1, fixValue(data.received)) +
|
|
24
|
+
(data.extra ? "\n\t" + tab(1, data.extra) : "") +
|
|
25
|
+
(data.sourceFile ? "\n\t" + tab(1, data.sourceFile) : "") +
|
|
26
|
+
"\n");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface CreateErrorInput {
|
|
31
|
+
context?: string;
|
|
32
|
+
test: string,
|
|
33
|
+
expected: any,
|
|
34
|
+
received: any,
|
|
35
|
+
extra?: any
|
|
36
|
+
sourceFile?: string;
|
|
37
37
|
}
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @returns path to the source test file
|
|
3
|
-
*/
|
|
4
|
-
export function captureStackSourceFile(): string {
|
|
5
|
-
return getLinesFromTestFile(new Error().stack.split("\n"))[0] ?? "[Source not found]";
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Switches source files part of the stack for err with actualSourceLocation, but keeping the original error message.
|
|
10
|
-
*/
|
|
11
|
-
export function updateErrorStack(err: Error, actualSourceLocation?: Error) {
|
|
12
|
-
const lines = (err.stack ?? '').split("\n");
|
|
13
|
-
const errorLines = getErrorLinesOnly(lines);
|
|
14
|
-
const errLines = (actualSourceLocation?.stack ?? err.stack ?? '').split("\n");
|
|
15
|
-
const fileLines = getLinesFromTestFile(errLines);
|
|
16
|
-
err.stack = errorLines.join("\n") + "\n" + fileLines.join("\n");
|
|
17
|
-
return err;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Returns error message part of the stack lines. Skips all lines after first path is detected.
|
|
22
|
-
*/
|
|
23
|
-
function getErrorLinesOnly(lines: string[]): string [] {
|
|
24
|
-
const addedLines: string[] = [];
|
|
25
|
-
for (let i = 0; i < lines.length; i++) {
|
|
26
|
-
const line = lines[i];
|
|
27
|
-
if (line.startsWith(" at ")) {
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
addedLines.push(line);
|
|
31
|
-
}
|
|
32
|
-
return addedLines;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Returns source file path lines from the lines.
|
|
37
|
-
* Starts stack from the "test" source file and skips all framework paths.
|
|
38
|
-
* For example:
|
|
39
|
-
* ' at PATH_TO_SOURCE'
|
|
40
|
-
*/
|
|
41
|
-
function getLinesFromTestFile(lines: string[]): string[] {
|
|
42
|
-
const startIdx = lines.findIndex(line => {
|
|
43
|
-
return line.includes('.test.ts')
|
|
44
|
-
|| line.includes('.spec.ts')
|
|
45
|
-
});
|
|
46
|
-
const endIds = lines.findIndex(line => {
|
|
47
|
-
return line.includes('@vitest')
|
|
48
|
-
});
|
|
49
|
-
if (startIdx === -1) {
|
|
50
|
-
return [];
|
|
51
|
-
}
|
|
52
|
-
return lines.slice(startIdx, endIds);
|
|
53
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @returns path to the source test file
|
|
3
|
+
*/
|
|
4
|
+
export function captureStackSourceFile(): string {
|
|
5
|
+
return getLinesFromTestFile(new Error().stack.split("\n"))[0] ?? "[Source not found]";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Switches source files part of the stack for err with actualSourceLocation, but keeping the original error message.
|
|
10
|
+
*/
|
|
11
|
+
export function updateErrorStack(err: Error, actualSourceLocation?: Error) {
|
|
12
|
+
const lines = (err.stack ?? '').split("\n");
|
|
13
|
+
const errorLines = getErrorLinesOnly(lines);
|
|
14
|
+
const errLines = (actualSourceLocation?.stack ?? err.stack ?? '').split("\n");
|
|
15
|
+
const fileLines = getLinesFromTestFile(errLines);
|
|
16
|
+
err.stack = errorLines.join("\n") + "\n" + fileLines.join("\n");
|
|
17
|
+
return err;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns error message part of the stack lines. Skips all lines after first path is detected.
|
|
22
|
+
*/
|
|
23
|
+
function getErrorLinesOnly(lines: string[]): string [] {
|
|
24
|
+
const addedLines: string[] = [];
|
|
25
|
+
for (let i = 0; i < lines.length; i++) {
|
|
26
|
+
const line = lines[i];
|
|
27
|
+
if (line.startsWith(" at ")) {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
addedLines.push(line);
|
|
31
|
+
}
|
|
32
|
+
return addedLines;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns source file path lines from the lines.
|
|
37
|
+
* Starts stack from the "test" source file and skips all framework paths.
|
|
38
|
+
* For example:
|
|
39
|
+
* ' at PATH_TO_SOURCE'
|
|
40
|
+
*/
|
|
41
|
+
function getLinesFromTestFile(lines: string[]): string[] {
|
|
42
|
+
const startIdx = lines.findIndex(line => {
|
|
43
|
+
return line.includes('.test.ts')
|
|
44
|
+
|| line.includes('.spec.ts')
|
|
45
|
+
});
|
|
46
|
+
const endIds = lines.findIndex(line => {
|
|
47
|
+
return line.includes('@vitest')
|
|
48
|
+
});
|
|
49
|
+
if (startIdx === -1) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
return lines.slice(startIdx, endIds);
|
|
53
|
+
|
|
54
54
|
}
|