fix(notification): replace view-all anchor with button to prevent iframe navigation
SvelteKit's capture-phase link interceptor fires before the component's onclick handler, so e.preventDefault() was structurally too late to stop iframe navigation in vitest-browser. Replacing the <a href> with a <button type="button"> removes the href entirely — the interceptor never fires — and the existing goto() mock in tests is sufficient. Also splits the single view-all test into two focused it() blocks and clears mocks in afterEach to prevent cross-test mock leakage. Fixes #551 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,8 +13,7 @@ type Props = {
|
|||||||
|
|
||||||
let { notifications, onMarkRead, onMarkAllRead, onClose }: Props = $props();
|
let { notifications, onMarkRead, onMarkAllRead, onClose }: Props = $props();
|
||||||
|
|
||||||
function handleViewAll(e: MouseEvent) {
|
function handleViewAll() {
|
||||||
e.preventDefault();
|
|
||||||
onClose();
|
onClose();
|
||||||
goto('/aktivitaeten');
|
goto('/aktivitaeten');
|
||||||
}
|
}
|
||||||
@@ -134,12 +133,12 @@ function handleViewAll(e: MouseEvent) {
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="border-t border-line px-4 py-2">
|
<div class="border-t border-line px-4 py-2">
|
||||||
<a
|
<button
|
||||||
href="/aktivitaeten"
|
type="button"
|
||||||
onclick={handleViewAll}
|
onclick={handleViewAll}
|
||||||
class="text-xs font-medium text-ink-2 transition-colors hover:text-ink"
|
class="text-xs font-medium text-ink-2 transition-colors hover:text-ink"
|
||||||
>
|
>
|
||||||
{m.chronik_view_all()}
|
{m.chronik_view_all()}
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ import NotificationDropdown from './NotificationDropdown.svelte';
|
|||||||
|
|
||||||
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
||||||
|
|
||||||
afterEach(cleanup);
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
const makeNotification = (overrides: Record<string, unknown> = {}) => ({
|
const makeNotification = (overrides: Record<string, unknown> = {}) => ({
|
||||||
id: 'n1',
|
id: 'n1',
|
||||||
@@ -156,7 +159,7 @@ describe('NotificationDropdown', () => {
|
|||||||
expect(onMarkAllRead).toHaveBeenCalledOnce();
|
expect(onMarkAllRead).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls onClose when the view-all link is clicked', async () => {
|
it('calls onClose when the view-all button is clicked', async () => {
|
||||||
const onClose = vi.fn();
|
const onClose = vi.fn();
|
||||||
render(NotificationDropdown, {
|
render(NotificationDropdown, {
|
||||||
props: {
|
props: {
|
||||||
@@ -167,11 +170,23 @@ describe('NotificationDropdown', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const viewAllLink = page.getByRole('link', { name: /alle aktivitäten|view all/i });
|
await page.getByRole('button', { name: /alle aktivitäten|view all/i }).click();
|
||||||
await expect.element(viewAllLink).toHaveAttribute('href', '/aktivitaeten');
|
|
||||||
await viewAllLink.click();
|
|
||||||
|
|
||||||
expect(onClose).toHaveBeenCalledOnce();
|
expect(onClose).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('navigates to /aktivitaeten when the view-all button is clicked', async () => {
|
||||||
|
render(NotificationDropdown, {
|
||||||
|
props: {
|
||||||
|
notifications: [],
|
||||||
|
onMarkRead: () => {},
|
||||||
|
onMarkAllRead: () => {},
|
||||||
|
onClose: () => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: /alle aktivitäten|view all/i }).click();
|
||||||
|
|
||||||
expect(goto).toHaveBeenCalledWith('/aktivitaeten');
|
expect(goto).toHaveBeenCalledWith('/aktivitaeten');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user