// packages/nextjs-jsonapi/src/shadcnui/ui/__tests__/cursor-menu-items.test.tsx import { describe, expect, it } from "vitest"; import { render, screen } from "@testing-library/react"; import { Command, CommandItem, CommandList } from "../command"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../dropdown-menu"; import { Select, SelectContent, SelectItem, SelectTrigger } from "../select"; describe("cursor affordance — menu and listbox items", () => { it("DropdownMenuItem is a pointer, not cursor-default", () => { render( Open Edit , ); const item = screen.getByRole("menuitem"); expect(item).toHaveClass("cursor-pointer"); expect(item).not.toHaveClass("cursor-default"); }); it("SelectItem is a pointer, not cursor-default", () => { render( , ); const option = screen.getByRole("option"); expect(option).toHaveClass("cursor-pointer"); expect(option).not.toHaveClass("cursor-default"); }); it("CommandItem is a pointer, not cursor-default", () => { render( Alpha , ); const item = screen.getByRole("option"); expect(item).toHaveClass("cursor-pointer"); expect(item).not.toHaveClass("cursor-default"); }); it("no shadcnui primitive still ships a bare cursor-default class", async () => { // Guards the sites a render test cannot cheaply reach (context-menu's // right-click trigger, select's scroll arrows, submenu triggers, // checkbox/radio items) and stops the class returning via a shadcn resync. // // Reads the ui/ directory instead of a hardcoded file list so new // primitives are covered automatically. Only flags a BARE `cursor-default` // token — a variant-prefixed one like `data-disabled:cursor-default` (or a // future `disabled:cursor-default`) is a legitimate, intentional class // (see slider.tsx's Control) and must NOT be flagged. const fs = await import("node:fs/promises"); const path = await import("node:path"); const dir = path.resolve(__dirname, ".."); const entries = await fs.readdir(dir, { withFileTypes: true }); const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".tsx")).map((entry) => entry.name); expect(files.length).toBeGreaterThan(0); // Matches a bare `cursor-default` token: the character immediately before // it must NOT be `:` or `-` (which would mean it's part of a variant // prefix like `data-disabled:cursor-default`). Start-of-string, // whitespace, and quote characters preceding it all count as bare. const bareCursorDefault = /(? { const bareCursorDefault = /(?