Files
wursor/web/__tests__/marketing/search.test.ts
T
SinachPatandClaude Sonnet 5.5 f69ce33dd0 feat(web): marketing theme toggle, site search palette, and phone dev access
- Theme toggle in the nav: follows the device until chosen, remembers the choice, sets data-theme before first paint (no flash).
- Site search: Cmd/Ctrl+K command palette on every marketing page, opened by the shortcut, a nav Search button, or the Docs search field. Searches pages, docs topics and articles that have a page.
- Docs topics move to content.ts so the page and the search index share one source.
- next.config: allowedDevOrigins for private network ranges so a phone on the same Wi-Fi hydrates the dev site. Dev only.
- Sun and moon icons added by hand (not Figma exports); noted in the icons README.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 22:27:34 +01:00

89 lines
3.2 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { SEARCH_ENTRIES, isPaletteShortcut, searchEntries, shortcutLabel, type SearchEntry } from '../../src/marketing/search.ts';
const entry = (id: string, title: string, description = '', group: SearchEntry['group'] = 'Page'): SearchEntry => ({
id, title, description, group, href: `/${id}`,
});
describe('searchEntries', () => {
const entries = [
entry('a', 'Pricing', 'Plans for every size'),
entry('b', 'Review and approve', 'Compare before and after, then publish', 'Docs topic'),
entry('c', 'Trust', 'We never publish without approval'),
];
it('shows the pages when nothing has been typed', () => {
expect(searchEntries('', entries).map((e) => e.id)).toEqual(['a', 'c']);
});
it('ignores case and surrounding spaces', () => {
expect(searchEntries(' PRICING ', entries).map((e) => e.id)).toEqual(['a']);
});
it('finds an entry by its description', () => {
expect(searchEntries('compare', entries).map((e) => e.id)).toEqual(['b']);
});
it('needs every word to match', () => {
expect(searchEntries('pricing banana', entries)).toEqual([]);
});
it('puts title matches before description-only matches', () => {
expect(searchEntries('approv', entries).map((e) => e.id)).toEqual(['b', 'c']);
});
it('puts titles that start with the query first', () => {
const list = [entry('x', 'Undo a change'), entry('y', 'How to undo')];
expect(searchEntries('undo', list).map((e) => e.id)).toEqual(['x', 'y']);
});
});
describe('SEARCH_ENTRIES', () => {
it('only holds results that go somewhere', () => {
expect(SEARCH_ENTRIES.length).toBeGreaterThan(0);
for (const e of SEARCH_ENTRIES) expect(e.href).toMatch(/^\//);
});
it('has unique ids', () => {
const ids = SEARCH_ENTRIES.map((e) => e.id);
expect(new Set(ids).size).toBe(ids.length);
});
it('lists every navigation page', () => {
const titles = searchEntries('').map((e) => e.title);
expect(titles).toEqual(expect.arrayContaining(['Product', 'How it works', 'Trust and safety', 'Pricing', 'Docs']));
});
it('finds a docs topic by what it covers', () => {
expect(searchEntries('invite').map((e) => e.title)).toContain('Team and billing');
});
});
describe('isPaletteShortcut', () => {
const key = (k: string, mods: { metaKey?: boolean; ctrlKey?: boolean; shiftKey?: boolean; altKey?: boolean } = {}) =>
isPaletteShortcut({ key: k, metaKey: false, ctrlKey: false, shiftKey: false, altKey: false, ...mods });
it('accepts Cmd+K and Ctrl+K', () => {
expect(key('k', { metaKey: true })).toBe(true);
expect(key('k', { ctrlKey: true })).toBe(true);
});
it('accepts a capital K (Caps Lock)', () => {
expect(key('K', { metaKey: true })).toBe(true);
});
it('rejects K alone, other letters, and extra modifiers', () => {
expect(key('k')).toBe(false);
expect(key('j', { metaKey: true })).toBe(false);
expect(key('k', { metaKey: true, shiftKey: true })).toBe(false);
expect(key('k', { ctrlKey: true, altKey: true })).toBe(false);
});
});
describe('shortcutLabel', () => {
it('shows the Command symbol on Apple devices and Ctrl elsewhere', () => {
expect(shortcutLabel(true)).toBe('⌘K');
expect(shortcutLabel(false)).toBe('Ctrl K');
});
});