diff --git a/.gitignore b/.gitignore index 7eb0d32..e1a47f5 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ public/sitemap-0.xml # Sentry .sentryclirc +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/package-lock.json b/package-lock.json index a22f16f..07e25eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "swr": "^1.2.2" }, "devDependencies": { + "@playwright/test": "^1.38.1", "@types/jest": "^27.4.1", "@types/js-cookie": "^3.0.1", "@types/node": "^16.11.36", @@ -2723,6 +2724,21 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, + "node_modules/@playwright/test": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz", + "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==", + "dev": true, + "dependencies": { + "playwright": "1.38.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -11199,6 +11215,36 @@ "node": ">=6" } }, + "node_modules/playwright": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz", + "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==", + "dev": true, + "dependencies": { + "playwright-core": "1.38.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz", + "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/pngjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", @@ -17049,6 +17095,15 @@ } } }, + "@playwright/test": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz", + "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==", + "dev": true, + "requires": { + "playwright": "1.38.1" + } + }, "@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -23383,6 +23438,22 @@ } } }, + "playwright": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz", + "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.38.1" + } + }, + "playwright-core": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz", + "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==", + "dev": true + }, "pngjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", diff --git a/package.json b/package.json index 44bc95d..7964673 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,13 @@ "start-prod": "next start --port ${SERVER_PORT:=80}", "serve": "next start --port 3000", "test:unit": "jest --coverage", - "test": "npm run testcafe", + "test": "playwright test", "testcafe": "testcafe --config-file testcafe.json", "build-analyze": "ANALYZE=true npm run build", "prepare": "husky install" }, "devDependencies": { + "@playwright/test": "^1.38.1", "@types/jest": "^27.4.1", "@types/js-cookie": "^3.0.1", "@types/node": "^16.11.36", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..fbac3f3 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,104 @@ +import { defineConfig, devices } from "@playwright/test"; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: "./tests/playwright", + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: "html", + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: "https://dev.sahkoinsinoorikilta.fi", + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: "on-first-retry", + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: "event setup", + testMatch: /event-setup\.test\.ts/, + }, + { + name: "test teardown", + testMatch: /event-teardown\.test\.ts/, + }, + { + name: "tests", + testIgnore: /admin\//, + dependencies: ["event setup"], + teardown: "test teardown", + use: { ...devices["Desktop Chrome"] }, + }, + { + name: "admin setup", + testMatch: /admin\/setup\.test\.ts/, + }, + { + name: "admin tests", + testDir: "/tests/playwright/admin", + testIgnore: /admin\/setup\.test\.ts/, + dependencies: ["admin setup"], + use: { ...devices["Desktop Chrome"] }, + }, + + // { + // name: "chromium", + // use: { ...devices["Desktop Chrome"] }, + // }, + + // { + // name: "firefox", + // use: { ...devices["Desktop Firefox"] }, + // }, + + // { + // name: "webkit", + // use: { ...devices["Desktop Safari"] }, + // }, + + // /* Test against mobile viewports. */ + // { + // name: "Mobile Chrome", + // use: { ...devices["Pixel 5"] }, + // }, + // { + // name: "Mobile Safari", + // use: { ...devices["iPhone 12"] }, + // }, + + // /* Test against branded browsers. */ + // { + // name: "Microsoft Edge", + // use: { ...devices["Desktop Edge"], channel: "msedge" }, + // }, + // { + // name: "Google Chrome", + // use: { ...devices["Desktop Chrome"], channel: "chrome" }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/tests/playwright/admin/setup.test.ts b/tests/playwright/admin/setup.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/tests/playwright/event-setup.test.ts b/tests/playwright/event-setup.test.ts new file mode 100644 index 0000000..cd024ef --- /dev/null +++ b/tests/playwright/event-setup.test.ts @@ -0,0 +1,11 @@ +import { test as setup } from "@playwright/test"; +import { generateAccessToken, generateTestEvent, generateTestForm } from "../utils"; + +setup("create events", async () => { + const token = await generateAccessToken(); + const form = await generateTestForm(token); + const event = await generateTestEvent([form.id], token); + + process.env.FORM_ID = form.id; + process.env.EVENT_ID = event.id; +}); diff --git a/tests/playwright/event-teardown.test.ts b/tests/playwright/event-teardown.test.ts new file mode 100644 index 0000000..a5805c1 --- /dev/null +++ b/tests/playwright/event-teardown.test.ts @@ -0,0 +1,10 @@ +import { test as teardown } from "@playwright/test"; +import { deleteEvent, deleteForm, generateAccessToken } from "../utils"; + +teardown("delete events", async () => { + const token = await generateAccessToken(); + const { FORM_ID: formId, EVENT_ID: eventId } = process.env; + + await deleteEvent(eventId, token); + await deleteForm(formId, token); +}); diff --git a/tests/playwright/pages.test.ts b/tests/playwright/pages.test.ts new file mode 100644 index 0000000..45791f2 --- /dev/null +++ b/tests/playwright/pages.test.ts @@ -0,0 +1,24 @@ +import { test, expect } from "@playwright/test"; +// import fs from "fs"; + +// const sitemap = fs.readFileSync("../../public/sitemap-0.xml"); + +// const pages = sitemap.; + +// test("favicon exists", async ({ page }) => { +// await page.goto("/"); +// }); + +// Page +// const pagePath = "/"; + +test("landing page renders correctly", async ({ page }) => { + await page.goto("/"); + await expect(page).toHaveTitle("Aalto-yliopiston Sähköinsinöörikilta ry"); + await expect(page).toHaveScreenshot("index-page.png"); +}); + +test("404 page renders correctly", async ({ page }) => { + await page.goto("/404"); + await expect(page).toHaveScreenshot("404 page.png"); +}); diff --git a/tests/playwright/pages.test.ts-snapshots/404-page-chromium-darwin.png b/tests/playwright/pages.test.ts-snapshots/404-page-chromium-darwin.png new file mode 100644 index 0000000..d6a30b6 Binary files /dev/null and b/tests/playwright/pages.test.ts-snapshots/404-page-chromium-darwin.png differ diff --git a/tests/playwright/pages.test.ts-snapshots/index-page-chromium-darwin.png b/tests/playwright/pages.test.ts-snapshots/index-page-chromium-darwin.png new file mode 100644 index 0000000..083b8da Binary files /dev/null and b/tests/playwright/pages.test.ts-snapshots/index-page-chromium-darwin.png differ diff --git a/tests/playwright/pages.test.ts-snapshots/index-page-tests-darwin.png b/tests/playwright/pages.test.ts-snapshots/index-page-tests-darwin.png new file mode 100644 index 0000000..083b8da Binary files /dev/null and b/tests/playwright/pages.test.ts-snapshots/index-page-tests-darwin.png differ diff --git a/tests/playwright/signup.test.ts b/tests/playwright/signup.test.ts new file mode 100644 index 0000000..0d84bcf --- /dev/null +++ b/tests/playwright/signup.test.ts @@ -0,0 +1,19 @@ +import { + test, expect, +} from "@playwright/test"; + +const NAME = "Testi Testeri"; +const EMAIL = "e2e@sahkoinsinoorikilta.fi"; + +test("signup to event", async ({ page }) => { + await page.goto("/"); + await page.locator("[id=\"\\#events\"]").getByRole("button", { name: "Lue lisää ›" }).first().click(); + await page.locator("button").filter({ hasText: "Finland flag" }).click(); + await page.getByRole("button", { name: "Test" }).click(); + await page.getByLabel("Nimi*").fill(NAME); + await page.getByLabel("S-Posti*").fill(EMAIL); + await page.locator("label").filter({ hasText: "Testaaja" }).locator("span").click(); + await page.getByRole("button", { name: "Submit" }).click(); + // await expect(page.getByText("Sign-up submitted successfully 😎")).toBeVisible(); + await expect(page.getByRole("list")).toHaveText(NAME); +}); diff --git a/tests/testcafe/404.test.ts b/tests/testcafe/404.test.ts index 28a5493..f02f771 100644 --- a/tests/testcafe/404.test.ts +++ b/tests/testcafe/404.test.ts @@ -1,5 +1,5 @@ import { Selector } from "testcafe"; -import { getSiteRoot } from "./utils"; +import { getSiteRoot } from "../utils"; fixture`404 page renders and functions correctly`.page(`${getSiteRoot()}/404`); diff --git a/tests/testcafe/admin/create-event.test.ts b/tests/testcafe/admin/create-event.test.ts index d280d74..b27cb38 100644 --- a/tests/testcafe/admin/create-event.test.ts +++ b/tests/testcafe/admin/create-event.test.ts @@ -1,7 +1,7 @@ import { Selector } from "testcafe"; import { getSiteRoot, getPageUrl, generateTestForm, deleteEvent, deleteForm, doLogin, generateAccessToken, getPostRequestLogger, waitForLogger, -} from "../utils"; +} from "../../utils"; const LOGGER = getPostRequestLogger("events/"); diff --git a/tests/testcafe/admin/create-signup.test.ts b/tests/testcafe/admin/create-signup.test.ts index 17426ab..81d136d 100644 --- a/tests/testcafe/admin/create-signup.test.ts +++ b/tests/testcafe/admin/create-signup.test.ts @@ -1,7 +1,7 @@ import { Selector } from "testcafe"; import { getSiteRoot, getPageUrl, deleteForm, doLogin, generateAccessToken, getPostRequestLogger, waitForLogger -} from "../utils"; +} from "../../utils"; const LOGGER = getPostRequestLogger("signupForm/"); diff --git a/tests/testcafe/admin/login.test.ts b/tests/testcafe/admin/login.test.ts index 5eabb67..0190927 100644 --- a/tests/testcafe/admin/login.test.ts +++ b/tests/testcafe/admin/login.test.ts @@ -1,5 +1,5 @@ import { Selector, ClientFunction } from "testcafe"; -import { getSiteRoot, doLogin } from "../utils"; +import { getSiteRoot, doLogin } from "../../utils"; fixture`Admin login page functions correctly`.page(`${getSiteRoot()}/admin/login`); diff --git a/tests/testcafe/index.test.ts b/tests/testcafe/index.test.ts index 05e9e59..b0a2b43 100644 --- a/tests/testcafe/index.test.ts +++ b/tests/testcafe/index.test.ts @@ -1,5 +1,5 @@ import { Selector } from "testcafe"; -import { getSiteRoot } from "./utils"; +import { getSiteRoot } from "../utils"; fixture`Front page renders and functions correctly`.page(`${getSiteRoot()}`); diff --git a/tests/testcafe/signupToEvent.test.ts b/tests/testcafe/signupToEvent.test.ts index 5205188..e6fde30 100644 --- a/tests/testcafe/signupToEvent.test.ts +++ b/tests/testcafe/signupToEvent.test.ts @@ -1,7 +1,7 @@ import { Selector } from "testcafe"; import { getSiteRoot, getPageUrl, generateTestEvent, generateTestForm, deleteEvent, deleteForm, generateAccessToken, -} from "./utils"; +} from "../utils"; fixture`Event signup`.page(getSiteRoot()) .before(async (ctx) => { diff --git a/tests/testcafe/utils.ts b/tests/utils.ts similarity index 95% rename from tests/testcafe/utils.ts rename to tests/utils.ts index 113df83..fa4985d 100644 --- a/tests/testcafe/utils.ts +++ b/tests/utils.ts @@ -156,13 +156,15 @@ export const generateTestEvent = async (formIds = [], jwt_access: string) => ( }, jwt_access) ); -export const sleep = async (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); +export const sleep = async (ms: number) => new Promise((resolve) => { setTimeout(resolve, ms); }); export const waitForLogger = async (logger: RequestLogger) => { - for (let i = 0; i < 50; i++) { - await sleep(100); - if (logger.requests.length > 0 ) { - return; + let i = 0; + while (i < 50) { + await sleep(100); // eslint-disable-line no-await-in-loop + if (logger.requests.length > 0) { + return; } + i += 1; } -} +}; diff --git a/tsconfig.json b/tsconfig.json index cbc080f..f169a10 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -58,11 +58,12 @@ "include": [ "./src/**/*", "./types/**/*", - "./tests/testcafe/**/*", + "./tests/**/*", + ".eslintrc.js", + "jest.config.js", "next-sitemap.config.js", "next.config.js", - "jest.config.js", - ".eslintrc.js", + "playwright.config.ts", "sentry.client.config.js", "sentry.server.config.js" ],