@suffolklitlab/alkiln 5.13.2 → 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.
- package/.github/workflows/github_server.yml +7 -7
- package/.github/workflows/playground.yml +1 -1
- package/CHANGELOG.md +32 -1
- package/action.yml +70 -28
- package/action_for_github_server/action.yml +61 -29
- package/docs/4_environments.md +243 -0
- package/docs/artifacts_folder_name.md +135 -0
- package/docs/constrained_random_tests.md +347 -0
- package/docs/decisions/006_which_log_level_to_pick_2024_09_5.md +70 -0
- package/docs/decisions/007_getting_and_storing_logs.md +179 -0
- package/docs/decisions/move_runtime_config_to_sources_folder.md +3 -0
- package/docs/decisions/reports_and_logs_2024_08_11.md +161 -0
- package/docs/kinds_of_errors.md +26 -0
- package/docs/prioritize_constrained_random_tests.md +101 -0
- package/docs/seprate_file_generation_GitHub_action.md +11 -0
- package/lib/docassemble/docassemble_api_interface.js +77 -51
- package/lib/docassemble/server_install.js +32 -9
- package/lib/docassemble/setup.js +40 -25
- package/lib/docassemble/takedown.js +33 -10
- package/lib/parse.js +36 -0
- package/lib/run_cucumber.js +115 -36
- package/lib/scope.js +69 -77
- package/lib/steps.js +70 -42
- package/lib/utils/Log.js +982 -0
- package/lib/utils/files.js +18 -11
- package/lib/utils/get_sources_paths.js +162 -0
- package/lib/utils/parse.js +1 -0
- package/lib/utils/proccess_command_line_args.js +267 -0
- package/lib/utils/reports.js +64 -17
- package/lib/utils/session_vars.js +34 -21
- package/lib/utils/time.js +44 -0
- package/package.json +1 -1
- package/tests/log_codes/check_codes.sh +2 -1
- package/tests/log_codes/log_code_expected_instances.txt +33 -9
- package/tests/unit_tests/getAllFields.test.js +5 -0
- package/tests/unit_tests/getMatchingRows.test.js +5 -0
- package/tests/unit_tests/log.test.js +581 -0
- package/tests/unit_tests/normalizeTable.test.js +5 -0
- package/lib/utils/log.js +0 -96
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# Constrained random input tests architecture and decisions
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## What should throw an error in the generator parsing stage?
|
|
5
|
+
|
|
6
|
+
Nothing? Should it all get reported in the ALKiln tests?
|
|
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
|
+
|
|
328
|
+
## Author wrote multiple generator Steps in one Scenario
|
|
329
|
+
|
|
330
|
+
Discuss: Warning or error
|
|
331
|
+
|
|
332
|
+
We think the easiest and clearest reaction is to refuse to generate any test and give a warning or error.
|
|
333
|
+
|
|
334
|
+
Other options:
|
|
335
|
+
|
|
336
|
+
- Use the last Step and delete the others. This may be hard to do. We'd need to mutate the Scenario's file text lines, maybe as we were looping through them. It would take some research
|
|
337
|
+
- Use the first Step and leave the others as they are. This would generate a broken test, but the test would at least work until it gets to the next generator Step line. We could add a Step in the core code for the generator Step and use it to give a meaningful error instead of just letting cucumber set a status of `undefined`. Maybe that would at least be a bit more clear, but I think not as clear as just refusing to generate and producing a warning or error.
|
|
338
|
+
|
|
339
|
+
Complications:
|
|
340
|
+
|
|
341
|
+
- What if there are other Steps between and after the generator Steps? Do we keep those and put them all at the bottom? Do we only keep the ones after the chosen generator Step?
|
|
342
|
+
|
|
343
|
+
If we do decide to pick a generator Step, maybe we can make sure to pick one that is otherwise valid. That is, it has a dataTable that has rows, is otherwise correctly formatted, etc.
|
|
344
|
+
|
|
345
|
+
## Grammar
|
|
346
|
+
|
|
347
|
+
Post MVP, discuss: should generator Steps without a number, but with the word "tests" generate 2 Scenarios?
|
|
@@ -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,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?
|
|
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.
|