Files
summercms/admin/tests/form/SingletonForm.test.ts

156 lines
6.6 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { enableAutoUnmount, flushPromises } from '@vue/test-utils'
import { setSettings } from '../../src/state/useSettings'
import { clone, companionsFormSchemaFixture, listFixture, listSchemaFixture } from '../fixtures/typed'
import { API, mountApp, notFound, requestsTo, resetState, type Route } from '../helpers'
// Phase 17 plan 06 specs for the singleton form (D-11, UI-SPEC "Singleton
// form"): a settings link to a pact.AdminSingleton controller opens its one
// record through GET …/singleton once the list answers 404 (WR-07: the same
// for a click, a new tab and a bookmark); the form has no Delete, and back,
// Cancel and Save and close return to the settings index.
const PROFILES = `${API}/acme/companions/profiles`
function singletonSchema() {
const schema = clone(companionsFormSchemaFixture)
schema.data.singleton = true
schema.data.fields = schema.data.fields.filter((field) => field.name === 'title')
return schema
}
function routes(overrides: Record<string, Route> = {}): Record<string, Route> {
return {
[`GET ${PROFILES}/singleton`]: { body: { data: { id: 9 }, meta: {} } },
[`GET ${PROFILES}/schema/form`]: { body: singletonSchema() },
[`GET ${PROFILES}/9`]: { body: { data: { id: 9, title: 'Main site' }, meta: {} } },
...overrides,
}
}
beforeEach(() => {
resetState()
setSettings([
{
code: 'profile',
label: 'Site profile',
description: 'The one site profile',
icon: 'settings',
category: 'CMS',
keywords: [],
model: '',
controller: 'acme.companions.profiles',
order: 1,
},
])
})
afterEach(() => {
document.body.innerHTML = ''
})
enableAutoUnmount(afterEach)
describe('SingletonForm', () => {
it('opens through /singleton from settings, has no Delete, and Save and close returns to settings', async () => {
let body: Record<string, unknown> | null = null
const { wrapper, router, calls } = await mountApp(
'/settings',
routes({
[`PUT ${PROFILES}/9`]: async (request) => {
body = (await request.json()) as Record<string, unknown>
return { body: { data: { id: 9, ...body }, meta: {} } }
},
}),
{ attach: true },
)
const link = wrapper.find('[data-settings="profile"]')
expect(link.attributes('href')).toBe('/acme/companions/profiles')
await link.trigger('click', { button: 0 })
await flushPromises()
expect(requestsTo(calls, 'GET', `${PROFILES}/singleton`)).toHaveLength(1)
expect(router.currentRoute.value.path).toBe('/acme/companions/profiles/9')
expect(wrapper.find('[data-action="delete"]').exists()).toBe(false)
expect(wrapper.find('[data-action="back"]').attributes('aria-label')).toBe('Wróć do ustawień')
expect(wrapper.find<HTMLInputElement>('#field-title').element.value).toBe('Main site')
// Save stays on the form.
await wrapper.find('#field-title').setValue('Renamed')
await wrapper.find('[data-action="save"]').trigger('click')
await flushPromises()
expect(body).toEqual({ title: 'Renamed' })
expect(router.currentRoute.value.path).toBe('/acme/companions/profiles/9')
// Save and close returns to the settings index.
await wrapper.find('[data-action="save-close"]').trigger('click')
await flushPromises()
expect(requestsTo(calls, 'PUT', `${PROFILES}/9`)).toHaveLength(2)
expect(router.currentRoute.value.path).toBe('/settings')
})
it('back returns to the settings index', async () => {
const { wrapper, router } = await mountApp('/acme/companions/profiles/9', routes(), { attach: true })
expect(wrapper.find('[data-action="delete"]').exists()).toBe(false)
await wrapper.find('[data-action="back"]').trigger('click')
await flushPromises()
expect(router.currentRoute.value.path).toBe('/settings')
})
it('a link to a controller that is not a singleton opens its list', async () => {
const { wrapper, router, calls } = await mountApp(
'/settings',
routes({
[`GET ${PROFILES}/singleton`]: { status: 404, body: notFound },
[`GET ${PROFILES}/schema/list`]: { body: listSchemaFixture },
[`GET ${PROFILES}`]: { body: listFixture },
}),
{ attach: true },
)
await wrapper.find('[data-settings="profile"]').trigger('click', { button: 0 })
await flushPromises()
expect(requestsTo(calls, 'GET', `${PROFILES}/singleton`)).toHaveLength(0)
expect(router.currentRoute.value.path).toBe('/acme/companions/profiles')
expect(wrapper.find('[data-list-failed]').exists()).toBe(false)
})
it('the list route of a singleton, opened directly (a new tab), opens its record', async () => {
const { wrapper, router, calls } = await mountApp('/acme/companions/profiles', routes(), { attach: true })
await vi.waitFor(() => expect(router.currentRoute.value.path).toBe('/acme/companions/profiles/9'))
await flushPromises()
expect(requestsTo(calls, 'GET', `${PROFILES}/singleton`)).toHaveLength(1)
expect(wrapper.find<HTMLInputElement>('#field-title').element.value).toBe('Main site')
})
it('a list that answers 404 for a controller that is not a singleton shows the failure', async () => {
const { wrapper, router } = await mountApp(
'/acme/companions/profiles',
routes({ [`GET ${PROFILES}/singleton`]: { status: 404, body: notFound } }),
{ attach: true },
)
await flushPromises()
expect(router.currentRoute.value.path).toBe('/acme/companions/profiles')
expect(wrapper.find('[data-list-failed]').exists()).toBe(true)
})
it('a modified click opens the link as it is, without asking for the record', async () => {
const { wrapper, router, calls } = await mountApp('/settings', routes(), { attach: true })
await wrapper.find('[data-settings="profile"]').trigger('click', { button: 0, ctrlKey: true })
await flushPromises()
expect(requestsTo(calls, 'GET', `${PROFILES}/singleton`)).toHaveLength(0)
expect(router.currentRoute.value.path).toBe('/settings')
})
it('a form without the singleton flag keeps Delete and returns to its list', async () => {
const schema = singletonSchema()
schema.data.singleton = false
const { wrapper, router } = await mountApp(
'/acme/companions/profiles/9',
routes({ [`GET ${PROFILES}/schema/form`]: { body: schema }, [`GET ${PROFILES}/singleton`]: { status: 404, body: notFound } }),
{ attach: true },
)
expect(wrapper.find('[data-action="delete"]').exists()).toBe(true)
await wrapper.find('[data-action="back"]').trigger('click')
await flushPromises()
expect(router.currentRoute.value.path).toBe('/acme/companions/profiles')
})
})