-
-
Notifications
You must be signed in to change notification settings - Fork 386
feat(leaderboard): 缓存系数列增加 tooltip 与分层上色 #1389
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -124,6 +124,104 @@ describe("LeaderboardView cache coefficient column", () => { | |
| expect(text).toContain("–"); | ||
| }); | ||
|
|
||
| it("shows a tooltip trigger on the cache coefficient column header", async () => { | ||
| fetchMock.mockImplementation(async (input) => { | ||
| const url = String(input); | ||
| if (url.includes("scope=providerCacheHitRate")) { | ||
| return { | ||
| ok: true, | ||
| json: async () => [cacheHitEntry({ providerId: 1, cacheCoefficientBp: 9000 })], | ||
| } as Response; | ||
| } | ||
| return { ok: true, json: async () => [] } as Response; | ||
| }); | ||
|
|
||
| await act(async () => { | ||
| root!.render(<LeaderboardView isAdmin />); | ||
| }); | ||
|
|
||
| const coefficientHeader = Array.from(container!.querySelectorAll("th")).find((th) => | ||
| th.textContent?.includes("columns.cacheCoefficient") | ||
| ); | ||
| expect(coefficientHeader).toBeDefined(); | ||
| const trigger = coefficientHeader!.querySelector('[data-slot="tooltip-trigger"]'); | ||
| expect(trigger).not.toBeNull(); | ||
| }); | ||
|
|
||
| it("does not trigger column sorting when the help icon is clicked", async () => { | ||
| fetchMock.mockImplementation(async (input) => { | ||
| const url = String(input); | ||
| if (url.includes("scope=providerCacheHitRate")) { | ||
| return { | ||
| ok: true, | ||
| json: async () => [ | ||
| cacheHitEntry({ providerId: 1, providerName: "high-first", cacheCoefficientBp: 9500 }), | ||
| cacheHitEntry({ providerId: 2, providerName: "low-second", cacheCoefficientBp: 5000 }), | ||
| ], | ||
| } as Response; | ||
| } | ||
| return { ok: true, json: async () => [] } as Response; | ||
| }); | ||
|
Comment on lines
+151
to
+164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n 'scope=(providerCacheHitRate|userCacheHitRate)' \
tests/unit/dashboard/leaderboard-view-cache-coefficient.test.tsxRepository: ding113/claude-code-hub Length of output: 484 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "file_stats"
wc -l tests/unit/dashboard/leaderboard-view-cache-coefficient.test.tsx
echo
echo "outline"
ast-grep outline tests/unit/dashboard/leaderboard-view-cache-coefficient.test.tsx || true
echo
echo "scope/usages"
rg -n 'userCacheHitRate|providerCacheHitRate|scope=|cacheCoefficient|cacheCoefficientBp|Leaderboard' tests/unit/dashboard/leaderboard-view-cache-coefficient.test.tsx
echo
echo "sections"
sed -n '1,220p' tests/unit/dashboard/leaderboard-view-cache-coefficient.test.tsxRepository: ding113/claude-code-hub Length of output: 10217 为 当前用例只使用 🤖 Prompt for AI Agents |
||
|
|
||
| await act(async () => { | ||
| root!.render(<LeaderboardView isAdmin />); | ||
| }); | ||
|
|
||
| const coefficientHeader = Array.from(container!.querySelectorAll("th")).find((th) => | ||
| th.textContent?.includes("columns.cacheCoefficient") | ||
| ); | ||
| const trigger = coefficientHeader!.querySelector('[data-slot="tooltip-trigger"]'); | ||
| expect(trigger).not.toBeNull(); | ||
|
|
||
| await act(async () => { | ||
| trigger!.dispatchEvent(new MouseEvent("click", { bubbles: true })); | ||
| }); | ||
|
|
||
| // 行顺序保持默认(未触发升序排序,否则 low-second 会排到第一行) | ||
| const bodyText = container!.querySelector("tbody")?.textContent ?? ""; | ||
| expect(bodyText.indexOf("high-first")).toBeLessThan(bodyText.indexOf("low-second")); | ||
| }); | ||
|
|
||
| it("colors the coefficient by tier: >=0.9 green, >=0.8 yellow, else orange", async () => { | ||
| fetchMock.mockImplementation(async (input) => { | ||
| const url = String(input); | ||
| if (url.includes("scope=providerCacheHitRate")) { | ||
| return { | ||
| ok: true, | ||
| json: async () => [ | ||
| cacheHitEntry({ providerId: 1, providerName: "excellent", cacheCoefficientBp: 9500 }), | ||
| cacheHitEntry({ | ||
| providerId: 2, | ||
| providerName: "edge-excellent", | ||
| cacheCoefficientBp: 9000, | ||
| }), | ||
| cacheHitEntry({ providerId: 3, providerName: "good", cacheCoefficientBp: 8600 }), | ||
| cacheHitEntry({ providerId: 4, providerName: "edge-good", cacheCoefficientBp: 8000 }), | ||
| cacheHitEntry({ providerId: 5, providerName: "poor", cacheCoefficientBp: 5000 }), | ||
| cacheHitEntry({ providerId: 6, providerName: "missing", cacheCoefficientBp: null }), | ||
| ], | ||
| } as Response; | ||
| } | ||
| return { ok: true, json: async () => [] } as Response; | ||
| }); | ||
|
|
||
| await act(async () => { | ||
| root!.render(<LeaderboardView isAdmin />); | ||
| }); | ||
|
|
||
| const hasColoredValue = (selector: string, text: string) => | ||
| Array.from(container!.querySelectorAll(selector)).some((el) => el.textContent === text); | ||
| expect(hasColoredValue("span.text-green-600", "0.95")).toBe(true); | ||
| // 边界值:0.90 仍属优秀档 | ||
| expect(hasColoredValue("span.text-green-600", "0.90")).toBe(true); | ||
| expect(hasColoredValue("span.text-yellow-600", "0.86")).toBe(true); | ||
| // 边界值:0.80 仍属良好档 | ||
| expect(hasColoredValue("span.text-yellow-600", "0.80")).toBe(true); | ||
| expect(hasColoredValue("span.text-orange-600", "0.50")).toBe(true); | ||
| // 缺失值:muted 样式展示占位符 | ||
| expect(hasColoredValue("span.text-muted-foreground", "–")).toBe(true); | ||
| }); | ||
|
|
||
| it("renders the coefficient column on the provider usage board too", async () => { | ||
| searchParamsState.value = new URLSearchParams("scope=provider"); | ||
| fetchMock.mockImplementation(async (input) => { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.