GeezerVison is showing github.com — test_runner: add assert.register() API · nodejs/node@3143566
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/test.md +23Lines changed: 23 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -1748,6 +1748,29 @@ describe('tests', async () => { | 1748 | 1748 | }); | 1749 | 1749 | ``` | 1750 | 1750 |
| | 1751 | +## `assert` | | 1752 | + | | 1753 | +<!-- YAML | | 1754 | +added: REPLACEME | | 1755 | +--> | | 1756 | + | | 1757 | +An object whose methods are used to configure available assertions on the | | 1758 | +`TestContext` objects in the current process. The methods from `node:assert` | | 1759 | +and snapshot testing functions are available by default. | | 1760 | + | | 1761 | +It is possible to apply the same configuration to all files by placing common | | 1762 | +configuration code in a module | | 1763 | +preloaded with `--require` or `--import`. | | 1764 | + | | 1765 | +### `assert.register(name, fn)` | | 1766 | + | | 1767 | +<!-- YAML | | 1768 | +added: REPLACEME | | 1769 | +--> | | 1770 | + | | 1771 | +Defines a new assertion function with the provided name and function. If an | | 1772 | +assertion already exists with the same name, it is overwritten. | | 1773 | + | 1751 | 1774 | ## `snapshot` | 1752 | 1775 |
| 1753 | 1776 | <!-- YAML | |
Collapse file Copy file name to clipboard +50Lines changed: 50 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | +const { | | 3 | + SafeMap, | | 4 | +} = primordials; | | 5 | +const { | | 6 | + validateFunction, | | 7 | + validateString, | | 8 | +} = require('internal/validators'); | | 9 | +const assert = require('assert'); | | 10 | +const methodsToCopy = [ | | 11 | + 'deepEqual', | | 12 | + 'deepStrictEqual', | | 13 | + 'doesNotMatch', | | 14 | + 'doesNotReject', | | 15 | + 'doesNotThrow', | | 16 | + 'equal', | | 17 | + 'fail', | | 18 | + 'ifError', | | 19 | + 'match', | | 20 | + 'notDeepEqual', | | 21 | + 'notDeepStrictEqual', | | 22 | + 'notEqual', | | 23 | + 'notStrictEqual', | | 24 | + 'partialDeepStrictEqual', | | 25 | + 'rejects', | | 26 | + 'strictEqual', | | 27 | + 'throws', | | 28 | +]; | | 29 | +let assertMap; | | 30 | + | | 31 | +function getAssertionMap() { | | 32 | + if (assertMap === undefined) { | | 33 | + assertMap = new SafeMap(); | | 34 | + | | 35 | + for (let i = 0; i < methodsToCopy.length; i++) { | | 36 | + assertMap.set(methodsToCopy[i], assert[methodsToCopy[i]]); | | 37 | + } | | 38 | + } | | 39 | + | | 40 | + return assertMap; | | 41 | +} | | 42 | + | | 43 | +function register(name, fn) { | | 44 | + validateString(name, 'name'); | | 45 | + validateFunction(fn, 'fn'); | | 46 | + const map = getAssertionMap(); | | 47 | + map.set(name, fn); | | 48 | +} | | 49 | + | | 50 | +module.exports = { getAssertionMap, register }; |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/test_runner/test.js +18-34Lines changed: 18 additions & 34 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -100,34 +100,15 @@ function lazyFindSourceMap(file) { | 100 | 100 |
| 101 | 101 | function lazyAssertObject(harness) { | 102 | 102 | if (assertObj === undefined) { | 103 | | - assertObj = new SafeMap(); | 104 | | - const assert = require('assert'); | 105 | | - const { SnapshotManager } = require('internal/test_runner/snapshot'); | 106 | | - const methodsToCopy = [ | 107 | | - 'deepEqual', | 108 | | - 'deepStrictEqual', | 109 | | - 'doesNotMatch', | 110 | | - 'doesNotReject', | 111 | | - 'doesNotThrow', | 112 | | - 'equal', | 113 | | - 'fail', | 114 | | - 'ifError', | 115 | | - 'match', | 116 | | - 'notDeepEqual', | 117 | | - 'notDeepStrictEqual', | 118 | | - 'notEqual', | 119 | | - 'notStrictEqual', | 120 | | - 'partialDeepStrictEqual', | 121 | | - 'rejects', | 122 | | - 'strictEqual', | 123 | | - 'throws', | 124 | | - ]; | 125 | | - for (let i = 0; i < methodsToCopy.length; i++) { | 126 | | - assertObj.set(methodsToCopy[i], assert[methodsToCopy[i]]); | 127 | | - } | | 103 | + const { getAssertionMap } = require('internal/test_runner/assert'); | | 104 | + | | 105 | + assertObj = getAssertionMap(); | | 106 | + if (!assertObj.has('snapshot')) { | | 107 | + const { SnapshotManager } = require('internal/test_runner/snapshot'); | 128 | 108 |
| 129 | | - harness.snapshotManager = new SnapshotManager(harness.config.updateSnapshots); | 130 | | - assertObj.set('snapshot', harness.snapshotManager.createAssert()); | | 109 | + harness.snapshotManager = new SnapshotManager(harness.config.updateSnapshots); | | 110 | + assertObj.set('snapshot', harness.snapshotManager.createAssert()); | | 111 | + } | 131 | 112 | } | 132 | 113 | return assertObj; | 133 | 114 | } | @@ -264,15 +245,18 @@ class TestContext { | 264 | 245 | }; | 265 | 246 | }); | 266 | 247 |
| 267 | | - // This is a hack. It allows the innerOk function to collect the stacktrace from the correct starting point. | 268 | | - function ok(...args) { | 269 | | - if (plan !== null) { | 270 | | - plan.actual++; | | 248 | + if (!map.has('ok')) { | | 249 | + // This is a hack. It allows the innerOk function to collect the | | 250 | + // stacktrace from the correct starting point. | | 251 | + function ok(...args) { | | 252 | + if (plan !== null) { | | 253 | + plan.actual++; | | 254 | + } | | 255 | + innerOk(ok, args.length, ...args); | 271 | 256 | } | 272 | | - innerOk(ok, args.length, ...args); | 273 | | - } | 274 | 257 |
| 275 | | - assert.ok = ok; | | 258 | + assert.ok = ok; | | 259 | + } | 276 | 260 | } | 277 | 261 | return this.#assert; | 278 | 262 | } | |
Collapse file Copy file name to clipboardExpand all lines: lib/test.js +12Lines changed: 12 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -61,3 +61,15 @@ ObjectDefineProperty(module.exports, 'snapshot', { | 61 | 61 | return lazySnapshot; | 62 | 62 | }, | 63 | 63 | }); | | 64 | + | | 65 | +ObjectDefineProperty(module.exports, 'assert', { | | 66 | + __proto__: null, | | 67 | + configurable: true, | | 68 | + enumerable: true, | | 69 | + get() { | | 70 | + const { register } = require('internal/test_runner/assert'); | | 71 | + const assert = { __proto__: null, register }; | | 72 | + ObjectDefineProperty(module.exports, 'assert', assert); | | 73 | + return assert; | | 74 | + }, | | 75 | +}); |
Collapse file Copy file name to clipboard +63Lines changed: 63 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | +require('../common'); | | 3 | +const assert = require('node:assert'); | | 4 | +const { test, assert: testAssertions } = require('node:test'); | | 5 | + | | 6 | +testAssertions.register('isOdd', (n) => { | | 7 | + assert.strictEqual(n % 2, 1); | | 8 | +}); | | 9 | + | | 10 | +testAssertions.register('ok', () => { | | 11 | + return 'ok'; | | 12 | +}); | | 13 | + | | 14 | +testAssertions.register('snapshot', () => { | | 15 | + return 'snapshot'; | | 16 | +}); | | 17 | + | | 18 | +testAssertions.register('deepStrictEqual', () => { | | 19 | + return 'deepStrictEqual'; | | 20 | +}); | | 21 | + | | 22 | +testAssertions.register('context', function() { | | 23 | + return this; | | 24 | +}); | | 25 | + | | 26 | +test('throws if name is not a string', () => { | | 27 | + assert.throws(() => { | | 28 | + testAssertions.register(5); | | 29 | + }, { | | 30 | + code: 'ERR_INVALID_ARG_TYPE', | | 31 | + message: 'The "name" argument must be of type string. Received type number (5)' | | 32 | + }); | | 33 | +}); | | 34 | + | | 35 | +test('throws if fn is not a function', () => { | | 36 | + assert.throws(() => { | | 37 | + testAssertions.register('foo', 5); | | 38 | + }, { | | 39 | + code: 'ERR_INVALID_ARG_TYPE', | | 40 | + message: 'The "fn" argument must be of type function. Received type number (5)' | | 41 | + }); | | 42 | +}); | | 43 | + | | 44 | +test('invokes a custom assertion as part of the test plan', (t) => { | | 45 | + t.plan(2); | | 46 | + t.assert.isOdd(5); | | 47 | + assert.throws(() => { | | 48 | + t.assert.isOdd(4); | | 49 | + }, { | | 50 | + code: 'ERR_ASSERTION', | | 51 | + message: /Expected values to be strictly equal/ | | 52 | + }); | | 53 | +}); | | 54 | + | | 55 | +test('can override existing assertions', (t) => { | | 56 | + assert.strictEqual(t.assert.ok(), 'ok'); | | 57 | + assert.strictEqual(t.assert.snapshot(), 'snapshot'); | | 58 | + assert.strictEqual(t.assert.deepStrictEqual(), 'deepStrictEqual'); | | 59 | +}); | | 60 | + | | 61 | +test('"this" is set to the TestContext', (t) => { | | 62 | + assert.strictEqual(t.assert.context(), t); | | 63 | +}); |
|