@nestia/e2e 7.4.0 → 8.0.0-dev.20250829

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.
@@ -7,21 +7,21 @@
7
7
  * HTTP error validation, pagination testing, search functionality validation,
8
8
  * and sorting validation.
9
9
  *
10
- * All functions follow a currying pattern to enable reusable test
11
- * configurations and provide detailed error messages for debugging failed
12
- * assertions.
10
+ * Most functions use direct parameter passing for simplicity, while some
11
+ * maintain currying patterns for advanced composition. All provide detailed
12
+ * error messages for debugging failed assertions.
13
13
  *
14
14
  * @author Jeongho Nam - https://github.com/samchon
15
15
  * @example
16
16
  * ```typescript
17
17
  * // Basic condition testing
18
- * TestValidator.predicate("user should be authenticated")(user.isAuthenticated);
18
+ * TestValidator.predicate("user should be authenticated", user.isAuthenticated);
19
19
  *
20
20
  * // Equality validation
21
- * TestValidator.equals("API response should match expected")(expected)(actual);
21
+ * TestValidator.equals("API response should match expected", x, y);
22
22
  *
23
23
  * // Error validation
24
- * TestValidator.error("should throw on invalid input")(() => validateInput(""));
24
+ * TestValidator.error("should throw on invalid input", () => assertInput(""));
25
25
  * ```;
26
26
  */
27
27
  export declare namespace TestValidator {
@@ -35,23 +35,25 @@ export declare namespace TestValidator {
35
35
  * @example
36
36
  * ```typescript
37
37
  * // Synchronous boolean
38
- * TestValidator.predicate("user should exist")(user !== null);
38
+ * TestValidator.predicate("user should exist", user !== null);
39
39
  *
40
40
  * // Synchronous function
41
- * TestValidator.predicate("array should be empty")(() => arr.length === 0);
41
+ * TestValidator.predicate("array should be empty", () => arr.length === 0);
42
42
  *
43
43
  * // Asynchronous function
44
- * await TestValidator.predicate("database should be connected")(
44
+ * await TestValidator.predicate("database should be connected",
45
45
  * async () => await db.ping()
46
46
  * );
47
47
  * ```;
48
48
  *
49
49
  * @param title - Descriptive title used in error messages when validation
50
50
  * fails
51
- * @returns A currying function that accepts the condition to validate
51
+ * @param condition - The condition to validate (boolean, function, or async
52
+ * function)
53
+ * @returns Void or Promise<void> based on the input type
52
54
  * @throws Error with descriptive message when condition is not satisfied
53
55
  */
54
- const predicate: (title: string) => <T extends boolean | (() => boolean) | (() => Promise<boolean>)>(condition: T) => T extends () => Promise<boolean> ? Promise<void> : void;
56
+ function predicate<T extends boolean | (() => boolean) | (() => Promise<boolean>)>(title: string, condition: T): T extends () => Promise<boolean> ? Promise<void> : void;
55
57
  /**
56
58
  * Validates deep equality between two values using JSON comparison.
57
59
  *
@@ -59,48 +61,35 @@ export declare namespace TestValidator {
59
61
  * exception filter to ignore specific keys during comparison. Useful for
60
62
  * validating API responses, data transformations, and object state changes.
61
63
  *
62
- * **Type Safety Notes:**
63
- *
64
- * - The generic type T is inferred from the `actual` parameter (first in the
65
- * currying chain)
66
- * - The `expected` parameter must be assignable to `T | null | undefined`
67
- * - For objects, `expected` must have the same or subset of properties as
68
- * `actual`
69
- * - For union types like `string | null`, ensure proper type compatibility:
70
- *
71
- * ```typescript
72
- * const x: string | null;
73
- * TestValidator.equals("works")(x)(null); // ✅ Works: null is assignable to string | null
74
- * TestValidator.equals("error")(null)(x); // ❌ Error: x might be string, but expected is null
75
- * ```
76
- *
77
64
  * @example
78
65
  * ```typescript
79
66
  * // Basic equality
80
- * TestValidator.equals("response should match expected")(expectedUser)(actualUser);
67
+ * TestValidator.equals("response should match expected", expectedUser, actualUser);
81
68
  *
82
69
  * // Ignore timestamps in comparison
83
- * TestValidator.equals("user data should match", (key) => key === "updatedAt")(
84
- * expectedUser
85
- * )(actualUser);
70
+ * TestValidator.equals("user data should match", expectedUser, actualUser,
71
+ * (key) => key === "updatedAt"
72
+ * );
86
73
  *
87
74
  * // Validate API response structure
88
- * const validateResponse = TestValidator.equals("API response structure");
89
- * validateResponse({ id: 1, name: "John" })({ id: 1, name: "John" });
75
+ * TestValidator.equals("API response structure",
76
+ * { id: 1, name: "John" },
77
+ * { id: 1, name: "John" }
78
+ * );
90
79
  *
91
80
  * // Type-safe nullable comparisons
92
81
  * const nullableData: { name: string } | null = getData();
93
- * TestValidator.equals("nullable check")(nullableData)(null); // ✅ Safe
82
+ * TestValidator.equals("nullable check", nullableData, null);
94
83
  * ```;
95
84
  *
96
85
  * @param title - Descriptive title used in error messages when values differ
86
+ * @param x - The first value to compare
87
+ * @param y - The second value to compare (can be null or undefined)
97
88
  * @param exception - Optional filter function to exclude specific keys from
98
89
  * comparison
99
- * @returns A currying function chain: first accepts expected value, then
100
- * actual value
101
90
  * @throws Error with detailed diff information when values are not equal
102
91
  */
103
- const equals: (title: string, exception?: (key: string) => boolean) => <T>(actual: T) => (expected: T | null | undefined) => void;
92
+ function equals<T>(title: string, x: T, y: T | null | undefined, exception?: (key: string) => boolean): void;
104
93
  /**
105
94
  * Validates deep inequality between two values using JSON comparison.
106
95
  *
@@ -109,49 +98,33 @@ export declare namespace TestValidator {
109
98
  * comparison. Useful for validating that data has changed, objects are
110
99
  * different, or mutations have occurred.
111
100
  *
112
- * **Type Safety Notes:**
113
- *
114
- * - The generic type T is inferred from the `actual` parameter (first in the
115
- * currying chain)
116
- * - The `expected` parameter must be assignable to `T | null | undefined`
117
- * - For objects, `expected` must have the same or subset of properties as
118
- * `actual`
119
- * - For union types like `string | null`, ensure proper type compatibility:
120
- *
121
- * ```typescript
122
- * const x: string | null;
123
- * TestValidator.notEquals("works")(x)(null); // ✅ Works: null is assignable to string | null
124
- * TestValidator.notEquals("error")(null)(x); // ❌ Error: x might be string, but expected is null
125
- * ```
126
- *
127
101
  * @example
128
102
  * ```typescript
129
103
  * // Basic inequality
130
- * TestValidator.notEquals("user should be different after update")(originalUser)(updatedUser);
104
+ * TestValidator.notEquals("user should be different after update", originalUser, updatedUser);
131
105
  *
132
106
  * // Ignore timestamps in comparison
133
- * TestValidator.notEquals("user data should differ", (key) => key === "updatedAt")(
134
- * originalUser
135
- * )(modifiedUser);
107
+ * TestValidator.notEquals("user data should differ", originalUser, modifiedUser,
108
+ * (key) => key === "updatedAt"
109
+ * );
136
110
  *
137
111
  * // Validate state changes
138
- * const validateStateChange = TestValidator.notEquals("state should have changed");
139
- * validateStateChange(initialState)(currentState);
112
+ * TestValidator.notEquals("state should have changed", initialState, currentState);
140
113
  *
141
114
  * // Type-safe nullable comparisons
142
115
  * const mutableData: { count: number } | null = getMutableData();
143
- * TestValidator.notEquals("should have changed")(mutableData)(null); // ✅ Safe
116
+ * TestValidator.notEquals("should have changed", mutableData, null);
144
117
  * ```;
145
118
  *
146
119
  * @param title - Descriptive title used in error messages when values are
147
120
  * equal
121
+ * @param x - The first value to compare
122
+ * @param y - The second value to compare (can be null or undefined)
148
123
  * @param exception - Optional filter function to exclude specific keys from
149
124
  * comparison
150
- * @returns A currying function chain: first accepts expected value, then
151
- * actual value
152
125
  * @throws Error when values are equal (indicating validation failure)
153
126
  */
154
- const notEquals: (title: string, exception?: (key: string) => boolean) => <T>(actual: T) => (expected: T | null | undefined) => void;
127
+ function notEquals<T>(title: string, x: T, y: T | null | undefined, exception?: (key: string) => boolean): void;
155
128
  /**
156
129
  * Validates that a function throws an error or rejects when executed.
157
130
  *
@@ -162,27 +135,28 @@ export declare namespace TestValidator {
162
135
  * @example
163
136
  * ```typescript
164
137
  * // Synchronous error validation
165
- * TestValidator.error("should reject invalid email")(
138
+ * TestValidator.error("should reject invalid email",
166
139
  * () => validateEmail("invalid-email")
167
140
  * );
168
141
  *
169
142
  * // Asynchronous error validation
170
- * await TestValidator.error("should reject unauthorized access")(
143
+ * await TestValidator.error("should reject unauthorized access",
171
144
  * async () => await api.functional.getSecretData()
172
145
  * );
173
146
  *
174
147
  * // Validate input validation
175
- * TestValidator.error("should throw on empty string")(
148
+ * TestValidator.error("should throw on empty string",
176
149
  * () => processRequiredInput("")
177
150
  * );
178
151
  * ```;
179
152
  *
180
153
  * @param title - Descriptive title used in error messages when no error
181
154
  * occurs
182
- * @returns A currying function that accepts the task function to validate
155
+ * @param task - The function that should throw an error or reject
156
+ * @returns Void or Promise<void> based on the input type
183
157
  * @throws Error when the task function does not throw an error or reject
184
158
  */
185
- const error: (title: string) => <T>(task: () => T) => T extends Promise<any> ? Promise<void> : void;
159
+ function error<T>(title: string, task: () => T): T extends Promise<any> ? Promise<void> : void;
186
160
  /**
187
161
  * Validates that a function throws an HTTP error with specific status codes.
188
162
  *
@@ -193,61 +167,29 @@ export declare namespace TestValidator {
193
167
  * @example
194
168
  * ```typescript
195
169
  * // Validate 401 Unauthorized
196
- * await TestValidator.httpError("should return 401 for invalid token")(401)(
170
+ * await TestValidator.httpError("should return 401 for invalid token", 401,
197
171
  * async () => await api.functional.getProtectedResource("invalid-token")
198
172
  * );
199
173
  *
200
174
  * // Validate multiple possible error codes
201
- * await TestValidator.httpError("should return client error")(400, 404, 422)(
175
+ * await TestValidator.httpError("should return client error", [400, 404, 422],
202
176
  * async () => await api.functional.updateNonexistentResource(data)
203
177
  * );
204
178
  *
205
179
  * // Validate server errors
206
- * TestValidator.httpError("should handle server errors")(500, 502, 503)(
180
+ * TestValidator.httpError("should handle server errors", [500, 502, 503],
207
181
  * () => callFaultyEndpoint()
208
182
  * );
209
183
  * ```;
210
184
  *
211
185
  * @param title - Descriptive title used in error messages
212
- * @returns A currying function that accepts status codes, then the task
213
- * function
186
+ * @param status - Expected status code(s), can be a single number or array
187
+ * @param task - The function that should throw an HttpError
188
+ * @returns Void or Promise<void> based on the input type
214
189
  * @throws Error when function doesn't throw HttpError or status code doesn't
215
190
  * match
216
191
  */
217
- const httpError: (title: string) => (...statuses: number[]) => <T>(task: () => T) => T extends Promise<any> ? Promise<void> : void;
218
- /**
219
- * Safely executes a function and captures any errors without throwing.
220
- *
221
- * Utility function for error handling in tests. Executes the provided
222
- * function and returns any error that occurs, or null if successful. Supports
223
- * both synchronous and asynchronous functions. Useful for testing error
224
- * conditions without stopping test execution.
225
- *
226
- * @example
227
- * ```typescript
228
- * // Synchronous error capture
229
- * const error = TestValidator.proceed(() => {
230
- * throw new Error("Something went wrong");
231
- * });
232
- * console.log(error?.message); // "Something went wrong"
233
- *
234
- * // Asynchronous error capture
235
- * const asyncError = await TestValidator.proceed(async () => {
236
- * await failingAsyncOperation();
237
- * });
238
- *
239
- * // Success case
240
- * const noError = TestValidator.proceed(() => {
241
- * return "success";
242
- * });
243
- * console.log(noError); // null
244
- * ```;
245
- *
246
- * @param task - Function to execute safely
247
- * @returns Error object if function throws/rejects, null if successful
248
- */
249
- function proceed(task: () => Promise<any>): Promise<Error | null>;
250
- function proceed(task: () => any): Error | null;
192
+ function httpError<T>(title: string, status: number | number[], task: () => T): T extends Promise<any> ? Promise<void> : void;
251
193
  /**
252
194
  * Validates pagination index API results against expected entity order.
253
195
  *
@@ -262,8 +204,7 @@ export declare namespace TestValidator {
262
204
  * const expectedArticles = await db.articles.findAll({ order: 'created_at DESC' });
263
205
  * const actualArticles = await api.functional.getArticles({ page: 1, limit: 10 });
264
206
  *
265
- * TestValidator.index("article pagination order")(expectedArticles)(
266
- * actualArticles,
207
+ * TestValidator.index("article pagination order", expectedArticles, actualArticles,
267
208
  * true // enable trace logging
268
209
  * );
269
210
  *
@@ -271,16 +212,16 @@ export declare namespace TestValidator {
271
212
  * const manuallyFilteredUsers = allUsers.filter(u => u.name.includes("John"));
272
213
  * const apiSearchResults = await api.functional.searchUsers({ query: "John" });
273
214
  *
274
- * TestValidator.index("user search results")(manuallyFilteredUsers)(
275
- * apiSearchResults
276
- * );
215
+ * TestValidator.index("user search results", manuallyFilteredUsers, apiSearchResults);
277
216
  * ```;
278
217
  *
279
218
  * @param title - Descriptive title used in error messages when order differs
280
- * @returns A currying function chain: expected entities, then actual entities
219
+ * @param expected - The expected entities in correct order
220
+ * @param gotten - The actual entities returned by the API
221
+ * @param trace - Optional flag to enable debug logging (default: false)
281
222
  * @throws Error when entity order differs between expected and actual results
282
223
  */
283
- const index: (title: string) => <Solution extends IEntity<any>>(expected: Solution[]) => <Summary extends IEntity<any>>(gotten: Summary[], trace?: boolean) => void;
224
+ const index: <Summary extends IEntity<any>>(title: string, expected: Summary[], gotten: Summary[], trace?: boolean) => void;
284
225
  /**
285
226
  * Validates search functionality by testing API results against manual
286
227
  * filtering.
@@ -292,38 +233,49 @@ export declare namespace TestValidator {
292
233
  *
293
234
  * @example
294
235
  * ```typescript
295
- * // Test article search functionality
236
+ * // Test article search functionality with exact matching
296
237
  * const allArticles = await db.articles.findAll();
297
- * const searchValidator = TestValidator.search("article search API")(
298
- * (req) => api.searchArticles(req)
299
- * )(allArticles, 5); // test with 5 random samples
238
+ * const searchValidator = TestValidator.search(
239
+ * "article search API",
240
+ * (req) => api.searchArticles(req),
241
+ * allArticles,
242
+ * 5 // test with 5 random samples
243
+ * );
300
244
  *
245
+ * // Test exact match search
301
246
  * await searchValidator({
302
- * fields: ["title", "content"],
303
- * values: (article) => [article.title.split(" ")[0]], // first word
304
- * filter: (article, [keyword]) =>
305
- * article.title.includes(keyword) || article.content.includes(keyword),
247
+ * fields: ["title"],
248
+ * values: (article) => [article.title], // full title for exact match
249
+ * filter: (article, [title]) => article.title === title, // exact match
250
+ * request: ([title]) => ({ search: { title } })
251
+ * });
252
+ *
253
+ * // Test partial match search with includes
254
+ * await searchValidator({
255
+ * fields: ["content"],
256
+ * values: (article) => [article.content.substring(0, 20)], // partial content
257
+ * filter: (article, [keyword]) => article.content.includes(keyword),
306
258
  * request: ([keyword]) => ({ q: keyword })
307
259
  * });
308
260
  *
309
- * // Test user search with multiple criteria
310
- * await TestValidator.search("user search with filters")(
311
- * (req) => api.getUsers(req)
312
- * )(allUsers, 3)({
313
- * fields: ["status", "role"],
314
- * values: (user) => [user.status, user.role],
315
- * filter: (user, [status, role]) =>
316
- * user.status === status && user.role === role,
317
- * request: ([status, role]) => ({ status, role })
261
+ * // Test multi-field search with exact matching
262
+ * await searchValidator({
263
+ * fields: ["writer", "title"],
264
+ * values: (article) => [article.writer, article.title],
265
+ * filter: (article, [writer, title]) =>
266
+ * article.writer === writer && article.title === title,
267
+ * request: ([writer, title]) => ({ search: { writer, title } })
318
268
  * });
319
269
  * ```;
320
270
  *
321
271
  * @param title - Descriptive title used in error messages when search fails
322
- * @returns A currying function chain: API getter function, then dataset and
323
- * sample count
272
+ * @param getter - API function that performs the search
273
+ * @param total - Complete dataset to sample from for testing
274
+ * @param sampleCount - Number of random samples to test (default: 1)
275
+ * @returns A function that accepts search configuration properties
324
276
  * @throws Error when API search results don't match manual filtering results
325
277
  */
326
- const search: (title: string) => <Entity extends IEntity<any>, Request>(getter: (input: Request) => Promise<Entity[]>) => (total: Entity[], sampleCount?: number) => <Values extends any[]>(props: ISearchProps<Entity, Values, Request>) => Promise<void>;
278
+ const search: <Entity extends IEntity<any>, Request>(title: string, getter: (input: Request) => Promise<Entity[]>, total: Entity[], sampleCount?: number) => <Values extends any[]>(props: ISearchProps<Entity, Values, Request>) => Promise<void>;
327
279
  /**
328
280
  * Configuration interface for search validation functionality.
329
281
  *
@@ -372,37 +324,47 @@ export declare namespace TestValidator {
372
324
  *
373
325
  * @example
374
326
  * ```typescript
375
- * // Test single field sorting
376
- * const sortValidator = TestValidator.sort("article sorting")(
327
+ * // Test single field sorting with GaffComparator
328
+ * const sortValidator = TestValidator.sort(
329
+ * "article sorting",
377
330
  * (sortable) => api.getArticles({ sort: sortable })
378
331
  * )("created_at")(
379
- * (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
332
+ * GaffComparator.dates((a) => a.created_at)
380
333
  * );
381
334
  *
382
335
  * await sortValidator("+"); // ascending
383
336
  * await sortValidator("-"); // descending
384
337
  *
385
- * // Test multi-field sorting with filtering
386
- * const userSortValidator = TestValidator.sort("user sorting")(
338
+ * // Test multi-field sorting with GaffComparator
339
+ * const userSortValidator = TestValidator.sort(
340
+ * "user sorting",
387
341
  * (sortable) => api.getUsers({ sort: sortable })
388
- * )("status", "created_at")(
389
- * (a, b) => {
390
- * if (a.status !== b.status) return a.status.localeCompare(b.status);
391
- * return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
392
- * },
342
+ * )("lastName", "firstName")(
343
+ * GaffComparator.strings((user) => [user.lastName, user.firstName]),
393
344
  * (user) => user.isActive // only test active users
394
345
  * );
395
346
  *
396
347
  * await userSortValidator("+", true); // ascending with trace logging
348
+ *
349
+ * // Custom comparator for complex logic
350
+ * const customSortValidator = TestValidator.sort(
351
+ * "custom sorting",
352
+ * (sortable) => api.getProducts({ sort: sortable })
353
+ * )("price", "rating")(
354
+ * (a, b) => {
355
+ * const priceDiff = a.price - b.price;
356
+ * return priceDiff !== 0 ? priceDiff : b.rating - a.rating; // price asc, rating desc
357
+ * }
358
+ * );
397
359
  * ```;
398
360
  *
399
361
  * @param title - Descriptive title used in error messages when sorting fails
400
- * @returns A currying function chain: API getter, field names, comparator,
401
- * then direction
362
+ * @param getter - API function that fetches sorted data
363
+ * @returns A currying function chain: field names, comparator, then direction
402
364
  * @throws Error when API results are not properly sorted according to
403
365
  * specification
404
366
  */
405
- const sort: (title: string) => <T extends object, Fields extends string, Sortable extends Array<`-${Fields}` | `+${Fields}`> = Array<`-${Fields}` | `+${Fields}`>>(getter: (sortable: Sortable) => Promise<T[]>) => (...fields: Fields[]) => (comp: (x: T, y: T) => number, filter?: (elem: T) => boolean) => (direction: "+" | "-", trace?: boolean) => Promise<void>;
367
+ const sort: <T extends object, Fields extends string, Sortable extends Array<`-${Fields}` | `+${Fields}`> = Array<`-${Fields}` | `+${Fields}`>>(title: string, getter: (sortable: Sortable) => Promise<T[]>) => (...fields: Fields[]) => (comp: (x: T, y: T) => number, filter?: (elem: T) => boolean) => (direction: "+" | "-", trace?: boolean) => Promise<void>;
406
368
  /**
407
369
  * Type alias for sortable field specifications.
408
370
  *