@nestia/e2e 0.1.0
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/LICENSE +21 -0
- package/README.md +87 -0
- package/lib/ArrayUtil.d.ts +11 -0
- package/lib/ArrayUtil.js +216 -0
- package/lib/ArrayUtil.js.map +1 -0
- package/lib/DynamicExecutor.d.ts +124 -0
- package/lib/DynamicExecutor.js +358 -0
- package/lib/DynamicExecutor.js.map +1 -0
- package/lib/GaffComparator.d.ts +33 -0
- package/lib/GaffComparator.js +68 -0
- package/lib/GaffComparator.js.map +1 -0
- package/lib/RandomGenerator.d.ts +92 -0
- package/lib/RandomGenerator.js +186 -0
- package/lib/RandomGenerator.js.map +1 -0
- package/lib/StopWatch.d.ts +24 -0
- package/lib/StopWatch.js +92 -0
- package/lib/StopWatch.js.map +1 -0
- package/lib/TestValidator.d.ts +48 -0
- package/lib/TestValidator.js +178 -0
- package/lib/TestValidator.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +32 -0
- package/lib/index.js.map +1 -0
- package/lib/module.d.ts +6 -0
- package/lib/module.js +23 -0
- package/lib/module.js.map +1 -0
- package/package.json +49 -0
- package/src/ArrayUtil.ts +86 -0
- package/src/DynamicExecutor.ts +270 -0
- package/src/GaffComparator.ts +69 -0
- package/src/RandomGenerator.ts +178 -0
- package/src/StopWatch.ts +36 -0
- package/src/TestValidator.ts +142 -0
- package/src/index.ts +4 -0
- package/src/module.ts +6 -0
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nestia/e2e",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "E2E test utilify functions",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "rimraf lib && tsc",
|
|
8
|
+
"dev": "npm run build -- --watch",
|
|
9
|
+
"test": "node lib/test"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/samchon/nestia"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"e2e",
|
|
17
|
+
"nestia",
|
|
18
|
+
"nestjs",
|
|
19
|
+
"test",
|
|
20
|
+
"tdd",
|
|
21
|
+
"utility"
|
|
22
|
+
],
|
|
23
|
+
"author": "Jeongho Nam",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/samchon/nestia/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/samchon/nestia#readme",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/cli": "^0.11.21",
|
|
31
|
+
"@types/node": "^18.11.18",
|
|
32
|
+
"rimraf": "^4.1.2",
|
|
33
|
+
"typescript": "^4.9.5"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"chalk": "^4.1.2",
|
|
37
|
+
"cli": "^1.0.1",
|
|
38
|
+
"tstl": "^2.5.13"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"lib",
|
|
42
|
+
"src",
|
|
43
|
+
"!lib/test",
|
|
44
|
+
"!src/test",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE",
|
|
47
|
+
"package.json"
|
|
48
|
+
]
|
|
49
|
+
}
|
package/src/ArrayUtil.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export namespace ArrayUtil {
|
|
2
|
+
export function at<T>(array: T[], index: number): T {
|
|
3
|
+
return array[index < 0 ? array.length + index : index];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export async function asyncFilter<Input>(
|
|
7
|
+
elements: readonly Input[],
|
|
8
|
+
pred: (
|
|
9
|
+
elem: Input,
|
|
10
|
+
index: number,
|
|
11
|
+
array: readonly Input[],
|
|
12
|
+
) => Promise<boolean>,
|
|
13
|
+
): Promise<Input[]> {
|
|
14
|
+
const ret: Input[] = [];
|
|
15
|
+
await asyncForEach(elements, async (elem, index, array) => {
|
|
16
|
+
const flag: boolean = await pred(elem, index, array);
|
|
17
|
+
if (flag === true) ret.push(elem);
|
|
18
|
+
});
|
|
19
|
+
return ret;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function asyncForEach<Input>(
|
|
23
|
+
elements: readonly Input[],
|
|
24
|
+
closure: (
|
|
25
|
+
elem: Input,
|
|
26
|
+
index: number,
|
|
27
|
+
array: readonly Input[],
|
|
28
|
+
) => Promise<any>,
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
await asyncRepeat(elements.length, (index) =>
|
|
31
|
+
closure(elements[index], index, elements),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function asyncMap<Input, Output>(
|
|
36
|
+
elements: readonly Input[],
|
|
37
|
+
closure: (
|
|
38
|
+
elem: Input,
|
|
39
|
+
index: number,
|
|
40
|
+
array: readonly Input[],
|
|
41
|
+
) => Promise<Output>,
|
|
42
|
+
): Promise<Output[]> {
|
|
43
|
+
const ret: Output[] = [];
|
|
44
|
+
await asyncForEach(elements, async (elem, index, array) => {
|
|
45
|
+
const output: Output = await closure(elem, index, array);
|
|
46
|
+
ret.push(output);
|
|
47
|
+
});
|
|
48
|
+
return ret;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function asyncRepeat<T>(
|
|
52
|
+
count: number,
|
|
53
|
+
closure: (index: number) => Promise<T>,
|
|
54
|
+
): Promise<T[]> {
|
|
55
|
+
const indexes: number[] = new Array(count)
|
|
56
|
+
.fill(1)
|
|
57
|
+
.map((_, index) => index);
|
|
58
|
+
|
|
59
|
+
const output: T[] = [];
|
|
60
|
+
for (const index of indexes) output.push(await closure(index));
|
|
61
|
+
|
|
62
|
+
return output;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function has<T>(
|
|
66
|
+
elements: readonly T[],
|
|
67
|
+
pred: (elem: T) => boolean,
|
|
68
|
+
): boolean {
|
|
69
|
+
return elements.find(pred) !== undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function repeat<T>(
|
|
73
|
+
count: number,
|
|
74
|
+
closure: (index: number) => T,
|
|
75
|
+
): T[] {
|
|
76
|
+
return new Array(count).fill("").map((_, index) => closure(index));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function last<T>(array: T[]): T {
|
|
80
|
+
return array[array.length - 1];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function flat<T>(matrix: T[][]): T[] {
|
|
84
|
+
return ([] as T[]).concat(...matrix);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import cli from "cli";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import NodePath from "path";
|
|
5
|
+
|
|
6
|
+
import { StopWatch } from "./StopWatch";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Dynamic Executor running prefixed functions.
|
|
10
|
+
*
|
|
11
|
+
* `DynamicExecutor` runs every prefixed functions in a specific directory.
|
|
12
|
+
* However, if you want to run only specific functions, you can use
|
|
13
|
+
* `--include` or `--exclude` option in the CLI (Command Line Interface) level.
|
|
14
|
+
*
|
|
15
|
+
* When you want to see example utilization cases, see the below example links.
|
|
16
|
+
*
|
|
17
|
+
* @example https://github.com/samchon/backend/blob/master/src/test/index.ts
|
|
18
|
+
* @example https://github.com/samchon/nestia-template/blob/master/src/test/index.ts
|
|
19
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
20
|
+
*/
|
|
21
|
+
export namespace DynamicExecutor {
|
|
22
|
+
/**
|
|
23
|
+
* Function type of a prefixed.
|
|
24
|
+
*
|
|
25
|
+
* @template Arguments Type of parameters
|
|
26
|
+
* @template Ret Type of return value
|
|
27
|
+
*/
|
|
28
|
+
export interface Closure<Arguments extends any[], Ret = any> {
|
|
29
|
+
(...args: Arguments): Promise<Ret>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Options for dynamic executor.
|
|
34
|
+
*/
|
|
35
|
+
export interface IOptions<Parameters extends any[], Ret = any> {
|
|
36
|
+
/**
|
|
37
|
+
* Prefix of function name.
|
|
38
|
+
*
|
|
39
|
+
* Every prefixed function will be executed.
|
|
40
|
+
*
|
|
41
|
+
* In other words, if a function name doesn't start with the prefix, then it would never be executed.
|
|
42
|
+
*/
|
|
43
|
+
prefix: string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get parameters of a function.
|
|
47
|
+
*
|
|
48
|
+
* @param name Function name
|
|
49
|
+
* @returns Parameters
|
|
50
|
+
*/
|
|
51
|
+
parameters: (name: string) => Parameters;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Wrapper of test function.
|
|
55
|
+
*
|
|
56
|
+
* If you specify this `wrapper` property, every dynamic functions
|
|
57
|
+
* loaded and called by this `DynamicExecutor` would be wrapped by
|
|
58
|
+
* the `wrapper` function.
|
|
59
|
+
*
|
|
60
|
+
* @param name Function name
|
|
61
|
+
* @param closure Function to be executed
|
|
62
|
+
* @returns Wrapper function
|
|
63
|
+
*/
|
|
64
|
+
wrapper?: (
|
|
65
|
+
name: string,
|
|
66
|
+
closure: Closure<Parameters, Ret>,
|
|
67
|
+
) => Promise<any>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Whether to show elapsed time on `console` or not.
|
|
71
|
+
*
|
|
72
|
+
* @default true
|
|
73
|
+
*/
|
|
74
|
+
showElapsedTime?: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Report, result of dynamic execution.
|
|
79
|
+
*/
|
|
80
|
+
export interface IReport {
|
|
81
|
+
/**
|
|
82
|
+
* Location path of dynamic functions.
|
|
83
|
+
*/
|
|
84
|
+
location: string;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Execution results of dynamic functions.
|
|
88
|
+
*/
|
|
89
|
+
executions: IReport.IExecution[];
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Total elapsed time.
|
|
93
|
+
*/
|
|
94
|
+
time: number;
|
|
95
|
+
}
|
|
96
|
+
export namespace IReport {
|
|
97
|
+
/**
|
|
98
|
+
* Execution result of a dynamic function.
|
|
99
|
+
*/
|
|
100
|
+
export interface IExecution {
|
|
101
|
+
/**
|
|
102
|
+
* Name of function.
|
|
103
|
+
*/
|
|
104
|
+
name: string;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Location path of the function.
|
|
108
|
+
*/
|
|
109
|
+
location: string;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Error when occured.
|
|
113
|
+
*/
|
|
114
|
+
error: Error | null;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Elapsed time.
|
|
118
|
+
*/
|
|
119
|
+
time: number;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Prepare dynamic executor in strict mode.
|
|
125
|
+
*
|
|
126
|
+
* In strict mode, if any error occurs, the program will be terminated directly.
|
|
127
|
+
* Otherwise, {@link validate} mode does not terminate when error occurs, but
|
|
128
|
+
* just archive the error log.
|
|
129
|
+
*
|
|
130
|
+
* @param options Options of dynamic executor
|
|
131
|
+
* @returns Runner of dynamic functions with specific location
|
|
132
|
+
*/
|
|
133
|
+
export const assert =
|
|
134
|
+
<Arguments extends any[]>(options: IOptions<Arguments>) =>
|
|
135
|
+
/**
|
|
136
|
+
* Run dynamic executor.
|
|
137
|
+
*
|
|
138
|
+
* @param path Location of prefixed functions
|
|
139
|
+
*/
|
|
140
|
+
(path: string): Promise<IReport> =>
|
|
141
|
+
main(options)(true)(path);
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Prepare dynamic executor in loose mode.
|
|
145
|
+
*
|
|
146
|
+
* In loose mode, the program would not be terminated even when error occurs.
|
|
147
|
+
* Instead, the error would be archived and returns as a list. Otherwise,
|
|
148
|
+
* {@link assert} mode terminates the program directly when error occurs.
|
|
149
|
+
*
|
|
150
|
+
* @param options Options of dynamic executor
|
|
151
|
+
* @returns Runner of dynamic functions with specific location
|
|
152
|
+
*/
|
|
153
|
+
export const validate =
|
|
154
|
+
<Arguments extends any[]>(options: IOptions<Arguments>) =>
|
|
155
|
+
/**
|
|
156
|
+
* Run dynamic executor.
|
|
157
|
+
*
|
|
158
|
+
* @param path Location of prefix functions
|
|
159
|
+
* @returns List of errors
|
|
160
|
+
*/
|
|
161
|
+
(path: string): Promise<IReport> =>
|
|
162
|
+
main(options)(false)(path);
|
|
163
|
+
|
|
164
|
+
const main =
|
|
165
|
+
<Arguments extends any[]>(options: IOptions<Arguments>) =>
|
|
166
|
+
(assert: boolean) =>
|
|
167
|
+
async (path: string) => {
|
|
168
|
+
const command: ICommand = cli.parse();
|
|
169
|
+
const report: IReport = {
|
|
170
|
+
location: path,
|
|
171
|
+
time: Date.now(),
|
|
172
|
+
executions: [],
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const executor = execute(options)(command)(report)(assert);
|
|
176
|
+
const iterator = iterate(executor);
|
|
177
|
+
await iterator(path);
|
|
178
|
+
|
|
179
|
+
report.time = Date.now() - report.time;
|
|
180
|
+
return report;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const iterate = <Arguments extends any[]>(
|
|
184
|
+
executor: (path: string, modulo: Module<Arguments>) => Promise<void>,
|
|
185
|
+
) => {
|
|
186
|
+
const visitor = async (path: string): Promise<void> => {
|
|
187
|
+
const directory: string[] = await fs.promises.readdir(path);
|
|
188
|
+
for (const file of directory) {
|
|
189
|
+
const location: string = NodePath.resolve(`${path}/${file}`);
|
|
190
|
+
const stats: fs.Stats = await fs.promises.lstat(location);
|
|
191
|
+
|
|
192
|
+
if (stats.isDirectory() === true) {
|
|
193
|
+
await visitor(location);
|
|
194
|
+
continue;
|
|
195
|
+
} else if (file.substr(-3) !== `.${EXTENSION}`) continue;
|
|
196
|
+
|
|
197
|
+
const modulo: Module<Arguments> = await import(location);
|
|
198
|
+
await executor(location, modulo);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
return visitor;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const execute =
|
|
205
|
+
<Arguments extends any[]>(options: IOptions<Arguments>) =>
|
|
206
|
+
(command: ICommand) =>
|
|
207
|
+
(report: IReport) =>
|
|
208
|
+
(assert: boolean) =>
|
|
209
|
+
async (location: string, modulo: Module<Arguments>): Promise<void> => {
|
|
210
|
+
for (const key in modulo) {
|
|
211
|
+
if (command.exclude && key.indexOf(command.exclude) !== -1)
|
|
212
|
+
continue;
|
|
213
|
+
else if (command.include && key.indexOf(command.include) === -1)
|
|
214
|
+
continue;
|
|
215
|
+
else if (
|
|
216
|
+
key.substring(0, options.prefix.length) !== options.prefix
|
|
217
|
+
)
|
|
218
|
+
continue;
|
|
219
|
+
else if (!(modulo[key] instanceof Function)) continue;
|
|
220
|
+
|
|
221
|
+
const closure: Closure<Arguments> = modulo[key];
|
|
222
|
+
const func = async () => {
|
|
223
|
+
if (options.wrapper !== undefined)
|
|
224
|
+
await options.wrapper(key, closure);
|
|
225
|
+
else await closure(...options.parameters(key));
|
|
226
|
+
};
|
|
227
|
+
const label: string = chalk.greenBright(key);
|
|
228
|
+
|
|
229
|
+
const result: IReport.IExecution = {
|
|
230
|
+
name: key,
|
|
231
|
+
location,
|
|
232
|
+
error: null,
|
|
233
|
+
time: Date.now(),
|
|
234
|
+
};
|
|
235
|
+
report.executions.push(result);
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
if (options.showElapsedTime === false) {
|
|
239
|
+
await func();
|
|
240
|
+
result.time = Date.now() - result.time;
|
|
241
|
+
console.log(` - ${label}`);
|
|
242
|
+
} else {
|
|
243
|
+
result.time = await StopWatch.measure(func);
|
|
244
|
+
console.log(
|
|
245
|
+
` - ${label}: ${chalk.yellowBright(
|
|
246
|
+
result.time.toLocaleString(),
|
|
247
|
+
)} ms`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
} catch (exp) {
|
|
251
|
+
if (!(exp instanceof Error)) return;
|
|
252
|
+
|
|
253
|
+
result.time = Date.now() - result.time;
|
|
254
|
+
result.error = exp;
|
|
255
|
+
|
|
256
|
+
console.log(` - ${label} -> ${chalk.redBright(exp.name)}`);
|
|
257
|
+
if (assert === true) throw exp;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
interface ICommand {
|
|
263
|
+
include?: string;
|
|
264
|
+
exclude?: string;
|
|
265
|
+
}
|
|
266
|
+
interface Module<Arguments extends any[]> {
|
|
267
|
+
[key: string]: Closure<Arguments>;
|
|
268
|
+
}
|
|
269
|
+
const EXTENSION: string = __filename.substring(__filename.length - 2);
|
|
270
|
+
}
|
|
@@ -0,0 +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[0].localeCompare(y[0]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function wrap<T>(elem: T | T[]): T[] {
|
|
67
|
+
return Array.isArray(elem) ? elem : [elem];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { back_inserter, randint } from "tstl";
|
|
2
|
+
import { sample as _Sample } from "tstl/ranges";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Random data generator.
|
|
6
|
+
*
|
|
7
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
8
|
+
*/
|
|
9
|
+
export namespace RandomGenerator {
|
|
10
|
+
/* ----------------------------------------------------------------
|
|
11
|
+
IDENTIFICATIONS
|
|
12
|
+
---------------------------------------------------------------- */
|
|
13
|
+
const CHARACTERS = "abcdefghijklmnopqrstuvwxyz";
|
|
14
|
+
const LETTERS: string = "0123456789" + CHARACTERS;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate random alphabets
|
|
18
|
+
*
|
|
19
|
+
* @param length Length of alphabets
|
|
20
|
+
* @returns Generated alphabets
|
|
21
|
+
*/
|
|
22
|
+
export function alphabets(length: number): string {
|
|
23
|
+
return new Array(length)
|
|
24
|
+
.fill("")
|
|
25
|
+
.map(() => CHARACTERS[randint(0, CHARACTERS.length - 1)])
|
|
26
|
+
.join("");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generate random alpha-numeric characters.
|
|
31
|
+
*
|
|
32
|
+
* Generate random string constructed with only alphabets and numbers.
|
|
33
|
+
*
|
|
34
|
+
* @param length Length of characters
|
|
35
|
+
* @returns Generated string
|
|
36
|
+
*/
|
|
37
|
+
export function alphaNumeric(length: number): string {
|
|
38
|
+
return new Array(length)
|
|
39
|
+
.fill("")
|
|
40
|
+
.map(() => LETTERS[randint(0, LETTERS.length - 1)])
|
|
41
|
+
.join("");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate random name.
|
|
46
|
+
*
|
|
47
|
+
* @param length Length of paragraph, default is 2 or 3
|
|
48
|
+
* @returns Generated name
|
|
49
|
+
*/
|
|
50
|
+
export function name(length: number = randint(2, 3)): string {
|
|
51
|
+
return paragraph(length)();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Generate random paragraph.
|
|
56
|
+
*
|
|
57
|
+
* @param sentences Number of sentences
|
|
58
|
+
* @returns Paragraph generator
|
|
59
|
+
*/
|
|
60
|
+
export const paragraph =
|
|
61
|
+
(sentences: number) =>
|
|
62
|
+
/**
|
|
63
|
+
* @param wordMin Minimum number of characters in a sentence
|
|
64
|
+
* @param wordMax Maximum number of characters in a sentence
|
|
65
|
+
* @returns Generated paragraph
|
|
66
|
+
*/
|
|
67
|
+
(wordMin: number = 3, wordMax: number = 7): string =>
|
|
68
|
+
new Array(sentences)
|
|
69
|
+
.fill("")
|
|
70
|
+
.map(() => alphabets(randint(wordMin, wordMax)))
|
|
71
|
+
.join(" ");
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Generate random content.
|
|
75
|
+
*
|
|
76
|
+
* @param paragraphes Number of paragraphes
|
|
77
|
+
* @returns Currying function
|
|
78
|
+
*/
|
|
79
|
+
export const content =
|
|
80
|
+
(paragraphes: number) =>
|
|
81
|
+
/**
|
|
82
|
+
* @param sentenceMin Minimum number of sentences in a paragraph
|
|
83
|
+
* @param sentenceMax Maximum number of sentences in a paragraph
|
|
84
|
+
* @returns Currying function
|
|
85
|
+
*/
|
|
86
|
+
(sentenceMin: number = 10, sentenceMax: number = 40) =>
|
|
87
|
+
/**
|
|
88
|
+
* @param wordMin Minimum number of characters in a sentence
|
|
89
|
+
* @param wordMax Maximum number of characters in a sentence
|
|
90
|
+
* @returns Content generator
|
|
91
|
+
*/
|
|
92
|
+
(wordMin: number = 1, wordMax: number = 7): string =>
|
|
93
|
+
new Array(paragraphes)
|
|
94
|
+
.fill("")
|
|
95
|
+
.map(() =>
|
|
96
|
+
paragraph(randint(sentenceMin, sentenceMax))(
|
|
97
|
+
wordMin,
|
|
98
|
+
wordMax,
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
.join("\n\n");
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Generate random substring.
|
|
105
|
+
*
|
|
106
|
+
* @param content Target content
|
|
107
|
+
* @returns Random substring
|
|
108
|
+
*/
|
|
109
|
+
export function substring(content: string): string {
|
|
110
|
+
const first: number = randint(0, content.length - 1);
|
|
111
|
+
const last: number = randint(first + 1, content.length);
|
|
112
|
+
|
|
113
|
+
return content.substring(first, last).trim();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Generate random mobile number.
|
|
118
|
+
*
|
|
119
|
+
* @param prefix Prefix string, default is "010"
|
|
120
|
+
* @returns Random mobile number
|
|
121
|
+
*/
|
|
122
|
+
export function mobile(prefix: string = "010"): string {
|
|
123
|
+
return `${prefix}${digit(3, 4)}${digit(4, 4)}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Generate random digit.
|
|
128
|
+
*
|
|
129
|
+
* Generate random digit that filling front with zero characters
|
|
130
|
+
* when value is less than maximum cipher.
|
|
131
|
+
*
|
|
132
|
+
* @param minC Minimum cipher
|
|
133
|
+
* @param maxC Maximum cipher
|
|
134
|
+
* @returns
|
|
135
|
+
*/
|
|
136
|
+
export function digit(minC: number, maxC: number): string {
|
|
137
|
+
const val: number = randint(0, Math.pow(10.0, maxC) - 1);
|
|
138
|
+
const log10: number = val ? Math.floor(Math.log10(val)) + 1 : 0;
|
|
139
|
+
const prefix: string = "0".repeat(Math.max(0, minC - log10));
|
|
140
|
+
|
|
141
|
+
return prefix + val.toString();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Generate random date.
|
|
146
|
+
*
|
|
147
|
+
* @param from Start date
|
|
148
|
+
* @param range Range of random milliseconds
|
|
149
|
+
* @returns Random date
|
|
150
|
+
*/
|
|
151
|
+
export function date(from: Date, range: number): Date {
|
|
152
|
+
const time: number = from.getTime() + randint(0, range);
|
|
153
|
+
return new Date(time);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Pick random elements from an array.
|
|
158
|
+
*
|
|
159
|
+
* @param array Target array
|
|
160
|
+
* @param count Number of count to pick
|
|
161
|
+
* @returns Sampled array
|
|
162
|
+
*/
|
|
163
|
+
export function sample<T>(array: T[], count: number): T[] {
|
|
164
|
+
const ret: T[] = [];
|
|
165
|
+
_Sample(array, back_inserter(ret), count);
|
|
166
|
+
return ret;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Pick random element from an array.
|
|
171
|
+
*
|
|
172
|
+
* @param array Target array
|
|
173
|
+
* @returns picked element
|
|
174
|
+
*/
|
|
175
|
+
export function pick<T>(array: T[]): T {
|
|
176
|
+
return array[randint(0, array.length - 1)];
|
|
177
|
+
}
|
|
178
|
+
}
|
package/src/StopWatch.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Elapsed time measurement utility.
|
|
3
|
+
*
|
|
4
|
+
* @author Sachon
|
|
5
|
+
*/
|
|
6
|
+
export namespace StopWatch {
|
|
7
|
+
/**
|
|
8
|
+
* Type of task.
|
|
9
|
+
*/
|
|
10
|
+
export type Task = () => Promise<void>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param task
|
|
15
|
+
* @returns
|
|
16
|
+
*/
|
|
17
|
+
export async function measure(task: Task): Promise<number> {
|
|
18
|
+
const time: number = Date.now();
|
|
19
|
+
await task();
|
|
20
|
+
return Date.now() - time;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
* @param title
|
|
26
|
+
* @param task
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
29
|
+
export async function trace(title: string, task: Task): Promise<number> {
|
|
30
|
+
process.stdout.write(` - ${title}: `);
|
|
31
|
+
const time: number = await measure(task);
|
|
32
|
+
|
|
33
|
+
console.log(`${time.toLocaleString()} ms`);
|
|
34
|
+
return time;
|
|
35
|
+
}
|
|
36
|
+
}
|