@suffolklitlab/alkiln 5.4.0 → 5.4.1

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 CHANGED
@@ -44,6 +44,11 @@ Format:
44
44
  -->
45
45
  <!-- ## [Unreleased] -->
46
46
 
47
+ ## [5.4.1] - 2023-10-18
48
+
49
+ ### Internal
50
+ - BREAKS ALKilnInThePlayground v1.2.0 and below (as we discussed) as that project is still exploratory. Can now take the location of the "sources" folder or folders as an argument in the command line. The formats are `npm run cucumber @tags -- --sources=./foo` and `alkiln-run @tags --sources=./foo`.
51
+
47
52
  ## [5.4.0] - 2023-10-14
48
53
 
49
54
  ### Added
@@ -6,64 +6,97 @@ const session_vars = require('./utils/session_vars');
6
6
 
7
7
  const cucumber_api = require('@cucumber/cucumber/api');
8
8
 
9
- (async function main() {
10
- let argv = process.argv;
11
-
12
- const environment = { cwd: process.cwd() };
13
- console.log("alkiln-run: environment: %o", environment);
14
- const provided_config = {
15
- "publishQuiet": true,
16
- "require": "./lib/index.js",
17
- "format": ["progress", "summary:cucumber-report.txt"],
18
- "paths": ["docassemble/*/data/sources/*.feature"],
19
- "retry": 1,
20
- }
21
-
22
-
23
- // If they're running it from an interview on their server instead of from GitHub
24
- if ( session_vars.get_origin() === `playground` ) {
25
- provided_config.paths.push(`/usr/share/docassemble/files/playgroundsources/${session_vars.get_user_id()}/${session_vars.get_user_project_name()}/*.feature`);
26
- // For S3
27
- provided_config.paths.push(`/tmp/playgroundsources/${session_vars.get_user_id()}/${session_vars.get_user_project_name()}/*.feature`);
9
+ function build_config( command_args ) {
10
+ // Turn all the paths to the sources folders into paths to the feature files
11
+ let sources_paths = session_vars.get_sources_paths();
12
+ let feature_paths = [];
13
+ for ( let source of sources_paths ) {
14
+ feature_paths.push( `${ source }/*.feature` );
28
15
  }
29
16
 
30
- let tags = argv.slice(2);
31
- if ( session_vars.get_origin() === `playground` ) {
32
- tags = session_vars.get_tags();
17
+ const config = {
18
+ publishQuiet: true,
19
+ require: `./lib/index.js`,
20
+ format: [`progress`, `summary:cucumber-report.txt`],
21
+ paths: feature_paths,
22
+ retry: 1,
33
23
  }
34
24
 
25
+ // Normalize tag expressions, if there are any
26
+ let tags = command_args[`_`];
35
27
  if (tags.length > 0) {
36
28
  let joined_tags = '';
37
29
  // Cucumber expects tags in boolean expressions, like (@a0 or @a1) and @random.
38
30
  // If we get a list of only tags, then "or" them all together
39
- if (tags.every((val) => val.startsWith("@"))) {
40
- joined_tags = tags.join(' or ');
31
+ if (tags.every((val) => val.startsWith(`@`))) {
32
+ joined_tags = tags.join(` or `);
41
33
  }
42
34
  // If we get a list of seemingly no tags, assume they meant to use tags
43
- else if (tags.every((val) => !val.startsWith("@"))) {
44
- joined_tags = tags.map((val) => "@" + val).join(' or ');
35
+ else if (tags.every((val) => !val.startsWith(`@`))) {
36
+ joined_tags = tags.map((val) => `@` + val).join(` or `);
45
37
  }
46
38
  // If it's a mix of tags and no tags, it's probably a boolean expression!
47
39
  else {
48
- joined_tags = tags.join(' ');
40
+ joined_tags = tags.join(` `);
49
41
  }
50
42
 
51
- provided_config.tags = joined_tags;
52
- } // end if there are tags
43
+ config.tags = joined_tags;
44
+ }
45
+
46
+ return config;
47
+ }; // Ends build_config()
48
+
49
+
50
+ (async function main() {
51
+ /** Create the cucumber config, run the tests, create and
52
+ * manipluate the output. */
53
+ const environment = { cwd: process.cwd() };
54
+ console.log("alkiln-run: environment: %o", environment);
55
+
56
+ // We'll use the user info to construct the possible paths to /sources
57
+ let id = session_vars.get_user_id();
58
+ // `project` is the name in the server docassemble "Project" page
59
+ let project = session_vars.get_user_project_name();
60
+
61
+ // process.argv is a list of strings of commands and args.
62
+ // If using `npm run cucumber`, use `--` before `--sources=./foo`
63
+ let argv = require(`minimist`)( process.argv.slice(2), {
64
+ default: {
65
+ sources: [
66
+ `./docassemble/*/data/sources`, // In GitHub folder
67
+ `/usr/share/docassemble/files/playgroundsources/${id}/${project}/*.feature`, // From playground without S3
68
+ `/tmp/playgroundsources/${id}/${project}/*.feature` // From playground with S3
69
+ ],
70
+ }
71
+ });
72
+
73
+ // Make sure `sources` is always a list
74
+ let sources_list = argv.sources;
75
+ if ( typeof argv.sources === `string` ) {
76
+ sources_list = [ argv.sources ];
77
+ }
53
78
 
79
+ let one_dir_exists = session_vars.set_sources_paths( sources_list );
80
+ if ( !one_dir_exists ) {
81
+ // If we don't throw, it'll be more confusing when no tests run
82
+ throw new ReferenceError( `ALKiln ERROR: ALKiln can't find your "sources" folder(s). See above for details.`);
83
+ }
84
+
54
85
  const { runConfiguration } = await cucumber_api.loadConfiguration({
55
- provided: provided_config
86
+ provided: build_config( argv ),
56
87
  }, environment);
88
+
57
89
  console.log("alkiln-run: runConfiguration: %o", runConfiguration);
90
+
58
91
  var path = require('path');
59
92
  var codeDir = path.dirname(require.main.filename);
93
+ console.log("alkiln-run: working directory: %o", codeDir);
60
94
  const support = await cucumber_api.loadSupport({ support: {
61
- requireModules: [`${ codeDir }/index.js`],
62
- requirePaths: [ `${ codeDir }/index.js`],
95
+ requireModules: [ `${ codeDir }/index.js` ],
96
+ requirePaths: [ `${ codeDir }/index.js` ],
63
97
  importPaths: [],
64
98
  }, sources: {paths: []}}, environment);
65
99
 
66
- console.log("alkiln-run: working directory: %o", codeDir);
67
100
  console.log(`\n\n`);
68
101
 
69
102
  const { success } = await cucumber_api.runCucumber({...runConfiguration, support});
@@ -86,6 +119,6 @@ const cucumber_api = require('@cucumber/cucumber/api');
86
119
 
87
120
  // Put it in the correct file in the correct folder
88
121
  let artifacts_path = session_vars.get_artifacts_path_name();
89
- fs.appendFileSync( `${ artifacts_path}/${ log.debug_log_file }`, data);
122
+ fs.appendFileSync( `${ artifacts_path }/${ log.debug_log_file }`, data);
90
123
  });
91
124
  })();
package/lib/scope.js CHANGED
@@ -1681,75 +1681,52 @@ module.exports = {
1681
1681
  }, // Ends scope.today_to_date()
1682
1682
 
1683
1683
  findFiles: async function( scope, {to_find_names}) {
1684
+ /** Returns a list of paths to the sources folder associated
1685
+ * with the given list of file names.
1686
+ *
1687
+ * @param to_find_names { [str] } List of file names
1688
+ *
1689
+ * @returns { [str] } sources folder paths
1690
+ */
1684
1691
  // First make sure the list of files was defined. Don't remove spaces yet -
1685
1692
  // allow a typo where someone forgot to include a space after a comma.
1693
+ // TODO: wish we had the var name here that folks are trying to set
1686
1694
  if ( to_find_names.length === 0 ) {
1687
- reports.addToReport( scope, {
1688
- type: `error`,
1689
- value: `Your list of files to find is empty.`
1690
- });
1691
- return [];
1692
- }
1693
-
1694
- let dirs = [];
1695
- if ( session_vars.get_origin() === `playground` ) {
1696
- let files_playground = fg.sync(
1697
- [ `/usr/share/docassemble/files/playgroundsources/${session_vars.get_user_id()}/${session_vars.get_user_project_name()}` ],
1698
- { onlyFiles: false, suppressErrors: true }
1699
- );
1700
- let files_s3 = fg.sync(
1701
- [ `/tmp/playgroundsources/${session_vars.get_user_id()}/${session_vars.get_user_project_name()}` ],
1702
- { onlyFiles: false, suppressErrors: true }
1703
- );
1704
-
1705
- dirs = files_playground.concat( files_s3 );
1706
- } else {
1707
- dirs = fg.sync(
1708
- [ `**/docassemble/**/data/sources` ],
1709
- { onlyFiles: false, suppressErrors: true }
1710
- );
1711
- }
1712
-
1713
- if ( session_vars.get_debug() ) {
1714
- console.log( `All discovered dirs to find files from:`, dirs );
1715
- }
1716
-
1717
- // Make sure sources directory exists and has files in it
1718
- if ( dirs.length === 0 ) {
1719
1695
  reports.addToReport( scope, {
1720
1696
  type: `warning`,
1721
- value: `The test files need to be in the "sources" directory, `
1722
- + `but ALKiln couldn't find the directory or it was empty.`
1697
+ value: `Your Step is missing the names of which file(s) to use.`
1723
1698
  });
1724
1699
  return [];
1725
1700
  }
1726
1701
 
1727
- // Get the full paths of all the files
1702
+ let sources_dirs = session_vars.get_sources_paths();
1703
+ // Get the full paths of all the existing files
1728
1704
  let paths = [];
1729
1705
  let missing_files = []; // For any user warnings
1730
- for ( let name of to_find_names) {
1731
-
1706
+ for ( let name of to_find_names ) {
1707
+
1732
1708
  let trimmed_name = name.trim();
1733
1709
  let matching_paths = [];
1734
-
1735
- for ( let one_dir of dirs ) {
1736
- // accumulate all matching paths
1737
- let new_paths = fg.sync([ `${ one_dir }/${ trimmed_name }` ]);
1710
+ // Look for the file in all "sources" folders
1711
+ for ( let one_dir of sources_dirs ) {
1712
+ let new_paths = fg.sync(
1713
+ [ `${ one_dir }/${ trimmed_name }` ],
1714
+ { suppressErrors: true }
1715
+ );
1738
1716
  matching_paths.push( ...new_paths );
1739
- } // ends for each dir
1717
+ }
1740
1718
 
1741
1719
  if ( matching_paths.length === 0 ) {
1742
1720
  missing_files.push( trimmed_name );
1743
1721
  } else {
1744
1722
  paths.push( ...matching_paths );
1745
1723
  }
1746
-
1747
1724
  } // ends for each file
1748
1725
 
1749
1726
  if ( missing_files.length > 0 ) {
1750
1727
  reports.addToReport( scope, {
1751
1728
  type: `warning`,
1752
- value: `ALKiln could not find "${ missing_files.join('", "') }" in any of these folders: "${ dirs.join('", "') }".`
1729
+ value: `ALKiln could not find "${ missing_files.join('", "') }" in the folder(s) at "${ sources_dirs.join('", "') }".`
1753
1730
  });
1754
1731
  }
1755
1732
 
@@ -1758,7 +1735,7 @@ module.exports = {
1758
1735
  return [];
1759
1736
  }
1760
1737
  return paths;
1761
- },
1738
+ }, // Ends scope.findFiles()
1762
1739
 
1763
1740
  uploadFiles: async function ( scope, { handle, answer }) {
1764
1741
  /* Try to upload one or more files to a file-type input field.
@@ -1999,7 +1976,7 @@ module.exports = {
1999
1976
  }
2000
1977
  if ( num_unused_rows > 0 ) {
2001
1978
  let { id } = await scope.examinePageID( scope, 'none to match' );
2002
- let msg = `Missing variable or variables on page "${id}"`;
1979
+ let msg = `Error on page "${id}". See screenshot and see text above for more details.`;
2003
1980
  reports.addToReport(scope, { type: `error`, value: msg });
2004
1981
  expect( num_unused_rows, msg ).to.equal( 0 );
2005
1982
  }
@@ -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
 
@@ -37,13 +38,6 @@ session_vars.get_origin = function () {
37
38
  };
38
39
  session_vars.get_user_project_name = function () { return process.env._PROJECT_NAME || null; };
39
40
  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
41
 
48
42
  // More complex logic
49
43
  session_vars.get_server_reload_timeout = function () {
@@ -210,6 +204,61 @@ session_vars.get_artifacts_path_name = function () {
210
204
  }
211
205
  }; // Ends get_artifacts_path_name()
212
206
 
207
+ session_vars.set_sources_paths = function ( paths ) {
208
+ /** Returns whether at least one of the given path exists. Store
209
+ * a list of the existing paths. Warn of any missing paths. Can be
210
+ * used for getting, e.g .feature files, files to upload, etc.
211
+ *
212
+ * Our code should ensure this gets run before cucumber tests get triggered.
213
+ *
214
+ * @param paths { [str] } List of paths to the 'sources' folder.
215
+ *
216
+ * @returns bool
217
+ */
218
+
219
+ // Check every path, tracking which exist and which are missing
220
+ let existing_paths = [];
221
+ let missing_paths = [];
222
+ for ( let path of paths ) {
223
+ let dirs = fg.sync(
224
+ [ path ],
225
+ { onlyFiles: false, suppressErrors: true }
226
+ );
227
+
228
+ if ( dirs.length > 0 ) {
229
+ existing_paths.push( ...dirs );
230
+ } else {
231
+ missing_paths.push( path );
232
+ }
233
+ }
234
+
235
+ // Warn about missing paths
236
+ if ( missing_paths.length > 0 ) {
237
+ console.warn(
238
+ `ALKiln WARNING: ALKiln can't find folders at "${ paths.join(", ") }".`
239
+ );
240
+ }
241
+
242
+ // If no paths were valid
243
+ if ( existing_paths.length <= 0 ) {
244
+ session_vars._sources_paths = null;
245
+ return false;
246
+ }
247
+
248
+ // Otherwise, store valid paths
249
+ session_vars._sources_paths = existing_paths;
250
+ return true;
251
+ };
252
+
253
+ session_vars.get_sources_paths = function () {
254
+ /** Get the "sources" folder path. Should never be undefined during
255
+ * a run of the test. run_cucumber.js should make sure it has
256
+ * a default value.
257
+ *
258
+ * @returns str || null */
259
+ return session_vars._sources_paths;
260
+ };
261
+
213
262
  session_vars.validateEnvironment = function() {
214
263
  /** Throw a useful error for the developer if any of the env vars
215
264
  * required by absolutely all tests are missing. Gather all