From b192892a6165ff485c2fe990cd04021b9c8e9c01 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 13:25:33 +0000 Subject: [PATCH] test(plugin-chatbot): the pending-count pulse pin waits for the count render, not for the button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The draft card draws its 发布 button before the first fetchPendingDraftCount read answers; the count lands on a later render. Both tests read the count with a one-shot toHaveTextContent right after findByTestId, which resolves on the button alone, so under load the check could run before that render and read 发布 instead of 发布 (2) — the merge-queue ejections' signature. The pin now waits for the render that carries the count. Product code unchanged. Claude-Session: https://claude.ai/code/session_01J8VVAkq9LgnKiBeyJBZqSp Co-authored-by: Claude --- ...tbotEnhanced.pendingCountPulse-26471.test.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ui/packages/plugin-chatbot/src/__tests__/ChatbotEnhanced.pendingCountPulse-26471.test.tsx b/ui/packages/plugin-chatbot/src/__tests__/ChatbotEnhanced.pendingCountPulse-26471.test.tsx index f52ec419a1a..45aa97c3628 100644 --- a/ui/packages/plugin-chatbot/src/__tests__/ChatbotEnhanced.pendingCountPulse-26471.test.tsx +++ b/ui/packages/plugin-chatbot/src/__tests__/ChatbotEnhanced.pendingCountPulse-26471.test.tsx @@ -21,7 +21,7 @@ */ import '@testing-library/jest-dom/vitest'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { act, cleanup, render, screen } from '@testing-library/react'; +import { act, cleanup, render, screen, waitFor } from '@testing-library/react'; import { ChatbotEnhanced, type ChatbotEnhancedMessage } from '../ChatbotEnhanced'; import { detectDraftResult } from '../mapMessages'; @@ -81,6 +81,16 @@ function chat(pulse: ReturnType, fetchPendingDraftCount: (pkg: ); } +/** + * The button draws with the card, before the first count read answers; the + * count lands on a later render. `findByTestId` resolves on the button alone, + * so a one-shot text check after it races that render (objectstack#26647): + * wait for the render that carries the count. + */ +async function expectPublishButtonText(text: string) { + await waitFor(() => expect(screen.getByTestId('draft-publish')).toHaveTextContent(text)); +} + describe('objectstack#26471 — the draft card re-reads its pending count on the metadata pulse', () => { it('发布 (2), then a publish elsewhere fires the pulse and the card stops offering 发布', async () => { const pulse = makePulse(); @@ -88,7 +98,7 @@ describe('objectstack#26471 — the draft card re-reads its pending count on the const fetchPendingDraftCount = vi.fn(async (_pkg: string) => pending); const { rerender } = render(chat(pulse, fetchPendingDraftCount)); - expect(await screen.findByTestId('draft-publish')).toHaveTextContent('发布 (2)'); + await expectPublishButtonText('发布 (2)'); expect(fetchPendingDraftCount).toHaveBeenCalledTimes(1); expect(fetchPendingDraftCount).toHaveBeenCalledWith(PACKAGE_ID); @@ -113,7 +123,7 @@ describe('objectstack#26471 — the draft card re-reads its pending count on the const fetchPendingDraftCount = vi.fn(async (_pkg: string) => 2); const { unmount } = render(chat(pulse, fetchPendingDraftCount)); - expect(await screen.findByTestId('draft-publish')).toHaveTextContent('发布 (2)'); + await expectPublishButtonText('发布 (2)'); expect(pulse.listeners.size).toBe(1); unmount();