feat(ui): NoteCard 의 notebook chip + 1-click 이동
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,16 +2,17 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
import { render, screen, fireEvent, cleanup, waitFor } from '@testing-library/react';
|
||||
import type { Note } from '@shared/types';
|
||||
import type { Note, Notebook } from '@shared/types';
|
||||
|
||||
const { mockOpenMedia, mockSetStatus, mockClassify, mockUpdateRawText } = vi.hoisted(() => ({
|
||||
const { mockOpenMedia, mockSetStatus, mockClassify, mockUpdateRawText, mockMoveNoteToNotebook } = vi.hoisted(() => ({
|
||||
mockOpenMedia: vi.fn(async () => ({ ok: true })),
|
||||
mockSetStatus: vi.fn(async () => ({ ok: true as const })),
|
||||
mockClassify: vi.fn(async () => ({
|
||||
recommended: 'archived' as const,
|
||||
rationale: 'stub'
|
||||
})),
|
||||
mockUpdateRawText: vi.fn(async () => ({ ok: true as const }))
|
||||
mockUpdateRawText: vi.fn(async () => ({ ok: true as const })),
|
||||
mockMoveNoteToNotebook: vi.fn(async () => {})
|
||||
}));
|
||||
|
||||
vi.mock('../../src/renderer/inbox/api.js', () => ({
|
||||
@@ -33,9 +34,24 @@ vi.mock('../../src/renderer/inbox/api.js', () => ({
|
||||
}));
|
||||
|
||||
const mockRefreshMeta = vi.fn();
|
||||
|
||||
// Notebooks used across notebook-chip tests.
|
||||
const stubNotebooks: Notebook[] = [
|
||||
{ id: 'nb-1', name: '회사', color: '#4a90d9', createdAt: '2026-01-01T00:00:00Z', updatedAt: '2026-01-01T00:00:00Z', noteCount: 1 },
|
||||
{ id: 'nb-2', name: '개인', color: '#e67e22', createdAt: '2026-01-01T00:00:00Z', updatedAt: '2026-01-01T00:00:00Z', noteCount: 0 }
|
||||
];
|
||||
|
||||
vi.mock('../../src/renderer/inbox/store.js', () => ({
|
||||
useInbox: Object.assign(
|
||||
() => ({}),
|
||||
// Selector-aware: if selector is a function, call it with the mock state.
|
||||
(selector?: (s: unknown) => unknown) => {
|
||||
const state = {
|
||||
notebooks: stubNotebooks,
|
||||
moveNoteToNotebook: mockMoveNoteToNotebook
|
||||
};
|
||||
if (typeof selector === 'function') return selector(state);
|
||||
return state;
|
||||
},
|
||||
{ getState: () => ({ setTagFilter: vi.fn(), refreshMeta: mockRefreshMeta }) }
|
||||
)
|
||||
}));
|
||||
@@ -190,3 +206,33 @@ describe('NoteCard — raw_text editing', () => {
|
||||
expect(last.rawText).toBe('new');
|
||||
});
|
||||
});
|
||||
|
||||
describe('NoteCard — notebook chip (Task 17)', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('현재 notebook 이름 chip 렌더링', () => {
|
||||
render(<NoteCard note={{ ...baseNote, notebookId: 'nb-1' }} onUpdated={vi.fn()} mode="inbox" />);
|
||||
expect(screen.getByTitle('다른 노트북으로 이동')).toBeInTheDocument();
|
||||
expect(screen.getByTitle('다른 노트북으로 이동').textContent).toContain('회사');
|
||||
});
|
||||
|
||||
it('chip 클릭 → 다른 notebook 목록 dropdown', () => {
|
||||
render(<NoteCard note={{ ...baseNote, notebookId: 'nb-1' }} onUpdated={vi.fn()} mode="inbox" />);
|
||||
fireEvent.click(screen.getByTitle('다른 노트북으로 이동'));
|
||||
// 현재 nb-1('회사') 는 제외, nb-2('개인') 만 보임.
|
||||
expect(screen.getByText('개인')).toBeInTheDocument();
|
||||
expect(screen.queryAllByText('회사').length).toBe(1); // chip 버튼 안에만 존재
|
||||
});
|
||||
|
||||
it('dropdown 의 notebook 클릭 → store.moveNoteToNotebook 호출', async () => {
|
||||
render(<NoteCard note={{ ...baseNote, notebookId: 'nb-1' }} onUpdated={vi.fn()} mode="inbox" />);
|
||||
fireEvent.click(screen.getByTitle('다른 노트북으로 이동'));
|
||||
fireEvent.click(screen.getByText('개인'));
|
||||
await waitFor(() => {
|
||||
expect(mockMoveNoteToNotebook).toHaveBeenCalledWith('n1', 'nb-2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user