x
CI / changes (push) Has been cancelled
CI / Build (push) Has been cancelled
Deploy Docs / build (push) Has been cancelled
Docker Build and Test / Build and Test Docker Image (push) Has been cancelled
Docker Publish (release, main, dry-run) / Pre-publish Tests (push) Has been cancelled
CI / Frontend — svelte-check, ESLint, Stylelint, Prettier (push) Has been cancelled
CI / Message-bus spec — AsyncAPI + TypeScript DTO drift (push) Has been cancelled
CI / Migration ordering (new migrations postdate target branch) (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Wasm — fmt + clippy (push) Has been cancelled
CI / Wasm — release tests (push) Has been cancelled
CI / Plugins — fixtures + runtime tests (push) Has been cancelled
CI / Server Unit and Functionnal Tests (push) Has been cancelled
CI / Security Audit (push) Has been cancelled
CI / API, WebDAV & OIDC tests (push) Has been cancelled
CI / Bundled-assets binary — embed + SPA-serve integration (push) Has been cancelled
CI / WebDAV RFC 4918 — litmus (59/59) (push) Has been cancelled
CI / CalDAV + CardDAV — python-caldav (push) Has been cancelled
CI / Frontend end-to-end tests (via Playwright) (push) Has been cancelled
Deploy Docs / deploy (push) Has been cancelled
Docker Publish (release, main, dry-run) / Build & Push Multi-Arch (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / Build (push) Has been cancelled
Deploy Docs / build (push) Has been cancelled
Docker Build and Test / Build and Test Docker Image (push) Has been cancelled
Docker Publish (release, main, dry-run) / Pre-publish Tests (push) Has been cancelled
CI / Frontend — svelte-check, ESLint, Stylelint, Prettier (push) Has been cancelled
CI / Message-bus spec — AsyncAPI + TypeScript DTO drift (push) Has been cancelled
CI / Migration ordering (new migrations postdate target branch) (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Wasm — fmt + clippy (push) Has been cancelled
CI / Wasm — release tests (push) Has been cancelled
CI / Plugins — fixtures + runtime tests (push) Has been cancelled
CI / Server Unit and Functionnal Tests (push) Has been cancelled
CI / Security Audit (push) Has been cancelled
CI / API, WebDAV & OIDC tests (push) Has been cancelled
CI / Bundled-assets binary — embed + SPA-serve integration (push) Has been cancelled
CI / WebDAV RFC 4918 — litmus (59/59) (push) Has been cancelled
CI / CalDAV + CardDAV — python-caldav (push) Has been cancelled
CI / Frontend end-to-end tests (via Playwright) (push) Has been cancelled
Deploy Docs / deploy (push) Has been cancelled
Docker Publish (release, main, dry-run) / Build & Push Multi-Arch (push) Has been cancelled
This commit is contained in:
@@ -27,14 +27,19 @@ vi.mock('$app/state', () => ({ page: pageState }));
|
||||
vi.mock('$lib/stores/session.svelte', () => ({ session }));
|
||||
vi.mock('$lib/stores/ui.svelte', () => ({ ui }));
|
||||
vi.mock('$lib/stores/dialogs.svelte', () => ({ confirmDialog, promptDialog }));
|
||||
vi.mock('$lib/api/client', () => ({ apiFetch: vi.fn() }));
|
||||
vi.mock('$lib/api/client', () => ({ apiFetch: vi.fn(), apiJson: vi.fn() }));
|
||||
vi.mock('$lib/api/csrf', () => ({ getCsrfHeaders: () => ({}) }));
|
||||
vi.mock('$lib/api/endpoints/search', () => ({ searchResources: vi.fn() }));
|
||||
vi.mock('$lib/api/endpoints/deltaUpload', () => ({
|
||||
instantUploadOwned: vi.fn(),
|
||||
resolveOwnedHashes: vi.fn(),
|
||||
tryDeltaUpload: vi.fn()
|
||||
}));
|
||||
vi.mock('$lib/api/endpoints/favorites', () => ({ addFavorite: vi.fn(), removeFavorite: vi.fn() }));
|
||||
vi.mock('$lib/api/endpoints/favorites', () => ({
|
||||
addFavorite: vi.fn(),
|
||||
removeFavorite: vi.fn(),
|
||||
addFavoritesBatch: vi.fn()
|
||||
}));
|
||||
vi.mock('$lib/api/endpoints/wopi', () => ({
|
||||
canEditWithWopi: () => false,
|
||||
getEditorUrlWithFallback: vi.fn()
|
||||
@@ -77,7 +82,8 @@ vi.mock('$lib/api/endpoints/folders', () => ({
|
||||
import { fetchFolderPage, createFolder, deleteFolder } from '$lib/api/endpoints/folders';
|
||||
import { deleteFile, uploadFileWithProgress } from '$lib/api/endpoints/files';
|
||||
import { resolveOwnedHashes, tryDeltaUpload } from '$lib/api/endpoints/deltaUpload';
|
||||
import { apiFetch } from '$lib/api/client';
|
||||
import { addFavoritesBatch } from '$lib/api/endpoints/favorites';
|
||||
import { searchResources } from '$lib/api/endpoints/search';
|
||||
import { files as filesStore } from '$lib/stores/files.svelte';
|
||||
import FilesPage from './[...path]/+page.svelte';
|
||||
|
||||
@@ -196,6 +202,41 @@ it('loads the home folder listing on mount and renders its contents', async () =
|
||||
await screen.findByTestId('files-new-folder-btn');
|
||||
});
|
||||
|
||||
it('shows the folder item count next to the breadcrumb', async () => {
|
||||
withListing(); // 1 folder + 1 file, `nextCursor` undefined → last page
|
||||
render(FilesPage);
|
||||
const stat = await screen.findByTestId('files-folder-stat');
|
||||
expect(stat.textContent).toContain('1 folders · 1 files');
|
||||
// No "+" suffix — the listing is complete, the count is exact.
|
||||
expect(stat.textContent!.trim().endsWith('+')).toBe(false);
|
||||
});
|
||||
|
||||
it('marks the folder count as partial while more pages exist', async () => {
|
||||
const folder = folderItem('sub1', 'Sub');
|
||||
const file = fileItem('f1', 'hello.txt');
|
||||
m(fetchFolderPage).mockResolvedValue({
|
||||
items: [folder, file],
|
||||
folders: [folder],
|
||||
files: [file],
|
||||
nextCursor: 'page-2'
|
||||
});
|
||||
render(FilesPage);
|
||||
const stat = await screen.findByTestId('files-folder-stat');
|
||||
// Counts reflect the pages loaded so far; the trailing "+" says more
|
||||
// are on the way via infinite scroll instead of claiming exactness.
|
||||
expect(stat.textContent).toContain('1 folders · 1 files');
|
||||
expect(stat.textContent!.trim().endsWith('+')).toBe(true);
|
||||
});
|
||||
|
||||
it('reloads the listing when the refresh button is clicked', async () => {
|
||||
withListing();
|
||||
render(FilesPage);
|
||||
await screen.findByTestId('files-refresh-btn');
|
||||
// Initial mount load = 1 call; the click resets pagination and refetches.
|
||||
await fireEvent.click(screen.getByTestId('files-refresh-btn'));
|
||||
await waitFor(() => expect(fetchFolderPage).toHaveBeenCalledTimes(2));
|
||||
});
|
||||
|
||||
it('shows an error when the listing fails with no cache', async () => {
|
||||
m(fetchFolderPage).mockRejectedValue(Object.assign(new Error('nope'), { status: 500 }));
|
||||
render(FilesPage);
|
||||
@@ -233,14 +274,50 @@ it('batch-deletes the whole selection after confirmation', async () => {
|
||||
|
||||
it('batch-favorites the selection via the favorites batch endpoint', async () => {
|
||||
withListing();
|
||||
m(apiFetch).mockResolvedValue({ ok: true });
|
||||
m(addFavoritesBatch).mockResolvedValue(undefined);
|
||||
render(FilesPage);
|
||||
await fireEvent.click(await screen.findByTestId('resource-list-select-all-checkbox'));
|
||||
await fireEvent.click(await screen.findByTestId('files-batch-favorite-btn'));
|
||||
await waitFor(() =>
|
||||
expect(apiFetch).toHaveBeenCalledWith(
|
||||
'/api/favorites/batch',
|
||||
expect.objectContaining({ method: 'POST' })
|
||||
)
|
||||
expect(addFavoritesBatch).toHaveBeenCalledWith([
|
||||
{ item_id: 'sub1', item_type: 'folder' },
|
||||
{ item_id: 'f1', item_type: 'file' }
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it('runs a scoped recursive search when the filter keyword is set', async () => {
|
||||
withListing();
|
||||
m(searchResources).mockResolvedValue({ items: [], query_time_ms: 1 });
|
||||
render(FilesPage);
|
||||
await waitFor(() => expect(fetchFolderPage).toHaveBeenCalled());
|
||||
vi.useFakeTimers();
|
||||
await fireEvent.input(screen.getByTestId('filter-keyword-input'), {
|
||||
target: { value: 'hello' }
|
||||
});
|
||||
await vi.advanceTimersByTimeAsync(400);
|
||||
vi.useRealTimers();
|
||||
expect(searchResources).toHaveBeenCalledWith(
|
||||
'hello',
|
||||
expect.objectContaining({ folderId: 'home', recursive: true })
|
||||
);
|
||||
});
|
||||
|
||||
it('shows the search result count while the filter is active', async () => {
|
||||
withListing();
|
||||
const hit = fileItem('s1', 'found.txt');
|
||||
m(searchResources).mockResolvedValue({
|
||||
items: [{ resource_type: 'file', resource: hit, meta: { score: 50 } }],
|
||||
query_time_ms: 1
|
||||
});
|
||||
render(FilesPage);
|
||||
await waitFor(() => expect(fetchFolderPage).toHaveBeenCalled());
|
||||
vi.useFakeTimers();
|
||||
await fireEvent.input(screen.getByTestId('filter-keyword-input'), {
|
||||
target: { value: 'found' }
|
||||
});
|
||||
await vi.advanceTimersByTimeAsync(400);
|
||||
vi.useRealTimers();
|
||||
const stat = await screen.findByTestId('files-folder-stat');
|
||||
expect(stat.textContent).toContain('1 results');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user