import { test, expect } from "./e2e-fixtures"; import { parse } from "yaml"; test("custom YAML nodes use compact forms, persist edits and integrate with subscription groups", async ({ page, }) => { const errors: string[] = []; page.on("pageerror", (e) => errors.push(e.message)); await page.goto("/login"); await page.getByLabel("用户名", { exact: true }).fill("ui-review"); await page.getByLabel("密码", { exact: true }).fill("ui-review-local-2026"); await page.getByRole("button", { name: "登录", exact: true }).click(); await expect(page).toHaveURL(/dashboard/); await page.getByRole("link", { name: "节点管理" }).click(); const create = page .locator(".compact-create-panel details") .filter({ has: page.getByRole("heading", { name: "新增自定义订阅节点" }) }); await create.locator(":scope > summary").click(); await create.getByLabel("Slug", { exact: true }).fill("custom-browser"); await create.getByLabel("显示名", { exact: true }).fill("香港自定义节点"); await create.getByRole("textbox", { name: "节点 YAML", exact: true }).fill("name: [invalid"); await create.getByRole("button", { name: "创建自定义节点" }).click(); await expect(create.getByRole("alert")).toContainText("YAML 格式错误"); await expect(create.getByRole("textbox", { name: "节点 YAML", exact: true })).toHaveValue( "name: [invalid", ); const source = '# keep this comment\nproxies:\n - name: \'HK "AnyTLS": #1\'\n type: anytls\n server: hk.example.com\n port: 443\n password: "test-only:password"\n udp: true\n'; await create.getByRole("textbox", { name: "节点 YAML", exact: true }).fill(source); await create.getByRole("button", { name: "创建自定义节点" }).click(); const row = page .locator(".entity-table__item") .filter({ hasText: "custom-browser" }); await expect(row).toBeVisible(); await expect(row).toContainText("不轮询"); await row.locator(":scope > summary").click(); await expect(row.getByRole("textbox", { name: "节点 YAML", exact: true })).toHaveValue( source, ); await expect(row.getByLabel("客户端地址", { exact: true })).toHaveCount(0); const updated = source.replace('HK "AnyTLS": #1', 'HK "AnyTLS": #2'); await row.getByRole("textbox", { name: "节点 YAML", exact: true }).fill(updated); await row.getByRole("button", { name: "保存自定义节点" }).click(); await expect(row.locator("p[role=status]")).toHaveText("已保存"); await page.reload(); await row.locator(":scope > summary").click(); await expect( row.getByRole("textbox", { name: "节点 YAML", exact: true }), ).toHaveValue(updated); await page.screenshot({ path: "test-results/custom-node-desktop.png", fullPage: true, }); const config = await page.request.get( "/api/admin/nodes/custom-browser/config", ); expect(config.status()).toBe(409); const auth = await page.request.post("/api/hy2/nodes/custom-browser/auth", { data: { auth: "test:test" }, }); expect(await auth.json()).toMatchObject({ ok: false }); await page.getByRole("link", { name: "订阅配置" }).click(); const main = page.locator(".proxy-group-card").first(); await expect(main.locator(".proxy-sort-item")).toContainText( 'HK "AnyTLS": #2', ); const createGroup = page .locator("details") .filter({ has: page.locator("summary", { hasText: "新建分组" }) }); await createGroup.locator(":scope > summary").click(); await createGroup.getByLabel("新建分组名").fill("Custom Work"); await createGroup.getByRole("button", { name: /HK.*添加/ }).click(); await createGroup .getByRole("button", { name: "新建分组", exact: true }) .click(); await expect( page.locator("summary strong", { hasText: "Custom Work" }), ).toBeVisible(); const url = await page.locator(".copy-line code").first().textContent(); const response = await page.request.get(url!); expect(response.status()).toBe(200); const subscription = parse(await response.text()); const proxy = subscription.proxies.find( (p: { type: string }) => p.type === "anytls", ); expect(proxy).toMatchObject({ name: 'HK "AnyTLS": #2', server: "hk.example.com", password: "test-only:password", udp: true, }); expect( subscription["proxy-groups"].find( (g: { name: string }) => g.name === "Custom Work", ).proxies, ).toEqual([proxy.name]); const group = page .locator("details") .filter({ has: page.locator("summary strong", { hasText: "Custom Work" }), }); await group.locator(":scope > summary").click(); await group.getByRole("button", { name: "删除分组" }).click(); await page.getByRole("button", { name: "确认删除分组" }).click(); await expect(group).toHaveCount(0); await page.getByRole("link", { name: "节点管理" }).click(); await page.getByRole("searchbox").fill("custom"); await expect(page.locator(".entity-table__item:visible")).toHaveCount(1); await row.locator(":scope > summary").click(); await page.setViewportSize({ width: 390, height: 844 }); await expect .poll(() => page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), ) .toBe(true); await page.screenshot({ path: "test-results/custom-node-mobile.png", fullPage: true, }); await row.getByLabel("启用节点", { exact: true }).uncheck(); await row.getByRole("button", { name: "保存自定义节点" }).click(); await expect(row.locator("p[role=status]")).toHaveText("已保存"); const disabled = parse(await (await page.request.get(url!)).text()); expect( disabled.proxies.some((p: { type: string }) => p.type === "anytls"), ).toBe(false); await row.getByRole("button", { name: "删除节点" }).click(); await page.getByRole("button", { name: "确认删除节点" }).click(); await expect(row).toHaveCount(0); expect(errors).toEqual([]); });