Files
electric-cherry/tests/unit/ui-breadcrumbs.test.ts
T

57 lines
2.3 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { render } from 'svelte/server';
const { page } = await vi.hoisted(async () => {
const { writable } = await import('svelte/store');
return {
page: writable({
url: new URL('http://localhost/servers/t5_zombie_theater_1'),
data: { user: { role: 'admin' } }
})
};
});
vi.mock('$app/stores', () => ({ page }));
import Breadcrumbs from '../../src/lib/ui/components/primitives/Breadcrumbs.svelte';
const items = [{ label: 'Fleet', href: '/' }, { label: 'Kino Der Toten' }];
describe('contextual breadcrumbs', () => {
it('restores the parent list filters in its only parent link', () => {
page.set({
url: new URL(
'http://localhost/servers/t5_zombie_theater_1?returnTo=%2F%3Ffilter%3Doccupied%26sort%3Dround'
),
data: { user: { role: 'admin' } }
});
const html = render(Breadcrumbs, { props: { items } }).body;
expect(html).toContain('href="/?filter=occupied&sort=round"');
expect(html.match(/<a\b/g)).toHaveLength(1);
expect(html).toContain('aria-current="page"');
});
it('names a contextual parent and retains its saved section', () => {
page.set({
url: new URL(
'http://localhost/servers/t5_zombie_theater_1?returnTo=%2Fmaintenance%3Fview%3Dactivity'
),
data: { user: { role: 'admin' } }
});
const html = render(Breadcrumbs, { props: { items } }).body;
expect(html).toContain('href="/maintenance?view=activity"');
expect(html).toContain('Maintenance');
});
it('rejects external and API destinations while preserving the filtered fallback', () => {
for (const returnTo of ['https://evil.invalid/', '/api/auth/logout']) {
page.set({
url: new URL(
`http://localhost/servers/t5_zombie_theater_1?returnTo=${encodeURIComponent(returnTo)}`
),
data: { user: { role: 'admin' } }
});
const html = render(Breadcrumbs, {
props: { items, fallback: '/?filter=pinned' }
}).body;
expect(html).toContain('href="/?filter=pinned"');
expect(html).not.toContain(returnTo);
}
});
});