import { vi } from 'vitest' import { flushPromises } from '@vue/test-utils' import { addCollection } from '@iconify/vue' import lucide from 'virtual:lucide-subset' import { mountAt, type SchemaMount } from './schemaTestKit' export { button, calls, customTypes, failWith, failWithBody, manyTypes, networkFailure, pending, text, unmountAll, } from './schemaTestKit' addCollection(lucide) export const SKELETON_DELAY_MS = 150 export const DEFAULT_SORT = 'updated_at:desc' const MARTA = 3 const INES = 4 const JONAS = 5 type Row = [ number, string, string, string, boolean, number | null, string, string, string, string, number, ] const ROWS: Row[] = [ [ 42, 'Lime plaster workshop', '2026-11-21', '2026-11-22', false, 18, 'Workshop', 'Two days of hands-on work with lime putty and old stone.', '2026-10-12', '2026-10-15', MARTA, ], [ 41, 'Heat pumps: what the numbers say', '2026-11-03', '', true, 500, 'Talk', 'A look at running costs after two winters.', '2026-10-08', '2026-10-14', JONAS, ], [ 40, 'Open day at the Rue Verte site', '2026-11-12', '2026-11-12', false, 120, 'Meetup', '', '2026-10-02', '2026-10-12', INES, ], [ 39, 'Winter windows clinic', '2026-12-05', '', false, null, 'Workshop', 'Bring a window, leave with a plan.', '2026-09-30', '2026-10-09', MARTA, ], [ 38, 'Timber joinery for beginners', '2027-01-14', '2027-01-15', false, 12, 'Workshop', 'Mortise, tenon and a lot of patience.', '2026-09-28', '2026-10-07', INES, ], [ 37, 'Q&A with the site managers', '2026-10-28', '', true, null, 'Meetup', '', '2026-09-21', '2026-10-02', JONAS, ], [ 36, 'Reclaimed floorboards talk', '2026-10-17', '', false, 60, 'Talk', 'Where to find them and how to lay them.', '2026-09-14', '2026-09-29', MARTA, ], ] export function entry(row: Row) { const [ id, title, startsOn, endsOn, online, capacity, category, description, created, updated, by, ] = row return { id, content_type: 'events', data: { title, starts_on: startsOn, ends_on: endsOn, online, capacity, category, description, }, created_by: by, updated_by: by, created_at: `${created}T09:14:00Z`, updated_at: `${updated}T11:02:00Z`, } } export const eventEntries = ROWS.map(entry) export const userNames = [ { id: MARTA, display_name: 'Marta Alves' }, { id: INES, display_name: 'Inès Duarte' }, { id: JONAS, display_name: 'Jonas Pereira' }, ] export function listPage(items: unknown[], total: number, query: URLSearchParams) { return { items, total, page: Number(query.get('page')), size: Number(query.get('size')), } } export function populated(key: string, query: URLSearchParams) { return key === 'events' ? listPage(eventEntries, 7, query) : listPage([], 0, query) } export function lookupNames(ids: number[]) { return userNames.filter((user) => ids.includes(user.id)) } export function mountList(path: string, options: SchemaMount = {}) { return mountAt(path, { list: populated, lookup: lookupNames, ...options }) } export async function settle() { await flushPromises() } export function listRequests(calls: string[]) { return calls.filter((url) => /\/entry\?/.test(url) && !/size=5$/.test(url)) } export function lookupRequests(calls: string[]) { return calls.filter((url) => url.startsWith('/v1/user/lookup')) } export function installSelectPolyfills() { Element.prototype.scrollIntoView = vi.fn() Element.prototype.hasPointerCapture = vi.fn(() => false) Element.prototype.releasePointerCapture = vi.fn() } export function openSelect(trigger: Element | null) { trigger?.dispatchEvent( Object.assign(new MouseEvent('pointerdown', { bubbles: true, button: 0 }), { pointerType: 'mouse', }), ) } export async function chooseSort(label: string) { openSelect(document.body.querySelector('[aria-label="Sort by"]')) await flushPromises() const option = [...document.body.querySelectorAll('[role="option"]')].find( (candidate) => candidate.textContent?.trim() === label, )! option.dispatchEvent(new MouseEvent('pointerup', { bubbles: true })) option.click() await flushPromises() } export async function chooseSize(label: string) { document.body.querySelector('[aria-label="Rows per page"]')!.click() await flushPromises() const option = [...document.body.querySelectorAll('[role="option"]')].find( (candidate) => candidate.textContent?.trim() === label, )! option.click() await flushPromises() } export async function press(label: string) { document.body.querySelector(`[aria-label="${label}"]`)!.click() await flushPromises() }