Skip to content

Commit 18d17ec

Browse files
author
Flatlogic Bot
committed
test: add vitest smoke coverage for core routes
1 parent 9e27275 commit 18d17ec

8 files changed

Lines changed: 1097 additions & 4 deletions

File tree

package-lock.json

Lines changed: 908 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"preview": "vite preview",
1212
"lint": "cross-env NODE_ENV=development BABEL_ENV=development eslint src --ext .js,.jsx",
1313
"lint:fix": "cross-env NODE_ENV=development BABEL_ENV=development eslint src --ext .js,.jsx --fix",
14-
"test": "echo \"Tests are not configured yet\" && exit 0",
14+
"test": "vitest run",
15+
"test:watch": "vitest",
1516
"format": "prettier --write src/"
1617
},
1718
"dependencies": {
@@ -95,7 +96,9 @@
9596
"cross-env": "^7.0.3",
9697
"eslint": "^8.57.1",
9798
"eslint-config-react-app": "^7.0.1",
99+
"jsdom": "^25.0.1",
98100
"sass": "^1.81.0",
99-
"vite": "^5.4.10"
101+
"vite": "^5.4.10",
102+
"vitest": "^2.1.8"
100103
}
101104
}

src/test/setup.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import '@testing-library/jest-dom/vitest';
2+
import { afterEach, vi } from 'vitest';
3+
import { cleanup } from '@testing-library/react';
4+
5+
afterEach(() => {
6+
cleanup();
7+
vi.restoreAllMocks();
8+
});
9+
10+
vi.mock('uuid', () => ({
11+
v4: () => 'test-uuid',
12+
default: {
13+
v4: () => 'test-uuid',
14+
},
15+
}));
16+
17+
if (!window.matchMedia) {
18+
window.matchMedia = vi.fn().mockImplementation((query) => ({
19+
matches: false,
20+
media: query,
21+
onchange: null,
22+
addListener: vi.fn(),
23+
removeListener: vi.fn(),
24+
addEventListener: vi.fn(),
25+
removeEventListener: vi.fn(),
26+
dispatchEvent: vi.fn(),
27+
}));
28+
}
29+
30+
if (!window.ResizeObserver) {
31+
window.ResizeObserver = class {
32+
observe() {}
33+
unobserve() {}
34+
disconnect() {}
35+
};
36+
}
37+
38+
if (!window.IntersectionObserver) {
39+
window.IntersectionObserver = class {
40+
observe() {}
41+
unobserve() {}
42+
disconnect() {}
43+
};
44+
}
45+
46+
if (!window.HTMLElement.prototype.scrollTo) {
47+
window.HTMLElement.prototype.scrollTo = () => {};
48+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import { screen } from '@testing-library/react';
3+
import { vi } from 'vitest';
4+
import Dashboard from '../../pages/dashboard/Dashboard';
5+
import { renderWithProviders } from '../utils/renderWithProviders';
6+
7+
vi.mock('@mui/icons-material', () =>
8+
new Proxy(
9+
{ __esModule: true },
10+
{
11+
get: (_target, prop) => {
12+
if (prop === '__esModule') {
13+
return true;
14+
}
15+
if (prop === 'then') {
16+
return undefined;
17+
}
18+
return () => null;
19+
},
20+
has: (_target, prop) => prop !== 'then',
21+
},
22+
),
23+
);
24+
25+
describe('Dashboard Smoke', () => {
26+
it('renders key dashboard widgets', () => {
27+
renderWithProviders(<Dashboard />, { route: '/app/dashboard' });
28+
29+
expect(screen.getByText(/support tracker/i)).toBeInTheDocument();
30+
expect(screen.getByText(/revenue breakdown/i)).toBeInTheDocument();
31+
});
32+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
import { screen } from '@testing-library/react';
3+
4+
import Login from '../../pages/login/Login';
5+
import { renderWithProviders } from '../utils/renderWithProviders';
6+
7+
describe('Login Smoke', () => {
8+
it('renders login screen with main controls', () => {
9+
renderWithProviders(<Login />, { route: '/login' });
10+
11+
expect(screen.getByRole('tab', { name: /login/i })).toBeInTheDocument();
12+
expect(screen.getByPlaceholderText(/email adress/i)).toBeInTheDocument();
13+
expect(
14+
screen.getByRole('button', { name: /forgot password/i }),
15+
).toBeInTheDocument();
16+
});
17+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import { screen } from '@testing-library/react';
3+
import { Route, Routes } from 'react-router-dom';
4+
5+
import UsersTable from '../../pages/CRUD/Users/table/UsersTable.js';
6+
import UsersFormPage from '../../pages/CRUD/Users/form/UsersFormPage.js';
7+
import { renderWithProviders } from '../utils/renderWithProviders';
8+
9+
describe('Users Smoke', () => {
10+
it('renders users table and mock data', async () => {
11+
renderWithProviders(<UsersTable />, { route: '/app/users' });
12+
13+
expect(screen.getByRole('button', { name: /^new$/i })).toBeInTheDocument();
14+
expect(await screen.findByText('Admin')).toBeInTheDocument();
15+
});
16+
17+
it('renders users edit form route', async () => {
18+
const editRoute = (
19+
<Routes>
20+
<Route path='/app/users/:id/edit' element={<UsersFormPage />} />
21+
</Routes>
22+
);
23+
24+
renderWithProviders(
25+
editRoute,
26+
{ route: '/app/users/1/edit' },
27+
);
28+
29+
expect(await screen.findByText(/edit users/i)).toBeInTheDocument();
30+
expect(await screen.findByDisplayValue('Admin')).toBeInTheDocument();
31+
});
32+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React from 'react';
2+
import { render } from '@testing-library/react';
3+
import { MemoryRouter } from 'react-router-dom';
4+
import { ThemeProvider as ThemeProviderV5 } from '@mui/material/styles';
5+
import { StyledEngineProvider } from '@mui/material/styles';
6+
7+
import { LayoutProvider } from '../../context/LayoutContext';
8+
import { UserProvider } from '../../context/UserContext';
9+
import { ManagementProvider } from '../../context/ManagementContext';
10+
import {
11+
ThemeProvider as ThemeChangeProvider,
12+
ThemeStateContext,
13+
} from '../../context/ThemeContext';
14+
15+
function Providers({ children, route = '/' }) {
16+
localStorage.setItem('theme', 'default');
17+
18+
return (
19+
<MemoryRouter initialEntries={[route]}>
20+
<LayoutProvider>
21+
<UserProvider>
22+
<StyledEngineProvider injectFirst>
23+
<ThemeChangeProvider>
24+
<ThemeStateContext.Consumer>
25+
{(theme) => (
26+
<ThemeProviderV5 theme={theme}>
27+
<ManagementProvider>{children}</ManagementProvider>
28+
</ThemeProviderV5>
29+
)}
30+
</ThemeStateContext.Consumer>
31+
</ThemeChangeProvider>
32+
</StyledEngineProvider>
33+
</UserProvider>
34+
</LayoutProvider>
35+
</MemoryRouter>
36+
);
37+
}
38+
39+
function renderWithProviders(ui, options = {}) {
40+
const { route = '/', ...rest } = options;
41+
return render(ui, {
42+
wrapper: ({ children }) => <Providers route={route}>{children}</Providers>,
43+
...rest,
44+
});
45+
}
46+
47+
export { Providers, renderWithProviders };

vite.config.mjs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default defineConfig(({ mode }) => ({
3434
},
3535
esbuild: {
3636
loader: 'jsx',
37-
include: /src\/.*\.js$/,
37+
include: /src\/.*\.[jt]sx?$/,
3838
exclude: [],
3939
},
4040
resolve: {
@@ -71,4 +71,11 @@ export default defineConfig(({ mode }) => ({
7171
server: {
7272
port: 3000,
7373
},
74+
test: {
75+
globals: true,
76+
environment: 'jsdom',
77+
setupFiles: './src/test/setup.js',
78+
css: true,
79+
fileParallelism: false,
80+
},
7481
}));

0 commit comments

Comments
 (0)