Build advertiser console with scoped page URLs and runner-based deployment tests
Advertiser console CI and test deployment / build-deploy (push) Has been cancelled

This commit is contained in:
2026-09-16 15:00:52 +08:00
parent 0f8d3ddbf5
commit d64eb72ff2
18 changed files with 3331 additions and 130 deletions
+154
View File
@@ -0,0 +1,154 @@
import { test, expect } from "@playwright/test";
import {
parseRoute,
projectPath,
promotionPath,
safeReturnTo,
stepSlugs,
} from "../src/navigation";
test("URL hierarchy and wizard steps resolve independently", () => {
for (const [step] of stepSlugs.entries()) {
expect(
parseRoute(projectPath("main-id", "child-id", "project-id", step)),
).toEqual({
kind: "project",
accountId: "main-id",
promotionId: "child-id",
projectId: "project-id",
step,
});
expect(
parseRoute(projectPath("main-id", "child-id", undefined, step)),
).toEqual({
kind: "project",
accountId: "main-id",
promotionId: "child-id",
step,
});
}
expect(parseRoute(promotionPath("main-id", "child-id"))).toEqual({
kind: "promotion",
accountId: "main-id",
promotionId: "child-id",
});
expect(
parseRoute(
"/account-manage/main-id/promotion-manage/child-id/create-project/unknown",
).kind,
).toBe("not-found");
expect(safeReturnTo("?returnTo=https://external.example")).toBe("/workbench");
expect(safeReturnTo("?returnTo=//external.example")).toBe("/workbench");
});
test("deployed console supports registration, scoped URLs, editing, refresh and login return", async ({
page,
context,
}) => {
const email = `ci-${Date.now()}-${Math.random().toString(16).slice(2)}@example.com`;
const password = `CI-${crypto.randomUUID()}`;
await page.goto("/register");
await page.getByLabel("联系人姓名", { exact: true }).fill("CI 自动验证");
await page
.getByLabel("企业 / 主账户名称", { exact: true })
.fill("CI 广告主验收");
await page.getByLabel("邮箱", { exact: true }).fill(email);
await page.getByLabel("密码", { exact: true }).fill(password);
await page
.getByRole("button", { name: "注册并进入工作台", exact: true })
.click();
await expect(page).toHaveURL(/\/workbench$/);
await page.getByRole("button", { name: "账户管理", exact: true }).click();
await expect(page).toHaveURL(/\/account-manage$/);
await page.getByRole("link", { name: "CI 广告主验收", exact: true }).click();
await expect(page).toHaveURL(/\/account-manage\/[^/]+$/);
const parentUrl = new URL(page.url()).pathname;
await page
.getByRole("button", { name: "+ 新建子账户", exact: true })
.click();
await expect(page).toHaveURL(/\/promotion-manage\/new$/);
await page.getByLabel("账户名称", { exact: true }).fill("CI 第二投放账户");
await page.getByRole("button", { name: "保存账户", exact: true }).click();
await expect(page).toHaveURL(new RegExp(`${parentUrl}$`));
await page
.getByRole("link", { name: "CI 第二投放账户", exact: true })
.click();
await expect(page).toHaveURL(/\/promotion-manage\/[^/]+$/);
const promotionUrl = new URL(page.url()).pathname;
await page.getByRole("button", { name: "+ 新建项目", exact: true }).click();
await expect(page).toHaveURL(/\/create-project\/goal$/);
await page.getByRole("button", { name: "下一步 →", exact: true }).click();
await expect(page).toHaveURL(/\/create-project\/product$/);
await page.getByLabel("推广产品或服务", { exact: true }).fill("秋季新品");
await page.getByRole("button", { name: "下一步 →", exact: true }).click();
await page.getByLabel("投放地域", { exact: true }).fill("上海");
await page.getByRole("button", { name: "下一步 →", exact: true }).click();
await page.getByLabel("日预算(元,最低 100)", { exact: true }).fill("500");
await page.getByRole("button", { name: "下一步 →", exact: true }).click();
await page
.getByLabel("落地页地址(可选)", { exact: true })
.fill("https://example.com/product");
await page.getByRole("button", { name: "下一步 →", exact: true }).click();
await page.getByLabel("项目名称", { exact: true }).fill("CI 六步创建项目");
await page.getByRole("button", { name: "保存项目", exact: true }).click();
await expect(page).toHaveURL(new RegExp(`${promotionUrl}$`));
await page
.getByRole("link", { name: "CI 六步创建项目", exact: true })
.click();
await expect(page).toHaveURL(/\/projects\/[^/]+\/edit\/goal$/);
const editRoot = new URL(page.url()).pathname.replace(/goal$/, "");
await page.goto(editRoot + "name");
await expect(page.getByLabel("项目名称", { exact: true })).toHaveValue(
"CI 六步创建项目",
);
await page.getByLabel("项目名称", { exact: true }).fill("CI 已修改项目");
await page.getByRole("button", { name: "保存项目", exact: true }).click();
await expect(
page.getByRole("link", { name: "CI 已修改项目", exact: true }),
).toBeVisible();
await page.goto(editRoot + "budget");
await page.reload();
await expect(
page.getByLabel("日预算(元,最低 100)", { exact: true }),
).toHaveValue("500");
await page.getByRole("button", { name: "下一步 →", exact: true }).click();
await expect(page).toHaveURL(/\/edit\/tracking$/);
await page.goBack();
await expect(page).toHaveURL(/\/edit\/budget$/);
await page.goForward();
await expect(page).toHaveURL(/\/edit\/tracking$/);
await page.getByRole("button", { name: "退出", exact: true }).click();
await page.goto(editRoot + "name");
await expect(page).toHaveURL(/\/login\?returnTo=/);
await page.getByLabel("邮箱", { exact: true }).fill(email);
await page.getByLabel("密码", { exact: true }).fill(password);
await page.getByRole("button", { name: "登录", exact: true }).click();
await expect(page).toHaveURL(new RegExp(`${editRoot}name$`));
await expect(page.getByLabel("项目名称", { exact: true })).toHaveValue(
"CI 已修改项目",
);
await page.goto(promotionUrl.replace(/[^/]+$/, "invalid-id"));
await expect(
page.getByRole("heading", { name: "页面或账户不存在" }),
).toBeVisible();
await page.goto(promotionUrl);
await page
.getByRole("combobox", { name: "当前投放子账户" })
.selectOption({ label: "CI 广告主验收 · 默认投放账户" });
await expect(page).not.toHaveURL(new RegExp(`${promotionUrl}$`));
await expect(
page.getByRole("heading", { name: "还没有推广项目" }),
).toBeVisible();
// A second tab can open the exact project and restore its account context.
const other = await context.newPage();
await other.goto(editRoot + "name");
await expect(other.getByLabel("项目名称", { exact: true })).toHaveValue(
"CI 已修改项目",
);
await other.close();
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/workbench");
await expect(
page.getByRole("heading", { name: "你好,CI 自动验证" }),
).toBeVisible();
});