Node modules not being parsed correctly for tests? - javascript

I'm working in an application and am trying to add a testing framework to automate testing across the app. I'm using Vue, Electron, TypeScript, and Node and am having trouble getting any test that actually uses components to run. For some reason Jest doesn't seem to want to work with the node modules... I have had very little luck finding anything online that points to ways to fix this.
Anyone with any knowledge on the subject who has any pointers or ideas on how to resolve the issue with importing these node modules while running the tests would be greatly appreciated. I can't imagine that this is impossible to do but there really isn't much I can find through my searches.
Error:
$ npm run test
> mdc#1.0.0 test C:\Users\daniel\Desktop\MDC\mdc
> jest --detectOpenHandles
FAIL src/app/features/mdc-window/mdc-window.component.test.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:
C:\Users\daniel\Desktop\MDC\mdc\node_modules\bootstrap-vue\es\components\modal\modal.js:3
import bBtn from '../button/button';
^^^^^^
SyntaxError: Unexpected token import
> 1 | import bModal from 'bootstrap-vue/es/components/modal/modal'
| ^
2 | import bTooltip from 'bootstrap-vue/es/components/tooltip/tooltip'
3 | import throttle from 'lodash.throttle'
4 | import Vue from 'vue'
at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)
at Object.<anonymous> (src/app/features/mdc-window/mdc-window.component.ts:1:1)
at Object.<anonymous> (src/app/features/mdc-window/mdc-window.component.test.ts:3:1)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 9.13s
Ran all test suites.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! mdc#1.0.0 test: `jest --detectOpenHandles`
npm ERR! Exit status 1
jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
verbose: true,
testPathIgnorePatterns: [
'/build/',
'/config/',
'/data/',
'/dist/',
'/node_modules/',
'/test/',
'/vendor/'
],
globals: {
'ts-jest': {
tsConfig: './src/app/tsconfig.json'
}
}
}
test.ts
import { expect } from 'chai'
import 'jest'
import { MDCWindowComponent } from './mdc-window.component'
const mdcWindowComponent = new MDCWindowComponent()
describe('Test: Set Title Function', () => {
it('should set the variable title to the passed variable', () => {
const title = 'this is a test'
mdcWindowComponent.setTitle(title)
expect(mdcWindowComponent.title).to.equal(title)
})
tsconfig
{
"compilerOptions": {
"allowJs": true,
"outDir": "./built/",
"sourceMap": true,
"strict": true,
"moduleResolution": "node",
"target": "ES2017",
"experimentalDecorators": true,
"noImplicitAny": false,
"emitDecoratorMetadata": true,
"allowSyntheticDefaultImports": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"lib": ["es2017", "dom"]
},
"include": [
"**/*",
"../types/**/*",
"../../node_modules/bootstrap-vue/**/*",
"../../node_modules/electron/**/*"
]
}
relevant packages
"#types/jest": "^23.3.9",
"jest": "^23.6.0",
"ts-jest": "^23.10.4",

To fix this specific error
SyntaxError: Unexpected token import
I fixed this using the following jest.config.js
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'node',
verbose: true,
moduleDirectories: ['node_modules', 'src'],
modulePaths: ['<rootDir>/src', '<rootDir>/node_modules'],
moduleFileExtensions: ['js', 'ts', 'json', 'node'],
transformIgnorePatterns: ['node_modules/(?!(bootstrap-vue)/)'],
testPathIgnorePatterns: [
'/build/',
'/config/',
'/data/',
'/dist/',
'/node_modules/',
'/test/',
'/vendor/'
],
globals: {
'ts-jest': {
tsConfig: './src/app/tsconfig.json'
},
NODE_ENV: 'test'
}
}
Note
preset: 'ts-jest/presets/js-with-ts',
transformIgnorePatterns: ['node_modules/(?!(bootstrap-vue)/)'],
this seemingly was what helped fix the issue, though if you compare the jest.config.js in this answer to my response i did add a few other options which may also be helpful for others.
A healthy amount of reading on the ts-jest config documentation, jest config documentation, and tsconfig documentation does wonders.

Related

Is it possible to preload dynamic module imported via dynamic import in Webpack (Next.js)?

Let's say that I have private npm package called '#plugin/test-plugin' which is added to dependencies in package.json of Next.js app.
Then, I import it using dynamic import like so:
// [next.js app]/pages/index.tsx
import('#plugin/test-plugin');
// [next.js app]/package.json
...
"dependencies": {
...
"#plugin/test-plugin": "0.0.0"
}
...
That works fine, webpack can handle it and I can see exported module.
But my goal here is to make this plugin name dynamic from array (every plugin will be added in dependencies in package.json):
// [next.js app]/pages/index.tsx
const data = [
'test-plugin'
]
const pluginImports = data.map(n => import(`#plugin/${n}`))
Webpack finds the plugin but has a problem with loading it - it stops on plugin's index.d.ts giving following error:
app: error - ../plugins/test-plugin/lib/cjs/index.d.ts
app: Module parse failed: Unexpected token (2:8)
app: You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
app: | /// <reference types="react" />
app: > declare const linkDatabasePlugin: {
app: | component: () => JSX.Element;
app: | meta: {
#plugin/test-plugin has only one file "index.tsx" for testing:
// [#plugin/test-plugin]/src/index.tsx
import React from "react";
const A = () => {
return <div>Test</div>
}
const linkDatabasePlugin = {
component: A,
meta: {
name: "Test Plugin Interface"
}
}
export { linkDatabasePlugin as DATABASE_PLUGIN_MANIFEST };
Plugin is built using native typescript with following tsconfig.json with command tsc && tsc --module commonjs --outDir lib/cjs:
{
"compilerOptions": {
"outDir": "lib/esm",
"module": "esnext",
"target": "es5",
"lib": [
"es6",
"dom",
"es2016",
"es2017"
],
"jsx": "react",
"declaration": true,
"moduleResolution": "node",
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"allowSyntheticDefaultImports": true
},
"include": [
"src"
],
"exclude": [
"node_modules",
"lib"
]
}
As an output I'm getting built library as so:
- lib
--- cjs
------ index.d.ts
------ index.js
--- esm
------ index.d.ts
------ index.js
So the real problem is that Webpack doesn't know how to handle those files in runtime. Is there any way to tell webpack to preload some modules from 'node_modules' in webpack.config for the Next.js app? Plugins and Next.js app are in monorepo and plugins are shared to Next.js app using Lerna. Everything works if I type static names, but it isn't an option for future app grow.
I will always know what plugins to load (plugin names are static during runtime).
I've made research of my own and I haven't found solution for this type of case. Maybe webpack has some functions which will allow me to preload those modules that I'm not aware of.
Is there a solution or any workaround?

Jest fails with "Unexpected token *" on import statement

Why does Jest fail with "Unexpected token *" on a simple import statement???
Error log:
Admin#Admin-PC MINGW32 /d/project (master)
$ npm run test
> MyApp#0.0.1 test D:\project
> jest
FAIL __tests__/App-test.tsx
? 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:
D:\project\node_modules\react-navigation-tabs\src\navigators\createBottomTabNavigator.js:3
import * as React from 'react';
^
SyntaxError: Unexpected token *
14 | // );
15 |
> 16 | export default createBottomTabNavigator({
| ^
17 | map: {
18 | screen: MapView,
19 | navigationOptions: {
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 Object.get createBottomTabNavigator [as createBottomTabNavigator] (node_modules/react-navigation-tabs/src/index.js:9:12)
at Object.<anonymous> (src/app/main.view.tsx:16:16)
FAIL src/component/reinput/example/__tests__/index.ios.js (19.352s)
? Console
console.error node_modules/react-native/Libraries/YellowBox/YellowBox.js:59
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
? renders correctly
Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
at invariant (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:55:15)
at createFiberFromTypeAndProps (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2054:11)
at createFiberFromElement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2075:15)
at reconcileSingleElement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4605:23)
at reconcileChildFibers (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4662:35)
at reconcileChildren (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6329:28)
at updateHostRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6741:5)
at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7566:14)
at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11234:12)
at workLoop (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11266:24)
FAIL src/component/reinput/example/__tests__/index.android.js (19.365s)
? Console
console.error node_modules/react-native/Libraries/YellowBox/YellowBox.js:59
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
? renders correctly
Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
at invariant (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:55:15)
at createFiberFromTypeAndProps (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2054:11)
at createFiberFromElement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2075:15)
at reconcileSingleElement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4605:23)
at reconcileChildFibers (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4662:35)
at reconcileChildren (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6329:28)
at updateHostRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6741:5)
at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7566:14)
at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11234:12)
at workLoop (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11266:24)
Test Suites: 3 failed, 3 total
Tests: 2 failed, 2 total
Snapshots: 0 total
Time: 22.774s
Ran all test suites.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! MyApp#0.0.1 test: `jest`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the MyApp#0.0.1 test script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\Admin\AppData\Roaming\Roaming\npm-cache\_logs\2019-04-22T11_52_36_984Z-debug.log
package.json file:
{
"name": "MyApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.8.3",
"react-native": "0.59.4",
"react-native-gesture-handler": "^1.1.0",
"react-native-reanimated": "^1.0.1",
"react-native-splash-screen": "^3.2.0",
"react-navigation": "^3.8.1",
"react-navigation-tabs": "^2.1.1"
},
"devDependencies": {
"#babel/core": "^7.4.3",
"#babel/runtime": "^7.4.3",
"#types/jest": "^24.0.11",
"#types/react": "^16.8.13",
"#types/react-dom": "^16.8.4",
"#types/react-native": "^0.57.46",
"#types/react-test-renderer": "^16.8.1",
"babel-jest": "^24.7.1",
"jest": "^24.7.1",
"metro-react-native-babel-preset": "^0.53.1",
"react-test-renderer": "16.8.3",
"typescript": "^3.4.3"
},
"jest": {
"preset": "react-native"
}
}
babel.config.js file:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
};
jest.config.js file:
module.exports = {
preset: 'react-native',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
}
Note: I am using react-native type-script template, like react-native init MyApp --template typescript
Some react-native libraries ship uncompiled ES6 code.
ES6 code needs to be compiled before it can be run by Jest.
The Jest doc about Testing React Native Apps includes a section about compiling dependencies that don't ship pre-compiled code.
You will need to tell Jest to compile react-navigation-tabs by whitelisting it in the transformIgnorePatterns option in your Jest config.
Example:
Changing the jest.config.js file into something like below, fixed the issue mentioned in OP.
But the react-native-reanimated module (which requires native integration) needs further work, and we should "Mock" modules with such native requirements (as described in another post).
module.exports = {
preset: 'react-native',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
transformIgnorePatterns: [
"node_modules/(?!(react-native"
+ "|react-navigation-tabs"
+ "|react-native-splash-screen"
+ "|react-native-screens"
+ "|react-native-reanimated"
+ ")/)",
],
}
Note that the transformIgnorePatterns option (which is an array of Regular-Expressions) is originally meant to exclude files from being compiled, but using (?!(some-dir-name|another-name)) pattern, with the "(?!...)" negative look-ahead, we do tell Jest to exclude anything in node_modules directory, except the names that we did specify.
As it has been said, some modules needs to be transpiled, & some don't.
Here is a regex I use that work in a lot of projects
"jest": {
"preset": "react-native",
"transformIgnorePatterns": [
"node_modules/(?!(jest-)?react-native|react-(native|universal|navigation)-(.*)|#react-native-community/(.*)|#react-navigation/(.*)|bs-platform|(#[a-zA-Z]+/)?(bs|reason|rescript)-(.*)+)"
]
}
It's working for most common react native thing, & include also a specific package (here bs-platform) as an example, when isn't captured by previous patterns.
I had a similar issue on a React + Typescript app.
The first mistake I made was to define the jest.config.js as jest.config.ts
Running on Node v12.latest
Then the configuration that worked for me were the following:
// jest.config.js
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
roots: ["./src"],
transform: { "\\.ts$": ["ts-jest"] },
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
globals: {
"ts-jest": {
tsConfig: {
// allow js in typescript
allowJs: true,
},
},
},
};
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"baseUrl": "."
},
"include": ["src"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
// package.json
"devDependencies": {
"#types/jest": "^26.0.5",
"jest": "^26.1.0",
"ts-jest": "^26.1.3"
}
If you're using not only React Native but also Expo, I fixed the same issue but for Expo components by following the official guide, Testing with Jest. Specifically, install via package manager the jest preset jest-expo, and then update my package.json as they describe:
"jest": {
"preset": "jest-expo",
"transformIgnorePatterns": [
"node_modules/(?!(jest-)?react-native|react-clone-referenced-element|#react-native-community|expo(nent)?|#expo(nent)?/.*|react-navigation|#react-navigation/.*|#unimodules/.*|unimodules|sentry-expo|native-base|#sentry/.*)"
]
}
I also needed to add to the end of the transformIgnorePatterns regexp string an exception for |.*font.* as the Expo Fonts were what were giving me trouble. Probably I could have made it a bit more specific, but I hate regex, and this worked so I left it there!
Somewhere in your config file (.babelrc.js or package.json) you have to have "modules" under "presets" set to one of the "amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false.
referer this fragment from the documentation
something like this:
"presets": [
[
"#babel/preset-env", {
"targets": process.env.BABEL_TARGET === 'node' ? {
"node": 'current'
} : {
"browsers": [ "last 2 versions" ]
},
"loose": true,
"modules": 'commonjs'
}
]
]
I am using react-native-web and the fix for my issue was to add the react-native-web preset to my jest.config.js:
module.exports = {
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
timers: 'fake',
testPathIgnorePatterns: [
'<rootDir>/build/',
'<rootDir>/node_modules/',
'<rootDir>/rndemo/build/',
],
globals: {
'ts-jest': {
diagnostics: {
warnOnly: true,
},
},
},
preset: 'react-native-web',
}
modifying babel.config.js to below worked for me. Also presets should come first and then all other configs.
module.exports = {
presets: [['#babel/preset-env',{targets: {node:
'current',},loose:true,},],],
}

..._1.default is not a constructor when testing TypeScript with Mocha

I'm trying to get unit tests with mocha to work. I'm using typescript which gets compiled down to plain javascript with tsc. I'm always getting the error:
src\index.ts:22
[new FrontendEndpoint(), ...],
^
TypeError: v1_1.default is not a constructor
I approached two ways (and ran into the same problem twice):
First I created a dummy test test.test.ts, importing some of my modules for testing purposes:
import { APIServer } from './../api/index';
import { describe } from 'mocha';
import FrontendEndpoint from '../api/endpoints/frontend/v1';
import { SocketConnector } from '../api/sockets/socketio';
describe('TestTest', () => {
it('should run', (done) => {
const server = new APIServer(4000, [new FrontendEndpoint()], new SocketConnector([]));
done();
});
});
Using ts-mocha
Installed ts-mocha, mocha, #types/mocha
Ran ts-mocha src/test/test.test.ts
Using mocha & compiled ts files
Installed mocha, #types/mocha
Ran mocha build/test/test.test.js
Both ways produce the error above.
index.ts looks like this:
import FrontendEndpoint from './api/endpoints/frontend/v1';
[...]
new FrontendEndpoint()
Compiled (index.js):
[...]
const v1_1 = require("./api/endpoints/frontend/v1");
[...]
new v1_1.default()
And frontend/v1.ts:
export default class FrontendEndpoint {
[...]
}
Compiled (v1.js):
class FrontendEndpoint {
[...]
}
exports.default = FrontendEndpoint;
My tsconfig looks like this:
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"noImplicitReturns": true,
"noImplicitAny": true,
"preserveConstEnums": true,
"strictPropertyInitialization": false,
"experimentalDecorators": true,
"typeRoots": [
"src/types"
],
"emitDecoratorMetadata": true,
"sourceRoot": "src",
"outDir": "build"
},
"compileOnSave": true,
"exclude": [
"node_modules",
"coverage",
"build",
"logs"
],
}
It seems to only have issues with default exports. Why don't they work as expected? When running the app using node build/index.js everything is working fine, the default exports/imports work as expected.
I'm having the same issues when trying to add unit tests to my frontend React app with Webpack, with Mocha and with Jest. Do I miss something completely?
I found the solution myself.
While debugging my tests I discovered that some of the exports are not being called. This is due to cyclic dependencies of the files, that prevented them from being exported correctly.
After finding these cycles using https://github.com/pahen/madge and resolving them, running tests is working just fine.

getting nyc/istanbul coverage report to work with typescript

I'm struggling to get proper coverage with nyc/istanbul for my typescript/mocha/gulp project. I've tried a number of approaches, some of them seem to be unable to use source maps and other fails due to ts-node/tsc errors. My current setup is:
nyc relevant config in package.json
"scripts": {
"test:coverage": "nyc npm run test:unit",
"test:unit": "gulp mocha"
}
"nyc": {
"check-coverage": true,
"all": true,
"extension": [
".js",
".jsx",
".ts",
".tsx"
],
"include": [
"src/**/!(*.test.*).[tj]s?(x)"
],
"reporter": [
"html",
"lcov",
"text",
"text-summary"
],
"report-dir": "docs/reports/coverage"
}
gulpfile.js mocha relevant part
const SRC_DIR = path.join(__dirname, 'src');
const SRC_FILES = path.join(SRC_DIR, '**', '*.[jt]s?(x)');
const TEST_FILES = path.join(SRC_DIR, '**', '*.test.[jt]s?(x)');
const MOCHA_CONFIG = {
src: [
TEST_FILES
],
watchSrc: [
SRC_FILES,
TEST_FILES
],
mocha: {
// compilers: [
// 'ts:ts-node/register',
// 'tsx:ts-node/register'
// ],
require: [
'./tests/setup.js',
'ignore-styles',
'source-map-support/register'
]
}
};
gulp.task('mocha', mocha(MOCHA_CONFIG));
tsconfig.json
{
"compilerOptions": {
"baseUrl": "./",
"rootDir": "./src",
"outDir": "./build",
"allowJs": true,
"module": "commonjs",
"target": "es5",
"lib": ["es5", "es6", "dom"],
"sourceMap": true,
"inlineSourceMap": false,
"inlineSources": false,
"experimentalDecorators": true,
"noUnusedParameters": true,
"noUnusedLocals": true,
"jsx": "react",
"moduleResolution": "node"
},
"exclude": [
"docs",
"tests",
"**/*.test.js",
"**/*.test.jsx",
"**/*.test.ts",
"**/*.test.tsx",
"tools",
"gulpfile.js",
"node_modules",
"build",
"typings/main",
"typings/main.d.ts"
],
"awesomeTypescriptLoaderOptions": {
"useCache": true,
"useBabel": true
}
}
With the above setup coverage produces results for all the files but they are incorrect for TS files most probably due to source maps not being used (i.e. report shows no coverage for lines which are comments and the numbers seems to be wrong as well).
With a number of variant approaches tried with no success one of the most commonly suggested is to add "require": ["ts-node/register"] to nyc configuration yet then I'm getting errors complaining about i.e. gulpfile.js, docs/reports/coverage/lcov-report/prettify.js and number of other JS files to be not under 'rootDir' which is correct yet it is not clear why ts-node tries to process all the files out of src even if they are excluded in tsconfig.json (still the configuration gets really complex).
I'll appreciate any suggestion which way to go to get proper coverage report for TS files.
Recently I found a satisfiable solution by using "target": "es6" instead of es5 in tsconfig.json's compilerOptions. While changing target directly in tsconfig.json may not be an option as it affects build, the other tip is to use TS_NODE_COMPILER_OPTIONS='{"target":"es6"} which can be added directly in package.json scripts as i.e. :
"test:coverage": "TS_NODE_COMPILER_OPTIONS='{\"target\":\"es6\"}' nyc npm run test:unit",
where test:unit is whatever way being used to run actual tests (in my case just gulp mocha.
NOTE: I've also updated nyc to latest 11.1.0 and ts-node to 3.3.0 as suggested on https://github.com/istanbuljs/nyc/issues/618 thread
I'm not sure this is the same problem but I'll put this here in case it helps future developers...
I wasn't getting any coverage data until I added exclude-after-remap=false to the nyc section of my package.json.
This is listed in the documentation but not in a very prominent way (IMO).
Since a lot of changes broke old working setups I created a verbose example project covering typescript + mocha + nyc supporting proper coverage also for non called files (this is often not included in examples) as well as some unit test examples and quality checks using latest versions.
I had several issues whilst going to mocha 8+ nyc 15+. Maybe it also helps someone else stumbling across it.
https://github.com/Flowkap/typescript-node-template
If you're only interested in coverage check the .ncyrc.yml and mocharc.yml as well as the call config in package.json. VsCode launch configs also included:
.nycrc.yml
extends: "#istanbuljs/nyc-config-typescript"
reporter:
- html
- lcovonly
- clover
# those 2 are for commandline outputs
- text
- text-summary
report-dir: coverage
.mocharc.yml
require:
- ts-node/register
- source-map-support/register
recursive: true
color: true
exit: true
extension:
- ts
- test.ts
test job in package.json
"test": "npm run lint && nyc mocha src test",

Angular 2 AoT Rollup fails with 'require is not defined'

I have and Angular2 application that I'm compiling to run with AoT.
I've solved the issues I had with the actual compilation and I've also been able to run Rollup from start to end without errors (although there's a lot of warning, which I think are to be expected).
However, when running the application, the browser always states that require is not defined on my app.bundle.js.
What am I doing wrong?
Here's my functional non-AoT sample code/configs:
https://plnkr.co/edit/oCAaeXUKWGyd34YKgho9?p=info
And here is my functional AoT sample configs that throw the require error:
https://plnkr.co/edit/Y1C5HaQS3ddCBrbRaaoM?p=info
Does anyone find any errors here, especially when comparing the non-AoT system.js configs and the AoT rollup configs?
Why am I hitting this error?
I understand that the browser is incapable of working with require but shouldn't rollup attend to that?
Best Regards
So, eventually I was able to solve the issue.
There was a rogue const _ = require("lodash") in the code.
Once I removed it, it all went along without issues.
Two things worth mentioning:
Due to memory limitations on node.js (1.7GB of RAM), the ngc command is run with node --max-old-space-size=8192 ./node_modules/.bin/ngc -p tsconfig-aot.json
Again, for the same reason, rollup is run with node --max-old-space-size=8192 ./node_modules/.bin/rollup -c rollup-config.js
You might be able to get away with --max-old-memory=4096, depending on the size of your project and memory on your computer.
As for my rollup-config.js, though I'm not sure if everything here is really necessary, here's what worked for me:
import builtins from 'rollup-plugin-node-builtins';
import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import uglify from 'rollup-plugin-uglify';
export default {
entry: 'app/app.aot.js',
dest: 'www/bundle.js', // output a single application bundle
sourceMap: false,
format: 'iife',
plugins: [
nodeResolve({
jsnext: true,
module: true,
browser: true
}),
commonjs({
// non-CommonJS modules will be ignored, but you can also
// specifically include/exclude files
include: [
'node_modules/**',
'node_modules/primeng/**',
'node_modules/moment/**',
'node_modules/rxjs/**',
'node_modules/lodash/**'
], // Default: undefined
exclude: ['node_modules/ws/**'], // Default: undefined
// search for files other than .js files (must already
// be transpiled by a previous plugin!)
extensions: ['.js'], // Default: [ '.js' ]
// if true then uses of `global` won't be dealt with by this plugin
ignoreGlobal: false, // Default: false
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'node_modules/primeng/primeng.js': [
'PanelModule',
'InputSwitchModule',
'InputMaskModule',
'ProgressBarModule',
'DropdownModule',
'CalendarModule',
'InputTextModule',
'DataTableModule',
'DataListModule',
'ButtonModule',
'DialogModule',
'AccordionModule',
'RadioButtonModule',
'ToggleButtonModule',
'CheckboxModule',
'SplitButtonModule',
'ToolbarModule',
'SelectButtonModule',
'OverlayPanelModule',
'TieredMenuModule',
'GrowlModule',
'ChartModule',
'Checkbox',
'Dropdown',
'Calendar',
'DataGridModule',
'DataTable',
'MultiSelectModule',
'TooltipModule',
'FileUploadModule',
'TabViewModule',
'AutoCompleteModule'
],
'node_modules/ng2-uploader/index.js': ['Ng2Uploader']
},
// if false then skip sourceMap generation for CommonJS modules
sourceMap: false, // Default: true
}),
builtins(),
uglify()
]
}
rollup still complains about default imports on some packages, which can be probably solved using the named exports (if you really want to) but even with those warnings everything seems to be running.
As for my "final" tsconfig.json:
{
"compilerOptions": {
"lib": ["es2015", "dom"],
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"declaration": false,
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": true,
"noImplicitAny": false,
"watch": false,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"suppressImplicitAnyIndexErrors": true,
"baseUrl": ".",
"typeRoots": [
"./node_modules/#types",
"./node_modules"
],
"types": [
"node",
"lodash",
"jasmine",
"bluebird",
"socket.io-client"
]
},
"compileOnSave": false,
"buildOnSave": false,
"files": [
"app/app.module.ts",
"app/app.aot.ts"
],
// "exclude": [
// "node_modules"
// ],
"angularCompilerOptions": {
"genDir": "compiled",
"skipMetadataEmit": true
}
}
Finally, these two links were also helpful in understanding what's going on behind the scenes:
https://code.lengstorf.com/learn-rollup-js/
https://github.com/rollup/rollup/issues/737
Hope this helps someone.
My solution for this problem was the following:
I tracked, what the system wants to require: the modules fs, events, and timer. All of them were referenced in the zone.js.
I've found some zone.js imports hacked into my code in my earlier tries to make in smaller as 5M.
After I removed them, the problem disappeared.
For the googlers of the future with a similar problem:
The cause of the problem is that your npm module uses require() internally. You have to update it, or to recompile it, but this time as ES6 package (which doesn't use require(), it uses import). If a require is really deeply hardcoded into it (for example, it is in .js and uses require), then you have to modify its source.
Additional extension:
It seems, rollup can't correctly handle non-ordinary imports like import 'Zone.js'; and similar! It can handle only import { something } from 'Anything';-like imports!
The root of all problems is that Angular requires zonejs imported on this way, furthermore any typescript decorator require the reflect-metadata package, imported also on this way.
But, it is not really a problem. Things looked so before, like
<script src="...zone.js"></script>
Or with an
import 'zone.js';
, shouldn't be exist in the source (or in the HTML) any more. Instead, you have to compile the sources without them, and then simply concatenate them to the beginning of your source. In my case, I use the following Grunt rule:
concat: {
dev: {
src: [ "node_modules/zone.js/dist/zone.js", "node_modules/reflect-metadata/Reflect.js", "target/MyApp-core.js" ],
dest: "target/MyApp.js"
}
},
It is part of the grunt-contrib-concat Grunt package.
The resulting target/MyApp.js can be further processed by any other tools (uglify, or the best is google closure compiler).

Categories