@suffolklitlab/alkiln 5.0.0-experimental1 → 5.0.0-experimental3
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 +1 -0
- package/lib/scope.js +63 -69
- package/npm-shrinkwrap.json +840 -36
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -62,6 +62,7 @@ Format:
|
|
|
62
62
|
- 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
63
|
- Updated field decoding to handle new object field encoding. See [#711](https://github.com/SuffolkLITLab/ALKiln/issues/711)
|
|
64
64
|
- Allows ALKilnInThePlayground to upload files. See https://github.com/SuffolkLITLab/docassemble-ALKilnInThePlayground/issues/18.
|
|
65
|
+
- 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.
|
|
65
66
|
|
|
66
67
|
### Security
|
|
67
68
|
- Pass docassemble API keys through HTTP headers instead of as parameters.
|
package/lib/scope.js
CHANGED
|
@@ -2,6 +2,8 @@ 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');
|
|
6
|
+
const fg = require('fast-glob');
|
|
5
7
|
const { AxePuppeteer } = require('@axe-core/puppeteer');
|
|
6
8
|
const safe_filename = require("sanitize-filename");
|
|
7
9
|
// "en" is faster to import: https://github.com/faker-js/faker/issues/1114#issuecomment-1169532948
|
|
@@ -1528,7 +1530,10 @@ module.exports = {
|
|
|
1528
1530
|
let row = matching_input_rows[ matching_input_rows.length - 1 ];
|
|
1529
1531
|
let answer = row.value;
|
|
1530
1532
|
|
|
1531
|
-
|
|
1533
|
+
// Will implement `was_used` in cases as they come along. For now, this is
|
|
1534
|
+
// necessary for file uploads to be as flexible as any other Story Table row.
|
|
1535
|
+
let was_used = await scope.funnel_the_answer( scope, { handle, answer, field });
|
|
1536
|
+
if ( was_used === false ) { row = null; }
|
|
1532
1537
|
|
|
1533
1538
|
return row;
|
|
1534
1539
|
}, // Ends scope.setVariable()
|
|
@@ -1590,16 +1595,18 @@ module.exports = {
|
|
|
1590
1595
|
custom_datatype = await scope.get_custom_datatype( scope, { handle });
|
|
1591
1596
|
}
|
|
1592
1597
|
|
|
1598
|
+
let row_was_used = true;
|
|
1593
1599
|
// If there is a custom datatype function available, use it to set the field
|
|
1594
1600
|
if ( scope.setCustomDatatype[ custom_datatype ] !== undefined ) {
|
|
1595
1601
|
// Set da package custom fields. TODO: find `custom_handle` in the setter?
|
|
1596
1602
|
let custom_handle = await handle.evaluateHandle(( elem )=> { return elem.closest( `.da-form-group` ) });
|
|
1597
|
-
await scope.setCustomDatatype[ custom_datatype ]( scope, { handle: custom_handle, answer });
|
|
1603
|
+
row_was_used = await scope.setCustomDatatype[ custom_datatype ]( scope, { handle: custom_handle, answer });
|
|
1598
1604
|
} else {
|
|
1599
1605
|
// If no custom datatype, set a regular field
|
|
1600
|
-
await scope.enter_answer[ field.tag ]( scope, { handle, answer });
|
|
1606
|
+
row_was_used = await scope.enter_answer[ field.tag ]( scope, { handle, answer });
|
|
1601
1607
|
}
|
|
1602
1608
|
|
|
1609
|
+
return row_was_used;
|
|
1603
1610
|
}, // Ends scope.funnel_the_answer()
|
|
1604
1611
|
|
|
1605
1612
|
// Handle setting values for da custom datatypes. E.g. `da-field-container-datatype-BirthDate`
|
|
@@ -1660,10 +1667,14 @@ module.exports = {
|
|
|
1660
1667
|
}, // ends setCustomDatatype {}
|
|
1661
1668
|
|
|
1662
1669
|
enter_answer: {
|
|
1663
|
-
button: async function ( scope, { handle, answer }) {
|
|
1670
|
+
button: async function ( scope, { handle, answer }) {
|
|
1671
|
+
await scope.tapElement( scope, handle );
|
|
1672
|
+
return true;
|
|
1673
|
+
},
|
|
1664
1674
|
textarea: async function ( scope, { handle, answer }) {
|
|
1665
1675
|
await scope.setText( scope, { handle, answer });
|
|
1666
1676
|
await scope.afterStep(scope); // No showifs for this one?
|
|
1677
|
+
return true;
|
|
1667
1678
|
},
|
|
1668
1679
|
select: async function ( scope, { handle, answer }) {
|
|
1669
1680
|
// Note: I don't remember what's really going on in here.
|
|
@@ -1692,13 +1703,16 @@ module.exports = {
|
|
|
1692
1703
|
}
|
|
1693
1704
|
|
|
1694
1705
|
await scope.afterStep(scope, { waitForShowIf: true });
|
|
1706
|
+
return true;
|
|
1695
1707
|
},
|
|
1696
1708
|
canvas: async function ( scope, { handle, answer }) {
|
|
1697
1709
|
await scope.draw_signature( scope );
|
|
1698
1710
|
await scope.afterStep( scope );
|
|
1711
|
+
return true;
|
|
1699
1712
|
},
|
|
1700
1713
|
input: async function ( scope, { handle, answer }) {
|
|
1701
1714
|
/* Set value of some `input` element to the given value. */
|
|
1715
|
+
let row_was_used = true;
|
|
1702
1716
|
|
|
1703
1717
|
let type = await handle.evaluate( elem => { return elem.getAttribute('type'); });
|
|
1704
1718
|
if ( type === `radio` || type === `checkbox` ) {
|
|
@@ -1717,7 +1731,7 @@ module.exports = {
|
|
|
1717
1731
|
await scope.afterStep(scope, { waitForShowIf: true });
|
|
1718
1732
|
|
|
1719
1733
|
} else if ( type === `file` ) {
|
|
1720
|
-
await scope.uploadFiles( scope, { handle, answer });
|
|
1734
|
+
row_was_used = await scope.uploadFiles( scope, { handle, answer });
|
|
1721
1735
|
await scope.afterStep(scope, { waitForShowIf: true });
|
|
1722
1736
|
|
|
1723
1737
|
} else if (type == `date`) {
|
|
@@ -1746,6 +1760,8 @@ module.exports = {
|
|
|
1746
1760
|
await scope.setText( scope, { handle, answer });
|
|
1747
1761
|
await scope.afterStep(scope, { waitForShowIf: true });
|
|
1748
1762
|
}
|
|
1763
|
+
|
|
1764
|
+
return row_was_used;
|
|
1749
1765
|
}, // Ends scope.enter_answer.input()
|
|
1750
1766
|
hidden: async function ( scope, { handle, answer }) {
|
|
1751
1767
|
// Do nothing. The only way the code should get here is if
|
|
@@ -1762,80 +1778,57 @@ module.exports = {
|
|
|
1762
1778
|
uploadFiles: async function ( scope, { handle, answer }) {
|
|
1763
1779
|
/* Try to upload one or more files to a file-type input field.
|
|
1764
1780
|
* Does not submit, so no danger of timeout with server reload.
|
|
1781
|
+
*
|
|
1782
|
+
* Returns whether the row was used or not.
|
|
1765
1783
|
*/
|
|
1784
|
+
console.log( process.cwd() );
|
|
1766
1785
|
|
|
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;
|
|
1786
|
+
// First make sure the list of files is of the correct format
|
|
1787
|
+
// Allow a typo where someone forgot to include a space after a comma
|
|
1788
|
+
let uploads_names = answer.split(`,`);
|
|
1789
|
+
if ( uploads_names.length === 0 ) {
|
|
1790
|
+
await scope.addToReport( scope, {
|
|
1791
|
+
type: `error`,
|
|
1792
|
+
value: `Your list of files to upload is empty.`
|
|
1793
|
+
});
|
|
1794
|
+
return false;
|
|
1808
1795
|
}
|
|
1809
1796
|
|
|
1810
|
-
|
|
1811
|
-
|
|
1797
|
+
// Make sure sources directory exists
|
|
1798
|
+
let dirs = fg.sync([ `**/data/sources` ], { onlyFiles: false });
|
|
1799
|
+
if ( dirs.length === 0 ) {
|
|
1800
|
+
await scope.addToReport( scope, {
|
|
1801
|
+
type: `warning`,
|
|
1802
|
+
value: `The test files need to be in the "sources" directory anywhere within "${ process.cwd() }", but ALKiln couldn't find it.`
|
|
1803
|
+
});
|
|
1804
|
+
return false;
|
|
1812
1805
|
}
|
|
1806
|
+
let dir = dirs[0];
|
|
1813
1807
|
|
|
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
1808
|
let paths = [];
|
|
1819
|
-
|
|
1809
|
+
let missing_files = [];
|
|
1810
|
+
for ( let name of uploads_names ) {
|
|
1811
|
+
let trimmed_name = name.trim();
|
|
1812
|
+
let matching_paths = fg.sync([ `${ dir }/${ trimmed_name }` ]);
|
|
1813
|
+
if ( matching_paths.length === 0 ) {
|
|
1814
|
+
missing_files.push( trimmed_name );
|
|
1815
|
+
} else {
|
|
1816
|
+
paths.push( matching_paths[0] );
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1820
1819
|
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1820
|
+
if ( missing_files.length > 0 ) {
|
|
1821
|
+
await scope.addToReport( scope, {
|
|
1822
|
+
type: `warning`,
|
|
1823
|
+
value: `ALKiln could not find "${ missing_files.join('", "') }" in "${ dir }".`
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1825
1826
|
|
|
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
|
-
}
|
|
1827
|
+
// If all files missing, return that the row wasn't used
|
|
1828
|
+
if ( missing_files.length === uploads_names.length ) {
|
|
1829
|
+
return false;
|
|
1833
1830
|
}
|
|
1834
1831
|
|
|
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
1832
|
// Prepare to wait for upload to complete
|
|
1840
1833
|
let name = await handle.evaluate( elem => { return elem.getAttribute(`name`); });
|
|
1841
1834
|
let selector = `*[name="${ name }"]`
|
|
@@ -1855,8 +1848,9 @@ module.exports = {
|
|
|
1855
1848
|
await wait_for_disable;
|
|
1856
1849
|
// When it's again enabled, the files have finished loading. Or so I have observed.
|
|
1857
1850
|
await scope.page.waitForSelector( enabled_selector );
|
|
1858
|
-
|
|
1859
1851
|
|
|
1852
|
+
// Return that the row was used
|
|
1853
|
+
return true;
|
|
1860
1854
|
}, // Ends scope.uploadFiles()
|
|
1861
1855
|
|
|
1862
1856
|
setCheckbox: async function ( scope, { label, answer }) {
|