@suffolklitlab/alkiln 5.13.3 → 5.13.4

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 (37) hide show
  1. package/.github/workflows/github_server.yml +7 -7
  2. package/.github/workflows/playground.yml +1 -1
  3. package/CHANGELOG.md +24 -0
  4. package/action.yml +70 -28
  5. package/action_for_github_server/action.yml +61 -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 +320 -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 +3 -1
  12. package/docs/decisions/reports_and_logs_2024_08_11.md +161 -0
  13. package/docs/kinds_of_errors.md +26 -0
  14. package/lib/docassemble/docassemble_api_interface.js +77 -51
  15. package/lib/docassemble/server_install.js +32 -9
  16. package/lib/docassemble/setup.js +40 -25
  17. package/lib/docassemble/takedown.js +33 -10
  18. package/lib/parse.js +36 -0
  19. package/lib/run_cucumber.js +115 -36
  20. package/lib/scope.js +60 -75
  21. package/lib/steps.js +70 -42
  22. package/lib/utils/Log.js +982 -0
  23. package/lib/utils/files.js +18 -11
  24. package/lib/utils/get_sources_paths.js +162 -0
  25. package/lib/utils/parse.js +1 -0
  26. package/lib/utils/proccess_command_line_args.js +267 -0
  27. package/lib/utils/reports.js +64 -17
  28. package/lib/utils/session_vars.js +34 -21
  29. package/lib/utils/time.js +44 -0
  30. package/package.json +1 -1
  31. package/tests/log_codes/check_codes.sh +2 -1
  32. package/tests/log_codes/log_code_expected_instances.txt +33 -9
  33. package/tests/unit_tests/getAllFields.test.js +5 -0
  34. package/tests/unit_tests/getMatchingRows.test.js +5 -0
  35. package/tests/unit_tests/log.test.js +581 -0
  36. package/tests/unit_tests/normalizeTable.test.js +5 -0
  37. package/lib/utils/log.js +0 -96
@@ -5,6 +5,326 @@
5
5
 
6
6
  Nothing? Should it all get reported in the ALKiln tests?
7
7
 
8
+ Want to run as many Scenarios as possible? If so,
9
+ - Don't throw a generator validity error at parse time.
10
+ - Also catch cucumber parse errors? But then it won't be reported with other parse errors.
11
+
12
+ All of these happen quickly, so there's not much time wasted:
13
+ - cucumber parse error
14
+ - ALKiln generator parse error (which is a cucumber error we can catch)
15
+ - ALKiln invalid generator error
16
+
17
+ - Test using wrong variable (out of date)
18
+ - Form code using wrong variable
19
+
20
+ Confusing to try to show multiple errors at the same time:
21
+
22
+ ```gherkin
23
+ Scenario: blah
24
+ Given ALKiln shows an error for the generator test:
25
+ """
26
+ ALK0000 Error message
27
+ """
28
+ And I start the interview at "foo.yml"
29
+ And I generate 10 random tests...
30
+ And I set "name" to "Ethan"
31
+ ```
32
+
33
+ Potentially less confusing:
34
+
35
+ ```gherkin
36
+ Scenario: blah
37
+ Given ALKiln shows an error for the generator test:
38
+ """
39
+ ALK0000 Error message
40
+
41
+ Your Scenario:
42
+
43
+ Scenario: blah
44
+ And I start the interview at "foo.yml"
45
+ And I generate 10 random tests...
46
+ And I set "name" to "Ethan"
47
+ """
48
+ ```
49
+
50
+
51
+ ### If ALKiln throws all its errors
52
+
53
+ **Just cucumber throws file parsing errors at parse stage:**
54
+
55
+ ```
56
+ GitHub starts stuff, lots of text
57
+ Cucumber expected #Tag, #Feature, or #Example (more text)
58
+ ** Process exits **
59
+ ```
60
+
61
+ **Just ALKiln throws cucumber parsing errors from at parse stage:**
62
+
63
+ ```
64
+ GitHub starts stuff, lots of text
65
+ Cucumber expected #Tag, #Feature, or #Example (more text) (cucumber parsing error in ALKiln generator file)
66
+ ** Process exits **
67
+ ```
68
+
69
+ **Just ALKiln throws generator parsing errors at parse stage:**
70
+
71
+ ```
72
+ GitHub starts stuff, lots of text
73
+ ALK0000 Error: generator 1 (file and Scenario with text of Scenario) test was bad
74
+ ALK0000 Error: generator 2 (file and Scenario with text of Scenario) test was bad
75
+ ** Process exits **
76
+ ```
77
+
78
+ **All 3:**
79
+
80
+ ```
81
+ GitHub starts stuff, lots of text
82
+ ALK0000 Error: generator 1 (file and Scenario with text of Scenario) test was bad
83
+ ALK0000 Error: generator 2 (file and Scenario with text of Scenario) test was bad
84
+ Cucumber expected #Tag, #Feature, or #Example (more text) (cucumber parsing error in ALKiln generator file)
85
+ ** Process exits **
86
+ ```
87
+
88
+ Once we throw the errors, cucumber isn't going to get a chance to throw its own parsing errors
89
+
90
+ ## Tangents
91
+
92
+ TODO: Make issue - log reports of failed Scenarios right under their progress line?
93
+ TODO: Make issue - log Scenario cucumber error output right under each Scenario's ALKiln output?
94
+
95
+ ## Alternative ways to run generated tests
96
+
97
+ - In their own action
98
+ - At the very start before any of the other test stuff. Before we start any server or install any projects. Then we wouldn't have warnings, only errors, and we'd throw them all before doing anything else. These could fail really fast. We could fail these as soon as the action started so that problems with them could be seen right away. (we might want to parse the cucumber files and throw those errors right away regardless of what we do with the generators.)
99
+
100
+ ## Where errors/warnings?
101
+
102
+ Can see them together
103
+ Can see them as soon as possible
104
+ Can see them in all places (duplication)
105
+ Put in report at parse time
106
+ Exclude from report at run time?
107
+ Show in console at run time in both places
108
+ Maybe put at the top of the file and console so they can be in the same place in both the report and the console output
109
+ Can we make sure generated tests are run first? Or maybe last? Two separate runs? Then splits up their location again. Want to find another way.
110
+
111
+ ### If ALKiln logs all its errors and warnings in at cucumber parse time
112
+
113
+ **ALKiln logs generator and cucumber parsing errors at parse stage:**
114
+
115
+ (console output)
116
+
117
+ ```
118
+ GitHub starts stuff, lots of text
119
+
120
+ ALK0000 ERROR: generator 1 file Scenario 1 (file and Scenario with text of Scenario) test was bad
121
+ ALK0000 ERROR: generator 1 file Scenario 2 (file and Scenario with text of Scenario) test was bad
122
+ Cucumber expected #Tag, #Feature, or #Example (more text) (cucumber parsing error in ALKiln generator file)
123
+
124
+ ALK0000 Warning: generator 3 file Scenario 6 wasn't great
125
+ ALK0000 Warning: generator 4 file Scenario 1 wasn't great
126
+
127
+ {
128
+ cucumber config...
129
+ }
130
+
131
+ Tests started successfully
132
+
133
+ ## (report starts here)
134
+ Scenario: 8 I do something ...***..>..**..F
135
+ Scenario: 7 ..**v**>***...
136
+ Scenario: generator 3 Scenario 6 ..
137
+ Scenario: generator 4 Scenario 1 ..
138
+
139
+ ====
140
+ Failed
141
+ ===
142
+
143
+ Scenario: 8 I do something
144
+ Step 1
145
+ Step 2
146
+
147
+ ALK0000 ERROR: Took too long to continue
148
+
149
+
150
+ ===
151
+ Success
152
+ ===
153
+
154
+ Scenario: 7
155
+ Step 1
156
+ Step 2
157
+ Step 3
158
+
159
+ Scenario: generator 3 Scenario 6
160
+ Step 1
161
+ Step 2
162
+ Step 3
163
+ Step 4
164
+
165
+ Scenario: generator 4 Scenario 1
166
+ Step 1
167
+ Step 2
168
+
169
+
170
+ Failures:
171
+
172
+ 1) Scenario: Sign in to server successfully (attempt 2) # docassemble/ALKilnTests/data/sources/reports.feature:591
173
+ ✔ Before # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:115
174
+ ✔ Given the Scenario report should include: # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1161
175
+ """
176
+ signed in
177
+ """
178
+ ✔ Given I sign in with the email "USER1_EMAIL" and the password "USER1_PASSWORD" # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:284
179
+ ✖ After # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1266
180
+ ReferenceError: errors is not defined
181
+ at World.<anonymous> (/opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1516:131)
182
+
183
+ Warnings:
184
+
185
+ 1) Scenario: Sign in to server successfully (attempt 1, retried) # docassemble/ALKilnTests/data/sources/reports.feature:591
186
+ ✔ Before # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:115
187
+ ✔ Given the Scenario report should include: # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1161
188
+ """
189
+ signed in
190
+ """
191
+ ✔ Given I sign in with the email "USER1_EMAIL" and the password "USER1_PASSWORD" # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:284
192
+ ✖ After # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1266
193
+ ReferenceError: errors is not defined
194
+ at World.<anonymous> (/opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1516:131)
195
+
196
+ 103 scenarios (1 failed, 102 passed)
197
+ 582 steps (582 passed)
198
+ 8m18.453s (executing steps: 8m18.014s)
199
+ ```
200
+
201
+
202
+
203
+ ### ALK logs at test run time
204
+
205
+ ```
206
+ GitHub starts stuff, lots of text
207
+
208
+ {
209
+ cucumber config...
210
+ }
211
+
212
+ Tests started successfully
213
+
214
+ ## (report starts here)
215
+ Scenario: 8 I do something ...***..>..**..F
216
+ Scenario: 7 ..**v**>***...
217
+ Scenario: generator 1 Scenario 1 ..F
218
+ Scenario: generator 1 Scenario 2 ..F
219
+ Scenario: generator 3 Scenario 6 ..
220
+ Scenario: generator 4 Scenario 1 ..
221
+
222
+ ====
223
+ Failed
224
+ ===
225
+
226
+ Scenario: 8 I do something
227
+ Step 1
228
+ Step 2
229
+
230
+ ALK0000 ERROR: Took too long to continue
231
+
232
+ Scenario: generator 1 Scenario 1 ..F
233
+ ALK0000 ERROR: Generator 1 Scenario 1 was bad
234
+
235
+ Scenario: generator 1 Scenario 2 ..F
236
+ ALK0000 ERROR: Generator 1 Scenario 2 was bad
237
+
238
+
239
+ ===
240
+ Success
241
+ ===
242
+
243
+ Scenario: 7
244
+ Step 1
245
+ Step 2
246
+ Step 3
247
+
248
+ Scenario: generator 3 Scenario 6
249
+ Step 1
250
+ Step 2
251
+ Step 3
252
+ Step 4
253
+ ALK0000 WARNING: generator 3 Scenario 6 wasn't great
254
+
255
+ Scenario: generator 4 Scenario 1
256
+ Step 1
257
+ Step 2
258
+ ALK0000 WARNING: generator 4 Scenario 1 wasn't great
259
+
260
+
261
+ Failures:
262
+
263
+ 1) Scenario: generator 1 Scenario 1 (attempt 2) # docassemble/ALKilnTests/data/sources/reports.feature:591
264
+ ✔ Before # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:115
265
+ ✔ Given the generator Scenario was invalid with this error: # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1161
266
+ """
267
+ ALK0000 Error: No rows in this generator Scenario
268
+
269
+ Scenario: generator 1 Scenario 1
270
+ Given I start the interview at "file.yml"
271
+ And I generate 10 tests:
272
+ | var | possible values |
273
+ And I take a screenshot
274
+ """
275
+ ✔ Given I sign in with the email "USER1_EMAIL" and the password "USER1_PASSWORD" # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:284
276
+ ✖ After # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1266
277
+ ReferenceError: errors is not defined
278
+ at World.<anonymous> (/opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1516:131)
279
+
280
+ 2) Scenario: generator 1 Scenario 2 (attempt 2) # docassemble/ALKilnTests/data/sources/reports.feature:591
281
+ ✔ Before # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:115
282
+ ✔ Given the generator Scenario was invalid with this error: # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1161
283
+ """
284
+ ALK0000 Error: No rows in this generator Scenario
285
+
286
+ Scenario: generator 1 Scenario 2
287
+ Given I start the interview at "file.yml"
288
+ And I generate 10 tests:
289
+ | var | possible values |
290
+ And I take a screenshot
291
+ """
292
+ ✔ Given I sign in with the email "USER1_EMAIL" and the password "USER1_PASSWORD" # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:284
293
+ ✖ After # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1266
294
+ ReferenceError: errors is not defined
295
+ at World.<anonymous> (/opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1516:131)
296
+
297
+ 3) Scenario: 8 I do something (attempt 2) # docassemble/ALKilnTests/data/sources/reports.feature:591
298
+ ✔ Before # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:115
299
+ ✔ Given the Scenario report should include: # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1161
300
+ """
301
+ signed in
302
+ """
303
+ ✔ Given I sign in with the email "USER1_EMAIL" and the password "USER1_PASSWORD" # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:284
304
+ ✖ After # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1266
305
+ ReferenceError: errors is not defined
306
+ at World.<anonymous> (/opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1516:131)
307
+
308
+ Warnings:
309
+
310
+ 1) Scenario: Sign in to server successfully (attempt 1, retried) # docassemble/ALKilnTests/data/sources/reports.feature:591
311
+ ✔ Before # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:115
312
+ ✔ Given the Scenario report should include: # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1161
313
+ """
314
+ signed in
315
+ """
316
+ ✔ Given I sign in with the email "USER1_EMAIL" and the password "USER1_PASSWORD" # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:284
317
+ ✖ After # ../../../../../opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1266
318
+ ReferenceError: errors is not defined
319
+ at World.<anonymous> (/opt/hostedtoolcache/node/18.20.5/x64/lib/node_modules/@suffolklitlab/alkiln/lib/steps.js:1516:131)
320
+
321
+ 103 scenarios (1 failed, 102 passed)
322
+ 582 steps (582 passed)
323
+ 8m18.453s (executing steps: 8m18.014s)
324
+ ```
325
+
326
+
327
+
8
328
  ## Author wrote multiple generator Steps in one Scenario
9
329
 
10
330
  Discuss: Warning or error
@@ -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".
@@ -1 +1,3 @@
1
- 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?
1
+ 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?
2
+
3
+ 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 past tests). If so, that's what's going on now too.