214 lines
7.3 KiB
TypeScript
214 lines
7.3 KiB
TypeScript
|
|
import { fireEvent, screen } from '@testing-library/react'
|
||
|
|
import { describe, expect, it, vi } from 'vitest'
|
||
|
|
import { APP_COMMAND_IDS, getAppCommandShortcutDisplay } from '../hooks/appCommandCatalog'
|
||
|
|
import { makeEntry, mockEntries, renderNoteList } from '../test-utils/noteListTestUtils'
|
||
|
|
|
||
|
|
function setViewportSize(width: number, height: number) {
|
||
|
|
Object.defineProperty(window, 'innerWidth', { value: width, configurable: true })
|
||
|
|
Object.defineProperty(window, 'innerHeight', { value: height, configurable: true })
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderNoteListWithFullActionMenu() {
|
||
|
|
renderNoteList({
|
||
|
|
canCopyGitUrl: vi.fn(() => true),
|
||
|
|
onBulkArchive: vi.fn(),
|
||
|
|
onBulkDeletePermanently: vi.fn(),
|
||
|
|
onCopyFilePath: vi.fn(),
|
||
|
|
onCopyGitUrl: vi.fn(),
|
||
|
|
onEnterNeighborhood: vi.fn(),
|
||
|
|
onExportPdf: vi.fn(),
|
||
|
|
onOpenInNewWindow: vi.fn(),
|
||
|
|
onRenameFilename: vi.fn(),
|
||
|
|
onRevealFile: vi.fn(),
|
||
|
|
onToggleFavorite: vi.fn(),
|
||
|
|
onToggleOrganized: vi.fn(),
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
function openBuildLaputaActions() {
|
||
|
|
fireEvent.contextMenu(screen.getByText('Build Laputa App'))
|
||
|
|
}
|
||
|
|
|
||
|
|
function clickBuildLaputaAction(label: string) {
|
||
|
|
openBuildLaputaActions()
|
||
|
|
fireEvent.click(screen.getByText(label))
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('NoteList context menu', () => {
|
||
|
|
it('opens note actions from a right-clicked note item', () => {
|
||
|
|
const onOpenInNewWindow = vi.fn()
|
||
|
|
const onEnterNeighborhood = vi.fn()
|
||
|
|
const onBulkArchive = vi.fn()
|
||
|
|
const onBulkDeletePermanently = vi.fn()
|
||
|
|
const onExportPdf = vi.fn()
|
||
|
|
const onToggleFavorite = vi.fn()
|
||
|
|
const onToggleOrganized = vi.fn()
|
||
|
|
const onRenameFilename = vi.fn()
|
||
|
|
const onRevealFile = vi.fn()
|
||
|
|
const onCopyFilePath = vi.fn()
|
||
|
|
const canCopyGitUrl = vi.fn(() => true)
|
||
|
|
const onCopyGitUrl = vi.fn()
|
||
|
|
|
||
|
|
renderNoteList({
|
||
|
|
onOpenInNewWindow,
|
||
|
|
onEnterNeighborhood,
|
||
|
|
onBulkArchive,
|
||
|
|
onBulkDeletePermanently,
|
||
|
|
onExportPdf,
|
||
|
|
onToggleFavorite,
|
||
|
|
onToggleOrganized,
|
||
|
|
onRenameFilename,
|
||
|
|
onRevealFile,
|
||
|
|
onCopyFilePath,
|
||
|
|
canCopyGitUrl,
|
||
|
|
onCopyGitUrl,
|
||
|
|
})
|
||
|
|
|
||
|
|
openBuildLaputaActions()
|
||
|
|
|
||
|
|
expect(screen.getByTestId('note-list-context-menu')).toBeInTheDocument()
|
||
|
|
expect(screen.getByTestId('note-list-context-menu')).toHaveClass('z-[12000]')
|
||
|
|
expect(screen.getByText(getAppCommandShortcutDisplay(APP_COMMAND_IDS.noteOpenInNewWindow)!)).toBeInTheDocument()
|
||
|
|
expect(screen.getByText(getAppCommandShortcutDisplay(APP_COMMAND_IDS.noteToggleFavorite)!)).toBeInTheDocument()
|
||
|
|
expect(screen.getByText(getAppCommandShortcutDisplay(APP_COMMAND_IDS.noteToggleOrganized)!)).toBeInTheDocument()
|
||
|
|
expect(screen.getByText(getAppCommandShortcutDisplay(APP_COMMAND_IDS.noteDelete)!)).toBeInTheDocument()
|
||
|
|
|
||
|
|
fireEvent.click(screen.getByText('Open in New Window'))
|
||
|
|
expect(onOpenInNewWindow).toHaveBeenCalledWith(mockEntries[0])
|
||
|
|
|
||
|
|
clickBuildLaputaAction('Add to Favorites')
|
||
|
|
expect(onToggleFavorite).toHaveBeenCalledWith(mockEntries[0].path)
|
||
|
|
|
||
|
|
clickBuildLaputaAction('Mark as Organized')
|
||
|
|
expect(onToggleOrganized).toHaveBeenCalledWith(mockEntries[0].path)
|
||
|
|
|
||
|
|
clickBuildLaputaAction('Rename filename')
|
||
|
|
expect(screen.getByTestId('note-list-rename-dialog')).toBeInTheDocument()
|
||
|
|
expect(screen.getByTestId('note-list-rename-input')).toHaveValue('26q1-laputa-app')
|
||
|
|
fireEvent.change(screen.getByTestId('note-list-rename-input'), { target: { value: 'renamed-from-menu.md ' } })
|
||
|
|
fireEvent.click(screen.getByText('Rename'))
|
||
|
|
expect(onRenameFilename).toHaveBeenCalledWith(mockEntries[0].path, 'renamed-from-menu')
|
||
|
|
|
||
|
|
clickBuildLaputaAction("Open note's neighborhood")
|
||
|
|
expect(onEnterNeighborhood).toHaveBeenCalledWith(mockEntries[0])
|
||
|
|
|
||
|
|
clickBuildLaputaAction('Reveal in Finder')
|
||
|
|
expect(onRevealFile).toHaveBeenCalledWith(mockEntries[0].path)
|
||
|
|
|
||
|
|
clickBuildLaputaAction('Copy file path')
|
||
|
|
expect(onCopyFilePath).toHaveBeenCalledWith(mockEntries[0].path)
|
||
|
|
|
||
|
|
clickBuildLaputaAction('Copy git URL')
|
||
|
|
expect(canCopyGitUrl).toHaveBeenCalledWith(mockEntries[0])
|
||
|
|
expect(onCopyGitUrl).toHaveBeenCalledWith(mockEntries[0])
|
||
|
|
|
||
|
|
clickBuildLaputaAction('Export note as PDF')
|
||
|
|
expect(onExportPdf).toHaveBeenCalledWith(mockEntries[0])
|
||
|
|
|
||
|
|
clickBuildLaputaAction('Archive this note')
|
||
|
|
expect(onBulkArchive).toHaveBeenCalledWith([mockEntries[0].path])
|
||
|
|
|
||
|
|
clickBuildLaputaAction('Delete this note')
|
||
|
|
expect(onBulkDeletePermanently).toHaveBeenCalledWith([mockEntries[0].path])
|
||
|
|
})
|
||
|
|
|
||
|
|
it('shows stateful favorite and organized labels for pinned notes', () => {
|
||
|
|
renderNoteList({
|
||
|
|
entries: [
|
||
|
|
makeEntry({
|
||
|
|
favorite: true,
|
||
|
|
organized: true,
|
||
|
|
path: '/vault/stateful.md',
|
||
|
|
title: 'Stateful Note',
|
||
|
|
}),
|
||
|
|
],
|
||
|
|
onToggleFavorite: vi.fn(),
|
||
|
|
onToggleOrganized: vi.fn(),
|
||
|
|
})
|
||
|
|
|
||
|
|
fireEvent.contextMenu(screen.getByText('Stateful Note'))
|
||
|
|
|
||
|
|
expect(screen.getByText('Remove from Favorites')).toBeInTheDocument()
|
||
|
|
expect(screen.getByText('Mark as Unorganized')).toBeInTheDocument()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('hides the organized action for PDF file rows while keeping file actions', () => {
|
||
|
|
const pdfEntry = makeEntry({
|
||
|
|
fileKind: 'binary',
|
||
|
|
filename: 'research.pdf',
|
||
|
|
path: '/vault/research.pdf',
|
||
|
|
title: 'research.pdf',
|
||
|
|
})
|
||
|
|
const onCopyFilePath = vi.fn()
|
||
|
|
const onRevealFile = vi.fn()
|
||
|
|
const onRenameFilename = vi.fn()
|
||
|
|
const onToggleOrganized = vi.fn()
|
||
|
|
|
||
|
|
renderNoteList({
|
||
|
|
allNotesFileVisibility: { pdfs: true, images: false, unsupported: false },
|
||
|
|
entries: [pdfEntry],
|
||
|
|
onCopyFilePath,
|
||
|
|
onRenameFilename,
|
||
|
|
onRevealFile,
|
||
|
|
onToggleOrganized,
|
||
|
|
})
|
||
|
|
|
||
|
|
fireEvent.contextMenu(screen.getByTestId('pdf-file-item'))
|
||
|
|
|
||
|
|
expect(screen.getByTestId('note-list-context-menu')).toBeInTheDocument()
|
||
|
|
expect(screen.queryByText('Mark as Organized')).not.toBeInTheDocument()
|
||
|
|
expect(screen.queryByText('Rename filename')).not.toBeInTheDocument()
|
||
|
|
expect(screen.getByText('Reveal in Finder')).toBeInTheDocument()
|
||
|
|
|
||
|
|
fireEvent.click(screen.getByText('Copy file path'))
|
||
|
|
expect(onCopyFilePath).toHaveBeenCalledWith(pdfEntry.path)
|
||
|
|
expect(onToggleOrganized).not.toHaveBeenCalled()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('hides the git URL action for notes without a remote', () => {
|
||
|
|
renderNoteList({
|
||
|
|
canCopyGitUrl: () => false,
|
||
|
|
onCopyGitUrl: vi.fn(),
|
||
|
|
onCopyFilePath: vi.fn(),
|
||
|
|
})
|
||
|
|
|
||
|
|
fireEvent.contextMenu(screen.getByText('Build Laputa App'))
|
||
|
|
|
||
|
|
expect(screen.queryByText('Copy git URL')).not.toBeInTheDocument()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('keeps note actions visible when opened near the bottom-right viewport edge', () => {
|
||
|
|
setViewportSize(1024, 768)
|
||
|
|
renderNoteList({
|
||
|
|
onOpenInNewWindow: vi.fn(),
|
||
|
|
onBulkDeletePermanently: vi.fn(),
|
||
|
|
})
|
||
|
|
|
||
|
|
fireEvent.contextMenu(screen.getByText('Build Laputa App'), { clientX: 1000, clientY: 740 })
|
||
|
|
|
||
|
|
const menu = screen.getByTestId('note-list-context-menu')
|
||
|
|
expect(menu.style.left).toBe('')
|
||
|
|
expect(menu.style.top).toBe('')
|
||
|
|
expect(menu).toHaveStyle({
|
||
|
|
bottom: '28px',
|
||
|
|
maxHeight: '732px',
|
||
|
|
right: '24px',
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
it('caps note actions to the available viewport space from a mid-height right-click', () => {
|
||
|
|
setViewportSize(420, 320)
|
||
|
|
renderNoteListWithFullActionMenu()
|
||
|
|
|
||
|
|
fireEvent.contextMenu(screen.getByText('Build Laputa App'), { clientX: 271, clientY: 157 })
|
||
|
|
|
||
|
|
const menu = screen.getByTestId('note-list-context-menu')
|
||
|
|
expect(menu).toHaveStyle({
|
||
|
|
maxHeight: '155px',
|
||
|
|
overflowY: 'auto',
|
||
|
|
right: '149px',
|
||
|
|
top: '157px',
|
||
|
|
})
|
||
|
|
})
|
||
|
|
})
|