test(front): correct frontend test + end2end tests
This commit is contained in:
@@ -130,6 +130,14 @@
|
||||
emptyHint?: string;
|
||||
/** Empty-state icon-registry name (e.g. "star", "clock", "trash"). */
|
||||
emptyIcon?: string;
|
||||
/**
|
||||
* Call-to-action rendered inside the empty state. Used by
|
||||
* `/files` to surface a "Show hidden files" button when the
|
||||
* folder holds only dotfiles the user has chosen to hide — the
|
||||
* page-specific hint stays in `emptyHint`, the action goes
|
||||
* here. `<EmptyState>` renders it below the hint text.
|
||||
*/
|
||||
emptyAction?: Snippet;
|
||||
hasMore?: boolean;
|
||||
onloadmore?: () => void;
|
||||
/** Show the path/location column (list view only). */
|
||||
@@ -318,6 +326,7 @@
|
||||
emptyText,
|
||||
emptyHint,
|
||||
emptyIcon,
|
||||
emptyAction,
|
||||
hasMore = false,
|
||||
onloadmore,
|
||||
showPath = true,
|
||||
@@ -1156,7 +1165,9 @@
|
||||
icon={emptyIcon}
|
||||
title={emptyText ?? t('common.empty', 'Nothing here yet.')}
|
||||
hint={emptyHint}
|
||||
/>
|
||||
>
|
||||
{#if emptyAction}{@render emptyAction()}{/if}
|
||||
</EmptyState>
|
||||
{:else}
|
||||
<div class="files-container" bind:clientWidth={gridWidth}>
|
||||
{#if grouped && filesStore.viewMode === 'list'}
|
||||
|
||||
@@ -1615,6 +1615,23 @@
|
||||
onitemdragleave={rlOnItemDragLeave}
|
||||
onitemdrop={rlOnItemDrop}
|
||||
>
|
||||
{#snippet emptyAction()}
|
||||
<!-- Surfaces only when the folder isn't really empty — it's just
|
||||
filtered because the user chose to hide dotfiles. Clicking
|
||||
flips the app-wide `preferences.hideDotfiles` back off,
|
||||
re-populating the list without a hunt through settings. -->
|
||||
{#if hiddenCount > 0}
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
onclick={() => preferences.setHideDotfiles(false)}
|
||||
data-testid="files-show-hidden-btn"
|
||||
>
|
||||
<Icon name="eye" />
|
||||
{t('files.show_hidden', 'Show hidden files')}
|
||||
</button>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#snippet breadcrumb()}
|
||||
<nav class="breadcrumb" aria-label="Breadcrumb">
|
||||
<!-- Persistent home link → the root listing (bare /files canonicalizes to
|
||||
|
||||
@@ -177,7 +177,7 @@ it('batch-deletes the whole selection after confirmation', async () => {
|
||||
m(deleteFolder).mockResolvedValue(undefined);
|
||||
m(deleteFile).mockResolvedValue(undefined);
|
||||
render(FilesPage);
|
||||
await fireEvent.click(await screen.findByTestId('files-select-all-checkbox'));
|
||||
await fireEvent.click(await screen.findByTestId('resource-list-select-all-checkbox'));
|
||||
await fireEvent.click(await screen.findByTestId('files-batch-delete-btn'));
|
||||
await waitFor(() => expect(deleteFolder).toHaveBeenCalledWith('sub1'));
|
||||
await waitFor(() => expect(deleteFile).toHaveBeenCalledWith('f1'));
|
||||
@@ -187,7 +187,7 @@ it('batch-favorites the selection via the favorites batch endpoint', async () =>
|
||||
withListing();
|
||||
m(apiFetch).mockResolvedValue({ ok: true });
|
||||
render(FilesPage);
|
||||
await fireEvent.click(await screen.findByTestId('files-select-all-checkbox'));
|
||||
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(
|
||||
|
||||
@@ -5,12 +5,13 @@ const { confirmDialog, promptDialog } = vi.hoisted(() => ({
|
||||
confirmDialog: vi.fn(),
|
||||
promptDialog: vi.fn()
|
||||
}));
|
||||
vi.mock('$lib/api/endpoints/recent', () => ({ clearRecent: vi.fn(), fetchRecentPage: vi.fn() }));
|
||||
vi.mock('$lib/api/endpoints/recent', () => ({
|
||||
clearRecent: vi.fn(),
|
||||
fetchRecentPage: vi.fn(),
|
||||
removeFromRecent: vi.fn()
|
||||
}));
|
||||
vi.mock('$lib/api/endpoints/favorites', () => ({
|
||||
addFavorite: vi.fn(),
|
||||
dateBucket: () => 'Today',
|
||||
fetchFavoritesPage: vi.fn(async () => ({ items: [], next_cursor: null })),
|
||||
removeFavorite: vi.fn(),
|
||||
resolveOwnerName: vi.fn(async () => 'me'),
|
||||
sizeBucket: () => 'Small',
|
||||
typeLabel: () => 'File'
|
||||
@@ -23,8 +24,7 @@ vi.mock('$lib/api/endpoints/files', () => ({
|
||||
vi.mock('$lib/api/endpoints/folders', () => ({ renameFolder: vi.fn(), deleteFolder: vi.fn() }));
|
||||
vi.mock('$lib/stores/dialogs.svelte', () => ({ confirmDialog, promptDialog }));
|
||||
|
||||
import { fetchRecentPage, clearRecent } from '$lib/api/endpoints/recent';
|
||||
import { addFavorite } from '$lib/api/endpoints/favorites';
|
||||
import { fetchRecentPage, clearRecent, removeFromRecent } from '$lib/api/endpoints/recent';
|
||||
import { deleteFile } from '$lib/api/endpoints/files';
|
||||
import RecentPage from './+page.svelte';
|
||||
|
||||
@@ -79,13 +79,17 @@ it('clears recent activity after confirmation', async () => {
|
||||
await waitFor(() => expect(clearRecent).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('favorites a recent row via the star button', async () => {
|
||||
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(addFavorite).mockResolvedValue(undefined);
|
||||
m(removeFromRecent).mockResolvedValue(undefined);
|
||||
render(RecentPage);
|
||||
await screen.findByText('notes.txt');
|
||||
await fireEvent.click(screen.getByTestId('resource-list-favorite-r1-btn'));
|
||||
await waitFor(() => expect(addFavorite).toHaveBeenCalledWith('file', 'r1'));
|
||||
await fireEvent.click(screen.getByTestId('recent-remove-btn-r1'));
|
||||
await waitFor(() => expect(removeFromRecent).toHaveBeenCalledWith('file', 'r1'));
|
||||
});
|
||||
|
||||
it('batch-deletes selected recent items after confirmation', async () => {
|
||||
|
||||
Reference in New Issue
Block a user