jestjs / jest

Delightful JavaScript Testing.
https://jestjs.io
MIT License
44.2k stars 6.46k forks source link

Chai "should" assertions incompatible with Jest. #6463

Closed phyllisstein closed 6 years ago

phyllisstein commented 6 years ago

🐛 Bug Report

Hey Jesters! I was recently attempting to integrate Chai's should assertion syntax into a Jest test suite, and discovered something a little odd. For some reason, tests that work fine in Mocha and in Jest with jest-mocha-runner crash in Jest, with errors indicating that the should methods are no longer defined at various points in the language chain.

I've seen the issue with built-in objects like URL...

● ProsceniumClient › initialization › creates a URL object for the server URL

  TypeError: Cannot read property 'be' of undefined

    13 |   describe('initialization', () => {
    14 |     it('creates a URL object for the server URL', () => {
  > 15 |       pc.serverURL.should.be.an.instanceof(URL)
       |                           ^
    16 |     })
    17 |
    18 |     it('creates a new IFrame', () => {

    at Object.it (spec/client/proscenium-client-spec.ts:15:27)

...as well as custom constructors...

● RPCDispatcher › initialization › stores a reference to the client

  TypeError: Cannot read property 'be' of undefined

    14 |   describe('initialization', () => {
    15 |     it('stores a reference to the client', () => {
  > 16 |       rpc.client.should.be.an.instanceof(ProsceniumClient)
       |                         ^
    17 |     })
    18 |   })
    19 |

    at Object.it (spec/client/rpc/rpc-dispatcher-spec.ts:16:25)

...and objects from libraries like JSDOM...

● IFrame › #initialize › is ignored by screen readers

  TypeError: Cannot read property 'have' of undefined

    45 |
    46 |     it('is ignored by screen readers', () => {
  > 47 |       document.body.querySelector('iframe')
       |       ^
    48 |         .should.have.attribute('aria-hidden')
    49 |         .that.equals('true')
    50 |     })

    at Object.it (spec/client/dom/iframe-spec.ts:47:7)

...but it doesn't affect all should assertions---some work just fine, and I haven't figured out the pattern yet.

should works by manipulating Object.prototype, so it seems likely to me that Jest's environment is making that difficult or unpredictable. However, I don't know Jest's internals well enough to guess what the conflict might be, and I was hoping you might have some insight.

Thanks in advance for any thoughts you have! Let me know if I can provide any more color.

To Reproduce

Steps to reproduce the behavior:

  1. Register Chai's should helpers on the global object by importing chai/register-should or calling chai.should().
  2. Attempt to make an assertion with Chai's fluent language chains.
  3. :boom:

Expected behavior

I would expect that the Object.prototype.should global would remain defined through the entire test suite and allow chaining of its methods, as it does in the Mocha and jest-mocha-runner environments.

Link to repl or repo (highly encouraged)

https://github.com/phyllisstein/expect-should-repro-repo

Run npx envinfo --preset jest

System:
  OS: macOS 10.14
  CPU: x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
Binaries:
  Node: 10.4.1 - /usr/local/bin/node
  Yarn: 1.7.0 - /usr/local/bin/yarn
  npm: 6.1.0 - /usr/local/bin/npm
npmPackages:
  @types/jest: ^23.0.2 => 23.0.2
  jest: ^23.1.0 => 23.1.0
phyllisstein commented 6 years ago

N.B. also that I flagged this with the Chai maintainers and they suggested that I take my whinge to you folks. Q.v. https://github.com/chaijs/chai/issues/1165.

SimenB commented 6 years ago

I haven't checked, but I'd guess this is due to #2549

phyllisstein commented 6 years ago

Oh wow, thank you so much for weighing in @SimenB! In the course of getting up to speed on the details of #2549, I wound up checking to see what would happen if I moved the environment bootstrapping code from a setupTestFrameworkScriptFile script to a custom environment:

// spec/_should-environment.js

const browserEnv = require('browser-env')
const chai = require('chai')
const chaiDOM = require('chai-dom')
const JSDOMEnvironment = require('jest-environment-jsdom')

class ShouldEnvironment extends JSDOMEnvironment {
  async setup() {
    await super.setup()
    browserEnv()
    chai.use(chaiDOM)
    this.global.should = chai.should()
  }
}

module.exports = ShouldEnvironment

After pointing Jest's testEnvironment option to this file and removing the setupTestFrameworkScriptFile, all my should assertions worked beautifully. (For whatever reason, it was still necessary to import chai/register-should in setupFiles.) Here's the complete changeset: https://github.com/phyllisstein/expect-should-repro-repo/commit/8571f66e7f82624eba6fcabf258fbddd52f5b0e1.

This resolves a greasy headache of long standing and frees me once and for all from Jasmine's clunky assertion style. Thank you for pointing me in the right direction.

github-actions[bot] commented 3 years ago

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.