Files
SnapOtter/tests/unit/landing/bento-grid.test.tsx
T
SnapOtter 8633dba431 fix: sync API docs, register content-aware-resize, normalize tool counts
- Fix 23 OpenAPI schema discrepancies across 16+ tools (wrong ranges,
  missing fields, incorrect schemas for gif-tools/collage/ocr)
- Add content-aware-resize to canonical TOOLS array and landing BentoGrid
- Normalize tool count to 47 across README, docs, landing, i18n, OpenAPI
- Remove dead "automation" ToolCategory variant
- Add BMP and JPEG XL format decoding via ImageMagick
- Add libopenexr-dev to Docker runtime image
- Update e2e test selectors for current pipeline builder UI
2026-04-24 23:27:08 +08:00

120 lines
4.0 KiB
TypeScript

// @vitest-environment jsdom
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import type React from "react";
import { afterEach, describe, expect, it, vi } from "vitest";
vi.mock("framer-motion", () => ({
motion: {
div: ({ children, ...props }: React.PropsWithChildren<Record<string, unknown>>) => (
<div {...props}>{children}</div>
),
},
AnimatePresence: ({ children }: React.PropsWithChildren) => <>{children}</>,
}));
import { BentoGrid } from "@landing/components/bento-grid";
afterEach(cleanup);
function getCountText(): string {
const el = screen.getByText(/^Showing/);
return el.textContent ?? "";
}
describe("BentoGrid", () => {
it("renders the section heading", () => {
render(<BentoGrid />);
expect(screen.getByText("47 tools. Zero cloud dependency.")).toBeDefined();
});
it("renders the search input", () => {
render(<BentoGrid />);
expect(screen.getByPlaceholderText("Search tools...")).toBeDefined();
});
it("shows all tools by default", () => {
render(<BentoGrid />);
const text = getCountText();
expect(text).toMatch(/Showing 47 of 47 tools/);
});
it("renders category filter pills including All", () => {
render(<BentoGrid />);
expect(screen.getByText((_, el) => el?.textContent === "All (47)")).toBeDefined();
expect(screen.getByText(/Essentials/)).toBeDefined();
expect(screen.getByText(/AI Tools/)).toBeDefined();
expect(screen.getByText(/Optimization/)).toBeDefined();
});
it("filters tools by search query", () => {
render(<BentoGrid />);
const input = screen.getByPlaceholderText("Search tools...");
fireEvent.change(input, { target: { value: "resize" } });
expect(screen.getByText("Resize")).toBeDefined();
const text = getCountText();
expect(text).toMatch(/Showing \d+ of 47 tools/);
expect(screen.queryByText("OCR / Text Extraction")).toBeNull();
});
it("filters tools by category", () => {
render(<BentoGrid />);
const aiButton = screen.getByText(/AI Tools/);
fireEvent.click(aiButton);
const text = getCountText();
expect(text).toMatch(/Showing 14 of 47 tools/);
expect(screen.getByText("Remove Background")).toBeDefined();
expect(screen.queryByText("Resize")).toBeNull();
});
it("shows empty state when search matches nothing", () => {
render(<BentoGrid />);
const input = screen.getByPlaceholderText("Search tools...");
fireEvent.change(input, { target: { value: "xyznonexistent" } });
expect(screen.getByText("No tools found. Try a different search.")).toBeDefined();
const text = getCountText();
expect(text).toMatch(/Showing 0 of 47 tools/);
});
it("combines search and category filter", () => {
render(<BentoGrid />);
const aiButton = screen.getByText(/AI Tools/);
fireEvent.click(aiButton);
const input = screen.getByPlaceholderText("Search tools...");
fireEvent.change(input, { target: { value: "background" } });
expect(screen.getByText("Remove Background")).toBeDefined();
expect(screen.queryByText("Image Upscaling")).toBeNull();
});
it("clicking All resets category filter", () => {
render(<BentoGrid />);
fireEvent.click(screen.getByText(/AI Tools/));
expect(getCountText()).toMatch(/Showing 14 of 47 tools/);
fireEvent.click(screen.getByText((_, el) => el?.textContent === "All (47)"));
expect(getCountText()).toMatch(/Showing 47 of 47 tools/);
});
it("renders each tool with name and description", () => {
render(<BentoGrid />);
expect(screen.getByText("Crop")).toBeDefined();
expect(screen.getByText("Freeform crop, aspect ratio presets, shape crop")).toBeDefined();
});
it("renders all 8 category pills", () => {
render(<BentoGrid />);
const categoryNames = [
"Essentials",
"Optimization",
"Adjustments",
"AI Tools",
"Watermark & Overlay",
"Utilities",
"Layout & Composition",
"Format & Conversion",
];
for (const name of categoryNames) {
expect(screen.getByText(new RegExp(name))).toBeDefined();
}
});
});