141 lines
3.7 KiB
TypeScript
141 lines
3.7 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import {
|
|
cleanup,
|
|
fireEvent,
|
|
render,
|
|
screen,
|
|
waitFor,
|
|
} from "@testing-library/react"
|
|
import { I18nProvider } from "@workspace/i18n"
|
|
import { afterEach, describe, expect, it, vi } from "vitest"
|
|
|
|
import { SearchProvider } from "./context"
|
|
import { SearchDialog } from "./dialog"
|
|
import { searchDialogHandle } from "./handle"
|
|
import { splitHighlightSegments } from "./highlight"
|
|
import { messages as englishMessages } from "./locales/en-US"
|
|
import type { SearchAdapter } from "./types"
|
|
|
|
class ResizeObserverStub {
|
|
disconnect() {}
|
|
observe() {}
|
|
unobserve() {}
|
|
}
|
|
|
|
vi.stubGlobal("ResizeObserver", ResizeObserverStub)
|
|
Object.defineProperty(Element.prototype, "getAnimations", {
|
|
configurable: true,
|
|
value: () => [],
|
|
})
|
|
|
|
function renderSearch({
|
|
adapter,
|
|
onSelect,
|
|
}: {
|
|
adapter: SearchAdapter
|
|
onSelect?: (event: { item: { id: string }; query: string }) => void
|
|
}) {
|
|
return render(
|
|
<I18nProvider locale="en-US" catalogs={{ "en-US": englishMessages }}>
|
|
<SearchProvider
|
|
adapter={adapter}
|
|
historyStorageKey={false}
|
|
onSelect={onSelect}
|
|
>
|
|
<SearchDialog hotkey={false} />
|
|
</SearchProvider>
|
|
</I18nProvider>
|
|
)
|
|
}
|
|
|
|
describe("search package", () => {
|
|
afterEach(() => {
|
|
cleanup()
|
|
window.localStorage.clear()
|
|
})
|
|
|
|
it("delegates searching, pagination, and selection to the provider", async () => {
|
|
const adapter: SearchAdapter = {
|
|
search: vi.fn(async ({ cursor, query }) => {
|
|
if (cursor) {
|
|
return {
|
|
groups: [
|
|
{
|
|
id: "people",
|
|
label: "People",
|
|
items: [{ id: "2", title: "Grace Hopper" }],
|
|
},
|
|
],
|
|
nextCursor: null,
|
|
}
|
|
}
|
|
|
|
return {
|
|
groups: [
|
|
{
|
|
id: "people",
|
|
label: "People",
|
|
total: 2,
|
|
items: [
|
|
{
|
|
id: "1",
|
|
title: "Ada Lovelace",
|
|
description: `Matched ${query}`,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nextCursor: "next-page",
|
|
}
|
|
}),
|
|
}
|
|
const onSelect = vi.fn()
|
|
renderSearch({ adapter, onSelect })
|
|
|
|
searchDialogHandle.open(null)
|
|
const input = await screen.findByPlaceholderText("Search your workspace…")
|
|
fireEvent.change(input, { target: { value: "ada" } })
|
|
|
|
await waitFor(() =>
|
|
expect(adapter.search).toHaveBeenCalledWith(
|
|
expect.objectContaining({ query: "ada" })
|
|
)
|
|
)
|
|
await waitFor(() =>
|
|
expect(screen.getByRole("dialog").textContent).toContain("Ada Lovelace")
|
|
)
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Load more" }))
|
|
await waitFor(() =>
|
|
expect(screen.getByRole("dialog").textContent).toContain("Grace Hopper")
|
|
)
|
|
expect(adapter.search).toHaveBeenLastCalledWith(
|
|
expect.objectContaining({ cursor: "next-page", query: "ada" })
|
|
)
|
|
|
|
fireEvent.click(screen.getByRole("option", { name: "Open Ada Lovelace" }))
|
|
await waitFor(() =>
|
|
expect(onSelect).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
item: expect.objectContaining({ id: "1" }),
|
|
query: "ada",
|
|
})
|
|
)
|
|
)
|
|
})
|
|
|
|
it("splits case-insensitive literal query matches for highlighting", () => {
|
|
expect(splitHighlightSegments("Ada ADA Lovelace", "ada")).toEqual([
|
|
{ highlighted: true, text: "Ada" },
|
|
{ highlighted: false, text: " " },
|
|
{ highlighted: true, text: "ADA" },
|
|
{ highlighted: false, text: " Lovelace" },
|
|
])
|
|
expect(splitHighlightSegments("price [usd]", "[usd]")).toEqual([
|
|
{ highlighted: false, text: "price " },
|
|
{ highlighted: true, text: "[usd]" },
|
|
])
|
|
})
|
|
})
|