@suffolklitlab/alkiln 5.12.0 → 5.13.0-logs1
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/.env.example +29 -0
- package/.github/workflows/github_server.yml +59 -12
- package/.github/workflows/playground.yml +11 -4
- package/CHANGELOG.md +41 -1
- package/CONTRIBUTING.md +98 -83
- package/README.md +1 -1
- package/action.yml +67 -29
- package/action_for_github_server/action.yml +59 -31
- package/docs/decisions/006_which_log_level_to_pick_2024_09_5.md +46 -0
- package/docs/decisions/adopt_architectural_decision_records.md +58 -0
- package/docs/decisions/detect_sign_in.md +124 -0
- package/docs/decisions/pdfjs_version_2024_08_08.md +18 -0
- package/docs/decisions/reports_and_logs_2024_08_11.md +141 -0
- package/docs/decisions/update_puppeteer_2024_07_28.md +62 -0
- package/docs/story_tables.md +1 -1
- package/lib/docassemble/docassemble_api_REST.js +67 -1
- package/lib/docassemble/docassemble_api_interface.js +225 -18
- package/lib/docassemble/server_install.js +23 -9
- package/lib/docassemble/setup.js +33 -20
- package/lib/docassemble/takedown.js +27 -7
- package/lib/run_cucumber.js +83 -33
- package/lib/scope.js +222 -82
- package/lib/steps.js +206 -62
- package/lib/utils/files.js +8 -6
- package/lib/utils/log.js +477 -84
- package/lib/utils/reports.js +14 -5
- package/lib/utils/session_vars.js +25 -18
- package/lib/utils/time.js +44 -0
- package/npm-shrinkwrap.json +635 -193
- package/package.json +8 -8
- package/setup.py +1 -1
- package/tests/log_codes/check_codes.sh +163 -108
- package/tests/log_codes/log_code_expected_instances.txt +213 -0
- 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 +20 -0
- package/tests/unit_tests/normalizeTable.test.js +5 -0
- package/tests/log_codes/deleted_codes.json +0 -8
package/lib/utils/log.js
CHANGED
|
@@ -1,96 +1,489 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const
|
|
1
|
+
const fs = require(`fs`);
|
|
2
|
+
const util = require(`node:util`);
|
|
3
|
+
const session_vars = require( `./session_vars` );
|
|
4
|
+
const time = require(`./time`);
|
|
5
|
+
const files = require(`./files`);
|
|
3
6
|
|
|
4
|
-
// ? 🌈 💡 🔎 🤕
|
|
7
|
+
// ? 🌈 💡 🔎 🤕 🐛
|
|
5
8
|
// Alternatives: 🩹 ❤️🩹 🚑
|
|
6
9
|
// TODO: return full message so it can be passed to manually
|
|
7
10
|
// thrown errors if needed.
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
/**
|
|
13
|
+
* TODO:
|
|
14
|
+
* - [x] Store log filename in runtime_config
|
|
15
|
+
* - [x] `types` -> `context`
|
|
16
|
+
* - [x] Remove "cosmetic"/"plain" logs and just work that beauty into the logs themselves
|
|
17
|
+
* - [ ] Save logs from GitHub actions as well
|
|
18
|
+
* - [ ] Capture cucumber ProgressFormatter logs instead of making them ourselves?
|
|
19
|
+
* - [ ] Write user errors and warnings (sent to .console()) to unexpected results file
|
|
20
|
+
* - [ ] Write debug errors and warnings to unexpected results file?
|
|
21
|
+
* - [ ] write tests...?
|
|
22
|
+
* - [ ] Edit the decision doc? Too many individual decisions? Use separate docs instead?
|
|
23
|
+
* - streams vs. formatter vs. fs.appendFileSync
|
|
24
|
+
* - general architecture?
|
|
25
|
+
* - keeping report separate for now
|
|
26
|
+
* - try/catching everything + collecting errors
|
|
27
|
+
* - `throw` (non-standard) (throw errors in logs? (bad stack trace) 2 lines everywhere instead?)
|
|
28
|
+
* - `success` (non-standard)
|
|
29
|
+
* - `before` option and no others? (others can be included in message, but no way to log before the code otherwise.)
|
|
30
|
+
* - Name "debug" meaning change instead of "verbose"
|
|
31
|
+
* */
|
|
32
|
+
//
|
|
11
33
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
class Log {
|
|
35
|
+
/** Handles logging to the console, saving to files and reports,
|
|
36
|
+
* and throwing errors.
|
|
37
|
+
*
|
|
38
|
+
* Use this to:
|
|
39
|
+
* - Log to the console
|
|
40
|
+
* - Save to the debug log
|
|
41
|
+
* - Throw errors with log codes and other info
|
|
42
|
+
* - Log without new lines
|
|
43
|
+
*
|
|
44
|
+
* What are the different log files:
|
|
45
|
+
* - report is a pretty file that is only complete at the end of all tests
|
|
46
|
+
* - report_log is not pretty, but has the same info as the report in case tests end early
|
|
47
|
+
* - debug_log is very ugly and has notes for developers
|
|
48
|
+
*
|
|
49
|
+
* TODO:
|
|
50
|
+
* - Save to the report and report log
|
|
51
|
+
*
|
|
52
|
+
* @params
|
|
53
|
+
* options { object }
|
|
54
|
+
* options.path { str } - Optional. The path where the final logs
|
|
55
|
+
* should be stored.
|
|
56
|
+
* options.context { str } - Optional. Info about what's creating
|
|
57
|
+
* this Log.
|
|
58
|
+
*
|
|
59
|
+
* We're not using console.Console or write streams because we can't
|
|
60
|
+
* control their life cycle in steps.js. We can't tell when to
|
|
61
|
+
* properly flush and end the stream in the case of a sudden failure.
|
|
62
|
+
* */
|
|
63
|
+
|
|
64
|
+
report_filename = `report.txt`;
|
|
65
|
+
report_log_filename = `report_log.txt`;
|
|
66
|
+
debug_log_filename = `debug_log.txt`;
|
|
67
|
+
unexpected_filename = `unexpected_results.txt`;
|
|
68
|
+
|
|
69
|
+
constructor({ path, context } = {}) {
|
|
70
|
+
// this.id = uuid();
|
|
71
|
+
|
|
72
|
+
// Make sure folder exists
|
|
73
|
+
if ( path ) { files.make_artifacts_folder( path ); }
|
|
74
|
+
// In local development and ALKilnInThePlayground, this will
|
|
75
|
+
// mostly be called by the `run` command.
|
|
76
|
+
else { path = files.make_artifacts_folder(); }
|
|
77
|
+
|
|
78
|
+
this.path = path;
|
|
79
|
+
session_vars.save_artifacts_path_name( this.path );
|
|
80
|
+
|
|
81
|
+
if ( context ) { context = context + ` log`; }
|
|
82
|
+
else { context = `log`; }
|
|
83
|
+
this.info({ code: `ALK0215`, context, },
|
|
84
|
+
`Saving files to "${ this.path }"`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console( console_opts = {}, ...logs ) {
|
|
89
|
+
/** Log the logs to the console at the right level and with the metadata.
|
|
90
|
+
* Return the start of the message - the part without the logs
|
|
91
|
+
*
|
|
92
|
+
* Questions:
|
|
93
|
+
* - Allow `logs` to be a string?
|
|
94
|
+
* - Allow `context` to be a space-separated string?
|
|
95
|
+
* - Throw errors? Need to manipulate call stack.
|
|
96
|
+
* - Turn logs that are non-strings into `JSON.stringify`d strings?
|
|
97
|
+
* - Should every log be able to have an error? To keep the
|
|
98
|
+
* signature consistent.
|
|
99
|
+
*
|
|
100
|
+
* @examples:
|
|
101
|
+
* Log = require('./lib/utils/log.js');
|
|
102
|
+
* log = new Log('_alkiln-');
|
|
103
|
+
*
|
|
104
|
+
* log.console();
|
|
105
|
+
* // * ALK000c console LOG [2024-08-16 00:30:39UTC]:
|
|
106
|
+
*
|
|
107
|
+
* log.console({
|
|
108
|
+
* level: 'info',
|
|
109
|
+
* icon: '&',
|
|
110
|
+
* code: 'ALK00t1',
|
|
111
|
+
* context: 'a test',
|
|
112
|
+
* }, 'test info log');
|
|
113
|
+
*
|
|
114
|
+
* try {
|
|
115
|
+
* let czar = zoo + 5;
|
|
116
|
+
* } catch ( error ) {
|
|
117
|
+
* log.console({
|
|
118
|
+
* level: 'error',
|
|
119
|
+
* before: '===\n===\n',
|
|
120
|
+
* icon: 'X'
|
|
121
|
+
* code: 'ALK00t2'
|
|
122
|
+
* context: 'err_test'
|
|
123
|
+
* error: new Error('This is an error'),
|
|
124
|
+
* }, 'error test log 1', 'error test log 2')
|
|
125
|
+
* }
|
|
126
|
+
* */
|
|
127
|
+
|
|
128
|
+
let { // Defaults
|
|
129
|
+
level = `log`,
|
|
130
|
+
before = ``, // different name? above? start_decorator?
|
|
131
|
+
icon = `*`,
|
|
132
|
+
code = `ALK000c`,
|
|
133
|
+
context = ``,
|
|
134
|
+
error = null,
|
|
135
|
+
do_throw = false,
|
|
136
|
+
} = console_opts;
|
|
137
|
+
|
|
138
|
+
let with_error = [...logs];
|
|
139
|
+
if ( error ) {
|
|
140
|
+
with_error.push( error );
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let formatted_log = this.debug({ level, before, icon, code, context }, ...with_error );
|
|
144
|
+
// Shame to do this in .debug too, but haven't found a
|
|
145
|
+
// better way to return consistent values otherwise. We
|
|
146
|
+
// want other logs to be logged by the console in its way.
|
|
147
|
+
const metadata = this._format_metadata({ level, icon, code, context });
|
|
148
|
+
|
|
149
|
+
// Throw whatever we can throw
|
|
150
|
+
if ( do_throw ) {
|
|
151
|
+
let custom_msg = this._stringify_logs({ logs: [ metadata, ...logs ] });
|
|
152
|
+
// TODO: Save to unexpected results file
|
|
153
|
+
if ( error instanceof Error ) {
|
|
154
|
+
console.log( before + custom_msg );
|
|
155
|
+
throw error;
|
|
156
|
+
} else if ( error !== null ) {
|
|
157
|
+
console.log( before + custom_msg );
|
|
158
|
+
throw new Error( error );
|
|
159
|
+
} else {
|
|
160
|
+
console.log( before ); // Adds extra new line, unfortunately
|
|
161
|
+
throw new Error( custom_msg );
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
// Otherwise, log at the given level if console has that method (`log` by default)
|
|
167
|
+
// If a non-standard level was passed in, like `success`, use `info` by default
|
|
168
|
+
if ( console[ level ]) {
|
|
169
|
+
// TODO: Save `warn` to unexpected results file
|
|
170
|
+
console[ level ]( before + metadata, ...with_error );
|
|
171
|
+
} else {
|
|
172
|
+
console.info( before + metadata, ...with_error );
|
|
173
|
+
}
|
|
174
|
+
} catch ( console_log_error ) {
|
|
175
|
+
// Fail silently, gather errors
|
|
176
|
+
try {
|
|
177
|
+
this.debug({
|
|
178
|
+
level: `warn`, icon: `🔎`, code: `ALK0211`,
|
|
179
|
+
context: `internal`,
|
|
180
|
+
error: console_log_error
|
|
181
|
+
}, `Skipped saving a ${ level } message to the debug log`);
|
|
182
|
+
} catch ( console_debug_error ) {
|
|
183
|
+
console.warn( `🔎 ALK0217 internal WARNING: Skipped the same log two times consecutively`, console_debug_error, console_log_error );
|
|
184
|
+
}
|
|
185
|
+
} finally {
|
|
186
|
+
return formatted_log;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
debug( debug_opts = {}, ...logs ) {
|
|
191
|
+
/** Save in the debug log file and return a string version
|
|
192
|
+
* of the log. If there are errors, just log them too.
|
|
193
|
+
* Keep as much info from the caller as possible. Avoid throwing
|
|
194
|
+
* errors if at all possible while giving the max info possible.
|
|
195
|
+
* Avoid logging to the visible console.
|
|
196
|
+
*
|
|
197
|
+
* TODO: Log warning with stack trace
|
|
198
|
+
*
|
|
199
|
+
* @examples:
|
|
200
|
+
*
|
|
201
|
+
* Log = require('./lib/utils/log.js')
|
|
202
|
+
* log = new Log({ path: '_alkiln-' })
|
|
203
|
+
* log.debug()
|
|
204
|
+
* // 🐛 ALK000v debug DEBUG [2024-08-16 00:38:49UTC]:
|
|
205
|
+
* */
|
|
206
|
+
let {
|
|
207
|
+
level=`debug`,
|
|
208
|
+
before = ``,
|
|
209
|
+
icon = `🐛`,
|
|
210
|
+
code = `ALK000v`,
|
|
211
|
+
context = ``,
|
|
212
|
+
error = null,
|
|
213
|
+
} = debug_opts;
|
|
214
|
+
|
|
215
|
+
// function stackTrace() { return (new Error()).stack; }
|
|
216
|
+
// and
|
|
217
|
+
// https://stackoverflow.com/a/41820537/14144258
|
|
218
|
+
// var mystring=require('util').inspect(error_object);
|
|
219
|
+
|
|
220
|
+
let logging_errors = [];
|
|
221
|
+
|
|
222
|
+
// Get a proper error log if needed
|
|
223
|
+
if ( level === `throw` ) { level = `error`; }
|
|
224
|
+
|
|
225
|
+
let metadata = `No metadata: `;
|
|
226
|
+
try {
|
|
227
|
+
metadata = this._format_metadata({ level, icon, code, context });
|
|
228
|
+
} catch ( metadata_error ) {
|
|
229
|
+
// Consider using `util.inspect()` for the error
|
|
230
|
+
logging_errors.push(`🔎 ALK0212 internal WARNING: Skipped creating debug metadata\n${ metadata_error.stack }`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Include error as the last log
|
|
234
|
+
let with_error = [...logs];
|
|
235
|
+
if ( error ) {
|
|
236
|
+
with_error.push( error );
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let formatted_log = `${ metadata }No formatted log.`;
|
|
240
|
+
try {
|
|
241
|
+
let stringified = this._stringify_logs({ logs: with_error });
|
|
242
|
+
formatted_log = `${ before }${ metadata }${ stringified }`;
|
|
243
|
+
} catch ( formatted_log_error ) {
|
|
244
|
+
// Consider using `util.inspect()` for the error
|
|
245
|
+
logging_errors.push(`🔎 ALK0213 internal WARNING: Skipped creating full debug log\n${ formatted_log_error.stack }`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
let fs_append_failed = false;
|
|
249
|
+
try {
|
|
250
|
+
fs.appendFileSync( `${ this.path }/${ this.debug_log_filename }`, `\n` + formatted_log )
|
|
251
|
+
} catch ( fs_append_error ) {
|
|
252
|
+
fs_append_failed = true;
|
|
253
|
+
// Consider using `util.inspect()` for the error
|
|
254
|
+
logging_errors.push(`🔎 ALK0214 internal WARNING: Could not append to debug log file\n${ fs_append_error.stack }`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Record any errors in the most unobtrusive way possible
|
|
258
|
+
if ( logging_errors.length > 0 ) {
|
|
259
|
+
for ( let logging_error of logging_errors ) {
|
|
260
|
+
if ( fs_append_failed ) {
|
|
261
|
+
console.warn( logging_error );
|
|
262
|
+
} else {
|
|
263
|
+
// TODO: Log warning with stack trace
|
|
264
|
+
fs.appendFileSync( `${ this.path }/${ this.debug_log_filename }`, `\n` + logging_error.stack );
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return formatted_log;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
_format_metadata( metadata_opts = {} ) {
|
|
273
|
+
/** Format and return the log metadata.
|
|
274
|
+
* Example: "💡 ALK#### internal INFO [10:24:40 pm UTC]: "
|
|
275
|
+
* */
|
|
276
|
+
let {
|
|
277
|
+
level=`log`,
|
|
278
|
+
icon = `?`,
|
|
279
|
+
code = `ALK000m`,
|
|
280
|
+
context = ``,
|
|
281
|
+
} = metadata_opts;
|
|
282
|
+
|
|
283
|
+
let metadata = `? ALK000m unspecified: `;
|
|
284
|
+
try {
|
|
285
|
+
let metadata_level = level;
|
|
286
|
+
if ( level === `warn` ) { metadata_level = `warning`; }
|
|
287
|
+
let level_caps = metadata_level.toUpperCase();
|
|
288
|
+
// For elapsed time, see https://nodejs.org/api/console.html#consoletimelabel
|
|
289
|
+
// Should this really be a pretty date, or should it be a raw timestamp?
|
|
290
|
+
|
|
291
|
+
// let metadata_list = [ icon, code, context, level_caps, `[${ time.log_date() }]` ];
|
|
292
|
+
// let metadata_list = [ icon, code, context, level_caps, `[${ time.log_date() }]` ].filter(function ( item ) {
|
|
293
|
+
// return item !== ``;
|
|
294
|
+
// }).join(` `) + `: `;
|
|
295
|
+
|
|
296
|
+
let metadata_list = [];
|
|
297
|
+
for ( let item of [ icon, code, context, level_caps, `[${ time.log_date() }]` ]) {
|
|
298
|
+
if ( item !== `` ) { metadata_list.push(item); }
|
|
299
|
+
}
|
|
300
|
+
metadata = metadata_list.join(` `) + `: `;
|
|
301
|
+
} catch ( metadata_error ) {
|
|
302
|
+
let msg = `Skipped creating metadata`;
|
|
303
|
+
code = `ALK0220`;
|
|
304
|
+
icon = `🔎`;
|
|
305
|
+
context = `internal logs metadata`;
|
|
306
|
+
try {
|
|
307
|
+
this.debug({ icon, level: `warn`, code, context, error: metadata_error }, msg );
|
|
308
|
+
} catch {
|
|
309
|
+
console.warn( `${ icon } ${ code } ${ context } WARNING [${ Date.now() }]: ${ msg }`, metadata_error );
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return metadata;
|
|
19
313
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
314
|
+
|
|
315
|
+
_stringify_logs(str_log_opts = {}) {
|
|
316
|
+
/** Return a string - a formatted version of the list
|
|
317
|
+
* of logs by stringifying non-strings. An empty list
|
|
318
|
+
* or no argument is valid. */
|
|
319
|
+
|
|
320
|
+
let { logs = [] } = str_log_opts;
|
|
321
|
+
|
|
322
|
+
let stringified_logs = ``;
|
|
323
|
+
let prev_line_type = null;
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
for ( let log of logs ) {
|
|
327
|
+
// TODO: Try/catch each of these?
|
|
328
|
+
if ( log === null || [`string`, `number`, `undefined`, `boolean`].includes( typeof(log) )) {
|
|
329
|
+
|
|
330
|
+
stringified_logs += this._stringify_inline({ prev_line_type, log });
|
|
331
|
+
prev_line_type = `inline`;
|
|
332
|
+
|
|
333
|
+
} else {
|
|
334
|
+
// For objects and errors
|
|
335
|
+
stringified_logs += this._stringify_object({ obj: log });
|
|
336
|
+
prev_line_type = `block`;
|
|
337
|
+
} // ends typeof log
|
|
338
|
+
} // Ends for logs
|
|
339
|
+
} catch ( stringify_error ) {
|
|
340
|
+
try {
|
|
341
|
+
// TODO: Should `stringify_error` get recorded even when util.inspect works?
|
|
342
|
+
stringified_logs = util.inspect( logs, { depth: 8, maxArrayLength: null, maxStringLength: null, });
|
|
343
|
+
} catch ( util_error ) {
|
|
344
|
+
|
|
345
|
+
if ( stringified_logs ) {
|
|
346
|
+
stringified_logs += `Unable to stringify logs. Got:\n${ stringified_logs }\n${ util_error.stack }\n${ stringify_error.stack }`;
|
|
347
|
+
} else {
|
|
348
|
+
stringified_logs = `Unable to stringify logs.\n${ util_error.stack }\n${ stringify_error.stack }`;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return stringified_logs;
|
|
354
|
+
} // Ends Log._stringify_logs()
|
|
355
|
+
|
|
356
|
+
_stringify_inline({ prev_line_type, log }) {
|
|
357
|
+
/** Format an inline log. Mostly involves fiddling with
|
|
358
|
+
* the start of the string. */
|
|
359
|
+
// An inline log's string starts with nothing if it's the first string...
|
|
360
|
+
let start = ``;
|
|
361
|
+
// ...starts after a space if its next to an inline string...
|
|
362
|
+
if ( prev_line_type === `inline` ) { start = ` `; }
|
|
363
|
+
// ...or starts on a new line if it's after a block of text,
|
|
364
|
+
// like an error or object.
|
|
365
|
+
else if ( prev_line_type === `block` ) { start = `\n`; }
|
|
366
|
+
return `${ start }${ log }`;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
_stringify_object({ obj }) {
|
|
370
|
+
/** Try to stringify an object a few different ways. Works for
|
|
371
|
+
* errors too. */
|
|
372
|
+
try {
|
|
373
|
+
// A block of text (the object) always starts on a new line
|
|
374
|
+
// Try to stringify the object.
|
|
375
|
+
return `\n${ util.inspect( obj, { depth: 8, maxArrayLength: null, maxStringLength: null, } )}`;
|
|
376
|
+
} catch ( outer_error ) {
|
|
377
|
+
// If that fails, see if it has its own way of stringifying
|
|
378
|
+
// TODO: record these errors?
|
|
379
|
+
try {
|
|
380
|
+
return `\n${ obj }`;
|
|
381
|
+
} catch ( inner_error ) {
|
|
382
|
+
// Lastly, give at least some information
|
|
383
|
+
return `\nUnable to log this ${ typeof obj }`;
|
|
384
|
+
} // ends inner try
|
|
385
|
+
} // ends outer try
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
success( success_opts = {}, ...logs ) {
|
|
389
|
+
/** Console log success - log level with success icon. */
|
|
390
|
+
// Discuss: do we want to log some successes as "💡 ... SUCCESS: "?
|
|
391
|
+
// Should we allow callers to override levels and icons?
|
|
392
|
+
let {
|
|
393
|
+
code = `ALK000s`, before = ``, context = ``
|
|
394
|
+
} = success_opts;
|
|
395
|
+
return this.console({
|
|
396
|
+
level: `success`, icon: `🌈`,
|
|
397
|
+
before, code, context,
|
|
398
|
+
}, ...logs);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
info( info_opts = {}, ...logs ) {
|
|
402
|
+
/** Console log info. */
|
|
403
|
+
let {
|
|
404
|
+
code = `ALK000i`, before = ``, context = ``,
|
|
405
|
+
} = info_opts;
|
|
406
|
+
return this.console({
|
|
407
|
+
level: `info`, icon: `💡`,
|
|
408
|
+
before, code, context,
|
|
409
|
+
}, ...logs);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
warn( warning_opts = {}, ...logs ) {
|
|
413
|
+
/** Console log warning. */
|
|
414
|
+
let {
|
|
415
|
+
code = `ALK000w`, before = ``, context = ``
|
|
416
|
+
} = warning_opts;
|
|
417
|
+
return this.console({
|
|
418
|
+
level: `warn`, icon: `🔎`,
|
|
419
|
+
before, code, context,
|
|
420
|
+
}, ...logs);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
throw( throw_opts = {}, ...logs ){
|
|
424
|
+
/** Console log and throw error.
|
|
425
|
+
* The `error` option is required. */
|
|
426
|
+
let {
|
|
427
|
+
code = `ALK000t`, before = ``, context = ``,
|
|
428
|
+
do_throw = true, error,
|
|
429
|
+
} = throw_opts;
|
|
430
|
+
return this.console({
|
|
431
|
+
level: `error`, icon: `🤕`,
|
|
432
|
+
before, code, context, error,
|
|
433
|
+
}, ...logs);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
unexpected({ text = `` } = {}) {
|
|
437
|
+
/** TODO: Save to the unexpected results file. (Errors, maybe warnings) */
|
|
438
|
+
fs.appendFileSync(`${ this.path }/${ this.unexpected_filename }`, `\n${ text }` );
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
stdout(stdout_opts = {}, ...logs) {
|
|
442
|
+
/** Prints in the console inline and saves to the debug log
|
|
443
|
+
* and the report log.
|
|
444
|
+
*
|
|
445
|
+
* @params
|
|
446
|
+
* options { obj }
|
|
447
|
+
* options.records_only { bool } - Whether to only save logs
|
|
448
|
+
* without printing them to the console. E.g. progress
|
|
449
|
+
* summary characters like those that cucumber is already
|
|
450
|
+
* printing to the console.
|
|
451
|
+
* */
|
|
452
|
+
let {
|
|
453
|
+
records_only = false,
|
|
454
|
+
} = stdout_opts;
|
|
455
|
+
let whole_log = logs.join(` `);
|
|
456
|
+
|
|
457
|
+
// Write to the debug and report files
|
|
458
|
+
this._record_stdout({ logs });
|
|
459
|
+
|
|
460
|
+
if ( !records_only ) {
|
|
461
|
+
process.stdout.write( whole_log );
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return logs;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
_record_stdout({ logs = [] }) {
|
|
468
|
+
/** Save stdout output to various debug and report files. */
|
|
469
|
+
this.debug({ code: `ALK0219`, context: `stdout` }, ...logs);
|
|
470
|
+
let whole_log = logs.join(` `);
|
|
471
|
+
fs.appendFileSync( `${ this.path }/${ this.report_log_filename }`, whole_log );
|
|
472
|
+
// TODO: write to report after report metadata
|
|
81
473
|
}
|
|
82
474
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
475
|
+
clear() {
|
|
476
|
+
/** Empty all files possible. Mostly for testing. */
|
|
477
|
+
try { fs.writeFileSync( `${ this.path }/${ this.debug_log_filename }`, `` ); }
|
|
478
|
+
catch (error) { /* Do nothing, no file there or something */ }
|
|
479
|
+
try { fs.writeFileSync( `${ this.path }/${ this.report_log_filename }`, `` ); }
|
|
480
|
+
catch (error) { /* Do nothing, no file there or something */ }
|
|
481
|
+
try { fs.writeFileSync( `${ this.path }/${ this.report_filename }`, `` ); }
|
|
482
|
+
catch (error) { /* Do nothing, no file there or something */ }
|
|
483
|
+
try { fs.writeFileSync( `${ this.path }/${ this.unexpected_filename }`, `` ); }
|
|
484
|
+
catch (error) { /* Do nothing, no file there or something */ }
|
|
88
485
|
}
|
|
89
|
-
};
|
|
90
486
|
|
|
91
|
-
|
|
92
|
-
fs.appendFileSync(log.debug_log_file, value);
|
|
93
|
-
};
|
|
487
|
+
} // Ends Log{}
|
|
94
488
|
|
|
95
|
-
|
|
96
|
-
log.unexpected_filename = `unexpected_results.txt`;
|
|
489
|
+
module.exports = Log;
|
package/lib/utils/reports.js
CHANGED
|
@@ -66,14 +66,20 @@ reports.addToReport = function( scope, { type, code, value }) {
|
|
|
66
66
|
const MISC_ARTIFACTS_DIR = `_alkiln-misc_artifacts`;
|
|
67
67
|
if ( !scope.paths ) {
|
|
68
68
|
// Can this be usefully abstracted?
|
|
69
|
-
|
|
69
|
+
let date = files.readable_date();
|
|
70
|
+
scope.paths = {
|
|
71
|
+
debug_log: `${ MISC_ARTIFACTS_DIR }/unit_tests_debug_log-${ date }.txt`,
|
|
72
|
+
report_log: `${ MISC_ARTIFACTS_DIR }/unit_tests_report_log-${ date }.txt`,
|
|
73
|
+
};
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
try {
|
|
73
77
|
fs.appendFileSync( scope.paths.debug_log, `\nReport:\n${debug_line}` );
|
|
74
|
-
|
|
78
|
+
fs.appendFileSync( scope.paths.report_log, `\nReport:\n${debug_line}` );
|
|
79
|
+
} catch ( file_error ) {
|
|
75
80
|
fs.mkdirSync( MISC_ARTIFACTS_DIR );
|
|
76
81
|
fs.appendFileSync( scope.paths.debug_log, `\nReport:\n${debug_line}` );
|
|
82
|
+
fs.appendFileSync( scope.paths.report_log, `\nReport:\n${debug_line}` );
|
|
77
83
|
// An error in here should throw (for now)
|
|
78
84
|
}
|
|
79
85
|
|
|
@@ -81,7 +87,7 @@ reports.addToReport = function( scope, { type, code, value }) {
|
|
|
81
87
|
let scenario = reports.makeSureScenarioExists( scope );
|
|
82
88
|
scenario.lines.push({ type, code, value });
|
|
83
89
|
|
|
84
|
-
return
|
|
90
|
+
return getLineText({ type, code, value });
|
|
85
91
|
}; // Ends reports.addToReport()
|
|
86
92
|
|
|
87
93
|
reports.create = function( scope ) {
|
|
@@ -134,6 +140,9 @@ reports.saveFinalScenarioData = function( scope, { status=`in progress` }) {
|
|
|
134
140
|
return scenario;
|
|
135
141
|
}; // Ends reports.saveFinalScenarioData()
|
|
136
142
|
|
|
143
|
+
reports.progress = function ({ logs=[] }) {
|
|
144
|
+
// TODO: save stdout-style to report_log, object, and getting printable scenario
|
|
145
|
+
};
|
|
137
146
|
|
|
138
147
|
reports.setReportScenarioStatus = function( scope, { status=`PASSED` }) {
|
|
139
148
|
/** Add final status of scenario to scenario report object - passed, failed, etc. */
|
|
@@ -203,7 +212,7 @@ reports.getPrintableScenario = function( scenario ) {
|
|
|
203
212
|
return report;
|
|
204
213
|
}; // Ends reports.getPrintableScenario()
|
|
205
214
|
|
|
206
|
-
let getLineText = function ({ type=`no type`, code=`
|
|
215
|
+
let getLineText = function ({ type=`no type`, code=`ALK00rl`, value=`` }) {
|
|
207
216
|
let start = ``;
|
|
208
217
|
if ( type.includes(`page_id`) ) { start += `screen id: `; }
|
|
209
218
|
else if ( type.includes(`outcome`) ) { start += `\n`; } // extra new line
|
|
@@ -212,7 +221,7 @@ let getLineText = function ({ type=`no type`, code=`ALK0000`, value=`` }) {
|
|
|
212
221
|
return `${ start }${ value }`;
|
|
213
222
|
}; // Ends getLine()
|
|
214
223
|
|
|
215
|
-
let getDebugLine = function ({ type=``, code=`
|
|
224
|
+
let getDebugLine = function ({ type=``, code=`ALK00rd`, value=`` }) {
|
|
216
225
|
/** In debug log lines, include all the information possible. */
|
|
217
226
|
|
|
218
227
|
// `plain` is usually used for visual clarity and shouldn't have
|