diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b72336d..b3b44796 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -174,4 +174,6 @@ jobs: sleep 30 - name: Run Playwright tests + env: + CI: true run: npx playwright test diff --git a/.gitignore b/.gitignore index 74cc18ce..e713990e 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,7 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts -test-results/.last-run.json + +# tests +test-results/ +playwright-report/ \ No newline at end of file diff --git a/package.json b/package.json index 19f5fc52..c8eeeea1 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "preinstall": "npx only-allow pnpm", - "dev": "next dev", + "dev": "next dev --turbopack", "build": "next build", "start": "next start -p 1030", "stage": "next start -p 1031", diff --git a/playwright.config.ts b/playwright.config.ts index 04c599d1..7d3d1158 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,5 +1,20 @@ import { defineConfig } from "@playwright/test"; +// Default to localhost for local development, use staging URL for CI +const baseURL = process.env.CI + ? "https://staging.veganify.app" + : "http://localhost:3000"; + export default defineConfig({ testMatch: ["**/*.spec.ts"], + use: { + baseURL, + }, + webServer: process.env.CI + ? undefined + : { + command: "pnpm dev", + url: "http://localhost:3000", + reuseExistingServer: true, + }, }); diff --git a/src/tests/e2e/EnteredIngredients.spec.ts b/src/tests/e2e/EnteredIngredients.spec.ts new file mode 100644 index 00000000..90e0d2f1 --- /dev/null +++ b/src/tests/e2e/EnteredIngredients.spec.ts @@ -0,0 +1,41 @@ +import { test, expect } from "@playwright/test"; + +test("should relfect entered ingredients", async ({ page }) => { + await page.goto("/de"); + await page.getByRole("link", { name: "ຐ Zutatencheck" }).nth(1).click(); + await page.getByPlaceholder("Zutaten kommagetrennt eingeben").click(); + await expect( + page.getByPlaceholder("Zutaten kommagetrennt eingeben") + ).toBeVisible(); + await page.getByPlaceholder("Zutaten kommagetrennt eingeben").click(); + await page + .getByPlaceholder("Zutaten kommagetrennt eingeben") + .fill("Duck, E101, Beet Roots, Carrot"); + await page.getByLabel("Absenden").click(); + await expect(page.getByText("Beet roots")).toBeVisible(); + await expect(page.getByText("Duck")).toBeVisible(); + await expect(page.getByText("E101")).toBeVisible(); + await expect(page.getByText("Carrot")).toBeVisible(); +}); + +test("User should be able to input ingredients and get a result", async ({ + page, +}) => { + await page.goto("/en/ingredients"); + + const inputField = await page.$('textarea[id="ingredients"]'); + await inputField?.fill("Duck"); + const submitButton = await page.$('button[name="checkingredients"]'); + await submitButton?.click(); + + await page.waitForSelector(".loading_skeleton", { state: "hidden" }); + const resultText = await page.textContent(".resultborder"); + expect(resultText).toContain("Duck"); + + await page.route("**/v0/ingredients/*", (route) => { + expect(route.request().url()).toBe( + "https://api.veganify.app/v1/ingredients/Duck" + ); + expect(route.request().method()).toBe("GET"); + }); +}); diff --git a/src/tests/e2e/LanguageChange.spec.ts b/src/tests/e2e/LanguageChange.spec.ts new file mode 100644 index 00000000..d50d361f --- /dev/null +++ b/src/tests/e2e/LanguageChange.spec.ts @@ -0,0 +1,11 @@ +import { test, expect } from "@playwright/test"; + +test("language change should work", async ({ page }) => { + await page.goto("/de/more"); + await page.getByText("Sprache").click(); + await Promise.all([ + page.waitForLoadState("networkidle"), + page.getByRole("link", { name: "Englisch" }).click(), + ]); + expect(page.url()).toMatch("/en/more"); +}); diff --git a/src/tests/e2e/SponsorOptions.spec.ts b/src/tests/e2e/SponsorOptions.spec.ts new file mode 100644 index 00000000..c1c56606 --- /dev/null +++ b/src/tests/e2e/SponsorOptions.spec.ts @@ -0,0 +1,22 @@ +import { test, expect } from "@playwright/test"; + +test("should display sponsoring options", async ({ page }) => { + await page.goto("de"); + await page.getByRole("link", { name: " Mehr" }).nth(1).click(); + await expect(page.getByText("Kauf uns einen Kaffee")).toBeVisible(); + await page.getByText("Kauf uns einen Kaffee").click(); + await expect(page.getByText("Einmal via Ko-Fi")).toBeVisible(); + await page.getByText("-50€").click(); + await expect( + page.getByRole("link", { name: " Sponsor on Ko-Fi" }) + ).toBeVisible(); + await page.getByText("Monatlich via GitHub").click(); + await expect( + page.getByRole("link", { name: " Sponsor on GitHub" }) + ).toBeVisible(); + await page.getByText("Einmalig via PayPal").click(); + await expect( + page.getByRole("link", { name: " Donate with PayPal" }) + ).toBeVisible(); + await page.getByRole("button", { name: "×" }).click(); +}); diff --git a/src/tests/e2e/barcodeInput.spec.ts b/src/tests/e2e/barcodeInput.spec.ts new file mode 100644 index 00000000..0bd2c51b --- /dev/null +++ b/src/tests/e2e/barcodeInput.spec.ts @@ -0,0 +1,33 @@ +import { test, expect } from "@playwright/test"; + +test("should give out product information after entering barcode", async ({ + page, +}) => { + await page.goto("/de"); + await expect(page.getByPlaceholder("Barcode eingeben")).toBeVisible(); + await page.getByPlaceholder("Barcode eingeben").click(); + await page.getByPlaceholder("Barcode eingeben").fill("4066600204404"); + await page.getByLabel("Absenden").click(); + await expect(page.getByText("Vegan")).toBeVisible(); +}); + +test("User should be able to input a barcode via the URL parameter `ean` ", async ({ + page, +}) => { + await page.route("**/v0/product/*", (route) => { + expect(route.request().url()).toMatch( + /^https:\/\/(api|staging\.api)\.veganify\.app\/v0\/product\/4066600204404$/ + ); + expect(route.request().method()).toBe("POST"); + }); + + await page.goto("/en?ean=4066600204404"); + const inputField = await page.waitForSelector('input[name="barcode"]'); + const inputValue = await inputField.inputValue(); + expect(inputValue).toBe("4066600204404"); + + await page.waitForSelector(".loading_skeleton", { state: "hidden" }); + + const resultText = await page.textContent("#result"); + expect(resultText).toContain("Paulaner Spezi Zero"); +}); diff --git a/src/tests/e2e/usability.spec.ts b/src/tests/e2e/usability.spec.ts new file mode 100644 index 00000000..4b3905bc --- /dev/null +++ b/src/tests/e2e/usability.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from "@playwright/test"; + +test("User should be able to input a barcode and get a result", async ({ + page, +}) => { + await page.goto("/en"); + + const inputField = await page.$('input[name="barcode"]'); + await inputField?.fill("4066600204404"); + const submitButton = await page.$('button[name="submit"]'); + await submitButton?.click(); + + await page.waitForSelector(".loading_skeleton", { state: "hidden" }); + + const resultText = await page.textContent("#result"); + expect(resultText).toContain("Paulaner Spezi Zero"); +}); diff --git a/src/tests/usability.spec.ts b/src/tests/usability.spec.ts deleted file mode 100644 index 6ec3a34b..00000000 --- a/src/tests/usability.spec.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { test, expect } from "@playwright/test"; - -test("User should be able to input a barcode and get a result", async ({ - page, -}) => { - await page.goto("https://staging.veganify.app/en"); - - const inputField = await page.$('input[name="barcode"]'); - await inputField?.type("4066600204404"); - const submitButton = await page.$('button[name="submit"]'); - await submitButton?.click(); - - await page.waitForSelector(".loading_skeleton", { state: "hidden" }); - - const resultText = await page.textContent("#result"); - expect(resultText).toContain("Paulaner Spezi Zero"); -}); - -test("User should be able to input ingredients and get a result", async ({ - page, -}) => { - await page.goto("https://staging.veganify.app/en/ingredients"); - - const inputField = await page.$('textarea[id="ingredients"]'); - await inputField?.type("Duck"); - const submitButton = await page.$('button[name="checkingredients"]'); - await submitButton?.click(); - - await page.waitForSelector(".loading_skeleton", { state: "hidden" }); - const resultText = await page.textContent(".resultborder"); - expect(resultText).toContain("Duck"); - - await page.route("**/v0/ingredients/*", (route) => { - expect(route.request().url()).toBe( - "https://api.veganify.app/v0/ingredients/Duck" - ); - expect(route.request().method()).toBe("GET"); - route.fulfill({ - status: 200, - contentType: "application/json", - body: JSON.stringify({ - code: "OK", - status: "200", - message: "Success", - data: { - vegan: "false", - flagged: ["duck"], - }, - }), - }); - }); -}); - -test("User should be able to input a barcode via the URL parameter `ean` ", async ({ - page, -}) => { - await page.route("**/v0/product/*", (route) => { - expect(route.request().url()).toMatch( - /^https:\/\/(api|staging\.api)\.veganify\.app\/v0\/product\/4066600204404$/ - ); - expect(route.request().method()).toBe("POST"); - route.fulfill({ - status: 200, - contentType: "application/json", - body: JSON.stringify({ - status: 200, - product: { - productname: "Paulaner Spezi Zero", - vegan: "true", - vegetarian: "true", - animaltestfree: "n/a", - palmoil: "false", - nutriscore: "c", - grade: "B", - }, - sources: { - processed: "false", - api: "OpenFoodFacts", - baseuri: "https://world.openfoodfacts.org", - }, - }), - }); - }); - - await page.goto("https://staging.veganify.app/en?ean=4066600204404"); - const inputField = await page.waitForSelector('input[name="barcode"]'); - const inputValue = await inputField.inputValue(); - expect(inputValue).toBe("4066600204404"); - - await page.waitForSelector(".loading_skeleton", { state: "hidden" }); - - const resultText = await page.textContent("#result"); - expect(resultText).toContain("Paulaner Spezi Zero"); -});