adapt-authoring-logger 1.2.0 → 1.3.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.
@@ -25,7 +25,7 @@ jobs:
25
25
  - name: Update npm
26
26
  run: npm install -g npm@latest
27
27
  - name: Install dependencies
28
- run: npm ci
28
+ run: npm install
29
29
  - name: Release
30
30
  env:
31
31
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -9,5 +9,5 @@ jobs:
9
9
  with:
10
10
  node-version: 'lts/*'
11
11
  cache: 'npm'
12
- - run: npm ci
12
+ - run: npm install
13
13
  - run: npx standard
@@ -11,5 +11,5 @@ jobs:
11
11
  with:
12
12
  node-version: 'lts/*'
13
13
  cache: 'npm'
14
- - run: npm ci
14
+ - run: npm install
15
15
  - run: npm test
@@ -1,45 +1,12 @@
1
1
  import { AbstractModule, Hook } from 'adapt-authoring-core'
2
2
  import chalk from 'chalk'
3
+ import { colourise, getDateStamp, getModuleOverrides, isLevelEnabled, isLoggingEnabled } from './utils.js'
3
4
  /**
4
5
  * Module for logging message to the console
5
6
  * @memberof logger
6
7
  * @extends {AbstractModule}
7
8
  */
8
9
  class LoggerModule extends AbstractModule {
9
- /**
10
- * Colours an input string
11
- * @param {String} str
12
- * @param {String} colour
13
- * @return {String}
14
- */
15
- static colourise (str, colourFunc) {
16
- if (typeof colourFunc === 'string') colourFunc = chalk[colourFunc]
17
- return colourFunc ? colourFunc(str) : str
18
- }
19
-
20
- /**
21
- * Returns a formatted date stamp
22
- * @param {Object} config
23
- * @return {String}
24
- */
25
- static getDateStamp (config) {
26
- if (!config.timestamp) {
27
- return ''
28
- }
29
- let str
30
- if (config.dateFormat === 'iso') {
31
- str = new Date().toISOString()
32
- } else if (config.dateFormat === 'short') {
33
- const d = new Date()
34
- const m = d.getMonth() + 1
35
- const s = d.getSeconds()
36
- const date = `${d.getDate()}/${m < 10 ? `0${m}` : m}/${d.getFullYear().toString().slice(2)}`
37
- const time = `${d.getHours()}:${d.getMinutes()}:${s < 10 ? `0${s}` : s}`
38
- str = `${date}-${time}`
39
- }
40
- return LoggerModule.colourise(`${str} `, chalk.dim)
41
- }
42
-
43
10
  /** @override */
44
11
  async init () {
45
12
  await this.app.waitForModule('config')
@@ -60,33 +27,33 @@ class LoggerModule extends AbstractModule {
60
27
  this.config = {
61
28
  levels: {
62
29
  error: {
63
- enable: this.isLevelEnabled('error'),
64
- moduleOverrides: this.getModuleOverrides('error'),
30
+ enable: isLevelEnabled(this.levelsConfig, 'error'),
31
+ moduleOverrides: getModuleOverrides(this.levelsConfig, 'error'),
65
32
  colour: chalk.red
66
33
  },
67
34
  warn: {
68
- enable: this.isLevelEnabled('warn'),
69
- moduleOverrides: this.getModuleOverrides('warn'),
35
+ enable: isLevelEnabled(this.levelsConfig, 'warn'),
36
+ moduleOverrides: getModuleOverrides(this.levelsConfig, 'warn'),
70
37
  colour: chalk.yellow
71
38
  },
72
39
  success: {
73
- enable: this.isLevelEnabled('success'),
74
- moduleOverrides: this.getModuleOverrides('success'),
40
+ enable: isLevelEnabled(this.levelsConfig, 'success'),
41
+ moduleOverrides: getModuleOverrides(this.levelsConfig, 'success'),
75
42
  colour: chalk.green
76
43
  },
77
44
  info: {
78
- enable: this.isLevelEnabled('info'),
79
- moduleOverrides: this.getModuleOverrides('info'),
45
+ enable: isLevelEnabled(this.levelsConfig, 'info'),
46
+ moduleOverrides: getModuleOverrides(this.levelsConfig, 'info'),
80
47
  colour: chalk.cyan
81
48
  },
82
49
  debug: {
83
- enable: this.isLevelEnabled('debug'),
84
- moduleOverrides: this.getModuleOverrides('debug'),
50
+ enable: isLevelEnabled(this.levelsConfig, 'debug'),
51
+ moduleOverrides: getModuleOverrides(this.levelsConfig, 'debug'),
85
52
  colour: chalk.dim
86
53
  },
87
54
  verbose: {
88
- enable: this.isLevelEnabled('verbose'),
89
- moduleOverrides: this.getModuleOverrides('verbose'),
55
+ enable: isLevelEnabled(this.levelsConfig, 'verbose'),
56
+ moduleOverrides: getModuleOverrides(this.levelsConfig, 'verbose'),
90
57
  colour: chalk.grey.italic
91
58
  }
92
59
  },
@@ -100,42 +67,6 @@ class LoggerModule extends AbstractModule {
100
67
  this.app.logger = this
101
68
  }
102
69
 
103
- /**
104
- * Determines whether a specific log level is enabled
105
- * @param {String} level
106
- * @return {Boolean}
107
- */
108
- isLevelEnabled (level) { // note explicit disable takes preference
109
- return !this.levelsConfig.includes(`!${level}`) && this.levelsConfig.includes(level)
110
- }
111
-
112
- /**
113
- * Returns a list of log levels with overrides, either inclusive or exclusive
114
- * @param {String} level
115
- * @return {Array}
116
- */
117
- getModuleOverrides (level) {
118
- const levels = []
119
- this.levelsConfig.forEach(l => {
120
- const s = `${level}.`; const notS = `!${level}.`
121
- if (l.indexOf(s) === 0 || l.indexOf(notS) === 0) levels.push(l)
122
- })
123
- return levels
124
- }
125
-
126
- /**
127
- * Returns whether a message should be logged (i.e. not disabled in the config)
128
- * @param {string} level Logging level
129
- * @param {string} id Id of log caller
130
- * @returns {boolean}
131
- */
132
- isLoggingEnabled (level, id) {
133
- const { enable, moduleOverrides = [] } = this?.config?.levels[level] || {}
134
- const isEnabled = enable || moduleOverrides.includes(`${level}.${id}`)
135
- const disableOverride = moduleOverrides.includes(`!${level}.${id}`)
136
- return isEnabled && !disableOverride
137
- }
138
-
139
70
  /**
140
71
  * Logs a message to the console
141
72
  * @param {String} level Severity of the message
@@ -150,12 +81,12 @@ class LoggerModule extends AbstractModule {
150
81
  id = this.name.split('-').pop()
151
82
  args = [AbstractModule.MODULE_READY, ...args]
152
83
  }
153
- if (!this.isLoggingEnabled(level, id)) {
84
+ if (!isLoggingEnabled(this.config?.levels, level, id)) {
154
85
  return
155
86
  }
156
87
  const colour = this?.config?.levels[level]?.colour
157
88
  const logFunc = console[level] ?? console.log
158
- logFunc(`${LoggerModule.getDateStamp(this.config)}${LoggerModule.colourise(level, colour)} ${LoggerModule.colourise(id, chalk.magenta)}`, ...args)
89
+ logFunc(`${getDateStamp(this.config)}${colourise(level, colour)} ${colourise(id, chalk.magenta)}`, ...args)
159
90
  this.logHook.invoke(new Date(), level, id, ...args)
160
91
  }
161
92
  }
@@ -0,0 +1,12 @@
1
+ import chalk from 'chalk'
2
+ /**
3
+ * Colours an input string using a chalk function or colour name
4
+ * @param {String} str The string to colourise
5
+ * @param {String|Function} colourFunc A chalk colour function or string name of a chalk colour
6
+ * @return {String} The colourised string
7
+ * @memberof logger
8
+ */
9
+ export function colourise (str, colourFunc) {
10
+ if (typeof colourFunc === 'string') colourFunc = chalk[colourFunc]
11
+ return colourFunc ? colourFunc(str) : str
12
+ }
@@ -0,0 +1,27 @@
1
+ import chalk from 'chalk'
2
+ import { colourise } from './colourise.js'
3
+ /**
4
+ * Returns a formatted date stamp string based on config
5
+ * @param {Object} config Logger configuration object
6
+ * @param {Boolean} config.timestamp Whether to include a timestamp
7
+ * @param {String} config.dateFormat Date format ('iso' or 'short')
8
+ * @return {String} The formatted date stamp (empty string if timestamps disabled)
9
+ * @memberof logger
10
+ */
11
+ export function getDateStamp (config) {
12
+ if (!config.timestamp) {
13
+ return ''
14
+ }
15
+ let str
16
+ if (config.dateFormat === 'iso') {
17
+ str = new Date().toISOString()
18
+ } else if (config.dateFormat === 'short') {
19
+ const d = new Date()
20
+ const m = d.getMonth() + 1
21
+ const s = d.getSeconds()
22
+ const date = `${d.getDate()}/${m < 10 ? `0${m}` : m}/${d.getFullYear().toString().slice(2)}`
23
+ const time = `${d.getHours()}:${d.getMinutes()}:${s < 10 ? `0${s}` : s}`
24
+ str = `${date}-${time}`
25
+ }
26
+ return colourise(`${str} `, chalk.dim)
27
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Returns a list of module-specific log level overrides from the levels config
3
+ * @param {Array<String>} levelsConfig Array of level configuration strings
4
+ * @param {String} level The log level to find overrides for
5
+ * @return {Array<String>} Array of override strings (e.g. ['debug.mymod', '!debug.other'])
6
+ * @memberof logger
7
+ */
8
+ export function getModuleOverrides (levelsConfig, level) {
9
+ const levels = []
10
+ levelsConfig.forEach(l => {
11
+ const s = `${level}.`; const notS = `!${level}.`
12
+ if (l.indexOf(s) === 0 || l.indexOf(notS) === 0) levels.push(l)
13
+ })
14
+ return levels
15
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Determines whether a specific log level is enabled in the levels config
3
+ * @param {Array<String>} levelsConfig Array of level configuration strings
4
+ * @param {String} level The log level to check (e.g. 'error', 'warn', 'debug')
5
+ * @return {Boolean} Whether the level is enabled
6
+ * @memberof logger
7
+ */
8
+ export function isLevelEnabled (levelsConfig, level) {
9
+ return !levelsConfig.includes(`!${level}`) && levelsConfig.includes(level)
10
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Returns whether a message should be logged based on the resolved config
3
+ * @param {Object} configLevels The resolved levels config object (e.g. { error: { enable, moduleOverrides }, ... })
4
+ * @param {String} level Logging level (e.g. 'error', 'warn', 'debug')
5
+ * @param {String} id Id of log caller (module name)
6
+ * @returns {Boolean} Whether logging is enabled for this level and caller
7
+ * @memberof logger
8
+ */
9
+ export function isLoggingEnabled (configLevels, level, id) {
10
+ const { enable, moduleOverrides = [] } = configLevels?.[level] || {}
11
+ const isEnabled = enable || moduleOverrides.includes(`${level}.${id}`)
12
+ const disableOverride = moduleOverrides.includes(`!${level}.${id}`)
13
+ return isEnabled && !disableOverride
14
+ }
package/lib/utils.js ADDED
@@ -0,0 +1,5 @@
1
+ export { colourise } from './utils/colourise.js'
2
+ export { getDateStamp } from './utils/getDateStamp.js'
3
+ export { getModuleOverrides } from './utils/getModuleOverrides.js'
4
+ export { isLevelEnabled } from './utils/isLevelEnabled.js'
5
+ export { isLoggingEnabled } from './utils/isLoggingEnabled.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adapt-authoring-logger",
3
- "version": "1.2.0",
3
+ "version": "1.3.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,7 +8,7 @@
8
8
  "main": "index.js",
9
9
  "repository": "adapt-security/adapt-authoring-logger",
10
10
  "dependencies": {
11
- "adapt-authoring-core": "^1.7.0",
11
+ "adapt-authoring-core": "^2.0.0",
12
12
  "chalk": "^5.3.0"
13
13
  },
14
14
  "peerDependencies": {
@@ -3,220 +3,191 @@ import assert from 'node:assert/strict'
3
3
  import chalk from 'chalk'
4
4
  import { AbstractModule } from 'adapt-authoring-core'
5
5
  import LoggerModule from '../lib/LoggerModule.js'
6
+ import { colourise, getDateStamp, isLevelEnabled, getModuleOverrides, isLoggingEnabled } from '../lib/utils.js'
6
7
 
7
8
  describe('LoggerModule', () => {
8
- describe('#colourise()', () => {
9
+ describe('colourise()', () => {
9
10
  it('should return string with colour function applied', () => {
10
- const result = LoggerModule.colourise('test', chalk.red)
11
+ const result = colourise('test', chalk.red)
11
12
  assert.ok(result.includes('test'))
12
13
  })
13
14
 
14
15
  it('should accept colour name as string', () => {
15
- const result = LoggerModule.colourise('test', 'green')
16
+ const result = colourise('test', 'green')
16
17
  assert.ok(result.includes('test'))
17
18
  })
18
19
 
19
20
  it('should return uncoloured string if no colour function provided', () => {
20
- const result = LoggerModule.colourise('test', null)
21
+ const result = colourise('test', null)
21
22
  assert.equal(result, 'test')
22
23
  })
23
24
 
24
25
  it('should handle undefined colour function', () => {
25
- const result = LoggerModule.colourise('test', undefined)
26
+ const result = colourise('test', undefined)
26
27
  assert.equal(result, 'test')
27
28
  })
28
29
 
29
30
  it('should return empty string unchanged when no colour', () => {
30
- const result = LoggerModule.colourise('', null)
31
+ const result = colourise('', null)
31
32
  assert.equal(result, '')
32
33
  })
33
34
 
34
35
  it('should apply colour function to empty string', () => {
35
- const result = LoggerModule.colourise('', chalk.red)
36
+ const result = colourise('', chalk.red)
36
37
  assert.equal(typeof result, 'string')
37
38
  })
38
39
  })
39
40
 
40
- describe('#getDateStamp()', () => {
41
+ describe('getDateStamp()', () => {
41
42
  it('should return empty string when timestamp is disabled', () => {
42
43
  const config = { timestamp: false }
43
- const result = LoggerModule.getDateStamp(config)
44
+ const result = getDateStamp(config)
44
45
  assert.equal(result, '')
45
46
  })
46
47
 
47
48
  it('should return ISO format date when dateFormat is "iso"', () => {
48
49
  const config = { timestamp: true, dateFormat: 'iso' }
49
- const result = LoggerModule.getDateStamp(config)
50
+ const result = getDateStamp(config)
50
51
  assert.ok(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(result))
51
52
  })
52
53
 
53
54
  it('should return short format date when dateFormat is "short"', () => {
54
55
  const config = { timestamp: true, dateFormat: 'short' }
55
- const result = LoggerModule.getDateStamp(config)
56
+ const result = getDateStamp(config)
56
57
  assert.ok(/\d{1,2}\/\d{2}\/\d{2}-\d{1,2}:\d{1,2}:\d{2}/.test(result))
57
58
  })
58
59
 
59
60
  it('should return undefined-based string for unrecognised dateFormat', () => {
60
61
  const config = { timestamp: true, dateFormat: 'unknown' }
61
- const result = LoggerModule.getDateStamp(config)
62
+ const result = getDateStamp(config)
62
63
  assert.ok(result.includes('undefined'))
63
64
  })
64
65
 
65
66
  it('should include trailing space in formatted timestamp', () => {
66
67
  const config = { timestamp: true, dateFormat: 'iso' }
67
- const result = LoggerModule.getDateStamp(config)
68
+ const result = getDateStamp(config)
68
69
  assert.ok(result.includes(' '))
69
70
  })
70
71
  })
71
72
 
72
- describe('#isLevelEnabled()', () => {
73
- let logger
74
-
75
- beforeEach(() => {
76
- logger = new LoggerModule('test-logger')
77
- logger.levelsConfig = ['error', 'warn', 'info']
78
- })
79
-
73
+ describe('isLevelEnabled()', () => {
80
74
  it('should return true for enabled levels', () => {
81
- assert.equal(logger.isLevelEnabled('error'), true)
82
- assert.equal(logger.isLevelEnabled('warn'), true)
83
- assert.equal(logger.isLevelEnabled('info'), true)
75
+ const levelsConfig = ['error', 'warn', 'info']
76
+ assert.equal(isLevelEnabled(levelsConfig, 'error'), true)
77
+ assert.equal(isLevelEnabled(levelsConfig, 'warn'), true)
78
+ assert.equal(isLevelEnabled(levelsConfig, 'info'), true)
84
79
  })
85
80
 
86
81
  it('should return false for disabled levels', () => {
87
- assert.equal(logger.isLevelEnabled('debug'), false)
88
- assert.equal(logger.isLevelEnabled('verbose'), false)
82
+ const levelsConfig = ['error', 'warn', 'info']
83
+ assert.equal(isLevelEnabled(levelsConfig, 'debug'), false)
84
+ assert.equal(isLevelEnabled(levelsConfig, 'verbose'), false)
89
85
  })
90
86
 
91
87
  it('should return false when level is explicitly disabled', () => {
92
- logger.levelsConfig = ['error', '!warn', 'info']
93
- assert.equal(logger.isLevelEnabled('warn'), false)
88
+ const levelsConfig = ['error', '!warn', 'info']
89
+ assert.equal(isLevelEnabled(levelsConfig, 'warn'), false)
94
90
  })
95
91
 
96
92
  it('should give preference to explicit disable', () => {
97
- logger.levelsConfig = ['warn', '!warn']
98
- assert.equal(logger.isLevelEnabled('warn'), false)
93
+ const levelsConfig = ['warn', '!warn']
94
+ assert.equal(isLevelEnabled(levelsConfig, 'warn'), false)
99
95
  })
100
96
 
101
97
  it('should return false for empty levelsConfig', () => {
102
- logger.levelsConfig = []
103
- assert.equal(logger.isLevelEnabled('error'), false)
98
+ assert.equal(isLevelEnabled([], 'error'), false)
104
99
  })
105
100
 
106
101
  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)
102
+ const levelsConfig = ['info']
103
+ assert.equal(isLevelEnabled(levelsConfig, 'inf'), false)
104
+ assert.equal(isLevelEnabled(levelsConfig, 'information'), false)
110
105
  })
111
106
  })
112
107
 
113
- describe('#getModuleOverrides()', () => {
114
- let logger
115
-
116
- beforeEach(() => {
117
- logger = new LoggerModule('test-logger')
118
- })
119
-
108
+ describe('getModuleOverrides()', () => {
120
109
  it('should return module-specific overrides for a level', () => {
121
- logger.levelsConfig = ['error', 'error.myModule', 'error.anotherModule', 'warn']
122
- const result = logger.getModuleOverrides('error')
110
+ const levelsConfig = ['error', 'error.myModule', 'error.anotherModule', 'warn']
111
+ const result = getModuleOverrides(levelsConfig, 'error')
123
112
  assert.ok(result.includes('error.myModule'))
124
113
  assert.ok(result.includes('error.anotherModule'))
125
114
  assert.equal(result.length, 2)
126
115
  })
127
116
 
128
117
  it('should include negative overrides', () => {
129
- logger.levelsConfig = ['error', '!error.myModule']
130
- const result = logger.getModuleOverrides('error')
118
+ const levelsConfig = ['error', '!error.myModule']
119
+ const result = getModuleOverrides(levelsConfig, 'error')
131
120
  assert.ok(result.includes('!error.myModule'))
132
121
  })
133
122
 
134
123
  it('should return empty array when no overrides exist', () => {
135
- logger.levelsConfig = ['error', 'warn']
136
- const result = logger.getModuleOverrides('info')
124
+ const levelsConfig = ['error', 'warn']
125
+ const result = getModuleOverrides(levelsConfig, 'info')
137
126
  assert.equal(result.length, 0)
138
127
  })
139
128
 
140
129
  it('should not include overrides for other levels', () => {
141
- logger.levelsConfig = ['error', 'error.moduleA', 'warn.moduleB']
142
- const result = logger.getModuleOverrides('error')
130
+ const levelsConfig = ['error', 'error.moduleA', 'warn.moduleB']
131
+ const result = getModuleOverrides(levelsConfig, 'error')
143
132
  assert.ok(!result.includes('warn.moduleB'))
144
133
  })
145
134
 
146
135
  it('should return both positive and negative overrides together', () => {
147
- logger.levelsConfig = ['error', 'error.modA', '!error.modB']
148
- const result = logger.getModuleOverrides('error')
136
+ const levelsConfig = ['error', 'error.modA', '!error.modB']
137
+ const result = getModuleOverrides(levelsConfig, 'error')
149
138
  assert.equal(result.length, 2)
150
139
  assert.ok(result.includes('error.modA'))
151
140
  assert.ok(result.includes('!error.modB'))
152
141
  })
153
142
 
154
143
  it('should return empty array for empty levelsConfig', () => {
155
- logger.levelsConfig = []
156
- const result = logger.getModuleOverrides('error')
157
- assert.deepEqual(result, [])
144
+ assert.deepEqual(getModuleOverrides([], 'error'), [])
158
145
  })
159
146
  })
160
147
 
161
- describe('#isLoggingEnabled()', () => {
162
- let logger
163
-
164
- beforeEach(() => {
165
- logger = new LoggerModule('test-logger')
166
- logger.config = {
167
- levels: {
168
- error: { enable: true, moduleOverrides: [] },
169
- warn: { enable: false, moduleOverrides: ['warn.specific'] },
170
- info: { enable: true, moduleOverrides: ['!info.blocked'] }
171
- }
172
- }
173
- })
174
-
148
+ describe('isLoggingEnabled()', () => {
175
149
  it('should return true for enabled levels', () => {
176
- assert.equal(logger.isLoggingEnabled('error', 'anyId'), true)
150
+ const levels = { error: { enable: true, moduleOverrides: [] } }
151
+ assert.equal(isLoggingEnabled(levels, 'error', 'anyId'), true)
177
152
  })
178
153
 
179
154
  it('should return false for disabled levels without overrides', () => {
180
- assert.equal(logger.isLoggingEnabled('warn', 'generic'), false)
155
+ const levels = { warn: { enable: false, moduleOverrides: ['warn.specific'] } }
156
+ assert.equal(isLoggingEnabled(levels, 'warn', 'generic'), false)
181
157
  })
182
158
 
183
159
  it('should return true for disabled level with positive override', () => {
184
- assert.equal(logger.isLoggingEnabled('warn', 'specific'), true)
160
+ const levels = { warn: { enable: false, moduleOverrides: ['warn.specific'] } }
161
+ assert.equal(isLoggingEnabled(levels, 'warn', 'specific'), true)
185
162
  })
186
163
 
187
164
  it('should return false for enabled level with negative override', () => {
188
- assert.equal(logger.isLoggingEnabled('info', 'blocked'), false)
165
+ const levels = { info: { enable: true, moduleOverrides: ['!info.blocked'] } }
166
+ assert.equal(isLoggingEnabled(levels, 'info', 'blocked'), false)
189
167
  })
190
168
 
191
169
  it('should return true for enabled level without override', () => {
192
- assert.equal(logger.isLoggingEnabled('info', 'allowed'), true)
170
+ const levels = { info: { enable: true, moduleOverrides: ['!info.blocked'] } }
171
+ assert.equal(isLoggingEnabled(levels, 'info', 'allowed'), true)
193
172
  })
194
173
 
195
174
  it('should handle missing level config gracefully', () => {
196
- assert.equal(logger.isLoggingEnabled('nonexistent', 'id'), false)
175
+ const levels = { error: { enable: true, moduleOverrides: [] } }
176
+ assert.equal(isLoggingEnabled(levels, 'nonexistent', 'id'), false)
197
177
  })
198
178
 
199
179
  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)
180
+ const levels = { error: { enable: true } }
181
+ assert.equal(isLoggingEnabled(levels, 'error', 'anyId'), true)
206
182
  })
207
183
 
208
184
  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)
185
+ const levels = { debug: { enable: false, moduleOverrides: ['debug.other'] } }
186
+ assert.equal(isLoggingEnabled(levels, 'debug', 'notOther'), false)
215
187
  })
216
188
 
217
189
  it('should handle config being undefined gracefully', () => {
218
- logger.config = undefined
219
- assert.equal(logger.isLoggingEnabled('error', 'id'), false)
190
+ assert.equal(isLoggingEnabled(undefined, 'error', 'id'), false)
220
191
  })
221
192
  })
222
193
 
@@ -0,0 +1,36 @@
1
+ import { describe, it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { colourise } from '../lib/utils/colourise.js'
4
+
5
+ describe('colourise()', () => {
6
+ it('should return string unchanged when colourFunc is undefined', () => {
7
+ assert.equal(colourise('hello', undefined), 'hello')
8
+ })
9
+
10
+ it('should return string unchanged when colourFunc is null', () => {
11
+ assert.equal(colourise('hello', null), 'hello')
12
+ })
13
+
14
+ it('should apply a function colour', () => {
15
+ const mockColour = (s) => `[coloured]${s}[/coloured]`
16
+ assert.equal(colourise('hello', mockColour), '[coloured]hello[/coloured]')
17
+ })
18
+
19
+ it('should resolve string colour names via chalk', () => {
20
+ // chalk resolves 'red' to chalk.red and applies it
21
+ const result = colourise('hello', 'red')
22
+ assert.ok(result.includes('hello'))
23
+ // chalk.red is a function, so the result should be the return value of that function
24
+ // In non-TTY environments chalk may strip ANSI codes, so just verify it resolved and ran
25
+ assert.equal(typeof result, 'string')
26
+ })
27
+
28
+ it('should return string unchanged for invalid chalk colour name', () => {
29
+ assert.equal(colourise('hello', 'notARealColour'), 'hello')
30
+ })
31
+
32
+ it('should handle empty string input', () => {
33
+ const mockColour = (s) => `[c]${s}[/c]`
34
+ assert.equal(colourise('', mockColour), '[c][/c]')
35
+ })
36
+ })
@@ -0,0 +1,34 @@
1
+ import { describe, it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { getDateStamp } from '../lib/utils/getDateStamp.js'
4
+
5
+ // eslint-disable-next-line no-control-regex
6
+ const ANSI_REGEX = /\u001b\[\d+m/g
7
+
8
+ describe('getDateStamp()', () => {
9
+ it('should return empty string when timestamp is falsy', () => {
10
+ assert.equal(getDateStamp({ timestamp: false, dateFormat: 'iso' }), '')
11
+ })
12
+
13
+ it('should return empty string when timestamp is undefined', () => {
14
+ assert.equal(getDateStamp({ dateFormat: 'iso' }), '')
15
+ })
16
+
17
+ it('should return an ISO date string when dateFormat is "iso"', () => {
18
+ const result = getDateStamp({ timestamp: true, dateFormat: 'iso' })
19
+ const stripped = result.replace(ANSI_REGEX, '')
20
+ assert.ok(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z /.test(stripped))
21
+ })
22
+
23
+ it('should return a short date string when dateFormat is "short"', () => {
24
+ const result = getDateStamp({ timestamp: true, dateFormat: 'short' })
25
+ const stripped = result.replace(ANSI_REGEX, '')
26
+ assert.ok(/^\d{1,2}\/\d{2}\/\d{2}-\d{1,2}:\d{1,2}:\d{1,2} /.test(stripped))
27
+ })
28
+
29
+ it('should handle unknown dateFormat by returning "undefined " (colourised)', () => {
30
+ const result = getDateStamp({ timestamp: true, dateFormat: 'unknown' })
31
+ const stripped = result.replace(ANSI_REGEX, '')
32
+ assert.equal(stripped, 'undefined ')
33
+ })
34
+ })
@@ -0,0 +1,39 @@
1
+ import { describe, it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { getModuleOverrides } from '../lib/utils/getModuleOverrides.js'
4
+
5
+ describe('getModuleOverrides()', () => {
6
+ it('should return empty array when no overrides exist', () => {
7
+ assert.deepEqual(getModuleOverrides(['error', 'warn', 'info'], 'debug'), [])
8
+ })
9
+
10
+ it('should return module-specific includes', () => {
11
+ const config = ['error', 'debug.mymod', 'debug.other']
12
+ assert.deepEqual(getModuleOverrides(config, 'debug'), ['debug.mymod', 'debug.other'])
13
+ })
14
+
15
+ it('should return module-specific excludes', () => {
16
+ const config = ['error', '!debug.mymod']
17
+ assert.deepEqual(getModuleOverrides(config, 'debug'), ['!debug.mymod'])
18
+ })
19
+
20
+ it('should return both includes and excludes', () => {
21
+ const config = ['error', 'debug.mymod', '!debug.other', 'debug.third']
22
+ const result = getModuleOverrides(config, 'debug')
23
+ assert.deepEqual(result, ['debug.mymod', '!debug.other', 'debug.third'])
24
+ })
25
+
26
+ it('should not include plain level entries', () => {
27
+ const config = ['debug', 'debug.mymod']
28
+ assert.deepEqual(getModuleOverrides(config, 'debug'), ['debug.mymod'])
29
+ })
30
+
31
+ it('should not match other levels with similar prefixes', () => {
32
+ const config = ['error.mymod', 'warn.mymod']
33
+ assert.deepEqual(getModuleOverrides(config, 'error'), ['error.mymod'])
34
+ })
35
+
36
+ it('should handle empty config', () => {
37
+ assert.deepEqual(getModuleOverrides([], 'error'), [])
38
+ })
39
+ })
@@ -0,0 +1,33 @@
1
+ import { describe, it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { isLevelEnabled } from '../lib/utils/isLevelEnabled.js'
4
+
5
+ describe('isLevelEnabled()', () => {
6
+ it('should return true when level is in config', () => {
7
+ assert.equal(isLevelEnabled(['error', 'warn', 'info'], 'error'), true)
8
+ })
9
+
10
+ it('should return false when level is not in config', () => {
11
+ assert.equal(isLevelEnabled(['error', 'warn'], 'debug'), false)
12
+ })
13
+
14
+ it('should return false when level is explicitly disabled', () => {
15
+ assert.equal(isLevelEnabled(['error', '!warn', 'warn'], 'warn'), false)
16
+ })
17
+
18
+ it('should return true when level is present and not explicitly disabled', () => {
19
+ assert.equal(isLevelEnabled(['error', 'warn', '!debug'], 'warn'), true)
20
+ })
21
+
22
+ it('should return false for empty config', () => {
23
+ assert.equal(isLevelEnabled([], 'error'), false)
24
+ })
25
+
26
+ it('should not match partial level names', () => {
27
+ assert.equal(isLevelEnabled(['error.mymod'], 'error'), false)
28
+ })
29
+
30
+ it('should handle explicit disable taking precedence even when level is included', () => {
31
+ assert.equal(isLevelEnabled(['debug', '!debug'], 'debug'), false)
32
+ })
33
+ })
@@ -0,0 +1,50 @@
1
+ import { describe, it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { isLoggingEnabled } from '../lib/utils/isLoggingEnabled.js'
4
+
5
+ describe('isLoggingEnabled()', () => {
6
+ it('should return true when level is globally enabled', () => {
7
+ const levels = { error: { enable: true, moduleOverrides: [] } }
8
+ assert.equal(isLoggingEnabled(levels, 'error', 'mymod'), true)
9
+ })
10
+
11
+ it('should return false when level is globally disabled and no override', () => {
12
+ const levels = { error: { enable: false, moduleOverrides: [] } }
13
+ assert.equal(isLoggingEnabled(levels, 'error', 'mymod'), false)
14
+ })
15
+
16
+ it('should return true when module has an include override', () => {
17
+ const levels = { debug: { enable: false, moduleOverrides: ['debug.mymod'] } }
18
+ assert.equal(isLoggingEnabled(levels, 'debug', 'mymod'), true)
19
+ })
20
+
21
+ it('should return false when module has a disable override', () => {
22
+ const levels = { error: { enable: true, moduleOverrides: ['!error.mymod'] } }
23
+ assert.equal(isLoggingEnabled(levels, 'error', 'mymod'), false)
24
+ })
25
+
26
+ it('should return false when both include and disable override exist', () => {
27
+ const levels = { debug: { enable: false, moduleOverrides: ['debug.mymod', '!debug.mymod'] } }
28
+ assert.equal(isLoggingEnabled(levels, 'debug', 'mymod'), false)
29
+ })
30
+
31
+ it('should return false when level is not in config', () => {
32
+ const levels = { error: { enable: true, moduleOverrides: [] } }
33
+ assert.equal(isLoggingEnabled(levels, 'debug', 'mymod'), false)
34
+ })
35
+
36
+ it('should handle null/undefined configLevels gracefully', () => {
37
+ assert.equal(isLoggingEnabled(null, 'error', 'mymod'), false)
38
+ assert.equal(isLoggingEnabled(undefined, 'error', 'mymod'), false)
39
+ })
40
+
41
+ it('should default moduleOverrides to empty array when missing', () => {
42
+ const levels = { error: { enable: true } }
43
+ assert.equal(isLoggingEnabled(levels, 'error', 'mymod'), true)
44
+ })
45
+
46
+ it('should not match overrides for different modules', () => {
47
+ const levels = { debug: { enable: false, moduleOverrides: ['debug.other'] } }
48
+ assert.equal(isLoggingEnabled(levels, 'debug', 'mymod'), false)
49
+ })
50
+ })