diff --git a/frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js b/frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js
index 242bd27..1adbec8 100644
--- a/frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js
+++ b/frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js
@@ -1,12 +1,87 @@
-import { describe, it } from 'vitest'
+import { describe, it, expect, vi, beforeEach } from 'vitest'
+import { mount } from '@vue/test-utils'
+import { setActivePinia, createPinia } from 'pinia'
+
+vi.mock('../../../api/client.js', () => ({
+ adminListAuditLog: vi.fn().mockResolvedValue({ items: [], total: 0 }),
+ adminExportAuditLogCsv: vi.fn().mockResolvedValue({}),
+ adminListDailyExports: vi.fn().mockResolvedValue({ items: [] }),
+ adminDownloadDailyExport: vi.fn().mockResolvedValue({}),
+}))
+
+const stubs = {
+ BreadcrumbBar: true,
+ EmptyState: true,
+ AppIcon: true,
+}
+
+async function mountAudit(overrides = {}) {
+ const { default: AdminAuditView } = await import('../AdminAuditView.vue')
+ return mount(AdminAuditView, {
+ global: { stubs, plugins: [createPinia()] },
+ ...overrides,
+ })
+}
describe('UX-04: AdminAuditView shows skeleton table rows during loading', () => {
- it.todo('renders 5+ skeleton
rows when loading=true')
- it.todo('skeleton rows have 5 columns matching the real table header')
- it.todo('animated spinner Loading audit log… text is removed')
+ beforeEach(() => {
+ setActivePinia(createPinia())
+ vi.resetModules()
+ })
+
+ it('renders 8 skeleton
rows when loading=true', async () => {
+ const { adminListAuditLog } = await import('../../../api/client.js')
+ adminListAuditLog.mockReturnValue(new Promise(() => {})) // never resolves => loading stays true
+ const wrapper = await mountAudit()
+ const tbody = wrapper.find('tbody')
+ expect(tbody.exists()).toBe(true)
+ const rows = tbody.findAll('tr')
+ expect(rows.length).toBeGreaterThanOrEqual(8)
+ })
+
+ it('skeleton rows have exactly 5 cells matching column count', async () => {
+ const { adminListAuditLog } = await import('../../../api/client.js')
+ adminListAuditLog.mockReturnValue(new Promise(() => {}))
+ const wrapper = await mountAudit()
+ const rows = wrapper.find('tbody').findAll('tr')
+ rows.forEach(row => {
+ expect(row.findAll('td').length).toBe(5)
+ })
+ })
+
+ it('Loading audit log text is absent when loading=true', async () => {
+ const { adminListAuditLog } = await import('../../../api/client.js')
+ adminListAuditLog.mockReturnValue(new Promise(() => {}))
+ const wrapper = await mountAudit()
+ expect(wrapper.text()).not.toContain('Loading audit log')
+ })
})
describe('UX-01 (audit empty): EmptyState renders when entries is empty after load', () => {
- it.todo('EmptyState icon=clipboardList headline="No entries found" appears when entries.length===0 and not loading')
- it.todo('clear-filters button rendered in #cta slot')
+ beforeEach(() => {
+ setActivePinia(createPinia())
+ vi.resetModules()
+ })
+
+ it('renders when entries empty and not loading', async () => {
+ const { adminListAuditLog } = await import('../../../api/client.js')
+ adminListAuditLog.mockResolvedValue({ items: [], total: 0 })
+ const wrapper = await mountAudit()
+ await new Promise(r => setTimeout(r, 50))
+ await wrapper.vm.$nextTick()
+ const emptyState = wrapper.findComponent({ name: 'EmptyState' })
+ expect(emptyState.exists()).toBe(true)
+ expect(emptyState.attributes('icon') ?? emptyState.props('icon')).toBe('clipboardList')
+ const headline = emptyState.attributes('headline') ?? emptyState.props('headline')
+ expect(headline).toBe('No entries found')
+ })
+
+ it('EmptyState includes a Clear filters CTA button', async () => {
+ const { adminListAuditLog } = await import('../../../api/client.js')
+ adminListAuditLog.mockResolvedValue({ items: [], total: 0 })
+ const wrapper = await mountAudit()
+ await new Promise(r => setTimeout(r, 50))
+ await wrapper.vm.$nextTick()
+ expect(wrapper.text()).toContain('Clear filters')
+ })
})
diff --git a/frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js b/frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js
index 584951d..b66bc54 100644
--- a/frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js
+++ b/frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js
@@ -1,7 +1,60 @@
-import { describe, it } from 'vitest'
+import { describe, it, expect, vi, beforeEach } from 'vitest'
+import { mount } from '@vue/test-utils'
+import { setActivePinia, createPinia } from 'pinia'
+
+vi.mock('../../../api/client.js', () => ({
+ adminListUsers: vi.fn().mockResolvedValue({ items: [] }),
+ adminCreateUser: vi.fn().mockResolvedValue({}),
+ adminDeactivateUser: vi.fn().mockResolvedValue({}),
+ adminReactivateUser: vi.fn().mockResolvedValue({}),
+ adminDeleteUser: vi.fn().mockResolvedValue({}),
+ adminResetUserPassword: vi.fn().mockResolvedValue({}),
+}))
+
+const stubs = {
+ BreadcrumbBar: true,
+ EmptyState: true,
+ AppIcon: true,
+}
+
+async function mountUsers(overrides = {}) {
+ const { default: AdminUsersView } = await import('../AdminUsersView.vue')
+ return mount(AdminUsersView, {
+ global: { stubs, plugins: [createPinia()] },
+ ...overrides,
+ })
+}
describe('UX-04: AdminUsersView shows skeleton table rows during loading', () => {
- it.todo('renders 5+ skeleton rows when loading=true')
- it.todo('skeleton rows have 6 columns matching the real table header')
- it.todo('animated spinner Loading users… text is removed')
+ beforeEach(() => {
+ setActivePinia(createPinia())
+ vi.resetModules()
+ })
+
+ it('renders 5 skeleton rows when loading=true', async () => {
+ const { adminListUsers } = await import('../../../api/client.js')
+ adminListUsers.mockReturnValue(new Promise(() => {})) // never resolves => loading stays true
+ const wrapper = await mountUsers()
+ const tbody = wrapper.find('tbody')
+ expect(tbody.exists()).toBe(true)
+ const rows = tbody.findAll('tr')
+ expect(rows.length).toBeGreaterThanOrEqual(5)
+ })
+
+ it('skeleton rows have exactly 6 | cells', async () => {
+ const { adminListUsers } = await import('../../../api/client.js')
+ adminListUsers.mockReturnValue(new Promise(() => {}))
+ const wrapper = await mountUsers()
+ const rows = wrapper.find('tbody').findAll('tr')
+ rows.forEach(row => {
+ expect(row.findAll('td').length).toBe(6)
+ })
+ })
+
+ it('Loading users text is absent when loading=true', async () => {
+ const { adminListUsers } = await import('../../../api/client.js')
+ adminListUsers.mockReturnValue(new Promise(() => {}))
+ const wrapper = await mountUsers()
+ expect(wrapper.text()).not.toContain('Loading users')
+ })
})
| |