Skip to content

test(cli): cover bottom-stuck virtualized list behavior#7652

Open
jay666mnj wants to merge 5 commits into
QwenLM:mainfrom
jay666mnj:fix-virtualized-list-resume-spacing
Open

test(cli): cover bottom-stuck virtualized list behavior#7652
jay666mnj wants to merge 5 commits into
QwenLM:mainfrom
jay666mnj:fix-virtualized-list-resume-spacing

Conversation

@jay666mnj

@jay666mnj jay666mnj commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What this PR does

This PR adds regression coverage for bottom-stuck VirtualizedList layout behavior in the CLI. The tests pin two current invariants: a bottom-stuck list with overestimated row heights still renders through the final item before excessive natural rerender convergence, and a short bottom-stuck list inside a taller container renders only the measured rows with no trailing blank lines.

Why it's needed

Reviewer verification showed the previous production diff did not produce observable behavior changes because the tested states already pass on the base implementation. Following that feedback, this PR now keeps only the useful regression tests as behavior pins for the existing bottom-stuck virtualization contract and removes the unreachable defensive production branch.

Reviewer Test Plan

How to verify

Run the focused VirtualizedList.test.tsx suite and the formatting/lint checks for the touched test file. A reviewer should confirm that the new tests pass while the production VirtualizedList.tsx implementation remains unchanged relative to the PR base.

Evidence (Before & After)

N/A - this PR is now test-only. The latest review feedback specifically found no reachable behavior change from the previous production diff, so the production change was removed and only regression coverage remains.

Tested on

OS Status
🍏 macOS ⚠️ not tested
🪟 Windows ✅ tested
🐧 Linux ⚠️ not tested

Environment (optional)

Windows local workspace, Node.js v22.13.1. Verified with node node_modules\prettier\bin\prettier.cjs --check packages/cli/src/ui/components/shared/VirtualizedList.test.tsx, node node_modules\eslint\bin\eslint.js packages/cli/src/ui/components/shared/VirtualizedList.test.tsx, cd packages/cli; node ..\..\node_modules\vitest\vitest.mjs run src/ui/components/shared/VirtualizedList.test.tsx --coverage.enabled=false --silent=true --poolOptions.threads.minThreads=1 --poolOptions.threads.maxThreads=1, and git diff --check.

Risk & Scope

  • Main risk or tradeoff: Test-only change; no production behavior changes.
  • Not validated / out of scope: This PR does not claim a current-main runtime reproduction for the originally related report, and it does not change the virtualized list implementation.
  • Breaking changes / migration notes: None.

Linked Issues

Related to #7485.

中文说明

What this PR does

本 PR 为 CLI 中底部吸附的 VirtualizedList 布局行为增加回归覆盖。测试固定两个现有不变量:估算行高偏大的底部吸附列表在过多自然 rerender 收敛前仍能渲染到最后一项;短列表位于更高容器中且底部吸附时,只渲染实际测量行,不在末尾留下额外空行。

Why it's needed

维护者验证表明,之前的生产代码 diff 没有带来可观察行为变化,因为相关测试状态在基线实现上已经通过。根据该反馈,本 PR 现在只保留有价值的回归测试,用作现有底部吸附虚拟化契约的行为 pin,并移除不可达的防御性生产分支。

Reviewer Test Plan

How to verify

运行聚焦的 VirtualizedList.test.tsx 测试套件,以及触及测试文件的格式化和 lint 检查。Reviewer 应确认新增测试通过,同时生产 VirtualizedList.tsx 实现相对 PR 基线保持不变。

Evidence (Before & After)

不适用 - 本 PR 现在仅包含测试。最新 review 反馈明确指出之前的生产 diff 没有可达行为变化,因此已移除生产改动,只保留回归覆盖。

Tested on

OS Status
🍏 macOS ⚠️ not tested
🪟 Windows ✅ tested
🐧 Linux ⚠️ not tested

Environment (optional)

Windows 本地工作区,Node.js v22.13.1。已验证命令:node node_modules\prettier\bin\prettier.cjs --check packages/cli/src/ui/components/shared/VirtualizedList.test.tsxnode node_modules\eslint\bin\eslint.js packages/cli/src/ui/components/shared/VirtualizedList.test.tsxcd packages/cli; node ..\..\node_modules\vitest\vitest.mjs run src/ui/components/shared/VirtualizedList.test.tsx --coverage.enabled=false --silent=true --poolOptions.threads.minThreads=1 --poolOptions.threads.maxThreads=1git diff --check

Risk & Scope

  • Main risk or tradeoff: 仅测试改动;不改变生产行为。
  • Not validated / out of scope: 本 PR 不声称已经在当前 main 上复现原关联 issue 的运行时问题,也不修改虚拟化列表实现。
  • Breaking changes / migration notes: 无。

Linked Issues

关联 #7485

@jay666mnj jay666mnj closed this Jul 24, 2026
@jay666mnj jay666mnj reopened this Jul 24, 2026

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @jay666mnj — thanks for the PR! The code change itself looks interesting, but the PR body is empty, so there's nothing for a reviewer to evaluate the motivation, reproduction, or test plan against.

Could you fill in the PR template? The key sections we need:

  • What this PR does — describe the change in prose
  • Why it's needed — what user-visible bug does this fix? A link to an issue or a before/after reproduction goes a long way here
  • Reviewer Test Plan — how can a reviewer confirm the fix works (steps, expected vs observed behavior, tmux logs or screenshots for TUI changes)
  • Risk & Scope and Linked Issues

Once the description is in place, re-run with @qwen-code /triage and we'll pick it up from there. 🙏

中文说明

感谢贡献!代码改动本身看起来有意义,但 PR 描述为空,reviewer 无法评估动机、复现步骤或测试方案。

请填写 PR 模板,重点包括:

  • What this PR does — 用文字描述改动内容
  • Why it's needed — 修复了什么用户可见的 bug?关联 issue 或提供 before/after 复现会很有帮助
  • Reviewer Test Plan — reviewer 如何确认修复有效(步骤、预期 vs 实际行为,TUI 改动请附 tmux 日志或截图)
  • Risk & ScopeLinked Issues

填写完成后,使用 @qwen-code /triage 重新触发审查。🙏

Qwen Code · qwen3.8-max-preview

@jay666mnj

Copy link
Copy Markdown
Contributor Author

@qwen-code /triage

@jay666mnj

Copy link
Copy Markdown
Contributor Author

@qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestions are inline.

— qwen3.7-max via Qwen Code /review

Comment on lines +128 to +130
expect(frame).toContain('item-0');
expect(frame).toContain('item-19');
expect(frame.endsWith('item-19')).toBe(true);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The regression test passes without the PR's source changes — verified by running the full test suite against the pre-PR VirtualizedList.tsx. With 20 items, tallEstimate = () => 4, containerHeight = 20, and 10 rerenders, the natural iterative measurement convergence completes fully (~5–6 iterations), so all items render regardless of whether the renderStartIndex/measuredRenderedHeight collapse mechanism exists.

Concrete cost: a future regression in the collapse logic (e.g., an off-by-one in i - 1, or a wrong early-return condition) would ship undetected because this test provides no regression safety net for the code paths it claims to exercise.

Consider reducing the rerender count to 2–3 and asserting frame.endsWith('item-19') at that point (which requires the collapse mechanism since convergence hasn't finished), or adding a separate test with 50+ items where the virtualization window never renders all items and the collapse path is the only way to pass.

— qwen3.7-max via Qwen Code /review

Comment on lines +493 to +496
const renderStartIndex = (() => {
if (!isStickingToBottom || renderStatic === true || endIndex < 0) {
return startIndex;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The backward walk recomputes cumulative item heights using heights[key] ?? estimatedItemHeight(i) with the same coercion (Number.isFinite(raw) && raw > 0 ? raw : 0) already baked into the offsets memo above. The existing findLastLE binary-search helper could derive the same result in one call, since heightFromEnd >= viewHeightForEndIndex is equivalent to offsets[i] <= offsets[endIndex + 1] - viewHeightForEndIndex.

Concrete cost: two independent code paths compute per-item heights with identical coercion. If a future change adjusts the coercion in one place but not the other (e.g., adding a minimum-height clamp to offsets), the render range derived from renderStartIndex would disagree with the scroll math derived from offsets, producing either a blank strip or an extra row rendered off-screen.

Suggested change
const renderStartIndex = (() => {
if (!isStickingToBottom || renderStatic === true || endIndex < 0) {
return startIndex;
}
const renderStartIndex = (() => {
if (!isStickingToBottom || renderStatic === true || endIndex < 0) {
return startIndex;
}
const targetOffset =
(offsets[endIndex + 1] ?? totalHeight) - viewHeightForEndIndex;
const backStart = findLastLE(offsets, targetOffset);
return Math.min(startIndex, Math.max(0, backStart));
})();

— qwen3.7-max via Qwen Code /review

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline. 2 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here. Not reviewed: Agent 1a: Line-by-line correctness, Agent 2: Security, Agent 3: Code quality, Agent 4: Performance & efficiency, Agent 5: Test coverage, Agent 6a: Undirected audit — attacker mindset, Agent 6b: Undirected audit — 3 AM oncall mindset, Agent 6c: Undirected audit — six-months-later maintainer, Agent 1b: Removed-behavior audit, Agent 1c: Cross-file tracer, Agent 7: Build & test verification — its prompt was built, but no agent on record was launched with it. Not reviewed: verification — a verifier ran and opened its brief, but no agent was launched with the prompt the CLI built — the launch was written by hand, and the posted findings cannot be counted as verified against it.

— qwen3.7-max via Qwen Code /review

Comment on lines +914 to +917
measuredRenderedHeight !== undefined &&
measuredRenderedHeight < props.containerHeight
? measuredRenderedHeight
: totalHeight,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The measuredRenderedHeight < props.containerHeight true-branch — the actual viewport collapse — is never exercised by any test in the suite.

In the new test, after convergence measuredRenderedHeight = 20 = containerHeight, so the strict < is false and totalHeight is used (same as pre-fix behavior). In the existing SCROLL_TO_ITEM_END tests (containerHeight=5, 20+ items at height 1), measuredRenderedHeight ≥ containerHeight. No test creates a scenario where measuredRenderedHeight is defined and strictly less than containerHeight.

Failure scenario: a bug in this branch (e.g. collapsing when it shouldn't, or computing wrong height) goes undetected — the user-visible result would be either a blank gap below short content (the original bug regressing) or content clipped too short.

Consider adding a test with few items in a large container — e.g. 5 items with estimatedItemHeight = () => 1, containerHeight = 20, SCROLL_TO_ITEM_END. After convergence measuredRenderedHeight = 5 < 20, and rootHeight should collapse to 5. Assert the frame ends immediately after the last item with no trailing blank lines.

— qwen3.7-max via Qwen Code /review

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. 3 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here.

— qwen3.7-max via Qwen Code /review

@doudouOUC doudouOUC left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestions are inline.

— qwen3.7-max via Qwen Code /review

Comment on lines +914 to +917
measuredRenderedHeight !== undefined &&
measuredRenderedHeight < props.containerHeight
? measuredRenderedHeight
: totalHeight,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] This PR links Fixes #7485, but that issue was closed as NOT_PLANNED (not reproducible) during this PR's lifecycle — two independent testers (@zjunothing on macOS, @ComplexSimply on Linux with 10+ configurations across both legacy and VP modes) could not reproduce the described gap between conversation content and the input prompt. The closing comment states: "Since this issue was bot-filed and we have no human runtime evidence of the symptom on current code, I'm going to close it as not reproducible." Additionally, the original report described behavior in the legacy (non-VP) rendering path, while this PR only modifies the VP-mode VirtualizedList. The two new tests also pass without the source changes (verified by running them against the pre-PR VirtualizedList.tsx), so they do not demonstrate that the fix changes observable behavior.

Concrete cost: The Fixes #7485 linkage will auto-close an issue that was already closed as not reproducible, and future readers may assume the bug was real and fixed. Consider reframing this PR as a defensive viewport-collapse improvement (the measuredRenderedHeight collapse is a sound optimization for short content) and removing the Fixes #7485 linkage, or providing a runtime reproduction on current main that shows the gap before the fix and its absence after.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] This PR links Fixes #7485, but that issue was closed as NOT_PLANNED (not reproducible) during this PR's lifecycle — two independent testers (@zjunothing on macOS, @ComplexSimply on Linux with 10+ configurations across both legacy and VP modes) could not reproduce the described gap between conversation content and the input prompt. The closing comment states: "Since this issue was bot-filed and we have no human runtime evidence of the symptom on current code, I'm going to close it as not reproducible." Additionally, the original report described behavior in the legacy (non-VP) rendering path, while this PR only modifies the VP-mode VirtualizedList. The two new tests also pass without the source changes (verified by running them against the pre-PR VirtualizedList.tsx), so they do not demonstrate that the fix changes observable behavior.

Concrete cost: The Fixes #7485 linkage will auto-close an issue that was already closed as not reproducible, and future readers may assume the bug was real and fixed. Consider reframing this PR as a defensive viewport-collapse improvement (the measuredRenderedHeight collapse is a sound optimization for short content) and removing the Fixes #7485 linkage, or providing a runtime reproduction on current main that shows the gap before the fix and its absence after.

— qwen3.7-max via Qwen Code /review

Updated the PR description based on the latest review feedback: removed the \Fixes https://github.com/QwenLM/qwen-code/issues/7485\ auto-closing linkage, reframed the change as a defensive viewport-collapse improvement for the virtualized path, and explicitly noted that this PR does not claim a current-main runtime reproduction for the originally related report.

@jay666mnj
jay666mnj requested a review from doudouOUC July 25, 2026 01:09
@gwinthis

Copy link
Copy Markdown
Collaborator

Review + Linux verification report (real build, tmux)

Verdict: the change is functionally safe, but I could not find any reachable state where it changes behavior — both new tests pass without the production change. I recommend keeping the regression tests and dropping (or re-justifying) the production diff.

The decisive experiment

I ran the PR's full test file against the base implementation (merge-base 819cd4ab4, i.e. VirtualizedList.tsx reverted, new tests kept):

Tests  22 passed (22)   # including both new "collapses…" tests

Both new tests pass without the production change. So the tests pin existing behavior; they do not demonstrate the defect the new code defends against. (This is consistent with the PR's own honesty that the original report #7485 was closed as not reproducible.)

Why the new branch is unreachable (bottom-stuck analysis)

offsets is built from heights[key] ?? estimate — measured heights already flow into the same offset table the window is derived from. measuredRenderedHeight is only defined when every rendered row is measured, and in that case it equals offsets[endIndex+1] − offsets[renderStartIndex] exactly. When bottom-stuck:

  • endIndex is the last item, so offsets[endIndex+1] = totalHeight.
  • If content overflows: scrollTop = totalHeight − containerHeight and offsets[startIndex] ≤ scrollTop, hence measuredRenderedHeight ≥ containerHeight — the < containerHeight collapse branch is never taken.
  • If content fits: renderStartIndex = 0 and measuredRenderedHeight = totalHeight — but base's Math.min(containerHeight, totalHeight) already collapses to the identical value.

renderStartIndex likewise reduces to startIndex in both cases (backStart = findLastLE(offsets, scrollTop) ≥ startIndex), so the spacer/range changes are also no-ops. The empirical result (tests green on base) and the math agree.

Verification evidence (Linux, commit 018707e)

  • Focused tests on PR branch: 22/22 passed; same file with base implementation: 22/22 passed (the key finding).
  • Full npm run build && npm run bundle clean; bundle verified to contain the new code path.
  • tmux smoke (120x30, real session): 30-line model output renders bottom-stuck to the final line with no trailing blank space; short-content startup keeps the composer directly beneath content. No regressions observed.

Suggestion

Per the repo's "Simplicity First" principle, ~30 lines of production logic guarding a state that appears unreachable is a maintenance cost without observable benefit. Two constructive paths: (1) keep the two new tests (they are good pins of bottom-stuck behavior) and drop the production change, or (2) provide a failing test on main that the change fixes — that would immediately upgrade this from defensive to justified.

@jay666mnj jay666mnj changed the title fix(cli): collapse bottom-stuck virtualized list viewport test(cli): cover bottom-stuck virtualized list behavior Jul 25, 2026
@jay666mnj

jay666mnj commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the verification. I followed path (1): removed the production VirtualizedList.tsx diff and kept the two regression tests as bottom-stuck behavior pins. I also updated the PR title/body to make this explicitly test-only and to avoid claiming a current-main runtime reproduction for #7485. Local verification on Windows: prettier check for VirtualizedList.test.tsx, eslint for VirtualizedList.test.tsx, full VirtualizedList.test.tsx via Vitest (22/22 passed), and git diff --check. @gwinthis

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Downgraded from Approve to Comment: CI still running. Reviewed.

— qwen3.7-max via Qwen Code /review

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅

中文说明

未发现问题。LGTM!✅

— qwen3.8-max-preview via Qwen Code /review

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.

5 participants