9ac580869d3e555ac6f4bbdd028151cdfe13461c
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 630d40c4f8 |
test(04-02): Wave 0 — no-new-function-in-sw-chunk RED + dead-code-grep regression pin
Two new build-gate vitest files at `tests/build/` per Plan 04-02 Wave 0 TDD-strict RED-first contract: - `no-new-function-in-sw-chunk.test.ts`: SW-chunk CSP-hardening grep gate. Narrows the file walk to `dist/assets/index.ts-*.js` (the SW + loader chunks; cf. plan-checker iter-1 BLOCKER 1 fix). RED today: 1 occurrence of `new Function` in the SW chunk (the pre-existing `setimmediate` npm package fallback bundled transitively by vite-plugin-node-polyfills, per .planning/phases/01-stabilize-video-pipeline/deferred-items.md). Flips GREEN after Task 2's setimmediate replacement lands. Build-prep gate (npm run build + dist/assets/ existence + ≥1 SW chunk match) precedes the grep gate so the test is self-bootstrapping under SKIP_BUILD=0 and self-asserting under SKIP_BUILD=1. - `dead-code-grep.test.ts`: ROADMAP SC #4 regression pin against `src/`. Asserts absence of `permissions.request` (removed in Phase 1 Plan 01-05 SW shrink). GREEN-on-arrival today; acts as regression guard so re-introducing the deleted permission-request flow breaks CI. The offscreen-inline-string sub-test is documented as delegated to the vite.config.ts review + tests/build/no-remote-fonts.test.ts (no single literal sentinel pinnable post-Plan-01-06 collapse). Polarity confirmation: - Acceptance grep: `grep -v '^//' tests/build/no-new-function-in-sw-chunk.test.ts | grep -c 'new Function'` returns 3 (≥2 required). - Acceptance grep: `grep -v '^//' tests/build/dead-code-grep.test.ts | grep -c 'permissions.request'` returns 2 (≥2 required). - SKIP_BUILD=1 npm test -- tests/build/no-new-function-in-sw-chunk.test.ts tests/build/dead-code-grep.test.ts --run: 2 passed + 1 failed (the expected RED gate). - Full vitest: 180 passed + 3 failed (1 = this task's expected RED + 2 = pre-existing ffmpeg/ffprobe flakes per 04-01-SUMMARY Issues Encountered — owned by Plan 04-03). References: - .planning/phases/04-harden-clean-up-optional/04-PATTERNS.md §"tests/build/no-new-function-in-sw-chunk.test.ts" + §"tests/build/dead-code-grep.test.ts" - .planning/phases/04-harden-clean-up-optional/04-RESEARCH.md §Q1 - Plan 04-02 threat model T-04-02-01 (Elevation of Privilege) + T-04-02-03 (Information Disclosure regression pin) - tests/build/no-remote-fonts.test.ts (Plan 01-12 analog scaffold) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 79964e62d2 |
feat(02-02): SW — downloadArchive via offscreen-minted Blob URL + revoke lifecycle (D-P2-01 closes P0-6)
Production changes (src/background/index.ts):
- pendingDownloadUrlResolvers Map<requestId, resolver> routes DOWNLOAD_URL
responses back to the in-flight downloadArchive Promise; mirrors the
pendingBufferRequests pattern from the BUFFER round-trip so port
replacement mid-mint does not lose the response.
- pendingRevokes Map<downloadId, url> tracks (downloadId → minted blob:URL)
for the chrome.downloads.onChanged revoke dispatch.
- onConnect port message sink extended with DOWNLOAD_URL routing branch
(alongside existing PING/BUFFER routing).
- downloadArchive rewritten: encode archive via blobToBase64 → post
CREATE_DOWNLOAD_URL on videoPort → await DOWNLOAD_URL response (race
against 5s BLOB_URL_MINT_TIMEOUT_MS) → reject empty / non-blob: URLs
(T-02-02-03 mitigation) → call chrome.downloads.download → register
(downloadId, url) in pendingRevokes. NO data:URL fallback — typed
errors route through saveArchive's catch to RECORDING_ERROR.
- chrome.downloads.onChanged listener registered at module init:
on terminal state ('complete' / 'interrupted'), posts REVOKE_DOWNLOAD_URL
to videoPort and clears the pendingRevokes entry.
Deviation (Rule 3 — auto-fix blocking issue):
- Plan 02-01's test helpers in blob-url-download.test.ts +
meta-json-urls-schema.test.ts + strict-meta-json-validation.test.ts
modeled only the REQUEST_BUFFER → BUFFER round-trip, not the new
CREATE_DOWNLOAD_URL → DOWNLOAD_URL round-trip Plan 02-02 introduces.
Without the test-side mint simulation, the SW's downloadArchive
times out at the offscreen mint step → chrome.downloads.download
never called → ALL existing meta.json tests timeout.
- Each helper extended with a tryFireDownloadUrl block that decodes
the CREATE_DOWNLOAD_URL.dataBase64, mints a Node-native blob:URL via
URL.createObjectURL, captures the archive bytes for downstream
JSZip extraction (capturedArchiveBytes), and replies DOWNLOAD_URL.
Test 3 (revoke lifecycle) additionally shims port.postMessage to
call URL.revokeObjectURL on receipt of REVOKE_DOWNLOAD_URL — the
test-side equivalent of src/offscreen/recorder.ts handleCreateDownloadUrl.
- Pre-existing Plan-02-02-era TODO comments in both test files
explicitly anticipated this extension ("Plan 02-03 implementer will
likely need a different helper, e.g. spy on URL.createObjectURL").
Verification (full §verification block from plan):
- npx tsc --noEmit: clean
- npm run build: clean
- npx vitest run tests/background/blob-url-download.test.ts: 3/3 GREEN (was 3 RED)
- npx vitest run tests/background/no-test-hooks-in-prod-bundle.test.ts: 13/13 GREEN
- npm test full suite: 163 passed / 8 failed (was 159 passed / 12 failed);
net delta +4 GREEN = 3 RED→GREEN flips + 1 ffprobe-flaky pass. 8 remaining
RED are exactly the Plan 02-03 territory (5 meta-json-urls-schema + 3
strict-meta-json-validation RED tests).
- grep -c "data:application/zip;base64," src/background/index.ts: 0 (gone)
- grep -c "blob:" src/background/index.ts: 8 (new pipeline)
- grep -c "chrome.downloads.onChanged" src/background/index.ts: 5 (listener wired)
- dist/ post-build: 0 "data:application/zip;base64," matches; 1 file with
"chrome.downloads.onChanged" (the SW chunk).
|
|||
| 94e03467c6 |
test(02-01): RED — pin strict 8-field meta.json schema validation (D-P2-03)
Plan 02-01 Task 3 RED gate. Eight strict-validation tests pin D-P2-03
(strict 8-field meta.json schema) plus F2 plan-checker-iter-1
resolution (empty urls[] permitted for whole-desktop-no-tab sessions).
Tests (3 RED-today + 5 GREEN-today regression guards; ALL 8 GREEN
after Plan 02-03):
1. RED — Object.keys(meta).length === 8.
2. GREEN — timestamp matches ISO-8601 Z-suffix regex.
3. RED — urls is Array of valid URLs (empty permitted per F2).
4. GREEN — extensionVersion matches semver.
5. GREEN — totalEvents is non-negative integer.
6. GREEN — videoBufferSeconds === 30 (CON-video-window).
7. GREEN — logDurationMinutes === 10 (CON-event-log-window).
8. RED — no extra fields beyond EXPECTED_KEYS.
RED evidence (vitest 4.1.6 against current HEAD):
× Test 1: meta.json has 7 fields; D-P2-03 requires exactly 8.
Current keys: [timestamp, url, userAgent, extensionVersion,
videoBufferSeconds, logDurationMinutes, totalEvents].
× Test 3: meta.urls is not an Array. Got: undefined.
× Test 8: meta.json contains extra (unexpected) fields: ["url"].
PLANNER-RESOLVED TENSIONS (documented in file header):
- D-P2-03 'non-empty urls[]' vs CONTEXT.md permissive empty-array:
resolved in favor of the permissive clause (F2 — empty is the
canonical representation of whole-desktop-no-tab sessions).
- 8th field name 'schemaVersion': tentative planner pick to mark
the D-P2-02 url→urls breaking-change cutover.
- Plan's 'ALL 8 fail' claim vs reality: 5 of 8 already pass under
the current 7-field shape (timestamp, semver, totalEvents,
videoBufferSeconds, logDurationMinutes). These stay GREEN as
regression guards after Plan 02-03 lands.
EXPECTED_KEYS constant:
['timestamp', 'urls', 'userAgent', 'extensionVersion',
'videoBufferSeconds', 'logDurationMinutes', 'totalEvents',
'schemaVersion']
Plan 02-03 implementer MUST add `schemaVersion` (recommended value:
'2') to satisfy Tests 1 + 8 simultaneously.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 34a9ce10d4 |
test(01-12): wave-0 — scaffold RED unit tests (tokens / fonts / icons / no-remote-fonts / manifest-i18n / locale-parity)
Wave 0 of the design-integration plan. Six new test files at tests/build/ and tests/i18n/ pin the contracts that later waves will GREEN: - tokens-adopted.test.ts (4 cases): src/shared/tokens.css exists + parses; src/popup/style.css @imports it; popup/style.css has zero hex literals; welcome.css conditional check. - fonts-present.test.ts: 7 required WOFF2 faces (Lora normal + Plex Sans ×4 + Plex Mono ×2) + LICENSE-Lora + LICENSE-IBM-Plex + README + optional Lora-Italic (A5 verify-at-execute). - icons-present.test.ts (15 cases across 3 sizes): existence, size FLOOR per assets-spec.md, PNG signature, dimensions, color-type byte === 6 (RGBA — RED until Wave 2 rsvg-convert overwrites the 16-bit-RGB placeholders). - no-remote-fonts.test.ts: production dist/ contains zero fonts.googleapis.com / https://fonts / googleapis substrings (MV3 CSP self-host invariant T-01-12-01). - manifest-i18n.test.ts (10 cases): manifest:name === '__MSG_extName__', :description === '__MSG_extDesc__', :default_locale === 'en', :action.default_title === '__MSG_tooltipOff__'; _locales/{en,ru}/ messages.json carry D-07 + D-08 canonical strings. - locale-parity.test.ts (4 cases): ru→en parity, en→ru symmetric, non-empty .message strings (RESEARCH Pitfall 4 mitigation). Current polarity: 29 RED + 18 GREEN across the 6 new files (placeholders already clear dim+size floors; no-remote-fonts vacuous-GREEN since tokens.css doesn't yet exist with remote URLs). Existing 100/100 vitest baseline preserved (verified SKIP_BUILD=1 npx vitest run). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |