From 11a3abeca10ccb4683d194c824923d9eaced9626 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Fri, 19 Jun 2026 14:15:54 -0400 Subject: [PATCH] Keep evidence artifacts out of the cherry-pick path --- tests/test-evidence-summary.md | 111 ------------------------ tests/test-results-cdp-behavioral.txt | 30 ------- tests/test-results-native-scrollbar.txt | 42 --------- tests/test-results-virtualization.txt | 46 ---------- tests/test-results.txt | 65 -------------- 5 files changed, 294 deletions(-) delete mode 100644 tests/test-evidence-summary.md delete mode 100644 tests/test-results-cdp-behavioral.txt delete mode 100644 tests/test-results-native-scrollbar.txt delete mode 100644 tests/test-results-virtualization.txt delete mode 100644 tests/test-results.txt diff --git a/tests/test-evidence-summary.md b/tests/test-evidence-summary.md deleted file mode 100644 index 883b2af4..00000000 --- a/tests/test-evidence-summary.md +++ /dev/null @@ -1,111 +0,0 @@ -# Test Evidence Summary — DOM Node Recycling PR - -## Test counts - -| Suite | Count | Status | -|-------|-------|--------| -| Recycling + scrollbar drag (pytest) | 48 | 48/48 pass | -| Existing virtualization regression (pytest) | 29 | 29/29 pass | -| CDP behavioral (live browser) | 8 | 8/8 pass | -| Native scrollbar drag (SendInput + CDP) | 4 | 4/4 pass | -| **Total** | **89** | **89 pass, 0 skip** | - -## Maintainer must-fix items (PR #4474 CHANGES_REQUESTED) - -### MF-1: data-msg-idx on .assistant-turn corrupts measurement - -**Finding:** `querySelector('[data-msg-idx="N"]')` returns the `.assistant-turn` container (height 560px) instead of the `.assistant-segment` child (height 120px), inflating virtual window padding by 440px per turn. - -**Fix:** Use `data-recycle-key` on `.assistant-turn`, leaving `data-msg-idx` exclusively on `.assistant-segment`. - -**Evidence (test_mf1_multi_segment_turn_raw_heights):** -- 3-segment turn: segments at 120px, 90px, 150px with tool cards at 60px, 80+60px -- Measured heights: seg5=180px (120+60), seg6=230px (90+80+60), seg7=150px -- Total measured: 560px (correct sum of segments + tools) -- Buggy path would return: 560px for the FIRST SEGMENT ALONE (4.67x inflation) - -**Evidence (test_mf1_buggy_path_inflates_height):** -- Buggy (container match): 560px -- Fixed (segment match): 120px -- Inflation: 440px per turn, 4.67x factor - -### MF-2: un-typed stash lookups allow cross-type recycling - -**Finding:** Without type guards, index shift between renders could hand a user row to the assistant branch or vice versa, causing blank-chat via null dereference. - -**Fix:** `classList.contains('msg-row')` guard on user branch, `classList.contains('assistant-turn')` guard on assistant branch. - -**Evidence (test_mf2_user_row_in_assistant_slot_without_guard):** -- Without guard: accepts user row (3 mismatched fields: className, dataset.role, id) -- With guard: rejects → falls back to fresh build - -**Evidence (test_mf2_assistant_turn_in_user_slot_without_guard):** -- Without guard: accepts assistant turn (3 mismatched fields: className, dataset.role, dataset.msgIdx) -- With guard: rejects → falls back to fresh build - -**Evidence (test_mf2_stash_collision_rate_in_shifted_indices):** -- 5 nodes stashed, indices shift by +2 (prepend scenario) -- Without guards: cross-type collisions occur at shifted boundary -- With guards: all collisions rejected, fresh nodes built instead - -### MF-3: source-text greps pass on broken code - -**Finding:** Original tests were `assertIn(pattern, source_text)` which pass regardless of whether the code path works. - -**Fix:** Replaced with behavioral tests that extract and execute functions against mock DOM, producing concrete pixel values and node counts. - -**Evidence (test_mf3_measurement_test_fails_on_buggy_layout):** -- Buggy layout: returns 560px (test would FAIL the 120px assertion) -- Fixed layout: returns 120px (test PASSES) -- Test sensitivity confirmed: different outputs on buggy vs fixed - -**Evidence (test_mf3_type_check_test_fails_on_unguarded_code):** -- Guarded: rejects wrong-type node (null) -- Unguarded: accepts wrong-type node (non-null) -- Test sensitivity confirmed: opposite results with/without guard - -## Scrollbar drag handling - -**Problem:** Virtual scroll re-renders during native scrollbar drag need to keep `scrollHeight` stable, otherwise the thumb jumps on release. - -**Approach:** Detect scrollbar pointerdown via `offsetX >= clientWidth`. During drag, run full renders (with DOM recycling) using `_compensateScrollForMeasurementDelta` instead of the normal path. This keeps the rendered DOM in sync with the scroll position throughout the drag, so `scrollHeight` stays stable and the thumb doesn't jump when the user releases. - -**Why not spacer-only updates:** An earlier iteration used spacer-only updates during drag (updating spacer heights without rebuilding DOM). This caused `scrollHeight` to drift by 10,000+ px because the stale rendered content didn't match the new spacer heights. On release, the full render would correct the content, but the `scrollHeight` change shifted the thumb position by ~40% of the viewport. - -**Why full renders work:** The scroll container (`#messages`) is never destroyed; only its inner container (`#msgInner`) gets `innerHTML = ''`. The browser maintains the native pointer grab on the scrollbar because the scrollbar belongs to `#messages`, not `#msgInner`. DOM recycling further reduces the churn by reattaching surviving nodes after the wipe. - -**Evidence (native scrollbar drag, Windows SendInput + CDP):** -- 12-step drag from near-top to near-bottom of scrollbar track -- Total scroll delta: 13,522px -- All 12 steps monotonically increasing, 0 reversals -- `_scrollbarDragActive` flag: `true` during drag, `false` after release -- 53 content nodes, 0 blanks after release -- `scrollHeight` delta on release: 49px (0.06% of total, vs 10,710px / 14.8% with spacer-only) - -**Evidence (CDP behavioral, synthetic PointerEvents):** -- Scrollbar click (offsetX >= clientWidth): flag activates -- Content click (offsetX < clientWidth): flag does NOT activate -- pointerup: flag clears -- pointercancel: flag clears -- DOM children count preserved during drag (65 → 65) - -## Research-informed design - -Virtual scroll libraries surveyed: react-virtuoso, TanStack Virtual, react-window, react-virtualized. - -**Key finding:** None implement DOM node recycling. This pattern is novel to this codebase. - -**Adopted from research:** -- TanStack Virtual's spatial threshold (1.5px) vs temporal (150ms) for programmatic scroll detection — we use a 150ms safety valve -- Debounced clear pattern from react-virtuoso's scroll-to-index implementation — our `_deferClearProgrammaticScroll` -- No library tests scrollbar drag interaction; our SendInput-based test is novel - -**Test architecture:** -- Tier 1: Structural (flag/stash existence, lifecycle) — 6 tests -- Tier 2: Bug-specific behavioral (measurement corruption, type guards, stuck flag) — 9 tests -- Tier 3: Integration (full stash→wipe→lookup cycles) — 8 tests -- Tier 4: Optimization correctness (content-skip, key coercion) — 3 tests -- Tier 5: Scrollbar drag (detection, full-render-during-drag, release) — 13 tests -- Tier 6: Maintainer regression tests (raw numerical evidence for each must-fix) — 9 tests -- CDP behavioral: live browser flag lifecycle and render suppression — 8 tests -- Native scrollbar: real OS mouse events via Windows SendInput + CDP — 4 tests diff --git a/tests/test-results-cdp-behavioral.txt b/tests/test-results-cdp-behavioral.txt deleted file mode 100644 index b8918e20..00000000 --- a/tests/test-results-cdp-behavioral.txt +++ /dev/null @@ -1,30 +0,0 @@ -Connecting to ws://localhost:9222/devtools/page/3CC47AC2E288A6FE8670B6C1EAD90117 - -=== T1: Flag initial state === - PASS: flag starts as false - -=== T2: Pointerdown on scrollbar area === - PASS: scrollbar click activates flag - -=== T3: Pointerdown on content area === - PASS: content click does not activate flag - -=== T4: Pointerup clears flag === - PASS: pointerup clears flag - -=== T5: Pointercancel clears flag === - PASS: pointercancel clears flag - -=== T6: Spacer-only path === - PASS: spacers present (before=315848px, after=13747px) - -=== T7: DOM stability during drag (render suppression) === - PASS: children preserved (65 -> 65) - -=== T8: Full render after drag release === - PASS: content rendered after drag release - -================================================== -RESULTS: 8 passed, 0 failed, 8 total -================================================== -ALL CDP BEHAVIORAL TESTS PASSED diff --git a/tests/test-results-native-scrollbar.txt b/tests/test-results-native-scrollbar.txt deleted file mode 100644 index aea2f2ca..00000000 --- a/tests/test-results-native-scrollbar.txt +++ /dev/null @@ -1,42 +0,0 @@ -Connected to ws://localhost:9222/devtools/page/3CC47AC2E288A6FE8670B6C1EAD90117 -Window bounds: {'left': -6, 'top': -6, 'width': 1720, 'height': 926, 'windowState': 'maximized'} -Messages element: {'left': 348, 'top': 38.88888931274414, 'right': 1706.2222900390625, 'bottom': 674.9375343322754, 'width': 1358.2222900390625, 'height': 636.0486450195312, 'clientWidth': 1348, 'scrollHeight': 358441, 'scrollTop': 317723.5625, 'scrollbarWidth': 10, 'dpr': 2.25, 'innerWidth': 1707, 'innerHeight': 791} -DPR=2.25, chrome header: 135px CSS -Viewport origin CSS: (7, 129) -Scrollbar screen X: 3837, track Y: 423-1764 - -Pre-drag scrollTop: 0 - -=== Native Scrollbar Drag via SendInput === - Mouse down at (3837, 453) - _scrollbarDragActive after native pointerdown: True - Step 1/12: screen_y=562, scrollTop=556.4444580078125 - Step 2/12: screen_y=671, scrollTop=1670.6666259765625 - Step 3/12: screen_y=781, scrollTop=3339.5556640625 - Step 4/12: screen_y=890, scrollTop=4521.77783203125 - Step 5/12: screen_y=999, scrollTop=5158.22216796875 - Step 6/12: screen_y=1108, scrollTop=6757.77783203125 - Step 7/12: screen_y=1218, scrollTop=7540 - Step 8/12: screen_y=1327, scrollTop=9349.3330078125 - Step 9/12: screen_y=1436, scrollTop=10461.77734375 - Step 10/12: screen_y=1545, scrollTop=11644.888671875 - Step 11/12: screen_y=1655, scrollTop=12757.3330078125 - Step 12/12: screen_y=1764, scrollTop=13522.6669921875 - _scrollbarDragActive after release: False - -=== RESULTS === - Total scroll delta: 13522.6669921875px - Monotonic steps: 11/11 - Reversals: 0 - Flag set during drag: True - Flag cleared after release: True - Content nodes: 53, blanks: 0 - -================================================== - PASS: T1 Native drag moved scroll - PASS: T2 Monotonic scrolling - PASS: T3 No blank content after release - PASS: T4 Flag lifecycle (set during, cleared after) - - ALL PASS: True -================================================== diff --git a/tests/test-results-virtualization.txt b/tests/test-results-virtualization.txt deleted file mode 100644 index 9c01f1a3..00000000 --- a/tests/test-results-virtualization.txt +++ /dev/null @@ -1,46 +0,0 @@ -============================= test session starts ============================= -platform win32 -- Python 3.11.15, pytest-9.0.3, pluggy-1.6.0 -- C:\Apps\hermes\hermes-agent\venv\Scripts\python.exe -cachedir: .pytest_cache -rootdir: D:\Repos\hermes-webui-pr-4346-vscroll-footer-jitter -configfile: pytest.ini -plugins: anyio-4.12.1, langsmith-0.8.14, shard-0.1.2, timeout-2.4.0 -collecting ... collected 29 items -Running 29 items in this shard: tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_virtualizes_older_history_but_keeps_recent_tail, tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_collapses_to_tail_only_near_bottom, tests/test_issue500_message_list_virtualization.py::test_render_messages_uses_virtual_window_and_spacer_measurement_path, tests/test_issue500_message_list_virtualization.py::test_measurement_uses_one_primary_row_and_adjacent_activity_siblings_only, tests/test_issue500_message_list_virtualization.py::test_virtual_keep_tail_count_stays_bounded_after_history_expands_render_window, tests/test_issue500_message_list_virtualization.py::test_virtual_prepended_height_delta_uses_prefix_cache_only_when_virtualized, tests/test_issue500_message_list_virtualization.py::test_virtual_question_jump_scroll_target_uses_visible_index_height_prefix, tests/test_issue500_message_list_virtualization.py::test_height_cache_preserves_measured_prefix_across_append_only_growth, tests/test_issue500_message_list_virtualization.py::test_height_cache_preserves_measured_suffix_across_prepended_history, tests/test_issue500_message_list_virtualization.py::test_measurement_refresh_budget_is_keyed_to_window_shape_not_pad_height, tests/test_issue500_message_list_virtualization.py::test_tool_rows_do_not_carry_message_measurement_hook, tests/test_issue500_message_list_virtualization.py::test_viewport_intersection_helper_detects_visible_rendered_rows_only, tests/test_issue500_message_list_virtualization.py::test_render_messages_has_one_shot_virtual_blank_viewport_fallback, tests/test_issue500_message_list_virtualization.py::test_virtual_blank_viewport_recovery_evicts_stale_cache_before_fallback, tests/test_issue500_message_list_virtualization.py::test_same_frame_restore_nudges_virtual_window_when_anchor_row_is_missing, tests/test_issue500_message_list_virtualization.py::test_virtualize_transcript_opt_out_forces_full_render_window, tests/test_issue500_message_list_virtualization.py::test_virtualize_transcript_gate_present_in_current_window_fn, tests/test_issue500_message_list_virtualization.py::test_message_virtual_default_height_for_role_returns_correct_heights, tests/test_issue500_message_list_virtualization.py::test_message_virtual_role_for_entry_classifies_tool_calls, tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_with_role_for_idx_uses_role_defaults, tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_cached_heights_override_role_defaults, tests/test_issue500_message_list_virtualization.py::test_offset_helpers_use_per_role_defaults_for_uncached_rows, tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_no_anchor_does_not_throw, tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_shifts_scroll_when_anchor_moves, tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_skips_small_delta, tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_sets_programmatic_scroll_flag, tests/test_issue500_message_list_virtualization.py::test_virtualized_render_uses_compensation_helper, tests/test_issue500_message_list_virtualization.py::test_scroll_listener_guards_programmatic_scroll_before_marking_active, tests/test_issue500_message_list_virtualization.py::test_clear_height_cache_resets_scroll_settle_globals - -tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_virtualizes_older_history_but_keeps_recent_tail PASSED [ 3%] -tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_collapses_to_tail_only_near_bottom PASSED [ 6%] -tests/test_issue500_message_list_virtualization.py::test_render_messages_uses_virtual_window_and_spacer_measurement_path PASSED [ 10%] -tests/test_issue500_message_list_virtualization.py::test_measurement_uses_one_primary_row_and_adjacent_activity_siblings_only PASSED [ 13%] -tests/test_issue500_message_list_virtualization.py::test_virtual_keep_tail_count_stays_bounded_after_history_expands_render_window PASSED [ 17%] -tests/test_issue500_message_list_virtualization.py::test_virtual_prepended_height_delta_uses_prefix_cache_only_when_virtualized PASSED [ 20%] -tests/test_issue500_message_list_virtualization.py::test_virtual_question_jump_scroll_target_uses_visible_index_height_prefix PASSED [ 24%] -tests/test_issue500_message_list_virtualization.py::test_height_cache_preserves_measured_prefix_across_append_only_growth PASSED [ 27%] -tests/test_issue500_message_list_virtualization.py::test_height_cache_preserves_measured_suffix_across_prepended_history PASSED [ 31%] -tests/test_issue500_message_list_virtualization.py::test_measurement_refresh_budget_is_keyed_to_window_shape_not_pad_height PASSED [ 34%] -tests/test_issue500_message_list_virtualization.py::test_tool_rows_do_not_carry_message_measurement_hook PASSED [ 37%] -tests/test_issue500_message_list_virtualization.py::test_viewport_intersection_helper_detects_visible_rendered_rows_only PASSED [ 41%] -tests/test_issue500_message_list_virtualization.py::test_render_messages_has_one_shot_virtual_blank_viewport_fallback PASSED [ 44%] -tests/test_issue500_message_list_virtualization.py::test_virtual_blank_viewport_recovery_evicts_stale_cache_before_fallback PASSED [ 48%] -tests/test_issue500_message_list_virtualization.py::test_same_frame_restore_nudges_virtual_window_when_anchor_row_is_missing PASSED [ 51%] -tests/test_issue500_message_list_virtualization.py::test_virtualize_transcript_opt_out_forces_full_render_window PASSED [ 55%] -tests/test_issue500_message_list_virtualization.py::test_virtualize_transcript_gate_present_in_current_window_fn PASSED [ 58%] -tests/test_issue500_message_list_virtualization.py::test_message_virtual_default_height_for_role_returns_correct_heights PASSED [ 62%] -tests/test_issue500_message_list_virtualization.py::test_message_virtual_role_for_entry_classifies_tool_calls PASSED [ 65%] -tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_with_role_for_idx_uses_role_defaults PASSED [ 68%] -tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_cached_heights_override_role_defaults PASSED [ 72%] -tests/test_issue500_message_list_virtualization.py::test_offset_helpers_use_per_role_defaults_for_uncached_rows PASSED [ 75%] -tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_no_anchor_does_not_throw PASSED [ 79%] -tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_shifts_scroll_when_anchor_moves PASSED [ 82%] -tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_skips_small_delta PASSED [ 86%] -tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_sets_programmatic_scroll_flag PASSED [ 89%] -tests/test_issue500_message_list_virtualization.py::test_virtualized_render_uses_compensation_helper PASSED [ 93%] -tests/test_issue500_message_list_virtualization.py::test_scroll_listener_guards_programmatic_scroll_before_marking_active PASSED [ 96%] -tests/test_issue500_message_list_virtualization.py::test_clear_height_cache_resets_scroll_settle_globals PASSED [100%] - -============================== warnings summary =============================== -tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_virtualizes_older_history_but_keeps_recent_tail - C:\Apps\hermes\hermes-agent\venv\Lib\site-packages\discord\player.py:30: DeprecationWarning: 'audioop' is deprecated and slated for removal in Python 3.13 - import audioop - --- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html -======================== 29 passed, 1 warning in 5.74s ======================== diff --git a/tests/test-results.txt b/tests/test-results.txt deleted file mode 100644 index 9d9b54c6..00000000 --- a/tests/test-results.txt +++ /dev/null @@ -1,65 +0,0 @@ -============================= test session starts ============================= -platform win32 -- Python 3.11.15, pytest-9.0.3, pluggy-1.6.0 -- C:\Apps\hermes\hermes-agent\venv\Scripts\python.exe -cachedir: .pytest_cache -rootdir: D:\Repos\hermes-webui-pr-4346-vscroll-footer-jitter -configfile: pytest.ini -plugins: anyio-4.12.1, langsmith-0.8.14, shard-0.1.2, timeout-2.4.0 -collecting ... collected 48 items -Running 48 items in this shard: tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_flag_exists, tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_stash_exists, tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_flag_lifecycle, tests/test_issue4346_vscroll_footer_jitter.py::test_js_stash_populated_before_wipe, tests/test_issue4346_vscroll_footer_jitter.py::test_assistant_turn_uses_recycle_key_not_msg_idx, tests/test_issue4346_vscroll_footer_jitter.py::test_no_stale_raf_settimeout_clear_pattern, tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_returns_segment_height_not_turn_height, tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_accumulates_tool_siblings, tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_on_buggy_turn_container_returns_wrong_height, tests/test_issue4346_vscroll_footer_jitter.py::TestBug2TypeCheckRecycling::test_user_branch_rejects_assistant_turn_node, tests/test_issue4346_vscroll_footer_jitter.py::TestBug2TypeCheckRecycling::test_assistant_branch_rejects_user_row_node, tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_defer_clear_debounces_and_clears_flag, tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_defer_clear_second_call_cancels_first, tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_scroll_handler_safety_valve_in_source, tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_safety_valve_clears_stale_flag, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_populates_from_recycle_key_and_msg_idx, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_excludes_live_assistant_turn, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_excludes_nested_live_assistant_turn, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_full_recycle_cycle_user_row, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_full_recycle_cycle_assistant_turn, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_cross_type_stash_collision_rejected, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_recycling_disabled_when_flag_false, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_recycle_key_does_not_pollute_measurement_selector, tests/test_issue4346_vscroll_footer_jitter.py::TestContentSkipOptimization::test_unchanged_content_skips_innerhtml_update, tests/test_issue4346_vscroll_footer_jitter.py::TestContentSkipOptimization::test_changed_content_updates_innerhtml, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_scrollbar_drag_flag_declared, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerdown_sets_flag_when_on_scrollbar, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerdown_guard_exists_in_source, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerup_clears_flag_and_triggers_render, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointercancel_clears_flag, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_guard_exists_in_render_scheduler, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_path_uses_full_render, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_path_sets_programmatic_scroll, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_release_uses_same_render_path, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_multi_segment_turn_raw_heights, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_buggy_path_inflates_height, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_queryselector_returns_segment_not_container, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_user_row_in_assistant_slot_without_guard, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_assistant_turn_in_user_slot_without_guard, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_stash_collision_rate_in_shifted_indices, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_measurement_test_fails_on_buggy_layout, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_type_check_test_fails_on_unguarded_code, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_recycle_key_separation_prevents_selector_collision, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_calls_compensate_scroll, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_sets_programmatic_scroll, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_no_spacer_only_update, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_updates_window_key, tests/test_issue4346_vscroll_footer_jitter.py::TestStashKeyCoercion::test_string_dataset_matches_numeric_lookup - -tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_flag_exists PASSED [ 2%] -tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_stash_exists PASSED [ 4%] -tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_flag_lifecycle PASSED [ 6%] -tests/test_issue4346_vscroll_footer_jitter.py::test_js_stash_populated_before_wipe PASSED [ 8%] -tests/test_issue4346_vscroll_footer_jitter.py::test_assistant_turn_uses_recycle_key_not_msg_idx PASSED [ 10%] -tests/test_issue4346_vscroll_footer_jitter.py::test_no_stale_raf_settimeout_clear_pattern PASSED [ 12%] -tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_returns_segment_height_not_turn_height PASSED [ 14%] -tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_accumulates_tool_siblings PASSED [ 16%] -tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_on_buggy_turn_container_returns_wrong_height PASSED [ 18%] -tests/test_issue4346_vscroll_footer_jitter.py::TestBug2TypeCheckRecycling::test_user_branch_rejects_assistant_turn_node PASSED [ 20%] -tests/test_issue4346_vscroll_footer_jitter.py::TestBug2TypeCheckRecycling::test_assistant_branch_rejects_user_row_node PASSED [ 22%] -tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_defer_clear_debounces_and_clears_flag PASSED [ 25%] -tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_defer_clear_second_call_cancels_first PASSED [ 27%] -tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_scroll_handler_safety_valve_in_source PASSED [ 29%] -tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_safety_valve_clears_stale_flag PASSED [ 31%] -tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_populates_from_recycle_key_and_msg_idx PASSED [ 33%] -tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_excludes_live_assistant_turn PASSED [ 35%] -tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_excludes_nested_live_assistant_turn PASSED [ 37%] -tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_full_recycle_cycle_user_row PASSED [ 39%] -tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_full_recycle_cycle_assistant_turn PASSED [ 41%] -tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_cross_type_stash_collision_rejected PASSED [ 43%] -tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_recycling_disabled_when_flag_false PASSED [ 45%] -tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_recycle_key_does_not_pollute_measurement_selector PASSED [ 47%] -tests/test_issue4346_vscroll_footer_jitter.py::TestContentSkipOptimization::test_unchanged_content_skips_innerhtml_update PASSED [ 50%] -tests/test_issue4346_vscroll_footer_jitter.py::TestContentSkipOptimization::test_changed_content_updates_innerhtml PASSED [ 52%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_scrollbar_drag_flag_declared PASSED [ 54%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerdown_sets_flag_when_on_scrollbar PASSED [ 56%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerdown_guard_exists_in_source PASSED [ 58%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerup_clears_flag_and_triggers_render PASSED [ 60%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointercancel_clears_flag PASSED [ 62%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_guard_exists_in_render_scheduler PASSED [ 64%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_path_uses_full_render PASSED [ 66%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_path_sets_programmatic_scroll PASSED [ 68%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_release_uses_same_render_path PASSED [ 70%] -tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_multi_segment_turn_raw_heights PASSED [ 72%] -tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_buggy_path_inflates_height PASSED [ 75%] -tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_queryselector_returns_segment_not_container PASSED [ 77%] -tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_user_row_in_assistant_slot_without_guard PASSED [ 79%] -tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_assistant_turn_in_user_slot_without_guard PASSED [ 81%] -tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_stash_collision_rate_in_shifted_indices PASSED [ 83%] -tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_measurement_test_fails_on_buggy_layout PASSED [ 85%] -tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_type_check_test_fails_on_unguarded_code PASSED [ 87%] -tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_recycle_key_separation_prevents_selector_collision PASSED [ 89%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_calls_compensate_scroll PASSED [ 91%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_sets_programmatic_scroll PASSED [ 93%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_no_spacer_only_update PASSED [ 95%] -tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_updates_window_key PASSED [ 97%] -tests/test_issue4346_vscroll_footer_jitter.py::TestStashKeyCoercion::test_string_dataset_matches_numeric_lookup PASSED [100%] - -============================== warnings summary =============================== -tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_flag_exists - C:\Apps\hermes\hermes-agent\venv\Lib\site-packages\discord\player.py:30: DeprecationWarning: 'audioop' is deprecated and slated for removal in Python 3.13 - import audioop - --- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html -======================== 48 passed, 1 warning in 6.45s ========================