niceeval 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/INDEX.md +23 -23
  2. package/README.zh.md +6 -6
  3. package/dist/agents/types.d.ts +69 -7
  4. package/dist/context/types.d.ts +32 -12
  5. package/dist/i18n/en.d.ts +54 -0
  6. package/dist/i18n/zh-CN.d.ts +57 -3
  7. package/dist/o11y/types.d.ts +16 -2
  8. package/dist/report/aggregate.d.ts +32 -24
  9. package/dist/report/aggregate.js +158 -50
  10. package/dist/report/built-in/index.d.ts +2 -0
  11. package/dist/report/built-in/index.js +8 -0
  12. package/dist/report/components.d.ts +93 -160
  13. package/dist/report/components.js +377 -114
  14. package/dist/report/compute.d.ts +87 -81
  15. package/dist/report/compute.js +597 -417
  16. package/dist/report/flag.d.ts +32 -6
  17. package/dist/report/flag.js +92 -4
  18. package/dist/report/format.d.ts +19 -11
  19. package/dist/report/format.js +30 -13
  20. package/dist/report/index.d.ts +16 -16
  21. package/dist/report/index.js +20 -21
  22. package/dist/report/load.js +3 -2
  23. package/dist/report/locale.d.ts +57 -33
  24. package/dist/report/locale.js +122 -56
  25. package/dist/report/metrics.d.ts +23 -4
  26. package/dist/report/metrics.js +110 -25
  27. package/dist/report/primitives.d.ts +48 -15
  28. package/dist/report/primitives.js +135 -26
  29. package/dist/report/react/AttemptList.d.ts +9 -7
  30. package/dist/report/react/AttemptList.js +17 -10
  31. package/dist/report/react/DeltaTable.js +19 -18
  32. package/dist/report/react/EvalList.d.ts +4 -4
  33. package/dist/report/react/EvalList.js +0 -0
  34. package/dist/report/react/ExperimentComparison.d.ts +10 -0
  35. package/dist/report/react/ExperimentComparison.js +12 -0
  36. package/dist/report/react/ExperimentList.d.ts +4 -3
  37. package/dist/report/react/ExperimentList.js +17 -18
  38. package/dist/report/react/MetricBars.js +5 -4
  39. package/dist/report/react/MetricLine.js +12 -5
  40. package/dist/report/react/MetricMatrix.js +1 -1
  41. package/dist/report/react/MetricScatter.js +59 -28
  42. package/dist/report/react/MetricTable.js +2 -12
  43. package/dist/report/react/ScopeSummary.d.ts +10 -0
  44. package/dist/report/react/ScopeSummary.js +17 -0
  45. package/dist/report/react/Scoreboard.js +6 -6
  46. package/dist/report/react/cell.js +2 -2
  47. package/dist/report/react/chart-math.d.ts +23 -6
  48. package/dist/report/react/chart-math.js +71 -19
  49. package/dist/report/react/fixtures.d.ts +5 -9
  50. package/dist/report/react/fixtures.js +110 -147
  51. package/dist/report/react/index.d.ts +15 -5
  52. package/dist/report/react/index.js +18 -7
  53. package/dist/report/report.d.ts +137 -16
  54. package/dist/report/report.js +259 -28
  55. package/dist/report/text/faces.d.ts +17 -19
  56. package/dist/report/text/faces.js +253 -184
  57. package/dist/report/text/plot.js +1 -1
  58. package/dist/report/text/table.js +38 -7
  59. package/dist/report/tree.d.ts +90 -40
  60. package/dist/report/tree.js +252 -94
  61. package/dist/report/types.d.ts +247 -284
  62. package/dist/report/types.js +4 -3
  63. package/dist/report/web.d.ts +21 -5
  64. package/dist/report/web.js +42 -16
  65. package/dist/results/select.d.ts +38 -16
  66. package/dist/results/select.js +73 -25
  67. package/dist/results/types.d.ts +49 -14
  68. package/dist/runner/feedback/sink.d.ts +110 -0
  69. package/dist/runner/types.d.ts +513 -22
  70. package/dist/sandbox/docker.d.ts +23 -2
  71. package/dist/sandbox/e2b.d.ts +15 -1
  72. package/dist/sandbox/errors.d.ts +30 -3
  73. package/dist/sandbox/io-retry.d.ts +17 -0
  74. package/dist/sandbox/registry.d.ts +2 -0
  75. package/dist/sandbox/resolve.d.ts +18 -5
  76. package/dist/sandbox/retry.d.ts +11 -1
  77. package/dist/sandbox/types.d.ts +39 -5
  78. package/dist/sandbox/vercel.d.ts +7 -1
  79. package/dist/scoring/coverage.d.ts +30 -0
  80. package/dist/scoring/display.d.ts +21 -0
  81. package/dist/scoring/display.js +120 -0
  82. package/dist/scoring/types.d.ts +103 -20
  83. package/dist/shared/aggregate.d.ts +4 -2
  84. package/dist/shared/aggregate.js +8 -7
  85. package/dist/shared/types.d.ts +28 -0
  86. package/dist/tty-line.d.ts +0 -4
  87. package/dist/util.d.ts +23 -0
  88. package/docs-site/zh/README.md +44 -0
  89. package/docs-site/zh/examples/ai-agent-application.mdx +63 -0
  90. package/docs-site/zh/examples/coding-agent-extensions.mdx +57 -0
  91. package/docs-site/zh/examples/index.mdx +50 -0
  92. package/docs-site/zh/{concepts → explanation}/adapter.mdx +31 -13
  93. package/docs-site/zh/{concepts → explanation}/assert.mdx +7 -7
  94. package/docs-site/zh/{concepts → explanation}/drive.mdx +8 -8
  95. package/docs-site/zh/{concepts → explanation}/evals.mdx +4 -4
  96. package/docs-site/zh/{concepts → explanation}/experiment.mdx +8 -8
  97. package/docs-site/zh/{concepts → explanation}/hitl.mdx +8 -8
  98. package/docs-site/zh/{concepts → explanation}/judge.mdx +5 -5
  99. package/docs-site/zh/{concepts → explanation}/overview.mdx +11 -11
  100. package/docs-site/zh/{guides → explanation}/runner.mdx +18 -8
  101. package/docs-site/zh/{concepts → explanation}/tier.mdx +6 -6
  102. package/docs-site/zh/{guides → how-to}/agent-feedback-loop.mdx +35 -33
  103. package/docs-site/zh/{guides → how-to}/authoring.mdx +35 -2
  104. package/docs-site/zh/{guides → how-to}/ci-integration.mdx +23 -12
  105. package/docs-site/zh/{guides → how-to}/connect-otel.mdx +6 -6
  106. package/docs-site/zh/{guides → how-to}/connect-your-agent.mdx +47 -21
  107. package/docs-site/zh/{guides → how-to}/custom-reports.mdx +34 -39
  108. package/docs-site/zh/{guides → how-to}/dataset-fanout.mdx +25 -3
  109. package/docs-site/zh/{guides → how-to}/experiments.mdx +12 -5
  110. package/docs-site/zh/how-to/publish-report.mdx +105 -0
  111. package/docs-site/zh/{guides → how-to}/reporters.mdx +2 -2
  112. package/docs-site/zh/{guides → how-to}/sandbox-agent.mdx +56 -7
  113. package/docs-site/zh/how-to/sandbox-providers.mdx +350 -0
  114. package/docs-site/zh/{guides → how-to}/scoring-guide.mdx +4 -4
  115. package/docs-site/zh/{guides → how-to}/viewing-results.mdx +82 -39
  116. package/docs-site/zh/{guides → how-to}/write-experiment.mdx +6 -4
  117. package/docs-site/zh/{guides → how-to}/write-send.mdx +30 -14
  118. package/docs-site/zh/index.mdx +24 -26
  119. package/docs-site/zh/introduction.mdx +8 -8
  120. package/docs-site/zh/reference/builtin-agents.mdx +32 -5
  121. package/docs-site/zh/reference/capabilities.mdx +8 -8
  122. package/docs-site/zh/reference/cli.mdx +40 -12
  123. package/docs-site/zh/reference/define-agent.mdx +58 -5
  124. package/docs-site/zh/reference/define-config.mdx +1 -1
  125. package/docs-site/zh/reference/define-eval.mdx +42 -9
  126. package/docs-site/zh/reference/events.mdx +3 -3
  127. package/docs-site/zh/reference/expect.mdx +26 -1
  128. package/docs-site/zh/{guides → reference}/official-adapters.mdx +32 -8
  129. package/docs-site/zh/{guides → reference}/report-components.mdx +45 -33
  130. package/docs-site/zh/{guides → reference}/results-data.mdx +21 -13
  131. package/docs-site/zh/troubleshooting/debug-sandbox.mdx +57 -0
  132. package/docs-site/zh/troubleshooting/debugging.mdx +212 -0
  133. package/docs-site/zh/{quickstart.mdx → tutorials/quickstart.mdx} +5 -17
  134. package/package.json +10 -2
  135. package/src/agents/ai-sdk-otel.test.ts +1 -0
  136. package/src/agents/ai-sdk.test.ts +3 -0
  137. package/src/agents/ai-sdk.ts +3 -0
  138. package/src/agents/bub-install-spec.test.ts +34 -0
  139. package/src/agents/bub-install-spec.ts +32 -0
  140. package/src/agents/bub.ts +31 -32
  141. package/src/agents/claude-code.test.ts +130 -9
  142. package/src/agents/claude-code.ts +76 -4
  143. package/src/agents/codex.test.ts +189 -40
  144. package/src/agents/codex.ts +155 -14
  145. package/src/agents/coding-cli-versions.test.ts +15 -0
  146. package/src/agents/coding-cli-versions.ts +3 -0
  147. package/src/agents/index.ts +13 -2
  148. package/src/agents/langgraph.test.ts +204 -0
  149. package/src/agents/langgraph.ts +495 -0
  150. package/src/agents/marketplace.ts +85 -0
  151. package/src/agents/native-config.test.ts +179 -0
  152. package/src/agents/native-config.ts +267 -0
  153. package/src/agents/openai-compat.test.ts +1 -0
  154. package/src/agents/openai-compat.ts +1 -1
  155. package/src/agents/openclaw.test.ts +31 -0
  156. package/src/agents/openclaw.ts +171 -0
  157. package/src/agents/plugin-config.test.ts +1 -0
  158. package/src/agents/sdk-streams.test.ts +79 -0
  159. package/src/agents/sdk-streams.ts +55 -10
  160. package/src/agents/skills.test.ts +1 -0
  161. package/src/agents/streaming.test.ts +3 -9
  162. package/src/agents/streaming.ts +2 -2
  163. package/src/agents/types.ts +71 -8
  164. package/src/agents/ui-message-stream.test.ts +3 -0
  165. package/src/cli.ts +446 -124
  166. package/src/context/context.test.ts +51 -12
  167. package/src/context/context.ts +162 -30
  168. package/src/context/session.test.ts +2 -1
  169. package/src/context/session.ts +115 -7
  170. package/src/context/types.ts +30 -12
  171. package/src/define.test.ts +13 -8
  172. package/src/define.ts +25 -4
  173. package/src/expect/index.ts +53 -23
  174. package/src/i18n/en.ts +81 -17
  175. package/src/i18n/zh-CN.ts +80 -17
  176. package/src/o11y/cost.test.ts +1 -0
  177. package/src/o11y/execution-tree.test.ts +1 -20
  178. package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
  179. package/src/o11y/otlp/parse.test.ts +1 -0
  180. package/src/o11y/otlp/turn-otel.test.ts +1 -0
  181. package/src/o11y/parsers/bub.test.ts +1 -0
  182. package/src/o11y/parsers/claude-code.test.ts +1 -34
  183. package/src/o11y/parsers/openclaw.test.ts +154 -0
  184. package/src/o11y/parsers/openclaw.ts +310 -0
  185. package/src/o11y/prices.json +746 -311
  186. package/src/o11y/tool-names.test.ts +1 -0
  187. package/src/o11y/types.ts +16 -2
  188. package/src/report/aggregate.ts +178 -61
  189. package/src/report/built-in/index.tsx +9 -0
  190. package/src/report/components.tsx +625 -279
  191. package/src/report/compute.ts +723 -491
  192. package/src/report/dual-render.test.tsx +741 -1024
  193. package/src/report/flag.ts +104 -12
  194. package/src/report/format.ts +32 -12
  195. package/src/report/index.ts +119 -46
  196. package/src/report/load.ts +3 -2
  197. package/src/report/locale.ts +136 -65
  198. package/src/report/metrics.ts +108 -25
  199. package/src/report/primitives.tsx +196 -45
  200. package/src/report/react/AttemptList.tsx +30 -43
  201. package/src/report/react/DeltaTable.tsx +63 -45
  202. package/src/report/react/EvalList.tsx +0 -0
  203. package/src/report/react/ExperimentComparison.tsx +73 -0
  204. package/src/report/react/ExperimentList.tsx +50 -32
  205. package/src/report/react/MetricBars.tsx +5 -4
  206. package/src/report/react/MetricLine.tsx +13 -8
  207. package/src/report/react/MetricMatrix.tsx +2 -2
  208. package/src/report/react/MetricScatter.tsx +86 -34
  209. package/src/report/react/MetricTable.tsx +4 -76
  210. package/src/report/react/ScopeSummary.tsx +86 -0
  211. package/src/report/react/Scoreboard.tsx +28 -10
  212. package/src/report/react/cell.tsx +2 -2
  213. package/src/report/react/chart-math.test.ts +85 -0
  214. package/src/report/react/chart-math.ts +101 -22
  215. package/src/report/react/enhance.js +89 -5
  216. package/src/report/react/fixtures.ts +114 -154
  217. package/src/report/react/index.tsx +24 -39
  218. package/src/report/react/render.test.tsx +138 -158
  219. package/src/report/react/styles.css +243 -82
  220. package/src/report/report.test.ts +779 -841
  221. package/src/report/report.ts +423 -41
  222. package/src/report/text/faces.ts +290 -193
  223. package/src/report/text/plot.ts +1 -1
  224. package/src/report/text/table.ts +44 -7
  225. package/src/report/tree.ts +362 -104
  226. package/src/report/types.ts +261 -271
  227. package/src/report/web.ts +63 -20
  228. package/src/results/annotated-source.test.ts +62 -9
  229. package/src/results/annotated-source.ts +64 -6
  230. package/src/results/attempt-evidence.test.ts +13 -11
  231. package/src/results/attempt-evidence.ts +20 -13
  232. package/src/results/attempt-source.ts +6 -3
  233. package/src/results/copy.ts +150 -60
  234. package/src/results/host-equivalence.test.ts +34 -20
  235. package/src/results/index.ts +12 -4
  236. package/src/results/locator.test.ts +1 -22
  237. package/src/results/open.ts +15 -5
  238. package/src/results/publish.ts +149 -0
  239. package/src/results/results.test.ts +89 -54
  240. package/src/results/select.ts +104 -34
  241. package/src/results/truncate.ts +90 -0
  242. package/src/results/types.ts +43 -14
  243. package/src/results/writer.ts +31 -13
  244. package/src/runner/attempt.test.ts +138 -7
  245. package/src/runner/attempt.ts +603 -104
  246. package/src/runner/discover.test.ts +47 -0
  247. package/src/runner/discover.ts +36 -2
  248. package/src/runner/eval-source.test.ts +1 -27
  249. package/src/runner/feedback/agent.test.ts +504 -0
  250. package/src/runner/feedback/agent.ts +409 -0
  251. package/src/runner/feedback/ci.test.ts +562 -0
  252. package/src/runner/feedback/ci.ts +401 -0
  253. package/src/runner/feedback/coordinator.test.ts +317 -0
  254. package/src/runner/feedback/coordinator.ts +397 -0
  255. package/src/runner/feedback/failure.ts +40 -0
  256. package/src/runner/feedback/human.test.ts +616 -0
  257. package/src/runner/feedback/human.ts +535 -0
  258. package/src/runner/feedback/index.ts +66 -0
  259. package/src/runner/feedback/io.ts +78 -0
  260. package/src/runner/feedback/profile.test.ts +50 -0
  261. package/src/runner/feedback/profile.ts +58 -0
  262. package/src/runner/feedback/reducer.test.ts +395 -0
  263. package/src/runner/feedback/reducer.ts +260 -0
  264. package/src/runner/feedback/renderer.ts +82 -0
  265. package/src/runner/feedback/sink.ts +203 -0
  266. package/src/runner/feedback/testing.ts +106 -0
  267. package/src/runner/ledger.test.ts +230 -0
  268. package/src/runner/ledger.ts +329 -0
  269. package/src/runner/report.test.ts +128 -3
  270. package/src/runner/report.ts +33 -9
  271. package/src/runner/reporters/artifacts.ts +8 -2
  272. package/src/runner/reporters/braintrust.test.ts +8 -7
  273. package/src/runner/reporters/braintrust.ts +9 -2
  274. package/src/runner/reporters/index.ts +2 -2
  275. package/src/runner/reporters/json.test.ts +162 -0
  276. package/src/runner/reporters/json.ts +35 -8
  277. package/src/runner/reporters/shared.ts +1 -5
  278. package/src/runner/run.test.ts +760 -3
  279. package/src/runner/run.ts +243 -37
  280. package/src/runner/sandbox-prep.ts +3 -42
  281. package/src/runner/timing.ts +158 -0
  282. package/src/runner/types.ts +518 -22
  283. package/src/sandbox/checkpoint.test.ts +55 -0
  284. package/src/sandbox/checkpoint.ts +29 -8
  285. package/src/sandbox/cli-commands.ts +407 -0
  286. package/src/sandbox/docker.ts +115 -16
  287. package/src/sandbox/e2b-agent-template.test.ts +56 -0
  288. package/src/sandbox/e2b-agent-template.ts +94 -0
  289. package/src/sandbox/e2b.ts +74 -9
  290. package/src/sandbox/errors.ts +111 -4
  291. package/src/sandbox/index.ts +2 -0
  292. package/src/sandbox/io-retry.test.ts +58 -0
  293. package/src/sandbox/io-retry.ts +45 -0
  294. package/src/sandbox/keep-registry.test.ts +86 -0
  295. package/src/sandbox/keep-registry.ts +142 -0
  296. package/src/sandbox/keep.ts +178 -0
  297. package/src/sandbox/paths.test.ts +1 -0
  298. package/src/sandbox/paths.ts +19 -8
  299. package/src/sandbox/registry.ts +20 -3
  300. package/src/sandbox/resolve.ts +76 -11
  301. package/src/sandbox/retry.test.ts +70 -0
  302. package/src/sandbox/retry.ts +46 -4
  303. package/src/sandbox/types.ts +44 -6
  304. package/src/sandbox/vercel.ts +43 -20
  305. package/src/scoring/collector.ts +60 -17
  306. package/src/scoring/coverage.ts +95 -0
  307. package/src/scoring/diff.ts +81 -0
  308. package/src/scoring/display.test.ts +121 -0
  309. package/src/scoring/display.ts +133 -0
  310. package/src/scoring/evidence.test.ts +189 -0
  311. package/src/scoring/judge.test.ts +142 -0
  312. package/src/scoring/judge.ts +15 -18
  313. package/src/scoring/scoped.ts +217 -50
  314. package/src/scoring/types.ts +117 -20
  315. package/src/scoring/verdict.ts +16 -4
  316. package/src/shared/aggregate.ts +8 -6
  317. package/src/shared/types.ts +31 -0
  318. package/src/show/compose.ts +50 -67
  319. package/src/show/index.ts +127 -56
  320. package/src/show/render.ts +662 -131
  321. package/src/show/report-host.test.ts +188 -0
  322. package/src/show/report-host.ts +375 -0
  323. package/src/show/show.test.ts +320 -54
  324. package/src/tty-line.ts +8 -26
  325. package/src/util.test.ts +1 -0
  326. package/src/util.ts +41 -0
  327. package/src/view/app/App.test.tsx +69 -0
  328. package/src/view/app/App.tsx +144 -48
  329. package/src/view/app/components/AttemptModal.tsx +423 -11
  330. package/src/view/app/components/CodeView.tsx +41 -14
  331. package/src/view/app/components/CopyControls.tsx +2 -2
  332. package/src/view/app/i18n.ts +37 -17
  333. package/src/view/app/lib/attempt-route.test.ts +1 -0
  334. package/src/view/app/lib/verdict.ts +7 -9
  335. package/src/view/app/main.tsx +13 -8
  336. package/src/view/app/pages/{RunsPage.tsx → AttemptsPage.tsx} +6 -6
  337. package/src/view/app/types.ts +4 -1
  338. package/src/view/artifact-serving.test.ts +2 -1
  339. package/src/view/client-dist/app.css +1 -1
  340. package/src/view/client-dist/app.js +17 -17
  341. package/src/view/data.test.ts +10 -3
  342. package/src/view/data.ts +155 -49
  343. package/src/view/index.ts +56 -41
  344. package/src/view/server.ts +37 -15
  345. package/src/view/shared/types.ts +34 -5
  346. package/src/view/styles.css +227 -0
  347. package/src/view/view-report.test.ts +167 -62
  348. package/dist/report/built-ins/experiment-comparison.d.ts +0 -1
  349. package/dist/report/built-ins/experiment-comparison.js +0 -13
  350. package/dist/report/built-ins/index.d.ts +0 -1
  351. package/dist/report/built-ins/index.js +0 -2
  352. package/dist/report/react/GroupSummary.d.ts +0 -8
  353. package/dist/report/react/GroupSummary.js +0 -8
  354. package/dist/report/react/RunOverview.d.ts +0 -8
  355. package/dist/report/react/RunOverview.js +0 -12
  356. package/docs-site/zh/example/ai-agent-application.mdx +0 -152
  357. package/docs-site/zh/example/claude-code-codex-plugin.mdx +0 -167
  358. package/docs-site/zh/example/claude-code-codex-skill.mdx +0 -152
  359. package/docs-site/zh/example/showcase.mdx +0 -39
  360. package/docs-site/zh/guides/publish-report.mdx +0 -91
  361. package/docs-site/zh/guides/sandbox-providers.mdx +0 -102
  362. package/src/report/built-in-user-parity.test.tsx +0 -640
  363. package/src/report/built-ins/experiment-comparison.tsx +0 -19
  364. package/src/report/built-ins/index.ts +0 -2
  365. package/src/report/react/GroupSummary.tsx +0 -66
  366. package/src/report/react/RunOverview.tsx +0 -109
  367. package/src/runner/reporters/console.ts +0 -70
  368. package/src/runner/reporters/live.test.ts +0 -56
  369. package/src/runner/reporters/live.ts +0 -247
  370. package/src/runner/reporters/quiet.test.ts +0 -66
  371. package/src/runner/reporters/quiet.ts +0 -49
  372. package/src/runner/reporters/table.ts +0 -277
  373. /package/docs-site/zh/{example/tier1-ai-sdk-v7.mdx → examples/integrations/ai-sdk-v7.mdx} +0 -0
  374. /package/docs-site/zh/{example/tier1-claude-sdk.mdx → examples/integrations/claude-sdk.mdx} +0 -0
  375. /package/docs-site/zh/{example/tier1-codex-sdk.mdx → examples/integrations/codex-sdk.mdx} +0 -0
  376. /package/docs-site/zh/{example/tier1-langgraph.mdx → examples/integrations/langgraph.mdx} +0 -0
  377. /package/docs-site/zh/{example/tier1-pi-sdk.mdx → examples/integrations/pi-sdk.mdx} +0 -0
  378. /package/docs-site/zh/{guides → how-to}/fixtures.mdx +0 -0
@@ -1,11 +1,9 @@
1
+ // cases: docs/engineering/unit-tests/reports/cases.md
1
2
  // 「不 hydrate 也完整」的验收测试:每个组件过 renderToStaticMarkup,
2
3
  // 断言纯静态 HTML 里就有全部关键内容——数字、覆盖率角标、缺数据文案、
3
4
  // 散点的 SVG 与系列名、truncated 行、attemptHref 链接。
4
- // 另外锁两条契约:源码零 hooks;维度键跨组件同色。
5
+ // 另外锁一条契约:维度键跨组件同色。
5
6
 
6
- import { readFileSync, readdirSync } from "node:fs";
7
- import { dirname, join } from "node:path";
8
- import { fileURLToPath } from "node:url";
9
7
  import { renderToStaticMarkup } from "react-dom/server";
10
8
  import { describe, expect, it } from "vitest";
11
9
  import type { AttemptLocator } from "../../results/locator.ts";
@@ -18,8 +16,8 @@ import {
18
16
  MetricMatrix,
19
17
  MetricScatter,
20
18
  MetricTable,
21
- RunOverview,
22
19
  Scoreboard,
20
+ ScopeSummary,
23
21
  } from "./index.tsx";
24
22
  import { colorClassForKey, seriesClassForKey } from "./colors.ts";
25
23
  import {
@@ -28,62 +26,53 @@ import {
28
26
  evalListItems,
29
27
  experimentListItems,
30
28
  matrixData,
31
- overviewData,
32
- overviewWithCost,
33
29
  scatterData,
30
+ scopeSummaryData,
34
31
  scoreboardData,
35
32
  tableData,
36
- tableDataWithMeta,
37
33
  } from "./fixtures.ts";
38
34
 
39
35
  const attemptHref = (locator: AttemptLocator) => `/attempts/${locator}`;
40
36
 
41
- describe("RunOverview", () => {
42
- const html = renderToStaticMarkup(<RunOverview data={overviewData} />);
37
+ describe("ScopeSummary", () => {
38
+ const html = renderToStaticMarkup(<ScopeSummary data={scopeSummaryData} />);
43
39
 
44
- it("KPI 条:快照数、题数、attempts、通过率、耗时", () => {
45
- expect(html).toContain("nre-overview");
46
- expect(html).toContain("<dd>12</dd>"); // 题目
47
- expect(html).toContain("<dd>48</dd>"); // attempts
48
- // totals.passRate.display 原样渲染(两级聚合 70%),不是从 passed/failed/errored
49
- // 现场重算的 78%(36/(36+8+2))—— 两个数刻意不同,证明组件没有偷懒重算
50
- expect(html).toContain("70%");
51
- expect(html).not.toContain("78%");
52
- expect(html).toContain("46/48"); // samples < total:覆盖率角标(2 个 skipped attempt 不进桶)
53
- expect(html).toContain("4m 21s"); // 261000ms
40
+ it("KPI:experiment/eval/attempt 数、成功率与总成本原样渲染(不从计票重算)", () => {
41
+ expect(html).toContain("nre-scope-summary");
42
+ expect(html).toContain("<dd>2</dd>"); // experiments
43
+ expect(html).toContain("<dd>6</dd>"); // evals
44
+ expect(html).toContain("<dd>9</dd>"); // attempts
45
+ // endToEndPassRate.display 原样渲染(两级聚合 60%),不是 eval 计票 3/5 = 60% 巧合之外的重算;
46
+ // attempt 计票 4/9 ≈ 44% 也不该出现
47
+ expect(html).toContain("60%");
48
+ expect(html).not.toContain("44%");
49
+ expect(html).toContain("$1.50");
54
50
  });
55
51
 
56
- it("costUSD 全缺 = null:显示缺数据,不编 $0", () => {
57
- expect(html).toContain("no data");
58
- expect(html).not.toContain("$0");
59
- const withCost = renderToStaticMarkup(<RunOverview data={overviewWithCost} />);
60
- expect(withCost).toContain("$1.23");
52
+ it("votes 默认 eval 级计票;votes='attempt' 切换显示但 data 不变", () => {
53
+ expect(html).toContain("3 passed");
54
+ expect(html).toContain('data-votes="eval"');
55
+ const attempts = renderToStaticMarkup(<ScopeSummary data={scopeSummaryData} votes="attempt" />);
56
+ expect(attempts).toContain("4 passed");
57
+ expect(attempts).toContain('data-votes="attempt"');
61
58
  });
62
59
 
63
- it("数据来源与 warnings 直接渲染在条内", () => {
64
- expect(html).toContain("2 snapshots");
65
- expect(html).toContain("compare/bub");
60
+ it("时间窗来自 range,不编造当前时间", () => {
66
61
  expect(html).toContain("2026-07-01T10:00:00Z");
67
- expect(html).toContain("snapshot covers 9 of 12 evals seen in history");
68
- expect(html).toContain("nre-warnings");
62
+ expect(html).toContain("2026-07-01T11:30:00Z");
69
63
  });
70
64
  });
71
65
 
72
66
  describe("MetricTable", () => {
73
67
  const html = renderToStaticMarkup(<MetricTable data={tableData} attemptHref={attemptHref} />);
74
68
 
75
- it("按传入顺序渲染行,不重排(排序在数据侧)", () => {
76
- // fixture 里 codex(50%)在 bub(87%)前面,输出必须保持
77
- expect(html.indexOf(">codex<")).toBeGreaterThan(-1);
78
- expect(html.indexOf(">codex<")).toBeLessThan(html.indexOf(">bub<"));
79
- });
80
-
81
- it("列头带 label、unit 与 better 方向", () => {
69
+ it("列头带 label、unit 与 better 方向,首列是 rowDimension", () => {
82
70
  expect(html).toContain("pass rate");
83
71
  expect(html).toContain("(%)");
84
72
  expect(html).toContain("code lines");
85
73
  expect(html).toContain("↑");
86
74
  expect(html).toContain("↓");
75
+ expect(html).toContain(">agent</th>");
87
76
  });
88
77
 
89
78
  it("数字与覆盖率角标:samples < total 如实标出", () => {
@@ -98,7 +87,7 @@ describe("MetricTable", () => {
98
87
  });
99
88
 
100
89
  it("refs + attemptHref 出普通 <a>", () => {
101
- expect(html).toContain('href="/attempts/@1a0a0a0"');
90
+ expect(html).toContain('href="/attempts/@1a0a0a0a"');
102
91
  });
103
92
 
104
93
  it("渐进增强的 data 属性:所有表头 data-nre-sort、格子 data-sort-value(无 JS 时纯属性,内容完整)", () => {
@@ -108,30 +97,9 @@ describe("MetricTable", () => {
108
97
  // 缺数据格子的排序值为空串:enhance.js 排序时恒沉底
109
98
  expect(html).toContain('data-sort-value=""');
110
99
  });
111
- });
112
-
113
- describe("MetricTable:meta 榜单 parity 与 filter", () => {
114
- const html = renderToStaticMarkup(<MetricTable data={tableDataWithMeta} />);
115
-
116
- it("meta 在场补 Model / Agent 列,列序 experiment、model、agent、指标、verdicts", () => {
117
- expect(html).toContain(">Model</th>");
118
- expect(html).toContain(">Agent</th>");
119
- expect(html).toContain(">Verdicts</th>");
120
- expect(html.indexOf(">Model</th>")).toBeLessThan(html.indexOf(">Agent</th>"));
121
- expect(html.indexOf(">Agent</th>")).toBeLessThan(html.indexOf("pass rate"));
122
- expect(html).toContain("gpt-5.4");
123
- });
124
-
125
- it("verdict 计票列:eval 级折叠口径的 pill,零计数不出 pill", () => {
126
- expect(html).toContain("nre-verdict-pill");
127
- expect(html).toContain("1 passed");
128
- expect(html).toContain("1 failed");
129
- expect(html).toContain("2 passed");
130
- expect(html).not.toContain("0 errored");
131
- });
132
100
 
133
101
  it("filter 开:表格前渲染 data-nre-filter 输入框(无 JS 静默无功能),仍无 <script>", () => {
134
- const filtered = renderToStaticMarkup(<MetricTable data={tableDataWithMeta} filter />);
102
+ const filtered = renderToStaticMarkup(<MetricTable data={tableData} filter />);
135
103
  expect(filtered).toContain("data-nre-filter");
136
104
  expect(filtered).toContain('class="nre-filter"');
137
105
  expect(filtered.indexOf("nre-filter")).toBeLessThan(filtered.indexOf("<table"));
@@ -139,11 +107,8 @@ describe("MetricTable:meta 榜单 parity 与 filter", () => {
139
107
  expect(filtered).not.toContain("<script");
140
108
  });
141
109
 
142
- it("locale=zh-CN:chrome 文案与 meta 列头走字典;display 不本地化", () => {
143
- const zh = renderToStaticMarkup(<MetricTable data={tableDataWithMeta} filter locale="zh-CN" />);
144
- expect(zh).toContain(">模型</th>");
145
- expect(zh).toContain(">结果</th>");
146
- expect(zh).toContain("1 通过");
110
+ it("locale=zh-CN:chrome 文案走字典;display 不本地化", () => {
111
+ const zh = renderToStaticMarkup(<MetricTable data={tableData} filter locale="zh-CN" />);
147
112
  expect(zh).toContain('placeholder="筛选行…"');
148
113
  expect(zh).toContain("50%"); // display 是 format 产物,不本地化
149
114
  });
@@ -152,25 +117,17 @@ describe("MetricTable:meta 榜单 parity 与 filter", () => {
152
117
  describe("MetricMatrix", () => {
153
118
  const html = renderToStaticMarkup(<MetricMatrix data={matrixData} attemptHref={attemptHref} />);
154
119
 
155
- it("caption 标出指标与行列维度", () => {
120
+ it("caption 标出指标与行列维度(rowDimension × columnDimension)", () => {
156
121
  expect(html).toContain("pass rate");
157
122
  expect(html).toContain("eval × agent");
158
123
  });
159
124
 
160
- it("稀疏格子:没有样本的格子空着(恰好一个)", () => {
161
- expect(html.match(/nre-td-empty/g)).toHaveLength(1);
162
- });
163
-
164
- it("格子数字与 refs 下钻链接", () => {
125
+ it("格子数字与 refs 下钻链接;稀疏格子空着不补 0", () => {
165
126
  expect(html).toContain("100%");
166
127
  expect(html).toContain("0%");
167
- expect(html).toContain('href="/attempts/@1b3b3b3"');
168
- expect(html).toContain('href="/attempts/@1b7b7b7"');
169
- });
170
-
171
- it("列头(维度键)带稳定散列配色 class", () => {
172
- expect(html).toContain(colorClassForKey("bub"));
173
- expect(html).toContain(colorClassForKey("codex"));
128
+ expect(html).toContain('href="/attempts/@1b3b3b3b"');
129
+ expect(html).toContain('href="/attempts/@1b7b7b7b"');
130
+ expect(html).toContain("nre-td-empty");
174
131
  });
175
132
  });
176
133
 
@@ -183,19 +140,20 @@ describe("Scoreboard", () => {
183
140
  expect(html).toContain("/ 100");
184
141
  });
185
142
 
186
- it("分科小计 earned/possible", () => {
187
- expect(html).toContain("14/16");
188
- expect(html).toContain("3/4");
143
+ it("分科小计 display(earned/possible 与同尺度百分比)", () => {
144
+ expect(html).toContain("14/16 (87.5%)");
145
+ expect(html).toContain("3/4 (75%)");
189
146
  });
190
147
 
191
- it("missing 注脚:没跑按 0 计的题数如实展示", () => {
192
- expect(html).toContain("1 eval missing, scored 0");
193
- expect(html).toContain("2 evals missing, scored 0");
148
+ it("notRun unscorable 分开注脚,不合并成一个笼统的缺失数", () => {
149
+ expect(html).toContain("1 eval not run, scored 0");
150
+ expect(html).toContain("2 evals unscorable, scored 0");
194
151
  });
195
152
 
196
- it("实际生效的权重表可审计", () => {
153
+ it("实际生效的权重表可审计;题集外被忽略的 eval 如实报数", () => {
197
154
  expect(html).toContain("algebra/ ×2");
198
155
  expect(html).toContain("others ×1");
156
+ expect(html).toContain("1 eval outside the question set ignored");
199
157
  });
200
158
  });
201
159
 
@@ -220,10 +178,37 @@ describe("MetricScatter", () => {
220
178
  expect(html).toContain("pass rate(%)");
221
179
  });
222
180
 
223
- it("better:lower 的 x 轴反向:便宜($5)在贵($10)右边,好的角落恒在右上", () => {
181
+ it("轴方向跟随 better:成本轴(lower)反向,便宜($5)在贵($10)右边;提示恒为「越靠右上越好」", () => {
224
182
  expect(cxOf("compare/bub-low")).toBeGreaterThan(cxOf("compare/bub-high"));
225
- // y 轴 better:higher:通过率高(90%)在低(50%)上方(SVG y 向下增长)
183
+ // y 轴(higher)正向:通过率高(90%)在低(50%)上方(SVG y 向下增长)
226
184
  expect(cyOf("compare/bub-high")).toBeLessThan(cyOf("compare/bub-low"));
185
+ expect(html).toContain("better → upper right");
186
+ });
187
+
188
+ it("反向轴的刻度显示真实值:x 刻度从左到右数值递减(值大在左),数字不变", () => {
189
+ // x 轴刻度 <text class="nre-scatter-tick" x="…">$N</text>:抓出 (x, 值) 对,按 x 升序后值应递减。
190
+ const ticks = [...html.matchAll(/nre-scatter-tick" x="([\d.]+)" y="[\d.]+" text-anchor="middle">\$([\d.]+)/g)].map(
191
+ (m) => ({ px: Number(m[1]), value: Number(m[2]) }),
192
+ );
193
+ expect(ticks.length).toBeGreaterThanOrEqual(2);
194
+ const byPx = [...ticks].sort((a, b) => a.px - b.px);
195
+ for (let i = 1; i < byPx.length; i++) {
196
+ expect(byPx[i]!.value).toBeLessThan(byPx[i - 1]!.value);
197
+ }
198
+ });
199
+
200
+ it("任一轴未声明 better:该轴正向渲染且整图无方向提示(组件不猜「更好」朝哪边)", () => {
201
+ const noBetter = renderToStaticMarkup(
202
+ <MetricScatter data={{ ...scatterData, x: { key: "cost", label: "cost", unit: "$" } }} />,
203
+ );
204
+ expect(noBetter).not.toContain("better →");
205
+ // x 正向:便宜($5)回到贵($10)左边。
206
+ const cx = (key: string): number => {
207
+ const m = noBetter.match(new RegExp(`data-key="${key}"[\\s\\S]*?\\bcx="([\\d.]+)"`));
208
+ expect(m, `circle for ${key}`).toBeTruthy();
209
+ return Number(m![1]);
210
+ };
211
+ expect(cx("compare/bub-low")).toBeLessThan(cx("compare/bub-high"));
227
212
  });
228
213
 
229
214
  it("同系列点连线,系列色走类名(nre-series-cN,CSS 上色跟随深浅主题),图例列系列名", () => {
@@ -231,21 +216,31 @@ describe("MetricScatter", () => {
231
216
  expect(html).toContain(`nre-scatter-line ${seriesClassForKey("bub")}`);
232
217
  // 渲染面零内联 hex:深色主题下由 CSS 变量切换
233
218
  expect(html).not.toMatch(/#[0-9a-f]{6}/i);
234
- // 图例:codex 只有 1 个可画点、不出线,但系列名仍在图例里
235
219
  expect(html).toContain(">bub</span>");
236
220
  expect(html).toContain(">codex</span>");
237
221
  });
238
222
 
239
- it("niceTicks 网格与每点直接标签(id 末段)", () => {
223
+ it("niceTicks 网格与每点直接标签(末段唯一时缩成末段)", () => {
240
224
  expect(html).toContain("nre-scatter-grid");
241
225
  expect(html.match(/nre-scatter-tick/g)!.length).toBeGreaterThanOrEqual(6);
242
226
  expect(html).toContain(">bub-low</text>");
243
227
  expect(html).toContain(">codex-mid</text>");
244
228
  });
245
229
 
246
- it("null 点不画,底部注脚如实报数", () => {
247
- expect(html).not.toContain('data-key="compare/codex-broken"');
248
- expect(html).toContain("1 point missing data");
230
+ it("末段重名的点使用能区分它们的最短路径后缀", () => {
231
+ const dupes = renderToStaticMarkup(
232
+ <MetricScatter
233
+ data={{
234
+ ...scatterData,
235
+ rows: [
236
+ { ...scatterData.rows[0]!, key: "compare/a/run" },
237
+ { ...scatterData.rows[1]!, key: "compare/b/run" },
238
+ ],
239
+ }}
240
+ />,
241
+ );
242
+ expect(dupes).toContain(">a/run</text>");
243
+ expect(dupes).toContain(">b/run</text>");
249
244
  });
250
245
 
251
246
  it("hover 退化为 <title>:display 与 samples/total", () => {
@@ -298,71 +293,57 @@ describe("DeltaTable", () => {
298
293
  expect(html).toContain(">Δ</span>");
299
294
  });
300
295
 
301
- it("涨跌好坏按 better 配色:通过率涨=好,成本涨=坏,0=平", () => {
296
+ it("涨跌好坏按数据侧算好的 outcome 配色:improved / regressed / unchanged / unavailable", () => {
302
297
  expect(html).toContain("nre-delta-good");
303
298
  expect(html).toContain("nre-delta-bad");
304
299
  expect(html).toContain("nre-delta-flat");
300
+ expect(html).toContain('data-outcome="unavailable"');
305
301
  });
306
302
 
307
- it("任一侧缺数据:Δ 显示为缺,不硬算", () => {
308
- expect(html).toContain("nre-delta-missing");
309
- expect(html).toContain("no data");
303
+ it("每行显示作者声明的 pair label 与 A → B 的维度键", () => {
304
+ expect(html).toContain(">bub</span>");
305
+ expect(html).toContain("compare/bub → compare/bub--agents-md");
310
306
  });
311
307
 
312
- it("每行标出 A → B 的 experimentId", () => {
313
- expect(html).toContain("compare/bub compare/bub--agents-md");
308
+ it("0 对显示明确空态并报告配对域实验数", () => {
309
+ const empty = renderToStaticMarkup(<DeltaTable data={{ ...deltaData, experiments: 3, rows: [] }} />);
310
+ expect(empty).not.toContain("<table");
311
+ expect(empty).toContain("3 experiments, 0 comparable pairs");
314
312
  });
315
313
  });
316
314
 
317
- // AttemptList / EvalList / ExperimentList 的公开(报告组件)Props 没有 attemptHref 覆盖口子——
318
- // 证据室深链恒经 ctx.attemptHref,宿主外直接嵌进 React 应用时退化为默认 `#/attempt/<locator>`
319
- // (tree.ts 的 DEFAULT_WEB_CONTEXT),不是纯展示、也不发明另一套断链(与 MetricTable 等
320
- // 可选 attemptHref 的组件不同,这三个组件的下钻不是可选行为)。
321
-
322
315
  describe("AttemptList", () => {
323
- const html = renderToStaticMarkup(<AttemptList items={attemptListItems} />);
324
-
325
- it("逐条断言:name、score、detail、evidence", () => {
326
- expect(html).toContain("roots-correct");
327
- expect(html).toContain("expected x=2, got x=3");
328
- expect(html).toContain("judge: sign flipped when substituting into the quadratic formula");
329
- });
316
+ const html = renderToStaticMarkup(<AttemptList data={attemptListItems} />);
330
317
 
331
- it("errored error 摘要", () => {
332
- expect(html).toContain("TypeError: cannot read properties of undefined");
318
+ it("显示算好的 failureSummary +N more failures 计数,不重算摘要", () => {
319
+ expect(html).toContain("gate: roots-correct · expected x=2 · received x=3");
320
+ expect(html).toContain("+1 more failure");
321
+ // errored 的一层摘要(phase · code · message)
322
+ expect(html).toContain("eval.run · unexpected-error · TypeError");
333
323
  });
334
324
 
335
- it("total > items.length 时如实报「还有 n 条没列」", () => {
336
- const html2 = renderToStaticMarkup(<AttemptList items={attemptListItems} total={attemptListItems.length + 2} />);
325
+ it("total > data.length 时如实报「还有 n 条没列」", () => {
326
+ const html2 = renderToStaticMarkup(<AttemptList data={attemptListItems} total={attemptListItems.length + 2} />);
337
327
  expect(html2).toContain("and 2 more not shown");
338
- // 不传 total(或 total === items.length)不产出截断文案
328
+ // 不传 total(或 total === data.length)不产出截断文案
339
329
  expect(html).not.toContain("more not shown");
340
330
  });
341
331
 
342
- it("每条 attempt locator + 默认证据室深链,不追加字母缩写", () => {
343
- expect(html).toContain('href="#/attempt/@1a4a4a4"');
344
- expect(html).toContain('href="#/attempt/@1c1c1c1"');
345
- expect(html).not.toMatch(/\[[EXD⏱,]+\]/);
332
+ it("costUSD null 时不显示成本,也不伪造 $0", () => {
333
+ expect(html).toContain("$0.12");
334
+ expect(html.match(/nre-attempt-cost/g)).toHaveLength(1); // errored 那条 costUSD: null
346
335
  });
347
336
 
348
- it("长文本(evidence)收进 <details>,零 JS 可展开", () => {
349
- expect(html).toContain("<details");
350
- expect(html).toContain("<summary>");
337
+ it("AttemptList 自身不展开断言详情", () => {
338
+ expect(html).not.toContain("<details");
351
339
  });
352
340
  });
353
341
 
354
342
  describe("EvalList", () => {
355
- const html = renderToStaticMarkup(<EvalList items={evalListItems} />);
356
-
357
- it("每项一个 experimentId + evalId,判定与分数在场", () => {
358
- expect(html).toContain("algebra/quadratic");
359
- expect(html).toContain("geometry/angles");
360
- expect(html).toContain("compare/bub");
361
- expect(html).toContain("compare/codex");
362
- });
343
+ const html = renderToStaticMarkup(<EvalList data={evalListItems} />);
363
344
 
364
345
  it("展开到这道题的 Attempt(与 AttemptList 同一套 AttemptRow 渲染)", () => {
365
- expect(html).toContain('href="#/attempt/@1a4a4a4"');
346
+ expect(html).toContain('href="#/attempt/@1a4a4a4a"');
366
347
  expect(html).toContain("roots-correct");
367
348
  });
368
349
 
@@ -373,34 +354,45 @@ describe("EvalList", () => {
373
354
  });
374
355
 
375
356
  describe("ExperimentList", () => {
376
- const html = renderToStaticMarkup(<ExperimentList items={experimentListItems} filter />);
357
+ const html = renderToStaticMarkup(<ExperimentList data={experimentListItems} filter />);
377
358
 
378
- it("web 面是带过滤框与八个固定列头的 experiment 比较表", () => {
359
+ it("web 面是带过滤框与八个固定列头的 experiment 比较表(成本列头是 Cost)", () => {
379
360
  expect(html).toContain('data-nre-experiment-filter=""');
380
- for (const header of ["Experiment", "Model", "Agent", "Avg duration", "Pass rate", "Tokens", "Est. cost", "Result"]) {
361
+ for (const header of ["Experiment", "Model", "Agent", "Avg duration", "End-to-end pass rate", "Tokens", "Cost", "Result"]) {
381
362
  expect(html).toContain(`>${header}</button>`);
382
363
  }
364
+ expect(html).not.toContain("Est. cost");
383
365
  expect(html).toContain('data-nre-experiment-sort="4" class="nre-sort-desc"');
384
366
  });
385
367
 
386
- it("主行:身份、agent/model、官方两级聚合指标与判定构成", () => {
368
+ it("主行:身份、agent/model、官方两级聚合指标与 eval 级判定构成", () => {
387
369
  expect(html).toContain("compare/bub");
388
370
  expect(html).toContain("compare/codex");
389
371
  expect(html).toContain("gpt-5.4");
390
- expect(html).toContain("50%"); // passRate.display
372
+ expect(html).toContain("50%"); // endToEndPassRate.display
391
373
  expect(html).toContain("1 passed / 1 failed");
392
374
  expect(html).toContain("memory=true");
393
375
  });
394
376
 
395
- it("展开到 Eval:同一道题的全部 Attempt 逐条显示", () => {
377
+ it("展开到 Eval:父行只有折叠判定与题级聚合,失败摘要只在 Attempt 子行", () => {
396
378
  expect(html).toContain("algebra/quadratic");
397
- expect(html).toContain('href="#/attempt/@1a4a4a4"');
398
- expect(html).toContain('href="#/attempt/@1b5b5b5"');
379
+ expect(html).toContain('href="#/attempt/@1a4a4a4a"');
380
+ expect(html).toContain('href="#/attempt/@1b5b5b5b"');
399
381
  expect(html.match(/algebra\/quadratic/g)).toHaveLength(1);
400
382
  expect(html).toContain("nre-experiment-eval-header");
401
- expect(html.match(/nre-experiment-attempt-row/g)).toHaveLength(3);
383
+ expect(html.match(/nre-experiment-attempt-row/g)).toHaveLength(4);
402
384
  expect(html).toContain("├─");
403
385
  expect(html).toContain("└─");
386
+ // 主失败摘要只出现在 attempt 子行:两次(同一 eval 的两轮 attempt 各自的摘要),不在父行重复
387
+ expect(html.match(/gate: roots-correct/g)).toHaveLength(2);
388
+ });
389
+
390
+ it("relativeTo 去掉组前缀只显示末段;data-sort-value 仍是完整 id", () => {
391
+ const relative = renderToStaticMarkup(<ExperimentList data={experimentListItems} relativeTo="compare" />);
392
+ expect(relative).toContain(">bub</b>");
393
+ expect(relative).toContain('data-sort-value="compare/bub"');
394
+ // 不传 relativeTo 显示完整 id
395
+ expect(html).toContain(">compare/bub</b>");
404
396
  });
405
397
 
406
398
  it("零 JS 靠原生 <details>,无 <script>", () => {
@@ -415,9 +407,8 @@ describe("跨组件契约", () => {
415
407
  const table = renderToStaticMarkup(<MetricTable data={tableData} />);
416
408
  const matrix = renderToStaticMarkup(<MetricMatrix data={matrixData} />);
417
409
  const board = renderToStaticMarkup(<Scoreboard data={scoreboardData} />);
418
- const delta = renderToStaticMarkup(<DeltaTable data={deltaData} />);
419
- const attempts = renderToStaticMarkup(<AttemptList items={attemptListItems} />);
420
- for (const html of [table, matrix, board, delta, attempts]) {
410
+ const attempts = renderToStaticMarkup(<AttemptList data={attemptListItems} />);
411
+ for (const html of [table, matrix, board, attempts]) {
421
412
  expect(html).toContain(cls);
422
413
  }
423
414
  const scatter = renderToStaticMarkup(<MetricScatter data={scatterData} />);
@@ -426,27 +417,16 @@ describe("跨组件契约", () => {
426
417
 
427
418
  it("静态输出不含 <script>:交互只靠 <a>/<details>/CSS", () => {
428
419
  const all = [
429
- renderToStaticMarkup(<RunOverview data={overviewData} />),
420
+ renderToStaticMarkup(<ScopeSummary data={scopeSummaryData} />),
430
421
  renderToStaticMarkup(<MetricTable data={tableData} attemptHref={attemptHref} />),
431
422
  renderToStaticMarkup(<MetricMatrix data={matrixData} attemptHref={attemptHref} />),
432
423
  renderToStaticMarkup(<Scoreboard data={scoreboardData} />),
433
424
  renderToStaticMarkup(<MetricScatter data={scatterData} />),
434
425
  renderToStaticMarkup(<DeltaTable data={deltaData} />),
435
- renderToStaticMarkup(<AttemptList items={attemptListItems} />),
436
- renderToStaticMarkup(<EvalList items={evalListItems} />),
437
- renderToStaticMarkup(<ExperimentList items={experimentListItems} />),
426
+ renderToStaticMarkup(<AttemptList data={attemptListItems} />),
427
+ renderToStaticMarkup(<EvalList data={evalListItems} />),
428
+ renderToStaticMarkup(<ExperimentList data={experimentListItems} />),
438
429
  ].join("");
439
430
  expect(all).not.toContain("<script");
440
431
  });
441
-
442
- it("组件源码零 hooks(本实验的「不 hydrate 也完整」用最笨的方式保证)", () => {
443
- const dir = dirname(fileURLToPath(import.meta.url));
444
- const sources = readdirSync(dir)
445
- .filter((f) => (f.endsWith(".tsx") || f.endsWith(".ts")) && !f.includes(".test."))
446
- .map((f) => readFileSync(join(dir, f), "utf8"));
447
- expect(sources.length).toBeGreaterThanOrEqual(12);
448
- for (const src of sources) {
449
- expect(src).not.toMatch(/\buse[A-Z][A-Za-z]*\s*\(/);
450
- }
451
- });
452
432
  });