Close #1014, ALKiP notifications#1041
Conversation
any announcements new ALKiln versions have even if ALKilnInThePlayground isn't itself updated. Closes #1014 Co-authored-by: will-morningstar <mxwillmorningstar@gmail.com>
…n into 1014_breaking_news_2
tobynorth
left a comment
There was a problem hiding this comment.
Looks good to me! Didn't find any major issues. One high-level, almost certainly out of scope question for my own understanding: it seems like the need for this PR arose out of ALKiln and ALKiP being separate packages -- why is that? (would you ever want to install ALKiP without wanting to install ALKiln?)
And a general question: how are all the logging changes (artifacts.js, validate.js, Log.js) and path changes (files.js, session_vars.js, set_sources_path.js, scope.js) related to the new notifications feature? For clarity, I'd suggest either putting something about these changes in the PR description or moving them to separate PR(s).
| const argv = require(`minimist`)( process.argv.slice(2) ); | ||
| const artifacts_path = files.make_artifacts_folder( argv.path ); | ||
|
|
||
| const artifacts_path = files.make_artifacts_folder(); |
There was a problem hiding this comment.
nit: argv is no longer used -- consider either cleaning it up or (if this was accidental) restoring it as an arg to make_artifacts_folder
There was a problem hiding this comment.
Great point, thank you!
I realize you have no context, but I'm going to think about this "out loud", so if you do have thoughts I'd appreciate them. tl;dr: I think I could bring it back to add flexibility without any particular downside. [I think I should remove argv considering how session_vars finds the config path now, though I'm still not sure I'm a fan.]
If I recall correctly, I had meant to remove it. I think the caller is now meant to make sure the root of the command is where the artifacts folder should be. In ALKiP (a project that uses this lib), for example, the cwd argument for subprocess ensures the root is in /tmp. I'm actually not sure I like it as the behavior is a bit hidden. I remember having trouble wrangling the root of the path for ALKiP for some reason, though, and landing on this solution. Seems to me like I could have both without a problem. [Going to keep using cwd for now, though, since session_vars is strictly using cwd with no other arguments. It makes sense I suppose. We'd otherwise have to manage an additional env var. See session_vars.get_runtime_config_path().]
[Note to self: Worth adding some of these notes to the docs.]
| // Save Scenario data in Scenario folder | ||
| // '@alkiln @generated' | ||
| // console.log( JSON.stringify( scenario ) ); |
There was a problem hiding this comment.
question/nit: did you mean to commit these comments?
There was a problem hiding this comment.
Nice catch!
| // Save Scenario data in Scenario folder | |
| // '@alkiln @generated' | |
| // console.log( JSON.stringify( scenario ) ); |
| scope.paths.scenario_report = `${ scope.paths.scenario }/report.txt`; | ||
| // If it's a generated Scenario, save the `.feature` file in here | ||
| // GENERATED_FILES_FOLDER_NAME | ||
| if ( scenario.gherkinDocument.uri.includes("_alkiln_generated") ) { |
There was a problem hiding this comment.
nit: _alkiln_generated is a magic string, ideally would be defined in a more maintainable location. (Where did it come from, anyway? I don't see any other references to it in this PR)
There was a problem hiding this comment.
Good catch here too. The name is already in globals.js and I should be using that instead. I made a variable for it and everything! Looks like I don't need a separate variable for that atm anyway.
| if ( scenario.gherkinDocument.uri.includes("_alkiln_generated") ) { | |
| if ( scenario.gherkinDocument.uri.includes(globals.generated_files_folder_name) ) { |
| * Note that the function prints the value to the console before returning the | ||
| * value. This is the only way ALKilnInThePlayground can get the value. |
There was a problem hiding this comment.
question: why is this? Seems cleaner to use a temp file; is that not feasible for some reason?
There was a problem hiding this comment.
You're right, this comment is inaccurate. The idea of creating a file hadn't occurred to me. I am reluctant to do so because file reference wrangling has been an albatross around my neck throughout this project and I still haven't figured out why.
Response: May be subject to cli GUI changes. I'm concerned about future breaking changes.
Response to response: There's no cli, just stdout, and that's purely the HTML. Maybe I should make this a JSON string though to future-proof in case I want to include more info in the future.
Response: Are there any other consumers? Or could there be in the future? It's pretty tightly coupled.
Response to response: Good question. Right now there aren't. If there are in the future, putting this in a file instead wouldn't create a breaking change, so I'm not as concerned about that. It would be easy to keep this grandfathered in as a fallback to a file. It is tightly coupled. I'm not completely comfortable with that.
Response: It's odd there are 2 different repositories.
Response to response: I've been wanting to document this. It's a good point. When people install the ALKiln package, they'd have to install all the ALKiP code as well. It makes the size of the package larger and increases the complexity of the repository. This adds some complexity, but avoids other complexity. Also, they sometimes progress at different rates. That's not ideal, but actually may not be easier to wrangle in one repo. For example, we currently have two ALKiP versions available and we've had to implement the same changes on both. Maybe I'm wrong and the complexity would actually be reduced, but I'm not sure how test that out. What if ALKiP's ALKiln version was different than the repo it's in. I think it's worth re-examining, though.
TODO: Put this string in a JSON object at the very least. Consider storing it in a file.
TODO: Discuss merging ALKiP and ALKiln repos.
| for ( let subhead of shuffled_subheads ) { | ||
|
|
||
| if ( subhead.includes(`!`) && used_exclamation ) { continue; } | ||
| if ( subhead.includes(`?`) && used_question ) { continue; } | ||
| if ( subhead.includes(`"`) && used_quotes ) { continue; } | ||
| if ( subheads_used[ subhead ]) { continue; } | ||
|
|
||
| if ( subhead.includes(`!`)) { used_exclamation = true; } | ||
| if ( subhead.includes(`?`)) { used_question = true; } | ||
| if ( subhead.includes(`"`)) { used_quotes = true; } | ||
|
|
||
| final_subhead = subhead; | ||
| subheads_used[ subhead ] = true; | ||
| } |
There was a problem hiding this comment.
suggestion: it feels like it would be a little cleaner to refactor this into get_shuffled_subheads by just ensuring that a max of 1 subhead with each checked char (and no duplicate subheads) are added to the return array. Then in this function, you could just pop or shift an element off the array to pass to get_one_articles_parts
There was a problem hiding this comment.
I like this!
TODO: Implement above!
| let html_paragraphs = []; | ||
| for ( let one_paragraph of paragraphs ) { | ||
| html_paragraphs.push(`<p class="body_copy">${ one_paragraph }</p>`); | ||
| } | ||
| return html_paragraphs; |
There was a problem hiding this comment.
suggestion: this could be simplified to:
| let html_paragraphs = []; | |
| for ( let one_paragraph of paragraphs ) { | |
| html_paragraphs.push(`<p class="body_copy">${ one_paragraph }</p>`); | |
| } | |
| return html_paragraphs; | |
| return paragraphs.map(paragraph => `<p class="body_copy">${ paragraph }</p>`); |
| if ( typeof list_or_str === `string` ) { | ||
| indent_str = ` `.repeat( indent ); | ||
| return [ indent_str + list_or_str ]; | ||
| } | ||
|
|
||
| let flattened = []; | ||
| for ( let nested_str_or_list of list_or_str ) { | ||
| let result = indent_and_flatten_html_list( | ||
| nested_str_or_list, | ||
| indent + 1 | ||
| ); | ||
| // // .concat() feels evil because it accepts a string as well | ||
| // flattened = flattened.concat( result ); | ||
| flattened.push( ...result ); | ||
| } | ||
|
|
||
| return flattened; |
There was a problem hiding this comment.
suggestion: I think this could be simplified to:
| if ( typeof list_or_str === `string` ) { | |
| indent_str = ` `.repeat( indent ); | |
| return [ indent_str + list_or_str ]; | |
| } | |
| let flattened = []; | |
| for ( let nested_str_or_list of list_or_str ) { | |
| let result = indent_and_flatten_html_list( | |
| nested_str_or_list, | |
| indent + 1 | |
| ); | |
| // // .concat() feels evil because it accepts a string as well | |
| // flattened = flattened.concat( result ); | |
| flattened.push( ...result ); | |
| } | |
| return flattened; | |
| return list_or_str.flatMap(nested_str_or_list => | |
| Array.isArray(nested_str_or_list) | |
| ? indent_and_flatten_html_list(nested_str_or_list, indent + 1) | |
| : ` `.repeat(indent) + nested_str_or_list | |
| ); |
| } | ||
|
|
||
|
|
||
| function get_semver_parts( version_str ) { |
There was a problem hiding this comment.
suggestion: Consider using a 3rd party library to replace all your semver-related functions if you haven't already (example). Since this is neither related to the core ALKiln logic nor (it seems) something ALKiln needs to go outside the semver spec for, probably better to let someone else maintain it
There was a problem hiding this comment.
TODO: Research! This is an even better candidate for a 3rd party library. So much more fiddly.
| const log = new Log({ path: artifacts_path, context: `artifacts` }); | ||
| log.info({ code: `ALK0284`, context: `artifacts`,}, | ||
| `Created the artifacts folder at "${ artifacts_path }"` | ||
| ); | ||
|
|
||
| // This is just for artifacts, not any other config values. Leave the config | ||
| // Project name value as it is. Local developers find it useful to re-use it. |
There was a problem hiding this comment.
What does "This" mean, Past Self? I think it meant that saving the artifacts path name here is unique in some way. Maybe because it's updated with every run of the tests. If so, we should write documentation about which config values get changed and when they get changed. Suggestion:
Changing config values:
- Artifacts folder: name is different with each test run since artifacts are unique to each test run.
- Project name: the env vars determine the Project name. Local devs often re-use the same Project. Other environments, like GitHub or ALKiP are one-use and manage their own Project name value.
Remove comment from code here:
| const log = new Log({ path: artifacts_path, context: `artifacts` }); | |
| log.info({ code: `ALK0284`, context: `artifacts`,}, | |
| `Created the artifacts folder at "${ artifacts_path }"` | |
| ); | |
| // This is just for artifacts, not any other config values. Leave the config | |
| // Project name value as it is. Local developers find it useful to re-use it. | |
| const log = new Log({ path: artifacts_path, context: `artifacts` }); | |
| log.info({ code: `ALK0284`, context: `artifacts`,}, | |
| `Created the artifacts folder at "${ artifacts_path }"` | |
| ); | |
| // Store path name for this Scenario's individualized report | ||
| scope.paths.scenario_report = `${ scope.paths.scenario }/report.txt`; | ||
| // If it's a generated Scenario, save the `.feature` file in here | ||
| // GENERATED_FILES_FOLDER_NAME |
There was a problem hiding this comment.
| // GENERATED_FILES_FOLDER_NAME |
|
|
||
| const GENERATED_FILES_FOLDER_NAME = globals.generated_files_folder_name; |
There was a problem hiding this comment.
| const GENERATED_FILES_FOLDER_NAME = globals.generated_files_folder_name; |
|
Thanks for the excellent and detailed review! I've finally started in on this, though it'll stay here a while longer (see below).
Good insticts! The logging and path changes were indeed meant to be in a separate PR and I'll move them there and deal with those first before addressing the notification feature, so this PR is going to end up sitting on the back burner a little while longer unfortunately. And will probably run into merge conflicts unless I make a whole new PR, which I won't because I don't want to lose this awesome review. Some days you can't have it both ways
I think it's understandable without too much further context. There are 4 environments in which ALKiln can run and ALKiP is just one of those. As such, though you'd never want to install ALKiP without ALKiln, you may very well want to install ALKiln without ALKiP. At least, we have some users who do that. |
|
Before merging, change workflow files to use feature branch. |
In this PR, I have:
TODO:
Reason for this PR
[Early PR for quick top-level sanity check review, failing tests are fine for now.]
Summary: Add notifications to ALKilnInThePlayground (ALKiP) for ALKiln changes
Details:
This is a testing framework. One of the environments in which it runs is on a test author's own server. ALKiP is a package of mine that helps authors runs these tests on their server. I work hard to ensure backwards compatibility, but it can be useful to keep both up to date with the other and sometimes to notify authors of other changes to their server that might affect how ALKiP is running. It's hard for authors to tell which versions match up with each other and when to update in general.
This ALKiln PR generates HTML that ALKiP can use to add notifications about relevant changes to ALKiln, to the author's server, or about whatever else ALKiln thinks is helpful. We will use notifications sparingly.
Demo of the visual appearance of a notification. Visual preview:
Links to any solved or related issues
Closes #1014
Any manual testing I have done to ensure my PR is working
Publishing to npm and testing on ALKiP