// FE-PACKEXPORTMENU-001 to FE-PACKEXPORTMENU-006 import { beforeEach, describe, expect, it, vi } from 'vitest' import userEvent from '@testing-library/user-event' import { fireEvent, render, screen } from '../../../tests/helpers/render' import PackingExportMenu from './PackingExportMenu' import PackingPrintPreview from './PackingPrintPreview' import type { PackingExport } from './usePackingExport' const packingExport: PackingExport = { hasItems: true, exportMarkdown: vi.fn(), exportCsv: vi.fn(async () => {}), openPrint: vi.fn(async () => {}), printHtml: null, printTitle: 'Packing List: Lisbon & Porto', closePrint: vi.fn(), } vi.mock('./usePackingExport', () => ({ usePackingExport: () => packingExport })) const renderMenu = () => render() beforeEach(() => { vi.clearAllMocks() packingExport.hasItems = true packingExport.printHtml = null }) describe('PackingExportMenu', () => { it('FE-PACKEXPORTMENU-001: renders nothing while the view has no items', () => { packingExport.hasItems = false const { container } = renderMenu() expect(container).toBeEmptyDOMElement() }) it('FE-PACKEXPORTMENU-002: is an icon button named Export that opens a menu of three ways out', async () => { const user = userEvent.setup() renderMenu() const button = screen.getByRole('button', { name: 'Export' }) expect(button).toHaveAttribute('title', 'Export') expect(button).toHaveAttribute('aria-expanded', 'false') await user.click(button) expect(button).toHaveAttribute('aria-expanded', 'true') expect(screen.getAllByRole('menuitem').map(i => i.textContent)).toEqual([ 'Print or save as PDF', 'Markdown checklist (.md)', 'CSV for import (.csv)', ]) }) it('FE-PACKEXPORTMENU-003: runs the chosen export and closes the menu', async () => { const user = userEvent.setup() renderMenu() for (const [label, action] of [ ['Print or save as PDF', packingExport.openPrint], ['Markdown checklist (.md)', packingExport.exportMarkdown], ['CSV for import (.csv)', packingExport.exportCsv], ] as const) { await user.click(screen.getByRole('button', { name: 'Export' })) await user.click(screen.getByRole('menuitem', { name: label })) expect(action).toHaveBeenCalledTimes(1) expect(screen.queryByRole('menu')).not.toBeInTheDocument() } }) it('FE-PACKEXPORTMENU-004: closes on Escape and on a click anywhere else', async () => { const user = userEvent.setup() renderMenu() await user.click(screen.getByRole('button', { name: 'Export' })) fireEvent.keyDown(document, { key: 'Escape' }) expect(screen.queryByRole('menu')).not.toBeInTheDocument() await user.click(screen.getByRole('button', { name: 'Export' })) fireEvent.mouseDown(document.body) expect(screen.queryByRole('menu')).not.toBeInTheDocument() }) it('FE-PACKEXPORTMENU-005: keeps the preview open even if the view has been emptied meanwhile', () => { packingExport.hasItems = false packingExport.printHtml = '

Lisbon

' renderMenu() expect(screen.getByTitle('Packing List: Lisbon & Porto')).toBeInTheDocument() }) }) describe('PackingPrintPreview', () => { it('FE-PACKEXPORTMENU-006: shows the page in a frame, prints that frame, and closes', async () => { const user = userEvent.setup() const onClose = vi.fn() const { rerender } = render() expect(screen.queryByTitle('Packing List')).not.toBeInTheDocument() rerender() const frame = screen.getByTitle('Packing List') as HTMLIFrameElement expect(frame.getAttribute('srcdoc')).toBe('List') expect(frame.getAttribute('sandbox')).toBe('allow-same-origin allow-modals') const print = vi.fn() Object.defineProperty(frame, 'contentWindow', { value: { print }, configurable: true }) await user.click(screen.getByRole('button', { name: 'Print or save as PDF' })) expect(print).toHaveBeenCalledTimes(1) // The modal's X and the preview's own button both close; the footer one is pressed here. const closeButtons = screen.getAllByRole('button', { name: 'Close' }) await user.click(closeButtons[closeButtons.length - 1]) expect(onClose).toHaveBeenCalledTimes(1) }) })