Skip to content

fix(cli): flaky AppContainer sticky todo remeasure test #4415

@pomelo-nwu

Description

@pomelo-nwu

Summary

AppContainer.test.tsx — "does not remeasure footer height for sticky todo status-only updates" is flaky on CI (macOS runner). It fails intermittently with:

AssertionError: expected "spy" to be called 1 times, but got 2 times
  ❯ src/ui/AppContainer.test.tsx:2373

CI failure: https://github.com/QwenLM/qwen-code/actions/runs/26231823721/job/77194055495

Root cause analysis

The test asserts that measureElement is not called again after rerender when only the todo status changes (pendingin_progress). It captures the absolute call count after initial render, rerenders, then asserts the count is unchanged.

The useLayoutEffect in AppContainer.tsx:2305-2324 has these deps:

[buffer, terminalWidth, terminalHeight, btwItem, dialogsVisible, stickyTodosLayoutKey]

The test correctly ensures stickyTodosLayoutKey is stable (status is excluded by getStickyTodosLayoutKey). However, the assertion relies on the absolute mock.calls.length count, which is fragile under React 19's Ink test renderer — where the number of useLayoutEffect invocations during initial mount can vary between environments (e.g. StrictMode double-invoke, multiple reconciliation passes).

The companion unhandled rejection (Config was already initialized) is a separate StrictMode artifact from config.initialize() being called twice, but is not the direct cause of the assertion failure.

Proposed fix

Replace the absolute call-count assertion with mockClear() + not.toHaveBeenCalled(), which:

  • Clearly expresses the intent ("no new calls after rerender")
  • Is independent of how many times measureElement was called during mount
  • Eliminates the flaky behavior regardless of React reconciler pass count

Follow-up PR

A fix PR will follow shortly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    category/cliCommand line interface and interactionscope/testingTest frameworks and casestype/bugSomething isn't working as expected

    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