Skip to content

feat(arc-vue): 描画の環境依存とタイミング依存を切り離す - #25

Open
sousuke0422 wants to merge 4 commits into
masterfrom
feat/vrt-arc-measurement
Open

feat(arc-vue): 描画の環境依存とタイミング依存を切り離す#25
sousuke0422 wants to merge 4 commits into
masterfrom
feat/vrt-arc-measurement

Conversation

@sousuke0422

@sousuke0422 sousuke0422 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

VRT でスクリーンショットを撮るとき、描画結果が実行環境やタイミングによって変わらないようにする。

背景

detectDeviceTier()navigator.deviceMemorynavigator.hardwareConcurrency を読んで low / high を決めていた。
この判定が useVirtualization を切り替え、viewportJson を通って render_svg に渡る引数を変える。
つまり同じ入力データでも、動かす機械によって描画結果が変わりうる。

VRT はスクリーンショットの差分で回帰を検出する仕組みなので、環境で絵が変わる要因は誤検出の直接の原因になる。

計測結果

VRT に載せる前に、環境依存で問題になりうる箇所を 3 つ想定して実測した。
結果として、今のストーリー構成では 3 つとも画像の差分にはならなかった。

wasm の初期化待ちで空の絵が撮れる可能性を確認したが、20 回撮って 1 回も再現しなかった。

デバイス階層の違いは、DOM のクラス属性としては実際に分岐していた。
ただし PNG のハッシュは全パターンで同一だった。
チャートの高さが約 210px、ビューポートが 240px で、仮想化が省略する対象が存在しなかったため。

clientHeight の測定タイミングによる差も再現しなかった。

したがってこの PR は、今すでに起きている差分の修正ではない。
ストーリーのデータ量が増えて仮想化が実際に働き始めた時点で顕在化する問題への、事前の対処にあたる。

変更内容

deviceTier prop を追加し、渡された場合は自動判定より優先する。
Storybook の全ストーリーでこれを high に固定した。

deviceTierconst から computed に変えている。
prop の変更に追従させるため。

反応性を通したことで生まれた欠陥を二つ塞いだ

deviceTier を computed にしたことで、実行時に階層が変わりうるようになった。
その結果、測定が追従していない箇所が二つ露見した。
どちらもレビューで指摘を受けている。

一つ目は階層が high から low へ変わったときで、clientHeight が古いまま viewportJson が組まれ、全範囲の WASM 描画が一度走っていた。

二つ目は初回マウント時で、init() の完了後に描画が可能になった時点ではまだ測定が済んでおらず、同じく全範囲の描画が一度走っていた。
低性能な端末ほど、起動のたびに仮想化が避けるはずだった処理を通ることになる。

どちらも viewportPending という単一の条件でまとめて塞いだ。
仮想化が要求されていて、かつ測定が済んでいない間は、SVG と Canvas のどちらの経路でも WASM を呼ばない。
経路ごとに個別の手当てを積むと、次に増えた経路が漏れるため。

検証

回帰テストは、階層が変わった後に起きた render_svgrender_canvas_commands の呼び出しを全件走査する。
viewportJson を欠くもの、あるいは古い高さを含むものが一つでもあれば落ちる。

最初に書いたテストは最後の呼び出ししか見ておらず、途中の誤った描画を素通りさせていた。
最終的な出力は正しくなるため、それでは欠陥を検出できない。

各テストが修正前のコードで落ちることを実測している。
viewportPending を常に false へ潰した状態では 4 件が落ち、SVG と Canvas の両方で「viewport を伴わない呼び出しが何回目に起きたか」が出力される。

テストが CI で実行されるようにした

これまで vue の job は typecheckbuild しか実行しておらず、追加したテストは CI で一度も走っていなかった。
job に非対話の vitest 実行を追加している。

追加しただけで終わらせず、テストをわざと壊した状態で job が失敗することを確認した。
実行ステップが存在していても、テストを拾っていなければ意味がないため。

既知の制約

ストーリーを high に固定したことで、仮想化が有効な経路は VRT の対象から外れている。
この経路を継続的に見るなら、low に固定したストーリーを別途用意する必要がある。

チャートがビューポートを超える大きさのストーリーが存在しないため、仮想化そのものの描画結果は未検証のままになっている。

wasm 初期化については、遅延を人為的に作って空の絵が撮れることを確認する対照実験を行っていない。
20 回の非再現は、この環境で初期化が速かったことを示すにとどまる。

この三つは、タスク数がビューポートを超えるストーリーを lowhigh の二本用意すれば、まとめて解消できる見込みがある。

CI の状況

rust、vue、chromatic、Storybook Publish はいずれも成功している。

UI TestsUI Review は Chromatic の月次スナップショット枠を使い切ったため保留になっている。
この PR の内容とは無関係で、プラン側の対応が必要になる。

Assisted-by: multi-agent-shogun-aki-tweak

@chromatic-com

chromatic-com Bot commented Aug 10, 2026

Copy link
Copy Markdown

Warning

Testing paused

Monthly snapshot limit reached. Update your plan for additional snapshots and to resume testing.

Pin device tier in Storybook stories so CI runners with different
deviceMemory/hardwareConcurrency render the same virtualization path.

Assisted-by: multi-agent-shogun-aki-tweak
Watch useVirtualization and remeasure scrollRef after DOM updates so
viewportJson.client_height matches the virtualized container on tier flip.

Assisted-by: multi-agent-shogun-aki-tweak
@sousuke0422
sousuke0422 force-pushed the feat/vrt-arc-measurement branch from 906f88f to a28be49 Compare August 11, 2026 09:45
Split virtualization state into requestedVirtualization (CSS class,
applied immediately) and viewportReady (WASM viewport, enabled only
after nextTick + scroll metrics sync). viewportJson stays undefined
until the measured state is ready, so no render call can carry a stale
client_height from the previous tier.

Tests drop latestViewportJson()/.at(-1) and assert every
render_svg / render_canvas_commands call: no stale 880 anywhere, and
every post-flip call must carry the measured viewport (600). Verified
to fail on a28be49 (call #2 passed client_height 880) and pass with
the fix.

Assisted-by: multi-agent-shogun-aki-tweak
…t in CI

While virtualization is requested but the viewport is not yet measured
(initial mount, tier-flip window), both render_svg and
render_canvas_commands could run once with viewport_json undefined,
rendering the full range. Defer both computeds at a single chokepoint
until viewportReady; non-virtual (high tier) renders are not delayed.

Add window-invariant tests scanning every mock call (W1 mount svg/canvas,
W2 flip-window props/backend leaks, high-tier non-delay guard) and wire
'pnpm test' (vitest run) into the vue CI job so the suite actually runs.

Assisted-by: multi-agent-shogun-aki-tweak
@sousuke0422 sousuke0422 changed the title feat(arc-vue): deviceTier prop で描画の環境依存を切り離す feat(arc-vue): 描画の環境依存とタイミング依存を切り離す Aug 12, 2026
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.

1 participant