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 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 requires `ffmpeg` installed on the machine that runs Doc Detective and also creates `.mp4` files of the recordings.
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.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",
@@ -2,6 +2,8 @@
2
2
  "env": "sample/variables.env",
3
3
  "input": "sample/doc-content.md",
4
4
  "output": "sample/results.json",
5
+ "setup": "",
6
+ "cleanup": "",
5
7
  "recursive": true,
6
8
  "testExtensions": [
7
9
  ".md",
package/src/config.json CHANGED
@@ -2,6 +2,8 @@
2
2
  "env": "",
3
3
  "input": ".",
4
4
  "output": "./results.json",
5
+ "setup": "",
6
+ "cleanup": "",
5
7
  "recursive": true,
6
8
  "testExtensions": [
7
9
  ".md",
@@ -483,7 +483,7 @@ async function sendAnalytics(config, results) {
483
483
  .then(() => {
484
484
  log(
485
485
  config,
486
- "info",
486
+ "debug",
487
487
  `Sucessfully sent analytics to ${server.displayname}.`
488
488
  );
489
489
  })
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 = argv.logLevel || process.env.DOC_LOG_LEVEL || config.logLevel || "info";
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
- config.env = path.resolve(config.env);
176
- if (config.env && fs.existsSync(config.env)) {
177
- let envResult = setEnvs(config.env);
178
- if (envResult.status === "PASS")
179
- log(config, "debug", `Env file set: ${config.env}`);
180
- if (envResult.status === "FAIL")
181
- log(config, "warning", `File format issue. Can't load env file.`);
182
- } else if (config.env && !fs.existsSync(config.env)) {
183
- log(config, "warning", `Invalid file path. Can't load env file.`);
184
- } else if (!config.env) {
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
- config.input = path.resolve(config.input);
193
- if (fs.existsSync(config.input)) {
194
- log(config, "debug", `Input path set: ${config.input}`);
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 = path.resolve(config.browserOptions.path);
324
- if (fs.existsSync(config.browserOptions.path)) {
325
- log(config, "debug", `Browser path set: ${config.browserOptions.path}`);
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 = defaultConfig.browserOptions.path;
328
- log(
329
- config,
330
- "warning",
331
- `Invalid browser path. Reverted to default Chromium install.`
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 input = path.resolve(config.input);
507
- let isFile = fs.statSync(input).isFile();
508
- let isDir = fs.statSync(input).isDirectory();
509
- if (!isFile && !isDir) {
510
- log(config, "error", "Input isn't a valid file or directory.");
511
- exit(1);
512
- }
513
-
514
- // Parse input
515
- if (isFile) {
516
- // if single file specified
517
- files[0] = input;
518
- return files;
519
- } else if (isDir) {
520
- // Load files from drectory
521
- dirs[0] = input;
522
- for (let i = 0; i < dirs.length; i++) {
523
- fs.readdirSync(dirs[i]).forEach((object) => {
524
- let content = path.resolve(dirs[i] + "/" + object);
525
- let isFile = fs.statSync(content).isFile();
526
- let isDir = fs.statSync(content).isDirectory();
527
- if (isFile) {
528
- // is a file
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
- // No specified extension filter list, or file extension is present in extension filter list.
531
- config.testExtensions === "" ||
532
- config.testExtensions.includes(path.extname(content))
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
- let command = `ffmpeg -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}`;
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);