120 lines
4.0 KiB
TypeScript
120 lines
4.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
buildVisibleComboboxOptions,
|
|
filterAndRankComboboxOptions,
|
|
getComboboxFallbackIndex,
|
|
orderVisibleComboboxOptions,
|
|
} from "./combobox-options";
|
|
|
|
describe("buildVisibleComboboxOptions", () => {
|
|
const options = [
|
|
{ id: "/Users/me/project-a", label: "/Users/me/project-a", kind: "directory" as const },
|
|
{ id: "/Users/me/project-b", label: "/Users/me/project-b", kind: "directory" as const },
|
|
];
|
|
|
|
it("keeps a custom row visible while searching with no matches", () => {
|
|
const visible = buildVisibleComboboxOptions({
|
|
options,
|
|
searchQuery: "/tmp/new-project",
|
|
searchable: true,
|
|
allowCustomValue: true,
|
|
customValuePrefix: "",
|
|
customValueKind: "directory",
|
|
});
|
|
|
|
expect(visible).toHaveLength(1);
|
|
expect(visible[0]).toEqual({
|
|
id: "/tmp/new-project",
|
|
label: "/tmp/new-project",
|
|
description: undefined,
|
|
kind: "directory",
|
|
});
|
|
});
|
|
|
|
it("does not duplicate a row when search exactly matches an existing option", () => {
|
|
const visible = buildVisibleComboboxOptions({
|
|
options,
|
|
searchQuery: "/Users/me/project-a",
|
|
searchable: true,
|
|
allowCustomValue: true,
|
|
customValuePrefix: "",
|
|
customValueKind: "directory",
|
|
});
|
|
|
|
expect(visible).toEqual([
|
|
{ id: "/Users/me/project-a", label: "/Users/me/project-a", kind: "directory" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("filterAndRankComboboxOptions", () => {
|
|
const options = [
|
|
{ id: "feat/login", label: "feat/login" },
|
|
{ id: "main", label: "main" },
|
|
{ id: "feat/main-nav", label: "feat/main-nav" },
|
|
{ id: "fix/logout", label: "fix/logout", description: "fixes main logout bug" },
|
|
];
|
|
|
|
it("returns all options when search is empty", () => {
|
|
expect(filterAndRankComboboxOptions(options, "")).toEqual(options);
|
|
});
|
|
|
|
it("filters by label substring", () => {
|
|
const result = filterAndRankComboboxOptions(options, "login");
|
|
expect(result.map((o) => o.id)).toEqual(["feat/login"]);
|
|
});
|
|
|
|
it("filters by id substring", () => {
|
|
const result = filterAndRankComboboxOptions(options, "fix/");
|
|
expect(result.map((o) => o.id)).toEqual(["fix/logout"]);
|
|
});
|
|
|
|
it("filters by description substring", () => {
|
|
const result = filterAndRankComboboxOptions(options, "logout bug");
|
|
expect(result.map((o) => o.id)).toEqual(["fix/logout"]);
|
|
});
|
|
|
|
it("ranks prefix matches above substring matches", () => {
|
|
const result = filterAndRankComboboxOptions(options, "main");
|
|
expect(result.map((o) => o.id)).toEqual(["main", "feat/main-nav", "fix/logout"]);
|
|
});
|
|
|
|
it("is case-insensitive", () => {
|
|
const items = [{ id: "Alpha", label: "Alpha" }];
|
|
expect(filterAndRankComboboxOptions(items, "alpha")).toHaveLength(1);
|
|
});
|
|
|
|
it("returns empty when nothing matches", () => {
|
|
expect(filterAndRankComboboxOptions(options, "zzz")).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("combobox above-search ordering", () => {
|
|
const visible = [
|
|
{ id: "/tmp/new-project", label: "/tmp/new-project", kind: "directory" as const },
|
|
{ id: "/Users/me/project-a", label: "/Users/me/project-a", kind: "directory" as const },
|
|
{ id: "/Users/me/project-b", label: "/Users/me/project-b", kind: "directory" as const },
|
|
];
|
|
|
|
it("renders first logical option closest to the search box in above-search mode", () => {
|
|
const ordered = orderVisibleComboboxOptions(visible, "above-search");
|
|
expect(ordered.map((option) => option.id)).toEqual([
|
|
"/Users/me/project-b",
|
|
"/Users/me/project-a",
|
|
"/tmp/new-project",
|
|
]);
|
|
expect(getComboboxFallbackIndex(ordered.length, "above-search")).toBe(2);
|
|
});
|
|
|
|
it("keeps normal top-down order in below-search mode", () => {
|
|
const ordered = orderVisibleComboboxOptions(visible, "below-search");
|
|
expect(ordered.map((option) => option.id)).toEqual([
|
|
"/tmp/new-project",
|
|
"/Users/me/project-a",
|
|
"/Users/me/project-b",
|
|
]);
|
|
expect(getComboboxFallbackIndex(ordered.length, "below-search")).toBe(0);
|
|
});
|
|
});
|