diff --git a/jest.config.ts b/jest.config.ts index 89f63907d..1b61fec23 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -2,7 +2,7 @@ import type { Config } from 'jest'; const config: Config = { preset: 'ts-jest', - setupFiles: ['/src/renderer/__helpers__/jest.setup.ts'], + setupFilesAfterEnv: ['/src/renderer/__helpers__/jest.setup.ts'], testEnvironment: 'jsdom', collectCoverage: true, collectCoverageFrom: ['src/**/*', '!**/__snapshots__/**'], diff --git a/package.json b/package.json index 7f3a93747..18842c3f9 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,9 @@ "@primer/primitives": "10.6.0", "@primer/react": "36.27.0", "@tailwindcss/postcss": "4.1.7", + "@testing-library/jest-dom": "6.6.3", "@testing-library/react": "16.3.0", + "@testing-library/user-event": "14.6.1", "@types/jest": "29.5.14", "@types/node": "22.15.18", "@types/react": "19.1.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68cdec7cc..237447e84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,9 +54,15 @@ importers: '@tailwindcss/postcss': specifier: 4.1.7 version: 4.1.7 + '@testing-library/jest-dom': + specifier: 6.6.3 + version: 6.6.3 '@testing-library/react': specifier: 16.3.0 version: 16.3.0(@testing-library/dom@10.0.0)(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@testing-library/user-event': + specifier: 14.6.1 + version: 14.6.1(@testing-library/dom@10.0.0) '@types/jest': specifier: 29.5.14 version: 29.5.14 @@ -177,6 +183,9 @@ packages: 7zip-bin@5.2.0: resolution: {integrity: sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==} + '@adobe/css-tools@4.4.2': + resolution: {integrity: sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -833,6 +842,10 @@ packages: resolution: {integrity: sha512-PmJPnogldqoVFf+EwbHvbBJ98MmqASV8kLrBYgsDNxQcFMeIS7JFL48sfyXvuMtgmWO/wMhh25odr+8VhDmn4g==} engines: {node: '>=18'} + '@testing-library/jest-dom@6.6.3': + resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@testing-library/react@16.3.0': resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==} engines: {node: '>=18'} @@ -848,6 +861,12 @@ packages: '@types/react-dom': optional: true + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + '@tootallnate/once@2.0.0': resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} @@ -1407,6 +1426,10 @@ packages: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1687,6 +1710,9 @@ packages: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -1845,6 +1871,9 @@ packages: dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dom-converter@0.2.0: resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} @@ -3064,6 +3093,10 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + mini-css-extract-plugin@2.9.2: resolution: {integrity: sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==} engines: {node: '>= 12.13.0'} @@ -3695,6 +3728,10 @@ packages: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} engines: {node: '>= 10.13.0'} + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -3965,6 +4002,10 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -4431,6 +4472,8 @@ snapshots: 7zip-bin@5.2.0: {} + '@adobe/css-tools@4.4.2': {} + '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': @@ -5291,6 +5334,16 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 + '@testing-library/jest-dom@6.6.3': + dependencies: + '@adobe/css-tools': 4.4.2 + aria-query: 5.3.0 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + redent: 3.0.0 + '@testing-library/react@16.3.0(@testing-library/dom@10.0.0)(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.24.1 @@ -5301,6 +5354,10 @@ snapshots: '@types/react': 19.1.4 '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@testing-library/user-event@14.6.1(@testing-library/dom@10.0.0)': + dependencies: + '@testing-library/dom': 10.0.0 + '@tootallnate/once@2.0.0': {} '@trysound/sax@0.2.0': {} @@ -6065,6 +6122,11 @@ snapshots: escape-string-regexp: 1.0.5 supports-color: 5.5.0 + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -6324,6 +6386,8 @@ snapshots: css-what@6.1.0: {} + css.escape@1.5.1: {} + cssesc@3.0.0: {} cssnano-preset-default@7.0.6(postcss@8.5.3): @@ -6494,6 +6558,8 @@ snapshots: dom-accessibility-api@0.5.16: {} + dom-accessibility-api@0.6.3: {} + dom-converter@0.2.0: dependencies: utila: 0.4.0 @@ -8028,6 +8094,8 @@ snapshots: mimic-response@3.1.0: {} + min-indent@1.0.1: {} + mini-css-extract-plugin@2.9.2(webpack@5.99.8): dependencies: schema-utils: 4.2.0 @@ -8656,6 +8724,11 @@ snapshots: dependencies: resolve: 1.22.8 + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + regenerator-runtime@0.14.1: {} relateurl@0.2.7: {} @@ -8925,6 +8998,10 @@ snapshots: strip-final-newline@2.0.0: {} + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + strip-json-comments@3.1.1: {} style-to-object@0.4.4: diff --git a/src/renderer/__helpers__/jest.setup.ts b/src/renderer/__helpers__/jest.setup.ts index 658eb3613..120075f58 100644 --- a/src/renderer/__helpers__/jest.setup.ts +++ b/src/renderer/__helpers__/jest.setup.ts @@ -1,3 +1,5 @@ +import '@testing-library/jest-dom'; + import { TextDecoder, TextEncoder } from 'node:util'; if (!global.TextEncoder || !global.TextDecoder) { diff --git a/src/renderer/components/fields/RadioGroup.tsx b/src/renderer/components/fields/RadioGroup.tsx index d395edeb4..0a92cf1e6 100644 --- a/src/renderer/components/fields/RadioGroup.tsx +++ b/src/renderer/components/fields/RadioGroup.tsx @@ -24,7 +24,7 @@ export const RadioGroup: FC = (props: IRadioGroup) => { {props.options.map((item) => { - const name = `radio-${props.name}-${item.value}`.toLowerCase(); + const name = `radio-${props.name}-${item.value.toLowerCase()}`; return ( { ); }); - fireEvent.click(screen.getByTestId('radio-groupby-date')); + fireEvent.click(screen.getByTestId('radio-groupBy-date')); expect(updateSetting).toHaveBeenCalledTimes(1); expect(updateSetting).toHaveBeenCalledWith('groupBy', 'DATE'); diff --git a/src/renderer/components/settings/SystemSettings.test.tsx b/src/renderer/components/settings/SystemSettings.test.tsx index 6cc78d44d..e4d669b6d 100644 --- a/src/renderer/components/settings/SystemSettings.test.tsx +++ b/src/renderer/components/settings/SystemSettings.test.tsx @@ -1,5 +1,7 @@ -import { act, fireEvent, render, screen } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { MemoryRouter } from 'react-router-dom'; + import { mockAuth, mockSettings } from '../../__mocks__/state-mocks'; import { AppContext } from '../../context/App'; import { SystemSettings } from './SystemSettings'; @@ -28,7 +30,7 @@ describe('renderer/components/settings/SystemSettings.tsx', () => { ); }); - fireEvent.click(screen.getByTestId('radio-openlinks-background')); + await userEvent.click(screen.getByTestId('radio-openLinks-background')); expect(updateSetting).toHaveBeenCalledTimes(1); expect(updateSetting).toHaveBeenCalledWith('openLinks', 'BACKGROUND'); @@ -51,9 +53,7 @@ describe('renderer/components/settings/SystemSettings.tsx', () => { ); }); - fireEvent.click(screen.getByTestId('checkbox-keyboardShortcut'), { - target: { checked: true }, - }); + await userEvent.click(screen.getByTestId('checkbox-keyboardShortcut')); expect(updateSetting).toHaveBeenCalledTimes(1); expect(updateSetting).toHaveBeenCalledWith('keyboardShortcut', false); @@ -76,11 +76,8 @@ describe('renderer/components/settings/SystemSettings.tsx', () => { ); }); - fireEvent.click( + await userEvent.click( screen.getByTestId('checkbox-showNotificationsCountInTray'), - { - target: { checked: true }, - }, ); expect(updateSetting).toHaveBeenCalledTimes(1); @@ -107,16 +104,52 @@ describe('renderer/components/settings/SystemSettings.tsx', () => { ); }); - fireEvent.click(screen.getByTestId('checkbox-showNotifications'), { - target: { checked: true }, - }); + await userEvent.click(screen.getByTestId('checkbox-showNotifications')); expect(updateSetting).toHaveBeenCalledTimes(1); expect(updateSetting).toHaveBeenCalledWith('showNotifications', false); }); - it('should toggle the playSound checkbox', async () => { - await act(async () => { + describe('playSound', () => { + it('should toggle the playSound checkbox', async () => { + const { rerender } = render( + + + + + , + ); + + await userEvent.click(screen.getByTestId('checkbox-playSound')); + + expect(updateSetting).toHaveBeenCalledTimes(1); + expect(updateSetting).toHaveBeenCalledWith('playSound', false); + + // Simulate update to context with playSound = false + rerender( + + + + + , + ); + + expect(screen.getByTestId('settings-volume-group')).not.toBeVisible(); + }); + + it('should increase playSound volume', async () => { render( { , ); + + await userEvent.click(screen.getByTestId('settings-volume-up')); + + expect(updateSetting).toHaveBeenCalledTimes(1); + expect(updateSetting).toHaveBeenCalledWith('notificationVolume', 30); }); - fireEvent.click(screen.getByTestId('checkbox-playSound'), { - target: { checked: true }, + it('should decrease playSound volume', async () => { + render( + + + + + , + ); + + await userEvent.click(screen.getByTestId('settings-volume-down')); + + expect(updateSetting).toHaveBeenCalledTimes(1); + expect(updateSetting).toHaveBeenCalledWith('notificationVolume', 10); }); - expect(updateSetting).toHaveBeenCalledTimes(1); - expect(updateSetting).toHaveBeenCalledWith('playSound', false); + it('should reset playSound volume', async () => { + render( + + + + + , + ); + + await userEvent.click(screen.getByTestId('settings-volume-reset')); + + expect(updateSetting).toHaveBeenCalledTimes(1); + expect(updateSetting).toHaveBeenCalledWith('notificationVolume', 20); + }); }); it('should toggle the useAlternateIdleIcon checkbox', async () => { @@ -157,12 +233,10 @@ describe('renderer/components/settings/SystemSettings.tsx', () => { ); }); - fireEvent.click(screen.getByTestId('checkbox-useAlternateIdleIcon'), { - target: { checked: true }, - }); + await userEvent.click(screen.getByTestId('checkbox-useAlternateIdleIcon')); expect(updateSetting).toHaveBeenCalledTimes(1); - expect(updateSetting).toHaveBeenCalledWith('useAlternateIdleIcon', false); + expect(updateSetting).toHaveBeenCalledWith('useAlternateIdleIcon', true); }); it('should toggle the openAtStartup checkbox', async () => { @@ -182,11 +256,9 @@ describe('renderer/components/settings/SystemSettings.tsx', () => { ); }); - fireEvent.click(screen.getByTestId('checkbox-openAtStartup'), { - target: { checked: true }, - }); + await userEvent.click(screen.getByTestId('checkbox-openAtStartup')); expect(updateSetting).toHaveBeenCalledTimes(1); - expect(updateSetting).toHaveBeenCalledWith('openAtStartup', false); + expect(updateSetting).toHaveBeenCalledWith('openAtStartup', true); }); }); diff --git a/src/renderer/components/settings/SystemSettings.tsx b/src/renderer/components/settings/SystemSettings.tsx index ab224cba4..7b30b00b6 100644 --- a/src/renderer/components/settings/SystemSettings.tsx +++ b/src/renderer/components/settings/SystemSettings.tsx @@ -93,7 +93,11 @@ export const SystemSettings: FC = () => { onChange={(evt) => updateSetting('playSound', evt.target.checked)} /> -