testaro 5.7.6 → 5.8.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/run.js +116 -49
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "5.7.6",
3
+ "version": "5.8.0",
4
4
  "description": "Automation of accessibility testing",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/run.js CHANGED
@@ -70,7 +70,7 @@ const browserTypeNames = {
70
70
  'firefox': 'Firefox'
71
71
  };
72
72
  // Items that may be waited for.
73
- const waitables = ['url', 'title', 'body'];
73
+ const waitables = ['url', 'title', 'body', 'mailLink'];
74
74
  // Tenon data.
75
75
  const tenonData = {
76
76
  accessToken: '',
@@ -442,7 +442,7 @@ const textOf = async (page, element) => {
442
442
  return null;
443
443
  }
444
444
  };
445
- // Returns an element of a type case-insensitively matching a text.
445
+ // Returns an element of a type case-insensitively including a text.
446
446
  const matchElement = async (page, selector, matchText, index = 0) => {
447
447
  if (matchText) {
448
448
  // If the page still exists:
@@ -454,14 +454,18 @@ const matchElement = async (page, selector, matchText, index = 0) => {
454
454
  if (selections.length) {
455
455
  // If there are enough to make a match possible:
456
456
  if (index < selections.length) {
457
- // Return the specified one, if any.
457
+ // For each element of the specified type:
458
458
  let matchCount = 0;
459
459
  const selectionTexts = [];
460
460
  for (const selection of selections) {
461
+ // Add its text to the list of texts of such elements.
461
462
  const selectionText = await textOf(page, selection);
462
463
  selectionTexts.push(selectionText);
464
+ // If its text includes the specified text:
463
465
  if (selectionText.includes(slimText)) {
466
+ // If the count of such elements with such texts found so far is the specified count:
464
467
  if (matchCount++ === index) {
468
+ // Return it as the matching element.
465
469
  return {
466
470
  success: true,
467
471
  matchingElement: selection
@@ -661,7 +665,8 @@ const isTrue = (object, specs) => {
661
665
  // Adds a wait error result to an act.
662
666
  const waitError = (page, act, error, what) => {
663
667
  console.log(`ERROR waiting for ${what} (${error.message})`);
664
- act.result = {url: page.url()};
668
+ act.result.found = false;
669
+ act.result.url = page.url();
665
670
  act.result.error = `ERROR waiting for ${what}`;
666
671
  return false;
667
672
  };
@@ -754,49 +759,95 @@ const doActs = async (report, actIndex, page) => {
754
759
  // Otherwise, if the act is a wait for text:
755
760
  else if (act.type === 'wait') {
756
761
  const {what, which} = act;
757
- console.log(`>> for ${what} to include “${which}”`);
758
- // Wait 5 or 10 seconds for the specified text, and quit if it does not appear.
759
- if (act.what === 'url') {
760
- await page.waitForURL(act.which, {timeout: 15000})
761
- .catch(error => {
762
+ console.log(`>> ${what}`);
763
+ const result = act.result = {};
764
+ // Wait for the specified text, and quit if it does not appear.
765
+ if (what === 'url') {
766
+ try {
767
+ await page.waitForURL(which, {timeout: 15000});
768
+ result.found = true;
769
+ result.url = page.url();
770
+ }
771
+ catch(error) {
762
772
  actIndex = -2;
763
773
  waitError(page, act, error, 'URL');
764
- });
774
+ }
765
775
  }
766
- else if (act.what === 'title') {
767
- await page.waitForFunction(
768
- text => document && document.title && document.title.includes(text),
769
- act.which,
770
- {
771
- polling: 1000,
772
- timeout: 5000
773
- }
774
- )
775
- .catch(error => {
776
+ else if (what === 'title') {
777
+ try {
778
+ await page.waitForFunction(
779
+ text => document
780
+ && document.title
781
+ && document.title.toLowerCase().includes(text.toLowerCase()),
782
+ which,
783
+ {
784
+ polling: 1000,
785
+ timeout: 5000
786
+ }
787
+ );
788
+ result.found = true;
789
+ result.title = await page.title();
790
+ }
791
+ catch(error) {
776
792
  actIndex = -2;
777
793
  waitError(page, act, error, 'title');
778
- });
794
+ }
779
795
  }
780
- else if (act.what === 'body') {
781
- await page.waitForFunction(
782
- text => document && document.body && document.body.innerText.includes(text),
783
- act.which,
784
- {
785
- polling: 2000,
786
- timeout: 10000
787
- }
788
- )
789
- .catch(async error => {
796
+ else if (what === 'body') {
797
+ try {
798
+ await page.waitForFunction(
799
+ text => document
800
+ && document.body
801
+ && document.body.innerText.toLowerCase().includes(text.toLowerCase()),
802
+ which,
803
+ {
804
+ polling: 2000,
805
+ timeout: 10000
806
+ }
807
+ );
808
+ result.found = true;
809
+ }
810
+ catch(error) {
790
811
  actIndex = -2;
791
812
  waitError(page, act, error, 'body');
792
- });
813
+ }
793
814
  }
794
- // If the text was found:
795
- if (actIndex > -2) {
796
- // Add this to the report.
797
- act.result = {url: page.url()};
798
- if (act.what === 'title') {
799
- act.result.title = await page.title();
815
+ else if (what === 'mailLink') {
816
+ try {
817
+ const addressJSHandle = await page.waitForFunction(
818
+ text => {
819
+ const mailLinks = document
820
+ && document.body
821
+ && document.body.querySelectorAll('a[href^="mailto:"]');
822
+ if (mailLinks && mailLinks.length) {
823
+ const textLC = text.toLowerCase();
824
+ const a11yLink = Array
825
+ .from(mailLinks)
826
+ .find(link => link.textContent.toLowerCase().includes(textLC));
827
+ if (a11yLink) {
828
+ return a11yLink.href.replace(/^mailto:/, '');
829
+ }
830
+ else {
831
+ return false;
832
+ }
833
+ }
834
+ else {
835
+ return false;
836
+ }
837
+ },
838
+ which,
839
+ {
840
+ polling: 1000,
841
+ timeout: 5000
842
+ }
843
+ );
844
+ const address = await addressJSHandle.jsonValue();
845
+ result.found = true;
846
+ result.address = address;
847
+ }
848
+ catch(error) {
849
+ actIndex = -2;
850
+ waitError(page, act, error, 'mailLink');
800
851
  }
801
852
  }
802
853
  }
@@ -1054,24 +1105,36 @@ const doActs = async (report, actIndex, page) => {
1054
1105
  await matchingElement.focus({timeout: 2000});
1055
1106
  act.result = 'focused';
1056
1107
  }
1057
- // Otherwise, if it is clicking a link, perform it.
1108
+ // Otherwise, if it is clicking a link:
1058
1109
  else if (act.type === 'link') {
1110
+ // Try to click it.
1059
1111
  const href = await matchingElement.getAttribute('href');
1060
1112
  const target = await matchingElement.getAttribute('target');
1061
- await matchingElement.click({timeout: 2000})
1062
- .catch(async () => {
1063
- console.log('ERROR: First attempt to click link timed out');
1113
+ await matchingElement.click({timeout: 3000})
1114
+ // If it cannot be clicked within 3 seconds:
1115
+ .catch(async error => {
1116
+ // Try to force-click it without actionability checks.
1117
+ const errorSummary = error.message.replace(/\n.+/, '');
1118
+ console.log(`ERROR: Link to ${href} not clickable (${errorSummary})`);
1064
1119
  await matchingElement.click({
1065
- force: true,
1066
- timeout: 10000
1120
+ force: true
1067
1121
  })
1068
- .catch(() => {
1122
+ // If it cannot be force-clicked:
1123
+ .catch(error => {
1124
+ // Quit and report the failure.
1069
1125
  actIndex = -2;
1070
- console.log('ERROR: Second (forced) attempt to click link timed out');
1071
- act.result = 'ERROR: Normal and forced click attempts timed out';
1126
+ const errorSummary = error.message.replace(/\n.+/, '');
1127
+ console.log(`ERROR: Link to ${href} not force-clickable (${errorSummary})`);
1128
+ act.result = {
1129
+ href: href || 'NONE',
1130
+ target: target || 'NONE',
1131
+ error: 'ERROR: Normal and forced attempts to click link timed out'
1132
+ };
1072
1133
  });
1073
1134
  });
1135
+ // If it was clicked:
1074
1136
  if (actIndex > -2) {
1137
+ // Report the success.
1075
1138
  act.result = {
1076
1139
  href: href || 'NONE',
1077
1140
  target: target || 'NONE',
@@ -1089,7 +1152,9 @@ const doActs = async (report, actIndex, page) => {
1089
1152
  const optionText = await option.textContent();
1090
1153
  optionTexts.push(optionText);
1091
1154
  }
1092
- const matchTexts = optionTexts.map((text, index) => text.includes(act.what) ? index : -1);
1155
+ const matchTexts = optionTexts.map(
1156
+ (text, index) => text.includes(act.what) ? index : -1
1157
+ );
1093
1158
  const index = matchTexts.filter(text => text > -1)[act.index || 0];
1094
1159
  if (index !== undefined) {
1095
1160
  await matchingElement.selectOption({index});
@@ -1185,7 +1250,7 @@ const doActs = async (report, actIndex, page) => {
1185
1250
  }
1186
1251
  // If there is a current element:
1187
1252
  if (currentElement) {
1188
- // If it was already reached within this command performance:
1253
+ // If it was already reached within this act:
1189
1254
  if (currentElement.dataset.pressesReached === actCount.toString(10)) {
1190
1255
  // Report the error.
1191
1256
  console.log(`ERROR: ${currentElement.tagName} element reached again`);
@@ -1319,6 +1384,8 @@ const doActs = async (report, actIndex, page) => {
1319
1384
  const errorMsg = `ERROR: Invalid command of type ${act.type}`;
1320
1385
  act.result = errorMsg;
1321
1386
  console.log(errorMsg);
1387
+ // Quit.
1388
+ actIndex = -2;
1322
1389
  }
1323
1390
  // Perform the remaining acts.
1324
1391
  await doActs(report, actIndex + 1, page);