jestjs / jest

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

Make "globalSetup" and "globalTeardown" work together with "transform" #5164

Closed opasno closed 5 years ago

opasno commented 6 years ago

Do you want to request a feature or report a bug? Feature

What is the current behavior? Modules defined in globalSetup and globalTeardown are not being transformed as defined in transform configuration entry.

What is the expected behavior? Modules should be transformed.

Please provide your exact Jest configuration and mention your Jest, node, yarn/npm version and operating system.

"jest": {
        "globalSetup": "<rootDir>/test/setup.ts",
        "globalTeardown": "<rootDir>/test/teardown.ts",
        "collectCoverage": true,
        "mapCoverage": true,
        "transform": {
            "^.+\\.ts?$": "ts-jest"
        },
        "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(js|ts)$",
        "moduleFileExtensions": [
            "ts",
            "js",
            "json",
            "node"
        ]
    }

jest 22.0.4 ts-jest 22.0.0 node 8.9.2 npm 5.5.1 OS Windows 10

SimenB commented 6 years ago

I'm not sure we can do this, since the whole point of global{Setup,Teardown} is that it runs before we create a runtime (which is what contains our require which passes stuff through the transforms).

@cpojer thoughts on this?

cpojer commented 6 years ago

I think we should transform it if we can, but I’m not sure right now what code changes that would entail. It may make sense to split the transformer into a separate package, something I wanted to do for a while.

glasser commented 6 years ago

Is there a workaround here? I'd like to be able to load some support code in globalSetup that uses ES6 modules and I'd prefer to not have to rewrite all of it back to requires/exports.

SimenB commented 6 years ago

No current workaround that I know of, sorry. PR welcome here, though 🙂

sourcec0de commented 6 years ago

@glasser I'm currently working around this issue by using babel-register and babel-pollyfill at the top of my jest.config.js.

yarn add babel-register babel-pollyfil
const { readFileSync } = require('fs')
const babelConfig = JSON.parse(readFileSync('./.babelrc', 'utf8'))

require('babel-register')(babelConfig)
require('babel-polyfill')

const { join } = require('path')

const ROOT = process.cwd()
// const TESTS = join(ROOT, '__tests__')
const JEST_ENV = join(ROOT, '__jest_env__')

module.exports = {
  verbose: true,
  transform: {
    '^.+\\.jsx?$': 'babel-jest'
  },
  globalSetup: join(JEST_ENV, 'setup.js'),
  globalTeardown: join(JEST_ENV, 'teardown.js'),
  testEnvironment: join(JEST_ENV, 'server-environment.js')
}
keroxp commented 6 years ago

@opasno You seem to be using typescript, so try this method. In my case, I could easily solve the problem. I do not know if this method is useful in all cases, but I did the following things.

code will be like this: __tests__/globalSetup.js, db/index.ts

require("ts-node/register");
const {db} = require("../db");
module.exports = async function () {
  return db.setup();
};

Note that entire setup/teardown codes will be executed in non-jest environment, so all modules and objects related to Jest can not be referred while setup/teardown. However, It is enough to do setup/teardown.

isaachinman commented 6 years ago

Is anybody working on a PR for this already? Just encountered this issue and took me awhile to realise what was happening and track down this issue. At the very least, maybe the docs could be updated to indicate that transforms will not take place on globalSetup due to it being outside a jest runtime.

WillAvudim commented 6 years ago

If anyone runs into this, you can trivially work around by performing these two steps.

  1. Specify in jest.json:

    "globalSetup": "<rootDir>/test_setup/global_setup_hook.js"
  2. Create a .js file 'global_setup_hook.js' and invoke your typescript code from there:

    
    require("ts-node/register");

// If you want to reference other typescript modules, do it via require: const my_typescript_module = require('my_typescript_module');

module.exports = async function () { // Call your initialization methods here. await my_typescript_module.Initialize(); return null; };



This assumes that you have "ts-node": "^5.0.1" in your package.json. No changes required in jest or ts-jest for this to work.
KhaledSakr commented 6 years ago

If anyone is working on a PR for this, keep in mind that testEnvironment files are not transpiled either.

tachang commented 6 years ago

Is this the same with setupTestFrameworkScriptFile? It is not transpiled?

isaachinman commented 6 years ago

@tachang Most likely. It would be extremely easy to test, why don't you do that yourself?

aljazerzen commented 6 years ago

@WillAvudim your solution works greatly :) I have shortened it to a two-liner file:

setup_hook.js:

require("ts-node/register");
module.exports = require('./setup').default;

This works, because I have the setup file declared with default export function:

setup.ts:

import { anything } from 'you-want';

export default async function() {
  // do all the setup you want in TypeScript
}

I hope someone finds this useful.

SimenB commented 6 years ago

@tachang setupTestFrameworkScriptFile should work as it's required through the runtime: https://github.com/facebook/jest/blob/b60f44a9829b51bc4bc387dbef8d7d37de3cc7e6/packages/jest-jasmine2/src/index.js#L129-L131 & https://github.com/facebook/jest/blob/b60f44a9829b51bc4bc387dbef8d7d37de3cc7e6/packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter.js#L76-L78


I'm still not sure how to solve this. One thing is to transpile the setup file itself (that can be done pretty easily), but to also transpile files required from it forces us to implement require or hook into node's own require. The first is difficult since it's part of jest-runtime which is not created yet. The second since we should use something like https://www.npmjs.com/package/pirates (which is what babel-register uses) which feels... invasive. Might be the correct solution though.

Anybody wanna create a PoC using https://www.npmjs.com/package/pirates?

SimenB commented 6 years ago

We could also print a pretty error if we get a syntax error saying we don't transform. That's probably better than what we have now

binygal commented 6 years ago

Should we always bail if there's a syntax error in the global setup, or should we respect bail flag in the config?

kelly-tock commented 6 years ago

would love to see this. I am using both babel and typescript, and not being able to use es6 for some global setup in test files in a centralized manner kind of sucks. is another workaround just importing the function in the tests you need as well? not as convenient but it would be transpiled right?

binygal commented 6 years ago

@SimenB What is the approach we decided to go with?

riglar commented 6 years ago

I amended @WillAvudim example to support tsconfig-paths

yarn add --dev tsconfig-paths

require("ts-node/register");
require('tsconfig-paths/register');

// If you want to reference other typescript modules, do it via require:
const my_typescript_module = require('my_typescript_module');

module.exports = async function () {
  // Call your initialization methods here.
  await my_typescript_module.Initialize();
  return null;
};
shai32 commented 6 years ago

I don't use typescript, is there another solution?

ezze commented 6 years ago

@shai32, you can do the same with @babel/register and @babel/polyfill:

require('@babel/register');
require('@babel/polyfill');
module.exports = require('./setup').default;
shai32 commented 6 years ago

@ezze I tried that, but I am using yarn workspace, and it doesn't work with yarn workspace (shared packages are not compiled because they are in node_moduels)

I don't understand why setupTestFrameworkScriptFile is transformed but globalSetup is not.

SimenB commented 5 years ago

This is available in Jest 24

donedgardo commented 5 years ago

Are https://jestjs.io/docs/en/configuration.html#modulenamemapper-object-string-string concidered if using global setup with typescript

SimenB commented 5 years ago

No, jest does not control the resolution algorithm in this case

AlvSovereign commented 5 years ago

@glasser I'm currently working around this issue by using babel-register and babel-pollyfill at the top of my jest.config.js.

yarn add babel-register babel-pollyfil
const { readFileSync } = require('fs')
const babelConfig = JSON.parse(readFileSync('./.babelrc', 'utf8'))

require('babel-register')(babelConfig)
require('babel-polyfill')

const { join } = require('path')

const ROOT = process.cwd()
// const TESTS = join(ROOT, '__tests__')
const JEST_ENV = join(ROOT, '__jest_env__')

module.exports = {
  verbose: true,
  transform: {
    '^.+\\.jsx?$': 'babel-jest'
  },
  globalSetup: join(JEST_ENV, 'setup.js'),
  globalTeardown: join(JEST_ENV, 'teardown.js'),
  testEnvironment: join(JEST_ENV, 'server-environment.js')
}

Small typo here on your yarn commands.

yarn add babel-register babel-polyfill

MichaelBitard commented 5 years ago

Based on @AlvSovereign and @sourcec0de work, adding

const { readFileSync } = require('fs');
const babelConfig = require('./babel.config.js');
require('@babel/register')(babelConfig);

in your jest.config.js worked for me

SimenB commented 5 years ago

No need for workarounds, the feature has been implemented

MichaelBitard commented 5 years ago

You said it was implemented in jest 24, but we are using jest 24.8.0 and if I don't do that, it does not work. Am i missing something?

Also, in the documentation, this is still present:

Note: While code transformation is applied to the linked setup-file, Jest will not transform any code in node_modules. This is due to the need to load the actual transformers (e.g. babel or typescript) to perform transformation.

SimenB commented 5 years ago

Transforming node_modules is not the same as Make "globalSetup" and "globalTeardown" work together with "transform", which is what this issue is about.

EDIT: We also transform everything not ignore via transformIgnorePatterns since 24.6: #8143. It should have updated the docs, though

MichaelBitard commented 5 years ago

Thanks. Indeed these 2 subjects got mixed in this issue, that's why I replied in it, sorry.

I was not able to make it work using transformIgnorePatterns: []. I'll try some other things and if I can't find a solution, maybe I'll open a new issue.

lanistor commented 5 years ago

Variable added to global in global{Setup,Teardown} cannot be used in test cases? I add a key to global in globalSetup file, but i cannot get it from my test cases. And it seems that, there're two different globals, the first one is instanceof NodeJS.Global, howere the second one is Window.

// globalSetup.ts
module.exports = async () => {
  (global as any).aaa = 111;
}

// testcase.test.ts
console.log('>>global', typeof global, (global as any).aaa);
jeysal commented 5 years ago

@vifird that is correct, each test runs in its own sandboxed environment.

lanistor commented 5 years ago

@jeysal So is there a way to add global settings? except "globals" field in jest's configuration.

SimenB commented 5 years ago

Correct. We might add a way to send variables from globalSetup to tests (there are some issues about it, but I'm currently on mobile) but that's not in place yet

hereiscasio commented 5 years ago

@SimenB i still can't use the import statement like import vue from 'vue' in the file which globalSetup point to

i have an error on my terminal without any extra information

Determining test suites to run...

SyntaxError: Unexpected identifier

Did i missing something ? And what are u meaning about "This is available in Jest 24" ? i thought i don't need to config anything and i can have such feature


below is my jest.config.js

module.exports = {
    globalSetup: './tests/unit/run-once-before-all-files.js',

    moduleFileExtensions: [
        'js',
        'jsx',
        'json',
        'vue'
    ],
    transform: {
        '^.+\\.vue$': 'vue-jest',
        '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
        '^.+\\.jsx?$': 'babel-jest'
    },

    transformIgnorePatterns: [
        '/node_modules/',
        '/public/',
        '/__mocks__/',
        '/api/',
        '/src/assets/',
        'tests/e2e/'
    ],
    moduleNameMapper: {
        '^@/(.*)$': '<rootDir>/src/$1'
    },
    snapshotSerializers: [
        'jest-serializer-vue'
    ],
    testMatch: [
        '**/tests/unit/**/*.test.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
    ],
    testURL: 'http://localhost/',
    watchPlugins: [
        'jest-watch-typeahead/filename',
        'jest-watch-typeahead/testname'
    ],
    'setupFiles': ['jest-canvas-mock']
}
Ericnr commented 5 years ago

using Jest 24.9 and had to require('ts-node/register) to get it working too. I don't understand how this is available in Jest 24

dmasterstempus commented 4 years ago

I can confirm this is not working in 24.9. As a matter of fact, it only works up to 24.1 for me.

Related: #9041

SimenB commented 4 years ago

Please open up new issues (with reproductions) if you have a regression to report - this old issue is not the place for it

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.