All checks were successful
Nouvelle section `/admin/utilisateurs` (nav + route) : tableau des comptes (date, nom, email, foyer) et un `Dialog` de création en deux temps — formulaire prénom/nom/email, puis affichage unique du mot de passe temporaire généré, avec bouton « Copier ». Validation cliente via `adminCreateUserSchema`, erreurs API traduites (`EMAIL_ALREADY_IN_USE` déjà mappé). Spec Cypress `admin-users.cy.ts` (API mockée). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
111 lines
3.5 KiB
TypeScript
111 lines
3.5 KiB
TypeScript
// Mocks the admin API via cy.intercept — no live backend. Covers the
|
|
// `/admin/utilisateurs` page: the account list, the create dialog, the
|
|
// one-time temporary-password reveal, and the duplicate-email error.
|
|
|
|
const adminBody = {
|
|
id: 1,
|
|
email: "ops@example.com",
|
|
name: "Ops",
|
|
createdAt: "2026-08-01T00:00:00.000Z",
|
|
lastLoginAt: "2026-08-28T09:00:00.000Z",
|
|
};
|
|
|
|
function users() {
|
|
return [
|
|
{
|
|
id: 2,
|
|
firstName: "Alice",
|
|
lastName: "Martin",
|
|
email: "alice@example.com",
|
|
householdName: "Foyer Martin",
|
|
createdAt: "2026-09-09T10:00:00.000Z",
|
|
},
|
|
{
|
|
id: 3,
|
|
firstName: "Bob",
|
|
lastName: "Durand",
|
|
email: "bob@example.com",
|
|
householdName: null,
|
|
createdAt: "2026-09-08T08:00:00.000Z",
|
|
},
|
|
];
|
|
}
|
|
|
|
describe("Admin — utilisateurs", () => {
|
|
beforeEach(() => {
|
|
cy.viewport(1400, 900);
|
|
cy.intercept("GET", "**/admin/api/auth/me", { statusCode: 200, body: adminBody });
|
|
cy.intercept("GET", "**/admin/api/users", { statusCode: 200, body: users() }).as("list");
|
|
});
|
|
|
|
it("lists every account with its household (or a dash when none)", () => {
|
|
cy.visit("/admin/utilisateurs");
|
|
cy.wait("@list");
|
|
|
|
cy.contains(".users-table tr", "alice@example.com").should("contain.text", "Foyer Martin");
|
|
cy.contains(".users-table tr", "bob@example.com").should("contain.text", "—");
|
|
});
|
|
|
|
it("creates an account and reveals the one-time temporary password", () => {
|
|
cy.intercept("POST", "**/admin/api/users", (req) => {
|
|
req.reply({
|
|
statusCode: 201,
|
|
body: {
|
|
user: {
|
|
id: 4,
|
|
firstName: req.body.firstName,
|
|
lastName: req.body.lastName,
|
|
email: req.body.email,
|
|
householdName: null,
|
|
createdAt: "2026-09-10T12:00:00.000Z",
|
|
},
|
|
temporaryPassword: "Tmp4kR7qWzP2xY9n",
|
|
},
|
|
});
|
|
}).as("create");
|
|
|
|
cy.visit("/admin/utilisateurs");
|
|
cy.wait("@list");
|
|
cy.contains("button", "Nouvel utilisateur").click();
|
|
|
|
cy.get('[data-testid="create-user-form"]').within(() => {
|
|
cy.get("#newUserFirstName").type("Chloé");
|
|
cy.get("#newUserLastName").type("Petit");
|
|
cy.get("#newUserEmail").type("chloe@example.com");
|
|
});
|
|
cy.contains(".dialog-panel__footer button", "Créer le compte").click();
|
|
|
|
cy.wait("@create").its("request.body").should("deep.equal", {
|
|
firstName: "Chloé",
|
|
lastName: "Petit",
|
|
email: "chloe@example.com",
|
|
});
|
|
|
|
cy.get('[data-testid="temp-password-reveal"]')
|
|
.find("#tempPassword")
|
|
.should("have.value", "Tmp4kR7qWzP2xY9n");
|
|
});
|
|
|
|
it("shows the localized error when the email is already in use", () => {
|
|
cy.intercept("POST", "**/admin/api/users", {
|
|
statusCode: 409,
|
|
body: { code: 4001, message: "Email already in use" },
|
|
}).as("createDup");
|
|
|
|
cy.visit("/admin/utilisateurs");
|
|
cy.wait("@list");
|
|
cy.contains("button", "Nouvel utilisateur").click();
|
|
|
|
cy.get('[data-testid="create-user-form"]').within(() => {
|
|
cy.get("#newUserFirstName").type("Alice");
|
|
cy.get("#newUserLastName").type("Martin");
|
|
cy.get("#newUserEmail").type("alice@example.com");
|
|
});
|
|
cy.contains(".dialog-panel__footer button", "Créer le compte").click();
|
|
|
|
cy.wait("@createDup");
|
|
cy.get('[data-testid="create-user-form"]').should("contain.text", "Cet email est déjà utilisé");
|
|
// Still on the form — no password reveal.
|
|
cy.get('[data-testid="temp-password-reveal"]').should("not.exist");
|
|
});
|
|
});
|