@nestia/e2e 14.0.0 → 14.0.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.
- package/README.md +1 -1
- package/lib/ArrayUtil.d.ts +152 -70
- package/lib/ArrayUtil.js +169 -75
- package/lib/ArrayUtil.js.map +1 -1
- package/lib/DynamicExecutor.d.ts +139 -11
- package/lib/DynamicExecutor.js +98 -10
- package/lib/DynamicExecutor.js.map +1 -1
- package/lib/GaffComparator.d.ts +176 -73
- package/lib/GaffComparator.js +196 -77
- package/lib/GaffComparator.js.map +1 -1
- package/lib/MapUtil.d.ts +37 -6
- package/lib/MapUtil.js +37 -6
- package/lib/MapUtil.js.map +1 -1
- package/lib/RandomGenerator.d.ts +157 -84
- package/lib/RandomGenerator.js +173 -89
- package/lib/RandomGenerator.js.map +1 -1
- package/lib/TestValidator.d.ts +253 -74
- package/lib/TestValidator.js +274 -97
- package/lib/TestValidator.js.map +1 -1
- package/lib/internal/json_equal_to.d.ts +21 -0
- package/lib/internal/json_equal_to.js +35 -6
- package/lib/internal/json_equal_to.js.map +1 -1
- package/package.json +3 -2
- package/src/ArrayUtil.ts +173 -77
- package/src/DynamicExecutor.ts +158 -15
- package/src/GaffComparator.ts +195 -77
- package/src/MapUtil.ts +37 -6
- package/src/RandomGenerator.ts +172 -89
- package/src/TestValidator.ts +302 -107
- package/src/internal/json_equal_to.ts +36 -4
package/src/DynamicExecutor.ts
CHANGED
|
@@ -16,12 +16,45 @@ import { pathToFileURL } from "url";
|
|
|
16
16
|
*
|
|
17
17
|
* When you want to see some utilization cases, see the below example links.
|
|
18
18
|
*
|
|
19
|
+
* Native boundary: Discovery lists directories with `readdir` and `stat` and
|
|
20
|
+
* joins children with `path.resolve`; symbolic links are followed, and each
|
|
21
|
+
* directory is entered once by its filesystem real path so aliases and ancestor
|
|
22
|
+
* cycles do not repeat discovery. The extension is matched as a file-name
|
|
23
|
+
* suffix and the prefix with `startsWith`, both case-sensitively as spelled, so
|
|
24
|
+
* name case is judged by the caller's spelling and not by the volume. A file is
|
|
25
|
+
* imported through a specifier: the path relative to this module in `/` form
|
|
26
|
+
* when `path.relative` can express one, and where it cannot, as on Windows
|
|
27
|
+
* between drive roots, the absolute path for a CommonJS build, whose `require`
|
|
28
|
+
* rejects a URL, or a `file:` URL from `pathToFileURL` for native ESM, which
|
|
29
|
+
* rejects a Windows path.
|
|
30
|
+
*
|
|
31
|
+
* Processing cost: Discovery visits each real directory once and performs one
|
|
32
|
+
* metadata lookup per listed entry. A monotone queue index dispatches each
|
|
33
|
+
* admitted module once without shifting the remaining queue; execution walks
|
|
34
|
+
* module exports once. Storage grows with discovered directories, modules and
|
|
35
|
+
* execution records; concurrency allocates the requested number of workers.
|
|
36
|
+
*
|
|
37
|
+
* Reuse: Discovery and module loading are shared by the tests of one run. New
|
|
38
|
+
* runs rediscover files; Node owns module caching, so editing an already
|
|
39
|
+
* imported module does not promise reload in the same process.
|
|
40
|
+
*
|
|
41
|
+
* Resource ownership: One run owns the discovered module queue, visited
|
|
42
|
+
* directory set and report. At most the requested number of module tasks are
|
|
43
|
+
* active, all are awaited before success or strict rejection, and returned
|
|
44
|
+
* records transfer to the caller. Listener failure also settles workers;
|
|
45
|
+
* cancellation and unloading Node cached modules are not provided.
|
|
46
|
+
*
|
|
19
47
|
* @author Jeongho Nam - https://github.com/samchon
|
|
20
48
|
* @example
|
|
21
49
|
* https://github.com/samchon/nestia-start/blob/master/test/index.ts
|
|
22
50
|
*
|
|
23
51
|
* @example
|
|
24
52
|
* https://github.com/samchon/backend/blob/master/test/index.ts
|
|
53
|
+
*
|
|
54
|
+
* @evidence contracts/common.md#principled-implementation The executor walks the location recursively, admits a file only when its basename ends with the extension and starts with the prefix and the filter accepts it, imports the admitted files, and runs every exported function whose name starts with the prefix, with at most `simultaneous` runs in flight; the file gate runs before the import so an excluded file is never loaded.
|
|
55
|
+
* @evidence contracts/common.md#clear-and-simple-design Two public entry points share one private pipeline: discovery (`iterate`), the import specifier (`specifier`), and execution (`execute`) are separate helpers, and the two modes differ by one flag.
|
|
56
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Selection is by prefix, extension, and the caller's filter, with no known file names; the import specifier logic distinguishes the module system in use rather than special-casing a platform.
|
|
57
|
+
* @evidence contracts/common.md#meaningful-documentation The namespace prose says what it runs and why, with links to example repositories, and the members document their options.
|
|
25
58
|
*/
|
|
26
59
|
export namespace DynamicExecutor {
|
|
27
60
|
/**
|
|
@@ -29,12 +62,27 @@ export namespace DynamicExecutor {
|
|
|
29
62
|
*
|
|
30
63
|
* @template Arguments Type of parameters
|
|
31
64
|
* @template Ret Type of return value
|
|
65
|
+
* @evidence contracts/common.md#principled-implementation A dynamic function is an asynchronous function of the parameters the executor supplies.
|
|
66
|
+
* @evidence contracts/common.md#clear-and-simple-design A single call signature.
|
|
67
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
68
|
+
* @evidence contracts/common.md#meaningful-documentation The comment names the parameter and return type arguments.
|
|
32
69
|
*/
|
|
33
70
|
export interface Closure<Arguments extends any[], Ret = any> {
|
|
34
71
|
(...args: Arguments): Promise<Ret>;
|
|
35
72
|
}
|
|
36
73
|
|
|
37
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Options for dynamic executor.
|
|
76
|
+
*
|
|
77
|
+
* Native boundary: The location is a native directory path resolved with
|
|
78
|
+
* `path.resolve`, the extension is a suffix without a dot, and `simultaneous`
|
|
79
|
+
* bounds in-process runs only, so no process or thread is created.
|
|
80
|
+
*
|
|
81
|
+
* @evidence contracts/common.md#principled-implementation The fields are what selection and execution need: the prefix, the location, the parameter factory, and optional hooks, concurrency, and extension; `simultaneous` and the extension have documented defaults.
|
|
82
|
+
* @evidence contracts/common.md#clear-and-simple-design A flat option record whose optional members are the extension points (listener, filter, wrapper).
|
|
83
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Every value is caller-supplied; the defaults are the documented ones (`js`, one at a time).
|
|
84
|
+
* @evidence contracts/common.md#meaningful-documentation Each option documents its meaning, including that the filter receives a file basename and never a function name.
|
|
85
|
+
*/
|
|
38
86
|
export interface IProps<Parameters extends any[], Ret = any> {
|
|
39
87
|
/**
|
|
40
88
|
* Prefix of function name.
|
|
@@ -56,6 +104,10 @@ export namespace DynamicExecutor {
|
|
|
56
104
|
*
|
|
57
105
|
* @param name Function name
|
|
58
106
|
* @returns Parameters
|
|
107
|
+
* @evidence contracts/common.md#principled-implementation The function is called with the function name at each execution and returns the argument list, so each test gets fresh parameters keyed by its name.
|
|
108
|
+
* @evidence contracts/common.md#clear-and-simple-design One required callback with one input.
|
|
109
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Arguments come only from the caller's function.
|
|
110
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the parameter and the return value.
|
|
59
111
|
*/
|
|
60
112
|
parameters: (name: string) => Parameters;
|
|
61
113
|
|
|
@@ -64,7 +116,14 @@ export namespace DynamicExecutor {
|
|
|
64
116
|
*
|
|
65
117
|
* Listener of completion of a test function.
|
|
66
118
|
*
|
|
119
|
+
* A thrown listener error rejects the run in either mode after active
|
|
120
|
+
* module tasks settle; listeners should handle their reporting errors.
|
|
121
|
+
*
|
|
67
122
|
* @param exec Execution result of a test function
|
|
123
|
+
* @evidence contracts/common.md#principled-implementation The listener is called from the `finally` of each execution with the execution record, so it observes both success and failure, after the completion time is set.
|
|
124
|
+
* @evidence contracts/common.md#clear-and-simple-design One optional callback with one record argument.
|
|
125
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The completion hook receives the actual execution record in finally; its own exception is not swallowed and can reject either execution mode.
|
|
126
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states that it listens to the completion of a test function.
|
|
68
127
|
*/
|
|
69
128
|
onComplete?: (exec: IExecution) => void;
|
|
70
129
|
|
|
@@ -76,8 +135,16 @@ export namespace DynamicExecutor {
|
|
|
76
135
|
* `false`, the file would never be imported, so that every function defined
|
|
77
136
|
* in the file would never be executed either.
|
|
78
137
|
*
|
|
138
|
+
* Native boundary: The predicate receives the entry name exactly as
|
|
139
|
+
* `readdir` lists it, without case or separator normalization, so a
|
|
140
|
+
* comparison on a case-insensitive volume is the caller's to make.
|
|
141
|
+
*
|
|
79
142
|
* @param file File name (basename) of the dynamic functions
|
|
80
143
|
* @returns Whether to run or not
|
|
144
|
+
* @evidence contracts/common.md#principled-implementation The predicate is evaluated on the file basename before the file is imported, so a `false` result prevents both the import and the execution of every function in it.
|
|
145
|
+
* @evidence contracts/common.md#clear-and-simple-design One optional predicate over one string.
|
|
146
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Selection follows the caller's predicate, not fixed names.
|
|
147
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states the argument and the effect of a `false` answer.
|
|
81
148
|
*/
|
|
82
149
|
filter?: (file: string) => boolean;
|
|
83
150
|
|
|
@@ -92,6 +159,10 @@ export namespace DynamicExecutor {
|
|
|
92
159
|
* @param closure Function to be executed
|
|
93
160
|
* @param parameters Parameters, result of options.parameters function.
|
|
94
161
|
* @returns Wrapper function
|
|
162
|
+
* @evidence contracts/common.md#principled-implementation When present, the wrapper is called instead of the function directly with the name, the function, and the parameters, so a caller can add setup, retries, or measurement around each test.
|
|
163
|
+
* @evidence contracts/common.md#clear-and-simple-design One optional callback that replaces the direct call.
|
|
164
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The executor calls the wrapper for every function it runs, without a test-specific bypass.
|
|
165
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents each parameter and the return value.
|
|
95
166
|
*/
|
|
96
167
|
wrapper?: (
|
|
97
168
|
name: string,
|
|
@@ -119,7 +190,18 @@ export namespace DynamicExecutor {
|
|
|
119
190
|
extension?: string;
|
|
120
191
|
}
|
|
121
192
|
|
|
122
|
-
/**
|
|
193
|
+
/**
|
|
194
|
+
* Report, result of dynamic execution.
|
|
195
|
+
*
|
|
196
|
+
* Native boundary: The report preserves the caller's native directory
|
|
197
|
+
* spelling, including relative paths; discovered execution locations are
|
|
198
|
+
* resolved separately with Node path operations.
|
|
199
|
+
*
|
|
200
|
+
* @evidence contracts/common.md#principled-implementation The report keeps the location, every execution record in start order, and the elapsed time computed as the difference of two clock reads.
|
|
201
|
+
* @evidence contracts/common.md#clear-and-simple-design A flat record with no behavior.
|
|
202
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Every field is measured by the executor.
|
|
203
|
+
* @evidence contracts/common.md#meaningful-documentation Each field documents its meaning.
|
|
204
|
+
*/
|
|
123
205
|
export interface IReport {
|
|
124
206
|
/** Location path of dynamic functions. */
|
|
125
207
|
location: string;
|
|
@@ -131,7 +213,17 @@ export namespace DynamicExecutor {
|
|
|
131
213
|
time: number;
|
|
132
214
|
}
|
|
133
215
|
|
|
134
|
-
/**
|
|
216
|
+
/**
|
|
217
|
+
* Execution of a test function.
|
|
218
|
+
*
|
|
219
|
+
* Native boundary: The location field holds the native absolute path of the
|
|
220
|
+
* file as resolved, so it carries backslashes on Windows.
|
|
221
|
+
*
|
|
222
|
+
* @evidence contracts/common.md#principled-implementation An execution records the function name, its file, the returned value, the error or `null`, and the start and completion instants, which is what a listener needs to report a test.
|
|
223
|
+
* @evidence contracts/common.md#clear-and-simple-design A flat record with no behavior.
|
|
224
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Every field is measured by the executor.
|
|
225
|
+
* @evidence contracts/common.md#meaningful-documentation Each field documents its meaning, and the two time fields state that they hold ISO 8601 strings.
|
|
226
|
+
*/
|
|
135
227
|
export interface IExecution {
|
|
136
228
|
/** Name of function. */
|
|
137
229
|
name: string;
|
|
@@ -145,22 +237,40 @@ export namespace DynamicExecutor {
|
|
|
145
237
|
/** Error when occurred. */
|
|
146
238
|
error: Error | null;
|
|
147
239
|
|
|
148
|
-
/**
|
|
240
|
+
/** Start time, as an ISO 8601 string. */
|
|
149
241
|
started_at: string;
|
|
150
242
|
|
|
151
|
-
/** Completion time. */
|
|
243
|
+
/** Completion time, as an ISO 8601 string. */
|
|
152
244
|
completed_at: string;
|
|
153
245
|
}
|
|
154
246
|
|
|
155
247
|
/**
|
|
156
248
|
* Prepare dynamic executor in strict mode.
|
|
157
249
|
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
250
|
+
* Strict mode stops dispatching further modules after a failure and rejects
|
|
251
|
+
* after active module tasks settle. {@link validate} records test failures and
|
|
252
|
+
* continues instead.
|
|
253
|
+
*
|
|
254
|
+
* Processing cost: The shared executor discovers modules once, dispatches
|
|
255
|
+
* each queue index once and walks each admitted export; this entry only
|
|
256
|
+
* selects the failure policy.
|
|
257
|
+
*
|
|
258
|
+
* Reuse: Discovery and module loading are shared by the tests of one run. New
|
|
259
|
+
* runs rediscover files; Node owns module caching, so editing an already
|
|
260
|
+
* imported module does not promise reload in the same process.
|
|
261
|
+
*
|
|
262
|
+
* Resource ownership: One run owns the discovered module queue, visited
|
|
263
|
+
* directory set and report. At most the requested number of module tasks are
|
|
264
|
+
* active, all are awaited before success or strict rejection, and returned
|
|
265
|
+
* records transfer to the caller. Listener failure also settles workers;
|
|
266
|
+
* cancellation and unloading Node cached modules are not provided.
|
|
161
267
|
*
|
|
162
268
|
* @param props Properties of dynamic execution
|
|
163
269
|
* @returns Report of dynamic test functions execution
|
|
270
|
+
* @evidence contracts/common.md#principled-implementation Strict mode rethrows the first error of a function after the execution record and the listener have seen it; no further file is dispatched once a task has failed, and the returned promise rejects with that first failure only after the in-flight tasks have settled, so no test runs after the caller observes the failure.
|
|
271
|
+
* @evidence contracts/common.md#clear-and-simple-design A one-line binding of the shared pipeline with the strict flag.
|
|
272
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The mode changes only whether a failure is rethrown; nothing is silenced.
|
|
273
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states the strict behavior, contrasts it with `validate`, and documents the parameter and the report.
|
|
164
274
|
*/
|
|
165
275
|
export const assert = <Arguments extends any[]>(
|
|
166
276
|
props: IProps<Arguments>,
|
|
@@ -169,12 +279,30 @@ export namespace DynamicExecutor {
|
|
|
169
279
|
/**
|
|
170
280
|
* Prepare dynamic executor in loose mode.
|
|
171
281
|
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* {@link assert}
|
|
282
|
+
* Loose mode records test failures and continues with subsequent tests.
|
|
283
|
+
* Discovery, import and completion-listener errors still reject the run.
|
|
284
|
+
* {@link assert} rejects on a test failure after active tasks settle.
|
|
285
|
+
*
|
|
286
|
+
* Processing cost: The shared executor discovers modules once, dispatches
|
|
287
|
+
* each queue index once and walks each admitted export; this entry only
|
|
288
|
+
* selects the failure policy.
|
|
289
|
+
*
|
|
290
|
+
* Reuse: Discovery and module loading are shared by the tests of one run. New
|
|
291
|
+
* runs rediscover files; Node owns module caching, so editing an already
|
|
292
|
+
* imported module does not promise reload in the same process.
|
|
293
|
+
*
|
|
294
|
+
* Resource ownership: One run owns the discovered module queue, visited
|
|
295
|
+
* directory set and report. At most the requested number of module tasks are
|
|
296
|
+
* active, all are awaited before success or strict rejection, and returned
|
|
297
|
+
* records transfer to the caller. Listener failure also settles workers;
|
|
298
|
+
* cancellation and unloading Node cached modules are not provided.
|
|
175
299
|
*
|
|
176
300
|
* @param props Properties of dynamic executor
|
|
177
301
|
* @returns Report of dynamic test functions execution
|
|
302
|
+
* @evidence contracts/common.md#principled-implementation Loose mode records test errors and continues, returning them in execution records. Discovery, module import and completion-listener failures are outside that test-error policy and reject the run.
|
|
303
|
+
* @evidence contracts/common.md#clear-and-simple-design A one-line binding of the shared pipeline with the loose flag.
|
|
304
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The mode changes only whether a failure is rethrown; the error stays in the report.
|
|
305
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states the loose behavior, contrasts it with `assert`, and documents the parameter and the report.
|
|
178
306
|
*/
|
|
179
307
|
export const validate = <Arguments extends any[]>(
|
|
180
308
|
props: IProps<Arguments>,
|
|
@@ -204,14 +332,24 @@ export namespace DynamicExecutor {
|
|
|
204
332
|
filter: props.filter,
|
|
205
333
|
executor,
|
|
206
334
|
});
|
|
335
|
+
// A rejected task stops the dispatch of the remaining files, and the run
|
|
336
|
+
// settles only after the in-flight tasks end, so that no test keeps
|
|
337
|
+
// running after the caller has seen the failure.
|
|
338
|
+
let failure: { error: unknown } | null = null;
|
|
339
|
+
let next: number = 0;
|
|
207
340
|
await Promise.all(
|
|
208
341
|
new Array(simultaneous).fill(0).map(async () => {
|
|
209
|
-
while (processes.length
|
|
210
|
-
const task = processes
|
|
211
|
-
|
|
342
|
+
while (next < processes.length && failure === null) {
|
|
343
|
+
const task = processes[next++];
|
|
344
|
+
try {
|
|
345
|
+
await task?.();
|
|
346
|
+
} catch (error) {
|
|
347
|
+
failure ??= { error };
|
|
348
|
+
}
|
|
212
349
|
}
|
|
213
350
|
}),
|
|
214
351
|
);
|
|
352
|
+
if (failure !== null) throw (failure as { error: unknown }).error;
|
|
215
353
|
report.time = Date.now() - report.time;
|
|
216
354
|
return report;
|
|
217
355
|
};
|
|
@@ -247,16 +385,21 @@ export namespace DynamicExecutor {
|
|
|
247
385
|
executor: (path: string, modulo: Module<Arguments>) => Promise<void>;
|
|
248
386
|
}): Promise<Array<() => Promise<void>>> => {
|
|
249
387
|
const container: Array<() => Promise<void>> = [];
|
|
388
|
+
const visited: Set<string> = new Set();
|
|
250
389
|
const visitor = async (path: string): Promise<void> => {
|
|
390
|
+
const identity: string = await fs.promises.realpath(path);
|
|
391
|
+
if (visited.has(identity)) return;
|
|
392
|
+
visited.add(identity);
|
|
251
393
|
const directory: string[] = await fs.promises.readdir(path);
|
|
252
394
|
for (const file of directory) {
|
|
253
395
|
const location: string = NodePath.resolve(`${path}/${file}`);
|
|
254
|
-
const stats: fs.Stats = await fs.promises.
|
|
396
|
+
const stats: fs.Stats = await fs.promises.stat(location);
|
|
255
397
|
|
|
256
398
|
if (stats.isDirectory() === true) {
|
|
257
399
|
await visitor(location);
|
|
258
400
|
continue;
|
|
259
401
|
}
|
|
402
|
+
if (stats.isFile() === false) continue;
|
|
260
403
|
// Compare the whole suffix. A fixed-width slice silently assumed a
|
|
261
404
|
// two-character extension, so every longer one ("mjs", "cjs", "tsx")
|
|
262
405
|
// matched nothing and the run reported success with no test executed.
|