@suffolklitlab/alkiln 3.0.9 → 4.0.0-pre-api.4
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/.github/workflows/test_tests.yml +11 -2
- package/CHANGELOG.md +22 -20
- package/action.yml +42 -32
- package/lib/cucumber_8_shim.js +25 -0
- package/lib/docassemble/docassemble_api_REST.js +130 -0
- package/lib/docassemble/docassemble_api_interface.js +404 -0
- package/lib/docassemble/setup.js +38 -0
- package/lib/docassemble/takedown.js +18 -0
- package/lib/scope.js +337 -60
- package/lib/steps.js +209 -138
- package/lib/utils/env_vars.js +73 -69
- package/lib/utils/log.js +51 -0
- package/lib/utils/session_vars.js +188 -0
- package/lib/utils/time.js +26 -0
- package/lib/world.js +2 -2
- package/package.json +13 -12
- package/tests/features/establishing_steps.feature +15 -0
- package/tests/features/observation_steps.feature +31 -0
- package/tests/features/reports.feature +70 -12
- package/lib/github_integration/install-branch.js +0 -84
- package/lib/github_integration/puppeteer-utils.js +0 -224
- package/lib/github_integration/setup.js +0 -4
- package/lib/github_integration/takedown.js +0 -4
package/lib/scope.js
CHANGED
|
@@ -2,9 +2,12 @@ const { expect } = require('chai');
|
|
|
2
2
|
const cheerio = require('cheerio');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const fs = require('fs');
|
|
5
|
-
|
|
6
5
|
const safe_filename = require("sanitize-filename");
|
|
6
|
+
|
|
7
|
+
// Ours
|
|
7
8
|
const env_vars = require('./utils/env_vars');
|
|
9
|
+
const session_vars = require('./utils/session_vars');
|
|
10
|
+
const time = require('./utils/time');
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
let ordinal_to_integer = {
|
|
@@ -28,11 +31,13 @@ module.exports = {
|
|
|
28
31
|
*
|
|
29
32
|
* The report has a key for each scenario and each of those
|
|
30
33
|
* keys should correspond to a list containing the relevant data. */
|
|
31
|
-
if ( env_vars.DEBUG ) { console.log(`Report:\
|
|
34
|
+
if ( env_vars.DEBUG ) { console.log(`Report:\ntype: ${ type }, value: ${ value }`) }
|
|
32
35
|
let scenario = await scope.makeSureReportPartsExist( scope );
|
|
33
36
|
scenario.lines.push({ type, value });
|
|
34
37
|
|
|
38
|
+
fs.appendFileSync('log.txt', `Report:\ntype: ${ type }, value: ${ value }`);
|
|
35
39
|
return scenario;
|
|
40
|
+
|
|
36
41
|
}, // Ends scope.addToReport()
|
|
37
42
|
|
|
38
43
|
makeSureReportPartsExist: async function( scope ) {
|
|
@@ -51,7 +56,7 @@ module.exports = {
|
|
|
51
56
|
return scenario;
|
|
52
57
|
}, // Ends scope.makeSureReportPartsExist()
|
|
53
58
|
|
|
54
|
-
setReportScenarioStatus: async function( scope, { status=`
|
|
59
|
+
setReportScenarioStatus: async function( scope, { status=`PASSED` }) {
|
|
55
60
|
/* Add final status of scenario to scenario report object - passed, failed, etc. */
|
|
56
61
|
scope.report.get( scope.scenario_id ).status = status;
|
|
57
62
|
}, // Ends scope.setReportScenarioStatus()
|
|
@@ -114,8 +119,8 @@ module.exports = {
|
|
|
114
119
|
|
|
115
120
|
let report = await scope.getPrintableScenario( scope, { scenario });
|
|
116
121
|
let [ scenario_id, data ] = scenario;
|
|
117
|
-
if ( data.status === `
|
|
118
|
-
else if ( data.status === `
|
|
122
|
+
if ( data.status === `FAILED` ) { failed_reports += report; }
|
|
123
|
+
else if ( data.status === `PASSED` ) { passed_reports += report; }
|
|
119
124
|
else { other_reports += report; }
|
|
120
125
|
} // ends for every scenario
|
|
121
126
|
|
|
@@ -179,11 +184,35 @@ module.exports = {
|
|
|
179
184
|
return safer_name;
|
|
180
185
|
}, // Ends scope.getSafeScenarioFilename()
|
|
181
186
|
|
|
187
|
+
getJSONFilename: async function( scope ) {
|
|
188
|
+
let filename = await scope.getSafeScenarioFilename( scope, {
|
|
189
|
+
prefix: `downloads_${ scope.page_id }_JSON`
|
|
190
|
+
});
|
|
191
|
+
filename += `.json`;
|
|
192
|
+
return filename;
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
savePageJSONData: async function( scope ) {
|
|
196
|
+
/** Save the JSON variables of the current page to a json file.
|
|
197
|
+
*
|
|
198
|
+
* @returns {Object} info Info about the result.
|
|
199
|
+
* @returns {string} info.filename The name of the file that was saved.
|
|
200
|
+
* @returns {Object} info.json The variables object.
|
|
201
|
+
*/
|
|
202
|
+
// Get a unique json file filename
|
|
203
|
+
let filename = await scope.getJSONFilename( scope );
|
|
204
|
+
// Get the data
|
|
205
|
+
let json = await scope.getPageJSON( scope );
|
|
206
|
+
// Save the data
|
|
207
|
+
fs.writeFileSync( filename, JSON.stringify( json, null, 2 ));
|
|
208
|
+
return { filename, json };
|
|
209
|
+
}, // Ends scope.savePageJSONData()
|
|
210
|
+
|
|
182
211
|
waitForShowIf: async function waitForShowIf(scope) {
|
|
183
212
|
// If something might be hidden or shown, wait extra time to let it hide or show
|
|
184
213
|
// I think `.$()` does not use a timeout
|
|
185
214
|
let showif = await scope.page.$('.dashowif');
|
|
186
|
-
if ( showif ) { await scope.page.
|
|
215
|
+
if ( showif ) { await scope.page.waitForTimeout( scope.showif_timeout ); }
|
|
187
216
|
},
|
|
188
217
|
|
|
189
218
|
afterStep: async function afterStep(scope, options = {waitForShowIf: false, navigated: false, waitForTimeout: 0}) {
|
|
@@ -191,37 +220,39 @@ module.exports = {
|
|
|
191
220
|
* TODO: discuss splitting into `afterField` and `afterStep` or something. Or just change name
|
|
192
221
|
* TODO: Update to latest cucumberjs, which as `AfterStep` */
|
|
193
222
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
223
|
+
if ( scope.page ) {
|
|
224
|
+
// Use a while loop until the below does not error with `Error: Execution context was destroyed`
|
|
225
|
+
// Temporary fix for race condition. See https://github.com/plocket/docassemble-cucumber/issues/190
|
|
226
|
+
let error_id_elem;
|
|
227
|
+
while ( true ) {
|
|
228
|
+
try {
|
|
229
|
+
// Waiting for `page` to exist. '#da-retry' does not need to exist, it's incidental at this point.
|
|
230
|
+
// If this doesn't throw an error, then the page exists and the result is valid.
|
|
231
|
+
error_id_elem = await scope.page.$('#da-retry');
|
|
232
|
+
break;
|
|
233
|
+
} catch ( err ) {}
|
|
234
|
+
}
|
|
205
235
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
236
|
+
// If there's a system error, throw it
|
|
237
|
+
if ( error_id_elem ) {
|
|
238
|
+
let error_elem = await scope.page.$('blockquote')
|
|
239
|
+
let error_text_prop = await error_elem.getProperty( 'textContent' );
|
|
240
|
+
let system_error_text = await error_text_prop.jsonValue();
|
|
211
241
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
242
|
+
await scope.addToReport(scope, { type: `error`, value: system_error_text });
|
|
243
|
+
throw system_error_text;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// Wait for elements that might get revealed
|
|
247
|
+
if ( options.waitForShowIf ) { await scope.waitForShowIf( scope ); }
|
|
248
|
+
} // ends if scope.page
|
|
215
249
|
|
|
216
|
-
//
|
|
250
|
+
// Reset navigation (TODO: discuss use for observational steps)
|
|
217
251
|
if ( options.navigated ) { scope.navigated = true; } // Consider `daPageLoad` event
|
|
218
252
|
else { scope.navigated = false; }
|
|
219
|
-
|
|
220
|
-
//
|
|
221
|
-
if ( options.
|
|
222
|
-
// Plain old waiting. Yeah, I abstracted it into here so only
|
|
223
|
-
// one thing was being called at the end
|
|
224
|
-
if ( options.waitForTimeout ) { await scope.page.waitFor( options.waitForTimeout ); }
|
|
253
|
+
|
|
254
|
+
// Plain old waiting. Here to avoid doing this in each individual place
|
|
255
|
+
if ( options.waitForTimeout ) { await time.waitForTimeout( options.waitForTimeout ); }
|
|
225
256
|
}, // Ends afterStep
|
|
226
257
|
|
|
227
258
|
tapElement: async function tapElement(scope, elem) {
|
|
@@ -293,7 +324,7 @@ module.exports = {
|
|
|
293
324
|
return true;
|
|
294
325
|
} else {
|
|
295
326
|
// Otherwise wait and then check again
|
|
296
|
-
await scope.page.
|
|
327
|
+
await scope.page.waitForTimeout( 100 );
|
|
297
328
|
await scope.detectDownloadComplete(scope, endTime);
|
|
298
329
|
}
|
|
299
330
|
|
|
@@ -525,6 +556,8 @@ module.exports = {
|
|
|
525
556
|
encoded_name = match[1];
|
|
526
557
|
} else {
|
|
527
558
|
// Some action buttons just use urls instead of da actions
|
|
559
|
+
// TODO: Does this `replace` need the `g` flag?
|
|
560
|
+
// TODO: This only replaces the chars at the start of the string. Is that intentional?
|
|
528
561
|
encoded_name = href.replace(/^[A-Za-z0-9]*/, '_');
|
|
529
562
|
}
|
|
530
563
|
}
|
|
@@ -569,6 +602,15 @@ module.exports = {
|
|
|
569
602
|
return fields;
|
|
570
603
|
}, // Ends scope.getAllFields()
|
|
571
604
|
|
|
605
|
+
getPageJSON: async function ( scope ) {
|
|
606
|
+
/* Get the JSON data of the current page. */
|
|
607
|
+
return await scope.page.evaluate(async function ( elem ) {
|
|
608
|
+
return await get_interview_variables();
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// return data;
|
|
612
|
+
}, // Ends scope.getPageJSON()
|
|
613
|
+
|
|
572
614
|
getFieldSelector: async function ( scope, { $, node }) {
|
|
573
615
|
/* Given the page object and the node object, return the selector
|
|
574
616
|
* we'll use to set the field's value later. Excludes `canvas`.
|
|
@@ -968,7 +1010,7 @@ module.exports = {
|
|
|
968
1010
|
catch(err){ elem.style.background = 'Khaki'; }
|
|
969
1011
|
return elem.getAttribute('for') || elem.getAttribute('name');
|
|
970
1012
|
});
|
|
971
|
-
await scope.page.
|
|
1013
|
+
await scope.page.waitForTimeout( 400 );
|
|
972
1014
|
}
|
|
973
1015
|
|
|
974
1016
|
let { tag, type } = field;
|
|
@@ -1186,30 +1228,30 @@ module.exports = {
|
|
|
1186
1228
|
}
|
|
1187
1229
|
}
|
|
1188
1230
|
|
|
1189
|
-
if (
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1231
|
+
if ( paths.length === 0 ) {
|
|
1232
|
+
await scope.addToReport( scope, { type: `error`, value: `Could not find "${set_to}".` });
|
|
1233
|
+
throw ReferenceError(`Could not find "${set_to}".`)
|
|
1234
|
+
}
|
|
1235
|
+
// Prepare to wait for upload to complete
|
|
1236
|
+
let name = await handle.evaluate( elem => { return elem.getAttribute(`name`); });
|
|
1237
|
+
let selector = `*[name="${ name }"]`
|
|
1238
|
+
let disabled_selector = `${ selector }[disabled]`;
|
|
1239
|
+
let enabled_selector = `${ selector }:not([disabled])`;
|
|
1240
|
+
|
|
1241
|
+
// This comes before the upload starts
|
|
1242
|
+
// Notice `await` is absent. Don't actually wait here.
|
|
1243
|
+
// Just start looking for it now. That way we'll catch it before it's gone
|
|
1244
|
+
let wait_for_disable = scope.page.waitForSelector( disabled_selector );
|
|
1245
|
+
|
|
1246
|
+
// Upload
|
|
1247
|
+
await handle.uploadFile(...paths);
|
|
1248
|
+
|
|
1249
|
+
// Halt progress to wait for the input to be disabled. If it already is, then this
|
|
1250
|
+
// will have resolved already and will keep going.
|
|
1251
|
+
await wait_for_disable;
|
|
1252
|
+
// When it's again enabled, the files have finished loading. Or so I have observed.
|
|
1253
|
+
await scope.page.waitForSelector( enabled_selector );
|
|
1254
|
+
|
|
1213
1255
|
|
|
1214
1256
|
}, // Ends scope.uploadFiles()
|
|
1215
1257
|
|
|
@@ -1546,7 +1588,7 @@ module.exports = {
|
|
|
1546
1588
|
|
|
1547
1589
|
// Attempt to solve weird race condition issues where page has not properly continued
|
|
1548
1590
|
while ( curr_url === prev_url && !error.was_found && !timedOut ) {
|
|
1549
|
-
await scope.page.
|
|
1591
|
+
await scope.page.waitForTimeout( 50 );
|
|
1550
1592
|
curr_url = await scope.page.url();
|
|
1551
1593
|
error = await scope.checkForError( scope );
|
|
1552
1594
|
}
|
|
@@ -1579,7 +1621,7 @@ module.exports = {
|
|
|
1579
1621
|
if ( possible_errors[ error_key ][0] ) {
|
|
1580
1622
|
// If you're debugging and you close the window early, the process will still
|
|
1581
1623
|
// take this long to exit after the tests finish. Not sure how to account for that.
|
|
1582
|
-
if ( env_vars.DEBUG ) { await scope.page.
|
|
1624
|
+
if ( env_vars.DEBUG ) { await scope.page.waitForTimeout( 60 * 1000 ); }
|
|
1583
1625
|
was_found = true;
|
|
1584
1626
|
error_handlers[ error_key ] = possible_errors[ error_key ][0];
|
|
1585
1627
|
}
|
|
@@ -1605,6 +1647,241 @@ module.exports = {
|
|
|
1605
1647
|
return scope;
|
|
1606
1648
|
}, // Ends scope.throwPageError()
|
|
1607
1649
|
|
|
1650
|
+
|
|
1651
|
+
//#####################################
|
|
1652
|
+
//#####################################
|
|
1653
|
+
// Custom timeouts for **a bunch of stuff** for v7+ of cucumber
|
|
1654
|
+
//#####################################
|
|
1655
|
+
//#####################################
|
|
1656
|
+
steps: {
|
|
1657
|
+
|
|
1658
|
+
screenshot: async ( scope, name ) => {
|
|
1659
|
+
/* Download and save a screenshot. `name` does not have to be
|
|
1660
|
+
* unique as the filename will be made unique. */
|
|
1661
|
+
name = name || '';
|
|
1662
|
+
let safe_path = await scope.getSafeScenarioFilename(
|
|
1663
|
+
scope, { prefix: `screenshot_${ name }` }
|
|
1664
|
+
);
|
|
1665
|
+
|
|
1666
|
+
await scope.page.screenshot({
|
|
1667
|
+
path: `./${ safe_path }.jpg`,
|
|
1668
|
+
type: 'jpeg',
|
|
1669
|
+
fullPage: true
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1672
|
+
await scope.afterStep( scope );
|
|
1673
|
+
}, // Ends screenshot()
|
|
1674
|
+
|
|
1675
|
+
add_json_vars_to_report: async ( scope ) => {
|
|
1676
|
+
/** Uses https://docassemble.org/docs/functions.html#js_get_interview_variables
|
|
1677
|
+
* to get the variable values on the page of the interview and add
|
|
1678
|
+
* them to the report. */
|
|
1679
|
+
let data = await scope.page.evaluate(async function ( elem ) {
|
|
1680
|
+
return await get_interview_variables();
|
|
1681
|
+
});
|
|
1682
|
+
|
|
1683
|
+
// In future, may be saved to artifact file
|
|
1684
|
+
await scope.addToReport( scope, {
|
|
1685
|
+
type: `json`,
|
|
1686
|
+
value: JSON.stringify( data, null, 2 )
|
|
1687
|
+
});
|
|
1688
|
+
}, // Ends add_json_vars_to_report()
|
|
1689
|
+
|
|
1690
|
+
link_works: async ( scope, linkText ) => {
|
|
1691
|
+
let [link] = await scope.page.$x(`//a[contains(text(), "${ linkText }")]`);
|
|
1692
|
+
let prop_obj = await link.getProperty( 'href' );
|
|
1693
|
+
let actual_url = await prop_obj.jsonValue();
|
|
1694
|
+
|
|
1695
|
+
let linkPage = await scope.browser.newPage();
|
|
1696
|
+
let response = await linkPage.goto( actual_url, { waitUntil: 'domcontentloaded' });
|
|
1697
|
+
|
|
1698
|
+
let msg = `The "${ linkText }" link is broken.`;
|
|
1699
|
+
if ( !response.ok() ) { await scope.addToReport( scope, { type: `error`, value: msg }); }
|
|
1700
|
+
expect( response.ok(), msg ).to.be.true;
|
|
1701
|
+
|
|
1702
|
+
linkPage.close()
|
|
1703
|
+
|
|
1704
|
+
await scope.afterStep(scope);
|
|
1705
|
+
}, // Ends link_works()
|
|
1706
|
+
|
|
1707
|
+
set_regular_var: async ( scope, var_name, set_to ) => {
|
|
1708
|
+
/** Set any variable (with or without a choice associated with it) to a value. Set:
|
|
1709
|
+
* - Buttons associated with variables
|
|
1710
|
+
* - Dropdowns
|
|
1711
|
+
* - Checkboxes (multiple choice checkboxes must be set to "true" or "false")
|
|
1712
|
+
* - Radio buttons
|
|
1713
|
+
* - Text inputs
|
|
1714
|
+
* - Textareas
|
|
1715
|
+
*/
|
|
1716
|
+
let var_data = await scope.normalizeTable( scope, {
|
|
1717
|
+
var_data: [{ var: var_name, value: set_to, trigger: '' }],
|
|
1718
|
+
});
|
|
1719
|
+
// Don't continue if the variable doesn't navigate and if this variable
|
|
1720
|
+
// cannot be seton this page, trigger an error
|
|
1721
|
+
await scope.setFields( scope, { var_data, ensure_navigation: false, ensure_all_vars_used: true });
|
|
1722
|
+
}, // Ends set_regular_var()
|
|
1723
|
+
|
|
1724
|
+
tap_term: async ( scope, phrase ) => {
|
|
1725
|
+
/** Tap a link that doesn't navigate. Depends on the language of the text. */
|
|
1726
|
+
const [link] = await scope.page.$x(`//a[contains(text(), "${phrase}")]`);
|
|
1727
|
+
|
|
1728
|
+
let msg = `The term "${ phrase }" seems to be missing.`;
|
|
1729
|
+
if ( !link ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
|
|
1730
|
+
expect( link, msg ).to.exist;
|
|
1731
|
+
|
|
1732
|
+
await link.evaluate( elem => { return elem.click(); });
|
|
1733
|
+
|
|
1734
|
+
await scope.afterStep(scope, {waitForShowIf: true});
|
|
1735
|
+
}, // Ends tap_term()
|
|
1736
|
+
|
|
1737
|
+
sign: async ( scope ) => {
|
|
1738
|
+
/** Sign on a signature page. */
|
|
1739
|
+
await scope.sign( scope );
|
|
1740
|
+
await scope.afterStep(scope);
|
|
1741
|
+
}, // Ends sign()
|
|
1742
|
+
|
|
1743
|
+
download: async ( scope, filename ) => {
|
|
1744
|
+
/* Taps the link that leads to the given filename to trigger downloading.
|
|
1745
|
+
* and waits till the file has been downloaded before allowing the tests to continue.
|
|
1746
|
+
* WARNING: Cannot download the same file twice in a single scenario.
|
|
1747
|
+
* WARNING: Must not have any characters that are totally unique to that
|
|
1748
|
+
* page load. It's usually just the name of the file.
|
|
1749
|
+
*/
|
|
1750
|
+
let [elem] = await scope.page.$x(`//a[contains(@href, "${ filename }")]`);
|
|
1751
|
+
|
|
1752
|
+
let msg = `"${ filename }" seems to be missing. Cannot find a link to that document.`;
|
|
1753
|
+
if ( !elem ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
|
|
1754
|
+
expect( elem, msg ).to.exist;
|
|
1755
|
+
|
|
1756
|
+
scope.toDownload = filename;
|
|
1757
|
+
await elem.evaluate( elem => { return elem.click(); });
|
|
1758
|
+
await scope.detectDownloadComplete( scope );
|
|
1759
|
+
}, // Ends download()
|
|
1760
|
+
|
|
1761
|
+
set_al_name: async ( scope, var_base, name_str ) => {
|
|
1762
|
+
/** Given a variable name and a space-separated string, fills the
|
|
1763
|
+
* appropriate fields on an Assembly Line name question.
|
|
1764
|
+
* It handles any of:
|
|
1765
|
+
* 1. A one-word value to set name.text (ex: 'LandlordEtAl')
|
|
1766
|
+
* 1. An Individual's name that it will set based on the number of words:
|
|
1767
|
+
* 1. Two words - a first and last name (ex: "Tal Taylor")
|
|
1768
|
+
* 1. Three words - a first, middle, and last name (ex: "Tal Tay Taylor")
|
|
1769
|
+
* 1. Four words - a first, middle, last, and suffix (ex: "Tal Tay Taylor III")
|
|
1770
|
+
*
|
|
1771
|
+
* The string 'the var'/'the variable' part of the step is optional, as you can see below.
|
|
1772
|
+
*/
|
|
1773
|
+
let name_patterns = [
|
|
1774
|
+
[ `text` ],
|
|
1775
|
+
[ `first`, `last` ],
|
|
1776
|
+
[ `first`, `middle`, `last` ],
|
|
1777
|
+
[ `first`, `middle`, `last`, `suffix` ],
|
|
1778
|
+
];
|
|
1779
|
+
|
|
1780
|
+
let name_parts = name_str.split(' ');
|
|
1781
|
+
|
|
1782
|
+
if (name_parts.length > 4) {
|
|
1783
|
+
let name_parts_last = name_parts[name_parts.length - 1];
|
|
1784
|
+
let name_parts_slice = name_parts.slice(0, 3);
|
|
1785
|
+
name_parts_slice.push(name_parts_last);
|
|
1786
|
+
name_parts = name_parts_slice;
|
|
1787
|
+
|
|
1788
|
+
await scope.addToReport(scope, {
|
|
1789
|
+
type: `warning`,
|
|
1790
|
+
value: `The name "${name_str}" has more than 4 parts, but 4 is the maximum allowed. The test will set the name to "${name_parts.join(" ")}".`
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
let desired_pattern = name_patterns[( name_parts.length - 1 )];
|
|
1795
|
+
|
|
1796
|
+
for ( let parti = 0; parti < name_parts.length; parti++ ) {
|
|
1797
|
+
let var_name = `${ var_base }.name.${ desired_pattern[ parti ] }`
|
|
1798
|
+
let value = name_parts[ parti ];
|
|
1799
|
+
await scope.setVar( scope, var_name, value );
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
// Continue to next page...?
|
|
1803
|
+
await scope.afterStep(scope, {waitForShowIf: true});
|
|
1804
|
+
}, // Ends set_al_name()
|
|
1805
|
+
|
|
1806
|
+
set_al_address: async ( scope, var_base, address_str ) => {
|
|
1807
|
+
/** Given a variable name and a four-part address formatted as below,
|
|
1808
|
+
* sets the appropriate Assembly Line address fields.
|
|
1809
|
+
*
|
|
1810
|
+
* The string 'the var'/'the variable' part of the step is optional,
|
|
1811
|
+
* as you can see below.
|
|
1812
|
+
*/
|
|
1813
|
+
|
|
1814
|
+
// Get all the parts of the address properly separated
|
|
1815
|
+
// First, split apart each line of the address to [address line 1, address line 2, city, state zip]...
|
|
1816
|
+
let split = address_str.split(/,\s*/);
|
|
1817
|
+
// ...then, separate the state and the zip code...
|
|
1818
|
+
let last_parts = (split.pop()).split(/\s+/);
|
|
1819
|
+
// ...and add them back in, to end up with [address line 1, address line 2, city, state, zip].
|
|
1820
|
+
let address_parts = split.concat( last_parts );
|
|
1821
|
+
let start = `${ var_base }.address.`;
|
|
1822
|
+
|
|
1823
|
+
let index = 0;
|
|
1824
|
+
await scope.setVar( scope, `${start}address`, address_parts[0] );
|
|
1825
|
+
// Ensure it's without 'Unit '/'unit '
|
|
1826
|
+
await scope.setVar( scope, `${start}unit`, (address_parts[1].toLowerCase()).replace(/unit\s*/, '') );
|
|
1827
|
+
await scope.setVar( scope, `${start}city`, address_parts[2] );
|
|
1828
|
+
await scope.setVar( scope, `${start}state`, address_parts[3] );
|
|
1829
|
+
await scope.setVar( scope, `${start}zip`, address_parts[4] );
|
|
1830
|
+
|
|
1831
|
+
await scope.afterStep(scope, {waitForShowIf: true});
|
|
1832
|
+
}, // Ends set_al_address()
|
|
1833
|
+
|
|
1834
|
+
sign_in: async function ( scope, { email_secret_name, password_secret_name }) {
|
|
1835
|
+
/** Allow the developer to log a user into their server using GitHub
|
|
1836
|
+
* secrets to authenticate.
|
|
1837
|
+
*/
|
|
1838
|
+
// If there is no browser open, start a new one
|
|
1839
|
+
if (!scope.browser) {
|
|
1840
|
+
scope.browser = await scope.driver.launch({ headless: !session_vars.DEBUG });
|
|
1841
|
+
}
|
|
1842
|
+
if ( !scope.page ) { scope.page = await scope.browser.newPage(); }
|
|
1843
|
+
|
|
1844
|
+
// Go to the sign in page
|
|
1845
|
+
let login_url = `${ session_vars.get_da_server_url() }/user/sign-in`;
|
|
1846
|
+
await scope.page.goto( login_url, { waitUntil: `domcontentloaded`, timeout: scope.timeout })
|
|
1847
|
+
await scope.page.waitForSelector( `.dabody` );
|
|
1848
|
+
|
|
1849
|
+
let email = process.env[ email_secret_name ];
|
|
1850
|
+
let password = process.env[ password_secret_name ];
|
|
1851
|
+
|
|
1852
|
+
// Give as many useful messages to the user as possible if the env vars
|
|
1853
|
+
// aren't defined. Eventually, just provide a link to the docs because
|
|
1854
|
+
// this is complex
|
|
1855
|
+
let email_msg = `The email address GitHub SECRET "${ email_secret_name }" doesn't exist. Check for a typo and check your workflow file.`
|
|
1856
|
+
if ( email === undefined ) {
|
|
1857
|
+
await scope.addToReport( scope, { type: `error`, value: email_msg })
|
|
1858
|
+
}
|
|
1859
|
+
let password_msg = `The password GitHub SECRET "${ password_secret_name }" doesn't exist. Check for a typo and check your workflow file.`
|
|
1860
|
+
if ( password === undefined ) {
|
|
1861
|
+
await scope.addToReport( scope, { type: `error`, value: password_msg })
|
|
1862
|
+
}
|
|
1863
|
+
expect( email, email_msg ).to.not.equal( undefined );
|
|
1864
|
+
expect( password, password_msg ).to.not.equal( undefined );
|
|
1865
|
+
|
|
1866
|
+
// Try to log in
|
|
1867
|
+
await scope.page.type( `#email`, email );
|
|
1868
|
+
await scope.page.type( `#password`, password );
|
|
1869
|
+
await scope.page.$eval( `button[name="submit"]`, (elem) => { return elem.click(); });
|
|
1870
|
+
|
|
1871
|
+
// Wait to see the success message.
|
|
1872
|
+
// This is a bit of an assumption and needs success and failure testing
|
|
1873
|
+
// TODO: use a Promise.race to fail faster.
|
|
1874
|
+
await scope.page.waitForSelector( `.datopcenter .alert-success` );
|
|
1875
|
+
|
|
1876
|
+
await scope.addToReport( scope, {
|
|
1877
|
+
type: `row`,
|
|
1878
|
+
value: `Logged into ${ session_vars.get_da_server_url() }/user/sign-in as a ${ email } with ${ password }.`
|
|
1879
|
+
});
|
|
1880
|
+
}, // Ends sign_in
|
|
1881
|
+
|
|
1882
|
+
}, // ends scope.steps
|
|
1883
|
+
|
|
1884
|
+
|
|
1608
1885
|
//#####################################
|
|
1609
1886
|
//#####################################
|
|
1610
1887
|
// Framework Development
|