From 5efc3a5491ded414b88972775038b43011f08370 Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Sun, 31 May 2026 18:09:08 -0400 Subject: [PATCH] =?UTF-8?q?test(ext/vault):=20handler=E2=86=92renderer=20s?= =?UTF-8?q?tatus=20integration=20+=20indicator=20CSS=20(Plan=20C=20Phase?= =?UTF-8?q?=206)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pins the 6.1↔6.2 contract: handleGetVaultStatus output feeds straight into renderStatusIndicator. Adds minimal self-contained .vault-status CSS. Stays out of vault-sidebar.ts — the footer wiring (Task 6.3) is Dev-B's boundary. Co-Authored-By: Claude Opus 4.8 --- .../__tests__/status-integration.test.ts | 65 +++++++++++++++++++ extension/src/vault/vault.css | 12 ++++ 2 files changed, 77 insertions(+) create mode 100644 extension/src/vault/__tests__/status-integration.test.ts diff --git a/extension/src/vault/__tests__/status-integration.test.ts b/extension/src/vault/__tests__/status-integration.test.ts new file mode 100644 index 0000000..197abb7 --- /dev/null +++ b/extension/src/vault/__tests__/status-integration.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, it } from 'vitest'; +import { handleGetVaultStatus } from '../../service-worker/vault'; +import { renderStatusIndicator } from '../vault-status'; +import type { Manifest, ManifestEntry } from '../../shared/types'; + +// Integration seam: the get_vault_status SW handler (Phase 6 Task 6.1) produces +// the exact data shape the sidebar renderer (Task 6.2) consumes. This pins the +// contract between the two so a future change to either side can't silently +// drift the keys apart. It does NOT touch vault-sidebar.ts — the wiring layer +// (Task 6.3) is Dev-B's boundary and lands separately. + +const cache = (lastSyncAt: number | null, ahead = 0, behind = 0) => + ({ lastSyncAt, ahead, behind }); + +function manifestWith(activeCount: number, trashedCount = 0): Manifest { + const items: Record = {}; + for (let i = 0; i < activeCount; i++) { + items[`a${i}`] = { trashed_at: undefined } as ManifestEntry; + } + for (let i = 0; i < trashedCount; i++) { + items[`t${i}`] = { trashed_at: 1000 } as ManifestEntry; + } + return { items } as Manifest; +} + +describe('vault status: handler → renderer integration', () => { + it('renders "in sync" from a freshly-synced, no-pending handler response', () => { + const resp = handleGetVaultStatus({ gitHost: cache(1700000000), manifest: manifestWith(0) }); + expect(resp.ok).toBe(true); + if (!resp.ok) return; + const el = document.createElement('div'); + renderStatusIndicator(el, resp.data); + expect(el.textContent).toMatch(/in sync/i); + expect(el.textContent).toMatch(/last sync/i); + }); + + it('surfaces the handler\'s active-item count as "N pending" in the DOM', () => { + const resp = handleGetVaultStatus({ gitHost: cache(1700000000), manifest: manifestWith(7, 3) }); + expect(resp.ok).toBe(true); + if (!resp.ok) return; + expect(resp.data.pendingItems).toBe(7); + const el = document.createElement('div'); + renderStatusIndicator(el, resp.data); + expect(el.textContent).toMatch(/7 pending/i); + }); + + it('surfaces cached ahead/behind from the handler in the DOM', () => { + const resp = handleGetVaultStatus({ gitHost: cache(1700000000, 2, 1), manifest: manifestWith(0) }); + expect(resp.ok).toBe(true); + if (!resp.ok) return; + const el = document.createElement('div'); + renderStatusIndicator(el, resp.data); + expect(el.textContent).toMatch(/2 ahead/i); + expect(el.textContent).toMatch(/1 behind/i); + }); + + it('renders "never synced" when the handler reports a null lastSyncAt', () => { + const resp = handleGetVaultStatus({ gitHost: cache(null), manifest: manifestWith(0) }); + expect(resp.ok).toBe(true); + if (!resp.ok) return; + const el = document.createElement('div'); + renderStatusIndicator(el, resp.data); + expect(el.textContent).toMatch(/never synced/i); + }); +}); diff --git a/extension/src/vault/vault.css b/extension/src/vault/vault.css index 8e7ac7d..81bf140 100644 --- a/extension/src/vault/vault.css +++ b/extension/src/vault/vault.css @@ -2113,3 +2113,15 @@ textarea { .history-index-row__info { flex: 1; display: flex; flex-direction: column; } .history-index-row__title { color: var(--text); } .history-index-row__meta { font-size: 11px; } + +/* Sidebar-footer vault status indicator (Plan C Phase 6, vault-status.ts). + The footer slot + refresh button are wired by vault-sidebar.ts in Task 6.3. */ +.vault-status { + display: flex; + flex-direction: column; + gap: 2px; + font-size: 11px; + line-height: 1.4; +} +.vault-status__state { color: var(--text-dim); } +.vault-status__ts { color: var(--text-muted); }