Skip to content

Fix flaky UnreadIndicatorsTest - mark as unread last message deleted #92122

@neil-marcellini

Description

@neil-marcellini

(Neil's AI agent)

Problem

The test Unread Indicators › Mark the chat as unread on clicking "Mark as unread" on an item in LHN when the last message of the chat was deleted by another user (tests/ui/UnreadIndicatorsTest.tsx:831) is flaky. It regularly passes but intermittently fails on main.

Failure output:

expect(received).toHaveLength(expected)
Expected length: 1
Received length: 0
Received array:  []
  at Object.toHaveLength (tests/ui/UnreadIndicatorsTest.tsx:831:34)

The LHN chat row's display name has not rendered yet, so screen.queryAllByLabelText('accessibilityHints.chatUserDisplayNames') returns [].

Evidence it is flaky (fails on unrelated PRs)

The same test, at the same line, failed on two unrelated main merges within ~3 hours:

Neither PR touches UnreadIndicatorsTest.tsx or the LHN rendering logic, and the test file itself has not changed since 2026-05-13.

Surfaced from the workflow-failure issue #92118.

Likely root cause (hypothesis)

The assertion reads the rendered LHN synchronously after a single await waitForBatchedUpdates():

markCommentAsUnread(REPORT_ID, reportActions, {reportActionID: -1} as unknown as ReportAction, USER_A_ACCOUNT_ID);

await waitForBatchedUpdates();
const hintText = TestHelper.translateLocal('accessibilityHints.chatUserDisplayNames');
const displayNameTexts = screen.queryAllByLabelText(hintText);
expect(displayNameTexts).toHaveLength(1);

The preceding steps fire several async Onyx.merge calls (two report actions, lastVisibleActionCreated set twice, last message html cleared) plus markCommentAsUnread, which triggers LHN recomputation and re-sort. A single waitForBatchedUpdates() does not guarantee the LHN row has re-mounted, so the synchronous query can run before the row renders. Other UI assertions in this same file already wrap reads in waitFor(...) (e.g. lines 285, 453, 487) to avoid this exact race.

Solution

Determine the root cause and fix it. A likely fix is to wrap the assertion in waitFor so it retries until the row renders:

const hintText = TestHelper.translateLocal('accessibilityHints.chatUserDisplayNames');
await waitFor(() => {
    const displayNameTexts = screen.queryAllByLabelText(hintText);
    expect(displayNameTexts).toHaveLength(1);
    expect((displayNameTexts.at(0)?.props?.style as TextStyle)?.fontWeight).toBe(FontUtils.fontWeight.bold);
});

Validate stability by running the test in a loop locally (e.g. npm test -- UnreadIndicatorsTest -i, ~10–20x).

Context

This test was introduced by @c3024 in #61374.

Issue OwnerCurrent Issue Owner: @c3024

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions