doc-detective 1.0.0 → 1.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.
- package/README.md +1 -6
- package/package.json +2 -1
- package/sample/config.json +2 -0
- package/src/config.json +2 -0
- package/src/lib/analytics.js +1 -1
- package/src/lib/utils.js +136 -61
package/README.md
CHANGED
|
@@ -18,8 +18,6 @@ You can use Doc Detective as an [NPM package](#npm-package) or a standalone [CLI
|
|
|
18
18
|
|
|
19
19
|
Doc Detective integrates with Node projects as an NPM package. When using the NPM package, you must specify all options in the `run()` method's `config` argument, which is a JSON object with the same structure as [config.json](https://github.com/hawkeyexl/doc-detective/blob/master/sample/config.json).
|
|
20
20
|
|
|
21
|
-
0. Install prerequisites:
|
|
22
|
-
- [FFmpeg](https://ffmpeg.org/) (Only required if you want the [Start recording](#start-recording) action to output GIFs. Not required for MP4 output.)
|
|
23
21
|
1. In a terminal, navigate to your Node project, then install Doc Detective:
|
|
24
22
|
|
|
25
23
|
```bash
|
|
@@ -45,7 +43,6 @@ You can run Doc Detective as a standalone CLI tool. When running as a CLI tool,
|
|
|
45
43
|
0. Install prerequisites:
|
|
46
44
|
|
|
47
45
|
- [Node.js](https://nodejs.org/)
|
|
48
|
-
- [FFmpeg](https://ffmpeg.org/) (Only required if you want the [Start recording](#start-recording) action to output GIFs. Not required for MP4 output.)
|
|
49
46
|
|
|
50
47
|
1. In a terminal, clone the repo and install dependencies:
|
|
51
48
|
|
|
@@ -276,7 +273,7 @@ Format:
|
|
|
276
273
|
|
|
277
274
|
Start recording the current browser viewport. Must be followed by a `stopRecording` action. Supported extensions: .mp4, .gif
|
|
278
275
|
|
|
279
|
-
**Note:** `.gif` format is **not** recommended. Because of file format and encoding differences, `.gif` files tend to be ~6.5 times larger than `.mp4` files, and with lower visual fidelity. But if `.gif` is a hard requirement for you, it's here. Creating `.gif` files
|
|
276
|
+
**Note:** `.gif` format is **not** recommended. Because of file format and encoding differences, `.gif` files tend to be ~6.5 times larger than `.mp4` files, and with lower visual fidelity. But if `.gif` is a hard requirement for you, it's here. Creating `.gif` files also creates `.mp4` files of the recordings.
|
|
280
277
|
|
|
281
278
|
Format:
|
|
282
279
|
|
|
@@ -524,8 +521,6 @@ Analytics reporting is off by default. If you want to make extra sure that Doc D
|
|
|
524
521
|
## Potential future features
|
|
525
522
|
|
|
526
523
|
- Browser auto-detection and fallback.
|
|
527
|
-
- Improved default config experience.
|
|
528
|
-
- Environment variable overrides for config options.
|
|
529
524
|
- Docker image with bundled Chromium/Chrome/Firefox.
|
|
530
525
|
- New/upgraded test actions:
|
|
531
526
|
- New: Curl commands. (Support substitution/setting env vars. Only check for `200 OK`.)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doc-detective",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Unit test documentation (and record videos of those tests).",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"homepage": "https://github.com/hawkeyexl/doc-detective#readme",
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
27
28
|
"axios": "^0.27.2",
|
|
28
29
|
"dotenv": "^16.0.2",
|
|
29
30
|
"n-readlines": "^1.0.1",
|
package/sample/config.json
CHANGED
package/src/config.json
CHANGED
package/src/lib/analytics.js
CHANGED
package/src/lib/utils.js
CHANGED
|
@@ -50,6 +50,16 @@ function setArgs(args) {
|
|
|
50
50
|
description: "Path for a JSON file of test result output.",
|
|
51
51
|
type: "string",
|
|
52
52
|
})
|
|
53
|
+
.option("setup", {
|
|
54
|
+
description:
|
|
55
|
+
"Path to a file or directory to parse for tests to run before 'input' tests. Useful for preparing environments to perform tests.",
|
|
56
|
+
type: "string",
|
|
57
|
+
})
|
|
58
|
+
.option("cleanup", {
|
|
59
|
+
description:
|
|
60
|
+
"Path to a file or directory to parse for tests to run after 'input' tests. Useful for resetting environments after tests run.",
|
|
61
|
+
type: "string",
|
|
62
|
+
})
|
|
53
63
|
.option("recursive", {
|
|
54
64
|
alias: "r",
|
|
55
65
|
description:
|
|
@@ -122,7 +132,8 @@ function setArgs(args) {
|
|
|
122
132
|
function setLogLevel(config, argv) {
|
|
123
133
|
let logLevel = "";
|
|
124
134
|
let enums = ["debug", "info", "warning", "error", "silent"];
|
|
125
|
-
logLevel =
|
|
135
|
+
logLevel =
|
|
136
|
+
argv.logLevel || process.env.DOC_LOG_LEVEL || config.logLevel || "info";
|
|
126
137
|
logLevel = String(logLevel).toLowerCase();
|
|
127
138
|
if (enums.indexOf(logLevel) >= 0) {
|
|
128
139
|
config.logLevel = logLevel;
|
|
@@ -159,7 +170,7 @@ function selectConfig(config, argv) {
|
|
|
159
170
|
log(config, "debug", "Loaded config from function parameter.");
|
|
160
171
|
} else {
|
|
161
172
|
// Default
|
|
162
|
-
config = defaultConfig;
|
|
173
|
+
config = JSON.parse(JSON.stringify(defaultConfig));
|
|
163
174
|
setLogLevel(config, argv);
|
|
164
175
|
log(
|
|
165
176
|
config,
|
|
@@ -172,16 +183,18 @@ function selectConfig(config, argv) {
|
|
|
172
183
|
|
|
173
184
|
function setEnv(config, argv) {
|
|
174
185
|
config.env = argv.env || process.env.DOC_ENV_PATH || config.env;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
186
|
+
if (config.env) {
|
|
187
|
+
config.env = path.resolve(config.env);
|
|
188
|
+
if (fs.existsSync(config.env)) {
|
|
189
|
+
let envResult = setEnvs(config.env);
|
|
190
|
+
if (envResult.status === "PASS")
|
|
191
|
+
log(config, "debug", `Env file set: ${config.env}`);
|
|
192
|
+
if (envResult.status === "FAIL")
|
|
193
|
+
log(config, "warning", `File format issue. Can't load env file.`);
|
|
194
|
+
} else {
|
|
195
|
+
log(config, "warning", `Invalid file path. Can't load env file.`);
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
185
198
|
log(config, "debug", "No env file specified.");
|
|
186
199
|
}
|
|
187
200
|
return config;
|
|
@@ -189,9 +202,17 @@ function setEnv(config, argv) {
|
|
|
189
202
|
|
|
190
203
|
function setInput(config, argv) {
|
|
191
204
|
config.input = argv.input || process.env.DOC_INPUT_PATH || config.input;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
205
|
+
if (config.input) {
|
|
206
|
+
config.input = path.resolve(config.input);
|
|
207
|
+
if (fs.existsSync(config.input)) {
|
|
208
|
+
log(config, "debug", `Input path set: ${config.input}`);
|
|
209
|
+
} else {
|
|
210
|
+
log(
|
|
211
|
+
config,
|
|
212
|
+
"warning",
|
|
213
|
+
`Invalid input path. Reverted to default: ${config.input}`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
195
216
|
} else {
|
|
196
217
|
config.input = path.resolve(defaultConfig.input);
|
|
197
218
|
log(
|
|
@@ -210,6 +231,40 @@ function setOutput(config, argv) {
|
|
|
210
231
|
return config;
|
|
211
232
|
}
|
|
212
233
|
|
|
234
|
+
function setSetup(config, argv) {
|
|
235
|
+
config.setup = argv.setup || process.env.DOC_SETUP || config.setup;
|
|
236
|
+
if (config.setup === "") {
|
|
237
|
+
log(config, "debug", `No setup tests.`);
|
|
238
|
+
return config;
|
|
239
|
+
} else {
|
|
240
|
+
config.setup = path.resolve(config.setup);
|
|
241
|
+
if (fs.existsSync(config.setup)) {
|
|
242
|
+
log(config, "debug", `Setup tests path set: ${config.setup}`);
|
|
243
|
+
} else {
|
|
244
|
+
config.setup = defaultConfig.setup;
|
|
245
|
+
log(config, "warning", `Invalid setup tests path.`);
|
|
246
|
+
}
|
|
247
|
+
return config;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function setCleanup(config, argv) {
|
|
252
|
+
config.cleanup = argv.cleanup || process.env.DOC_CLEANUP || config.cleanup;
|
|
253
|
+
if (config.cleanup === "") {
|
|
254
|
+
log(config, "debug", `No cleanup tests.`);
|
|
255
|
+
return config;
|
|
256
|
+
} else {
|
|
257
|
+
config.cleanup = path.resolve(config.cleanup);
|
|
258
|
+
if (fs.existsSync(config.cleanup)) {
|
|
259
|
+
log(config, "debug", `Cleanup tests path set: ${config.cleanup}`);
|
|
260
|
+
} else {
|
|
261
|
+
config.cleanup = defaultConfig.cleanup;
|
|
262
|
+
log(config, "warning", `Invalid cleanup tests path.`);
|
|
263
|
+
}
|
|
264
|
+
return config;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
213
268
|
function setMediaDirectory(config, argv) {
|
|
214
269
|
config.mediaDirectory =
|
|
215
270
|
argv.mediaDir ||
|
|
@@ -320,18 +375,23 @@ function setBrowserPath(config, argv) {
|
|
|
320
375
|
argv.browserPath ||
|
|
321
376
|
process.env.DOC_BROWSER_PATH ||
|
|
322
377
|
config.browserOptions.path;
|
|
323
|
-
config.browserOptions.path
|
|
324
|
-
|
|
325
|
-
|
|
378
|
+
if (config.browserOptions.path === "") {
|
|
379
|
+
log(config, "debug", `Browser set to default Chromium install.`);
|
|
380
|
+
return config;
|
|
326
381
|
} else {
|
|
327
|
-
config.browserOptions.path =
|
|
328
|
-
|
|
329
|
-
config,
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
382
|
+
config.browserOptions.path = path.resolve(config.browserOptions.path);
|
|
383
|
+
if (fs.existsSync(config.browserOptions.path)) {
|
|
384
|
+
log(config, "debug", `Browser path set: ${config.browserOptions.path}`);
|
|
385
|
+
} else {
|
|
386
|
+
config.browserOptions.path = defaultConfig.browserOptions.path;
|
|
387
|
+
log(
|
|
388
|
+
config,
|
|
389
|
+
"warning",
|
|
390
|
+
`Invalid browser path. Reverted to default Chromium install.`
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
return config;
|
|
333
394
|
}
|
|
334
|
-
return config;
|
|
335
395
|
}
|
|
336
396
|
|
|
337
397
|
function setBrowserHeight(config, argv) {
|
|
@@ -462,8 +522,6 @@ function setAnalyticsServers(config, argv) {
|
|
|
462
522
|
}
|
|
463
523
|
|
|
464
524
|
function setConfig(config, argv) {
|
|
465
|
-
config = setLogLevel(config, argv);
|
|
466
|
-
|
|
467
525
|
config = selectConfig(config, argv);
|
|
468
526
|
|
|
469
527
|
config = setEnv(config, argv);
|
|
@@ -472,6 +530,10 @@ function setConfig(config, argv) {
|
|
|
472
530
|
|
|
473
531
|
config = setOutput(config, argv);
|
|
474
532
|
|
|
533
|
+
config = setSetup(config, argv);
|
|
534
|
+
|
|
535
|
+
config = setCleanup(config, argv);
|
|
536
|
+
|
|
475
537
|
config = setMediaDirectory(config, argv);
|
|
476
538
|
|
|
477
539
|
config = setRecursion(config, argv);
|
|
@@ -501,49 +563,59 @@ function setConfig(config, argv) {
|
|
|
501
563
|
function setFiles(config) {
|
|
502
564
|
let dirs = [];
|
|
503
565
|
let files = [];
|
|
566
|
+
let sequence = [];
|
|
504
567
|
|
|
505
568
|
// Validate input
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
569
|
+
const setup = config.setup;
|
|
570
|
+
if (setup) sequence.push(setup);
|
|
571
|
+
const input = config.input;
|
|
572
|
+
sequence.push(input);
|
|
573
|
+
const cleanup = config.cleanup;
|
|
574
|
+
if (cleanup) sequence.push(cleanup);
|
|
575
|
+
|
|
576
|
+
for (s = 0; s < sequence.length; s++) {
|
|
577
|
+
let isFile = fs.statSync(sequence[s]).isFile();
|
|
578
|
+
let isDir = fs.statSync(sequence[s]).isDirectory();
|
|
579
|
+
|
|
580
|
+
// Parse input
|
|
581
|
+
if (
|
|
582
|
+
// Is a file
|
|
583
|
+
isFile &&
|
|
584
|
+
// Isn't present in files array already
|
|
585
|
+
files.indexOf(sequence[s]) < 0 &&
|
|
586
|
+
// No extension filter or extension included in filter
|
|
587
|
+
(config.testExtensions === "" ||
|
|
588
|
+
config.testExtensions.includes(path.extname(sequence[s])))
|
|
589
|
+
) {
|
|
590
|
+
files.push(sequence[s]);
|
|
591
|
+
} else if (isDir) {
|
|
592
|
+
// Load files from directory
|
|
593
|
+
dirs = [];
|
|
594
|
+
dirs[0] = sequence[s];
|
|
595
|
+
for (let i = 0; i < dirs.length; i++) {
|
|
596
|
+
fs.readdirSync(dirs[i]).forEach((object) => {
|
|
597
|
+
let content = path.resolve(dirs[i] + "/" + object);
|
|
598
|
+
let isFile = fs.statSync(content).isFile();
|
|
599
|
+
let isDir = fs.statSync(content).isDirectory();
|
|
529
600
|
if (
|
|
530
|
-
//
|
|
531
|
-
|
|
532
|
-
|
|
601
|
+
// Is a file
|
|
602
|
+
isFile &&
|
|
603
|
+
// Isn't present in files array already
|
|
604
|
+
files.indexOf(s) < 0 &&
|
|
605
|
+
// No extension filter or extension included in filter
|
|
606
|
+
(config.testExtensions === "" ||
|
|
607
|
+
config.testExtensions.includes(path.extname(content)))
|
|
533
608
|
) {
|
|
534
609
|
files.push(content);
|
|
535
|
-
}
|
|
536
|
-
} else if (isDir) {
|
|
537
|
-
// is a directory
|
|
538
|
-
if (config.recursive) {
|
|
610
|
+
} else if (isDir && config.recursive) {
|
|
539
611
|
// recursive set to true
|
|
540
612
|
dirs.push(content);
|
|
541
613
|
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
614
|
+
});
|
|
615
|
+
}
|
|
544
616
|
}
|
|
545
|
-
return files;
|
|
546
617
|
}
|
|
618
|
+
return files;
|
|
547
619
|
}
|
|
548
620
|
|
|
549
621
|
// Parse files for tests
|
|
@@ -619,13 +691,16 @@ async function outputResults(config, results) {
|
|
|
619
691
|
}
|
|
620
692
|
|
|
621
693
|
async function convertToGif(config, input, fps, width) {
|
|
694
|
+
const ffmpegPath = require("@ffmpeg-installer/ffmpeg").path;
|
|
695
|
+
|
|
622
696
|
if (!fs.existsSync(input)) return { error: "Invalid input." };
|
|
623
697
|
let output = path.join(
|
|
624
698
|
path.parse(input).dir,
|
|
625
699
|
path.parse(input).name + ".gif"
|
|
626
700
|
);
|
|
627
701
|
if (!fps) fps = 15;
|
|
628
|
-
|
|
702
|
+
|
|
703
|
+
let command = `${ffmpegPath} -nostats -loglevel 0 -y -i ${input} -vf "fps=${fps},scale=${width}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 ${output}`;
|
|
629
704
|
exec(command, (error, stdout, stderr) => {
|
|
630
705
|
if (error) {
|
|
631
706
|
log(config, "debug", error.message);
|