- 重命名 ContentRules→ContentExpectations, KeyValueExpect→KeyedExpectations - 新增 Raw/Resolved 双层模型:resolve 阶段物化为执行计划,store 持久化 Raw 快照 - HTTP body 按需读取:status/headers 失败或无 body expectation 时不读取 body - 新增 displayValueExpectation() 解包 failure.expected 用户可读展示 - 修复 checkEarlyTimeout 独立 lte/lt 检查,修复 KeyedExpectations JSON Schema - 新增 expect/value.ts(resolve/check/display)、keyed.ts、content.ts、headers.ts、status.ts - 删除旧 normalize.ts/matcher.ts/validate-matcher.ts/key-value.ts - 更新 DEVELOPMENT.md:expect 五层管线表、displayValueExpectation、1.7↔1.10 交叉引用 - 同步 13 个 main specs,归档 refactor-expect-type-model 变更(62/62 tasks)
186 lines
5.9 KiB
TypeScript
186 lines
5.9 KiB
TypeScript
import type { PrimaryTableCellParams, PrimaryTableCol } from "tdesign-react";
|
|
|
|
import { describe, expect, test } from "bun:test";
|
|
|
|
import type { TargetStatus } from "../../../src/shared/api";
|
|
|
|
import { createTargetTableColumns } from "../../../src/web/constants/target-table-columns";
|
|
|
|
interface TableFilter {
|
|
list?: Array<{ label: string; value: string }>;
|
|
type?: string;
|
|
}
|
|
|
|
function getColumn(columns: Array<PrimaryTableCol<TargetStatus>>, colKey: string): PrimaryTableCol<TargetStatus> {
|
|
const column = columns.find((item) => item.colKey === colKey);
|
|
expect(column).toBeDefined();
|
|
return column!;
|
|
}
|
|
|
|
function makeTarget(overrides: Partial<TargetStatus> = {}): TargetStatus {
|
|
return {
|
|
currentStreak: null,
|
|
description: null,
|
|
group: "default",
|
|
id: "1",
|
|
interval: "5s",
|
|
latestCheck: null,
|
|
name: "test",
|
|
recentSamples: [],
|
|
stats: { availability: 100, downChecks: 0, totalChecks: 0, upChecks: 0 },
|
|
target: "https://example.com",
|
|
type: "http",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("createTargetTableColumns", () => {
|
|
test("生成 7 个目标表格列", () => {
|
|
const columns = createTargetTableColumns(["http", "cmd"]);
|
|
|
|
expect(columns.map((column) => column.colKey)).toEqual([
|
|
"latestCheck.matched",
|
|
"name",
|
|
"type",
|
|
"stats.availability",
|
|
"recentSamples",
|
|
"currentStreak",
|
|
"latestCheck.durationMs",
|
|
]);
|
|
});
|
|
|
|
test("根据 checkerTypes 生成类型筛选器", () => {
|
|
const typeColumn = getColumn(createTargetTableColumns(["http", "cmd", "tcp"]), "type");
|
|
const filter = typeColumn.filter as TableFilter;
|
|
|
|
expect(filter.type).toBe("single");
|
|
expect(filter.list).toEqual([
|
|
{ label: "全部", value: "" },
|
|
{ label: "http", value: "http" },
|
|
{ label: "cmd", value: "cmd" },
|
|
{ label: "tcp", value: "tcp" },
|
|
]);
|
|
});
|
|
|
|
test("checkerTypes 为空时只保留全部选项", () => {
|
|
const typeColumn = getColumn(createTargetTableColumns([]), "type");
|
|
const filter = typeColumn.filter as TableFilter;
|
|
|
|
expect(filter.list).toEqual([{ label: "全部", value: "" }]);
|
|
});
|
|
|
|
test("类型列直接渲染原始 type 文本", () => {
|
|
const typeColumn = getColumn(createTargetTableColumns(["tcp"]), "type");
|
|
const renderCell = typeColumn.cell as (params: PrimaryTableCellParams<TargetStatus>) => {
|
|
props: { children: unknown };
|
|
};
|
|
const element = renderCell({
|
|
col: typeColumn,
|
|
colIndex: 2,
|
|
row: makeTarget({ type: "tcp" }),
|
|
rowIndex: 0,
|
|
});
|
|
|
|
expect(element.props.children).toBe("tcp");
|
|
});
|
|
|
|
test("连续状态列渲染 capped 标记", () => {
|
|
const streakColumn = getColumn(createTargetTableColumns(["http"]), "currentStreak");
|
|
const renderCell = streakColumn.cell as (params: PrimaryTableCellParams<TargetStatus>) => {
|
|
props: { children: unknown[] };
|
|
};
|
|
const element = renderCell({
|
|
col: streakColumn,
|
|
colIndex: 5,
|
|
row: makeTarget({ currentStreak: { capped: true, count: 30, up: false } }),
|
|
rowIndex: 0,
|
|
});
|
|
|
|
expect(streakColumn.title).toBe("连续(次)");
|
|
expect(element.props.children.join("")).toBe("▼ 30+");
|
|
});
|
|
|
|
test("延迟列超过 9999ms 时显示上限文案", () => {
|
|
const latencyColumn = getColumn(createTargetTableColumns(["http"]), "latestCheck.durationMs");
|
|
const renderCell = latencyColumn.cell as (params: PrimaryTableCellParams<TargetStatus>) => {
|
|
props: { children: string[]; className: string };
|
|
};
|
|
const element = renderCell({
|
|
col: latencyColumn,
|
|
colIndex: 6,
|
|
row: makeTarget({
|
|
latestCheck: {
|
|
detail: "200",
|
|
durationMs: 12000,
|
|
failure: null,
|
|
matched: true,
|
|
observation: null,
|
|
timestamp: "2026-01-01T00:00:00.000Z",
|
|
},
|
|
}),
|
|
rowIndex: 0,
|
|
});
|
|
|
|
expect(element.props.children).toEqual(["9999+", " ms"]);
|
|
expect(element.props.className).toContain("latency-value");
|
|
});
|
|
|
|
test("延迟列标题为 延迟", () => {
|
|
const latencyColumn = getColumn(createTargetTableColumns(["http"]), "latestCheck.durationMs");
|
|
expect(latencyColumn.title).toBe("延迟");
|
|
});
|
|
|
|
test("延迟列正常值包含 ms 单位", () => {
|
|
const latencyColumn = getColumn(createTargetTableColumns(["http"]), "latestCheck.durationMs");
|
|
const renderCell = latencyColumn.cell as (params: PrimaryTableCellParams<TargetStatus>) => {
|
|
props: { children: string[]; className: string };
|
|
};
|
|
const element = renderCell({
|
|
col: latencyColumn,
|
|
colIndex: 6,
|
|
row: makeTarget({
|
|
latestCheck: {
|
|
detail: "200",
|
|
durationMs: 123,
|
|
failure: null,
|
|
matched: true,
|
|
observation: null,
|
|
timestamp: "2026-01-01T00:00:00.000Z",
|
|
},
|
|
}),
|
|
rowIndex: 0,
|
|
});
|
|
expect(element.props.children).toEqual(["123", " ms"]);
|
|
});
|
|
|
|
test("名称列无排序配置", () => {
|
|
const nameColumn = getColumn(createTargetTableColumns(["http"]), "name");
|
|
expect(nameColumn.sorter).toBeUndefined();
|
|
expect(nameColumn.sortType).toBeUndefined();
|
|
});
|
|
|
|
test("名称列 name 为 null 时显示 id", () => {
|
|
const nameColumn = getColumn(createTargetTableColumns(["http"]), "name");
|
|
const renderCell = nameColumn.cell as (params: PrimaryTableCellParams<TargetStatus>) => string;
|
|
const result = renderCell({
|
|
col: nameColumn,
|
|
colIndex: 1,
|
|
row: makeTarget({ id: "my-api", name: null }),
|
|
rowIndex: 0,
|
|
});
|
|
expect(result).toBe("my-api");
|
|
});
|
|
|
|
test("名称列 name 有值时显示 name", () => {
|
|
const nameColumn = getColumn(createTargetTableColumns(["http"]), "name");
|
|
const renderCell = nameColumn.cell as (params: PrimaryTableCellParams<TargetStatus>) => string;
|
|
const result = renderCell({
|
|
col: nameColumn,
|
|
colIndex: 1,
|
|
row: makeTarget({ id: "my-api", name: "我的 API" }),
|
|
rowIndex: 0,
|
|
});
|
|
expect(result).toBe("我的 API");
|
|
});
|
|
});
|