-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add boilerplate for playwright e2e testing (#2519)
* add playwrite e2e testing
- Loading branch information
1 parent
e1c7d8c
commit 7332f79
Showing
9 changed files
with
302 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import path from "path"; | ||
import { defineConfig, devices } from "@playwright/test"; | ||
import dotenv from "dotenv"; | ||
import { fileURLToPath } from "url"; | ||
|
||
// Replicate __dirname functionality in ES modules | ||
const __filename = fileURLToPath(import.meta.url); | ||
const __dirname = path.dirname(__filename); | ||
|
||
/** | ||
* Read environment variables from file. | ||
* https://github.com/motdotla/dotenv | ||
*/ | ||
dotenv.config({ path: path.resolve(__dirname, ".env.local") }); | ||
|
||
// Simplified environment variable handling | ||
const requiredEnvVars = { | ||
REACT_APP_USERNAME: process.env.REACT_APP_USERNAME, | ||
REACT_APP_PASSWORD: process.env.REACT_APP_PASSWORD, | ||
REACT_APP_URL: process.env.REACT_APP_URL, | ||
}; | ||
|
||
// Validate required environment variables | ||
Object.entries(requiredEnvVars).forEach(([key, value]) => { | ||
if (!value) { | ||
throw new Error( | ||
`Required environment variable ${key} is missing. Please add it to .env.local` | ||
); | ||
} | ||
}); | ||
|
||
/** | ||
* @see https://playwright.dev/docs/test-configuration | ||
*/ | ||
export default defineConfig({ | ||
testDir: "./playwright/tests", | ||
headless: true, // Run in headless mode for faster execution | ||
fullyParallel: true, | ||
forbidOnly: !!process.env.CI, | ||
retries: process.env.CI ? 2 : 0, | ||
workers: process.env.CI ? 1 : undefined, | ||
reporter: "html", | ||
globalSetup: "./playwright/global-setup.js", | ||
|
||
use: { | ||
baseURL: process.env.REACT_APP_URL || "http://localhost:3000", | ||
storageState: "./playwright/.auth/state.json", | ||
trace: "on-first-retry", | ||
navigationTimeout: 30000, | ||
actionTimeout: 15000, | ||
}, | ||
|
||
projects: [ | ||
{ | ||
name: "chromium", | ||
use: { | ||
...devices["Desktop Chrome"], | ||
}, | ||
}, | ||
{ | ||
name: "firefox", | ||
use: { | ||
...devices["Desktop Firefox"], | ||
}, | ||
}, | ||
{ | ||
name: "webkit", | ||
use: { | ||
...devices["Desktop Safari"], | ||
}, | ||
}, | ||
{ | ||
name: "edge", | ||
use: { | ||
...devices["Desktop Edge"], | ||
}, | ||
}, | ||
], | ||
|
||
webServer: { | ||
command: "yarn run test:e2e:start", | ||
url: process.env.REACT_APP_URL || "http://localhost:3000", | ||
reuseExistingServer: !process.env.CI, | ||
timeout: 30000, | ||
env: requiredEnvVars, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { chromium } from "@playwright/test"; | ||
import fs from "fs"; | ||
import path from "path"; | ||
|
||
async function globalSetup() { | ||
const storageState = "./playwright/.auth/state.json"; | ||
const storageDir = path.dirname(storageState); | ||
|
||
if (!fs.existsSync(storageDir)) { | ||
fs.mkdirSync(storageDir, { recursive: true }); | ||
} | ||
const browser = await chromium.launch(); | ||
const context = await browser.newContext(); | ||
const page = await context.newPage(); | ||
|
||
try { | ||
// Navigate and sign in | ||
await page.goto(process.env.REACT_APP_URL || "http://localhost:3000"); | ||
await page.locator("a").filter({ hasText: "Sign in" }).click(); | ||
|
||
// Handle OSM login | ||
await page.locator("#username").fill(process.env.REACT_APP_USERNAME); | ||
await page.locator("#password").fill(process.env.REACT_APP_PASSWORD); | ||
await page.locator('input[type="submit"][value="Log in"]').click(); | ||
|
||
// Handle OAuth if needed | ||
try { | ||
const authorizeButton = await page.waitForSelector( | ||
'input[type="submit"][value="Authorize"]', | ||
{ timeout: 5000 } | ||
); | ||
if (authorizeButton) { | ||
await authorizeButton.click(); | ||
} | ||
} catch (e) {} | ||
|
||
await context.storageState({ path: storageState }); | ||
} finally { | ||
await context.close(); | ||
await browser.close(); | ||
} | ||
} | ||
|
||
export default globalSetup; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { test, expect } from "@playwright/test"; | ||
|
||
test.describe("Logged in navigation", () => { | ||
test.beforeEach(async ({ page }) => { | ||
await page.goto("/"); | ||
await page.waitForLoadState("networkidle"); | ||
await page | ||
.getByRole("banner") | ||
.locator("a") | ||
.filter({ hasText: "Sign in" }) | ||
.click(); | ||
await page | ||
.getByRole("link", { name: "My Points" }) | ||
.waitFor({ state: "visible", timeout: 5000 }); | ||
}); | ||
|
||
test("should navigate to Find Challenges", async ({ page }) => { | ||
await page | ||
.getByRole("navigation") | ||
.getByRole("link", { name: "Find Challenges" }) | ||
.click(); | ||
await expect( | ||
page.getByRole("heading", { name: "Challenges" }).locator("span") | ||
).toBeVisible(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { test, expect } from "@playwright/test"; | ||
|
||
test.describe("Logged out navigation", () => { | ||
test.use({ storageState: { cookies: [], origins: [] } }); | ||
|
||
test.beforeEach(async ({ page }) => { | ||
await page.goto("/"); | ||
await page.waitForLoadState("networkidle"); | ||
}); | ||
|
||
test("should load find challenges page", async ({ page }) => { | ||
await page | ||
.getByRole("navigation") | ||
.getByRole("link", { name: "Find Challenges" }) | ||
.click(); | ||
await expect( | ||
page.getByRole("heading", { name: "Challenges" }).locator("span") | ||
).toBeVisible(); | ||
await expect( | ||
page.locator("a").filter({ hasText: "Sign in" }) | ||
).toBeVisible(); | ||
}); | ||
|
||
test("should load leaderboard page", async ({ page }) => { | ||
await page | ||
.getByRole("navigation") | ||
.getByRole("link", { name: "Leaderboard" }) | ||
.click(); | ||
await page.waitForLoadState("networkidle"); | ||
}); | ||
|
||
test("should load learn page", async ({ page }) => { | ||
await page | ||
.getByRole("navigation") | ||
.getByRole("link", { name: "Learn" }) | ||
.click(); | ||
await page.waitForLoadState("networkidle"); | ||
}); | ||
|
||
test("should load blog page", async ({ page }) => { | ||
await page | ||
.getByRole("navigation") | ||
.getByRole("link", { name: "Blog" }) | ||
.click(); | ||
await page.waitForLoadState("networkidle"); | ||
}); | ||
|
||
test("should load donate page", async ({ page }) => { | ||
await page | ||
.getByRole("navigation") | ||
.getByRole("link", { name: "Donate" }) | ||
.click(); | ||
await page.waitForLoadState("networkidle"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { test } from "@playwright/test"; | ||
|
||
test.describe("Logged in navigation", () => { | ||
test.use({ storageState: { cookies: [], origins: [] } }); | ||
|
||
test("should login and redirect to maproulette", async ({ page }) => { | ||
await page.goto("/"); | ||
await page | ||
.getByRole("banner") | ||
.locator("a") | ||
.filter({ hasText: "Sign in" }) | ||
.click(); | ||
await page | ||
.getByLabel("Email Address or Username") | ||
.fill(process.env.REACT_APP_USERNAME || ""); | ||
await page | ||
.getByLabel("Password") | ||
.fill(process.env.REACT_APP_PASSWORD || ""); | ||
await page.getByRole("button", { name: "Log in" }).click(); | ||
await page.waitForLoadState("networkidle"); | ||
await page | ||
.getByRole("link", { name: "My Points" }) | ||
.waitFor({ state: "visible", timeout: 5000 }); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters