| 'use strict'; |
| |
| require('../common'); |
| const assert = require('assert'); |
| // eslint-disable-next-line node-core/must-call-assert |
| const { Assert } = require('assert'); |
| const { inspect } = require('util'); |
| const { test } = require('node:test'); |
| |
| // Disable colored output to prevent color codes from breaking assertion |
| // message comparisons. This should only be an issue when process.stdout |
| // is a TTY. |
| if (process.stdout.isTTY) { |
| process.env.NODE_DISABLE_COLORS = '1'; |
| } |
| |
| test('Assert constructor requires new', () => { |
| assert.throws(() => Assert(), { |
| code: 'ERR_CONSTRUCT_CALL_REQUIRED', |
| name: 'TypeError', |
| }); |
| }); |
| |
| test('Assert class non strict', () => { |
| const assertInstance = new Assert({ diff: undefined, strict: false }); |
| |
| assertInstance.ok( |
| assert.AssertionError.prototype instanceof Error, |
| 'assert.AssertionError instanceof Error' |
| ); |
| assert.strictEqual(typeof assertInstance.ok, 'function'); |
| assert.strictEqual(assertInstance.ok.strictEqual, undefined); |
| assert.strictEqual(typeof assertInstance.strictEqual, 'function'); |
| assertInstance.ok(true); |
| assertInstance.throws( |
| () => { |
| assertInstance.fail(); |
| }, |
| { |
| code: 'ERR_ASSERTION', |
| name: 'AssertionError', |
| message: 'Failed', |
| operator: 'fail', |
| actual: undefined, |
| expected: undefined, |
| generatedMessage: true, |
| stack: /Failed/, |
| } |
| ); |
| assertInstance.equal(undefined, undefined); |
| assertInstance.equal(null, undefined); |
| assertInstance.equal(2, '2'); |
| assertInstance.notEqual(true, false); |
| assertInstance.throws(() => assertInstance.deepEqual(/a/), { |
| code: 'ERR_MISSING_ARGS', |
| }); |
| assertInstance.throws(() => assertInstance.notDeepEqual('test'), { |
| code: 'ERR_MISSING_ARGS', |
| }); |
| assertInstance.notStrictEqual(2, '2'); |
| assertInstance.throws( |
| () => assertInstance.strictEqual(2, '2'), |
| assertInstance.AssertionError, |
| "strictEqual(2, '2')" |
| ); |
| assertInstance.throws( |
| () => { |
| assertInstance.partialDeepStrictEqual( |
| { a: true }, |
| { a: false }, |
| 'custom message' |
| ); |
| }, |
| { |
| code: 'ERR_ASSERTION', |
| name: 'AssertionError', |
| message: |
| 'custom message\n+ actual - expected\n\n {\n+ a: true\n- a: false\n }\n', |
| } |
| ); |
| assertInstance.throws(() => assertInstance.match(/abc/, 'string'), { |
| code: 'ERR_INVALID_ARG_TYPE', |
| message: |
| 'The "regexp" argument must be an instance of RegExp. ' + |
| "Received type string ('string')", |
| }); |
| assertInstance.throws(() => assertInstance.doesNotMatch(/abc/, 'string'), { |
| code: 'ERR_INVALID_ARG_TYPE', |
| message: |
| 'The "regexp" argument must be an instance of RegExp. ' + |
| "Received type string ('string')", |
| }); |
| |
| /* eslint-disable no-restricted-syntax */ |
| { |
| function thrower(errorConstructor) { |
| throw new errorConstructor({}); |
| } |
| |
| let threw = false; |
| try { |
| assertInstance.doesNotThrow( |
| () => thrower(TypeError), |
| assertInstance.AssertionError |
| ); |
| } catch (e) { |
| threw = true; |
| assertInstance.ok(e instanceof TypeError); |
| } |
| assertInstance.ok( |
| threw, |
| 'assertInstance.doesNotThrow with an explicit error is eating extra errors' |
| ); |
| } |
| { |
| let threw = false; |
| const rangeError = new RangeError('my range'); |
| |
| try { |
| assertInstance.doesNotThrow( |
| () => { |
| throw new TypeError('wrong type'); |
| }, |
| TypeError, |
| rangeError |
| ); |
| } catch (e) { |
| threw = true; |
| assertInstance.ok(e.message.includes(rangeError.message)); |
| assertInstance.ok(e instanceof assertInstance.AssertionError); |
| assertInstance.ok(!e.stack.includes('doesNotThrow'), e); |
| } |
| assertInstance.ok(threw); |
| } |
| /* eslint-enable no-restricted-syntax */ |
| }); |
| |
| test('Assert class strict', () => { |
| const assertInstance = new Assert(); |
| |
| assertInstance.equal(assertInstance.equal, assertInstance.strictEqual); |
| assertInstance.equal( |
| assertInstance.deepEqual, |
| assertInstance.deepStrictEqual |
| ); |
| assertInstance.equal(assertInstance.notEqual, assertInstance.notStrictEqual); |
| assertInstance.equal( |
| assertInstance.notDeepEqual, |
| assertInstance.notDeepStrictEqual |
| ); |
| }); |
| |
| test('Assert class with invalid diff option', () => { |
| assert.throws(() => new Assert({ diff: 'invalid' }), { |
| code: 'ERR_INVALID_ARG_VALUE', |
| name: 'TypeError', |
| message: "The property 'options.diff' must be one of: 'simple', 'full'. Received 'invalid'", |
| }); |
| }); |
| |
| const longLinesOfAs = 'A\n'.repeat(100); |
| const longLinesOFBs = 'B\n'.repeat(100); |
| const truncatedAs = 'A\\n'.repeat(10) + '...'; |
| const truncatedBs = 'B\\n'.repeat(10) + '...'; |
| |
| const longStringOfAs = 'A'.repeat(10_000); |
| const longStringOfBs = 'B'.repeat(10_000); |
| |
| const longLinesOfAsWithEllipsis = longStringOfAs.substring(0, 9_488) + '...'; |
| const longLinesOFBsWithEllipsis = longStringOfBs.substring(0, 9_488) + '...'; |
| test('Assert class non strict with full diff', () => { |
| const assertInstance = new Assert({ diff: 'full', strict: false }); |
| |
| // long strings |
| { |
| assertInstance.throws( |
| () => { |
| assertInstance.strictEqual(longStringOfAs, longStringOfBs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'strictEqual'); |
| assertInstance.strictEqual(err.diff, 'full'); |
| assertInstance.strictEqual(err.actual, longStringOfAs); |
| assertInstance.strictEqual(err.expected, longStringOfBs); |
| |
| assertInstance.strictEqual( |
| err.message, |
| `Expected values to be strictly equal:\n+ actual - expected\n\n` + |
| `+ '${longStringOfAs}'\n- '${longStringOfBs}'\n` |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`actual: '${longLinesOfAsWithEllipsis}'`) |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`expected: '${longLinesOFBsWithEllipsis}'`) |
| ); |
| return true; |
| } |
| ); |
| |
| assertInstance.throws( |
| () => { |
| assertInstance.notStrictEqual(longStringOfAs, longStringOfAs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'notStrictEqual'); |
| assertInstance.strictEqual(err.diff, 'full'); |
| assertInstance.strictEqual(err.actual, longStringOfAs); |
| assertInstance.strictEqual(err.expected, longStringOfAs); |
| |
| assertInstance.strictEqual( |
| err.message, |
| `Expected "actual" to be strictly unequal to:\n\n` + |
| `'${longStringOfAs}'` |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`actual: '${longLinesOfAsWithEllipsis}'`) |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`expected: '${longLinesOfAsWithEllipsis}'`) |
| ); |
| return true; |
| } |
| ); |
| |
| assertInstance.throws( |
| () => { |
| assertInstance.deepEqual(longStringOfAs, longStringOfBs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'deepEqual'); |
| assertInstance.strictEqual(err.diff, 'full'); |
| assertInstance.strictEqual(err.actual, longStringOfAs); |
| assertInstance.strictEqual(err.expected, longStringOfBs); |
| |
| assertInstance.strictEqual( |
| err.message, |
| `Expected values to be loosely deep-equal:\n\n` + |
| `'${longStringOfAs}'\n\nshould loosely deep-equal\n\n'${longStringOfBs}'` |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`actual: '${longLinesOfAsWithEllipsis}'`) |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`expected: '${longLinesOFBsWithEllipsis}'`) |
| ); |
| return true; |
| } |
| ); |
| } |
| |
| // long lines |
| { |
| assertInstance.throws( |
| () => { |
| assertInstance.strictEqual(longLinesOfAs, longLinesOFBs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'strictEqual'); |
| assertInstance.strictEqual(err.diff, 'full'); |
| assertInstance.strictEqual(err.actual, longLinesOfAs); |
| assertInstance.strictEqual(err.expected, longLinesOFBs); |
| |
| assertInstance.strictEqual(err.message.split('\n').length, 204); |
| assertInstance.strictEqual(err.actual.split('\n').length, 101); |
| assertInstance.ok( |
| err.message.includes('Expected values to be strictly equal') |
| ); |
| assertInstance.ok(inspect(err).includes(`actual: '${truncatedAs}`)); |
| assertInstance.ok(inspect(err).includes(`expected: '${truncatedBs}`)); |
| return true; |
| } |
| ); |
| |
| assertInstance.throws( |
| () => { |
| assertInstance.notStrictEqual(longLinesOfAs, longLinesOfAs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'notStrictEqual'); |
| assertInstance.strictEqual(err.diff, 'full'); |
| assertInstance.strictEqual(err.actual, longLinesOfAs); |
| assertInstance.strictEqual(err.expected, longLinesOfAs); |
| |
| assertInstance.strictEqual(err.message.split('\n').length, 103); |
| assertInstance.strictEqual(err.actual.split('\n').length, 101); |
| assertInstance.ok( |
| err.message.includes(`Expected "actual" to be strictly unequal to:`) |
| ); |
| assertInstance.ok(inspect(err).includes(`actual: '${truncatedAs}`)); |
| assertInstance.ok(inspect(err).includes(`expected: '${truncatedAs}`)); |
| return true; |
| } |
| ); |
| |
| assertInstance.throws( |
| () => { |
| assertInstance.deepEqual(longLinesOfAs, longLinesOFBs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'deepEqual'); |
| assertInstance.strictEqual(err.diff, 'full'); |
| assertInstance.strictEqual(err.actual, longLinesOfAs); |
| assertInstance.strictEqual(err.expected, longLinesOFBs); |
| |
| assertInstance.strictEqual(err.message.split('\n').length, 205); |
| assertInstance.strictEqual(err.actual.split('\n').length, 101); |
| assertInstance.ok( |
| err.message.includes(`Expected values to be loosely deep-equal:`) |
| ); |
| assertInstance.ok(inspect(err).includes(`actual: '${truncatedAs}`)); |
| assertInstance.ok(inspect(err).includes(`expected: '${truncatedBs}`)); |
| return true; |
| } |
| ); |
| } |
| }); |
| |
| test('Assert class non strict with simple diff', () => { |
| const assertInstance = new Assert({ diff: 'simple', strict: false }); |
| |
| // long strings |
| { |
| assertInstance.throws( |
| () => { |
| assertInstance.strictEqual(longStringOfAs, longStringOfBs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'strictEqual'); |
| assertInstance.strictEqual(err.diff, 'simple'); |
| assertInstance.strictEqual(err.actual, longStringOfAs); |
| assertInstance.strictEqual(err.expected, longStringOfBs); |
| |
| assertInstance.strictEqual( |
| err.message, |
| `Expected values to be strictly equal:\n+ actual - expected\n\n` + |
| `+ '${longStringOfAs}'\n- '${longStringOfBs}'\n` |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`actual: '${longLinesOfAsWithEllipsis}'`) |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`expected: '${longLinesOFBsWithEllipsis}'`) |
| ); |
| return true; |
| } |
| ); |
| |
| assertInstance.throws( |
| () => { |
| assertInstance.notStrictEqual(longStringOfAs, longStringOfAs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'notStrictEqual'); |
| assertInstance.strictEqual(err.diff, 'simple'); |
| assertInstance.strictEqual(err.actual, longStringOfAs); |
| assertInstance.strictEqual(err.expected, longStringOfAs); |
| |
| assertInstance.strictEqual( |
| err.message, |
| `Expected "actual" to be strictly unequal to:\n\n` + |
| `'${longStringOfAs}'` |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`actual: '${longLinesOfAsWithEllipsis}'`) |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`expected: '${longLinesOfAsWithEllipsis}'`) |
| ); |
| return true; |
| } |
| ); |
| |
| assertInstance.throws( |
| () => { |
| assertInstance.deepEqual(longStringOfAs, longStringOfBs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'deepEqual'); |
| assertInstance.strictEqual(err.diff, 'simple'); |
| assertInstance.strictEqual(err.actual, longStringOfAs); |
| assertInstance.strictEqual(err.expected, longStringOfBs); |
| |
| assertInstance.strictEqual( |
| err.message, |
| `Expected values to be loosely deep-equal:\n\n` + |
| `'${ |
| longStringOfAs.substring(0, 508) + '...' |
| }\n\nshould loosely deep-equal\n\n'${ |
| longStringOfBs.substring(0, 508) + '...' |
| }` |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`actual: '${longLinesOfAsWithEllipsis}'`) |
| ); |
| assertInstance.ok( |
| inspect(err).includes(`expected: '${longLinesOFBsWithEllipsis}'`) |
| ); |
| return true; |
| } |
| ); |
| } |
| |
| // long lines |
| { |
| assertInstance.throws( |
| () => { |
| assertInstance.strictEqual(longLinesOfAs, longLinesOFBs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'strictEqual'); |
| assertInstance.strictEqual(err.diff, 'simple'); |
| assertInstance.strictEqual(err.actual, longLinesOfAs); |
| assertInstance.strictEqual(err.expected, longLinesOFBs); |
| assertInstance.strictEqual(err.message.split('\n').length, 204); |
| assertInstance.strictEqual(err.actual.split('\n').length, 101); |
| |
| assertInstance.ok( |
| err.message.includes('Expected values to be strictly equal') |
| ); |
| assertInstance.ok(inspect(err).includes(`actual: '${truncatedAs}`)); |
| assertInstance.ok(inspect(err).includes(`expected: '${truncatedBs}`)); |
| return true; |
| } |
| ); |
| |
| assertInstance.throws( |
| () => { |
| assertInstance.notStrictEqual(longLinesOfAs, longLinesOfAs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'notStrictEqual'); |
| assertInstance.strictEqual(err.diff, 'simple'); |
| assertInstance.strictEqual(err.actual, longLinesOfAs); |
| assertInstance.strictEqual(err.expected, longLinesOfAs); |
| |
| assertInstance.strictEqual(err.message.split('\n').length, 50); |
| assertInstance.strictEqual(err.actual.split('\n').length, 101); |
| assertInstance.ok( |
| err.message.includes(`Expected "actual" to be strictly unequal to:`) |
| ); |
| assertInstance.ok(inspect(err).includes(`actual: '${truncatedAs}`)); |
| assertInstance.ok(inspect(err).includes(`expected: '${truncatedAs}`)); |
| return true; |
| } |
| ); |
| |
| assertInstance.throws( |
| () => { |
| assertInstance.deepEqual(longLinesOfAs, longLinesOFBs); |
| }, |
| (err) => { |
| assertInstance.strictEqual(err.code, 'ERR_ASSERTION'); |
| assertInstance.strictEqual(err.operator, 'deepEqual'); |
| assertInstance.strictEqual(err.diff, 'simple'); |
| assertInstance.strictEqual(err.actual, longLinesOfAs); |
| assertInstance.strictEqual(err.expected, longLinesOFBs); |
| |
| assertInstance.strictEqual(err.message.split('\n').length, 109); |
| assertInstance.strictEqual(err.actual.split('\n').length, 101); |
| assertInstance.ok( |
| err.message.includes(`Expected values to be loosely deep-equal:`) |
| ); |
| assertInstance.ok(inspect(err).includes(`actual: '${truncatedAs}`)); |
| assertInstance.ok(inspect(err).includes(`expected: '${truncatedBs}`)); |
| return true; |
| } |
| ); |
| } |
| }); |
| |
| // Shared setup for skipPrototype tests |
| { |
| const message = 'Expected values to be strictly deep-equal:\n' + |
| '+ actual - expected\n' + |
| '\n' + |
| ' [\n' + |
| ' 1,\n' + |
| ' 2,\n' + |
| ' 3,\n' + |
| ' 4,\n' + |
| ' 5,\n' + |
| '+ 6,\n' + |
| '- 9,\n' + |
| ' 7\n' + |
| ' ]\n'; |
| |
| function CoolClass(name) { this.name = name; } |
| |
| function AwesomeClass(name) { this.name = name; } |
| |
| class Modern { constructor(value) { this.value = value; } } |
| class Legacy { constructor(value) { this.value = value; } } |
| |
| const cool = new CoolClass('Assert is inspiring'); |
| const awesome = new AwesomeClass('Assert is inspiring'); |
| const modern = new Modern(42); |
| const legacy = new Legacy(42); |
| |
| test('Assert class strict with skipPrototype', () => { |
| const assertInstance = new Assert({ skipPrototype: true }); |
| |
| assert.throws( |
| () => assertInstance.deepEqual([1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 9, 7]), |
| { message } |
| ); |
| |
| assertInstance.deepEqual(cool, awesome); |
| assertInstance.deepStrictEqual(cool, awesome); |
| assertInstance.deepEqual(modern, legacy); |
| assertInstance.deepStrictEqual(modern, legacy); |
| |
| const cool2 = new CoolClass('Soooo coooool'); |
| assert.throws( |
| () => assertInstance.deepStrictEqual(cool, cool2), |
| { code: 'ERR_ASSERTION' } |
| ); |
| |
| const nested1 = { obj: new CoolClass('test'), arr: [1, 2, 3] }; |
| const nested2 = { obj: new AwesomeClass('test'), arr: [1, 2, 3] }; |
| assertInstance.deepStrictEqual(nested1, nested2); |
| |
| const arr = new Uint8Array([1, 2, 3]); |
| const buf = Buffer.from([1, 2, 3]); |
| assertInstance.deepStrictEqual(arr, buf); |
| }); |
| |
| test('Assert class non strict with skipPrototype', () => { |
| const assertInstance = new Assert({ strict: false, skipPrototype: true }); |
| |
| assert.throws( |
| () => assertInstance.deepStrictEqual([1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 9, 7]), |
| { message } |
| ); |
| |
| assertInstance.deepStrictEqual(cool, awesome); |
| assertInstance.deepStrictEqual(modern, legacy); |
| }); |
| |
| test('Assert class skipPrototype with complex objects', () => { |
| const assertInstance = new Assert({ skipPrototype: true }); |
| |
| function ComplexAwesomeClass(name, age) { |
| this.name = name; |
| this.age = age; |
| this.settings = { |
| theme: 'dark', |
| lang: 'en' |
| }; |
| } |
| |
| function ComplexCoolClass(name, age) { |
| this.name = name; |
| this.age = age; |
| this.settings = { |
| theme: 'dark', |
| lang: 'en' |
| }; |
| } |
| |
| const awesome1 = new ComplexAwesomeClass('Foo', 30); |
| const cool1 = new ComplexCoolClass('Foo', 30); |
| |
| assertInstance.deepStrictEqual(awesome1, cool1); |
| |
| const cool2 = new ComplexCoolClass('Foo', 30); |
| cool2.settings.theme = 'light'; |
| |
| assert.throws( |
| () => assertInstance.deepStrictEqual(awesome1, cool2), |
| { code: 'ERR_ASSERTION' } |
| ); |
| }); |
| |
| test('Assert class skipPrototype with arrays and special objects', () => { |
| const assertInstance = new Assert({ skipPrototype: true }); |
| |
| const arr1 = [1, 2, 3]; |
| const arr2 = new Array(1, 2, 3); |
| assertInstance.deepStrictEqual(arr1, arr2); |
| |
| const date1 = new Date('2023-01-01'); |
| const date2 = new Date('2023-01-01'); |
| assertInstance.deepStrictEqual(date1, date2); |
| |
| const regex1 = /test/g; |
| const regex2 = new RegExp('test', 'g'); |
| assertInstance.deepStrictEqual(regex1, regex2); |
| |
| const date3 = new Date('2023-01-02'); |
| assert.throws( |
| () => assertInstance.deepStrictEqual(date1, date3), |
| { code: 'ERR_ASSERTION' } |
| ); |
| }); |
| |
| test('Assert class skipPrototype with notDeepStrictEqual', () => { |
| const assertInstance = new Assert({ skipPrototype: true }); |
| |
| assert.throws( |
| () => assertInstance.notDeepStrictEqual(cool, awesome), |
| { code: 'ERR_ASSERTION' } |
| ); |
| |
| const notAwesome = new AwesomeClass('Not so awesome'); |
| assertInstance.notDeepStrictEqual(cool, notAwesome); |
| |
| const defaultAssertInstance = new Assert({ skipPrototype: false }); |
| defaultAssertInstance.notDeepStrictEqual(cool, awesome); |
| }); |
| |
| test('Assert class skipPrototype with mixed types', () => { |
| const assertInstance = new Assert({ skipPrototype: true }); |
| |
| const obj1 = { value: 42, nested: { prop: 'test' } }; |
| |
| function CustomObj(value, nested) { |
| this.value = value; |
| this.nested = nested; |
| } |
| |
| const obj2 = new CustomObj(42, { prop: 'test' }); |
| assertInstance.deepStrictEqual(obj1, obj2); |
| |
| assert.throws( |
| () => assertInstance.deepStrictEqual({ num: 42 }, { num: '42' }), |
| { code: 'ERR_ASSERTION' } |
| ); |
| }); |
| } |