testaro 1.0.2 → 2.0.3
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/index.js +106 -138
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
testaro main script.
|
|
4
4
|
*/
|
|
5
5
|
// ########## IMPORTS
|
|
6
|
-
// Module to access files.
|
|
7
|
-
const fs = require('fs/promises');
|
|
8
6
|
// Requirements for commands.
|
|
9
7
|
const {commands} = require('./commands');
|
|
10
8
|
// ########## CONSTANTS
|
|
@@ -198,52 +196,35 @@ const isValidScript = script => {
|
|
|
198
196
|
};
|
|
199
197
|
// Validates a batch.
|
|
200
198
|
const isValidBatch = batch => {
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
199
|
+
// If the batch exists:
|
|
200
|
+
if (batch) {
|
|
201
|
+
// Get its data.
|
|
202
|
+
const {what, hosts} = batch;
|
|
203
|
+
// Return whether the batch is valid:
|
|
204
|
+
return what
|
|
205
|
+
&& hosts
|
|
206
|
+
&& typeof what === 'string'
|
|
207
|
+
&& Array.isArray(hosts)
|
|
208
|
+
&& hosts.every(host => host.which && host.what && isURL(host.which));
|
|
209
|
+
}
|
|
210
|
+
// Otherwise, i.e. if the batch does not exist:
|
|
211
|
+
else {
|
|
212
|
+
// Return that it is valid, because it is optional.
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
210
215
|
};
|
|
211
|
-
// Validates
|
|
212
|
-
const isValidReports = reports =>
|
|
216
|
+
// Validates an initialized reports array.
|
|
217
|
+
const isValidReports = reports => Array.isArray(reports) && ! reports.length;
|
|
218
|
+
// Validates an initialized log array.
|
|
219
|
+
const isValidLog = log => Array.isArray(log) && ! log.length;
|
|
213
220
|
// Validates an options object.
|
|
214
221
|
const isValidOptions = async options => {
|
|
215
222
|
if (options) {
|
|
216
|
-
const {script, batch, reports} = options;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (reports && isValidReports(reports)) {
|
|
222
|
-
if (batch) {
|
|
223
|
-
const batchJSON = await fs.readFile(batch, 'utf8');
|
|
224
|
-
const batchObj = JSON.parse(batchJSON);
|
|
225
|
-
if (isValidBatch(batchObj)) {
|
|
226
|
-
return true;
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
223
|
+
const {script, batch, log, reports} = options;
|
|
224
|
+
return isValidScript(script)
|
|
225
|
+
&& isValidBatch(batch)
|
|
226
|
+
&& isValidLog(log)
|
|
227
|
+
&& isValidReports(reports);
|
|
247
228
|
}
|
|
248
229
|
else {
|
|
249
230
|
return false;
|
|
@@ -580,6 +561,13 @@ const isTrue = (object, specs) => {
|
|
|
580
561
|
}
|
|
581
562
|
return [actual, satisfied];
|
|
582
563
|
};
|
|
564
|
+
// Adds an error result to an act.
|
|
565
|
+
const waitError = (page, act, error, what) => {
|
|
566
|
+
console.log(`ERROR waiting for ${what} (${error.message})`);
|
|
567
|
+
act.result = {url: page.url()};
|
|
568
|
+
act.result.error = `ERROR waiting for ${what}`;
|
|
569
|
+
return false;
|
|
570
|
+
};
|
|
583
571
|
// Recursively performs the commands in a report.
|
|
584
572
|
const doActs = async (report, actIndex, page) => {
|
|
585
573
|
const {acts} = report;
|
|
@@ -658,25 +646,19 @@ const doActs = async (report, actIndex, page) => {
|
|
|
658
646
|
else if (act.type === 'wait') {
|
|
659
647
|
const {what, which} = act;
|
|
660
648
|
console.log(`>> for ${what} to include “${which}”`);
|
|
661
|
-
const waitError = (error, what) => {
|
|
662
|
-
console.log(`ERROR waiting for ${what} (${error.message})`);
|
|
663
|
-
act.result = {url: page.url()};
|
|
664
|
-
act.result.error = `ERROR waiting for ${what}`;
|
|
665
|
-
return false;
|
|
666
|
-
};
|
|
667
649
|
// Wait 5 seconds for the specified text to appear in the specified place.
|
|
668
650
|
let successJSHandle;
|
|
669
651
|
if (act.what === 'url') {
|
|
670
652
|
successJSHandle = await page.waitForFunction(
|
|
671
653
|
text => document.URL.includes(text), act.which, {timeout: 5000}
|
|
672
654
|
)
|
|
673
|
-
.catch(error => waitError(error, 'URL'));
|
|
655
|
+
.catch(error => waitError(page, act, error, 'URL'));
|
|
674
656
|
}
|
|
675
657
|
else if (act.what === 'title') {
|
|
676
658
|
successJSHandle = await page.waitForFunction(
|
|
677
659
|
text => document.title.includes(text), act.which, {timeout: 5000}
|
|
678
660
|
)
|
|
679
|
-
.catch(error => waitError(error, 'title'));
|
|
661
|
+
.catch(error => waitError(page, act, error, 'title'));
|
|
680
662
|
}
|
|
681
663
|
else if (act.what === 'body') {
|
|
682
664
|
successJSHandle = await page.waitForFunction(
|
|
@@ -685,7 +667,7 @@ const doActs = async (report, actIndex, page) => {
|
|
|
685
667
|
return innerText.includes(matchText);
|
|
686
668
|
}, which, {timeout: 5000}
|
|
687
669
|
)
|
|
688
|
-
.catch(error => waitError(error, 'body'));
|
|
670
|
+
.catch(error => waitError(page, act, error, 'body'));
|
|
689
671
|
}
|
|
690
672
|
if (successJSHandle) {
|
|
691
673
|
act.result = {url: page.url()};
|
|
@@ -1031,7 +1013,7 @@ const doActs = async (report, actIndex, page) => {
|
|
|
1031
1013
|
}
|
|
1032
1014
|
// Otherwise, i.e. if the move is unknown, add the failure to the act.
|
|
1033
1015
|
else {
|
|
1034
|
-
//
|
|
1016
|
+
// Report the error.
|
|
1035
1017
|
act.result = 'ERROR: move unknown';
|
|
1036
1018
|
}
|
|
1037
1019
|
}
|
|
@@ -1085,22 +1067,16 @@ const doActs = async (report, actIndex, page) => {
|
|
|
1085
1067
|
// Perform the remaining acts.
|
|
1086
1068
|
await doActs(report, actIndex + 1, page);
|
|
1087
1069
|
}
|
|
1088
|
-
// Otherwise, i.e. if no more acts are to be performed:
|
|
1089
|
-
else {
|
|
1090
|
-
// Return a Promise.
|
|
1091
|
-
console.log('All commands performed');
|
|
1092
|
-
return Promise.resolve('');
|
|
1093
|
-
}
|
|
1094
1070
|
};
|
|
1095
1071
|
// Performs the commands in a script and returns a report.
|
|
1096
|
-
const doScript = async report => {
|
|
1072
|
+
const doScript = async (options, report) => {
|
|
1097
1073
|
// Reinitialize the log statistics.
|
|
1098
1074
|
logCount = logSize = prohibitedCount = visitTimeoutCount = visitRejectionCount= 0;
|
|
1099
1075
|
// Add initialized properties to the report.
|
|
1100
1076
|
report.presses = 0;
|
|
1101
1077
|
report.amountRead = 0;
|
|
1102
1078
|
report.testTimes = [];
|
|
1103
|
-
// Perform the specified acts
|
|
1079
|
+
// Perform the specified acts.
|
|
1104
1080
|
await doActs(report, 0, null);
|
|
1105
1081
|
// Close the browser.
|
|
1106
1082
|
await closeBrowser();
|
|
@@ -1129,9 +1105,49 @@ const doScript = async report => {
|
|
|
1129
1105
|
}
|
|
1130
1106
|
}
|
|
1131
1107
|
}
|
|
1132
|
-
//
|
|
1133
|
-
|
|
1134
|
-
|
|
1108
|
+
// Add the report to the reports array.
|
|
1109
|
+
options.reports.push(report);
|
|
1110
|
+
};
|
|
1111
|
+
// Recursively performs commands on the hosts of a batch.
|
|
1112
|
+
const doBatch = async (options, reportTemplate, hostIndex = 0) => {
|
|
1113
|
+
const {hosts} = options.batch;
|
|
1114
|
+
const host = hosts[hostIndex];
|
|
1115
|
+
// If the specified host exists:
|
|
1116
|
+
if (host) {
|
|
1117
|
+
// Copy the report for it.
|
|
1118
|
+
const hostReport = JSON.parse(JSON.stringify(reportTemplate));
|
|
1119
|
+
// Copy the properties of the specified host to all url acts.
|
|
1120
|
+
hostReport.acts.forEach(act => {
|
|
1121
|
+
if (act.type === 'url') {
|
|
1122
|
+
act.which = host.which;
|
|
1123
|
+
act.what = host.what;
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1126
|
+
// Perform the commands on the host.
|
|
1127
|
+
await doScript(options, hostReport);
|
|
1128
|
+
// Process the remaining hosts.
|
|
1129
|
+
await doBatch(options, reportTemplate, hostIndex + 1);
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
// Performs a script, with or without a batch.
|
|
1133
|
+
const doScriptOrBatch = async (options, reportTemplate) => {
|
|
1134
|
+
// If there is a batch:
|
|
1135
|
+
if (options.batch) {
|
|
1136
|
+
// Perform the script on all the hosts in the batch.
|
|
1137
|
+
console.log('Starting batch');
|
|
1138
|
+
await doBatch(options, reportTemplate);
|
|
1139
|
+
}
|
|
1140
|
+
// Otherwise, i.e. if there is no batch:
|
|
1141
|
+
else {
|
|
1142
|
+
// Perform the script.
|
|
1143
|
+
console.log('Starting no-batch script');
|
|
1144
|
+
await doScript(options, reportTemplate);
|
|
1145
|
+
}
|
|
1146
|
+
// Add an end time to the log.
|
|
1147
|
+
options.log.push({
|
|
1148
|
+
event: 'endTime',
|
|
1149
|
+
value: ((new Date()).toISOString().slice(0, 19))
|
|
1150
|
+
});
|
|
1135
1151
|
};
|
|
1136
1152
|
// Injects url commands into a report where necessary to undo DOM changes.
|
|
1137
1153
|
const injectURLCommands = commands => {
|
|
@@ -1163,84 +1179,36 @@ const injectURLCommands = commands => {
|
|
|
1163
1179
|
}
|
|
1164
1180
|
}
|
|
1165
1181
|
};
|
|
1166
|
-
// Recursively performs commands on the hosts of a batch.
|
|
1167
|
-
const doBatch = async (report, batch, hostIndex, reportList) => {
|
|
1168
|
-
const {hosts} = batch;
|
|
1169
|
-
const host = hosts[hostIndex];
|
|
1170
|
-
// If the specified host exists:
|
|
1171
|
-
if (host) {
|
|
1172
|
-
// Copy the report for it.
|
|
1173
|
-
const hostReport = JSON.parse(JSON.stringify(report));
|
|
1174
|
-
// Copy the properties of the specified host to all url acts.
|
|
1175
|
-
hostReport.acts.forEach(act => {
|
|
1176
|
-
if (act.type === 'url') {
|
|
1177
|
-
act.which = host.which;
|
|
1178
|
-
act.what = host.what;
|
|
1179
|
-
}
|
|
1180
|
-
});
|
|
1181
|
-
// Record the batch size in the report.
|
|
1182
|
-
batch.size = hosts.length;
|
|
1183
|
-
delete batch.hosts;
|
|
1184
|
-
// Perform the commands on the host and produce a report.
|
|
1185
|
-
const finalReport = await doScript(hostReport);
|
|
1186
|
-
const hostSuffix = hostIndex > -1 ? `-${hostIndex.toString().padStart(3, '0')}` : '';
|
|
1187
|
-
const reportName = `report-${finalReport.timeStamp}${hostSuffix}.json`;
|
|
1188
|
-
finalReport.reportName = reportName;
|
|
1189
|
-
const reportPath = `${report.options.reports}/${reportName}`;
|
|
1190
|
-
// Save the report.
|
|
1191
|
-
await fs.writeFile(reportPath, JSON.stringify(finalReport, null, 2));
|
|
1192
|
-
// Send the report name to the console.
|
|
1193
|
-
reportList.push(reportName);
|
|
1194
|
-
// Process the remaining hosts.
|
|
1195
|
-
return await doBatch(hostReport, hostIndex + 1, reportList);
|
|
1196
|
-
}
|
|
1197
|
-
// Otherwise, i.e. if the hosts have been exhausted:
|
|
1198
|
-
else {
|
|
1199
|
-
// Return the list of reports.
|
|
1200
|
-
return reportList;
|
|
1201
|
-
}
|
|
1202
|
-
};
|
|
1203
|
-
// Performs a script.
|
|
1204
|
-
const doScriptOrBatch = async report => {
|
|
1205
|
-
// If the report has an options property:
|
|
1206
|
-
const {options} = report;
|
|
1207
|
-
// If there is a batch:
|
|
1208
|
-
if (options.batch) {
|
|
1209
|
-
// Perform the script on all the hosts in the batch and return a list of the reports.
|
|
1210
|
-
const batchJSON = await fs.readFile(options.batch, 'utf8');
|
|
1211
|
-
const batch = JSON.parse(batchJSON);
|
|
1212
|
-
const reportList = await doBatch(report, batch, 0, []);
|
|
1213
|
-
console.log(reportList);
|
|
1214
|
-
}
|
|
1215
|
-
// Otherwise, i.e. if there is no batch:
|
|
1216
|
-
else {
|
|
1217
|
-
// Perform the script and save the report.
|
|
1218
|
-
const finalReport = await doScript(report);
|
|
1219
|
-
const reportName = `report-${finalReport.timeStamp}.json`;
|
|
1220
|
-
finalReport.reportName = reportName;
|
|
1221
|
-
const reportPath = `${report.options.reports}/${reportName}`;
|
|
1222
|
-
// Save the report.
|
|
1223
|
-
await fs.writeFile(reportPath, JSON.stringify(finalReport, null, 2));
|
|
1224
|
-
// Send the report name to the console.
|
|
1225
|
-
console.log(`Report ${reportName} saved`);
|
|
1226
|
-
}
|
|
1227
|
-
};
|
|
1228
1182
|
// Handles a request.
|
|
1229
1183
|
exports.handleRequest = async options => {
|
|
1230
1184
|
// If the options object is valid:
|
|
1231
1185
|
if(isValidOptions(options)) {
|
|
1232
|
-
//
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1186
|
+
// Add a start time and a timeStamp to the log.
|
|
1187
|
+
options.log.push(
|
|
1188
|
+
{
|
|
1189
|
+
event: 'startTime',
|
|
1190
|
+
value: ((new Date()).toISOString().slice(0, 19))
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
event: 'timeStamp',
|
|
1194
|
+
value: Math.floor((Date.now() - Date.UTC(2022, 3)) / 10000).toString(36)
|
|
1195
|
+
}
|
|
1196
|
+
);
|
|
1197
|
+
// Add the batch size to the log if there is a batch.
|
|
1198
|
+
if (options.batch) {
|
|
1199
|
+
options.log.push({
|
|
1200
|
+
event: 'batchSize',
|
|
1201
|
+
value: options.batch.hosts.length
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
// Create a report template, containing a copy of the commands as its acts.
|
|
1205
|
+
const reportTemplate = {
|
|
1206
|
+
acts: JSON.parse(JSON.stringify(options.script.commands))
|
|
1207
|
+
};
|
|
1239
1208
|
// Inject url acts where necessary to undo DOM changes.
|
|
1240
|
-
injectURLCommands(
|
|
1241
|
-
// Perform the script, with or without a batch,
|
|
1242
|
-
await doScriptOrBatch(
|
|
1243
|
-
console.log('Request handled');
|
|
1209
|
+
injectURLCommands(reportTemplate.acts);
|
|
1210
|
+
// Perform the script, with or without a batch, asynchronously adding to the log and reports.
|
|
1211
|
+
await doScriptOrBatch(options, reportTemplate);
|
|
1244
1212
|
}
|
|
1245
1213
|
else {
|
|
1246
1214
|
console.log('ERROR: options missing or invalid');
|