React, TypeScript, Webpack & Jest
Here is an article about first three things in the title, but what about testing?
If you’r using jest and confusing of how to test your ts-react app within it, then you in right place to get started!
So let’s start from scratch and create a new project, I will be using yarn.
First of all we gonna initialize a new project typing:
cd path/to/your/working/directory/
mkdir ts-react-jest
yarn init
Then you will need to provide your project info, but for now you could just hit Enter to proceed
At this point we have basic project setup. Let’s add all necessary typescript dependencies.
First of all it is good to have typescript installed globally in your system so let’s do it:
yarn global add typescript
Also we should add typescript as a dev dependency to our project:
yarn add typescript --dev
If you don’t have webpack installed globally it is time to install it:
yarn global add webpack
And let’s add webpack as a dev dependency to our project
yarn add webpack --dev
After it we should add all other necessary dev dependencies:
yarn add awesome-typescript-loader source-map-loader --dev
awesome-typescript-loader — just a webpack loader for typescript. About the differences between it and ts-loader (another popular loader for ts) you could read here.
source-map-loader — provides us cool debugging experience as if you were debugging regular ts code in your browser.
Since we are gonna develop react app we need it is as a dependency as well:
yarn add react react-dom
Also we will add declaration files:
yarn add @types/react @types/react-dom
Next we should create tsconfig.json
file in the root of our project and put there such content:
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "commonjs",
"target": "es5",
"lib": ["es5", "es6", "dom"],
"jsx": "react"
},
"include": [
"./src/**/*"
]
}
You can take more info about what is tsconfig and how to configure it here.
And at least let’s create webpack.config.js and put it to the root of our project as well:
module.exports = {
entry: "./src/index.tsx",
output: {
filename: "bundle.js",
path: __dirname + "/dist"
},
devtool: "source-map",
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"],
},
module: {
rules: [{
test: /\.tsx?$/,
loader: "awesome-typescript-loader",
}, {
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader",
}]
},
};
It is a pretty simple config and you could get more about it here.
And one more step before coding is adding tslint
:
yarn add tslint tslint-react --dev
tslint-react it is react preset for tslint.
Next we should run tslint --init
in the root of our project
This will create tslint.json
in the root put there such content:
{
"defaultSeverity": "error",
"extends": [
"tslint:latest",
"tslint-react",
],
"jsRules": {},
"rules": {},
"rulesDirectory": [],
"exclude": [
"node_modules",
]
}
tslint is typescript code-style checker more about it you could read here.
So we’r ready to write some code!
Let’s now create our target index.html
file. In the root of our project put index.html with such content:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello ts-react app!</title>
</head>
<body>
<div id="app"></div>
<script src="./dist/bundle.js"></script>
</body>
</html>
Then create src
directory in the root of our project
mkdir src
Let’s add index.tsx
file in the src
and put there this code
import * as React from "react";
import * as ReactDOM from "react-dom";import App from "./App";ReactDOM.render(
<App testMessage="Hello ts-react-jest app" />,
document.getElementById("app"),
);
Here what we do is rendering our root App component to the DOM
Put in src
directory App.tsx
file:
import * as React from "react";export interface InterfaceAppProps { testMessage: string };class App extends React.Component<InterfaceAppProps, undefined> {
render() {
return (
<div>
{this.props.testMessage}
</div>
);
}
}export default App;
It is just a basic react component
At this point we can test how it all works just type webpack
at the root:
webpack
After build just open your index.html
in any browser and you should see something like this:
Let’s create one more component, which will be a child of our App.tsx
Name it Child.tsx
:
import * as React from 'react';interface InterfaceChildProps { items: Array<string> }class Child extends React.Component<InterfaceChildProps, undefined>{
render() {
return (
<div>
{
this.props.items.map((item, i) =>
<p key={i} >{item}</p>
)
}
</div>
);
}
}export default Child;
Then add Child
as a descendant to App
and provide to it some mock data:
import * as React from "react";import Child from './Child';export interface InterfaceAppProps { testMessage: string };const mockItems = ["First", "Second", "Third"];class App extends React.Component<InterfaceAppProps, undefined> {
render() {
return (
<div>
<Child items={mockItems} />
</div>
);
}
}export default App;
Save, recompile with webpack
and reload index.hml, result should be next:
Let’s add some tests!
First of all we should add jest
globally and as a dev dependency
yarn global add jest
yarn add jest --dev
Secondly we will need enzyme
for testing react components:
yarn add enzyme --dev
It’s declarations and react-test-renderer
:
yarn add @types/enzyme --dev
yarn add react-test-renderer --dev
Also let’s add ts-jest
and @types/jest
yarn add ts-jest @types/jest --dev
As it’s said on official ts-jest github page:
ts-jest
is a TypeScript preprocessor with source map support for Jest that lets you use Jest to test projects written in TypeScript.
And I think it is pretty clear =)
After that we should update our package.json
file to give jest understanding that it works in typescript env and how it could be compiled in regular JS. Just put it at the end of your package.json:
"jest": {
"transform": {
".(ts|tsx)": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
]
}
From ts-jest doc:
This setup should allow you to write Jest tests in Typescript and be able to locate errors without any additional gymnastics.
Now we are able to write tests and launch it with jest
command, as usual.
Let’s write test for our Child
component:
At first lets create __test__
directory in the src
and put there Child.tsx
file. Jest will scan our project and understood that all files which are in __test__
directories is test files.
Child.tsx
:
import * as React from "react";
import { HTMLAttributes, shallow, ShallowWrapper } from "enzyme";import Child from "../Child";const testChildProps = {
items: ["1", "2", "3"],
};let child: ShallowWrapper<undefined, undefined>;beforeEach(() =>
child=shallow(<Child {...testChildProps}/>));// checking that all is fine and component has been rendered
it("should render without error", () =>
expect(child.length).toBe(1));it("should render paragraph for each item that has been passed
through props", () => { const pNodes: ShallowWrapper<HTMLAttributes, undefined> =
child.find("p");
expect(pNodes.length).toBe(testChildProps.items.length);});
Now you can launch tests with jest
command from the root of the project:
Here you can find all the sources.