151 lines
5.4 KiB
TypeScript
151 lines
5.4 KiB
TypeScript
import { test, expect } from "./e2e-fixtures";
|
|
import { certificateFixture } from "./certificate-fixture";
|
|
import { readFileSync } from "node:fs";
|
|
import { parse } from "yaml";
|
|
|
|
test("upload wildcard certificate, download script and complete NAT node registration", async ({
|
|
page,
|
|
request,
|
|
}) => {
|
|
const errors: string[] = [];
|
|
page.on("pageerror", (e) => errors.push(e.message));
|
|
const fixture = certificateFixture();
|
|
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: "证书与安装", exact: true }).click();
|
|
const upload = page.locator("details").filter({
|
|
has: page.getByRole("heading", { name: "上传证书", exact: true }),
|
|
});
|
|
await upload.getByLabel("证书名称").fill("测试通配符证书");
|
|
await upload.getByLabel("CRT / 完整证书链").setInputFiles({
|
|
name: "test.crt",
|
|
mimeType: "text/plain",
|
|
buffer: Buffer.from(fixture.certificate),
|
|
});
|
|
await upload.getByLabel("KEY / 私钥").setInputFiles({
|
|
name: "test.key",
|
|
mimeType: "text/plain",
|
|
buffer: Buffer.from("invalid"),
|
|
});
|
|
await upload.getByRole("button", { name: "上传证书", exact: true }).click();
|
|
await expect(upload.getByRole("alert")).toContainText("无法解析");
|
|
await upload.getByLabel("KEY / 私钥").setInputFiles({
|
|
name: "test.key",
|
|
mimeType: "text/plain",
|
|
buffer: Buffer.from(fixture.privateKey),
|
|
});
|
|
await upload.getByRole("button", { name: "上传证书", exact: true }).click();
|
|
await expect(
|
|
page.getByText("测试通配符证书", { exact: true }).first(),
|
|
).toBeVisible();
|
|
await page.reload();
|
|
const create = page.locator("details").filter({
|
|
has: page.getByRole("heading", {
|
|
name: "生成服务器安装脚本",
|
|
exact: true,
|
|
}),
|
|
});
|
|
if ((await create.getAttribute("open")) === null)
|
|
await create.locator("summary").click();
|
|
await create.getByLabel("节点名称", { exact: true }).fill("NAT 浏览器测试");
|
|
await create.getByLabel("节点 Slug", { exact: true }).fill("nat-browser");
|
|
await create
|
|
.getByLabel("托管证书", { exact: true })
|
|
.selectOption({ label: "测试通配符证书" });
|
|
await create
|
|
.getByRole("button", { name: "生成安装脚本", exact: true })
|
|
.click();
|
|
const row = page.locator(".provision-row").filter({ hasText: "nat-browser" });
|
|
await expect(row).toContainText("等待安装");
|
|
const downloadPath = await row
|
|
.getByRole("link", { name: "下载 SH" })
|
|
.getAttribute("href");
|
|
expect((await request.get(downloadPath!, { maxRedirects: 0 })).status()).toBe(
|
|
307,
|
|
);
|
|
const [download] = await Promise.all([
|
|
page.waitForEvent("download"),
|
|
row.getByRole("link", { name: "下载 SH" }).click(),
|
|
]);
|
|
const script = readFileSync((await download.path())!, "utf8");
|
|
expect(script).not.toContain("PRIVATE KEY");
|
|
expect(script).toContain("--domain");
|
|
const token = script.match(/^TOKEN='([a-f0-9]+)'$/m)![1];
|
|
const ports = {
|
|
domain: "nat.p.xn--876a.net",
|
|
hy2Port: 23456,
|
|
statsPort: 23457,
|
|
hy2ListenPort: 443,
|
|
statsListenPort: 9999,
|
|
};
|
|
expect(
|
|
(
|
|
await request.post("/api/install", { data: { action: "prepare", ports } })
|
|
).status(),
|
|
).toBe(400);
|
|
expect(
|
|
(await request.post("/api/install", { data: "x".repeat(5000) })).status(),
|
|
).toBe(413);
|
|
const prepare = await request.post("/api/install", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
data: { action: "prepare", ports },
|
|
});
|
|
expect(prepare.status()).toBe(200);
|
|
expect(prepare.headers()["cache-control"]).toBe("no-store");
|
|
const bundle = await prepare.json();
|
|
expect(bundle.privateKey).toBe(fixture.privateKey);
|
|
const config = parse(bundle.config);
|
|
expect(config.listen).toBe(":443");
|
|
expect(config.trafficStats.listen).toBe(":9999");
|
|
expect(
|
|
(
|
|
await request.post("/api/install", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
data: { action: "complete" },
|
|
})
|
|
).status(),
|
|
).toBe(200);
|
|
await page.reload();
|
|
await expect(row).toContainText("已安装");
|
|
await expect(row.getByRole("link", { name: "下载 SH" })).toHaveCount(0);
|
|
expect(
|
|
(
|
|
await request.post("/api/install", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
data: { action: "prepare", ports },
|
|
})
|
|
).status(),
|
|
).toBe(400);
|
|
await page.screenshot({
|
|
path: "test-results/certificates-desktop.png",
|
|
fullPage: true,
|
|
});
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await page.screenshot({
|
|
path: "test-results/certificates-mobile.png",
|
|
fullPage: true,
|
|
});
|
|
expect(
|
|
await page.evaluate(
|
|
() => document.documentElement.scrollWidth <= innerWidth + 1,
|
|
),
|
|
).toBe(true);
|
|
await page.getByRole("link", { name: "节点管理", exact: true }).click();
|
|
const node = page
|
|
.locator(".entity-table__item")
|
|
.filter({ hasText: "nat-browser" });
|
|
await expect(node).toContainText("nat.p.xn--876a.net");
|
|
await node.locator(":scope > summary").click();
|
|
await expect(node.getByLabel("对外端口", { exact: true })).toHaveValue(
|
|
"23456",
|
|
);
|
|
await expect(node.getByLabel("SNI", { exact: true })).toHaveValue(
|
|
"nat.p.xn--876a.net",
|
|
);
|
|
await expect(node.getByLabel("启用节点", { exact: true })).toBeChecked();
|
|
expect(errors).toEqual([]);
|
|
});
|