Files
tibi-svelte-starter/tests/e2e/fixtures.ts

140 lines
5.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test as base, expect, type Page } from "@playwright/test"
import { ensureTestUser, type TestUserCredentials } from "../api/helpers/test-user"
const API_BASE = "/api"
/**
* Shared E2E test fixtures.
*
* Worker-scoped:
* - `testUser` persistent test user (created/reused once per worker)
*
* Test-scoped:
* - `authedPage` Page with logged-in user (token injection via sessionStorage)
* - `accessToken` raw JWT access token
*/
type E2eWorkerFixtures = {
testUser: TestUserCredentials
}
type E2eFixtures = {
authedPage: Page
accessToken: string
}
export const test = base.extend<E2eFixtures, E2eWorkerFixtures>({
/**
* Override page fixture: BrowserSync keeps a WebSocket open permanently,
* preventing "load" and "networkidle" from resolving. We default all
* navigation methods to "domcontentloaded".
*/
page: async ({ page }, use) => {
const origGoto = page.goto.bind(page)
const origReload = page.reload.bind(page)
const origGoBack = page.goBack.bind(page)
const origGoForward = page.goForward.bind(page)
page.goto = ((url: string, opts?: any) =>
origGoto(url, { waitUntil: "domcontentloaded", ...opts })) as typeof page.goto
page.reload = ((opts?: any) => origReload({ waitUntil: "domcontentloaded", ...opts })) as typeof page.reload
page.goBack = ((opts?: any) => origGoBack({ waitUntil: "domcontentloaded", ...opts })) as typeof page.goBack
page.goForward = ((opts?: any) =>
origGoForward({ waitUntil: "domcontentloaded", ...opts })) as typeof page.goForward
await use(page)
},
// Worker-scoped: create/reuse test user once per worker
testUser: [
async ({ playwright }, use) => {
const baseURL = process.env.CODING_URL || "https://localhost:3000"
const user = await ensureTestUser(baseURL)
await use(user)
},
{ scope: "worker" },
],
accessToken: async ({ testUser }, use) => {
await use(testUser.accessToken)
},
// Test-scoped: Page with logged-in user via sessionStorage token injection
authedPage: async ({ page, testUser, baseURL }, use) => {
// Navigate to home so domain is set for sessionStorage
await page.goto("/", { waitUntil: "domcontentloaded" })
await page.waitForLoadState("domcontentloaded")
// Inject auth token into sessionStorage (adapt key names to your app)
await page.evaluate(
({ token, user }) => {
sessionStorage.setItem("auth_token", token)
sessionStorage.setItem(
"auth_user",
JSON.stringify({
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
})
)
},
{
token: testUser.accessToken,
user: testUser,
}
)
// Reload so the app reads the token from sessionStorage
await page.reload({ waitUntil: "domcontentloaded" })
await page.waitForLoadState("domcontentloaded")
await expect(page.locator("#appContainer")).not.toBeEmpty({ timeout: 15000 })
await use(page)
},
})
// ── Helpers ──────────────────────────────────────────────────────────────────
/**
* Wait for the SPA to be ready (appContainer rendered).
* Returns the detected language prefix from the URL.
*/
export async function waitForSpaReady(page: Page): Promise<string> {
await page.waitForLoadState("domcontentloaded")
await expect(page.locator("#appContainer")).not.toBeEmpty({ timeout: 15000 })
const url = page.url()
const match = url.match(/\/([a-z]{2})(\/|$)/)
return match?.[1] || "de"
}
/**
* Navigate to a route, prepending the current language prefix.
*/
export async function navigateToRoute(page: Page, routePath: string): Promise<void> {
const url = page.url()
const match = url.match(/\/([a-z]{2})(\/|$)/)
const lang = match?.[1] || "de"
const fullPath = routePath === "/" ? `/${lang}` : `/${lang}${routePath}`
await page.goto(fullPath)
await page.waitForLoadState("domcontentloaded")
await expect(page.locator("#appContainer")).not.toBeEmpty({ timeout: 15000 })
}
/**
* Click an SPA link and verify no full page reload occurred.
*/
export async function clickSpaLink(page: Page, linkSelector: string): Promise<void> {
await page.evaluate(() => {
;(window as any).__spa_navigation_marker = true
})
await page.locator(linkSelector).first().click()
await page.waitForLoadState("domcontentloaded")
const markerExists = await page.evaluate(() => {
return (window as any).__spa_navigation_marker === true
})
if (!markerExists) {
throw new Error(`SPA navigation failed: full page reload detected when clicking "${linkSelector}"`)
}
}
export { expect, API_BASE, type Page }