Clarity: Syntax Error in @clr/core when using Jest

Created on 13 Jan 2020  ·  45Comments  ·  Source: vmware/clarity

Describe the bug

After the @clr/core dependency of @clr/angular was introduced in 3.0, Jest is now throwing a syntax error coming from the new package.

node_modules\@clr\core\fesm2015\clr-core.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import { __decorate, __metadata, __awaiter } from 'tslib';
                                                                                                    ^

    SyntaxError: Unexpected token {

      at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:471:17)    
      at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:513:25)
      at node_modules/@clr/angular/bundles/clr-angular.umd.js:2:156
      at Object.<anonymous> (node_modules/@clr/angular/bundles/clr-angular.umd.js:5:2)

Test Suites: 2 failed, 4 passed, 6 total
Tests:       16 passed, 16 total

How to reproduce

_Please provide a link to a reproduction scenario using one of the Clarity Stackblitz templates. Reports without a clear reproduction may not be prioritized until one is provided._

Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior

_A clear and concise description of what you expected to happen._

Versions

App

  • Angular: [e.g. 6]
  • Node: [e.g. 8.10.0]
  • Clarity: [e.g. 0.12.5]

Device:

  • Type: [e.g. MacBook]
  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Additional notes

_Add any other notes about the problem here._

has workaround

Most helpful comment

Thanks @GrahamFord for sharing.

@coryrylan @TekSiDoT temporary workaround via moduleNameMapper

"moduleNameMapper": {
    "base.element.css": "@clr/core/common/base/base.element.css.js"
}

Tests are now working inside NX workspace

All 45 comments

Hi @whizkidwwe1217
Is it possible to reproduce this on a stackblitz to help us investigate what may be causing it?

Thanks,

Scott

Hi @mathisscott I don't know how to run unit tests in StackBlitz but you can clone https://github.com/whizkidwwe1217/jest-test. It's just very simple Angular app generated using ng new. Removed all karma files and replaced with Jest. To run, simply execute npm install and then npm test. Thank you.

As you can see in the spec, @clr/core is throwing error when the ClarityModule is added to the imports. It was working fine in Clarity v2.

import { TestBed } from "@angular/core/testing";
import { AppComponent } from "./app.component";
import { ClarityModule } from "@clr/angular";
import { HttpClientTestingModule } from "@angular/common/http/testing";
import { APP_BASE_HREF } from "@angular/common";

beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [HttpClientTestingModule, ClarityModule],
    declarations: [AppComponent],
    providers: [
      {
        provide: APP_BASE_HREF,
        useValue: "/"
      }
    ]
  }).compileComponents();
});

test("should create app component", () => {
  expect(true).toBeTruthy();
});

@whizkidwwe1217 what specific version of Clarity in your pacakge.json are you using? It looks like we had a couple of bad npm publishes and I am trying to narrow down the cause.

@coryrylan If I use 2.3.5 you can see that the simple test is passing but when using 2.3.5/6, I got a syntax error from @clr/core package.

v2.3.5 (branch: v2)
image

v3.0.0-next.5 (branch: master)
image

Please let me know if it's still feasible to use Jest for the latest Clarity. If not, then I guess I have to switch back to using Karma. Thanks!

It sounds like your configuration isn't handling ES6 syntax, which I'm not familiar with Jest enough but looks like you can add plugins to support this.

@gnomeontherun Thank you! I think I'll just switch back to Karma for now. That will be easier for me than going into crazy Jest configurations. It'll also be better if I just align my workflow with Clarity's since I'm using Clarity packages anyway. I'll close this for now.

@whizkidwwe1217 , @coryrylan : I had the same issue and solved it in the following way:
my package.json (you have to install babel to compile JS down to ES5):

    "jest": "~24.9.0",
    "@babel/preset-env": "~7.8.4",
    "babel-jest": "~24.9.0",

babel.config.js (in the root dir):

// https://babeljs.io/docs/en/babel-preset-env
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: {
          esmodules: true
        }
      }
    ]
  ]
};

jest.config.js (in the root dir; notice -> transform and transformIgnorePatterns):

module.exports = {
  moduleNameMapper: {
    '^@app/(.*)$': '<rootDir>/src/app/$1',
    '^@env/(.*)$': '<rootDir>/src/environments/$1',
  },
  coverageDirectory: 'jest/coverage',
  transform: {
    '^.+\\.js?$': 'babel-jest',
    '^.+\\.ts?$': 'ts-jest'
  },
  transformIgnorePatterns: [
    'node_modules/?!(@clr)'
  ]
};

The initial test run needs some time, but then it (I think is cached) and will run a lot faster.
Hope it works for you as well ;-)

@alexej-strelzow Thanks for the workaround suggestion. Unfortunately, we've hit a wall with that approach:

   Cannot find module './base/base.element.css' from 'index.js'

      at Resolver.resolveModule (../../node_modules/jest-resolve/build/index.js:276:11)
      at Object.<anonymous> (../../node_modules/src/clr-core/common/index.ts:26:1)

This is most likely related to the fix not being 100% transferable to using nrwl/nx for multi-project mono-repo management.

This effectively shuts down about half of our tests - and we don't have the freedom to arbitrarily switch test runners.

@TekSiDoT which test runner is specifically not working with Clarity? To make sure I understand, you are using nrwl/nx for your project? I do not want this being a blocker issue. If this is a problem with nx please open an issue with as much detail as you can provide and I will be sure to investigate.

@coryrylan Thank you, I'll get back to you with as many details / a repro repository as soon as I can.

A reproduction repository is now available at https://github.com/TekSiDoT/clarity-jest-nx-repro .

  • Yes, we're using nrwl/nx for mono repo management
  • We're using Jest as test runner

If you think this is an issue with nx rather than Clarity let me know, I'll open an issue over there. Thanks for your time!

@TekSiDoT hey thank you so much for the repro! I'll take a look at this asap to see if I can narrow it down. I can reach out to nrwl if needed.

We have a functional workaround, but at the cost of 5x testing time (presumably because of down compiling clarity core to es5):

in ./jest.config.js set transformIgnorePatterns to make sure that clarity core (and a couple others for us) get considered for inclusion in jest:

module.exports = {
  transform: {
    '^.+\\.(ts|js|html)$': 'ts-jest'
  },
  transformIgnorePatterns: ['node_modules/(?!@clr/core|lit-element|lit-html|ramda)'],
  resolver: '@nrwl/jest/plugins/resolver',
  moduleFileExtensions: ['ts', 'js', 'html'],
  collectCoverage: true,
  coverageReporters: ['html']
};

Then, in each project's tsconfig.spec.ts allow js, and force the es5 target:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "allowJs": true,
    "target": "ES5",
    "outDir": "../../../dist/out-tsc/apps/analytics-launchpad",
    "module": "commonjs",
    "types": ["jest", "node"]
  },
  "files": ["src/test-setup.ts"],
  "include": ["**/*.spec.ts", "**/*.d.ts"]
}

@RobHelgeson Thank you. Unfortunately, your workaround does not seem to work with the reproduction project ...

@RobHelgeson is there anything in your test-setup.ts that may be contributing to the tests running?

I've got a Jest setup via @angular-builders/jest with the Babel changes suggested by https://github.com/thymikee/jest-preset-angular#unexpected-token-importexportother. My setup is pretty much equivalent to what you've got but I'm still seeing the message

Cannot find module '@babel/runtime/helpers/interopRequireDefault' from 'button.base.js'

I'm using a straight Angular setup without Nx.

@TekSiDoT Did you manage to get this fixed? Thanks

@coryrylan Can we not build clr-core through ng-packagr to ship all the bundles? Jest plays nicely with commonjs.

Jest tests with latest Clarity run perfectly for me by just following the instructions in the jest preset angular repo.

@whizkidwwe1217 Thanks, I see that you are using angular-builders/jest, with nx we use @nrwl/jest.

Not sure about the differences, here is my jest config. I have tried babel-jest as well but no luck. I keep getting error at clr-angular/utils/i18n/common-strings.service.ts where CommonStringsServiceInternal is being resolved as null or undefined since it's not being converted to commonjs for some reason.

Error

TypeError: Object prototype may only be an Object or null: undefined
        at setPrototypeOf (<anonymous>)

jest.config.json

"transform": {
    "^.+\\.(ts|js|html)$": "ts-jest"
  },
"transformIgnorePatterns": ["node_modules/(?!(lodash-es|@swimlane|decode-html-entities|@clr/core|lit-html|lit-element|ramda))"],

@vinayakpatil no, we're currently waiting for @coryrylan's investigation into this issue. Until then, we've pushed our Angular 9 / Clarity 3 update schedule.

I am still investigating this. Due to the different Jest packages I am reopening this for tracking.

Thanks @coryrylan , with some tweaks I am now getting the exact same error mentioned by @TekSiDoT

Cannot find module './base/base.element.css' from 'index.js'

      at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:230:17)
      at Object.<anonymous> (node_modules/@clr/core/common/index.js:23:1)

@vinayakpatil @coryrylan

I did manage to get my tests running. I had to include a babel.config.js with this content.

module.exports = function(api) {
  api.cache(true);

  const presets = ["@babel/preset-env"];
  const plugins = [
    ["@babel/plugin-transform-runtime", {
        regenerator: true
    }]
  ];

  return {
    presets,
    plugins
  };
};

And this is my Jest config

module.exports = {
  preset: 'jest-preset-angular',
  roots: ['<rootDir>/src/'],
  testMatch: ['**/+(*.)+(spec).+(ts)'],
  setupFilesAfterEnv: ['<rootDir>/src/test.ts'],
  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths || {}, {
    prefix: '<rootDir>/'
  }),
  transformIgnorePatterns: [
    'node_modules/(?!(@clr/core/common|lit-html|lit-element|ramda)/)'
  ],
  transform: {
    "^.+\\.(ts|html)$": "ts-jest",
    "^.+\\.js$": "babel-jest"
  },
};

In my tsconfig.spec.json I have

    "module": "commonjs",
    "allowJs": true,
    "target": "ES5"

I had to install the packages
"@babel/plugin-transform-regenerator": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.8.3",
"@babel/runtime": "^7.8.4",
"babel-jest": "^25.1.0",

Thanks @GrahamFord for sharing.

@coryrylan @TekSiDoT temporary workaround via moduleNameMapper

"moduleNameMapper": {
    "base.element.css": "@clr/core/common/base/base.element.css.js"
}

Tests are now working inside NX workspace

@vinayakpatil That did the trick (at least for the reproduction repository), I'll check with our main one and report back.

Update:
This workaround works for around 95% of our tests. The remaining 5% now throw TypeError: Class constructor CommonStringsServiceInternal cannot be invoked without 'new'. This is probably related to Babels down-compilation and https://github.com/vmware/clarity/issues/4094

Overall test-duration has increased from around 60 seconds to around 80 seconds so far.

@TekSiDoT
The code above from @alexej-strelzow solved that problem for me.

Specifically the following config option...

  transformIgnorePatterns: [
    'node_modules/?!(@clr)'
  ]

Hey there @mathisscott, thanks for chiming in. Yes, ignoring Clarity modules for transform is one of the elements of the current approach, which unfortunately doesnt yet get us all the way to a solution.

@RobHelgeson is there anything in your test-setup.ts that may be contributing to the tests running?

@GrahamFord here is my test-setup.ts:

import 'jest-canvas-mock';
import 'jest-preset-angular';

@vinayakpatil Thank you for the moduleNameMapper tip!

FYI, if you encounter the issue that your tests run a lot longer when you use transformIgnorePatterns and allowJs: true, I have found, that adding:

  globals: {
    'ts-jest': {
      diagnostics: false,
    },
  },

to your jest.config.js file will ease the pain a little.

@vinayakpatil That did the trick (at least for the reproduction repository), I'll check with our main one and report back.

Update:
This workaround works for around 95% of our tests. The remaining 5% now throw TypeError: Class constructor CommonStringsServiceInternal cannot be invoked without 'new'. This is probably related to Babels down-compilation and #4094

Overall test-duration has increased from around 60 seconds to around 80 seconds so far.

@TekSiDoT We ran into the same error like your 5% (CommonStringsServiceInternal), did you find a solution for this?

@twittwer I'm afraid not, still waiting for a solution

@TekSiDoT ok, that's sad - anyway thx for your quick response

@coryrylan @mathisscott will the resolution: wontfix remain or will there be some investigation around the usage of Clarity in nx workspaces with their recommended unit test runner Jest

@twittwer I did not face such issue since my target is es6, if it helps.

"module": "commonjs",
"target": "es6"

@twittwer
@coryrylan reopened this 8 days ago and didn't remove the resolution: wontfix label. Happy to see that people are paying attention to the labels! 😊

That said, this error is debatably _not_ a Clarity issue. I've gotten @clr/core working in two different versions of Jest. And likely our "fix" is just going to be showing people options to fix this issue in their projects.

The fix from @alexej-strelzow has worked in every instance I've come across with Jest. The crux of the problem appears to be that Jest is compiling things down to commonjs in a sneaky fashion so it needs es6 dependencies to compile down to es5. That means you'll need @clr/core, @clr/angular, and possibly the dependencies of lit-element, lit-html, et al. to likewise get compiled down in Jest's sneaky way of doing things.

It seems that Jest may be working on addressing this but it seems like a fairly big task on their end.

We'll keep working on different examples of configurations to help people out. But please note this is a Jest and a an app configuration issue. It's not a top priority for us because @clr/core isn't actually causing the problem.

I recommend working with transformIgnorePatterns in your config. You may need to include more than just core as Jest may be trying to sneaky compile @clr/core or @clr/angular's dependencies as well.

@TekSiDoT
My last commit on this fork has all the tests running. There's one minor issue with a core filename that's throwing a wrench in the works.

@mathisscott Thank you! Excited to try your solution out as soon as possible.

@TekSiDoT
My last commit on this fork has all the tests running. There's one minor issue with a core filename that's throwing a wrench in the works.

@mathisscott Thx for your response

I added a lib to this fork of the repo to reproduce our error in a simple us case. Can you have a look at it?

@twittwer
I spent way too much time on that repo this morning. I have to hop off for now but here's what I ran into.

It seems libraries are more affected by Jest's stealthy CommonJS transpiles and dependency than applications.

This is a well known (dare I say) "problem?" with Jest and there's an entire thread about it here (https://github.com/facebook/jest/issues/4842).

I think it's important to point out that this is not a Clarity issue but it's Clarity trying to help you work through a Jest configuration issue.

That said, I'll keep plinking away at this problem in hopes that I can find a solution. Unfortunately, I'm out of pocket after today so the earliest I can get back to it is a week and half or two weeks from now. And it's an extra-curricular. Meaning I can't commit to when or if I can find a solution.

As noted above, this is an issue with Jest. It's one they may or may not be addressing in Jest 25 and looks like a peculiar architecture decision in Jest's early days led us all here.

In addition, if you could put some time towards solving the problem, we'd appreciate you posting back whatever you find to help the overall community (both Jest and Clarity).

@mathisscott thx for the ongoing investigation
Based on my today's investigation I do not think the problem appearance is based on lib or app but which component is used. So I get the error in apps as well if components with a dependency to the CommonStringService, like clr-alert.

@twittwer
So a component that depends on CommonStringService triggers it? That helps. Thanks!

Note that Jest issue I posted has many, many attempts at workarounds (with varying degrees of success). There is one where CommonJS (or Jest's fork of it...) translates an extends into something ES5 dies on. That may be what we are seeing. I have a couple of unrelated PRs to finish up today. If I get extra time, I'll look into that because I know CommonStringsService extends CommonStringsServiceInternal from @clr/core. That at least gets us closer...

Hey guys, just a quick update on my end. I updated to jest 25.1 recently to get rid of performance problems caused by jest 24.9 and babel. ts-jest as explicit devDependency lets you get rid of the warning that ts-jest 24.x is not compatible with jest 25.1. But also with an older version it works normal.

My final configuration looks like this and is working like a charm:

package.json (with Angular 9)

    "@clr/angular": "~3.0.0",
    "@clr/core": "~3.0.0",
    "@clr/ui": "~3.0.0",

    RELEVANT DEV DEPENDENCIES
    "@types/jest": "~25.1.2",
    "@types/node": "~12.12.26",
    "babel-jest": "~25.1.0",
    "jest": "~25.1.0",
    "ts-jest": "~25.2.1",
    "ts-node": "~8.3.0",
    "typescript": "~3.7.5",

jest.config.js:

module.exports = {
  moduleNameMapper: {
    '^@app/(.*)$': '<rootDir>/src/app/$1',
    '^@env/(.*)$': '<rootDir>/src/environments/$1'
  },
  coverageDirectory: 'jest/coverage',
  transform: {
    '^.+\\.js?$': 'babel-jest',
    '^.+\\.ts?$': 'ts-jest'
  },
  transformIgnorePatterns: [
    '<rootDir>/node_modules/(?!(@clr/core|lit-element|lit-html|ramda)/)'
  ],
  testMatch: ['<rootDir>/src/app/**/*.spec.ts']
};

babel.config.js:

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

tsconfig.json:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./src",
    "downlevelIteration": true,
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "module": "esnext",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "paths": {
      "@app/*": [
        "app/*"
      ],
      "@env/*": [
        "environments/*"
      ]
    },
    "importHelpers": true,
    "target": "es2015",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  },
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": true
  }
}

tsconfig.spec.json:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/spec",
    "types": ["jest"],
    "esModuleInterop": true
  },
  "files": [
    "src/polyfills.ts"
  ],
  "include": ["**/*.spec.ts", "**/*.d.ts"]
}

I still have some workarounds here and there where I use the ClrTab, e.g. - I overwrite the template with a blank to make the test pass:

    TestBed.configureTestingModule({
      imports: [LucyWebSharedModule.forRoot(), LocaleManagerModule],
      providers: [...TEST_PROVIDER]
    }) // TODO: below workaround for https://github.com/vmware/clarity/issues/4094
      .overrideTemplate(LocaleManagerComponent, '');

Error w.o. this fix:

 FAIL  src/app/features/locale-manager/locale-manager.component.spec.ts (9.925s)
  ● LocaleManagerComponent › should create

    TypeError: Class constructor CommonStringsServiceInternal cannot be invoked without 'new'

      at new ClrCommonStringsService (node_modules/@clr/angular/bundles/clr-angular.umd.js:1425:46)
      at ClrCommonStringsService_Factory (node_modules/@clr/angular/bundles/clr-angular.umd.js:1427:131)
      at _callFactory (../packages/core/src/view/ng_module.ts:196:14)
      at _createProviderInstance (../packages/core/src/view/ng_module.ts:149:20)
      at resolveNgModuleDep (../packages/core/src/view/ng_module.ts:122:15)
      at NgModuleRef_.Object.<anonymous>.NgModuleRef_.get (../packages/core/src/view/refs.ts:390:12)
      at resolveDep (../packages/core/src/view/provider.ts:418:43)
      at createClass (../packages/core/src/view/provider.ts:282:24)
      at createDirectiveInstance (../packages/core/src/view/provider.ts:135:20)
      at createViewNodes (../packages/core/src/view/view.ts:303:28)
      at callViewAction (../packages/core/src/view/view.ts:636:7)
      at execComponentViewsAction (../packages/core/src/view/view.ts:559:7)
      at createViewNodes (../packages/core/src/view/view.ts:331:3)
      at createRootView (../packages/core/src/view/view.ts:210:3)
      at callWithDebugContext (../packages/core/src/view/services.ts:629:23)
      at Object.debugCreateRootView [as createRootView] (../packages/core/src/view/services.ts:121:10)
      at ComponentFactory_.Object.<anonymous>.ComponentFactory_.create (../packages/core/src/view/refs.ts:92:27)
      at initComponent (../../packages/core/testing/src/test_bed.ts:612:28)
      at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:396:30)
      at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:117:43)
      at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:395:36)
      at Object.onInvoke (../packages/core/src/zone/ng_zone.ts:302:25)
      at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:395:36)
      at Zone.run (node_modules/zone.js/dist/zone.js:153:47)
      at NgZone.Object.<anonymous>.NgZone.run (../packages/core/src/zone/ng_zone.ts:178:50)
      at TestBedViewEngine.Object.<anonymous>.TestBedViewEngine.createComponent (../../packages/core/testing/src/test_bed.ts:616:56)
      at Function.Object.<anonymous>.TestBedViewEngine.createComponent (../../packages/core/testing/src/test_bed.ts:241:36)
      at beforeEach (src/app/features/locale-manager/locale-manager.component.spec.ts:21:23)
      at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:396:30)
      at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:117:43)
      at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:395:36)
      at Zone.run (node_modules/zone.js/dist/zone.js:153:47)


But all in all I am super happy with my current working setup :)

That's awesome, @alexej-strelzow

Thank you so much for sharing!

We'll continue to look into these configurations as time allows but most certainly appreciate the guidance and help.

Thx @alexej-strelzow , with the idea of overriding I was able to get around the CommonStringsServiceInternal Error by overriding the problematic ClrCommonStringsService itself.

TestBed.configureTestingModule({
    imports: [ClarityModule],
    declarations: [AlertsComponent],
    providers: [
        {
            provide: ClrCommonStringsService,
            useClass: CommonStringsServiceInternal
        }
    ]
});

I'm not sure it this approach can be problematic regarding test results (bug detection).

Currently, there are some other errors I have to investigate - but so far it felt like a huge step forward

Closing as this dependency was removed from clr/angular. We have verified Jest working with clr/core outside of this dependency.

Hi there 👋, this is an automated message. To help Clarity keep track of discussions, we automatically lock closed issues after 14 days. Please look for another open issue or open a new issue with updated details and reference this one as necessary.

Was this page helpful?
0 / 5 - 0 ratings