@ucdjs/test-utils 1.0.1-beta.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/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/file-tree-CColHWG6.mjs +4436 -0
- package/dist/fs-bridges/index.d.mts +84 -0
- package/dist/fs-bridges/index.mjs +303 -0
- package/dist/helpers-D3XLw9D1.d.mts +1050 -0
- package/dist/index.d.mts +76 -0
- package/dist/index.mjs +89 -0
- package/dist/matchers/types.mjs +149 -0
- package/dist/matchers/vitest-setup.d.mts +1 -0
- package/dist/matchers/vitest-setup.mjs +239 -0
- package/dist/mock-store.d.mts +21 -0
- package/dist/mock-store.mjs +210 -0
- package/dist/msw/vitest-setup.d.mts +1 -0
- package/dist/msw/vitest-setup.mjs +14 -0
- package/dist/msw.d.mts +25 -0
- package/dist/msw.mjs +30 -0
- package/dist/pipelines.d.mts +21 -0
- package/dist/pipelines.mjs +35 -0
- package/package.json +79 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { n as unsafeResponse, o as MockStoreConfig, t as configure } from "./helpers-D3XLw9D1.mjs";
|
|
2
|
+
import { mockStoreApi } from "./mock-store.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/async.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Collects all values from an async iterable into an array.
|
|
7
|
+
*
|
|
8
|
+
* This function consumes an async iterable and collects all emitted values into
|
|
9
|
+
* a regular array. If the async iterable throws an error at any point, the error
|
|
10
|
+
* is propagated after the iterator's `return()` method is called (if available)
|
|
11
|
+
* to allow for cleanup.
|
|
12
|
+
*
|
|
13
|
+
* @typeParam T - The type of values yielded by the async iterable.
|
|
14
|
+
* @param {AsyncIterable<T>} iterable - The async iterable to consume completely.
|
|
15
|
+
* @returns {Promise<T[]>} A promise that resolves to an array containing all values in emission order.
|
|
16
|
+
* @throws {Error} Propagates any error thrown by the async iterable.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const values = await collect(asyncFromArray([1, 2, 3]));
|
|
21
|
+
* console.assert(values.equals([1, 2, 3]));
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example Cleanup on error
|
|
25
|
+
* ```ts
|
|
26
|
+
* async function* source() {
|
|
27
|
+
* try {
|
|
28
|
+
* yield 1;
|
|
29
|
+
* throw new Error('boom');
|
|
30
|
+
* } finally {
|
|
31
|
+
* console.log('Cleanup!'); // Called even though an error was thrown
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* await collect(source()); // throws, but cleanup runs first
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function collect<T>(iterable: AsyncIterable<T>): Promise<T[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Wraps a synchronous iterable as an async iterable.
|
|
40
|
+
*
|
|
41
|
+
* This is useful in tests when you want to simulate an async source but only have
|
|
42
|
+
* synchronous data. The resulting async iterable properly implements the async
|
|
43
|
+
* iterator protocol, including support for cleanup via `return()`.
|
|
44
|
+
*
|
|
45
|
+
* @typeParam T - The type of elements in the iterable.
|
|
46
|
+
* @param {Iterable<T>} iterable - A synchronous iterable (array, Set, Map, etc.) to wrap.
|
|
47
|
+
* @param {object} [options] - Optional configuration.
|
|
48
|
+
* @param {number} [options.delay] - Number of milliseconds to wait before each value is yielded.
|
|
49
|
+
* Useful for simulating network latency or slow producers in tests.
|
|
50
|
+
* Must be a non-negative number.
|
|
51
|
+
* @returns {AsyncIterable<T>} An async iterable that yields each element from the source iterable.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* // Simple case: wrap an array
|
|
56
|
+
* for await (const value of asyncFromArray([1, 2, 3])) {
|
|
57
|
+
* console.log(value);
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* // Simulate network latency (50ms between values)
|
|
64
|
+
* for await (const value of asyncFromArray(['a', 'b', 'c'], { delay: 50 })) {
|
|
65
|
+
* console.log(value);
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
declare function asyncFromArray<T>(iterable: Iterable<T>, options?: {
|
|
70
|
+
readonly delay?: number;
|
|
71
|
+
}): AsyncIterable<T>;
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/index.d.ts
|
|
74
|
+
declare function encodeBase64(content: string): string;
|
|
75
|
+
//#endregion
|
|
76
|
+
export { type MockStoreConfig, asyncFromArray, collect, configure, encodeBase64, mockStoreApi, unsafeResponse };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { c as unsafeResponse, s as configure } from "./file-tree-CColHWG6.mjs";
|
|
2
|
+
import { mockStoreApi } from "./mock-store.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/async.ts
|
|
5
|
+
/**
|
|
6
|
+
* Collects all values from an async iterable into an array.
|
|
7
|
+
*
|
|
8
|
+
* This function consumes an async iterable and collects all emitted values into
|
|
9
|
+
* a regular array. If the async iterable throws an error at any point, the error
|
|
10
|
+
* is propagated after the iterator's `return()` method is called (if available)
|
|
11
|
+
* to allow for cleanup.
|
|
12
|
+
*
|
|
13
|
+
* @typeParam T - The type of values yielded by the async iterable.
|
|
14
|
+
* @param {AsyncIterable<T>} iterable - The async iterable to consume completely.
|
|
15
|
+
* @returns {Promise<T[]>} A promise that resolves to an array containing all values in emission order.
|
|
16
|
+
* @throws {Error} Propagates any error thrown by the async iterable.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const values = await collect(asyncFromArray([1, 2, 3]));
|
|
21
|
+
* console.assert(values.equals([1, 2, 3]));
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example Cleanup on error
|
|
25
|
+
* ```ts
|
|
26
|
+
* async function* source() {
|
|
27
|
+
* try {
|
|
28
|
+
* yield 1;
|
|
29
|
+
* throw new Error('boom');
|
|
30
|
+
* } finally {
|
|
31
|
+
* console.log('Cleanup!'); // Called even though an error was thrown
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* await collect(source()); // throws, but cleanup runs first
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
async function collect(iterable) {
|
|
38
|
+
const result = [];
|
|
39
|
+
for await (const value of iterable) result.push(value);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Wraps a synchronous iterable as an async iterable.
|
|
44
|
+
*
|
|
45
|
+
* This is useful in tests when you want to simulate an async source but only have
|
|
46
|
+
* synchronous data. The resulting async iterable properly implements the async
|
|
47
|
+
* iterator protocol, including support for cleanup via `return()`.
|
|
48
|
+
*
|
|
49
|
+
* @typeParam T - The type of elements in the iterable.
|
|
50
|
+
* @param {Iterable<T>} iterable - A synchronous iterable (array, Set, Map, etc.) to wrap.
|
|
51
|
+
* @param {object} [options] - Optional configuration.
|
|
52
|
+
* @param {number} [options.delay] - Number of milliseconds to wait before each value is yielded.
|
|
53
|
+
* Useful for simulating network latency or slow producers in tests.
|
|
54
|
+
* Must be a non-negative number.
|
|
55
|
+
* @returns {AsyncIterable<T>} An async iterable that yields each element from the source iterable.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* // Simple case: wrap an array
|
|
60
|
+
* for await (const value of asyncFromArray([1, 2, 3])) {
|
|
61
|
+
* console.log(value);
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* // Simulate network latency (50ms between values)
|
|
68
|
+
* for await (const value of asyncFromArray(['a', 'b', 'c'], { delay: 50 })) {
|
|
69
|
+
* console.log(value);
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
function asyncFromArray(iterable, options) {
|
|
74
|
+
return (async function* () {
|
|
75
|
+
for (const value of iterable) {
|
|
76
|
+
if (options?.delay) await new Promise((resolve) => setTimeout(resolve, options.delay));
|
|
77
|
+
yield value;
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/index.ts
|
|
84
|
+
function encodeBase64(content) {
|
|
85
|
+
return Buffer.from(content, "utf-8").toString("base64");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
export { asyncFromArray, collect, configure, encodeBase64, mockStoreApi, unsafeResponse };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import "vitest";
|
|
2
|
+
import z$2 from "zod";
|
|
3
|
+
import "@vitest/expect";
|
|
4
|
+
|
|
5
|
+
//#region src/matchers/error-matchers.d.ts
|
|
6
|
+
var ErrorMatcherOptions = [
|
|
7
|
+
30,
|
|
8
|
+
() => [
|
|
9
|
+
Error,
|
|
10
|
+
RegExp,
|
|
11
|
+
Error,
|
|
12
|
+
Record
|
|
13
|
+
],
|
|
14
|
+
[
|
|
15
|
+
"",
|
|
16
|
+
"",
|
|
17
|
+
"",
|
|
18
|
+
"",
|
|
19
|
+
"",
|
|
20
|
+
"",
|
|
21
|
+
"",
|
|
22
|
+
"",
|
|
23
|
+
"",
|
|
24
|
+
""
|
|
25
|
+
]
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/matchers/response-matchers.d.ts
|
|
30
|
+
var ApiErrorOptions = [
|
|
31
|
+
25,
|
|
32
|
+
() => [RegExp],
|
|
33
|
+
[
|
|
34
|
+
"",
|
|
35
|
+
"",
|
|
36
|
+
""
|
|
37
|
+
]
|
|
38
|
+
];
|
|
39
|
+
var ResponseMatcherOptions = [
|
|
40
|
+
28,
|
|
41
|
+
() => [
|
|
42
|
+
RegExp,
|
|
43
|
+
Record,
|
|
44
|
+
RegExp,
|
|
45
|
+
RegExp
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
"",
|
|
49
|
+
"",
|
|
50
|
+
"",
|
|
51
|
+
"",
|
|
52
|
+
"",
|
|
53
|
+
"",
|
|
54
|
+
"",
|
|
55
|
+
"",
|
|
56
|
+
"",
|
|
57
|
+
"",
|
|
58
|
+
""
|
|
59
|
+
]
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/matchers/schema-matchers.d.ts
|
|
64
|
+
var SchemaMatcherOptions = [
|
|
65
|
+
23,
|
|
66
|
+
(TSchema) => [
|
|
67
|
+
z$2.ZodType,
|
|
68
|
+
TSchema,
|
|
69
|
+
TSchema,
|
|
70
|
+
z$2.infer,
|
|
71
|
+
Partial
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
"",
|
|
75
|
+
"",
|
|
76
|
+
"",
|
|
77
|
+
"",
|
|
78
|
+
"",
|
|
79
|
+
"",
|
|
80
|
+
"",
|
|
81
|
+
"",
|
|
82
|
+
"",
|
|
83
|
+
"",
|
|
84
|
+
""
|
|
85
|
+
]
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/matchers/types.d.ts
|
|
90
|
+
var CustomMatchers = [
|
|
91
|
+
0,
|
|
92
|
+
(TSchema, R) => [
|
|
93
|
+
ErrorMatcherOptions,
|
|
94
|
+
R,
|
|
95
|
+
z.ZodType,
|
|
96
|
+
TSchema,
|
|
97
|
+
SchemaMatcherOptions,
|
|
98
|
+
R,
|
|
99
|
+
ApiErrorOptions,
|
|
100
|
+
R,
|
|
101
|
+
Promise,
|
|
102
|
+
R,
|
|
103
|
+
ResponseMatcherOptions,
|
|
104
|
+
R,
|
|
105
|
+
Promise
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
"",
|
|
109
|
+
"",
|
|
110
|
+
"",
|
|
111
|
+
"",
|
|
112
|
+
"",
|
|
113
|
+
"",
|
|
114
|
+
"",
|
|
115
|
+
"",
|
|
116
|
+
"",
|
|
117
|
+
"",
|
|
118
|
+
"",
|
|
119
|
+
"",
|
|
120
|
+
"",
|
|
121
|
+
"",
|
|
122
|
+
"",
|
|
123
|
+
"",
|
|
124
|
+
"",
|
|
125
|
+
"",
|
|
126
|
+
"",
|
|
127
|
+
"",
|
|
128
|
+
"",
|
|
129
|
+
"",
|
|
130
|
+
"",
|
|
131
|
+
"",
|
|
132
|
+
"",
|
|
133
|
+
""
|
|
134
|
+
]
|
|
135
|
+
];
|
|
136
|
+
var _0 = [
|
|
137
|
+
1,
|
|
138
|
+
(T) => [T, CustomMatchers],
|
|
139
|
+
[
|
|
140
|
+
"",
|
|
141
|
+
"",
|
|
142
|
+
"",
|
|
143
|
+
""
|
|
144
|
+
],
|
|
145
|
+
sideEffect(_0)
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
//#endregion
|
|
149
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { tryOr } from "@ucdjs-internal/shared";
|
|
2
|
+
import { expect } from "vitest";
|
|
3
|
+
|
|
4
|
+
//#region src/matchers/error-matchers.ts
|
|
5
|
+
const toMatchError = function(received, options) {
|
|
6
|
+
let error = null;
|
|
7
|
+
if (typeof received === "function") try {
|
|
8
|
+
received();
|
|
9
|
+
return {
|
|
10
|
+
pass: false,
|
|
11
|
+
message: () => "Expected function to throw an error, but it did not"
|
|
12
|
+
};
|
|
13
|
+
} catch (e) {
|
|
14
|
+
if (!(e instanceof Error)) return {
|
|
15
|
+
pass: false,
|
|
16
|
+
message: () => `Expected function to throw an Error, but it threw ${typeof e}`
|
|
17
|
+
};
|
|
18
|
+
error = e;
|
|
19
|
+
}
|
|
20
|
+
else if (received instanceof Error) error = received;
|
|
21
|
+
else return {
|
|
22
|
+
pass: false,
|
|
23
|
+
message: () => `Expected an Error instance or a function that throws, but received ${typeof received}`
|
|
24
|
+
};
|
|
25
|
+
const errorName = error.constructor.name;
|
|
26
|
+
if (options.type && !(error instanceof options.type)) return {
|
|
27
|
+
pass: false,
|
|
28
|
+
message: () => `Expected error to be instance of ${options.type.name}, but got ${errorName}`
|
|
29
|
+
};
|
|
30
|
+
if (options.message) {
|
|
31
|
+
if (!(typeof options.message === "string" ? error.message === options.message : options.message.test(error.message))) return {
|
|
32
|
+
pass: false,
|
|
33
|
+
message: () => `Expected error message to match ${options.message}, but got "${error.message}"`
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (options.cause) {
|
|
37
|
+
const causeError = error.cause ?? error.originalError;
|
|
38
|
+
if (!causeError) return {
|
|
39
|
+
pass: false,
|
|
40
|
+
message: () => `Expected error to have a cause, but none was found`
|
|
41
|
+
};
|
|
42
|
+
if (!(causeError instanceof options.cause)) {
|
|
43
|
+
const causeName = causeError instanceof Error ? causeError.constructor.name : typeof causeError;
|
|
44
|
+
return {
|
|
45
|
+
pass: false,
|
|
46
|
+
message: () => `Expected cause to be instance of ${options.cause.name}, but got ${causeName}`
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (options.fields) {
|
|
51
|
+
const errorRecord = error;
|
|
52
|
+
for (const [key, expectedValue] of Object.entries(options.fields)) {
|
|
53
|
+
const actualValue = errorRecord[key];
|
|
54
|
+
if (!(typeof expectedValue === "object" && expectedValue !== null ? JSON.stringify(actualValue) === JSON.stringify(expectedValue) : actualValue === expectedValue)) return {
|
|
55
|
+
pass: false,
|
|
56
|
+
message: () => `Expected error.${key} to be ${JSON.stringify(expectedValue)}, but got ${JSON.stringify(actualValue)}`
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
pass: true,
|
|
62
|
+
message: () => `Expected error not to match the given criteria`
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/matchers/response-matchers.ts
|
|
68
|
+
const toBeApiError = async function(received, options) {
|
|
69
|
+
const { isNot, equals } = this;
|
|
70
|
+
if (!equals(received.status, options.status)) return {
|
|
71
|
+
pass: false,
|
|
72
|
+
message: () => `Expected response to${isNot ? " not" : ""} be an API error with status ${options.status}, but got ${received.status}`
|
|
73
|
+
};
|
|
74
|
+
if (!received.headers.get("content-type")?.includes("application/json")) return {
|
|
75
|
+
pass: false,
|
|
76
|
+
message: () => `Expected response to${isNot ? " not" : ""} have application/json content-type`
|
|
77
|
+
};
|
|
78
|
+
const error = await received.json();
|
|
79
|
+
if (!error.status || !error.message || !error.timestamp) return {
|
|
80
|
+
pass: false,
|
|
81
|
+
message: () => `Expected response to${isNot ? " not" : ""} have status, message, and timestamp properties`
|
|
82
|
+
};
|
|
83
|
+
if (options.message) {
|
|
84
|
+
if (!(typeof options.message === "string" ? error.message === options.message : options.message.test(error.message))) {
|
|
85
|
+
const expectedMsg = typeof options.message === "string" ? options.message : options.message.source;
|
|
86
|
+
return {
|
|
87
|
+
pass: false,
|
|
88
|
+
message: () => `Expected error message to${isNot ? " not" : ""} match ${expectedMsg}, but got "${error.message}"`
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
pass: true,
|
|
94
|
+
message: () => `Expected response to${isNot ? " not" : ""} be an API error`
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
const toBeHeadError = function(received, expectedStatus) {
|
|
98
|
+
const { isNot, equals } = this;
|
|
99
|
+
if (!equals(received.status, expectedStatus)) return {
|
|
100
|
+
pass: false,
|
|
101
|
+
message: () => `Expected HEAD response status to${isNot ? " not" : ""} be ${expectedStatus}, but got ${received.status}`
|
|
102
|
+
};
|
|
103
|
+
const contentLength = received.headers.get("content-length");
|
|
104
|
+
if (contentLength !== null && Number.parseInt(contentLength, 10) !== 0) return {
|
|
105
|
+
pass: false,
|
|
106
|
+
message: () => `Expected HEAD response to${isNot ? " not" : ""} have content-length of 0`
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
pass: true,
|
|
110
|
+
message: () => `Expected HEAD response to${isNot ? " not" : ""} have status ${expectedStatus}`
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
const toMatchResponse = async function(received, options) {
|
|
114
|
+
const { isNot, equals } = this;
|
|
115
|
+
if (options.status !== void 0 && !equals(received.status, options.status)) return {
|
|
116
|
+
pass: false,
|
|
117
|
+
message: () => `Expected status to${isNot ? " not" : ""} be ${options.status}, but got ${received.status}`
|
|
118
|
+
};
|
|
119
|
+
const isJson = received.headers.get("content-type")?.includes("application/json");
|
|
120
|
+
if (options.json && !isJson) return {
|
|
121
|
+
pass: false,
|
|
122
|
+
message: () => `Expected response to${isNot ? " not" : ""} have application/json content-type`
|
|
123
|
+
};
|
|
124
|
+
if (options.cache) {
|
|
125
|
+
const cacheControl = received.headers.get("cache-control");
|
|
126
|
+
if (!cacheControl) return {
|
|
127
|
+
pass: false,
|
|
128
|
+
message: () => `Expected response to${isNot ? " not" : ""} have cache-control header`
|
|
129
|
+
};
|
|
130
|
+
if (options.cacheMaxAgePattern && !options.cacheMaxAgePattern.test(cacheControl)) return {
|
|
131
|
+
pass: false,
|
|
132
|
+
message: () => `Expected cache-control to${isNot ? " not" : ""} match ${options.cacheMaxAgePattern.source}`
|
|
133
|
+
};
|
|
134
|
+
if (!options.cacheMaxAgePattern && !/max-age=\d+/.test(cacheControl)) return {
|
|
135
|
+
pass: false,
|
|
136
|
+
message: () => `Expected cache-control to${isNot ? " not" : ""} have max-age`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (options.headers) for (const [key, value] of Object.entries(options.headers)) {
|
|
140
|
+
const headerValue = received.headers.get(key);
|
|
141
|
+
if (!headerValue) return {
|
|
142
|
+
pass: false,
|
|
143
|
+
message: () => `Expected response to${isNot ? " not" : ""} have ${key} header`
|
|
144
|
+
};
|
|
145
|
+
if (!(typeof value === "string" ? equals(headerValue, value) : value.test(headerValue))) {
|
|
146
|
+
const expected = typeof value === "string" ? value : value.source;
|
|
147
|
+
return {
|
|
148
|
+
pass: false,
|
|
149
|
+
message: () => `Expected ${key} header to${isNot ? " not" : ""} match ${expected}, but got "${headerValue}"`
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (options.error) {
|
|
154
|
+
if (!isJson) return {
|
|
155
|
+
pass: false,
|
|
156
|
+
message: () => `Expected error response to${isNot ? " not" : ""} have application/json content-type`
|
|
157
|
+
};
|
|
158
|
+
const error = await tryOr({
|
|
159
|
+
try: async () => received.json(),
|
|
160
|
+
err(err) {
|
|
161
|
+
console.error("Failed to parse response JSON:", err);
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
if (error == null) return {
|
|
166
|
+
pass: false,
|
|
167
|
+
message: () => `Expected response body to${isNot ? " not" : ""} be valid JSON`
|
|
168
|
+
};
|
|
169
|
+
if (!error.status) return {
|
|
170
|
+
pass: false,
|
|
171
|
+
message: () => `Expected error to${isNot ? " not" : ""} have "status" property`
|
|
172
|
+
};
|
|
173
|
+
if (!error.message) return {
|
|
174
|
+
pass: false,
|
|
175
|
+
message: () => `Expected error to${isNot ? " not" : ""} have "message" property`
|
|
176
|
+
};
|
|
177
|
+
if (!error.timestamp) return {
|
|
178
|
+
pass: false,
|
|
179
|
+
message: () => `Expected error to${isNot ? " not" : ""} have "timestamp" property`
|
|
180
|
+
};
|
|
181
|
+
if (options.status !== void 0 && !equals(error.status, options.status)) return {
|
|
182
|
+
pass: false,
|
|
183
|
+
message: () => `Expected error.status to${isNot ? " not" : ""} be ${options.status}, but got ${error.status}`
|
|
184
|
+
};
|
|
185
|
+
if (options.error.message) {
|
|
186
|
+
if (!(options.error.message instanceof RegExp ? options.error.message.test(error.message) : equals(error.message, options.error.message))) {
|
|
187
|
+
const expectedMsg = typeof options.error.message === "string" ? options.error.message : options.error.message.source;
|
|
188
|
+
return {
|
|
189
|
+
pass: false,
|
|
190
|
+
message: () => `Expected error.message to${isNot ? " not" : ""} match "${expectedMsg}", but got "${error.message}"`
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
pass: true,
|
|
197
|
+
message: () => `Expected response to${isNot ? " not" : ""} match the given criteria`
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/matchers/schema-matchers.ts
|
|
203
|
+
const toMatchSchema = function(received, options) {
|
|
204
|
+
const result = options.schema.safeParse(received);
|
|
205
|
+
if (!(result.success === options.success)) {
|
|
206
|
+
const expectedStatus = options.success ? "succeed" : "fail";
|
|
207
|
+
const actualStatus = result.success ? "succeeded" : "failed";
|
|
208
|
+
const issues = result.error?.issues ? `\n${this.utils.printExpected(result.error.issues)}` : "";
|
|
209
|
+
return {
|
|
210
|
+
pass: false,
|
|
211
|
+
message: () => `Expected schema validation to ${expectedStatus}, but it ${actualStatus}${issues}`
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (options.data && result.success) for (const key of Object.keys(options.data)) {
|
|
215
|
+
const expected = options.data[key];
|
|
216
|
+
const received = result.data[key];
|
|
217
|
+
if (!this.equals(received, expected)) return {
|
|
218
|
+
pass: false,
|
|
219
|
+
message: () => `Expected property "${key}" to equal ${this.utils.printExpected(expected)}, but received ${this.utils.printReceived(received)}`
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
pass: true,
|
|
224
|
+
message: () => `Expected schema validation to not match`
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/matchers/vitest-setup.ts
|
|
230
|
+
expect.extend({
|
|
231
|
+
toMatchError,
|
|
232
|
+
toMatchSchema,
|
|
233
|
+
toBeApiError,
|
|
234
|
+
toBeHeadError,
|
|
235
|
+
toMatchResponse
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
//#endregion
|
|
239
|
+
export { };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { a as createFileTree, i as FileTreeNodeWithContent, n as unsafeResponse, o as MockStoreConfig, r as FileTreeInput, s as MockStoreFiles, t as configure } from "./helpers-D3XLw9D1.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/mock-store/index.d.ts
|
|
4
|
+
declare function mockStoreApi(config?: MockStoreConfig): void;
|
|
5
|
+
/**
|
|
6
|
+
* Sets up mock handlers for the store subdomain (ucd-store.ucdjs.dev).
|
|
7
|
+
*
|
|
8
|
+
* This is used for the HTTP fs-bridge which directly accesses files via the store subdomain
|
|
9
|
+
* rather than through the API. The store subdomain handles paths like /:version/:filepath
|
|
10
|
+
* without the /ucd/ prefix (it's handled internally by the subdomain).
|
|
11
|
+
*
|
|
12
|
+
* @param {object} config - Configuration for the store subdomain mock
|
|
13
|
+
* @param {string} [config.storeBaseUrl] - Base URL for the store subdomain (defaults to https://ucd-store.ucdjs.dev)
|
|
14
|
+
* @param {MockStoreFiles} config.files - The files to mock
|
|
15
|
+
*/
|
|
16
|
+
declare function mockStoreSubdomain(config: {
|
|
17
|
+
storeBaseUrl?: string;
|
|
18
|
+
files: MockStoreFiles;
|
|
19
|
+
}): void;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { type FileTreeInput, type FileTreeNodeWithContent, type MockStoreConfig, configure, createFileTree, mockStoreApi, mockStoreSubdomain, unsafeResponse };
|