MotorSignal-App/website/tests/founder-notifications.test.tsx

74 lines
4.5 KiB
TypeScript

import React from 'react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
import { FounderTopBar } from '../components/founder-portal/founder-top-bar';
import { FIXTURE_FOUNDER_CLUB } from '../lib/club-fixtures';
import { getPortalPermissions } from '../lib/club-data';
vi.mock('next/navigation', () => ({ useSearchParams: () => new URLSearchParams('preview=demo') }));
vi.mock('../lib/firebase-client', () => ({ firestore: {}, firebaseAuth: {} }));
afterEach(cleanup);
const props = { club: FIXTURE_FOUNDER_CLUB, permissions: getPortalPermissions('owner') };
describe('Founder notification bell', () => {
it('opens and closes club updates and preserves demo preview on destination links', () => {
render(<FounderTopBar {...props} pendingApplicationsCount={2} upcomingRidesCount={1} />);
const bell = screen.getByRole('button', { name: 'Notifications' });
expect(bell.getAttribute('aria-expanded')).toBe('false');
fireEvent.click(bell);
expect(screen.getByRole('region', { name: 'Club updates' })).toBeDefined();
expect(bell.getAttribute('aria-expanded')).toBe('true');
expect(screen.getByRole('link', { name: /2 pending applications/ }).getAttribute('href')).toBe(`/clubs/${props.club.slug}/manage/applications?preview=demo`);
expect(screen.getByRole('link', { name: /1 upcoming ride/ }).getAttribute('href')).toBe(`/clubs/${props.club.slug}/manage/rides?preview=demo`);
expect(screen.getByRole('link', { name: /Club Wall/ }).getAttribute('href')).toBe(`/clubs/${props.club.slug}/manage/wall?preview=demo`);
fireEvent.click(bell);
expect(screen.queryByRole('region', { name: 'Club updates' })).toBeNull();
});
it('does not fabricate an unread indicator or empty-inbox claim when counts are unavailable', () => {
render(<FounderTopBar {...props} />);
expect(screen.queryByLabelText(/pending applications/)).toBeNull();
fireEvent.click(screen.getByRole('button', { name: 'Notifications' }));
expect(screen.getByRole('link', { name: /Membership applications/ })).toBeDefined();
expect(screen.getByText('Open a section to review current club activity.')).toBeDefined();
});
it('closes on Escape and restores keyboard focus to the bell', () => {
render(<FounderTopBar {...props} />);
const bell = screen.getByRole('button', { name: 'Notifications' });
fireEvent.click(bell);
screen.getByRole('button', { name: 'Close notifications' }).focus();
fireEvent.keyDown(document, { key: 'Escape' });
expect(screen.queryByRole('region', { name: 'Club updates' })).toBeNull();
expect(document.activeElement).toBe(bell);
});
it('dismisses when clicking outside, following a link, or switching to the rider menu', () => {
render(<FounderTopBar {...props} />);
const bell = screen.getByRole('button', { name: 'Notifications' });
fireEvent.click(bell);
fireEvent.mouseDown(document.body);
expect(screen.queryByRole('region', { name: 'Club updates' })).toBeNull();
fireEvent.click(bell);
const wallLink = screen.getByRole('link', { name: /Club Wall/ });
wallLink.addEventListener('click', (event) => event.preventDefault());
fireEvent.click(wallLink);
expect(screen.queryByRole('region', { name: 'Club updates' })).toBeNull();
fireEvent.click(bell);
fireEvent.click(screen.getByRole('button', { name: 'Open rider menu' }));
expect(screen.queryByRole('region', { name: 'Club updates' })).toBeNull();
expect(screen.getByRole('menu')).toBeDefined();
});
it('closes when club context changes', () => {
const view = render(<FounderTopBar {...props} />);
fireEvent.click(screen.getByRole('button', { name: 'Notifications' }));
view.rerender(<FounderTopBar {...props} club={{ ...props.club, id: 'other-club' }} />);
expect(screen.queryByRole('region', { name: 'Club updates' })).toBeNull();
});
it('does not expose management destinations or pending counts to ordinary members', () => {
render(<FounderTopBar {...props} permissions={getPortalPermissions('member')} pendingApplicationsCount={4} upcomingRidesCount={2} />);
expect(screen.queryByLabelText(/pending applications/)).toBeNull();
fireEvent.click(screen.getByRole('button', { name: 'Notifications' }));
expect(screen.getByText('Club management updates are available to Owners and Admins.')).toBeDefined();
const panel = screen.getByRole('region', { name: 'Club updates' });
expect(panel.querySelector('a')).toBeNull();
});
});