diff --git a/src/components/TabBar.test.tsx b/src/components/TabBar.test.tsx
index fa150e8a..5ddf4632 100644
--- a/src/components/TabBar.test.tsx
+++ b/src/components/TabBar.test.tsx
@@ -132,6 +132,71 @@ describe('TabBar', () => {
expect(screen.getAllByTestId('tab-modified-indicator')).toHaveLength(3)
})
+ it('does not reorder on drag cancel (dragEnd without drop)', () => {
+ const onReorderTabs = vi.fn()
+ const tabs = makeTabs(['Alpha', 'Beta', 'Gamma'])
+ render(
+
+ )
+
+ const alphaTab = screen.getByText('Alpha').closest('[draggable]')!
+ const betaTab = screen.getByText('Beta').closest('[draggable]')!
+
+ fireEvent.dragStart(alphaTab, {
+ dataTransfer: { effectAllowed: 'move', setData: vi.fn() },
+ })
+
+ const rect = betaTab.getBoundingClientRect()
+ fireEvent.dragOver(betaTab, {
+ clientX: rect.left + rect.width * 0.75,
+ dataTransfer: { dropEffect: 'move' },
+ })
+
+ // Cancel via dragEnd (Escape or release outside tab bar)
+ fireEvent.dragEnd(alphaTab)
+
+ expect(onReorderTabs).not.toHaveBeenCalled()
+ })
+
+ it('reorders from last toward first position', () => {
+ const onReorderTabs = vi.fn()
+ const tabs = makeTabs(['Alpha', 'Beta', 'Gamma'])
+ render(
+
+ )
+
+ const gammaTab = screen.getByText('Gamma').closest('[draggable]')!
+ const alphaTab = screen.getByText('Alpha').closest('[draggable]')!
+
+ fireEvent.dragStart(gammaTab, {
+ dataTransfer: { effectAllowed: 'move', setData: vi.fn() },
+ })
+
+ // jsdom returns zero-sized rects, so clientX always hits "right half"
+ // (insert after index 0 → insert index 1). This still validates
+ // that dragging the last tab toward the front produces a reorder.
+ const rect = alphaTab.getBoundingClientRect()
+ fireEvent.dragOver(alphaTab, {
+ clientX: rect.left + rect.width * 0.75,
+ dataTransfer: { dropEffect: 'move' },
+ })
+
+ fireEvent.drop(alphaTab, { dataTransfer: {} })
+
+ // Gamma (2) dragged onto Alpha (0) → reorder from 2 to 1
+ expect(onReorderTabs).toHaveBeenCalledWith(2, 1)
+ })
+
it('switches tab on click', () => {
const onSwitchTab = vi.fn()
const tabs = makeTabs(['Alpha', 'Beta'])
diff --git a/src/hooks/useTabManagement.test.ts b/src/hooks/useTabManagement.test.ts
index 749a2c79..ca7186d9 100644
--- a/src/hooks/useTabManagement.test.ts
+++ b/src/hooks/useTabManagement.test.ts
@@ -205,6 +205,28 @@ describe('useTabManagement', () => {
expect(result.current.tabs.map(t => t.entry.title)).toEqual(['C', 'A', 'B'])
})
+ it('preserves active tab after reorder', async () => {
+ const { result } = renderHook(() => useTabManagement())
+
+ await act(async () => {
+ await result.current.handleSelectNote(makeEntry({ path: '/vault/a.md', title: 'A' }))
+ })
+ await act(async () => {
+ await result.current.handleSelectNote(makeEntry({ path: '/vault/b.md', title: 'B' }))
+ })
+ await act(async () => {
+ await result.current.handleSelectNote(makeEntry({ path: '/vault/c.md', title: 'C' }))
+ })
+
+ // C is active (last opened). Move it to the front.
+ act(() => {
+ result.current.handleReorderTabs(2, 0)
+ })
+
+ expect(result.current.tabs.map(t => t.entry.title)).toEqual(['C', 'A', 'B'])
+ expect(result.current.activeTabPath).toBe('/vault/c.md')
+ })
+
it('persists tab order to localStorage', async () => {
const { result } = renderHook(() => useTabManagement())