test(web): cover compacting status presentation

This commit is contained in:
2026-09-18 00:26:40 +09:00
parent 6d9c9cdb48
commit ecc0602f53
6 changed files with 70 additions and 25 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
"dev": "deno run -A npm:vite@7.2.7 dev",
"dev:backend": "cd ../.. && cargo run -p yoi-workspace-server --bin yoi-server -- serve --listen 127.0.0.1:8787",
"check": "deno run -A npm:@sveltejs/kit@2.49.4 sync && deno run -A npm:svelte-check@4.3.4 --tsconfig ./tsconfig.json",
"test": "deno test --allow-read=src,test,tests --allow-env=LOG,VSCODE_TEXTMATE_DEBUG,NODE_ENV tests/workspace-model.test.ts tests/workspace-catalog.test.ts tests/profile-api.test.ts tests/skill-api.test.ts src/lib/workspace/auth/model.test.ts tests/auth-api.test.ts src/lib/workspace/api/http.test.ts src/lib/workspace/api/workers.test.ts src/lib/workspace/header/breadcrumb-model.test.ts src/lib/workspace/console/chat-submit.test.ts src/lib/workspace/console/composer-delivery.test.ts test/composer-history.test.ts tests/composer-paste.test.ts src/lib/workspace/console/composer-command.test.ts src/lib/workspace/console/composer-draft.test.ts src/lib/workspace/console/composer-completion.test.ts src/lib/workspace/console/markdown.test.ts test/console/ansi.test.ts src/lib/workspace/console/model.test.ts src/lib/workspace/companion/api.test.ts tests/workdir-api.test.ts src/lib/workspace/console/tasks.test.ts test/ticket-detail-route-reuse.test.ts test/objective-pages.test.ts test/repositories/ui.test.ts src/lib/workspace/console/worker-console.ui.test.ts src/lib/workspace/settings/model.test.ts tests/runtime-connection.test.ts tests/runtime-management.test.ts tests/runtime-management-source.test.ts src/lib/workspace/sidebar/override-stack.test.ts src/lib/workspace/sidebar/workers.test.ts src/lib/workspace/sidebar/workspace-switcher.test.ts src/lib/workspace/sidebar/worker-subscription.test.ts src/lib/workspace/sidebar/worker-launch.test.ts test/sidebar/worker-actions.test.ts src/lib/workspace/tickets/merge-request-resources.test.ts src/lib/workspace/tickets/ticket-panel.test.ts test/merge-request-status.test.ts test/config-source/decodal-grammar.test.ts test/config-source/editor-state.test.ts test/config-source/fixed-schema-wrapper.test.ts test/config-source/toolchain.test.ts test/config-source/wasm-parity.test.ts test/repository-access/api.test.ts test/repository-access/loader.test.ts test/repository-access/ui.test.ts test/repository-ssh-connection-ui.test.ts",
"test": "deno test --allow-read=src,test,tests --allow-env=LOG,VSCODE_TEXTMATE_DEBUG,NODE_ENV tests/workspace-model.test.ts tests/workspace-catalog.test.ts tests/profile-api.test.ts tests/skill-api.test.ts src/lib/workspace/auth/model.test.ts tests/auth-api.test.ts src/lib/workspace/api/http.test.ts src/lib/workspace/api/workers.test.ts src/lib/workspace/header/breadcrumb-model.test.ts src/lib/workspace/console/chat-submit.test.ts src/lib/workspace/console/composer-delivery.test.ts test/composer-history.test.ts tests/composer-paste.test.ts src/lib/workspace/console/composer-command.test.ts src/lib/workspace/console/composer-draft.test.ts src/lib/workspace/console/composer-completion.test.ts src/lib/workspace/console/markdown.test.ts test/console/ansi.test.ts src/lib/workspace/console/model.test.ts src/lib/workspace/console/run-status.test.ts src/lib/workspace/companion/api.test.ts tests/workdir-api.test.ts src/lib/workspace/console/tasks.test.ts test/ticket-detail-route-reuse.test.ts test/objective-pages.test.ts test/repositories/ui.test.ts src/lib/workspace/console/worker-console.ui.test.ts src/lib/workspace/settings/model.test.ts tests/runtime-connection.test.ts tests/runtime-management.test.ts tests/runtime-management-source.test.ts src/lib/workspace/sidebar/override-stack.test.ts src/lib/workspace/sidebar/workers.test.ts src/lib/workspace/sidebar/workspace-switcher.test.ts src/lib/workspace/sidebar/worker-subscription.test.ts src/lib/workspace/sidebar/worker-launch.test.ts test/sidebar/worker-actions.test.ts src/lib/workspace/tickets/merge-request-resources.test.ts src/lib/workspace/tickets/ticket-panel.test.ts test/merge-request-status.test.ts test/config-source/decodal-grammar.test.ts test/config-source/editor-state.test.ts test/config-source/fixed-schema-wrapper.test.ts test/config-source/toolchain.test.ts test/config-source/wasm-parity.test.ts test/repository-access/api.test.ts test/repository-access/loader.test.ts test/repository-access/ui.test.ts test/repository-ssh-connection-ui.test.ts",
"build": "deno run -A npm:vite@7.2.7 build",
"preview": "deno run -A npm:vite@7.2.7 preview"
},
@@ -1,7 +1,14 @@
<script lang="ts">
import type { InFlightCompaction } from "$lib/generated/protocol.ts";
import type {
InFlightCompaction,
WorkerStateSnapshot,
} from "$lib/generated/protocol.ts";
import Spinner from "./Spinner.svelte";
import { formatRunElapsed, formatRunTokens } from "./run-status";
import {
formatRunElapsed,
formatRunTokens,
resolveCompactionStatusPresentation,
} from "./run-status";
type Props = {
startedAtMs: number | null;
@@ -9,12 +16,24 @@
uploadTokens: number;
outputTokens: number;
compaction?: InFlightCompaction | null;
workerState?: WorkerStateSnapshot | null;
};
let { startedAtMs, requests, uploadTokens, outputTokens, compaction = null }: Props =
$props();
let {
startedAtMs,
requests,
uploadTokens,
outputTokens,
compaction = null,
workerState = null,
}: Props = $props();
let nowMs = $state(Date.now());
const clockStartedAtMs = $derived(compaction?.started_at_ms ?? startedAtMs);
const compactionStatus = $derived(
resolveCompactionStatusPresentation(compaction, workerState),
);
const clockStartedAtMs = $derived(
compactionStatus?.progress.started_at_ms ?? startedAtMs,
);
$effect(() => {
clockStartedAtMs;
@@ -33,8 +52,8 @@
<div class="worker-run-status" role="status" aria-live="off">
<Spinner />
{#if compaction}
<span>Compacting · {compaction.phase}</span>
{#if compactionStatus}
<span>{compactionStatus.label}</span>
<span aria-hidden="true">|</span>
{/if}
<span>{elapsed}</span>
@@ -5,6 +5,7 @@ import {
formatRunElapsed,
formatRunElapsedCompact,
formatRunTokens,
resolveCompactionStatusPresentation,
visibleCompactionProgress,
} from "./run-status.ts";
@@ -100,7 +101,7 @@ Deno.test("run status formatting matches the compact TUI shape", () => {
assertEquals(formatRunTokens(999), "999");
});
Deno.test("visible compaction requires a matching busy worker state", () => {
Deno.test("WorkerRunStatus presentation follows compaction and busy state", () => {
const manual = {
phase: "preparing",
started_at_ms: 1_700_000_000_000,
@@ -137,4 +138,19 @@ Deno.test("visible compaction requires a matching busy worker state", () => {
assertEquals(visibleCompactionProgress(manual, idle), null);
assertEquals(visibleCompactionProgress(manual, null), null);
assertEquals(visibleCompactionProgress(null, maintenance), null);
assertEquals(resolveCompactionStatusPresentation(manual, maintenance), {
progress: manual,
label: "Compacting · preparing",
});
assertEquals(resolveCompactionStatusPresentation(automatic, run), {
progress: automatic,
label: "Compacting · summarizing",
});
assertEquals(resolveCompactionStatusPresentation(manual, run), null);
assertEquals(
resolveCompactionStatusPresentation(automatic, maintenance),
null,
);
assertEquals(resolveCompactionStatusPresentation(manual, idle), null);
});
@@ -76,6 +76,21 @@ export function formatRunTokens(tokens: number): string {
return String(tokens);
}
export type CompactionStatusPresentation = {
progress: InFlightCompaction;
label: string;
};
export function resolveCompactionStatusPresentation(
compaction: InFlightCompaction | null,
workerState: WorkerStateSnapshot | null,
): CompactionStatusPresentation | null {
const progress = visibleCompactionProgress(compaction, workerState);
return progress
? { progress, label: `Compacting · ${progress.phase}` }
: null;
}
export function visibleCompactionProgress(
compaction: InFlightCompaction | null,
workerState: WorkerStateSnapshot | null,
@@ -1129,15 +1129,16 @@ Deno.test("Web Console gates compaction progress before rendering run status", a
);
assert(
consolePage.includes("visibleCompactionProgress(") &&
consolePage.includes("consoleProjection.compaction") &&
consolePage.includes("consoleProjection.workerState") &&
consolePage.includes("compaction={compactionProgress}"),
"Console page should pass only state-consistent compaction progress to the status component",
consolePage.includes("compaction={consoleProjection.compaction}") &&
consolePage.includes("workerState={consoleProjection.workerState}"),
"Console page should pass compaction metadata with authoritative worker state to the status component",
);
assert(
runStatus.includes("compaction?.started_at_ms ?? startedAtMs") &&
runStatus.includes("Compacting · {compaction.phase}"),
"run status should render the compaction phase and derive elapsed time from the authoritative start timestamp",
runStatus.includes("resolveCompactionStatusPresentation(") &&
runStatus.includes("{compactionStatus.label}") &&
runStatus.includes(
"compactionStatus?.progress.started_at_ms ?? startedAtMs",
),
"run status should render the guarded presentation and derive elapsed time from its authoritative start timestamp",
);
});
@@ -21,7 +21,6 @@
type ComposerCompletionToken,
} from "$lib/workspace/console/composer-completion";
import WorkerRunStatus from "$lib/workspace/console/WorkerRunStatus.svelte";
import { visibleCompactionProgress } from "$lib/workspace/console/run-status";
import { resolveWorkerControlShortcut } from "$lib/workspace/console/worker-control-shortcuts";
import {
consoleWorkerViews,
@@ -249,12 +248,6 @@
liveWorkerState ?? (worker?.state === "stopped" ? "stopped" : "loading"),
);
const workerRunning = $derived(workerState === "running");
const compactionProgress = $derived(
visibleCompactionProgress(
consoleProjection.compaction,
consoleProjection.workerState,
),
);
const workerPaused = $derived(workerState === "paused");
const composerEditable = $derived(protocolState === "open" && !sending);
const draftHasText = $derived(draft.content.trim().length > 0);
@@ -1912,7 +1905,8 @@
requests={consoleProjection.runActivity.requests}
uploadTokens={consoleProjection.runActivity.uploadTokens}
outputTokens={consoleProjection.runActivity.outputTokens}
compaction={compactionProgress}
compaction={consoleProjection.compaction}
workerState={consoleProjection.workerState}
/>
{/if}