Files
webmail/components/admin/__tests__/flash-message.test.tsx
T
Linus Rath 10b4d2a9db feat: rework the admin panel around a settings-style sidebar and one URL per page
The old panel kept its active tab in a store, so every page sat at
/admin, the sidebar duplicated the webmail rail by hand, and each page
was a stack of boxed cards with their own save buttons. Plugin settings
were only reachable through a gear icon in a row of five.

- one catch-all route for the whole panel; pages switch through
  history.pushState, so `/admin/plugins/<id>`, the back button, bookmarks
  and connector links all work without a route transition
- sidebar laid out like the user settings: back to mail, search over
  pages and their sections, two group headings, change password and
  sign out at the bottom; the phone shows a list, then the page
- pages use SettingsSection rows instead of nested cards, with one page
  header, one flash message and one save bar at the bottom (Discard,
  Save, Ctrl+S); the unsaved-changes prompt still guards leaving
- plugins and themes are lists of rows with a toggle each; a row opens
  the extension's own page with its settings, force-on, permissions,
  kept versions and removal
- overview shows stat tiles, server facts and recent activity
- old `/admin?tab=x` links redirect; the tab store is gone and the
  connector registry resolves admin links to the page URL
- `NEXT_TURBOPACK_ROOT` overrides the Turbopack root so `next dev` runs
  from a worktree with a junctioned node_modules
2026-10-20 15:16:05 +02:00

32 lines
1.3 KiB
TypeScript

import { act, fireEvent, render, screen } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { Flash } from '../flash-message';
describe('Flash', () => {
beforeEach(() => { vi.useFakeTimers(); });
afterEach(() => { vi.useRealTimers(); });
it('renders nothing without a message', () => {
const { container } = render(<Flash message={null} onDismiss={() => {}} />);
expect(container.firstChild).toBeNull();
});
it('shows an error as an alert until dismissed', () => {
const onDismiss = vi.fn();
render(<Flash message={{ type: 'error', text: 'Save failed' }} onDismiss={onDismiss} />);
expect(screen.getByRole('alert').textContent).toContain('Save failed');
act(() => { vi.advanceTimersByTime(10_000); });
expect(onDismiss).not.toHaveBeenCalled();
fireEvent.click(screen.getByRole('button', { name: 'Dismiss' }));
expect(onDismiss).toHaveBeenCalledTimes(1);
});
it('lets a success go away by itself', () => {
const onDismiss = vi.fn();
render(<Flash message={{ type: 'success', text: 'Saved' }} onDismiss={onDismiss} />);
expect(screen.getByRole('status').textContent).toContain('Saved');
act(() => { vi.advanceTimersByTime(6_000); });
expect(onDismiss).toHaveBeenCalledTimes(1);
});
});