As a JS library author I compile and publish my source code in the following formats:
commonJS (/lib/index.js)
ES (/es/index.js)
UMD (/dist/index.js)
My unit tests cover my source code and I trust my bundling/compiling tools to generate working bundles.
Then I read this enlightening post by React team where they explain that they run part of their unit tests against the bundled version of the library.
They introduced a test-build-prod which runs Jest with a special configuration file which replace the original import declarations in the test to point the bundled files using Jest's moduleNameMapper option.
It's cool but a bit overwhelming to replicate in my tiny open source side projects.
Before I give this a try, is there any other tool or more portable solution I should consider to run the same test I run on my source code against the compiled bundles?
I'll share the solution I finally went with, which is the same adopted by React team but on small scale.
I added a special npm script to run my unit tests against each compiled bundle with a different Jest configuration for each bundle:
{
"test:bundles": "jest --config ./jest/es.config.js && jest --config ./jest/lib.config.js && jest --config ./jest/dist.config.js"
}
Each Jest configuration file extends default Jest configuration and declares a moduleNameMapper object plus a rootDir property like:
// jest/lib.config.js
const pkg = require('../package.json');
module.exports = Object.assign({}, pkg.jest, {
rootDir: '../',
moduleNameMapper: {
'/src/index$': '<rootDir>/lib/index', // path of "CommonJS" bundle
},
});
moduleNameMapper will make sure that the same tests used for source code will run against an exported bundle since Jest will transform the import statements on runtime like:
import myLibrary from './src/index';
// transformed into:
import myLibrary from '../lib/index';
The only point to pay attention to is making tests import statements needing remapping easily recognizable by moduleNameMapper regex.
If import from './index' is not unique enough in test the files, it might be rewritten as import from '../src/index'.
Related
I'm trying to use Jest to test some TS code I've written. I need this code to work on older browsers so am importing some polyfills and such for side effects.
My code looks something like this (variables changed to be more generic):
src/my_code.ts
import i18next from "i18next";
import Backend from 'i18next-http-backend';
import "promise-polyfill/src/polyfill";
var someVar = 12;
export function someFunc() {
return someVar + 1;
}
And my test looks something like this:
test/my_code.test.ts
import { someFunc } from '../src/my_code';
describe('some test case', function () {
console.log(someFunc());
});
I think my package.json and jest.config.js are okay, but running the test gives:
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
• 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/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation
Details:
my_code/node_modules/promise-polyfill/src/polyfill.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import Promise from './index';
^^^^^^
SyntaxError: Cannot use import statement outside a module
1 | import i18next from "i18next";
2 | import Backend from 'i18next-http-backend';
> 3 | import "promise-polyfill/src/polyfill";
| ^
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1479:14)
at Object.<anonymous> (src/my_code.ts:3:1)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 1.701 s
Ran all test suites.
npm ERR! Test failed. See above for more details.
The other modules I've imported in my source work, but these are using the import x from "y"; syntax rather than import "z"; for side effects.
Anything here I could try?
EDIT: Added jest.config.js below:
module.exports = {
transform: { '^.+\\.ts?$': 'ts-jest' },
testEnvironment: 'node',
testRegex: '/test/.*\\.(test|spec)?\\.(ts|tsx)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
};
Come on brotha, please show me yow jest.config file also yow app works with them import statements, bcuz there might be something bundling yow code up to a point it is understandable to the environment it runs, but on the other head you is running them tests files with nodejs and as you already see, node doesn’t understand that, plus adding the fact that them test cases are written in ts instead of js, try this first, if it doesn’t work I’ll need yow jest config.
Install ts-node.
If you want to execute some ts code ether you need yo compile them files first or you can install ts-node
npm i -D ts-node.
To run them tests you need ts-node ./path-to-yow-file.
I think there is already a package call ts-jest
Although I am able to start the npm project using npm start without any issues with webpack or babel, once I run npm test, I find the following error related to testing App.js using App.test.js (where App.js imports ApolloClient):
TypeError: Cannot assign to read only property '__esModule' of object '[object Object]'
| import ApolloClient from 'apollo-boost';
| ^
at node_modules/apollo-boost/lib/bundle.cjs.js:127:74
at Array.forEach (<anonymous>)
at Object.<anonymous> (node_modules/apollo-boost/lib/bundle.cjs.js:127:36)
Essentially, I'm confused as to why I get an error when running the test but not when starting the project.
I've tried adding in a number of babel plugins to both .babelrc and in my webpack config file:
#babel/plugin-transform-object-assign
#babel/plugin-transform-modules-commonjs
babel-plugin-transform-es2015-modules-commonjs
However, I haven't been able to resolve the issue. My thinking was that this is related to the fact that the file that fails to compile was originally CommonJS.
I was only able to find something relatively similar here, https://github.com/ReactTraining/react-router/pull/6758, but I didn't find a solution.
Is there something that I'm missing specifically related to running tests? I should also mention I've tried frameworks other than Jest and ran into the same issue.
EDIT:
I removed everything from App.test.js except the imports to isolate the issue so it just contains the following:
import React from 'react';
import { shallow } from 'enzyme/build';
import App from './App';
UPDATE:
I was able to resolve the initial error by upgrading apollo-boost from version 0.3.1 to 0.4.2. However, I now have a different error that is similarly frustrating. I am using Babel 7 and have added the plugin #babel/plugin-syntax-dynamic-import to both my .babelrc and to my webpack.config.js files. Despite this, I get the following error related to the use of a dynamic import in App.js when running the Jest to test App.test.js:
SyntaxError: Support for the experimental syntax 'dynamicImport' isn't currently enabled
Add #babel/plugin-syntax-dynamic-import (https://git.io/vb4Sv) to the 'plugins' section of your Babel config to enable parsing.
I'm not sure if there is a parsing error or something else, but I've tried numerous things that have not worked. The closest discussion I could find related to this problem is, https://github.com/facebook/jest/issues/5920, however, the proposed solutions don't work for me.
UPDATE:
One thing that I'm trying is to avoid duplication of the babel options as right now they're both in .babelrc and in the babel-loader options within webpack.config.js. From what I found online (Whats the difference when configuring webpack babel-loader vs configuring it within package.json?), the way to make webpack use the settings in .babelrc is to not specify options. However, doing so results in the same error described above showing up only this time when running npm start. I will add that the project that was originally created using create-react-app, however, in order to support multiple pages, I needed to customize webpack's configuration and so ejected from it. I'm not sure why this is so convoluted.
its probably a babel configuration issue, I'm pretty sure jest needs to be compiled to work with create-react-app...
did you specify a setup file in package.json:
"jest": {
"setupFiles": [
"/setupTests.js"
]
}
and in setupTests.js:
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
It turns out that one of the components in the project's src directory had its own local package.json file even though it wasn't being used and was not installed as a local dependency in the top level package.json (instead imports were done using relative urls). For some reason, the existence of this file changed the behavior of webpack and other tools when starting and testing the project such that none of the top level configurations were used for files within directories with separate package.json files. Once I removed these local package.json files from the components sub-directory, all the prior issues were resolved. One hallmark of this problem is that compilation errors were not showing up for JavaScript files that weren't nested under an alternate package.json file.
Hopefully this is useful for anyone that encounters similar errors as I don't think the cause can be directly determined from the compiler messages alone.
I want to replace all app-imports in some old project with webpack aliases, like
import { LS } from '../../../utils/noSSR';
to
import { LS } from 'utils/noSSR';
I know how to configure my WebStorm to make him understand aliases (moreover it should understand them out-of-box), but I don't know how to refactor all my current imports from relative paths to aliases wherever it's possible. The thing is there are hundreds and hundreds of imports and I'm looking for some automatization.
Is it possible to do such a refactoring with WebStorm?
My Javascript codebase is based on new ES6 Modules.
So I have Javascript files like this for example:
export class MyClass {
constructor() {
this.list = [];
}
add(el) { this.list.push(el); }
}
As a module, I import this file in other Javascript files like this:
import * as lists from "./myclass";
And inside an HTML page, the following syntax has to be used:
<script src="myclass.js" type="module"></script>
Unit testing
I need a framework for testing my code. The problem is that I am using Javascript 6 modules, so modern frameworks like karma have problems as they import the files not as modules:
module.exports = function(config) {
config.set({
files: [
'src/**/*.js',
'test/**/*.js'
],
...
})
}
Above is an example of karma.conf.js. In the specific case of Karma, the runner will not import the files as modules, thus the injection in page fails.
What unit test frameworks can I use for testing Javascript 6 modules?
I'm using a combination of Mocha and Babel - Babel transpiles the ES6 modules to code Mocha can work with, and so you can use import in the test files.
To run mocha with the Babel transpiler:
mocha --compilers js:babel-core/register --recursive test/*
I'm pretty sure other frameworks have a similar solution.
You can check out Jest, it's Facebook's test framework that allows you to run your tests on Node (with JSDOM).
It runs your tests in parallel and without browser, therefore suppose to be much faster.
I'm not sure Jest actually supports modules, right? I.e. I want my test files .. those with the assertions .. to be able to import modules which also import other modules.
It seems the best you can do with node based testing frameworks, with I think may be all of them, is us pretty awful babel/webpack stunts which I'd really prefer not to use.
Headless Chrome certainly seems to be interesting, as does Puppeteer
https://github.com/GoogleChrome/puppeteer
https://developers.google.com/web/updates/2017/06/headless-karma-mocha-chai
Our project is using the webpack resolve.root option to import modules with absolute paths. (avoiding something like ../../../module)
In its current state the project is using babel-loader which works perfectly fine.
My task is to migrate the app to Angular 2.
Therefor I am currently in the process of transitioning to TypeScript.
Somehow it seems like the ts-loader does not work in combination with the resolve.root option of the webpack config.
Example of the webpack.config.js
resolve: {
root: [
path.resolve('./node_modules'),
path.resolve('./app'),
path.resolve('./app/lib'),
]
},
Example of a module import
import AbstractListState from 'states/abstract_list_state';
The states directory is inside the app/lib directory.
Error when executing webpack
ERROR in ./app/mainViews/panel/panel.controller.ts
Module not found: Error: Cannot resolve module 'states/abstract_list_state' in C:\Users\...\Project\app\mainViews\panel
# ./app/mainViews/panel/panel.controller.ts 4:28-65
Pre version 2.0 TypeScript will try to load modules with an absolute path from the node_modules directory. This is because TypeScript's module resultion is per default set to "node". Which means it works like node's require method. So, even if you're using webpack to build your app, TypeScript (and its compiler) will still want to load the files.
In order to let webpack import your modules with absolute path you have to go back and use the require method. This way TypeScript will let webpack import stuff. But of course you will not get any type-inference, autocomplete, ...
Or, you update to the TypeScript 2.0 beta and give this a try: https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#module-resolution-enhancements-baseurl-path-mapping-rootdirs-and-tracing