Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Tests/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
node_modules
coverage
cypress/videos
cypress/screenshots
cypress/downloads
52 changes: 52 additions & 0 deletions Tests/Contacts/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Tests de bout en bout (Cypress)

## Mise en situation

Le code fourni est un petit carnet de contacts sur 2 pages :

- [`index.html`](./src/index.html) : la page d'accueil, qui affiche la liste des contacts et un champ de recherche pour les filtrer.
- [`add.html`](./src/add.html) : la page pour ajouter un nouveau contact (nom et courriel).

Un menu de navigation est présent sur les 2 pages pour passer de l'une à l'autre. Au premier chargement de la page d'accueil, 3 contacts par défaut sont automatiquement ajoutés au carnet (voir `seedContacts()` dans [contacts.js](./src/contacts.js)). Les contacts sont conservés dans le `localStorage` du navigateur, donc ils persistent entre les rechargements de page.

Vous pouvez essayer le site web en lançant un serveur statique avec la commande `npm start`. Assurez-vous d'installer les dépendances avec `npm ci` en premier.

## Code fourni

Toute la logique de l'application est déjà implémentée :

- [`contacts.js`](./src/contacts.js) : classe `ContactStore`, qui gère les données (ajout, suppression, recherche, validation) dans le `localStorage`.
- [`index.js`](./src/index.js) : classe `ContactListPage`, qui affiche et filtre la liste sur la page d'accueil.
- [`add.js`](./src/add.js) : classe `AddContactPage`, qui valide et soumet le formulaire d'ajout.

Vous n'avez **aucun code à écrire dans `src/`**. L'exercice consiste uniquement à écrire les tests de bout en bout (e2e) avec Cypress.

## Tests à écrire

Vous devez implémenter les tests dans le fichier [contacts.cy.js](./cypress/e2e/contacts.cy.js). Un test "vide" (`expect(true).to.equal(false)`) y est présent pour déclencher l'exécution de la suite ; vous pouvez le retirer une fois vos tests écrits.

Voici les comportements à couvrir :

1. **Page d'accueil** : le titre s'affiche et les 3 contacts par défaut sont présents au premier chargement.
2. **Recherche** : taper dans le champ de recherche filtre la liste ; une recherche sans résultat affiche le message vide.
3. **Suppression** : cliquer sur `Supprimer` retire un contact de la liste.
4. **Persistance** : après un rechargement de la page, les changements (ex. une suppression) sont conservés.
5. **Navigation** : cliquer sur les liens du menu change de page et met à jour le lien actif.
6. **Ajout** : soumettre le formulaire vide affiche une erreur ; le remplir correctement ajoute le contact et retourne à la page d'accueil.

## Installation des librairies de tests

Les dépendances nécessaires sont définies dans le fichier [package.json](./package.json). Vous pouvez utiliser la commande `npm ci` pour installer la version exacte de la librairie utilisée pour la solution.

## Exécution des tests

Démarrez d'abord le serveur statique avec `npm start` (dans un terminal séparé, laissez-le tourner). Vous pouvez ensuite exécuter les tests avec :

- `npm run cypress` : ouvre l'interface graphique de Cypress pour lancer et déboguer vos tests un à un.
- `npm run cypress:run` : exécute tous les tests en mode headless dans le terminal.

# Solution

Une solution est disponible dans le fichier [contacts.solution.cy.js](./cypress/e2e/contacts.solution.cy.js). Notez qu'il peut y avoir plus qu'une manière d'implémenter correctement l'exercice.

Notez que, par défaut, l'exécution de ces tests est ignorée à cause de la méthode `skip` sur la fonction `describe`. Vous pouvez retirer `skip` pour exécuter les tests.
15 changes: 15 additions & 0 deletions Tests/Contacts/cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineConfig } from "cypress";

export default defineConfig({
e2e: {
baseUrl: "http://localhost:3000/src",
setupNodeEvents(on, config) { },
supportFile: "cypress/support/e2e.js"
},
defaultCommandTimeout: 4000,
video: false,
screenshotOnRunFailure: false,
viewportHeight: 1000,
viewportWidth: 1600,
allowCypressEnv: false
});
5 changes: 5 additions & 0 deletions Tests/Contacts/cypress/e2e/contacts.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
describe("Carnet de contacts", () => {
it("est un test vide pour déclencher la suite", () => {
expect(true).to.equal(false);
});
});
76 changes: 76 additions & 0 deletions Tests/Contacts/cypress/e2e/contacts.solution.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
describe.skip("Carnet de contacts", () => {
beforeEach(() => {
cy.visit("/index.html");
});

describe("Page d'accueil", () => {
it("affiche le titre et les 3 contacts par défaut", () => {
cy.get("h1").should("have.text", "Carnet de contacts");
cy.get(".contact-item").should("have.length", 3);
cy.get(".contact-item").first().should("contain.text", "Alice Tremblay");
});

it("cache le message vide quand il y a des contacts", () => {
cy.get("#empty-message").should("be.hidden");
});

it("filtre la liste en tapant dans la recherche", () => {
cy.get("#search-input").type("benoit");
cy.get(".contact-item").should("have.length", 1);
cy.get(".contact-item").should("contain.text", "Benoit Roy");
});

it("affiche le message vide quand la recherche ne trouve rien", () => {
cy.get("#search-input").type("zzzzz");
cy.get(".contact-item").should("have.length", 0);
cy.get("#empty-message").should("be.visible");
});

it("supprime un contact au clic sur Supprimer", () => {
cy.get(".contact-item").contains("Alice Tremblay").parent().find(".delete-btn").click();
cy.get(".contact-item").should("have.length", 2);
cy.get(".contact-item").should("not.contain.text", "Alice Tremblay");
});

it("conserve les contacts après un rechargement de la page", () => {
cy.get(".contact-item").contains("Alice Tremblay").parent().find(".delete-btn").click();
cy.reload();
cy.get(".contact-item").should("have.length", 2);
});
});

describe("Navigation", () => {
it("marque le lien Accueil comme actif sur la page d'accueil", () => {
cy.get("#nav-home").should("have.class", "active");
cy.get("#nav-add").should("not.have.class", "active");
});

it("navigue vers la page d'ajout au clic sur le lien Ajouter", () => {
cy.get("#nav-add").click();
cy.url().should("include", "/add.html");
cy.get("#nav-add").should("have.class", "active");
});
});

describe("Page d'ajout", () => {
beforeEach(() => {
cy.visit("/add.html");
});

it("affiche une erreur si le formulaire est soumis vide", () => {
cy.get("#add-form").submit();
cy.get("#error-message").should("be.visible");
cy.url().should("include", "/add.html");
});

it("ajoute un nouveau contact et retourne à la liste", () => {
cy.get("#name-input").type("David Bouchard");
cy.get("#email-input").type("david.bouchard@example.com");
cy.get("#add-form").submit();

cy.url().should("include", "/index.html");
cy.get(".contact-item").should("have.length", 4);
cy.get(".contact-item").should("contain.text", "David Bouchard");
});
});
});
1 change: 1 addition & 0 deletions Tests/Contacts/cypress/support/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Fichier de support Cypress. Aucune commande personnalisée n'est requise pour cet exercice.
Loading