From 638a55635ca7df6d87ad4f70393bdf447b953658 Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Sun, 6 Sep 2026 18:54:14 +0200 Subject: [PATCH] test(client): pin both web-search rows to the shared glyph rail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rebase onto dev silently dropped `ROW_GLYPH_SLOT` from the completed row — its glyph had since moved inside a shared `Button` — and left the streaming row on a 10px gap, so nothing caught either. `Part.tsx` routes web search past `ProgressText`, so this renderer owns its glyph and has to take the slot itself; the test fails on both regressions. --- .../Content/__tests__/WebSearch.test.tsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/client/src/components/Chat/Messages/Content/__tests__/WebSearch.test.tsx b/client/src/components/Chat/Messages/Content/__tests__/WebSearch.test.tsx index f83b765430..33b2b9ebc8 100644 --- a/client/src/components/Chat/Messages/Content/__tests__/WebSearch.test.tsx +++ b/client/src/components/Chat/Messages/Content/__tests__/WebSearch.test.tsx @@ -4,6 +4,7 @@ import { Tools } from 'librechat-data-provider'; import { fireEvent, render, screen } from '@testing-library/react'; import type { TAttachment, SearchResultData, ValidSource } from 'librechat-data-provider'; import { SearchContext } from '~/Providers'; +import { ROW_GLYPH_SLOT } from '../rows'; import WebSearch from '../WebSearch'; jest.mock('~/hooks', () => ({ @@ -452,6 +453,35 @@ describe('WebSearch', () => { expect(chevron.className).not.toContain('transition-opacity'); }); + it('puts both rows glyph on the shared row rail', () => { + /** `Part.tsx` routes web search past `ProgressText`, so this renderer + * draws its own glyph and has to take the shared slot itself or its + * favicon stack sits left of every other row under the header. */ + const searchResults = makeSearchResults({ + 0: { organic: [makeSource('https://example.com', 'Example')] }, + }); + + const { unmount } = renderWebSearch({ searchResults }); + + const completedRow = screen.getByRole('button', { name: /Searched the web/ }); + const completedSlot = completedRow.firstElementChild; + expect(completedSlot).toContainElement(screen.getByTestId('favicon')); + for (const token of ROW_GLYPH_SLOT.split(' ')) { + expect(completedSlot).toHaveClass(token); + } + unmount(); + + renderWebSearch({ isSubmitting: true, isLast: true, initialProgress: 0.5 }); + + const streamingSlot = screen.getByTestId('globe-icon').parentElement; + for (const token of ROW_GLYPH_SLOT.split(' ')) { + expect(streamingSlot).toHaveClass(token); + } + /** 24px slot plus an 8px gap puts the label where the header's name + * starts; 10px would leave the streaming row 2px off the rail. */ + expect(streamingSlot?.parentElement).toHaveClass('gap-2'); + }); + it('renders searching state during streaming', () => { renderWebSearch({ isSubmitting: true,