Jest: ArrayBuffer regression in node env

Created on 2 Feb 2019  ·  19Comments  ·  Source: facebook/jest

🐛 Bug Report

https://github.com/facebook/jest/pull/7626 introduced regression

expect(new Uint8Array([]).buffer).toBeInstanceOf(ArrayBuffer);

fails with

    Expected constructor: ArrayBuffer
    Received constructor: ArrayBuffer
    Received value: []
Bug

Most helpful comment

I lied. I came back. Workaround:

// __test-utils__/custom-jest-environment.js
// Stolen from: https://github.com/ipfs/jest-environment-aegir/blob/master/src/index.js
// Overcomes error from jest internals.. this thing: https://github.com/facebook/jest/issues/6248
"use strict";

const NodeEnvironment = require("jest-environment-node");

class MyEnvironment extends NodeEnvironment {
  constructor(config) {
    super(
      Object.assign({}, config, {
        globals: Object.assign({}, config.globals, {
          Uint32Array: Uint32Array,
          Uint8Array: Uint8Array,
          ArrayBuffer: ArrayBuffer,
        }),
      }),
    );
  }

  async setup() {}

  async teardown() {}

}

module.exports = MyEnvironment;

Then in package.json:

  "jest": {
    "testEnvironment": "__test-utils__/custom-jest-environment.js",
    ...
  },

All 19 comments

@H1Gdev ideas?

This _might_ be #2549

@SimenB

Oh...:cry:

Seems that there are multiple type of ArrayBuffer in Node.js vm.

There is also a way to add TypedArrays like ArrayBuffer is easy workaround.
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/TypedArray

@SimenB

Execute the following code in each environment.(--env=node, --env=jsdom, and node).

const bufFromArray = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);
bufFromArray instanceof Uint8Array;
const bufFromArrayBuffer = Buffer.from(new ArrayBuffer(6));
bufFromArrayBuffer instanceof Uint8Array;

In --env=node, results are not true.

It seems that Uint8Array also has the same issue as ArrayBuffer.

@SimenB

Oh...😢

Seems that there are multiple type of ArrayBuffer in Node.js vm.

There is also a way to add TypedArrays like ArrayBuffer is easy workaround.
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/TypedArray

not only ArrayBuffer. all globals in vm context differs from globals of main process.
But new vm context doesn't have Buffer. So in this line vmGlobal.Buffer = mainGlobal.Buffer explicitly assigned https://github.com/facebook/jest/blob/master/packages/jest-util/src/installCommonGlobals.js#L65

so both contexts (main and vm) has same Buffer from main context. And Buffer.from() called in vmContext returns instance of mainGlobal.ArrayBuffer.

Probably it's better to contextify Buffer constructor, Buffer.prototype and static methods in installCommonGlobals. Like in vm2 https://github.com/patriksimek/vm2

Moreover since Buffer from different context this both fails:

expect(Buffer instanceof Object).toBe(true);
expect(Buffer instanceof Function).toBe(true);

Upd. Full test case of globals if needed:

const data = [];
for (const key in global) {
    const value = global[key];
    if ("function" === typeof value || null !== value && "object" === typeof value) {
        data.push(key);
    }
}

test.each(data)("global[%p] instanceof Object", (key) => {
    expect(global[key]).toBeInstanceOf(Object);
});

image

This is really painful in our project. Caused a lot of false positives and nagatives in tests involving checks for value types.
Instead of simple value instanceof Object before chucking the value into WeakMap as a key I have to do
value instanceof Object || value instanceof Buffer
And explain to colleagues, that this is there just because Jest enviroment demands it to be.

Possible workaround:

Object.prototype.toString.call( new Uint8Array(10)) === '[object Uint8Array]'

This forced me to use mocha.

I lied. I came back. Workaround:

// __test-utils__/custom-jest-environment.js
// Stolen from: https://github.com/ipfs/jest-environment-aegir/blob/master/src/index.js
// Overcomes error from jest internals.. this thing: https://github.com/facebook/jest/issues/6248
"use strict";

const NodeEnvironment = require("jest-environment-node");

class MyEnvironment extends NodeEnvironment {
  constructor(config) {
    super(
      Object.assign({}, config, {
        globals: Object.assign({}, config.globals, {
          Uint32Array: Uint32Array,
          Uint8Array: Uint8Array,
          ArrayBuffer: ArrayBuffer,
        }),
      }),
    );
  }

  async setup() {}

  async teardown() {}

}

module.exports = MyEnvironment;

Then in package.json:

  "jest": {
    "testEnvironment": "__test-utils__/custom-jest-environment.js",
    ...
  },

I just had to put a ./ before the testEnvironment path:

  "jest": {
    "testEnvironment": "./__test-utils__/custom-jest-environment.js",
    ...
  },

@odahcam @wi-ski

Thanks for sharing the workaround, but it seems to me that the solution is already out of date. I've just installed jest-environment-node@^26.0.1 and in jest.config.js I set:

module.exports = {
  // ...
  testEnvironment: 'node',
};

That's it. I didn't need to extend the NodeEnvironment and overload the constructor. It just worked.

Correct me if I'm wrong, but it seems that when jest-environment-node is installed then jest uses that module to configure the environment. So it looks like the bug is already fixed in jest-environment-node modlue.

Am I right?

Setting testEnvironment: 'node' in jest.config.js works for me too without using the workaround. I do not need to install jest-environment-node.

this bug is over a year old - is it an intended side-effect in v26.1.0 or can I open a PR that addresses this

let t = Buffer.alloc(0)
t instanceof Uint8Array

true in jest-environment-jsdom@25
false in jest-environment-jsdom@26

this bug is over a year old - is it an intended side-effect in v26.1.0 or can I open a PR that addresses this

still bit me on jest 26.4.2… but wujekbogdan's solution above adding to jest.config.js did help me!

I'm running Jest v26.4.0 and the problem occurs even though I added testEnvironment="node" in jest.config.js. I used the code posted by @wi-ski and that solved the problem.
In my case I'm using Amplify and for Auth it uses its own crypto-js module version and it was failing when comparing aVariable instanceof Uint8Array presumably due the the same problem reported here.
Happy to have found this issue and how to cope with it.

I'm running Jest v26.4.0 and the problem occurs even though I added testEnvironment="node" in jest.config.js. I used the code posted by @wi-ski and that solved the problem.
In my case I'm using Amplify and for Auth it uses its own crypto-js module version and it was failing when comparing aVariable instanceof Uint8Array presumably due the the same problem reported here.
Happy to have found this issue and how to cope with it.

Same issue here

i worked around this by adding jest-environment-jsdom@25 as a dev dep
per https://github.com/facebook/jest/issues/7780#issuecomment-669828353

Was this page helpful?
0 / 5 - 0 ratings