doc-detective 1.0.5 → 1.0.7
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 -7
- package/package.json +1 -1
- package/sample/config.json +12 -4
- package/sample/doc-content.md +19 -15
- package/sample/failedTests/failed-test-recordings-go-here +0 -0
- package/sample/results.gif +0 -0
- package/sample/results.json +73 -64
- package/sample/results.png +0 -0
- package/sample/tests.json +2 -0
- package/src/config.json +12 -4
- package/src/index.js +2 -2
- package/src/lib/tests/record.js +32 -8
- package/src/lib/tests/screenshot.js +48 -25
- package/src/lib/tests.js +61 -13
- package/src/lib/utils.js +266 -33
package/README.md
CHANGED
|
@@ -530,9 +530,7 @@ If you opt into sending analytics, you can add additional servers that Doc Detec
|
|
|
530
530
|
|
|
531
531
|
```json
|
|
532
532
|
{
|
|
533
|
-
...
|
|
534
533
|
"analytics": {
|
|
535
|
-
...
|
|
536
534
|
"customServers": [
|
|
537
535
|
{
|
|
538
536
|
"name": "My Analytics Server",
|
|
@@ -547,7 +545,6 @@ If you opt into sending analytics, you can add additional servers that Doc Detec
|
|
|
547
545
|
}
|
|
548
546
|
]
|
|
549
547
|
}
|
|
550
|
-
...
|
|
551
548
|
}
|
|
552
549
|
```
|
|
553
550
|
|
|
@@ -569,15 +566,12 @@ Analytics reporting is off by default. If you want to make extra sure that Doc D
|
|
|
569
566
|
- New/upgraded test actions:
|
|
570
567
|
- New: Test if a referenced image (such as an icon) is present in the captured screenshot.
|
|
571
568
|
- Upgrade: Additional `httpRequest` input sanitization.
|
|
572
|
-
- Upgrade: `screenshot` and `startRecording` boolean for whether to perform the action or not if the expected output file already exists.
|
|
573
569
|
- Upgrade: `startRecording` and `stopRecording` to support start, stop, and intermediate test action state image matching to track differences between video captures from different runs.
|
|
574
|
-
|
|
575
|
-
- In-content test framing to identify when content is covered by a test defined in another file. This could enable content coverage analysis.
|
|
570
|
+
- Content coverage analysis based on in-content test statements and markup declarations.
|
|
576
571
|
- Suggest tests by parsing document text.
|
|
577
572
|
- Automatically insert suggested tests based on document text.
|
|
578
573
|
- Detailed field descriptions per action.
|
|
579
574
|
- Refactor tests into individual files.
|
|
580
|
-
- Rewrite cross-action recording status tracking.
|
|
581
575
|
|
|
582
576
|
## License
|
|
583
577
|
|
package/package.json
CHANGED
package/sample/config.json
CHANGED
|
@@ -14,14 +14,19 @@
|
|
|
14
14
|
".json"
|
|
15
15
|
],
|
|
16
16
|
"mediaDirectory": "sample",
|
|
17
|
+
"saveFailedTestRecordings": true,
|
|
18
|
+
"failedTestDirectory": "sample/failedTests",
|
|
17
19
|
"fileTypes": [
|
|
18
20
|
{
|
|
19
21
|
"extensions": [
|
|
20
22
|
".md",
|
|
21
23
|
".mdx"
|
|
22
24
|
],
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
+
"testStartStatementOpen": "[comment]: # (test start",
|
|
26
|
+
"testStartStatementClose": ")",
|
|
27
|
+
"testEndStatement": "[comment]: # (test end)",
|
|
28
|
+
"actionStatementOpen": "[comment]: # (action",
|
|
29
|
+
"actionStatementClose": ")"
|
|
25
30
|
},
|
|
26
31
|
{
|
|
27
32
|
"extensions": [
|
|
@@ -29,8 +34,11 @@
|
|
|
29
34
|
".htm",
|
|
30
35
|
".xml"
|
|
31
36
|
],
|
|
32
|
-
"
|
|
33
|
-
"
|
|
37
|
+
"testStartStatementOpen": "<!-- test start",
|
|
38
|
+
"testStartStatementClose": "-->",
|
|
39
|
+
"testEndStatement": "<!-- test end -->",
|
|
40
|
+
"actionStatementOpen": "<!-- action",
|
|
41
|
+
"actionStatementClose": "-->"
|
|
34
42
|
}
|
|
35
43
|
],
|
|
36
44
|
"browserOptions": {
|
package/sample/doc-content.md
CHANGED
|
@@ -2,21 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
To use Google Search to find information on kittens,
|
|
4
4
|
|
|
5
|
+
[comment]: # (test start {"id":"process-search-kittens" })
|
|
6
|
+
|
|
5
7
|
1. Open [Google Search](https://www.google.com).
|
|
6
8
|
|
|
7
|
-
[comment]: # (
|
|
8
|
-
[comment]: # (
|
|
9
|
+
[comment]: # (action {"action":"startRecording", "overwrite":false, "filename":"results.gif", "fps":15})
|
|
10
|
+
[comment]: # (action {"action":"goTo", "uri":"www.google.com"})
|
|
9
11
|
|
|
10
12
|
2. In the search bar, enter "kittens", then press Enter.
|
|
11
13
|
|
|
12
|
-
[comment]: # (
|
|
13
|
-
[comment]: # (
|
|
14
|
-
[comment]: # (
|
|
15
|
-
[comment]: # (
|
|
16
|
-
[comment]: # (
|
|
17
|
-
[comment]: # (
|
|
18
|
-
[comment]: # (
|
|
19
|
-
[comment]: # (
|
|
14
|
+
[comment]: # (action {"action":"moveMouse", "css":"#gbqfbb", "alignH": "center", "alignV": "center"})
|
|
15
|
+
[comment]: # (action {"action":"wait", "duration":"5000"})
|
|
16
|
+
[comment]: # (action {"action":"moveMouse", "css":"[title=Search]", "alignV": "center"})
|
|
17
|
+
[comment]: # (action {"action":"type", "css":"[title=Search]", "keys":"kittens", "trailingSpecialKey":"Enter"})
|
|
18
|
+
[comment]: # (action {"action":"wait", "duration":"5000"})
|
|
19
|
+
[comment]: # (action {"action":"scroll", "y": 300})
|
|
20
|
+
[comment]: # (action {"action":"stopRecording"})
|
|
21
|
+
[comment]: # (action {"action":"screenshot", "filename":"results.png", "matchPrevious": true, "matchThreshold": 0.1})
|
|
22
|
+
|
|
23
|
+
[comment]: # (test end {"id":"process-search-kittens" })
|
|
20
24
|
|
|
21
25
|
Search results appear on the page.
|
|
22
26
|
|
|
@@ -26,17 +30,17 @@ Search results appear on the page.
|
|
|
26
30
|
|
|
27
31
|
To go directly to a recommended result for your search, use the **I'm Feeling Lucky** button. If you're searching for american shorthair information,
|
|
28
32
|
|
|
29
|
-
[comment]: # (
|
|
30
|
-
[comment]: # (
|
|
33
|
+
[comment]: # (action {"testId":"text-match-lucky", "action":"goTo", "uri":"www.google.com"})
|
|
34
|
+
[comment]: # (action {"testId":"text-match-lucky", "action":"matchText", "css":"#gbqfbb", "text":"I'm Feeling Lucky"})
|
|
31
35
|
|
|
32
36
|
1. Open [Google Search](https://www.google.com).
|
|
33
37
|
|
|
34
|
-
[comment]: # (
|
|
38
|
+
[comment]: # (action {"testId":"process-lucky-shorthair", "action":"goTo", "uri":"www.google.com"})
|
|
35
39
|
|
|
36
40
|
2. In the search bar, enter "american shorthair cats".
|
|
37
41
|
|
|
38
|
-
[comment]: # (
|
|
42
|
+
[comment]: # (action {"testId":"process-lucky-shorthair", "action":"type", "css":"[title=Search]", "keys":"american shorthair cats"})
|
|
39
43
|
|
|
40
44
|
3. Click **I'm Feeling Lucky**.
|
|
41
45
|
|
|
42
|
-
[comment]: # (
|
|
46
|
+
[comment]: # (action {"testId":"process-lucky-shorthair", "action":"click", "css":"#gbqfbb"})
|
|
File without changes
|
|
Binary file
|
package/sample/results.json
CHANGED
|
@@ -2,20 +2,65 @@
|
|
|
2
2
|
"tests": [
|
|
3
3
|
{
|
|
4
4
|
"id": "process-search-kittens",
|
|
5
|
+
"file": "/config/workspace/doc-detective/sample/doc-content.md",
|
|
5
6
|
"actions": [
|
|
7
|
+
{
|
|
8
|
+
"action": "startRecording",
|
|
9
|
+
"overwrite": false,
|
|
10
|
+
"filename": "results.gif",
|
|
11
|
+
"fps": 15,
|
|
12
|
+
"line": 9,
|
|
13
|
+
"result": {
|
|
14
|
+
"status": "PASS",
|
|
15
|
+
"description": "Started recording: /config/workspace/doc-detective/sample/temp_results.mp4",
|
|
16
|
+
"video": "/config/workspace/doc-detective/sample/temp_results.mp4"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
6
19
|
{
|
|
7
20
|
"action": "goTo",
|
|
8
21
|
"uri": "www.google.com",
|
|
22
|
+
"line": 10,
|
|
9
23
|
"result": {
|
|
10
24
|
"status": "PASS",
|
|
11
25
|
"description": "Opened URI."
|
|
12
26
|
}
|
|
13
27
|
},
|
|
28
|
+
{
|
|
29
|
+
"action": "moveMouse",
|
|
30
|
+
"css": "#gbqfbb",
|
|
31
|
+
"alignH": "center",
|
|
32
|
+
"alignV": "center",
|
|
33
|
+
"line": 14,
|
|
34
|
+
"result": {
|
|
35
|
+
"status": "PASS",
|
|
36
|
+
"description": "Moved mouse to element."
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"action": "wait",
|
|
41
|
+
"duration": "5000",
|
|
42
|
+
"line": 15,
|
|
43
|
+
"result": {
|
|
44
|
+
"status": "PASS",
|
|
45
|
+
"description": "Wait complete."
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"action": "moveMouse",
|
|
50
|
+
"css": "[title=Search]",
|
|
51
|
+
"alignV": "center",
|
|
52
|
+
"line": 16,
|
|
53
|
+
"result": {
|
|
54
|
+
"status": "PASS",
|
|
55
|
+
"description": "Moved mouse to element."
|
|
56
|
+
}
|
|
57
|
+
},
|
|
14
58
|
{
|
|
15
59
|
"action": "type",
|
|
16
60
|
"css": "[title=Search]",
|
|
17
61
|
"keys": "kittens",
|
|
18
62
|
"trailingSpecialKey": "Enter",
|
|
63
|
+
"line": 17,
|
|
19
64
|
"result": {
|
|
20
65
|
"status": "PASS",
|
|
21
66
|
"description": "Typed keys."
|
|
@@ -24,16 +69,35 @@
|
|
|
24
69
|
{
|
|
25
70
|
"action": "wait",
|
|
26
71
|
"duration": "5000",
|
|
72
|
+
"line": 18,
|
|
27
73
|
"result": {
|
|
28
74
|
"status": "PASS",
|
|
29
75
|
"description": "Wait complete."
|
|
30
76
|
}
|
|
31
77
|
},
|
|
78
|
+
{
|
|
79
|
+
"action": "scroll",
|
|
80
|
+
"y": 300,
|
|
81
|
+
"line": 19,
|
|
82
|
+
"result": {
|
|
83
|
+
"status": "PASS",
|
|
84
|
+
"description": "Scroll complete."
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"action": "stopRecording",
|
|
89
|
+
"line": 20,
|
|
90
|
+
"result": {
|
|
91
|
+
"status": "PASS",
|
|
92
|
+
"description": "Stopped recording: /config/workspace/doc-detective/sample/results.gif"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
32
95
|
{
|
|
33
96
|
"action": "screenshot",
|
|
34
97
|
"filename": "results.png",
|
|
35
98
|
"matchPrevious": false,
|
|
36
99
|
"matchThreshold": 0.1,
|
|
100
|
+
"line": 21,
|
|
37
101
|
"result": {
|
|
38
102
|
"status": "PASS",
|
|
39
103
|
"description": "Captured screenshot.",
|
|
@@ -45,10 +109,12 @@
|
|
|
45
109
|
},
|
|
46
110
|
{
|
|
47
111
|
"id": "text-match-lucky",
|
|
112
|
+
"file": "/config/workspace/doc-detective/sample/doc-content.md",
|
|
48
113
|
"actions": [
|
|
49
114
|
{
|
|
50
115
|
"action": "goTo",
|
|
51
116
|
"uri": "www.google.com",
|
|
117
|
+
"line": 33,
|
|
52
118
|
"result": {
|
|
53
119
|
"status": "PASS",
|
|
54
120
|
"description": "Opened URI."
|
|
@@ -58,16 +124,7 @@
|
|
|
58
124
|
"action": "matchText",
|
|
59
125
|
"css": "#gbqfbb",
|
|
60
126
|
"text": "I'm Feeling Lucky",
|
|
61
|
-
"
|
|
62
|
-
"status": "PASS",
|
|
63
|
-
"description": "Element text matched expected text."
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
"action": "matchText",
|
|
68
|
-
"css": "#gbqfbb",
|
|
69
|
-
"text": "$TEXT",
|
|
70
|
-
"env": "./sample/variables.env",
|
|
127
|
+
"line": 34,
|
|
71
128
|
"result": {
|
|
72
129
|
"status": "PASS",
|
|
73
130
|
"description": "Element text matched expected text."
|
|
@@ -78,11 +135,12 @@
|
|
|
78
135
|
},
|
|
79
136
|
{
|
|
80
137
|
"id": "process-lucky-shorthair",
|
|
138
|
+
"file": "/config/workspace/doc-detective/sample/doc-content.md",
|
|
81
139
|
"actions": [
|
|
82
140
|
{
|
|
83
141
|
"action": "goTo",
|
|
84
|
-
"uri": "
|
|
85
|
-
"
|
|
142
|
+
"uri": "www.google.com",
|
|
143
|
+
"line": 38,
|
|
86
144
|
"result": {
|
|
87
145
|
"status": "PASS",
|
|
88
146
|
"description": "Opened URI."
|
|
@@ -91,7 +149,8 @@
|
|
|
91
149
|
{
|
|
92
150
|
"action": "type",
|
|
93
151
|
"css": "[title=Search]",
|
|
94
|
-
"keys": "
|
|
152
|
+
"keys": "american shorthair cats",
|
|
153
|
+
"line": 42,
|
|
95
154
|
"result": {
|
|
96
155
|
"status": "PASS",
|
|
97
156
|
"description": "Typed keys."
|
|
@@ -100,6 +159,7 @@
|
|
|
100
159
|
{
|
|
101
160
|
"action": "click",
|
|
102
161
|
"css": "#gbqfbb",
|
|
162
|
+
"line": 46,
|
|
103
163
|
"result": {
|
|
104
164
|
"status": "PASS",
|
|
105
165
|
"description": "Clicked element."
|
|
@@ -107,57 +167,6 @@
|
|
|
107
167
|
}
|
|
108
168
|
],
|
|
109
169
|
"status": "PASS"
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"id": "non-ui-tests",
|
|
113
|
-
"actions": [
|
|
114
|
-
{
|
|
115
|
-
"action": "runShell",
|
|
116
|
-
"command": "echo $USERNAME",
|
|
117
|
-
"env": "./sample/variables.env",
|
|
118
|
-
"result": {
|
|
119
|
-
"status": "PASS",
|
|
120
|
-
"description": "Executed command.",
|
|
121
|
-
"stdout": "foo",
|
|
122
|
-
"stderr": "",
|
|
123
|
-
"exitCode": 0
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
"action": "checkLink",
|
|
128
|
-
"uri": "https://www.google.com",
|
|
129
|
-
"statusCodes": [
|
|
130
|
-
200
|
|
131
|
-
],
|
|
132
|
-
"result": {
|
|
133
|
-
"status": "PASS",
|
|
134
|
-
"description": "Returned 200"
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
"action": "checkLink",
|
|
139
|
-
"uri": "$URL",
|
|
140
|
-
"statusCodes": [
|
|
141
|
-
200
|
|
142
|
-
],
|
|
143
|
-
"result": {
|
|
144
|
-
"status": "PASS",
|
|
145
|
-
"description": "Returned 200"
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
"action": "httpRequest",
|
|
150
|
-
"uri": "$URL",
|
|
151
|
-
"statusCodes": [
|
|
152
|
-
200
|
|
153
|
-
],
|
|
154
|
-
"result": {
|
|
155
|
-
"status": "PASS",
|
|
156
|
-
"description": "Returned 200."
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
],
|
|
160
|
-
"status": "PASS"
|
|
161
170
|
}
|
|
162
171
|
]
|
|
163
172
|
}
|
|
Binary file
|
package/sample/tests.json
CHANGED
package/src/config.json
CHANGED
|
@@ -14,14 +14,19 @@
|
|
|
14
14
|
".json"
|
|
15
15
|
],
|
|
16
16
|
"mediaDirectory": ".",
|
|
17
|
+
"saveFailedTestRecordings": true,
|
|
18
|
+
"failedTestDirectory": ".",
|
|
17
19
|
"fileTypes": [
|
|
18
20
|
{
|
|
19
21
|
"extensions": [
|
|
20
22
|
".md",
|
|
21
23
|
".mdx"
|
|
22
24
|
],
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
+
"testStartStatementOpen": "[comment]: # (test start",
|
|
26
|
+
"testStartStatementClose": ")",
|
|
27
|
+
"testEndStatement": "[comment]: # (test end)",
|
|
28
|
+
"actionStatementOpen": "[comment]: # (action",
|
|
29
|
+
"actionStatementClose": ")"
|
|
25
30
|
},
|
|
26
31
|
{
|
|
27
32
|
"extensions": [
|
|
@@ -29,8 +34,11 @@
|
|
|
29
34
|
".htm",
|
|
30
35
|
".xml"
|
|
31
36
|
],
|
|
32
|
-
"
|
|
33
|
-
"
|
|
37
|
+
"testStartStatementOpen": "<!-- test start",
|
|
38
|
+
"testStartStatementClose": "-->",
|
|
39
|
+
"testEndStatement": "<!-- test end -->",
|
|
40
|
+
"actionStatementOpen": "<!-- action",
|
|
41
|
+
"actionStatementClose": "-->"
|
|
34
42
|
}
|
|
35
43
|
],
|
|
36
44
|
"browserOptions": {
|
package/src/index.js
CHANGED
|
@@ -2,7 +2,7 @@ const {
|
|
|
2
2
|
setArgs,
|
|
3
3
|
setConfig,
|
|
4
4
|
setFiles,
|
|
5
|
-
|
|
5
|
+
parseTests,
|
|
6
6
|
outputResults,
|
|
7
7
|
log,
|
|
8
8
|
} = require("./lib/utils");
|
|
@@ -30,7 +30,7 @@ async function main(config, argv) {
|
|
|
30
30
|
log(config, "debug", files);
|
|
31
31
|
|
|
32
32
|
// Set tests
|
|
33
|
-
const tests =
|
|
33
|
+
const tests = parseTests(config, files);
|
|
34
34
|
if (config.logLevel === "debug") {
|
|
35
35
|
console.log("(DEBUG) TESTS:");
|
|
36
36
|
tests.tests.forEach((test) => {
|
package/src/lib/tests/record.js
CHANGED
|
@@ -87,12 +87,17 @@ async function startRecording(action, page, config) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// Set FPS
|
|
90
|
-
|
|
90
|
+
targetFps = action.fps || action.gifFps || defaultPayload.fps;
|
|
91
91
|
try {
|
|
92
|
-
|
|
92
|
+
targetFps = Number(targetFps);
|
|
93
|
+
if (targetFps >= 30) {
|
|
94
|
+
fps = targetFps;
|
|
95
|
+
} else {
|
|
96
|
+
fps = 30;
|
|
97
|
+
}
|
|
93
98
|
} catch {
|
|
94
|
-
|
|
95
|
-
log(config, "warning", `Invalid FPS. Reverting to default: ${
|
|
99
|
+
targetFps = defaultPayload.fps;
|
|
100
|
+
log(config, "warning", `Invalid FPS. Reverting to default: ${targetFps}`);
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
// Set height
|
|
@@ -126,6 +131,7 @@ async function startRecording(action, page, config) {
|
|
|
126
131
|
filepath,
|
|
127
132
|
tempFilepath,
|
|
128
133
|
fps,
|
|
134
|
+
targetFps,
|
|
129
135
|
height,
|
|
130
136
|
width,
|
|
131
137
|
};
|
|
@@ -143,12 +149,30 @@ async function stopRecording(videoDetails, config) {
|
|
|
143
149
|
let status;
|
|
144
150
|
let description;
|
|
145
151
|
let result;
|
|
152
|
+
|
|
153
|
+
if (typeof videoDetails.recorder === "undefined") {
|
|
154
|
+
status = "PASS";
|
|
155
|
+
description = `Skipping action. No action-defined recording in progress.`;
|
|
156
|
+
result = { status, description };
|
|
157
|
+
return { result };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
recorder = videoDetails.recorder;
|
|
161
|
+
targetExtension = videoDetails.targetExtension;
|
|
162
|
+
height = videoDetails.height;
|
|
163
|
+
width = videoDetails.width;
|
|
164
|
+
filepath = videoDetails.filepath;
|
|
165
|
+
tempFilepath = videoDetails.tempFilepath;
|
|
166
|
+
fps = videoDetails.fps;
|
|
167
|
+
targetFps = videoDetails.targetFps;
|
|
168
|
+
|
|
146
169
|
try {
|
|
147
|
-
await
|
|
170
|
+
await recorder.stop();
|
|
148
171
|
if (
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
172
|
+
targetExtension === ".gif" ||
|
|
173
|
+
height != config.browserOptions.height ||
|
|
174
|
+
width != config.browserOptions.width ||
|
|
175
|
+
targetFps != fps
|
|
152
176
|
) {
|
|
153
177
|
let output = await convertVideo(config, videoDetails);
|
|
154
178
|
filepath = output;
|
|
@@ -87,36 +87,59 @@ async function screenshot(action, page, config) {
|
|
|
87
87
|
if (action.matchPrevious) {
|
|
88
88
|
const expected = PNG.sync.read(fs.readFileSync(previousFilePath));
|
|
89
89
|
const actual = PNG.sync.read(fs.readFileSync(filePath));
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
90
|
+
try {
|
|
91
|
+
const numDiffPixels = pixelmatch(
|
|
92
|
+
expected.data,
|
|
93
|
+
actual.data,
|
|
94
|
+
null,
|
|
95
|
+
expected.width,
|
|
96
|
+
expected.height,
|
|
97
|
+
{
|
|
98
|
+
threshold: action.matchThreshold,
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
fs.unlink(filePath, function (err) {
|
|
102
|
+
if (err) {
|
|
103
|
+
log(
|
|
104
|
+
config,
|
|
105
|
+
"warning",
|
|
106
|
+
`Couldn't delete intermediate file: ${filePath}`
|
|
107
|
+
);
|
|
108
|
+
} else {
|
|
109
|
+
log(config, "debug", `Deleted intermediate file: ${filePath}`);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
if (numDiffPixels) {
|
|
113
|
+
// FAIL: Couldn't capture screenshot
|
|
114
|
+
const diffPercentage =
|
|
115
|
+
numDiffPixels / (expected.width * expected.height);
|
|
116
|
+
status = "FAIL";
|
|
117
|
+
description = `Screenshot comparison had larger diff (${diffPercentage}) than threshold (${action.matchThreshold}).`;
|
|
118
|
+
result = { status, description };
|
|
119
|
+
return { result };
|
|
103
120
|
} else {
|
|
104
|
-
|
|
121
|
+
// PASS
|
|
122
|
+
status = "PASS";
|
|
123
|
+
description = `Screenshot matches previously captured image.`;
|
|
124
|
+
result = { status, description, image: previousFilePath };
|
|
125
|
+
return { result };
|
|
105
126
|
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
127
|
+
} catch {
|
|
128
|
+
fs.unlink(filePath, function (err) {
|
|
129
|
+
if (err) {
|
|
130
|
+
log(
|
|
131
|
+
config,
|
|
132
|
+
"warning",
|
|
133
|
+
`Couldn't delete intermediate file: ${filePath}`
|
|
134
|
+
);
|
|
135
|
+
} else {
|
|
136
|
+
log(config, "debug", `Deleted intermediate file: ${filePath}`);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
110
139
|
status = "FAIL";
|
|
111
|
-
description = `
|
|
140
|
+
description = `Image sizes don't match.`;
|
|
112
141
|
result = { status, description };
|
|
113
142
|
return { result };
|
|
114
|
-
} else {
|
|
115
|
-
// PASS
|
|
116
|
-
status = "PASS";
|
|
117
|
-
description = `Screenshot matches previously captured image.`;
|
|
118
|
-
result = { status, description, image: previousFilePath };
|
|
119
|
-
return { result };
|
|
120
143
|
}
|
|
121
144
|
}
|
|
122
145
|
}
|
package/src/lib/tests.js
CHANGED
|
@@ -2,7 +2,7 @@ const puppeteer = require("puppeteer");
|
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const { exit, stdout, exitCode } = require("process");
|
|
4
4
|
const { installMouseHelper } = require("./install-mouse-helper");
|
|
5
|
-
const { setEnvs, log } = require("./utils");
|
|
5
|
+
const { setEnvs, log, timestamp } = require("./utils");
|
|
6
6
|
const util = require("util");
|
|
7
7
|
const exec = util.promisify(require("child_process").exec);
|
|
8
8
|
const axios = require("axios");
|
|
@@ -88,33 +88,54 @@ async function runTests(config, tests) {
|
|
|
88
88
|
let pass = 0;
|
|
89
89
|
let warning = 0;
|
|
90
90
|
let fail = 0;
|
|
91
|
-
|
|
91
|
+
config.videoDetails = {};
|
|
92
|
+
config.debugRecording = {};
|
|
92
93
|
// Instantiate page
|
|
93
94
|
const page = await browser.newPage();
|
|
95
|
+
if (
|
|
96
|
+
test.saveFailedTestRecordings ||
|
|
97
|
+
(config.saveFailedTestRecordings &&
|
|
98
|
+
test.saveFailedTestRecordings != false)
|
|
99
|
+
) {
|
|
100
|
+
failedTestDirectory =
|
|
101
|
+
test.failedTestDirectory || config.failedTestDirectory;
|
|
102
|
+
debugRecordingOptions = {
|
|
103
|
+
action: "startRecording",
|
|
104
|
+
mediaDirectory: failedTestDirectory,
|
|
105
|
+
filename: `${test.id}-${timestamp()}.mp4`,
|
|
106
|
+
overwrite: true,
|
|
107
|
+
};
|
|
108
|
+
config.debugRecording = await startRecording(
|
|
109
|
+
debugRecordingOptions,
|
|
110
|
+
page,
|
|
111
|
+
config
|
|
112
|
+
);
|
|
113
|
+
}
|
|
94
114
|
// Instantiate mouse cursor
|
|
95
115
|
await installMouseHelper(page);
|
|
96
116
|
// Iterate through actions
|
|
97
117
|
for (const action of test.actions) {
|
|
98
118
|
log(config, "debug", `ACTION: ${JSON.stringify(action)}`);
|
|
99
|
-
action.result = await runAction(
|
|
119
|
+
action.result = await runAction(
|
|
120
|
+
config,
|
|
121
|
+
action,
|
|
122
|
+
page,
|
|
123
|
+
config.videoDetails
|
|
124
|
+
);
|
|
100
125
|
if (action.result.videoDetails) {
|
|
101
|
-
videoDetails = action.result.videoDetails;
|
|
126
|
+
config.videoDetails = action.result.videoDetails;
|
|
102
127
|
}
|
|
103
128
|
action.result = action.result.result;
|
|
104
129
|
if (action.result.status === "FAIL") fail++;
|
|
105
130
|
if (action.result.status === "WARNING") warning++;
|
|
106
131
|
if (action.result.status === "PASS") pass++;
|
|
107
|
-
log(
|
|
132
|
+
log(
|
|
133
|
+
config,
|
|
134
|
+
"debug",
|
|
135
|
+
`RESULT: ${action.result.status}. ${action.result.description}`
|
|
136
|
+
);
|
|
108
137
|
}
|
|
109
138
|
|
|
110
|
-
// Close open recorders/pages
|
|
111
|
-
if (videoDetails) {
|
|
112
|
-
await runAction("", { action: "stopRecording" }, "", videoDetails);
|
|
113
|
-
}
|
|
114
|
-
try {
|
|
115
|
-
await page.close();
|
|
116
|
-
} catch {}
|
|
117
|
-
|
|
118
139
|
// Calc overall test result
|
|
119
140
|
if (fail) {
|
|
120
141
|
test.status = "FAIL";
|
|
@@ -126,6 +147,33 @@ async function runTests(config, tests) {
|
|
|
126
147
|
console.log("Error: Couldn't read test action results.");
|
|
127
148
|
exit(1);
|
|
128
149
|
}
|
|
150
|
+
|
|
151
|
+
// Close open recorders/pages
|
|
152
|
+
if (config.debugRecording.videoDetails) {
|
|
153
|
+
await stopRecording(config.debugRecording.videoDetails, config);
|
|
154
|
+
if (!fail) {
|
|
155
|
+
fs.unlink(config.debugRecording.videoDetails.filepath, function (err) {
|
|
156
|
+
if (err) {
|
|
157
|
+
log(
|
|
158
|
+
config,
|
|
159
|
+
"warning",
|
|
160
|
+
`Couldn't delete debug recording: ${config.debugRecording.videoDetails.filepath}`
|
|
161
|
+
);
|
|
162
|
+
} else {
|
|
163
|
+
log(
|
|
164
|
+
config,
|
|
165
|
+
"debug",
|
|
166
|
+
`Deleted debug recording: ${config.debugRecording.videoDetails.filepath}`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Close page
|
|
174
|
+
try {
|
|
175
|
+
await page.close();
|
|
176
|
+
} catch {}
|
|
129
177
|
}
|
|
130
178
|
await browser.close();
|
|
131
179
|
return tests;
|
package/src/lib/utils.js
CHANGED
|
@@ -11,11 +11,12 @@ const defaultConfig = require("../config.json");
|
|
|
11
11
|
exports.setArgs = setArgs;
|
|
12
12
|
exports.setConfig = setConfig;
|
|
13
13
|
exports.setFiles = setFiles;
|
|
14
|
-
exports.
|
|
14
|
+
exports.parseTests = parseTests;
|
|
15
15
|
exports.outputResults = outputResults;
|
|
16
16
|
exports.setEnvs = setEnvs;
|
|
17
17
|
exports.loadEnvsForObject = loadEnvsForObject;
|
|
18
18
|
exports.log = log;
|
|
19
|
+
exports.timestamp = timestamp;
|
|
19
20
|
|
|
20
21
|
const analyticsRequest =
|
|
21
22
|
"Thanks for using Doc Detective! If you want to contribute to the project, consider sending analytics to help us understand usage patterns and functional gaps. To turn on analytics, set 'analytics.send = true' in your config, or use the '-a true' argument. See https://github.com/hawkeyexl/doc-detective#analytics";
|
|
@@ -182,7 +183,8 @@ function selectConfig(config, argv) {
|
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
function setEnv(config, argv) {
|
|
185
|
-
config.env =
|
|
186
|
+
config.env =
|
|
187
|
+
argv.env || process.env.DOC_ENV_PATH || config.env || defaultConfig.env;
|
|
186
188
|
if (config.env) {
|
|
187
189
|
config.env = path.resolve(config.env);
|
|
188
190
|
if (fs.existsSync(config.env)) {
|
|
@@ -201,7 +203,11 @@ function setEnv(config, argv) {
|
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
function setInput(config, argv) {
|
|
204
|
-
config.input =
|
|
206
|
+
config.input =
|
|
207
|
+
argv.input ||
|
|
208
|
+
process.env.DOC_INPUT_PATH ||
|
|
209
|
+
config.input ||
|
|
210
|
+
defaultConfig.input;
|
|
205
211
|
if (config.input) {
|
|
206
212
|
config.input = path.resolve(config.input);
|
|
207
213
|
if (fs.existsSync(config.input)) {
|
|
@@ -225,14 +231,19 @@ function setInput(config, argv) {
|
|
|
225
231
|
}
|
|
226
232
|
|
|
227
233
|
function setOutput(config, argv) {
|
|
228
|
-
config.output =
|
|
234
|
+
config.output =
|
|
235
|
+
argv.output ||
|
|
236
|
+
process.env.DOC_OUTPUT_PATH ||
|
|
237
|
+
config.output ||
|
|
238
|
+
defaultConfig.output;
|
|
229
239
|
config.output = path.resolve(config.output);
|
|
230
240
|
log(config, "debug", `Output path set: ${config.output}`);
|
|
231
241
|
return config;
|
|
232
242
|
}
|
|
233
243
|
|
|
234
244
|
function setSetup(config, argv) {
|
|
235
|
-
config.setup =
|
|
245
|
+
config.setup =
|
|
246
|
+
argv.setup || process.env.DOC_SETUP || config.setup || defaultConfig.setup;
|
|
236
247
|
if (config.setup === "") {
|
|
237
248
|
log(config, "debug", `No setup tests.`);
|
|
238
249
|
return config;
|
|
@@ -249,7 +260,11 @@ function setSetup(config, argv) {
|
|
|
249
260
|
}
|
|
250
261
|
|
|
251
262
|
function setCleanup(config, argv) {
|
|
252
|
-
config.cleanup =
|
|
263
|
+
config.cleanup =
|
|
264
|
+
argv.cleanup ||
|
|
265
|
+
process.env.DOC_CLEANUP ||
|
|
266
|
+
config.cleanup ||
|
|
267
|
+
defaultConfig.cleanup;
|
|
253
268
|
if (config.cleanup === "") {
|
|
254
269
|
log(config, "debug", `No cleanup tests.`);
|
|
255
270
|
return config;
|
|
@@ -269,7 +284,8 @@ function setMediaDirectory(config, argv) {
|
|
|
269
284
|
config.mediaDirectory =
|
|
270
285
|
argv.mediaDir ||
|
|
271
286
|
process.env.DOC_MEDIA_DIRECTORY_PATH ||
|
|
272
|
-
config.mediaDirectory
|
|
287
|
+
config.mediaDirectory ||
|
|
288
|
+
defaultConfig.mediaDirectory;
|
|
273
289
|
config.mediaDirectory = path.resolve(config.mediaDirectory);
|
|
274
290
|
if (fs.existsSync(config.mediaDirectory)) {
|
|
275
291
|
log(config, "debug", `Media directory set: ${config.mediaDirectory}`);
|
|
@@ -284,9 +300,75 @@ function setMediaDirectory(config, argv) {
|
|
|
284
300
|
return config;
|
|
285
301
|
}
|
|
286
302
|
|
|
303
|
+
function setFailedTestRecording(config, argv) {
|
|
304
|
+
config.saveFailedTestRecordings =
|
|
305
|
+
argv.saveFailedTestRecordings ||
|
|
306
|
+
process.env.DOC_SAVE_FAILED_RECORDINGS ||
|
|
307
|
+
config.saveFailedTestRecordings ||
|
|
308
|
+
defaultConfig.saveFailedTestRecordings;
|
|
309
|
+
switch (config.saveFailedTestRecordings) {
|
|
310
|
+
case true:
|
|
311
|
+
case "true":
|
|
312
|
+
config.saveFailedTestRecordings = true;
|
|
313
|
+
log(
|
|
314
|
+
config,
|
|
315
|
+
"debug",
|
|
316
|
+
`Save failed test recordings set: ${config.saveFailedTestRecordings}`
|
|
317
|
+
);
|
|
318
|
+
break;
|
|
319
|
+
case false:
|
|
320
|
+
case "false":
|
|
321
|
+
config.saveFailedTestRecordings = false;
|
|
322
|
+
log(
|
|
323
|
+
config,
|
|
324
|
+
"debug",
|
|
325
|
+
`Save failed test recordings set: ${config.saveFailedTestRecordings}`
|
|
326
|
+
);
|
|
327
|
+
log(config, "info", analyticsRequest);
|
|
328
|
+
break;
|
|
329
|
+
default:
|
|
330
|
+
config.saveFailedTestRecordings = defaultConfig.saveFailedTestRecordings;
|
|
331
|
+
log(
|
|
332
|
+
config,
|
|
333
|
+
"warning",
|
|
334
|
+
`Invalid save failed test recordings value. Reverted to default: ${config.saveFailedTestRecordings}`
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
return config;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function setFailedTestDirectory(config, argv) {
|
|
341
|
+
config.failedTestDirectory =
|
|
342
|
+
argv.failedTestDirectory ||
|
|
343
|
+
process.env.DOC_FAILED_TEST_DIRECTORY_PATH ||
|
|
344
|
+
config.failedTestDirectory ||
|
|
345
|
+
defaultConfig.failedTestDirectory;
|
|
346
|
+
config.failedTestDirectory = path.resolve(config.failedTestDirectory);
|
|
347
|
+
if (fs.existsSync(config.failedTestDirectory)) {
|
|
348
|
+
log(
|
|
349
|
+
config,
|
|
350
|
+
"debug",
|
|
351
|
+
`Failed test directory set: ${config.failedTestDirectory}`
|
|
352
|
+
);
|
|
353
|
+
} else {
|
|
354
|
+
config.failedTestDirectory = path.resolve(
|
|
355
|
+
defaultConfig.failedTestDirectory
|
|
356
|
+
);
|
|
357
|
+
log(
|
|
358
|
+
config,
|
|
359
|
+
"warning",
|
|
360
|
+
`Invalid failed test directory. Reverted to default: ${config.failedTestDirectory}`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
return config;
|
|
364
|
+
}
|
|
365
|
+
|
|
287
366
|
function setRecursion(config, argv) {
|
|
288
367
|
config.recursive =
|
|
289
|
-
argv.recursive ||
|
|
368
|
+
argv.recursive ||
|
|
369
|
+
process.env.DOC_RECURSIVE ||
|
|
370
|
+
config.recursive ||
|
|
371
|
+
defaultConfig.recursive;
|
|
290
372
|
switch (config.recursive) {
|
|
291
373
|
case true:
|
|
292
374
|
case "true":
|
|
@@ -309,9 +391,33 @@ function setRecursion(config, argv) {
|
|
|
309
391
|
return config;
|
|
310
392
|
}
|
|
311
393
|
|
|
394
|
+
function setFileTypes(config, argv) {
|
|
395
|
+
config.fileTypes =
|
|
396
|
+
argv.fileTypes ||
|
|
397
|
+
process.env.DOC_FILE_TYPES ||
|
|
398
|
+
config.fileTypes ||
|
|
399
|
+
defaultConfig.fileTypes;
|
|
400
|
+
if (config.fileTypes.length > 0) {
|
|
401
|
+
log(config, "debug", `File types set: ${JSON.stringify(config.fileTypes)}`);
|
|
402
|
+
} else {
|
|
403
|
+
config.fileTypes = defaultConfig.fileTypes;
|
|
404
|
+
log(
|
|
405
|
+
config,
|
|
406
|
+
"debug",
|
|
407
|
+
`Invalid file type value(s). Reverted to default: ${JSON.stringify(
|
|
408
|
+
config.fileTypes
|
|
409
|
+
)}`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
return config;
|
|
413
|
+
}
|
|
414
|
+
|
|
312
415
|
function setTestFileExtensions(config, argv) {
|
|
313
416
|
config.testExtensions =
|
|
314
|
-
argv.ext ||
|
|
417
|
+
argv.ext ||
|
|
418
|
+
process.env.DOC_TEST_EXTENSTIONS ||
|
|
419
|
+
config.testExtensions ||
|
|
420
|
+
defaultConfig.testExtensions;
|
|
315
421
|
if (typeof config.testExtensions === "string")
|
|
316
422
|
config.testExtensions = config.testExtensions
|
|
317
423
|
.replace(/\s+/g, "")
|
|
@@ -339,7 +445,8 @@ function setBrowserHeadless(config, argv) {
|
|
|
339
445
|
config.browserOptions.headless =
|
|
340
446
|
argv.browserHeadless ||
|
|
341
447
|
process.env.DOC_BROWSER_HEADLESS ||
|
|
342
|
-
config.browserOptions.headless
|
|
448
|
+
config.browserOptions.headless ||
|
|
449
|
+
defaultConfig.browserOptions.headless;
|
|
343
450
|
switch (config.browserOptions.headless) {
|
|
344
451
|
case true:
|
|
345
452
|
case "true":
|
|
@@ -374,7 +481,8 @@ function setBrowserPath(config, argv) {
|
|
|
374
481
|
config.browserOptions.path =
|
|
375
482
|
argv.browserPath ||
|
|
376
483
|
process.env.DOC_BROWSER_PATH ||
|
|
377
|
-
config.browserOptions.path
|
|
484
|
+
config.browserOptions.path ||
|
|
485
|
+
defaultConfig.browserOptions.path;
|
|
378
486
|
if (config.browserOptions.path === "") {
|
|
379
487
|
log(config, "debug", `Browser set to default Chromium install.`);
|
|
380
488
|
return config;
|
|
@@ -398,7 +506,8 @@ function setBrowserHeight(config, argv) {
|
|
|
398
506
|
config.browserOptions.height =
|
|
399
507
|
argv.browserHeight ||
|
|
400
508
|
process.env.DOC_BROWSER_HEIGHT ||
|
|
401
|
-
config.browserOptions.height
|
|
509
|
+
config.browserOptions.height ||
|
|
510
|
+
defaultConfig.browserOptions.height;
|
|
402
511
|
if (typeof config.browserOptions.height === "string") {
|
|
403
512
|
try {
|
|
404
513
|
config.browserOptions.height = Number(config.browserOptions.height);
|
|
@@ -428,7 +537,8 @@ function setBrowserWidth(config, argv) {
|
|
|
428
537
|
config.browserOptions.width =
|
|
429
538
|
argv.browserWidth ||
|
|
430
539
|
process.env.DOC_BROWSER_WIDTH ||
|
|
431
|
-
config.browserOptions.width
|
|
540
|
+
config.browserOptions.width ||
|
|
541
|
+
defaultConfig.browserOptions.width;
|
|
432
542
|
if (typeof config.browserOptions.width === "string") {
|
|
433
543
|
try {
|
|
434
544
|
config.browserOptions.width = Number(config.browserOptions.width);
|
|
@@ -456,7 +566,10 @@ function setBrowserWidth(config, argv) {
|
|
|
456
566
|
|
|
457
567
|
function setAnalytics(config, argv) {
|
|
458
568
|
config.analytics.send =
|
|
459
|
-
argv.analytics ||
|
|
569
|
+
argv.analytics ||
|
|
570
|
+
process.env.DOC_ANALYTICS ||
|
|
571
|
+
config.analytics.send ||
|
|
572
|
+
defaultConfig.analytics.send;
|
|
460
573
|
switch (config.analytics.send) {
|
|
461
574
|
case true:
|
|
462
575
|
case "true":
|
|
@@ -484,7 +597,8 @@ function setAnalyticsUserId(config, argv) {
|
|
|
484
597
|
config.analytics.userId =
|
|
485
598
|
argv.analyticsUserId ||
|
|
486
599
|
process.env.DOC_ANALYTICS_USER_ID ||
|
|
487
|
-
config.analytics.userId
|
|
600
|
+
config.analytics.userId ||
|
|
601
|
+
defaultConfig.analytics.userId;
|
|
488
602
|
log(config, "debug", `Analytics user ID set: ${config.analytics.userId}`);
|
|
489
603
|
return config;
|
|
490
604
|
}
|
|
@@ -494,7 +608,8 @@ function setAnalyticsDetailLevel(config, argv) {
|
|
|
494
608
|
detailLevel =
|
|
495
609
|
argv.analyticsDetailLevel ||
|
|
496
610
|
process.env.DOC_ANALYTCS_DETAIL_LEVEL ||
|
|
497
|
-
config.analytics.detailLevel
|
|
611
|
+
config.analytics.detailLevel ||
|
|
612
|
+
defaultConfig.analytics.detailLevel;
|
|
498
613
|
detailLevel = String(detailLevel).toLowerCase();
|
|
499
614
|
if (enums.indexOf(detailLevel) >= 0) {
|
|
500
615
|
config.analytics.detailLevel = detailLevel;
|
|
@@ -537,8 +652,14 @@ function setConfig(config, argv) {
|
|
|
537
652
|
|
|
538
653
|
config = setMediaDirectory(config, argv);
|
|
539
654
|
|
|
655
|
+
config = setFailedTestRecording(config, argv);
|
|
656
|
+
|
|
657
|
+
config = setFailedTestDirectory(config, argv);
|
|
658
|
+
|
|
540
659
|
config = setRecursion(config, argv);
|
|
541
660
|
|
|
661
|
+
config = setFileTypes(config, argv);
|
|
662
|
+
|
|
542
663
|
config = setTestFileExtensions(config, argv);
|
|
543
664
|
|
|
544
665
|
config = setBrowserHeadless(config, argv);
|
|
@@ -620,13 +741,14 @@ function setFiles(config) {
|
|
|
620
741
|
}
|
|
621
742
|
|
|
622
743
|
// Parse files for tests
|
|
623
|
-
function
|
|
744
|
+
function parseTests(config, files) {
|
|
624
745
|
let json = { tests: [] };
|
|
625
746
|
|
|
626
747
|
// Loop through test files
|
|
627
748
|
files.forEach((file) => {
|
|
628
749
|
log(config, "debug", `file: ${file}`);
|
|
629
|
-
let
|
|
750
|
+
let fileId = `${uuid.v4()}`;
|
|
751
|
+
let id = fileId;
|
|
630
752
|
let line;
|
|
631
753
|
let lineNumber = 1;
|
|
632
754
|
let inputFile = new nReadlines(file);
|
|
@@ -634,36 +756,135 @@ function parseFiles(config, files) {
|
|
|
634
756
|
let fileType = config.fileTypes.find((fileType) =>
|
|
635
757
|
fileType.extensions.includes(extension)
|
|
636
758
|
);
|
|
759
|
+
let testStartStatementOpen;
|
|
760
|
+
let testStartStatementClose;
|
|
761
|
+
let testEndStatement;
|
|
762
|
+
let actionStatementOpen;
|
|
763
|
+
let actionStatementClose;
|
|
764
|
+
|
|
765
|
+
if (typeof fileType != "undefined") {
|
|
766
|
+
testStartStatementOpen = fileType.testStartStatementOpen;
|
|
767
|
+
if (!testStartStatementOpen) {
|
|
768
|
+
log(
|
|
769
|
+
config,
|
|
770
|
+
"warning",
|
|
771
|
+
`Skipping tests in ${file}. No 'testStartStatementOpen' value specified.`
|
|
772
|
+
);
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
testStartStatementClose = fileType.testStartStatementClose;
|
|
776
|
+
if (!testStartStatementClose) {
|
|
777
|
+
log(
|
|
778
|
+
config,
|
|
779
|
+
"warning",
|
|
780
|
+
`Skipping tests in ${file}. No 'testStartStatementClose' value specified.`
|
|
781
|
+
);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
testEndStatement = fileType.testEndStatement;
|
|
785
|
+
if (!testEndStatement) {
|
|
786
|
+
log(
|
|
787
|
+
config,
|
|
788
|
+
"warning",
|
|
789
|
+
`Skipping tests in ${file}. No 'testEndStatement' value specified.`
|
|
790
|
+
);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
actionStatementOpen =
|
|
794
|
+
fileType.actionStatementOpen ||
|
|
795
|
+
fileType.openActionStatement ||
|
|
796
|
+
fileType.openTestStatement;
|
|
797
|
+
if (!actionStatementOpen) {
|
|
798
|
+
log(
|
|
799
|
+
config,
|
|
800
|
+
"warning",
|
|
801
|
+
`Skipping tests in ${file}. No 'actionStatementOpen' value specified.`
|
|
802
|
+
);
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
actionStatementClose =
|
|
806
|
+
fileType.actionStatementClose ||
|
|
807
|
+
fileType.closeActionStatement ||
|
|
808
|
+
fileType.closeTestStatement;
|
|
809
|
+
if (!actionStatementClose) {
|
|
810
|
+
log(
|
|
811
|
+
config,
|
|
812
|
+
"warning",
|
|
813
|
+
`Skipping tests in ${file}. No 'actionStatementClose' value specified.`
|
|
814
|
+
);
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
637
819
|
if (!fileType && extension !== ".json") {
|
|
638
820
|
// Missing filetype options
|
|
639
|
-
|
|
640
|
-
|
|
821
|
+
log(
|
|
822
|
+
config,
|
|
823
|
+
"warning",
|
|
824
|
+
`Skipping file with ${extension} extension. Specify options for the ${extension} extension in your config file.`
|
|
641
825
|
);
|
|
642
|
-
|
|
826
|
+
return;
|
|
643
827
|
}
|
|
644
828
|
|
|
645
829
|
// If file is JSON, add tests straight to array
|
|
646
830
|
if (path.extname(file) === ".json") {
|
|
647
831
|
content = require(file);
|
|
648
|
-
content.tests.
|
|
649
|
-
|
|
650
|
-
|
|
832
|
+
if (typeof content.tests === "object" && content.tests.length > 0) {
|
|
833
|
+
content.tests.forEach((test) => {
|
|
834
|
+
json.tests.push(test);
|
|
835
|
+
});
|
|
836
|
+
} else {
|
|
837
|
+
log(
|
|
838
|
+
config,
|
|
839
|
+
"warning",
|
|
840
|
+
`Skipping ${file} because of unexpected object structure.`
|
|
841
|
+
);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
651
844
|
} else {
|
|
652
845
|
// Loop through lines
|
|
653
846
|
while ((line = inputFile.next())) {
|
|
654
|
-
let lineJson
|
|
655
|
-
let subStart
|
|
656
|
-
let subEnd
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
847
|
+
let lineJson;
|
|
848
|
+
let subStart;
|
|
849
|
+
let subEnd;
|
|
850
|
+
const lineAscii = line.toString("ascii");
|
|
851
|
+
|
|
852
|
+
if (line.includes(testStartStatementOpen)) {
|
|
853
|
+
// Test start
|
|
854
|
+
if (testStartStatementClose) {
|
|
855
|
+
subEnd = lineAscii.lastIndexOf(testStartStatementClose);
|
|
661
856
|
} else {
|
|
662
857
|
subEnd = lineAscii.length;
|
|
663
858
|
}
|
|
664
859
|
subStart =
|
|
665
|
-
lineAscii.indexOf(
|
|
666
|
-
|
|
860
|
+
lineAscii.indexOf(testStartStatementOpen) +
|
|
861
|
+
testStartStatementOpen.length;
|
|
862
|
+
lineJson = JSON.parse(lineAscii.substring(subStart, subEnd));
|
|
863
|
+
// If test is defined in this file instead of referencing a test defined in another file
|
|
864
|
+
if (!lineJson.file) {
|
|
865
|
+
test = { id, file, actions: [] };
|
|
866
|
+
if (lineJson.id) {
|
|
867
|
+
test.id = lineJson.id;
|
|
868
|
+
// Set ID for following actions
|
|
869
|
+
id = lineJson.id;
|
|
870
|
+
}
|
|
871
|
+
if (lineJson.saveFailedTestRecordings)
|
|
872
|
+
test.saveFailedTestRecordings = lineJson.saveFailedTestRecordings;
|
|
873
|
+
if (lineJson.failedTestDirectory)
|
|
874
|
+
test.failedTestDirectory = lineJson.failedTestDirectory;
|
|
875
|
+
json.tests.push(test);
|
|
876
|
+
}
|
|
877
|
+
} else if (line.includes(testEndStatement)) {
|
|
878
|
+
// Revert back to file-based ID
|
|
879
|
+
id = fileId;
|
|
880
|
+
} else if (line.includes(actionStatementOpen)) {
|
|
881
|
+
if (actionStatementClose) {
|
|
882
|
+
subEnd = lineAscii.lastIndexOf(actionStatementClose);
|
|
883
|
+
} else {
|
|
884
|
+
subEnd = lineAscii.length;
|
|
885
|
+
}
|
|
886
|
+
subStart =
|
|
887
|
+
lineAscii.indexOf(actionStatementOpen) + actionStatementOpen.length;
|
|
667
888
|
lineJson = JSON.parse(lineAscii.substring(subStart, subEnd));
|
|
668
889
|
if (!lineJson.testId) {
|
|
669
890
|
lineJson.testId = id;
|
|
@@ -692,6 +913,7 @@ async function outputResults(config, results) {
|
|
|
692
913
|
log(config, "info", "RESULTS:");
|
|
693
914
|
log(config, "info", results);
|
|
694
915
|
log(config, "info", `See detailed results at ${config.output}`);
|
|
916
|
+
log(config, "info", "Cleaning up and finishing post-processing.");
|
|
695
917
|
}
|
|
696
918
|
|
|
697
919
|
async function setEnvs(envsFile) {
|
|
@@ -759,3 +981,14 @@ function loadEnvsForObject(object) {
|
|
|
759
981
|
});
|
|
760
982
|
return object;
|
|
761
983
|
}
|
|
984
|
+
|
|
985
|
+
function timestamp() {
|
|
986
|
+
let timestamp = new Date();
|
|
987
|
+
return `${timestamp.getFullYear()}${("0" + (timestamp.getMonth() + 1)).slice(
|
|
988
|
+
-2
|
|
989
|
+
)}${("0" + timestamp.getDate()).slice(-2)}-${(
|
|
990
|
+
"0" + timestamp.getHours()
|
|
991
|
+
).slice(-2)}${("0" + timestamp.getMinutes()).slice(-2)}${(
|
|
992
|
+
"0" + timestamp.getSeconds()
|
|
993
|
+
).slice(-2)}`;
|
|
994
|
+
}
|