jest: Setting NODE_TLS_REJECT_UNAUTHORIZED=0 doesn't work in jest@22+

đŸ’„ Regression Report

A clear and concise description of what the regression is.

Last working version

Worked up to version: jest@21.2.1 Stopped working in version: jest@22.0.0

To Reproduce

Steps to reproduce the behavior:

Expected behavior

I expected test to succeed, it fails with:

 FAIL  src/__tests__/server.test.js
  server
    ✕ server sends response (38ms)

  ● server â€ș server sends response

    self signed certificate

Link to repl or repo (highly encouraged)

https://github.com/trivikr/jest-self-signed-certificate

Run npx envinfo --preset jest

Paste the results here:

npx: installed 1 in 1.231s

  System:
    OS: macOS High Sierra 10.13.6
    CPU: (4) x64 Intel(R) Core(TM) i7-5557U CPU @ 3.10GHz
  Binaries:
    Node: 12.1.0 - /usr/local/bin/node
    Yarn: 1.15.2 - /usr/local/bin/yarn
    npm: 6.9.0 - /usr/local/bin/npm
  npmPackages:
    jest: ^24.8.0 => 24.8.0 

Ref: https://github.com/aws/aws-sdk-js-v3/issues/244

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 30
  • Comments: 21 (9 by maintainers)

Most upvoted comments

Hey I found a workaround for this in jest 26 (if you are using jsdom). You are going to have to create a custom environment to run jest in which extends jsdom. With the upgrade to jsdom 16, jest is not aware of the “resources” option on the JSDOM constructor, this and a combination of a regression somewhere with process.env which some of this thread has picked up on doesnt allow NODE_TLS_REJECT_UNAUTHORIZED=0 to work anymore.

Steps to patch:

  1. create a custom jsdom environment (save this as custom-jsdom-env.js next to package.json). This is similiar to the jest-environment-jsdom which ships with jest HOWEVER, it includes the “resources” component of JSDOM with the strictSSL set to false, there are two other options in the resources which you can find on jsdom but I have just set this one.
// custom-jsdom-environment
const JSDOMEnvironment = require('jest-environment-jsdom')
const { ResourceLoader, JSDOM, VirtualConsole } = require('jsdom')
const { installCommonGlobals } = require('jest-util')
const { ModuleMocker } = require('jest-mock')
const { LegacyFakeTimers, ModernFakeTimers } = require('@jest/fake-timers')

class CustomEnvironment extends JSDOMEnvironment {
  constructor(config, context) {
    super(config, context)

    this.dom = new JSDOM('<!DOCTYPE html>', {
      pretendToBeVisual: true,
      runScripts: 'dangerously',
      url: config.testURL,
      virtualConsole: new VirtualConsole().sendTo(context.console || console),
      ...config.testEnvironmentOptions,
      resources: new ResourceLoader({ strictSSL: false }),
    })
    const global = (this.global = this.dom.window.document.defaultView)

    if (!global) {
      throw new Error('JSDOM did not return a Window object')
    }

    // for "universal" code (code should use `globalThis`)
    global.global = global

    // Node's error-message stack size is limited at 10, but it's pretty useful
    // to see more than that when a test fails.
    this.global.Error.stackTraceLimit = 100
    installCommonGlobals(global, config.globals)

    // Report uncaught errors.
    this.errorEventListener = (event) => {
      if (userErrorListenerCount === 0 && event.error) {
        process.emit('uncaughtException', event.error)
      }
    }
    global.addEventListener('error', this.errorEventListener)

    // However, don't report them as uncaught if the user listens to 'error' event.
    // In that case, we assume the might have custom error handling logic.
    const originalAddListener = global.addEventListener
    const originalRemoveListener = global.removeEventListener
    let userErrorListenerCount = 0
    global.addEventListener = function(...args) {
      if (args[0] === 'error') {
        userErrorListenerCount++
      }
      return originalAddListener.apply(this, args)
    }
    global.removeEventListener = function(...args) {
      if (args[0] === 'error') {
        userErrorListenerCount--
      }
      return originalRemoveListener.apply(this, args)
    }

    this.moduleMocker = new ModuleMocker(global)

    const timerConfig = {
      idToRef: (id) => id,
      refToId: (ref) => ref,
    }

    this.fakeTimers = new LegacyFakeTimers({
      config,
      global,
      moduleMocker: this.moduleMocker,
      timerConfig,
    })

    this.fakeTimersModern = new ModernFakeTimers({ config, global })
  }

  async setup() {
    await super.setup()
  }

  async teardown() {
    await super.teardown()
  }

  runScript(script) {
    return super.runScript(script)
  }
}

module.exports = CustomEnvironment
  1. Add the testEnvironment to jest settings: You need to set the testEnvironment option to {PATH_TO}/cutom-jsdom-env.js or the --env={PATH_TO}/custom-jsdom-env.js

Any update on this request?

After researching this problem, it appears that jest wraps the env object with a proxy. Hence, any change on process.env would not be propagated to the real one.

https://github.com/facebook/jest/blob/c83051789cc60789f2c575374270236e3a428879/packages/jest-util/src/createProcessObject.ts#L82-L128

I ended up solving the problem by a monkey patching technique which enforces node trusting the self-signed root CA via syswide-cas. i.e.

import { addCAs } from 'syswide-cas';

addCAs('path/to/rootCA.crt');

It looks like it doesn’t pick up any change to process.env not only to NODE_TLS_REJECT_UNAUTHORIZED.

This is a blocker for us. We’re trying not to have a flag in our services to turn off SSL. For testing, we have to use self-signed certs. Pretty much a dealbreaker 😩

for me export NODE_TLS_REJECT_UNAUTHORIZED=0 worked set NODE_TLS_REJECT_UNAUTHORIZED=0 did not

After some experimenting/debugging, I tbh still have no idea why Node doesn’t pick up the setting or which change in v22 could have introduced this. As a workaround it seems that it works if you launch Jest with the env var set to 0 straight away. If it’s just a few tests that need this, could also consider doing a separate Jest run for those if you don’t want to set it for all tests.

Any update on this regression?

Hi, is there any update on this regression? Or any proposed solution to test https server by setting NODE_TLS_REJECT_UNAUTHORIZED=0

This might block users from updating past Jest v21