@suffolklitlab/alkiln 5.13.2 → 5.13.3-feat-random

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.
Files changed (49) hide show
  1. package/.github/workflows/github_server.yml +7 -7
  2. package/.github/workflows/playground.yml +2 -1
  3. package/CHANGELOG.md +32 -1
  4. package/action.yml +89 -29
  5. package/action_for_github_server/action.yml +73 -29
  6. package/docs/4_environments.md +243 -0
  7. package/docs/artifacts_folder_name.md +135 -0
  8. package/docs/constrained_random_tests.md +254 -0
  9. package/docs/decisions/006_which_log_level_to_pick_2024_09_5.md +70 -0
  10. package/docs/decisions/007_getting_and_storing_logs.md +179 -0
  11. package/docs/decisions/move_runtime_config_to_sources_folder.md +5 -0
  12. package/docs/decisions/reports_and_logs_2024_08_11.md +161 -0
  13. package/docs/kinds_of_errors.md +26 -0
  14. package/docs/prioritize_constrained_random_tests.md +60 -0
  15. package/lib/docassemble/docassemble_api_interface.js +77 -51
  16. package/lib/docassemble/server_install.js +32 -9
  17. package/lib/docassemble/setup.js +41 -24
  18. package/lib/docassemble/takedown.js +33 -10
  19. package/lib/globals.js +10 -0
  20. package/lib/run_cucumber.js +155 -50
  21. package/lib/scope.js +101 -77
  22. package/lib/setup/TestGenerator.js +1745 -0
  23. package/lib/setup/artifacts.js +20 -0
  24. package/lib/setup/generate.js +42 -0
  25. package/lib/setup/get_Gherkin_AST.js +26 -0
  26. package/lib/setup/validate.js +89 -0
  27. package/lib/steps.js +176 -51
  28. package/lib/utils/Log.js +1132 -0
  29. package/lib/utils/_alkiln-constrained_random_tests_old.js +1727 -0
  30. package/lib/utils/files.js +18 -11
  31. package/lib/utils/get_error_stacks.js +16 -0
  32. package/lib/utils/reports.js +95 -28
  33. package/lib/utils/session_vars.js +102 -73
  34. package/lib/utils/set_sources_paths.js +98 -0
  35. package/lib/utils/time.js +44 -0
  36. package/package.json +16 -8
  37. package/tests/log_codes/check_codes.sh +19 -14
  38. package/tests/log_codes/log_code_expected_instances.txt +69 -9
  39. package/tests/unit_tests/constrained_random_answers/invalid.fixtures.js +155 -0
  40. package/tests/unit_tests/constrained_random_answers/invalid.test.js +233 -0
  41. package/tests/unit_tests/constrained_random_answers/valid.fixtures.js +905 -0
  42. package/tests/unit_tests/constrained_random_answers/valid.test.js +521 -0
  43. package/tests/unit_tests/example_of_chai_test_lifecycle.js +78 -0
  44. package/tests/unit_tests/getAllFields.test.js +5 -0
  45. package/tests/unit_tests/getMatchingRows.test.js +5 -0
  46. package/tests/unit_tests/log.test.js +557 -0
  47. package/tests/unit_tests/normalizeTable.test.js +5 -0
  48. package/lib/utils/log.js +0 -96
  49. /package/docs/{expected_inernal_results.md → expected_internal_results.md} +0 -0
@@ -0,0 +1,70 @@
1
+ # Which log levels to pick
2
+
3
+ ## Context and Problem Statement
4
+
5
+ Both authors and internal ALKiln developers see and use console logs and logs in debug files to track down bugs and understand how the tests and ALKiln are working.
6
+
7
+ <!-- TODO: Should this be about `Log` or about log levels?
8
+
9
+ `Log` uses the standard log levels `info`, `warn`, `error`, and `log`. It sometimes uses them differently than nodejs's `console` object would. For example, `Log` has a `.throw()` instead of a `.error()`, which stores the error information in the debug files and then throws the error.
10
+
11
+ `Log` saves and prints these logs with metadata, like icons and date stamps. For example, `Log`'s `.success()` uses the console log level `info` and includes a rainbow icon.
12
+
13
+ When we need to, we skip using the `Log` class and write straight to the console. -->
14
+
15
+ We want to give the internal development team descriptive information. We also want to give authors the best signal we can about their tests. How do we balance these? For example, if we show an error icon whenever there's an "informational" error about an aborted promise - an expected event that we cause whenever we use `Promise.race()` - an author could waste a lot of time trying to track down a problem that doesn't exist. Using `Log`'s `.success()` too much could also be confusing - successfully deleting a Project from a server shouldn't look like a passing test.
16
+
17
+ When picking the log level for a log, how do we know which level to choose?
18
+
19
+ ## Considered Options
20
+
21
+ - Stay as true to technical behavior as possible and be very literal - throw all errors, log anything successfully completed action with `.success()`.
22
+ - Stay as clear to authors as possible - use levels that will help authors pay attention to what affects them.
23
+
24
+ Example options for `success`:
25
+
26
+ - Use whenever anything completes successfully.
27
+ - Only use for success of major operations.
28
+ - Use in the summary report for every test success.
29
+ - Use in the summary report for success of the whole test suite.
30
+
31
+ Example options for `error`:
32
+
33
+ - Always use `error` when any kind of error happens.
34
+ - Use `error` when an unexpected error happens.
35
+ - Only use `error` for author-relevant errors.
36
+
37
+ (See the architecture design document about errors.)
38
+
39
+
40
+ ## Decision Outcome
41
+
42
+ Distract the author as little as possible everywhere.
43
+
44
+ "success" icon:
45
+
46
+ - Only actually log to the console and debug files with `log.success()` once for success of all tests.
47
+ - Include the success icon at the top of the Scenario "Passed" section of the report.
48
+ - Include the success icon in individual Scenario reports for Scenarios that passed.
49
+
50
+ Throwing an error[^1]:
51
+
52
+ - Throw an error for a test that fails. cucumberjs will use this error to mark the test as failed.
53
+ - Throw an error when ALKiln has to unexpectedly abort a test.
54
+ - Throw an error when ALKiln's code has to unexpectedly abort one of its own runtime processes. This includes processes like setup and takedown.
55
+
56
+ "error" log level:
57
+
58
+ - Only use the "error" level with errors that the test author should investigate[^1].
59
+
60
+ "info" log level:
61
+
62
+ - Log expected errors (errors that are purely informational) with the "info" level. For example, when we wait for navigation, we use a `Promise.race()`. When one promise wins the race, the correct thing to do is to abort the other promises. That causes errors that we catch. We log that *information*[^1] as data about which promise won during that test.
63
+
64
+ "warn" log level[^1]:
65
+
66
+ - Use the "warn" log level in the console for messages the test author should investigate.
67
+ - Use the "warn" log level in the debug log for messages that could give ALKiln devs clues about a potential problem.
68
+ - Use the "warn" log level for messages for internal problems that do not impact the tests (non-critical errors). For example, errors that happen when ALKiln tries to save to log files or errors when ALKiln tries to delete finished interview sessions. These messages should be silent and should look nonthreatening. Do try to be clear about what happened, but also try to avoid using the word "error" if possible. E.g. use "Skipped creating metadata for the logs" instead of "Error while creating metadata for the logs". Include the error along with that log message, but avoid throwing it. ALKiln should only write the message and `Error` to the console if no other output is possible.
69
+
70
+ [^1]: Read more about this in the documentation about errors.
@@ -0,0 +1,179 @@
1
+ # Getting and storing logs
2
+
3
+ ## Context and Problem Statement
4
+
5
+ There are multiple processes that log to the console - our own console logs, cucumberjs logs, and GitHub action logs[^1]. We want to save some of those to the reports and all of those to the debugging logs. We also want to save plain debug messages that get sent only to the instance of `Log` and not to the console[^2].
6
+
7
+ Also, if we want to search one of the debugging logs for output that was sent to the console, it would be nice to just copy the text and search for the exact text.
8
+
9
+ Every process that we trigger separately in the GitHub action - setup, server_install, run_cucumber, and takedown - each need their own instance of `Log`. There isn't a way to pass the instance between those processes[^1]. Also, until we wrap the entire cucumberjs process inside another manager of some kind (which we may do in the future to improve and manage internal tests for failure handling), it seems impossible to pass an instance of `Log` from our cucumberjs runner (currently `run_cucumber.js`) to the cucumberjs `World` object so we can use it while running the actual tests with the tests handler[^3].
10
+
11
+ [^1]: See the architecture design doc about our 4 environments.
12
+
13
+ [^2]: See the decision doc about our report and log architecture.
14
+
15
+ ## Goals
16
+
17
+ MVP:
18
+ - Have a unified way of getting and storing all messages including console logs, stdout, and internal silent debug logs.
19
+ - Make the logs look like the console's logs to more easily visually match the output.
20
+
21
+ Ideally:
22
+ - Also capture information from cucumberjs.
23
+
24
+ ## Considered Options
25
+
26
+ - Listen for write to `stdout`
27
+ - Save with `fs` `console.Console` `writeStream`s
28
+ - Cucumber formatters
29
+ - `fs.appendFileSync` with node's `util.inspect`
30
+ - A combination of methods
31
+
32
+ ## Decision Outcome
33
+
34
+ Use fs.appendFileSync with node's `util.inspect`. In future, we might explore capturing cucumber logs in this process too.
35
+
36
+ ## Pros and Cons of the Options
37
+
38
+ ### Listen for write to `stdout`
39
+
40
+ We'd listen for `stdout` and `stderr` events. We haven't yet experimented with detecting which logs are cucumber's and storing only those. See pseudo code in the supplementary material section.
41
+
42
+ **Pros:**
43
+
44
+ - We wouldn't have to make an intermediary for handling first saving to debug logs and then logging to the console. We could simply log straight to the console which would trigger a function that would save to the debug files.
45
+ - Those saved strings would look the same as those console logs.
46
+ - We might be able to catch some of cucumber's output more easily and accurately.
47
+
48
+ **Cons:**
49
+
50
+ - When we tried this, the data that came into the console listener was unable to handle emoji, even with various methods of encoding/decoding. It may have problems with other characters as well.
51
+ - We'd still need to create metadata, which makes the process more complex than "simply logging to the console".
52
+ - We might still need to make custom error throwing behavior, which might then make our code less consistent.
53
+ - We haven't experimented with detecting what log level the caller used.
54
+
55
+ ### Save with `fs` Console write streams
56
+
57
+ We would create one custom `console.Console` instance that saves to the debugging files and maybe another for the report logs.
58
+
59
+ **Pros:**
60
+
61
+ - The saved strings would look the same as console logs because we'd literally be using an actual console object's `.log()`.
62
+ - We could avoid handling circular references and other annoying errors ourselves.
63
+
64
+ **Cons:**
65
+
66
+ - We have to manage the streams' lifecycles - opening, flushing, and closing the streams. That includes special ways to flush the streams to get their text into the debugging and report files with event listeners for `end` and `close` and such. We need to do that by wrapping node processes in a try/catch. There are problems with this.
67
+ 1. It seems fragile.
68
+ 2. In one case, it's potentially impossible - the tests handler[^3]. The cucumber runner has a clear beginning and end to its node process and we can open and close the write streams around that process. The tests handler is buried in that and we don't have the experience we need to understand how to wrap the node process in there. E.g. do we wrap the whole steps.js file in a try/catch? Do we wrap each hook? Do we wrap the code in each hook? We need to throw errors so that cucumber can catch them and detect failures. See example below in supplementary material.
69
+
70
+ ### Cucumber formatters
71
+
72
+ `cucumberjs` recommends using its [formatters](https://github.com/cucumber/cucumber-js/blob/main/docs/formatters.md)
73
+
74
+ **Pros:**
75
+
76
+ - We would be working within the `cucumberjs` ecosystem. That could give us access to data we might not otherwise be able to get and may have other benefits that we haven't explored fully because of the cons.
77
+
78
+ **Cons:**
79
+
80
+ - We were unable to get anything to work.
81
+ - We won't be able to use that with anything outside cucumber. For example, setup, takedown, possibly the cucumber runner, and (in future) with GitHub action logs.
82
+ - We would need to keep up with `cucumberjs` updates for this too.
83
+
84
+ ### `fs.appendFileSync` with node's `util.inspect`
85
+
86
+ **Pros:**
87
+
88
+ - We're familiar with the functionality.
89
+ - `util.inspect` is what node uses to write to the console, so it can give us very similar output, though there's some customization we still have to do.
90
+
91
+ **Cons:**
92
+
93
+ - We have to do some of our own formatting and it would probably take a lot of care to make it completely match the console logs. For example, we could easily inspect the list of logs, but then it would look like a list, not like individual logs.
94
+ - We have to manage circular dependencies and possibly other details.
95
+
96
+ ### A combination of methods
97
+
98
+ For example, the cucumber formatter as well as one of the other methods.
99
+
100
+ **Pros:**
101
+
102
+ - We'd get the functionality of the pros of each.
103
+
104
+ **Cons:**
105
+
106
+ - We'd get at least some of the cons of each.
107
+ - It may be a more complex system and possibly harder to maintain.
108
+
109
+ ## Supplementary material
110
+
111
+ ### Use `fs` Console to write to custom streams
112
+
113
+ Examples of using node `fs` custom [console.Console](https://nodejs.org/docs/latest-v18.x/api/console.html) and [writeStream](https://nodejs.org/docs/latest-v18.x/api/fs.html#filehandlecreatewritestreamoptions). Before exiting, either with 0 or with an error, we have to flush the stream with `.on("close")` and `.on("error")` to write to the file.
114
+
115
+ Flushing streams:
116
+
117
+ ```js
118
+ // Based on https://github.com/nodejs/node/issues/24688#issuecomment-442688666
119
+ const fs = require(`fs`);
120
+ const util = require(`util`);
121
+
122
+ async function run() {
123
+ // Create the stream to save to the debug.log file
124
+ const stream = fs.createWriteStream(`debug.log`);
125
+ // We can use this later to wait for the stream to close
126
+ const streamPromise = new Promise((res, rej) => {
127
+ stream.once(`close`, res).once(`error`, rej);
128
+ });
129
+
130
+ // Write some things
131
+ stream.write(new Date().toISOString() + `\n`);
132
+ stream.write(new Date().toISOString() + `\n`);
133
+
134
+ try {
135
+ // Example of exiting early
136
+ throw new Error(`Without ending the stream and awaiting the promise, this causes a blank debug.log`);
137
+ } catch (error) {
138
+ // Add more information to the file
139
+ stream.write(util.inspect(error));
140
+ stream.write(`We managed to avoid a blank file`);
141
+ // Avoid leaving the file blank
142
+ stream.end();
143
+ await streamPromise;
144
+ throw error;
145
+ }
146
+ }
147
+
148
+ run();
149
+ ```
150
+
151
+ To keep the logic in `Log`, `Log` could have a method that a process can use to wrap its functionality:
152
+
153
+ ```js
154
+ const run = function() { /* Do stuff */ };
155
+ log.with_flush( run );
156
+ ```
157
+
158
+ `Log` would handle creating the promises. `log.with_flush()` would handle the try/catch logic to wait for the listeners and close the streams.
159
+
160
+ ### Listen for write to `stdout`
161
+
162
+ For capturing console output, look into how to get the `stdin` for capturing various console output. https://stackoverflow.com/a/54202970:
163
+
164
+ ```js
165
+ const stdin = process.openStdin()
166
+
167
+ process.stdout.write('Enter name: ')
168
+
169
+ stdin.addListener('data', text => {
170
+ const name = text.toString().trim()
171
+ console.log('Your name is: ' + name)
172
+
173
+ stdin.pause() // stop reading
174
+ })
175
+ ```
176
+
177
+ We couldn't figure out how to handle decoding emoji.
178
+
179
+ [^3]: I'm not sure of good names to differentiate the code in run_cucumber.js, which triggers the rest of the test running, but is separate from it, and the code that handles the nitty gritty - like index.js, steps.js, scope.js, etc. "Running cucumber" is ambiguous. I'm calling run_cucumber.js code the "cucumber runner" and I'm calling the code that handles the actual tests the "tests handler".
@@ -0,0 +1,5 @@
1
+ For ALKilnInThePlayground tests
2
+
3
+ Moving the runtime config to the sources folder would make it very easy for each Project to have its own runtime config which would make ALKilnInThePlayground work more smoothly. On the other hand, it could be confusing to authors (though it's fine if they check it in now, it may not be fine later). On the other other hand, we could delete it after every test suite run and and "temp" and "alkiln" to the name. If there are multiple sources folders with config files then...? We'll just have to pick one?
4
+
5
+ Maybe this wouldn't be useful? Check if runtime_config file might be used after further tests are run and will only point to the most recent test (and thus be unable to get previous tests). If so, that's what's going on now too.
@@ -0,0 +1,161 @@
1
+ # Shape and behavior of the report and log objects
2
+
3
+ <!-- TODO: turn this into a architecture design doc? -->
4
+ <!-- TODO: Rename to just "logs" -->
5
+
6
+ ## Context and scope
7
+
8
+ We show logs to the user in the console and as test suite artifacts. We have logs we use for development. cucumberjs also has logs we want to include in the above until we are confident we are handling all cases ourselves.
9
+
10
+ Right now, the system for logging is fractured, scattered, and duplicated. Logs that are not from the report don't get saved anywhere. Some logs only get shown in debug mode in the console, which can be frustrating and unhelpful. We lack verbose output information for tests that authors run in the wild.
11
+
12
+ This document excludes discussion of report architecture, but it is useful to note that at the moment they are completely separate from logs and yet save to some of the same files, so they influence our current choices.
13
+
14
+ ## Goals
15
+
16
+ - Make log information persistent and visible
17
+ - Add additional meaningful information to logs (e.g. metadata)
18
+ - Make sure logs are clear without needing to read the surrounding code
19
+ - Unify and clarify log flow
20
+ - Make log behavior consistent
21
+
22
+ ## Non/anti-goals
23
+
24
+ - Disrupt the signals we're giving authors about their tests with the noise of internal ALKiln details
25
+ - Create a complex and heavy system to try to abstract absolutely everything
26
+ - Let the complexity of the relationship with report behavior too heavily influencing our log design goals
27
+
28
+ ## The actual design
29
+
30
+ ### `Log` internal errors
31
+
32
+ Logs are informational and not related to the behavior of authors' tests. Because of that, these logs should avoid adding noise to what an author sees. That is, internal warnings or errors should look different than authors' test errors. To help with this:
33
+
34
+ - All log methods` arguments have default values.
35
+ - We use try/catch very aggressively in the `Log` class.
36
+
37
+ Read more of the thoughts behind this choice in the decision doc on which log level to pick and the design doc on types of errors.
38
+
39
+ ### Purposes of methods
40
+
41
+ Some of these notes have to do with reports because logs and reports are using some of the same files at the moment and yet are not integrated. We'll write a doc for reports when we integrate reports more fully with logs.
42
+
43
+ 1. `Log`: Messages or errors from wrapping functionality, like setup and takedown messages.
44
+ 2. `Log`: Test progress (a compact way to see what's happening/happened in each test):
45
+
46
+ Test1: .............F--.
47
+
48
+ Test 2: ....
49
+ 3. Report: A pretty report at the end with details about each test. It has sections, like "failed" and "passed", headings, etc.
50
+ 4. Report: cucumberjs's various console logs.
51
+ 5. `Log`: Lots of other informational logs for internal development and debugging purposes.
52
+
53
+ Different logs belong in different places:
54
+
55
+ - ALKiln always logs items 1, 2, 3, and 4 to the console.
56
+ - Ideally save all items in a verbose debug file sequentially as the tests run.
57
+ - During the tests, save items 2, 3, and 4 to a running report file sequentially as they come. Include metadata. Suggested filenames: `report_log.txt`/`running_report.txt`/`report_run.txt`. If we have to stop the tests early because of an infinite loop or something, this will at least show the test author some information.
58
+ - At the end of the tests, format items 1, 2, 3, and 4 nicely in a file called `report.txt`. This will mostly exclude the metadata.
59
+ - When there is a warning-level or error-level log, save 1, 3, 4, and 5 in an unexpected results file too.
60
+ - In future, discuss the usefulness of having a debug mode where ALKiln logs all items to the console while the tests are running.
61
+
62
+ We may also want copies of various files that have different formats for different environments (see docs on the different environments in which ALKiln runs).
63
+
64
+ - Console logs with colors.
65
+ - Plain text versions that exclude those color codes.
66
+ - HTML versions to show with, for example ALKilnInThePlayground. Maybe in GitHub as well in the future.
67
+
68
+ ### Anatomy and behavior of logging methods
69
+
70
+ Most of a `Log`'s logging methods add additional metadata to a log:
71
+
72
+ 1. The level of the log (success, info, warning, error, and custom values)
73
+ 2. An icon showing the level of a log
74
+ 3. A log code, like (e.g. ALK0105)
75
+ 4. Contextual info about where the log originated (e.g. "setup", "takedown", "invalid answer")
76
+ 9. A timestamp
77
+
78
+ To be able to show that metadata, most methods will accept custom values for parts of that metadata. Most methods offer:
79
+
80
+ - Log code
81
+ - Contextual info
82
+ - The error throwing information
83
+
84
+ For the debug method, everything other than the timestamp can be customized.
85
+
86
+ Callers can influence the content and formatting of most parts of the log message. For example, they can call `.warn` to show a warning icon and level metadata. The one exception would usually be the part before the metadata. Customizing that could be useful for things like creating a visual separator before ALKiln starts evaluating the fields of a web page. For that reason, the caller can add a `before` property to the metadata that a `Log` object will add before the metadata.
87
+
88
+ A `Log` object should be able to throw errors so that it can log metadata and any additional styling and context along with the error. It can also make sure that the error will be an actual `Error` object (as opposed to a string or other object). Otherwise everywhere we needed to throw an error, we would need duplicate handling bad data (like objects with circular references), using `Log` to save to files (etc.), throwing the error, and other possible complications. For that, `Logs` also need to accept this additional information:
89
+
90
+ - Whether to throw an error
91
+ - The error to throw
92
+
93
+ Those methods should also accept multiple individual log arguments the same way `console.log()` does. This lets the caller include strings separately from objects they want to log, including `Error` objects, so that a `Log` object can handle them safely and style them consistently. We could have required a list of logs instead, but the extra syntax was frustrating.
94
+
95
+ Right now, the only way we differentiate methods that just store data vs. methods that also log to the console is with documentation. This seems less than ideal. Using the name `record` could be confusing because developers might think they need to log to the console separately from saving the information to debug files.
96
+
97
+ ## APIs
98
+
99
+ Proposed external methods: `success`, `info`, `warn`, `throw`, `stdout`, `debug`. See the `Log` class and its tests for the most up-to-date API.
100
+
101
+ <!-- Discuss: better name for `stdout`? -->
102
+
103
+ ## Data storage
104
+
105
+ - `report.txt` - final pretty output for the user with headings, etc.
106
+ - Each Scenario's `report.txt` - pretty output in each test's folder.
107
+ - `report_log.txt` (name?) - ugly, but still sparse, giving the user some info about what happened during the tests without overwhelming them.
108
+ - `debug_log.txt` (name? `debug`?) - very ugly and full of all the information from everywhere else and more. Useful for internal development and troubleshooting. Includes setup, takedown, etc.
109
+ - `temp_unexpected_results_debug_log.txt` - A file containing messages about unexpected behavior, like test warnings and errors, as well as internal ALKiln warnings. We don't output this in our GitHub actions - the file itself is temporary, we just add its contents to the final unexpected results file. All its information exists in the debug file too.
110
+ - `unexpected_results.txt` - Logs for failures and other unexpected behavior. It has information about test warnings and errors, as well as internal ALKiln warnings. At first we only save report information there. When the tests finish, we add the contents of the specific debug file that contains only unexpected results.
111
+
112
+ ## Degree of constraint
113
+
114
+ - Right now we're a bit limited by report functionality staying separate. Other than that, we can design as we want. We should avoid the current behavior of reports substantially influencing our design decisions here. We do have to keep in mind how reports might integrate with this in the future, but we will also need to change the design of the report behavior significantly in the future, so we should keep an open mind.
115
+ - We have not yet approached saving logs from GitHub actions, though this should be fairly simple. It may add some complication.
116
+ - We have not yet solved getting all logs from the console (e.g. cucumberjs logs).
117
+
118
+ ## Alternatives considered
119
+
120
+ - Avoid the complexity of storing setup and takedown output in the verbose/debug file. In GitHub, they're in the GitHub console. Locally, we see the results immediately. On the other hand, storing them ourselves lets us look for information in just one place.
121
+ - Include GitHub action output in the debug files. Out of scope for the current iteration.
122
+ - Catch cucumberjs's actual console output. Out of scope for the current iteration.
123
+
124
+ ### System-context diagram
125
+
126
+ Current design before integration of reports, GitHub action logs, and other non-ALKiln console logs.
127
+
128
+ ```
129
+ ## `Log`
130
+
131
+ console methods -> ._console() -> .debug() -> log to the console
132
+ .stdout() -> .debug() -> sometimes write to stdout
133
+
134
+ .debug() -> debug_log.txt with metadata
135
+ -> sometimes temp_unexpected_results_debug_log.txt with metadata
136
+
137
+ ## `Report`
138
+ report and its methods -> report_log.txt (plain text)
139
+ -> debug_log.txt (plain text)
140
+ -> sometimes unexpected_results.txt (plain text)
141
+ -> report summary at end of tests
142
+ -> final report.txt (plain text summary)
143
+ -> final console log (summary with console colors)
144
+ -> individual Scenario reports at end of tests
145
+ -> final individual report summary file in each Scenario's folder
146
+
147
+ ## takedown
148
+ Add temp_unexpected_results_debug_log.txt to the end of unexpected_results.txt
149
+ ```
150
+
151
+ ## Cross-cutting concerns
152
+
153
+ _Security, privacy, observability, etc._
154
+
155
+ Code that calls `Log` methods should avoid passing sensitive information. A `Log` object doesn't protect against exposing information.
156
+
157
+ ## Related documentation
158
+
159
+ Decision doc about which log level to use.
160
+ Architecture design doc about types of unexpected and expected "errors".
161
+ Decision doc about getting and storing logs.
@@ -0,0 +1,26 @@
1
+ # Kinds of Errors
2
+
3
+ An error, or even an `Error` instance, can mean a lot of different things in the context of this testing framework. Throwing errors for failing tests is what we are most familiar with, but it is just one kind of error.
4
+
5
+ ## Interview `Error`s
6
+
7
+ 1. **Interview bugs**: For example, the interview says ALKiln's answer is invalid. The author expected that answer to be valid. ALKiln saves an error message to the report and throws an error to tell cucumber to mark the test as failed. Currently, these logs errors come with the 🤕 icon. When all the tests are done, the test suite will show as failing in the console, the report, and the various logs. In future, ALKiln will use a method like `report.throw()` to save to the various log files with the `error` level and then throw the error to let cucumber know the test failed.
8
+ 1. **Interview expected errors**: For example, the interview says ALKiln's answer is invalid. The author was testing their interview's field validation rules. The test passes.
9
+
10
+ ## Test file `Error`s
11
+
12
+ 1. **Test file syntax error**: For example, a row in a table in a test file has too many columns. cucumberjs throws an error and no tests are run. ALKiln should show the tests as failed. ALKiln is unable to get the output of that error into a log, but there should be an error in the console.
13
+ 1. **Test Step typo**: For example, `And I ta p to contcontinue`. cucumberjs will cause this test to fail, giving an "undefined" Step as the reason. The test suite will show as failing in the console, the report, and the various logs.
14
+ 1. **Test bugs**: For example, a test has old values that need to be updated. The test fails. This will look like an interview bug, as this document describes.
15
+
16
+ ## ALKiln `Error`s
17
+
18
+ 1. **ALKiln un-handled bugs**: For example, we have forgotten to define a variable and none of our tests has yet triggered that code. These are unexpected errors that force processes to abort or force individual tests to fail in uninformative ways. To an author that runs into that error, it looks like their test has failed, but the fault is ours and we need to troubleshoot it. We should make that clear.
19
+ 1. **ALKiln silent bugs**: For example, a debug log has an object that causes an error when ALKiln tries to turn it into a string. ALKiln should record the failure somewhere, but should avoid throwing it. We can troubleshoot those if we see them and add new internal tests. See decisions docs on when to use which kind of log level.
20
+ 1. **ALKiln expected errors**: For example, one promise won a `Promise.race()`, so ALKiln aborted the other promises. That causes those aborted promises to error. That is why ALKiln saves the "informational" Error to the debug files and it has a level of "info" or "debug".
21
+
22
+ ## ALKiln's internal tests
23
+
24
+ 1. **ALKiln internal success-handling test failures**: For example, the test fails to fill out radio buttons correctly.
25
+ 1. **ALKiln internal failure-handling test successes**: For example, we test that the summary report shows a specific log code when an interview fails to load and the log code shows up correctly. The "test" fails - the interview fails to load - but *our* test passes - the right log code was in the summary report.
26
+ 1. **ALKiln internal failure-handling test failures**: For example, we test that the report shows a specific log code when an interview fails to load, but the log code is missing. The "test" fails and *our* test fails.
@@ -0,0 +1,60 @@
1
+ # Prioritize constrained random answers tests over pure random answers tests
2
+
3
+ ## Context and Problem Statement
4
+
5
+ We could develop constrained randomness first or pure randomness first. Is there a reason to prioritize one over another?
6
+
7
+ ## Considered Options
8
+
9
+ - Purely random answers. They fill in fields without any guidance.
10
+ - Constrained random answers tests. They make the test author list all the variable names and all the choices of answers they want to allow for those variables.
11
+ - Hybrid approach. Allow an author to specify some values while letting other values stay completely random.
12
+
13
+ ## Decision Outcome
14
+
15
+ Prioritize constrained random answers tests over pure random answers tests. Strangely, they seem to be the more flexible of the two options.
16
+
17
+ ## Pros and Cons of the Options
18
+
19
+ The discussion about the general pros and cons of random answers tests will be in the `constrained_random_tests` doc for now.
20
+
21
+ ### Purely random answers
22
+
23
+ Pros:
24
+
25
+ - Very little work for test authors to write.
26
+ - Because they're so free-form, they may do a better job finding unexpected behavior that authors didn't think to test for.
27
+
28
+ Cons:
29
+
30
+ - Unless we can find better ways of generating purely random test files for the authors, copying a random answers test to keep permanently might be very challenging.
31
+ - Fields might get nonsensical answers. For example, a very large number for age or a very small number for income.
32
+ - Impossible to use for fields that need very specific answers, like a real lawyer's bar number or a specific file name for uploading exhibits.
33
+
34
+ ### Constrained random answers
35
+
36
+ Pros:
37
+
38
+ - Useful for fields that need very specific answers, like a real lawyer's bar number or a specific file name for uploading exhibits.
39
+ - An author can focus the tests' answers on important user stories. For example, they might want to focus some tests on users that have children while keeping other answers more random.
40
+ - An author might be able to make more efficient random tests. For example, a multiple choice question may have multiple answers that, behind the scenes, set the same value. Authors might do this to help users who are confused about their options give more accurate answers. A purely random test would pick that repeated value too often and waste testing time.
41
+
42
+ Cons:
43
+
44
+ - They take more time to write.
45
+ - They take more work to maintain.
46
+
47
+ <!-- TODO: Add to docs - each `possible_values` cell should contain 1 or more possible answers. -->
48
+
49
+ ### Hybrid
50
+
51
+ In future, we believe we can figure out how to let users have partially constrained tests where unspecified fields get purely random answers while constrained fields choose from a set of answers. It would need to be its own Step.
52
+
53
+ Pros:
54
+
55
+ - Offers the most flexibility and control.
56
+
57
+ Cons:
58
+
59
+ - Might be hard to avoid confusion about test behavior.
60
+ - Means developing both methods of testing. That's out of scope for one PR.