Étape 1 du refactor (voir PR) : met en place le vrai mode Component Testing de Cypress, séparé de l'e2e — monte un composant React isolé (pas de routeur, pas de backend), pour tester les composants génériques (components/ui/) indépendamment de tout parcours utilisateur. - cypress.config.ts : nouveau bloc `component` (devServer Vite, réutilise vite.config.ts de l'appli — même plugin React, même Sass). Le hook GPU-disable est factorisé (`disableGpu`) puisque e2e et component ont chacun leur propre `setupNodeEvents`, pas de config partagée par défaut. - cypress/support/component.ts + component-index.html : fichiers de support standards Cypress CT — importe le vrai global.scss de l'appli (les composants génériques sont stylés via lui, pas de CSS scopé à eux). - cypress/component/CheckboxOption.cy.tsx : 4 scénarios sur components/ui/Checkbox.tsx (rendu du label, reflet du prop `checked` sur l'input natif + la classe `is-selected`, callback `onChange` avec la valeur inversée, comportement contrôlé via un wrapper avec état). Dépendance ajoutée, épinglée : @cypress/vite-dev-server@5.2.1 (dernière version sans peer dependency cypress >=14 — 6.0.3+ l'exige explicitement, on est sur cypress@13.17.0). Testé en local jusqu'au mur GPU/Electron habituel (config + devServer Vite chargent sans erreur) — l'exécution réelle du montage reste à vérifier via la CI.
68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
import { useState } from "react";
|
|
import { CheckboxOption } from "../../src/components/ui/Checkbox";
|
|
|
|
// First real component test — mounts CheckboxOption in isolation (no
|
|
// router, no backend), unlike everything under cypress/e2e/ which always
|
|
// visits a full routed page. See cypress.config.ts's `component` block.
|
|
|
|
describe("CheckboxOption", () => {
|
|
it("renders its label content", () => {
|
|
cy.mount(
|
|
<CheckboxOption checked={false} onChange={() => {}}>
|
|
Végétarien
|
|
</CheckboxOption>,
|
|
);
|
|
|
|
cy.contains("label", "Végétarien").should("be.visible");
|
|
});
|
|
|
|
it("reflects the checked prop on the native input, and the is-selected class", () => {
|
|
cy.mount(
|
|
<CheckboxOption checked={false} onChange={() => {}}>
|
|
Végétarien
|
|
</CheckboxOption>,
|
|
);
|
|
cy.get("input[type=checkbox]").should("not.be.checked");
|
|
cy.get("label").should("not.have.class", "is-selected");
|
|
|
|
cy.mount(
|
|
<CheckboxOption checked={true} onChange={() => {}}>
|
|
Végétarien
|
|
</CheckboxOption>,
|
|
);
|
|
cy.get("input[type=checkbox]").should("be.checked");
|
|
cy.get("label").should("have.class", "is-selected");
|
|
});
|
|
|
|
it("calls onChange with the toggled value when clicked", () => {
|
|
const onChange = cy.stub().as("onChange");
|
|
cy.mount(
|
|
<CheckboxOption checked={false} onChange={onChange}>
|
|
Végétarien
|
|
</CheckboxOption>,
|
|
);
|
|
|
|
cy.get("input[type=checkbox]").click();
|
|
|
|
cy.get("@onChange").should("have.been.calledOnceWith", true);
|
|
});
|
|
|
|
it("is a controlled component — stays checked only while the parent says so", () => {
|
|
// A tiny stateful wrapper, since CheckboxOption itself takes no
|
|
// internal state — this is what actually exercises the checked/onChange
|
|
// contract the way a real caller (AllergySelect, the theme picker…)
|
|
// would.
|
|
function Wrapper() {
|
|
const [checked, setChecked] = useState(false);
|
|
return (
|
|
<CheckboxOption checked={checked} onChange={setChecked}>
|
|
Végétarien
|
|
</CheckboxOption>
|
|
);
|
|
}
|
|
cy.mount(<Wrapper />);
|
|
|
|
cy.get("input[type=checkbox]").should("not.be.checked").click();
|
|
cy.get("input[type=checkbox]").should("be.checked");
|
|
});
|
|
});
|