160 lines
4.8 KiB
TypeScript
160 lines
4.8 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { flushPromises } from '@vue/test-utils'
|
|
import { MOBILE_WIDTH } from '@/components/__tests__/viewport'
|
|
import {
|
|
button,
|
|
calls,
|
|
chip,
|
|
failWith,
|
|
fieldWrapper,
|
|
hasText,
|
|
text,
|
|
unmountAll,
|
|
} from './entryEditorKit'
|
|
import {
|
|
focusedName,
|
|
libraryBody,
|
|
mediaButton,
|
|
mediaState,
|
|
mediaText,
|
|
mountMedia,
|
|
pickFromLibrary,
|
|
} from './entryMediaKit'
|
|
import { fieldLibrary } from './mediaFieldKit'
|
|
|
|
vi.mock('@/composables/useApi', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('@/composables/useApi')>()
|
|
return { ...actual, useApi: vi.fn() }
|
|
})
|
|
|
|
afterEach(unmountAll)
|
|
|
|
const dialog = () => document.body.querySelector<HTMLElement>('[role="dialog"]')
|
|
const tiles = () => [...document.body.querySelectorAll<HTMLButtonElement>('[data-picker-tile]')]
|
|
|
|
async function open(opener = 'Change image') {
|
|
mediaButton(opener)!.click()
|
|
await flushPromises()
|
|
await flushPromises()
|
|
}
|
|
|
|
describe('The picker opened from the entry editor', () => {
|
|
it('loads only the library page and never the variants', async () => {
|
|
await mountMedia('/content/events/42', { thumbnail: 7 })
|
|
|
|
await open()
|
|
|
|
expect(calls.filter((url) => url.startsWith('/v1/media/'))).toEqual([
|
|
'/v1/media/?page=1&size=25',
|
|
])
|
|
expect(calls.some((url) => url.includes('image-variants'))).toBe(false)
|
|
expect(text(dialog())).toContain('Choose an image')
|
|
expect(text(dialog())).not.toContain('Alt text')
|
|
})
|
|
|
|
it('marks the stored image as Current', async () => {
|
|
await mountMedia('/content/events/42', { thumbnail: 7 })
|
|
|
|
await open()
|
|
|
|
const current = tiles().filter((tile) =>
|
|
tile.getAttribute('aria-label')!.endsWith(', Current'),
|
|
)
|
|
expect(current.map((tile) => tile.dataset.imageId)).toEqual(['7'])
|
|
})
|
|
|
|
it('is 640 px wide on desktop and a bottom sheet on mobile', async () => {
|
|
await mountMedia('/content/events/42', { thumbnail: 7 })
|
|
await open()
|
|
expect(dialog()!.className).toContain('sm:max-w-160')
|
|
unmountAll()
|
|
|
|
await mountMedia('/content/events/42', { thumbnail: 7, width: MOBILE_WIDTH })
|
|
await open()
|
|
expect(dialog()!.className).toContain('max-sm:!bottom-0')
|
|
})
|
|
|
|
it('does not open while the form is saving', async () => {
|
|
let release = () => {}
|
|
await mountMedia('/content/events/42', {
|
|
thumbnail: 7,
|
|
writeEntry: () => new Promise((resolve) => (release = () => resolve(undefined))),
|
|
})
|
|
const title = fieldWrapper('title').querySelector('input')!
|
|
title.value = 'Changed'
|
|
title.dispatchEvent(new Event('input', { bubbles: true }))
|
|
await flushPromises()
|
|
button('Save')!.click()
|
|
await flushPromises()
|
|
|
|
mediaButton('Change image')!.click()
|
|
await flushPromises()
|
|
|
|
expect(dialog()).toBeNull()
|
|
expect(hasText('Saving…')).toBe(true)
|
|
release()
|
|
await flushPromises()
|
|
})
|
|
|
|
it('shows the empty library with the field copy and focuses nothing else', async () => {
|
|
await mountMedia('/content/events/new', {
|
|
library: () => ({ ...libraryBody([]), total: 0 }),
|
|
})
|
|
|
|
await open('Pick an image for Thumbnail')
|
|
const library = [...document.body.querySelectorAll<HTMLElement>('[role="tab"]')].find(
|
|
(tab) => text(tab) === 'Library',
|
|
)!
|
|
library.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, button: 0 }))
|
|
await flushPromises()
|
|
|
|
expect(text(dialog())).toContain(
|
|
'Images you upload appear here, ready to use in any article or entry.',
|
|
)
|
|
})
|
|
|
|
it('shows the error alert when the library fails and keeps the entry untouched', async () => {
|
|
await mountMedia('/content/events/42', { thumbnail: 7, library: failWith(500) })
|
|
|
|
await open()
|
|
|
|
expect(text(dialog())).toContain("Couldn't load your library")
|
|
expect(text(dialog())).toContain('Try again')
|
|
expect(chip()!.textContent).toContain('Saved')
|
|
})
|
|
})
|
|
|
|
describe('Focus and announcements in the editor', () => {
|
|
it('lands on Change image after Use this image', async () => {
|
|
await mountMedia('/content/events/42', { thumbnail: 7 })
|
|
|
|
await pickFromLibrary('bathroom-tiles.png')
|
|
|
|
expect(focusedName()).toBe('Change image')
|
|
expect(mediaState()).toBe('filled')
|
|
expect(mediaText()).toContain('bathroom-tiles.png')
|
|
})
|
|
|
|
it('lands on Pick an image after removing the image', async () => {
|
|
await mountMedia('/content/events/42', { thumbnail: 7 })
|
|
|
|
mediaButton('Remove image from Thumbnail')!.click()
|
|
await flushPromises()
|
|
await flushPromises()
|
|
|
|
expect(focusedName()).toBe('Pick an image for Thumbnail')
|
|
expect(chip()!.textContent).toContain('Unsaved changes')
|
|
})
|
|
|
|
it('returns to the opener with Cancel', async () => {
|
|
await mountMedia('/content/events/42', { thumbnail: 7 })
|
|
const opener = mediaButton('Change image')!
|
|
opener.focus()
|
|
await open()
|
|
;[...dialog()!.querySelectorAll('button')].find((b) => text(b) === 'Cancel')!.click()
|
|
|
|
await vi.waitFor(() => expect(document.activeElement).toBe(opener))
|
|
expect(fieldLibrary()).toHaveLength(12)
|
|
})
|
|
})
|