doc-detective 1.1.0 → 1.2.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/cli/suggest.js +4 -0
- package/package.json +5 -3
- package/sample/coverage.json +5 -5
- package/src/config.json +114 -71
- package/src/index.js +51 -5
- package/src/lib/analysis.js +276 -0
- package/src/lib/coverage.js +33 -260
- package/src/lib/sanitize.js +23 -0
- package/src/lib/suggest.js +749 -0
- package/src/lib/tests/moveMouse.js +14 -0
- package/src/lib/tests.js +8 -8
package/cli/suggest.js
ADDED
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doc-detective",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Unit test documentation (and record videos of those tests).",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "node ./cli/index.js",
|
|
8
|
-
"coverage": "node ./cli/coverage.js"
|
|
8
|
+
"coverage": "node ./cli/coverage.js",
|
|
9
|
+
"suggest": "node ./cli/suggest.js"
|
|
9
10
|
},
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
"n-readlines": "^1.0.1",
|
|
34
35
|
"pixelmatch": "^5.3.0",
|
|
35
36
|
"pngjs": "^6.0.0",
|
|
37
|
+
"prompt-sync": "^4.2.0",
|
|
36
38
|
"puppeteer": "^13.7.0",
|
|
37
39
|
"puppeteer-screen-recorder": "^2.0.2",
|
|
38
40
|
"uuid": "^8.3.2",
|
|
@@ -41,4 +43,4 @@
|
|
|
41
43
|
"devDependencies": {
|
|
42
44
|
"make": "^0.8.1"
|
|
43
45
|
}
|
|
44
|
-
}
|
|
46
|
+
}
|
package/sample/coverage.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "Doc Detective Content Coverage Report",
|
|
3
|
-
"timestamp": "
|
|
3
|
+
"timestamp": "20221021-132211",
|
|
4
4
|
"summary": {
|
|
5
|
-
"covered":
|
|
5
|
+
"covered": 11,
|
|
6
6
|
"uncovered": 0,
|
|
7
7
|
"onscreenText": {
|
|
8
8
|
"covered": 2,
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
"uncovered": 0
|
|
38
38
|
},
|
|
39
39
|
"interaction": {
|
|
40
|
-
"covered":
|
|
40
|
+
"covered": 1,
|
|
41
41
|
"uncovered": 0
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"files": [
|
|
45
45
|
{
|
|
46
46
|
"file": "/config/workspace/doc-detective/sample/doc-content.md",
|
|
47
|
-
"covered":
|
|
47
|
+
"covered": 11,
|
|
48
48
|
"uncovered": 0,
|
|
49
49
|
"onscreenText": {
|
|
50
50
|
"covered": 2,
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"uncoveredMatches": []
|
|
88
88
|
},
|
|
89
89
|
"interaction": {
|
|
90
|
-
"covered":
|
|
90
|
+
"covered": 1,
|
|
91
91
|
"uncovered": 0,
|
|
92
92
|
"uncoveredMatches": []
|
|
93
93
|
}
|
package/src/config.json
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
"cleanup": "",
|
|
7
7
|
"recursive": true,
|
|
8
8
|
"coverageOutput": "./sample/coverage.json",
|
|
9
|
+
"testSuggestions": {
|
|
10
|
+
"reportOutput": "./sample/suggestions.json"
|
|
11
|
+
},
|
|
9
12
|
"testExtensions": [
|
|
10
13
|
".md",
|
|
11
14
|
".mdx",
|
|
@@ -31,42 +34,62 @@
|
|
|
31
34
|
"actionStatementOpen": "[comment]: # (action",
|
|
32
35
|
"actionStatementClose": ")",
|
|
33
36
|
"markup": {
|
|
34
|
-
"onscreenText":
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
"[
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"[
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
37
|
+
"onscreenText": {
|
|
38
|
+
"regex": [
|
|
39
|
+
"\\*\\*.+?\\*\\*"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"emphasis": {
|
|
43
|
+
"regex": [
|
|
44
|
+
"(?<!\\*)\\*(?!\\*).+?(?<!\\*)\\*(?!\\*)"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"image": {
|
|
48
|
+
"regex": [
|
|
49
|
+
"!\\[.+?\\]\\(.+?\\)"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"hyperlink": {
|
|
53
|
+
"regex": [
|
|
54
|
+
"(?<!!)\\[.+?\\]\\(.+?\\)"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"orderedList": {
|
|
58
|
+
"regex": [
|
|
59
|
+
"(?<=\n) *?[0-9][0-9]?[0-9]?.\\s*.*"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"unorderedList": {
|
|
63
|
+
"regex": [
|
|
64
|
+
"(?<=\n) *?\\*.\\s*.*",
|
|
65
|
+
"(?<=\n) *?-.\\s*.*"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"codeInline": {
|
|
69
|
+
"regex": [
|
|
70
|
+
"(?<!`)`(?!`).+?(?<!`)`(?!`)"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"codeBlock": {
|
|
74
|
+
"regex": [
|
|
75
|
+
"(?=(```))(\\w|\\W)*(?<=```)"
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"interaction": {
|
|
79
|
+
"regex": [
|
|
80
|
+
"[cC]lick",
|
|
81
|
+
"[tT]ap",
|
|
82
|
+
"[tT]ouch",
|
|
83
|
+
"[sS]elect",
|
|
84
|
+
"[cC]hoose",
|
|
85
|
+
"[tT]oggle",
|
|
86
|
+
"[eE]nable",
|
|
87
|
+
"[dD]isable",
|
|
88
|
+
"[tT]urn [oO][ff|n]",
|
|
89
|
+
"[tT]ype",
|
|
90
|
+
"[eE]nter"
|
|
91
|
+
]
|
|
92
|
+
}
|
|
70
93
|
}
|
|
71
94
|
},
|
|
72
95
|
{
|
|
@@ -82,41 +105,61 @@
|
|
|
82
105
|
"actionStatementOpen": "<!-- action",
|
|
83
106
|
"actionStatementClose": "-->",
|
|
84
107
|
"markup": {
|
|
85
|
-
"onscreenText":
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
"[
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
"[
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
108
|
+
"onscreenText": {
|
|
109
|
+
"regex": [
|
|
110
|
+
"(?=(<b))(\\w|\\W)*(?<=<\/b>)"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"emphasis": {
|
|
114
|
+
"regex": [
|
|
115
|
+
"(?=(<i))(\\w|\\W)*(?<=<\/i>)"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
"image": {
|
|
119
|
+
"regex": [
|
|
120
|
+
"(?=(<img))(\\w|\\W)*(?<=<\/img>|>)"
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
"hyperlink": {
|
|
124
|
+
"regex": [
|
|
125
|
+
"(?=(<a))(\\w|\\W)*(?<=<\/a>)"
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
"orderedList": {
|
|
129
|
+
"regex": [
|
|
130
|
+
"(?=(<ol))(\\w|\\W)*(?<=<\/ol>)"
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
"unorderedList": {
|
|
134
|
+
"regex": [
|
|
135
|
+
"(?=(<ul))(\\w|\\W)*(?<=<\/ul>)"
|
|
136
|
+
]
|
|
137
|
+
},
|
|
138
|
+
"codeInline": {
|
|
139
|
+
"regex": [
|
|
140
|
+
"(?=(<code))(\\w|\\W)*(?<=<\/code>)"
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
"codeBlock": {
|
|
144
|
+
"regex": [
|
|
145
|
+
"(?=(<pre))(\\w|\\W)*(?<=<\/pre>)"
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
"interaction": {
|
|
149
|
+
"regex": [
|
|
150
|
+
"[cC]lick",
|
|
151
|
+
"[tT]ap",
|
|
152
|
+
"[tT]ouch",
|
|
153
|
+
"[sS]elect",
|
|
154
|
+
"[cC]hoose",
|
|
155
|
+
"[tT]oggle",
|
|
156
|
+
"[eE]nable",
|
|
157
|
+
"[dD]isable",
|
|
158
|
+
"[tT]urn [oO][ff|n]",
|
|
159
|
+
"[tT]ype",
|
|
160
|
+
"[eE]nter"
|
|
161
|
+
]
|
|
162
|
+
}
|
|
120
163
|
}
|
|
121
164
|
}
|
|
122
165
|
],
|
package/src/index.js
CHANGED
|
@@ -8,10 +8,14 @@ const {
|
|
|
8
8
|
} = require("./lib/utils");
|
|
9
9
|
const { sendAnalytics } = require("./lib/analytics.js");
|
|
10
10
|
const { runTests } = require("./lib/tests");
|
|
11
|
-
const { checkTestCoverage, checkMarkupCoverage } = require("./lib/
|
|
11
|
+
const { checkTestCoverage, checkMarkupCoverage } = require("./lib/analysis");
|
|
12
|
+
const { reportCoverage } = require("./lib/coverage");
|
|
13
|
+
const { suggestTests, runSuggestions } = require("./lib/suggest");
|
|
14
|
+
const { exit } = require("process");
|
|
12
15
|
|
|
13
16
|
exports.run = main;
|
|
14
17
|
exports.coverage = coverage;
|
|
18
|
+
exports.suggest = suggest;
|
|
15
19
|
|
|
16
20
|
async function main(config, argv) {
|
|
17
21
|
// Set args
|
|
@@ -44,7 +48,7 @@ async function main(config, argv) {
|
|
|
44
48
|
// Output
|
|
45
49
|
outputResults(config.output, results, config);
|
|
46
50
|
if (config.analytics.send) {
|
|
47
|
-
sendAnalytics(config, results);
|
|
51
|
+
// sendAnalytics(config, results);
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
54
|
|
|
@@ -65,13 +69,55 @@ async function coverage(config, argv) {
|
|
|
65
69
|
log(config, "debug", files);
|
|
66
70
|
|
|
67
71
|
const testCoverage = checkTestCoverage(config, files);
|
|
68
|
-
log(config, "debug", "
|
|
72
|
+
log(config, "debug", "TEST COVERAGE:");
|
|
69
73
|
log(config, "debug", testCoverage);
|
|
70
74
|
|
|
71
75
|
const markupCoverage = checkMarkupCoverage(config, testCoverage);
|
|
72
|
-
log(config, "debug", "
|
|
76
|
+
log(config, "debug", "MARKUP COVERAGE:");
|
|
73
77
|
log(config, "debug", markupCoverage);
|
|
74
78
|
|
|
79
|
+
const coverageReport = reportCoverage(config, markupCoverage);
|
|
80
|
+
log(config, "debug", "COVERAGE REPORT:");
|
|
81
|
+
log(config, "debug", coverageReport);
|
|
82
|
+
|
|
83
|
+
// Output
|
|
84
|
+
outputResults(config.coverageOutput, coverageReport, config);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function suggest(config, argv) {
|
|
88
|
+
// Set args
|
|
89
|
+
argv = setArgs(argv);
|
|
90
|
+
log(config, "debug", `ARGV:`);
|
|
91
|
+
log(config, "debug", argv);
|
|
92
|
+
|
|
93
|
+
// Set config
|
|
94
|
+
config = setConfig(config, argv);
|
|
95
|
+
log(config, "debug", `CONFIG:`);
|
|
96
|
+
log(config, "debug", config);
|
|
97
|
+
|
|
98
|
+
// Set files
|
|
99
|
+
const files = setFiles(config);
|
|
100
|
+
log(config, "debug", `FILES:`);
|
|
101
|
+
log(config, "debug", files);
|
|
102
|
+
|
|
103
|
+
const testCoverage = checkTestCoverage(config, files);
|
|
104
|
+
log(config, "debug", "TEST COVERAGE:");
|
|
105
|
+
log(config, "debug", testCoverage);
|
|
106
|
+
|
|
107
|
+
const markupCoverage = checkMarkupCoverage(config, testCoverage);
|
|
108
|
+
log(config, "debug", "MARKUP COVERAGE:");
|
|
109
|
+
log(config, "debug", markupCoverage);
|
|
110
|
+
|
|
111
|
+
const suggestionReport = suggestTests(config, markupCoverage);
|
|
112
|
+
log(config, "debug", "TEST SUGGESTIONS:");
|
|
113
|
+
log(config, "debug", suggestionReport);
|
|
114
|
+
|
|
115
|
+
await runSuggestions(config, suggestionReport);
|
|
116
|
+
|
|
75
117
|
// Output
|
|
76
|
-
outputResults(
|
|
118
|
+
outputResults(
|
|
119
|
+
config.testSuggestions.reportOutput,
|
|
120
|
+
suggestionReport,
|
|
121
|
+
config
|
|
122
|
+
);
|
|
77
123
|
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
const { log } = require("./utils");
|
|
2
|
+
const uuid = require("uuid");
|
|
3
|
+
const nReadlines = require("n-readlines");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { exit } = require("process");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
|
|
8
|
+
exports.checkTestCoverage = checkTestCoverage;
|
|
9
|
+
exports.checkMarkupCoverage = checkMarkupCoverage;
|
|
10
|
+
|
|
11
|
+
function checkTestCoverage(config, files) {
|
|
12
|
+
let testCoverage = {
|
|
13
|
+
files: [],
|
|
14
|
+
errors: [],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Loop through test files
|
|
18
|
+
files.forEach((file) => {
|
|
19
|
+
log(config, "debug", `file: ${file}`);
|
|
20
|
+
fileJson = {
|
|
21
|
+
file,
|
|
22
|
+
coveredLines: [],
|
|
23
|
+
uncoveredLines: [],
|
|
24
|
+
};
|
|
25
|
+
let inTest = false;
|
|
26
|
+
let line;
|
|
27
|
+
let lineNumber = 1;
|
|
28
|
+
let inputFile = new nReadlines(file);
|
|
29
|
+
let extension = path.extname(file);
|
|
30
|
+
let fileType = config.fileTypes.find((fileType) =>
|
|
31
|
+
fileType.extensions.includes(extension)
|
|
32
|
+
);
|
|
33
|
+
fileJson.fileType = fileType;
|
|
34
|
+
|
|
35
|
+
if (typeof fileType === "undefined") {
|
|
36
|
+
// Missing filetype options
|
|
37
|
+
log(
|
|
38
|
+
config,
|
|
39
|
+
"debug",
|
|
40
|
+
`Skipping ${file}. Specify options for the ${extension} extension in your config file.`
|
|
41
|
+
);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let testStartStatementOpen = fileType.testStartStatementOpen;
|
|
46
|
+
if (!testStartStatementOpen) {
|
|
47
|
+
log(
|
|
48
|
+
config,
|
|
49
|
+
"warning",
|
|
50
|
+
`Skipping ${file}. No 'testStartStatementOpen' value specified.`
|
|
51
|
+
);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
let testStartStatementClose = fileType.testStartStatementClose;
|
|
55
|
+
if (!testStartStatementClose) {
|
|
56
|
+
log(
|
|
57
|
+
config,
|
|
58
|
+
"warning",
|
|
59
|
+
`Skipping ${file}. No 'testStartStatementClose' value specified.`
|
|
60
|
+
);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
let testIgnoreStatement = fileType.testIgnoreStatement;
|
|
64
|
+
if (!testIgnoreStatement) {
|
|
65
|
+
log(
|
|
66
|
+
config,
|
|
67
|
+
"warning",
|
|
68
|
+
`Skipping ${file}. No 'testIgnoreStatement' value specified.`
|
|
69
|
+
);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
let testEndStatement = fileType.testEndStatement;
|
|
73
|
+
if (!testEndStatement) {
|
|
74
|
+
log(
|
|
75
|
+
config,
|
|
76
|
+
"warning",
|
|
77
|
+
`Skipping ${file}. No 'testEndStatement' value specified.`
|
|
78
|
+
);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let actionStatementOpen =
|
|
82
|
+
fileType.actionStatementOpen ||
|
|
83
|
+
fileType.openActionStatement ||
|
|
84
|
+
fileType.openTestStatement;
|
|
85
|
+
if (!actionStatementOpen) {
|
|
86
|
+
log(
|
|
87
|
+
config,
|
|
88
|
+
"warning",
|
|
89
|
+
`Skipping ${file}. No 'actionStatementOpen' value specified.`
|
|
90
|
+
);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let actionStatementClose =
|
|
94
|
+
fileType.actionStatementClose ||
|
|
95
|
+
fileType.closeActionStatement ||
|
|
96
|
+
fileType.closeTestStatement;
|
|
97
|
+
if (!actionStatementClose) {
|
|
98
|
+
log(
|
|
99
|
+
config,
|
|
100
|
+
"warning",
|
|
101
|
+
`Skipping ${file}. No 'actionStatementClose' value specified.`
|
|
102
|
+
);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Loop through lines
|
|
107
|
+
while ((line = inputFile.next())) {
|
|
108
|
+
let lineJson;
|
|
109
|
+
let subStart;
|
|
110
|
+
let subEnd;
|
|
111
|
+
let ignore = false;
|
|
112
|
+
const lineAscii = line.toString("ascii");
|
|
113
|
+
|
|
114
|
+
if (line.includes(testStartStatementOpen)) {
|
|
115
|
+
// Test start
|
|
116
|
+
if (testStartStatementClose) {
|
|
117
|
+
subEnd = lineAscii.lastIndexOf(testStartStatementClose);
|
|
118
|
+
} else {
|
|
119
|
+
subEnd = lineAscii.length;
|
|
120
|
+
}
|
|
121
|
+
subStart =
|
|
122
|
+
lineAscii.indexOf(testStartStatementOpen) +
|
|
123
|
+
testStartStatementOpen.length;
|
|
124
|
+
lineJson = JSON.parse(lineAscii.substring(subStart, subEnd));
|
|
125
|
+
// Set inTest to true
|
|
126
|
+
inTest = true;
|
|
127
|
+
ignore = true;
|
|
128
|
+
// Check if test is defined externally
|
|
129
|
+
if (lineJson.file) {
|
|
130
|
+
referencePath = path.resolve(path.dirname(file), lineJson.file);
|
|
131
|
+
// Check to make sure file exists
|
|
132
|
+
if (fs.existsSync(referencePath)) {
|
|
133
|
+
if (lineJson.id) {
|
|
134
|
+
remoteJson = require(referencePath);
|
|
135
|
+
// Make sure test of matching ID exists in file
|
|
136
|
+
idMatch = remoteJson.tests.find(
|
|
137
|
+
(test) => test.id === lineJson.id
|
|
138
|
+
);
|
|
139
|
+
if (!idMatch) {
|
|
140
|
+
// log error
|
|
141
|
+
testCoverage.errors.push({
|
|
142
|
+
file,
|
|
143
|
+
lineNumber,
|
|
144
|
+
description: `Test with ID ${lineJson.id} missing from ${referencePath}.`,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
// log error
|
|
150
|
+
testCoverage.errors.push({
|
|
151
|
+
file,
|
|
152
|
+
lineNumber,
|
|
153
|
+
description: `Referenced file missing: ${referencePath}.`,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
} else if (line.includes(testIgnoreStatement)) {
|
|
158
|
+
inTest = true;
|
|
159
|
+
ignore = true;
|
|
160
|
+
} else if (line.includes(testEndStatement)) {
|
|
161
|
+
inTest = false;
|
|
162
|
+
ignore = true;
|
|
163
|
+
} else if (line.includes(actionStatementOpen) && line.includes(actionStatementClose)) {
|
|
164
|
+
ignore = true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (inTest && !ignore) {
|
|
168
|
+
fileJson.coveredLines.push(lineNumber);
|
|
169
|
+
} else if (!inTest && !ignore) {
|
|
170
|
+
fileJson.uncoveredLines.push(lineNumber);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
lineNumber++;
|
|
174
|
+
}
|
|
175
|
+
testCoverage.files.push(fileJson);
|
|
176
|
+
});
|
|
177
|
+
return testCoverage;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function checkMarkupCoverage(config, testCoverage) {
|
|
181
|
+
let markupCoverage = testCoverage;
|
|
182
|
+
|
|
183
|
+
markupCoverage.files.forEach((file) => {
|
|
184
|
+
file.markup = {};
|
|
185
|
+
let extension = path.extname(file.file);
|
|
186
|
+
let markup = file.fileType.markup;
|
|
187
|
+
|
|
188
|
+
Object.keys(markup).forEach((mark) => {
|
|
189
|
+
if (markup[mark].regex.length === 1 && markup[mark].regex[0] === "") {
|
|
190
|
+
log(
|
|
191
|
+
config,
|
|
192
|
+
"warning",
|
|
193
|
+
`No regex for '${mark}'. Set 'fileType.markup.${mark}' for the '${extension}' extension in your config.`
|
|
194
|
+
);
|
|
195
|
+
delete markup[mark];
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const fileBody = fs.readFileSync(file.file, {
|
|
200
|
+
encoding: "utf8",
|
|
201
|
+
flag: "r",
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Only keep marks that have a truthy (>0) length
|
|
205
|
+
Object.keys(markup).forEach((mark) => {
|
|
206
|
+
markCoverage = {
|
|
207
|
+
coveredLines: [],
|
|
208
|
+
coveredMatches: [],
|
|
209
|
+
uncoveredLines: [],
|
|
210
|
+
uncoveredMatches: [],
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
markup[mark].regex.forEach((matcher) => {
|
|
214
|
+
// Run a match
|
|
215
|
+
regex = new RegExp(matcher, "g");
|
|
216
|
+
matches = fileBody.match(regex);
|
|
217
|
+
if (matches != null) {
|
|
218
|
+
matches.forEach((match) => {
|
|
219
|
+
// Check for duplicates and handle lines separately
|
|
220
|
+
matchEscaped = match.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
221
|
+
start = 0;
|
|
222
|
+
occuranceRegex = new RegExp(matchEscaped, "g");
|
|
223
|
+
occurances = fileBody.match(occuranceRegex).length;
|
|
224
|
+
for (i = 0; i < occurances; i++) {
|
|
225
|
+
index = fileBody.slice(start).match(matchEscaped).index;
|
|
226
|
+
line = fileBody
|
|
227
|
+
.slice(0, start + index)
|
|
228
|
+
.split(/\r\n|\r|\n/).length;
|
|
229
|
+
start = start + index + 1;
|
|
230
|
+
matchObject = {
|
|
231
|
+
line,
|
|
232
|
+
indexInFile: start + index,
|
|
233
|
+
text: match,
|
|
234
|
+
};
|
|
235
|
+
isCovered = file.coveredLines.includes(line);
|
|
236
|
+
isUncovered = file.uncoveredLines.includes(line);
|
|
237
|
+
inCoveredMatches = markCoverage.coveredMatches.some(
|
|
238
|
+
(object) =>
|
|
239
|
+
object.line === matchObject.line &&
|
|
240
|
+
object.text === matchObject.text &&
|
|
241
|
+
object.indexInFile === matchObject.indexInFile
|
|
242
|
+
);
|
|
243
|
+
inUncoveredMatches = markCoverage.uncoveredMatches.some(
|
|
244
|
+
(object) =>
|
|
245
|
+
object.line === matchObject.line &&
|
|
246
|
+
object.text === matchObject.text &&
|
|
247
|
+
object.indexInFile === matchObject.indexInFile
|
|
248
|
+
);
|
|
249
|
+
inCoveredLines = markCoverage.coveredLines.includes(line);
|
|
250
|
+
inUncoveredLines = markCoverage.uncoveredLines.includes(line);
|
|
251
|
+
// console.log({
|
|
252
|
+
// mark,
|
|
253
|
+
// matchObject,
|
|
254
|
+
// isCovered,
|
|
255
|
+
// isUncovered,
|
|
256
|
+
// inCoveredLines,
|
|
257
|
+
// inCoveredMatches,
|
|
258
|
+
// inUncoveredLines,
|
|
259
|
+
// inUncoveredMatches,
|
|
260
|
+
// });
|
|
261
|
+
if (isCovered) {
|
|
262
|
+
if (!inCoveredLines) markCoverage.coveredLines.push(line);
|
|
263
|
+
if (!inCoveredMatches) markCoverage.coveredMatches.push(matchObject);
|
|
264
|
+
} else if (isUncovered) {
|
|
265
|
+
if (!inUncoveredLines) markCoverage.uncoveredLines.push(line);
|
|
266
|
+
if (!inUncoveredMatches) markCoverage.uncoveredMatches.push(matchObject);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
file.markup[mark] = markCoverage;
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
return markupCoverage;
|
|
276
|
+
}
|