feat(SPA): media field type
This commit is contained in:
1 parent
a2d596f4ca
commit
3e7fc9a4a5
57 files changed
+6109
-75
No files matched your search
@@ -0,0 +1,159 @@
|
||||
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)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user