TL;DR
<figure>、<img>、<figcaption>、<strong>の複数の DOM 要素を描画するCarouselSlideを、TDD で実装する。- 暗黙の ARIA ロールがない
<figcaption>は、テスト ID で取得する。
内容
第3章の 2 つ目の実践例である。各段階で、先にテストを書き、それをパスする最低限の実装を書く。
Step 1: 定義
コンポーネントが <figure> を描画することをテストする。
// CarouselSlide.test.tsx
describe("CarouselSlide", () => {
it("renders a <figure>", () => {
render(<CarouselSlide />)
expect(screen.getByRole("figure")).toBeInTheDocument()
})
})// CarouselSlide.tsx
const CarouselSlide = () => <figure />
export default CarouselSlideStep 2: figure に img と figcaption を含める
<figure> の中に <img> と <figcaption> があることをテストする。
// CarouselSlide.test.tsx
it("renders an <img> and a <figcaption>", () => {
render(<CarouselSlide />)
const figure = screen.getByRole("figure")
const img = screen.getByRole("img")
const figcaption = screen.getByTestId("caption")
expect(figure).toContainElement(img)
expect(figure).toContainElement(figcaption)
})<figcaption> には暗黙の ARIA ロールがない。そのため、getByTestId でテスト ID を使って取得している。実装では、<figure> の中に <img> と data-testid="caption" を付けた <figcaption> を置く。
Step 3: 画像の URL を Props で受け取る
// CarouselSlide.test.tsx
it("passes `imgUrl` through to the <img>", () => {
const imgUrl = "https://example.com/image.png"
render(<CarouselSlide imgUrl={imgUrl} />)
expect(screen.getByRole("img")).toHaveAttribute("src", imgUrl)
})// CarouselSlide.tsx
const CarouselSlide = ({ imgUrl }: { imgUrl?: string }) => (
<figure>
<img src={imgUrl} />
<figcaption data-testid="caption" />
</figure>
)Step 4: キャプションの説明文と作者名を Props で受け取る
// CarouselSlide.test.tsx
it("uses `description` and `attribution` as the caption", () => {
const props = {
description: "A jaw-droppingly spectacular image",
attribution: "Trevor Burnham",
}
render(<CarouselSlide {...props} />)
const figcaption = screen.getByTestId("caption")
expect(figcaption).toHaveTextContent(`${props.description} ${props.attribution}`)
})// CarouselSlide.tsx
const CarouselSlide = ({
imgUrl,
description,
attribution,
}: {
imgUrl?: string
description?: ReactNode
attribution?: ReactNode
}) => (
<figure>
<img src={imgUrl} />
<figcaption data-testid="caption">
<strong>{description}</strong> {attribution}
</figcaption>
</figure>
)Step 5: figure 要素が扱うすべての Props を受け取る
// CarouselSlide.test.tsx
it("passes other props through to the <figure>", () => {
const className = "my-carousel-slide"
const dataAction = "prev"
render(<CarouselSlide className={className} data-action={dataAction} />)
const figure = screen.getByRole("figure")
expect(figure).toHaveClass(className)
expect(figure).toHaveAttribute("data-action", dataAction)
})残りの Props を ...rest で受け取り、<figure> に渡す。
// CarouselSlide.tsx
const CarouselSlide = ({
imgUrl,
description,
attribution,
...rest
}: {
imgUrl?: string
description?: ReactNode
attribution?: ReactNode
} & ComponentPropsWithRef<"figure">) => (
<figure {...rest}>
<img src={imgUrl} />
<figcaption data-testid="caption">
<strong>{description}</strong> {attribution}
</figcaption>
</figure>
)