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.
- package/INDEX.md +23 -23
- package/README.zh.md +6 -6
- package/dist/agents/types.d.ts +69 -7
- package/dist/context/types.d.ts +32 -12
- package/dist/i18n/en.d.ts +54 -0
- package/dist/i18n/zh-CN.d.ts +57 -3
- package/dist/o11y/types.d.ts +16 -2
- package/dist/report/aggregate.d.ts +32 -24
- package/dist/report/aggregate.js +158 -50
- package/dist/report/built-in/index.d.ts +2 -0
- package/dist/report/built-in/index.js +8 -0
- package/dist/report/components.d.ts +93 -160
- package/dist/report/components.js +377 -114
- package/dist/report/compute.d.ts +87 -81
- package/dist/report/compute.js +597 -417
- package/dist/report/flag.d.ts +32 -6
- package/dist/report/flag.js +92 -4
- package/dist/report/format.d.ts +19 -11
- package/dist/report/format.js +30 -13
- package/dist/report/index.d.ts +16 -16
- package/dist/report/index.js +20 -21
- package/dist/report/load.js +3 -2
- package/dist/report/locale.d.ts +57 -33
- package/dist/report/locale.js +122 -56
- package/dist/report/metrics.d.ts +23 -4
- package/dist/report/metrics.js +110 -25
- package/dist/report/primitives.d.ts +48 -15
- package/dist/report/primitives.js +135 -26
- package/dist/report/react/AttemptList.d.ts +9 -7
- package/dist/report/react/AttemptList.js +17 -10
- package/dist/report/react/DeltaTable.js +19 -18
- package/dist/report/react/EvalList.d.ts +4 -4
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentComparison.d.ts +10 -0
- package/dist/report/react/ExperimentComparison.js +12 -0
- package/dist/report/react/ExperimentList.d.ts +4 -3
- package/dist/report/react/ExperimentList.js +17 -18
- package/dist/report/react/MetricBars.js +5 -4
- package/dist/report/react/MetricLine.js +12 -5
- package/dist/report/react/MetricMatrix.js +1 -1
- package/dist/report/react/MetricScatter.js +59 -28
- package/dist/report/react/MetricTable.js +2 -12
- package/dist/report/react/ScopeSummary.d.ts +10 -0
- package/dist/report/react/ScopeSummary.js +17 -0
- package/dist/report/react/Scoreboard.js +6 -6
- package/dist/report/react/cell.js +2 -2
- package/dist/report/react/chart-math.d.ts +23 -6
- package/dist/report/react/chart-math.js +71 -19
- package/dist/report/react/fixtures.d.ts +5 -9
- package/dist/report/react/fixtures.js +110 -147
- package/dist/report/react/index.d.ts +15 -5
- package/dist/report/react/index.js +18 -7
- package/dist/report/report.d.ts +137 -16
- package/dist/report/report.js +259 -28
- package/dist/report/text/faces.d.ts +17 -19
- package/dist/report/text/faces.js +253 -184
- package/dist/report/text/plot.js +1 -1
- package/dist/report/text/table.js +38 -7
- package/dist/report/tree.d.ts +90 -40
- package/dist/report/tree.js +252 -94
- package/dist/report/types.d.ts +247 -284
- package/dist/report/types.js +4 -3
- package/dist/report/web.d.ts +21 -5
- package/dist/report/web.js +42 -16
- package/dist/results/select.d.ts +38 -16
- package/dist/results/select.js +73 -25
- package/dist/results/types.d.ts +49 -14
- package/dist/runner/feedback/sink.d.ts +110 -0
- package/dist/runner/types.d.ts +513 -22
- package/dist/sandbox/docker.d.ts +23 -2
- package/dist/sandbox/e2b.d.ts +15 -1
- package/dist/sandbox/errors.d.ts +30 -3
- package/dist/sandbox/io-retry.d.ts +17 -0
- package/dist/sandbox/registry.d.ts +2 -0
- package/dist/sandbox/resolve.d.ts +18 -5
- package/dist/sandbox/retry.d.ts +11 -1
- package/dist/sandbox/types.d.ts +39 -5
- package/dist/sandbox/vercel.d.ts +7 -1
- package/dist/scoring/coverage.d.ts +30 -0
- package/dist/scoring/display.d.ts +21 -0
- package/dist/scoring/display.js +120 -0
- package/dist/scoring/types.d.ts +103 -20
- package/dist/shared/aggregate.d.ts +4 -2
- package/dist/shared/aggregate.js +8 -7
- package/dist/shared/types.d.ts +28 -0
- package/dist/tty-line.d.ts +0 -4
- package/dist/util.d.ts +23 -0
- package/docs-site/zh/README.md +44 -0
- package/docs-site/zh/examples/ai-agent-application.mdx +63 -0
- package/docs-site/zh/examples/coding-agent-extensions.mdx +57 -0
- package/docs-site/zh/examples/index.mdx +50 -0
- package/docs-site/zh/{concepts → explanation}/adapter.mdx +31 -13
- package/docs-site/zh/{concepts → explanation}/assert.mdx +7 -7
- package/docs-site/zh/{concepts → explanation}/drive.mdx +8 -8
- package/docs-site/zh/{concepts → explanation}/evals.mdx +4 -4
- package/docs-site/zh/{concepts → explanation}/experiment.mdx +8 -8
- package/docs-site/zh/{concepts → explanation}/hitl.mdx +8 -8
- package/docs-site/zh/{concepts → explanation}/judge.mdx +5 -5
- package/docs-site/zh/{concepts → explanation}/overview.mdx +11 -11
- package/docs-site/zh/{guides → explanation}/runner.mdx +18 -8
- package/docs-site/zh/{concepts → explanation}/tier.mdx +6 -6
- package/docs-site/zh/{guides → how-to}/agent-feedback-loop.mdx +35 -33
- package/docs-site/zh/{guides → how-to}/authoring.mdx +35 -2
- package/docs-site/zh/{guides → how-to}/ci-integration.mdx +23 -12
- package/docs-site/zh/{guides → how-to}/connect-otel.mdx +6 -6
- package/docs-site/zh/{guides → how-to}/connect-your-agent.mdx +47 -21
- package/docs-site/zh/{guides → how-to}/custom-reports.mdx +34 -39
- package/docs-site/zh/{guides → how-to}/dataset-fanout.mdx +25 -3
- package/docs-site/zh/{guides → how-to}/experiments.mdx +12 -5
- package/docs-site/zh/how-to/publish-report.mdx +105 -0
- package/docs-site/zh/{guides → how-to}/reporters.mdx +2 -2
- package/docs-site/zh/{guides → how-to}/sandbox-agent.mdx +56 -7
- package/docs-site/zh/how-to/sandbox-providers.mdx +350 -0
- package/docs-site/zh/{guides → how-to}/scoring-guide.mdx +4 -4
- package/docs-site/zh/{guides → how-to}/viewing-results.mdx +82 -39
- package/docs-site/zh/{guides → how-to}/write-experiment.mdx +6 -4
- package/docs-site/zh/{guides → how-to}/write-send.mdx +30 -14
- package/docs-site/zh/index.mdx +24 -26
- package/docs-site/zh/introduction.mdx +8 -8
- package/docs-site/zh/reference/builtin-agents.mdx +32 -5
- package/docs-site/zh/reference/capabilities.mdx +8 -8
- package/docs-site/zh/reference/cli.mdx +40 -12
- package/docs-site/zh/reference/define-agent.mdx +58 -5
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +42 -9
- package/docs-site/zh/reference/events.mdx +3 -3
- package/docs-site/zh/reference/expect.mdx +26 -1
- package/docs-site/zh/{guides → reference}/official-adapters.mdx +32 -8
- package/docs-site/zh/{guides → reference}/report-components.mdx +45 -33
- package/docs-site/zh/{guides → reference}/results-data.mdx +21 -13
- package/docs-site/zh/troubleshooting/debug-sandbox.mdx +57 -0
- package/docs-site/zh/troubleshooting/debugging.mdx +212 -0
- package/docs-site/zh/{quickstart.mdx → tutorials/quickstart.mdx} +5 -17
- package/package.json +10 -2
- package/src/agents/ai-sdk-otel.test.ts +1 -0
- package/src/agents/ai-sdk.test.ts +3 -0
- package/src/agents/ai-sdk.ts +3 -0
- package/src/agents/bub-install-spec.test.ts +34 -0
- package/src/agents/bub-install-spec.ts +32 -0
- package/src/agents/bub.ts +31 -32
- package/src/agents/claude-code.test.ts +130 -9
- package/src/agents/claude-code.ts +76 -4
- package/src/agents/codex.test.ts +189 -40
- package/src/agents/codex.ts +155 -14
- package/src/agents/coding-cli-versions.test.ts +15 -0
- package/src/agents/coding-cli-versions.ts +3 -0
- package/src/agents/index.ts +13 -2
- package/src/agents/langgraph.test.ts +204 -0
- package/src/agents/langgraph.ts +495 -0
- package/src/agents/marketplace.ts +85 -0
- package/src/agents/native-config.test.ts +179 -0
- package/src/agents/native-config.ts +267 -0
- package/src/agents/openai-compat.test.ts +1 -0
- package/src/agents/openai-compat.ts +1 -1
- package/src/agents/openclaw.test.ts +31 -0
- package/src/agents/openclaw.ts +171 -0
- package/src/agents/plugin-config.test.ts +1 -0
- package/src/agents/sdk-streams.test.ts +79 -0
- package/src/agents/sdk-streams.ts +55 -10
- package/src/agents/skills.test.ts +1 -0
- package/src/agents/streaming.test.ts +3 -9
- package/src/agents/streaming.ts +2 -2
- package/src/agents/types.ts +71 -8
- package/src/agents/ui-message-stream.test.ts +3 -0
- package/src/cli.ts +446 -124
- package/src/context/context.test.ts +51 -12
- package/src/context/context.ts +162 -30
- package/src/context/session.test.ts +2 -1
- package/src/context/session.ts +115 -7
- package/src/context/types.ts +30 -12
- package/src/define.test.ts +13 -8
- package/src/define.ts +25 -4
- package/src/expect/index.ts +53 -23
- package/src/i18n/en.ts +81 -17
- package/src/i18n/zh-CN.ts +80 -17
- package/src/o11y/cost.test.ts +1 -0
- package/src/o11y/execution-tree.test.ts +1 -20
- package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
- package/src/o11y/otlp/parse.test.ts +1 -0
- package/src/o11y/otlp/turn-otel.test.ts +1 -0
- package/src/o11y/parsers/bub.test.ts +1 -0
- package/src/o11y/parsers/claude-code.test.ts +1 -34
- package/src/o11y/parsers/openclaw.test.ts +154 -0
- package/src/o11y/parsers/openclaw.ts +310 -0
- package/src/o11y/prices.json +746 -311
- package/src/o11y/tool-names.test.ts +1 -0
- package/src/o11y/types.ts +16 -2
- package/src/report/aggregate.ts +178 -61
- package/src/report/built-in/index.tsx +9 -0
- package/src/report/components.tsx +625 -279
- package/src/report/compute.ts +723 -491
- package/src/report/dual-render.test.tsx +741 -1024
- package/src/report/flag.ts +104 -12
- package/src/report/format.ts +32 -12
- package/src/report/index.ts +119 -46
- package/src/report/load.ts +3 -2
- package/src/report/locale.ts +136 -65
- package/src/report/metrics.ts +108 -25
- package/src/report/primitives.tsx +196 -45
- package/src/report/react/AttemptList.tsx +30 -43
- package/src/report/react/DeltaTable.tsx +63 -45
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentComparison.tsx +73 -0
- package/src/report/react/ExperimentList.tsx +50 -32
- package/src/report/react/MetricBars.tsx +5 -4
- package/src/report/react/MetricLine.tsx +13 -8
- package/src/report/react/MetricMatrix.tsx +2 -2
- package/src/report/react/MetricScatter.tsx +86 -34
- package/src/report/react/MetricTable.tsx +4 -76
- package/src/report/react/ScopeSummary.tsx +86 -0
- package/src/report/react/Scoreboard.tsx +28 -10
- package/src/report/react/cell.tsx +2 -2
- package/src/report/react/chart-math.test.ts +85 -0
- package/src/report/react/chart-math.ts +101 -22
- package/src/report/react/enhance.js +89 -5
- package/src/report/react/fixtures.ts +114 -154
- package/src/report/react/index.tsx +24 -39
- package/src/report/react/render.test.tsx +138 -158
- package/src/report/react/styles.css +243 -82
- package/src/report/report.test.ts +779 -841
- package/src/report/report.ts +423 -41
- package/src/report/text/faces.ts +290 -193
- package/src/report/text/plot.ts +1 -1
- package/src/report/text/table.ts +44 -7
- package/src/report/tree.ts +362 -104
- package/src/report/types.ts +261 -271
- package/src/report/web.ts +63 -20
- package/src/results/annotated-source.test.ts +62 -9
- package/src/results/annotated-source.ts +64 -6
- package/src/results/attempt-evidence.test.ts +13 -11
- package/src/results/attempt-evidence.ts +20 -13
- package/src/results/attempt-source.ts +6 -3
- package/src/results/copy.ts +150 -60
- package/src/results/host-equivalence.test.ts +34 -20
- package/src/results/index.ts +12 -4
- package/src/results/locator.test.ts +1 -22
- package/src/results/open.ts +15 -5
- package/src/results/publish.ts +149 -0
- package/src/results/results.test.ts +89 -54
- package/src/results/select.ts +104 -34
- package/src/results/truncate.ts +90 -0
- package/src/results/types.ts +43 -14
- package/src/results/writer.ts +31 -13
- package/src/runner/attempt.test.ts +138 -7
- package/src/runner/attempt.ts +603 -104
- package/src/runner/discover.test.ts +47 -0
- package/src/runner/discover.ts +36 -2
- package/src/runner/eval-source.test.ts +1 -27
- package/src/runner/feedback/agent.test.ts +504 -0
- package/src/runner/feedback/agent.ts +409 -0
- package/src/runner/feedback/ci.test.ts +562 -0
- package/src/runner/feedback/ci.ts +401 -0
- package/src/runner/feedback/coordinator.test.ts +317 -0
- package/src/runner/feedback/coordinator.ts +397 -0
- package/src/runner/feedback/failure.ts +40 -0
- package/src/runner/feedback/human.test.ts +616 -0
- package/src/runner/feedback/human.ts +535 -0
- package/src/runner/feedback/index.ts +66 -0
- package/src/runner/feedback/io.ts +78 -0
- package/src/runner/feedback/profile.test.ts +50 -0
- package/src/runner/feedback/profile.ts +58 -0
- package/src/runner/feedback/reducer.test.ts +395 -0
- package/src/runner/feedback/reducer.ts +260 -0
- package/src/runner/feedback/renderer.ts +82 -0
- package/src/runner/feedback/sink.ts +203 -0
- package/src/runner/feedback/testing.ts +106 -0
- package/src/runner/ledger.test.ts +230 -0
- package/src/runner/ledger.ts +329 -0
- package/src/runner/report.test.ts +128 -3
- package/src/runner/report.ts +33 -9
- package/src/runner/reporters/artifacts.ts +8 -2
- package/src/runner/reporters/braintrust.test.ts +8 -7
- package/src/runner/reporters/braintrust.ts +9 -2
- package/src/runner/reporters/index.ts +2 -2
- package/src/runner/reporters/json.test.ts +162 -0
- package/src/runner/reporters/json.ts +35 -8
- package/src/runner/reporters/shared.ts +1 -5
- package/src/runner/run.test.ts +760 -3
- package/src/runner/run.ts +243 -37
- package/src/runner/sandbox-prep.ts +3 -42
- package/src/runner/timing.ts +158 -0
- package/src/runner/types.ts +518 -22
- package/src/sandbox/checkpoint.test.ts +55 -0
- package/src/sandbox/checkpoint.ts +29 -8
- package/src/sandbox/cli-commands.ts +407 -0
- package/src/sandbox/docker.ts +115 -16
- package/src/sandbox/e2b-agent-template.test.ts +56 -0
- package/src/sandbox/e2b-agent-template.ts +94 -0
- package/src/sandbox/e2b.ts +74 -9
- package/src/sandbox/errors.ts +111 -4
- package/src/sandbox/index.ts +2 -0
- package/src/sandbox/io-retry.test.ts +58 -0
- package/src/sandbox/io-retry.ts +45 -0
- package/src/sandbox/keep-registry.test.ts +86 -0
- package/src/sandbox/keep-registry.ts +142 -0
- package/src/sandbox/keep.ts +178 -0
- package/src/sandbox/paths.test.ts +1 -0
- package/src/sandbox/paths.ts +19 -8
- package/src/sandbox/registry.ts +20 -3
- package/src/sandbox/resolve.ts +76 -11
- package/src/sandbox/retry.test.ts +70 -0
- package/src/sandbox/retry.ts +46 -4
- package/src/sandbox/types.ts +44 -6
- package/src/sandbox/vercel.ts +43 -20
- package/src/scoring/collector.ts +60 -17
- package/src/scoring/coverage.ts +95 -0
- package/src/scoring/diff.ts +81 -0
- package/src/scoring/display.test.ts +121 -0
- package/src/scoring/display.ts +133 -0
- package/src/scoring/evidence.test.ts +189 -0
- package/src/scoring/judge.test.ts +142 -0
- package/src/scoring/judge.ts +15 -18
- package/src/scoring/scoped.ts +217 -50
- package/src/scoring/types.ts +117 -20
- package/src/scoring/verdict.ts +16 -4
- package/src/shared/aggregate.ts +8 -6
- package/src/shared/types.ts +31 -0
- package/src/show/compose.ts +50 -67
- package/src/show/index.ts +127 -56
- package/src/show/render.ts +662 -131
- package/src/show/report-host.test.ts +188 -0
- package/src/show/report-host.ts +375 -0
- package/src/show/show.test.ts +320 -54
- package/src/tty-line.ts +8 -26
- package/src/util.test.ts +1 -0
- package/src/util.ts +41 -0
- package/src/view/app/App.test.tsx +69 -0
- package/src/view/app/App.tsx +144 -48
- package/src/view/app/components/AttemptModal.tsx +423 -11
- package/src/view/app/components/CodeView.tsx +41 -14
- package/src/view/app/components/CopyControls.tsx +2 -2
- package/src/view/app/i18n.ts +37 -17
- package/src/view/app/lib/attempt-route.test.ts +1 -0
- package/src/view/app/lib/verdict.ts +7 -9
- package/src/view/app/main.tsx +13 -8
- package/src/view/app/pages/{RunsPage.tsx → AttemptsPage.tsx} +6 -6
- package/src/view/app/types.ts +4 -1
- package/src/view/artifact-serving.test.ts +2 -1
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +17 -17
- package/src/view/data.test.ts +10 -3
- package/src/view/data.ts +155 -49
- package/src/view/index.ts +56 -41
- package/src/view/server.ts +37 -15
- package/src/view/shared/types.ts +34 -5
- package/src/view/styles.css +227 -0
- package/src/view/view-report.test.ts +167 -62
- package/dist/report/built-ins/experiment-comparison.d.ts +0 -1
- package/dist/report/built-ins/experiment-comparison.js +0 -13
- package/dist/report/built-ins/index.d.ts +0 -1
- package/dist/report/built-ins/index.js +0 -2
- package/dist/report/react/GroupSummary.d.ts +0 -8
- package/dist/report/react/GroupSummary.js +0 -8
- package/dist/report/react/RunOverview.d.ts +0 -8
- package/dist/report/react/RunOverview.js +0 -12
- package/docs-site/zh/example/ai-agent-application.mdx +0 -152
- package/docs-site/zh/example/claude-code-codex-plugin.mdx +0 -167
- package/docs-site/zh/example/claude-code-codex-skill.mdx +0 -152
- package/docs-site/zh/example/showcase.mdx +0 -39
- package/docs-site/zh/guides/publish-report.mdx +0 -91
- package/docs-site/zh/guides/sandbox-providers.mdx +0 -102
- package/src/report/built-in-user-parity.test.tsx +0 -640
- package/src/report/built-ins/experiment-comparison.tsx +0 -19
- package/src/report/built-ins/index.ts +0 -2
- package/src/report/react/GroupSummary.tsx +0 -66
- package/src/report/react/RunOverview.tsx +0 -109
- package/src/runner/reporters/console.ts +0 -70
- package/src/runner/reporters/live.test.ts +0 -56
- package/src/runner/reporters/live.ts +0 -247
- package/src/runner/reporters/quiet.test.ts +0 -66
- package/src/runner/reporters/quiet.ts +0 -49
- package/src/runner/reporters/table.ts +0 -277
- /package/docs-site/zh/{example/tier1-ai-sdk-v7.mdx → examples/integrations/ai-sdk-v7.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-claude-sdk.mdx → examples/integrations/claude-sdk.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-codex-sdk.mdx → examples/integrations/codex-sdk.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-langgraph.mdx → examples/integrations/langgraph.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-pi-sdk.mdx → examples/integrations/pi-sdk.mdx} +0 -0
- /package/docs-site/zh/{guides → how-to}/fixtures.mdx +0 -0
package/src/view/styles.css
CHANGED
|
@@ -62,6 +62,10 @@ a { color: inherit; }
|
|
|
62
62
|
display: flex;
|
|
63
63
|
align-items: center;
|
|
64
64
|
justify-content: space-between;
|
|
65
|
+
/* space-between 只在有富余空间时才撑开相邻项;.shell-links 的 margin-left:auto 会把富余
|
|
66
|
+
空间全部吸收到它左侧,导致品牌与导航贴脸、shell-links 与语言切换贴脸。显式 gap 保证
|
|
67
|
+
每一对相邻项(品牌—导航、shell-links—语言切换)恒有最小间距,不依赖富余空间。 */
|
|
68
|
+
gap: 28px;
|
|
65
69
|
padding: 0 clamp(20px, 5vw, 80px);
|
|
66
70
|
background: color-mix(in oklch, var(--bg), transparent 4%);
|
|
67
71
|
position: sticky;
|
|
@@ -849,6 +853,7 @@ a.brand:hover { opacity: 0.75; }
|
|
|
849
853
|
.gstat.good { color: var(--good); }
|
|
850
854
|
.gstat.bad { color: var(--bad); }
|
|
851
855
|
.gstat.warn { color: var(--warn); }
|
|
856
|
+
.gstat.na { color: var(--muted, #8a8f98); }
|
|
852
857
|
.gsend { width: 13px; height: 13px; flex-shrink: 0; color: var(--blue); }
|
|
853
858
|
|
|
854
859
|
.ctext { white-space: pre; color: var(--text); padding-left: 8px; }
|
|
@@ -866,6 +871,8 @@ a.brand:hover { opacity: 0.75; }
|
|
|
866
871
|
.abadge.good { color: var(--good); border-color: color-mix(in oklch, var(--good), var(--line) 55%); background: color-mix(in oklch, var(--good), transparent 90%); }
|
|
867
872
|
.abadge.bad { color: var(--bad); border-color: color-mix(in oklch, var(--bad), var(--line) 55%); background: color-mix(in oklch, var(--bad), transparent 90%); }
|
|
868
873
|
.abadge.warn { color: var(--warn); border-color: color-mix(in oklch, var(--warn), var(--line) 55%); background: color-mix(in oklch, var(--warn), transparent 90%); }
|
|
874
|
+
/* unavailable:独立第三态(非红非绿),证据评不了 ≠ 失败 */
|
|
875
|
+
.abadge.na { color: var(--muted, #8a8f98); border-color: var(--line); background: color-mix(in oklch, var(--muted, #8a8f98), transparent 92%); }
|
|
869
876
|
.abadge-th { opacity: 0.55; font-weight: 400; }
|
|
870
877
|
|
|
871
878
|
/* 可点击行的显式提示:send 行一个 "reply" 药丸,断言行一个右侧展开箭头;hover 时变亮。 */
|
|
@@ -956,9 +963,229 @@ a.brand:hover { opacity: 0.75; }
|
|
|
956
963
|
.code-orphans { border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; }
|
|
957
964
|
.code-orphans-head { font-size: 11px; color: var(--muted); margin-bottom: 8px; }
|
|
958
965
|
|
|
966
|
+
/* ── Attempt 详情(契约:docs/feature/reports/view.md「Attempt 详情」) ── */
|
|
967
|
+
.modal-sect {
|
|
968
|
+
font-size: 11px;
|
|
969
|
+
font-weight: 660;
|
|
970
|
+
letter-spacing: 0.06em;
|
|
971
|
+
text-transform: uppercase;
|
|
972
|
+
color: var(--muted);
|
|
973
|
+
margin-bottom: 6px;
|
|
974
|
+
}
|
|
975
|
+
/* 断言区:仅源码不可用时出现的 fallback 呈现面。 */
|
|
976
|
+
.attempt-asserts { margin-bottom: 14px; }
|
|
977
|
+
.attempt-asserts-open {
|
|
978
|
+
margin-top: 6px;
|
|
979
|
+
border: 1px solid var(--line);
|
|
980
|
+
border-radius: 10px;
|
|
981
|
+
background: var(--panel);
|
|
982
|
+
overflow: hidden;
|
|
983
|
+
}
|
|
984
|
+
/* 复用 CodeView 的 .assert-detail 排版,但脱离代码行上下文:去掉行内缩进与左缘。 */
|
|
985
|
+
.attempt-asserts .assert-detail { padding: 10px 12px; border-left: 0; background: transparent; }
|
|
986
|
+
/* 长 expected/received(整文件 dump)一律有界滚动:断言明细无论住在失败索引 fallback、
|
|
987
|
+
源码行展开还是 NoSourceBody,都不把页面其余区块挤出视口。 */
|
|
988
|
+
.assert-reason { max-height: 180px; overflow-y: auto; }
|
|
989
|
+
/* 失败索引行:summary 一行是标题(徽章/名字/matcher/锚),明细收在行内一次展开。 */
|
|
990
|
+
.fail-row { border-bottom: 1px solid var(--line); }
|
|
991
|
+
.fail-row:last-child { border-bottom: 0; }
|
|
992
|
+
.fail-sum {
|
|
993
|
+
display: flex;
|
|
994
|
+
align-items: baseline;
|
|
995
|
+
flex-wrap: wrap;
|
|
996
|
+
gap: 4px 8px;
|
|
997
|
+
padding: 8px 12px;
|
|
998
|
+
cursor: pointer;
|
|
999
|
+
user-select: none;
|
|
1000
|
+
list-style: none;
|
|
1001
|
+
min-width: 0;
|
|
1002
|
+
}
|
|
1003
|
+
.fail-sum::-webkit-details-marker { display: none; }
|
|
1004
|
+
.fail-sum::before {
|
|
1005
|
+
content: "";
|
|
1006
|
+
width: 0;
|
|
1007
|
+
height: 0;
|
|
1008
|
+
border-left: 4px solid var(--soft);
|
|
1009
|
+
border-top: 3.5px solid transparent;
|
|
1010
|
+
border-bottom: 3.5px solid transparent;
|
|
1011
|
+
flex-shrink: 0;
|
|
1012
|
+
align-self: center;
|
|
1013
|
+
transition: transform 0.12s;
|
|
1014
|
+
}
|
|
1015
|
+
.fail-row[open] > .fail-sum::before { transform: rotate(90deg); }
|
|
1016
|
+
.fail-sum:hover { background: color-mix(in oklch, var(--panel), var(--line) 30%); }
|
|
1017
|
+
.fail-name {
|
|
1018
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1019
|
+
font-size: 12px;
|
|
1020
|
+
color: var(--text);
|
|
1021
|
+
overflow-wrap: anywhere;
|
|
1022
|
+
}
|
|
1023
|
+
.fail-matcher {
|
|
1024
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1025
|
+
font-size: 11.5px;
|
|
1026
|
+
color: var(--muted);
|
|
1027
|
+
overflow-wrap: anywhere;
|
|
1028
|
+
}
|
|
1029
|
+
.fail-body {
|
|
1030
|
+
display: flex;
|
|
1031
|
+
flex-direction: column;
|
|
1032
|
+
gap: 6px;
|
|
1033
|
+
padding: 2px 12px 10px 24px;
|
|
1034
|
+
}
|
|
1035
|
+
.assert-loc {
|
|
1036
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1037
|
+
font-size: 11px;
|
|
1038
|
+
color: var(--soft);
|
|
1039
|
+
margin-left: 8px;
|
|
1040
|
+
white-space: nowrap;
|
|
1041
|
+
}
|
|
1042
|
+
.asserts-passed { margin-top: 8px; }
|
|
1043
|
+
.asserts-passed > summary,
|
|
1044
|
+
.asserts-passed-group > summary {
|
|
1045
|
+
cursor: pointer;
|
|
1046
|
+
user-select: none;
|
|
1047
|
+
font-size: 12px;
|
|
1048
|
+
color: var(--muted);
|
|
1049
|
+
}
|
|
1050
|
+
.asserts-passed > summary:hover,
|
|
1051
|
+
.asserts-passed-group > summary:hover { color: var(--text); }
|
|
1052
|
+
.asserts-passed-group { margin: 6px 0 0 14px; }
|
|
1053
|
+
.asserts-passed-group > .assert-detail { margin-top: 4px; }
|
|
1054
|
+
|
|
1055
|
+
/* ── Attempt 详情:统一时间树(整区默认收合;展开后主链分解条 + 默认收合的 children) ── */
|
|
1056
|
+
.attempt-timing { margin: 14px 0; }
|
|
1057
|
+
summary.timing-sum {
|
|
1058
|
+
display: flex;
|
|
1059
|
+
align-items: center;
|
|
1060
|
+
gap: 8px;
|
|
1061
|
+
cursor: pointer;
|
|
1062
|
+
user-select: none;
|
|
1063
|
+
list-style: none;
|
|
1064
|
+
}
|
|
1065
|
+
summary.timing-sum::-webkit-details-marker { display: none; }
|
|
1066
|
+
summary.timing-sum::before {
|
|
1067
|
+
content: "";
|
|
1068
|
+
width: 0;
|
|
1069
|
+
height: 0;
|
|
1070
|
+
border-left: 4px solid var(--soft);
|
|
1071
|
+
border-top: 3.5px solid transparent;
|
|
1072
|
+
border-bottom: 3.5px solid transparent;
|
|
1073
|
+
flex-shrink: 0;
|
|
1074
|
+
transition: transform 0.12s;
|
|
1075
|
+
}
|
|
1076
|
+
details[open] > summary.timing-sum::before { transform: rotate(90deg); }
|
|
1077
|
+
summary.timing-sum:hover .modal-sect { color: var(--text); }
|
|
1078
|
+
.timing-sum .modal-sect { margin-bottom: 0; }
|
|
1079
|
+
.phase-bar {
|
|
1080
|
+
display: flex;
|
|
1081
|
+
height: 8px;
|
|
1082
|
+
border-radius: 4px;
|
|
1083
|
+
overflow: hidden;
|
|
1084
|
+
gap: 1px;
|
|
1085
|
+
margin: 7px 0 8px;
|
|
1086
|
+
}
|
|
1087
|
+
.phase-seg { min-width: 2px; flex-basis: 0; }
|
|
1088
|
+
.phase-seg-1 { background: var(--series-1); }
|
|
1089
|
+
.phase-seg-2 { background: var(--series-2); }
|
|
1090
|
+
.phase-seg-3 { background: var(--series-3); }
|
|
1091
|
+
.phase-seg-4 { background: var(--series-4); }
|
|
1092
|
+
.phase-seg-5 { background: var(--series-5); }
|
|
1093
|
+
.phase-seg-6 { background: var(--series-6); }
|
|
1094
|
+
.phase-seg-bad { background: var(--bad); }
|
|
1095
|
+
.phase-list,
|
|
1096
|
+
.timing-kids { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
|
|
1097
|
+
.timing-kids { padding-left: 18px; margin-top: 2px; }
|
|
1098
|
+
.phase-row,
|
|
1099
|
+
.tnode-row { display: flex; align-items: center; gap: 7px; font-size: 12px; line-height: 1.7; min-width: 0; }
|
|
1100
|
+
summary.phase-row,
|
|
1101
|
+
summary.tnode-row { cursor: pointer; user-select: none; list-style: none; }
|
|
1102
|
+
summary.phase-row::-webkit-details-marker,
|
|
1103
|
+
summary.tnode-row::-webkit-details-marker { display: none; }
|
|
1104
|
+
summary.phase-row::before,
|
|
1105
|
+
summary.tnode-row::before {
|
|
1106
|
+
content: "";
|
|
1107
|
+
width: 0;
|
|
1108
|
+
height: 0;
|
|
1109
|
+
border-left: 4px solid var(--soft);
|
|
1110
|
+
border-top: 3.5px solid transparent;
|
|
1111
|
+
border-bottom: 3.5px solid transparent;
|
|
1112
|
+
flex-shrink: 0;
|
|
1113
|
+
transition: transform 0.12s;
|
|
1114
|
+
}
|
|
1115
|
+
details[open] > summary.phase-row::before,
|
|
1116
|
+
details[open] > summary.tnode-row::before { transform: rotate(90deg); }
|
|
1117
|
+
/* 无 children 的行:占位对齐有箭头的行。 */
|
|
1118
|
+
li.phase-row::before,
|
|
1119
|
+
li.tnode-row::before { content: ""; width: 4px; flex-shrink: 0; }
|
|
1120
|
+
.phase-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
|
|
1121
|
+
.phase-dot-none { background: transparent; }
|
|
1122
|
+
/* 名称列定宽:各行的耗时起始对齐成一列(最长 phase 名 telemetry.configure 也在宽度内)。 */
|
|
1123
|
+
.phase-name { color: var(--text); white-space: nowrap; width: 168px; flex-shrink: 0; }
|
|
1124
|
+
.phase-dur { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
1125
|
+
.phase-fail { color: var(--bad); font-weight: 650; }
|
|
1126
|
+
.phase-kids {
|
|
1127
|
+
font-size: 10px;
|
|
1128
|
+
color: var(--soft);
|
|
1129
|
+
border: 1px solid var(--line);
|
|
1130
|
+
border-radius: 999px;
|
|
1131
|
+
padding: 0 6px;
|
|
1132
|
+
line-height: 1.5;
|
|
1133
|
+
}
|
|
1134
|
+
.tnode-label {
|
|
1135
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1136
|
+
font-size: 11.5px;
|
|
1137
|
+
color: var(--muted);
|
|
1138
|
+
overflow: hidden;
|
|
1139
|
+
text-overflow: ellipsis;
|
|
1140
|
+
white-space: nowrap;
|
|
1141
|
+
min-width: 0;
|
|
1142
|
+
}
|
|
1143
|
+
.tnode-meta { font-size: 11px; color: var(--soft); padding: 3px 0 3px 18px; }
|
|
1144
|
+
.tnode-trace { margin: 4px 0 6px 18px; }
|
|
1145
|
+
.phase-closing { margin-top: 8px; }
|
|
1146
|
+
.phase-closing-head { font-size: 11px; color: var(--soft); margin-bottom: 2px; }
|
|
1147
|
+
|
|
1148
|
+
/* usage / sandbox 元信息 chips。 */
|
|
1149
|
+
.meta-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
|
|
1150
|
+
.meta-chip {
|
|
1151
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1152
|
+
font-size: 11px;
|
|
1153
|
+
color: var(--muted);
|
|
1154
|
+
border: 1px solid var(--line);
|
|
1155
|
+
border-radius: 6px;
|
|
1156
|
+
padding: 3px 9px;
|
|
1157
|
+
background: var(--panel);
|
|
1158
|
+
}
|
|
1159
|
+
.meta-chip b { color: var(--text); font-weight: 640; }
|
|
1160
|
+
|
|
959
1161
|
/* lightweight TS syntax tokens */
|
|
960
1162
|
.tok-comment { color: var(--soft); font-style: italic; }
|
|
961
1163
|
.tok-str { color: light-dark(#0a7d3c, #7ee787); }
|
|
962
1164
|
.tok-kw { color: light-dark(#9333ea, #c792ea); }
|
|
963
1165
|
.tok-num { color: light-dark(#b25000, #f0a36b); }
|
|
964
1166
|
.tok-fn { color: light-dark(#0b62d6, #82aaff); }
|
|
1167
|
+
|
|
1168
|
+
/* ── 外壳:导航右侧外部链接与页脚(docs/feature/reports/library/shell.md) ── */
|
|
1169
|
+
.shell-links { display: flex; gap: 8px; align-items: center; margin-left: auto; }
|
|
1170
|
+
/* 链接自带 padding 撑大可点击区域(而非仅靠文字宽度);.shell-links 的 gap 相应收窄,
|
|
1171
|
+
两条链接之间的视觉间距(padding + gap + padding)与改前基本持平。 */
|
|
1172
|
+
.shell-links a { color: var(--soft); text-decoration: none; font-size: 13px; display: inline-flex; align-items: center; gap: 6px; padding: 6px 4px; }
|
|
1173
|
+
.shell-links a:hover { color: var(--text); }
|
|
1174
|
+
/* ReportLink.icon:内联 SVG 字标,渲染在 label 前(shell.md「字段穷尽」)。 */
|
|
1175
|
+
.shell-link-icon { display: inline-flex; align-items: center; }
|
|
1176
|
+
.shell-link-icon svg { width: 14px; height: 14px; display: block; fill: currentColor; }
|
|
1177
|
+
.site-footer {
|
|
1178
|
+
display: flex;
|
|
1179
|
+
gap: 12px;
|
|
1180
|
+
align-items: baseline;
|
|
1181
|
+
justify-content: center;
|
|
1182
|
+
flex-wrap: wrap;
|
|
1183
|
+
padding: 24px 24px 32px;
|
|
1184
|
+
color: var(--soft);
|
|
1185
|
+
font-size: 12px;
|
|
1186
|
+
}
|
|
1187
|
+
/* 品牌行:hero 之下、品牌蓝,恒带官网链接(shell.md「行为约束」)。 */
|
|
1188
|
+
.hero .powered-by { display: block; margin-top: 16px; font-size: 12px; }
|
|
1189
|
+
.hero .powered-by a { color: var(--blue); text-decoration: none; }
|
|
1190
|
+
.hero .powered-by a:hover { text-decoration: underline; }
|
|
1191
|
+
.nre-page-error pre { white-space: pre-wrap; }
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
// cases: docs/engineering/unit-tests/reports/cases.md
|
|
1
2
|
// niceeval view 的报告槽与宿主组合语义(docs/feature/reports/architecture.md「Selection 是计算入口」
|
|
2
|
-
// 与裁决记录 6;公开行为准绳 docs-site/zh/
|
|
3
|
+
// 与裁决记录 6;公开行为准绳 docs-site/zh/how-to/viewing-results.mdx / custom-reports.mdx)。
|
|
3
4
|
// 覆盖:
|
|
4
|
-
// - 组合语义与 show 对齐:位置前缀收窄报告槽
|
|
5
|
-
// -
|
|
6
|
-
//
|
|
5
|
+
// - 组合语义与 show 对齐:位置前缀收窄报告槽 Scope、--experiment 过滤、匹配不到直说;
|
|
6
|
+
// - 输入语义:位置参数只表示 eval id 前缀(不随文件系统状态改变),结果根走 --results,
|
|
7
|
+
// 单开一份快照走 --snapshot(文件不可读时失败);
|
|
8
|
+
// - 报告槽恒在:裸跑填充内建报告,--report 整槽替换;en / zh-CN 双语各渲染一遍;
|
|
7
9
|
// 裸跑 ≡ --report <re-export ExperimentComparison 的文件>(等价性);
|
|
8
10
|
// - --out 静态导出:index.html 含两个语言的报告块、官方样式与增强 runtime,报告块零 <script>;
|
|
9
11
|
// - dev server 装载语义:报告文件变更 → 下次装载整页重算(mtime cache-busting)。
|
|
@@ -20,7 +22,8 @@ import { afterEach, describe, expect, it } from "vitest";
|
|
|
20
22
|
// (see src/view/data.ts's comment) — a raw-src import would be a structurally-identical but
|
|
21
23
|
// `instanceof`-incompatible class.
|
|
22
24
|
import { ReportLoadError } from "../../dist/report/load.js";
|
|
23
|
-
import { ViewInputError, loadViewScan } from "./data.ts";
|
|
25
|
+
import { ViewInputError, loadViewScan, type ViewScan } from "./data.ts";
|
|
26
|
+
import { renderHtml } from "./server.ts";
|
|
24
27
|
import { buildView, resolveViewInput } from "./index.ts";
|
|
25
28
|
import { runShow } from "../show/index.ts";
|
|
26
29
|
import { RESULTS_FORMAT, RESULTS_SCHEMA_VERSION, type EvalResult, type Verdict } from "../types.ts";
|
|
@@ -84,7 +87,7 @@ async function seedRoot(): Promise<string> {
|
|
|
84
87
|
await writeSnapshot(root, "compare_bub", "2026-07-08T10-00-00-000Z", { experimentId: "compare/bub", agent: "bub", startedAt: "2026-07-08T10:00:00.000Z" }, [
|
|
85
88
|
res("weather/brooklyn", "passed"),
|
|
86
89
|
res("fixtures/button", "failed", {
|
|
87
|
-
assertions: [{ name: 'fileChanged("Button.tsx")', severity: "gate", score: 0,
|
|
90
|
+
assertions: [{ name: 'fileChanged("Button.tsx")', severity: "gate", score: 0, outcome: "failed" as const }],
|
|
88
91
|
}),
|
|
89
92
|
]);
|
|
90
93
|
await writeSnapshot(root, "compare_codex", "2026-07-09T10-00-00-000Z", { experimentId: "compare/codex", agent: "codex", startedAt: "2026-07-09T10:00:00.000Z" }, [
|
|
@@ -95,47 +98,62 @@ async function seedRoot(): Promise<string> {
|
|
|
95
98
|
|
|
96
99
|
// ───────────────────────── 位置参数语义(单文件模式共存) ─────────────────────────
|
|
97
100
|
|
|
98
|
-
describe("resolveViewInput ·
|
|
99
|
-
it("
|
|
101
|
+
describe("resolveViewInput · 输入语义", () => {
|
|
102
|
+
it("位置参数只表示 eval id 前缀:文件与目录路径不改变含义", async () => {
|
|
100
103
|
const root = await seedRoot();
|
|
101
104
|
const file = join(root, "compare_bub", "2026-07-08T10-00-00-000Z", "snapshot.json");
|
|
102
|
-
|
|
105
|
+
// 恰好是存在文件/目录的位置参数也照常当前缀(后续按「无匹配」报错,不做模糊猜测)。
|
|
106
|
+
expect(resolveViewInput(root, [file])).toEqual({ patterns: [file] });
|
|
107
|
+
expect(resolveViewInput(root, ["compare_bub"])).toEqual({ patterns: ["compare_bub"] });
|
|
103
108
|
expect(resolveViewInput(root, ["weather", "fixtures/button"])).toEqual({
|
|
104
109
|
patterns: ["weather", "fixtures/button"],
|
|
105
110
|
});
|
|
106
111
|
expect(resolveViewInput(root, [])).toEqual({ patterns: [] });
|
|
107
112
|
});
|
|
108
113
|
|
|
109
|
-
it("
|
|
114
|
+
it("--snapshot 单开一份快照文件;文件不可读时失败(与扫描模式的跳过相反)", async () => {
|
|
110
115
|
const root = await seedRoot();
|
|
111
|
-
|
|
112
|
-
expect(
|
|
116
|
+
const file = join(root, "compare_bub", "2026-07-08T10-00-00-000Z", "snapshot.json");
|
|
117
|
+
expect(resolveViewInput(root, [], { snapshot: file })).toEqual({ input: file, patterns: [] });
|
|
118
|
+
expect(() => resolveViewInput(root, [], { snapshot: join(root, "nope.json") })).toThrow(ViewInputError);
|
|
119
|
+
expect(() => resolveViewInput(root, [], { snapshot: join(root, "nope.json") })).toThrow(/--snapshot/);
|
|
120
|
+
// 目录不是快照文件。
|
|
121
|
+
expect(() => resolveViewInput(root, [], { snapshot: join(root, "compare_bub") })).toThrow(/--snapshot/);
|
|
113
122
|
});
|
|
114
123
|
|
|
115
|
-
it("
|
|
124
|
+
it("--results 指向不存在的目录:直说", async () => {
|
|
116
125
|
const root = await seedRoot();
|
|
117
|
-
|
|
118
|
-
expect(() => resolveViewInput(root, [file, "weather"])).toThrow(/exactly one path/);
|
|
119
|
-
expect(() => resolveViewInput(root, [file], root)).toThrow(/--run/);
|
|
126
|
+
expect(() => resolveViewInput(root, [], { results: join(root, "nope") })).toThrow(/Results directory not found/);
|
|
120
127
|
});
|
|
121
128
|
|
|
122
|
-
it("--
|
|
129
|
+
it("--results 换结果根,位置参数仍是前缀", async () => {
|
|
123
130
|
const root = await seedRoot();
|
|
124
|
-
expect(
|
|
131
|
+
expect(resolveViewInput("/elsewhere", ["weather"], { results: root })).toEqual({ input: root, patterns: ["weather"] });
|
|
125
132
|
});
|
|
126
133
|
|
|
127
|
-
it("--
|
|
134
|
+
it("--results 与 --snapshot 互斥:报错直说", async () => {
|
|
128
135
|
const root = await seedRoot();
|
|
129
|
-
|
|
136
|
+
const file = join(root, "compare_bub", "2026-07-08T10-00-00-000Z", "snapshot.json");
|
|
137
|
+
expect(() => resolveViewInput(root, [], { results: root, snapshot: file })).toThrow(/mutually exclusive/);
|
|
130
138
|
});
|
|
131
139
|
});
|
|
132
140
|
|
|
141
|
+
|
|
142
|
+
/** 单页报告(裸跑 / 树形态 --report)的报告槽 HTML:规范化后唯一页 id 恒为 `report`。 */
|
|
143
|
+
function slotHtml(scan: ViewScan): { en: string; "zh-CN": string } {
|
|
144
|
+
expect(scan.reportPages).toHaveLength(1);
|
|
145
|
+
expect(scan.reportPages[0]!.id).toBe("report");
|
|
146
|
+
return scan.reportPages[0]!.html as { en: string; "zh-CN": string };
|
|
147
|
+
}
|
|
148
|
+
|
|
133
149
|
// ───────────────────────── 组合语义(与 show 对齐) ─────────────────────────
|
|
134
150
|
|
|
135
151
|
describe("loadViewScan · 组合语义", () => {
|
|
136
152
|
it("位置前缀收窄报告槽 Selection;证据室快照不收窄,深链恒可达", async () => {
|
|
137
153
|
const root = await seedRoot();
|
|
138
|
-
const
|
|
154
|
+
const scan = await loadViewScan(root, { patterns: ["weather"] });
|
|
155
|
+
const { viewData } = scan;
|
|
156
|
+
const reportHtml = slotHtml(scan);
|
|
139
157
|
// 报告槽:两实验都只剩 weather/brooklyn 一题,范围外的失败不再出现。
|
|
140
158
|
expect(reportHtml.en).toContain("compare/bub");
|
|
141
159
|
expect(reportHtml.en).toContain("compare/codex");
|
|
@@ -147,7 +165,7 @@ describe("loadViewScan · 组合语义", () => {
|
|
|
147
165
|
|
|
148
166
|
it("--experiment 过滤:报告槽 Selection 只留该实验", async () => {
|
|
149
167
|
const root = await seedRoot();
|
|
150
|
-
const
|
|
168
|
+
const reportHtml = slotHtml(await loadViewScan(root, { experiment: "compare/codex" }));
|
|
151
169
|
expect(reportHtml.en).toContain("compare/codex");
|
|
152
170
|
expect(reportHtml.en).not.toContain("compare/bub");
|
|
153
171
|
});
|
|
@@ -161,7 +179,7 @@ describe("loadViewScan · 组合语义", () => {
|
|
|
161
179
|
|
|
162
180
|
it("全部缺省:报告槽使用现刻水位口径(与 show 相同的合成规则)", async () => {
|
|
163
181
|
const root = await seedRoot();
|
|
164
|
-
const
|
|
182
|
+
const reportHtml = slotHtml(await loadViewScan(root));
|
|
165
183
|
// 默认报告的榜单含两个实验;官方组件的稳定类名在场。
|
|
166
184
|
expect(reportHtml.en).toContain("compare/bub");
|
|
167
185
|
expect(reportHtml.en).toContain("compare/codex");
|
|
@@ -174,19 +192,21 @@ describe("loadViewScan · 组合语义", () => {
|
|
|
174
192
|
describe("loadViewScan · 默认报告槽(裸跑)", () => {
|
|
175
193
|
it("裸跑产出的报告槽 HTML 与 --report <re-export ExperimentComparison 的文件> 完全一致(双语)", async () => {
|
|
176
194
|
const root = await seedRoot();
|
|
177
|
-
const bare = await loadViewScan(root);
|
|
178
|
-
const viaReport =
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
195
|
+
const bare = slotHtml(await loadViewScan(root));
|
|
196
|
+
const viaReport = slotHtml(
|
|
197
|
+
await loadViewScan(root, {
|
|
198
|
+
report: { path: DEFAULT_REPORT_REEXPORT, cwd: root },
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
201
|
+
expect(bare.en).toBe(viaReport.en);
|
|
202
|
+
expect(bare["zh-CN"]).toBe(viaReport["zh-CN"]);
|
|
183
203
|
});
|
|
184
204
|
|
|
185
205
|
it("报告槽双语渲染:同一棵树按 locale 渲染两遍,chrome 文案分语言、数据不分语言", async () => {
|
|
186
206
|
const root = await seedRoot();
|
|
187
|
-
const
|
|
188
|
-
expect(reportHtml.en).toContain("
|
|
189
|
-
expect(reportHtml["zh-CN"]).toContain("
|
|
207
|
+
const reportHtml = slotHtml(await loadViewScan(root));
|
|
208
|
+
expect(reportHtml.en).toContain("End-to-end pass rate"); // ExperimentList 主行(en)
|
|
209
|
+
expect(reportHtml["zh-CN"]).toContain("端到端成功率"); // ExperimentList 主行(zh-CN)
|
|
190
210
|
for (const html of [reportHtml.en, reportHtml["zh-CN"]]) {
|
|
191
211
|
expect(html).toContain("compare/bub");
|
|
192
212
|
// 失败案例深链进证据室:不透明 AttemptLocator 单段路由 `#/attempt/@<locator>`,
|
|
@@ -198,8 +218,9 @@ describe("loadViewScan · 默认报告槽(裸跑)", () => {
|
|
|
198
218
|
|
|
199
219
|
it("失败清单与警告住在报告槽里:ExperimentList 列出失败,壳的 viewData 不携带统计产物", async () => {
|
|
200
220
|
const root = await seedRoot();
|
|
201
|
-
const
|
|
202
|
-
|
|
221
|
+
const scan = await loadViewScan(root);
|
|
222
|
+
const { viewData } = scan;
|
|
223
|
+
expect(slotHtml(scan).en).toContain("fixtures/button");
|
|
203
224
|
// viewData 只有证据室数据:快照 + skipped + 壳元信息。
|
|
204
225
|
expect(viewData).not.toHaveProperty("overview");
|
|
205
226
|
expect(viewData).not.toHaveProperty("table");
|
|
@@ -213,14 +234,14 @@ describe("loadViewScan · --report 报告槽", () => {
|
|
|
213
234
|
it("报告树渲染为静态 HTML:官方水位 + 自定义摆法 + <Style> 产物 + 证据室深链,零 <script>", async () => {
|
|
214
235
|
const root = await seedRoot();
|
|
215
236
|
const scan = await loadViewScan(root, { report: { path: EXAM_REPORT, cwd: root } });
|
|
216
|
-
const html = scan.
|
|
237
|
+
const html = slotHtml(scan).en;
|
|
217
238
|
expect(html).toContain("考试成绩单"); // 自定义 Section
|
|
218
239
|
expect(html).toContain("nre-"); // 官方组件的稳定类名
|
|
219
240
|
expect(html).toContain("<style>.exam-note { color: #4a7; }</style>"); // <Style> 随树带走
|
|
220
241
|
expect(html).toMatch(/href="#\/attempt\/@[0-9a-z]+"/); // 失败案例深链进证据室(单段 locator 路由)
|
|
221
242
|
expect(html).not.toContain("<script"); // 报告槽产物零客户端 JS,不 hydrate
|
|
222
243
|
// 用户报告同样双语渲染两遍(壳按界面语言摆放)。
|
|
223
|
-
expect(scan
|
|
244
|
+
expect(slotHtml(scan)["zh-CN"]).toContain("考试成绩单");
|
|
224
245
|
// 证据室数据契约(__NICEEVAL_VIEW_DATA__)原样保留:快照、locator、skipped 不动。
|
|
225
246
|
expect(scan.viewData.snapshots.length).toBeGreaterThan(0);
|
|
226
247
|
expect(scan.viewData.snapshots.flatMap((s) => s.results).every((r) => r.locator)).toBe(true);
|
|
@@ -234,15 +255,16 @@ describe("loadViewScan · --report 报告槽", () => {
|
|
|
234
255
|
report: { path: EXAM_REPORT, cwd: root },
|
|
235
256
|
});
|
|
236
257
|
// 范围外的失败(fixtures/button)不再出现在报告里;范围内的实验行都在。
|
|
237
|
-
|
|
238
|
-
expect(
|
|
239
|
-
expect(
|
|
258
|
+
const html = slotHtml(scan);
|
|
259
|
+
expect(html.en).not.toContain("fixtures/button");
|
|
260
|
+
expect(html.en).toContain("compare/bub");
|
|
261
|
+
expect(html.en).toContain("compare/codex");
|
|
240
262
|
});
|
|
241
263
|
|
|
242
264
|
it("show --report 与 view --report 吃同一个报告文件,判定口径一致", async () => {
|
|
243
265
|
const root = await seedRoot();
|
|
244
266
|
let text = "";
|
|
245
|
-
const code = await runShow(root, [], {
|
|
267
|
+
const code = await runShow(root, [], { results: root, report: EXAM_REPORT }, {
|
|
246
268
|
out: (s) => (text += s),
|
|
247
269
|
err: () => {},
|
|
248
270
|
width: 120,
|
|
@@ -250,9 +272,10 @@ describe("loadViewScan · --report 报告槽", () => {
|
|
|
250
272
|
expect(code).toBe(0);
|
|
251
273
|
const scan = await loadViewScan(root, { report: { path: EXAM_REPORT, cwd: root } });
|
|
252
274
|
// 同一棵树的两个面:同一份失败清单与同一个自定义 Section。
|
|
275
|
+
const html = slotHtml(scan);
|
|
253
276
|
for (const needle of ["考试成绩单", "compare/bub", "compare/codex", "fixtures/button"]) {
|
|
254
277
|
expect(text).toContain(needle);
|
|
255
|
-
expect(
|
|
278
|
+
expect(html.en).toContain(needle);
|
|
256
279
|
}
|
|
257
280
|
});
|
|
258
281
|
|
|
@@ -269,6 +292,57 @@ describe("loadViewScan · --report 报告槽", () => {
|
|
|
269
292
|
});
|
|
270
293
|
});
|
|
271
294
|
|
|
295
|
+
// ───────────────────── 外壳:title 落点(hero / <title>)与 ReportLink.icon ─────────────────────
|
|
296
|
+
|
|
297
|
+
describe("loadViewScan · 外壳标题与 ReportLink.icon", () => {
|
|
298
|
+
/** 不经包入口也合法的最小外壳报告(与下方 reportSource 同一姿势):声明 title 与带 icon 的 link。 */
|
|
299
|
+
function shellReportSource(): string {
|
|
300
|
+
return [
|
|
301
|
+
'const FACES = Symbol.for("niceeval.report.faces");',
|
|
302
|
+
'const DEFINITION = Symbol.for("niceeval.report.definition");',
|
|
303
|
+
"const Block = (props) => Block[FACES].web(props);",
|
|
304
|
+
"Block[FACES] = {",
|
|
305
|
+
' web: () => "SHELL_BODY",',
|
|
306
|
+
' text: () => "SHELL_BODY",',
|
|
307
|
+
"};",
|
|
308
|
+
"const definition = {",
|
|
309
|
+
' kind: "report",',
|
|
310
|
+
' title: { en: "Memory Evals", "zh-CN": "记忆能力评测" },',
|
|
311
|
+
' links: [{ label: "GitHub", href: "https://example.com", icon: { svg: "<svg data-mark></svg>" } }],',
|
|
312
|
+
" scripts: [],",
|
|
313
|
+
" styles: [],",
|
|
314
|
+
' pages: [{ id: "report", title: "Report", content: { $$typeof: Symbol.for("react.transitional.element"), type: Block, props: {}, key: null } }],',
|
|
315
|
+
"};",
|
|
316
|
+
"Object.defineProperty(definition, DEFINITION, { value: true });",
|
|
317
|
+
"export default definition;",
|
|
318
|
+
"",
|
|
319
|
+
].join("\n");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
it("def.title 进 viewData.report.title,初始 <title> 同源烘进 HTML;link 的 icon svg 原样透传", async () => {
|
|
323
|
+
const root = await seedRoot();
|
|
324
|
+
const path = join(root, "shell-report.mjs");
|
|
325
|
+
await writeFile(path, shellReportSource(), "utf-8");
|
|
326
|
+
const scan = await loadViewScan(root, { report: { path, cwd: root } });
|
|
327
|
+
// 标题回退链第一级:def.title 原样(hero 与浏览器标题都吃它)。
|
|
328
|
+
expect(scan.viewData.report?.title).toEqual({ en: "Memory Evals", "zh-CN": "记忆能力评测" });
|
|
329
|
+
// ReportLink.icon 经 viewData 序列化边界原样透传(web 面在 label 前渲染,静态导出原样内联)。
|
|
330
|
+
expect(scan.viewData.report?.links).toEqual([
|
|
331
|
+
{ label: "GitHub", href: "https://example.com", icon: { svg: "<svg data-mark></svg>" } },
|
|
332
|
+
]);
|
|
333
|
+
const html = await renderHtml(scan);
|
|
334
|
+
expect(html).toContain("<title>Memory Evals</title>");
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it("无 def.title 且快照无 name:标题落内置文案「Eval 运行结果 / Eval Results」,不再是产品名", async () => {
|
|
338
|
+
const root = await seedRoot(); // seedRoot 的快照都没有 name
|
|
339
|
+
const scan = await loadViewScan(root);
|
|
340
|
+
expect(scan.viewData.report?.title).toEqual({ en: "Eval Results", "zh-CN": "Eval 运行结果" });
|
|
341
|
+
const html = await renderHtml(scan);
|
|
342
|
+
expect(html).toContain("<title>Eval Results</title>");
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
272
346
|
// ───────────────────────── dev server 装载语义:整页重算 ─────────────────────────
|
|
273
347
|
|
|
274
348
|
describe("loadViewScan · 报告文件变更整页重算", () => {
|
|
@@ -276,20 +350,21 @@ describe("loadViewScan · 报告文件变更整页重算", () => {
|
|
|
276
350
|
function reportSource(marker: string): string {
|
|
277
351
|
return [
|
|
278
352
|
'const FACES = Symbol.for("niceeval.report.faces");',
|
|
353
|
+
'const DEFINITION = Symbol.for("niceeval.report.definition");',
|
|
279
354
|
"const Block = (props) => Block[FACES].web(props);",
|
|
280
355
|
"Block[FACES] = {",
|
|
281
356
|
` web: () => "${marker}",`,
|
|
282
357
|
` text: () => "${marker}",`,
|
|
283
358
|
"};",
|
|
284
|
-
"
|
|
285
|
-
'
|
|
286
|
-
"
|
|
287
|
-
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
" key: null,",
|
|
291
|
-
" }),",
|
|
359
|
+
"const definition = {",
|
|
360
|
+
' kind: "report",',
|
|
361
|
+
" links: [],",
|
|
362
|
+
" scripts: [],",
|
|
363
|
+
" styles: [],",
|
|
364
|
+
' pages: [{ id: "report", title: "Report", content: { $$typeof: Symbol.for("react.transitional.element"), type: Block, props: {}, key: null } }],',
|
|
292
365
|
"};",
|
|
366
|
+
"Object.defineProperty(definition, DEFINITION, { value: true });",
|
|
367
|
+
"export default definition;",
|
|
293
368
|
"",
|
|
294
369
|
].join("\n");
|
|
295
370
|
}
|
|
@@ -298,16 +373,16 @@ describe("loadViewScan · 报告文件变更整页重算", () => {
|
|
|
298
373
|
const root = await seedRoot();
|
|
299
374
|
const path = join(root, "report.mjs");
|
|
300
375
|
await writeFile(path, reportSource("FIRST_RENDER"), "utf-8");
|
|
301
|
-
const first = await loadViewScan(root, { report: { path, cwd: root } });
|
|
302
|
-
expect(first.
|
|
376
|
+
const first = slotHtml(await loadViewScan(root, { report: { path, cwd: root } }));
|
|
377
|
+
expect(first.en).toContain("FIRST_RENDER");
|
|
303
378
|
|
|
304
379
|
await writeFile(path, reportSource("SECOND_RENDER"), "utf-8");
|
|
305
380
|
// mtime 精度兜底:显式把 mtime 拨到未来,确保与首次装载可区分。
|
|
306
381
|
const future = new Date(Date.now() + 5000);
|
|
307
382
|
await utimes(path, future, future);
|
|
308
|
-
const second = await loadViewScan(root, { report: { path, cwd: root } });
|
|
309
|
-
expect(second.
|
|
310
|
-
expect(second.
|
|
383
|
+
const second = slotHtml(await loadViewScan(root, { report: { path, cwd: root } }));
|
|
384
|
+
expect(second.en).toContain("SECOND_RENDER");
|
|
385
|
+
expect(second.en).not.toContain("FIRST_RENDER");
|
|
311
386
|
});
|
|
312
387
|
});
|
|
313
388
|
|
|
@@ -323,16 +398,16 @@ describe("buildView · --out 与 --report", () => {
|
|
|
323
398
|
await writeFile(join(artifactDir, "events.json"), "[]", "utf-8");
|
|
324
399
|
|
|
325
400
|
const out = join(root, "site");
|
|
326
|
-
await buildView({ input: root, out, scan: { report: { path: EXAM_REPORT, cwd: root } } });
|
|
401
|
+
await buildView({ input: root, out, allowSensitiveArtifacts: true, scan: { report: { path: EXAM_REPORT, cwd: root } } });
|
|
327
402
|
|
|
328
403
|
const html = await readFile(join(out, "index.html"), "utf-8");
|
|
329
404
|
// 双语两个 <template> 静态块都在,壳按界面语言摆放。
|
|
330
|
-
expect(html).toContain('<template id="niceeval-report-en">');
|
|
331
|
-
expect(html).toContain('<template id="niceeval-report-zh-CN">');
|
|
405
|
+
expect(html).toContain('<template id="niceeval-report-report-en">');
|
|
406
|
+
expect(html).toContain('<template id="niceeval-report-report-zh-CN">');
|
|
332
407
|
expect(html).toContain("考试成绩单");
|
|
333
408
|
expect(html).toContain("nre-"); // 官方组件样式(report/react/styles.css)随页注入
|
|
334
409
|
// 报告块本体零 <script>:静态块起于 <template>,内部只有标记与 <style>。
|
|
335
|
-
const block = html.split('<template id="niceeval-report-en">')[1]!.split("</template>")[0]!;
|
|
410
|
+
const block = html.split('<template id="niceeval-report-report-en">')[1]!.split("</template>")[0]!;
|
|
336
411
|
expect(block).not.toContain("<script");
|
|
337
412
|
// 证据室同站: artifact 按 /artifact/<base>/ 布局复制。
|
|
338
413
|
expect(
|
|
@@ -340,17 +415,47 @@ describe("buildView · --out 与 --report", () => {
|
|
|
340
415
|
).toBe(true);
|
|
341
416
|
});
|
|
342
417
|
|
|
418
|
+
it("--out 无档位:diff.json 有就带,o11y.json 永不复制", async () => {
|
|
419
|
+
const root = await seedRoot();
|
|
420
|
+
const artifactDir = join(root, "compare_codex", "2026-07-09T10-00-00-000Z", "weather", "brooklyn", "a0");
|
|
421
|
+
await mkdir(artifactDir, { recursive: true });
|
|
422
|
+
await writeFile(join(artifactDir, "events.json"), "[]", "utf-8");
|
|
423
|
+
await writeFile(join(artifactDir, "diff.json"), '{"windows":[]}', "utf-8");
|
|
424
|
+
await writeFile(join(artifactDir, "o11y.json"), "{}", "utf-8");
|
|
425
|
+
|
|
426
|
+
const out = join(root, "site");
|
|
427
|
+
await buildView({ input: root, out, allowSensitiveArtifacts: true });
|
|
428
|
+
const exported = join(out, "artifact", "compare_codex/2026-07-09T10-00-00-000Z/weather/brooklyn/a0");
|
|
429
|
+
expect(existsSync(join(exported, "diff.json"))).toBe(true);
|
|
430
|
+
expect(existsSync(join(exported, "events.json"))).toBe(true);
|
|
431
|
+
expect(existsSync(join(exported, "o11y.json"))).toBe(false);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it("--out 与位置参数 / --experiment 互斥:报错含 copySnapshots + filter 下一步", async () => {
|
|
435
|
+
const root = await seedRoot();
|
|
436
|
+
const out = join(root, "site");
|
|
437
|
+
for (const scan of [{ patterns: ["weather"] }, { experiment: "compare/bub" }]) {
|
|
438
|
+
const attempt = buildView({ input: root, out, allowSensitiveArtifacts: true, scan });
|
|
439
|
+
await expect(attempt).rejects.toBeInstanceOf(ViewInputError);
|
|
440
|
+
await expect(
|
|
441
|
+
buildView({ input: root, out, allowSensitiveArtifacts: true, scan }),
|
|
442
|
+
).rejects.toThrow(/copySnapshots/);
|
|
443
|
+
}
|
|
444
|
+
// 同参数不带 --out 时照常收窄报告槽(不报错)。
|
|
445
|
+
await expect(loadViewScan(root, { patterns: ["weather"] })).resolves.toBeTruthy();
|
|
446
|
+
});
|
|
447
|
+
|
|
343
448
|
it("默认导出(无 --report):报告槽填充 ExperimentComparison,双语块与增强 runtime 恒内联", async () => {
|
|
344
449
|
const root = await seedRoot();
|
|
345
450
|
const out = join(root, "site");
|
|
346
|
-
await buildView({ input: root, out });
|
|
451
|
+
await buildView({ input: root, out, allowSensitiveArtifacts: true });
|
|
347
452
|
const html = await readFile(join(out, "index.html"), "utf-8");
|
|
348
|
-
expect(html).toContain('<template id="niceeval-report-en">');
|
|
349
|
-
expect(html).toContain('<template id="niceeval-report-zh-CN">');
|
|
453
|
+
expect(html).toContain('<template id="niceeval-report-report-en">');
|
|
454
|
+
expect(html).toContain('<template id="niceeval-report-report-zh-CN">');
|
|
350
455
|
expect(html).toContain("nre-"); // 官方组件的稳定类名(KPI / 榜单 / 散点来自 nre 组件)
|
|
351
456
|
// 渐进增强 runtime(排序 / 过滤 / tooltip)恒内联;报告块本体仍零 <script>。
|
|
352
457
|
expect(html).toContain("__nreEnhanced");
|
|
353
|
-
const block = html.split('<template id="niceeval-report-en">')[1]!.split("</template>")[0]!;
|
|
458
|
+
const block = html.split('<template id="niceeval-report-report-en">')[1]!.split("</template>")[0]!;
|
|
354
459
|
expect(block).not.toContain("<script");
|
|
355
460
|
});
|
|
356
461
|
});
|