Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor E2E tests to use Page Object Model (POM), fixtures, and add additional tests #2945

Merged
merged 3 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,4 @@ docker/.env
/test-results/
/playwright-report/
/playwright/.cache/
/tests/e2e/artifacts/*
/tests/static/e2e/artifacts/*
28 changes: 13 additions & 15 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@ import type { PlaywrightTestConfig } from "@playwright/test"
import { devices } from "@playwright/test"

/**
* See https://playwright.dev/docs/test-configuration.
* @see https://playwright.dev/docs/test-configuration
*/
const config: PlaywrightTestConfig = {
testDir: "./tests/e2e",
outputDir: "./tests/e2e/artifacts/test-failures",
testDir: "./tests/static/e2e",
outputDir: "./tests/static/e2e/artifacts/test-failures",
use: {
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 0,
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://localhost:8080/",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on",
video: "on",
screenshot: "on",
},
/* Maximum time one test can run for. */
timeout: 100 * 1000,
expect: {
Expand All @@ -26,18 +36,6 @@ const config: PlaywrightTestConfig = {
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: {
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 0,
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://localhost:8080/",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on",
video: "on",
screenshot: "on",
},

/* Configure projects for major browsers */
projects: [
{
Expand Down
2 changes: 2 additions & 0 deletions src/dispatch/static/dispatch/src/incident/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
:sort-by.sync="sortBy"
:sort-desc.sync="descending"
:loading="loading"
data-testid="incident-data-table"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to consider a single SOT for these data-testid strings as an Enum that we can use here and in tests Pages

v-model="selected"
loading-text="Loading... Please wait"
show-select
Expand Down Expand Up @@ -88,6 +89,7 @@
</template>
<v-list>
<v-list-item
data-testid="incident-table-edit"
:to="{
name: 'IncidentTableEdit',
params: { name: item.name },
Expand Down
106 changes: 0 additions & 106 deletions tests/e2e/report-submission.spec.ts

This file was deleted.

26 changes: 0 additions & 26 deletions tests/e2e/utils/login.ts

This file was deleted.

26 changes: 26 additions & 0 deletions tests/static/e2e/fixtures/dispatch-fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { test as base } from "@playwright/test"
import { AuthPage } from "../pages/auth-page"
import { ReportIncidentPage } from "../pages/report-incident-page"
import { IncidentsPage } from "../pages/incidents-page"

type DispatchFixtures = {
authPage: AuthPage
reportIncidentPage: ReportIncidentPage
incidentsPage: IncidentsPage
}

export const test = base.extend<DispatchFixtures>({
authPage: async ({ page }, use) => {
await use(new AuthPage(page))
},

reportIncidentPage: async ({ page }, use) => {
await use(new ReportIncidentPage(page))
},

incidentsPage: async ({ page }, use) => {
const incidentsPage = new IncidentsPage(page)
await use(incidentsPage)
},
})
export { expect } from "@playwright/test"
20 changes: 20 additions & 0 deletions tests/static/e2e/incidents-table.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { test, expect } from "./fixtures/dispatch-fixtures"
import register from "./utils/register"

test.describe("Authenticated Dispatch App", () => {
test.beforeEach(async ({ authPage }) => {
await register(authPage)
}),
test("The edit list should appear after clicking the incident edit kebab.", async ({
incidentsPage,
}) => {
await incidentsPage.goto()
await incidentsPage.EditKebab.click()
await expect(incidentsPage.EditMenu).toBeVisible()
await expect.soft(incidentsPage.EditMenu).toBeVisible()
await expect.soft(incidentsPage.EditViewEdit).toBeVisible()
await expect.soft(incidentsPage.EditCreateReport).toBeVisible()
await expect.soft(incidentsPage.EditRunWorkflow).toBeVisible()
await expect.soft(incidentsPage.EditDelete).toBeVisible()
})
})
71 changes: 71 additions & 0 deletions tests/static/e2e/pages/auth-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { expect, Locator, Page } from "@playwright/test"
import { orgSlug, Routes } from "../routes"

export class AuthPage {
readonly page: Page
// Login
readonly loginRoute: string
readonly loginHeader: Locator
// Register
readonly registerRoute: string
readonly registerHeader: Locator
readonly registerLink: Locator
readonly registerButton: Locator
// Shared Components
readonly emailLabel: Locator
readonly passwordLabel: Locator

constructor(page: Page) {
this.page = page
// Login
this.loginRoute = orgSlug + Routes.Login
this.loginHeader = page.getByText("Login").first()
// Register
this.registerRoute = orgSlug + Routes.Register
this.registerHeader = page.getByText("Register").first()
this.registerLink = page.getByRole("link", { name: "Register" })
this.registerButton = page.getByRole("button", { name: "Register" })
// Shared Components
this.emailLabel = page.getByLabel("Email")
this.passwordLabel = page.getByLabel("Password")
}

async gotoLogin() {
await Promise.all([
this.page.goto(this.loginRoute),
await this.page.waitForURL(this.loginRoute),
await expect(this.loginHeader).toBeVisible(),
])
}

async gotoRegisterWithLink() {
await Promise.all([
/*
(wshel) Directly visiting register page will redirect the user to the login page.
We must by click the register button on the login page.
*/
await this.gotoLogin(),
await this.registerLink.first().click(),
await this.page.waitForURL(this.registerRoute),
await expect(this.registerHeader).toBeVisible(),
])
}

async registerNewUser(email: string, password: string) {
await this.gotoRegisterWithLink()
await this.emailLabel.first().click()
await this.emailLabel.fill(email)

await this.passwordLabel.first().click()
await this.passwordLabel.fill(password)

await Promise.all([
this.registerButton.click(),
this.page.waitForURL(orgSlug + Routes.Dashboards),
])
}

async pageObjectModel(email: string, password: string) {
await this.registerNewUser(email, password)
}
}
38 changes: 38 additions & 0 deletions tests/static/e2e/pages/incident-edit-sheet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Locator, Page } from "@playwright/test"
import { Routes, orgSlug } from "../routes"

export class IncidentsPage {
readonly page: Page
readonly route: string

readonly SaveButton: Locator
readonly CloseButton: Locator

readonly CostsTab: Locator
readonly CostsAmount: Locator

constructor(page: Page) {
this.page = page
this.route = orgSlug + Routes.Incidents

this.SaveButton = page.getByRole("button").filter({ hasText: "save" })
this.CloseButton = page.getByRole("button", { name: "Close" })

this.CostsTab = page.getByRole("tab", { name: "Costs" })
this.CostsAmount = page.getByLabel("Amount")
}

async goto(incident: string) {
await Promise.all([
this.page.goto(this.route + `/${incident}`),
await this.page.waitForURL(this.route),
])
}

async addCost(incident: string) {
await this.goto(incident)
await this.CostsTab.first().click()
await this.CostsAmount.click()
await this.CostsAmount.fill("100000")
}
}
39 changes: 39 additions & 0 deletions tests/static/e2e/pages/incidents-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { expect, Locator, Page } from "@playwright/test"
import { Routes, orgSlug } from "../routes"

export class IncidentsPage {
readonly page: Page
readonly route: string
readonly Row: Locator
readonly FirstRow: Locator
readonly OtherRow: Locator
readonly NextPage: Locator
readonly EditKebab: Locator
readonly EditMenu: Locator
readonly EditViewEdit: Locator
readonly EditCreateReport: Locator
readonly EditRunWorkflow: Locator
readonly EditDelete: Locator

constructor(page: Page, incident: string = `dispatch-${orgSlug}-${orgSlug}-2`) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default incident here is a bit fragile to it being present in the table, there's probably a better way.

this.page = page
this.route = orgSlug + Routes.Incidents
this.Row = page.locator("tr")
this.NextPage = page.getByRole("button", { name: "Next page" })
this.EditKebab = page
.getByRole("row", {
name: incident,
})
.getByRole("button")
.nth(2)
this.EditMenu = page.getByTestId("incident-table-edit")
this.EditViewEdit = page.getByRole("menuitem", { name: "View / Edit" })
this.EditCreateReport = page.getByRole("menuitem", { name: "Create Report" })
this.EditRunWorkflow = page.getByRole("menuitem", { name: "Run Workflow" })
this.EditDelete = page.getByRole("menuitem", { name: "Delete" })
}

async goto() {
await Promise.all([this.page.goto(this.route), await this.page.waitForURL(this.route)])
}
}
Loading