115 lines
3.8 KiB
TypeScript
115 lines
3.8 KiB
TypeScript
import { it, expect, vi, beforeEach } from 'vitest';
|
|
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
|
|
|
|
const { confirmDialog, promptDialog } = vi.hoisted(() => ({
|
|
confirmDialog: vi.fn(),
|
|
promptDialog: vi.fn()
|
|
}));
|
|
vi.mock('$lib/api/endpoints/recent', () => ({
|
|
clearRecent: vi.fn(),
|
|
fetchRecentPage: vi.fn(),
|
|
removeFromRecent: vi.fn()
|
|
}));
|
|
vi.mock('$lib/api/endpoints/favorites', () => ({
|
|
dateBucket: () => 'Today',
|
|
resolveOwnerName: vi.fn(async () => 'me'),
|
|
sizeBucket: () => 'Small',
|
|
typeLabel: () => 'File'
|
|
}));
|
|
vi.mock('$lib/api/endpoints/files', () => ({
|
|
fileDownloadUrl: () => '/dl',
|
|
renameFile: vi.fn(),
|
|
deleteFile: vi.fn()
|
|
}));
|
|
vi.mock('$lib/api/endpoints/folders', () => ({ renameFolder: vi.fn(), deleteFolder: vi.fn() }));
|
|
vi.mock('$lib/stores/dialogs.svelte', () => ({ confirmDialog, promptDialog }));
|
|
|
|
import { fetchRecentPage, clearRecent, removeFromRecent } from '$lib/api/endpoints/recent';
|
|
import RecentPage from './+page.svelte';
|
|
|
|
const m = (fn: unknown) => fn as ReturnType<typeof vi.fn>;
|
|
|
|
function withOneFile() {
|
|
m(fetchRecentPage).mockResolvedValue({
|
|
items: [
|
|
{
|
|
resource_type: 'file',
|
|
accessed_at: '2024-01-01T00:00:00Z',
|
|
resource: {
|
|
category: 'Document',
|
|
created_at: 0,
|
|
icon_class: 'fa-file',
|
|
icon_special_class: '',
|
|
id: 'r1',
|
|
mime_type: 'text/plain',
|
|
modified_at: 0,
|
|
name: 'notes.txt',
|
|
created_by: 'me',
|
|
updated_by: 'me',
|
|
folder_id: 'root',
|
|
path: '/notes.txt',
|
|
size: 4,
|
|
size_formatted: '4 B',
|
|
sort_date: 0,
|
|
etag: 'e',
|
|
content_hash: 'h'
|
|
}
|
|
}
|
|
],
|
|
next_cursor: null
|
|
});
|
|
}
|
|
|
|
beforeEach(() => vi.clearAllMocks());
|
|
|
|
it('renders recent items returned by the API', async () => {
|
|
withOneFile();
|
|
render(RecentPage);
|
|
await waitFor(() => expect(fetchRecentPage).toHaveBeenCalled());
|
|
await waitFor(() => expect(screen.getByText('notes.txt')).toBeTruthy());
|
|
});
|
|
|
|
it('clears recent activity after confirmation', async () => {
|
|
withOneFile();
|
|
confirmDialog.mockResolvedValue(true);
|
|
m(clearRecent).mockResolvedValue(undefined);
|
|
render(RecentPage);
|
|
await fireEvent.click(await screen.findByTestId('recent-clear-btn'));
|
|
await waitFor(() => expect(clearRecent).toHaveBeenCalled());
|
|
});
|
|
|
|
it('removes a recent row via the broom button', async () => {
|
|
// Recent no longer surfaces a favorite star (users go to the item's
|
|
// real home for that). The per-row affordance is now a broom that
|
|
// calls `DELETE /api/recent/{kind}/{id}` — verified end-to-end via
|
|
// the `removeFromRecent` mock.
|
|
withOneFile();
|
|
m(removeFromRecent).mockResolvedValue(undefined);
|
|
render(RecentPage);
|
|
await screen.findByText('notes.txt');
|
|
await fireEvent.click(screen.getByTestId('recent-remove-btn-r1'));
|
|
await waitFor(() => expect(removeFromRecent).toHaveBeenCalledWith('file', 'r1'));
|
|
});
|
|
|
|
it('batch-removes-from-recent the selection', async () => {
|
|
// /recent's batch bar was intentionally trimmed to Download +
|
|
// Remove-from-recent. Bulk-deleting the underlying file from
|
|
// this history view (previous behaviour) confused the "this is
|
|
// activity log" semantics — destructive actions belong in the
|
|
// row's context menu, not in the batch bar. This test pins the
|
|
// new shape: batch button just forgets the selection from history.
|
|
withOneFile();
|
|
m(removeFromRecent).mockResolvedValue(undefined);
|
|
render(RecentPage);
|
|
await screen.findByText('notes.txt');
|
|
await fireEvent.click(screen.getByTestId('resource-list-select-r1-checkbox'));
|
|
await fireEvent.click(await screen.findByTestId('recent-batch-remove-btn'));
|
|
await waitFor(() => expect(removeFromRecent).toHaveBeenCalledWith('file', 'r1'));
|
|
});
|
|
|
|
it('renders an empty state when there is no recent activity', async () => {
|
|
m(fetchRecentPage).mockResolvedValue({ items: [], next_cursor: null });
|
|
render(RecentPage);
|
|
await waitFor(() => expect(fetchRecentPage).toHaveBeenCalled());
|
|
});
|