test(admin): import the untested SPA modules directly for check-phase10 hygiene

- mlLocale: type predicates, localeRecord, the ML_LOCALE_CHANGE broadcast
- MLField: locale switch, whole-map emit, broadcast follow, content-locale
- FileCaptionModal: prefill, save via FileRoutes.update, 422 stays, danger toast
- ApiTokensPanel and ApiTokenCreateDialog mounted directly next to the app tests
This commit is contained in:
2026-10-11 16:13:19 +02:00
parent 42b5cf887f
commit 1541cd4167
5 changed files with 425 additions and 4 deletions

View File

@@ -1,7 +1,9 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { enableAutoUnmount, flushPromises } from '@vue/test-utils'
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
import ApiTokenCreateDialog from '../../src/components/account/ApiTokenCreateDialog.vue'
import { useConfirm } from '../../src/components/ui/confirm'
import { toasts } from '../../src/state/useToasts'
import { API, keydown, mountApp, requestsTo, resetState, type Reply, type Route } from '../helpers'
import { API, keydown, mockApi, mountApp, requestsTo, resetState, type Reply, type Route } from '../helpers'
const TOKENS = `${API}/account/tokens`
const SCOPES = `${API}/account/tokens/scopes`
@@ -452,3 +454,41 @@ describe('ApiTokenCreateDialog step 2', () => {
).toBe(true)
})
})
describe('ApiTokenCreateDialog mounted directly', () => {
// The panel mounts the dialog closed and opens it; opening loads the scopes.
async function openDialog() {
const calls = mockApi(tokenRoutes())
const wrapper = mount(ApiTokenCreateDialog, {
props: { open: false, confirm: useConfirm() },
attachTo: document.body,
})
await wrapper.setProps({ open: true })
await flushPromises()
return { wrapper, calls }
}
it('opens with the scopes from one GET', async () => {
const { calls } = await openDialog()
expect(dialog()).toBeTruthy()
expect(requestsTo(calls, 'GET', SCOPES)).toHaveLength(1)
expect(dialog()!.querySelector('[data-scope="*"]')).toBeTruthy()
expect(dialog()!.querySelector('[data-scope="admin:read"]')).toBeTruthy()
})
it('creates a token with one POST, shows the plaintext and emits created', async () => {
const { wrapper, calls } = await openDialog()
await fillName('CI runner')
await pickAdminRead()
await click('[data-action="create-token-submit"]', dialog()!)
const posts = requestsTo(calls, 'POST', TOKENS)
expect(posts).toHaveLength(1)
expect(await posts[0]!.clone().json()).toEqual({ name: 'CI runner', expires: '90d', scopes: ['admin:read'] })
expect(dialog()!.querySelector<HTMLInputElement>('[data-token-plaintext]')!.value).toBe(PLAIN)
expect(wrapper.emitted('created')).toHaveLength(1)
await click('[data-action="hide-token"]', dialog()!)
expect(wrapper.emitted('update:open')).toEqual([[false]])
})
})