mirror of
https://github.com/responsively-org/responsively-app
synced 2024-11-10 14:54:12 +00:00
Merge pull request #1270 from violetadev/violetadev/testsetup
[Tests] extract jest config to file, add jest commands, add mocks and add missing tests
This commit is contained in:
commit
1d0b0313ea
7 changed files with 443 additions and 34 deletions
21
desktop-app/jest.config.js
Normal file
21
desktop-app/jest.config.js
Normal file
|
@ -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',
|
||||
},
|
||||
};
|
|
@ -38,7 +38,9 @@
|
|||
"start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
|
||||
"start:preloadWebview": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload-webview.dev.ts",
|
||||
"start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
|
||||
"test": "jest",
|
||||
"test": "jest --config ./jest.config.js",
|
||||
"test:watch": "jest --watch --config ./jest.config.js",
|
||||
"test:coverage": "jest --coverage --config ./jest.config.js",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"lint-staged": {
|
||||
|
@ -70,38 +72,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"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"
|
||||
],
|
||||
"testEnvironment": "jsdom",
|
||||
"testEnvironmentOptions": {
|
||||
"url": "http://localhost/"
|
||||
},
|
||||
"testPathIgnorePatterns": [
|
||||
"release/app/dist",
|
||||
".erb/dll"
|
||||
],
|
||||
"transform": {
|
||||
"\\.(ts|tsx|js|jsx)$": "ts-jest"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/lato": "^5.0.17",
|
||||
"@headlessui-float/react": "^0.12.0",
|
||||
|
|
24
desktop-app/setupTests.js
Normal file
24
desktop-app/setupTests.js
Normal file
|
@ -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
desktop-app/src/renderer/components/Button/Button.test.tsx
Normal file
87
desktop-app/src/renderer/components/Button/Button.test.tsx
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,138 @@
|
|||
import '@testing-library/jest-dom';
|
||||
// import { downloadFile, onFileDownload, setCustomDevices } from './helpers';
|
||||
|
||||
import { Device } from 'common/deviceList';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import Button from 'renderer/components/Button';
|
||||
import * as Helpers from './helpers';
|
||||
|
||||
describe('onFileDownload', () => {
|
||||
beforeAll(() => {
|
||||
global.URL.createObjectURL = jest.fn(() => 'mockedURL');
|
||||
global.URL.revokeObjectURL = jest.fn(); // Mocking revokeObjectURL too
|
||||
});
|
||||
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
|
||||
it('should get customDevices and suites from the store and download the file', () => {
|
||||
const mockCustomDevices = [{ name: 'Device1', width: 800, height: 600 }];
|
||||
const mockSuites = [{ name: 'Suite1' }];
|
||||
|
||||
const spyOnDownloadFileFn = jest.spyOn(Helpers, 'downloadFile');
|
||||
|
||||
(window.electron.store.get as jest.Mock).mockImplementation(
|
||||
(key: string) => {
|
||||
if (key === 'deviceManager.customDevices') {
|
||||
return mockCustomDevices;
|
||||
}
|
||||
if (key === 'deviceManager.previewSuites') {
|
||||
return mockSuites;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
Helpers.onFileDownload();
|
||||
|
||||
expect(window.electron.store.get).toHaveBeenCalledWith(
|
||||
'deviceManager.customDevices'
|
||||
);
|
||||
expect(window.electron.store.get).toHaveBeenCalledWith(
|
||||
'deviceManager.previewSuites'
|
||||
);
|
||||
expect(spyOnDownloadFileFn).toHaveBeenCalledWith({
|
||||
customDevices: mockCustomDevices,
|
||||
suites: mockSuites,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('downloadFile', () => {
|
||||
it('should create and download a JSON file', () => {
|
||||
const mockFileData = { key: 'value' };
|
||||
const mockedUrl = 'http://localhost/#';
|
||||
|
||||
const createObjectURLSpy = jest
|
||||
.spyOn(URL, 'createObjectURL')
|
||||
.mockReturnValue(mockedUrl);
|
||||
const revokeObjectURLSpy = jest.spyOn(URL, 'revokeObjectURL');
|
||||
const spyOnDownloadFileFn = jest.spyOn(Helpers, 'downloadFile');
|
||||
|
||||
const { getByTestId } = render(
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => Helpers.downloadFile(mockFileData)}
|
||||
data-testid="mockDownloadBtn"
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
const link = getByTestId('mockDownloadBtn');
|
||||
fireEvent.click(link);
|
||||
|
||||
expect(spyOnDownloadFileFn).toHaveBeenCalled();
|
||||
expect(createObjectURLSpy).toHaveBeenCalledWith(expect.any(Blob));
|
||||
expect(revokeObjectURLSpy).toHaveBeenCalledWith(mockedUrl);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setCustomDevices', () => {
|
||||
it('should filter out default devices and store custom devices', () => {
|
||||
const mockCustomDevices: Device[] = [
|
||||
{
|
||||
name: 'Device1',
|
||||
width: 800,
|
||||
height: 600,
|
||||
id: '1',
|
||||
userAgent: '',
|
||||
type: '',
|
||||
dpi: 0,
|
||||
isTouchCapable: false,
|
||||
isMobileCapable: false,
|
||||
capabilities: [],
|
||||
},
|
||||
{
|
||||
name: 'Device2',
|
||||
width: 1024,
|
||||
height: 768,
|
||||
id: '2',
|
||||
userAgent: '',
|
||||
type: '',
|
||||
dpi: 0,
|
||||
isTouchCapable: false,
|
||||
isMobileCapable: false,
|
||||
capabilities: [],
|
||||
},
|
||||
];
|
||||
|
||||
const mockDefaultDevices: Device[] = [
|
||||
{
|
||||
name: 'Device1',
|
||||
width: 800,
|
||||
height: 600,
|
||||
id: '0',
|
||||
userAgent: '',
|
||||
type: '',
|
||||
dpi: 0,
|
||||
isTouchCapable: false,
|
||||
isMobileCapable: false,
|
||||
capabilities: [],
|
||||
},
|
||||
];
|
||||
|
||||
jest.mock('common/deviceList', () => ({
|
||||
defaultDevices: mockDefaultDevices,
|
||||
}));
|
||||
|
||||
const filteredDevices = Helpers.setCustomDevices(mockCustomDevices);
|
||||
|
||||
expect(window.electron.store.set).not.toHaveBeenCalledWith(
|
||||
'deviceManager.customDevices',
|
||||
[mockCustomDevices[1]]
|
||||
);
|
||||
|
||||
expect(filteredDevices).toEqual(mockCustomDevices);
|
||||
});
|
||||
});
|
|
@ -26,7 +26,7 @@ export const FileUploader = ({
|
|||
}, [handleFileUpload, resetUploadedFile, uploadedFile]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row flex-wrap">
|
||||
<div className="flex flex-row flex-wrap" data-testid="file-uploader">
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
|
|
Loading…
Reference in a new issue