From ce7a94e492e0248550b12f8067d3e8b5ca140c31 Mon Sep 17 00:00:00 2001 From: QAComet Date: Sun, 21 Apr 2024 17:16:30 -0600 Subject: [PATCH] feat(e2e): add fixtures and data seeding --- e2e/data/user.ts | 20 +++++++++++++ e2e/fixtures/base/dashboard-page.ts | 10 +++++++ e2e/fixtures/base/modal.ts | 45 +++++++++++++++++++++++++++++ e2e/fixtures/base/page.ts | 20 +++++++++++++ e2e/fixtures/index.ts | 27 +++++++++++++++++ e2e/fixtures/login-page.ts | 27 +++++++++++++++++ e2e/fixtures/registration-page.ts | 28 ++++++++++++++++++ e2e/index.ts | 2 ++ 8 files changed, 179 insertions(+) create mode 100644 e2e/data/user.ts create mode 100644 e2e/fixtures/base/dashboard-page.ts create mode 100644 e2e/fixtures/base/modal.ts create mode 100644 e2e/fixtures/base/page.ts create mode 100644 e2e/fixtures/index.ts create mode 100644 e2e/fixtures/login-page.ts create mode 100644 e2e/fixtures/registration-page.ts create mode 100644 e2e/index.ts diff --git a/e2e/data/user.ts b/e2e/data/user.ts new file mode 100644 index 0000000..3ae0b73 --- /dev/null +++ b/e2e/data/user.ts @@ -0,0 +1,20 @@ +import axios, { AxiosError } from "axios" + +axios.defaults.baseURL = "http://localhost:3000" + +export async function seedUser (username?: string, password?: string, name?: string) { + try { + return await axios.post("/api/v1/users", { + username: username || "test", + password: password || "password", + name: name || "Test User", + }) + } catch (e: any) { + if (e instanceof AxiosError) { + if (e.response?.status === 400) { + return + } + } + throw e + } +} \ No newline at end of file diff --git a/e2e/fixtures/base/dashboard-page.ts b/e2e/fixtures/base/dashboard-page.ts new file mode 100644 index 0000000..559a084 --- /dev/null +++ b/e2e/fixtures/base/dashboard-page.ts @@ -0,0 +1,10 @@ +import { Locator, Page } from "playwright"; +import { BasePage } from "./page"; + +export class DashboardPage extends BasePage { + container: Locator; + constructor(page: Page) { + super(page); + this.container = this.page.getByTestId("dashboard-wrapper"); + } +} diff --git a/e2e/fixtures/base/modal.ts b/e2e/fixtures/base/modal.ts new file mode 100644 index 0000000..7cf788b --- /dev/null +++ b/e2e/fixtures/base/modal.ts @@ -0,0 +1,45 @@ +import { Locator, Page } from "@playwright/test"; + +export class BaseModal { + page: Page; + container: Locator; + mobileContainer: Locator; + closeModalButton: Locator; + mobileModalSlider: Locator; + + constructor(page: Page) { + this.page = page; + this.container = page.getByTestId("modal-container"); + this.mobileContainer = page.getByTestId("mobile-modal-container"); + this.closeModalButton = this.container.getByTestId("close-modal-button"); + this.mobileModalSlider = this.mobileContainer.getByTestId( + "mobile-modal-slider" + ); + } + + async close() { + if (await this.container.isVisible()) { + await this.closeModalButton.click(); + } + if (await this.mobileContainer.isVisible()) { + const box = await this.mobileModalSlider.boundingBox(); + if (!box) { + return; + } + const pageHeight = await this.page.evaluate(() => window.innerHeight); + const startX = box.x + box.width / 2; + const startY = box.y + box.height / 2; + await this.page.mouse.move(startX, startY); + await this.page.mouse.down(); + await this.page.mouse.move(startX, startY + pageHeight / 2); + await this.page.mouse.up(); + } + } + + async isOpen() { + return ( + (await this.container.isVisible()) || + (await this.mobileContainer.isVisible()) + ); + } +} diff --git a/e2e/fixtures/base/page.ts b/e2e/fixtures/base/page.ts new file mode 100644 index 0000000..b078f6c --- /dev/null +++ b/e2e/fixtures/base/page.ts @@ -0,0 +1,20 @@ +import { Locator, Page } from "@playwright/test"; + +export class BasePage { + page: Page; + toastMessage: Locator; + + constructor(page: Page) { + this.page = page; + this.toastMessage = this.page.getByTestId("toast-message-container"); + } + + async getLatestToast() { + const toast = this.toastMessage.first(); + return { + locator: toast, + closeButton: toast.getByTestId("close-toast-button"), + message: toast.getByTestId("toast-message"), + }; + } +} diff --git a/e2e/fixtures/index.ts b/e2e/fixtures/index.ts new file mode 100644 index 0000000..a0cebe2 --- /dev/null +++ b/e2e/fixtures/index.ts @@ -0,0 +1,27 @@ +import { test as baseTest } from "@playwright/test"; +import { LoginPage } from "./login-page"; +import { RegistrationPage } from "./registration-page"; +import { DashboardPage } from "./base/dashboard-page"; + +export const test = baseTest.extend<{ + dashboardPage: DashboardPage; + loginPage: LoginPage; + registrationPage: RegistrationPage; +}>({ + page: async ({ page }, use) => { + await page.goto("/"); + use(page); + }, + dashboardPage: async ({ page }, use) => { + const dashboardPage = new DashboardPage(page); + await use(dashboardPage); + }, + loginPage: async ({ page }, use) => { + const loginPage = new LoginPage(page); + await use(loginPage); + }, + registrationPage: async ({ page }, use) => { + const registrationPage = new RegistrationPage(page); + await use(registrationPage); + }, +}); diff --git a/e2e/fixtures/login-page.ts b/e2e/fixtures/login-page.ts new file mode 100644 index 0000000..375da6b --- /dev/null +++ b/e2e/fixtures/login-page.ts @@ -0,0 +1,27 @@ +import { Locator, Page } from "@playwright/test"; +import { BasePage } from "./base/page"; + +export class LoginPage extends BasePage { + submitLoginButton: Locator; + loginForm: Locator; + registerLink: Locator; + passwordInput: Locator; + usernameInput: Locator; + + constructor(page: Page) { + super(page); + + this.submitLoginButton = page.getByTestId("submit-login-button"); + + this.loginForm = page.getByTestId("login-form"); + + this.registerLink = page.getByTestId("register-link"); + + this.passwordInput = page.getByTestId("password-input"); + this.usernameInput = page.getByTestId("username-input"); + } + + async goto() { + await this.page.goto("/login"); + } +} diff --git a/e2e/fixtures/registration-page.ts b/e2e/fixtures/registration-page.ts new file mode 100644 index 0000000..64a407b --- /dev/null +++ b/e2e/fixtures/registration-page.ts @@ -0,0 +1,28 @@ +import { Locator, Page } from "@playwright/test"; +import { BasePage } from "./base/page"; + +export class RegistrationPage extends BasePage { + registerButton: Locator; + registrationForm: Locator; + + loginLink: Locator; + + displayNameInput: Locator; + passwordConfirmInput: Locator; + passwordInput: Locator; + usernameInput: Locator; + + constructor(page: Page) { + super(page); + + this.registerButton = page.getByTestId("register-button"); + this.registrationForm = page.getByTestId("registration-form"); + + this.loginLink = page.getByTestId("login-link"); + + this.displayNameInput = page.getByTestId("display-name-input"); + this.passwordConfirmInput = page.getByTestId("password-confirm-input"); + this.passwordInput = page.getByTestId("password-input"); + this.usernameInput = page.getByTestId("username-input"); + } +} diff --git a/e2e/index.ts b/e2e/index.ts new file mode 100644 index 0000000..106985f --- /dev/null +++ b/e2e/index.ts @@ -0,0 +1,2 @@ +export { test } from "./fixtures"; +export { expect } from "@playwright/test";