Make dashboard event toasts persistent
This commit is contained in:
1 parent
4798385d8b
commit
c75e71f26e
4 files changed
+91
-14
No files matched your search
@@ -11,6 +11,7 @@ import {
|
||||
NotificationProvider,
|
||||
NotificationViewport,
|
||||
notificationForEvent,
|
||||
shouldToastForEvent,
|
||||
} from '../../apps/dashboard/src/app/notifications.js';
|
||||
|
||||
class FakeRealtime {
|
||||
@@ -28,7 +29,9 @@ class FakeRealtime {
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
globalThis.localStorage.clear();
|
||||
vi.restoreAllMocks();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe('dashboard notifications', () => {
|
||||
@@ -90,4 +93,57 @@ describe('dashboard notifications', () => {
|
||||
await user.click(within(centre).getByRole('button', { name: 'Clear all' }));
|
||||
expect(within(centre).getByText('All quiet')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('keeps routine sync completions in the notification centre without showing a toast', async () => {
|
||||
const realtime = new FakeRealtime();
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<NotificationProvider realtime={realtime}>
|
||||
<NotificationBell />
|
||||
<NotificationViewport />
|
||||
</NotificationProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
const syncEvent: DashboardEvent = {
|
||||
id: 'sync-1',
|
||||
occurredAt: '2026-08-09T09:30:00.000Z',
|
||||
type: 'sync.completed',
|
||||
payload: { syncId: 'conversation-sync-1', kind: 'conversations', processed: 12 },
|
||||
};
|
||||
expect(shouldToastForEvent(syncEvent)).toBe(false);
|
||||
act(() => realtime.emit(syncEvent));
|
||||
|
||||
expect(screen.queryByRole('status')).not.toBeInTheDocument();
|
||||
await user.click(screen.getByRole('button', { name: 'Notifications, 1 unread' }));
|
||||
expect(within(screen.getByLabelText('Notifications', { selector: 'aside' })).getByText('Conversations sync complete'))
|
||||
.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('keeps important event toasts visible until they are dismissed', async () => {
|
||||
vi.useFakeTimers();
|
||||
const realtime = new FakeRealtime();
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<NotificationProvider realtime={realtime}>
|
||||
<NotificationViewport />
|
||||
</NotificationProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
act(() => realtime.emit({
|
||||
id: 'media-1',
|
||||
occurredAt: '2026-08-09T09:30:00.000Z',
|
||||
type: 'media.received',
|
||||
payload: { conversationId: 'conversation-1', sender: 'alice', count: 1 },
|
||||
}));
|
||||
expect(screen.getByRole('status')).toHaveTextContent('Image received from u/alice');
|
||||
|
||||
act(() => vi.advanceTimersByTime(60_000));
|
||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||
|
||||
act(() => screen.getByRole('button', { name: 'Dismiss Image received from u/alice' }).click());
|
||||
expect(screen.queryByRole('status')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user