testaro 8.4.7 → 9.0.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 (45) hide show
  1. package/README.md +94 -155
  2. package/{commands.js → actSpecs.js} +1 -1
  3. package/call.js +35 -13
  4. package/package.json +2 -3
  5. package/run.js +155 -181
  6. package/samples/00000-ts18-example.json +3 -3
  7. package/validation/executors/run.js +40 -0
  8. package/validation/executors/watchDir.js +7 -6
  9. package/validation/executors/watchNet.js +5 -6
  10. package/validation/jobs/done/README.md +3 -0
  11. package/validation/jobs/{00000-simple-example.json → todo/00000-simple-example.json} +6 -7
  12. package/validation/jobs/{README.md → todo/README.md} +0 -0
  13. package/validation/tests/jobs/allHidden.json +3 -2
  14. package/validation/tests/jobs/bulk.json +3 -2
  15. package/validation/tests/jobs/docType.json +3 -2
  16. package/validation/tests/jobs/elements.json +3 -2
  17. package/validation/tests/jobs/embAc.json +3 -2
  18. package/validation/tests/jobs/filter.json +3 -2
  19. package/validation/tests/jobs/focAll.json +3 -2
  20. package/validation/tests/jobs/focInd.json +3 -2
  21. package/validation/tests/jobs/focOp.json +3 -2
  22. package/validation/tests/jobs/focVis.json +3 -2
  23. package/validation/tests/jobs/hover.json +3 -2
  24. package/validation/tests/jobs/labClash.json +3 -2
  25. package/validation/tests/jobs/linkTo.json +3 -2
  26. package/validation/tests/jobs/linkUl.json +3 -2
  27. package/validation/tests/jobs/menuNav.json +3 -2
  28. package/validation/tests/jobs/miniText.json +3 -2
  29. package/validation/tests/jobs/motion.json +3 -2
  30. package/validation/tests/jobs/nonTable.json +3 -2
  31. package/validation/tests/jobs/radioSet.json +3 -2
  32. package/validation/tests/jobs/role.json +3 -2
  33. package/validation/tests/jobs/styleDiff.json +3 -2
  34. package/validation/tests/jobs/tabNav.json +3 -2
  35. package/validation/tests/jobs/textNodes.json +3 -2
  36. package/validation/tests/jobs/title.json +3 -2
  37. package/validation/tests/jobs/titledEl.json +3 -2
  38. package/validation/tests/jobs/zIndex.json +3 -2
  39. package/validation/validateTest.js +3 -8
  40. package/validation/watch/{README.md → done/README.md} +0 -0
  41. package/validation/watch/todo/README.md +3 -0
  42. package/watch.js +39 -49
  43. package/high.js +0 -60
  44. package/validation/executors/high.js +0 -52
  45. package/validation/executors/low.js +0 -53
package/run.js CHANGED
@@ -7,8 +7,10 @@
7
7
 
8
8
  // Module to keep secrets.
9
9
  require('dotenv').config();
10
- // Requirements for commands.
11
- const {commands} = require('./commands');
10
+ // Requirements for acts.
11
+ const {actSpecs} = require('./actSpecs');
12
+ // Playwright package.
13
+ const playwright = require('playwright');
12
14
 
13
15
  // ########## CONSTANTS
14
16
 
@@ -16,7 +18,6 @@ const {commands} = require('./commands');
16
18
  const debug = process.env.DEBUG === 'true';
17
19
  // Set WAITS environment variable to a positive number to insert delays (in ms).
18
20
  const waits = Number.parseInt(process.env.WAITS) || 0;
19
- const urlInject = process.env.URL_INJECT || 'yes';
20
21
  // CSS selectors for targets of moves.
21
22
  const moves = {
22
23
  button: 'button, [role=button], input[type=submit]',
@@ -66,20 +67,6 @@ const tests = {
66
67
  wave: 'WAVE',
67
68
  zIndex: 'z indexes'
68
69
  };
69
- // Tests that may change the DOM.
70
- const domChangers = new Set([
71
- 'axe',
72
- 'continuum',
73
- 'focAll',
74
- 'focInd',
75
- 'focOp',
76
- 'hover',
77
- 'htmlcs',
78
- 'ibm',
79
- 'menuNav',
80
- 'textNodes',
81
- 'wave'
82
- ]);
83
70
  // Browser types available in PlayWright.
84
71
  const browserTypeNames = {
85
72
  'chromium': 'Chrome',
@@ -187,24 +174,24 @@ const hasSubtype = (variable, subtype) => {
187
174
  return true;
188
175
  }
189
176
  };
190
- // Validates a command.
191
- const isValidCommand = command => {
192
- // Identify the type of the command.
193
- const type = command.type;
177
+ // Validates an act.
178
+ const isValidAct = act => {
179
+ // Identify the type of the act.
180
+ const type = act.type;
194
181
  // If the type exists and is known:
195
- if (type && commands.etc[type]) {
182
+ if (type && actSpecs.etc[type]) {
196
183
  // Copy the validator of the type for possible expansion.
197
- const validator = Object.assign({}, commands.etc[type][1]);
184
+ const validator = Object.assign({}, actSpecs.etc[type][1]);
198
185
  // If the type is test:
199
186
  if (type === 'test') {
200
187
  // Identify the test.
201
- const testName = command.which;
188
+ const testName = act.which;
202
189
  // If one was specified and is known:
203
190
  if (testName && tests[testName]) {
204
191
  // If it has special properties:
205
- if (commands.tests[testName]) {
192
+ if (actSpecs.tests[testName]) {
206
193
  // Expand the validator by adding them.
207
- Object.assign(validator, commands.tests[testName][1]);
194
+ Object.assign(validator, actSpecs.tests[testName][1]);
208
195
  }
209
196
  }
210
197
  // Otherwise, i.e. if no or an unknown test was specified:
@@ -213,22 +200,22 @@ const isValidCommand = command => {
213
200
  return false;
214
201
  }
215
202
  }
216
- // Return whether the command is valid.
203
+ // Return whether the act is valid.
217
204
  return Object.keys(validator).every(property => {
218
205
  if (property === 'name') {
219
206
  return true;
220
207
  }
221
208
  else {
222
209
  const vP = validator[property];
223
- const cP = command[property];
224
- // If it is optional and omitted or present and valid:
225
- const optAndNone = ! vP[0] && ! cP;
226
- const isValidCommand = cP !== undefined && hasType(cP, vP[1]) && hasSubtype(cP, vP[2]);
227
- return optAndNone || isValidCommand;
210
+ const aP = act[property];
211
+ // If it is optional and omitted or is present and valid:
212
+ const optAndNone = ! vP[0] && ! aP;
213
+ const isValidAct = aP !== undefined && hasType(aP, vP[1]) && hasSubtype(aP, vP[2]);
214
+ return optAndNone || isValidAct;
228
215
  }
229
216
  });
230
217
  }
231
- // Otherwise, i.e. if the command has an unknown or no type:
218
+ // Otherwise, i.e. if the act has an unknown or no type:
232
219
  else {
233
220
  // Return invalidity.
234
221
  return false;
@@ -238,42 +225,60 @@ const isValidCommand = command => {
238
225
  const isValidReport = report => {
239
226
  if (report) {
240
227
  // Return whether the report is valid.
241
- const {job, acts, jobData} = report;
242
- const {id, what, strict, commands, sources, jobCreationTime, timeStamp} = job;
243
- const criteria = [
244
- [job, 'report has job property'],
245
- [acts, 'report has acts property'],
246
- [jobData, 'report has jobData property'],
247
- [id, 'job has id property'],
248
- [what, 'job has what property'],
249
- [typeof strict === 'boolean', 'job has true or false strict property'],
250
- [commands, 'job has commands property'],
251
- [sources, 'job has sources property'],
252
- [jobCreationTime, 'job has jobCreationTime property'],
253
- [timeStamp, 'job has timeStamp property'],
254
- [Array.isArray(acts), 'acts has array value'],
255
- [typeof id === 'string', 'id has string value'],
256
- [typeof what === 'string', 'what has string value'],
257
- [Array.isArray(commands), 'commands has array value'],
258
- [commands[0].type, 'first command has type property'],
259
- [commands[0].type === 'launch', 'first command has launch type'],
260
- [commands.length > 1, 'command count greater than 1'],
261
- [commands[1].type, 'second command has type property'],
262
- [commands[1].type === 'url', 'second command has url type'],
263
- [commands[1].which, 'second command has which property'],
264
- [isURL(commands[1].which), 'second command which property has URL value'],
265
- [commands.every(command => isValidCommand(command)), 'every command is valid'],
266
- [sources && typeof sources.script === 'string', 'sources has script property with string value'],
267
- [sources && sources.host, 'sources has host property']
268
- ];
269
- const invalidityIndex = criteria.findIndex(criterion => ! criterion[0]);
270
- if (invalidityIndex > -1) {
271
- console.log(`ERROR: report fails “${criteria[invalidityIndex][1]}” requirement`);
228
+ const {id, what, strict, timeLimit, acts, sources, creationTime, timeStamp} = report;
229
+ if (! id || typeof id !== 'string') {
230
+ return 'Bad report ID';
272
231
  }
273
- return invalidityIndex === -1;
232
+ if (! what || typeof what !== 'string') {
233
+ return 'Bad report what';
234
+ }
235
+ if (! strict || typeof strict !== 'boolean') {
236
+ return 'Bad report strict';
237
+ }
238
+ if (! timeLimit || typeof timeLimit !== 'number' || timeLimit < 1) {
239
+ return 'Bad report time limit';
240
+ }
241
+ if (! acts || ! Array.isArray(acts) || acts.length < 2) {
242
+ return 'Bad report acts';
243
+ }
244
+ if (! acts.every(act => act.type && typeof act.type === 'string')) {
245
+ return 'Act with no type';
246
+ }
247
+ if (acts[0].type !== 'launch') {
248
+ return 'First act type not launch';
249
+ }
250
+ if (acts[1].type !== 'url') {
251
+ return 'Second act type not url';
252
+ }
253
+ if (! ['chromium', 'webkit', 'firefox'].includes(acts[0].which)) {
254
+ return 'Bad first act which';
255
+ }
256
+ if (! acts[1].which || typeof acts[1].which !== 'string' || ! isURL(acts[1].which)) {
257
+ return 'Second act which not a URL';
258
+ }
259
+ if (acts.some(act => ! isValidAct(act))) {
260
+ return 'Not all acts valid';
261
+ }
262
+ if (! sources || typeof sources !== 'object') {
263
+ return 'Bad report sources';
264
+ }
265
+ if (typeof sources.script !== 'string') {
266
+ return 'Bad sources script';
267
+ }
268
+ if (
269
+ ! creationTime
270
+ || typeof creationTime !== 'string'
271
+ || ! /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/.test(creationTime)
272
+ ) {
273
+ return 'bad job creation time';
274
+ }
275
+ if (! timeStamp || typeof timeStamp !== 'string') {
276
+ return 'bad report timestamp';
277
+ }
278
+ return '';
274
279
  }
275
280
  else {
276
- return false;
281
+ return 'no report';
277
282
  }
278
283
  };
279
284
 
@@ -295,7 +300,7 @@ const browserClose = async () => {
295
300
  const errorStart = error => error.message.replace(/\n.+/s, '');
296
301
  // Launches a browser.
297
302
  const launch = async (report, typeName, lowMotion = false) => {
298
- const browserType = require('playwright')[typeName];
303
+ const browserType = playwright[typeName];
299
304
  // If the specified browser type exists:
300
305
  if (browserType) {
301
306
  // Close the current browser, if any.
@@ -604,6 +609,18 @@ const goTo = async (report, page, url, timeout, waitUntil, isStrict) => {
604
609
  };
605
610
  // Recursively performs the acts in a report.
606
611
  const doActs = async (report, actIndex, page) => {
612
+ // Quits and reports the performance being aborted.
613
+ const abortActs = async () => {
614
+ // Add data on the aborted act to the report.
615
+ report.jobData.abortTime = nowString();
616
+ report.jobData.abortedAct = actIndex;
617
+ // Prevent performance of additional acts.
618
+ actIndex = -2;
619
+ // Report this.
620
+ console.log('ERROR: Job aborted');
621
+ // Stop the action until the user resumes it.
622
+ await page.pause();
623
+ };
607
624
  process.on('message', message => {
608
625
  if (message === 'interrupt') {
609
626
  console.log('ERROR: Terminal interrupted doActs');
@@ -611,18 +628,18 @@ const doActs = async (report, actIndex, page) => {
611
628
  }
612
629
  });
613
630
  const {acts} = report;
614
- // If any more commands are to be performed:
631
+ // If any more acts are to be performed:
615
632
  if (actIndex > -1 && actIndex < acts.length) {
616
- // Identify the command to be performed.
633
+ // Identify the act to be performed.
617
634
  const act = acts[actIndex];
618
635
  // If it is valid:
619
- if (isValidCommand(act)) {
636
+ if (isValidAct(act)) {
620
637
  const whichSuffix = act.which ? ` (${act.which})` : '';
621
638
  console.log(`>>>> ${act.type}${whichSuffix}`);
622
- // Increment the count of commands performed.
639
+ // Increment the count of acts performed.
623
640
  actCount++;
624
641
  act.startTime = Date.now();
625
- // If the command is an index changer:
642
+ // If the act is an index changer:
626
643
  if (act.type === 'next') {
627
644
  const condition = act.if;
628
645
  const logSuffix = condition.length === 3 ? ` ${condition[1]} ${condition[2]}` : '';
@@ -641,9 +658,9 @@ const doActs = async (report, actIndex, page) => {
641
658
  };
642
659
  // If the condition is true:
643
660
  if (truth[1]) {
644
- // If the performance of commands is to stop:
661
+ // If the performance of acts is to stop:
645
662
  if (act.jump === 0) {
646
- // Stop.
663
+ // Quit.
647
664
  actIndex = -2;
648
665
  }
649
666
  // Otherwise, if there is a numerical jump:
@@ -651,14 +668,14 @@ const doActs = async (report, actIndex, page) => {
651
668
  // Set the act index accordingly.
652
669
  actIndex += act.jump - 1;
653
670
  }
654
- // Otherwise, if there is a named next command:
671
+ // Otherwise, if there is a named next act:
655
672
  else if (act.next) {
656
673
  // Set the new index accordingly, or stop if it does not exist.
657
674
  actIndex = acts.map(act => act.name).indexOf(act.next) - 1;
658
675
  }
659
676
  }
660
677
  }
661
- // Otherwise, if the command is a launch:
678
+ // Otherwise, if the act is a launch:
662
679
  else if (act.type === 'launch') {
663
680
  // Launch the specified browser, creating a browser context and a page in it.
664
681
  await launch(report, act.which, act.lowMotion ? 'reduce' : 'no-preference');
@@ -667,13 +684,13 @@ const doActs = async (report, actIndex, page) => {
667
684
  }
668
685
  // Otherwise, if a current page exists:
669
686
  else if (page) {
670
- // If the command is a url:
687
+ // If the act is a url:
671
688
  if (act.type === 'url') {
672
689
  // Identify the URL.
673
690
  const resolved = act.which.replace('__dirname', __dirname);
674
691
  requestedURL = resolved;
675
692
  // Visit it and wait until the network is idle.
676
- const {strict} = report.job;
693
+ const {strict} = report;
677
694
  let response = await goTo(report, page, requestedURL, 15000, 'networkidle', strict);
678
695
  // If the visit fails:
679
696
  if (response.error) {
@@ -712,23 +729,17 @@ const doActs = async (report, actIndex, page) => {
712
729
  }
713
730
  // If none of the visits succeeded:
714
731
  if (response.error) {
715
- // Report this.
716
- report.jobData.aborted = true;
717
- report.jobData.abortedAct = actIndex;
732
+ // Report this and quit.
718
733
  addError(act, 'failure', 'ERROR: Visits failed');
719
- // Quit.
720
- actIndex = -2;
734
+ await abortActs();
721
735
  }
722
736
  // Otherwise, i.e. if the last visit attempt succeeded:
723
737
  else {
724
738
  // If a prohibited redirection occurred:
725
739
  if (response.exception === 'badRedirection') {
726
- // Report this.
727
- report.jobData.aborted = true;
728
- report.jobData.abortedAct = actIndex;
740
+ // Report this and quit.
729
741
  addError(act, 'badRedirection', 'ERROR: Navigation illicitly redirected');
730
- // Quit.
731
- actIndex = -2;
742
+ await abortActs();
732
743
  }
733
744
  // Add the resulting URL to the act.
734
745
  if (! act.result) {
@@ -744,20 +755,22 @@ const doActs = async (report, actIndex, page) => {
744
755
  const result = act.result = {};
745
756
  // If the text is to be the URL:
746
757
  if (what === 'url') {
747
- // Wait for the URL to be the exact text and quit on failure.
758
+ // Wait for the URL to be the exact text.
748
759
  try {
749
760
  await page.waitForURL(which, {timeout: 15000});
750
761
  result.found = true;
751
762
  result.url = page.url();
752
763
  }
764
+ // If the wait times out:
753
765
  catch(error) {
754
- actIndex = -2;
766
+ // Quit.
767
+ await abortActs();
755
768
  waitError(page, act, error, 'text in the URL');
756
769
  }
757
770
  }
758
771
  // Otherwise, if the text is to be a substring of the page title:
759
772
  else if (what === 'title') {
760
- // Wait for the page title to include the text, case-insensitively, and quit on failure.
773
+ // Wait for the page title to include the text, case-insensitively.
761
774
  try {
762
775
  await page.waitForFunction(
763
776
  text => document
@@ -772,14 +785,16 @@ const doActs = async (report, actIndex, page) => {
772
785
  result.found = true;
773
786
  result.title = await page.title();
774
787
  }
788
+ // If the wait times out:
775
789
  catch(error) {
776
- actIndex = -2;
790
+ // Quit.
791
+ await abortActs();
777
792
  waitError(page, act, error, 'text in the title');
778
793
  }
779
794
  }
780
795
  // Otherwise, if the text is to be a substring of the text of the page body:
781
796
  else if (what === 'body') {
782
- // Wait for the body to include the text, case-insensitively, and quit on failure.
797
+ // Wait for the body to include the text, case-insensitively.
783
798
  try {
784
799
  await page.waitForFunction(
785
800
  text => document
@@ -793,25 +808,31 @@ const doActs = async (report, actIndex, page) => {
793
808
  );
794
809
  result.found = true;
795
810
  }
811
+ // If the wait times out:
796
812
  catch(error) {
797
- actIndex = -2;
813
+ // Quit.
814
+ await abortActs();
798
815
  waitError(page, act, error, 'text in the body');
799
816
  }
800
817
  }
801
818
  }
802
819
  // Otherwise, if the act is a wait for a state:
803
820
  else if (act.type === 'state') {
804
- // Wait for it and quit on failure.
821
+ // Wait for it.
805
822
  const stateIndex = ['loaded', 'idle'].indexOf(act.which);
806
823
  await page.waitForLoadState(
807
824
  ['domcontentloaded', 'networkidle'][stateIndex], {timeout: [10000, 15000][stateIndex]}
808
825
  )
809
- .catch(error => {
826
+ // If the wait times out:
827
+ .catch(async error => {
828
+ // Quit.
810
829
  console.log(`ERROR waiting for page to be ${act.which} (${error.message})`);
811
830
  addError(act, 'timeout', `ERROR waiting for page to be ${act.which}`);
812
- actIndex = -2;
831
+ await abortActs();
813
832
  });
833
+ // If the wait succeeded:
814
834
  if (actIndex > -2) {
835
+ // Add state data to the report.
815
836
  act.result = {
816
837
  success: true,
817
838
  state: act.which
@@ -833,8 +854,10 @@ const doActs = async (report, actIndex, page) => {
833
854
  // Otherwise, if the page has a URL:
834
855
  else if (page.url() && page.url() !== 'about:blank') {
835
856
  const url = page.url();
836
- // If redirection is permitted or did not occur:
837
- if (! report.strict || deSlash(url) === deSlash(requestedURL)) {
857
+ // If redirection is inapplicable, is permitted, or did not occur:
858
+ if (
859
+ url.startsWith('file:') || ! report.strict || deSlash(url) === deSlash(requestedURL)
860
+ ) {
838
861
  // Add the URL to the act.
839
862
  act.url = url;
840
863
  // If the act is a revelation:
@@ -965,7 +988,7 @@ const doActs = async (report, actIndex, page) => {
965
988
  // Initialize the arguments.
966
989
  const args = [act.which === 'tenon' ? tenonData : page];
967
990
  // Identify the additional validator of the test.
968
- const testValidator = commands.tests[act.which];
991
+ const testValidator = actSpecs.tests[act.which];
969
992
  // If it exists:
970
993
  if (testValidator) {
971
994
  // Identify its argument properties.
@@ -1085,8 +1108,9 @@ const doActs = async (report, actIndex, page) => {
1085
1108
  // If a match was found:
1086
1109
  if (act.result.found) {
1087
1110
  // FUNCTION DEFINITION START
1088
- // Perform a click or Enter keypress and wait for the network to be idle.
1111
+ // Performs a click or Enter keypress and waits for the network to be idle.
1089
1112
  const doAndWait = async isClick => {
1113
+ // Perform and report the move.
1090
1114
  const move = isClick ? 'click' : 'Enter keypress';
1091
1115
  try {
1092
1116
  await isClick
@@ -1095,12 +1119,14 @@ const doActs = async (report, actIndex, page) => {
1095
1119
  act.result.success = true;
1096
1120
  act.result.move = move;
1097
1121
  }
1122
+ // If the move fails:
1098
1123
  catch(error) {
1124
+ // Quit and add failure data to the report.
1099
1125
  act.result.success = false;
1100
1126
  act.result.error = 'moveFailure';
1101
1127
  act.result.message = `ERROR: ${move} failed`;
1102
1128
  console.log(`ERROR: ${move} failed (${errorStart(error)})`);
1103
- actIndex = -2;
1129
+ await abortActs();
1104
1130
  }
1105
1131
  if (act.result.success) {
1106
1132
  try {
@@ -1186,15 +1212,16 @@ const doActs = async (report, actIndex, page) => {
1186
1212
  }
1187
1213
  // If the click or load failed:
1188
1214
  catch(error) {
1189
- // Quit and report the failure.
1215
+ // Quit and add failure data to the report.
1190
1216
  console.log(`ERROR clicking link (${errorStart(error)})`);
1191
1217
  act.result.success = false;
1192
1218
  act.result.error = 'unclickable';
1193
1219
  act.result.message = 'ERROR: click or load timed out';
1194
- actIndex = -2;
1220
+ await abortActs();
1195
1221
  }
1196
1222
  // If the link click succeeded:
1197
1223
  if (! act.result.error) {
1224
+ // Add success data to the report.
1198
1225
  act.result.success = true;
1199
1226
  act.result.move = 'clicked';
1200
1227
  }
@@ -1223,7 +1250,7 @@ const doActs = async (report, actIndex, page) => {
1223
1250
  act.result.move = 'selected';
1224
1251
  act.result.option = optionText;
1225
1252
  }
1226
- // Otherwise, if it is entering text on a text- or search-input element:
1253
+ // Otherwise, if it is entering text in an input element:
1227
1254
  else if (['text', 'search'].includes(act.type)) {
1228
1255
  act.result.attributes = {};
1229
1256
  const {attributes} = act.result;
@@ -1263,12 +1290,12 @@ const doActs = async (report, actIndex, page) => {
1263
1290
  }
1264
1291
  // Otherwise, i.e. if no match was found:
1265
1292
  else {
1266
- // Stop.
1293
+ // Quit and add failure data to the report.
1267
1294
  act.result.success = false;
1268
1295
  act.result.error = 'absent';
1269
1296
  act.result.message = 'ERROR: specified element not found';
1270
1297
  console.log('ERROR: Specified element not found');
1271
- actIndex = -2;
1298
+ await abortActs();
1272
1299
  }
1273
1300
  }
1274
1301
  // Otherwise, if the act is a keypress:
@@ -1436,7 +1463,7 @@ const doActs = async (report, actIndex, page) => {
1436
1463
  // Otherwise, i.e. if the act type is unknown:
1437
1464
  else {
1438
1465
  // Add the error result to the act.
1439
- addError(act, 'badType', 'ERROR: Invalid command type');
1466
+ addError(act, 'badType', 'ERROR: Invalid act type');
1440
1467
  }
1441
1468
  }
1442
1469
  // Otherwise, i.e. if redirection is prohibited but occurred:
@@ -1458,85 +1485,37 @@ const doActs = async (report, actIndex, page) => {
1458
1485
  }
1459
1486
  act.endTime = Date.now();
1460
1487
  }
1461
- // Otherwise, i.e. if the command is invalid:
1488
+ // Otherwise, i.e. if the act is invalid:
1462
1489
  else {
1463
- // Add an error result to the act.
1464
- const errorMsg = `ERROR: Invalid command of type ${act.type}`;
1490
+ // Quit and add error data to the report.
1491
+ const errorMsg = `ERROR: Invalid act of type ${act.type}`;
1465
1492
  console.log(errorMsg);
1466
- addError(act, 'badCommand', errorMsg);
1467
- // Quit.
1468
- actIndex = -2;
1493
+ addError(act, 'badAct', errorMsg);
1494
+ await abortActs();
1469
1495
  }
1470
- // Perform the remaining acts unless the performance of acts has been aborted.
1496
+ // Perform any remaining acts if not aborted.
1471
1497
  await doActs(report, actIndex + 1, page);
1472
1498
  }
1473
- else {
1474
- console.log('No more acts to perform; closing the browser');
1499
+ // Otherwise, if all acts have been performed and the job succeeded:
1500
+ else if (! report.jobData.abortTime) {
1501
+ console.log('Acts completed');
1475
1502
  await browserClose();
1476
1503
  console.log('Browser closed');
1477
1504
  }
1478
1505
  };
1479
- // Injects launch and url acts into a report where necessary to undo DOM changes.
1480
- const injectLaunches = acts => {
1481
- let injectMore = true;
1482
- while (injectMore) {
1483
- const injectIndex = acts.findIndex((act, index) =>
1484
- index < acts.length - 1
1485
- && act.type === 'test'
1486
- && acts[index + 1].type === 'test'
1487
- && domChangers.has(act.which)
1488
- );
1489
- if (injectIndex === -1) {
1490
- injectMore = false;
1491
- }
1492
- else {
1493
- const lastBrowserType = acts.reduce((browserType, act, index) => {
1494
- if (act.type === 'launch' && index < injectIndex) {
1495
- return act.which;
1496
- }
1497
- else {
1498
- return browserType;
1499
- }
1500
- }, '');
1501
- const lastURL = acts.reduce((url, act, index) => {
1502
- if (act.type === 'url' && index < injectIndex) {
1503
- return act.which;
1504
- }
1505
- else {
1506
- return url;
1507
- }
1508
- }, '');
1509
- acts.splice(
1510
- injectIndex + 1,
1511
- 0,
1512
- {
1513
- type: 'launch',
1514
- which: lastBrowserType,
1515
- what: `${lastBrowserType} browser`
1516
- },
1517
- {
1518
- type: 'url',
1519
- which: lastURL,
1520
- what: 'URL'
1521
- }
1522
- );
1523
- }
1524
- }
1525
- };
1526
- // Runs a job and adds the results to the report of the job.
1506
+ /*
1507
+ Returns whether an initialized job report is valid and, if so, runs the job and adds the results
1508
+ to the report.
1509
+ */
1527
1510
  exports.doJob = async report => {
1528
1511
  // If the report is valid:
1529
- if(isValidReport(report)) {
1530
- // Add the job commands to the report as its initial acts.
1531
- report.acts = JSON.parse(JSON.stringify(report.job.commands));
1532
- /*
1533
- Inject launch and url acts where necessary to undo DOM changes, if specified.
1534
- Injection of url acts alone does not guarantee test independence.
1535
- */
1536
- if (urlInject === 'yes') {
1537
- injectLaunches(report.acts);
1538
- }
1512
+ const reportInvalidity = isValidReport(report);
1513
+ if (reportInvalidity) {
1514
+ console.log(reportInvalidity);
1515
+ }
1516
+ else {
1539
1517
  // Add initialized job data to the report.
1518
+ report.jobData = {};
1540
1519
  const startTime = new Date();
1541
1520
  report.jobData.startTime = nowString();
1542
1521
  report.jobData.endTime = '';
@@ -1554,16 +1533,11 @@ exports.doJob = async report => {
1554
1533
  report.jobData.presses = 0;
1555
1534
  report.jobData.amountRead = 0;
1556
1535
  report.jobData.testTimes = [];
1557
- // Recursively perform the specified acts.
1536
+ // Recursively perform the acts and get any page if debugging is on and a job was aborted.
1558
1537
  await doActs(report, 0, null);
1559
1538
  // Add the end time and duration to the report.
1560
1539
  const endTime = new Date();
1561
1540
  report.jobData.endTime = nowString();
1562
1541
  report.jobData.elapsedSeconds = Math.floor((endTime - startTime) / 1000);
1563
- return true;
1564
- }
1565
- else {
1566
- console.log('ERROR: Initialized job report invalid');
1567
- return false;
1568
1542
  }
1569
1543
  };
@@ -11,7 +11,7 @@
11
11
  },
12
12
  {
13
13
  "type": "url",
14
- "which": "https://example.com",
14
+ "which": "https://example.edu",
15
15
  "what": "Example.com",
16
16
  "id": "example"
17
17
  },
@@ -36,7 +36,7 @@
36
36
  },
37
37
  {
38
38
  "type": "url",
39
- "which": "https://example.com",
39
+ "which": "https://example.edu",
40
40
  "what": "Example.com",
41
41
  "id": "example"
42
42
  },
@@ -221,7 +221,7 @@
221
221
  "batch": "",
222
222
  "host": {
223
223
  "id": "example",
224
- "which": "https://example.com",
224
+ "which": "https://example.edu",
225
225
  "what": "Example.com"
226
226
  },
227
227
  "requester": "user@domain.tld"