adapt-authoring-logger 1.1.2 → 1.2.0

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.
@@ -1,8 +1,10 @@
1
- name: Run test scripts
1
+ name: Tests
2
2
  on: push
3
3
  jobs:
4
4
  default:
5
5
  runs-on: ubuntu-latest
6
+ permissions:
7
+ contents: read
6
8
  steps:
7
9
  - uses: actions/checkout@master
8
10
  - uses: actions/setup-node@master
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adapt-authoring-logger",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "Basic logger for the Adapt authoring tool",
5
5
  "homepage": "https://github.com/adapt-security/adapt-authoring-logger",
6
6
  "license": "GPL-3.0",
@@ -8,10 +8,16 @@
8
8
  "main": "index.js",
9
9
  "repository": "adapt-security/adapt-authoring-logger",
10
10
  "dependencies": {
11
+ "adapt-authoring-core": "^1.7.0",
11
12
  "chalk": "^5.3.0"
12
13
  },
13
14
  "peerDependencies": {
14
- "adapt-authoring-core": "^1.0.0"
15
+ "adapt-authoring-config": "^1.1.4"
16
+ },
17
+ "peerDependenciesMeta": {
18
+ "adapt-authoring-config": {
19
+ "optional": true
20
+ }
15
21
  },
16
22
  "devDependencies": {
17
23
  "@semantic-release/git": "^10.0.1",
@@ -35,10 +41,18 @@
35
41
  ],
36
42
  "@semantic-release/npm",
37
43
  "@semantic-release/github",
38
- "@semantic-release/git"
44
+ [
45
+ "@semantic-release/git",
46
+ {
47
+ "assets": [
48
+ "package.json"
49
+ ],
50
+ "message": "Chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
51
+ }
52
+ ]
39
53
  ]
40
54
  },
41
55
  "scripts": {
42
- "test": "node --test"
56
+ "test": "node --test tests/*.spec.js"
43
57
  }
44
58
  }
@@ -1,10 +1,11 @@
1
- import assert from 'assert'
2
1
  import { describe, it, beforeEach, afterEach } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
3
  import chalk from 'chalk'
4
+ import { AbstractModule } from 'adapt-authoring-core'
4
5
  import LoggerModule from '../lib/LoggerModule.js'
5
6
 
6
7
  describe('LoggerModule', () => {
7
- describe('colourise()', () => {
8
+ describe('#colourise()', () => {
8
9
  it('should return string with colour function applied', () => {
9
10
  const result = LoggerModule.colourise('test', chalk.red)
10
11
  assert.ok(result.includes('test'))
@@ -17,20 +18,30 @@ describe('LoggerModule', () => {
17
18
 
18
19
  it('should return uncoloured string if no colour function provided', () => {
19
20
  const result = LoggerModule.colourise('test', null)
20
- assert.strictEqual(result, 'test')
21
+ assert.equal(result, 'test')
21
22
  })
22
23
 
23
24
  it('should handle undefined colour function', () => {
24
25
  const result = LoggerModule.colourise('test', undefined)
25
- assert.strictEqual(result, 'test')
26
+ assert.equal(result, 'test')
27
+ })
28
+
29
+ it('should return empty string unchanged when no colour', () => {
30
+ const result = LoggerModule.colourise('', null)
31
+ assert.equal(result, '')
32
+ })
33
+
34
+ it('should apply colour function to empty string', () => {
35
+ const result = LoggerModule.colourise('', chalk.red)
36
+ assert.equal(typeof result, 'string')
26
37
  })
27
38
  })
28
39
 
29
- describe('getDateStamp()', () => {
40
+ describe('#getDateStamp()', () => {
30
41
  it('should return empty string when timestamp is disabled', () => {
31
42
  const config = { timestamp: false }
32
43
  const result = LoggerModule.getDateStamp(config)
33
- assert.strictEqual(result, '')
44
+ assert.equal(result, '')
34
45
  })
35
46
 
36
47
  it('should return ISO format date when dateFormat is "iso"', () => {
@@ -42,11 +53,23 @@ describe('LoggerModule', () => {
42
53
  it('should return short format date when dateFormat is "short"', () => {
43
54
  const config = { timestamp: true, dateFormat: 'short' }
44
55
  const result = LoggerModule.getDateStamp(config)
45
- assert.ok(/\d{1,2}\/\d{2}\/\d{2}-\d{2}:\d{2}:\d{2}/.test(result))
56
+ assert.ok(/\d{1,2}\/\d{2}\/\d{2}-\d{1,2}:\d{1,2}:\d{2}/.test(result))
57
+ })
58
+
59
+ it('should return undefined-based string for unrecognised dateFormat', () => {
60
+ const config = { timestamp: true, dateFormat: 'unknown' }
61
+ const result = LoggerModule.getDateStamp(config)
62
+ assert.ok(result.includes('undefined'))
63
+ })
64
+
65
+ it('should include trailing space in formatted timestamp', () => {
66
+ const config = { timestamp: true, dateFormat: 'iso' }
67
+ const result = LoggerModule.getDateStamp(config)
68
+ assert.ok(result.includes(' '))
46
69
  })
47
70
  })
48
71
 
49
- describe('isLevelEnabled()', () => {
72
+ describe('#isLevelEnabled()', () => {
50
73
  let logger
51
74
 
52
75
  beforeEach(() => {
@@ -55,28 +78,39 @@ describe('LoggerModule', () => {
55
78
  })
56
79
 
57
80
  it('should return true for enabled levels', () => {
58
- assert.strictEqual(logger.isLevelEnabled('error'), true)
59
- assert.strictEqual(logger.isLevelEnabled('warn'), true)
60
- assert.strictEqual(logger.isLevelEnabled('info'), true)
81
+ assert.equal(logger.isLevelEnabled('error'), true)
82
+ assert.equal(logger.isLevelEnabled('warn'), true)
83
+ assert.equal(logger.isLevelEnabled('info'), true)
61
84
  })
62
85
 
63
86
  it('should return false for disabled levels', () => {
64
- assert.strictEqual(logger.isLevelEnabled('debug'), false)
65
- assert.strictEqual(logger.isLevelEnabled('verbose'), false)
87
+ assert.equal(logger.isLevelEnabled('debug'), false)
88
+ assert.equal(logger.isLevelEnabled('verbose'), false)
66
89
  })
67
90
 
68
91
  it('should return false when level is explicitly disabled', () => {
69
92
  logger.levelsConfig = ['error', '!warn', 'info']
70
- assert.strictEqual(logger.isLevelEnabled('warn'), false)
93
+ assert.equal(logger.isLevelEnabled('warn'), false)
71
94
  })
72
95
 
73
96
  it('should give preference to explicit disable', () => {
74
97
  logger.levelsConfig = ['warn', '!warn']
75
- assert.strictEqual(logger.isLevelEnabled('warn'), false)
98
+ assert.equal(logger.isLevelEnabled('warn'), false)
99
+ })
100
+
101
+ it('should return false for empty levelsConfig', () => {
102
+ logger.levelsConfig = []
103
+ assert.equal(logger.isLevelEnabled('error'), false)
104
+ })
105
+
106
+ it('should not match partial level names', () => {
107
+ logger.levelsConfig = ['info']
108
+ assert.equal(logger.isLevelEnabled('inf'), false)
109
+ assert.equal(logger.isLevelEnabled('information'), false)
76
110
  })
77
111
  })
78
112
 
79
- describe('getModuleOverrides()', () => {
113
+ describe('#getModuleOverrides()', () => {
80
114
  let logger
81
115
 
82
116
  beforeEach(() => {
@@ -88,7 +122,7 @@ describe('LoggerModule', () => {
88
122
  const result = logger.getModuleOverrides('error')
89
123
  assert.ok(result.includes('error.myModule'))
90
124
  assert.ok(result.includes('error.anotherModule'))
91
- assert.strictEqual(result.length, 2)
125
+ assert.equal(result.length, 2)
92
126
  })
93
127
 
94
128
  it('should include negative overrides', () => {
@@ -100,7 +134,7 @@ describe('LoggerModule', () => {
100
134
  it('should return empty array when no overrides exist', () => {
101
135
  logger.levelsConfig = ['error', 'warn']
102
136
  const result = logger.getModuleOverrides('info')
103
- assert.strictEqual(result.length, 0)
137
+ assert.equal(result.length, 0)
104
138
  })
105
139
 
106
140
  it('should not include overrides for other levels', () => {
@@ -108,9 +142,23 @@ describe('LoggerModule', () => {
108
142
  const result = logger.getModuleOverrides('error')
109
143
  assert.ok(!result.includes('warn.moduleB'))
110
144
  })
145
+
146
+ it('should return both positive and negative overrides together', () => {
147
+ logger.levelsConfig = ['error', 'error.modA', '!error.modB']
148
+ const result = logger.getModuleOverrides('error')
149
+ assert.equal(result.length, 2)
150
+ assert.ok(result.includes('error.modA'))
151
+ assert.ok(result.includes('!error.modB'))
152
+ })
153
+
154
+ it('should return empty array for empty levelsConfig', () => {
155
+ logger.levelsConfig = []
156
+ const result = logger.getModuleOverrides('error')
157
+ assert.deepEqual(result, [])
158
+ })
111
159
  })
112
160
 
113
- describe('isLoggingEnabled()', () => {
161
+ describe('#isLoggingEnabled()', () => {
114
162
  let logger
115
163
 
116
164
  beforeEach(() => {
@@ -125,31 +173,54 @@ describe('LoggerModule', () => {
125
173
  })
126
174
 
127
175
  it('should return true for enabled levels', () => {
128
- assert.strictEqual(logger.isLoggingEnabled('error', 'anyId'), true)
176
+ assert.equal(logger.isLoggingEnabled('error', 'anyId'), true)
129
177
  })
130
178
 
131
179
  it('should return false for disabled levels without overrides', () => {
132
- assert.strictEqual(logger.isLoggingEnabled('warn', 'generic'), false)
180
+ assert.equal(logger.isLoggingEnabled('warn', 'generic'), false)
133
181
  })
134
182
 
135
183
  it('should return true for disabled level with positive override', () => {
136
- assert.strictEqual(logger.isLoggingEnabled('warn', 'specific'), true)
184
+ assert.equal(logger.isLoggingEnabled('warn', 'specific'), true)
137
185
  })
138
186
 
139
187
  it('should return false for enabled level with negative override', () => {
140
- assert.strictEqual(logger.isLoggingEnabled('info', 'blocked'), false)
188
+ assert.equal(logger.isLoggingEnabled('info', 'blocked'), false)
141
189
  })
142
190
 
143
191
  it('should return true for enabled level without override', () => {
144
- assert.strictEqual(logger.isLoggingEnabled('info', 'allowed'), true)
192
+ assert.equal(logger.isLoggingEnabled('info', 'allowed'), true)
145
193
  })
146
194
 
147
195
  it('should handle missing level config gracefully', () => {
148
- assert.strictEqual(logger.isLoggingEnabled('nonexistent', 'id'), false)
196
+ assert.equal(logger.isLoggingEnabled('nonexistent', 'id'), false)
197
+ })
198
+
199
+ it('should default moduleOverrides to empty array when undefined', () => {
200
+ logger.config = {
201
+ levels: {
202
+ error: { enable: true }
203
+ }
204
+ }
205
+ assert.equal(logger.isLoggingEnabled('error', 'anyId'), true)
206
+ })
207
+
208
+ it('should return false when both enable is false and no matching override', () => {
209
+ logger.config = {
210
+ levels: {
211
+ debug: { enable: false, moduleOverrides: ['debug.other'] }
212
+ }
213
+ }
214
+ assert.equal(logger.isLoggingEnabled('debug', 'notOther'), false)
215
+ })
216
+
217
+ it('should handle config being undefined gracefully', () => {
218
+ logger.config = undefined
219
+ assert.equal(logger.isLoggingEnabled('error', 'id'), false)
149
220
  })
150
221
  })
151
222
 
152
- describe('log()', () => {
223
+ describe('#log()', () => {
153
224
  let logger
154
225
  let logOutput
155
226
  let originalConsoleLog
@@ -184,24 +255,24 @@ describe('LoggerModule', () => {
184
255
  it('should not log when muted', () => {
185
256
  logger.config.mute = true
186
257
  logger.log('info', 'test', 'message')
187
- assert.strictEqual(logOutput.length, 0)
258
+ assert.equal(logOutput.length, 0)
188
259
  })
189
260
 
190
261
  it('should not log when level is disabled', () => {
191
262
  logger.config.levels.debug.enable = false
192
263
  logger.log('debug', 'test', 'message')
193
- assert.strictEqual(logOutput.length, 0)
264
+ assert.equal(logOutput.length, 0)
194
265
  })
195
266
 
196
267
  it('should log message when enabled', () => {
197
268
  logger.log('info', 'testId', 'test message')
198
- assert.strictEqual(logOutput.length, 1)
269
+ assert.equal(logOutput.length, 1)
199
270
  assert.ok(logOutput[0].args.some(arg => typeof arg === 'string' && arg.includes('testId')))
200
271
  })
201
272
 
202
273
  it('should include multiple arguments', () => {
203
274
  logger.log('info', 'test', 'arg1', 'arg2', 'arg3')
204
- assert.strictEqual(logOutput.length, 1)
275
+ assert.equal(logOutput.length, 1)
205
276
  const args = logOutput[0].args
206
277
  assert.ok(args.includes('arg1'))
207
278
  assert.ok(args.includes('arg2'))
@@ -210,7 +281,7 @@ describe('LoggerModule', () => {
210
281
 
211
282
  it('should use correct console method for level', () => {
212
283
  logger.log('error', 'test', 'message')
213
- assert.strictEqual(logOutput[0].level, 'error')
284
+ assert.equal(logOutput[0].level, 'error')
214
285
  })
215
286
 
216
287
  it('should invoke logHook with correct parameters', () => {
@@ -220,21 +291,73 @@ describe('LoggerModule', () => {
220
291
  }
221
292
  logger.log('info', 'testId', 'message')
222
293
  assert.ok(hookArgs)
223
- assert.strictEqual(hookArgs[1], 'info')
224
- assert.strictEqual(hookArgs[2], 'testId')
225
- assert.strictEqual(hookArgs[3], 'message')
294
+ assert.equal(hookArgs[1], 'info')
295
+ assert.equal(hookArgs[2], 'testId')
296
+ assert.equal(hookArgs[3], 'message')
226
297
  })
227
298
 
228
299
  it('should colorise level in output', () => {
229
300
  logger.log('info', 'test', 'message')
230
- assert.strictEqual(logOutput.length, 1)
301
+ assert.equal(logOutput.length, 1)
231
302
  assert.ok(logOutput[0].args[0].includes('info'))
232
303
  })
233
304
 
234
305
  it('should colorise id in output', () => {
235
306
  logger.log('info', 'myModule', 'message')
236
- assert.strictEqual(logOutput.length, 1)
307
+ assert.equal(logOutput.length, 1)
237
308
  assert.ok(logOutput[0].args[0].includes('myModule'))
238
309
  })
310
+
311
+ it('should treat string "true" mute value as muted', () => {
312
+ logger.config.mute = 'true'
313
+ logger.log('info', 'test', 'message')
314
+ assert.equal(logOutput.length, 0)
315
+ })
316
+
317
+ it('should not mute when mute is "false"', () => {
318
+ logger.config.mute = 'false'
319
+ logger.log('info', 'test', 'message')
320
+ assert.equal(logOutput.length, 1)
321
+ })
322
+
323
+ it('should fall back to console.log for unknown level', () => {
324
+ logger.config.levels.success = { enable: true, moduleOverrides: [], colour: chalk.green }
325
+ logger.log('success', 'test', 'message')
326
+ assert.equal(logOutput.length, 1)
327
+ assert.equal(logOutput[0].level, 'log')
328
+ })
329
+
330
+ it('should handle MODULE_READY id by using module name suffix', () => {
331
+ logger.name = 'adapt-authoring-logger'
332
+ logger.config.levels.verbose = { enable: true, moduleOverrides: [], colour: chalk.grey }
333
+ let hookArgs = null
334
+ logger.logHook.invoke = (...args) => { hookArgs = args }
335
+ logger.log('verbose', AbstractModule.MODULE_READY, 'some-init-time')
336
+ assert.equal(logOutput.length, 1)
337
+ assert.ok(logOutput[0].args[0].includes('logger'))
338
+ assert.equal(hookArgs[2], 'logger')
339
+ assert.equal(hookArgs[3], AbstractModule.MODULE_READY)
340
+ assert.equal(hookArgs[4], 'some-init-time')
341
+ })
342
+
343
+ it('should prepend date stamp when timestamp is enabled', () => {
344
+ logger.config.timestamp = true
345
+ logger.config.dateFormat = 'iso'
346
+ logger.log('info', 'test', 'message')
347
+ assert.equal(logOutput.length, 1)
348
+ assert.ok(/\d{4}-\d{2}-\d{2}T/.test(logOutput[0].args[0]))
349
+ })
350
+
351
+ it('should pass Date as first argument to logHook', () => {
352
+ let hookArgs = null
353
+ logger.logHook.invoke = (...args) => { hookArgs = args }
354
+ logger.log('info', 'test', 'message')
355
+ assert.ok(hookArgs[0] instanceof Date)
356
+ })
357
+
358
+ it('should handle config being undefined gracefully', () => {
359
+ logger.config = undefined
360
+ assert.doesNotThrow(() => logger.log('info', 'test', 'message'))
361
+ })
239
362
  })
240
363
  })