feat: add project-aware agent workspace
Generic Container CI/CD / test-build-publish (push) Successful in 2m9s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 2m39s

This commit is contained in:
2026-10-22 18:52:18 +08:00
parent c6a0e74e72
commit 2d8506fea0
191 changed files with 12373 additions and 3556 deletions
+141 -18
View File
@@ -1,7 +1,7 @@
import { expect, test } from "@playwright/test";
import { mockAgentApi } from "./support/mock-agent-api";
test.describe("外围工单测试", () => {
test.describe("外围系统", () => {
test.beforeEach(async ({ page }) => {
await mockAgentApi(page);
await page.route("**/runtime-config.js", (route) =>
@@ -96,8 +96,8 @@ test.describe("外围工单测试", () => {
});
});
await page.goto("/");
await page.getByRole("button", { name: "外围样板管理" }).click();
const panel = page.getByRole("complementary", { name: "外围工单测试" });
await page.getByRole("button", { name: "外围系统", exact: true }).click();
const panel = page.getByRole("complementary", { name: "外围系统" });
await expect(panel).toBeVisible();
await expect(panel.getByText("工单接口测试", { exact: true })).toBeVisible();
await expect(panel.getByText("历史监测回放", { exact: true })).toHaveCount(0);
@@ -137,11 +137,11 @@ test.describe("外围工单测试", () => {
if (request.url().includes("/api/sample/v1")) peripheralRequests.push(request.url());
});
await page.goto("/");
await page.getByRole("button", { name: "外围样板管理" }).click();
await expect(page.getByRole("complementary", { name: "外围工单测试" })).toBeVisible();
await page.getByRole("button", { name: "外围系统", exact: true }).click();
await expect(page.getByRole("complementary", { name: "外围系统" })).toBeVisible();
await expect(
page
.getByRole("complementary", { name: "外围工单测试" })
.getByRole("complementary", { name: "外围系统" })
.getByText("仅用于验证外围工单接口与同步过程,不代表真实现场处置", { exact: true })
.last()
).toBeVisible();
@@ -149,6 +149,129 @@ test.describe("外围工单测试", () => {
expect(peripheralRequests).toEqual([]);
});
test("SCADA 系统按当前项目读取最新记录并设定模拟异常", async ({ page }, testInfo) => {
const latestRequests: string[] = [];
const simulatedWrites: Array<Record<string, unknown>> = [];
const projectId = "324ff7ee-c2d4-5184-a84b-48fb14a2933e";
await page.route("**/runtime-config.js", (route) =>
route.fulfill({
contentType: "text/javascript",
body: `globalThis.__TJWATER_CONFIG__ = ${JSON.stringify({
TJWATER_AUTH_MODE: "disabled",
TJWATER_SERVER_API_BASE_URL: "http://server.test",
TJWATER_PERIPHERAL_SAMPLE_API_BASE_URL: "http://peripheral.test"
})};`
})
);
await page.route("http://peripheral.test/api/sample/v1/**", (route) => {
const request = route.request();
const path = new URL(request.url()).pathname;
if (path.endsWith("/context")) return route.fulfill({ json: {
protocol_version: "tjwater-peripheral-sample@1", origin: "synthetic", project_id: projectId,
user_id: "50000000-0000-4000-8000-000000000001", can_write: true,
scenarios: ["normal"], managed_resources: { work_orders: "available", test_data: "available" },
state_transitions: { pending: [], processing: [], pending_review: [], completed: [] }
} });
if (path.endsWith("/test-data") && request.method() === "GET") {
return route.fulfill({ json: { items: [], next_cursor: null } });
}
if (path.endsWith("/test-data") && request.method() === "POST") {
const body = request.postDataJSON() as Record<string, unknown>;
simulatedWrites.push(body);
return route.fulfill({ status: 201, json: {
...body,
record_id: "50000000-0000-4000-8000-000000000017",
project_id: projectId,
revision: 1,
origin: "synthetic",
created_at: "2026-10-22T02:03:01Z",
updated_at: "2026-10-22T02:03:01Z",
created_by: "tester",
updated_by: "tester"
} });
}
if (path.endsWith("/work-orders")) {
return route.fulfill({ json: { items: [], next_cursor: null } });
}
return route.fulfill({ status: 404, json: {} });
});
await page.route("http://server.test/api/v1/timeseries/scada-readings/latest?**", (route) => {
latestRequests.push(route.request().headers()["x-project-id"] ?? "");
return route.fulfill({
json: {
items: [
{
device_id: "PRESSURE-17",
device_type: "pressure",
measurement_unit: "MPa",
transmission_mode: "realtime",
transmission_frequency: "15min",
observed_at: "2026-10-22T01:02:03Z",
monitored_value: 0.3172,
cleaned_value: 0.315
},
{
device_id: "FLOW-09",
device_type: "pipe_flow",
measurement_unit: "m³/h",
transmission_mode: "realtime",
transmission_frequency: "15min",
observed_at: null,
monitored_value: null,
cleaned_value: null
}
],
total: 2,
limit: 500,
offset: 0,
observed_count: 1,
latest_observed_at: "2026-10-22T01:02:03Z"
}
});
});
await page.goto("/");
await page.getByRole("button", { name: "外围系统", exact: true }).click();
const panel = page.getByRole("complementary", { name: "外围系统" });
await panel.getByRole("tab", { name: "SCADA 系统" }).click();
await expect(panel.getByRole("table", { name: "SCADA 最新数据表" })).toBeVisible();
await expect(panel.getByText("PRESSURE-17", { exact: true })).toBeVisible();
await expect(panel.getByText("0.3172 MPa", { exact: true })).toBeVisible();
await expect(panel.getByText("0.315 MPa", { exact: true })).toBeVisible();
await expect(panel.getByText("FLOW-09", { exact: true })).toBeVisible();
await expect(panel.getByText("无记录", { exact: true })).toBeVisible();
await expect(panel.getByLabel("SCADA 最新数据概览")).toContainText(/1\s*\/\s*2 个设备/);
expect(latestRequests).toEqual(["324ff7ee-c2d4-5184-a84b-48fb14a2933e"]);
await panel.getByRole("textbox", { name: "筛选 SCADA 设备" }).fill("pressure");
await expect(panel.getByText("PRESSURE-17", { exact: true })).toBeVisible();
await expect(panel.getByText("FLOW-09", { exact: true })).toHaveCount(0);
await panel.getByRole("textbox", { name: "筛选 SCADA 设备" }).fill("");
await panel.getByRole("button", { name: "设定读数" }).click();
for (const width of [1280, 375, 320]) {
await page.setViewportSize({ width, height: 900 });
await panel.getByPlaceholder("输入需要演练的异常值").scrollIntoViewIfNeeded();
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(width);
await testInfo.attach(`scada-peripheral-anomaly-${width}`, {
body: await page.screenshot(),
contentType: "image/png"
});
}
await panel.getByPlaceholder("输入需要演练的异常值").fill("0.08");
await panel.getByPlaceholder("例如:压力突降,偏离近一小时稳定区间").fill("压力突降");
await panel.getByRole("button", { name: "保存模拟读数", exact: true }).click();
await expect(panel.getByLabel("SCADA 模拟异常列表")).toContainText("PRESSURE-17");
await expect(panel.getByLabel("SCADA 模拟异常列表")).toContainText("0.08 MPa");
expect(simulatedWrites[0]).toMatchObject({
point_id: "PRESSURE-17",
value: 0.08,
quality: "valid",
notes: "[SCADA_ANOMALY] 压力突降"
});
});
test("管理面板打开时阻止地图接收鼠标行为", async ({ page }) => {
await page.goto("/");
@@ -194,10 +317,10 @@ test.describe("外围工单测试", () => {
.toEqual([]);
};
await page.getByRole("button", { name: "外围样板管理" }).click();
await expect(page.getByRole("complementary", { name: "外围工单测试" })).toBeVisible();
await expectMapInteractionBlocked("外围工单测试");
await page.getByRole("button", { name: "外围样板管理" }).click();
await page.getByRole("button", { name: "外围系统", exact: true }).click();
await expect(page.getByRole("complementary", { name: "外围系统" })).toBeVisible();
await expectMapInteractionBlocked("外围系统");
await page.getByRole("button", { name: "外围系统", exact: true }).click();
await page.getByRole("button", { name: "项目分析成果" }).click();
await expect(page.getByRole("complementary", { name: "项目分析成果" })).toBeVisible();
@@ -241,8 +364,8 @@ test.describe("外围工单测试", () => {
});
await page.goto("/");
await page.getByRole("button", { name: "外围样板管理" }).click();
const panel = page.getByRole("complementary", { name: "外围工单测试" });
await page.getByRole("button", { name: "外围系统" }).click();
const panel = page.getByRole("complementary", { name: "外围系统" });
await expect(panel.getByText("测试工单存储待供给", { exact: true })).toBeVisible();
await expect(panel.getByText("测试数据存储待供给", { exact: true })).toHaveCount(0);
await expect(panel.getByText("历史监测回放", { exact: true })).toHaveCount(0);
@@ -294,8 +417,8 @@ test.describe("外围工单测试", () => {
});
await page.goto("/");
await page.getByRole("button", { name: "外围样板管理" }).click();
const panel = page.getByRole("complementary", { name: "外围工单测试" });
await page.getByRole("button", { name: "外围系统" }).click();
const panel = page.getByRole("complementary", { name: "外围系统" });
await expect(panel.getByText("工单接口测试", { exact: true })).toBeVisible();
await expect(panel.getByLabel("预约工单列表")).toContainText("暂无预约工单");
await expect(panel.getByLabel("投诉工单列表")).toContainText("暂无投诉工单");
@@ -567,8 +690,8 @@ test.describe("外围工单测试", () => {
});
await page.goto("/");
await page.getByRole("button", { name: "外围样板管理" }).click();
const panel = page.getByRole("complementary", { name: "外围工单测试" });
await page.getByRole("button", { name: "外围系统" }).click();
const panel = page.getByRole("complementary", { name: "外围系统" });
await panel.getByRole("button", { name: "投诉与爆管上报" }).click();
await panel.getByRole("textbox", { name: "事件位置" }).fill("演示小区");
await panel.getByRole("textbox", { name: "现场描述" }).fill("多户居民反映晨间用水有异味");
@@ -681,10 +804,10 @@ test.describe("外围工单测试", () => {
test("在 375px 视口内完整呈现面板", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 720 });
await page.goto("/");
const launcher = page.getByRole("button", { name: "外围样板管理" });
const launcher = page.getByRole("button", { name: "外围系统" });
await launcher.focus();
await page.keyboard.press("Enter");
const panel = page.getByRole("complementary", { name: "外围工单测试" });
const panel = page.getByRole("complementary", { name: "外围系统" });
await expect(panel).toBeVisible();
const box = await panel.boundingBox();
expect(box).not.toBeNull();