Skip to content
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

Implicit and naive cloning of comparable objects causes errors #1782

Open
zloirock opened this issue Nov 17, 2021 · 2 comments
Open

Implicit and naive cloning of comparable objects causes errors #1782

zloirock opened this issue Nov 17, 2021 · 2 comments
Labels
Component: Assert Type: Bug Something isn't working right.

Comments

@zloirock
Copy link

const object = { get x() { throw 42 }};
assert.equal(object, object); // => Died on test qunitjs/node-qunit#1: 42
@Krinkle
Copy link
Member

Krinkle commented Sep 5, 2024

I've tested various versions from QUnit 2.0.0 to QUnit 2.22.0 and the following pass everywhere I tested:

QUnit.test('example', function (assert) {
  const object = { get x() { throw 42 }};
  assert.equal(object, object);

  assert.deepEqual(object, object);
});

I'm guessing in the legacy node-qunit wrapper, there's a separate bug and/or separate path to the same bug for the above case.

When the objects are distinct, there is indeed a failure always:

QUnit.test('equal', function (assert) {
  const a = { get x() { throw 42 }};
  const b = { get x() { throw 42 }};

  assert.equal(a, b);
});
Died on test #1: 42
  at get x
  at dump.object
  at dump.parse
  at onLog
  at runLoggingCallbacks

The place where it fails (when testing via an HTML file) is in the HTML Reporter, not in QUnit Core. Specifically, in these two calls:

   dump.parse(details.expected);
   dump.parse(details.actual);

Where the internal QUnit.dump.object() utility is attempting to access and render all keys, without care for accessors.

The good news is that this means the assertion logic in assert.equal() isn't performing unsafe access to these getters. But the HTML Reporter / QUnit.dump isn't yet.

But... the same can't be said for assert.deepEqual (aka QUnit.equiv):

QUnit.test('equal', function (assert) {
  const a = { get x() { throw 42 }};
  const b = { get x() { throw 42 }};

  assert.deepEqual(a, b);
});
Died on test #1: 42
  at get x
  at object
  at typeEquiv
  at innerEquiv
  at equiv
  at assert.deepEqual

This issue is related to #1325 which is also about property getter/accessors.

@Krinkle
Copy link
Member

Krinkle commented Sep 5, 2024

It's worth comparing the behaviour of other test frameworks.

Node.js: https://nodejs.org/api/assert.html

//> $ node --version
v22.5.1
//> $ node
assert = require('assert');
assert.deepEqual({ get x() { return 1; } }, { get x() { return 1; } });
//> OK
// Considers distinct functions with same function body as equal.

assert.deepEqual({ get x() { return 1; } }, { get x() { return 0 + 1; } });
//> OK
// Considers distinct functions with different function body but same return value.

assert.deepEqual({ get x() { return 1; } }, { get x() { return 2; } });
//> Uncaught AssertionError [ERR_ASSERTION]: Expected values to be loosely deep-equal:
//> {
//>  x: [Getter: 1]
//> }
//> should loosely deep-equal
//> {
//>  x: [Getter: 2]
//> }

> assert.deepEqual({ get x() { throw 42; } }, { get x() { throw 42; } });
//> Uncaught 42

So, Node.js currently also doesn't handle it. Like QUnit, the error is left uncaught.

Mocha / Expect.js: https://github.com/Automattic/expect.js

//> $ node
expect = require('expect.js');
expect({ get x() { return 1; } }).to.eql({ get x() { return 1; }});
//> OK

expect({ get x() { return 1; } }).to.eql({ get x() { return 2; }});
//> Uncaught Error: expected { x: [Getter] } to sort of equal { x: [Getter] }
//>  at Assertion.assert
//>  at Assertion.eql 
//> {
//>  actual: { x: [Getter] },
//>  expected: { x: [Getter] },
//>  showDiff: true
//> }

expect({ get x() { throw 42; } }).to.eql({ get x() { throw 42; }});
//> Uncaught 42

Idem. Left uncaught.

Mocha / Chai.js: https://www.chaijs.com/api/

//> $ node
chai = await import('chai');
chai.expect({ get x() { throw 42; } }).to.eql({ get x() { throw 42; }});
//> Uncaught 42

chai.assert.deepEqual({ get x() { throw 42; } }, { get x() { throw 42; }});
//> Uncaught 42

Idem. Left uncaught.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Assert Type: Bug Something isn't working right.
Development

No branches or pull requests

2 participants