Skip to content

Commit cea5cfb

Browse files
committed
frontend: add tests for PasswordComponent
1 parent c1b5230 commit cea5cfb

File tree

2 files changed

+60
-7
lines changed

2 files changed

+60
-7
lines changed

frontend/src/components/__tests__/Navbar.test.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { render, screen, fireEvent } from '@testing-library/preact';
22
import { describe, it, expect, vi, beforeEach } from 'vitest';
3-
import * as NavbarModule from './Navbar';
4-
import { connected as connectedSignal } from './Navbar';
3+
import * as NavbarModule from '../Navbar';
4+
import { connected as connectedSignal } from '../Navbar';
55
import { useLocation } from 'preact-iso';
6-
import { fetchClient, AppState, loggedIn, bc, resetSecret, clearSecretKeyFromServiceWorker } from '../utils';
6+
import { fetchClient, AppState, loggedIn, bc, resetSecret, clearSecretKeyFromServiceWorker } from '../../utils';
77

88
// Use actual module for component and functions (test-setup re-exports actual)
99
const { CustomNavbar } = NavbarModule;
@@ -54,7 +54,7 @@ describe('Navbar', () => {
5454
(fetchClient.GET as unknown as ReturnType<typeof vi.fn>).mockReset();
5555
(fetchClient.GET as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ error: undefined });
5656

57-
const actual = await vi.importActual<typeof import('./Navbar')>('./Navbar');
57+
const actual = await vi.importActual<typeof import('../Navbar')>('../Navbar');
5858
await actual.logout(false);
5959

6060
// Assert
@@ -69,10 +69,10 @@ describe('Navbar', () => {
6969
it('logout() shows alert when logout_all fails', async () => {
7070
(fetchClient.GET as unknown as ReturnType<typeof vi.fn>).mockReset();
7171
(fetchClient.GET as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ error: 'boom' });
72-
const { showAlert } = await vi.importMock<typeof import('./Alert')>('./Alert');
72+
const { showAlert } = await vi.importMock<typeof import('../Alert')>('../Alert');
7373
const showAlertSpy = showAlert as unknown as ReturnType<typeof vi.fn>;
7474

75-
const actual = await vi.importActual<typeof import('./Navbar')>('./Navbar');
75+
const actual = await vi.importActual<typeof import('../Navbar')>('../Navbar');
7676
await actual.logout(true);
7777

7878
// Assert
@@ -83,7 +83,7 @@ describe('Navbar', () => {
8383
it('setAppNavigation configures sidebar via Median', async () => {
8484
const Median = await vi.importMock<typeof import('median-js-bridge')>('median-js-bridge');
8585
const setItems = vi.spyOn(Median.default.sidebar, 'setItems');
86-
const mod = await vi.importActual<typeof import('./Navbar')>('./Navbar');
86+
const mod = await vi.importActual<typeof import('../Navbar')>('../Navbar');
8787
mod.setAppNavigation();
8888
expect(setItems).toHaveBeenCalled();
8989
});
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { beforeAll, describe, expect, it, vi } from 'vitest';
2+
import { render, screen, fireEvent } from '@testing-library/preact';
3+
import userEvent from '@testing-library/user-event';
4+
5+
let PasswordComponent: any;
6+
7+
beforeAll(async () => {
8+
// Bypass the global mock defined in test-setup.ts and import the real component
9+
({ PasswordComponent } = await vi.importActual<typeof import('../PasswordComponent')>('../PasswordComponent'));
10+
});
11+
12+
describe('PasswordComponent', () => {
13+
it('renders password input with placeholder and toggles visibility with icons', async () => {
14+
const user = userEvent.setup();
15+
const handleChange = vi.fn();
16+
17+
render(<PasswordComponent onChange={handleChange} />);
18+
19+
const input = screen.getByPlaceholderText('password');
20+
expect(input).toBeInTheDocument();
21+
22+
expect(screen.getByTestId('password-input')).toBeInTheDocument();
23+
expect(screen.getByTestId('eye-icon')).toBeInTheDocument();
24+
25+
await user.click(screen.getByRole('button'));
26+
27+
expect(screen.getByTestId('text-input')).toBeInTheDocument();
28+
expect(screen.getByTestId('eye-off-icon')).toBeInTheDocument();
29+
});
30+
31+
it('calls onChange with typed value', async () => {
32+
const handleChange = vi.fn();
33+
34+
render(<PasswordComponent onChange={handleChange} />);
35+
36+
const input = screen.getByTestId('password-input') as HTMLInputElement;
37+
fireEvent.change(input, { target: { value: 'Secret123!' } });
38+
39+
expect(handleChange).toHaveBeenCalled();
40+
expect(handleChange).toHaveBeenLastCalledWith('Secret123!');
41+
});
42+
43+
it('shows invalid state and feedback when provided', () => {
44+
const handleChange = vi.fn();
45+
render(<PasswordComponent onChange={handleChange} isInvalid invalidMessage="Invalid password" />);
46+
47+
const input = screen.getByTestId('password-input');
48+
expect(input).toHaveClass('invalid');
49+
50+
const feedback = screen.getByTestId('invalid-feedback');
51+
expect(feedback).toHaveTextContent('Invalid password');
52+
});
53+
});

0 commit comments

Comments
 (0)