fix(17): WR-07 open a singleton's record from its list route

A settings link to a singleton controller only worked on a plain left
click; a new tab landed on the list route, which answers 404, and every
non-singleton link paid an extra /singleton request. The settings index
now links like any controller link, and the list view opens the
controller's one record when its list answers 404 and the singleton
route answers an id.
This commit is contained in:
2026-10-12 21:43:07 +02:00
parent fb10b06b21
commit 9d7c21d800
5 changed files with 73 additions and 49 deletions

View File

@@ -1,13 +1,14 @@
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
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 } from '../fixtures/typed'
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; the form has no Delete, and back, Cancel
// and Save and close return to the settings index.
// 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() {
@@ -94,16 +95,40 @@ describe('SingletonForm', () => {
expect(router.currentRoute.value.path).toBe('/settings')
})
it('a link to a controller that is not a singleton opens its list', async () => {
it('a link to a controller that is not a singleton opens its list without asking for a record', async () => {
const { wrapper, router, calls } = await mountApp(
'/settings',
routes({ [`GET ${PROFILES}/singleton`]: { status: 404, body: notFound } }),
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(1)
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 () => {
@@ -117,7 +142,11 @@ describe('SingletonForm', () => {
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 } }), { attach: true })
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()