@jrpool/kilotest 44.0.0 → 44.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/aceconfig.js +1 -1
- package/docs/AI-TOOLS.md +2 -2
- package/docs/SERVICE.md +4 -4
- package/package.json +1 -1
- package/util.js +22 -22
- package/web/expungeReportsForm/index.js +4 -4
- package/web/listDiagnoses/index.js +6 -4
- package/web/listIssues/index.js +10 -10
- package/web/listReports/index.js +5 -5
- package/web/listTopIssues/index.js +2 -2
- package/web/listViolators/index.js +4 -4
- package/web/pruneReportsForm/index.js +4 -4
- package/web/reannotateForm/index.js +10 -10
- package/web/rewindReportsForm/index.js +4 -4
- package/web/tutorial/index.html +1 -1
package/aceconfig.js
CHANGED
package/docs/AI-TOOLS.md
CHANGED
|
@@ -188,13 +188,13 @@ The [Perplexity documentation](https://www.perplexity.ai/help-center/en/articles
|
|
|
188
188
|
|
|
189
189
|
Once the compatibility of Kilotest as a connector with AI platforms of two different vendors had been confirmed, the focus of work returned to completion of the missing endpoints of the API. The first was an endpoint that would let a model efficiently search for an available report about a particular web page.
|
|
190
190
|
|
|
191
|
-
|
|
191
|
+
A new `summarizeQualityOfMatchingWebPages` tool provided inputs for fragments of a description and of a URL hostname of a web page and responds with summary data about all and only the available reports of pages that match at least one of those fragments, where matching means either including or being included by the provided fragment, case-insensitively.
|
|
192
192
|
|
|
193
193
|
### Increment 7
|
|
194
194
|
|
|
195
195
|
Further experimentation made it clear that Increment 6 had been a detour. The problem with Increment 6 was that it does not improve efficiency by eliminating the need to get a list of all reports. Models must get a list of all reports in order to be confident that the answer to the question “Is a report available yet?” is correct. Any algorithm that matches a page description and a URL with those of existing reports is error-prone, organization names change, website designs change, URLs change, and organizations are related to one another. Typically, a desired report does not yet exist. In that case, the model needs the full list of reports in order to understand the naming convention for pages and to adhere to that convention when submitting a request to have the page of interest tested.
|
|
196
196
|
|
|
197
|
-
For this reason, the tool
|
|
197
|
+
For this reason, the tool developed in increment 6 was deleted.
|
|
198
198
|
|
|
199
199
|
### Increment 8
|
|
200
200
|
|
package/docs/SERVICE.md
CHANGED
|
@@ -332,17 +332,17 @@ In case Testaro again becomes a dependency of Kilotest, the notes below on secur
|
|
|
332
332
|
|
|
333
333
|
### Browser privileges
|
|
334
334
|
|
|
335
|
-
Testaro uses Playwright to launch and control headless browsers, often `chromium`. Those browsers navigate to web pages that are tested by the rule engines that Testaro integrates.
|
|
335
|
+
Testaro uses Playwright to launch and control headless browsers, often `chromium`. Those browsers navigate to web pages that are tested by the rule engines that Testaro integrates.
|
|
336
336
|
|
|
337
|
-
When
|
|
337
|
+
When Playwright (or Puppeteer) launches a `chromium` browser, in most environments it is [sandboxed](https://www.geeksforgeeks.org/ethical-hacking/what-is-browser-sandboxing/). Sandboxing is a security feature that prevents the browser from accessing potentially unsafe system resources. But in the Ubuntu Linux operating system that was installed on the Vultr Cloud Compute host a sandboxed browser requires an [unprivileged user namespace](https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces), and when Ubuntu was installed its configuration disallowed such namespaces. The file `/etc/sysctl.d/99-kilotest-userns.conf` with the content `kernel.apparmor_restrict_unprivileged_userns = 1` prohibited unprivileged user namespaces and thereby made sandboxed browsers unlaunchable.
|
|
338
338
|
|
|
339
339
|
#### Potential modification
|
|
340
340
|
|
|
341
341
|
One modification to cope with this prohibition on the Vultr Cloud Compute host would be to configure Playwright and Puppeteer to launch `chromium` non-sandboxed. In both cases, launch arguments `'--no-sandbox'` and `'--disable-setuid-sandbox'` are available to specify this.
|
|
342
342
|
|
|
343
343
|
- For Playwright, `'--no-sandbox'` and `'--disable-setuid-sandbox'` would be added to the arguments of `browserOptionArgs.push` in the Testaro `run.js` file.
|
|
344
|
-
- For the `qualWeb`
|
|
345
|
-
- The `ibm`
|
|
344
|
+
- For the `qualWeb` rule engine, this would be done in the Testaro `tests/qualweb.js` file, where the `qualWeb.start` method is called with an options argument. Its `args` array property would include `'--no-sandbox'` and `'--disable-setuid-sandbox'`.
|
|
345
|
+
- The `ibm` rule engine, too, could launch a Puppeteer `chromium` browser, if page content instead of a Playwright page were passed to the `accessibilityChecker.getCompliance` method, or if the implementation of the rule engine were changed in the future. For anticipation of such a case, the Testaro `aceconfig.js` file, of which a copy has been created at the root of the Kilotest project, would be modified. That file defines a `module.exports` object with a `puppeteerArgs` property, and, `--no-sandbox` and `--disable-setuid-sandbox` would be added to its array value.
|
|
346
346
|
|
|
347
347
|
#### Current modification
|
|
348
348
|
|
package/package.json
CHANGED
package/util.js
CHANGED
|
@@ -27,7 +27,7 @@ const hiddenReportsPath = exports.hiddenReportsPath = path.join(dbPath, 'hiddenR
|
|
|
27
27
|
// Path of the reports data file.
|
|
28
28
|
const reportsExtractPath = exports.reportsExtractPath = path.join(dbPath, 'reportsExtract.json');
|
|
29
29
|
// IDs, names, and sponsors of Testaro rule engines.
|
|
30
|
-
const
|
|
30
|
+
const ruleEngines = exports.ruleEngines = {
|
|
31
31
|
alfa: ['Alfa', 'Siteimprove'],
|
|
32
32
|
aslint: ['ASLint', 'eSSENTIAL Accessibility'],
|
|
33
33
|
axe: ['Axe', 'Deque'],
|
|
@@ -41,7 +41,6 @@ const tools = exports.tools = {
|
|
|
41
41
|
wave: ['WAVE', 'Utah State University'],
|
|
42
42
|
wax: ['WallyAX', 'Wally']
|
|
43
43
|
};
|
|
44
|
-
exports.ruleEngines = tools;
|
|
45
44
|
exports.researchAgents = {
|
|
46
45
|
'research-agent': 'Internal Research Agent'
|
|
47
46
|
}
|
|
@@ -252,13 +251,14 @@ exports.getTextFragmentHref = (text, url) => {
|
|
|
252
251
|
return `${url}#:~:text=${fragmentList}`;
|
|
253
252
|
};
|
|
254
253
|
// Returns a +-delimited list of sorted names of rule engines.
|
|
255
|
-
exports.
|
|
256
|
-
.map(
|
|
254
|
+
exports.getEngineList = engineIDs => Array.from(engineIDs)
|
|
255
|
+
.map(engineID => ruleEngines[engineID][0])
|
|
257
256
|
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}))
|
|
258
257
|
.join(' + ');
|
|
259
258
|
// Returns a string of names of rule engines.
|
|
260
|
-
exports.
|
|
261
|
-
|
|
259
|
+
exports.getEngineNamesString = engineIDSet => alphaSort(
|
|
260
|
+
Array.from(engineIDSet).map(engineID => ruleEngines[engineID]?.[0] || engineID)
|
|
261
|
+
).join(' + ');
|
|
262
262
|
// Gets the WCAG Understanding link for a numeric WCAG standard identifier.
|
|
263
263
|
exports.getWCAGLink = numericID => {
|
|
264
264
|
// Return the link.
|
|
@@ -444,7 +444,7 @@ const isValidReport = exports.isValidReport = report => {
|
|
|
444
444
|
&& report.acts.every(act =>
|
|
445
445
|
typeof act === 'object'
|
|
446
446
|
&& typeof act.type === 'string'
|
|
447
|
-
&& act.type === 'test' ? Object.keys(
|
|
447
|
+
&& act.type === 'test' ? Object.keys(ruleEngines).includes(act.which) : true
|
|
448
448
|
)
|
|
449
449
|
&& typeof report.jobData === 'object'
|
|
450
450
|
&& report.jobData.endTime
|
|
@@ -536,16 +536,16 @@ exports.getReportData = async (timeStamp, jobID) => {
|
|
|
536
536
|
creationDate: getDateTime(timeStamp),
|
|
537
537
|
daysAgo: getAgoDays(timeStamp),
|
|
538
538
|
issueCount: 0,
|
|
539
|
-
|
|
540
|
-
|
|
539
|
+
engineNames: [],
|
|
540
|
+
engineCount: 0,
|
|
541
541
|
reporterNames: [],
|
|
542
542
|
reporterCount: 0,
|
|
543
543
|
violatorCount: 0,
|
|
544
|
-
|
|
545
|
-
|
|
544
|
+
preventedEngineNames: [],
|
|
545
|
+
preventedEngineCount: 0
|
|
546
546
|
};
|
|
547
547
|
const issueIDSet = new Set();
|
|
548
|
-
const
|
|
548
|
+
const engineNameSet = new Set();
|
|
549
549
|
const reporterIDSet = new Set();
|
|
550
550
|
const violatorIndexSet = new Set();
|
|
551
551
|
// For each act of the report:
|
|
@@ -553,15 +553,15 @@ exports.getReportData = async (timeStamp, jobID) => {
|
|
|
553
553
|
// If it is a test act:
|
|
554
554
|
if (act.type === 'test') {
|
|
555
555
|
const {result, which} = act;
|
|
556
|
-
// Ensure that the
|
|
557
|
-
|
|
556
|
+
// Ensure that the rule engine is in the temporary data.
|
|
557
|
+
engineNameSet.add(ruleEngines[which][0]);
|
|
558
558
|
const instances = result?.standardResult?.instances ?? [];
|
|
559
559
|
// For each standard instance of the act:
|
|
560
560
|
instances.forEach(instance => {
|
|
561
561
|
const {catalogIndex, issueID} = instance;
|
|
562
562
|
// If it has a non-ignorable classified issue ID:
|
|
563
563
|
if (issueID && issueSpecs[issueID] && issueID !== 'ignorable') {
|
|
564
|
-
// Ensure that the
|
|
564
|
+
// Ensure that the rule engine is in the temporary data.
|
|
565
565
|
reporterIDSet.add(which);
|
|
566
566
|
// Ensure that the issue is in the temporary data.
|
|
567
567
|
issueIDSet.add(issueID);
|
|
@@ -576,21 +576,21 @@ exports.getReportData = async (timeStamp, jobID) => {
|
|
|
576
576
|
});
|
|
577
577
|
// Populate the data with the act data.
|
|
578
578
|
data.issueCount = issueIDSet.size;
|
|
579
|
-
data.
|
|
580
|
-
.from(
|
|
579
|
+
data.engineNames = Array
|
|
580
|
+
.from(engineNameSet)
|
|
581
581
|
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}));
|
|
582
|
-
data.
|
|
582
|
+
data.engineCount = engineNameSet.size;
|
|
583
583
|
data.reporterNames = Array
|
|
584
584
|
.from(reporterIDSet)
|
|
585
|
-
.map(id =>
|
|
585
|
+
.map(id => ruleEngines[id][0])
|
|
586
586
|
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}));
|
|
587
587
|
data.reporterCount = data.reporterNames.length;
|
|
588
588
|
data.violatorCount = violatorIndexSet.size;
|
|
589
589
|
// Add the names of any prevented rule engines to the data.
|
|
590
|
-
data.
|
|
591
|
-
.map(
|
|
590
|
+
data.preventedEngineNames = Object.keys(report.jobData?.preventions || {})
|
|
591
|
+
.map(engineID => ruleEngines[engineID][0])
|
|
592
592
|
.sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'}));
|
|
593
|
-
data.
|
|
593
|
+
data.preventedEngineCount = data.preventedEngineNames.length;
|
|
594
594
|
// Return the data.
|
|
595
595
|
return data;
|
|
596
596
|
}
|
|
@@ -54,7 +54,7 @@ exports.answer = async (_, search) => {
|
|
|
54
54
|
const [timeStamp, jobID] = reportName.slice(0, -5).split('-');
|
|
55
55
|
// Get a summary of it.
|
|
56
56
|
const reportFacts = await getReportData(timeStamp, jobID);
|
|
57
|
-
const {error, issueCount,
|
|
57
|
+
const {error, issueCount, preventedEngineCount, url} = reportFacts;
|
|
58
58
|
// If this failed:
|
|
59
59
|
if (error) {
|
|
60
60
|
// Return why.
|
|
@@ -67,7 +67,7 @@ exports.answer = async (_, search) => {
|
|
|
67
67
|
timeStamp,
|
|
68
68
|
jobID,
|
|
69
69
|
issueCount,
|
|
70
|
-
|
|
70
|
+
preventedEngineCount,
|
|
71
71
|
url
|
|
72
72
|
});
|
|
73
73
|
}
|
|
@@ -79,9 +79,9 @@ exports.answer = async (_, search) => {
|
|
|
79
79
|
let anyDeletable = false;
|
|
80
80
|
// For each summary:
|
|
81
81
|
reportSpecs.forEach((spec, index) => {
|
|
82
|
-
const {timeStamp, jobID, issueCount,
|
|
82
|
+
const {timeStamp, jobID, issueCount, preventedEngineCount, url} = spec;
|
|
83
83
|
const jobName = `${timeStamp}-${jobID}`;
|
|
84
|
-
const specString = `<code>${url}</code> (<code>${jobName}</code>): preventions ${
|
|
84
|
+
const specString = `<code>${url}</code> (<code>${jobName}</code>): preventions ${preventedEngineCount}, issues ${issueCount}`;
|
|
85
85
|
// If its report is the sole report on a target:
|
|
86
86
|
if (reportSpecs[index - 1]?.url !== url && reportSpecs[index + 1]?.url !== url) {
|
|
87
87
|
// Add a line with a deletion checkbox.
|
|
@@ -13,7 +13,7 @@ const {
|
|
|
13
13
|
getWeightName,
|
|
14
14
|
htmlSafe,
|
|
15
15
|
isHidden,
|
|
16
|
-
|
|
16
|
+
ruleEngines
|
|
17
17
|
} = require('../../util');
|
|
18
18
|
const {issues: issueSpecs} = require('testaro-issues');
|
|
19
19
|
const fs = require('fs/promises');
|
|
@@ -107,7 +107,7 @@ const populateQuery = async (issueID, timeStamp, jobID, catalogIndex, pathID, qu
|
|
|
107
107
|
const {ruleID, what} = instance;
|
|
108
108
|
// Add lines for it to the array.
|
|
109
109
|
diagnoses.push({
|
|
110
|
-
|
|
110
|
+
engineID: which,
|
|
111
111
|
ruleID,
|
|
112
112
|
what
|
|
113
113
|
});
|
|
@@ -115,10 +115,12 @@ const populateQuery = async (issueID, timeStamp, jobID, catalogIndex, pathID, qu
|
|
|
115
115
|
});
|
|
116
116
|
// For each diagnosis:
|
|
117
117
|
diagnoses.forEach(diagnosis => {
|
|
118
|
-
const {
|
|
118
|
+
const {engineID, ruleID, what} = diagnosis;
|
|
119
119
|
// Add lines.
|
|
120
120
|
lines.push(`${margin}<li>${htmlSafe(what)}`);
|
|
121
|
-
lines.push(
|
|
121
|
+
lines.push(
|
|
122
|
+
`${margin} <p>Rule engine: ${ruleEngines[engineID][0]} (${ruleEngines[engineID][1]})</p>`
|
|
123
|
+
);
|
|
122
124
|
if (ruleID !== what) {
|
|
123
125
|
lines.push(`${margin} <p>Rule: <code>${ruleID}</code></p>`);
|
|
124
126
|
}
|
package/web/listIssues/index.js
CHANGED
|
@@ -9,14 +9,14 @@ const {
|
|
|
9
9
|
getPageData,
|
|
10
10
|
getPageDataStrings,
|
|
11
11
|
getReport,
|
|
12
|
-
|
|
12
|
+
getEngineList,
|
|
13
13
|
getWCAGLink,
|
|
14
14
|
getWeightName,
|
|
15
15
|
htmlSafe,
|
|
16
16
|
isHidden,
|
|
17
17
|
isValidReport,
|
|
18
18
|
objectSort,
|
|
19
|
-
|
|
19
|
+
ruleEngines
|
|
20
20
|
} = require('../../util');
|
|
21
21
|
const {issues: issueSpecs} = require('testaro-issues');
|
|
22
22
|
const fs = require('fs/promises');
|
|
@@ -82,7 +82,7 @@ const getIssuesData = async (timeStamp, jobID) => {
|
|
|
82
82
|
reporterList: '',
|
|
83
83
|
violators: new Set()
|
|
84
84
|
};
|
|
85
|
-
// Ensure the
|
|
85
|
+
// Ensure the rule engine is in the temporary data.
|
|
86
86
|
temp.issues[issueID].reporters.add(which);
|
|
87
87
|
temp.reporters.add(which);
|
|
88
88
|
// If the instance has a catalog index:
|
|
@@ -97,7 +97,7 @@ const getIssuesData = async (timeStamp, jobID) => {
|
|
|
97
97
|
}
|
|
98
98
|
});
|
|
99
99
|
// Finish populating the final data.
|
|
100
|
-
final.reporterList =
|
|
100
|
+
final.reporterList = getEngineList(temp.reporters);
|
|
101
101
|
final.reporterCount = temp.reporters.size;
|
|
102
102
|
final.violatorCount = temp.violators.size;
|
|
103
103
|
Object.values(temp.issues).forEach(issue => {
|
|
@@ -109,7 +109,7 @@ const getIssuesData = async (timeStamp, jobID) => {
|
|
|
109
109
|
why,
|
|
110
110
|
weight
|
|
111
111
|
};
|
|
112
|
-
finalIssue.reporterList =
|
|
112
|
+
finalIssue.reporterList = getEngineList(issue.reporters);
|
|
113
113
|
finalIssue.reporterCount = issue.reporters.size;
|
|
114
114
|
finalIssue.violatorCount = issue.violators.size;
|
|
115
115
|
final.issues[issue.weight].push(finalIssue);
|
|
@@ -183,11 +183,11 @@ const populateQuery = async (timeStamp, jobID, query) => {
|
|
|
183
183
|
// Initialize strings for the prevention notices query property.
|
|
184
184
|
const preventionStrings = [];
|
|
185
185
|
const margin = ' '.repeat(6);
|
|
186
|
-
Object.keys(preventions).forEach(
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const causeString = htmlSafe(preventions[
|
|
190
|
-
const preventionString = `${margin}<li>Page not testable by ${
|
|
186
|
+
Object.keys(preventions).forEach(preventedEngineID => {
|
|
187
|
+
const engineName = ruleEngines[preventedEngineID];
|
|
188
|
+
const engineNameString = `${engineName[0]} (${engineName[1]})`;
|
|
189
|
+
const causeString = htmlSafe(preventions[preventedEngineID]);
|
|
190
|
+
const preventionString = `${margin}<li>Page not testable by ${engineNameString}: ${causeString}</li>`;
|
|
191
191
|
preventionStrings.push(preventionString);
|
|
192
192
|
});
|
|
193
193
|
// Add prevention notices to the query.
|
package/web/listReports/index.js
CHANGED
|
@@ -90,8 +90,8 @@ const populateQuery = async query => {
|
|
|
90
90
|
const {
|
|
91
91
|
error,
|
|
92
92
|
issueCount,
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
preventedEngineCount,
|
|
94
|
+
preventedEngineNames,
|
|
95
95
|
reporterNames,
|
|
96
96
|
reporterCount,
|
|
97
97
|
violatorCount
|
|
@@ -117,11 +117,11 @@ const populateQuery = async query => {
|
|
|
117
117
|
// Add facts about the report to the lines.
|
|
118
118
|
lines.tested.push(`${margin} <li>${testInfo}</li>`);
|
|
119
119
|
// If the page prevented any rule engine from performing its tests:
|
|
120
|
-
if (
|
|
120
|
+
if (preventedEngineCount) {
|
|
121
121
|
// Add this to the lines.
|
|
122
|
-
const engineCountString = getCountString(
|
|
122
|
+
const engineCountString = getCountString(preventedEngineCount, 'rule engine', 'rule engines');
|
|
123
123
|
lines.tested.push(
|
|
124
|
-
`${margin} <li>Page not testable by ${engineCountString} (${
|
|
124
|
+
`${margin} <li>Page not testable by ${engineCountString} (${preventedEngineNames.join(' + ')})</li>`,
|
|
125
125
|
);
|
|
126
126
|
}
|
|
127
127
|
// Add facts about the test results to the lines.
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
const {sendAlert} = require('../../alerts');
|
|
9
9
|
const {
|
|
10
|
+
getEngineNamesString,
|
|
10
11
|
getLatestReportExtracts,
|
|
11
12
|
getReport,
|
|
12
|
-
getToolNamesString,
|
|
13
13
|
getWCAGLink,
|
|
14
14
|
getWeightName,
|
|
15
15
|
objectSort,
|
|
@@ -78,7 +78,7 @@ const getIssuesSummary = async () => {
|
|
|
78
78
|
weight: issueSpecs[issueID].weight,
|
|
79
79
|
count,
|
|
80
80
|
percentage: 0,
|
|
81
|
-
reporters:
|
|
81
|
+
reporters: getEngineNamesString(reporters)
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
// Otherwise, i.e. if it is no longer classified:
|
|
@@ -9,7 +9,7 @@ const {
|
|
|
9
9
|
getPageDataStrings,
|
|
10
10
|
getPathID,
|
|
11
11
|
getReport,
|
|
12
|
-
|
|
12
|
+
getEngineNamesString,
|
|
13
13
|
getTextFragmentHref,
|
|
14
14
|
getWCAGLink,
|
|
15
15
|
getWeightName,
|
|
@@ -92,7 +92,7 @@ const populateQuery = async (issueID, timeStamp, jobID, query) => {
|
|
|
92
92
|
text: catalog[catalogIndex]?.text ?? '',
|
|
93
93
|
reporters: new Set()
|
|
94
94
|
};
|
|
95
|
-
// Ensure that the
|
|
95
|
+
// Ensure that the rule engine is in the sets of reporters of the violator and the issue.
|
|
96
96
|
violators[catalogIndex].reporters.add(which);
|
|
97
97
|
query.reporters.add(which);
|
|
98
98
|
});
|
|
@@ -103,12 +103,12 @@ const populateQuery = async (issueID, timeStamp, jobID, query) => {
|
|
|
103
103
|
// For each violator:
|
|
104
104
|
Object.values(violators).forEach(violatorData => {
|
|
105
105
|
// Convert the set of its reporters to a string.
|
|
106
|
-
violatorData.reporters =
|
|
106
|
+
violatorData.reporters = getEngineNamesString(violatorData.reporters);
|
|
107
107
|
});
|
|
108
108
|
const reporterCount = query.reporters.size;
|
|
109
109
|
query.reporterCount = reporterCount === 1 ? '1 rule engine' : `${reporterCount} rule engines`;
|
|
110
110
|
// Convert the set of issue reporters to a string.
|
|
111
|
-
query.reporters =
|
|
111
|
+
query.reporters = getEngineNamesString(query.reporters);
|
|
112
112
|
// Convert the violator data to an array.
|
|
113
113
|
violators = Object.entries(violators).map(entry => ({
|
|
114
114
|
catalogIndex: entry[0],
|
|
@@ -55,7 +55,7 @@ exports.answer = async (_, search) => {
|
|
|
55
55
|
const [timeStamp, jobID] = reportName.slice(0, -5).split('-');
|
|
56
56
|
// Get a summary of it.
|
|
57
57
|
const reportFacts = await getReportData(timeStamp, jobID);
|
|
58
|
-
const {error, issueCount,
|
|
58
|
+
const {error, issueCount, preventedEngineCount, url} = reportFacts;
|
|
59
59
|
// If this failed:
|
|
60
60
|
if (error) {
|
|
61
61
|
// Return why.
|
|
@@ -69,7 +69,7 @@ exports.answer = async (_, search) => {
|
|
|
69
69
|
timeStamp,
|
|
70
70
|
jobID,
|
|
71
71
|
issueCount,
|
|
72
|
-
|
|
72
|
+
preventedEngineCount,
|
|
73
73
|
url
|
|
74
74
|
});
|
|
75
75
|
}
|
|
@@ -85,9 +85,9 @@ exports.answer = async (_, search) => {
|
|
|
85
85
|
let anyDeletable = false;
|
|
86
86
|
// For each summary:
|
|
87
87
|
reportSpecs.forEach((spec, index) => {
|
|
88
|
-
const {timeStamp, jobID, issueCount,
|
|
88
|
+
const {timeStamp, jobID, issueCount, preventedEngineCount, url} = spec;
|
|
89
89
|
const jobName = `${timeStamp}-${jobID}`;
|
|
90
|
-
const specString = `<code>${url}</code> (<code>${jobName}</code>): preventions ${
|
|
90
|
+
const specString = `<code>${url}</code> (<code>${jobName}</code>): preventions ${preventedEngineCount}, issues ${issueCount}`;
|
|
91
91
|
// If its report is a non-latest report:
|
|
92
92
|
if (reportSpecs[index + 1]?.url === url) {
|
|
93
93
|
// Add a line with a deletion checkbox.
|
|
@@ -62,27 +62,27 @@ const populateQuery = async query => {
|
|
|
62
62
|
const stillUnclassifiedLines = [];
|
|
63
63
|
const reClassifiedLines = [];
|
|
64
64
|
const margin = ' '.repeat(6);
|
|
65
|
-
// For each
|
|
66
|
-
Object.keys(stillUnclassified).forEach(
|
|
65
|
+
// For each rule engine reporting any violations of still unclassified rules:
|
|
66
|
+
Object.keys(stillUnclassified).forEach(engineID => {
|
|
67
67
|
// For each such rule:
|
|
68
|
-
Object.keys(stillUnclassified[
|
|
69
|
-
const reportIDs = Array.from(stillUnclassified[
|
|
68
|
+
Object.keys(stillUnclassified[engineID]).forEach(ruleID => {
|
|
69
|
+
const reportIDs = Array.from(stillUnclassified[engineID][ruleID]);
|
|
70
70
|
// Add a line to the lines on the rule.
|
|
71
71
|
stillUnclassifiedLines.push(
|
|
72
|
-
`${margin}<li>${
|
|
72
|
+
`${margin}<li>${engineID}: ${ruleID} (${reportIDs.join(', ')})</li>`
|
|
73
73
|
);
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
76
|
// Add the lines to the query.
|
|
77
77
|
query.stillUnclassified = stillUnclassifiedLines.join('\n');
|
|
78
|
-
// For each
|
|
79
|
-
Object.keys(reClassified).forEach(
|
|
78
|
+
// For each rule engine reporting any discrepancies in rule classification:
|
|
79
|
+
Object.keys(reClassified).forEach(engineID => {
|
|
80
80
|
// For each such rule:
|
|
81
|
-
Object.keys(reClassified[
|
|
82
|
-
const reportIDs = Array.from(reClassified[
|
|
81
|
+
Object.keys(reClassified[engineID]).forEach(ruleID => {
|
|
82
|
+
const reportIDs = Array.from(reClassified[engineID][ruleID]);
|
|
83
83
|
// Add a line to the lines on the rule.
|
|
84
84
|
reClassifiedLines.push(
|
|
85
|
-
`${margin}<li>${
|
|
85
|
+
`${margin}<li>${engineID}: ${ruleID} (${reportIDs.join(', ')})</li>`
|
|
86
86
|
);
|
|
87
87
|
});
|
|
88
88
|
});
|
|
@@ -52,7 +52,7 @@ exports.answer = async (_, search) => {
|
|
|
52
52
|
const [timeStamp, jobID] = reportName.slice(0, -5).split('-');
|
|
53
53
|
// Get a summary of it.
|
|
54
54
|
const reportFacts = await getReportData(timeStamp, jobID);
|
|
55
|
-
const {error, issueCount,
|
|
55
|
+
const {error, issueCount, preventedEngineCount, url} = reportFacts;
|
|
56
56
|
// If this failed:
|
|
57
57
|
if (error) {
|
|
58
58
|
// Return why.
|
|
@@ -65,7 +65,7 @@ exports.answer = async (_, search) => {
|
|
|
65
65
|
timeStamp,
|
|
66
66
|
jobID,
|
|
67
67
|
issueCount,
|
|
68
|
-
|
|
68
|
+
preventedEngineCount,
|
|
69
69
|
url
|
|
70
70
|
});
|
|
71
71
|
}
|
|
@@ -81,9 +81,9 @@ exports.answer = async (_, search) => {
|
|
|
81
81
|
let anyDeletable = false;
|
|
82
82
|
// For each summary:
|
|
83
83
|
reportSpecs.forEach((spec, index) => {
|
|
84
|
-
const {timeStamp, jobID, issueCount,
|
|
84
|
+
const {timeStamp, jobID, issueCount, preventedEngineCount, url} = spec;
|
|
85
85
|
const jobName = `${timeStamp}-${jobID}`;
|
|
86
|
-
const specString = `<code>${url}</code> (<code>${jobName}</code>): preventions ${
|
|
86
|
+
const specString = `<code>${url}</code> (<code>${jobName}</code>): preventions ${preventedEngineCount}, issues ${issueCount}`;
|
|
87
87
|
// If its report is the latest report on a target with at least 2 reports:
|
|
88
88
|
if (reportSpecs[index - 1]?.url === url && reportSpecs[index + 1]?.url !== url) {
|
|
89
89
|
// Add a line with a deletion checkbox.
|
package/web/tutorial/index.html
CHANGED
|
@@ -419,7 +419,7 @@
|
|
|
419
419
|
<p>In this practicum, which rule engines discovered both <code>autocomplete</code> defects?</p>
|
|
420
420
|
<ol>
|
|
421
421
|
<li><label><input type="radio" name="q4" value="a"> All 10 rule engines</label></li>
|
|
422
|
-
<li><label><input type="radio" name="q4" value="b"> Six of the rule engines></li>
|
|
422
|
+
<li><label><input type="radio" name="q4" value="b"> Six of the rule engines</label></li>
|
|
423
423
|
<li><label><input type="radio" name="q4" value="c"> Only Testaro and HTML CodeSniffer</label></li>
|
|
424
424
|
<li><label><input type="radio" name="q4" value="d"> None of the rule engines</label></li>
|
|
425
425
|
</ol>
|