1
0

feat: 增强 expect 规则系统,支持多种 body 校验方法和操作符

- 新增 body 分组校验:contains、regex、json(JSONPath)、css(CSS选择器)、xpath
- 新增操作符系统:equals、contains、match、empty、exists、gte、lte、gt、lt
- 新增 headers 响应头校验
- 引入 cheerio、xpath、@xmldom/xmldom 依赖
- BREAKING: expect.bodyContains 迁移至 expect.body.contains
This commit is contained in:
2026-05-10 00:10:42 +08:00
parent 57d3a5cfb4
commit 599d973cbd
22 changed files with 923 additions and 80 deletions

View File

@@ -16,11 +16,7 @@ export function App() {
<p className="dashboard-subtitle">HTTP </p>
</header>
{error && (
<div className="error-banner">
: {error}
</div>
)}
{error && <div className="error-banner">: {error}</div>}
<SummaryCards summary={summary} loading={summaryLoading} />
<TargetTable targets={targets} loading={targetsLoading} />

View File

@@ -40,9 +40,7 @@ export function TargetDetail({ target }: TargetDetailProps) {
<div className="detail-stats">
<div className="detail-stat">
<span className="detail-stat-label"></span>
<span className={`detail-stat-value ${isUp ? "text-up" : "text-down"}`}>
{isUp ? "UP" : "DOWN"}
</span>
<span className={`detail-stat-value ${isUp ? "text-up" : "text-down"}`}>{isUp ? "UP" : "DOWN"}</span>
</div>
<div className="detail-stat">
<span className="detail-stat-label"></span>
@@ -80,18 +78,10 @@ export function TargetDetail({ target }: TargetDetailProps) {
<span className={`history-status ${item.success && item.matched ? "text-up" : "text-down"}`}>
{item.success && item.matched ? "UP" : "DOWN"}
</span>
<span className="history-time">
{new Date(item.timestamp).toLocaleString("zh-CN")}
</span>
{item.statusCode && (
<span className="history-code">{item.statusCode}</span>
)}
{item.latencyMs !== null && (
<span className="history-latency">{Math.round(item.latencyMs)}ms</span>
)}
{item.error && (
<span className="history-error">{item.error}</span>
)}
<span className="history-time">{new Date(item.timestamp).toLocaleString("zh-CN")}</span>
{item.statusCode && <span className="history-code">{item.statusCode}</span>}
{item.latencyMs !== null && <span className="history-latency">{Math.round(item.latencyMs)}ms</span>}
{item.error && <span className="history-error">{item.error}</span>}
</div>
))}
</div>

View File

@@ -26,8 +26,20 @@ export function TrendChart({ data, loading }: TrendChartProps) {
<LineChart data={chartData}>
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
<XAxis dataKey="hour" tick={{ fontSize: 12 }} stroke="#94a3b8" />
<YAxis yAxisId="latency" tick={{ fontSize: 12 }} stroke="#94a3b8" label={{ value: "ms", position: "insideTopRight", fontSize: 11 }} />
<YAxis yAxisId="availability" orientation="right" domain={[0, 100]} tick={{ fontSize: 12 }} stroke="#94a3b8" label={{ value: "%", position: "insideTopLeft", fontSize: 11 }} />
<YAxis
yAxisId="latency"
tick={{ fontSize: 12 }}
stroke="#94a3b8"
label={{ value: "ms", position: "insideTopRight", fontSize: 11 }}
/>
<YAxis
yAxisId="availability"
orientation="right"
domain={[0, 100]}
tick={{ fontSize: 12 }}
stroke="#94a3b8"
label={{ value: "%", position: "insideTopLeft", fontSize: 11 }}
/>
<Tooltip
formatter={(value: unknown, name: unknown) => {
const num = Number(value);
@@ -37,8 +49,24 @@ export function TrendChart({ data, loading }: TrendChartProps) {
return [String(value), nameStr];
}}
/>
<Line yAxisId="latency" type="monotone" dataKey="avgLatencyMs" stroke="#356dd2" strokeWidth={2} dot={false} name="avgLatencyMs" />
<Line yAxisId="availability" type="monotone" dataKey="availability" stroke="#1fbf75" strokeWidth={2} dot={false} name="availability" />
<Line
yAxisId="latency"
type="monotone"
dataKey="avgLatencyMs"
stroke="#356dd2"
strokeWidth={2}
dot={false}
name="avgLatencyMs"
/>
<Line
yAxisId="availability"
type="monotone"
dataKey="availability"
stroke="#1fbf75"
strokeWidth={2}
dot={false}
name="availability"
/>
</LineChart>
</ResponsiveContainer>
</div>