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

Support for BigInt #1321

Closed
fubar opened this issue Feb 25, 2020 · 9 comments
Closed

Support for BigInt #1321

fubar opened this issue Feb 25, 2020 · 9 comments

Comments

@fubar
Copy link

fubar commented Feb 25, 2020

Chai still doesn't support BigInts.

expect(BigInt(1)).to.equal(BigInt(2));

Results in TypeError: Do not know how to serialize a BigInt (triggered by JSON.stringify).

BigInt is now a standard primitive and it would be great if Chai supported it out of the box.

Stale/closed issue: #1195. The roadmap has that issue in todo but it has not been updated since Apr 2019: https://github.com/chaijs/chai/projects/2

@dwelle
Copy link
Contributor

dwelle commented Jun 16, 2020

Serialization can be solved by doing something like:

BigInt.prototype.toJSON = val => `${val}n`;

But assertions will also fail because chai boxes bigints into some kind of object (when storing it to __flags.object.

@keithamus
Copy link
Member

I'm going to close this, as it is a dupe of #1195. The roadmap is the preferred way to track progress for us. The current in flight project is loupe - which was deemed in most need of attention, and the last commit was 22nd Apr 2020. Once we're happy with loupe we'll be moving onto the next todo item.

If you uncover any bugs please feel free to file more issues, but rest assured we're aware of a lack of support for ES6+ types and are working hard to fix that.

@dwelle
Copy link
Contributor

dwelle commented Jun 17, 2020

@keithamus if it's not too much trouble, can you give me a hint where chai boxes BigInts (and I reckon any types it doesn't know about) into [BigInt: 122054031626471353n] when I log utils.flag(this, 'object')? I'm getting lost in the source, and could use some help for a fork before chai v2 is ready. Thanks!

EDIT: I guess I can hack it by monkey-patching the utils.flag getter/setter.

@keithamus
Copy link
Member

Just FYI we support BigInts for assertions like .to.be.equal. If you run expect(0n).to.equal(0n) you'll get a passing assertion. I don't know where the OP saw the error TypeError: Do not know how to serialize a BigInt (perhaps from a plugin or some kind of monkey-patch on BigInt?), but if I run expect(0n).to.equal(1n) I get AssertionError: expected {} to equal {}

Chai doesn't box the values in any way. Expect just wraps the given argument in a new Assertion type:

module.exports = function (chai, util) {
chai.expect = function (val, message) {
return new chai.Assertion(val, message);

Assertion just sets the object flag to whatever is given to it:

function Assertion (obj, msg, ssfi, lockSsfi) {
flag(this, 'ssfi', ssfi || Assertion);
flag(this, 'lockSsfi', lockSsfi);
flag(this, 'object', obj);
flag(this, 'message', msg);

Each assertion then does its own thing. When equal() is called, it pulls out the flag and runs X === Y:

function assertEqual (val, msg) {
if (msg) flag(this, 'message', msg);
var obj = flag(this, 'object');
if (flag(this, 'deep')) {
var prevLockSsfi = flag(this, 'lockSsfi');
flag(this, 'lockSsfi', true);
this.eql(val);
flag(this, 'lockSsfi', prevLockSsfi);
} else {
this.assert(
val === obj
, 'expected #{this} to equal #{exp}'
, 'expected #{this} to not equal #{exp}'
, val
, this._obj
, true
);
}
}
Assertion.addMethod('equal', assertEqual);
Assertion.addMethod('equals', assertEqual);
Assertion.addMethod('eq', assertEqual);

The failure message of AssertionError: expected {} to equal {} is the bit that sucks at the moment; and that's because of our inspection engine (which is why we're working on loupe, which is a rewrite of the inspection engine to be much faster and better). The error string from .equal (L1033) gets passed to getMessage which separates out the #{this} string, and calls objDisplay on that, which in turn calls inspect which in turn goes through a bunch of type checks, fails, and just returns {}.

@dwelle
Copy link
Contributor

dwelle commented Jun 17, 2020

Thanks for the detailed writeup. I've been through most of those places in the codebase myself, and as such couldn't figure out where that "boxing" (or what it is) is coming from. It must not be chai then. I'll investigate when I get time and get back to you if I find the culprit.

@dwelle
Copy link
Contributor

dwelle commented Jun 17, 2020

Ok, so it seems it's related only to should assertions:

const chai = require("chai");

chai.should();

chai.use(function (chai, utils) {
    chai.Assertion.addMethod('eq', function (expected) {
        console.log(utils.flag(this, 'object'))
    });
});

(10n).should.eq(10n); // logs [BigInt: 10n]

This is what the Assertion object looks like for should interface:

console.log((10n).should)

// Assertion {
//   __flags: [Object: null prototype] {
//     ssfi: [Function: shouldGetter],
//     lockSsfi: undefined,
//     object: [BigInt: 10n],
//     message: null
//   }
// }

as opposed to expect:

console.log(chai.expect(10n))

// Assertion {
//   __flags: [Object: null prototype] {
//     ssfi: [Function: Assertion] {
//       addProperty: [Function],
//       addMethod: [Function],
//       addChainableMethod: [Function],
//       overwriteProperty: [Function],
//       overwriteMethod: [Function],
//       overwriteChainableMethod: [Function]
//     },
//     lockSsfi: undefined,
//     object: 10n,
//     message: undefined
//   }
// }

@dwelle
Copy link
Contributor

dwelle commented Jun 17, 2020

Mhm.. turns out this has nothing to do with Chai, but JS. this refers to the boxed primitive value:

Object.defineProperty(Object.prototype, 'should', {
    get: function () {
        return this; // boxed value for primitives
    }
});

Chai accounts for this by unboxing primitive values, but bigint isn't in the list right now (obviously):

function shouldGetter() {
if (this instanceof String
|| this instanceof Number
|| this instanceof Boolean
|| typeof Symbol === 'function' && this instanceof Symbol) {
return new Assertion(this.valueOf(), null, shouldGetter);
}

@dwelle
Copy link
Contributor

dwelle commented Jun 17, 2020

I can open a PR for this one change if that's ok?

@keithamus
Copy link
Member

Yes! Please do so!

This was referenced Mar 15, 2021
keithamus added a commit that referenced this issue Feb 7, 2023
* Fixed a regression that caused SyntaxErrors on IE 11

The changes made in #1334 incorrectly used an arrow function and as this isn't supported on IE 11 it causes a SyntaxError to be thrown when loading chai.

* chai@4.3.2

* export chai.Assertion (#1378)

* chai@4.3.3

* fix: support inspecting bigints (#1321) (#1383)

* 4.3.4

* feat: use chaijs/loupe for inspection (#1401) (#1407)

Fix #1228

* fix: package.json - deprecation warning on exports field (#1400)

* fix package.json exports

* build(deps-dev): bump codecov from 3.1.0 to 3.7.1 (#1446)

Bumps [codecov](https://github.com/codecov/codecov-node) from 3.1.0 to 3.7.1.
- [Release notes](https://github.com/codecov/codecov-node/releases)
- [Changelog](https://github.com/codecov/codecov-node/blob/master/CHANGELOG.md)
- [Commits](codecov/codecov-node@v3.1.0...v3.7.1)

---
updated-dependencies:
- dependency-name: codecov
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build chaijs

* 4.3.5

* fix: use loupe@^2.3.1

* build chaijs

* 4.3.6

* fix: deep-eql bump package to support symbols comparison (#1483)

* 4.3.7

* build

* chore: 4.x.x: Fix link to commit logs on GitHub (#1487)

* 4.x.x: Fix link to commit logs on GitHub

As there is no `master` branch, the link returned a 404.

* Update History.md

Co-authored-by: Andre Meyering <info@andremeyering.de>

Co-authored-by: Keith Cirkel <keithamus@users.noreply.github.com>

* build(deps): bump socket.io-parser from 4.0.4 to 4.0.5 (#1488)

Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 4.0.4 to 4.0.5.
- [Release notes](https://github.com/socketio/socket.io-parser/releases)
- [Changelog](https://github.com/socketio/socket.io-parser/blob/main/CHANGELOG.md)
- [Commits](socketio/socket.io-parser@4.0.4...4.0.5)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* test: fix typo in test.js (#1459)

* docs: specify return type of objDisplay (#1490)

previously it was incorrectly labeled as `objDisplay(obj: object): void` in `@types/chai`.

* feat: rework into ES modules

This moves all of the sources to be ES modules rather than CommonJS.

In order to produce the entrypoints, we use esbuild as a bundler (and as
a transpiler in a way).

Due to the fact that some dependencies are written in CommonJS, we
actually use esbuild to create _two_ bundles: a CommonJS bundle, and an
ES module bundle.

Otherwise, someone importing the raw source would inevitably end up
trying to import a CommonJS module somewhere down the tree, which would
not work in browsers natively.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Lee Newson <lnewso@hotmail.com>
Co-authored-by: Keith Cirkel <keithamus@users.noreply.github.com>
Co-authored-by: rbruckheimer <rob@cosaic.io>
Co-authored-by: Mike Frysinger <vapier@gmail.com>
Co-authored-by: Pascal Corpet <pcorpet@users.noreply.github.com>
Co-authored-by: Mimi <stevenjoezhang@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Scott Newcomer <snewcomer@users.noreply.github.com>
Co-authored-by: Andre Meyering <info@andremeyering.de>
Co-authored-by: Mavaddat Javid <5055400+mavaddat@users.noreply.github.com>
Co-authored-by: scarf <greenscarf005@gmail.com>
Co-authored-by: James Garbutt <james.garbutt@crispthinking.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants