81e1eb47fb
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 8s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 44s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 1m8s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m0s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m28s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m1s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 3m54s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m22s
CI/CD Pipeline / Validate - Style (push) Successful in 4m24s
AI Code Review / AI Code Review (pull_request) Successful in 6m59s
CI/CD Pipeline / Validate - Security (push) Successful in 9m19s
CI/CD Pipeline / Unit Tests (push) Successful in 9m57s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 10m0s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 12m26s
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 14s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 14s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 14s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m36s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m11s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m47s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 16m0s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
106 lines
4.1 KiB
TypeScript
Executable File
106 lines
4.1 KiB
TypeScript
Executable File
import { expect, test, type APIRequestContext, type Page } from "@playwright/test"
|
|
|
|
const PASSWORD = "SmokePass123!"
|
|
const apiBase = process.env.E2E_API_BASE || "/api/v1"
|
|
const apiOrigin = apiBase.endsWith("/api/v1") ? apiBase.slice(0, -"/api/v1".length) : ""
|
|
|
|
async function routeBrowserApiToTestApi(page: Page) {
|
|
if (!apiOrigin) return
|
|
await page.route("**/api/v1/**", async (route) => {
|
|
const sourceUrl = new URL(route.request().url())
|
|
const response = await route.fetch({
|
|
url: `${apiOrigin}${sourceUrl.pathname}${sourceUrl.search}`,
|
|
})
|
|
await route.fulfill({ response })
|
|
})
|
|
}
|
|
|
|
async function loginWithRetry(request: APIRequestContext, email: string, password: string) {
|
|
for (let i = 0; i <= 2; i++) {
|
|
const r = await request.post(`${apiBase}/auth/login`, { data: { email, password } })
|
|
if (r.status() !== 429) {
|
|
expect(r.ok(), `login: ${await r.text()}`).toBeTruthy()
|
|
return (await r.json()).access_token as string
|
|
}
|
|
console.log(`[nav] 429 retry ${i + 1}/2`)
|
|
await new Promise((res) => setTimeout(res, 65000))
|
|
}
|
|
throw new Error("Login retries exhausted")
|
|
}
|
|
|
|
/**
|
|
* 核心页面导航冒烟:侧边栏主要入口能访问、文案库/配音库页面能正常加载(不出白屏/无致命 js error)
|
|
*/
|
|
test.describe("Core Navigation", () => {
|
|
let authToken: string
|
|
|
|
test.beforeAll(async ({ request }) => {
|
|
const suffix = Math.random().toString(36).slice(2, 8)
|
|
const email = `e2e-nav-${suffix}@example.com`
|
|
await request.post(`${apiBase}/auth/register`, {
|
|
data: { email, password: PASSWORD, username: `e2e_nav_${suffix}` },
|
|
})
|
|
authToken = await loginWithRetry(request, email, PASSWORD)
|
|
const authHeader = { Authorization: `Bearer ${authToken}` }
|
|
const proj = await request.post(`${apiBase}/projects`, {
|
|
headers: authHeader,
|
|
data: { name: `Smoke Nav ${suffix}` },
|
|
})
|
|
if (proj.ok()) {
|
|
const projectId = (await proj.json()).id ?? (await proj.json()).project_id
|
|
await request.post(`${apiBase}/asset-libraries`, {
|
|
headers: authHeader,
|
|
data: { project_id: projectId, name: "Nav Lib", kind: "video" },
|
|
})
|
|
}
|
|
})
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.setViewportSize({ width: 1440, height: 900 })
|
|
await page.addInitScript((t: string) => {
|
|
window.localStorage.setItem("access_token", t)
|
|
window.localStorage.setItem(
|
|
"auth-storage",
|
|
JSON.stringify({ state: { token: t, user: null } }),
|
|
)
|
|
}, authToken)
|
|
await routeBrowserApiToTestApi(page)
|
|
})
|
|
|
|
const navCases = [
|
|
{ path: "/app/dashboard", marker: /概览|工作台|最近/i, name: "概览" },
|
|
{ path: "/app/generate", marker: /智能剪辑|剪辑/, name: "智能剪辑" },
|
|
{ path: "/app/assets", marker: /视频库|素材/, name: "视频库" },
|
|
{ path: "/app/scripts", marker: /文案/, name: "文案库" },
|
|
{ path: "/app/voices", marker: /配音|我的音色|配音库/, name: "配音库" },
|
|
{ path: "/app/products", marker: /成品|作品/, name: "成品库" },
|
|
{ path: "/app/history", marker: /历史|任务/, name: "任务历史" },
|
|
{ path: "/app/tasks", marker: /任务中心|任务列表/, name: "任务中心" },
|
|
{ path: "/app/points", marker: /积分|我的积分/, name: "积分中心" },
|
|
]
|
|
|
|
for (const c of navCases) {
|
|
test(`visit ${c.name} (${c.path}) loads without fatal pageerror`, async ({ page }) => {
|
|
const errors: Error[] = []
|
|
page.on("pageerror", (e) => errors.push(e))
|
|
await page.goto(c.path)
|
|
await expect(page.locator("body")).not.toBeEmpty({ timeout: 20000 })
|
|
// 过滤掉常见第三方/非致命错误
|
|
const fatal = errors.filter(
|
|
(e) =>
|
|
!/ResizeObserver|Loading chunk|network error|Failed to fetch|chunkLoadError/i.test(
|
|
e.message,
|
|
),
|
|
)
|
|
expect(fatal, `${c.name} pageerrors: ${fatal.map((e) => e.message).join("; ")}`).toHaveLength(
|
|
0,
|
|
)
|
|
await expect(
|
|
page.getByText(c.marker).first(),
|
|
`${c.name} should show relevant text`,
|
|
).toBeVisible({ timeout: 15000 })
|
|
console.log(`[nav] ${c.name} loaded ✓`)
|
|
})
|
|
}
|
|
})
|