jest icon indicating copy to clipboard operation
jest copied to clipboard

[Bug]: Failure to expect rejection of Bluebird promises

Open eyalroth opened this issue 2 years ago • 7 comments

Version

27.4.7

Steps to reproduce

Run this test:

import { each } from 'bluebird';


test('my test', async () => {
    await expect(each([Promise.reject('foo')], () => {})).rejects.toEqual('foo');
});

Expected behavior

Test should pass.

Actual behavior

Test fails with error:

Error: thrown: "foo"
    at _getError (/myproject/node_modules/jest-circus/build/utils.js:566:18)
    at Array.map (<anonymous>)
    at makeSingleTestResult (/myproject/node_modules/jest-circus/build/utils.js:507:38)
    at /myproject/node_modules/jest-circus/build/testCaseReportHandler.js:19:58
    at dispatch (/myproject/node_modules/jest-circus/build/state.js:75:11)
    at _runTest (/myproject/node_modules/jest-circus/build/run.js:167:3)
    at _runTestsForDescribeBlock (/myproject/node_modules/jest-circus/build/run.js:66:9)
    at _runTestsForDescribeBlock (/myproject/node_modules/jest-circus/build/run.js:60:9)
    at run (/myproject/node_modules/jest-circus/build/run.js:25:3)
    at runAndTransformResultsToJestFormat (/myproject/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:170:21)
    at jestAdapter (/myproject/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:82:19)
    at runTestInternal (/myproject/node_modules/jest-runner/build/runTest.js:389:16)
    at runTest (/myproject/node_modules/jest-runner/build/runTest.js:475:34)
    at TestRunner.runTests (/myproject/node_modules/jest-runner/build/index.js:111:12)
    at TestScheduler.scheduleTests (/myproject/node_modules/@jest/core/build/TestScheduler.js:333:13)
    at runJest (/myproject/node_modules/@jest/core/build/runJest.js:404:19)

Additional context

This issue (or similar) seemed to have been posted here in the past in #8667, but was never addressed.

jest.config.js:

// All tests

// fix resolution of config/*.json in tests run from IntelliJ
process.env.NODE_CONFIG_DIR = `${__dirname}/config`;

/** @type {import("ts-jest/dist/types").InitialOptionsTsJest} */
module.exports = {
  preset: "ts-jest",
  testEnvironment: "node",
  rootDir: __dirname,
  testMatch: [
    "<rootDir>/test/**/*.test.ts"
  ],
  modulePathIgnorePatterns: ["<rootDir>/out/"],
  moduleNameMapper: {
    "^src/(.*)$": "<rootDir>/src/$1",
    "^test/(.*)$": "<rootDir>/test/$1"
  },
  globals: {
    "ts-jest": {
      tsconfig: "<rootDir>/test/tsconfig.json"
    }
  },
  resetMocks: true,
};

Environment

System:
    OS: macOS 12.6
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
  Binaries:
    Node: 16.14.0 - ~/.nvm/versions/node/v16.14.0/bin/node
    npm: 8.12.1 - ~/.nvm/versions/node/v16.14.0/bin/npm
  npmPackages:
    jest: ^27.4.7 => 27.4.7
    bluebird: 3.7.2

eyalroth avatar Nov 16 '22 08:11 eyalroth

Hey there! I tried your test using Jest 27.5.1 (creating a create-react-app environment to do it), and it passed. Maybe upgrading jest version would fix it?

image

feliperli avatar Nov 23 '22 10:11 feliperli

@feliperli I updated to the latest Jest (29.3.1) and I get the same error.

I'm not sure about the test setup of create-react-app as I'm not using React.

eyalroth avatar Nov 24 '22 10:11 eyalroth

Yeah, I created a simple jest environment and create-react-app indeed does something more with jest (a config or a plugin maybe).

it is weird because in a scenario like

import { each } from "bluebird";

test("my test", async () => {
  await expect(each([Promise.reject("foo")], () => {})).rejects.toEqual("foo");
});

test("my test resolved", async () => {
  await expect(each([Promise.resolve("foo")], () => {})).resolves.toEqual([
    "foo",
  ]);
});

The first test fails, but the second passes. And if I try to change to

import { each } from "bluebird";

test("my test", async () => {
  await expect(each([Promise.reject("foo")], () => {})).rejects.toEqual("foo2");
});
});

I get

Expected: "foo2" Received: "foo"

So, the test when ran is indeed receiving "foo", but failing.

feliperli avatar Nov 30 '22 21:11 feliperli

Hey @eyalroth to fix your problem you should

import Promise, { each } from 'bluebird'

this will ensure promise compatibility with bluebird and jest. If this solve your problem, please don't forget to close the issue :)

feliperli avatar Dec 12 '22 14:12 feliperli

@feliperli Sorry for the late reply.

The snippet you added makes the test fail (as expected) because it makes the line Promise.reject return a Bluebird (rejected) promise instead of a native one.

However, bluebird is compatible with native promises, and the test should reflect that.

Perhaps in my code I'm using Bluebird's functions for their utility, but I might be dealing with native promises that are emitted by third party libraries. I still want to test my code and see whether it deals with both native and bluebird's promises alike.

eyalroth avatar Dec 23 '22 13:12 eyalroth

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Jan 22 '23 13:01 github-actions[bot]

Not stale.

eyalroth avatar Jan 22 '23 13:01 eyalroth

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Feb 21 '23 13:02 github-actions[bot]

This is an issue with bluebird. Run the following snippet in Node and see await doesn't work.

// file.mjs
import bluebird from 'bluebird';

try {
  await bluebird.each([Promise.reject('booo')], () => {});
} catch {
  console.log('caught the error');
}
$ node:internal/process/promises:288
            triggerUncaughtException(err, true /* fromPromise */);
            ^

[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "booo".] {
  code: 'ERR_UNHANDLED_REJECTION'
}

Node.js v18.14.0

SimenB avatar Feb 22 '23 09:02 SimenB

@SimenB I don't see why Jest fails because of this.

The promise returned from bluebird.each is still a rejected promise:

const p = await each([Promise.reject('booo')], () => {}).catch(e => e);
console.log(p) // prints 'booo'

// also
try {
      await each([Promise.reject('booo')], () => {});
  } catch (e) {
      console.log('caught the error');
      console.log(e); // prints 'booo'
  }

And if we add global hook on rejections per bluebird's documentation:

process.on("unhandledRejection", function(reason, promise) {});

process.on("rejectionHandled", function(promise) {});

NodeJS doesn't emit PromiseRejectionHandledWarning anymore, but Jest still fails.

It's also worth noting that Mocha + Chai + chai-as-promised handles this case correctly:

// this passes
await chai.expect(bluebird.each([Promise.reject('booo')], () => {})).to.be.rejected;

eyalroth avatar Feb 22 '23 16:02 eyalroth

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

github-actions[bot] avatar Mar 25 '23 00:03 github-actions[bot]