Quatre écrans manquaient faute de spécification. Les captures du produit audité les fournissent : identité de l'entreprise, comportement des plannings, droits ouverts aux salariés, décompte de paie. Les préférences sont trois blocs typés, pas un sac de clés-valeurs. Ce n'est pas de la rigueur gratuite : `smoothOvertimeMonthly` change le calcul des heures supplémentaires, et un JSON libre rendrait indétectable une clé mal orthographiée — le réglage paraîtrait actif sans l'être. Chaque formulaire déclare les clés qu'il gouverne. Une case décochée n'arrive pas dans le FormData ; sans cette déclaration, enregistrer « Plannings » remettrait à faux tout « Droits », un formulaire partiel effaçant ce qu'il n'affiche pas. Trois réglages portent un avertissement en évidence parce qu'ils déplacent des heures et non des pixels. Le lissage mensuel des heures supplémentaires suppose un accord d'aménagement du temps de travail : le décompte hebdomadaire est le principe, et sans accord l'aménagement est inopposable. Les pauses rémunérées et l'inclusion des repos dans les heures normales changent la répartition entre heures normales et majorées. Un interrupteur qui coûte de l'argent ne doit pas se présenter comme un réglage d'affichage. La ligne de préférences est créée à la demande : son absence vaut « tous les défauts ». Semer une ligne à l'installation obligerait à migrer chaque compte au premier réglage ajouté. L'adresse va sur le compte et non sur l'établissement : le siège porte l'identité juridique et figure au registre du personnel, là où l'établissement porte le SIRET, le fuseau effectif et les plannings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
|
||
import {
|
||
minutesToTime,
|
||
PREFERENCE_KEYS,
|
||
PREFERENCE_TOGGLES,
|
||
timeToMinutes,
|
||
togglesOf,
|
||
} from '@/domain/settings/preferences';
|
||
|
||
describe('catalogue de préférences', () => {
|
||
it('n’a pas deux fois la même clé', () => {
|
||
expect(new Set(PREFERENCE_KEYS).size).toBe(PREFERENCE_KEYS.length);
|
||
});
|
||
|
||
it('range chaque préférence dans un seul groupe', () => {
|
||
const grouped = [
|
||
...togglesOf('planning'),
|
||
...togglesOf('rights'),
|
||
...togglesOf('payroll'),
|
||
];
|
||
expect(grouped).toHaveLength(PREFERENCE_TOGGLES.length);
|
||
});
|
||
|
||
it('avertit sur les réglages qui changent un calcul', () => {
|
||
// Un interrupteur qui déplace des heures entre normales et majorées ne doit
|
||
// pas se présenter comme un réglage d'affichage.
|
||
for (const key of [
|
||
'smoothOvertimeMonthly',
|
||
'includeRestInNormalHours',
|
||
'paidBreaks',
|
||
]) {
|
||
const toggle = PREFERENCE_TOGGLES.find((entry) => entry.key === key);
|
||
expect(toggle?.warning, `${key} sans avertissement`).toBeTruthy();
|
||
}
|
||
});
|
||
});
|
||
|
||
describe('heure de bascule du soir', () => {
|
||
it('convertit dans les deux sens', () => {
|
||
expect(minutesToTime(1200)).toBe('20:00');
|
||
expect(timeToMinutes('20:00')).toBe(1200);
|
||
expect(minutesToTime(0)).toBe('00:00');
|
||
expect(timeToMinutes('00:00')).toBe(0);
|
||
});
|
||
|
||
it('refuse une heure invalide plutôt que de la ramener à zéro', () => {
|
||
// Silencieusement ramenée à minuit, une saisie fautive ferait passer tous
|
||
// les créneaux pour des créneaux du soir.
|
||
expect(timeToMinutes('24:00')).toBeNull();
|
||
expect(timeToMinutes('20:60')).toBeNull();
|
||
expect(timeToMinutes('8:00')).toBeNull();
|
||
expect(timeToMinutes('')).toBeNull();
|
||
});
|
||
});
|