testilo 3.9.10 → 3.9.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "3.9.10",
3
+ "version": "3.9.14",
4
4
  "description": "Client that scores and digests Testaro reports",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,39 @@
1
+ <!DOCTYPE HTML>
2
+ <html lang="en-US">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="author" content="Testilo">
7
+ <meta name="creator" content="Testilo">
8
+ <meta name="publisher" name="Testilo">
9
+ <meta name="description" content="comparison of accessibility scores">
10
+ <meta name="keywords" content="accessibility a11y web testing">
11
+ <title>Accessibility score comparison</title>
12
+ <link rel="icon" href="favicon.png">
13
+ <link rel="stylesheet" href="style.css">
14
+ </head>
15
+ <body>
16
+ <main>
17
+ <header>
18
+ <h1>Transactional accessibility comparison</h1>
19
+ </header>
20
+ <h2>Introduction</h2>
21
+ <p>This report compares __pageCount__ web pages, rating each page on <dfn>transactional accessibility</dfn>. The score given to each page estimates the <a href="https://www.w3.org/WAI/fundamentals/accessibility-intro/">accessibility</a> of one particular transaction, in which the user discovers how to report an accessibility issue with the page to those who are responsible for the page.</p>
22
+ <p>You can find a more detailed explanation of transactional accessibility and how the accessibility of this particular transaction is scored in each of the reports. The scores in the table below are links to those reports.</p>
23
+ <p>This report was produced by the <code>cpA11yMessage</code> procedure of <a href="https://www.npmjs.com/package/testilo">Testilo</a>.
24
+ <h2>Comparison</h2>
25
+ <table class="allBorder">
26
+ <caption>Accessibility scores of accessibility-reporting transactions</caption>
27
+ <thead>
28
+ <tr><th scope="col">Page</th><th scope="col" colspan="2">Score (higher is better)</tr>
29
+ </thead>
30
+ <tbody class="linkSmaller secondCellRight">
31
+ __tableBody__
32
+ </tbody>
33
+ </table>
34
+ <footer>
35
+ <p class="date">Produced <time itemprop="datePublished" datetime="__dateISO__">__dateSlash__</time></p>
36
+ </footer>
37
+ </main>
38
+ </body>
39
+ </html>
@@ -0,0 +1,71 @@
1
+ /*
2
+ cpA11yMessage.js
3
+ Returns a query for an HTML page including a bar-graph table.
4
+ */
5
+
6
+ // ########## IMPORTS
7
+
8
+ // Module to keep secrets local.
9
+ require('dotenv').config();
10
+ // Module to access files.
11
+ const fs = require('fs/promises');
12
+
13
+ // ########## CONSTANTS
14
+
15
+ const reportDirScored = process.env.REPORTDIR_SCORED || 'reports/scored';
16
+ const query = {};
17
+
18
+ // ########## FUNCTIONS
19
+
20
+ // Returns data on the hosts in the report directory.
21
+ const getData = async () => {
22
+ const reportDirAbs = `${__dirname}/../../../${reportDirScored}`;
23
+ const reportFileNamesAll = await fs.readdir(reportDirAbs);
24
+ const reportFileNamesSource = reportFileNamesAll.filter(fileName => fileName.endsWith('.json'));
25
+ const pageCount = reportFileNamesSource.length;
26
+ const bodyData = [];
27
+ for (const fileName of reportFileNamesSource) {
28
+ const fileJSON = await fs.readFile(`${reportDirAbs}/${fileName}`, 'utf8');
29
+ const file = JSON.parse(fileJSON);
30
+ const {id, host, score} = file;
31
+ bodyData.push({
32
+ id,
33
+ org: host.what,
34
+ url: host.which,
35
+ score: score.total
36
+ });
37
+ };
38
+ return {
39
+ pageCount,
40
+ bodyData
41
+ }
42
+ };
43
+ // Returns the maximum score.
44
+ const getMaxScore = tableData => tableData.reduce((max, item) => Math.max(max, item.score), 0);
45
+ // Converts report data to a table body.
46
+ const getTableBody = async bodyData => {
47
+ const maxScore = getMaxScore(bodyData);
48
+ const rows = bodyData
49
+ .sort((a, b) => a.score - b.score)
50
+ .map(item => {
51
+ const {id, org, url, score} = item;
52
+ const pageCell = `<th scope="row"><a href="${url}">${org}</a></th>`;
53
+ const numCell = `<td><a href="digests/${id}.html">${score}</a></td>`;
54
+ const barWidth = 100 * score / maxScore;
55
+ const bar = `<rect height="100%" width="${barWidth}%" fill="red"></rect>`;
56
+ const barCell = `<td aria-hidden="true"><svg width="100%" height="0.7em">${bar}</svg></td>`;
57
+ const row = `<tr>${pageCell}${numCell}${barCell}</tr>`;
58
+ return row;
59
+ });
60
+ return rows.join('\n ');
61
+ };
62
+ // Returns a query for a comparative table.
63
+ exports.getQuery = async () => {
64
+ const data = await getData();
65
+ query.pageCount = data.pageCount;
66
+ query.tableBody = await getTableBody(data.bodyData);
67
+ const date = new Date();
68
+ query.dateISO = date.toISOString().slice(0, 10);
69
+ query.dateSlash = query.dateISO.replace(/-/g, '/');
70
+ return query;
71
+ };
@@ -27,8 +27,18 @@
27
27
  </div>
28
28
  </header>
29
29
  <h2>Introduction</h2>
30
- <p>The <a href="https://www.npmjs.com/package/testaro">Testaro</a> application used its <code>tpA11yMessage</code> testing procedure to test the <a href="https://www.w3.org/WAI/fundamentals/accessibility-intro/"><dfn>accessibility</dfn></a> (barrier-free design and coding) of the __org__ web page at <a href="__url__">__url__</a> on __dateSlash__. The procedure estimated how easily a user who finds an accessibility issue on the page can find a method for reporting that issue to the owner of the page. Testaro produced a report.</p>
31
- <p><a href="https://www.npmjs.com/package/testilo">Testilo</a> processed the report and used the <code>spA11yMessage</code> scoring procedure to compute a score for the page. The total score is __totalScore__ (where 0 is the worst and 12 is the best possible score). The scored report is appended below.</p>
30
+ <p>This is a report on transactional <a href="https://www.w3.org/WAI/fundamentals/accessibility-intro/">accessibility</a>. Suppose a person is visiting a website and wants to engage in some transaction with it. Is the transaction easy, predictable, and safe? If so, the website is <dfn>transactionally accessible</dfn>. Some disabilities make it difficult to discover how to do things on a website. Everybody, but especially any person with such a disability, benefits from transactions being designed to conform to the most common and standard conventions.</p>
31
+ <p>This report deals with an issue-reporting transaction. Specifically, the transaction begins when a person notices an accessibility issue on a page or view of a website or web application and wants to report that issue to whoever is responsible for the website. The simple, standard, and thus predictable transaction is assumed to look like this:</p>
32
+ <ol>
33
+ <li>The person finds an accessibility link on the page.</li>
34
+ <li>The person clicks that link.</li>
35
+ <li>That link takes the person to an accessibility page.</li>
36
+ <li>On the accessibility page, there is a link for sending an email message about accessibility, and there is another link for making a telephone call about accessibility.</li>
37
+ </ol>
38
+ <p>Thus, instead of the person needing to figure out how this particular website accepts issue reports, the person is assumed already to know how websites normally accept issue reports. So, instead of exploring the site to find its method, the person uses the standard method.</p>
39
+ <h2>Procedures</h2>
40
+ <p>The <a href="https://www.npmjs.com/package/testaro">Testaro</a> application used its <code>tpA11yMessage</code> testing procedure to evaluate the accessibility of this transaction on the __org__ web page at <a href="__url__">__url__</a> on __dateSlash__. Testaro produced a report.</p>
41
+ <p>The <a href="https://www.npmjs.com/package/testilo">Testilo</a> application processed the report and used the <code>spA11yMessage</code> scoring procedure to compute a score for the transaction. The total score is __totalScore__ (where 0 is the worst and 16 is the best possible score). The scored report is appended below.</p>
32
42
  <p>Finally, Testilo used procedure <code>dpA11yMessage</code> to produce this digest, briefly explaining how <code>spA11yMessage</code> computed the scores.</p>
33
43
  <h2>Score summary</h2>
34
44
  <table class="allBorder secondCellRight">
@@ -37,23 +47,15 @@
37
47
  __scoreRows__
38
48
  </tbody>
39
49
  </table>
40
- <h2>Score components</h2>
41
- __scoreComponents__
42
50
  <h2>Discussion</h2>
43
- <p>The test assumes that the user, noticing an accessibility issue on the page, will look for a link named <q>Accessibility</q>, <q>Accessibility Statement</q>, or the like. The user will click that link, which will open an accessibility page, whose title and first heading will identify it as such. On that page, the user will expect links that let the user contact the site owner to report the issue. Ideally, there will be an email link and a telephone link, both identifiable as accessibility-related. The more completely all these expectations are fulfilled, the greater the awarded score.</p>
44
- <p>Here, in brief, is how <code>spA11yMessage</code> computes a score for a page.</p>
45
- <p>Each score component begins as 0.</p>
51
+ <p>The components of the total score are:</p>
46
52
  <ul>
47
- <li>The <code>page</code> score becomes 2 if the page can be loaded. If it cannot, the test ends.</li>
48
- <li>The <code>a11yLink</code> score becomes 2 if there is a clickable link on the page whose text content includes <q>accessibility</q> (case-insensitively). If there is no such link, the test ends.</li>
49
- <li>The <code>title</code> score becomes 2 if the link opens a page whose title includes <q>accessibility</q> (case-insensitively).</li>
50
- <li>The <code>heading</code> score becomes 1 if that page has exactly 1 first-level heading.</li>
51
- <li>If the <code>heading</code> score is 1, it becomes 2 if that heading has text content that includes <q>accessibility</q> (case-insensitively).</li>
52
- <li>The <code>mailLink</code> score increases by 1 if that page contains a link with an email destination (i.e. an <code>href</code> with a value starting with <code>mailto:</code>).</li>
53
- <li>The <code>mailLink</code> score increases by 2 more if one of the email links has text content that includes <q>accessibility</q> (case-insensitively).</li>
54
- <li>If none of the email links has text content including <q>accessibility</q> but <q>accessibility</q> appears in the text content of the parent of any of the email links, the <code>mailLink</code> score increases by 1 more.</li>
55
- <li>The same computations as for the <code>mailLink</code> score are performed on links with telephone destinations (i.e. an <code>href</code> with a value starting with <code>tel:</code>), to determine the <code>telLink</code> score.</li>
56
- <li>The total score is the sum of the above scores.</li>
53
+ <li><code>page</code>: Can the page be visited?</li>
54
+ <li><code>a11yLink</code>: Does the page have a functioning <q>accessibility</q> link?</li>
55
+ <li><code>title</code>: Is the page to which the link goes titled to show it is about accessibility?</li>
56
+ <li><code>heading</code>: Does that page (the accessibility page) have a top heading indicating the page is about accessibility?</li>
57
+ <li><code>mailLink</code>: Does the accessibility page have a link to send email about accessibility?.</li>
58
+ <li><code>telLink</code>: Does the accessibility page have a link to make a telephone call about accessibility?</li>
57
59
  </ul>
58
60
  <p>The precise rules of <code>spA11yMessage</code> are found in the <a href="https://github.com/jrpool/testilo/blob/main/procs/score/spA11yMessage.js">code itself</a>.</p>
59
61
  <h2>Report</h2>
@@ -45,68 +45,11 @@ exports.makeQuery = (report, query) => {
45
45
  return;
46
46
  }
47
47
  }
48
- const {groupDetails, summary} = score;
49
- const {total, groups} = summary;
50
- if (typeof total === 'number') {
51
- query.totalScore = total;
52
- }
53
- else {
54
- console.log('ERROR: missing or invalid total score');
55
- return;
56
- }
57
- // Add the total to the query.
58
- const scoreRows = [getScoreRow('total', summary(['total']))];
59
- // Add the group rows of the score-summary table to the query.
60
- groups.forEach(group => {
61
- scoreRows.push(getScoreRow(`${group.groupName}`, group.score));
48
+ // Add the score data to the query.
49
+ const scoreRows = [];
50
+ Object.keys(score).forEach(scoreName => {
51
+ scoreRows.push(getScoreRow(scoreName, score[scoreName]));
62
52
  });
53
+ query.totalScore = score.total;
63
54
  query.scoreRows = scoreRows.join(innerJoiner);
64
- // If the score has any special components:
65
- const scoredSpecialIDs = specialComponentIDs.filter(item => summary[item]);
66
- if (scoredSpecialIDs.length) {
67
- // Add paragraphs about them for the issue summary to the query.
68
- const specialPs = [];
69
- scoredSpecialIDs.forEach(id => {
70
- specialPs.push(`${getSpecialPStart(summary, id)} ${specialMessages[id]}`);
71
- });
72
- query.specialSummary = specialPs.join(joiner);
73
- }
74
- // Otherwise, i.e. if the score has no special components:
75
- else {
76
- // Add a paragraph stating this for the issue summary to the query.
77
- query.specialSummary = '<p>No special issues contributed to the score.</p>'
78
- }
79
- // If the score has any classified issues as components:
80
- if (groups.length) {
81
- // Add paragraphs about them for the special summary to the query.
82
- const groupSummaryItems = [];
83
- groups.forEach(group => {
84
- const {groupName, score} = group;
85
- const groupP = `<p><span class="componentID">${groupName}</span>: Score ${score}. Issues reported by tests in this category:</p>`;
86
- const groupListItems = [];
87
- const groupData = groupDetails.groups[groupName];
88
- const packageIDs = Object.keys(groupData);
89
- packageIDs.forEach(packageID => {
90
- const testIDs = Object.keys(groupData[packageID]);
91
- testIDs.forEach(testID => {
92
- const testData = groupData[packageID][testID];
93
- const {score, what} = testData;
94
- const listItem = `<li>Package <code>${packageID}</code>, test <code>${testID}</code>, score ${score} (${what})</li>`;
95
- groupListItems.push(listItem);
96
- });
97
- });
98
- const groupList = [
99
- '<ul>',
100
- groupListItems.join('\n '),
101
- '</ul>'
102
- ].join(joiner);
103
- groupSummaryItems.push(groupP, groupList);
104
- });
105
- query.groupSummary = groupSummaryItems.join(joiner);
106
- }
107
- // Otherwise, i.e. if the score has no classified issues as components:
108
- else {
109
- // Add a paragraph stating this for the group summary to the query.
110
- query.groupSummary = '<p>No classified issues contributed to the score.</p>'
111
- }
112
55
  };
@@ -9,7 +9,7 @@
9
9
 
10
10
  This proc computes a score that is intended to represent how accessibly a web page offers
11
11
  a user an opportunity to report an accessibility issue about that page. Scores can range
12
- from 0 to 16.
12
+ from perfect 0 to 16.
13
13
  */
14
14
 
15
15
  // CONSTANTS
@@ -22,69 +22,75 @@ const scoreProcID = 'a11ymessage';
22
22
  // Scores the contact links of a type.
23
23
  const contactScorer = (result, score, type) => {
24
24
  const links = result.items;
25
- score[type] += 1;
26
- if (
27
- links.some(
28
- link => link.textContent.toLowerCase().includes('accessibility')
29
- )
30
- ) {
31
- score[type] += 2;
25
+ if (links.some(
26
+ link => link.textContent.toLowerCase().includes('accessibility')
27
+ )) {
28
+ score[type] -= 3;
32
29
  }
33
- else if (
34
- links.some(
35
- link => link.parentTextContent.toLowerCase().includes('accessibility')
36
- )
37
- ) {
38
- score[type] += 1;
30
+ else if (links.some(
31
+ link => link.parentTextContent.toLowerCase().includes('accessibility')
32
+ )) {
33
+ score[type] -= 2;
39
34
  }
40
35
  };
41
36
  // Scores a report.
42
37
  exports.scorer = async report => {
43
38
  const {acts} = report;
39
+ report.scoreProcID = scoreProcID;
44
40
  report.score = {
45
- page: 0,
46
- a11yLink: 0,
47
- title: 0,
48
- heading: 0,
49
- mailLink: 0,
50
- telLink: 0,
51
- total: 0
41
+ page: 3,
42
+ a11yLink: 4,
43
+ title: 3,
44
+ heading: 3,
45
+ mailLink: 3,
46
+ telLink: 3
52
47
  };
53
48
  const {score} = report;
54
49
  if (Array.isArray(acts)) {
55
50
  // Act 1: page loads.
56
51
  if (acts[1].result.startsWith('http')) {
57
- score.page = 2;
52
+ score.page -= 2;
53
+ if (acts[1].endTime - acts[1].startTime < 2500) {
54
+ score.page -= 1;
55
+ }
58
56
  // Act 2: accessibility link exists and loads promptly.
59
- if (acts[2].result.move === 'clicked') {
60
- score.a11yLink = 2;
61
- const loadScore = ['incomplete', 'loaded', 'idle'].indexOf(acts[2].result.loadState);
62
- if (loadScore > -1) {
63
- score.a11yLink += loadScore;
64
- }
65
- // Act 3: next page has an accessibility title.
66
- const act3Result = acts[3].result;
67
- if (act3Result && act3Result.toLowerCase().includes('accessibility')) {
68
- score.title = 2;
69
- // Act 4: page has exactly 1 h1 heading.
70
- const act4Result = acts[4].result;
71
- if (act4Result && act4Result.total === 1) {
72
- score.heading = 1;
73
- // Act 4: h1 is an accessibility heading.
74
- if (act4Result.items[0].textContent.toLowerCase().includes('accessibility')) {
75
- score.heading += 1;
57
+ const {result} = acts[2];
58
+ // If a link with text content including accessibility was found:
59
+ if (result.found) {
60
+ score.a11yLink -= 2;
61
+ // If it was clickable and the resulting load finished:
62
+ if (result.success) {
63
+ score.a11yLink -= 1;
64
+ // If the navigation and load took less than 1.5 seconds:
65
+ if (acts[2].endTime - acts[2].startTime < 1500) {
66
+ score.a11yLink -= 1;
67
+ }
68
+ // Act 3: next page has an accessibility title.
69
+ let {result} = acts[3];
70
+ if (result && result.success) {
71
+ score.title -= 1;
72
+ if (result.title.toLowerCase().includes('accessibility')) {
73
+ score.title -= 2;
76
74
  }
77
75
  }
78
- }
79
- // Act 5: page has accessibility email and telephone links.
80
- const act5Result = acts[5].result;
81
- if (act5Result.total) {
82
- contactScorer(act5Result, score, 'mailLink');
83
- }
84
- // Act 6: page has accessibility email and telephone links.
85
- const act6Result = acts[6].result;
86
- if (act6Result.total) {
87
- contactScorer(act6Result, score, 'telLink');
76
+ // Act 4: page has 1 h1 heading, and it is about accessibility.
77
+ result = acts[4].result;
78
+ if (result && result.total === 1) {
79
+ score.heading -= 1;
80
+ if (result.items[0].textContent.toLowerCase().includes('accessibility')) {
81
+ score.heading -= 2;
82
+ }
83
+ }
84
+ // Act 5: page has an accessibility email link.
85
+ result = acts[5].result;
86
+ if (result.total) {
87
+ contactScorer(result, score, 'mailLink');
88
+ }
89
+ // Act 6: page has accessibility telephone link.
90
+ result = acts[6].result;
91
+ if (result.total) {
92
+ contactScorer(result, score, 'telLink');
93
+ }
88
94
  }
89
95
  }
90
96
  }