// @vitest-environment jsdom /** * What AppProvider promises, pinned: a face handed no picture asks the app for * one, and a picture handed in still wins; a plain click on a link to the * app's own address changes page through the app instead of loading it, and * every other click is left to the browser; without a provider, nothing changes. */ import { type ReactNode } from 'react' import { afterEach, describe, expect, it, vi } from 'vitest' import { cleanup, render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { AppProvider } from './AppProvider' import { Avatar } from './Avatar' import { Chip } from './Chip' import { IconButton } from './IconButton' import { InlineChip } from './InlineChip' import { Link } from './Link' import { NavItem } from './NavItem' import { Person } from './Person' import { RailItem } from './RailItem' afterEach(cleanup) const imgOf = (container: HTMLElement) => container.querySelector('img') describe('pictures', () => { const pictureFor = (name: string) => (name === 'Ana Duarte' ? 'https://pictures.test/ana.png' : null) it("asks the app for a face's picture when none is handed in", () => { const { container } = render( , ) expect(imgOf(container)?.getAttribute('src')).toBe('https://pictures.test/ana.png') }) it('keeps a picture that is handed in', () => { const { container } = render( , ) expect(imgOf(container)?.getAttribute('src')).toBe('https://pictures.test/own.png') }) it('draws the initials when the app has no picture either', () => { const { container } = render( , ) expect(imgOf(container)).toBeNull() expect(container.textContent).toBe('RM') }) it('reaches a face drawn through another part', () => { const { container } = render( , ) expect(imgOf(container)?.getAttribute('src')).toBe('https://pictures.test/ana.png') }) it('asks nothing without a provider', () => { const { container } = render() expect(imgOf(container)).toBeNull() }) }) describe('changing page', () => { const inApp = (navigate: (href: string) => void, children: ReactNode) => render({children}) it.each([ ['Link', Topics], ['NavItem', ], ['RailItem', } label="Topics" />], ['Chip', ], ['InlineChip', Topics], ['IconButton', ], ])('%s: a plain click goes through the app', async (_name, part) => { const navigate = vi.fn() inApp(navigate, part) await userEvent.click(screen.getByRole('link')) expect(navigate).toHaveBeenCalledWith('/topics') }) it("hands over the path when the href is the app's whole address", async () => { const navigate = vi.fn() inApp(navigate, Message) await userEvent.click(screen.getByRole('link')) expect(navigate).toHaveBeenCalledWith('/message/1?thread=2#r3') }) it("runs the caller's own onClick first", async () => { const order: string[] = [] inApp(() => order.push('navigate'), order.push('caller')}>Topics) await userEvent.click(screen.getByRole('link')) expect(order).toEqual(['caller', 'navigate']) }) it('leaves a click the caller already handled', async () => { const navigate = vi.fn() inApp(navigate, event.preventDefault()}>Topics) await userEvent.click(screen.getByRole('link')) expect(navigate).not.toHaveBeenCalled() }) it.each([ ['Ctrl', { ctrlKey: true }], ['Cmd', { metaKey: true }], ['Shift', { shiftKey: true }], ['Alt', { altKey: true }], ['the middle button', { button: 1 }], ])('leaves a click with %s to the browser', (_name, init) => { const navigate = vi.fn() inApp(navigate, Topics) const link = screen.getByRole('link') const event = new MouseEvent('click', { bubbles: true, cancelable: true, ...init }) link.dispatchEvent(event) expect(navigate).not.toHaveBeenCalled() expect(event.defaultPrevented).toBe(false) }) it.each([ ['another site', Topics], ['another scheme', Topics], ['a new tab', Topics], ['a download', File], ])('leaves a link to %s to the browser', (_name, part) => { const navigate = vi.fn() inApp(navigate, part) const link = screen.getByRole('link') const event = new MouseEvent('click', { bubbles: true, cancelable: true }) link.dispatchEvent(event) expect(navigate).not.toHaveBeenCalled() }) it('stops the page load it replaces', () => { inApp(() => {}, Topics) const event = new MouseEvent('click', { bubbles: true, cancelable: true }) screen.getByRole('link').dispatchEvent(event) expect(event.defaultPrevented).toBe(true) }) it('changes nothing without a provider', () => { render(Topics) const event = new MouseEvent('click', { bubbles: true, cancelable: true }) screen.getByRole('link').dispatchEvent(event) expect(event.defaultPrevented).toBe(false) }) it('keeps the address on the link, for a new tab', () => { inApp(() => {}, ) expect(screen.getByRole('link').getAttribute('href')).toBe('/topics') }) })