test(record): cover providers fields and iteration

This commit is contained in:
Maofeng
2026-09-28 17:24:08 +08:00
parent 3eb0246c40
commit d3de7cc172
+190
View File
@@ -0,0 +1,190 @@
// @vitest-environment jsdom
import type { ReactNode } from "react"
import { cleanup, render, screen } from "@testing-library/react"
import { afterEach, describe, expect, it } from "vitest"
import {
OptionalRecordProvider,
RecordProvider,
RecordsIterator,
useFieldValue,
useRecord,
} from "@workspace/record"
interface Book {
author: {
name: string
}
id: number
title: string
}
afterEach(cleanup)
function RecordProbe({ record }: { record?: Book }) {
const current = useRecord<Book>({ record })
return <span>{current?.title ?? "No record"}</span>
}
function FieldProbe() {
const author = useFieldValue<Book, "author.name">({
defaultValue: "Anonymous",
source: "author.name",
})
return <span>{author}</span>
}
function PrimitiveProbe<TRecord extends boolean | number | string>() {
const record = useRecord<TRecord>()
return <span>{String(record)}</span>
}
function Wrapper({ children }: { children: ReactNode }) {
return (
<RecordProvider<Book>
value={{ author: { name: "Ursula Le Guin" }, id: 1, title: "Earthsea" }}
>
{children}
</RecordProvider>
)
}
describe("record context", () => {
it("reads context records and prefers an explicit override", () => {
render(
<Wrapper>
<RecordProbe />
<RecordProbe
record={{
author: { name: "Octavia Butler" },
id: 2,
title: "Parable of the Sower",
}}
/>
</Wrapper>
)
expect(screen.getByText("Earthsea")).toBeTruthy()
expect(screen.getByText("Parable of the Sower")).toBeTruthy()
})
it("keeps the parent context when the optional record is undefined", () => {
render(
<Wrapper>
<OptionalRecordProvider value={undefined}>
<RecordProbe />
</OptionalRecordProvider>
</Wrapper>
)
expect(screen.getByText("Earthsea")).toBeTruthy()
})
it("accepts every non-nullish value as a record", () => {
render(
<>
<RecordProvider value={0}>
<PrimitiveProbe<number> />
</RecordProvider>
<RecordProvider value="draft">
<PrimitiveProbe<string> />
</RecordProvider>
<RecordProvider value>
<OptionalRecordProvider value={false}>
<PrimitiveProbe<boolean> />
</OptionalRecordProvider>
</RecordProvider>
</>
)
expect(screen.getByText("0")).toBeTruthy()
expect(screen.getByText("draft")).toBeTruthy()
expect(screen.getByText("false")).toBeTruthy()
})
it("reads nested field values and applies a default without a record", () => {
const { rerender } = render(
<Wrapper>
<FieldProbe />
</Wrapper>
)
expect(screen.getByText("Ursula Le Guin")).toBeTruthy()
rerender(<FieldProbe />)
expect(screen.getByText("Anonymous")).toBeTruthy()
})
})
describe("RecordsIterator", () => {
const books: Book[] = [
{ author: { name: "N. K. Jemisin" }, id: 1, title: "The Fifth Season" },
{ author: { name: "Ted Chiang" }, id: 2, title: "Exhalation" },
]
it("provides each record to shared children", () => {
render(
<RecordsIterator data={books}>
<RecordProbe />
</RecordsIterator>
)
expect(screen.getByText("The Fifth Season")).toBeTruthy()
expect(screen.getByText("Exhalation")).toBeTruthy()
})
it("supports iterator functions and custom record keys", () => {
render(
<RecordsIterator
data={books.map(({ id: _id, ...book }) => book)}
iterator={(book, index) => (
<span>
{index + 1}. {book.title}
</span>
)}
recordKey={(book) => book.title}
/>
)
expect(screen.getByText("1. The Fifth Season")).toBeTruthy()
expect(screen.getByText("2. Exhalation")).toBeTruthy()
})
it("iterates primitive records", () => {
render(
<RecordsIterator data={["draft", "published"]}>
<PrimitiveProbe<string> />
</RecordsIterator>
)
expect(screen.getByText("draft")).toBeTruthy()
expect(screen.getByText("published")).toBeTruthy()
})
it("renders separators between records", () => {
render(
<div data-testid="book-list">
<RecordsIterator
data={books}
iterator={(book) => <span>{book.title}</span>}
separator={(index) => <span>|{index}|</span>}
/>
</div>
)
expect(screen.getByTestId("book-list").textContent).toBe(
"The Fifth Season|1|Exhalation"
)
expect(screen.getAllByText("|1|")).toHaveLength(1)
})
it("renders nothing for missing data or content", () => {
const { container, rerender } = render(
<RecordsIterator data={undefined} iterator={() => <span>unused</span>} />
)
expect(container.childElementCount).toBe(0)
rerender(<RecordsIterator data={books} />)
expect(container.childElementCount).toBe(0)
})
})