Skip to content

refactor: e2e mock の pure helper を search-pure.ts に統一 (#209 ③)#279

Merged
ymnao merged 4 commits into
mainfrom
refactor/e2e-mock-pure-209-3
Jul 5, 2026
Merged

refactor: e2e mock の pure helper を search-pure.ts に統一 (#209 ③)#279
ymnao merged 4 commits into
mainfrom
refactor/e2e-mock-pure-209-3

Conversation

@ymnao

@ymnao ymnao commented Jul 5, 2026

Copy link
Copy Markdown
Owner

概要

#209 項目 ③「e2e mock 純関数化」対応。electron/main/ipc/search.ts に inline 定義されていた pure helper 群 (8 個) を electron/main/utils/search-pure.ts に集約し、e2e/helpers/electron-api-mock.ts からも同じ search-pure.ts を .toString() シリアライズ経由で browser scope に inject する経路を確立。mock と本番の pure helper drift を 物理的に不可能 にする。/simplify/code-review medium の findings も同 PR 内で全対応済み。

関連 Issue

closes #209 (項目 ③、v0.7.0 parent の 3 項目のうち ② は #278 で対応済、① は別 PR で対応予定)

移行 Stage

  • Stage 3: 全文検索 (post-migration の内部品質改善、search IPC helper の整理)

変更内容

1. Main 側集約 (commit 56c5c06)

  • electron/main/ipc/search.ts の inline pure helper (fuzzyMatch / isEscaped / collectInlineCodeRanges / findFencedLines / maskRanges / byteCmp / buildLineStarts / isInRanges) を search-pure.ts に集約、search.ts は import に置換
  • search-pure.test.ts に helper 群のユニットテスト 44 case を追加、search.test.tsfuzzyMatch テストは移動
  • e2e/helpers/electron-api-mock.tspage.addInitScript({ content: PURE_HELPERS.map(f=>f.toString()).join() + \(${installApiMock})(...)` })` 経路に切替、installApiMock 内側の inline pure copy (200 行超) を削除
  • search-pure-mock-parity.test.ts を新規追加 (drift 検出)

2. /simplify 対応 (commit 6dd969b)

  • SCRIPT_PREFIX / SCRIPT_SUFFIX を module scope に hoist、setup() の per-call serialize 削減 (~750 行 × 133 spec)
  • maskRangestext.split("") + buf.join("") から slice + " ".repeat() + parts.join() に変更、per-file char array allocation を排除
  • search-pure.ts top doc の実装と食い違うコメント修正、mock 内の重複コメント削除

3. /code-review medium 対応 (commit e7afb12)

  • PURE_HELPERSObject.values(searchPure) 派生から named import と 1:1 対応する explicit list に戻す (2 list drift + wholesale inject scope 拡散を同時解消、真実源は named import block 1 つに)
  • parity test の inline 再定義検出 regex を const|let|var|function <name> の 4 形全対応に拡張 (hoisting により function 形の re-inline が gate をすり抜ける穴を塞ぐ)
  • mock ソースから named import と PURE_HELPERS 配列の identifier 列を extract し等価性 assert する gate を追加
  • isFenced.some((b) => b) ? guard を search.ts / mock 両方で削除 (新 maskRanges が identity 契約になったため冗長)
  • maskRanges の到達不能な parts.length === 0 ? text : ... 分岐を削除
  • search-pure.ts top doc から PR 番号 #279 を削除

動作確認

  • pnpm biome check — pass
  • pnpm typecheck (node/web/e2e) — pass
  • pnpm test — 100 files / 2271 pass / 2 skipped (44 case 追加 + parity 3 case 追加 + fuzzyMatch 4 case 移動)
  • pnpm build (electron-vite build) — pass
  • pnpm test:e2e (renderer-only mode) — sandbox 内 listen EPERM でローカル実行不可、CI (e2e job) で確認
  • pnpm test:e2e:electron (実 Electron mode) — mock 未使用のため回帰リスクは low、CI (electron-e2e job) で確認

Diff scale

6 files changed, +649 -408 lines。うち mock ソースは 1004 → ~735 行 (-270 行)、search.ts inline helper 分は search-pure.ts に集約。

ymnao and others added 4 commits July 5, 2026 01:33
electron/main/ipc/search.ts の inline pure helper (fuzzyMatch / isEscaped /
collectInlineCodeRanges / findFencedLines / maskRanges / byteCmp / buildLineStarts
/ isInRanges) を electron/main/utils/search-pure.ts に集約し、search.ts は
import に置換。search-pure.test.ts に helper 群のユニットテスト 44 ケースを
追加し、search.test.ts の fuzzyMatch テストは移動。

e2e/helpers/electron-api-mock.ts を addInitScript の script content 経路に
切替、search-pure.ts の named function を `.toString()` で browser scope に
inject する経路 (PURE_HELPERS 配列 + `\`${pure}\\n(${installApiMock})(...)\``) を
確立。installApiMock 内側の inline pure copy (200 行超) を削除し、mock と
本番の drift を物理的に不可能化。tsc の view では file top-level import に
resolve され typecheck 通過、browser 実行時は hoisted function declaration
として bare 参照に resolve される。

electron/main/utils/search-pure-mock-parity.test.ts を新規追加。以下を CI で
lock する:
- search-pure.ts の export 名と mock の EXPECTED_PURE_HELPERS の整合
- pure helper の `.toString()` が `function <name>(...)` 形にシリアライズされる
- mock ソースの PURE_HELPERS 配列 literal に全 entry が含まれる
- mock ソース内に inline pure 定義が復活していない (再 inline detect)

mock ソース: 1004 → 866 行 (-138 行)。sandbox listen EPERM で renderer-only
e2e はローカル実行不可、CI (electron-e2e job) で最終確認する。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ges 高速化

/simplify の 4 agent 並列レビューから 5 finding を採用 (reuse は 0 clean、
skip は E3 = findFencedLines return shape 変更、cost 対効果で見送り):

- Group 1 (S1/A1/A3, S4): PURE_HELPERS 手動 list + EXPECTED_PURE_HELPERS + import
  block の 3 重管理を Object.values(searchPure) 派生に集約。mock 側 named import
  は installApiMock 内側で bare 参照する identifier のみに絞り (isAsciiOnly は
  inject 経由のみで利用されるため named import から外す)、parity test の
  「PURE_HELPERS 配列 完全性」regex assertion (S4 の null-check 二重保険含む) を削除。
  「search-pure.ts に export した function は自動的に inject される」 contract に。
- Group 2 E1/E4: setup() で毎回呼ばれていた PURE_HELPERS.map(.toString()).join() +
  installApiMock.toString() (~750 行の serialize) を SCRIPT_PREFIX/SCRIPT_SUFFIX
  として module scope に hoist。setup() は JSON.stringify(payload) の per-call
  concat だけになり、e2e spec 起動時の 133 × ~750 行の redundant serialize を排除
- Group 3 E2: maskRanges の `text.split("") + buf.join("")` (1 文字 1 array entry
  の allocation) を `text.slice() + " ".repeat()` ベースの parts.join に変更。
  workspace 500 files × 10KB の scan で per-file char array allocation が消える。
  length preservation と char index 互換は既存テストで pass
- Group 4 S2/S3/A4: search-pure.ts top doc から e2e mock 事情由来の "inject 形の
  `const \${fn.name} = \${fn.toString()}`" という誤記を実装 (named function 直並べ) に
  合わせて修正、named function 縛りを一般的な `.toString()` シリアライズ経路として
  記述。mock 内 installApiMock 内側の中間コメント (304-306 / 326-329) を削除
  (file header に既に記述あり)。mockDisplayPath のコメントから "addInitScript 制約
  で import 不可" の旧経路言及を "mock は browser scope で走るため node:path は不使用" に置換

parity test: 4 case → 2 case (named function serialization / inline 復活 gate) に。

検証: biome / typecheck (node/web/e2e) / vitest 100 files 2270 pass / build 全 green。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…e 掃除

/code-review medium の 6 finding を採用 (correctness bug は 0、全て cleanup / drift gate 強化):

- Finding 2+5: mock の PURE_HELPERS を `Object.values(searchPure).filter(fn)` 派生から
  named import と 1:1 対応する explicit list に戻す。「import と PURE_HELPERS の 2 リスト
  drift」と「search-pure.ts 全 export を無条件 inject する scope 拡散」を同時解消。真実源は
  named import block、PURE_HELPERS はその value projection。`isAsciiOnly` は named import
  に戻し PURE_HELPERS の値として consume することで biome noUnusedImports を pass させる。
- Finding 1: search-pure-mock-parity.test.ts の regex を拡張。
  - inline 再定義検出を `const|let|var|function <name>` の 4 形全対応に (元は `const` 形のみ、
    hoisting により `function` 形の re-inline が gate をすり抜ける穴があった)
  - mock ソースから named import と PURE_HELPERS 配列の identifier 列を extract し
    等価性を assert する 3 番目 test を追加 (import に載せたが PURE_HELPERS 忘れ → browser
    runtime ReferenceError を静的 gate で catch)
- Finding 3: search.ts / mock 双方の `isFenced.some((b) => b) ? maskRanges(...) : text`
  guard を削除。新 maskRanges が mask all-false 時に text を identity 返却する契約になった
  今、guard は 1 パス無駄なだけで cognitive load を増やしていた。
- Finding 4: maskRanges の `return parts.length === 0 ? text : parts.join("")` の
  特別分岐は到達不能 (parts が空になる条件は text.length === 0 のみで、その場合 join も
  空 = text と等価) なので `return parts.join("")` に単純化。
- Finding 6: search-pure.ts top doc から PR 番号 `#279` を削除。安定 pure module の doc に
  PR 参照が入ると chase コストを maintainer に押し付ける altitude smell。

検証: biome / typecheck (node/web/e2e) / vitest 100 files 2271 pass / build 全 green。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… が壊れる問題

CI (`e2e` job) で backlink-panel / command-palette / search 系 spec が
「検索結果 0 件」で timeout していた regression の修正。root cause は
Playwright 1.61 内蔵 babel が本 repo (`package.json` に `"type": "module"` が
無い) の e2e helper を isModule=false 経路で transform し、
@babel/plugin-transform-modules-commonjs が named import の bare 参照を
`(0, _searchPure.foo)` / `_searchPure.bar` に rewrite することにあった。
`installApiMock.toString()` した body を browser scope に運ぶ経路では
`_searchPure` が undefined なため ReferenceError となり全 search / backlink
handler が壊れる。ローカル vitest / typecheck / build は helper を実行時に
評価しないためこの経路の問題を検出できなかった。

修正: `import { fuzzyMatch, ... }` から `import * as searchPure` +
`const { fuzzyMatch, ... } = searchPure` の destructure に切り替え、
installApiMock 内側の bare 参照を local const にする。babel の
modules-commonjs は module scope の named import は rewrite するが local
const は preserve するため、`.toString()` の中の bare identifier 形が保たれ
SCRIPT_PREFIX で並べた hoisted 関数宣言と正しく resolve できる。Playwright
bundled babel を直接呼んで transform 結果を確認済み (`_searchPure.*` 参照 0 件、
bare 参照 5+ 件)。

parity test の `import { ... }` extractor を `const { ... } = searchPure`
destructure extractor に更新し、同期 gate は継続。regression 予防のため
mock file の doc に「必ず `import * as` + local const destructure の形を保つ」
を明記。

検証: biome / typecheck (node/web/e2e) / vitest 100 files 2271 pass /
build 全 green。CI (`e2e` job) は本 push で再検証する。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ymnao ymnao merged commit 05fe83a into main Jul 5, 2026
9 checks passed
@ymnao ymnao deleted the refactor/e2e-mock-pure-209-3 branch July 5, 2026 03:13
@ymnao ymnao mentioned this pull request Jul 5, 2026
7 tasks
ymnao added a commit that referenced this pull request Jul 5, 2026
- package.json の version を 0.6.0 → 0.7.0 に bump
- CHANGELOG.md に v0.7.0 エントリを追加 (#243#280)
  - Fixed: FileTree scroll regression (#276)、electron e2e launch teardown timeout 拡大 (#247)
  - Internal: #209 3 項目 (drift-lock 系 refactor #278/#279/#280)、settings setter 集約 (#273#277)、search/workspace/backlink 系 helper 抽出 15 件
  - Dependencies: electron 43.0.0 / p-limit 7.3.0 major bump (#275)、Dependabot 7 件 combine (#274)、pnpm-workspace.yaml overrides で @codemirror dual copy を恒久防止

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: アーキテクチャ refactor backlog (preload 同期 / live-preview helper / e2e mock 同期)

1 participant