@nestia/e2e 0.4.2 → 0.5.0-dev.20240617
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/DynamicBenchmarker.d.ts +42 -0
- package/lib/DynamicBenchmarker.js +404 -0
- package/lib/DynamicBenchmarker.js.map +1 -0
- package/lib/RandomGenerator.js +2 -2
- package/lib/RandomGenerator.js.map +1 -1
- package/lib/TestValidator.d.ts +1 -1
- package/lib/TestValidator.js +7 -3
- package/lib/TestValidator.js.map +1 -1
- package/lib/module.d.ts +1 -0
- package/lib/module.js +1 -0
- package/lib/module.js.map +1 -1
- package/lib/structures/IBenchmarkEvent.d.ts +8 -0
- package/lib/structures/IBenchmarkEvent.js +3 -0
- package/lib/structures/IBenchmarkEvent.js.map +1 -0
- package/lib/structures/IBenchmarkMaster.d.ts +6 -0
- package/lib/structures/IBenchmarkMaster.js +3 -0
- package/lib/structures/IBenchmarkMaster.js.map +1 -0
- package/lib/structures/IBenchmarkServant.d.ts +7 -0
- package/lib/structures/IBenchmarkServant.js +3 -0
- package/lib/structures/IBenchmarkServant.js.map +1 -0
- package/package.json +5 -3
- package/src/ArrayUtil.ts +98 -98
- package/src/DynamicBenchmarker.ts +246 -0
- package/src/GaffComparator.ts +69 -69
- package/src/RandomGenerator.ts +161 -161
- package/src/StopWatch.ts +38 -38
- package/src/TestValidator.ts +343 -339
- package/src/index.ts +4 -4
- package/src/internal/json_equal_to.ts +34 -34
- package/src/module.ts +7 -6
- package/src/structures/IBenchmarkEvent.ts +9 -0
- package/src/structures/IBenchmarkMaster.ts +5 -0
- package/src/structures/IBenchmarkServant.ts +8 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IBenchmarkServant.js","sourceRoot":"","sources":["../../src/structures/IBenchmarkServant.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestia/e2e",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-dev.20240617",
|
|
4
4
|
"description": "E2E test utilify functions",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -38,12 +38,14 @@
|
|
|
38
38
|
"ts-node": "^10.9.1",
|
|
39
39
|
"ts-patch": "^3.1.0",
|
|
40
40
|
"typescript": "^5.4.2",
|
|
41
|
-
"typia": "^
|
|
41
|
+
"typia": "^6.1.0"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
+
"@nestia/fetcher": "^3.2.2",
|
|
44
45
|
"chalk": "^4.1.2",
|
|
45
46
|
"cli": "^1.0.1",
|
|
46
|
-
"
|
|
47
|
+
"tgrid": "^1.0.1",
|
|
48
|
+
"tstl": "^3.0.0"
|
|
47
49
|
},
|
|
48
50
|
"files": [
|
|
49
51
|
"lib",
|
package/src/ArrayUtil.ts
CHANGED
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility functions for arrays.
|
|
3
|
-
*
|
|
4
|
-
* @author Jeongho Nam - https://github.com/samchon
|
|
5
|
-
*/
|
|
6
|
-
export namespace ArrayUtil {
|
|
7
|
-
export const asyncFilter =
|
|
8
|
-
<Input>(elements: readonly Input[]) =>
|
|
9
|
-
async (
|
|
10
|
-
pred: (
|
|
11
|
-
elem: Input,
|
|
12
|
-
index: number,
|
|
13
|
-
array: readonly Input[],
|
|
14
|
-
) => Promise<boolean>,
|
|
15
|
-
): Promise<Input[]> => {
|
|
16
|
-
const ret: Input[] = [];
|
|
17
|
-
await asyncForEach(elements)(async (elem, index, array) => {
|
|
18
|
-
const flag: boolean = await pred(elem, index, array);
|
|
19
|
-
if (flag === true) ret.push(elem);
|
|
20
|
-
});
|
|
21
|
-
return ret;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export const asyncForEach =
|
|
25
|
-
<Input>(elements: readonly Input[]) =>
|
|
26
|
-
async (
|
|
27
|
-
closure: (
|
|
28
|
-
elem: Input,
|
|
29
|
-
index: number,
|
|
30
|
-
array: readonly Input[],
|
|
31
|
-
) => Promise<any>,
|
|
32
|
-
): Promise<void> => {
|
|
33
|
-
await asyncRepeat(elements.length)((index) =>
|
|
34
|
-
closure(elements[index], index, elements),
|
|
35
|
-
);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const asyncMap =
|
|
39
|
-
<Input>(elements: readonly Input[]) =>
|
|
40
|
-
async <Output>(
|
|
41
|
-
closure: (
|
|
42
|
-
elem: Input,
|
|
43
|
-
index: number,
|
|
44
|
-
array: readonly Input[],
|
|
45
|
-
) => Promise<Output>,
|
|
46
|
-
): Promise<Output[]> => {
|
|
47
|
-
const ret: Output[] = [];
|
|
48
|
-
await asyncForEach(elements)(async (elem, index, array) => {
|
|
49
|
-
const output: Output = await closure(elem, index, array);
|
|
50
|
-
ret.push(output);
|
|
51
|
-
});
|
|
52
|
-
return ret;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export const asyncRepeat =
|
|
56
|
-
(count: number) =>
|
|
57
|
-
async <T>(closure: (index: number) => Promise<T>): Promise<T[]> => {
|
|
58
|
-
const indexes: number[] = new Array(count)
|
|
59
|
-
.fill(1)
|
|
60
|
-
.map((_, index) => index);
|
|
61
|
-
|
|
62
|
-
const output: T[] = [];
|
|
63
|
-
for (const index of indexes) output.push(await closure(index));
|
|
64
|
-
|
|
65
|
-
return output;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const has =
|
|
69
|
-
<T>(elements: readonly T[]) =>
|
|
70
|
-
(pred: (elem: T) => boolean): boolean =>
|
|
71
|
-
elements.find(pred) !== undefined;
|
|
72
|
-
|
|
73
|
-
export const repeat =
|
|
74
|
-
(count: number) =>
|
|
75
|
-
<T>(closure: (index: number) => T): T[] =>
|
|
76
|
-
new Array(count).fill("").map((_, index) => closure(index));
|
|
77
|
-
|
|
78
|
-
export const flat = <T>(matrix: T[][]): T[] => ([] as T[]).concat(...matrix);
|
|
79
|
-
|
|
80
|
-
export const subsets = <T>(array: T[]): T[][] => {
|
|
81
|
-
const check: boolean[] = new Array(array.length).fill(false);
|
|
82
|
-
const output: T[][] = [];
|
|
83
|
-
|
|
84
|
-
const dfs = (depth: number): void => {
|
|
85
|
-
if (depth === check.length)
|
|
86
|
-
output.push(array.filter((_v, idx) => check[idx]));
|
|
87
|
-
else {
|
|
88
|
-
check[depth] = true;
|
|
89
|
-
dfs(depth + 1);
|
|
90
|
-
|
|
91
|
-
check[depth] = false;
|
|
92
|
-
dfs(depth + 1);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
dfs(0);
|
|
96
|
-
return output;
|
|
97
|
-
};
|
|
98
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for arrays.
|
|
3
|
+
*
|
|
4
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
5
|
+
*/
|
|
6
|
+
export namespace ArrayUtil {
|
|
7
|
+
export const asyncFilter =
|
|
8
|
+
<Input>(elements: readonly Input[]) =>
|
|
9
|
+
async (
|
|
10
|
+
pred: (
|
|
11
|
+
elem: Input,
|
|
12
|
+
index: number,
|
|
13
|
+
array: readonly Input[],
|
|
14
|
+
) => Promise<boolean>,
|
|
15
|
+
): Promise<Input[]> => {
|
|
16
|
+
const ret: Input[] = [];
|
|
17
|
+
await asyncForEach(elements)(async (elem, index, array) => {
|
|
18
|
+
const flag: boolean = await pred(elem, index, array);
|
|
19
|
+
if (flag === true) ret.push(elem);
|
|
20
|
+
});
|
|
21
|
+
return ret;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const asyncForEach =
|
|
25
|
+
<Input>(elements: readonly Input[]) =>
|
|
26
|
+
async (
|
|
27
|
+
closure: (
|
|
28
|
+
elem: Input,
|
|
29
|
+
index: number,
|
|
30
|
+
array: readonly Input[],
|
|
31
|
+
) => Promise<any>,
|
|
32
|
+
): Promise<void> => {
|
|
33
|
+
await asyncRepeat(elements.length)((index) =>
|
|
34
|
+
closure(elements[index], index, elements),
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const asyncMap =
|
|
39
|
+
<Input>(elements: readonly Input[]) =>
|
|
40
|
+
async <Output>(
|
|
41
|
+
closure: (
|
|
42
|
+
elem: Input,
|
|
43
|
+
index: number,
|
|
44
|
+
array: readonly Input[],
|
|
45
|
+
) => Promise<Output>,
|
|
46
|
+
): Promise<Output[]> => {
|
|
47
|
+
const ret: Output[] = [];
|
|
48
|
+
await asyncForEach(elements)(async (elem, index, array) => {
|
|
49
|
+
const output: Output = await closure(elem, index, array);
|
|
50
|
+
ret.push(output);
|
|
51
|
+
});
|
|
52
|
+
return ret;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const asyncRepeat =
|
|
56
|
+
(count: number) =>
|
|
57
|
+
async <T>(closure: (index: number) => Promise<T>): Promise<T[]> => {
|
|
58
|
+
const indexes: number[] = new Array(count)
|
|
59
|
+
.fill(1)
|
|
60
|
+
.map((_, index) => index);
|
|
61
|
+
|
|
62
|
+
const output: T[] = [];
|
|
63
|
+
for (const index of indexes) output.push(await closure(index));
|
|
64
|
+
|
|
65
|
+
return output;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const has =
|
|
69
|
+
<T>(elements: readonly T[]) =>
|
|
70
|
+
(pred: (elem: T) => boolean): boolean =>
|
|
71
|
+
elements.find(pred) !== undefined;
|
|
72
|
+
|
|
73
|
+
export const repeat =
|
|
74
|
+
(count: number) =>
|
|
75
|
+
<T>(closure: (index: number) => T): T[] =>
|
|
76
|
+
new Array(count).fill("").map((_, index) => closure(index));
|
|
77
|
+
|
|
78
|
+
export const flat = <T>(matrix: T[][]): T[] => ([] as T[]).concat(...matrix);
|
|
79
|
+
|
|
80
|
+
export const subsets = <T>(array: T[]): T[][] => {
|
|
81
|
+
const check: boolean[] = new Array(array.length).fill(false);
|
|
82
|
+
const output: T[][] = [];
|
|
83
|
+
|
|
84
|
+
const dfs = (depth: number): void => {
|
|
85
|
+
if (depth === check.length)
|
|
86
|
+
output.push(array.filter((_v, idx) => check[idx]));
|
|
87
|
+
else {
|
|
88
|
+
check[depth] = true;
|
|
89
|
+
dfs(depth + 1);
|
|
90
|
+
|
|
91
|
+
check[depth] = false;
|
|
92
|
+
dfs(depth + 1);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
dfs(0);
|
|
96
|
+
return output;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { IConnection } from "@nestia/fetcher";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { Driver, WorkerConnector, WorkerServer } from "tgrid";
|
|
4
|
+
import { HashMap, hash } from "tstl";
|
|
5
|
+
|
|
6
|
+
import { IBenchmarkEvent } from "./structures/IBenchmarkEvent";
|
|
7
|
+
import { IBenchmarkMaster } from "./structures/IBenchmarkMaster";
|
|
8
|
+
import { IBenchmarkServant } from "./structures/IBenchmarkServant";
|
|
9
|
+
|
|
10
|
+
export namespace DynamicBenchmarker {
|
|
11
|
+
export interface IMasterProps {
|
|
12
|
+
count: number;
|
|
13
|
+
threads: number;
|
|
14
|
+
simultaneous: number;
|
|
15
|
+
servant: string;
|
|
16
|
+
filter?: (name: string) => boolean;
|
|
17
|
+
progress?: (complete: number) => void;
|
|
18
|
+
stdio?: undefined | "overlapped" | "pipe" | "ignore" | "inherit";
|
|
19
|
+
}
|
|
20
|
+
export interface IServantProps<Parameters extends any[]> {
|
|
21
|
+
connection: IConnection;
|
|
22
|
+
location: string;
|
|
23
|
+
prefix: string;
|
|
24
|
+
parameters: (name: string, connection: IConnection) => Parameters;
|
|
25
|
+
}
|
|
26
|
+
export interface IReport {
|
|
27
|
+
time: number;
|
|
28
|
+
statistics: IReport.IStatistics;
|
|
29
|
+
endpoints: Array<IReport.IEndpoint & IReport.IStatistics>;
|
|
30
|
+
}
|
|
31
|
+
export namespace IReport {
|
|
32
|
+
export interface IEndpoint {
|
|
33
|
+
method: string;
|
|
34
|
+
path: string;
|
|
35
|
+
}
|
|
36
|
+
export interface IStatistics {
|
|
37
|
+
count: number;
|
|
38
|
+
success: number;
|
|
39
|
+
mean: number | null;
|
|
40
|
+
stdev: number | null;
|
|
41
|
+
minimum: number | null;
|
|
42
|
+
maximum: number | null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const master = async (props: IMasterProps): Promise<IReport> => {
|
|
47
|
+
const completes: number[] = new Array(props.threads).fill(0);
|
|
48
|
+
const servants: WorkerConnector<
|
|
49
|
+
null,
|
|
50
|
+
IBenchmarkMaster,
|
|
51
|
+
IBenchmarkServant
|
|
52
|
+
>[] = await Promise.all(
|
|
53
|
+
new Array(props.threads).fill(null).map(async (i) => {
|
|
54
|
+
const connector: WorkerConnector<
|
|
55
|
+
null,
|
|
56
|
+
IBenchmarkMaster,
|
|
57
|
+
IBenchmarkServant
|
|
58
|
+
> = new WorkerConnector(
|
|
59
|
+
null,
|
|
60
|
+
{
|
|
61
|
+
filter: props.filter ?? (() => true),
|
|
62
|
+
progress: (current) => {
|
|
63
|
+
completes[i] = current;
|
|
64
|
+
if (props.progress)
|
|
65
|
+
props.progress(completes.reduce((a, b) => a + b, 0));
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
"process",
|
|
69
|
+
);
|
|
70
|
+
await connector.connect(props.servant, { stdio: props.stdio });
|
|
71
|
+
return connector;
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
const started_at: Date = new Date();
|
|
75
|
+
const events: IBenchmarkEvent[] = (
|
|
76
|
+
await Promise.all(
|
|
77
|
+
servants.map((connector) =>
|
|
78
|
+
connector.getDriver().execute({
|
|
79
|
+
count: Math.ceil(props.count / props.threads),
|
|
80
|
+
simultaneous: Math.ceil(props.simultaneous / props.threads),
|
|
81
|
+
}),
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
).flat();
|
|
85
|
+
const time: number = Date.now() - started_at.getTime();
|
|
86
|
+
await Promise.all(servants.map((connector) => connector.close()));
|
|
87
|
+
if (props.progress) props.progress(props.count);
|
|
88
|
+
|
|
89
|
+
const endpoints: HashMap<IReport.IEndpoint, IBenchmarkEvent[]> =
|
|
90
|
+
new HashMap(
|
|
91
|
+
(key) => hash(key.method, key.path),
|
|
92
|
+
(x, y) => x.method === y.method && x.path === y.path,
|
|
93
|
+
);
|
|
94
|
+
for (const e of events)
|
|
95
|
+
endpoints
|
|
96
|
+
.take(
|
|
97
|
+
{
|
|
98
|
+
method: e.metadata.method,
|
|
99
|
+
path: e.metadata.template ?? e.metadata.path,
|
|
100
|
+
},
|
|
101
|
+
() => [],
|
|
102
|
+
)
|
|
103
|
+
.push(e);
|
|
104
|
+
return {
|
|
105
|
+
time,
|
|
106
|
+
statistics: statistics(events),
|
|
107
|
+
endpoints: [...endpoints].map((it) => ({
|
|
108
|
+
...statistics(it.second),
|
|
109
|
+
...it.first,
|
|
110
|
+
})),
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const servant = async <Parameters extends any[]>(
|
|
115
|
+
props: IServantProps<Parameters>,
|
|
116
|
+
): Promise<WorkerServer<null, IBenchmarkServant, IBenchmarkMaster>> => {
|
|
117
|
+
const server: WorkerServer<null, IBenchmarkServant, IBenchmarkMaster> =
|
|
118
|
+
new WorkerServer();
|
|
119
|
+
await server.open({
|
|
120
|
+
execute: execute({
|
|
121
|
+
driver: server.getDriver(),
|
|
122
|
+
props,
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
return server;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const execute =
|
|
129
|
+
<Parameters extends any[]>(ctx: {
|
|
130
|
+
driver: Driver<IBenchmarkMaster>;
|
|
131
|
+
props: IServantProps<Parameters>;
|
|
132
|
+
}) =>
|
|
133
|
+
async (mass: {
|
|
134
|
+
count: number;
|
|
135
|
+
simultaneous: number;
|
|
136
|
+
}): Promise<IBenchmarkEvent[]> => {
|
|
137
|
+
const functions: IFunction<Parameters>[] = [];
|
|
138
|
+
await iterate({
|
|
139
|
+
collection: functions,
|
|
140
|
+
driver: ctx.driver,
|
|
141
|
+
props: ctx.props,
|
|
142
|
+
})(ctx.props.location);
|
|
143
|
+
const events: IBenchmarkEvent[] = [];
|
|
144
|
+
|
|
145
|
+
ctx.props.connection.logger = async (e): Promise<void> => {
|
|
146
|
+
events.push({
|
|
147
|
+
metadata: e.route,
|
|
148
|
+
status: e.status,
|
|
149
|
+
started_at: e.started_at.toISOString(),
|
|
150
|
+
repond_at: e.respond_at?.toISOString() ?? null,
|
|
151
|
+
completed_at: e.completed_at.toISOString(),
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
await Promise.all(
|
|
156
|
+
new Array(mass.simultaneous).fill(null).map(async () => {
|
|
157
|
+
while (events.length < mass.count) {
|
|
158
|
+
const prev: number = events.length;
|
|
159
|
+
const func: IFunction<Parameters> =
|
|
160
|
+
functions[Math.floor(Math.random() * functions.length)];
|
|
161
|
+
try {
|
|
162
|
+
await func.value(
|
|
163
|
+
...ctx.props.parameters(func.key, ctx.props.connection),
|
|
164
|
+
);
|
|
165
|
+
const current: number = events.length;
|
|
166
|
+
if (current !== prev)
|
|
167
|
+
ctx.driver.progress(current).catch(() => {});
|
|
168
|
+
} catch {}
|
|
169
|
+
}
|
|
170
|
+
}),
|
|
171
|
+
);
|
|
172
|
+
await ctx.driver.progress(events.length);
|
|
173
|
+
return events;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const statistics = (events: IBenchmarkEvent[]): IReport.IStatistics => {
|
|
177
|
+
const successes: IBenchmarkEvent[] = events.filter(
|
|
178
|
+
(event) => event.status === 200 || event.status === 201,
|
|
179
|
+
);
|
|
180
|
+
return {
|
|
181
|
+
count: events.length,
|
|
182
|
+
success: successes.length,
|
|
183
|
+
...average(events),
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const average = (
|
|
188
|
+
events: IBenchmarkEvent[],
|
|
189
|
+
): Pick<IReport.IStatistics, "mean" | "stdev" | "minimum" | "maximum"> => {
|
|
190
|
+
if (events.length === 0)
|
|
191
|
+
return {
|
|
192
|
+
mean: null,
|
|
193
|
+
stdev: null,
|
|
194
|
+
minimum: null,
|
|
195
|
+
maximum: null,
|
|
196
|
+
};
|
|
197
|
+
let mean: number = 0;
|
|
198
|
+
let stdev: number = 0;
|
|
199
|
+
let minimum: number = Number.MAX_SAFE_INTEGER;
|
|
200
|
+
let maximum: number = Number.MIN_SAFE_INTEGER;
|
|
201
|
+
for (const event of events) {
|
|
202
|
+
const elapsed: number =
|
|
203
|
+
new Date(event.completed_at).getTime() -
|
|
204
|
+
new Date(event.started_at).getTime();
|
|
205
|
+
mean += elapsed;
|
|
206
|
+
stdev += elapsed * elapsed;
|
|
207
|
+
minimum = Math.min(minimum, elapsed);
|
|
208
|
+
maximum = Math.max(maximum, elapsed);
|
|
209
|
+
}
|
|
210
|
+
mean /= events.length;
|
|
211
|
+
stdev = Math.sqrt(stdev / events.length - mean * mean);
|
|
212
|
+
return { mean, stdev, minimum, maximum };
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
interface IFunction<Parameters extends any[]> {
|
|
217
|
+
key: string;
|
|
218
|
+
value: (...args: Parameters) => Promise<void>;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const iterate =
|
|
222
|
+
<Parameters extends any[]>(ctx: {
|
|
223
|
+
collection: IFunction<Parameters>[];
|
|
224
|
+
driver: Driver<IBenchmarkMaster>;
|
|
225
|
+
props: DynamicBenchmarker.IServantProps<Parameters>;
|
|
226
|
+
}) =>
|
|
227
|
+
async (path: string): Promise<void> => {
|
|
228
|
+
const directory: string[] = await fs.promises.readdir(path);
|
|
229
|
+
for (const file of directory) {
|
|
230
|
+
const location: string = `${path}/${file}`;
|
|
231
|
+
const stat: fs.Stats = await fs.promises.stat(location);
|
|
232
|
+
if (stat.isDirectory() === true) await iterate(ctx)(location);
|
|
233
|
+
else if (file.endsWith(".js") === true) {
|
|
234
|
+
const modulo = await import(location);
|
|
235
|
+
for (const [key, value] of Object.entries(modulo)) {
|
|
236
|
+
if (typeof value !== "function") continue;
|
|
237
|
+
else if (key.startsWith(ctx.props.prefix) === false) continue;
|
|
238
|
+
else if ((await ctx.driver.filter(key)) === false) continue;
|
|
239
|
+
ctx.collection.push({
|
|
240
|
+
key,
|
|
241
|
+
value: value as (...args: Parameters) => Promise<any>,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
package/src/GaffComparator.ts
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gaff comparator.
|
|
3
|
-
*
|
|
4
|
-
* `GaffComparator` is a set of comparator functions for `Array.sort()` function,
|
|
5
|
-
* which can be used with {@link TestValidator.sort} function. If you want to see
|
|
6
|
-
* how to use them, see the below example link.
|
|
7
|
-
*
|
|
8
|
-
* @example https://github.com/samchon/nestia-template/blob/master/src/test/features/api/bbs/test_api_bbs_article_index_sort.ts
|
|
9
|
-
* @author Jeongho Nam - https://github.com/samchon
|
|
10
|
-
*/
|
|
11
|
-
export namespace GaffComparator {
|
|
12
|
-
/**
|
|
13
|
-
* String(s) comparator.
|
|
14
|
-
*
|
|
15
|
-
* @param getter Getter of string(s) from input
|
|
16
|
-
* @returns Comparator function
|
|
17
|
-
*/
|
|
18
|
-
export const strings =
|
|
19
|
-
<T>(getter: (input: T) => string | string[]) =>
|
|
20
|
-
(x: T, y: T) => {
|
|
21
|
-
const a: string[] = wrap(getter(x));
|
|
22
|
-
const b: string[] = wrap(getter(y));
|
|
23
|
-
|
|
24
|
-
const idx: number = a.findIndex((v, i) => v !== b[i]);
|
|
25
|
-
return idx !== -1 ? compare(a[idx], b[idx]) : 0;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Date(s) comparator.
|
|
30
|
-
*
|
|
31
|
-
* @param getter Getter of date(s) from input
|
|
32
|
-
* @returns Comparator function
|
|
33
|
-
*/
|
|
34
|
-
export const dates =
|
|
35
|
-
<T>(getter: (input: T) => string | string[]) =>
|
|
36
|
-
(x: T, y: T) => {
|
|
37
|
-
const take = (v: T) =>
|
|
38
|
-
wrap(getter(v)).map((str) => new Date(str).getTime());
|
|
39
|
-
const a: number[] = take(x);
|
|
40
|
-
const b: number[] = take(y);
|
|
41
|
-
|
|
42
|
-
const idx: number = a.findIndex((v, i) => v !== b[i]);
|
|
43
|
-
return idx !== -1 ? a[idx] - b[idx] : 0;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Number(s) comparator.
|
|
48
|
-
*
|
|
49
|
-
* @param closure Getter of number(s) from input
|
|
50
|
-
* @returns Comparator function
|
|
51
|
-
*/
|
|
52
|
-
export const numbers =
|
|
53
|
-
<T>(closure: (input: T) => number | number[]) =>
|
|
54
|
-
(x: T, y: T) => {
|
|
55
|
-
const a: number[] = wrap(closure(x));
|
|
56
|
-
const b: number[] = wrap(closure(y));
|
|
57
|
-
|
|
58
|
-
const idx: number = a.findIndex((v, i) => v !== b[i]);
|
|
59
|
-
return idx !== -1 ? a[idx] - b[idx] : 0;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
function compare(x: string, y: string) {
|
|
63
|
-
return x.localeCompare(y);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function wrap<T>(elem: T | T[]): T[] {
|
|
67
|
-
return Array.isArray(elem) ? elem : [elem];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Gaff comparator.
|
|
3
|
+
*
|
|
4
|
+
* `GaffComparator` is a set of comparator functions for `Array.sort()` function,
|
|
5
|
+
* which can be used with {@link TestValidator.sort} function. If you want to see
|
|
6
|
+
* how to use them, see the below example link.
|
|
7
|
+
*
|
|
8
|
+
* @example https://github.com/samchon/nestia-template/blob/master/src/test/features/api/bbs/test_api_bbs_article_index_sort.ts
|
|
9
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
10
|
+
*/
|
|
11
|
+
export namespace GaffComparator {
|
|
12
|
+
/**
|
|
13
|
+
* String(s) comparator.
|
|
14
|
+
*
|
|
15
|
+
* @param getter Getter of string(s) from input
|
|
16
|
+
* @returns Comparator function
|
|
17
|
+
*/
|
|
18
|
+
export const strings =
|
|
19
|
+
<T>(getter: (input: T) => string | string[]) =>
|
|
20
|
+
(x: T, y: T) => {
|
|
21
|
+
const a: string[] = wrap(getter(x));
|
|
22
|
+
const b: string[] = wrap(getter(y));
|
|
23
|
+
|
|
24
|
+
const idx: number = a.findIndex((v, i) => v !== b[i]);
|
|
25
|
+
return idx !== -1 ? compare(a[idx], b[idx]) : 0;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Date(s) comparator.
|
|
30
|
+
*
|
|
31
|
+
* @param getter Getter of date(s) from input
|
|
32
|
+
* @returns Comparator function
|
|
33
|
+
*/
|
|
34
|
+
export const dates =
|
|
35
|
+
<T>(getter: (input: T) => string | string[]) =>
|
|
36
|
+
(x: T, y: T) => {
|
|
37
|
+
const take = (v: T) =>
|
|
38
|
+
wrap(getter(v)).map((str) => new Date(str).getTime());
|
|
39
|
+
const a: number[] = take(x);
|
|
40
|
+
const b: number[] = take(y);
|
|
41
|
+
|
|
42
|
+
const idx: number = a.findIndex((v, i) => v !== b[i]);
|
|
43
|
+
return idx !== -1 ? a[idx] - b[idx] : 0;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Number(s) comparator.
|
|
48
|
+
*
|
|
49
|
+
* @param closure Getter of number(s) from input
|
|
50
|
+
* @returns Comparator function
|
|
51
|
+
*/
|
|
52
|
+
export const numbers =
|
|
53
|
+
<T>(closure: (input: T) => number | number[]) =>
|
|
54
|
+
(x: T, y: T) => {
|
|
55
|
+
const a: number[] = wrap(closure(x));
|
|
56
|
+
const b: number[] = wrap(closure(y));
|
|
57
|
+
|
|
58
|
+
const idx: number = a.findIndex((v, i) => v !== b[i]);
|
|
59
|
+
return idx !== -1 ? a[idx] - b[idx] : 0;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function compare(x: string, y: string) {
|
|
63
|
+
return x.localeCompare(y);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function wrap<T>(elem: T | T[]): T[] {
|
|
67
|
+
return Array.isArray(elem) ? elem : [elem];
|
|
68
|
+
}
|
|
69
|
+
}
|