@suffolklitlab/alkiln 5.0.0-pre-6 → 5.0.0-pre-8
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/CHANGELOG.md +4 -0
- package/lib/run_cucumber.js +7 -4
- package/lib/scope.js +94 -69
- package/lib/steps.js +4 -23
- package/lib/utils/langs.js +2 -2
- package/npm-shrinkwrap.json +1975 -371
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -56,11 +56,15 @@ Format:
|
|
|
56
56
|
- don't print the ["publish this cucumber report" message](https://github.com/cucumber/cucumber-js/blob/main/docs/configuration.md#options)
|
|
57
57
|
- Adjusted validation of some environment variables to account for Playground vs. GitHub or local test runs. [Issue #661](https://github.com/SuffolkLITLab/ALKiln/issues/661)
|
|
58
58
|
- Docassemble Project name prefix now includes ALKiln in it for clarity
|
|
59
|
+
- Change interface for testing languages. See [#713](https://github.com/SuffolkLITLab/ALKiln/issues/713).
|
|
59
60
|
|
|
60
61
|
### Fixed
|
|
62
|
+
- DOCKER UPDATE (for ALKilnInThePlayground): Allows ALKilnInThePlayground to upload files. See https://github.com/SuffolkLITLab/docassemble-ALKilnInThePlayground/issues/18. This may require some developers to update their system (updating their docker image) if they are below [docassemble-os 1.0.8](https://github.com/jhpyle/docassemble-os/releases/tag/v1.0.8). You can tell if you are below docassemble-os 1.0.8 if you run `docker image inspect jhpyle/docassemble | grep Created` on your host server (Lightsail, etc.) and the created date is before 2023-04-09. You can update as described in [the documentation](https://suffolklitlab.org/legal-tech-class/docs/practical-guide-docassemble/maintaining-docassemble#updates-to-the-docassemble-container). Updating to a new docker images is something that all docassemble users will have to do sometime anyway, sometimes for security.
|
|
63
|
+
- Conform upload file behavior to that of other Story Table rows - avoid erroring when field isn't set properly unless using all variables is required.
|
|
61
64
|
- Projects created in da each have a unique name. https://github.com/SuffolkLITLab/ALKiln/issues/663
|
|
62
65
|
- Shorten path names to try to accommodate limitations of windows systems while still keeping enough useful information to help devs identify the test outputs. https://github.com/SuffolkLITLab/ALKiln/issues/618
|
|
63
66
|
- Updated field decoding to handle new object field encoding. See [#711](https://github.com/SuffolkLITLab/ALKiln/issues/711)
|
|
67
|
+
- Allow multiple languages to be tested again. See [#713](https://github.com/SuffolkLITLab/ALKiln/issues/713).
|
|
64
68
|
|
|
65
69
|
### Security
|
|
66
70
|
- Pass docassemble API keys through HTTP headers instead of as parameters.
|
package/lib/run_cucumber.js
CHANGED
|
@@ -33,20 +33,23 @@ const cucumber_api = require('@cucumber/cucumber/api');
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (tags.length > 0) {
|
|
36
|
+
let joined_tags = '';
|
|
36
37
|
// Cucumber expects tags in boolean expressions, like (@a0 or @a1) and @random.
|
|
37
38
|
// If we get a list of only tags, then "or" them all together
|
|
38
39
|
if (tags.every((val) => val.startsWith("@"))) {
|
|
39
|
-
|
|
40
|
+
joined_tags = tags.join(' or ');
|
|
40
41
|
}
|
|
41
42
|
// If we get a list of seemingly no tags, assume they meant to use tags
|
|
42
43
|
else if (tags.every((val) => !val.startsWith("@"))) {
|
|
43
|
-
|
|
44
|
+
joined_tags = tags.map((val) => "@" + val).join(' or ');
|
|
44
45
|
}
|
|
45
46
|
// If it's a mix of tags and no tags, it's probably a boolean expression!
|
|
46
47
|
else {
|
|
47
|
-
|
|
48
|
+
joined_tags = tags.join(' ');
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
+
|
|
51
|
+
provided_config.tags = joined_tags;
|
|
52
|
+
} // end if there are tags
|
|
50
53
|
|
|
51
54
|
const { runConfiguration } = await cucumber_api.loadConfiguration({
|
|
52
55
|
provided: provided_config
|
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 fg = require('fast-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,89 +1777,98 @@ 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
|
*/
|
|
1766
1783
|
|
|
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;
|
|
1784
|
+
// First make sure the list of files was defined. Don't remove spaces yet -
|
|
1785
|
+
// allow a typo where someone forgot to include a space after a comma.
|
|
1786
|
+
let uploads_names = answer.split(`,`);
|
|
1787
|
+
if ( uploads_names.length === 0 ) {
|
|
1788
|
+
await scope.addToReport( scope, {
|
|
1789
|
+
type: `error`,
|
|
1790
|
+
value: `Your list of files to upload is empty.`
|
|
1791
|
+
});
|
|
1792
|
+
return false; // The row wasn't used
|
|
1790
1793
|
}
|
|
1791
1794
|
|
|
1792
|
-
let
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1795
|
+
let dirs = [];
|
|
1796
|
+
if ( session_vars.get_origin() === `playground` ) {
|
|
1797
|
+
let files_playground = fg.sync(
|
|
1798
|
+
[ `/usr/share/docassemble/files/playgroundsources/${session_vars.get_user_id()}/${session_vars.get_user_project_name()}` ],
|
|
1799
|
+
{ onlyFiles: false, suppressErrors: true }
|
|
1800
|
+
);
|
|
1801
|
+
let files_s3 = fg.sync(
|
|
1802
|
+
[ `/tmp/playgroundsources/${session_vars.get_user_id()}/${session_vars.get_user_project_name()}` ],
|
|
1803
|
+
{ onlyFiles: false, suppressErrors: true }
|
|
1804
|
+
);
|
|
1805
|
+
|
|
1806
|
+
dirs = files_playground.concat( files_s3 );
|
|
1807
|
+
} else {
|
|
1808
|
+
dirs = fg.sync(
|
|
1809
|
+
[ `**/docassemble/**/data/sources` ],
|
|
1810
|
+
{ onlyFiles: false, suppressErrors: true }
|
|
1811
|
+
);
|
|
1808
1812
|
}
|
|
1809
1813
|
|
|
1810
|
-
if (
|
|
1811
|
-
|
|
1814
|
+
if ( session_vars.get_debug() ) {
|
|
1815
|
+
console.log( `All discovered dirs to upload from:`, dirs );
|
|
1812
1816
|
}
|
|
1813
1817
|
|
|
1814
|
-
//
|
|
1815
|
-
|
|
1818
|
+
// Make sure sources directory exists and has files in it
|
|
1819
|
+
if ( dirs.length === 0 ) {
|
|
1820
|
+
await scope.addToReport( scope, {
|
|
1821
|
+
type: `warning`,
|
|
1822
|
+
value: `The test files need to be in the "sources" directory, `
|
|
1823
|
+
+ `but ALKiln couldn't find the directory or it was empty.`
|
|
1824
|
+
});
|
|
1825
|
+
return false; // The row wasn't used
|
|
1826
|
+
}
|
|
1816
1827
|
|
|
1817
|
-
//
|
|
1828
|
+
// Get the full paths of all the files
|
|
1829
|
+
let dir = null;
|
|
1818
1830
|
let paths = [];
|
|
1819
|
-
|
|
1831
|
+
let missing_files = []; // For any user warnings
|
|
1832
|
+
for ( let name of uploads_names ) {
|
|
1833
|
+
|
|
1834
|
+
let trimmed_name = name.trim();
|
|
1835
|
+
let matching_paths = [];
|
|
1836
|
+
|
|
1837
|
+
for ( let one_dir of dirs ) {
|
|
1838
|
+
// accumulate all matching paths
|
|
1839
|
+
let new_paths = fg.sync([ `${ one_dir }/${ trimmed_name }` ]);
|
|
1840
|
+
matching_paths.push( ...new_paths );
|
|
1841
|
+
} // ends for each dir
|
|
1842
|
+
|
|
1843
|
+
if ( matching_paths.length === 0 ) {
|
|
1844
|
+
missing_files.push( trimmed_name );
|
|
1845
|
+
} else {
|
|
1846
|
+
paths.push( ...matching_paths );
|
|
1847
|
+
}
|
|
1820
1848
|
|
|
1821
|
-
|
|
1822
|
-
// start of the string. Remove it. If they forgot the space, that's fine too.
|
|
1823
|
-
name = name.replace(/^\s/, '');
|
|
1824
|
-
let file_path = path.join( chosen_dir, name );
|
|
1849
|
+
} // ends for each file
|
|
1825
1850
|
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1851
|
+
if ( missing_files.length > 0 ) {
|
|
1852
|
+
await scope.addToReport( scope, {
|
|
1853
|
+
type: `warning`,
|
|
1854
|
+
value: `ALKiln could not find "${ missing_files.join('", "') }" in any of these folders: "${ dirs.join('", "') }".`
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
// If all files missing, return that the row wasn't used
|
|
1859
|
+
if ( missing_files.length === uploads_names.length ) {
|
|
1860
|
+
return false; // The row wasn't used
|
|
1833
1861
|
}
|
|
1834
1862
|
|
|
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
1863
|
// Prepare to wait for upload to complete
|
|
1840
1864
|
let name = await handle.evaluate( elem => { return elem.getAttribute(`name`); });
|
|
1841
1865
|
let selector = `*[name="${ name }"]`
|
|
1842
1866
|
let disabled_selector = `${ selector }[disabled]`;
|
|
1843
1867
|
let enabled_selector = `${ selector }:not([disabled])`;
|
|
1844
1868
|
|
|
1845
|
-
// This comes before the upload starts
|
|
1846
|
-
// Notice `await` is absent. Don't actually wait here.
|
|
1847
|
-
//
|
|
1869
|
+
// This comes before the upload starts.
|
|
1870
|
+
// Notice `await` is absent. Don't actually wait here yet.
|
|
1871
|
+
// Start looking for it before it exists or we might miss it.
|
|
1848
1872
|
let wait_for_disable = scope.page.waitForSelector( disabled_selector );
|
|
1849
1873
|
|
|
1850
1874
|
// Upload
|
|
@@ -1855,8 +1879,9 @@ module.exports = {
|
|
|
1855
1879
|
await wait_for_disable;
|
|
1856
1880
|
// When it's again enabled, the files have finished loading. Or so I have observed.
|
|
1857
1881
|
await scope.page.waitForSelector( enabled_selector );
|
|
1858
|
-
|
|
1859
1882
|
|
|
1883
|
+
// Return that the row was used
|
|
1884
|
+
return true;
|
|
1860
1885
|
}, // Ends scope.uploadFiles()
|
|
1861
1886
|
|
|
1862
1887
|
setCheckbox: async function ( scope, { label, answer }) {
|
package/lib/steps.js
CHANGED
|
@@ -160,7 +160,7 @@ Before(async (scenario) => {
|
|
|
160
160
|
//#####################################
|
|
161
161
|
//#####################################
|
|
162
162
|
|
|
163
|
-
Given(/I start the interview at "([^"]+)"
|
|
163
|
+
Given(/I start the interview at "([^"]+)"/, {timeout: -1}, async (file_name) => { // √
|
|
164
164
|
|
|
165
165
|
// Load the right interview page
|
|
166
166
|
await scope.load( scope, file_name );
|
|
@@ -180,7 +180,7 @@ Given(/I start the interview at "([^"]+)"(?: in lang "([^"]+)")?/, {timeout: -1}
|
|
|
180
180
|
// I've seen stuff take an extra moment or two. Shame to have it everywhere
|
|
181
181
|
try {
|
|
182
182
|
// Interview files should get downloaded to downloads folder
|
|
183
|
-
await scope.page._client.send('Page.setDownloadBehavior', {
|
|
183
|
+
await scope.page._client().send('Page.setDownloadBehavior', {
|
|
184
184
|
behavior: 'allow',
|
|
185
185
|
downloadPath: path.resolve( scope.paths.scenario ), // Save in root of this scenario
|
|
186
186
|
});
|
|
@@ -203,7 +203,7 @@ Given(/I start the interview at "([^"]+)"(?: in lang "([^"]+)")?/, {timeout: -1}
|
|
|
203
203
|
// at any one time since each step completes synchronously
|
|
204
204
|
|
|
205
205
|
// If response has a file in it that matches the file we are downloading
|
|
206
|
-
if ( response.
|
|
206
|
+
if ( response.headers()['content-disposition'] && (response.headers()['content-disposition']).includes(`attachment;filename=${scope.toDownload}`) ) {
|
|
207
207
|
// We're choosing to avoid removing the event listener in case removal
|
|
208
208
|
// would prevent downloading other files. The listener should get
|
|
209
209
|
// destroyed when the page closes anyway.
|
|
@@ -211,7 +211,7 @@ Given(/I start the interview at "([^"]+)"(?: in lang "([^"]+)")?/, {timeout: -1}
|
|
|
211
211
|
// Watch event on download folder or file
|
|
212
212
|
await fs.watchFile( scope.paths.scenario, function (curr, prev) {
|
|
213
213
|
// If current size eq to size from response then close
|
|
214
|
-
if (parseInt(curr.size) === parseInt(response.
|
|
214
|
+
if (parseInt(curr.size) === parseInt(response.headers()['content-length'])) {
|
|
215
215
|
// Lets the related function in `scope` know that the download
|
|
216
216
|
// is done so that the next step can start.
|
|
217
217
|
scope.downloadComplete = true;
|
|
@@ -224,25 +224,6 @@ Given(/I start the interview at "([^"]+)"(?: in lang "([^"]+)")?/, {timeout: -1}
|
|
|
224
224
|
} // ends if this is the file the developer expected
|
|
225
225
|
}); // ends page.on 'response'
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
// TODO: Move language into a different step
|
|
229
|
-
scope.language = language;
|
|
230
|
-
let custom_lang_timeout = new Promise(async ( resolve, reject ) => {
|
|
231
|
-
let page_timeout = setTimeout(function() { reject( `It took to long to try to set the language "${ language }".` ); }, scope.timeout);
|
|
232
|
-
// self-invoke an anonymous async function so we don't have to
|
|
233
|
-
// abstract the details of resolving and rejecting.
|
|
234
|
-
(async function() {
|
|
235
|
-
try {
|
|
236
|
-
await scope.setLanguage( scope, { language });
|
|
237
|
-
resolve();
|
|
238
|
-
} catch ( err ) { reject( err ); }
|
|
239
|
-
// prevent temporary hang at end of tests
|
|
240
|
-
finally { clearTimeout( page_timeout ); }
|
|
241
|
-
})();
|
|
242
|
-
}); // ends custom_lang_timeout
|
|
243
|
-
await custom_lang_timeout;
|
|
244
|
-
|
|
245
|
-
|
|
246
227
|
let custom_afterStep_timeout = new Promise(async ( resolve, reject ) => {
|
|
247
228
|
let page_timeout = setTimeout(function() { reject(`\`afterStep()\` took too long.`); }, scope.timeout);
|
|
248
229
|
// self-invoke an anonymous async function so we don't have to
|
package/lib/utils/langs.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
let _path = require('path');
|
|
4
4
|
let fs = require('fs');
|
|
5
|
-
|
|
5
|
+
const fg = require('fast-glob');
|
|
6
6
|
|
|
7
7
|
let session_vars = require('./session_vars');
|
|
8
8
|
|
|
9
9
|
// Get the glob filepaths to find all the test files
|
|
10
10
|
let dir_root = process.env.INIT_CWD; // Path to root of project
|
|
11
11
|
let test_glob = process.argv[2]; // String of glob (regex for filepaths)
|
|
12
|
-
let filepaths =
|
|
12
|
+
let filepaths = fg.sync( _path.resolve( dir_root, test_glob ) );
|
|
13
13
|
let flag = '_lang_';
|
|
14
14
|
|
|
15
15
|
let langs = session_vars.get_languages();
|