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:
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user