doc-detective 1.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2021- Manny Silva
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,543 @@
1
+ # Doc Detective: A Documentation Unit Testing Framework
2
+
3
+ Unit test documentation to validate UX flows, in-GUI text, and images. Primarily useful for process docs, Doc Detective supports test definitions single-sourced in documentation or defined in separate test files to suit your infrastructure needs.
4
+
5
+ Doc Detective ingests text files, parses them for test actions, then executes those actions in a headless Chromium browser. The results (PASS/FAIL and context) are output as a JSON object so that other pieces of infrastructure can parse and manipulate them as needed.
6
+
7
+ This project handles test parsing and web-based UI testing--it doesn't support results reporting or notifications. This framework is a part of testing infrastructures and needs to be complimented by other components.
8
+
9
+ Doc Detective uses `puppeteer` to install, launch, and drive Chromium to perform tests. `puppeteer` removes the requirement to manually configure a local web browser and enables easy screenshotting and video recording.
10
+
11
+ **Note:** By default, `puppeteer`'s Chromium doesn't run in Docker containers, which means that `puppeteer` doesn't work either. Don't run Doc Detective in a Docker container unless you first confirm that you have a custom implementation of headless Chrome/Chromium functional in the container. The approved answer to [this question](https://askubuntu.com/questions/79280/how-to-install-chrome-browser-properly-via-command-line) works for me, but it may not work in all environments.
12
+
13
+ ## Get started
14
+
15
+ You can use Doc Detective as an [NPM package](#npm-package) or a standalone [CLI tool](#cli-tool).
16
+
17
+ ### NPM package
18
+
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
+
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
+ 1. In a terminal, navigate to your Node project, then install Doc Detective:
24
+
25
+ ```bash
26
+ npm i doc-detective
27
+ ```
28
+
29
+ 1. Add a reference to the package in your project:
30
+
31
+ ```node
32
+ const { run } = require("doc-detective");
33
+ ```
34
+
35
+ 1. Run tests with the `run()` method:
36
+
37
+ ```node
38
+ run(config);
39
+ ```
40
+
41
+ ### CLI tool
42
+
43
+ You can run Doc Detective as a standalone CLI tool. When running as a CLI tool, you can specify default configuration options in [config.json](https://github.com/hawkeyexl/doc-detective/blob/master/sample/config.json) and override those defaults with command-line arguments. (For a list of arguments, complete the following steps and run `npm run test -- -h`.)
44
+
45
+ 0. Install prerequisites:
46
+
47
+ - [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
+
50
+ 1. In a terminal, clone the repo and install dependencies:
51
+
52
+ ```bash
53
+ git clone https://github.com/hawkeyexl/doc-detective.git
54
+ cd doc-detective
55
+ npm install
56
+ ```
57
+
58
+ 1. Run tests according to your config. The `-c` argument is required and specifies the path to your config. The following example runs tests in the [sample/](https://github.com/hawkeyexl/doc-detective/tree/master/sample) directory:
59
+
60
+ ```bash
61
+ npm run test -- -c sample/config.json
62
+ ```
63
+
64
+ To customize your test, file type, and directory options, update [sample/config.json](https://github.com/hawkeyexl/doc-detective/blob/master/sample/config.json).
65
+
66
+ ## Tests
67
+
68
+ You can define tests within your documentation (see [doc-content.md](https://github.com/hawkeyexl/doc-detective/blob/master/sample/doc-content.md)), or as separate files. Non-JSON files only support single-line test action definitions, so make sure to keep the entire action definition on one line.
69
+
70
+ JSON files must follow the format and structure defined in [testDefinition](https://github.com/hawkeyexl/doc-detective/blob/master/ref/testDefinition.json). For an example, see [samples/tests.json](https://github.com/hawkeyexl/doc-detective/blob/master/sample/tests.json).
71
+
72
+ ## Actions
73
+
74
+ Each test is composed of multiple actions. Actions in a test perform sequentially as they're defined. If one or more actions fail, the test fails.
75
+
76
+ For information on each field, see [testDefinition](https://github.com/hawkeyexl/doc-detective/blob/master/ref/testDefinition.json).
77
+
78
+ ### goTo
79
+
80
+ Navigate to a specified URI.
81
+
82
+ Format:
83
+
84
+ ```json
85
+ {
86
+ "action": "goTo",
87
+ "uri": "https://www.google.com"
88
+ }
89
+ ```
90
+
91
+ ### Find
92
+
93
+ Identify if an element is on the current page based on CSS selectors.
94
+
95
+ Optionally, `find` canperform additional actions on the element in the specified order: [`wait`](#wait) (always waiting for the `css` value) > `find` > [`matchText`](#match-text) > [`moveMouse`](#move-mouse) > [`click`](#click) > [`type`](#type). Payloads for these additional actions are nested within the `find` action's definition and (other than omitting the `css` field) match the format for running each action separately.
96
+
97
+ Simple format:
98
+
99
+ ```json
100
+ {
101
+ "action": "find",
102
+ "css": "[title=Search]"
103
+ }
104
+ ```
105
+ Advanced format:
106
+
107
+ ```json
108
+ {
109
+ "action": "find",
110
+ "css": "[title=Search]",
111
+ "wait": {
112
+ "duration": 10000,
113
+ },
114
+ "matchText": {
115
+ "text": "Search"
116
+ },
117
+ "moveMouse": {
118
+ "alignH": "center",
119
+ "alignV": "center",
120
+ "offsetX": 0,
121
+ "offsetY": 0
122
+ },
123
+ "click": {},
124
+ "type": {
125
+ "keys": "$SHORTHAIR_CAT_SEARCH",
126
+ "trailingSpecialKey": "Enter",
127
+ "envs": "./sample/variables.env"
128
+ }
129
+ }
130
+ ```
131
+
132
+ ### Match text
133
+
134
+ Identify if an element displays the expected text.
135
+
136
+ Format:
137
+
138
+ ```json
139
+ {
140
+ "action": "matchText",
141
+ "css": "#gbqfbb",
142
+ "text": "I'm Feeling Lucky"
143
+ }
144
+ ```
145
+
146
+ ### Click
147
+
148
+ Click an element specified by CSS selectors.
149
+
150
+ Format:
151
+
152
+ ```json
153
+ {
154
+ "action": "click",
155
+ "css": "#gbqfbb",
156
+ "moveMouse": "true",
157
+ "alignH": "center",
158
+ "alignV": "center",
159
+ "offsetX": 10,
160
+ "offsetY": 10
161
+
162
+ }
163
+ ```
164
+
165
+ ### Type
166
+
167
+ Enter text in an element specified by CSS selectors.
168
+
169
+ `keys` can be either a string or an environment variable. Environment variables are identified by a leading '$', and you can set environment variables by passing a .env file ([sample](https://github.com/hawkeyexl/doc-detective/blob/master/sample/variables.env)) to the `env` field. If the variable is undefined on the machine running the test, the `keys` value is typed as a string. For example, if `keys` is "$KITTENS" and the `KITTENS` environment variable is set to "cute kittens", the test types "cute kittens", but if the `KITTENS` environment variable isn't defined, the test types the string "$KITTENS".
170
+
171
+ **Warning:** If you want to pass sensitive strings like usernames or passwords into the `type` action, store those values in a local .env file, point `env` to that file, and reference the variable in `keys`. Don't include cleartext passwords in your tests. Don't check .env files with sensitive data into a repository. Be careful with your credentials! Consult your security team if you have concerns.
172
+
173
+ Format:
174
+
175
+ ```json
176
+ {
177
+ "action": "type",
178
+ "css": "[title=Search]",
179
+ "keys": "kittens",
180
+ "trailingSpecialKey": "Enter"
181
+ }
182
+ ```
183
+
184
+ Advanced format with an environment variable:
185
+
186
+ ```json
187
+ {
188
+ "action": "type",
189
+ "css": "input#password",
190
+ "keys": "$PASSWORD",
191
+ "trailingSpecialKey": "Enter",
192
+ "envs": "./sample/variables.env"
193
+ }
194
+ ```
195
+
196
+ ### Move mouse
197
+
198
+ Move the mouse to an element specified by CSS selectors.
199
+
200
+ **Note:** The mouse cursor is visible in both recordings and screenshots.
201
+
202
+ Format:
203
+
204
+ ```json
205
+ {
206
+ "action": "moveMouse",
207
+ "css": "[title=Search]",
208
+ "alignH": "center",
209
+ "alignV": "center",
210
+ "offsetX": 10,
211
+ "offsetY": 10
212
+ }
213
+ ```
214
+
215
+ ### Scroll
216
+
217
+ Scroll the current page by a specified number of pixels. For `x`, positive values scroll right and negative values scroll left. For `y`, positive values scroll down and negative values scroll up.
218
+
219
+ Format:
220
+
221
+ ```json
222
+ {
223
+ "action": "scroll",
224
+ "x": 100,
225
+ "y": 100
226
+ }
227
+ ```
228
+
229
+ ### Wait
230
+
231
+ Pause before performing the next action. If `css` is set, this action waits until the element is available or the `duration` is met, whichever comes first. If not set, `duration` defaults to `10000` milliseconds.
232
+
233
+ Format:
234
+
235
+ ```json
236
+ {
237
+ "action": "wait",
238
+ "css": "[title=Search]",
239
+ "duration": 500
240
+ }
241
+ ```
242
+
243
+ ### Screenshot
244
+
245
+ Capture an image of the current browser viewport. Supported extensions: .png
246
+
247
+ To match previously captured screenshots to the current viewport, set `matchPrevious` to `true` and specify a `matchThreshold` value. `matchThreshold` is a value between 0 and 1 that specifies what percentage of a screenshot can change before the action fails. For example, a `matchThreshold` value of `0.1` makes the action pass if the screenshots are up to 10% different or fail if the screenshots are 11% or more different. Screenshot comparison is based on pixel-level image analysis.
248
+
249
+ Format:
250
+
251
+ ```json
252
+ {
253
+ "action": "screenshot",
254
+ "mediaDirectory": "samples",
255
+ "filename": "results.png",
256
+ "matchPrevious": true,
257
+ "matchThreshold": 0.1
258
+ }
259
+ ```
260
+
261
+ ### Check a link
262
+
263
+ Check if a link returns an acceptable status code from a GET request. If `uri` doesn't include a protocol, the protocol defaults to HTTPS. If `statuscodes` isn't specified, defaults to `[200]`.
264
+
265
+ Format:
266
+
267
+ ```json
268
+ {
269
+ "action": "checkLink",
270
+ "uri": "https://www.google.com",
271
+ "statusCodes": [ 200 ]
272
+ }
273
+ ```
274
+
275
+ ### Start recording
276
+
277
+ Start recording the current browser viewport. Must be followed by a `stopRecording` action. Supported extensions: .mp4, .gif
278
+
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.
280
+
281
+ Format:
282
+
283
+ ```json
284
+ {
285
+ "action": "startRecording",
286
+ "mediaDirectory": "samples",
287
+ "filename": "results.mp4",
288
+ "gifFps": 15,
289
+ "gifWidth": 400
290
+ }
291
+ ```
292
+
293
+ ### Stop recording
294
+
295
+ Stop recording the current browser viewport.
296
+
297
+ Format:
298
+
299
+ ```json
300
+ {
301
+ "action": "stopRecording"
302
+ }
303
+ ```
304
+
305
+ ### Run shell command
306
+
307
+ Perform a native shell command on the machine running Doc Detective. This can be a single command or a script. Set environment variables before running the command by specifying an env file in the `env` field. For reference, see [variables.env](https://github.com/hawkeyexl/doc-detective/blob/master/sample/variables.env).
308
+
309
+ Returns `PASS` if the command has an exit code of `0`. Returns `FAIL` if the command had a non-`0` exit code and outputs a `stderr` value.
310
+
311
+ Format:
312
+
313
+ ```json
314
+ {
315
+ "action": "runShell",
316
+ "command": "echo $username",
317
+ "env": "./variables.env"
318
+ }
319
+ ```
320
+
321
+ ## Analytics
322
+
323
+ By default, Doc Detective doesn't collect any information about tests, devices, users, or documentation and doesn't check in with any external service or server. If you want to help inform decisions about the future development of Doc Detective—such as feature development and documentation creation—you can opt into sending anonymized analytics after you run tests at one of the multiple levels of detail.
324
+
325
+ There are multiple ways to turn on analytics:
326
+
327
+ - config setting: In your [config](https://github.com/hawkeyexl/doc-detective/blob/master/sample/config.json), set `analytics.send` to true.
328
+ - CLI argument: When running Doc Detective as a CLI tool, include `-a true` or `--analytics true`. This overrides any setting you specified in your config.
329
+
330
+ Most fields are self-explanatory, but a few merit additional description:
331
+
332
+ - `version` is populated with the version of your Doc Detective instance.
333
+ - `userId` is whatever arbitrary string, if any, you specify to identify the individual, workgroup, or organization running the tests.
334
+ - `detailLevel` must match one of the four supported levels of detail:
335
+ - `run` indicates that tests were run, and that's about it. It omits the `tests`, `actions`, and `actionDetails` objects.
336
+ - `test` includes aggregate data on the number of tests you ran and the tests' PASS/FAIL rates. It omits the `actions`, and `actionDetails` objects.
337
+ - `action-simple` includes aggregate data on the number of actions in tests you ran and the actions' PASS/FAIL rates. It omits the `actionDetails` object.
338
+ - `action-detailed` includes aggregate data on the kinds of actions you ran and the actions' PASS/FAIL rates. It doesn't omit any objects.
339
+
340
+ The analytics object has the following schema:
341
+
342
+ ```json
343
+ {
344
+ "version": "0.1.8",
345
+ "userId": "",
346
+ "detailLevel": "action-detailed",
347
+ "tests": {
348
+ "numberTests": 0,
349
+ "passed": 0,
350
+ "failed": 0
351
+ },
352
+ "actions": {
353
+ "numberActions": 0,
354
+ "averageNumberActionsPerTest": 0,
355
+ "maxActionsPerTest": 0,
356
+ "minActionsPerTest": 0,
357
+ "passed": 0,
358
+ "failed": 0
359
+ },
360
+ "actionDetails": {
361
+ "goTo": {
362
+ "numberInstances": 0,
363
+ "passed": 0,
364
+ "failed": 0,
365
+ "uri": 0
366
+ },
367
+ "find": {
368
+ "numberInstances": 0,
369
+ "passed": 0,
370
+ "failed": 0,
371
+ "wait": {
372
+ "numberInstances": 0,
373
+ "duration": 0
374
+ },
375
+ "matchText": {
376
+ "numberInstances": 0,
377
+ "text": 0
378
+ },
379
+ "moveMouse": {
380
+ "numberInstances": 0,
381
+ "alignH": 0,
382
+ "alignV": 0,
383
+ "offsetX": 0,
384
+ "offsetY": 0
385
+ },
386
+ "click": {
387
+ "numberInstances": 0
388
+ },
389
+ "type": {
390
+ "numberInstances": 0,
391
+ "keys": 0,
392
+ "trailingSpecialKey": 0,
393
+ "env": 0
394
+ }
395
+ },
396
+ "matchText": {
397
+ "numberInstances": 0,
398
+ "passed": 0,
399
+ "failed": 0,
400
+ "css": 0,
401
+ "text": 0
402
+ },
403
+ "click": {
404
+ "numberInstances": 0,
405
+ "passed": 0,
406
+ "failed": 0,
407
+ "css": 0
408
+ },
409
+ "type": {
410
+ "numberInstances": 0,
411
+ "passed": 0,
412
+ "failed": 0,
413
+ "css": 0,
414
+ "keys": 0,
415
+ "trailingSpecialKey": 0,
416
+ "env": 0
417
+ },
418
+ "moveMouse": {
419
+ "numberInstances": 0,
420
+ "passed": 0,
421
+ "failed": 0,
422
+ "css": 0,
423
+ "alignH": 0,
424
+ "alignV": 0,
425
+ "offsetX": 0,
426
+ "offsetY": 0
427
+ },
428
+ "scroll": {
429
+ "numberInstances": 0,
430
+ "passed": 0,
431
+ "failed": 0,
432
+ "x": 0,
433
+ "y": 0
434
+ },
435
+ "wait": {
436
+ "numberInstances": 0,
437
+ "passed": 0,
438
+ "failed": 0,
439
+ "duration": 0,
440
+ "css": 0
441
+ },
442
+ "screenshot": {
443
+ "numberInstances": 0,
444
+ "passed": 0,
445
+ "failed": 0,
446
+ "mediaDirectory": 0,
447
+ "filename": 0,
448
+ "matchPrevious": 0,
449
+ "matchThreshold": 0
450
+ },
451
+ "startRecording": {
452
+ "numberInstances": 0,
453
+ "passed": 0,
454
+ "failed": 0,
455
+ "mediaDirectory": 0,
456
+ "filename": 0,
457
+ "gifFps": 0,
458
+ "gifWidth": 0
459
+ },
460
+ "stopRecording": {
461
+ "numberInstances": 0,
462
+ "passed": 0,
463
+ "failed": 0
464
+ },
465
+ "checkLink": {
466
+ "numberInstances": 0,
467
+ "passed": 0,
468
+ "failed": 0,
469
+ "uri": 0,
470
+ "statusCodes": 0
471
+ },
472
+ "runShell": {
473
+ "numberInstances": 0,
474
+ "passed": 0,
475
+ "failed": 0,
476
+ "command": 0,
477
+ "env": 0
478
+ }
479
+ }
480
+ }
481
+ ```
482
+
483
+ ### Custom analytics servers
484
+
485
+ If you opt into sending analytics, you can add additional servers that Doc Detective should send the analytics object to. Custom servers are specified in your config and have the following schema.
486
+
487
+ `params` and `headers` are optional.
488
+
489
+ ```json
490
+ {
491
+ ...
492
+ "analytics": {
493
+ ...
494
+ "customServers": [
495
+ {
496
+ "name": "My Analytics Server",
497
+ "method": "post",
498
+ "url": "https://my.analytics-server.com/endpoint",
499
+ "params": {
500
+ "param_secret": "LifeTheUniverseAndEverything"
501
+ },
502
+ "headers": {
503
+ "header_secret": "42"
504
+ }
505
+ }
506
+ ]
507
+ }
508
+ ...
509
+ }
510
+ ```
511
+
512
+ ### Turn off analytics
513
+
514
+ Analytics reporting is off by default. If you want to make extra sure that Doc Detective doesn't collect analytics, you have a few options:
515
+
516
+ - config setting: In your [config](https://github.com/hawkeyexl/doc-detective/blob/master/sample/config.json), set `analytics.send` to false.
517
+ - CLI argument: When running Doc Detective as a CLI tool, include `-a false` or `--analytics false`. This overrides any setting you specified in your config.
518
+ - Modify the code (if you're paranoid):
519
+ 1. In [src/index.js](https://github.com/hawkeyexl/doc-detective/blob/master/src/index.js), remove all references to `sendAnalytics()`.
520
+ 1. Delete [src/libs/analytics.js](https://github.com/hawkeyexl/doc-detective/blob/master/src/libs/analytics.js).
521
+
522
+ **Note:** Updating Doc Detective may revert any modified code, so be ready to make code edits repeatedly.
523
+
524
+ ## Potential future features
525
+
526
+ - Browser auto-detection and fallback.
527
+ - Improved default config experience.
528
+ - Environment variable overrides for config options.
529
+ - Docker image with bundled Chromium/Chrome/Firefox.
530
+ - New/upgraded test actions:
531
+ - New: Curl commands. (Support substitution/setting env vars. Only check for `200 OK`.)
532
+ - New: Test if a referenced image (such as an icon) is present in the captured screenshot.
533
+ - Upgrade: `screenshot` and `startRecording` boolean for whether to perform the action or not if the expected output file already exists.
534
+ - Upgrade: `startRecording` to remove MP4 when the output is a GIF.
535
+ - Upgrade: `startRecording` and `stopRecording` to support start, stop, and intermediate test action state image matching to track differences between video captures from different runs.
536
+ - Upgrade: `startRecording` to store the output file in a different location if a recorded action fails. This could help with debugging.
537
+ - In-content test framing to identify when content is covered by a test defined in another file. This could enable content coverage analysis.
538
+ - Suggest tests by parsing document text.
539
+ - Automatically insert suggested tests based on document text.
540
+
541
+ ## License
542
+
543
+ This project uses the [MIT license](https://github.com/hawkeyexl/doc-detective/blob/master/LICENSE).
package/cli/index.js ADDED
@@ -0,0 +1,4 @@
1
+ const { run } = require("../src/index.js");
2
+ const { argv } = require("node:process");
3
+
4
+ run({}, argv);
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "doc-detective",
3
+ "version": "1.0.0",
4
+ "description": "Unit test documentation (and record videos of those tests).",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "test": "node ./cli/index.js"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/hawkeyexl/doc-detective.git"
12
+ },
13
+ "keywords": [
14
+ "documentation",
15
+ "unit",
16
+ "test",
17
+ "doc",
18
+ "docs"
19
+ ],
20
+ "author": "Manny Silva",
21
+ "license": "MIT",
22
+ "bugs": {
23
+ "url": "https://github.com/hawkeyexl/doc-detective/issues"
24
+ },
25
+ "homepage": "https://github.com/hawkeyexl/doc-detective#readme",
26
+ "dependencies": {
27
+ "axios": "^0.27.2",
28
+ "dotenv": "^16.0.2",
29
+ "n-readlines": "^1.0.1",
30
+ "pixelmatch": "^5.3.0",
31
+ "pngjs": "^6.0.0",
32
+ "puppeteer": "^13.7.0",
33
+ "puppeteer-screen-recorder": "^2.0.2",
34
+ "uuid": "^8.3.2",
35
+ "yargs": "^17.4.1"
36
+ }
37
+ }