Skip to content

test(desktop): add MessageList component tests (Vitest + RTL)#2052

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

test(desktop): add MessageList component tests (Vitest + RTL)#2052
jaylfc merged 2 commits into
jaylfc:devfrom
hognek:feat/messagelist-tests

Conversation

@hognek

@hognek hognek commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Task: t_b86ff267. Closes #657 (C1).

Adds comprehensive Vitest + React Testing Library tests for the extracted MessageList component (from PR #1877).

Coverage:

  • Empty state / no-channel placeholder
  • Message rendering with author display and consecutive-message grouping
  • Day separators and new-message divider
  • Deleted message tombstones
  • Editing state (MessageEditor)
  • Pending/streaming/error message states
  • Edited indicator
  • Reactions (ReactionBar)
  • Thread indicators
  • Dead agent badges (inactive/removed)
  • Scroll-to-bottom button
  • Channel header variants (topic/group/dm)
  • Canvas attachment buttons
  • Pin request affordance
  • Typing footer
  • Pinned messages popover
  • Mobile keyboard inset padding
  • Imperative ref handle (scrollToBottom)

49 tests, all passing. npm run build succeeds. tsc -b clean.

Add comprehensive test suite for the extracted MessageList component
(PR jaylfc#1877, upstream jaylfc#657). Covers:

- Empty state / no-channel placeholder
- Message rendering with author display
- Day separators and new-message divider
- Deleted message tombstones
- Editing state (MessageEditor)
- Pending/streaming/error message states
- Edited indicator
- Reactions (ReactionBar)
- Thread indicators
- Dead agent badges (inactive/removed)
- Scroll-to-bottom button
- Channel header variants (topic/group/dm)
- Canvas attachment buttons
- Pin request affordance
- Typing footer
- Pinned messages popover
- Mobile keyboard inset
- Imperative ref handle (scrollToBottom)

49 tests, all passing.
@hognek
hognek marked this pull request as ready for review July 19, 2026 15:50
@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: 5 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: 061f9ccd-44f5-475f-8f79-8fd5613d7c0e

📥 Commits

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

📒 Files selected for processing (1)
  • desktop/src/apps/chat/__tests__/MessageList.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

});
// No reaction elements
const reactionButtons = document.querySelectorAll("[data-reaction]");
expect(reactionButtons.length).toBe(0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Vacuous assertion — this test can never fail.

ReactionBar (ReactionBar.tsx) does not attach a data-reaction attribute to its buttons, so document.querySelectorAll("[data-reaction]") is always empty. With reactions: {} the ReactionBar is correctly skipped by the component (it guards on Object.keys(msg.reactions).length > 0), but this assertion would also return 0 if ReactionBar were rendered unconditionally. It does not actually verify that no reactions are rendered. Assert on the absence of the emoji text or the ReactionBar's rendered structure instead (e.g. expect(screen.queryByText("👍")).toBeNull()).

/>,
);
// TypingFooter returns null when empty, so no typing text
expect(container.querySelector("[data-typing]")).toBeNull();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Vacuous assertion — this test can never fail.

TypingFooter (TypingFooter.tsx) returns null when no one is typing, but it never renders an element with a data-typing attribute regardless. container.querySelector("[data-typing]") is therefore always null, so this test passes even if TypingFooter rendered broken/placeholder markup. It does not validate empty-state behavior. Assert on the documented contract instead — e.g. that the typing text is absent: expect(screen.queryByText(/typing/i)).toBeNull() (noting the component filters selfId).

messages: [msg()],
});
// dayLabel returns "Today", "Yesterday", or a date string like "Tue, Nov 14"
expect(screen.getByText(/Today|Yesterday|[A-Z][a-z]{2}, [A-Z][a-z]{2} \d{1,2}/)).toBeInTheDocument();

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: Locale-dependent day-label assertion.

dayLabel (MessagesApp.tsx:315) formats past dates via toLocaleDateString(undefined, { weekday: "short", day: "numeric", month: "short" }), whose output depends on the test runner's locale (e.g. en-US yields "Tue, Nov 14", but other locales yield different orders/separators). This regex assumes the en-US Weekday, Mon DD shape and will fail on CI runners configured with a non-English locale. Pin the locale (e.g. set Intl/process.env.LANG in the test setup) or assert on a stable, locale-independent signal.

],
});
// Should have two day labels
const separators = screen.getAllByText(/Today|Yesterday|[A-Z][a-z]{2}, [A-Z][a-z]{2} \d{1,2}/);

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: Same locale-dependence as the day-label test above. The toLocaleDateString output in dayLabel is locale-specific, so the length === 2 result is correct only under an en-US locale. Pin the locale in test setup or rely on a locale-independent check so this stays green on any CI image.

renderWithMsg({
channel: channel({ type: "group", members: ["user", "alice", "bob"] }),
});
expect(screen.getByText("3")).toBeInTheDocument();

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: Brittle text matcher.

screen.getByText("3") for the member count matches any element whose text is exactly "3". It currently passes, but it will break (or worse, produce a confusing multiple-match error) if any other text in the header ever renders "3" (e.g. a future count badge). Prefer scoping the query, e.g. screen.getByText("3", { selector: "div" }) or asserting within the members container.

});
// ReactionBar should show the emoji and count
expect(screen.getByText("👍")).toBeInTheDocument();
expect(screen.getByText("1")).toBeInTheDocument();

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: Ambiguous / brittle count matcher.

screen.getByText("1") targets the reaction count, but "1" is a very common text fragment and this will throw a multiple-element error if any other "1" appears in the rendered tree (e.g. a future thread-reply count, new-message count, or relative-time value). Scope it or use a more specific query, e.g. assert the ReactionBar button's structure directly.

@kilo-code-bot

kilo-code-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Resolution Notes (incremental review)

All 6 findings from the previous review (commit 299d1c1) were addressed by the incremental diff in PR #2052:

  • WARNING vacuous reaction assertion (line 375) → now uses screen.queryAllByRole("button", { name: /👍|❤️/ }) and asserts length === 0.
  • WARNING vacuous typing assertion (line 573) → now checks [aria-live="polite"] is null.
  • SUGGESTION locale-dependent day-label (line 228) → now asserts on the select-none .text-white/40 separator element, locale-independent.
  • SUGGESTION same locale-dependence (line 241) → same CSS-class-based approach; asserts 2 separators.
  • SUGGESTION brittle getByText("3") (line 485) → now scoped to the .text-white/30.flex.items-center member container.
  • SUGGESTION ambiguous getByText("1") (line 362) → now uses getByRole("button", { name: /👍/ }) and checks textContent contains 1.

No new issues were introduced by the incremental changes.

Files Reviewed (1 files)
  • desktop/src/apps/chat/__tests__/MessageList.test.tsx - 0 new issues (6 previous findings resolved)
Previous Review Summary (commit 299d1c1)

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

Previous review (commit 299d1c1)

Status: 6 Issues Found | Recommendation: Address before merge

Overview

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

WARNING

File Line Issue
desktop/src/apps/chat/__tests__/MessageList.test.tsx 371 Vacuous test: querySelectorAll("[data-reaction]") always returns 0 because ReactionBar has no such attribute — test can never fail and does not verify empty reactions.
desktop/src/apps/chat/__tests__/MessageList.test.tsx 573 Vacuous test: querySelector("[data-typing]") always returns null because TypingFooter never renders that attribute — test can never fail and does not verify the empty state.

SUGGESTION

File Line Issue
desktop/src/apps/chat/__tests__/MessageList.test.tsx 228 Locale-dependent day-label assertion (toLocaleDateString(undefined, ...)) will fail on non-en-US CI runners.
desktop/src/apps/chat/__tests__/MessageList.test.tsx 241 Same locale-dependence for the two-separators day-change test.
desktop/src/apps/chat/__tests__/MessageList.test.tsx 485 Brittle getByText("3") for member count — any other "3" text breaks it.
desktop/src/apps/chat/__tests__/MessageList.test.tsx 362 Ambiguous getByText("1") for reaction count — fragile to other "1" text in the tree.
Files Reviewed (1 files)
  • desktop/src/apps/chat/__tests__/MessageList.test.tsx - 6 issues

Fix these issues in Kilo Cloud


Reviewed by hy3:free · Input: 37.2K · Output: 2.6K · Cached: 133.6K

- Replace vacuous data-reaction/data-typing selectors with real DOM checks
- Make day-label assertions locale-independent (query selectors)
- Scope reaction/member-count text matchers to avoid ambiguity
@jaylfc
jaylfc merged commit 452f9a3 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