@nestia/e2e 7.0.0 → 7.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 +2 -1
- package/lib/ArrayUtil.d.ts +227 -2
- package/lib/ArrayUtil.js +227 -30
- package/lib/ArrayUtil.js.map +1 -1
- package/lib/GaffComparator.d.ts +229 -14
- package/lib/GaffComparator.js +229 -14
- package/lib/GaffComparator.js.map +1 -1
- package/lib/RandomGenerator.d.ts +332 -35
- package/lib/RandomGenerator.js +337 -50
- package/lib/RandomGenerator.js.map +1 -1
- package/lib/TestValidator.d.ts +334 -35
- package/lib/TestValidator.js +267 -57
- package/lib/TestValidator.js.map +1 -1
- package/lib/internal/json_equal_to.d.ts +1 -1
- package/lib/internal/json_equal_to.js.map +1 -1
- package/package.json +1 -1
- package/src/ArrayUtil.ts +227 -3
- package/src/GaffComparator.ts +230 -14
- package/src/RandomGenerator.ts +339 -50
- package/src/TestValidator.ts +351 -58
- package/src/internal/json_equal_to.ts +1 -1
package/src/TestValidator.ts
CHANGED
|
@@ -2,18 +2,57 @@ import { RandomGenerator } from "./RandomGenerator";
|
|
|
2
2
|
import { json_equal_to } from "./internal/json_equal_to";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* A comprehensive collection of E2E validation utilities for testing
|
|
6
|
+
* applications.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
+
* TestValidator provides type-safe validation functions for common testing
|
|
9
|
+
* scenarios including condition checking, equality validation, error testing,
|
|
10
|
+
* HTTP error validation, pagination testing, search functionality validation,
|
|
11
|
+
* and sorting validation.
|
|
12
|
+
*
|
|
13
|
+
* All functions follow a currying pattern to enable reusable test
|
|
14
|
+
* configurations and provide detailed error messages for debugging failed
|
|
15
|
+
* assertions.
|
|
8
16
|
*
|
|
9
17
|
* @author Jeongho Nam - https://github.com/samchon
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Basic condition testing
|
|
21
|
+
* TestValidator.predicate("user should be authenticated")(user.isAuthenticated);
|
|
22
|
+
*
|
|
23
|
+
* // Equality validation
|
|
24
|
+
* TestValidator.equals("API response should match expected")(expected)(actual);
|
|
25
|
+
*
|
|
26
|
+
* // Error validation
|
|
27
|
+
* TestValidator.error("should throw on invalid input")(() => validateInput(""));
|
|
28
|
+
* ```;
|
|
10
29
|
*/
|
|
11
30
|
export namespace TestValidator {
|
|
12
31
|
/**
|
|
13
|
-
*
|
|
32
|
+
* Validates that a given condition evaluates to true.
|
|
33
|
+
*
|
|
34
|
+
* Supports synchronous boolean values, synchronous functions returning
|
|
35
|
+
* boolean, and asynchronous functions returning Promise<boolean>. The return
|
|
36
|
+
* type is automatically inferred based on the input type.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Synchronous boolean
|
|
41
|
+
* TestValidator.predicate("user should exist")(user !== null);
|
|
42
|
+
*
|
|
43
|
+
* // Synchronous function
|
|
44
|
+
* TestValidator.predicate("array should be empty")(() => arr.length === 0);
|
|
14
45
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
46
|
+
* // Asynchronous function
|
|
47
|
+
* await TestValidator.predicate("database should be connected")(
|
|
48
|
+
* async () => await db.ping()
|
|
49
|
+
* );
|
|
50
|
+
* ```;
|
|
51
|
+
*
|
|
52
|
+
* @param title - Descriptive title used in error messages when validation
|
|
53
|
+
* fails
|
|
54
|
+
* @returns A currying function that accepts the condition to validate
|
|
55
|
+
* @throws Error with descriptive message when condition is not satisfied
|
|
17
56
|
*/
|
|
18
57
|
export const predicate =
|
|
19
58
|
(title: string) =>
|
|
@@ -48,21 +87,38 @@ export namespace TestValidator {
|
|
|
48
87
|
};
|
|
49
88
|
|
|
50
89
|
/**
|
|
51
|
-
*
|
|
90
|
+
* Validates deep equality between two values using JSON comparison.
|
|
91
|
+
*
|
|
92
|
+
* Performs recursive comparison of objects and arrays. Supports an optional
|
|
93
|
+
* exception filter to ignore specific keys during comparison. Useful for
|
|
94
|
+
* validating API responses, data transformations, and object state changes.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* // Basic equality
|
|
99
|
+
* TestValidator.equals("response should match expected")(expectedUser)(actualUser);
|
|
52
100
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
101
|
+
* // Ignore timestamps in comparison
|
|
102
|
+
* TestValidator.equals("user data should match", (key) => key === "updatedAt")(
|
|
103
|
+
* expectedUser
|
|
104
|
+
* )(actualUser);
|
|
55
105
|
*
|
|
56
|
-
*
|
|
106
|
+
* // Validate API response structure
|
|
107
|
+
* const validateResponse = TestValidator.equals("API response structure");
|
|
108
|
+
* validateResponse({ id: 1, name: "John" })({ id: 1, name: "John" });
|
|
109
|
+
* ```;
|
|
57
110
|
*
|
|
58
|
-
* @param title
|
|
59
|
-
* @param exception
|
|
60
|
-
*
|
|
111
|
+
* @param title - Descriptive title used in error messages when values differ
|
|
112
|
+
* @param exception - Optional filter function to exclude specific keys from
|
|
113
|
+
* comparison
|
|
114
|
+
* @returns A currying function chain: first accepts expected value, then
|
|
115
|
+
* actual value
|
|
116
|
+
* @throws Error with detailed diff information when values are not equal
|
|
61
117
|
*/
|
|
62
118
|
export const equals =
|
|
63
119
|
(title: string, exception: (key: string) => boolean = () => false) =>
|
|
64
120
|
<T>(x: T) =>
|
|
65
|
-
(y: T) => {
|
|
121
|
+
(y: T | null | undefined) => {
|
|
66
122
|
const diff: string[] = json_equal_to(exception)(x)(y);
|
|
67
123
|
if (diff.length)
|
|
68
124
|
throw new Error(
|
|
@@ -75,13 +131,80 @@ export namespace TestValidator {
|
|
|
75
131
|
};
|
|
76
132
|
|
|
77
133
|
/**
|
|
78
|
-
*
|
|
134
|
+
* Validates deep inequality between two values using JSON comparison.
|
|
135
|
+
*
|
|
136
|
+
* Performs recursive comparison of objects and arrays to ensure they are NOT
|
|
137
|
+
* equal. Supports an optional exception filter to ignore specific keys during
|
|
138
|
+
* comparison. Useful for validating that data has changed, objects are
|
|
139
|
+
* different, or mutations have occurred.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* // Basic inequality
|
|
144
|
+
* TestValidator.notEquals("user should be different after update")(originalUser)(updatedUser);
|
|
145
|
+
*
|
|
146
|
+
* // Ignore timestamps in comparison
|
|
147
|
+
* TestValidator.notEquals("user data should differ", (key) => key === "updatedAt")(
|
|
148
|
+
* originalUser
|
|
149
|
+
* )(modifiedUser);
|
|
150
|
+
*
|
|
151
|
+
* // Validate state changes
|
|
152
|
+
* const validateStateChange = TestValidator.notEquals("state should have changed");
|
|
153
|
+
* validateStateChange(initialState)(currentState);
|
|
154
|
+
* ```;
|
|
155
|
+
*
|
|
156
|
+
* @param title - Descriptive title used in error messages when values are
|
|
157
|
+
* equal
|
|
158
|
+
* @param exception - Optional filter function to exclude specific keys from
|
|
159
|
+
* comparison
|
|
160
|
+
* @returns A currying function chain: first accepts expected value, then
|
|
161
|
+
* actual value
|
|
162
|
+
* @throws Error when values are equal (indicating validation failure)
|
|
163
|
+
*/
|
|
164
|
+
export const notEquals =
|
|
165
|
+
(title: string, exception: (key: string) => boolean = () => false) =>
|
|
166
|
+
<T>(x: T) =>
|
|
167
|
+
(y: T | null | undefined) => {
|
|
168
|
+
const diff: string[] = json_equal_to(exception)(x)(y);
|
|
169
|
+
if (diff.length === 0)
|
|
170
|
+
throw new Error(
|
|
171
|
+
[
|
|
172
|
+
`Bug on ${title}: values should be different but are equal:`,
|
|
173
|
+
"\n",
|
|
174
|
+
JSON.stringify({ x, y }, null, 2),
|
|
175
|
+
].join("\n"),
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Validates that a function throws an error or rejects when executed.
|
|
79
181
|
*
|
|
80
|
-
*
|
|
182
|
+
* Expects the provided function to fail. If the function executes
|
|
183
|
+
* successfully without throwing an error or rejecting, this validator will
|
|
184
|
+
* throw an exception. Supports both synchronous and asynchronous functions.
|
|
81
185
|
*
|
|
82
|
-
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* // Synchronous error validation
|
|
189
|
+
* TestValidator.error("should reject invalid email")(
|
|
190
|
+
* () => validateEmail("invalid-email")
|
|
191
|
+
* );
|
|
83
192
|
*
|
|
84
|
-
*
|
|
193
|
+
* // Asynchronous error validation
|
|
194
|
+
* await TestValidator.error("should reject unauthorized access")(
|
|
195
|
+
* async () => await api.functional.getSecretData()
|
|
196
|
+
* );
|
|
197
|
+
*
|
|
198
|
+
* // Validate input validation
|
|
199
|
+
* TestValidator.error("should throw on empty string")(
|
|
200
|
+
* () => processRequiredInput("")
|
|
201
|
+
* );
|
|
202
|
+
* ```;
|
|
203
|
+
*
|
|
204
|
+
* @param title - Descriptive title used in error messages when no error
|
|
205
|
+
* occurs
|
|
206
|
+
* @returns A currying function that accepts the task function to validate
|
|
207
|
+
* @throws Error when the task function does not throw an error or reject
|
|
85
208
|
*/
|
|
86
209
|
export const error =
|
|
87
210
|
(title: string) =>
|
|
@@ -99,6 +222,37 @@ export namespace TestValidator {
|
|
|
99
222
|
}
|
|
100
223
|
};
|
|
101
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Validates that a function throws an HTTP error with specific status codes.
|
|
227
|
+
*
|
|
228
|
+
* Specialized error validator for HTTP operations. Validates that the
|
|
229
|
+
* function throws an HttpError with one of the specified status codes. Useful
|
|
230
|
+
* for testing API endpoints, authentication, and authorization logic.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* // Validate 401 Unauthorized
|
|
235
|
+
* await TestValidator.httpError("should return 401 for invalid token")(401)(
|
|
236
|
+
* async () => await api.functional.getProtectedResource("invalid-token")
|
|
237
|
+
* );
|
|
238
|
+
*
|
|
239
|
+
* // Validate multiple possible error codes
|
|
240
|
+
* await TestValidator.httpError("should return client error")(400, 404, 422)(
|
|
241
|
+
* async () => await api.functional.updateNonexistentResource(data)
|
|
242
|
+
* );
|
|
243
|
+
*
|
|
244
|
+
* // Validate server errors
|
|
245
|
+
* TestValidator.httpError("should handle server errors")(500, 502, 503)(
|
|
246
|
+
* () => callFaultyEndpoint()
|
|
247
|
+
* );
|
|
248
|
+
* ```;
|
|
249
|
+
*
|
|
250
|
+
* @param title - Descriptive title used in error messages
|
|
251
|
+
* @returns A currying function that accepts status codes, then the task
|
|
252
|
+
* function
|
|
253
|
+
* @throws Error when function doesn't throw HttpError or status code doesn't
|
|
254
|
+
* match
|
|
255
|
+
*/
|
|
102
256
|
export const httpError =
|
|
103
257
|
(title: string) =>
|
|
104
258
|
(...statuses: number[]) =>
|
|
@@ -143,6 +297,37 @@ export namespace TestValidator {
|
|
|
143
297
|
}
|
|
144
298
|
};
|
|
145
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Safely executes a function and captures any errors without throwing.
|
|
302
|
+
*
|
|
303
|
+
* Utility function for error handling in tests. Executes the provided
|
|
304
|
+
* function and returns any error that occurs, or null if successful. Supports
|
|
305
|
+
* both synchronous and asynchronous functions. Useful for testing error
|
|
306
|
+
* conditions without stopping test execution.
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```typescript
|
|
310
|
+
* // Synchronous error capture
|
|
311
|
+
* const error = TestValidator.proceed(() => {
|
|
312
|
+
* throw new Error("Something went wrong");
|
|
313
|
+
* });
|
|
314
|
+
* console.log(error?.message); // "Something went wrong"
|
|
315
|
+
*
|
|
316
|
+
* // Asynchronous error capture
|
|
317
|
+
* const asyncError = await TestValidator.proceed(async () => {
|
|
318
|
+
* await failingAsyncOperation();
|
|
319
|
+
* });
|
|
320
|
+
*
|
|
321
|
+
* // Success case
|
|
322
|
+
* const noError = TestValidator.proceed(() => {
|
|
323
|
+
* return "success";
|
|
324
|
+
* });
|
|
325
|
+
* console.log(noError); // null
|
|
326
|
+
* ```;
|
|
327
|
+
*
|
|
328
|
+
* @param task - Function to execute safely
|
|
329
|
+
* @returns Error object if function throws/rejects, null if successful
|
|
330
|
+
*/
|
|
146
331
|
export function proceed(task: () => Promise<any>): Promise<Error | null>;
|
|
147
332
|
export function proceed(task: () => any): Error | null;
|
|
148
333
|
export function proceed(
|
|
@@ -163,16 +348,36 @@ export namespace TestValidator {
|
|
|
163
348
|
}
|
|
164
349
|
|
|
165
350
|
/**
|
|
166
|
-
*
|
|
351
|
+
* Validates pagination index API results against expected entity order.
|
|
352
|
+
*
|
|
353
|
+
* Compares the order of entities returned by a pagination API with manually
|
|
354
|
+
* sorted expected results. Validates that entity IDs appear in the correct
|
|
355
|
+
* sequence. Commonly used for testing database queries, search results, and
|
|
356
|
+
* any paginated data APIs.
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```typescript
|
|
360
|
+
* // Test article pagination
|
|
361
|
+
* const expectedArticles = await db.articles.findAll({ order: 'created_at DESC' });
|
|
362
|
+
* const actualArticles = await api.functional.getArticles({ page: 1, limit: 10 });
|
|
167
363
|
*
|
|
168
|
-
*
|
|
364
|
+
* TestValidator.index("article pagination order")(expectedArticles)(
|
|
365
|
+
* actualArticles,
|
|
366
|
+
* true // enable trace logging
|
|
367
|
+
* );
|
|
169
368
|
*
|
|
170
|
-
*
|
|
369
|
+
* // Test user search results
|
|
370
|
+
* const manuallyFilteredUsers = allUsers.filter(u => u.name.includes("John"));
|
|
371
|
+
* const apiSearchResults = await api.functional.searchUsers({ query: "John" });
|
|
171
372
|
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
373
|
+
* TestValidator.index("user search results")(manuallyFilteredUsers)(
|
|
374
|
+
* apiSearchResults
|
|
375
|
+
* );
|
|
376
|
+
* ```;
|
|
174
377
|
*
|
|
175
|
-
* @
|
|
378
|
+
* @param title - Descriptive title used in error messages when order differs
|
|
379
|
+
* @returns A currying function chain: expected entities, then actual entities
|
|
380
|
+
* @throws Error when entity order differs between expected and actual results
|
|
176
381
|
*/
|
|
177
382
|
export const index =
|
|
178
383
|
(title: string) =>
|
|
@@ -203,31 +408,53 @@ export namespace TestValidator {
|
|
|
203
408
|
};
|
|
204
409
|
|
|
205
410
|
/**
|
|
206
|
-
*
|
|
411
|
+
* Validates search functionality by testing API results against manual
|
|
412
|
+
* filtering.
|
|
207
413
|
*
|
|
208
|
-
*
|
|
414
|
+
* Comprehensive search validation that samples entities from a complete
|
|
415
|
+
* dataset, extracts search values, applies manual filtering, calls the search
|
|
416
|
+
* API, and compares results. Validates that search APIs return the correct
|
|
417
|
+
* subset of data matching the search criteria.
|
|
209
418
|
*
|
|
210
|
-
* @
|
|
211
|
-
*
|
|
419
|
+
* @example
|
|
420
|
+
* ```typescript
|
|
421
|
+
* // Test article search functionality
|
|
422
|
+
* const allArticles = await db.articles.findAll();
|
|
423
|
+
* const searchValidator = TestValidator.search("article search API")(
|
|
424
|
+
* (req) => api.searchArticles(req)
|
|
425
|
+
* )(allArticles, 5); // test with 5 random samples
|
|
212
426
|
*
|
|
213
|
-
*
|
|
427
|
+
* await searchValidator({
|
|
428
|
+
* fields: ["title", "content"],
|
|
429
|
+
* values: (article) => [article.title.split(" ")[0]], // first word
|
|
430
|
+
* filter: (article, [keyword]) =>
|
|
431
|
+
* article.title.includes(keyword) || article.content.includes(keyword),
|
|
432
|
+
* request: ([keyword]) => ({ q: keyword })
|
|
433
|
+
* });
|
|
434
|
+
*
|
|
435
|
+
* // Test user search with multiple criteria
|
|
436
|
+
* await TestValidator.search("user search with filters")(
|
|
437
|
+
* (req) => api.getUsers(req)
|
|
438
|
+
* )(allUsers, 3)({
|
|
439
|
+
* fields: ["status", "role"],
|
|
440
|
+
* values: (user) => [user.status, user.role],
|
|
441
|
+
* filter: (user, [status, role]) =>
|
|
442
|
+
* user.status === status && user.role === role,
|
|
443
|
+
* request: ([status, role]) => ({ status, role })
|
|
444
|
+
* });
|
|
445
|
+
* ```;
|
|
446
|
+
*
|
|
447
|
+
* @param title - Descriptive title used in error messages when search fails
|
|
448
|
+
* @returns A currying function chain: API getter function, then dataset and
|
|
449
|
+
* sample count
|
|
450
|
+
* @throws Error when API search results don't match manual filtering results
|
|
214
451
|
*/
|
|
215
452
|
export const search =
|
|
216
453
|
(title: string) =>
|
|
217
|
-
/**
|
|
218
|
-
* @param getter A pagination API function to be called
|
|
219
|
-
*/
|
|
220
454
|
<Entity extends IEntity<any>, Request>(
|
|
221
455
|
getter: (input: Request) => Promise<Entity[]>,
|
|
222
456
|
) =>
|
|
223
|
-
/**
|
|
224
|
-
* @param total Total entity records for comparison
|
|
225
|
-
* @param sampleCount Sampling count. Default is 1
|
|
226
|
-
*/
|
|
227
457
|
(total: Entity[], sampleCount: number = 1) =>
|
|
228
|
-
/**
|
|
229
|
-
* @param props Search properties
|
|
230
|
-
*/
|
|
231
458
|
async <Values extends any[]>(
|
|
232
459
|
props: ISearchProps<Entity, Values, Request>,
|
|
233
460
|
): Promise<void> => {
|
|
@@ -245,34 +472,94 @@ export namespace TestValidator {
|
|
|
245
472
|
}
|
|
246
473
|
};
|
|
247
474
|
|
|
475
|
+
/**
|
|
476
|
+
* Configuration interface for search validation functionality.
|
|
477
|
+
*
|
|
478
|
+
* Defines the structure needed to validate search operations by specifying
|
|
479
|
+
* how to extract search values from entities, filter the dataset manually,
|
|
480
|
+
* and construct API requests.
|
|
481
|
+
*
|
|
482
|
+
* @template Entity - Type of entities being searched, must have an ID field
|
|
483
|
+
* @template Values - Tuple type representing the search values extracted from
|
|
484
|
+
* entities
|
|
485
|
+
* @template Request - Type of the API request object
|
|
486
|
+
*/
|
|
248
487
|
export interface ISearchProps<
|
|
249
488
|
Entity extends IEntity<any>,
|
|
250
489
|
Values extends any[],
|
|
251
490
|
Request,
|
|
252
491
|
> {
|
|
492
|
+
/** Field names being searched, used in error messages for identification */
|
|
253
493
|
fields: string[];
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Extracts search values from a sample entity
|
|
497
|
+
*
|
|
498
|
+
* @param entity - The entity to extract search values from
|
|
499
|
+
* @returns Tuple of values used for searching
|
|
500
|
+
*/
|
|
254
501
|
values(entity: Entity): Values;
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Manual filter function to determine if an entity matches search criteria
|
|
505
|
+
*
|
|
506
|
+
* @param entity - Entity to test against criteria
|
|
507
|
+
* @param values - Search values to match against
|
|
508
|
+
* @returns True if entity matches the search criteria
|
|
509
|
+
*/
|
|
255
510
|
filter(entity: Entity, values: Values): boolean;
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Constructs API request object from search values
|
|
514
|
+
*
|
|
515
|
+
* @param values - Search values to include in request
|
|
516
|
+
* @returns Request object for the search API
|
|
517
|
+
*/
|
|
256
518
|
request(values: Values): Request;
|
|
257
519
|
}
|
|
258
520
|
|
|
259
521
|
/**
|
|
260
|
-
*
|
|
522
|
+
* Validates sorting functionality of pagination APIs.
|
|
523
|
+
*
|
|
524
|
+
* Tests sorting operations by calling the API with sort parameters and
|
|
525
|
+
* validating that results are correctly ordered. Supports multiple fields,
|
|
526
|
+
* ascending/descending order, and optional filtering. Provides detailed error
|
|
527
|
+
* reporting for sorting failures.
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```typescript
|
|
531
|
+
* // Test single field sorting
|
|
532
|
+
* const sortValidator = TestValidator.sort("article sorting")(
|
|
533
|
+
* (sortable) => api.getArticles({ sort: sortable })
|
|
534
|
+
* )("created_at")(
|
|
535
|
+
* (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
|
536
|
+
* );
|
|
261
537
|
*
|
|
262
|
-
*
|
|
538
|
+
* await sortValidator("+"); // ascending
|
|
539
|
+
* await sortValidator("-"); // descending
|
|
263
540
|
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
541
|
+
* // Test multi-field sorting with filtering
|
|
542
|
+
* const userSortValidator = TestValidator.sort("user sorting")(
|
|
543
|
+
* (sortable) => api.getUsers({ sort: sortable })
|
|
544
|
+
* )("status", "created_at")(
|
|
545
|
+
* (a, b) => {
|
|
546
|
+
* if (a.status !== b.status) return a.status.localeCompare(b.status);
|
|
547
|
+
* return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
|
|
548
|
+
* },
|
|
549
|
+
* (user) => user.isActive // only test active users
|
|
550
|
+
* );
|
|
267
551
|
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
552
|
+
* await userSortValidator("+", true); // ascending with trace logging
|
|
553
|
+
* ```;
|
|
554
|
+
*
|
|
555
|
+
* @param title - Descriptive title used in error messages when sorting fails
|
|
556
|
+
* @returns A currying function chain: API getter, field names, comparator,
|
|
557
|
+
* then direction
|
|
558
|
+
* @throws Error when API results are not properly sorted according to
|
|
559
|
+
* specification
|
|
270
560
|
*/
|
|
271
561
|
export const sort =
|
|
272
562
|
(title: string) =>
|
|
273
|
-
/**
|
|
274
|
-
* @param getter A pagination API function to be called
|
|
275
|
-
*/
|
|
276
563
|
<
|
|
277
564
|
T extends object,
|
|
278
565
|
Fields extends string,
|
|
@@ -282,18 +569,8 @@ export namespace TestValidator {
|
|
|
282
569
|
>(
|
|
283
570
|
getter: (sortable: Sortable) => Promise<T[]>,
|
|
284
571
|
) =>
|
|
285
|
-
/**
|
|
286
|
-
* @param fields List of fields to be sorted
|
|
287
|
-
*/
|
|
288
572
|
(...fields: Fields[]) =>
|
|
289
|
-
/**
|
|
290
|
-
* @param comp Comparator function for validation
|
|
291
|
-
* @param filter Filter function for data if required
|
|
292
|
-
*/
|
|
293
573
|
(comp: (x: T, y: T) => number, filter?: (elem: T) => boolean) =>
|
|
294
|
-
/**
|
|
295
|
-
* @param direction "+" means ascending order, and "-" means descending order
|
|
296
|
-
*/
|
|
297
574
|
async (direction: "+" | "-", trace: boolean = false) => {
|
|
298
575
|
let data: T[] = await getter(
|
|
299
576
|
fields.map((field) => `${direction}${field}` as const) as Sortable,
|
|
@@ -318,6 +595,22 @@ export namespace TestValidator {
|
|
|
318
595
|
}
|
|
319
596
|
};
|
|
320
597
|
|
|
598
|
+
/**
|
|
599
|
+
* Type alias for sortable field specifications.
|
|
600
|
+
*
|
|
601
|
+
* Represents an array of sort field specifications where each field can be
|
|
602
|
+
* prefixed with '+' for ascending order or '-' for descending order.
|
|
603
|
+
*
|
|
604
|
+
* @example
|
|
605
|
+
* ```typescript
|
|
606
|
+
* type UserSortable = TestValidator.Sortable<"name" | "email" | "created_at">;
|
|
607
|
+
* // Results in: Array<"-name" | "+name" | "-email" | "+email" | "-created_at" | "+created_at">
|
|
608
|
+
*
|
|
609
|
+
* const userSort: UserSortable = ["+name", "-created_at"];
|
|
610
|
+
* ```;
|
|
611
|
+
*
|
|
612
|
+
* @template Literal - String literal type representing available field names
|
|
613
|
+
*/
|
|
321
614
|
export type Sortable<Literal extends string> = Array<
|
|
322
615
|
`-${Literal}` | `+${Literal}`
|
|
323
616
|
>;
|