From 02630e206abe2c1e55016203c8690ca05cb12a4f Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Mon, 23 Mar 2026 15:40:55 -0400 Subject: [PATCH 1/3] refactor: test renderWithProviders for store initialization Signed-off-by: Adam Setch --- src/renderer/__helpers__/test-utils.tsx | 25 ++++++---- src/renderer/components/AllRead.test.tsx | 15 +++--- .../components/GlobalShortcuts.test.tsx | 50 +++++++++---------- src/renderer/components/Oops.test.tsx | 12 ++--- src/renderer/components/Sidebar.test.tsx | 43 ++++++++-------- .../avatars/AvatarWithFallback.test.tsx | 14 +++--- .../components/fields/Checkbox.test.tsx | 16 +++--- .../components/fields/FieldLabel.test.tsx | 4 +- .../components/fields/RadioGroup.test.tsx | 6 +-- .../components/fields/Tooltip.test.tsx | 8 +-- .../components/filters/FilterSection.test.tsx | 17 +++---- .../components/filters/ReasonFilter.test.tsx | 4 +- ...uiresDetailedNotificationsWarning.test.tsx | 4 +- .../components/filters/SearchFilter.test.tsx | 18 +++---- .../filters/SearchFilterSuggestions.test.tsx | 12 ++--- .../components/filters/StateFilter.test.tsx | 4 +- .../filters/SubjectTypeFilter.test.tsx | 4 +- .../filters/UserTypeFilter.test.tsx | 4 +- .../components/icons/LogoIcon.test.tsx | 14 +++--- .../components/icons/ScopeStatusIcon.test.tsx | 26 +++++----- .../components/layout/AppLayout.test.tsx | 4 +- .../components/layout/Centered.test.tsx | 6 +-- .../components/layout/Contents.test.tsx | 4 +- .../components/layout/EmojiSplash.test.tsx | 10 ++-- src/renderer/components/layout/Page.test.tsx | 4 +- .../components/metrics/CommentsPill.test.tsx | 8 +-- .../components/metrics/LabelsPill.test.tsx | 6 +-- .../metrics/LinkedIssuesPill.test.tsx | 8 +-- .../components/metrics/MetricGroup.test.tsx | 6 +-- .../components/metrics/MetricPill.test.tsx | 6 +-- .../components/metrics/MilestonePill.test.tsx | 8 +-- .../components/metrics/ReactionsPill.test.tsx | 8 +-- .../components/metrics/ReviewsPill.test.tsx | 6 +-- .../AccountNotifications.test.tsx | 28 +++++------ .../notifications/NotificationFooter.test.tsx | 12 ++--- .../notifications/NotificationHeader.test.tsx | 12 ++--- .../notifications/NotificationRow.test.tsx | 30 +++++------ .../notifications/NotificationTitle.test.tsx | 16 +++--- .../RepositoryNotifications.test.tsx | 18 +++---- .../primitives/CustomCounter.test.tsx | 4 +- .../components/primitives/EmojiText.test.tsx | 6 +-- .../components/primitives/Footer.test.tsx | 6 +-- .../components/primitives/Header.test.tsx | 8 +-- .../primitives/HoverButton.test.tsx | 6 +-- .../components/primitives/HoverGroup.test.tsx | 4 +- .../components/primitives/Title.test.tsx | 6 +-- .../settings/AppearanceSettings.test.tsx | 12 ++--- .../settings/NotificationSettings.test.tsx | 32 ++++++------ .../settings/SettingsFooter.test.tsx | 10 ++-- .../settings/SettingsReset.test.tsx | 6 +-- .../settings/SystemSettings.test.tsx | 22 ++++---- .../components/settings/TraySettings.test.tsx | 8 +-- src/renderer/context/App.test.tsx | 6 +-- src/renderer/routes/AccountScopes.test.tsx | 26 +++++----- src/renderer/routes/Accounts.test.tsx | 30 +++++------ src/renderer/routes/Filters.test.tsx | 8 +-- src/renderer/routes/Login.test.tsx | 12 ++--- .../routes/LoginWithDeviceFlow.test.tsx | 14 +++--- .../routes/LoginWithOAuthApp.test.tsx | 18 +++---- .../LoginWithPersonalAccessToken.test.tsx | 18 +++---- src/renderer/routes/Notifications.test.tsx | 18 +++---- src/renderer/routes/Settings.test.tsx | 6 +-- 62 files changed, 395 insertions(+), 391 deletions(-) diff --git a/src/renderer/__helpers__/test-utils.tsx b/src/renderer/__helpers__/test-utils.tsx index b631e63d0..e8c2dec60 100644 --- a/src/renderer/__helpers__/test-utils.tsx +++ b/src/renderer/__helpers__/test-utils.tsx @@ -7,21 +7,23 @@ import { BaseStyles, ThemeProvider } from '@primer/react'; import { mockAuth, mockSettings } from '../__mocks__/state-mocks'; import { AppContext, type AppContextState } from '../context/App'; +import { useFiltersStore, type FiltersStore } from '../stores'; export { navigateMock } from './vitest.setup'; export type DeepPartial = { [K in keyof T]?: DeepPartial }; const EMPTY_APP_CONTEXT: TestAppContext = {}; -interface RenderOptions extends Partial { - initialEntries?: string[]; -} - /** * Test context */ type TestAppContext = Partial; +interface RenderOptions extends TestAppContext { + initialEntries?: string[]; + filters?: Partial; +} + /** * Props for the AppContextProvider wrapper */ @@ -83,7 +85,7 @@ function AppContextProvider({ - + {children} @@ -93,15 +95,20 @@ function AppContextProvider({ } /** - * Custom render that wraps components with AppContextProvider by default. + * Custom render that wraps components with all providers needed for testing: + * MemoryRouter, AppContext, and Zustand stores. * * Usage: - * renderWithAppContext(, { auth, settings, ... }) + * renderWithProviders(, { notifications, accounts, settings, filters, ... }) */ -export function renderWithAppContext( +export function renderWithProviders( ui: ReactElement, - { initialEntries, ...context }: RenderOptions = {}, + { initialEntries, filters, ...context }: RenderOptions = {}, ) { + if (filters) { + useFiltersStore.setState(filters); + } + return render(ui, { wrapper: ({ children }) => ( diff --git a/src/renderer/components/AllRead.test.tsx b/src/renderer/components/AllRead.test.tsx index 1f474a42e..b0d58ac17 100644 --- a/src/renderer/components/AllRead.test.tsx +++ b/src/renderer/components/AllRead.test.tsx @@ -1,6 +1,6 @@ import { act } from '@testing-library/react'; -import { renderWithAppContext } from '../__helpers__/test-utils'; +import { renderWithProviders } from '../__helpers__/test-utils'; import { mockSettings } from '../__mocks__/state-mocks'; import { useFiltersStore } from '../stores'; @@ -8,10 +8,10 @@ import { AllRead } from './AllRead'; describe('renderer/components/AllRead.tsx', () => { it('should render itself & its children - no filters', async () => { - let tree: ReturnType | null = null; + let tree: ReturnType | null = null; await act(async () => { - tree = renderWithAppContext(, { + tree = renderWithProviders(, { settings: { ...mockSettings, }, @@ -22,15 +22,16 @@ describe('renderer/components/AllRead.tsx', () => { }); it('should render itself & its children - with filters', async () => { - useFiltersStore.setState({ reasons: ['author'] }); - - let tree: ReturnType | null = null; + let tree: ReturnType | null = null; await act(async () => { - tree = renderWithAppContext(, { + tree = renderWithProviders(, { settings: { ...mockSettings, }, + filters: { + reasons: ['author'], + }, }); }); diff --git a/src/renderer/components/GlobalShortcuts.test.tsx b/src/renderer/components/GlobalShortcuts.test.tsx index 360aec5fe..921111603 100644 --- a/src/renderer/components/GlobalShortcuts.test.tsx +++ b/src/renderer/components/GlobalShortcuts.test.tsx @@ -1,6 +1,6 @@ import userEvent from '@testing-library/user-event'; -import { navigateMock, renderWithAppContext } from '../__helpers__/test-utils'; +import { navigateMock, renderWithProviders } from '../__helpers__/test-utils'; import * as comms from '../utils/system/comms'; import * as links from '../utils/system/links'; @@ -14,7 +14,7 @@ describe('components/GlobalShortcuts.tsx', () => { describe('key bindings', () => { describe('ignores keys that are not valid', () => { it('ignores B key', async () => { - renderWithAppContext(); + renderWithProviders(); await userEvent.keyboard('b'); @@ -24,7 +24,7 @@ describe('components/GlobalShortcuts.tsx', () => { describe('home', () => { it('navigates home when pressing H key', async () => { - renderWithAppContext(); + renderWithProviders(); await userEvent.keyboard('h'); @@ -38,7 +38,7 @@ describe('components/GlobalShortcuts.tsx', () => { .mockImplementation(vi.fn()); it('opens primary account GitHub notifications webpage when pressing N while logged in', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: true, }); @@ -48,7 +48,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('does not open primary account GitHub notifications webpage when logged out', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: false, }); @@ -60,7 +60,7 @@ describe('components/GlobalShortcuts.tsx', () => { describe('focus mode', () => { it('toggles focus when pressing W while logged in', async () => { - renderWithAppContext(, { + renderWithProviders(, { updateSetting: updateSettingMock, isLoggedIn: true, }); @@ -71,7 +71,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('does not toggle focus mode when loading', async () => { - renderWithAppContext(, { + renderWithProviders(, { updateSetting: updateSettingMock, status: 'loading', isLoggedIn: true, @@ -83,7 +83,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('does not toggle focus mode when logged out', async () => { - renderWithAppContext(, { + renderWithProviders(, { updateSetting: updateSettingMock, isLoggedIn: false, }); @@ -96,7 +96,7 @@ describe('components/GlobalShortcuts.tsx', () => { describe('filters', () => { it('toggles filters when pressing F while logged in', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: true, }); @@ -106,7 +106,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('does not toggle filters when logged out', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: false, }); @@ -122,7 +122,7 @@ describe('components/GlobalShortcuts.tsx', () => { .mockImplementation(vi.fn()); it('opens primary account GitHub issues webpage when pressing I while logged in', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: true, }); @@ -132,7 +132,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('does not open primary account GitHub issues webpage when logged out', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: false, }); @@ -148,7 +148,7 @@ describe('components/GlobalShortcuts.tsx', () => { .mockImplementation(vi.fn()); it('opens primary account GitHub pull requests webpage when pressing N while logged in', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: true, }); @@ -158,7 +158,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('does not open primary account GitHub pull requests webpage when logged out', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: false, }); @@ -170,7 +170,7 @@ describe('components/GlobalShortcuts.tsx', () => { describe('refresh', () => { it('refreshes notifications when pressing R key', async () => { - renderWithAppContext(, { + renderWithProviders(, { fetchNotifications: fetchNotificationsMock, }); @@ -181,7 +181,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('does not refresh when status is loading', async () => { - renderWithAppContext(, { + renderWithProviders(, { status: 'loading', }); @@ -193,7 +193,7 @@ describe('components/GlobalShortcuts.tsx', () => { describe('settings', () => { it('toggles settings when pressing S while logged in', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: true, }); @@ -203,7 +203,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('does not toggle settings when logged out', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: false, }); @@ -215,7 +215,7 @@ describe('components/GlobalShortcuts.tsx', () => { describe('accounts', () => { it('navigates to accounts when pressing A on settings route', async () => { - renderWithAppContext(, { + renderWithProviders(, { initialEntries: ['/settings'], isLoggedIn: true, }); @@ -226,7 +226,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('does not trigger accounts when not on settings route', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: true, }); @@ -238,7 +238,7 @@ describe('components/GlobalShortcuts.tsx', () => { describe('quit app', () => { it('quits the app when pressing Q on settings route', async () => { - renderWithAppContext(, { + renderWithProviders(, { initialEntries: ['/settings'], isLoggedIn: true, }); @@ -249,7 +249,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('does not quit the app when not on settings route', async () => { - renderWithAppContext(, { + renderWithProviders(, { isLoggedIn: true, }); @@ -261,7 +261,7 @@ describe('components/GlobalShortcuts.tsx', () => { describe('modifiers', () => { it('ignores shortcuts when typing in an input', async () => { - renderWithAppContext( + renderWithProviders( <> @@ -281,7 +281,7 @@ describe('components/GlobalShortcuts.tsx', () => { }); it('ignores shortcuts when typing in a textarea', async () => { - renderWithAppContext( + renderWithProviders( <>