Skip to content

test(chat): add 27 vitest tests for ThreadPanel component#2053

Merged
jaylfc merged 2 commits into
jaylfc:devfrom
hognek:feat/thread-panel-tests
Jul 19, 2026
Merged

test(chat): add 27 vitest tests for ThreadPanel component#2053
jaylfc merged 2 commits into
jaylfc:devfrom
hognek:feat/thread-panel-tests

Conversation

@hognek

@hognek hognek commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Task: t_4c5477ca. Fixes #657 (C4).

Adds comprehensive Vitest + React Testing Library tests for the ThreadPanel
component extracted from MessagesApp.tsx.

Coverage (27 tests):

  • Rendering: normal panel layout, fullscreen layout, reply textarea
  • Parent message: fetch & display, author label, own-user display name
  • Replies: fetch & display, author labels, liveReply merging with de-duplication
  • Scroll: useEffect triggers on new liveReply arrival
  • Error handling: parent fetch failure, replies fetch failure, non-OK responses, missing messages field
  • Submit: Enter/Shift+Enter, empty input guard, input clearing on success/failure, disable-while-sending
  • Close button: normal mode (Close thread), fullscreen mode (Back)

All 97 existing chat tests + 27 new = 124 passing. tsc --noEmit clean.

Covers rendering (normal/fullscreen layouts), parent message fetch &
display, reply fetch & display (including de-duplication of liveReplies),
load errors (parent + replies failure), send errors, submit behavior
(Enter/Shift+Enter/empty input), input clearing on success/failure,
disabled-while-sending state, and close button (normal + fullscreen).

Task: t_4c5477ca. Ref: jaylfc#657
@hognek
hognek marked this pull request as ready for review July 19, 2026 16:21
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@hognek, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 32 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7a3838e3-a25c-4177-aff6-3919d47b431d

📥 Commits

Reviewing files that changed from the base of the PR and between 514d947 and 1fe0aa0.

📒 Files selected for processing (1)
  • desktop/src/apps/chat/__tests__/ThreadPanel.test.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gitar-bot

gitar-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

Gitar is working

Gitar

});

it("scrolls to bottom when a new liveReply arrives", async () => {
stubFetchOk();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: Test name says "scrolls to bottom" but asserts only that the reply rendered, not the scroll position.

The component scrolls via scrollRef.current.scrollTop = scrollRef.current.scrollHeight (ThreadPanel.tsx:57) in an effect keyed on liveReplies.length. This behavior is never actually verified. Either assert the scroll position (e.g. read the scroll container's scrollTop/scrollHeight after rerender) or rename the test so it doesn't over-promise coverage.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

vi.stubGlobal(
"fetch",
vi.fn().mockImplementation((url: string) => {
// Match on the LAST path segment that distinguishes the endpoints:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: The route-matching comments are inaccurate and a maintenance hazard.

The comment claims it matches on the "LAST path segment" / uses url.endsWith, but the code uses url.includes("/threads/") then url.includes("/messages/"). The replies URL /api/chat/channels/{ch}/threads/{id}/messages contains BOTH segments, and it maps to "threads" only because /threads/ is checked first. The documented logic does not match the implementation. If someone reorders the if branches or changes the URL shape, the stub will silently break. Correct the comment to describe the actual includes-based, order-dependent matching.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

created_at: 1700000200,
};

beforeEach(() => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: beforeEach only calls vi.restoreAllMocks(), which does not reset a vi.stubGlobal("fetch", ...) left over from a test that throws before afterEach runs vi.unstubAllGlobals().

Most tests rely on afterEach to unstub globals, so this works in practice. But if a test throws mid-flight, a leaked global fetch stub could bleed into a subsequent test and cause confusing failures. Consider adding vi.unstubAllGlobals() to beforeEach as a defensive reset.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

expect(onSend).not.toHaveBeenCalled();
});

it("does NOT submit on empty or whitespace-only input", () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: Coverage gap: the sending re-entrancy guard in submit() (if (!content || sending) return, ThreadPanel.tsx:89) is not tested.

The "disables textarea while sending" test confirms the disabled state changes, but no test verifies that a second Enter keypress mid-send is ignored (i.e. onSend is still called only once while sending is true). Since the component explicitly guards against double-submit, an assertion like expect(onSend).toHaveBeenCalledTimes(1) after firing a second Enter while the send promise is pending would lock in that behavior.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found (prior 4 suggestions resolved) | Recommendation: Merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 0
Previously Reported (now resolved in this update)

The incremental diff (6be787e8..1fe0aa0) addressed all 4 prior SUGGESTION-level findings:

  • Line 31beforeEach now calls vi.unstubAllGlobals(), fixing the leaked global fetch stub risk.
  • Lines 44-49 — Route-matching comments rewritten to accurately describe the includes-based, order-dependent implementation (checked /threads/ first because a thread-messages URL also contains /messages/).
  • Lines 313-342 — "scrolls to bottom" test now pins a non-zero scrollHeight (400) on the scroll container and asserts scroller.scrollTop === 400, so it genuinely verifies the scroll assignment instead of only that the reply rendered.
  • Lines 585-610 — New test "ignores a second Enter while a send is still in-flight" covers the sending re-entrancy guard (component guard at ThreadPanel.tsx:89 plus disabled={sending} at line 162).
Files Reviewed (1 file)
  • desktop/src/apps/chat/__tests__/ThreadPanel.test.tsx - 0 new issues; 4 prior suggestions resolved
Previous Review Summary (commit 6be787e)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 6be787e)

Status: 4 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 4
Issue Details (click to expand)

SUGGESTION

File Line Issue
desktop/src/apps/chat/__tests__/ThreadPanel.test.tsx 296 "scrolls to bottom" test never asserts scroll position, only that the reply rendered — weak/misleading coverage
desktop/src/apps/chat/__tests__/ThreadPanel.test.tsx 43 Route-matching comments describe endsWith/last-segment logic that doesn't match the includes-based, order-dependent implementation
desktop/src/apps/chat/__tests__/ThreadPanel.test.tsx 29 beforeEach uses restoreAllMocks but not unstubAllGlobals, so a leaked global fetch stub from a thrown test isn't reset defensively
desktop/src/apps/chat/__tests__/ThreadPanel.test.tsx 487 Untested sending re-entrancy guard — no assertion that a second Enter mid-send is ignored
Files Reviewed (1 files)
  • desktop/src/apps/chat/__tests__/ThreadPanel.test.tsx - 4 issues

Fix these issues in Kilo Cloud


Reviewed by hy3:free · Input: 36.4K · Output: 2.6K · Cached: 166.9K

- Strengthen scroll test with actual scrollTop assertion against pinned scrollHeight
- Fix route-matching comments: code uses includes, not endsWith
- Add unstubAllGlobals to beforeEach for defensive global state reset
- Add re-entrancy test verifying second Enter is ignored while sending
@jaylfc
jaylfc merged commit 57630db into jaylfc:dev Jul 19, 2026
9 checks passed
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.

2 participants