test(10-11): promote stub tests to real tests for drag-to-move + teleport dropdowns

- 6 real tests for UX-11 drag-to-move (click guard, drop emits, amber ring, dragend reset, toast wiring)
- 4 real tests for UX-13 Teleport dropdowns (DocumentCard, FolderRow position + scroll reposition)
- All dropdown tests RED (Teleport not yet implemented)
- Click-suppression test RED (guard not yet implemented)
This commit is contained in:
curo1305
2026-06-16 08:22:18 +02:00
parent dbb07c8c7d
commit f20420a4b5
2 changed files with 343 additions and 13 deletions
@@ -1,8 +1,154 @@
import { describe, it } from 'vitest'
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import { nextTick } from 'vue'
import DocumentCard from '../../documents/DocumentCard.vue'
import FolderRow from '../../folders/FolderRow.vue'
// ── Stubs ──────────────────────────────────────────────────────────────────────
const DocumentCardStubs = {
TopicBadge: true,
ShareModal: true,
}
// ── Fixtures ───────────────────────────────────────────────────────────────────
const SAMPLE_DOC = {
id: 'doc-1',
original_name: 'report.pdf',
size_bytes: 1024,
created_at: '2025-01-01T00:00:00Z',
status: 'ready',
topics: [],
is_shared: false,
}
const SAMPLE_FOLDER = {
id: 'folder-1',
name: 'Archive',
doc_count: 5,
}
describe('UX-13: Teleport + getBoundingClientRect dropdown pattern across components', () => {
it.todo('DocumentCard folder picker uses Teleport to body')
it.todo('DocumentCard folder picker position matches trigger getBoundingClientRect')
it.todo('FolderRow three-dot menu uses Teleport to body')
it.todo('FolderRow three-dot menu repositions on window scroll')
beforeEach(() => {
setActivePinia(createPinia())
// Mock getBoundingClientRect to return predictable values
vi.spyOn(Element.prototype, 'getBoundingClientRect').mockReturnValue({
top: 100,
bottom: 140,
left: 50,
right: 250,
width: 200,
height: 40,
x: 50,
y: 100,
})
// Mock window dimensions
Object.defineProperty(window, 'innerHeight', { value: 800, writable: true })
Object.defineProperty(window, 'innerWidth', { value: 1200, writable: true })
})
afterEach(() => {
vi.restoreAllMocks()
})
it('DocumentCard folder picker uses Teleport to body', async () => {
const wrapper = mount(DocumentCard, {
props: { doc: SAMPLE_DOC },
global: {
stubs: DocumentCardStubs,
// attachTo is required for Teleport to render to document.body
},
attachTo: document.body,
})
// Hover is not needed, just click the move button directly
const moveBtn = wrapper.find('[aria-label="Move to folder"]')
expect(moveBtn.exists()).toBe(true)
await moveBtn.trigger('click')
await nextTick()
// The folder picker should be teleported to body
const picker = document.body.querySelector('[data-test="folder-picker"]')
expect(picker).not.toBeNull()
wrapper.unmount()
})
it('DocumentCard folder picker position matches trigger getBoundingClientRect', async () => {
const wrapper = mount(DocumentCard, {
props: { doc: SAMPLE_DOC },
global: { stubs: DocumentCardStubs },
attachTo: document.body,
})
const moveBtn = wrapper.find('[aria-label="Move to folder"]')
await moveBtn.trigger('click')
await nextTick()
const picker = document.body.querySelector('[data-test="folder-picker"]')
expect(picker).not.toBeNull()
// Should have fixed positioning with top matching rect.bottom + offset
const style = picker.style
expect(style.top || style.bottom).toBeTruthy()
expect(style.left).toBeTruthy()
wrapper.unmount()
})
it('FolderRow three-dot menu uses Teleport to body', async () => {
const wrapper = mount(FolderRow, {
props: { folder: SAMPLE_FOLDER },
attachTo: document.body,
})
const menuBtn = wrapper.find('[aria-label="Folder actions"]')
expect(menuBtn.exists()).toBe(true)
await menuBtn.trigger('click')
await nextTick()
// The menu should be teleported to body
const menu = document.body.querySelector('[data-test="folder-row-menu"]')
expect(menu).not.toBeNull()
wrapper.unmount()
})
it('FolderRow three-dot menu repositions on window scroll', async () => {
const wrapper = mount(FolderRow, {
props: { folder: SAMPLE_FOLDER },
attachTo: document.body,
})
const menuBtn = wrapper.find('[aria-label="Folder actions"]')
await menuBtn.trigger('click')
await nextTick()
const menu = document.body.querySelector('[data-test="folder-row-menu"]')
expect(menu).not.toBeNull()
const initialTop = menu.style.top || menu.style.bottom
// Change what getBoundingClientRect returns (simulate scroll)
vi.spyOn(Element.prototype, 'getBoundingClientRect').mockReturnValue({
top: 200,
bottom: 240,
left: 50,
right: 250,
width: 200,
height: 40,
x: 50,
y: 200,
})
// Dispatch scroll event
window.dispatchEvent(new Event('scroll', { bubbles: true }))
await nextTick()
const updatedTop = menu.style.top || menu.style.bottom
// Position should have updated (top changed from 140+4 to 240+4)
expect(updatedTop).not.toBe(initialTop)
wrapper.unmount()
})
})