概要
フロントエンド開発のためのテスト入門 今からでも知っておきたい自動テスト戦略の必須知識のサンプルコードをNextではなくReact+react-router-domで書き換えた。
stroybookをv6からv7に。
import { ComponentMeta, ComponentStoryObj } from "@storybook/react";
deprecatedなので下記に修正。
import { Meta, StoryObj } from "@storybook/react";
S3
sdkをv3に。
services/client/UploadImage/type.ts
import type { PresignedPost } from "@aws-sdk/s3-presigned-post";
export type UploadImageData = {
url: string;
filename: string;
fields: PresignedPost["fields"];
};
react-router-dom
nextの使用をやめたので、代わりにnext-router-domを使っている。
書き方はそこそこ異なっている。
import { SelectFilterOption } from "@/components/molecules/SelectFilterOption";
import { parseAsNonEmptyString } from "@/lib/util";
import styles from "./styles.module.css";
import { useNavigate, useSearchParams } from "react-router-dom";
const options = [
{ value: "all", label: "すべて" },
{ value: "public", label: "公開" },
{ value: "private", label: "下書き" },
];
export const Header = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const defaultValue =
parseAsNonEmptyString(searchParams.get("status")) || "all";
return (
<header className={styles.header}>
<h2 className={styles.heading}>投稿記事一覧</h2>
<SelectFilterOption
title="公開ステータス"
options={options}
selectProps={{
defaultValue,
onChange: (event) => {
const status = event.target.value;
searchParams.set("status", status);
navigate({
pathname: `/my/posts`,
search: searchParams.toString(),
});
},
}}
/>
</header>
);
};
jest
test("公開ステータスを変更すると、status が変わる", async () => {
// すでにある page=1 が消えていないこともあわせて検証
const router = createMemoryRouter(
[
{ path: "/my/posts", element: <Header /> }
],
{ initialEntries: ["/my/posts?page=1&status=public"] },
);
render(<RouterProvider router={router} />);
const combobox = screen.getByRole("combobox", { name: "公開ステータス" });
async function selectOption(label: string) {
await user.selectOptions(combobox, label);
}
await waitFor(() => {
selectOption("公開");
expect(router.state.location.pathname).toBe(`/my/posts`);
expect(router.state.location.search).toBe(`?page=1&status=public`);
});
await selectOption("下書き");
await waitFor(() => {
expect(router.state.location.pathname).toBe(`/my/posts`);
expect(router.state.location.search).toBe(`?page=1&status=private`);
});
});
Storybook
+ import { withRouter } from "storybook-addon-react-router-v6";
export default {
component: Profile,
args: getMyProfileData,
+ decorators: [withRouter],
} as Meta<typeof Profile>;
type Story = StoryObj<typeof Profile>;
export const Default: Story = {};
svg
webpackからviteに変更しており、SVGを読み込む設定をしていないのでimgとして読み込む必要がある。
これは設定変更だけでできるならならいたい。
import GitHub from "./assets/github.svg";
import Twitter from "./assets/twitter.svg";
import styles from "./styles.module.css";
type Props = {
githubAccount?: string;
twitterAccount?: string;
};
export const Accounts = (props: Props) => {
return (
<div className={styles.accounts}>
{props.githubAccount && (
<p className={styles.github}>
<a
href={`https://github.com/${props.githubAccount}`}
target="_blank"
rel="noopener noreferrer"
>
- <Github />
+ <img src={GitHub} /> <span>{props.githubAccount}</span>
</a>
</p>
)}
{props.twitterAccount && (
<p className={styles.twitter}>
<a
href={`https://twitter.com/${props.twitterAccount}`}
target="_blank"
rel="noopener noreferrer"
>
- <Twitter /> <span>{props.twitterAccount}</span>
+ <img src={Twitter} /> <span>{props.twitterAccount}</span>
</a>
</p>
)}
</div>
);
};