159 lines
5.0 KiB
TypeScript
159 lines
5.0 KiB
TypeScript
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
|
||
import { initializeLocale, setLocale } from '@/i18n'
|
||
import { customAccess } from '@/test/access'
|
||
import {
|
||
customTypes,
|
||
eventEntries,
|
||
installSelectPolyfills,
|
||
listPage,
|
||
lookupNames,
|
||
mountList,
|
||
text,
|
||
unmountAll,
|
||
} from './contentTestKit'
|
||
import { failWithBody, mountEditor, main as editorMain } from './entryEditorKit'
|
||
|
||
vi.mock('@/composables/useApi', async (importOriginal) => {
|
||
const actual = await importOriginal<typeof import('@/composables/useApi')>()
|
||
return { ...actual, useApi: vi.fn() }
|
||
})
|
||
|
||
beforeAll(installSelectPolyfills)
|
||
|
||
afterEach(async () => {
|
||
localStorage.clear()
|
||
initializeLocale()
|
||
await unmountAll()
|
||
})
|
||
|
||
const READ_OWN = 'content.read:events=own, content.create:events=all'
|
||
const READ_ALL = 'content.read:events=all, content.create:events=all'
|
||
|
||
function main() {
|
||
return document.body.querySelector('#dashboard-content')!
|
||
}
|
||
|
||
function accessWith(spec: string) {
|
||
return customAccess(spec, 'Junior writer')
|
||
}
|
||
|
||
function newEntryButtons() {
|
||
return [...main().querySelectorAll('button')].filter(
|
||
(candidate) => text(candidate) === 'New entry',
|
||
)
|
||
}
|
||
|
||
const three = (_key: string, query: URLSearchParams) => listPage(eventEntries.slice(0, 3), 3, query)
|
||
const none = (_key: string, query: URLSearchParams) => listPage([], 0, query)
|
||
|
||
describe('Entry list: Read = Own with entries of their own', () => {
|
||
it('reads the header, the banner and the count line from the total', async () => {
|
||
await mountList('/content/events', {
|
||
access: accessWith(READ_OWN),
|
||
list: three,
|
||
lookup: lookupNames,
|
||
})
|
||
|
||
expect(text(main())).toContain('Events')
|
||
expect(text(main())).toContain('Your role shows only entries you created.')
|
||
expect(text(main())).toContain("Entries by others aren't shown")
|
||
expect(text(main())).toContain(
|
||
'Your role reads only entries you created. Ask an owner or admin if you need to see more.',
|
||
)
|
||
expect(text(main())).toContain('3 entries')
|
||
expect(main().querySelector('nav[aria-label*="pagination" i]')).toBeNull()
|
||
expect(newEntryButtons().length).toBeGreaterThan(0)
|
||
expect(newEntryButtons().every((button) => !button.disabled)).toBe(true)
|
||
})
|
||
|
||
it('shows neither the banner nor the own-scope header under Read = All', async () => {
|
||
await mountList('/content/events', {
|
||
access: accessWith(READ_ALL),
|
||
list: three,
|
||
lookup: lookupNames,
|
||
})
|
||
|
||
expect(text(main())).not.toContain("Entries by others aren't shown")
|
||
expect(text(main())).not.toContain('Your role shows only entries you created.')
|
||
})
|
||
})
|
||
|
||
describe('Entry list: Read = Own with no entries of their own', () => {
|
||
it('says what the person has not created, without hinting at other entries', async () => {
|
||
await mountList('/content/events', { access: accessWith(READ_OWN), list: none })
|
||
|
||
expect(text(main())).toContain("You haven't created an event yet")
|
||
expect(text(main())).toContain(
|
||
"Your role shows only entries you created, so other people's events aren't listed here. Use New entry to add one.",
|
||
)
|
||
expect(text(main())).not.toContain('No entries yet')
|
||
expect(newEntryButtons().length).toBeGreaterThan(0)
|
||
})
|
||
|
||
it('hides the action when the role cannot create', async () => {
|
||
await mountList('/content/events', {
|
||
access: accessWith('content.read:events=own'),
|
||
list: none,
|
||
})
|
||
|
||
expect(text(main())).toContain("You haven't created an event yet")
|
||
expect(newEntryButtons()).toHaveLength(0)
|
||
})
|
||
|
||
it('keeps the existing empty state under Read = All', async () => {
|
||
await mountList('/content/events', { access: accessWith(READ_ALL), list: none })
|
||
|
||
expect(text(main())).toContain('No entries yet')
|
||
expect(text(main())).not.toContain("You haven't created an event yet")
|
||
})
|
||
|
||
it('reads in French', async () => {
|
||
setLocale('fr')
|
||
await mountList('/content/events', { access: accessWith(READ_OWN), list: none })
|
||
|
||
expect(text(main())).toContain('Vous n’avez pas encore créé d’entrée dans Events')
|
||
expect(text(main())).toContain('Les entrées des autres ne sont pas affichées')
|
||
})
|
||
})
|
||
|
||
describe('Entry list: the singular noun comes from the type name', () => {
|
||
it.each([
|
||
[
|
||
'Recipes',
|
||
"You haven't created a recipe yet",
|
||
"other people's recipes aren't listed here",
|
||
],
|
||
[
|
||
'Press mentions',
|
||
"You haven't created a press mention yet",
|
||
"other people's press mentions aren't listed here",
|
||
],
|
||
[
|
||
'Categories',
|
||
"You haven't created a category yet",
|
||
"other people's categories aren't listed here",
|
||
],
|
||
])('%s', async (name, title, fragment) => {
|
||
await mountList('/content/events', {
|
||
access: accessWith(READ_OWN),
|
||
list: none,
|
||
type: () => ({ ...customTypes[0]!, name }),
|
||
})
|
||
|
||
expect(text(main())).toContain(title)
|
||
expect(text(main())).toContain(fragment)
|
||
})
|
||
})
|
||
|
||
describe('Entry list: a direct link to a hidden entry', () => {
|
||
it('shows the existing Entry not found page and nothing about the entry', async () => {
|
||
await mountEditor('/content/events/1', {
|
||
access: accessWith(READ_OWN),
|
||
entry: failWithBody(404, 'entry_not_found'),
|
||
})
|
||
|
||
expect(text(editorMain())).toContain('Entry not found')
|
||
expect(text(editorMain())).not.toContain('created by')
|
||
})
|
||
})
|