Skip to content

Asymetric asserts #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
53 changes: 51 additions & 2 deletions lib/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@
isPartialStrictEqual = comparison.isPartialStrictEqual;
}

function compareAsymetric(actual, expected) {
const isAsymetric = expected?.type === Symbol.for('blah');

Check failure on line 77 in lib/assert.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { SymbolFor } = primordials;` instead of the global

if (!isAsymetric && typeof expected === 'object') {
for (const key of ObjectKeys(expected)) {
if (!compareAsymetric(actual[key], expected[key])) {
return false;
}
}

return true;
}

return (isAsymetric && expected.match(actual));
}

let warned = false;

// The assert module provides functions that throw
Expand Down Expand Up @@ -171,7 +187,7 @@
throw new ERR_MISSING_ARGS('actual', 'expected');
}
// eslint-disable-next-line eqeqeq
if (actual != expected && (!NumberIsNaN(actual) || !NumberIsNaN(expected))) {
if (!compareAsymetric(actual, expected) && (actual != expected && (!NumberIsNaN(actual) || !NumberIsNaN(expected)))) {
innerFail({
actual,
expected,
Expand Down Expand Up @@ -313,7 +329,7 @@
if (arguments.length < 2) {
throw new ERR_MISSING_ARGS('actual', 'expected');
}
if (!ObjectIs(actual, expected)) {
if (!ObjectIs(actual, expected) || !compareAsymetric(actual, expected)) {
innerFail({
actual,
expected,
Expand All @@ -324,6 +340,39 @@
}
};

assert.asymetric = {
any: function any(sample) {
return {
type: Symbol.for('blah'),

Check failure on line 346 in lib/assert.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { SymbolFor } = primordials;` instead of the global
match: function match(other) {
if (sample === String) {
return typeof other === 'string' || other instanceof String;
}

if (sample === Number) {

Check failure on line 352 in lib/assert.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { Number } = primordials;` instead of the global
return typeof other === 'number' || other instanceof Number;

Check failure on line 353 in lib/assert.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { Number } = primordials;` instead of the global
}

if (sample === Object) {

Check failure on line 356 in lib/assert.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { Object } = primordials;` instead of the global
return typeof other === 'number' || other instanceof Object;

Check failure on line 357 in lib/assert.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { Object } = primordials;` instead of the global
}

return other instanceof sample;
},
getExpectedType() {
if (sample === String) return 'String';
if (sample === Number) return 'Number';

Check failure on line 364 in lib/assert.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { Number } = primordials;` instead of the global
if (sample === Object) return 'Object';

Check failure on line 365 in lib/assert.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Use `const { Object } = primordials;` instead of the global

https://github.com/jestjs/jest/blob/611d1a4ba0008d67b5dcda485177f0813b2b573e/packages/expect/src/asymmetricMatchers.ts#L27

Check failure on line 367 in lib/assert.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Expected exception block, space or tab after '//' in comment

Check failure on line 367 in lib/assert.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

'https:' is defined but never used
return sample.toString();
},

toString() { return `Any<${this.getExpectedType()}>`; }
};
}
};

/**
* The strict non-equivalence assertion tests for any strict inequality.
* @param {any} actual
Expand Down
27 changes: 26 additions & 1 deletion lib/internal/assert/assertion_error.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,20 @@ function isSimpleDiff(actual, inspectedActual, expected, inspectedExpected) {
function createErrDiff(actual, expected, operator, customMessage) {
operator = checkOperator(actual, expected, operator);

const newExpected = Object.keys(expected).reduce((acc, key) => {
const val = expected[key];
if (val?.type === Symbol.for('blah')) {
acc[key] = val.toString();
return acc;
}
acc[key] = expected[key];
return acc;
}, {});

let skipped = false;
let message = '';
const inspectedActual = inspectValue(actual);
const inspectedExpected = inspectValue(expected);
const inspectedExpected = inspectValue(newExpected);
const inspectedSplitActual = StringPrototypeSplit(inspectedActual, '\n');
const inspectedSplitExpected = StringPrototypeSplit(inspectedExpected, '\n');
const showSimpleDiff = isSimpleDiff(actual, inspectedSplitActual, expected, inspectedSplitExpected);
Expand Down Expand Up @@ -242,6 +252,19 @@ function addEllipsis(string) {
return string;
}

function transformAsymetric(value) {
let newValue = value;
if (value?.type === Symbol.for('blah')) {
newValue = value.toString();
} else if (typeof value === 'object') {
for (const key in value) {
newValue[key] = transformAsymetric(value[key]);
}
}

return newValue;
}

class AssertionError extends Error {
constructor(options) {
validateObject(options, 'options');
Expand All @@ -258,6 +281,8 @@ class AssertionError extends Error {
expected,
} = options;

expected = transformAsymetric(expected);

const limit = Error.stackTraceLimit;
if (isErrorStackTraceLimitWritable()) Error.stackTraceLimit = 0;

Expand Down
5 changes: 5 additions & 0 deletions lib/internal/util/comparisons.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ function innerDeepEqual(val1, val2, mode, memos) {
return val1 !== 0 || ObjectIs(val1, val2) || mode === kLoose;
}

// TODO: prop "type" has to be a symbol itself
if (val2?.type === Symbol.for('blah')) {
return val2.match(val1);
}

// Check more closely if val1 and val2 are equal.
if (mode !== kLoose) {
if (typeof val1 === 'number') {
Expand Down
44 changes: 44 additions & 0 deletions test/parallel/test-assert-asymetric-matchers.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import assert from 'node:assert';

assert.equal('Node.js', assert.asymetric.any(String));
assert.equal(new String('Node.js'), assert.asymetric.any(String));
assert.equal(123, assert.asymetric.any(Number));
assert.equal(4.56, assert.asymetric.any(Number));
assert.equal(new Number(123), assert.asymetric.any(Number));
assert.equal({name: 'foo'}, { name: assert.asymetric.any(Number) });
assert.deepStrictEqual({
a: {
b: {
c: 42,
d: 'edy',
e: {
f: 'foo',
g: 'bar'
}
}
}
}, {
a: {
b: {
c: assert.asymetric.any(Number),
d: assert.asymetric.any(String),
e: assert.asymetric.any(Object)
}
}
});

// assert.equal(123, assert.asymetric.any(String));
// assert.equal('123', assert.asymetric.any(Number));
// assert.strictEqual({name: 'foo'}, { name: assert.asymetric.any(String) });
// assert.equal({name: 'bar'}, { name: assert.asymetric.any(Number) });
// assert.throws(() => {
// assert.deepStrictEqual(
// {
// name: 'foo',
// },
// {
// name: assert.asymetric.any(Number),
// }
// );
// });

Loading