react-markdown
と remark-gfm
を使ったコンポーネントのテストコードを jest
で実行する設定のメモです。要点を挙げるなら、
- JSDOM で未実装のメソッドはモックを用意しておく
transformIgnorePatterns
にトランスコンパイルさせるものを設定しておく
といった辺りです。
テンプレ作成
create-next-app@14.2.4
で以下を指定。
react-markdown
と remark-gfm
を追加。
npm install \
react-markdown \
remark-gfm
npm install -D @tailwindcss/typography
jest
関連のパッケージを追加。
npm install -D \
@testing-library/jest-dom \
@testing-library/react \
@types/jest \
jest \
jest-environment-jsdom
packages.json
の scripts
に test
を追加。
{
"name": "my-app",
"version": "0.1.0
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "jest"
},
"dependencies": {
"next": "14.2.4",
"react": "^18",
"react-dom": "^18",
"react-markdown": "^9.0.1",
"remark-gfm": "^4.0.0"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.13",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@types/jest": "^29.5.12",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.4",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}
設定の追加
tsconfig.json
の types
に @testing-library/jest-dom
を追加。
しないとテストの関数の型などが解決されません。
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,tsconfig.json
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
},
"types": [
"@testing-library/jest-dom"
],
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
jest
の設定は JavaScript にしました。
TypeScript だとその設定も必要の様なので妥協しました。packages.json
と同じディレクトリに次のファイルを用意します。
・jest.setup.js
公式にある通り、jest で使用される DOM で未実装のメソッドはモックにしておきます。
import "@testing-library/jest-dom";
// https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
Object.defineProperty(window, "matchMedia", {
writable: true,
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
・jest.config.js
react-markdown
と remark-gfm
を足した後は、node_modules
配下に依存パッケージも含め、エラーになるものが結構出ました。
個々に設定するのが辛かったので、node_modules
配下は全部トランスコンパイルするように transformIgnorePatterns
に設定しました。不都合がでたら見直すつもり。
const nextJest = require("next/jest");
const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: "./",
});
// Add any custom config to be passed to Jest
const customJestConfig = {
setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
testEnvironment: "jsdom",
moduleNameMapper: {
"@/(.*)$": "<rootDir>/$1",
},
};
module.exports = async () => ({
...(await createJestConfig(customJestConfig)()),
transformIgnorePatterns: [
`node_modules/(?!.*)/`,
],
});
ページとテストコード
以前のページで試してみます。
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
const md = `
# GFM
## Autolink literals
www.example.com, https://example.com, and contact@example.com.
## Footnote
A note[^1]
[^1]: Big note.
## Strikethrough
~one~ or ~~two~~ tildes.
## Table
| a | b | c | d |
| - | :- | -: | :-: |
## Tasklist
* [ ] to do
* [x] done
`;
const Home = () => {
return <ReactMarkdown remarkPlugins={[remarkGfm]} className="prose">{md}</ReactMarkdown>;
};
export default Home;
テストコードは以下で。
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";
import Page from "./page";
describe("Page", () => {
it("renders a heading", () => {
render(<Page />);
const headers = screen.getAllByRole("heading");
expect(headers.length).toEqual(7);
});
});
テストの実行
npm run test
で実行します。
% npm run test
> my-app@0.1.0 test
> jest
(node:17255) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
PASS app/page.test.tsx
Page
✓ renders a heading (51 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.002 s
Ran all test suites.
コメント