Formatting was configured but never enforced: `bun run format:check` reported 199 unformatted files and ran in no workflow and in no git hook, so .prettierrc (printWidth 100, trailing commas) described an intention rather than the tree. This is the one-time sweep that makes the check gateable. Whitespace and token-reflow only -- no behavioural change: `bun run check` reports 0 errors and all 1053 frontend tests pass before and after. Kept out of every other commit on purpose. A 199-file diff mixed with real changes is unreviewable, and the next commit turns format:check into a hard CI gate so this cannot silently accumulate again.
241 lines
6.5 KiB
TypeScript
241 lines
6.5 KiB
TypeScript
import { describe, it, expect, vi } from "vitest";
|
|
import { render, screen, fireEvent } from "@testing-library/svelte";
|
|
import SearchBar from "./SearchBar.svelte";
|
|
|
|
describe("SearchBar", () => {
|
|
describe("Rendering Tests", () => {
|
|
it("should render input field with placeholder", () => {
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search test...",
|
|
onInput: vi.fn(),
|
|
},
|
|
});
|
|
|
|
const input = screen.getByPlaceholderText("Search test...");
|
|
expect(input).toBeTruthy();
|
|
});
|
|
|
|
it("should render search icon", () => {
|
|
const { container } = render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search...",
|
|
onInput: vi.fn(),
|
|
},
|
|
});
|
|
|
|
const svg = container.querySelector("svg");
|
|
expect(svg).toBeTruthy();
|
|
const classString = svg?.getAttribute("class") || "";
|
|
expect(classString).toContain("w-5");
|
|
expect(classString).toContain("h-5");
|
|
});
|
|
|
|
it("should display current value in input", () => {
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "test query",
|
|
placeholder: "Search...",
|
|
onInput: vi.fn(),
|
|
},
|
|
});
|
|
|
|
const input = screen.getByDisplayValue("test query") as HTMLInputElement;
|
|
expect(input.value).toBe("test query");
|
|
});
|
|
|
|
it("should apply custom className", () => {
|
|
const { container } = render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search...",
|
|
onInput: vi.fn(),
|
|
className: "custom-class",
|
|
},
|
|
});
|
|
|
|
const wrapper = container.firstChild as HTMLElement;
|
|
expect(wrapper.className).toContain("custom-class");
|
|
});
|
|
|
|
it("should have proper accessibility attributes", () => {
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search genres...",
|
|
onInput: vi.fn(),
|
|
},
|
|
});
|
|
|
|
const input = screen.getByPlaceholderText("Search genres...") as HTMLInputElement;
|
|
expect(input.type).toBe("text");
|
|
});
|
|
});
|
|
|
|
describe("Interaction Tests", () => {
|
|
it("should call onInput callback when user types", () => {
|
|
const onInput = vi.fn();
|
|
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search...",
|
|
onInput,
|
|
},
|
|
});
|
|
|
|
const input = screen.getByPlaceholderText("Search...") as HTMLInputElement;
|
|
fireEvent.input(input, { target: { value: "test" } });
|
|
|
|
expect(onInput).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should pass correct value to onInput callback", () => {
|
|
const onInput = vi.fn();
|
|
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search...",
|
|
onInput,
|
|
},
|
|
});
|
|
|
|
const input = screen.getByPlaceholderText("Search...") as HTMLInputElement;
|
|
fireEvent.input(input, { target: { value: "album search" } });
|
|
|
|
// Check that callback was called with the typed value
|
|
expect(onInput).toHaveBeenCalledWith("album search");
|
|
});
|
|
|
|
it("should handle multiple input changes", () => {
|
|
const onInput = vi.fn();
|
|
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search...",
|
|
onInput,
|
|
},
|
|
});
|
|
|
|
const input = screen.getByPlaceholderText("Search...") as HTMLInputElement;
|
|
fireEvent.input(input, { target: { value: "test" } });
|
|
fireEvent.input(input, { target: { value: "testing" } });
|
|
|
|
expect(onInput).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("Edge Cases", () => {
|
|
it("should handle empty search query", () => {
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search...",
|
|
onInput: vi.fn(),
|
|
},
|
|
});
|
|
|
|
const input = screen.getByPlaceholderText("Search...") as HTMLInputElement;
|
|
expect(input.value).toBe("");
|
|
});
|
|
|
|
it("should handle special characters in value", () => {
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "@$%^&*()",
|
|
placeholder: "Search...",
|
|
onInput: vi.fn(),
|
|
},
|
|
});
|
|
|
|
const input = screen.getByDisplayValue("@$%^&*()") as HTMLInputElement;
|
|
expect(input.value).toBe("@$%^&*()");
|
|
});
|
|
|
|
it("should handle very long input values", () => {
|
|
const longValue = "a".repeat(500);
|
|
render(SearchBar, {
|
|
props: {
|
|
value: longValue,
|
|
placeholder: "Search...",
|
|
onInput: vi.fn(),
|
|
},
|
|
});
|
|
|
|
const input = screen.getByDisplayValue(longValue) as HTMLInputElement;
|
|
expect(input.value).toBe(longValue);
|
|
});
|
|
|
|
it("should work with numeric input", () => {
|
|
const onInput = vi.fn();
|
|
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search...",
|
|
onInput,
|
|
},
|
|
});
|
|
|
|
const input = screen.getByPlaceholderText("Search...") as HTMLInputElement;
|
|
fireEvent.input(input, { target: { value: "12345" } });
|
|
|
|
expect(onInput).toHaveBeenCalledWith("12345");
|
|
});
|
|
});
|
|
|
|
describe("Requirement Tests", () => {
|
|
it("should support searching with spaces", () => {
|
|
const onInput = vi.fn();
|
|
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search...",
|
|
onInput,
|
|
},
|
|
});
|
|
|
|
const input = screen.getByPlaceholderText("Search...") as HTMLInputElement;
|
|
fireEvent.input(input, { target: { value: "search multiple words" } });
|
|
|
|
expect(onInput).toHaveBeenCalledWith("search multiple words");
|
|
});
|
|
|
|
it("should work as controlled component with value prop", () => {
|
|
render(SearchBar, {
|
|
props: {
|
|
value: "initial value",
|
|
placeholder: "Search...",
|
|
onInput: vi.fn(),
|
|
},
|
|
});
|
|
|
|
const input = screen.getByDisplayValue("initial value") as HTMLInputElement;
|
|
expect(input.value).toBe("initial value");
|
|
});
|
|
|
|
it("should have proper styling for dark theme", () => {
|
|
const { container } = render(SearchBar, {
|
|
props: {
|
|
value: "",
|
|
placeholder: "Search...",
|
|
onInput: vi.fn(),
|
|
},
|
|
});
|
|
|
|
const input = container.querySelector("input");
|
|
expect(input).toBeTruthy();
|
|
const classString = input?.getAttribute("class") || "";
|
|
expect(classString.length).toBeGreaterThan(0);
|
|
expect(classString).toContain("bg-");
|
|
expect(classString).toContain("text-white");
|
|
expect(classString).toContain("placeholder-gray");
|
|
});
|
|
});
|
|
});
|