test(19.7-09): named tests for every framework and admin SPA fix of plans 06 and 07

- pier: TestSessionReserve (A-WR-03), TestLockScope (A-WR-07),
  TestScopeWidening (A-WR-04), TestStatelessFanOut (A-WR-05, A-WR-06),
  TestPersonalTokenBinding (A-WR-09), TestDispatchHardening (A-WR-01,
  A-WR-02, A-WR-08, A-IN-04, A-IN-05, A-IN-08), TestCatalogPathFields
  (A-WR-02, A-IN-07), TestPromptWriteCeiling (C-IN-01),
  TestTokenScopedRevoke (B-WR-04, C-IN-02), TestResolveClientAddress
  (B-IN-04); test seam WrapStore
- pact TestFrontendTokenScopes, towel TestClientIP, cabana
  TestTokenScopeNameParity
- cabana TestAccountContributionURL (B-WR-03), TestAccountContributionsIsolation
  (B-WR-06, B-IN-03), TestRecordActionPromptLocalized (B-IN-02), and the
  Postgres TestAccountTokenRevokeEvent (B-WR-04) and
  TestResolveBackendTokenRemote (B-IN-04)
- vitest: hotkeysEditing.test.ts (B-WR-01, B-WR-02) and the extended
  AccountContributions.test.ts (B-WR-03, B-WR-06, B-IN-01)
- check-phase19.7.sh requires every name (GO_TESTS, GO_TAGGED_TESTS,
  DOCKER_TESTS, ADMIN_FILES) and scans the new files (PHASE_TEST_FILES)
This commit is contained in:
2026-10-23 18:36:16 +02:00
parent fda46466c7
commit ccf77b9103
20 changed files with 2010 additions and 1 deletions

View File

@@ -141,4 +141,83 @@ describe('My account contributions', () => {
expect(wrapper.findAll('[data-account-contribution]')).toHaveLength(0)
expect(wrapper.find('[data-api-tokens]').exists()).toBe(true)
})
// Plan 07 hardening (B-WR-03, B-WR-06, B-IN-01).
it('a javascript: URL answer renders no link and never opens a window', async () => {
const opened = vi.spyOn(window, 'open').mockImplementation(() => null)
const { wrapper } = await openAccount({
[`POST ${OPEN}`]: envelope({ message: 'Done.', url: 'javascript:alert(1)' }),
})
await wrapper.find('[data-account-contribution-action="open"]').trigger('click')
await flushPromises()
expect(opened).not.toHaveBeenCalled()
expect(wrapper.find('[data-account-contribution-url]').exists()).toBe(false)
expect(wrapper.html()).not.toContain('javascript:')
expect(toasts.value.map((toast) => toast.text)).toEqual(['Done.'])
})
it('a section with error: true shows the section error text', async () => {
setBundle({ ...langFixture.data, ...strings, 'backend::lang.myaccount.section_error': { other: 'This section could not be loaded.' } }, 'en')
const { wrapper } = await openAccount({
[`GET ${CONTRIBUTIONS}`]: envelope([{ ...sections[0], status: '', error: true }, sections[1]]),
})
const cards = wrapper.findAll('[data-account-contribution]')
expect(cards[0]!.find('[data-account-contribution-error]').text()).toBe('This section could not be loaded.')
expect(cards[0]!.find('[data-account-contribution-status]').exists()).toBe(false)
expect(cards[1]!.find('[data-account-contribution-error]').exists()).toBe(false)
expect(cards[1]!.find('[data-account-contribution-status]').text()).toBe('Weekly')
})
it('a 500 on the list reload shows the error toast and keeps the earlier sections', async () => {
setBundle({ ...langFixture.data, ...strings, 'backend::lang.form.error_generic': { other: 'Something went wrong.' } }, 'en')
let loads = 0
const { wrapper } = await openAccount({
[`GET ${CONTRIBUTIONS}`]: () => {
loads++
return loads === 1
? envelope(sections)
: { status: 500, body: { error: { code: 'server_error', message: 'Server error', details: {} } } }
},
[`POST ${OPEN}`]: envelope({ message: 'Done.', url: '' }),
})
await wrapper.find('[data-account-contribution-action="open"]').trigger('click')
await flushPromises()
expect(loads).toBe(2)
expect(wrapper.findAll('[data-account-contribution]').map((card) => card.attributes('data-account-contribution'))).toEqual([
'site_link',
'newsletter',
])
// Newest toast first.
expect(toasts.value.map((toast) => [toast.text, toast.tone])).toEqual([
['Something went wrong.', 'danger'],
['Done.', 'success'],
])
})
it('the confirm and stale texts are the My account ones, not the record form ones', async () => {
setBundle(
{
...langFixture.data,
...strings,
'backend::lang.myaccount.action_confirm': { other: 'Run “:action”?' },
'backend::lang.form.action_confirm': { other: 'Run “:action” on this record?' },
'backend::lang.form.action_stale': { other: 'This action no longer applies to this record.' },
},
'en',
)
const STOP = `${CONTRIBUTIONS}/newsletter/actions/stop`
const { wrapper } = await openAccount({
[`GET ${CONTRIBUTIONS}`]: envelope([sections[0], { ...sections[1], actions: [{ ...sections[1]!.actions[0], confirm: '' }] }]),
[`POST ${STOP}`]: { status: 409, body: { error: { code: 'conflict', message: 'Conflict', details: {} } } },
})
await wrapper.find('[data-account-contribution-action="stop"]').trigger('click')
await flushPromises()
const dialog = document.body.querySelector<HTMLElement>('[data-confirm-dialog]')
expect(dialog?.textContent).toContain('Run “Stop the digest”?')
expect(dialog?.textContent).not.toContain('on this record')
dialog!.querySelector<HTMLButtonElement>('[data-action="confirm"]')!.click()
await flushPromises()
expect(toasts.value.map((toast) => toast.text)).toEqual(['This action no longer applies.'])
})
})