import type { Response } from "@playwright/test"; import { expect, test as base } from "@playwright/test"; import { pipe } from "effect"; import { not } from "effect/Boolean"; import type { WCV3Product, 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"; /* * Faire un premier test simple où l'on clic sur la première carte du shop * On doit pouvoir naviguer sur la page * Le produit ne DOIT pas avoir de variations (pour l'instant) * Le bouton d'ajout du panier doit être visible * (Si le produit n'est pas en stock) On ne doit pas pouvoir ajouter le produit au panier * Le bouton doit afficher le texte "Out of stock" * Le bouton doit être désactivé */ type ProductsFixture = { products: ProductsKinds; }; type ProductsKinds = { allProducts: WCV3Products; simpleProducts: WCV3Products; simpleProductsWithoutStock: WCV3Products; simpleProductsWithStock: WCV3Products; }; export const test = base.extend({ products: async ({ page, request }, use) => { await page["goto"]("/shop"); 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 }, }); expect(response.ok(), "The API returned the list of every Product").toBeTruthy(); const isSimpleProduct = (product: WCV3Product) => product.type === "simple"; const hasStock = (product: WCV3Product) => (product.stock_quantity ?? 0) > 0; const hasNoStock = (product: WCV3Product) => pipe(hasStock(product), not); const allProducts = (await response.json()) as WCV3Products; const simpleProducts = allProducts.filter(isSimpleProduct); const simpleProductsWithStock = simpleProducts.filter(hasStock); const simpleProductsWithoutStock = simpleProducts.filter(hasNoStock); const kinds = { allProducts, simpleProducts, simpleProductsWithoutStock, simpleProductsWithStock, } satisfies ProductsKinds; await use(kinds); }, }); test("can add a Product without variation with stock to the Cart", async ({ page, products }) => { // Prend un produit au hasard. const randomProductIndex = getRandomIntInclusive(0, products.simpleProductsWithStock.length - 1); const randomProduct = products.simpleProductsWithStock.at(randomProductIndex); expect(randomProduct, "The selected random Product must exist").toBeTruthy(); if (randomProduct === undefined) { throw new Error("The random product can't be undefined"); } // Va à la page du Produit. await page["goto"](randomProduct.permalink); // Vérifie le bon état du bouton de l'ajout au Panier. const addToCartButton = page.getByRole("button", { disabled: false, name: "Add to cart" }); await addToCartButton.scrollIntoViewIfNeeded(); await expect(addToCartButton, "The add to cart button must be visible").toBeVisible(); await expect(addToCartButton, "The add to cart button must be enabled").toBeEnabled(); // Vérifie qu'au clic sur le bouton, l'ajout au Panier retourne un succès. const addToCartResponse: Promise = page.waitForResponse(new RegExp(".*/wp-json/wc/store/cart/add-item")); await addToCartButton.click(); const addToCartStatus = (await addToCartResponse).ok(); expect(addToCartStatus, "The cart addition must succeed").toBeTruthy(); // Vérifie que le bouton ait changé de texte. const addedToCartButton = page.getByRole("button", { name: "Added to cart!" }); expect(addedToCartButton, "The add to cart button's text has changed").toBeDefined(); // Vérifie que le compteur d'articles dans le Panier soit incrémenté. const cartLink = page.getByRole("link", { name: "cart (1)" }); expect(cartLink, "The cart items' indicator has been incremented").toBeDefined(); }); test("can't add a Product without variation without stock to the Cart", async ({ page, products }) => { // Prend un produit au hasard. const randomProductIndex = getRandomIntInclusive(0, products.simpleProductsWithoutStock.length - 1); const randomProduct = products.simpleProductsWithoutStock.at(randomProductIndex); expect(randomProduct, "The selected random Product must exist").toBeTruthy(); if (randomProduct === undefined) { throw new Error("The random product can't be undefined"); } // Va à la page du Produit. await page["goto"](randomProduct.permalink); // Vérifie le bon état du bouton de l'ajout au Panier. const outOfStockButton = page.getByRole("button", { disabled: true, name: "Out of stock" }); await outOfStockButton.scrollIntoViewIfNeeded(); await expect(outOfStockButton, "The add to cart button must be visible").toBeVisible(); await expect(outOfStockButton, "The add to cart button must be disabled").toBeDisabled(); }); const getRandomIntInclusive = (min: number, max: number): number => { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() * (maxFloored - minCeiled + 1) + minCeiled); };