Le calendrier portait tout : les demandes en attente, les décisions rendues et celles qui avaient expiré, distinguées par un simple badge. Une file d'attente se lit mal dans un calendrier — elle se lit par ancienneté, et le calendrier range par date d'absence. Quatre routes désormais : le calendrier, la file à traiter, les traitées et les expirées. Le calendrier a déménagé sous /absences plutôt que d'être dupliqué, et /conges redirige en conservant le mois : un lien partagé dans un message continue d'ouvrir la bonne page. Les trois files partagent une requête et un rendu. C'est délibéré : la règle qui masque le motif d'une absence Sécurité sociale vaut pour les trois, et trois copies de cette règle garantiraient qu'une finisse par diverger. Une donnée de santé qui ne fuit que sur l'écran « traitées » reste une fuite. La file à traiter classe par ancienneté de demande, les files closes par date d'absence décroissante : on cherche d'un côté ce qui attend depuis trop longtemps, de l'autre ce qui vient d'être décidé. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
|
||
import {
|
||
activeItem,
|
||
isActive,
|
||
matches,
|
||
NAVIGATION,
|
||
sectionForPath,
|
||
} from '@/components/shell/navigation';
|
||
|
||
describe('matches', () => {
|
||
it('reconnaît la racine sans capturer tout le site', () => {
|
||
expect(matches('/', '/')).toBe(true);
|
||
expect(matches('/', '/equipe')).toBe(false);
|
||
});
|
||
|
||
it('couvre les sous-routes', () => {
|
||
expect(matches('/equipe', '/equipe')).toBe(true);
|
||
expect(matches('/equipe', '/equipe/camille-ferrand')).toBe(true);
|
||
expect(matches('/equipe', '/equipements')).toBe(false);
|
||
});
|
||
|
||
it('écarte les liens d’ancre', () => {
|
||
expect(
|
||
matches('/absences/calendrier#compteurs', '/absences/calendrier'),
|
||
).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe('activeItem', () => {
|
||
it('retient la correspondance la plus spécifique', () => {
|
||
// Une fiche salarié est un détail de « Membres » : c'est cette entrée qui
|
||
// reste allumée, et non un lien codé en dur vers un salarié particulier.
|
||
expect(activeItem('/equipe/cm123abc')?.id).toBe('membres');
|
||
expect(activeItem('/equipe')?.id).toBe('membres');
|
||
expect(activeItem('/reglages/registre')?.id).toBe('registre');
|
||
});
|
||
|
||
it('ne renvoie rien pour une route hors navigation', () => {
|
||
expect(activeItem('/inconnu')).toBeUndefined();
|
||
});
|
||
});
|
||
|
||
describe('isActive', () => {
|
||
it('n’allume qu’une entrée par écran', () => {
|
||
const targets = NAVIGATION.flatMap((section) => section.items)
|
||
.map((item) => item.href)
|
||
.filter(
|
||
(href): href is string =>
|
||
typeof href === 'string' && !href.includes('#'),
|
||
);
|
||
|
||
for (const pathname of [...targets, '/equipe/cm123abc']) {
|
||
const lit = NAVIGATION.flatMap((section) => section.items).filter(
|
||
(item) => item.href && isActive(item.href, pathname),
|
||
);
|
||
expect(lit.length, `${pathname} allume ${lit.length} entrées`).toBe(1);
|
||
}
|
||
});
|
||
});
|
||
|
||
describe('sectionForPath', () => {
|
||
it('trouve la section portant la route courante', () => {
|
||
expect(sectionForPath('/planning/semaine').id).toBe('plannings');
|
||
expect(sectionForPath('/equipe/camille-ferrand').id).toBe('equipe');
|
||
expect(sectionForPath('/absences/calendrier').id).toBe('conges');
|
||
expect(sectionForPath('/absences/a-traiter').id).toBe('conges');
|
||
});
|
||
|
||
it('retombe sur la première section pour une route inconnue', () => {
|
||
expect(sectionForPath('/inconnu').id).toBe('apercu');
|
||
});
|
||
});
|