- onboarding.cy.ts / preferences.cy.ts / recipes.cy.ts mockaient encore
GET /reference/diets|allergies avec l'ancienne forme {id, name}. Depuis
les deux derniers commits l'API renvoie {id, key} (uid anglais) et le
composant résout le libellé via i18n (t(`catalog.diets.${key}`)) — avec
key manquant, ça affichait littéralement "catalog.diets.undefined" au
lieu de "Végétarien"/"Omnivore"/etc., faisant échouer cy.select()/
cy.contains() dans ces 3 specs. Corrigé pour mocker {key: "vegetarian"},
{key: "peanuts"}, etc.
- recipes.cy.ts : le test "shows a not-found message" utilisait le
mauvais code d'erreur (4041 au lieu de ErrorCode.RECIPE_NOT_FOUND =
4045), donc RecipeDetailPanel tombait dans son état d'erreur générique
au lieu du message "Cette recette n'existe pas." — bug dans mon propre
test, sans rapport avec le refactor.
- pnpm lint (biome) : les fichiers touchés par le refactor précédent
avaient quelques soucis de formatage/tri d'imports (des sed multi-
fichiers, pas d'édition via l'outil habituel) — corrigés par
`biome check --write`.
Vérifié : ces 3 specs + recipe-form.cy.ts passent maintenant dans le job
CI GitHub Actions (Linux, Cypress s'y exécute réellement — contrairement
à cet environnement Windows sandboxé, voir les commits précédents) ; 102
tests Mocha + 32 scénarios Cucumber toujours au vert en local.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
199 lines
7.8 KiB
TypeScript
199 lines
7.8 KiB
TypeScript
import { ErrorCode, type SignupInput } from "@batch-cooking/shared";
|
|
import { faker } from "@faker-js/faker";
|
|
import { expect } from "chai";
|
|
import request from "supertest";
|
|
import { createApp } from "../src/app.js";
|
|
import { getEnglishKey } from "../src/db/catalog-en-keys.js";
|
|
import { prisma } from "../src/db/prisma.js";
|
|
import { resetDatabase } from "../test-support/reset-db.js";
|
|
|
|
function buildSignupPayload(): SignupInput {
|
|
const firstName = faker.person.firstName();
|
|
const lastName = faker.person.lastName();
|
|
return {
|
|
firstName,
|
|
lastName,
|
|
email: faker.internet.email({ firstName, lastName }).toLowerCase(),
|
|
password: faker.internet.password({ length: 16 }),
|
|
};
|
|
}
|
|
|
|
describe("Profile", () => {
|
|
const app = createApp();
|
|
|
|
beforeEach(async () => {
|
|
await resetDatabase();
|
|
});
|
|
|
|
after(async () => {
|
|
await prisma.$disconnect();
|
|
});
|
|
|
|
describe("PATCH /profile/diet", () => {
|
|
it("rejects requests without a session cookie with 401 NOT_AUTHENTICATED", async () => {
|
|
const res = await request(app).patch("/profile/diet").send({ dietId: 1 });
|
|
|
|
expect(res.status).to.equal(401);
|
|
expect(res.body.code).to.equal(ErrorCode.NOT_AUTHENTICATED);
|
|
});
|
|
|
|
it("sets the profile's regime to a valid, seeded diet", async () => {
|
|
const agent = request.agent(app);
|
|
await agent.post("/auth/signup").send(buildSignupPayload());
|
|
const diet = await prisma.diet.findFirstOrThrow({
|
|
where: { key: getEnglishKey("Végétarien") },
|
|
});
|
|
|
|
const res = await agent.patch("/profile/diet").send({ dietId: diet.id });
|
|
|
|
expect(res.status).to.equal(200);
|
|
expect(res.body.dietId).to.equal(diet.id);
|
|
});
|
|
|
|
it("clears the regime when dietId is null", async () => {
|
|
const agent = request.agent(app);
|
|
await agent.post("/auth/signup").send(buildSignupPayload());
|
|
const diet = await prisma.diet.findFirstOrThrow({ where: { key: getEnglishKey("Végan") } });
|
|
await agent.patch("/profile/diet").send({ dietId: diet.id });
|
|
|
|
const res = await agent.patch("/profile/diet").send({ dietId: null });
|
|
|
|
expect(res.status).to.equal(200);
|
|
expect(res.body.dietId).to.equal(null);
|
|
});
|
|
|
|
it("rejects an unknown dietId with 404 DIET_NOT_FOUND", async () => {
|
|
const agent = request.agent(app);
|
|
await agent.post("/auth/signup").send(buildSignupPayload());
|
|
|
|
const res = await agent.patch("/profile/diet").send({ dietId: 999_999 });
|
|
|
|
expect(res.status).to.equal(404);
|
|
expect(res.body.code).to.equal(ErrorCode.DIET_NOT_FOUND);
|
|
});
|
|
});
|
|
|
|
describe("GET /profile/allergies + PATCH /profile/allergies", () => {
|
|
it("rejects requests without a session cookie with 401 NOT_AUTHENTICATED", async () => {
|
|
const getRes = await request(app).get("/profile/allergies");
|
|
const patchRes = await request(app).patch("/profile/allergies").send({ allergyIds: [] });
|
|
|
|
expect(getRes.status).to.equal(401);
|
|
expect(patchRes.status).to.equal(401);
|
|
});
|
|
|
|
it("starts empty, then reflects a saved selection", async () => {
|
|
const agent = request.agent(app);
|
|
await agent.post("/auth/signup").send(buildSignupPayload());
|
|
const allergies = await prisma.allergy.findMany({ include: { category: true } });
|
|
const peanuts = allergies.find((a) => a.category.key === getEnglishKey("Arachides"));
|
|
const gluten = allergies.find((a) => a.category.key === getEnglishKey("Gluten"));
|
|
if (!peanuts || !gluten) throw new Error("expected seeded allergens missing");
|
|
|
|
const initial = await agent.get("/profile/allergies");
|
|
expect(initial.body).to.deep.equal([]);
|
|
|
|
const patchRes = await agent
|
|
.patch("/profile/allergies")
|
|
.send({ allergyIds: [peanuts.id, gluten.id] });
|
|
expect(patchRes.status).to.equal(200);
|
|
expect(patchRes.body.sort()).to.deep.equal([peanuts.id, gluten.id].sort());
|
|
|
|
const refetch = await agent.get("/profile/allergies");
|
|
expect(refetch.body.sort()).to.deep.equal([peanuts.id, gluten.id].sort());
|
|
});
|
|
|
|
it("replaces (not merges) the previous selection", async () => {
|
|
const agent = request.agent(app);
|
|
await agent.post("/auth/signup").send(buildSignupPayload());
|
|
const allergies = await prisma.allergy.findMany({ include: { category: true } });
|
|
const peanuts = allergies.find((a) => a.category.key === getEnglishKey("Arachides"));
|
|
const gluten = allergies.find((a) => a.category.key === getEnglishKey("Gluten"));
|
|
if (!peanuts || !gluten) throw new Error("expected seeded allergens missing");
|
|
|
|
await agent.patch("/profile/allergies").send({ allergyIds: [peanuts.id] });
|
|
await agent.patch("/profile/allergies").send({ allergyIds: [gluten.id] });
|
|
|
|
const res = await agent.get("/profile/allergies");
|
|
expect(res.body).to.deep.equal([gluten.id]);
|
|
});
|
|
|
|
it("rejects an unknown allergyId with 404 ALLERGY_NOT_FOUND", async () => {
|
|
const agent = request.agent(app);
|
|
await agent.post("/auth/signup").send(buildSignupPayload());
|
|
|
|
const res = await agent.patch("/profile/allergies").send({ allergyIds: [999_999] });
|
|
|
|
expect(res.status).to.equal(404);
|
|
expect(res.body.code).to.equal(ErrorCode.ALLERGY_NOT_FOUND);
|
|
});
|
|
});
|
|
|
|
describe("GET /profile/disliked-ingredients + PATCH /profile/disliked-ingredients", () => {
|
|
it("rejects requests without a session cookie with 401 NOT_AUTHENTICATED", async () => {
|
|
const getRes = await request(app).get("/profile/disliked-ingredients");
|
|
const patchRes = await request(app)
|
|
.patch("/profile/disliked-ingredients")
|
|
.send({ dislikedIngredientIds: [] });
|
|
|
|
expect(getRes.status).to.equal(401);
|
|
expect(patchRes.status).to.equal(401);
|
|
});
|
|
|
|
it("starts empty, then reflects a saved selection", async () => {
|
|
const agent = request.agent(app);
|
|
await agent.post("/auth/signup").send(buildSignupPayload());
|
|
const tomate = await prisma.ingredient.findFirstOrThrow({
|
|
where: { key: getEnglishKey("Tomate") },
|
|
});
|
|
const oignon = await prisma.ingredient.findFirstOrThrow({
|
|
where: { key: getEnglishKey("Oignon") },
|
|
});
|
|
|
|
const initial = await agent.get("/profile/disliked-ingredients");
|
|
expect(initial.body).to.deep.equal([]);
|
|
|
|
const patchRes = await agent
|
|
.patch("/profile/disliked-ingredients")
|
|
.send({ dislikedIngredientIds: [tomate.id, oignon.id] });
|
|
expect(patchRes.status).to.equal(200);
|
|
expect(patchRes.body.sort()).to.deep.equal([tomate.id, oignon.id].sort());
|
|
|
|
const refetch = await agent.get("/profile/disliked-ingredients");
|
|
expect(refetch.body.sort()).to.deep.equal([tomate.id, oignon.id].sort());
|
|
});
|
|
|
|
it("replaces (not merges) the previous selection", async () => {
|
|
const agent = request.agent(app);
|
|
await agent.post("/auth/signup").send(buildSignupPayload());
|
|
const tomate = await prisma.ingredient.findFirstOrThrow({
|
|
where: { key: getEnglishKey("Tomate") },
|
|
});
|
|
const oignon = await prisma.ingredient.findFirstOrThrow({
|
|
where: { key: getEnglishKey("Oignon") },
|
|
});
|
|
|
|
await agent
|
|
.patch("/profile/disliked-ingredients")
|
|
.send({ dislikedIngredientIds: [tomate.id] });
|
|
await agent
|
|
.patch("/profile/disliked-ingredients")
|
|
.send({ dislikedIngredientIds: [oignon.id] });
|
|
|
|
const res = await agent.get("/profile/disliked-ingredients");
|
|
expect(res.body).to.deep.equal([oignon.id]);
|
|
});
|
|
|
|
it("rejects an unknown ingredientId with 404 INGREDIENT_NOT_FOUND", async () => {
|
|
const agent = request.agent(app);
|
|
await agent.post("/auth/signup").send(buildSignupPayload());
|
|
|
|
const res = await agent
|
|
.patch("/profile/disliked-ingredients")
|
|
.send({ dislikedIngredientIds: [999_999] });
|
|
|
|
expect(res.status).to.equal(404);
|
|
expect(res.body.code).to.equal(ErrorCode.INGREDIENT_NOT_FOUND);
|
|
});
|
|
});
|
|
});
|