@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.
@@ -1,87 +1,386 @@
1
1
  /**
2
- * Test validator.
2
+ * A comprehensive collection of E2E validation utilities for testing
3
+ * applications.
3
4
  *
4
- * `TestValidator` is a collection gathering E2E validation functions.
5
+ * TestValidator provides type-safe validation functions for common testing
6
+ * scenarios including condition checking, equality validation, error testing,
7
+ * HTTP error validation, pagination testing, search functionality validation,
8
+ * and sorting validation.
9
+ *
10
+ * All functions follow a currying pattern to enable reusable test
11
+ * configurations and provide detailed error messages for debugging failed
12
+ * assertions.
5
13
  *
6
14
  * @author Jeongho Nam - https://github.com/samchon
15
+ * @example
16
+ * ```typescript
17
+ * // Basic condition testing
18
+ * TestValidator.predicate("user should be authenticated")(user.isAuthenticated);
19
+ *
20
+ * // Equality validation
21
+ * TestValidator.equals("API response should match expected")(expected)(actual);
22
+ *
23
+ * // Error validation
24
+ * TestValidator.error("should throw on invalid input")(() => validateInput(""));
25
+ * ```;
7
26
  */
8
27
  export declare namespace TestValidator {
9
28
  /**
10
- * Test whether condition is satisfied.
29
+ * Validates that a given condition evaluates to true.
30
+ *
31
+ * Supports synchronous boolean values, synchronous functions returning
32
+ * boolean, and asynchronous functions returning Promise<boolean>. The return
33
+ * type is automatically inferred based on the input type.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // Synchronous boolean
38
+ * TestValidator.predicate("user should exist")(user !== null);
39
+ *
40
+ * // Synchronous function
41
+ * TestValidator.predicate("array should be empty")(() => arr.length === 0);
42
+ *
43
+ * // Asynchronous function
44
+ * await TestValidator.predicate("database should be connected")(
45
+ * async () => await db.ping()
46
+ * );
47
+ * ```;
11
48
  *
12
- * @param title Title of error message when condition is not satisfied
13
- * @return Currying function
49
+ * @param title - Descriptive title used in error messages when validation
50
+ * fails
51
+ * @returns A currying function that accepts the condition to validate
52
+ * @throws Error with descriptive message when condition is not satisfied
14
53
  */
15
54
  const predicate: (title: string) => <T extends boolean | (() => boolean) | (() => Promise<boolean>)>(condition: T) => T extends () => Promise<boolean> ? Promise<void> : void;
16
55
  /**
17
- * Test whether two values are equal.
56
+ * Validates deep equality between two values using JSON comparison.
18
57
  *
19
- * If you want to validate `covers` relationship,
20
- * call smaller first and then larger.
58
+ * Performs recursive comparison of objects and arrays. Supports an optional
59
+ * exception filter to ignore specific keys during comparison. Useful for
60
+ * validating API responses, data transformations, and object state changes.
21
61
  *
22
- * Otherwise you wanna non equals validator, combine with {@link error}.
62
+ * @example
63
+ * ```typescript
64
+ * // Basic equality
65
+ * TestValidator.equals("response should match expected")(expectedUser)(actualUser);
23
66
  *
24
- * @param title Title of error message when different
25
- * @param exception Exception filter for ignoring some keys
26
- * @returns Currying function
67
+ * // Ignore timestamps in comparison
68
+ * TestValidator.equals("user data should match", (key) => key === "updatedAt")(
69
+ * expectedUser
70
+ * )(actualUser);
71
+ *
72
+ * // Validate API response structure
73
+ * const validateResponse = TestValidator.equals("API response structure");
74
+ * validateResponse({ id: 1, name: "John" })({ id: 1, name: "John" });
75
+ * ```;
76
+ *
77
+ * @param title - Descriptive title used in error messages when values differ
78
+ * @param exception - Optional filter function to exclude specific keys from
79
+ * comparison
80
+ * @returns A currying function chain: first accepts expected value, then
81
+ * actual value
82
+ * @throws Error with detailed diff information when values are not equal
27
83
  */
28
- const equals: (title: string, exception?: (key: string) => boolean) => <T>(x: T) => (y: T) => void;
84
+ const equals: (title: string, exception?: (key: string) => boolean) => <T>(x: T) => (y: T | null | undefined) => void;
29
85
  /**
30
- * Test whether error occurs.
86
+ * Validates deep inequality between two values using JSON comparison.
87
+ *
88
+ * Performs recursive comparison of objects and arrays to ensure they are NOT
89
+ * equal. Supports an optional exception filter to ignore specific keys during
90
+ * comparison. Useful for validating that data has changed, objects are
91
+ * different, or mutations have occurred.
31
92
  *
32
- * If error occurs, nothing would be happened.
93
+ * @example
94
+ * ```typescript
95
+ * // Basic inequality
96
+ * TestValidator.notEquals("user should be different after update")(originalUser)(updatedUser);
33
97
  *
34
- * However, no error exists, then exception would be thrown.
98
+ * // Ignore timestamps in comparison
99
+ * TestValidator.notEquals("user data should differ", (key) => key === "updatedAt")(
100
+ * originalUser
101
+ * )(modifiedUser);
35
102
  *
36
- * @param title Title of exception because of no error exists
103
+ * // Validate state changes
104
+ * const validateStateChange = TestValidator.notEquals("state should have changed");
105
+ * validateStateChange(initialState)(currentState);
106
+ * ```;
107
+ *
108
+ * @param title - Descriptive title used in error messages when values are
109
+ * equal
110
+ * @param exception - Optional filter function to exclude specific keys from
111
+ * comparison
112
+ * @returns A currying function chain: first accepts expected value, then
113
+ * actual value
114
+ * @throws Error when values are equal (indicating validation failure)
115
+ */
116
+ const notEquals: (title: string, exception?: (key: string) => boolean) => <T>(x: T) => (y: T | null | undefined) => void;
117
+ /**
118
+ * Validates that a function throws an error or rejects when executed.
119
+ *
120
+ * Expects the provided function to fail. If the function executes
121
+ * successfully without throwing an error or rejecting, this validator will
122
+ * throw an exception. Supports both synchronous and asynchronous functions.
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * // Synchronous error validation
127
+ * TestValidator.error("should reject invalid email")(
128
+ * () => validateEmail("invalid-email")
129
+ * );
130
+ *
131
+ * // Asynchronous error validation
132
+ * await TestValidator.error("should reject unauthorized access")(
133
+ * async () => await api.functional.getSecretData()
134
+ * );
135
+ *
136
+ * // Validate input validation
137
+ * TestValidator.error("should throw on empty string")(
138
+ * () => processRequiredInput("")
139
+ * );
140
+ * ```;
141
+ *
142
+ * @param title - Descriptive title used in error messages when no error
143
+ * occurs
144
+ * @returns A currying function that accepts the task function to validate
145
+ * @throws Error when the task function does not throw an error or reject
37
146
  */
38
147
  const error: (title: string) => <T>(task: () => T) => T extends Promise<any> ? Promise<void> : void;
148
+ /**
149
+ * Validates that a function throws an HTTP error with specific status codes.
150
+ *
151
+ * Specialized error validator for HTTP operations. Validates that the
152
+ * function throws an HttpError with one of the specified status codes. Useful
153
+ * for testing API endpoints, authentication, and authorization logic.
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * // Validate 401 Unauthorized
158
+ * await TestValidator.httpError("should return 401 for invalid token")(401)(
159
+ * async () => await api.functional.getProtectedResource("invalid-token")
160
+ * );
161
+ *
162
+ * // Validate multiple possible error codes
163
+ * await TestValidator.httpError("should return client error")(400, 404, 422)(
164
+ * async () => await api.functional.updateNonexistentResource(data)
165
+ * );
166
+ *
167
+ * // Validate server errors
168
+ * TestValidator.httpError("should handle server errors")(500, 502, 503)(
169
+ * () => callFaultyEndpoint()
170
+ * );
171
+ * ```;
172
+ *
173
+ * @param title - Descriptive title used in error messages
174
+ * @returns A currying function that accepts status codes, then the task
175
+ * function
176
+ * @throws Error when function doesn't throw HttpError or status code doesn't
177
+ * match
178
+ */
39
179
  const httpError: (title: string) => (...statuses: number[]) => <T>(task: () => T) => T extends Promise<any> ? Promise<void> : void;
180
+ /**
181
+ * Safely executes a function and captures any errors without throwing.
182
+ *
183
+ * Utility function for error handling in tests. Executes the provided
184
+ * function and returns any error that occurs, or null if successful. Supports
185
+ * both synchronous and asynchronous functions. Useful for testing error
186
+ * conditions without stopping test execution.
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * // Synchronous error capture
191
+ * const error = TestValidator.proceed(() => {
192
+ * throw new Error("Something went wrong");
193
+ * });
194
+ * console.log(error?.message); // "Something went wrong"
195
+ *
196
+ * // Asynchronous error capture
197
+ * const asyncError = await TestValidator.proceed(async () => {
198
+ * await failingAsyncOperation();
199
+ * });
200
+ *
201
+ * // Success case
202
+ * const noError = TestValidator.proceed(() => {
203
+ * return "success";
204
+ * });
205
+ * console.log(noError); // null
206
+ * ```;
207
+ *
208
+ * @param task - Function to execute safely
209
+ * @returns Error object if function throws/rejects, null if successful
210
+ */
40
211
  function proceed(task: () => Promise<any>): Promise<Error | null>;
41
212
  function proceed(task: () => any): Error | null;
42
213
  /**
43
- * Validate index API.
214
+ * Validates pagination index API results against expected entity order.
215
+ *
216
+ * Compares the order of entities returned by a pagination API with manually
217
+ * sorted expected results. Validates that entity IDs appear in the correct
218
+ * sequence. Commonly used for testing database queries, search results, and
219
+ * any paginated data APIs.
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * // Test article pagination
224
+ * const expectedArticles = await db.articles.findAll({ order: 'created_at DESC' });
225
+ * const actualArticles = await api.functional.getArticles({ page: 1, limit: 10 });
44
226
  *
45
- * Test whether two indexed values are equal.
227
+ * TestValidator.index("article pagination order")(expectedArticles)(
228
+ * actualArticles,
229
+ * true // enable trace logging
230
+ * );
46
231
  *
47
- * If two values are different, then exception would be thrown.
232
+ * // Test user search results
233
+ * const manuallyFilteredUsers = allUsers.filter(u => u.name.includes("John"));
234
+ * const apiSearchResults = await api.functional.searchUsers({ query: "John" });
48
235
  *
49
- * @param title Title of error message when different
50
- * @return Currying function
236
+ * TestValidator.index("user search results")(manuallyFilteredUsers)(
237
+ * apiSearchResults
238
+ * );
239
+ * ```;
51
240
  *
52
- * @example https://github.com/samchon/nestia-template/blob/master/src/test/features/api/bbs/test_api_bbs_article_index_search.ts
241
+ * @param title - Descriptive title used in error messages when order differs
242
+ * @returns A currying function chain: expected entities, then actual entities
243
+ * @throws Error when entity order differs between expected and actual results
53
244
  */
54
245
  const index: (title: string) => <Solution extends IEntity<any>>(expected: Solution[]) => <Summary extends IEntity<any>>(gotten: Summary[], trace?: boolean) => void;
55
246
  /**
56
- * Valiate search options.
247
+ * Validates search functionality by testing API results against manual
248
+ * filtering.
57
249
  *
58
- * Test a pagination API supporting search options.
250
+ * Comprehensive search validation that samples entities from a complete
251
+ * dataset, extracts search values, applies manual filtering, calls the search
252
+ * API, and compares results. Validates that search APIs return the correct
253
+ * subset of data matching the search criteria.
59
254
  *
60
- * @param title Title of error message when searching is invalid
61
- * @returns Currying function
255
+ * @example
256
+ * ```typescript
257
+ * // Test article search functionality
258
+ * const allArticles = await db.articles.findAll();
259
+ * const searchValidator = TestValidator.search("article search API")(
260
+ * (req) => api.searchArticles(req)
261
+ * )(allArticles, 5); // test with 5 random samples
62
262
  *
63
- * @example https://github.com/samchon/nestia-template/blob/master/src/test/features/api/bbs/test_api_bbs_article_index_search.ts
263
+ * await searchValidator({
264
+ * fields: ["title", "content"],
265
+ * values: (article) => [article.title.split(" ")[0]], // first word
266
+ * filter: (article, [keyword]) =>
267
+ * article.title.includes(keyword) || article.content.includes(keyword),
268
+ * request: ([keyword]) => ({ q: keyword })
269
+ * });
270
+ *
271
+ * // Test user search with multiple criteria
272
+ * await TestValidator.search("user search with filters")(
273
+ * (req) => api.getUsers(req)
274
+ * )(allUsers, 3)({
275
+ * fields: ["status", "role"],
276
+ * values: (user) => [user.status, user.role],
277
+ * filter: (user, [status, role]) =>
278
+ * user.status === status && user.role === role,
279
+ * request: ([status, role]) => ({ status, role })
280
+ * });
281
+ * ```;
282
+ *
283
+ * @param title - Descriptive title used in error messages when search fails
284
+ * @returns A currying function chain: API getter function, then dataset and
285
+ * sample count
286
+ * @throws Error when API search results don't match manual filtering results
64
287
  */
65
288
  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>;
289
+ /**
290
+ * Configuration interface for search validation functionality.
291
+ *
292
+ * Defines the structure needed to validate search operations by specifying
293
+ * how to extract search values from entities, filter the dataset manually,
294
+ * and construct API requests.
295
+ *
296
+ * @template Entity - Type of entities being searched, must have an ID field
297
+ * @template Values - Tuple type representing the search values extracted from
298
+ * entities
299
+ * @template Request - Type of the API request object
300
+ */
66
301
  interface ISearchProps<Entity extends IEntity<any>, Values extends any[], Request> {
302
+ /** Field names being searched, used in error messages for identification */
67
303
  fields: string[];
304
+ /**
305
+ * Extracts search values from a sample entity
306
+ *
307
+ * @param entity - The entity to extract search values from
308
+ * @returns Tuple of values used for searching
309
+ */
68
310
  values(entity: Entity): Values;
311
+ /**
312
+ * Manual filter function to determine if an entity matches search criteria
313
+ *
314
+ * @param entity - Entity to test against criteria
315
+ * @param values - Search values to match against
316
+ * @returns True if entity matches the search criteria
317
+ */
69
318
  filter(entity: Entity, values: Values): boolean;
319
+ /**
320
+ * Constructs API request object from search values
321
+ *
322
+ * @param values - Search values to include in request
323
+ * @returns Request object for the search API
324
+ */
70
325
  request(values: Values): Request;
71
326
  }
72
327
  /**
73
- * Validate sorting options.
328
+ * Validates sorting functionality of pagination APIs.
329
+ *
330
+ * Tests sorting operations by calling the API with sort parameters and
331
+ * validating that results are correctly ordered. Supports multiple fields,
332
+ * ascending/descending order, and optional filtering. Provides detailed error
333
+ * reporting for sorting failures.
334
+ *
335
+ * @example
336
+ * ```typescript
337
+ * // Test single field sorting
338
+ * const sortValidator = TestValidator.sort("article sorting")(
339
+ * (sortable) => api.getArticles({ sort: sortable })
340
+ * )("created_at")(
341
+ * (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
342
+ * );
343
+ *
344
+ * await sortValidator("+"); // ascending
345
+ * await sortValidator("-"); // descending
74
346
  *
75
- * Test a pagination API supporting sorting options.
347
+ * // Test multi-field sorting with filtering
348
+ * const userSortValidator = TestValidator.sort("user sorting")(
349
+ * (sortable) => api.getUsers({ sort: sortable })
350
+ * )("status", "created_at")(
351
+ * (a, b) => {
352
+ * if (a.status !== b.status) return a.status.localeCompare(b.status);
353
+ * return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
354
+ * },
355
+ * (user) => user.isActive // only test active users
356
+ * );
76
357
  *
77
- * You can validate detailed sorting options both ascending and descending orders
78
- * with multiple fields. However, as it forms a complicate currying function,
79
- * I recommend you to see below example code before using.
358
+ * await userSortValidator("+", true); // ascending with trace logging
359
+ * ```;
80
360
  *
81
- * @param title Title of error message when sorting is invalid
82
- * @example https://github.com/samchon/nestia-template/blob/master/src/test/features/api/bbs/test_api_bbs_article_index_sort.ts
361
+ * @param title - Descriptive title used in error messages when sorting fails
362
+ * @returns A currying function chain: API getter, field names, comparator,
363
+ * then direction
364
+ * @throws Error when API results are not properly sorted according to
365
+ * specification
83
366
  */
84
367
  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>;
368
+ /**
369
+ * Type alias for sortable field specifications.
370
+ *
371
+ * Represents an array of sort field specifications where each field can be
372
+ * prefixed with '+' for ascending order or '-' for descending order.
373
+ *
374
+ * @example
375
+ * ```typescript
376
+ * type UserSortable = TestValidator.Sortable<"name" | "email" | "created_at">;
377
+ * // Results in: Array<"-name" | "+name" | "-email" | "+email" | "-created_at" | "+created_at">
378
+ *
379
+ * const userSort: UserSortable = ["+name", "-created_at"];
380
+ * ```;
381
+ *
382
+ * @template Literal - String literal type representing available field names
383
+ */
85
384
  type Sortable<Literal extends string> = Array<`-${Literal}` | `+${Literal}`>;
86
385
  }
87
386
  interface IEntity<Type extends string | number | bigint> {