https://remix.run/docs/en/main/guides/streaming
上記の <Product /> コンポーネントを例に考えてみる。Promise の状態に応じて、次の 3 つのテストを書く。
- pending → Suspense の fallback が表示される
- fulfilled → Await の children が表示される
- rejected → Await の errorElement が表示される
まず 1 から書いてみる。ポイントは、reviews に渡す Promise を resolve も reject もしないところ。
function loader() {
return defer({
product: mockProduct,
// arrange: 解決しない Promise を返す
reviews: new Promise<Review[]>(function noop() {})
})
}
const RemixStub = createRemixStub([
{ path: "/", loader, Component: Product }
])
test("pending の間、 <PreviewSkeleton /> を表示する", () => {
render(<RemixStub />)
// assert: <ReviewsSkeleton /> が存在する
expect(await screen.findByTestId('reviews-skeleton')).toBeInTheDocument()
}) 続いて 2。
function loader() {
return defer({
product: mockProduct,
// arrange: Promise<Review[]> を返す
reviews: Promise.resolve(mockReviews)
})
}
const RemixStub = createRemixStub([
{ path: "/", loader, Component: Product }
])
test("リクエスト完了後 <ProductReviews /> を表示する", () => {
render(<RemixStub />)
expect(await screen.findByTestId('product-reviews')).toBeInTheDocument()
}) 最後に 3。これは Await コンポーネントの props の errorElement に <ReviewsError /> を指定した場合のテストをイメージしている(例: <Await resolve={reviews} errorElement={<ReviewsError />}>...</Await>)。
function loader() {
return defer({
product: mockProduct,
// arrange: サーバーエラーをシミュレート
reviews: Promise.reject(
Response.json({ message: "something happened" }, { status: 500 }),
)
})
}
const RemixStub = createRemixStub([
{ path: "/", loader, Component: Product }
])
test("例外発生時に <ReviewsError /> を表示する", () => {
render(<RemixStub />)
// assert: <ReviewsError /> が表示されている
expect(await screen.findByTestId('reviews-error')).toBeInTheDocument()
}) 補足。先ほどのドキュメントのコード例のように errorElement の指定を省略すると、エラー発生時には ErrorBoundary が表示される。これをテストするときは、createRemixStub に ErrorBoundary も渡すようにする。
// 前略
const RemixStub = createRemixStub([
{ path: "/", loader, Component: Product, ErrorBoundary }
])
// 後略
No comments yet