Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to set transformIgnorePatterns to fix "Jest encountered an unexpected token" #812

Closed
luchsamapparat opened this issue Oct 12, 2018 · 31 comments
Labels

Comments

@luchsamapparat
Copy link
Contributor

First of all, thanks for bringing Jest to Angular!

I previously had configured Jest in my Angular project by myself. In order to use lodash-es, I had to set transformIgnorePatterns to inlude the path to lodash-es:

  "jest": {
    "preset": "jest-preset-angular",
...
    "transformIgnorePatterns": [
      "<rootDir>/node_modules/(?!lodash-es/.*)"
    ],

Now, after migrating to the Jest config provided by Nx, I don't know where I can set this option. My tests currently fail with this error:

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    C:\Users\Marvin\Projekte\hypershop-ng\node_modules\lodash-es\lodash.js:10
    export { default as add } from './add.js';
    ^^^^^^

    SyntaxError: Unexpected token export

      1 | import { ComponentFixture } from "@angular/core/testing";
    > 2 | import { isUndefined } from "lodash-es";
        | ^
      3 |
      4 | export function expectElementFromFixture<T>(fixture: ComponentFixture<T>, domQuery?: string): jasmine.Matchers<{} | null> {
      5 |     return expect(elementFromFixture(fixture, domQuery));

      at ScriptTransformer._transformAndBuildScript (../../node_modules/jest-runtime/build/script_transformer.js:403:17)
      at Object.<anonymous> (../../libs/common/src/test/expect.ts:2:1)
      at Object.<anonymous> (../../libs/common/src/test/index.ts:1:1)

Thanks for your help

@luchsamapparat
Copy link
Contributor Author

luchsamapparat commented Oct 12, 2018

Nevermind, obviously it's ./jest.config.json … 🙄

However, the problem is still there even when adding that config 🤔

@llwt
Copy link
Contributor

llwt commented Oct 12, 2018

I had to remove the <rootDir> from ours:

const esModules = ['@agm', 'ngx-bootstrap'].join('|');
// ...
module.exports = {
  //...
    transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
  // ...
};

@luchsamapparat
Copy link
Contributor Author

Yes, that confirms what I just discovered while debugging Jest. The <rootDir> is resolved to the lib/app directory being tested, not the workspace root...

`/absolute/path/to/project/libs/libname/node_modules//(?!lodash-es/.*)

@luchsamapparat
Copy link
Contributor Author

I've been debugging this for the last hour. lodash-es is definitely being passed to transformSource in jest-runtime/build/script_transformer.js, however the transformedSource that is returned by that function still contains non-ES5 statements.

@luchsamapparat
Copy link
Contributor Author

I finally fixed the issue by adding "allowJs": true to the compilerOptions of each lib/app's tsconfig.spec.json (or alternatively to the root tsconfig.json). Of course in addition to setting transformIgnorePatterns as @llwt suggested. Without that option the TypeScript compiler just skipped the lodash files.

@llwt
Copy link
Contributor

llwt commented Oct 12, 2018

One other option is to pull in babel-jest and tell it to transpile those js files.

From the jest-preset-angular docs:

Transpile js files through babel-jest

Some vendors publish their sources without transpiling. You need to say jest to transpile such files manually since typescript (and thus ts-jest used by this preset) do not transpile them.

  1. Install babel-preset-env and add .babelrc (or modify existing if needed) with that contents:
{
  "presets": ["env"]
}
  1. Update Jest configuration (by default TypeScript process untranspiled JS files which is source of the problem):
{
  "jest": {
    "transform": {
      "^.+\\.(ts|html)$": "<rootDir>/node_modules/jest-preset-angular/preprocessor.js",
      "^.+\\.js$": "babel-jest"
    },
  }
}

We took that and tweaked it to only pass the js files needed through:

const esModules = ['@agm', 'ngx-bootstrap', 'lodash-es'].join('|');

module.exports = {
  // ...
  transform: {
    [`(${esModules}).+\\.js$`]: 'babel-jest',
    '^.+\\.(ts|js|html)$': 'jest-preset-angular/preprocessor.js',
    // ...
  },
  transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
  // ...
};

@skorunka
Copy link

I have the similar problem when trying to debug jest tests in VS Code:

 FAIL  apps/el/admin/src/app/app.component.spec.ts
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    <project_path>\apps\el\admin\src\app\app.component.html:2
    <div style="text-align:center">
    ^

    SyntaxError: Unexpected token <

      at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)
      at apps/el/admin/src/app/app.component.ts:26:15
      at Object.<anonymous> (apps/el/admin/src/app/app.component.ts:30:2)

It is a nrwl workspace with one app and one lib, no code changes.

Debugger works, the breakpoint is hit. But got the error from above.

npm run test works - all tests are green.

{
	"type": "node",
	"request": "launch",
	"name": "Jest All",
	"program": "${workspaceFolder}\\node_modules\\jest\\bin\\jest",
	"args": ["--runInBand", "--config=${workspaceFolder}\\jest.config.js"],
	"console": "integratedTerminal",
	"internalConsoleOptions": "neverOpen"
},

Any idea what might be wrong? Thank you.

@luchsamapparat
Copy link
Contributor Author

@skorunka have a look at https://github.com/nrwl/nx/blob/master/packages/builders/src/jest/jest.builder.ts

When you run your tests via npm run test, the globals config is set by Nx' Jest builder. You need to somehow also provide these globals in order to debug your tests with VS Code. I added that config to temporarily to my Jest config while debugging, but that's certainly not the nicest option...

@skorunka
Copy link

@luchsamapparat Hi, could you please share your modified jest config file you use for debugging? Thanks.

@skorunka
Copy link

I have added __TRANSFORM_HTML__: true to globals in ./jest.config.js file, like

	globals: {
		'ts-jest': {
			tsConfigFile: './tsconfig.json',
		},
		__TRANSFORM_HTML__: true,
	},

and now there is no problem with HTML. But the easy test for checking that the components can be created fails. It works from command line.

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { LoginInlineComponent } from './login-inline.component';

describe('FooterComponent', () => {
	let component: LoginInlineComponent;
	let fixture: ComponentFixture<LoginInlineComponent>;

	beforeEach(async(() => {
		TestBed.configureTestingModule({
			imports: [FormsModule, ReactiveFormsModule],
			declarations: [LoginInlineComponent],
		}).compileComponents();
	}));

	beforeEach(() => {
		fixture = TestBed.createComponent(LoginInlineComponent);
		component = fixture.componentInstance;
		fixture.detectChanges();
	});

	it('should create', () => {
		expect(component).toBeTruthy();
	});
});

The breakpoint is hit, but the component is undefined

image

Command line works es expected:

image

Any idea what I'm still missing in here? This drives me crazy. I love jest, but making it work/debug in Angular + VS Code is painful.

@skorunka
Copy link

After some debugging, I can actually see that there is an exception when executing:

fixture = TestBed.createComponent(LoginInlineComponent);

It goes in queue_runner.js:

image

@danieldanielecki
Copy link

Solved this without allowJs, the only 1 file which changed it was jest.config.js (local lib file, not in global jest.config.js) by adding transformIgnorePatterns lijke below

module.exports = {
  name: 'contact',
  preset: '../../jest.config.js',
  coverageDirectory: '../../coverage/libs/contact',
  transformIgnorePatterns: ['../../node_modules/(?!${ng2-tel-input})']
};

Where ng2-tel-input was the problematic module, getting it for the following environment: Jest 23.6.0, @nrwl/builders 7.5.1, @nrwl/nx 7.5.1, @nrwl/schematics 7.5.1. jest-preset-angular 6.0.1.

@danieldanielecki
Copy link

Solved this without allowJs, the only 1 file which changed it was jest.config.js (local lib file, not in global jest.config.js) by adding transformIgnorePatterns lijke below

module.exports = {
  name: 'contact',
  preset: '../../jest.config.js',
  coverageDirectory: '../../coverage/libs/contact',
  transformIgnorePatterns: ['../../node_modules/(?!${ng2-tel-input})']
};

Where ng2-tel-input was the problematic module, getting it for the following environment: Jest 23.6.0, @nrwl/builders 7.5.1, @nrwl/nx 7.5.1, @nrwl/schematics 7.5.1. jest-preset-angular 6.0.1.

Update: apologizes for the mistake, you have to include allowJs as well. The reason why I got it working is I didn't clean cache. Before testing remember to do so.

@whizkidwwe1217
Copy link

@llwt Where can I find the jest-preset-angular/preprocessor.js? It's not available in [email protected]

@ahnpnl
Copy link
Contributor

ahnpnl commented Apr 29, 2020

@llwt Where can I find the jest-preset-angular/preprocessor.js? It's not available in [email protected]

8.0.0 no longer has preprocessor.js in source . It is now delegated fully to ts-jest. Jest-preset-angular only applies custom ast transformers to ts-jest to alter the compiled output.

@Lonli-Lokli
Copy link

Still same issue with anagular, jest-babel does not work and I actually do not want to meess both of them just because of this lodash-es stuff...
wtf

@shayll-herolo
Copy link

the only thing that worked for me was to add this to the main jest.config.js

moduleNameMapper: {
  "^lodash-es$": "lodash"
}

make sure you have lodash in you devDependencies

@zhangchi1
Copy link

Solved this without allowJs, the only 1 file which changed it was jest.config.js (local lib file, not in global jest.config.js) by adding transformIgnorePatterns lijke below

module.exports = {
  name: 'contact',
  preset: '../../jest.config.js',
  coverageDirectory: '../../coverage/libs/contact',
  transformIgnorePatterns: ['../../node_modules/(?!${ng2-tel-input})']
};

Where ng2-tel-input was the problematic module, getting it for the following environment: Jest 23.6.0, @nrwl/builders 7.5.1, @nrwl/nx 7.5.1, @nrwl/schematics 7.5.1. jest-preset-angular 6.0.1.

Hi @danieldanielecki thank you so much for your answer. I'm wondering how did you find out which module was the problematic module? Thanks~

@danieldanielecki
Copy link

Solved this without allowJs, the only 1 file which changed it was jest.config.js (local lib file, not in global jest.config.js) by adding transformIgnorePatterns lijke below

module.exports = {
  name: 'contact',
  preset: '../../jest.config.js',
  coverageDirectory: '../../coverage/libs/contact',
  transformIgnorePatterns: ['../../node_modules/(?!${ng2-tel-input})']
};

Where ng2-tel-input was the problematic module, getting it for the following environment: Jest 23.6.0, @nrwl/builders 7.5.1, @nrwl/nx 7.5.1, @nrwl/schematics 7.5.1. jest-preset-angular 6.0.1.

Hi @danieldanielecki thank you so much for your answer. I'm wondering how did you find out which module was the problematic module? Thanks~

Sorry, but don't remember now... For sure it was one of those which caused errors on Server-Side Rendering (SSR)/Angular Universal.

@shivaaudam
Copy link

I had to remove the <rootDir> from ours:

const esModules = ['@agm', 'ngx-bootstrap'].join('|');
// ...
module.exports = {
  //...
    transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
  // ...
};

I am using deepdash in place of loadash added below in jest.preset.js

transform: {
'^.+\.(ts|js|html)$': 'babel-jest',
},
transformIgnorePatterns: [
"/node_modules/(?!deepdash-es/.*)"
],

and created a new file babel.config.json and added

{
"presets": ["@babel/preset-env"]
}

and inside individual lib jest.config.js added

transformIgnorePatterns: ['../../node_modules/(?!${deepdash-es})'],
transform: {
"^.+\.jsx?$": "babel-jest"
},
moduleNameMapper: {
"^deepdash-es$": "deepdash"
},

in tsconfig.spec.json added "allowJs": true,

nothing is working, still the issue persists, could you please help on this.

@umairhm
Copy link
Contributor

umairhm commented Dec 14, 2020

This is what worked for me. We were having issues consuming imask and countup.js libs and used the configs mentioned here: https://github.com/thymikee/jest-preset-angular#transpile-js-files-through-babel-jest

Thanks @llwt 🙂

One other option is to pull in babel-jest and tell it to transpile those js files.

From the jest-preset-angular docs:

Transpile js files through babel-jest

Some vendors publish their sources without transpiling. You need to say jest to transpile such files manually since typescript (and thus ts-jest used by this preset) do not transpile them.

  1. Install babel-preset-env and add .babelrc (or modify existing if needed) with that contents:
{
  "presets": ["env"]
}
  1. Update Jest configuration (by default TypeScript process untranspiled JS files which is source of the problem):
{
  "jest": {
    "transform": {
      "^.+\\.(ts|html)$": "<rootDir>/node_modules/jest-preset-angular/preprocessor.js",
      "^.+\\.js$": "babel-jest"
    },
  }
}

We took that and tweaked it to only pass the js files needed through:

const esModules = ['@agm', 'ngx-bootstrap', 'lodash-es'].join('|');

module.exports = {
  // ...
  transform: {
    [`(${esModules}).+\\.js$`]: 'babel-jest',
    '^.+\\.(ts|js|html)$': 'jest-preset-angular/preprocessor.js',
    // ...
  },
  transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
  // ...
};

@carlnc
Copy link

carlnc commented Feb 16, 2021

Keywords: monorepo jest typescript create-react-app

Works: cd packages/somePackage && yarn jest --no-cache --coverage
Fails: yarn jest --no-cache --coverage packages/somePackage

Error:

    Details:

    /Users/…/development/work/monorepo-root/node_modules/babel-preset-react-app/node_modules/@babel/runtime/helpers/esm/slicedToArray.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import arrayWithHoles from "@babel/runtime/helpers/esm/arrayWithHoles";
                                                                                             ^^^^^^

    SyntaxError: Cannot use import statement outside a module

Workaround:
in: jest.config.js

    transformIgnorePatterns: [
        `[/\\\\]node_modules[/\\\\](?!${[
            'babel-preset-react-app',
            '@babel',
        ].join('|')})`,
    ],

@ahnpnl
Copy link
Contributor

ahnpnl commented Feb 27, 2021

the only thing that worked for me was to add this to the main jest.config.js

moduleNameMapper: {
  "^lodash-es$": "lodash"
}

make sure you have lodash in you devDependencies

Actually this one is better

'^lodash-es/(.*)$': '<rootDir>/node_modules/lodash/$1',

Because mapping to lodash will resolve in LodashWrapper object instead of the function itself

@rikriki
Copy link

rikriki commented Mar 16, 2021

for next release of nx(11.5.2) you can do this to fix the issue.
Install babel-jest and @babel/preset-env

create babel.config,js in the root of the workspace and add this
module.exports = { presets: ['@babel/preset-env'] };

then in your jest,config.js add this
transformIgnorePatterns: [/node_modules/?!lodash-es],
transform: {
'^.+\.(ts|html)$': 'ts-jest',
'^.+\.js$': 'babel-jest'
}

@austinthedeveloper
Copy link

austinthedeveloper commented Dec 10, 2021

I was not able to get this solution to work.

'^lodash-es/(.*)$': '<rootDir>/node_modules/lodash/$1',

Instead I wrote it like this and it worked:

moduleNameMapper: {
    '^lodash-es/(.*)$': 'lodash/$1',
  },

@dholms
Copy link

dholms commented Jul 7, 2022

For anyone that is still running into this issue in the context of a monorepo (yarn workspace in my case), make sure that you are not hoisting the package that needs to be transpiled.

The solution that worked for me:

babel.config.js in the package root (not the workspace root):

module.exports = {
  presets: [['@babel/preset-env']],
}

Jest config:

const esModules = ['ipld-hashmap'].join('|') // can add other packages here

module.exports = {
  ...
  transform: {
    '^.+\\.ts$': 'ts-jest',
    "^.+\\.js?$": "babel-jest"
  },
  transformIgnorePatterns: [`<rootDir>/node_modules/(?!${esModules})`],
  ...
}

Root package.json

  "workspaces": {
    "packages": ["packages/*"],
    "nohoist": ["**/ipld-hashmap", "**/ipld-hashmap/**"]
  }

Any one of those four things (nohoist, transform, transformIgnorePatterns, and babel config) missing & it won't work.

@hoi4
Copy link

hoi4 commented Aug 28, 2022

I had this issue with Swiper and was able to get it to work by using the following in the app's jest.config.js file:

transformIgnorePatterns: ['node_modules/(?!(.*\\.mjs$|swiper|ssr-window|dom7))'],
modulePaths: ['<rootDir>/../../node_modules/swiper/angular/'],

Swiper depends on ssr-window and dom7 which is why I had to add these modules to the transformIgnorePatterns as well.

Note that modulePaths need to be absolute, therefore the usage of <rootDir>.

@david-morris
Copy link

I had this issue with react-merge-refs while using @swc/jest and setting transformIgnorePatterns wasn't helping.

The problem? If you're setting this up for an error coming from a .mjs file, you probably need to change your pattern under transform in your jest config, since its recommended pattern does not match .mjs files. Depending on what you have in your options on your @swc/jest transformer entry, it might make sense to add a new transformer entry, like this:

transform: {
...
    "node_modules\\/.+\\.(js)|(mjs)$": "@swc/jest",
...
}

This is in addition to the transformIgnorePatterns.

@asztal
Copy link

asztal commented Dec 1, 2022

In case it helps anyone - in my case, using a monorepo with babel-jest, I needed to add:

transform: {
    '\\.[tj]sx?$': ['babel-jest', { rootMode: 'upward' }]``
}

without the rootMode option, babel-jest was giving syntax errors when importing other libraries in the monorepo.

@McGiogen
Copy link

McGiogen commented Feb 3, 2023

asztal solution works for me with Nx 15.x, Angular 15.x and Jest 28.x

@github-actions
Copy link

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests