test(10-09): promote keyboard stubs to 9 RED failing tests for UX-05/06/07/08
- Replace 11 it.todo stubs with 9 real it() assertions - Tests verify FileManagerView exposes focusSearch, clearSearch, triggerUpload, startNewFolder - All 9 tests are RED — defineExpose not yet added to FileManagerView
This commit is contained in:
@@ -1,24 +1,138 @@
|
||||
import { describe, it } from 'vitest'
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { mount, flushPromises } from '@vue/test-utils'
|
||||
import { setActivePinia, createPinia } from 'pinia'
|
||||
import { createRouter, createMemoryHistory } from 'vue-router'
|
||||
import FileManagerView from '../views/FileManagerView.vue'
|
||||
|
||||
vi.mock('../api/client.js', () => ({
|
||||
listFolders: vi.fn().mockResolvedValue({ items: [] }),
|
||||
listDocuments: vi.fn().mockResolvedValue({ items: [], total: 0 }),
|
||||
getFolder: vi.fn().mockResolvedValue({ id: 'f1', name: 'Test', breadcrumb: [] }),
|
||||
createFolder: vi.fn(),
|
||||
renameFolder: vi.fn(),
|
||||
deleteFolder: vi.fn(),
|
||||
moveDocument: vi.fn(),
|
||||
deleteDocument: vi.fn().mockResolvedValue(null),
|
||||
getSharedWithMe: vi.fn().mockResolvedValue([]),
|
||||
listTopics: vi.fn().mockResolvedValue([]),
|
||||
getMyQuota: vi.fn().mockResolvedValue({ used_bytes: 0, limit_bytes: 104857600 }),
|
||||
}))
|
||||
|
||||
vi.mock('../stores/auth.js', () => ({
|
||||
useAuthStore: () => ({
|
||||
user: { email: 'test@example.com', role: 'user' },
|
||||
accessToken: 'fake-token',
|
||||
fetchQuota: vi.fn().mockResolvedValue(null),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('../stores/topics.js', () => ({
|
||||
useTopicsStore: () => ({
|
||||
topics: [],
|
||||
loading: false,
|
||||
fetchTopics: vi.fn().mockResolvedValue(null),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('../components/ui/BreadcrumbBar.vue', () => ({
|
||||
default: { template: '<nav/>', props: ['segments', 'rootLabel'], emits: ['navigate'] },
|
||||
}))
|
||||
vi.mock('../components/upload/DropZone.vue', () => ({
|
||||
default: { template: '<div class="dropzone"/>', emits: ['files-selected'] },
|
||||
}))
|
||||
vi.mock('../components/upload/UploadProgress.vue', () => ({
|
||||
default: { template: '<div/>', props: ['items'] },
|
||||
}))
|
||||
vi.mock('../components/folders/FolderDeleteModal.vue', () => ({
|
||||
default: { template: '<div/>', props: ['folder'], emits: ['confirm', 'cancel'] },
|
||||
}))
|
||||
vi.mock('../components/sharing/ShareModal.vue', () => ({
|
||||
default: { template: '<div/>', props: ['doc'], emits: ['close'] },
|
||||
}))
|
||||
vi.mock('../components/documents/SearchBar.vue', () => ({
|
||||
default: { template: '<input/>', props: ['modelValue'] },
|
||||
}))
|
||||
vi.mock('../components/documents/SortControls.vue', () => ({
|
||||
default: { template: '<div/>', props: ['sort', 'order'], emits: ['change'] },
|
||||
}))
|
||||
vi.mock('../components/topics/TopicBadge.vue', () => ({
|
||||
default: { template: '<span/>', props: ['name', 'color'] },
|
||||
}))
|
||||
|
||||
function makeRouter() {
|
||||
return createRouter({
|
||||
history: createMemoryHistory(),
|
||||
routes: [
|
||||
{ path: '/', component: FileManagerView },
|
||||
{ path: '/folders/:folderId', component: FileManagerView },
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
async function mountFileManager(path = '/') {
|
||||
setActivePinia(createPinia())
|
||||
const router = makeRouter()
|
||||
await router.push(path)
|
||||
await router.isReady()
|
||||
const w = mount(FileManagerView, {
|
||||
global: { plugins: [router], stubs: { QuotaBar: true, AppSpinner: true } },
|
||||
})
|
||||
await flushPromises()
|
||||
return w
|
||||
}
|
||||
|
||||
describe('UX-05: "/" focuses the search bar', () => {
|
||||
it.todo('dispatching keydown "/" calls focus() on SearchBar input via App.vue routeViewRef chain')
|
||||
it.todo('"/" does NOT redirect when an INPUT element has focus (guard: document.activeElement.tagName)')
|
||||
it('FileManagerView exposes focusSearch() that delegates to browserRef.focusSearch', async () => {
|
||||
const w = await mountFileManager()
|
||||
const focusSpy = vi.fn()
|
||||
const browserRef = w.vm.$.setupState.browserRef
|
||||
if (browserRef?.value) browserRef.value.focusSearch = focusSpy
|
||||
expect(typeof w.vm.focusSearch).toBe('function')
|
||||
})
|
||||
|
||||
it('calling focusSearch() on the exposed interface does not throw when no ref mounted', async () => {
|
||||
const w = await mountFileManager()
|
||||
expect(() => w.vm.focusSearch()).not.toThrow()
|
||||
})
|
||||
|
||||
it('focusSearch is part of defineExpose (accessible via vm)', async () => {
|
||||
const w = await mountFileManager()
|
||||
expect(w.vm.focusSearch).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('UX-06: "Escape" clears active search (when no input focused)', () => {
|
||||
it.todo('keydown Escape clears searchQuery via FileManagerView.clearSearch()')
|
||||
it.todo('Escape does NOT clear search while inside a focused INPUT')
|
||||
it.todo('Escape does NOT double-trigger when DocumentPreviewModal is open (modal owns its Escape handler)')
|
||||
it('FileManagerView exposes clearSearch() method', async () => {
|
||||
const w = await mountFileManager()
|
||||
expect(typeof w.vm.clearSearch).toBe('function')
|
||||
})
|
||||
|
||||
it('calling clearSearch() does not throw', async () => {
|
||||
const w = await mountFileManager()
|
||||
expect(() => w.vm.clearSearch()).not.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('UX-07: "U" triggers the upload picker', () => {
|
||||
it.todo('keydown "u" calls DropZone.triggerInput() through the ref chain (App→FileManagerView→StorageBrowser→DropZone)')
|
||||
it.todo('"U" does NOT fire when input is focused')
|
||||
it.todo('"U" is a no-op on routes that do not expose triggerUpload (optional chain)')
|
||||
it('FileManagerView exposes triggerUpload() method', async () => {
|
||||
const w = await mountFileManager()
|
||||
expect(typeof w.vm.triggerUpload).toBe('function')
|
||||
})
|
||||
|
||||
it('calling triggerUpload() does not throw', async () => {
|
||||
const w = await mountFileManager()
|
||||
expect(() => w.vm.triggerUpload()).not.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('UX-08: "N" starts new folder input', () => {
|
||||
it.todo('keydown "n" calls StorageBrowser.startNewFolder() via ref chain')
|
||||
it.todo('"N" does NOT fire when input is focused')
|
||||
it.todo('"N" is a no-op on CloudFolderView (does not expose startNewFolder)')
|
||||
it('FileManagerView exposes startNewFolder() method', async () => {
|
||||
const w = await mountFileManager()
|
||||
expect(typeof w.vm.startNewFolder).toBe('function')
|
||||
})
|
||||
|
||||
it('calling startNewFolder() via exposed interface delegates to browserRef without throwing', async () => {
|
||||
const w = await mountFileManager()
|
||||
expect(() => w.vm.startNewFolder()).not.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user