/** @effect-diagnostics asyncFunction:skip-file */ import type { APIRequestContext, Locator, Page, Response } from "@playwright/test"; import { expect, test } from "@playwright/test"; import { ManagedRuntime } from "effect"; import { APIClient } from "../../web/app/themes/haiku-atelier-2024/src/scripts-effect/lib/api.ts"; import type { WCV3Products } from "../../web/app/themes/haiku-atelier-2024/src/scripts/lib/types/api/v3/products"; import type { BackendHeaders } from "./utils.ts"; import { getBackendHeadersFromHtml } from "./utils.ts"; test.describe.configure({ mode: "parallel", timeout: 60_000 }); /** _Runtime_ pour effectuer les appels API. */ const TestRuntime = ManagedRuntime.make(APIClient.Live); test("is on the first page of the Shop", async ({ page }): Promise => { const FIRST_PAGE_NUMBER = "1"; await page.goto("https://haikuatelier.gcch.local/shop/"); const productsGrid: Locator = page.locator(".grille-produits"); await expect(productsGrid, "La grille des Produits est visible.").toBeVisible(); expect(await productsGrid.getAttribute("data-page"), "Le numéro de page initial est correct.").toBe( FIRST_PAGE_NUMBER, ); }); test("can scroll to the end of the grid", async ({ page }): Promise => { await page.goto("https://haikuatelier.gcch.local/shop/"); let hasMoreProducts = true; let currentPageNumber = "1"; const productsGrid: Locator = page.locator(".grille-produits"); await expect(productsGrid, "La grille des Produits est visible.").toBeVisible(); const showMoreButton: Locator = page.getByRole("button", { name: "Show more" }); await expect(showMoreButton, "Le bouton « Show more » est visible.").toBeVisible(); while (hasMoreProducts) { const newProductsResponse: Promise = page.waitForResponse(new RegExp(".*wp-json/wc/v3/products.*")); await showMoreButton.click(); await newProductsResponse; const newPageNumber = String(Number(currentPageNumber) + 1); // Créé un nouveau Locator que l'on attend pour s'assurer que l'attribut soit bien mis à jour. const gridWithNewPageNumber = page.locator(`.grille-produits[data-page="${newPageNumber}"]`); await gridWithNewPageNumber.waitFor(); // Redondance pour expliciter la raison de l'assertion. expect(await productsGrid.getAttribute("data-page"), "Le numéro de page a été incrémenté.").toBe( newPageNumber, ); currentPageNumber = newPageNumber; // La fin de la grille est atteint. if (await showMoreButton.isHidden()) { hasMoreProducts = false; } } }); test("can access all Products' pages", async ({ page, request }): Promise => { await page.goto("https://haikuatelier.gcch.local/shop/"); const links = await getAllProductsLinks(page, request); for (const link of links) { // Vérifie que le lien de la page retourne OK. const req = await request.get(link); await expect(req, "La page du Produit est accessible.").toBeOK(); } }); test("has no Product with broken images", async ({ page }): Promise => { await page.goto("https://haikuatelier.gcch.local/shop/"); await loadMoreProducts(page); const brokenImages = await page.locator("a:has(img[src=''])").all(); const links = await Promise.all(brokenImages.map(locator => locator.getAttribute("href"))); console.info("Liens des Produits avec images cassées :", links); expect(links.length, "Il n'y pas d'images cassées.").toEqual(0); }); const getAllProductsLinks = async (page: Page, request: APIRequestContext): Promise> => { const backendHeaders: BackendHeaders = await getBackendHeadersFromHtml(page); const response = await request.get("/wp-json/wc/v3/products?page=1&per_page=100&status=publish", { headers: { Authorization: `Basic ${backendHeaders.authString}`, Nonce: backendHeaders.nonce }, }); const json = (await response.json()) as WCV3Products; return json.map(p => p.permalink); }; const loadMoreProducts = async (page: Page): Promise => { const showMoreButton: Locator = page.getByRole("button", { name: "Show more" }); const newProductsResponse: Promise = page.waitForResponse(new RegExp(".*wp-json/wc/v3/products.*")); await showMoreButton.click(); await newProductsResponse; };