Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const warning = createWarning({
message: 'Hello %s',
unlimited: true
})
warning('world')
const emitted = warning('world')
```

#### Methods
Expand Down Expand Up @@ -101,6 +101,45 @@ FST_ERROR_CODE('world') // will be emitted
FST_ERROR_CODE('world') // will be emitted again
```

#### `spyWarning(warning)`

Spy the created warning function for testing purpose.

```js
const { test } = require('node:test')
const { createWarning, spyWarning } = require('process-warning')
const FST_ERROR_CODE = createWarning({ name: 'MyAppWarning', code: 'FST_ERROR_CODE', message: 'Hello %s' })

test('spy warning', t => {
const spyData = spyWarning(FST_ERROR_CODE)

// call after spy
const emitted = FST_ERROR_CODE('world')
t.assert.strictEqual(emitted, true)

// restore the warning function
// it must be called when you do not need to spy anymore
// otherwise, the calls data will accumulates.
t.after(() => spyData.restore())

t.assert.strictEqual(FST_ERROR_CODE.emitted, true)
// calls return the arguments and result.
// result indicate whether warning is emitted through process.emitWarning
console.log(spyData.calls) // [{ arguments: ['world'], result: true }]
t.assert.deepStrictEqual(spyData.calls[0].arguments, ['world'])
t.assert.strictEqual(spyData.calls[0].result, true)
// number of times called the function
console.log(spyData.callCount()) // 1
t.assert.strictEqual(spyData.callCount(), 1)

// reset the spy stat and warning state
Comment thread
jean-michelet marked this conversation as resolved.
spyData.reset()
t.assert.strictEqual(FST_ERROR_CODE.emitted, false)
t.assert.deepStrictEqual(spyData.calls, [])
t.assert.strictEqual(spyData.callCount(), 0)
})
```

#### Suppressing warnings

It is possible to suppress warnings by utilizing one of node's built-in warning suppression mechanisms.
Expand Down
102 changes: 85 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const { format } = require('node:util')
* @property {string} message - The warning message.
* @property {boolean} emitted - Indicates if the warning has been emitted.
* @property {function} format - Formats the warning message.
* @returns {boolean} emit - Indicates if the warning has been emitted by this call.
*/

/**
Expand All @@ -33,8 +34,69 @@ const { format } = require('node:util')
* @typedef {Object} ProcessWarning
* @property {function} createWarning - Creates a warning item.
* @property {function} createDeprecation - Creates a deprecation warning item.
* @property {function} spyWarning - Spy a warning item.
*/

/**
* Represents the spy data.
* @typedef {Object} WarningSpyData
* @property {object} calls - Arguments of WarningItem calls with.
* @property {function} callCount - Number of counts called the WarningItem.
* @property {function} reset - Reset the calls data and state of WarningItem.
* @property {function} restore - Remove spy from WarningItem.
*/

const kWarningFn = Symbol('process-warning.fn')
const kWarningSpyData = Symbol('process-warning.spyData')

/**
* Spy a warning item.
* @function
* @memberof processWarning
* @param {WarningItem} warning - The warning item to spy.
* @returns {WarningSpyData} The created spy data.
*/
function spyWarning (warning) {
// Do not double spy the same warning
if (warning[kWarningSpyData] === null) {
const warningFn = warning[kWarningFn]
warning[kWarningFn] = function (a, b, c) {
const args = []
// since warning always call by fn(a, b, c)
// it need to remove the trailing undefined arguments
if (c) {
args.push(a, b, c)
} else if (b) {
args.push(a, b)
} else if (a) {
args.push(a)
}
warning[kWarningSpyData].calls.push({
arguments: args,
result: warningFn(a, b, c)
})
}
const spyData = {
calls: [],
callCount () {
return spyData.calls.length
},
reset () {
warning.emitted = false
spyData.calls.length = 0
},
restore () {
spyData.reset()
warning[kWarningFn] = warningFn
warning[kWarningSpyData] = null
}
}
warning[kWarningSpyData] = spyData
}

return warning[kWarningSpyData]
}

/**
* Creates a deprecation warning item.
* @function
Expand Down Expand Up @@ -62,21 +124,24 @@ function createWarning ({ name, code, message, unlimited = false } = {}) {

code = code.toUpperCase()

let warningContainer = {
[name]: function (a, b, c) {
const warningFn = unlimited === true
? function (a, b, c) {
warning.emitted = true
process.emitWarning(warning.format(a, b, c), warning.name, warning.code)
return true
}
: function (a, b, c) {
if (warning.emitted === true && warning.unlimited !== true) {
return
return false
}
warning.emitted = true
process.emitWarning(warning.format(a, b, c), warning.name, warning.code)
return true
}
}
if (unlimited) {
warningContainer = {
[name]: function (a, b, c) {
warning.emitted = true
process.emitWarning(warning.format(a, b, c), warning.name, warning.code)
}

const warningContainer = {
[name]: function (a, b, c) {
return warning[kWarningFn](a, b, c)
}
}

Expand All @@ -86,14 +151,16 @@ function createWarning ({ name, code, message, unlimited = false } = {}) {
warning.message = message
warning.unlimited = unlimited
warning.code = code
warning[kWarningFn] = warningFn
warning[kWarningSpyData] = null

/**
* Formats the warning message.
* @param {*} [a] Possible message interpolation value.
* @param {*} [b] Possible message interpolation value.
* @param {*} [c] Possible message interpolation value.
* @returns {string} The formatted warning message.
*/
* Formats the warning message.
* @param {*} [a] Possible message interpolation value.
* @param {*} [b] Possible message interpolation value.
* @param {*} [c] Possible message interpolation value.
* @returns {string} The formatted warning message.
*/
warning.format = function (a, b, c) {
let formatted
if (a && b && c) {
Expand All @@ -116,9 +183,10 @@ function createWarning ({ name, code, message, unlimited = false } = {}) {
* @namespace
* @property {function} createWarning - Creates a warning item.
* @property {function} createDeprecation - Creates a deprecation warning item.
* @property {function} spyWarning - Spy a warning item.
* @property {ProcessWarning} processWarning - Represents the process warning functionality.
*/
const out = { createWarning, createDeprecation }
const out = { createWarning, createDeprecation, spyWarning }
module.exports = out
module.exports.default = out
module.exports.processWarning = out
6 changes: 3 additions & 3 deletions test/emit-once-only.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { createWarning } = require('..')
const { withResolvers } = require('./promise')

test('emit should emit a given code only once', t => {
t.plan(4)
t.plan(6)

const { promise, resolve } = withResolvers()

Expand All @@ -22,8 +22,8 @@ test('emit should emit a given code only once', t => {
code: 'CODE',
message: 'Hello world'
})
warn()
warn()
t.assert.strictEqual(warn(), true)
t.assert.strictEqual(warn(), false)
setImmediate(() => {
process.removeListener('warning', onWarning)
resolve()
Expand Down
8 changes: 4 additions & 4 deletions test/emit-reset.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { createWarning } = require('../')
const { withResolvers } = require('./promise')

test('a limited warning can be re-set', t => {
t.plan(4)
t.plan(7)

const { promise, resolve } = withResolvers()
let count = 0
Expand All @@ -20,14 +20,14 @@ test('a limited warning can be re-set', t => {
message: 'Hello world'
})

warn()
t.assert.strictEqual(warn(), true)
t.assert.ok(warn.emitted)

warn()
t.assert.strictEqual(warn(), false)
t.assert.ok(warn.emitted)

warn.emitted = false
warn()
t.assert.strictEqual(warn(), true)
t.assert.ok(warn.emitted)

setImmediate(() => {
Expand Down
4 changes: 2 additions & 2 deletions test/emit-unlimited.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { createWarning } = require('..')
const { withResolvers } = require('./promise')

test('emit should emit a given code unlimited times', t => {
t.plan(50)
t.plan(60)

let runs = 0
const expectedRun = []
Expand All @@ -31,7 +31,7 @@ test('emit should emit a given code unlimited times', t => {

for (let i = 0; i < times; i++) {
expectedRun.push(i)
warn()
t.assert.strictEqual(warn(), true)
}
setImmediate(() => {
process.removeListener('warning', onWarning)
Expand Down
Loading
Loading