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