npm i @types/jest @testing-library/react @testing-ribrary/jest-dom jest ts-jest
// jest.config.js
module.exports = {
// The root of your source code, typically /src
// `<rootDir>` is a token Jest substitutes
rootDir: '.',
// Jest transformations -- this adds support for TypeScript
// using ts-jest
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
moduleNameMapper: {
// path alias setting
'@hooks/(.*)$': '<rootDir>/hooks/$1',
'@components/(.*)$': '<rootDir>/components/$1',
'@layouts/(.*)$': '<rootDir>/layouts/$1',
'@pages/(.*)$': '<rootDir>/pages/$1',
'@utils/(.*)$': '<rootDir>/utils/$1',
'@typings/(.*)$': '<rootDir>/typings/$1',
},
// Runs special logic, such as cleaning up components
// when using React Testing Library and adds special
// extended assertions to Jest
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
// Test spec file resolution pattern
// Matches parent folder `__tests__` and filename
// should contain `test` or `spec`.
testRegex: '(\\.|/)(test|spec)\\.tsx?$',
// Module file extensions for importing
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};
// globals.d.ts
// To ensure the additional Jest matchers are available for all test files,
import '@testing-library/jest-dom/extend-expect';
// package.json
"scripts": {
"test": "jest",
"test:watch": "jest --watch"
},
Query
redering된 페이지 내에서 일치하는 요소를 선택하기 위한 쿼리
조건에 일치하는 요소가 없을 경우 error 발생
조건에 일치하는 여러 요소 선택 배열 반환
조건에 일치하는 요소가 없어도 error발생 x
조건에 일치하는 여러 요소 선택 배열 반환
promise를 반환하는 query 기본 1000ms 시간동안 요소를 못 찾으면 error 반환.
조건에 일치하는 여러 요소 promise 반환
어떠한 역할을 하는 요소를 선택
<button>로그인</button>
getByrole('button', {name: '로그인'})
label 내용으로 input을 선택
<label for="username-input">아이디</label>
<input id="username-input" />
const inputNode = getByLabelText('아이디');
placeholder로 선택
text내용으로 선택
input 등의 value값으로 선택.
img, input 등의 alt 속성 값으로 선택.
title 속성 값으로 선택
data-testid 속성 값으로 선택
<button data-testid="ff"/>
getByTestId('ff')
screen
component 또는 dom rendering 후에 screen Object로 query가능
import { render, fireEvent, screen } from '@testing-library/react';
render(
<MemoryRouter>
<LogIn />
</MemoryRouter>,
);
screen.getByText('llo Worl', { exact: false }) // substring match
screen.getByText((content, element) => {
return element.tagName.toLowerCase() === 'span' && content.startsWith('Hello')
})
Firing Events
fireEvent[eventName](node: HTMLElement, eventProperties: Object)
DOM Event 실행
const rightClick = { button: 2 }
fireEvent.click(getByText('Submit'), rightClick)
fireEvent.change(getByLabelText(/username/i), { target: { value: 'a' } })
fireEvent.change(getByLabelText(/picture/i), {
target: {
files: [new File(['(⌐□_□)'], 'chucknorris.png', { type: 'image/png' })],
},
})
fireEvent.keyDown(domNode, { key: 'Enter', code: 'Enter' })
createEvent[eventName]
createEvent[eventName](node: HTMLElement, eventProperties: Object)
createEvent.click(node, {button: 2})
fireEvent(node, Event)
Async
const button = screen.getByRole('button', { name: 'Click Me' })
fireEvent.click(button)
await screen.findByText('Clicked once')
fireEvent.click(button)
await screen.findByText('Clicked twice')
callback내부가 실행될때까지 기다림.
function waitFor<T>(
callback: () => T | Promise<T>,
options?: {
container?: HTMLElement
timeout?: number
interval?: number
onTimeout?: (error: Error) => Error
mutationObserverOptions?: MutationObserverInit
}
): Promise<T>
await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1))
DOM에서 Element가 제거될 때까지 기다림.
function waitForElementToBeRemoved<T>(
callback: (() => T) | T,
options?: {
container?: HTMLElement
timeout?: number
interval?: number
onTimeout?: (error: Error) => Error
mutationObserverOptions?: MutationObserverInit
}
): Promise<void>
waitForElementToBeRemoved(document.querySelector('div.getOuttaHere')).then(() =>
console.log('Element no longer in DOM')
)