@suffolklitlab/alkiln 5.3.0 → 5.4.1-feat-github-server-15
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/github_server.yml +85 -0
- package/CHANGELOG.md +13 -0
- package/action.yml +33 -10
- package/github_server/action.yml +165 -0
- package/lib/docassemble/docassemble_api_REST.js +10 -2
- package/lib/docassemble/docassemble_api_interface.js +15 -10
- package/lib/docassemble/get_base_interview_url.js +13 -3
- package/lib/docassemble/server_install.js +40 -0
- package/lib/docassemble/setup.js +1 -1
- package/lib/run_cucumber.js +67 -34
- package/lib/scope.js +87 -58
- package/lib/steps.js +24 -1
- package/lib/utils/log.js +1 -1
- package/lib/utils/pdf_reader.js +113 -0
- package/lib/utils/session_vars.js +157 -7
- package/npm-shrinkwrap.json +452 -2262
- package/package.json +7 -2
- package/setup.py +2 -2
package/lib/scope.js
CHANGED
|
@@ -9,6 +9,7 @@ const safe_filename = require("sanitize-filename");
|
|
|
9
9
|
const faker = require('@faker-js/faker/locale/en').faker;
|
|
10
10
|
|
|
11
11
|
// Ours
|
|
12
|
+
const pdf_reader = require('./utils/pdf_reader');
|
|
12
13
|
const session_vars = require('./utils/session_vars');
|
|
13
14
|
const get_base_interview_url = require(`./docassemble/get_base_interview_url`);
|
|
14
15
|
const time = require('./utils/time');
|
|
@@ -45,6 +46,9 @@ let printable_var_value = function(row) {
|
|
|
45
46
|
/* `row` might be a row, a source row, or an original row. */
|
|
46
47
|
let original = get_original_row(row);
|
|
47
48
|
if ( is_secret_var(original) ) {
|
|
49
|
+
if ( session_vars.get_dangerous_debug() ) {
|
|
50
|
+
return `alkiln_dangerous ${ row.value }`;
|
|
51
|
+
}
|
|
48
52
|
return original.flags.secret_name;
|
|
49
53
|
}
|
|
50
54
|
return row.value;
|
|
@@ -1679,90 +1683,77 @@ module.exports = {
|
|
|
1679
1683
|
return valid_date;
|
|
1680
1684
|
}, // Ends scope.today_to_date()
|
|
1681
1685
|
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
*
|
|
1686
|
+
findFiles: async function( scope, {to_find_names}) {
|
|
1687
|
+
/** Returns a list of paths to the sources folder associated
|
|
1688
|
+
* with the given list of file names.
|
|
1685
1689
|
*
|
|
1686
|
-
*
|
|
1690
|
+
* @param to_find_names { [str] } List of file names
|
|
1691
|
+
*
|
|
1692
|
+
* @returns { [str] } sources folder paths
|
|
1687
1693
|
*/
|
|
1688
|
-
|
|
1689
1694
|
// First make sure the list of files was defined. Don't remove spaces yet -
|
|
1690
1695
|
// allow a typo where someone forgot to include a space after a comma.
|
|
1691
|
-
|
|
1692
|
-
if (
|
|
1693
|
-
reports.addToReport( scope, {
|
|
1694
|
-
type: `error`,
|
|
1695
|
-
value: `Your list of files to upload is empty.`
|
|
1696
|
-
});
|
|
1697
|
-
return false; // The row wasn't used
|
|
1698
|
-
}
|
|
1699
|
-
|
|
1700
|
-
let dirs = [];
|
|
1701
|
-
if ( session_vars.get_origin() === `playground` ) {
|
|
1702
|
-
let files_playground = fg.sync(
|
|
1703
|
-
[ `/usr/share/docassemble/files/playgroundsources/${session_vars.get_user_id()}/${session_vars.get_user_project_name()}` ],
|
|
1704
|
-
{ onlyFiles: false, suppressErrors: true }
|
|
1705
|
-
);
|
|
1706
|
-
let files_s3 = fg.sync(
|
|
1707
|
-
[ `/tmp/playgroundsources/${session_vars.get_user_id()}/${session_vars.get_user_project_name()}` ],
|
|
1708
|
-
{ onlyFiles: false, suppressErrors: true }
|
|
1709
|
-
);
|
|
1710
|
-
|
|
1711
|
-
dirs = files_playground.concat( files_s3 );
|
|
1712
|
-
} else {
|
|
1713
|
-
dirs = fg.sync(
|
|
1714
|
-
[ `**/docassemble/**/data/sources` ],
|
|
1715
|
-
{ onlyFiles: false, suppressErrors: true }
|
|
1716
|
-
);
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
if ( session_vars.get_debug() ) {
|
|
1720
|
-
console.log( `All discovered dirs to upload from:`, dirs );
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
// Make sure sources directory exists and has files in it
|
|
1724
|
-
if ( dirs.length === 0 ) {
|
|
1696
|
+
// TODO: wish we had the var name here that folks are trying to set
|
|
1697
|
+
if ( to_find_names.length === 0 ) {
|
|
1725
1698
|
reports.addToReport( scope, {
|
|
1726
1699
|
type: `warning`,
|
|
1727
|
-
value: `
|
|
1728
|
-
+ `but ALKiln couldn't find the directory or it was empty.`
|
|
1700
|
+
value: `Your Step is missing the names of which file(s) to use.`
|
|
1729
1701
|
});
|
|
1730
|
-
return
|
|
1702
|
+
return [];
|
|
1731
1703
|
}
|
|
1732
1704
|
|
|
1733
|
-
|
|
1734
|
-
|
|
1705
|
+
let sources_dirs = session_vars.get_sources_paths();
|
|
1706
|
+
// Get the full paths of all the existing files
|
|
1735
1707
|
let paths = [];
|
|
1736
1708
|
let missing_files = []; // For any user warnings
|
|
1737
|
-
for ( let name of
|
|
1738
|
-
|
|
1709
|
+
for ( let name of to_find_names ) {
|
|
1710
|
+
|
|
1739
1711
|
let trimmed_name = name.trim();
|
|
1740
1712
|
let matching_paths = [];
|
|
1741
|
-
|
|
1742
|
-
for ( let one_dir of
|
|
1743
|
-
|
|
1744
|
-
|
|
1713
|
+
// Look for the file in all "sources" folders
|
|
1714
|
+
for ( let one_dir of sources_dirs ) {
|
|
1715
|
+
let new_paths = fg.sync(
|
|
1716
|
+
[ `${ one_dir }/${ trimmed_name }` ],
|
|
1717
|
+
{ suppressErrors: true }
|
|
1718
|
+
);
|
|
1745
1719
|
matching_paths.push( ...new_paths );
|
|
1746
|
-
}
|
|
1720
|
+
}
|
|
1747
1721
|
|
|
1748
1722
|
if ( matching_paths.length === 0 ) {
|
|
1749
1723
|
missing_files.push( trimmed_name );
|
|
1750
1724
|
} else {
|
|
1751
1725
|
paths.push( ...matching_paths );
|
|
1752
1726
|
}
|
|
1753
|
-
|
|
1754
1727
|
} // ends for each file
|
|
1755
1728
|
|
|
1756
1729
|
if ( missing_files.length > 0 ) {
|
|
1757
1730
|
reports.addToReport( scope, {
|
|
1758
1731
|
type: `warning`,
|
|
1759
|
-
value: `ALKiln could not find "${ missing_files.join('", "') }" in
|
|
1732
|
+
value: `ALKiln could not find "${ missing_files.join('", "') }" in the folder(s) at "${ sources_dirs.join('", "') }".`
|
|
1760
1733
|
});
|
|
1761
1734
|
}
|
|
1762
1735
|
|
|
1763
|
-
// If all files missing, return
|
|
1764
|
-
if ( missing_files.length ===
|
|
1765
|
-
return
|
|
1736
|
+
// If all files missing, return an empty array
|
|
1737
|
+
if ( missing_files.length === to_find_names.length ) {
|
|
1738
|
+
return [];
|
|
1739
|
+
}
|
|
1740
|
+
return paths;
|
|
1741
|
+
}, // Ends scope.findFiles()
|
|
1742
|
+
|
|
1743
|
+
uploadFiles: async function ( scope, { handle, answer }) {
|
|
1744
|
+
/* Try to upload one or more files to a file-type input field.
|
|
1745
|
+
* Does not submit, so no danger of timeout with server reload.
|
|
1746
|
+
*
|
|
1747
|
+
* Returns whether the row was used or not.
|
|
1748
|
+
*/
|
|
1749
|
+
|
|
1750
|
+
// First make sure the list of files was defined. Don't remove spaces yet -
|
|
1751
|
+
// allow a typo where someone forgot to include a space after a comma.
|
|
1752
|
+
let to_find_names = answer.split(`,`);
|
|
1753
|
+
let paths = await scope.findFiles(scope, {to_find_names});
|
|
1754
|
+
if (paths.length == 0) {
|
|
1755
|
+
// The row wasn't used
|
|
1756
|
+
return false;
|
|
1766
1757
|
}
|
|
1767
1758
|
|
|
1768
1759
|
// Prepare to wait for upload to complete
|
|
@@ -1988,7 +1979,7 @@ module.exports = {
|
|
|
1988
1979
|
}
|
|
1989
1980
|
if ( num_unused_rows > 0 ) {
|
|
1990
1981
|
let { id } = await scope.examinePageID( scope, 'none to match' );
|
|
1991
|
-
let msg = `
|
|
1982
|
+
let msg = `Error on page "${id}". See screenshot and see text above for more details.`;
|
|
1992
1983
|
reports.addToReport(scope, { type: `error`, value: msg });
|
|
1993
1984
|
expect( num_unused_rows, msg ).to.equal( 0 );
|
|
1994
1985
|
}
|
|
@@ -2449,6 +2440,10 @@ module.exports = {
|
|
|
2449
2440
|
/** Sets a non-story table variable to a "secret" value (i.e. an env var) */
|
|
2450
2441
|
// Prevent pictures of a screen with a secret.
|
|
2451
2442
|
scope.disable_error_screenshot = true;
|
|
2443
|
+
if ( session_vars.get_dangerous_debug() ) {
|
|
2444
|
+
scope.disable_error_screenshot = false;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2452
2447
|
let var_data = await scope.normalizeTable( scope, {
|
|
2453
2448
|
var_data: [{
|
|
2454
2449
|
var: var_name,
|
|
@@ -2503,6 +2498,32 @@ module.exports = {
|
|
|
2503
2498
|
await scope.detectDownloadComplete( scope );
|
|
2504
2499
|
}, // Ends scope.steps.download()
|
|
2505
2500
|
|
|
2501
|
+
compare_pdfs: async function (scope, {existing_pdf_path, new_pdf_path}) {
|
|
2502
|
+
let existing_paths = await scope.findFiles(scope, {to_find_names: [existing_pdf_path]});
|
|
2503
|
+
if (existing_paths.length == 0) {
|
|
2504
|
+
// The row wasn't used
|
|
2505
|
+
let msg = `Could not find the existing PDF at ${ existing_pdf_path }`;
|
|
2506
|
+
reports.addToReport(scope, { type: `error`, value: msg });
|
|
2507
|
+
scope.failed_pdf_compares.push(msg);
|
|
2508
|
+
// Return early since we couldn't find the PDF
|
|
2509
|
+
return;
|
|
2510
|
+
}
|
|
2511
|
+
let full_existing_path = existing_paths[0];
|
|
2512
|
+
let full_new_pdf_path = `${scope.paths.scenario}/${new_pdf_path}`;
|
|
2513
|
+
let diffs = await pdf_reader.compare_pdfs(full_existing_path, full_new_pdf_path);
|
|
2514
|
+
if (diffs) {
|
|
2515
|
+
let added_str = diffs.filter(part => part.added).reduce((err_str, part) => {
|
|
2516
|
+
return err_str + `- ${ part.value }\n`
|
|
2517
|
+
}, 'The new PDF added: \n');
|
|
2518
|
+
let removed_str = diffs.filter(part => part.removed).reduce((err_str, part) => {
|
|
2519
|
+
return err_str + `- ${ part.value }\n`
|
|
2520
|
+
}, 'The new PDF removed: \n');
|
|
2521
|
+
let msg = `The PDFs were not the same.\n${ added_str }\n${removed_str}\n\n You can see the full PDFs at ${ full_existing_path } and ${ full_new_pdf_path}`;
|
|
2522
|
+
reports.addToReport(scope, { type: `error`, value: msg });
|
|
2523
|
+
scope.failed_pdf_compares.push(msg);
|
|
2524
|
+
}
|
|
2525
|
+
},
|
|
2526
|
+
|
|
2506
2527
|
set_al_name: async ( scope, var_base, name_str ) => {
|
|
2507
2528
|
/** Given a variable name and a space-separated string, fills the
|
|
2508
2529
|
* appropriate fields on an Assembly Line name question.
|
|
@@ -2592,7 +2613,10 @@ module.exports = {
|
|
|
2592
2613
|
*/
|
|
2593
2614
|
// Don't take a picture of a failed login in case one of the inputs is correct
|
|
2594
2615
|
scope.disable_error_screenshot = true;
|
|
2595
|
-
|
|
2616
|
+
if ( session_vars.get_dangerous_debug() ) {
|
|
2617
|
+
scope.disable_error_screenshot = false;
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2596
2620
|
// If there is no browser open, start a new one
|
|
2597
2621
|
if (!scope.browser) {
|
|
2598
2622
|
scope.browser = await scope.driver.launch({ headless: !session_vars.DEBUG });
|
|
@@ -2625,6 +2649,10 @@ module.exports = {
|
|
|
2625
2649
|
let email = process.env[ email_secret_name ];
|
|
2626
2650
|
let password = process.env[ password_secret_name ];
|
|
2627
2651
|
|
|
2652
|
+
if ( session_vars.get_dangerous_debug() ) {
|
|
2653
|
+
log.debug({ type: `unsafe`, data: `login: ${ email }, ${ password }` });
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2628
2656
|
// Give as many useful messages to the user as possible if the env vars
|
|
2629
2657
|
// aren't defined. Eventually, just provide a link to the docs because
|
|
2630
2658
|
// this is complex
|
|
@@ -2930,4 +2958,5 @@ module.exports = {
|
|
|
2930
2958
|
|
|
2931
2959
|
return none_included;
|
|
2932
2960
|
},
|
|
2961
|
+
|
|
2933
2962
|
};
|
package/lib/steps.js
CHANGED
|
@@ -147,6 +147,7 @@ Before(async (scenario) => {
|
|
|
147
147
|
scope.page_id = null;
|
|
148
148
|
scope.check_all_for_a11y = false;
|
|
149
149
|
scope.passed_all_for_a11y = true;
|
|
150
|
+
scope.failed_pdf_compares = [];
|
|
150
151
|
|
|
151
152
|
// Reset default timeout
|
|
152
153
|
scope.timeout = default_timeout;
|
|
@@ -705,6 +706,19 @@ Then(/I get the(?: page's)?(?: JSON)? var(?:iable)?s and val(?:ue)?s/i, { timeou
|
|
|
705
706
|
);
|
|
706
707
|
});
|
|
707
708
|
|
|
709
|
+
Then(/I expect the baseline PDF "([^"]+)" and the new PDF "([^"]+)" to be the same/i, async (existing_pdf_path, new_pdf_path) => {
|
|
710
|
+
/** Compare the text or fields of a baseline PDF and the newly downloaded PDF.
|
|
711
|
+
*
|
|
712
|
+
* Variations:
|
|
713
|
+
* Then I expect the baseline PDF "simple-doc-Baseline.pdf" and the new PDF "simple-doc.pdf" to be the same
|
|
714
|
+
*/
|
|
715
|
+
|
|
716
|
+
return await wrapPromiseWithTimeout(
|
|
717
|
+
scope.steps.compare_pdfs(scope, {existing_pdf_path, new_pdf_path}),
|
|
718
|
+
scope.timeout
|
|
719
|
+
);
|
|
720
|
+
});
|
|
721
|
+
|
|
708
722
|
//#####################################
|
|
709
723
|
//#####################################
|
|
710
724
|
// Actions
|
|
@@ -1104,12 +1118,21 @@ After(async function(scenario) {
|
|
|
1104
1118
|
if ( scope.check_all_for_a11y && !scope.passed_all_for_a11y) {
|
|
1105
1119
|
// The fact that the scenario failed has already been noted earlier in the report
|
|
1106
1120
|
changeable_test_status = `FAILED`;
|
|
1107
|
-
|
|
1121
|
+
reports.addToReport(scope, {
|
|
1108
1122
|
type: `error`,
|
|
1109
1123
|
value: `Accessibility standards weren't met. See information above.`
|
|
1110
1124
|
});
|
|
1111
1125
|
}
|
|
1112
1126
|
|
|
1127
|
+
if ( scope.failed_pdf_compares.length > 0) {
|
|
1128
|
+
let msg = scope.failed_pdf_compares.reduce((str, new_msg) => `${ str }\n---\n${ new_msg }`)
|
|
1129
|
+
changeable_test_status = `FAILED`;
|
|
1130
|
+
reports.addToReport(scope, {
|
|
1131
|
+
type: `error`,
|
|
1132
|
+
value: `PDF comparison failed ${ scope.failed_pdf_compares.length } time(s)\n---\n${ msg }\n---\n`
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1113
1136
|
// Record status before internal report tests can interfere with values
|
|
1114
1137
|
reports.setReportScenarioStatus( scope, { status: changeable_test_status });
|
|
1115
1138
|
|
package/lib/utils/log.js
CHANGED
|
@@ -42,7 +42,7 @@ log.error = function ({ type='', pre='', data='', post='' }) {
|
|
|
42
42
|
|
|
43
43
|
log.debug = function ({ type='', pre='', data='', post='' }) {
|
|
44
44
|
/* If debugging is turned on, log a message that includes `ALKiln` and `debug` */
|
|
45
|
-
if ( session_vars.get_debug() ) {
|
|
45
|
+
if ( session_vars.get_debug() || session_vars.get_dangerous_debug() ) {
|
|
46
46
|
let start = create_msg_start([ type, 'debug' ], pre );
|
|
47
47
|
// Log them each on a separate line
|
|
48
48
|
console.log( `ALKiln ${ type } debug: ${ pre }` );
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// All functions dealing with getting information from PDFs
|
|
2
|
+
|
|
3
|
+
const pdfjs_lib = require("pdfjs-dist/legacy/build/pdf.js");
|
|
4
|
+
const diff_lib = require("diff");
|
|
5
|
+
|
|
6
|
+
let _load_pdf = async function(pdf_path) {
|
|
7
|
+
// Simple wrapper to get the document
|
|
8
|
+
return await pdfjs_lib.getDocument({
|
|
9
|
+
url: pdf_path,
|
|
10
|
+
// https://github.com/mozilla/pdf.js/issues/4244#issuecomment-1479534301
|
|
11
|
+
useSystemFonts: true
|
|
12
|
+
}).promise;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
let _get_content = async function(pdf_doc) {
|
|
16
|
+
let num_pages = pdf_doc.numPages;
|
|
17
|
+
let contents = "";
|
|
18
|
+
for (var i = 1; i <= num_pages; i++) {
|
|
19
|
+
const page_contents = await (await pdf_doc.getPage(i)).getTextContent();
|
|
20
|
+
contents += "\n" + page_contents.items.map((item) => item.str).join(" ");
|
|
21
|
+
}
|
|
22
|
+
return contents;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
let pdf_reader = {};
|
|
26
|
+
|
|
27
|
+
pdf_reader.get_content = async function(pdf_path) {
|
|
28
|
+
/* Gets the full text content from the PDF.
|
|
29
|
+
*
|
|
30
|
+
* @returns str
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
let doc = await _load_pdf(pdf_path);
|
|
34
|
+
return await _get_content(doc);
|
|
35
|
+
}; // Ends get_content()
|
|
36
|
+
|
|
37
|
+
pdf_reader.get_fields = async function(pdf_path) {
|
|
38
|
+
/* Gets all of the fields in the PDF
|
|
39
|
+
*
|
|
40
|
+
* Example:
|
|
41
|
+
* {
|
|
42
|
+
* "users1_name_first": [
|
|
43
|
+
* {
|
|
44
|
+
* "id": "6R",
|
|
45
|
+
* "value": "aeu",
|
|
46
|
+
* "defaultValue": "",
|
|
47
|
+
* "multiline": false,
|
|
48
|
+
* "password": false,
|
|
49
|
+
* "charLimit": 0,
|
|
50
|
+
* "comb": false,
|
|
51
|
+
* "editable": true,
|
|
52
|
+
* "hidden": false,
|
|
53
|
+
* "name": "users1_name_first",
|
|
54
|
+
* "rect": [
|
|
55
|
+
* 143.52,
|
|
56
|
+
* 660.72,
|
|
57
|
+
* 315.11,
|
|
58
|
+
* 674.88
|
|
59
|
+
* ],
|
|
60
|
+
* "actions": null,
|
|
61
|
+
* "page": 0,
|
|
62
|
+
* "strokeColor": null,
|
|
63
|
+
* "fillColor": null,
|
|
64
|
+
* "rotation": 0,
|
|
65
|
+
* "type": "text"
|
|
66
|
+
* }
|
|
67
|
+
* ],
|
|
68
|
+
*/
|
|
69
|
+
let doc = await _load_pdf(pdf_path);
|
|
70
|
+
const field_objects = await doc.getFieldObjects();
|
|
71
|
+
return field_objects;
|
|
72
|
+
}; // Ends get_fields
|
|
73
|
+
|
|
74
|
+
pdf_reader.get_field_value = async function(pdf_path, field_name) {
|
|
75
|
+
let field_objects = await pdf_reader.get_fields(pdf_path);
|
|
76
|
+
if (field_name in field_objects) {
|
|
77
|
+
for (let inst of field_objects[field_name]) {
|
|
78
|
+
if ("value" in inst) {
|
|
79
|
+
return inst["value"];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}; // Ends get_field
|
|
85
|
+
|
|
86
|
+
pdf_reader.compare_pdfs = async function(existing_pdf_path, new_pdf_path) {
|
|
87
|
+
/**
|
|
88
|
+
* Returns the change objects between the PDF fields, if there's any diff
|
|
89
|
+
*/
|
|
90
|
+
let existing_doc = await _load_pdf(existing_pdf_path);
|
|
91
|
+
let new_doc = await _load_pdf(new_pdf_path);
|
|
92
|
+
let existing_content = await _get_content(existing_doc);
|
|
93
|
+
let new_content = await _get_content(new_doc);
|
|
94
|
+
|
|
95
|
+
// Assumption: authors won't be changing their PDF templates programmatically;
|
|
96
|
+
// so if the text of the PDFs are different, then it's actually the fields that are different,
|
|
97
|
+
// but they aren't editable / are flattened into the PDF.
|
|
98
|
+
let change = diff_lib.diffWords(existing_content, new_content);
|
|
99
|
+
if (change.some(diff => diff.added || diff.removed)) {
|
|
100
|
+
return change;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const existing_fields = await existing_doc.getFieldObjects();
|
|
104
|
+
const new_fields = await new_doc.getFieldObjects();
|
|
105
|
+
|
|
106
|
+
let changeObjs = diff_lib.diffJson(existing_fields, new_fields);
|
|
107
|
+
if (changeObjs.some(diff => diff.added || diff.removed)) {
|
|
108
|
+
return changeObjs;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}; // End compare_pdfs
|
|
112
|
+
|
|
113
|
+
module.exports = pdf_reader;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
+
const fg = require('fast-glob');
|
|
2
3
|
const path = require('path');
|
|
3
4
|
require('dotenv').config();
|
|
4
5
|
|
|
@@ -23,6 +24,11 @@ module.exports = session_vars;
|
|
|
23
24
|
// These default to null
|
|
24
25
|
// TODO: How to isolate debug elsewhere so this file can use the logger?
|
|
25
26
|
session_vars.get_debug = function () { return process.env.DEBUG || null; };
|
|
27
|
+
session_vars.get_dangerous_debug = function () {
|
|
28
|
+
/** Note this will cause failure of tests that reassure the user about
|
|
29
|
+
* security precautions we took, like not taking a screenshot. */
|
|
30
|
+
return process.env._ALKILN_DANGEROUS_DEBUG || null;
|
|
31
|
+
};
|
|
26
32
|
session_vars.is_dev_env = function () { return process.env.DEV || null; }
|
|
27
33
|
|
|
28
34
|
session_vars.get_dev_api_key = function () { return process.env.DOCASSEMBLE_DEVELOPER_API_KEY || null; };
|
|
@@ -37,13 +43,6 @@ session_vars.get_origin = function () {
|
|
|
37
43
|
};
|
|
38
44
|
session_vars.get_user_project_name = function () { return process.env._PROJECT_NAME || null; };
|
|
39
45
|
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
|
-
};
|
|
47
46
|
|
|
48
47
|
// More complex logic
|
|
49
48
|
session_vars.get_server_reload_timeout = function () {
|
|
@@ -210,6 +209,124 @@ session_vars.get_artifacts_path_name = function () {
|
|
|
210
209
|
}
|
|
211
210
|
}; // Ends get_artifacts_path_name()
|
|
212
211
|
|
|
212
|
+
|
|
213
|
+
const install_type_key = `da_install_type`;
|
|
214
|
+
session_vars.save_install_type = function (install_type) {
|
|
215
|
+
/* Save a key with the name of the current artifacts folder in case it doesn't exist. */
|
|
216
|
+
|
|
217
|
+
// Ensure artifacts path file exists
|
|
218
|
+
if ( !fs.existsSync( runtime_config_path ) ) {
|
|
219
|
+
// If the file doesn't exist, make one because next we need to read it
|
|
220
|
+
fs.writeFileSync( runtime_config_path, JSON.stringify( {}, null, 2 ) );
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Get the json, changing it, and re-write the file from scratch
|
|
224
|
+
let json = JSON.parse( fs.readFileSync( runtime_config_path ) );
|
|
225
|
+
json[ install_type_key ] = install_type;
|
|
226
|
+
let file = fs.writeFileSync( runtime_config_path, JSON.stringify( json, null, 2 ) );
|
|
227
|
+
|
|
228
|
+
return file;
|
|
229
|
+
}; // Ends save_install_type()
|
|
230
|
+
|
|
231
|
+
session_vars.get_install_type = function () {
|
|
232
|
+
/* Get the name of the current artifacts path. */
|
|
233
|
+
try {
|
|
234
|
+
let json = JSON.parse( fs.readFileSync( runtime_config_path ));
|
|
235
|
+
let install_type = json[ install_type_key ];
|
|
236
|
+
|
|
237
|
+
return install_type;
|
|
238
|
+
} catch ( error ) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}; // Ends get_install_type()
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
const repo_folder_key = `da_repo_folder_name`;
|
|
245
|
+
session_vars.save_repo_folder_name = function (repo_folder_name) {
|
|
246
|
+
/** Save a key with the name of the main package folder in the repository. */
|
|
247
|
+
|
|
248
|
+
// Ensure runtime config file exists
|
|
249
|
+
if ( !fs.existsSync( runtime_config_path ) ) {
|
|
250
|
+
// If the file doesn't exist, make one because next we need to read it
|
|
251
|
+
fs.writeFileSync( runtime_config_path, JSON.stringify( {}, null, 2 ) );
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Get the json, changing it, and re-write the file from scratch
|
|
255
|
+
let json = JSON.parse( fs.readFileSync( runtime_config_path ) );
|
|
256
|
+
json[ da_repo_folder_name ] = repo_folder_name;
|
|
257
|
+
let file = fs.writeFileSync( runtime_config_path, JSON.stringify( json, null, 2 ) );
|
|
258
|
+
|
|
259
|
+
return file;
|
|
260
|
+
}; // Ends session_vars.save_repo_folder_name()
|
|
261
|
+
|
|
262
|
+
session_vars.get_repo_folder_name = function () {
|
|
263
|
+
/** Get the name of the main folder in the repository. */
|
|
264
|
+
try {
|
|
265
|
+
let json = JSON.parse( fs.readFileSync( runtime_config_path ));
|
|
266
|
+
let name = json[ da_repo_folder_name ];
|
|
267
|
+
|
|
268
|
+
return name;
|
|
269
|
+
} catch ( error ) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}; // Ends session_vars.get_repo_folder_name()
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
session_vars.set_sources_paths = function ( paths ) {
|
|
276
|
+
/** Returns whether at least one of the given path exists. Store
|
|
277
|
+
* a list of the existing paths. Warn of any missing paths. Can be
|
|
278
|
+
* used for getting, e.g .feature files, files to upload, etc.
|
|
279
|
+
*
|
|
280
|
+
* Our code should ensure this gets run before cucumber tests get triggered.
|
|
281
|
+
*
|
|
282
|
+
* @param paths { [str] } List of paths to the 'sources' folder.
|
|
283
|
+
*
|
|
284
|
+
* @returns bool
|
|
285
|
+
*/
|
|
286
|
+
|
|
287
|
+
// Check every path, tracking which exist and which are missing
|
|
288
|
+
let existing_paths = [];
|
|
289
|
+
let missing_paths = [];
|
|
290
|
+
for ( let path of paths ) {
|
|
291
|
+
let dirs = fg.sync(
|
|
292
|
+
[ path ],
|
|
293
|
+
{ onlyFiles: false, suppressErrors: true }
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
if ( dirs.length > 0 ) {
|
|
297
|
+
existing_paths.push( ...dirs );
|
|
298
|
+
} else {
|
|
299
|
+
missing_paths.push( path );
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Warn about missing paths
|
|
304
|
+
if ( missing_paths.length > 0 ) {
|
|
305
|
+
console.warn(
|
|
306
|
+
`ALKiln WARNING: ALKiln can't find folders at "${ paths.join(", ") }".`
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// If no paths were valid
|
|
311
|
+
if ( existing_paths.length <= 0 ) {
|
|
312
|
+
session_vars._sources_paths = null;
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Otherwise, store valid paths
|
|
317
|
+
session_vars._sources_paths = existing_paths;
|
|
318
|
+
return true;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
session_vars.get_sources_paths = function () {
|
|
322
|
+
/** Get the "sources" folder path. Should never be undefined during
|
|
323
|
+
* a run of the test. run_cucumber.js should make sure it has
|
|
324
|
+
* a default value.
|
|
325
|
+
*
|
|
326
|
+
* @returns str || null */
|
|
327
|
+
return session_vars._sources_paths;
|
|
328
|
+
};
|
|
329
|
+
|
|
213
330
|
session_vars.validateEnvironment = function() {
|
|
214
331
|
/** Throw a useful error for the developer if any of the env vars
|
|
215
332
|
* required by absolutely all tests are missing. Gather all
|
|
@@ -274,3 +391,36 @@ session_vars.validateEnvironment = function() {
|
|
|
274
391
|
};
|
|
275
392
|
|
|
276
393
|
session_vars.validateEnvironment();
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
const save_runtime_config_var = function (key, value) {
|
|
397
|
+
/** Save a key and its value in the runtime config file. */
|
|
398
|
+
|
|
399
|
+
// Ensure runtime config file exists
|
|
400
|
+
if ( !fs.existsSync( runtime_config_path ) ) {
|
|
401
|
+
// If the file doesn't exist, make one because next we need to read it
|
|
402
|
+
fs.writeFileSync( runtime_config_path, JSON.stringify( {}, null, 2 ) );
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Get the json, changing it, and re-write the file from scratch
|
|
406
|
+
let json = JSON.parse( fs.readFileSync( runtime_config_path ) );
|
|
407
|
+
json[ key ] = value;
|
|
408
|
+
let file = fs.writeFileSync( runtime_config_path, JSON.stringify( json, null, 2 ) );
|
|
409
|
+
|
|
410
|
+
if ( session_vars.get_debug() ) {
|
|
411
|
+
console.log( `ALKiln debug: Stored "${key}" locally as "${value}".` );
|
|
412
|
+
}
|
|
413
|
+
return file;
|
|
414
|
+
}; // Ends save_runtime_config_var()
|
|
415
|
+
|
|
416
|
+
const get_runtime_config_var = function (key) {
|
|
417
|
+
/** Get the value of the given key from the runtime config file. */
|
|
418
|
+
try {
|
|
419
|
+
let json = JSON.parse( fs.readFileSync( runtime_config_path ));
|
|
420
|
+
let value = json[ key ];
|
|
421
|
+
|
|
422
|
+
return value;
|
|
423
|
+
} catch ( error ) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
}; // Ends get_runtime_config_var()
|