@nestia/e2e 0.1.1 → 0.1.3

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,145 +1,208 @@
1
- import { is_sorted } from "tstl/ranges";
2
-
3
- /**
4
- * Test validator.
5
- *
6
- * `TestValidator` is a collection gathering E2E validation functions.
7
- *
8
- * @author Jeongho Nam - https://github.com/samchon
9
- */
10
- export namespace TestValidator {
11
- /**
12
- * Test whether error occurs.
13
- *
14
- * If error occurs, nothing would be happened.
15
- *
16
- * However, no error exists, then exception would be thrown.
17
- *
18
- * @param title Title of exception because of no error exists
19
- */
20
- export const error =
21
- (title: string) =>
22
- async (task: () => any | Promise<any>): Promise<void> => {
23
- try {
24
- await task();
25
- } catch {
26
- return;
27
- }
28
- throw new Error(`Bug on ${title}: exception must be thrown.`);
29
- };
30
-
31
- /**
32
- * Validate index API.
33
- *
34
- * Test whether two indexed values are equal.
35
- *
36
- * If two values are different, then exception would be thrown.
37
- *
38
- * @param title Title of error message when different
39
- * @return Currying function
40
- *
41
- * @example https://github.com/samchon/nestia-template/blob/master/src/test/features/api/bbs/test_api_bbs_article_index_search.ts
42
- */
43
- export const index =
44
- (title: string) =>
45
- <Solution extends IEntity<any>>(expected: Solution[]) =>
46
- <Summary extends IEntity<any>>(
47
- gotten: Summary[],
48
- trace: boolean = true,
49
- ): void => {
50
- const length: number = Math.min(expected.length, gotten.length);
51
- expected = expected.slice(0, length);
52
- gotten = gotten.slice(0, length);
53
-
54
- const xIds: string[] = get_ids(expected).slice(0, length);
55
- const yIds: string[] = get_ids(gotten)
56
- .filter((id) => id >= xIds[0])
57
- .slice(0, length);
58
-
59
- const equals: boolean = xIds.every((x, i) => x === yIds[i]);
60
- if (equals === true) return;
61
- else if (trace === true)
62
- console.log({
63
- expected: xIds,
64
- gotten: yIds,
65
- });
66
- throw new Error(
67
- `Bug on ${title}: result of the index is different with manual aggregation.`,
68
- );
69
- };
70
-
71
- /**
72
- * Validate sorting options.
73
- *
74
- * Test a pagination API supporting sorting options.
75
- *
76
- * You can validate detailed sorting options both asceding and descending orders
77
- * with multiple fields. However, as it forms a complicate currying function,
78
- * I recomend you to see below example code before using.
79
- *
80
- * @param title Title of error messaeg when sorting is invalid
81
- *
82
- * @example https://github.com/samchon/nestia-template/blob/master/src/test/features/api/bbs/test_api_bbs_article_index_sort.ts
83
- */
84
- export const sort =
85
- (title: string) =>
86
- /**
87
- * @param getter A pagination API function to be called
88
- */
89
- <
90
- T extends object,
91
- Fields extends string,
92
- Sortable extends Array<`-${Fields}` | `+${Fields}`>,
93
- >(
94
- getter: (sortable: Sortable) => Promise<T[]>,
95
- ) =>
96
- /**
97
- * @param fields List of fields to be sorted
98
- */
99
- (...fields: Fields[]) =>
100
- /**
101
- * @param comp Comparator function for validation
102
- * @param filter Filter function for data if required
103
- */
104
- (comp: (x: T, y: T) => number, filter?: (elem: T) => boolean) =>
105
- /**
106
- * @param direction "+" means ascending order, and "-" means descending order
107
- */
108
- async (direction: "+" | "-", trace: boolean = true) => {
109
- let data: T[] = await getter(
110
- fields.map(
111
- (field) => `${direction}${field}` as const,
112
- ) as Sortable,
113
- );
114
- if (filter) data = data.filter(filter);
115
-
116
- const reversed: typeof comp =
117
- direction === "+" ? comp : (x, y) => comp(y, x);
118
- if (is_sorted(data, (x, y) => reversed(x, y) < 0) === false) {
119
- if (
120
- fields.length === 1 &&
121
- data.length &&
122
- (data as any)[0][fields[0]] !== undefined &&
123
- trace
124
- )
125
- console.log(data.map((elem) => (elem as any)[fields[0]]));
126
- throw new Error(
127
- `Bug on ${title}: wrong sorting on ${direction}(${fields.join(
128
- ", ",
129
- )}).`,
130
- );
131
- }
132
- };
133
-
134
- export type Sortable<Literal extends string> = Array<
135
- `-${Literal}` | `+${Literal}`
136
- >;
137
- }
138
-
139
- interface IEntity<Type extends string | number | bigint> {
140
- id: Type;
141
- }
142
-
143
- function get_ids<Entity extends IEntity<any>>(entities: Entity[]): string[] {
144
- return entities.map((entity) => entity.id).sort((x, y) => (x < y ? -1 : 1));
145
- }
1
+ import { is_sorted } from "tstl/ranges";
2
+
3
+ import { RandomGenerator } from "./RandomGenerator";
4
+
5
+ /**
6
+ * Test validator.
7
+ *
8
+ * `TestValidator` is a collection gathering E2E validation functions.
9
+ *
10
+ * @author Jeongho Nam - https://github.com/samchon
11
+ */
12
+ export namespace TestValidator {
13
+ /**
14
+ * Test whether error occurs.
15
+ *
16
+ * If error occurs, nothing would be happened.
17
+ *
18
+ * However, no error exists, then exception would be thrown.
19
+ *
20
+ * @param title Title of exception because of no error exists
21
+ */
22
+ export const error =
23
+ (title: string) =>
24
+ async (task: () => any | Promise<any>): Promise<void> => {
25
+ try {
26
+ await task();
27
+ } catch {
28
+ return;
29
+ }
30
+ throw new Error(`Bug on ${title}: exception must be thrown.`);
31
+ };
32
+
33
+ /**
34
+ * Validate index API.
35
+ *
36
+ * Test whether two indexed values are equal.
37
+ *
38
+ * If two values are different, then exception would be thrown.
39
+ *
40
+ * @param title Title of error message when different
41
+ * @return Currying function
42
+ *
43
+ * @example https://github.com/samchon/nestia-template/blob/master/src/test/features/api/bbs/test_api_bbs_article_index_search.ts
44
+ */
45
+ export const index =
46
+ (title: string) =>
47
+ <Solution extends IEntity<any>>(expected: Solution[]) =>
48
+ <Summary extends IEntity<any>>(
49
+ gotten: Summary[],
50
+ trace: boolean = true,
51
+ ): void => {
52
+ const length: number = Math.min(expected.length, gotten.length);
53
+ expected = expected.slice(0, length);
54
+ gotten = gotten.slice(0, length);
55
+
56
+ const xIds: string[] = get_ids(expected).slice(0, length);
57
+ const yIds: string[] = get_ids(gotten)
58
+ .filter((id) => id >= xIds[0])
59
+ .slice(0, length);
60
+
61
+ const equals: boolean = xIds.every((x, i) => x === yIds[i]);
62
+ if (equals === true) return;
63
+ else if (trace === true)
64
+ console.log({
65
+ expected: xIds,
66
+ gotten: yIds,
67
+ });
68
+ throw new Error(
69
+ `Bug on ${title}: result of the index is different with manual aggregation.`,
70
+ );
71
+ };
72
+
73
+ /**
74
+ * Valiate search options.
75
+ *
76
+ * Test a pagination API supporting search options.
77
+ *
78
+ * @param title Title of error message when searching is invalid
79
+ * @returns Currying function
80
+ *
81
+ * @example https://github.com/samchon/nestia-template/blob/master/src/test/features/api/bbs/test_api_bbs_article_index_search.ts
82
+ */
83
+ export const search =
84
+ (title: string) =>
85
+ /**
86
+ * @param getter A pagination API function to be called
87
+ * @param validator Validator function if required
88
+ */
89
+ <Entity extends IEntity<any>, Input>(
90
+ getter: (input: Input) => Promise<Entity[]>,
91
+ validator?: (data: Entity[]) => any,
92
+ ) =>
93
+ /**
94
+ * @param total Total entity records for comparison
95
+ * @param sampleCount Sampling count. Default is 1
96
+ */
97
+ (total: Entity[], sampleCount: number = 1) =>
98
+ /**
99
+ * @param props Search properties
100
+ */
101
+ async <Values extends any[]>(
102
+ props: ISearchProps<Entity, Values, Input>,
103
+ ) => {
104
+ const samples: Entity[] = RandomGenerator.sample(
105
+ total,
106
+ sampleCount,
107
+ );
108
+ for (const s of samples) {
109
+ const values: Values = props.values(s);
110
+ const filtered: Entity[] = total.filter((entity) =>
111
+ props.filter(entity, values),
112
+ );
113
+ const gotten: Entity[] = await getter(props.search(values));
114
+ if (validator) validator(gotten);
115
+
116
+ TestValidator.index(`${title} (${props.fields.join(", ")})`)(
117
+ filtered,
118
+ )(gotten);
119
+ }
120
+ };
121
+
122
+ export interface ISearchProps<
123
+ Entity extends IEntity<any>,
124
+ Values extends any[],
125
+ Input,
126
+ > {
127
+ fields: string[];
128
+ values(entity: Entity): Values;
129
+ filter(entity: Entity, values: Values): boolean;
130
+ search(values: Values): Input;
131
+ }
132
+
133
+ /**
134
+ * Validate sorting options.
135
+ *
136
+ * Test a pagination API supporting sorting options.
137
+ *
138
+ * You can validate detailed sorting options both asceding and descending orders
139
+ * with multiple fields. However, as it forms a complicate currying function,
140
+ * I recomend you to see below example code before using.
141
+ *
142
+ * @param title Title of error message when sorting is invalid
143
+ * @example https://github.com/samchon/nestia-template/blob/master/src/test/features/api/bbs/test_api_bbs_article_index_sort.ts
144
+ */
145
+ export const sort =
146
+ (title: string) =>
147
+ /**
148
+ * @param getter A pagination API function to be called
149
+ */
150
+ <
151
+ T extends object,
152
+ Fields extends string,
153
+ Sortable extends Array<`-${Fields}` | `+${Fields}`>,
154
+ >(
155
+ getter: (sortable: Sortable) => Promise<T[]>,
156
+ validator?: (data: T[]) => any,
157
+ ) =>
158
+ /**
159
+ * @param fields List of fields to be sorted
160
+ */
161
+ (...fields: Fields[]) =>
162
+ /**
163
+ * @param comp Comparator function for validation
164
+ * @param filter Filter function for data if required
165
+ */
166
+ (comp: (x: T, y: T) => number, filter?: (elem: T) => boolean) =>
167
+ /**
168
+ * @param direction "+" means ascending order, and "-" means descending order
169
+ */
170
+ async (direction: "+" | "-", trace: boolean = true) => {
171
+ let data: T[] = await getter(
172
+ fields.map(
173
+ (field) => `${direction}${field}` as const,
174
+ ) as Sortable,
175
+ );
176
+ if (validator) validator(data);
177
+ if (filter) data = data.filter(filter);
178
+
179
+ const reversed: typeof comp =
180
+ direction === "+" ? comp : (x, y) => comp(y, x);
181
+ if (is_sorted(data, (x, y) => reversed(x, y) < 0) === false) {
182
+ if (
183
+ fields.length === 1 &&
184
+ data.length &&
185
+ (data as any)[0][fields[0]] !== undefined &&
186
+ trace
187
+ )
188
+ console.log(data.map((elem) => (elem as any)[fields[0]]));
189
+ throw new Error(
190
+ `Bug on ${title}: wrong sorting on ${direction}(${fields.join(
191
+ ", ",
192
+ )}).`,
193
+ );
194
+ }
195
+ };
196
+
197
+ export type Sortable<Literal extends string> = Array<
198
+ `-${Literal}` | `+${Literal}`
199
+ >;
200
+ }
201
+
202
+ interface IEntity<Type extends string | number | bigint> {
203
+ id: Type;
204
+ }
205
+
206
+ function get_ids<Entity extends IEntity<any>>(entities: Entity[]): string[] {
207
+ return entities.map((entity) => entity.id).sort((x, y) => (x < y ? -1 : 1));
208
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as e2e from "./module";
2
-
3
- export default e2e;
4
- export * from "./module";
1
+ import * as e2e from "./module";
2
+
3
+ export default e2e;
4
+ export * from "./module";
package/src/module.ts CHANGED
@@ -1,6 +1,6 @@
1
- export * from "./ArrayUtil";
2
- export * from "./DynamicExecutor";
3
- export * from "./GaffComparator";
4
- export * from "./RandomGenerator";
5
- export * from "./StopWatch";
6
- export * from "./TestValidator";
1
+ export * from "./ArrayUtil";
2
+ export * from "./DynamicExecutor";
3
+ export * from "./GaffComparator";
4
+ export * from "./RandomGenerator";
5
+ export * from "./StopWatch";
6
+ export * from "./TestValidator";