-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1270 from violetadev/violetadev/testsetup
[Tests] extract jest config to file, add jest commands, add mocks and add missing tests
- Loading branch information
Showing
7 changed files
with
443 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
module.exports = { | ||
moduleDirectories: ['node_modules', 'release/app/node_modules', 'src'], | ||
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json'], | ||
moduleNameMapper: { | ||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': | ||
'<rootDir>/.erb/mocks/fileMock.js', | ||
'\\.(css|less|sass|scss)$': 'identity-obj-proxy', | ||
}, | ||
setupFiles: [ | ||
'./.erb/scripts/check-build-exists.ts', | ||
'<rootDir>/setupTests.js', | ||
], | ||
testEnvironment: 'jsdom', | ||
testEnvironmentOptions: { | ||
url: 'http://localhost/', | ||
}, | ||
testPathIgnorePatterns: ['release/app/dist', '.erb/dll'], | ||
transform: { | ||
'\\.(ts|tsx|js|jsx)$': 'ts-jest', | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
window.electron = { | ||
ipcRenderer: { | ||
sendMessage: jest.fn(), | ||
on: jest.fn(), | ||
once: jest.fn(), | ||
invoke: jest.fn(), | ||
removeListener: jest.fn(), | ||
removeAllListeners: jest.fn(), | ||
}, | ||
store: { | ||
set: jest.fn(), | ||
get: jest.fn(), | ||
}, | ||
}; | ||
|
||
global.IntersectionObserver = jest.fn(() => ({ | ||
root: null, | ||
rootMargin: '', | ||
thresholds: [], | ||
observe: jest.fn(), | ||
unobserve: jest.fn(), | ||
disconnect: jest.fn(), | ||
takeRecords: jest.fn(), | ||
})); |
87 changes: 87 additions & 0 deletions
87
desktop-app/src/renderer/components/Button/Button.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import '@testing-library/jest-dom'; | ||
import { act, render, screen } from '@testing-library/react'; | ||
import Button from './index'; | ||
|
||
jest.mock('@iconify/react', () => ({ | ||
Icon: () => <div data-testid="icon" />, | ||
})); | ||
|
||
describe('Button Component', () => { | ||
it('renders with default props', () => { | ||
render(<Button>Click me</Button>); | ||
const buttonElement = screen.getByRole('button', { name: /click me/i }); | ||
expect(buttonElement).toBeInTheDocument(); | ||
}); | ||
|
||
it('applies custom class name', () => { | ||
render(<Button className="custom-class">Click me</Button>); | ||
const buttonElement = screen.getByRole('button', { name: /click me/i }); | ||
expect(buttonElement).toHaveClass('custom-class'); | ||
}); | ||
|
||
it('renders loading icon when isLoading is true', () => { | ||
render(<Button isLoading>Click me</Button>); | ||
const loadingIcon = screen.getByTestId('icon'); | ||
expect(loadingIcon).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders confirmation icon when loading is done', () => { | ||
jest.useFakeTimers(); | ||
const { rerender } = render(<Button isLoading>Click me</Button>); | ||
|
||
act(() => { | ||
rerender(<Button isLoading={false}>Click me</Button>); | ||
jest.runAllTimers(); // Use act to advance timers | ||
}); | ||
|
||
const confirmationIcon = screen.getByTestId('icon'); | ||
expect(confirmationIcon).toBeInTheDocument(); | ||
jest.useRealTimers(); | ||
}); | ||
|
||
it('applies primary button styles', () => { | ||
render(<Button isPrimary>Click me</Button>); | ||
const buttonElement = screen.getByRole('button', { name: /click me/i }); | ||
expect(buttonElement).toHaveClass('bg-emerald-500'); | ||
expect(buttonElement).toHaveClass('text-white'); | ||
}); | ||
|
||
it('applies action button styles', () => { | ||
render(<Button isActionButton>Click me</Button>); | ||
const buttonElement = screen.getByRole('button', { name: /click me/i }); | ||
expect(buttonElement).toHaveClass('bg-slate-200'); | ||
}); | ||
|
||
it('applies subtle hover styles', () => { | ||
render(<Button subtle>Click me</Button>); | ||
const buttonElement = screen.getByRole('button', { name: /click me/i }); | ||
expect(buttonElement).toHaveClass('hover:bg-slate-200'); | ||
}); | ||
|
||
it('disables hover effects when disableHoverEffects is true', () => { | ||
render( | ||
<Button disableHoverEffects subtle> | ||
Click me | ||
</Button> | ||
); | ||
const buttonElement = screen.getByRole('button', { name: /click me/i }); | ||
expect(buttonElement).not.toHaveClass('hover:bg-slate-200'); | ||
}); | ||
|
||
it('renders children correctly when not loading or loading done', () => { | ||
render(<Button>Click me</Button>); | ||
const buttonElement = screen.getByText('Click me'); | ||
expect(buttonElement).toBeInTheDocument(); | ||
}); | ||
|
||
it('does not render children when loading or loading done', () => { | ||
const { rerender } = render(<Button isLoading>Click me</Button>); | ||
expect(screen.queryByText('Click me')).not.toBeInTheDocument(); | ||
|
||
act(() => { | ||
rerender(<Button isLoading={false}>Click me</Button>); | ||
}); | ||
|
||
expect(screen.queryByText('Click me')).not.toBeInTheDocument(); | ||
}); | ||
}); |
169 changes: 169 additions & 0 deletions
169
...enderer/components/DeviceManager/PreviewSuites/ManageSuitesTool/ManageSuitesTool.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import '@testing-library/jest-dom'; | ||
import { render, screen, fireEvent, waitFor } from '@testing-library/react'; | ||
import { Device } from 'common/deviceList'; | ||
import { Provider, useDispatch } from 'react-redux'; | ||
import { configureStore } from '@reduxjs/toolkit'; | ||
import { | ||
addSuites, | ||
deleteAllSuites, | ||
} from 'renderer/store/features/device-manager'; | ||
import { ReactNode } from 'react'; | ||
import { transformFile } from './utils'; | ||
import { ManageSuitesTool } from './ManageSuitesTool'; | ||
|
||
jest.mock('renderer/store/features/device-manager', () => ({ | ||
addSuites: jest.fn(() => ({ type: 'addSuites' })), | ||
deleteAllSuites: jest.fn(() => ({ type: 'deleteAllSuites' })), | ||
default: jest.fn((state = {}) => state), // Mock the reducer as a function | ||
})); | ||
|
||
jest.mock('./utils', () => ({ | ||
transformFile: jest.fn(), | ||
})); | ||
|
||
jest.mock('renderer/components/FileUploader', () => ({ | ||
FileUploader: ({ | ||
handleFileUpload, | ||
}: { | ||
handleFileUpload: (file: File) => void; | ||
}) => ( | ||
<button | ||
type="button" | ||
data-testid="mock-file-uploader" | ||
onClick={() => | ||
handleFileUpload( | ||
new File(['{}'], 'test.json', { type: 'application/json' }) | ||
) | ||
} | ||
> | ||
Mock File Uploader | ||
</button> | ||
), | ||
})); | ||
|
||
jest.mock('./helpers', () => ({ | ||
onFileDownload: jest.fn(), | ||
setCustomDevices: jest.fn(), | ||
})); | ||
|
||
jest.mock('react-redux', () => ({ | ||
...jest.requireActual('react-redux'), | ||
useDispatch: jest.fn(), | ||
})); | ||
|
||
const renderWithRedux = ( | ||
component: | ||
| string | ||
| number | ||
| boolean | ||
| Iterable<ReactNode> | ||
| JSX.Element | ||
| null | ||
| undefined | ||
) => { | ||
const store = configureStore({ | ||
reducer: { | ||
deviceManager: jest.requireMock('renderer/store/features/device-manager') | ||
.default, | ||
}, | ||
}); | ||
|
||
return { | ||
...render(<Provider store={store}>{component}</Provider>), | ||
store, | ||
}; | ||
}; | ||
|
||
describe('ManageSuitesTool', () => { | ||
let setCustomDevicesStateMock: jest.Mock<() => void, Device[]>; | ||
const dispatchMock = jest.fn(); | ||
|
||
beforeEach(() => { | ||
(useDispatch as jest.Mock).mockReturnValue(dispatchMock); | ||
|
||
setCustomDevicesStateMock = jest.fn(); | ||
|
||
renderWithRedux( | ||
<ManageSuitesTool setCustomDevicesState={setCustomDevicesStateMock} /> | ||
); | ||
}); | ||
|
||
it('renders the component correctly', () => { | ||
expect(screen.getByTestId('download-btn')).toBeInTheDocument(); | ||
expect(screen.getByTestId('upload-btn')).toBeInTheDocument(); | ||
expect(screen.getByTestId('reset-btn')).toBeInTheDocument(); | ||
}); | ||
|
||
it('opens the modal when download button is clicked', () => { | ||
fireEvent.click(screen.getByTestId('download-btn')); | ||
expect(screen.getByText('Import your devices')).toBeInTheDocument(); | ||
}); | ||
|
||
it('opens the reset confirmation dialog when reset button is clicked', () => { | ||
fireEvent.click(screen.getByTestId('reset-btn')); | ||
expect( | ||
screen.getByText('Do you want to reset all settings?') | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it('closes the reset confirmation dialog when the close button is clicked', () => { | ||
fireEvent.click(screen.getByTestId('reset-btn')); | ||
fireEvent.click(screen.getByText('Cancel')); | ||
expect( | ||
screen.queryByText('Do you want to reset all settings?') | ||
).not.toBeInTheDocument(); | ||
}); | ||
|
||
it('dispatches deleteAllSuites and clears custom devices on reset confirmation', async () => { | ||
fireEvent.click(screen.getByTestId('reset-btn')); | ||
fireEvent.click(screen.getByText('Confirm')); | ||
|
||
await waitFor(() => { | ||
expect(deleteAllSuites).toHaveBeenCalled(); | ||
expect(setCustomDevicesStateMock).toHaveBeenCalledWith([]); | ||
}); | ||
}); | ||
|
||
it('handles successful file upload and processes custom devices and suites', async () => { | ||
const mockSuites = [ | ||
{ id: '1', name: 'first suite', devices: [] }, | ||
{ id: '2', name: 'second suite', devices: [] }, | ||
]; | ||
|
||
(transformFile as jest.Mock).mockResolvedValue({ | ||
customDevices: ['device1', 'device2'], | ||
suites: mockSuites, | ||
}); | ||
|
||
fireEvent.click(screen.getByTestId('download-btn')); | ||
fireEvent.click(screen.getByTestId('mock-file-uploader')); | ||
|
||
await waitFor(() => { | ||
expect(transformFile).toHaveBeenCalledWith(expect.any(File)); | ||
expect(dispatchMock).toHaveBeenCalledWith(addSuites(mockSuites)); | ||
}); | ||
}); | ||
|
||
it('handles error in file upload', async () => { | ||
(transformFile as jest.Mock).mockRejectedValue( | ||
new Error('File upload failed') | ||
); | ||
|
||
fireEvent.click(screen.getByTestId('download-btn')); | ||
|
||
fireEvent.click(screen.getByTestId('mock-file-uploader')); | ||
|
||
await waitFor(() => { | ||
expect(transformFile).toHaveBeenCalledWith(expect.any(File)); | ||
expect( | ||
screen.getByText('There has been an error, please try again.') | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
fireEvent.click(screen.getByText('Close')); | ||
|
||
expect( | ||
screen.queryByText('There has been an error, please try again.') | ||
).not.toBeInTheDocument(); | ||
}); | ||
}); |
Oops, something went wrong.