@suffolklitlab/alkiln 4.11.6 → 5.0.0-experimental2
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/pull_request_template.md +17 -0
- package/.github/workflows/test_tests.yml +7 -6
- package/CHANGELOG.md +22 -15
- package/CONTRIBUTING.md +6 -0
- package/action.yml +10 -37
- package/lib/docassemble/docassemble_api_REST.js +12 -6
- package/lib/docassemble/docassemble_api_interface.js +28 -7
- package/lib/run_cucumber.js +88 -0
- package/lib/scope.js +80 -77
- package/lib/steps.js +18 -3
- package/lib/utils/session_vars.js +78 -36
- package/npm-shrinkwrap.json +5010 -0
- package/package.json +9 -9
- package/tests/unit_tests/session_vars.test.js +111 -18
- package/cucumber.js +0 -3
- package/lib/utils/clean_reports.js +0 -29
package/lib/scope.js
CHANGED
|
@@ -2,6 +2,7 @@ const { expect } = require('chai');
|
|
|
2
2
|
const cheerio = require('cheerio');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const fs = require('fs');
|
|
5
|
+
const { globSync } = require('glob');
|
|
5
6
|
const { AxePuppeteer } = require('@axe-core/puppeteer');
|
|
6
7
|
const safe_filename = require("sanitize-filename");
|
|
7
8
|
// "en" is faster to import: https://github.com/faker-js/faker/issues/1114#issuecomment-1169532948
|
|
@@ -1528,7 +1529,10 @@ module.exports = {
|
|
|
1528
1529
|
let row = matching_input_rows[ matching_input_rows.length - 1 ];
|
|
1529
1530
|
let answer = row.value;
|
|
1530
1531
|
|
|
1531
|
-
|
|
1532
|
+
// Will implement `was_used` in cases as they come along. For now, this is
|
|
1533
|
+
// necessary for file uploads to be as flexible as any other Story Table row.
|
|
1534
|
+
let was_used = await scope.funnel_the_answer( scope, { handle, answer, field });
|
|
1535
|
+
if ( was_used === false ) { row = null; }
|
|
1532
1536
|
|
|
1533
1537
|
return row;
|
|
1534
1538
|
}, // Ends scope.setVariable()
|
|
@@ -1590,16 +1594,18 @@ module.exports = {
|
|
|
1590
1594
|
custom_datatype = await scope.get_custom_datatype( scope, { handle });
|
|
1591
1595
|
}
|
|
1592
1596
|
|
|
1597
|
+
let row_was_used = true;
|
|
1593
1598
|
// If there is a custom datatype function available, use it to set the field
|
|
1594
1599
|
if ( scope.setCustomDatatype[ custom_datatype ] !== undefined ) {
|
|
1595
1600
|
// Set da package custom fields. TODO: find `custom_handle` in the setter?
|
|
1596
1601
|
let custom_handle = await handle.evaluateHandle(( elem )=> { return elem.closest( `.da-form-group` ) });
|
|
1597
|
-
await scope.setCustomDatatype[ custom_datatype ]( scope, { handle: custom_handle, answer });
|
|
1602
|
+
row_was_used = await scope.setCustomDatatype[ custom_datatype ]( scope, { handle: custom_handle, answer });
|
|
1598
1603
|
} else {
|
|
1599
1604
|
// If no custom datatype, set a regular field
|
|
1600
|
-
await scope.enter_answer[ field.tag ]( scope, { handle, answer });
|
|
1605
|
+
row_was_used = await scope.enter_answer[ field.tag ]( scope, { handle, answer });
|
|
1601
1606
|
}
|
|
1602
1607
|
|
|
1608
|
+
return row_was_used;
|
|
1603
1609
|
}, // Ends scope.funnel_the_answer()
|
|
1604
1610
|
|
|
1605
1611
|
// Handle setting values for da custom datatypes. E.g. `da-field-container-datatype-BirthDate`
|
|
@@ -1660,10 +1666,14 @@ module.exports = {
|
|
|
1660
1666
|
}, // ends setCustomDatatype {}
|
|
1661
1667
|
|
|
1662
1668
|
enter_answer: {
|
|
1663
|
-
button: async function ( scope, { handle, answer }) {
|
|
1669
|
+
button: async function ( scope, { handle, answer }) {
|
|
1670
|
+
await scope.tapElement( scope, handle );
|
|
1671
|
+
return true;
|
|
1672
|
+
},
|
|
1664
1673
|
textarea: async function ( scope, { handle, answer }) {
|
|
1665
1674
|
await scope.setText( scope, { handle, answer });
|
|
1666
1675
|
await scope.afterStep(scope); // No showifs for this one?
|
|
1676
|
+
return true;
|
|
1667
1677
|
},
|
|
1668
1678
|
select: async function ( scope, { handle, answer }) {
|
|
1669
1679
|
// Note: I don't remember what's really going on in here.
|
|
@@ -1692,13 +1702,16 @@ module.exports = {
|
|
|
1692
1702
|
}
|
|
1693
1703
|
|
|
1694
1704
|
await scope.afterStep(scope, { waitForShowIf: true });
|
|
1705
|
+
return true;
|
|
1695
1706
|
},
|
|
1696
1707
|
canvas: async function ( scope, { handle, answer }) {
|
|
1697
1708
|
await scope.draw_signature( scope );
|
|
1698
1709
|
await scope.afterStep( scope );
|
|
1710
|
+
return true;
|
|
1699
1711
|
},
|
|
1700
1712
|
input: async function ( scope, { handle, answer }) {
|
|
1701
1713
|
/* Set value of some `input` element to the given value. */
|
|
1714
|
+
let row_was_used = true;
|
|
1702
1715
|
|
|
1703
1716
|
let type = await handle.evaluate( elem => { return elem.getAttribute('type'); });
|
|
1704
1717
|
if ( type === `radio` || type === `checkbox` ) {
|
|
@@ -1717,7 +1730,7 @@ module.exports = {
|
|
|
1717
1730
|
await scope.afterStep(scope, { waitForShowIf: true });
|
|
1718
1731
|
|
|
1719
1732
|
} else if ( type === `file` ) {
|
|
1720
|
-
await scope.uploadFiles( scope, { handle, answer });
|
|
1733
|
+
row_was_used = await scope.uploadFiles( scope, { handle, answer });
|
|
1721
1734
|
await scope.afterStep(scope, { waitForShowIf: true });
|
|
1722
1735
|
|
|
1723
1736
|
} else if (type == `date`) {
|
|
@@ -1746,6 +1759,8 @@ module.exports = {
|
|
|
1746
1759
|
await scope.setText( scope, { handle, answer });
|
|
1747
1760
|
await scope.afterStep(scope, { waitForShowIf: true });
|
|
1748
1761
|
}
|
|
1762
|
+
|
|
1763
|
+
return row_was_used;
|
|
1749
1764
|
}, // Ends scope.enter_answer.input()
|
|
1750
1765
|
hidden: async function ( scope, { handle, answer }) {
|
|
1751
1766
|
// Do nothing. The only way the code should get here is if
|
|
@@ -1762,80 +1777,57 @@ module.exports = {
|
|
|
1762
1777
|
uploadFiles: async function ( scope, { handle, answer }) {
|
|
1763
1778
|
/* Try to upload one or more files to a file-type input field.
|
|
1764
1779
|
* Does not submit, so no danger of timeout with server reload.
|
|
1780
|
+
*
|
|
1781
|
+
* Returns whether the row was used or not.
|
|
1765
1782
|
*/
|
|
1783
|
+
console.log( process.cwd() );
|
|
1766
1784
|
|
|
1767
|
-
//
|
|
1768
|
-
//
|
|
1769
|
-
let
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
if (dirs.length < 1) {
|
|
1777
|
-
let msg = `You can store files to upload in 'docassemble/${ subpackage_name }/data/source'. These tests could not find a folder like that starting at ${ process.cwd() }.`;
|
|
1778
|
-
await scope.addToReport( scope, { type: `warning`, value: msg});
|
|
1779
|
-
throw Error(msg); // Most people will want to know the sources folder path;
|
|
1780
|
-
}
|
|
1781
|
-
for (let dir of dirs) {
|
|
1782
|
-
pkg_paths.push(`${docassemble_pkg_path}/${dir}`);
|
|
1783
|
-
}
|
|
1784
|
-
} catch ( error_da ) {
|
|
1785
|
-
let msg = `You can store files to upload in 'docassemble/${ subpackage_name }/data/source'. ` +
|
|
1786
|
-
`These tests could not find a folder like that. The current directory is ` +
|
|
1787
|
-
` ${ process.cwd() }; is that in the top level folder of your docassemble package? (${ session_vars.get_package_name() })?`;
|
|
1788
|
-
await scope.addToReport( scope, { type: `warning`, value: msg});
|
|
1789
|
-
throw error_da; // Most people will want to know the sources folder path;
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
let chosen_dir = null;
|
|
1793
|
-
let err_paths = [];
|
|
1794
|
-
for (let pkg_path of pkg_paths) {
|
|
1795
|
-
let dir = `${ pkg_path }/data/sources`;
|
|
1796
|
-
try {
|
|
1797
|
-
fs.accessSync(dir);
|
|
1798
|
-
chosen_dir = dir;
|
|
1799
|
-
break;
|
|
1800
|
-
} catch ( error_full ) {
|
|
1801
|
-
err_paths.push(`'${dir}'`);
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
1804
|
-
if (chosen_dir === null) {
|
|
1805
|
-
let msg = `You can store files to upload in ${err_paths.join(", or ")}. These tests could not find any of those folders.`;
|
|
1806
|
-
await scope.addToReport( scope, { type: `warning`, value: msg });
|
|
1807
|
-
throw Error(msg); // Most people will want to know the sources folder path;
|
|
1785
|
+
// First make sure the list of files is of the correct format
|
|
1786
|
+
// Allow a typo where someone forgot to include a space after a comma
|
|
1787
|
+
let uploads_names = answer.split(`,`);
|
|
1788
|
+
if ( uploads_names.length === 0 ) {
|
|
1789
|
+
await scope.addToReport( scope, {
|
|
1790
|
+
type: `error`,
|
|
1791
|
+
value: `Your list of files to upload is empty.`
|
|
1792
|
+
});
|
|
1793
|
+
return false;
|
|
1808
1794
|
}
|
|
1809
1795
|
|
|
1810
|
-
|
|
1811
|
-
|
|
1796
|
+
// Make sure sources directory exists
|
|
1797
|
+
let dirs = globSync( `**/data/sources` );
|
|
1798
|
+
if ( dirs.length === 0 ) {
|
|
1799
|
+
await scope.addToReport( scope, {
|
|
1800
|
+
type: `warning`,
|
|
1801
|
+
value: `ALKiln couldn't find a "sources" directory in "${ process.cwd() }" where it could get files to upload.`
|
|
1802
|
+
});
|
|
1803
|
+
return false;
|
|
1812
1804
|
}
|
|
1805
|
+
let dir = dirs[0];
|
|
1813
1806
|
|
|
1814
|
-
// Allow a typo where someone forgot to include a space after a comma
|
|
1815
|
-
let file_names = answer.split(`,`);
|
|
1816
|
-
|
|
1817
|
-
// If a file exist, add it to a list of files to be uploaded. If it doesn't, add a warning.
|
|
1818
1807
|
let paths = [];
|
|
1819
|
-
|
|
1808
|
+
let missing_files = [];
|
|
1809
|
+
for ( let name of uploads_names ) {
|
|
1810
|
+
let trimmed_name = name.trim();
|
|
1811
|
+
let matching_paths = globSync( `${ dir }/${ trimmed_name }` );
|
|
1812
|
+
if ( matching_paths.length === 0 ) {
|
|
1813
|
+
missing_files.push( trimmed_name );
|
|
1814
|
+
} else {
|
|
1815
|
+
paths.push( matching_paths[0] );
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1820
1818
|
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1819
|
+
if ( missing_files.length > 0 ) {
|
|
1820
|
+
await scope.addToReport( scope, {
|
|
1821
|
+
type: `warning`,
|
|
1822
|
+
value: `ALKiln could not find "${ missing_files.join('", "') }" in "${ dir }".`
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
1825
|
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
} catch ( err ) {
|
|
1830
|
-
// TODO: Should this throw an error?
|
|
1831
|
-
await scope.addToReport( scope, { type: `warning`, value: `Could not find "${ name }" in "${ chosen_dir }"` });
|
|
1832
|
-
}
|
|
1826
|
+
// If all files missing, return that the row wasn't used
|
|
1827
|
+
if ( missing_files.length === uploads_names.length ) {
|
|
1828
|
+
return false;
|
|
1833
1829
|
}
|
|
1834
1830
|
|
|
1835
|
-
if ( paths.length === 0 ) {
|
|
1836
|
-
await scope.addToReport( scope, { type: `error`, value: `Could not find "${answer}".` });
|
|
1837
|
-
throw ReferenceError(`Could not find "${answer}".`)
|
|
1838
|
-
}
|
|
1839
1831
|
// Prepare to wait for upload to complete
|
|
1840
1832
|
let name = await handle.evaluate( elem => { return elem.getAttribute(`name`); });
|
|
1841
1833
|
let selector = `*[name="${ name }"]`
|
|
@@ -1855,8 +1847,9 @@ module.exports = {
|
|
|
1855
1847
|
await wait_for_disable;
|
|
1856
1848
|
// When it's again enabled, the files have finished loading. Or so I have observed.
|
|
1857
1849
|
await scope.page.waitForSelector( enabled_selector );
|
|
1858
|
-
|
|
1859
1850
|
|
|
1851
|
+
// Return that the row was used
|
|
1852
|
+
return true;
|
|
1860
1853
|
}, // Ends scope.uploadFiles()
|
|
1861
1854
|
|
|
1862
1855
|
setCheckbox: async function ( scope, { label, answer }) {
|
|
@@ -1877,16 +1870,26 @@ module.exports = {
|
|
|
1877
1870
|
}, 500)
|
|
1878
1871
|
},
|
|
1879
1872
|
|
|
1880
|
-
draw_signature: async function ( scope ) {
|
|
1873
|
+
draw_signature: async function ( scope, name ) {
|
|
1881
1874
|
/* On a signature page, there should only be a single signature canvas
|
|
1882
1875
|
* and a continue button. Draw in the #dasigcanvas element. `<canvas>`
|
|
1883
1876
|
* takes a while to load. */
|
|
1884
|
-
let
|
|
1877
|
+
let canvas = await scope.page.waitForSelector( `#dasigcanvas` );
|
|
1885
1878
|
let msg = `Could not find a signature field.`;
|
|
1886
|
-
if ( !
|
|
1887
|
-
expect(
|
|
1888
|
-
|
|
1889
|
-
|
|
1879
|
+
if ( !canvas ) { await scope.addToReport(scope, { type: `error`, value: msg }); }
|
|
1880
|
+
expect( canvas, msg ).to.exist;
|
|
1881
|
+
|
|
1882
|
+
// Provides functionality to draw name arg on canvas
|
|
1883
|
+
if (name !== undefined) {
|
|
1884
|
+
await scope.page.$eval(`#dasigcanvas`, (canvas, name) => {
|
|
1885
|
+
const ctx = canvas.getContext("2d");
|
|
1886
|
+
ctx.font = "24px serif";
|
|
1887
|
+
ctx.fillText(name, 10, 50);
|
|
1888
|
+
}, name);
|
|
1889
|
+
}
|
|
1890
|
+
let bounding_box = await canvas.boundingBox();
|
|
1891
|
+
// We draw the dot because it allows the developer to tap continue
|
|
1892
|
+
// Docassemble does not allow to tap continue unless the user interacts with the canvas
|
|
1890
1893
|
await scope.page.mouse.move(bounding_box.x + bounding_box.width / 2, bounding_box.y + bounding_box.height / 2);
|
|
1891
1894
|
await scope.page.mouse.down();
|
|
1892
1895
|
// await scope.page.mouse.move(1, 1); // Too big? Wait a while before drawing?
|
|
@@ -2415,9 +2418,9 @@ module.exports = {
|
|
|
2415
2418
|
await scope.afterStep(scope, {waitForShowIf: true});
|
|
2416
2419
|
}, // Ends scope.steps.tap_term()
|
|
2417
2420
|
|
|
2418
|
-
sign: async ( scope ) => {
|
|
2421
|
+
sign: async ( scope, name ) => {
|
|
2419
2422
|
/** Sign on a signature page. */
|
|
2420
|
-
await scope.draw_signature( scope );
|
|
2423
|
+
await scope.draw_signature( scope, name );
|
|
2421
2424
|
await scope.afterStep(scope);
|
|
2422
2425
|
}, // Ends scope.steps.sign()
|
|
2423
2426
|
|
package/lib/steps.js
CHANGED
|
@@ -92,7 +92,7 @@ BeforeAll(async () => {
|
|
|
92
92
|
// Make the folder to store all the files for this run of the tests
|
|
93
93
|
scope.paths.artifacts = files.make_artifacts_folder();
|
|
94
94
|
// Save its name in a file. `session_vars` can get it back later.
|
|
95
|
-
//
|
|
95
|
+
// The value we are saving is used by at least run_cucumber.js.
|
|
96
96
|
session_vars.save_artifacts_path_name( scope.paths.artifacts );
|
|
97
97
|
|
|
98
98
|
scope.paths.report = `${ scope.paths.artifacts }/report.txt`;
|
|
@@ -103,7 +103,14 @@ BeforeAll(async () => {
|
|
|
103
103
|
Before(async (scenario) => {
|
|
104
104
|
// Create browser, which can't be created in BeforeAll() where .driver doesn't exist for some reason
|
|
105
105
|
if (!scope.browser) {
|
|
106
|
-
|
|
106
|
+
if (session_vars.get_origin() === 'playground') {
|
|
107
|
+
// Will only run in the Playground outside of a sandbox. TODO: There's a
|
|
108
|
+
// better way to do this, though it's more complicated. See comments in
|
|
109
|
+
// https://github.com/SuffolkLITLab/ALKiln/issues/661
|
|
110
|
+
scope.browser = await scope.driver.launch({ args: ['--no-sandbox'] });
|
|
111
|
+
} else {
|
|
112
|
+
scope.browser = await scope.driver.launch({ headless: !session_vars.get_debug(), devtools: session_vars.get_debug() });
|
|
113
|
+
}
|
|
107
114
|
}
|
|
108
115
|
// Clean up all previously existing pages
|
|
109
116
|
for (const page of await scope.browser.pages()) {
|
|
@@ -973,13 +980,21 @@ When('I tap the defined text link {string}', { timeout: -1 }, async ( phrase ) =
|
|
|
973
980
|
await scope.steps.tap_term( scope, phrase );
|
|
974
981
|
});
|
|
975
982
|
|
|
976
|
-
Then(
|
|
983
|
+
Then(/^I sign$/, { timeout: -1 }, async () => {
|
|
977
984
|
return wrapPromiseWithTimeout(
|
|
978
985
|
scope.steps.sign( scope ),
|
|
979
986
|
scope.timeout
|
|
980
987
|
);
|
|
981
988
|
});
|
|
982
989
|
|
|
990
|
+
Then('I sign with the name {string}', { timeout: -1 }, async ( name ) => {
|
|
991
|
+
/* Signs with the string argument */
|
|
992
|
+
return wrapPromiseWithTimeout(
|
|
993
|
+
scope.steps.sign( scope, name ),
|
|
994
|
+
scope.timeout
|
|
995
|
+
);
|
|
996
|
+
});
|
|
997
|
+
|
|
983
998
|
When(/I download "([^"]+)"$/, { timeout: -1 }, async ( filename ) => {
|
|
984
999
|
/* Taps the link that leads to the given filename to trigger downloading.
|
|
985
1000
|
* and waits till the file has been downloaded before allowing the tests to continue.
|
|
@@ -23,9 +23,27 @@ module.exports = session_vars;
|
|
|
23
23
|
// These default to null
|
|
24
24
|
// TODO: How to isolate debug elsewhere so this file can use the logger?
|
|
25
25
|
session_vars.get_debug = function () { return process.env.DEBUG || null; };
|
|
26
|
+
session_vars.is_dev_env = function () { return process.env.DEV || null; }
|
|
27
|
+
|
|
26
28
|
session_vars.get_dev_api_key = function () { return process.env.DOCASSEMBLE_DEVELOPER_API_KEY || null; };
|
|
27
29
|
session_vars.get_repo_url = function () { return process.env.REPO_URL || null; };
|
|
28
|
-
|
|
30
|
+
|
|
31
|
+
session_vars.get_origin = function () {
|
|
32
|
+
/** The location from which the tests are being run. Possible
|
|
33
|
+
* valid return values: 'playground', 'github', or 'local'.
|
|
34
|
+
* Default is 'local' for ease of our own onboarding process.
|
|
35
|
+
*/
|
|
36
|
+
return process.env._ORIGIN || 'local';
|
|
37
|
+
};
|
|
38
|
+
session_vars.get_user_project_name = function () { return process.env._PROJECT_NAME || null; };
|
|
39
|
+
session_vars.get_user_id = function () { return process.env._USER_ID || null; };
|
|
40
|
+
session_vars.get_tags = function () {
|
|
41
|
+
if ( process.env._TAGS ) {
|
|
42
|
+
return process.env._TAGS.split(` `);
|
|
43
|
+
} else {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
};
|
|
29
47
|
|
|
30
48
|
// More complex logic
|
|
31
49
|
session_vars.get_server_reload_timeout = function () {
|
|
@@ -130,7 +148,10 @@ session_vars.save_project_name = function ( project_name ) {
|
|
|
130
148
|
}; // Ends save_project_name()
|
|
131
149
|
|
|
132
150
|
session_vars.get_project_name = function () {
|
|
133
|
-
/* Get the name of the current docassemble Project
|
|
151
|
+
/* Get the name of the current docassemble Project */
|
|
152
|
+
if ( session_vars.get_origin() === 'playground' ) {
|
|
153
|
+
return session_vars.get_user_project_name();
|
|
154
|
+
}
|
|
134
155
|
let json = JSON.parse( fs.readFileSync( runtime_config_path ));
|
|
135
156
|
let project_name = json[ project_name_key ] || null;
|
|
136
157
|
if ( session_vars.get_debug() ) { console.log( `ALKiln debug: Project name from file is "${ project_name }".` ); }
|
|
@@ -174,7 +195,7 @@ session_vars.save_artifacts_path_name = function ( path_name ) {
|
|
|
174
195
|
console.log( `ALKiln debug: Stored name of artifacts directory "${ path_name }" locally.` );
|
|
175
196
|
}
|
|
176
197
|
return file;
|
|
177
|
-
}; // Ends
|
|
198
|
+
}; // Ends save_artifacts_path_name()
|
|
178
199
|
|
|
179
200
|
session_vars.get_artifacts_path_name = function () {
|
|
180
201
|
/* Get the name of the current artifacts path. */
|
|
@@ -187,48 +208,69 @@ session_vars.get_artifacts_path_name = function () {
|
|
|
187
208
|
} catch ( error ) {
|
|
188
209
|
return null;
|
|
189
210
|
}
|
|
190
|
-
}; // Ends
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Ensure all required environment variables exist. Add each missing variable
|
|
194
|
-
* to an error that will be thrown at the end.
|
|
195
|
-
*
|
|
196
|
-
* @throws ReferenceError with a message for all missing variables.
|
|
197
|
-
*/
|
|
198
|
-
session_vars.validateEnvironment = function() {
|
|
199
|
-
/** Throw a useful error for the developer if any of the required env vars
|
|
200
|
-
* are missing. For most devs, these will be GitHub secrets. */
|
|
201
|
-
|
|
202
|
-
// Note: We are checking for BASE_URL, but we are referring it as SERVER_URL as that
|
|
203
|
-
// is the name used in GitHub secrets, which is what most devs will be using.
|
|
211
|
+
}; // Ends get_artifacts_path_name()
|
|
204
212
|
|
|
213
|
+
session_vars.validateEnvironment = function() {
|
|
214
|
+
/** Throw a useful error for the developer if any of the env vars
|
|
215
|
+
* required by absolutely all tests are missing. Gather all
|
|
216
|
+
* missing var names into one message to ease dev process.
|
|
217
|
+
*
|
|
218
|
+
* Rational for doing it all in here:
|
|
219
|
+
* 1. Easier to test
|
|
220
|
+
* 2. Will avoid piecemeal errors in separate locations
|
|
221
|
+
*
|
|
222
|
+
* TODO: Also validate format of variables?
|
|
223
|
+
*
|
|
224
|
+
* @throws ReferenceError with a message for all missing variables.
|
|
225
|
+
*/
|
|
205
226
|
let errorMessage = '';
|
|
206
227
|
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
if ( !session_vars.get_dev_api_key() )
|
|
210
|
-
{
|
|
211
|
-
errorMessage += `\nThe DOCASSEMBLE_DEVELOPER_API_KEY GitHub secret must be defined. The DOCASSEMBLE_DEVELOPER_API_KEY is a docassemble API key you created for your server's testing account. It should have developer permissions.`
|
|
212
|
-
}
|
|
213
|
-
if ( !session_vars.get_da_server_url() )
|
|
214
|
-
{
|
|
228
|
+
// Env vars required for any tests
|
|
229
|
+
if ( !session_vars.get_da_server_url() ) {
|
|
215
230
|
errorMessage += `\nThe SERVER_URL GitHub secret must be defined. The SERVER_URL is the URL of the Docassemble Server where the tests should be run.`;
|
|
216
231
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
{
|
|
224
|
-
|
|
225
|
-
|
|
232
|
+
|
|
233
|
+
let origin = session_vars.get_origin();
|
|
234
|
+
if ( origin !== 'github' && origin !== 'local' && origin !== 'playground' ) {
|
|
235
|
+
errorMessage += `\nThe _ORIGIN environment variable should be set to a valid value automatically. If you see this error, file an issue at https://github.com/suffolkLITLab/docassemble-ALKilnInThePlayground. It must be defined as either 'playground', 'github', or 'local', but its value was ${origin}`;
|
|
236
|
+
|
|
237
|
+
// If origin has one of the allowed values, we can do the rest of the validations
|
|
238
|
+
} else {
|
|
239
|
+
|
|
240
|
+
// Env vars required for github or local runs, as both create Projects
|
|
241
|
+
// in the Playground and pull the repo from GitHub
|
|
242
|
+
if ( origin === 'github' || origin === 'local' ) {
|
|
243
|
+
if ( !session_vars.get_dev_api_key() ) {
|
|
244
|
+
errorMessage += `\nThe DOCASSEMBLE_DEVELOPER_API_KEY GitHub secret must be defined. The DOCASSEMBLE_DEVELOPER_API_KEY is a docassemble API key you created for your server's testing account. It should have developer permissions.`
|
|
245
|
+
}
|
|
246
|
+
// Github automatically gives this, but may be missing locally.
|
|
247
|
+
if ( !session_vars.get_repo_url() ) {
|
|
248
|
+
errorMessage += `\nThe REPO_URL environment variable must be defined. The REPO_URL is the URL to the GitHub repo whose Actions are being run.`;
|
|
249
|
+
}
|
|
250
|
+
// Github automatically gives this, but may be missing locally.
|
|
251
|
+
if ( !session_vars.get_branch_name() ) {
|
|
252
|
+
errorMessage += `\nThe BRANCH_NAME environment variable must be defined. The BRANCH_NAME is the name of the repository branch that should be pulled into the docassemble project.`;
|
|
253
|
+
}
|
|
254
|
+
} // ends github and local env vars
|
|
255
|
+
|
|
256
|
+
// We're the ones ensuring these exist for Playground runs
|
|
257
|
+
if ( origin === 'playground' ) {
|
|
258
|
+
if ( !session_vars.get_user_project_name() ) {
|
|
259
|
+
errorMessage += `\nThe _PROJECT_NAME environment variable should automatically exist. If you see this error, file an issue at https://github.com/suffolkLITLab/docassemble-ALKilnInThePlayground. It is the name of the Project the developer chose to test.`
|
|
260
|
+
}
|
|
261
|
+
if ( !session_vars.get_user_id() ) {
|
|
262
|
+
errorMessage += `\nThe _USER_ID environment variable should automatically exist. If you see this error, file an issue at https://github.com/suffolkLITLab/docassemble-ALKilnInThePlayground. It is the user ID of the developer on the server.`
|
|
263
|
+
}
|
|
264
|
+
// Tags are optional
|
|
265
|
+
} // ends Playground env vars
|
|
266
|
+
|
|
267
|
+
} // ends validation based on origin
|
|
226
268
|
|
|
227
269
|
errorMessage = errorMessage.trim();
|
|
228
270
|
|
|
229
271
|
if (errorMessage !== '') {
|
|
230
|
-
throw new ReferenceError(errorMessage);
|
|
272
|
+
throw new ReferenceError(`ALKiln error: ${errorMessage}`);
|
|
231
273
|
}
|
|
232
274
|
};
|
|
233
|
-
|
|
275
|
+
|
|
234
276
|
session_vars.validateEnvironment();
|