🎄
StorybookをJestで再利用する
Jestでテストを書く際、Storybookを描画するために設定したデコレーターと同じ設定を
記述しなければいけないのが面倒だなと思っていたところ、@storybook/testing-react を
利用するとStoryをJestで再利用出来ると知り早速試してみました。
その際にエラーが所々発生したので解消方法を残しておこうと思います。
Storyを再利用してjestを記述する
まず@storybook/testing-react
をインストールします。
# npm の場合
$ npm install --save-dev @storybook/testing-react
# yarn の場合
$ yarn add -D @storybook/testing-react
そして@storybook/testing-react
のREADMEを参考にJestでストーリーを再利用する様に
テストを修正します。
index.spec.tsx
import React from 'react';
import { render, within } from '@testing-library/react';
import { composeStories } from '@storybook/testing-react';
import '@testing-library/jest-dom';
import * as stories from './index.stories.tsx';
describe('TaskForm', () => {
it('メッセージが表示されること', async () => {
const { Success } = composeStories(stories);
const { container } = render(<Success />);
const canvas = within(container);
Success.play({ canvasElement: container });
expect(await canvas.findByText('タスクが作成されました')).toBeInTheDocument();
});
});
TaskFormコンポーネント
index.tsx
import React from 'react';
export const TaskForm = () => {
// 省略
return (
<div>
<h1>タスク作成</h1>
<form onSubmit={handleSubmit} />
<div>
<label htmlFor="title">タスク名</label>
<input id="title" type="text" placeholder="タスク名を入力してください" />
</div>
<button type="submit">作成</button>
</form>
</div>
);
};
TaskFormストーリー
index.stories.tsx
import { ComponentMeta, ComponentStoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library';
import { TaskForm } from './TaskForm';
type Story = ComponentStoryObj<typeof TaskForm>;
export default {
component: TaskForm,
} as ComponentMeta<typeof TaskForm>;
export const Default: Story = {}
export const Success: Story = {
...Default,
play: ({ canvasElement }) => {
const canvas = within(canvasElement);
userEvent.type(canvas.getByLabelText('タスク名'), 'title');
userEvent.click(canvas.getByText('作成'));
},
};
Storybookではデコレーターを使用しているのでセットアップファイルをjest.config.js
で読み込むように構成を変更します。
setup.jest.js
import { setGlobalConfig } from '@storybook/testing-react';
import * as globalStorybookConfig from './.storybook/preview';
setGlobalConfig(globalStorybookConfig);
preview.js
.storybook/preview.js
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import '../app/assets/stylesheets/application.tailwind.css';
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
const client = new QueryClient();
const BaseDecorator = (Story) => (
<QueryClientProvider client={client}>
<Story />
</QueryClientProvider>
);
export const decorators = [BaseDecorator];
jest.config.js
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
roots: ["<rootDir>/app/javascript/src"],
preset: 'ts-jest',
testEnvironment: 'jsdom',
+ setUpFiles: ['./setup.jest.js']
};
この状態でテストを実行すると..
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.
Details:
/Users/machmap/repo/test/app/frontend/src/test/setup.jest.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import { setGlobalConfig } from '@storybook/testing-react';
^^^^^^
SyntaxError: Cannot use import statement outside a module
こちらのエラーが発生しました。Node.js
では import/export
構文を使用することが
できないので、Jestは setup.jest.js
のパースに失敗している様です。
ts-jestのpresetを変更する
ts-jest
はESMに変換してくれるpresets
を提供してくれているので、そちらを使用する様に
jest.config.js
を修正します。
jest.config.js
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
roots: ["<rootDir>/app/javascript/src"],
- preset: 'ts-jest',
+ preset: 'ts-jest/presets/js-with-ts-esm',
testEnvironment: 'jsdom',
setUpFiles: ['./setup.jest.js']
};
再度テストを実行します。
Details:
/Users/machamp/repo/test/app/assets/stylesheets/application.tailwind.css:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){@tailwind base;
今度はpreview.js
のtailwind
をimport
している箇所でエラーが発生しました。
CSSをモック化
jest-transform-stub を使ってtailwind
をモック化することでこのエラーには対応できます。
jest.config.js
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
roots: ["<rootDir>/app/javascript/src"],
preset: 'ts-jest/presets/js-with-ts-esm',
testMatch: [
"**/__tests__/**/*.+(ts|tsx|js)",
"**/?(*.)+(spec|test).+(ts|tsx|js)"
],
testEnvironment: 'jsdom',
+ transform: {
+ ".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub"
+ },
setUpFiles: ['./setup.jest.js']
};
を追加します。
これで再度テストを実行したところ、テストがパスしました🎉
Discussion