doc-detective 2.13.0-dev.0 → 2.13.1

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.
Files changed (38) hide show
  1. package/.github/FUNDING.yml +14 -14
  2. package/.github/dependabot.yml +11 -11
  3. package/.github/workflows/docker-image.yml +19 -19
  4. package/.github/workflows/electron-publish.yml +17 -17
  5. package/.github/workflows/npm-publish.yml +67 -67
  6. package/.github/workflows/npm-test.yaml +49 -49
  7. package/CONTRIBUTIONS.md +27 -27
  8. package/LICENSE +20 -20
  9. package/README.md +126 -126
  10. package/dev/dev.spec.json +62 -62
  11. package/dev/index.js +5 -5
  12. package/doc-detective.svg +26 -0
  13. package/package.json +44 -43
  14. package/samples/.doc-detective.json +82 -90
  15. package/samples/{doc-content.md → doc-content-detect.md} +10 -8
  16. package/samples/doc-content-inline-tests.md +28 -28
  17. package/samples/docker-hello.spec.json +14 -0
  18. package/samples/http.spec.json +26 -0
  19. package/samples/kitten-search-inline.md +15 -0
  20. package/samples/kitten-search.spec.json +28 -0
  21. package/samples/tests.spec.json +70 -70
  22. package/samples/variables.env +4 -4
  23. package/src/index.js +84 -84
  24. package/src/utils.js +190 -188
  25. package/test/artifacts/cleanup.spec.json +18 -18
  26. package/test/artifacts/config.json +42 -42
  27. package/test/artifacts/doc-content.md +17 -17
  28. package/test/artifacts/env +2 -2
  29. package/test/artifacts/httpRequest.spec.json +55 -55
  30. package/test/artifacts/runShell.spec.json +29 -29
  31. package/test/artifacts/setup.spec.json +18 -18
  32. package/test/artifacts/test.spec.json +60 -60
  33. package/test/runCoverage.test.js +24 -18
  34. package/test/runTests.test.js +29 -22
  35. package/test/test-config.json +12 -12
  36. package/test/test-results.json +124 -124
  37. package/test/utils.test.js +210 -204
  38. package/.doc-detective.json +0 -63
package/src/index.js CHANGED
@@ -1,85 +1,85 @@
1
1
  #!/usr/bin/env node
2
-
3
- const { runTests, runCoverage } = require("doc-detective-core");
4
- const { setArgs, setConfig, outputResults, setMeta } = require("./utils");
5
- const { argv } = require("node:process");
6
- const path = require("path");
7
- const fs = require("fs");
8
- const prompt = require("prompt-sync")();
9
-
10
- function complete(commands) {
11
- return function (str) {
12
- var i;
13
- var ret = [];
14
- for (i = 0; i < commands.length; i++) {
15
- if (commands[i].indexOf(str) == 0) ret.push(commands[i]);
16
- }
17
- return ret;
18
- };
19
- }
20
-
21
- // Run
22
- setMeta();
23
- main(argv);
24
-
25
- // Run
26
- async function main(argv) {
27
- // Find index of `doc-detective` or `run` in argv
28
- const index = argv.findIndex(
29
- (arg) => arg.endsWith("doc-detective") || arg.endsWith("index.js")
30
- );
31
- // `command` is the next argument after `doc-detective` or `src/index.js`
32
- let command = argv[index + 1];
33
- // Set args
34
- argv = setArgs(argv);
35
- // Get .doc-detective.json config, if it exists
36
- const configPath = path.resolve(process.cwd(), ".doc-detective.json");
37
- let config = {};
38
- if (fs.existsSync(configPath)) {
39
- config = require(configPath);
40
- }
41
- // Set config
42
- config = setConfig(config, argv);
43
- command = command || config.defaultCommand;
44
- // If no command, prompt user to select a command
45
- if (command !== "runTests" && command !== "runCoverage") {
46
- const ask = `
47
- Welcome to Doc Detective. Choose a command:
48
- - 'runTests' - Run tests defined in specifications and documentation source files.
49
- - 'runCoverage' - Calculate test coverage of doc content.
50
-
51
- You can skip this next time by running 'npx doc-detective <command>'. You can also set 'defaultCommand' in your .doc-detective.json config file.
52
-
53
- For more info, visit https://doc-detective.com.
54
-
55
- Command: `;
56
- command = prompt({
57
- ask,
58
- value: "runTests",
59
- autocomplete: complete(["runTests", "runCoverage"]),
60
- });
61
- }
62
-
63
- // Run command
64
- let results = {};
65
- let outputDir;
66
- let outputReportType;
67
- if (command === "runCoverage") {
68
- outputDir = config?.runCoverage?.output || config.output;
69
- outputReportType = "coverageResults";
70
- results = await runCoverage(config);
71
- } else if (command === "runTests") {
72
- outputDir = config?.runTests?.output || config.output;
73
- outputReportType = "testResults";
74
- results = await runTests(config);
75
- } else {
76
- console.error(`Sorry, that's not a recognized command. Please try again.`);
77
- process.exit(1);
78
- }
79
- // Output results
80
- const outputPath = path.resolve(
81
- outputDir,
82
- `${outputReportType}-${Date.now()}.json`
83
- );
84
- await outputResults(config, outputPath, results);
85
- }
2
+
3
+ const { runTests, runCoverage } = require("doc-detective-core");
4
+ const { setArgs, setConfig, outputResults, setMeta } = require("./utils");
5
+ const { argv } = require("node:process");
6
+ const path = require("path");
7
+ const fs = require("fs");
8
+ const prompt = require("prompt-sync")();
9
+
10
+ function complete(commands) {
11
+ return function (str) {
12
+ var i;
13
+ var ret = [];
14
+ for (i = 0; i < commands.length; i++) {
15
+ if (commands[i].indexOf(str) == 0) ret.push(commands[i]);
16
+ }
17
+ return ret;
18
+ };
19
+ }
20
+
21
+ // Run
22
+ setMeta();
23
+ main(argv);
24
+
25
+ // Run
26
+ async function main(argv) {
27
+ // Find index of `doc-detective` or `run` in argv
28
+ const index = argv.findIndex(
29
+ (arg) => arg.endsWith("doc-detective") || arg.endsWith("index.js")
30
+ );
31
+ // `command` is the next argument after `doc-detective` or `src/index.js`
32
+ let command = argv[index + 1];
33
+ // Set args
34
+ argv = setArgs(argv);
35
+ // Get .doc-detective.json config, if it exists
36
+ const configPath = path.resolve(process.cwd(), ".doc-detective.json");
37
+ let config = {};
38
+ if (fs.existsSync(configPath)) {
39
+ config = require(configPath);
40
+ }
41
+ // Set config
42
+ config = setConfig(config, argv);
43
+ command = command || config.defaultCommand;
44
+ // If no command, prompt user to select a command
45
+ if (command !== "runTests" && command !== "runCoverage") {
46
+ const ask = `
47
+ Welcome to Doc Detective. Choose a command:
48
+ - 'runTests' - Run tests defined in specifications and documentation source files.
49
+ - 'runCoverage' - Calculate test coverage of doc content.
50
+
51
+ You can skip this next time by running 'npx doc-detective <command>'. You can also set 'defaultCommand' in your .doc-detective.json config file.
52
+
53
+ For more info, visit https://doc-detective.com.
54
+
55
+ Command: `;
56
+ command = prompt({
57
+ ask,
58
+ value: "runTests",
59
+ autocomplete: complete(["runTests", "runCoverage"]),
60
+ });
61
+ }
62
+
63
+ // Run command
64
+ let results = {};
65
+ let outputDir;
66
+ let outputReportType;
67
+ if (command === "runCoverage") {
68
+ outputDir = config?.runCoverage?.output || config.output;
69
+ outputReportType = "coverageResults";
70
+ results = await runCoverage(config);
71
+ } else if (command === "runTests") {
72
+ outputDir = config?.runTests?.output || config.output;
73
+ outputReportType = "testResults";
74
+ results = await runTests(config);
75
+ } else {
76
+ console.error(`Sorry, that's not a recognized command. Please try again.`);
77
+ process.exit(1);
78
+ }
79
+ // Output results
80
+ const outputPath = path.resolve(
81
+ outputDir,
82
+ `${outputReportType}-${Date.now()}.json`
83
+ );
84
+ await outputResults(config, outputPath, results);
85
+ }
package/src/utils.js CHANGED
@@ -1,188 +1,190 @@
1
- const yargs = require("yargs/yargs");
2
- const { hideBin } = require("yargs/helpers");
3
- const { validate } = require("doc-detective-common");
4
- const path = require("path");
5
- const fs = require("fs");
6
- const { spawn } = require("child_process");
7
- const os = require("os");
8
-
9
- exports.setArgs = setArgs;
10
- exports.setConfig = setConfig;
11
- exports.outputResults = outputResults;
12
- exports.spawnCommand = spawnCommand;
13
- exports.setMeta = setMeta;
14
-
15
- // Define args
16
- function setArgs(args) {
17
- if (!args) return {};
18
- let argv = yargs(hideBin(args))
19
- .option("config", {
20
- alias: "c",
21
- description: "Path to a `config.json` file.",
22
- type: "string",
23
- })
24
- .option("input", {
25
- alias: "i",
26
- description:
27
- "Path to test specifications and documentation source files. May be paths to specific files or to directories to scan for files.",
28
- type: "string",
29
- })
30
- .option("output", {
31
- alias: "o",
32
- description:
33
- "Path of the directory in which to store the output of Doc Detective commands.",
34
- type: "string",
35
- })
36
- .option("setup", {
37
- description:
38
- "Path to test specifications to perform before those specified by `input`. Useful for setting up testing environments.",
39
- type: "string",
40
- })
41
- .option("cleanup", {
42
- description:
43
- "Path to test specifications to perform after those specified by input. Useful for cleaning up testing environments.",
44
- type: "string",
45
- })
46
- .option("recursive", {
47
- alias: "r",
48
- description:
49
- "Boolean. If true searches input, setup, and cleanup paths recursively for test specificaions and source files. Defaults to `true`.",
50
- type: "string",
51
- })
52
- .option("logLevel", {
53
- alias: "l",
54
- description:
55
- "Detail level of logging events. Accepted values: silent, error, warning, info (default), debug",
56
- type: "string",
57
- })
58
- .help()
59
- .alias("help", "h").argv;
60
-
61
- return argv;
62
- }
63
-
64
- // Override config values based on args
65
- function setConfig(config, args) {
66
- // If no args, return config
67
- if (!args) return config;
68
-
69
- // Load config from file
70
- if (args.config) {
71
- const configPath = path.resolve(args.config);
72
- configContent = require(configPath);
73
- // Validate config
74
- const validation = validate("config_v2", configContent);
75
- if (validation.valid) {
76
- config = configContent;
77
- } else {
78
- // Output validation errors
79
- console.error("Invalid config file:");
80
- validation.errors.forEach((error) => {
81
- console.error(error);
82
- });
83
- process.exit(1);
84
- }
85
- }
86
-
87
- // Override config values
88
- if (args.input) config.input = args.input;
89
- if (args.output) config.output = args.output;
90
- if (args.recursive) config.recursive = args.recursive;
91
- if (args.logLevel) config.logLevel = args.logLevel;
92
- if (
93
- (args.setup || args.cleanup || args.input || args.output) &&
94
- !config.runTests
95
- )
96
- config.runTests = {};
97
- if (args.input) config.runTests.input = args.input;
98
- if (args.output) config.runTests.output = args.output;
99
- if (args.setup) config.runTests.setup = args.setup;
100
- if (args.cleanup) config.runTests.cleanup = args.cleanup;
101
-
102
- // Validate config
103
- const validation = validate("config_v2", config);
104
- if (!validation.valid) {
105
- // Output validation errors
106
- console.error("Invalid config.");
107
- validation.errors.forEach((error) => {
108
- console.error(error);
109
- });
110
- process.exit(1);
111
- }
112
-
113
- return config;
114
- }
115
-
116
- async function outputResults(config, path, results) {
117
- let data = JSON.stringify(results, null, 2);
118
- fs.writeFile(path, data, (err) => {
119
- if (err) throw err;
120
- });
121
- console.log(`See results at ${path}`);
122
- }
123
-
124
- // Perform a native command in the current working directory.
125
- async function spawnCommand(cmd, args) {
126
- // Split command into command and arguments
127
- if (cmd.includes(" ")) {
128
- const cmdArray = cmd.split(" ");
129
- cmd = cmdArray[0];
130
- cmdArgs = cmdArray.slice(1);
131
- // Add arguments to args array
132
- if (args) {
133
- args = cmdArgs.concat(args);
134
- } else {
135
- args = cmdArgs;
136
- }
137
- }
138
-
139
- const runCommand = spawn(cmd, args);
140
-
141
- // Capture stdout
142
- let stdout = "";
143
- for await (const chunk of runCommand.stdout) {
144
- stdout += chunk;
145
- }
146
- // Remove trailing newline
147
- stdout = stdout.replace(/\n$/, "");
148
-
149
- // Capture stderr
150
- let stderr = "";
151
- for await (const chunk of runCommand.stderr) {
152
- stderr += chunk;
153
- }
154
- // Remove trailing newline
155
- stderr = stderr.replace(/\n$/, "");
156
-
157
- // Capture exit code
158
- const exitCode = await new Promise((resolve, reject) => {
159
- runCommand.on("close", resolve);
160
- });
161
-
162
- return { stdout, stderr, exitCode };
163
- }
164
-
165
- function setMeta() {
166
- const platformMap = {
167
- win32: "windows",
168
- darwin: "mac",
169
- linux: "linux",
170
- };
171
-
172
- // Set meta
173
- const meta =
174
- process.env["DOC_DETECTIVE_META"] !== undefined
175
- ? JSON.parse(process.env["DOC_DETECTIVE_META"])
176
- : {};
177
- const package = require("../package.json");
178
- meta.distribution = "doc-detective";
179
- meta.dist_version = package.version;
180
- meta.dist_platform = platformMap[os.platform()] || os.platform();
181
- meta.dist_platform_version = os.release();
182
- meta.dist_platform_arch = os.arch();
183
- meta.dist_deployment = meta.dist_deployment || "node";
184
- meta.dist_deployment_version =
185
- meta.dist_deployment_version || process.version;
186
- meta.dist_interface = meta.dist_interface || "cli";
187
- process.env["DOC_DETECTIVE_META"] = JSON.stringify(meta);
188
- }
1
+ const yargs = require("yargs/yargs");
2
+ const { hideBin } = require("yargs/helpers");
3
+ const { validate } = require("doc-detective-common");
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+ const { spawn } = require("child_process");
7
+ const os = require("os");
8
+
9
+ exports.setArgs = setArgs;
10
+ exports.setConfig = setConfig;
11
+ exports.outputResults = outputResults;
12
+ exports.spawnCommand = spawnCommand;
13
+ exports.setMeta = setMeta;
14
+
15
+ // Define args
16
+ function setArgs(args) {
17
+ if (!args) return {};
18
+ let argv = yargs(hideBin(args))
19
+ .option("config", {
20
+ alias: "c",
21
+ description: "Path to a `config.json` file.",
22
+ type: "string",
23
+ })
24
+ .option("input", {
25
+ alias: "i",
26
+ description:
27
+ "Path to test specifications and documentation source files. May be paths to specific files or to directories to scan for files.",
28
+ type: "string",
29
+ })
30
+ .option("output", {
31
+ alias: "o",
32
+ description:
33
+ "Path of the directory in which to store the output of Doc Detective commands.",
34
+ type: "string",
35
+ })
36
+ .option("setup", {
37
+ description:
38
+ "Path to test specifications to perform before those specified by `input`. Useful for setting up testing environments.",
39
+ type: "string",
40
+ })
41
+ .option("cleanup", {
42
+ description:
43
+ "Path to test specifications to perform after those specified by input. Useful for cleaning up testing environments.",
44
+ type: "string",
45
+ })
46
+ .option("recursive", {
47
+ alias: "r",
48
+ description:
49
+ "Boolean. If true searches input, setup, and cleanup paths recursively for test specificaions and source files. Defaults to `true`.",
50
+ type: "string",
51
+ })
52
+ .option("logLevel", {
53
+ alias: "l",
54
+ description:
55
+ "Detail level of logging events. Accepted values: silent, error, warning, info (default), debug",
56
+ type: "string",
57
+ })
58
+ .help()
59
+ .alias("help", "h").argv;
60
+
61
+ return argv;
62
+ }
63
+
64
+ // Override config values based on args
65
+ function setConfig(config, args) {
66
+ // If no args, return config
67
+ if (!args) return config;
68
+
69
+ // Load config from file
70
+ if (args.config) {
71
+ const configPath = path.resolve(args.config);
72
+ configContent = require(configPath);
73
+ // Validate config
74
+ const validation = validate("config_v2", configContent);
75
+ if (validation.valid) {
76
+ config = configContent;
77
+ } else {
78
+ // Output validation errors
79
+ console.error("Invalid config file:");
80
+ validation.errors.forEach((error) => {
81
+ console.error(error);
82
+ });
83
+ process.exit(1);
84
+ }
85
+ }
86
+
87
+ // Override config values
88
+ if (args.input) config.input = args.input;
89
+ if (args.output) config.output = args.output;
90
+ if (args.recursive) config.recursive = args.recursive;
91
+ if (args.logLevel) config.logLevel = args.logLevel;
92
+ if (
93
+ (args.setup || args.cleanup || args.input || args.output) &&
94
+ !config.runTests
95
+ )
96
+ config.runTests = {};
97
+ if (args.input) config.runTests.input = args.input;
98
+ if (args.output) config.runTests.output = args.output;
99
+ if (args.setup) config.runTests.setup = args.setup;
100
+ if (args.cleanup) config.runTests.cleanup = args.cleanup;
101
+
102
+ // Validate config
103
+ const validation = validate("config_v2", config);
104
+ if (!validation.valid) {
105
+ // Output validation errors
106
+ console.error("Invalid config.");
107
+ validation.errors.forEach((error) => {
108
+ console.error(error);
109
+ });
110
+ process.exit(1);
111
+ }
112
+
113
+ return config;
114
+ }
115
+
116
+ async function outputResults(config, path, results) {
117
+ let data = JSON.stringify(results, null, 2);
118
+ try {
119
+ fs.writeFileSync(path, data);
120
+ console.log(`See results at ${path}`);
121
+ } catch (err) {
122
+ console.error(`Error writing results to ${path}: ${err}`);
123
+ }
124
+ }
125
+
126
+ // Perform a native command in the current working directory.
127
+ async function spawnCommand(cmd, args) {
128
+ // Split command into command and arguments
129
+ if (cmd.includes(" ")) {
130
+ const cmdArray = cmd.split(" ");
131
+ cmd = cmdArray[0];
132
+ cmdArgs = cmdArray.slice(1);
133
+ // Add arguments to args array
134
+ if (args) {
135
+ args = cmdArgs.concat(args);
136
+ } else {
137
+ args = cmdArgs;
138
+ }
139
+ }
140
+
141
+ const runCommand = spawn(cmd, args);
142
+
143
+ // Capture stdout
144
+ let stdout = "";
145
+ for await (const chunk of runCommand.stdout) {
146
+ stdout += chunk;
147
+ }
148
+ // Remove trailing newline
149
+ stdout = stdout.replace(/\n$/, "");
150
+
151
+ // Capture stderr
152
+ let stderr = "";
153
+ for await (const chunk of runCommand.stderr) {
154
+ stderr += chunk;
155
+ }
156
+ // Remove trailing newline
157
+ stderr = stderr.replace(/\n$/, "");
158
+
159
+ // Capture exit code
160
+ const exitCode = await new Promise((resolve, reject) => {
161
+ runCommand.on("close", resolve);
162
+ });
163
+
164
+ return { stdout, stderr, exitCode };
165
+ }
166
+
167
+ function setMeta() {
168
+ const platformMap = {
169
+ win32: "windows",
170
+ darwin: "mac",
171
+ linux: "linux",
172
+ };
173
+
174
+ // Set meta
175
+ const meta =
176
+ process.env["DOC_DETECTIVE_META"] !== undefined
177
+ ? JSON.parse(process.env["DOC_DETECTIVE_META"])
178
+ : {};
179
+ const package = require("../package.json");
180
+ meta.distribution = "doc-detective";
181
+ meta.dist_version = package.version;
182
+ meta.dist_platform = platformMap[os.platform()] || os.platform();
183
+ meta.dist_platform_version = os.release();
184
+ meta.dist_platform_arch = os.arch();
185
+ meta.dist_deployment = meta.dist_deployment || "node";
186
+ meta.dist_deployment_version =
187
+ meta.dist_deployment_version || process.version;
188
+ meta.dist_interface = meta.dist_interface || "cli";
189
+ process.env["DOC_DETECTIVE_META"] = JSON.stringify(meta);
190
+ }
@@ -1,19 +1,19 @@
1
- {
2
- "id": "cleanup",
3
- "tests": [
4
- {
5
- "steps": [
6
- {
7
- "action": "setVariables",
8
- "path": ".env"
9
- },
10
- {
11
- "action": "runShell",
12
- "command": "echo",
13
- "args": ["cleanup"]
14
- }
15
- ]
16
- }
17
- ]
18
- }
1
+ {
2
+ "id": "cleanup",
3
+ "tests": [
4
+ {
5
+ "steps": [
6
+ {
7
+ "action": "setVariables",
8
+ "path": ".env"
9
+ },
10
+ {
11
+ "action": "runShell",
12
+ "command": "echo",
13
+ "args": ["cleanup"]
14
+ }
15
+ ]
16
+ }
17
+ ]
18
+ }
19
19