// @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')
})
})