example code: https://github.com/Gloridea/react-ts-jest
$ create-react-app {PROJ} --scripts-version=react-scripts-ts
$ cd {PROJ}
$ yarn test
$ yarn add mobx
// TodoStore.ts
export class TodoStore {
@observable
todos: Todo[] = [];
@action
addTodo(label: string) {
this.todos.push(new Todo(label));
}
}
// TodoStore.test.ts
import {TodoStore} from './TodoStore';
import * as assert from 'assert';
describe('TodoStore', () => {
describe('addTodo()', () => {
it('should add a Todo', () => {
// Given
const todoStore = new TodoStore();
// When
todoStore.addTodo('hello');
// Then
assert.equal(todoStore.todos.length, 1);
});
});
});
$ yarn test
FAIL src/TodoStore.test.ts
● TodoStore › addTodo() › should add Todo
TypeError: Object.defineProperty called on non-object
at Function.defineProperty (native)
at classPropertyDecorator (node_modules/mobx/lib/mobx.js:991:24)
at Object.<anonymous> (src/TodoStore.test.ts:8:22)
at process._tickCallback (internal/process/next_tick.js:109:7)
$ yarn add -D ts-jest identity-obj-proxy
// fileTransformer.js
module.exports = {
process(src, filename, config, options) {
const str = JSON.stringify(path.basename(filename));
return 'module.exports = ' + str + ';';
}
};
// package.json
{
"jest": {
"transform": {
"^.+\\.tsx?$": "<rootDir>/node_modules/ts-jest/preprocessor.js",
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$"
:"<rootDir>/fileTransformer.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$",
"moduleNameMapper": {
"^.+\\.css$": "identity-obj-proxy"
},
"moduleFileExtensions": ["ts", "tsx", "js", "json", "jsx"]
},
"globals": {
"ts-jest": {
"skipBabel": true
}
}
}
$ yarn add -D jest
// package.json
{
"test": "jest",
"test:watch": "jest --watch"
}
├── TodoInput.tsx
├── TodoList.css
├── TodoList.tsx
└── __tests__
└── TodoList.test.tsx
└── __tests__
├── TodoList.test.tsx
└── __snapshots__
└── TodoList.test.tsx.snap
└── stores
├── TodoStore.ts
├── __mocks__
│ └── TodoStore.ts
└── __tests__
└── TodoStore.test.ts
// TodoList.tsx
@observer
export class TodoList extends React.Component<{todoStore: TodoStore}, {}> {
render() {
const list = this.props.todoStore.todos.map(todo => (
<li key={todo.id}>
{todo.label}
</li>
));
return (
<ul className="TodoList">{list}</ul>
);
}
}
// __tests__/__snapshots__/TodoList.test.tsx.snap
exports[`TodoList renders without crashing 1`] = `
<ul
className="TodoList"
>
<li>
hello
</li>
</ul>
`;
// __tests__/__snapshots__/TodoList.test.tsx.snap
exports[`TodoList renders without crashing 1`] = `
<ul
className="TodoList"
>
<li>
hello
</li>
</ul>
`;
$ yarn add -D enzyme enzyme-to-json \
@types/enzyme @types/enzyme-to-json
// TodoInput.test.tsx
describe('TodoInput', () => {
it('should add todo', () => {
// Given
const todoStore = new TodoStore();
const wrapper = mount(<TodoInput todoStore={todoStore}/>);
// When
const input = wrapper.find('input').first();
const button = wrapper.find('button').first();
input.simulate('change', {target: {value: 'hello world'}});
button.simulate('click');
// Then
expect(todoStore.todos.length).toBe(1);
expect((input.getDOMNode() as HTMLInputElement).value).toBe('');
});
});
$ jest --coverage
$ open coverage/index.html
// package.json
{
"jest": {
"coverageReporters": ["text", "html"]
}
}
$ jest --coverage
$ open coverage/index.html
// package.json
{
"jest": {
"mapCoverage": true
}
}
└── stores
├── TodoStore.ts
├── __mocks__
│ └── TodoStore.ts
└── __tests__
└── TodoStore.test.ts
import * as React from 'react';
import {TodoList} from '../TodoList';
import {TodoStore} from '../../stores/TodoStore';
import {mount} from 'enzyme';
jest.mock('../../stores/TodoStore');
describe('TodoList', () => {
it('should contain added todo item', () => {
const todoStore = new TodoStore(); // mock object
// additional test codes...
});
});
// timerGames.test.ts
test('waits 1 second before ending the game', async () => {
// Given
jest.useFakeTimers();
const callback = jest.fn();
timerGame(callback);
expect(callback).not.toBeCalled();
// When
jest.runAllTimers();
// Then
expect(callback).toBeCalled();
expect(callback.mock.calls.length).toBe(1);
});