From 42c12ea26d6766ca7845df4ca1d8bf38c418abfe Mon Sep 17 00:00:00 2001 From: Maurice Date: Sat, 11 Apr 2026 19:25:30 +0200 Subject: [PATCH] fix: update Dashboard tests for dual mobile+desktop rendering in jsdom - Use getAllBy* instead of getBy* where mobile + desktop render same content - Settings button finder uses .lucide-settings selector --- client/src/pages/DashboardPage.test.tsx | 50 +++++++++++-------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/client/src/pages/DashboardPage.test.tsx b/client/src/pages/DashboardPage.test.tsx index 66936ead..20448828 100644 --- a/client/src/pages/DashboardPage.test.tsx +++ b/client/src/pages/DashboardPage.test.tsx @@ -86,10 +86,10 @@ describe('DashboardPage', () => { render(); await waitFor(() => { - expect(screen.getByRole('button', { name: /new trip/i })).toBeInTheDocument(); + expect(screen.getAllByRole('button', { name: /new trip/i }).length).toBeGreaterThan(0); }); - await user.click(screen.getByRole('button', { name: /new trip/i })); + await user.click(screen.getAllByRole('button', { name: /new trip/i })[0]); // TripFormModal opens — "Create New Trip" appears in heading and submit button await waitFor(() => { @@ -322,17 +322,13 @@ describe('DashboardPage', () => { render(); await waitFor(() => { - expect(screen.getByText('Tokyo Trip')).toBeInTheDocument(); + // Mobile + desktop may render the same trip twice + expect(screen.getAllByText('Tokyo Trip').length).toBeGreaterThan(0); }); - // Click the trip title text (not an action button) on a non-spotlight card - // Tokyo Trip appears as a TripCard (not SpotlightCard since Paris Adventure is spotlight) - // Find the card by its title text — clicking it triggers navigate - const tokyoTrip = screen.getByText('Tokyo Trip'); + const tokyoTrip = screen.getAllByText('Tokyo Trip')[0]; await user.click(tokyoTrip); - // After click, MemoryRouter won't actually navigate but we verify no errors occur - // and the click was processed (the card was clickable) expect(tokyoTrip).toBeInTheDocument(); }); }); @@ -343,7 +339,7 @@ describe('DashboardPage', () => { render(); await waitFor(() => { - expect(screen.getByText('Paris Adventure')).toBeInTheDocument(); + expect(screen.getAllByText('Paris Adventure').length).toBeGreaterThan(0); }); // Switch to list view @@ -352,12 +348,12 @@ describe('DashboardPage', () => { // Both trips should still be visible in list view await waitFor(() => { - expect(screen.getByText('Paris Adventure')).toBeInTheDocument(); - expect(screen.getByText('Tokyo Trip')).toBeInTheDocument(); + expect(screen.getAllByText('Paris Adventure').length).toBeGreaterThan(0); + expect(screen.getAllByText('Tokyo Trip').length).toBeGreaterThan(0); }); // In list view, clicking Tokyo Trip card should work - const tokyoTrip = screen.getByText('Tokyo Trip'); + const tokyoTrip = screen.getAllByText('Tokyo Trip')[0]; await user.click(tokyoTrip); expect(tokyoTrip).toBeInTheDocument(); }); @@ -369,7 +365,7 @@ describe('DashboardPage', () => { render(); await waitFor(() => { - expect(screen.getByText('Paris Adventure')).toBeInTheDocument(); + expect(screen.getAllByText('Paris Adventure').length).toBeGreaterThan(0); }); // Switch to list view @@ -378,16 +374,11 @@ describe('DashboardPage', () => { // Both trips render in list view await waitFor(() => { - expect(screen.getByText('Paris Adventure')).toBeInTheDocument(); - expect(screen.getByText('Tokyo Trip')).toBeInTheDocument(); + expect(screen.getAllByText('Paris Adventure').length).toBeGreaterThan(0); + expect(screen.getAllByText('Tokyo Trip').length).toBeGreaterThan(0); }); - // In list view, CardAction buttons have no label/title — find by icon content - // The delete buttons are CardAction with danger style; there are multiple action groups - // Each trip row has: Edit, Copy, Archive, Delete buttons (4 per row) const allButtons = screen.getAllByRole('button'); - // Find delete buttons — they are the 4th in each group, but simpler: - // Just verify there are multiple action buttons rendered in list view expect(allButtons.length).toBeGreaterThan(4); }); }); @@ -425,20 +416,23 @@ describe('DashboardPage', () => { render(); await waitFor(() => { - expect(screen.getByText('Paris Adventure')).toBeInTheDocument(); + expect(screen.getAllByText('Paris Adventure').length).toBeGreaterThan(0); }); - // Header has 3 buttons: view-toggle (has title), settings gear (no title, no text), New Trip (has text) - // Find settings button: no title attr, and text content doesn't include 'New Trip' + // Find settings button — it's the gear icon button without title or text const allBtns = screen.getAllByRole('button'); const settingsButton = allBtns.find( - btn => !btn.getAttribute('title') && !btn.textContent?.trim() + btn => { + const title = btn.getAttribute('title'); + const text = btn.textContent?.trim() || ''; + // Settings gear: no title, no meaningful text, not the notification bell + return !title && !text && btn.querySelector('.lucide-settings'); + } ); expect(settingsButton).toBeDefined(); if (settingsButton) { await user.click(settingsButton); - // Widget settings panel shows "Widgets:" label await waitFor(() => { expect(screen.getByText('Widgets:')).toBeInTheDocument(); }); @@ -507,10 +501,10 @@ describe('DashboardPage', () => { render(); await waitFor(() => { - expect(screen.getByRole('button', { name: /new trip/i })).toBeInTheDocument(); + expect(screen.getAllByRole('button', { name: /new trip/i }).length).toBeGreaterThan(0); }); - await user.click(screen.getByRole('button', { name: /new trip/i })); + await user.click(screen.getAllByRole('button', { name: /new trip/i })[0]); await waitFor(() => { expect(screen.getAllByText(/create new trip/i).length).toBeGreaterThan(0);