testaro 4.3.3 → 4.5.1

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/commands.js CHANGED
@@ -1,7 +1,7 @@
1
1
  exports.commands = {
2
2
  etc: {
3
3
  button: [
4
- 'Click a button',
4
+ 'Click a button or submit input',
5
5
  {
6
6
  which: [true, 'string', 'hasLength', 'substring of button text'],
7
7
  index: [false, 'number', '', 'index among matches if not 0'],
@@ -94,14 +94,14 @@ exports.commands = {
94
94
  {
95
95
  which: [true, 'string', 'hasLength', 'substring of select-list text'],
96
96
  index: [false, 'number', '', 'index among matches if not 0'],
97
- what: [true, 'string', 'hasLength', 'substring of option text']
97
+ what: [true, 'string', 'hasLength', 'substring of option text content']
98
98
  }
99
99
  ],
100
100
  state: [
101
101
  'Wait until the page reaches a load state',
102
102
  {
103
- which: [true, 'string', 'hasLength', '“loaded” or “idle”'],
104
- what: [false, 'string', 'isState', 'comment']
103
+ which: [true, 'string', 'isState', '“loaded” or “idle”'],
104
+ what: [false, 'string', 'hasLength', 'comment']
105
105
  }
106
106
  ],
107
107
  tenonRequest: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "4.3.3",
3
+ "version": "4.5.1",
4
4
  "description": "Automation of accessibility testing",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/run.js CHANGED
@@ -12,12 +12,13 @@ const {commands} = require('./commands');
12
12
  const debug = process.env.DEBUG === 'true';
13
13
  // Set WAITS environment variable to a positive number to insert delays (in ms).
14
14
  const waits = Number.parseInt(process.env.WAITS) || 0;
15
+ const urlInject = process.env.URL_INJECT || 'yes';
15
16
  // CSS selectors for targets of moves.
16
17
  const moves = {
17
- button: 'button',
18
+ button: 'button, [role=button], input[type=submit]',
18
19
  checkbox: 'input[type=checkbox]',
19
20
  focus: true,
20
- link: 'a',
21
+ link: 'a, [role=link]',
21
22
  radio: 'input[type=radio]',
22
23
  select: 'select',
23
24
  text: 'input[type=text]'
@@ -255,7 +256,7 @@ const launch = async typeName => {
255
256
  // Make its console messages appear in the Playwright console.
256
257
  page.on('console', msg => {
257
258
  const msgText = msg.text();
258
- console.log(msgText);
259
+ console.log(`[${msgText}]`);
259
260
  logCount++;
260
261
  logSize += msgText.length;
261
262
  const msgLC = msgText.toLowerCase();
@@ -290,11 +291,20 @@ const textOf = async (page, element) => {
290
291
  if (['A', 'BUTTON', 'INPUT', 'SELECT'].includes(tagName)) {
291
292
  // Return its visible labels, descriptions, and legend if the first input in a fieldset.
292
293
  totalText = await page.evaluate(element => {
293
- const {tagName} = element;
294
- const ownText = ['A', 'BUTTON'].includes(tagName) ? element.textContent : '';
294
+ const {tagName, ariaLabel} = element;
295
+ let ownText = '';
296
+ if (['A', 'BUTTON'].includes(tagName)) {
297
+ ownText = element.textContent;
298
+ }
299
+ else if (tagName === 'INPUT' && element.type === 'submit') {
300
+ ownText = element.value;
301
+ }
295
302
  // HTML link elements have no labels property.
296
303
  const labels = tagName !== 'A' ? Array.from(element.labels) : [];
297
304
  const labelTexts = labels.map(label => label.textContent);
305
+ if (ariaLabel) {
306
+ labelTexts.push(ariaLabel);
307
+ }
298
308
  const refIDs = new Set([
299
309
  element.getAttribute('aria-labelledby') || '',
300
310
  element.getAttribute('aria-describedby') || ''
@@ -362,69 +372,79 @@ const textOf = async (page, element) => {
362
372
  return null;
363
373
  }
364
374
  };
365
- // Returns an element case-insensitively matching a text.
375
+ // Returns an element of a type case-insensitively matching a text.
366
376
  const matchElement = async (page, selector, matchText, index = 0) => {
367
- // If the page still exists:
368
- if (page) {
369
- // Wait 2 seconds until the body contains any text to be matched.
370
- const slimText = debloat(matchText);
371
- const bodyText = await page.textContent('body');
372
- const slimBody = debloat(bodyText);
373
- const textInBodyJSHandle = await page.waitForFunction(
374
- args => {
375
- const matchText = args[0];
376
- const bodyText = args[1];
377
- return ! matchText || bodyText.includes(matchText);
378
- },
379
- [slimText, slimBody],
380
- {timeout: 2000}
381
- )
382
- .catch(async error => {
383
- console.log(`ERROR: text to match not in body (${error.message})`);
384
- });
385
- // If there is no text to be matched or the body contained it:
386
- if (textInBodyJSHandle) {
387
- const lcText = matchText ? matchText.toLowerCase() : '';
388
- // Identify the selected elements.
389
- const selections = await page.$$(`body ${selector}`);
377
+ if (matchText) {
378
+ // If the page still exists:
379
+ if (page) {
380
+ const slimText = debloat(matchText);
381
+ // Identify the elements of the specified type.
382
+ const selections = await page.$$(selector);
390
383
  // If there are any:
391
384
  if (selections.length) {
392
385
  // If there are enough to make a match possible:
393
386
  if (index < selections.length) {
394
- // Return the nth one including any specified text, or the count of candidates if none.
395
- const elementTexts = [];
396
- let nth = 0;
397
- for (const element of selections) {
398
- const elementText = await textOf(page, element);
399
- elementTexts.push(elementText);
400
- if ((! lcText || elementText.includes(lcText)) && nth++ === index) {
401
- return element;
387
+ // Return the specified one, if any.
388
+ let matchCount = 0;
389
+ const selectionTexts = [];
390
+ for (const selection of selections) {
391
+ const selectionText = await textOf(page, selection);
392
+ selectionTexts.push(selectionText);
393
+ if (selectionText.includes(slimText)) {
394
+ if (matchCount++ === index) {
395
+ return {
396
+ success: true,
397
+ matchingElement: selection
398
+ };
399
+ }
402
400
  }
403
401
  }
404
- return elementTexts;
402
+ // None satisfied the specifications, so return a failure.
403
+ return {
404
+ success: false,
405
+ error: 'exhausted',
406
+ message: `Text found in only ${matchCount} (not ${index + 1}) of ${selections.length}`,
407
+ candidateTexts: selectionTexts
408
+ };
405
409
  }
406
- // Otherwise, i.e. if there are too few to make a match possible:
410
+ // Otherwise, i.e. if there are too few such elements to make a match possible:
407
411
  else {
408
- // Return the count of candidates.
409
- return selections.length;
412
+ // Return a failure.
413
+ return {
414
+ success: false,
415
+ error: 'fewer',
416
+ message: `Count of '${selector}' elements only ${selections.length}`
417
+ };
410
418
  }
411
419
  }
412
- // Otherwise, i.e. if there are no selected elements, return 0.
420
+ // Otherwise, i.e. if there are no elements of the specified type:
413
421
  else {
414
- return 0;
422
+ // Return a failure.
423
+ return {
424
+ success: false,
425
+ error: 'none',
426
+ message: `No '${selector}' elements found`
427
+ };
415
428
  }
416
429
  }
417
- // Otherwise, i.e. if the body did not contain it:
430
+ // Otherwise, i.e. if the page no longer exists:
418
431
  else {
419
- // Return the failure.
420
- return -1;
432
+ // Return a failure.
433
+ return {
434
+ success: false,
435
+ error: 'gone',
436
+ message: 'Page gone'
437
+ };
421
438
  }
422
439
  }
423
- // Otherwise, i.e. if the page no longer exists:
440
+ // Otherwise, i.e. if no text was specified:
424
441
  else {
425
- // Return null.
426
- console.log('ERROR: Page gone');
427
- return null;
442
+ // Return a failure.
443
+ return {
444
+ success: false,
445
+ error: 'text',
446
+ message: 'No text specified'
447
+ };
428
448
  }
429
449
  };
430
450
  // Returns a string with any final slash removed.
@@ -495,8 +515,9 @@ const visit = async (act, page, isStrict) => {
495
515
  // If the visit fails:
496
516
  if (response === 'error') {
497
517
  // Give up.
498
- console.log(`ERROR: Visits to ${requestedURL} failed`);
499
- act.result = `ERROR: Visit to ${requestedURL} failed`;
518
+ const errorMsg = `ERROR: Attemts to visit ${requestedURL} failed`;
519
+ console.log(errorMsg);
520
+ act.result = errorMsg;
500
521
  await page.goto('about:blank')
501
522
  .catch(error => {
502
523
  console.log(`ERROR: Navigation to blank page failed (${error.message})`);
@@ -549,13 +570,21 @@ const isTrue = (object, specs) => {
549
570
  }
550
571
  return [actual, satisfied];
551
572
  };
552
- // Adds an error result to an act.
573
+ // Adds a wait error result to an act.
553
574
  const waitError = (page, act, error, what) => {
554
575
  console.log(`ERROR waiting for ${what} (${error.message})`);
555
576
  act.result = {url: page.url()};
556
577
  act.result.error = `ERROR waiting for ${what}`;
557
578
  return false;
558
579
  };
580
+ // Waits.
581
+ const wait = ms => {
582
+ return new Promise(resolve => {
583
+ setTimeout(() => {
584
+ resolve('');
585
+ }, ms);
586
+ });
587
+ };
559
588
  // Recursively performs the acts in a report.
560
589
  const doActs = async (report, actIndex, page) => {
561
590
  const {acts} = report;
@@ -590,12 +619,12 @@ const doActs = async (report, actIndex, page) => {
590
619
  if (truth[1]) {
591
620
  // If the performance of commands is to stop:
592
621
  if (act.jump === 0) {
593
- // Set the command index to cause a stop.
622
+ // Stop.
594
623
  actIndex = -2;
595
624
  }
596
625
  // Otherwise, if there is a numerical jump:
597
626
  else if (act.jump) {
598
- // Set the command index accordingly.
627
+ // Set the act index accordingly.
599
628
  actIndex += act.jump - 1;
600
629
  }
601
630
  // Otherwise, if there is a named next command:
@@ -623,58 +652,65 @@ const doActs = async (report, actIndex, page) => {
623
652
  else if (act.type === 'wait') {
624
653
  const {what, which} = act;
625
654
  console.log(`>> for ${what} to include “${which}”`);
626
- // Wait 5 seconds for the specified text to appear in the specified place.
627
- let successJSHandle;
655
+ // Wait 5 or 10 seconds for the specified text, and quit if it does not appear.
628
656
  if (act.what === 'url') {
629
- successJSHandle = await page.waitForFunction(
630
- text => document.URL.includes(text), act.which, {timeout: 5000}
631
- )
632
- .catch(error => waitError(page, act, error, 'URL'));
657
+ await page.waitForURL(act.which, {timeout: 15000})
658
+ .catch(error => {
659
+ actIndex = -2;
660
+ waitError(page, act, error, 'URL');
661
+ });
633
662
  }
634
663
  else if (act.what === 'title') {
635
- successJSHandle = await page.waitForFunction(
636
- text => document.title.includes(text), act.which, {timeout: 5000}
664
+ await page.waitForFunction(
665
+ text => document && document.title && document.title.includes(text),
666
+ act.which,
667
+ {
668
+ polling: 1000,
669
+ timeout: 5000
670
+ }
637
671
  )
638
- .catch(error => waitError(page, act, error, 'title'));
672
+ .catch(error => {
673
+ actIndex = -2;
674
+ waitError(page, act, error, 'title');
675
+ });
639
676
  }
640
677
  else if (act.what === 'body') {
641
- successJSHandle = await page.waitForFunction(
642
- matchText => {
643
- const innerText = document.body.innerText;
644
- return innerText.includes(matchText);
645
- }, which, {timeout: 5000}
678
+ await page.waitForFunction(
679
+ text => document && document.body && document.body.innerText.includes(text),
680
+ act.which,
681
+ {
682
+ polling: 2000,
683
+ timeout: 10000
684
+ }
646
685
  )
647
- .catch(error => waitError(page, act, error, 'body'));
686
+ .catch(async error => {
687
+ actIndex = -2;
688
+ waitError(page, act, error, 'body');
689
+ });
648
690
  }
649
- if (successJSHandle) {
691
+ // If the text was found:
692
+ if (actIndex > -2) {
693
+ // Add this to the report.
650
694
  act.result = {url: page.url()};
651
695
  if (act.what === 'title') {
652
696
  act.result.title = await page.title();
653
697
  }
654
- await page.waitForLoadState('networkidle', {timeout: 10000})
655
- .catch(error => {
656
- console.log(`ERROR waiting for stability after ${act.what} (${error.message})`);
657
- act.result.error = `ERROR waiting for stability after ${act.what}`;
658
- });
659
698
  }
660
699
  }
661
700
  // Otherwise, if the act is a wait for a state:
662
701
  else if (act.type === 'state') {
663
- // If the state is valid:
702
+ // Wait for it, and quit if it does not appear.
664
703
  const stateIndex = ['loaded', 'idle'].indexOf(act.which);
665
- if (stateIndex !== -1) {
666
- // Wait for it.
667
- await page.waitForLoadState(
668
- ['domcontentloaded', 'networkidle'][stateIndex], {timeout: [10000, 5000][stateIndex]}
669
- )
670
- .catch(error => {
671
- console.log(`ERROR waiting for page to be ${act.which} (${error.message})`);
672
- act.result = `ERROR waiting for page to be ${act.which}`;
673
- });
674
- }
675
- else {
676
- console.log('ERROR: invalid state');
677
- act.result = 'ERROR: invalid state';
704
+ await page.waitForLoadState(
705
+ ['domcontentloaded', 'networkidle'][stateIndex], {timeout: [10000, 5000][stateIndex]}
706
+ )
707
+ .catch(error => {
708
+ console.log(`ERROR waiting for page to be ${act.which} (${error.message})`);
709
+ act.result = `ERROR waiting for page to be ${act.which}`;
710
+ actIndex = -2;
711
+ });
712
+ if (actIndex > -2) {
713
+ act.result = `Page became ${act.which}`;
678
714
  }
679
715
  }
680
716
  // Otherwise, if the act is a page switch:
@@ -864,48 +900,34 @@ const doActs = async (report, actIndex, page) => {
864
900
  // Otherwise, if the act is a move:
865
901
  else if (moves[act.type]) {
866
902
  const selector = typeof moves[act.type] === 'string' ? moves[act.type] : act.what;
867
- // Identify the element to perform the move on.
868
- const whichElement = await matchElement(page, selector, act.which || '', act.index);
869
- // If there were enough candidates but no text match:
870
- if (Array.isArray(whichElement)) {
871
- // Add the result to the act.
872
- act.result = {
873
- candidateCount: whichElement.length,
874
- error: 'ERROR: no element with matching text found',
875
- candidateTexts: whichElement
876
- };
877
- }
878
- // Otherwise, if the body did not contain the text:
879
- else if (whichElement === -1) {
880
- // Add the failure to the act.
881
- act.result = 'ERROR: body did not contain text to match';
882
- }
883
- // Otherwise, if there were not enough candidates:
884
- else if (typeof whichElement === 'number') {
885
- // Add the failure to the act.
886
- act.result = {
887
- candidateCount: whichElement,
888
- error: 'ERROR: too few such elements to allow a match'
889
- };
903
+ // Wait 10 seconds until the element to perform the move on is identified.
904
+ let matchResult = {success: false};
905
+ let tries = 0;
906
+ while (tries++ < 5 && ! matchResult.success) {
907
+ matchResult = await matchElement(page, selector, act.which || '', act.index);
908
+ if (! matchResult.success) {
909
+ await wait(2000);
910
+ }
890
911
  }
891
- // Otherwise, if a match was found:
892
- else if (whichElement !== null) {
912
+ // If a match was found:
913
+ if (matchResult.success) {
914
+ const {matchingElement} = matchResult;
893
915
  // If the move is a button click, perform it.
894
916
  if (act.type === 'button') {
895
- await whichElement.click({timeout: 3000});
917
+ await matchingElement.click({timeout: 3000});
896
918
  act.result = 'clicked';
897
919
  }
898
920
  // Otherwise, if it is checking a radio button or checkbox, perform it.
899
921
  else if (['checkbox', 'radio'].includes(act.type)) {
900
- await whichElement.waitForElementState('stable', {timeout: 2000})
922
+ await matchingElement.waitForElementState('stable', {timeout: 2000})
901
923
  .catch(error => {
902
924
  console.log(`ERROR waiting for stable ${act.type} (${error.message})`);
903
925
  act.result = `ERROR waiting for stable ${act.type}`;
904
926
  });
905
927
  if (! act.result) {
906
- const isEnabled = await whichElement.isEnabled();
928
+ const isEnabled = await matchingElement.isEnabled();
907
929
  if (isEnabled) {
908
- await whichElement.check({
930
+ await matchingElement.check({
909
931
  force: true,
910
932
  timeout: 2000
911
933
  })
@@ -926,28 +948,53 @@ const doActs = async (report, actIndex, page) => {
926
948
  }
927
949
  // Otherwise, if it is focusing the element, perform it.
928
950
  else if (act.type === 'focus') {
929
- await whichElement.focus({timeout: 2000});
951
+ await matchingElement.focus({timeout: 2000});
930
952
  act.result = 'focused';
931
953
  }
932
954
  // Otherwise, if it is clicking a link, perform it.
933
955
  else if (act.type === 'link') {
934
- const href = await whichElement.getAttribute('href');
935
- const target = await whichElement.getAttribute('target');
936
- await whichElement.click({timeout: 2000});
937
- act.result = {
938
- href: href || 'NONE',
939
- target: target || 'NONE',
940
- move: 'clicked'
941
- };
956
+ const href = await matchingElement.getAttribute('href');
957
+ const target = await matchingElement.getAttribute('target');
958
+ await matchingElement.click({timeout: 2000})
959
+ .catch(async () => {
960
+ console.log('ERROR: First attempt to click link timed out');
961
+ await matchingElement.click({
962
+ force: true,
963
+ timeout: 10000
964
+ })
965
+ .catch(() => {
966
+ actIndex = -2;
967
+ console.log('ERROR: Second (forced) attempt to click link timed out');
968
+ act.result = 'ERROR: Normal and forced click attempts timed out';
969
+ });
970
+ });
971
+ if (actIndex > -2) {
972
+ act.result = {
973
+ href: href || 'NONE',
974
+ target: target || 'NONE',
975
+ move: 'clicked'
976
+ };
977
+ }
942
978
  }
943
979
  // Otherwise, if it is selecting an option in a select list, perform it.
944
980
  else if (act.type === 'select') {
945
- await whichElement.selectOption({what: act.what});
946
- const optionText = await whichElement.$eval(
947
- 'option:selected', el => el.textContent
948
- );
981
+ const options = await matchingElement.$$('option');
982
+ let optionText = '';
983
+ if (options && Array.isArray(options) && options.length) {
984
+ const optionTexts = [];
985
+ for (const option of options) {
986
+ const optionText = await option.textContent();
987
+ optionTexts.push(optionText);
988
+ }
989
+ const matchTexts = optionTexts.map((text, index) => text.includes(act.what) ? index : -1);
990
+ const index = matchTexts.filter(text => text > -1)[act.index || 0];
991
+ if (index !== undefined) {
992
+ await matchingElement.selectOption({index});
993
+ optionText = optionTexts[index];
994
+ }
995
+ }
949
996
  act.result = optionText
950
- ? `&ldquo;${optionText}}&rdquo; selected`
997
+ ? `“${optionText} selected`
951
998
  : 'ERROR: option not found';
952
999
  }
953
1000
  // Otherwise, if it is entering text on the element:
@@ -961,19 +1008,24 @@ const doActs = async (report, actIndex, page) => {
961
1008
  what = what.replace(/__[A-Z]+__/, envValue);
962
1009
  }
963
1010
  // Enter the text.
964
- await whichElement.type(act.what);
1011
+ await matchingElement.type(act.what);
965
1012
  report.presses += act.what.length;
966
1013
  act.result = 'entered';
967
1014
  }
968
1015
  // Otherwise, i.e. if the move is unknown, add the failure to the act.
969
1016
  else {
970
1017
  // Report the error.
971
- act.result = 'ERROR: move unknown';
1018
+ const report = 'ERROR: move unknown';
1019
+ act.result = report;
1020
+ console.log(report);
972
1021
  }
973
1022
  }
974
- // Otherwise, i.e. if the page was gone:
1023
+ // Otherwise, i.e. if no match was found:
975
1024
  else {
976
- act.result = 'ERROR: page gone, so matching element not found';
1025
+ // Stop.
1026
+ act.result = matchResult;
1027
+ console.log('ERROR: Specified element not found');
1028
+ actIndex = -2;
977
1029
  }
978
1030
  }
979
1031
  // Otherwise, if the act is a keypress:
@@ -1161,7 +1213,9 @@ const doActs = async (report, actIndex, page) => {
1161
1213
  // Otherwise, i.e. if the command is invalid:
1162
1214
  else {
1163
1215
  // Add an error result to the act.
1164
- act.result = `ERROR: Invalid command of type ${act.type}`;
1216
+ const errorMsg = `ERROR: Invalid command of type ${act.type}`;
1217
+ act.result = errorMsg;
1218
+ console.log(errorMsg);
1165
1219
  }
1166
1220
  // Perform the remaining acts.
1167
1221
  await doActs(report, actIndex + 1, page);
@@ -1247,8 +1301,10 @@ exports.handleRequest = async report => {
1247
1301
  report.timeStamp = report.id.replace(/-.+/, '');
1248
1302
  // Add the script commands to the report as its initial acts.
1249
1303
  report.acts = JSON.parse(JSON.stringify(report.script.commands));
1250
- // Inject url acts where necessary to undo DOM changes.
1251
- injectURLActs(report.acts);
1304
+ // Inject url acts where necessary to undo DOM changes, if specified.
1305
+ if (urlInject === 'yes') {
1306
+ injectURLActs(report.acts);
1307
+ }
1252
1308
  // Perform the acts, asynchronously adding to the log and report.
1253
1309
  await doScript(report);
1254
1310
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "a11yorgs",
3
- "what": "Pages expected to pass all accessibility tests",
3
+ "what": "Home pages of accessibility organizations",
4
4
  "hosts": [
5
5
  {
6
6
  "id": "iaap",
@@ -0,0 +1,63 @@
1
+ {
2
+ "id": "digicert",
3
+ "what": "moves on DigiCert website",
4
+ "strict": true,
5
+ "commands": [
6
+ {
7
+ "type": "launch",
8
+ "which": "chromium",
9
+ "what": "Chrome"
10
+ },
11
+ {
12
+ "type": "url",
13
+ "which": "https://order.digicert.com/",
14
+ "what": "DigiCert"
15
+ },
16
+ {
17
+ "type": "focus",
18
+ "what": "button",
19
+ "which": "Choose payment plan"
20
+ },
21
+ {
22
+ "type": "press",
23
+ "which": "ArrowDown",
24
+ "again": 2,
25
+ "what": "Choose 2-year plan"
26
+ },
27
+ {
28
+ "type": "press",
29
+ "which": "Enter",
30
+ "what": "Commit to choose 2-year plan"
31
+ },
32
+ {
33
+ "type": "button",
34
+ "which": "I don't have",
35
+ "what": "I don’t have my CSR"
36
+ },
37
+ {
38
+ "type": "select",
39
+ "which": "Server app details",
40
+ "what": "NGINX"
41
+ },
42
+ {
43
+ "type": "text",
44
+ "which": "main website",
45
+ "what": "jpdev.pro"
46
+ },
47
+ {
48
+ "type": "button",
49
+ "which": "Continue",
50
+ "what": "Continue"
51
+ },
52
+ {
53
+ "type": "wait",
54
+ "what": "url",
55
+ "which": "step2"
56
+ },
57
+ {
58
+ "type": "test",
59
+ "which": "bulk",
60
+ "what": "count of visible elements"
61
+ }
62
+ ]
63
+ }
package/tests/aatt.js CHANGED
@@ -53,13 +53,23 @@ exports.reporter = async (page, waitLong) => {
53
53
  }
54
54
  catch (error) {
55
55
  console.log(`ERROR processing AATT report (${error.message})`);
56
- return {result: 'ERROR processing AATT report'};
56
+ return {
57
+ result: {
58
+ prevented: true,
59
+ error: 'ERROR processing AATT report'
60
+ }
61
+ };
57
62
  }
58
63
  }
59
64
  // Otherwise, i.e. if the result did not arrive within the time limit:
60
65
  else {
61
66
  // Report the failure.
62
67
  console.log('ERROR: getting report took too long');
63
- return {result: 'ERROR: getting AATT report took too long'};
68
+ return {
69
+ result: {
70
+ prevented: true,
71
+ error: 'ERROR: getting AATT report took too long'
72
+ }
73
+ };
64
74
  }
65
75
  };
package/tests/alfa.js CHANGED
@@ -4,11 +4,13 @@
4
4
  */
5
5
 
6
6
  // IMPORTS
7
+
7
8
  const {Audit} = require('@siteimprove/alfa-act');
8
9
  const {Scraper} = require('@siteimprove/alfa-scraper');
9
10
  const alfaRules = require('@siteimprove/alfa-rules');
10
11
 
11
12
  // FUNCTIONS
13
+
12
14
  // Conducts and reports an alfa test.
13
15
  exports.reporter = async page => {
14
16
  // Get the document containing the summaries of the alfa rules.
@@ -104,6 +106,11 @@ exports.reporter = async page => {
104
106
  }
105
107
  catch(error) {
106
108
  console.log(`ERROR: navigation to URL timed out (${error})`);
107
- return {result: {error: 'ERROR: navigation to URL timed out'}};
109
+ return {
110
+ result: {
111
+ prevented: true,
112
+ error: 'ERROR: navigation to URL timed out'
113
+ }
114
+ };
108
115
  }
109
116
  };
package/tests/axe.js CHANGED
@@ -16,10 +16,11 @@ exports.reporter = async (page, withItems, rules = []) => {
16
16
  await injectAxe(page)
17
17
  .catch(error => {
18
18
  console.log(`ERROR: Axe injection failed (${error.message})`);
19
- data.result = 'ERROR: axe injection failed';
19
+ data.prevented = true;
20
+ data.error = 'ERROR: axe injection failed';
20
21
  });
21
22
  // If the injection succeeded:
22
- if (! data.result) {
23
+ if (! data.prevented) {
23
24
  // Get the data on the elements violating the specified axe-core rules.
24
25
  const axeOptions = {};
25
26
  if (rules.length) {
@@ -100,6 +101,7 @@ exports.reporter = async (page, withItems, rules = []) => {
100
101
  // Otherwise, i.e. if the test failed:
101
102
  else {
102
103
  // Report this.
104
+ data.prevented = true;
103
105
  data.error = 'ERROR: axe failed';
104
106
  console.log('ERROR: axe failed');
105
107
  }
package/tests/bulk.js CHANGED
@@ -12,6 +12,7 @@ exports.reporter = async page => {
12
12
  await page.waitForSelector('body', {timeout: 10000})
13
13
  .catch(error => {
14
14
  console.log(`ERROR (${error.message})`);
15
+ data.prevented = true;
15
16
  data.error = 'ERROR: bulk timed out';
16
17
  return {result: data};
17
18
  });
package/tests/focOp.js CHANGED
@@ -127,6 +127,7 @@ exports.reporter = async (page, withItems) => {
127
127
  }, withItems)
128
128
  .catch(error => {
129
129
  console.log(`ERROR getting focOp data (${error.message})`);
130
+ data.prevented = true;
130
131
  });
131
132
  return {result: data};
132
133
  };
package/tests/hover.js CHANGED
@@ -212,6 +212,7 @@ exports.reporter = async (page, withItems) => {
212
212
  const triggers = await page.$$(selectors.join(', '))
213
213
  .catch(error => {
214
214
  console.log(`ERROR getting hover triggers (${error.message})`);
215
+ data.prevented = true;
215
216
  return [];
216
217
  });
217
218
  // If they number more than the sample size limit, sample them.
package/tests/ibm.js CHANGED
@@ -48,10 +48,12 @@ const report = (ibmReport, withItems) => {
48
48
  }
49
49
  }
50
50
  else {
51
+ data.prevented = true;
51
52
  data.error = 'ERROR: ibm test delivered no totals';
52
53
  }
53
54
  }
54
55
  else {
56
+ data.prevented = true;
55
57
  data.error = 'ERROR: ibm test delivered no report summary';
56
58
  }
57
59
  return data;
@@ -88,6 +90,7 @@ const doTest = async (content, withItems, timeLimit) => {
88
90
  else {
89
91
  console.log('ERROR: getting ibm test report took too long');
90
92
  return {
93
+ prevented: true,
91
94
  error: 'ERROR: getting ibm test report took too long'
92
95
  };
93
96
  }
@@ -100,12 +103,18 @@ exports.reporter = async (page, withItems, withNewContent) => {
100
103
  const timeLimit = 15;
101
104
  const typeContent = await page.content();
102
105
  result.content = await doTest(typeContent, withItems, timeLimit);
106
+ if (result.content.prevented) {
107
+ result.prevented = true;
108
+ }
103
109
  }
104
110
  // If a test with new content is to be performed:
105
111
  if ([true, undefined].includes(withNewContent)) {
106
112
  const timeLimit = 20;
107
113
  const typeContent = page.url();
108
114
  result.url = await doTest(typeContent, withItems, timeLimit);
115
+ if (result.content.prevented) {
116
+ result.prevented = true;
117
+ }
109
118
  }
110
119
  return {result};
111
120
  };
package/tests/labClash.js CHANGED
@@ -151,7 +151,10 @@ exports.reporter = async (page, withItems) => {
151
151
  }, withItems)
152
152
  .catch(error => {
153
153
  console.log(`ERROR: labClash failed (${error.message})`);
154
- const data = {error: 'ERROR: labClash failed'};
154
+ const data = {
155
+ prevented: true,
156
+ error: 'ERROR: labClash failed'
157
+ };
155
158
  return {result: data};
156
159
  });
157
160
  };
package/tests/motion.js CHANGED
@@ -110,6 +110,11 @@ exports.reporter = async (page, delay, interval, count) => {
110
110
  // Otherwise, i.e. if the shooting failed:
111
111
  else {
112
112
  // Return failure.
113
- return {result: {error: 'ERROR: screenshots failed'}};
113
+ return {
114
+ result: {
115
+ prevented: true,
116
+ error: 'ERROR: screenshots failed'
117
+ }
118
+ };
114
119
  }
115
120
  };
@@ -11,11 +11,6 @@ exports.reporter = async (page, withItems) => {
11
11
  return await page.$eval('body', (body, args) => {
12
12
  const withItems = args[0];
13
13
  const linkTypes = args[1];
14
- // Initialize the data to be returned.
15
- const data = {totals: {}};
16
- if (withItems) {
17
- data.items = {};
18
- }
19
14
  // Identify the settable style properties to be compared for all tag names.
20
15
  const mainStyles = [
21
16
  'borderStyle',
@@ -39,6 +34,15 @@ exports.reporter = async (page, withItems) => {
39
34
  const headingStyles = [
40
35
  'fontSize'
41
36
  ];
37
+ // Initialize the data to be returned.
38
+ const data = {
39
+ mainStyles,
40
+ headingStyles,
41
+ totals: {}
42
+ };
43
+ if (withItems) {
44
+ data.items = {};
45
+ }
42
46
  // Identify the heading tag names to be analyzed.
43
47
  const headingNames = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
44
48
  // Identify the other nonlink tag names to be analyzed.
package/tests/tabNav.js CHANGED
@@ -3,66 +3,71 @@
3
3
  This test reports nonstandard keyboard navigation among tab elements in tab lists.
4
4
  Standards are based on https://www.w3.org/TR/wai-aria-practices-1.1/#tabpanel.
5
5
  */
6
- exports.reporter = async (page, withItems) => {
7
- // Initialize a report.
8
- const data = {
9
- totals: {
10
- navigations: {
11
- all: {
6
+
7
+ // CONSTANTS
8
+
9
+ const data = {
10
+ totals: {
11
+ navigations: {
12
+ all: {
13
+ total: 0,
14
+ correct: 0,
15
+ incorrect: 0
16
+ },
17
+ specific: {
18
+ tab: {
12
19
  total: 0,
13
20
  correct: 0,
14
21
  incorrect: 0
15
22
  },
16
- specific: {
17
- tab: {
18
- total: 0,
19
- correct: 0,
20
- incorrect: 0
21
- },
22
- left: {
23
- total: 0,
24
- correct: 0,
25
- incorrect: 0
26
- },
27
- right: {
28
- total: 0,
29
- correct: 0,
30
- incorrect: 0
31
- },
32
- up: {
33
- total: 0,
34
- correct: 0,
35
- incorrect: 0
36
- },
37
- down: {
38
- total: 0,
39
- correct: 0,
40
- incorrect: 0
41
- },
42
- home: {
43
- total: 0,
44
- correct: 0,
45
- incorrect: 0
46
- },
47
- end: {
48
- total: 0,
49
- correct: 0,
50
- incorrect: 0
51
- }
23
+ left: {
24
+ total: 0,
25
+ correct: 0,
26
+ incorrect: 0
27
+ },
28
+ right: {
29
+ total: 0,
30
+ correct: 0,
31
+ incorrect: 0
32
+ },
33
+ up: {
34
+ total: 0,
35
+ correct: 0,
36
+ incorrect: 0
37
+ },
38
+ down: {
39
+ total: 0,
40
+ correct: 0,
41
+ incorrect: 0
42
+ },
43
+ home: {
44
+ total: 0,
45
+ correct: 0,
46
+ incorrect: 0
47
+ },
48
+ end: {
49
+ total: 0,
50
+ correct: 0,
51
+ incorrect: 0
52
52
  }
53
- },
54
- tabElements: {
55
- total: 0,
56
- correct: 0,
57
- incorrect: 0
58
- },
59
- tabLists: {
60
- total: 0,
61
- correct: 0,
62
- incorrect: 0
63
53
  }
54
+ },
55
+ tabElements: {
56
+ total: 0,
57
+ correct: 0,
58
+ incorrect: 0
59
+ },
60
+ tabLists: {
61
+ total: 0,
62
+ correct: 0,
63
+ incorrect: 0
64
64
  }
65
- };
65
+ }
66
+ };
67
+
68
+ // FUNCTIONS
69
+
70
+ exports.reporter = async (page, withItems) => {
66
71
  if (withItems) {
67
72
  data.tabElements = {
68
73
  incorrect: [],
@@ -182,6 +187,7 @@ exports.reporter = async (page, withItems) => {
182
187
  .catch(error => {
183
188
  console.log(`ERROR: could not get tag name (${error.message})`);
184
189
  found = false;
190
+ data.prevented = true;
185
191
  return 'ERROR: not found';
186
192
  });
187
193
  if (found) {
@@ -260,7 +266,10 @@ exports.reporter = async (page, withItems) => {
260
266
  if (! orientation) {
261
267
  orientation = 'horizontal';
262
268
  }
263
- if (orientation !== 'ERROR') {
269
+ if (orientation === 'ERROR') {
270
+ data.prevented = true;
271
+ }
272
+ else {
264
273
  const tabs = await firstTabList.$$('[role=tab]');
265
274
  // If the tablist contains at least 2 tab elements:
266
275
  if (tabs.length > 1) {
package/tests/tenon.js CHANGED
@@ -107,15 +107,19 @@ exports.reporter = async (tenonData, id) => {
107
107
  // Otherwise, if the test is still running after a wait for its status:
108
108
  else {
109
109
  // Report the test status.
110
- return {result: {
111
- error: 'ERROR: test status not completed',
112
- testStatus
113
- }};
110
+ return {
111
+ result: {
112
+ prevented: true,
113
+ error: 'ERROR: test status not completed',
114
+ testStatus
115
+ }
116
+ };
114
117
  }
115
118
  }
116
119
  else {
117
120
  return {
118
121
  result: {
122
+ prevented: true,
119
123
  error: 'ERROR: tenon authorization and test data incomplete'
120
124
  }
121
125
  };
package/tests/wave.js CHANGED
@@ -32,6 +32,7 @@ exports.reporter = async (page, reportType) => {
32
32
  }
33
33
  catch (error) {
34
34
  return resolve({
35
+ prevented: true,
35
36
  error: 'WAVE did not return JSON.',
36
37
  report
37
38
  });