// @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( ) } 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]" }, ]) }) })