From 96455a046ef2c3b4f67ef5ad90988bfe546cc115 Mon Sep 17 00:00:00 2001 From: plocket <52798256+plocket@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:15:59 -0400 Subject: [PATCH 1/2] Use `cwd` as the base for file paths to integrate with ALKiP changes, Fix error not printing when missing sources paths --- CHANGELOG.md | 10 +++++++++- docs/016_root_path_2026_04_09.md | 17 +++++++++++++++++ lib/scope.js | 2 +- lib/setup/artifacts.js | 20 ++++++++++---------- lib/utils/Log.js | 1 + lib/utils/files.js | 18 +++++++++++------- lib/utils/session_vars.js | 12 +++++++++++- lib/utils/set_sources_paths.js | 14 ++++++++------ package.json | 2 +- 9 files changed, 69 insertions(+), 27 deletions(-) create mode 100644 docs/016_root_path_2026_04_09.md diff --git a/CHANGELOG.md b/CHANGELOG.md index c5dca2db..a4275794 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,15 @@ Format: - --> - +## [Unreleased] + +### Changed + +- Use `cwd` as the base of file paths. Motivated by ALKiP's upcoming need to change the way it handles its own file paths. + +### Fixed + +- Print error correctly when missing sources paths ## [5.15.4] - 2025-11-29 diff --git a/docs/016_root_path_2026_04_09.md b/docs/016_root_path_2026_04_09.md new file mode 100644 index 00000000..a1569f50 --- /dev/null +++ b/docs/016_root_path_2026_04_09.md @@ -0,0 +1,17 @@ +# Root path + +This code assumes the current root directory will contain the necessary files and folders to get files like `.feature` files and the test run runtime config file. + +When you run ALKiln on the command line, run the tests in the root directory of the docassemble package with the tests. There are other ways to tell processes where the root directory is. For example, ALKiP, a docassemble package that uses ALKiln inside an author's server, runs `subprocess.run()` or `subprocess.Popen()` with `cwd=`. In ALKiP the `cwd` is most likely `/tmp`. + +Files that make this root path assumption: + +- lib/utils/set_sources_paths.js +- lib/utils/files.js +- lib/utils/session_vars.js + +ALKiln makes assumptions about specific folder names inside the root directory. See `lib/utils/set_sources_paths.js` and `validate.js` for where it looks for `.feature` files. + +## Anti-goals + +It is possible for us to make the root path configurable in a few ways (env vars, command line args, etc.), but we are avoiding adding that maintenance cost until we see a need. diff --git a/lib/scope.js b/lib/scope.js index c2d73d77..8a586c67 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -2768,7 +2768,7 @@ module.exports = { pic_name = `pic_on-${ short_id }-${ timestamp }`; } - await scope.take_a_screenshot( scope, { path: `./${ scope.paths.scenario }/${ pic_name }.jpg` }); + await scope.take_a_screenshot( scope, { path: `${ scope.paths.scenario }/${ pic_name }.jpg` }); await scope.afterStep( scope ); }, // Ends scope.steps.screenshot() diff --git a/lib/setup/artifacts.js b/lib/setup/artifacts.js index 04b49463..dd2be92d 100755 --- a/lib/setup/artifacts.js +++ b/lib/setup/artifacts.js @@ -2,19 +2,19 @@ const session_vars = require(`../utils/session_vars`); const files = require(`../utils/files`); -const Log = require(`../setup/artifacts.js`); +const Log = require(`../utils/Log.js`); -/** Reset and refresh artifacts path name */ +/** + * Reset and refresh artifacts path name. Use this at the start of every test + * run for that run's unique results. + * */ session_vars.delete_artifacts_path_name(); -// process.argv is a list of strings of commands and args. -// If using `npm run cucumber` (as opposed to the bin commands), -// use `--` before `--sources=./foo` -const argv = require(`minimist`)( process.argv.slice(2) ); -const artifacts_path = files.make_artifacts_folder( argv.path ); - +const artifacts_path = files.make_artifacts_folder(); session_vars.save_artifacts_path_name( artifacts_path ); -// Leave the config Project name value as it is. Local developers find it -// useful to re-use that value. +const log = new Log({ path: artifacts_path, context: `artifacts` }); +log.info({ code: `ALK0278`, context: `artifacts`,}, + `Created the artifacts folder at "${ artifacts_path }"` +); diff --git a/lib/utils/Log.js b/lib/utils/Log.js index bbeb40c4..b32aa41f 100644 --- a/lib/utils/Log.js +++ b/lib/utils/Log.js @@ -228,6 +228,7 @@ class Log { } if ( !fs.existsSync( dir )) { + process.stdout.write(`Making artifacts folder in Log at ${ dir }`) files.make_artifacts_folder( dir ); } diff --git a/lib/utils/files.js b/lib/utils/files.js index 3cd1b6e1..c13ddfea 100644 --- a/lib/utils/files.js +++ b/lib/utils/files.js @@ -1,5 +1,6 @@ const fs = require('fs'); const safe_filename = require('sanitize-filename'); +const path = require(`path`); let files = {}; @@ -21,27 +22,30 @@ files.readable_date = function() { return fancy_str; }; // Ends files.readable_date() -files.make_artifacts_folder = function (path) { +files.make_artifacts_folder = function ( file_path ) { /** Makes a folder in the root of the project if it doesn't * already exist, either with the given name or with a default name. * * Works for local development as well, without action.yml or workflow files. * */ - if (path) { - let name_parts = path.split(`/`); + if (file_path) { + let name_parts = file_path.split(`/`); let safe_parts = []; for ( let part of name_parts ) { safe_parts.push(safe_filename( part )); } - path = safe_parts.join(`/`); + file_path = path.join( ...safe_parts ); } else { - path = `alkiln-${ files.readable_date() }` + file_path = `alkiln-${ files.readable_date() }` } + const root = process.cwd(); + root_path = path.join( root, file_path ); + // `recursive` avoids errors if folder already exists // https://nodejs.org/docs/latest-v18.x/api/fs.html#fspromisesmkdirpath-options - fs.mkdirSync( path, { recursive: true }); - return path; + fs.mkdirSync( root_path, { recursive: true }); + return root_path; }; // Ends files.make_artifacts_folder() module.exports = files; diff --git a/lib/utils/session_vars.js b/lib/utils/session_vars.js index d4986d30..964f3551 100644 --- a/lib/utils/session_vars.js +++ b/lib/utils/session_vars.js @@ -52,6 +52,10 @@ session_vars.get_origin = function () { }; session_vars.get_user_project_name = function () { return process.env._PROJECT_NAME || null; }; session_vars.get_user_id = function () { return process.env._USER_ID || null; }; +session_vars.get_runtime_config_path = function () { + const root = process.cwd(); // absolute path just to be sure + return path.join( root, `runtime_config.json` ); +} // More complex logic session_vars.get_server_reload_timeout = function () { @@ -133,7 +137,6 @@ session_vars.get_languages = function () { }; // Ends session_vars.get_languages() -const runtime_config_path = `runtime_config.json`; const project_name_key = `da_project_name`; session_vars.save_project_name = function ( project_name ) { /* Saves the name of the project to a json obj so deletion can use it later. @@ -142,6 +145,7 @@ session_vars.save_project_name = function ( project_name ) { * Should we try to prevent someone overwriting a previous project name? * * @param project_name { string } - purely alphanumeric string */ + const runtime_config_path = session_vars.get_runtime_config_path(); // Warn local developers if file already exists if ( fs.existsSync( runtime_config_path ) ) { @@ -172,6 +176,8 @@ session_vars.get_project_name = function () { if ( session_vars.get_origin() === 'playground' ) { return session_vars.get_user_project_name(); } + + const runtime_config_path = session_vars.get_runtime_config_path(); let json = JSON.parse( fs.readFileSync( runtime_config_path )); let project_name = json[ project_name_key ] || null; if ( session_vars.get_debug() ) { console.debug( `🐛 ALK0037 DEBUG: Project name from file is "${ project_name }".` ); } @@ -180,6 +186,7 @@ session_vars.get_project_name = function () { session_vars.delete_project_name = function () { /* Empty the key storing the name of the docassemble Project */ + const runtime_config_path = session_vars.get_runtime_config_path(); try { // Get the json, changing it, and re-write the file from scratch let json = JSON.parse( fs.readFileSync( runtime_config_path )); @@ -252,6 +259,7 @@ session_vars.save_runtime_config_var = function ({ key, value }) { * * @returns {binary?} file Runtime config file */ + const runtime_config_path = session_vars.get_runtime_config_path(); // Ensure runtime config file exists if ( !fs.existsSync( runtime_config_path ) ) { @@ -277,6 +285,7 @@ session_vars.get_runtime_config_var = function ( key ) { * * @returns {any} - Value of given key in runtime config file or null */ + const runtime_config_path = session_vars.get_runtime_config_path(); try { let json = JSON.parse( fs.readFileSync( runtime_config_path )); let value = json[ key ]; @@ -300,6 +309,7 @@ session_vars.delete_runtime_config_var = function ({ key }) { * * @returns {null} * */ + const runtime_config_path = session_vars.get_runtime_config_path(); try { if ( !fs.existsSync( runtime_config_path )) { return null; } diff --git a/lib/utils/set_sources_paths.js b/lib/utils/set_sources_paths.js index 7e2b5717..41d1561a 100644 --- a/lib/utils/set_sources_paths.js +++ b/lib/utils/set_sources_paths.js @@ -1,4 +1,5 @@ const fast_glob = require(`fast-glob`); +const path = require(`path`); const session_vars = require(`./session_vars`); @@ -18,7 +19,7 @@ module.exports = function set_sources_paths({ log }) { let maybe_paths = get_potential_paths(); let sources_paths = get_sources_paths({ maybe_paths }); - if ( sources_paths === 0 ) { + if ( sources_paths.length === 0 ) { // If we don't throw, it'll be more confusing when no tests run log.throw({ code: `ALK0050`, context: `sources`, error: new ReferenceError( @@ -45,6 +46,7 @@ function get_potential_paths() { // `project` is the name in the server docassemble "Project" page let project = session_vars.get_user_project_name(); let id = session_vars.get_user_id(); + let root = process.cwd(); // Assumes correct root. See docs about root path. // process.argv is a list of strings of commands and args. // Internal note: If using `npm run cucumber`, use `--` before @@ -53,8 +55,8 @@ function get_potential_paths() { default: { sources: [ `./docassemble/*/data/sources`, // In GitHub folder - `/usr/share/docassemble/files/playgroundsources/${id}/${project}/*.feature`, // From playground without S3 - `/tmp/playgroundsources/${id}/${project}/*.feature` // From playground with S3 + `/usr/share/docassemble/files/playgroundsources/${id}/${project}`, // From playground without S3 + path.join( root, `/playgroundsources/${id}/${project}`), // From playground with S3 ], } }); @@ -81,16 +83,16 @@ function get_sources_paths({ maybe_paths }) { // Check every path, tracking which exist and which are missing let existing_paths = []; let missing_paths = []; - for ( let path of maybe_paths ) { + for ( let maybe_path of maybe_paths ) { let dirs = fast_glob.sync( - [ path ], + [ maybe_path ], { onlyFiles: false, suppressErrors: true } ); if ( dirs.length > 0 ) { existing_paths.push( ...dirs ); } else { - missing_paths.push( path ); + missing_paths.push( maybe_path ); } } diff --git a/package.json b/package.json index e376e90d..ef926b20 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@suffolklitlab/alkiln", - "version": "5.15.4", + "version": "5.15.4-ref-paths-1", "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.", "main": "lib/index.js", "scripts": { From b1ec4b9b0994493d4a35c8b93deea9d1da6708dc Mon Sep 17 00:00:00 2001 From: plocket <52798256+plocket@users.noreply.github.com> Date: Thu, 7 May 2026 09:19:18 -0400 Subject: [PATCH 2/2] Clarify documentation wording Co-authored-by: Bryce Willey --- docs/016_root_path_2026_04_09.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/016_root_path_2026_04_09.md b/docs/016_root_path_2026_04_09.md index a1569f50..36c95895 100644 --- a/docs/016_root_path_2026_04_09.md +++ b/docs/016_root_path_2026_04_09.md @@ -1,6 +1,6 @@ # Root path -This code assumes the current root directory will contain the necessary files and folders to get files like `.feature` files and the test run runtime config file. +This code assumes the current working directory (cwd) will contain the necessary files and folders to get files like `.feature` files and the test run runtime config file. When you run ALKiln on the command line, run the tests in the root directory of the docassemble package with the tests. There are other ways to tell processes where the root directory is. For example, ALKiP, a docassemble package that uses ALKiln inside an author's server, runs `subprocess.run()` or `subprocess.Popen()` with `cwd=`. In ALKiP the `cwd` is most likely `/tmp`.