import { axe } from 'vitest-axe';
import type { RenderResult } from '@testing-library/react';
import { act, cleanup, render, screen } from '@testing-library/react';
import * as PasswordToggleField from './password-toggle-field';
import { afterEach, describe, it, beforeEach, expect } from 'vitest';
import { userEvent, type UserEvent } from '@testing-library/user-event';
describe('given a default PasswordToggleField', () => {
let rendered: RenderResult;
let user: UserEvent;
afterEach(cleanup);
beforeEach(() => {
user = userEvent.setup();
rendered = render(
<>
>,
);
});
afterEach(cleanup);
it('should have no accessibility violations', async () => {
expect(await axe(rendered.container)).toHaveNoViolations();
});
it('should initially be hidden', () => {
const toggle = screen.getByRole('button', { name: 'Show' });
expect(toggle.textContent).toBe('Show');
});
it('toggles the children when clicked', async () => {
const toggle = screen.getByRole('button', { name: 'Show' });
await act(async () => await user.click(toggle));
expect(toggle.textContent).toBe('Hide');
});
it('should initially render input as `type=password`', async () => {
const input = screen.getByLabelText('Password');
expect(input.type).toBe('password');
});
it('should initially render input as `type=text` when toggled', async () => {
const input = screen.getByLabelText('Password');
const toggle = screen.getByRole('button', { name: 'Show' });
await act(async () => await user.click(toggle));
expect(input.type).toBe('text');
});
it('should re-focus the input after toggling', async () => {
const input = screen.getByLabelText('Password');
const toggle = screen.getByRole('button', { name: 'Show' });
await act(async () => await user.click(toggle));
expect(document.activeElement).toBe(input);
});
it("should restore the input's selection after toggling", async () => {
const input = screen.getByLabelText('Password');
const toggle = screen.getByRole('button', { name: 'Show' });
await act(async () => await user.click(input));
await act(async () => await user.type(input, 'p'));
await act(async () => await user.click(toggle));
// selection should be at the end of the input value
expect(input.selectionStart).toBe(1);
expect(input.selectionEnd).toBe(1);
await act(async () => await user.type(input, 'assword'));
input.selectionStart = 0;
input.selectionEnd = 4;
await act(async () => await user.click(toggle));
expect(input.selectionStart).toBe(0);
expect(input.selectionEnd).toBe(4);
});
});
describe('given a PasswordToggleField with an Icon', () => {
let rendered: RenderResult;
let user: UserEvent;
afterEach(cleanup);
beforeEach(() => {
user = userEvent.setup();
rendered = render(
<>
}
hidden={}
/>
>,
);
});
afterEach(cleanup);
it('should have no accessibility violations', async () => {
expect(await axe(rendered.container)).toHaveNoViolations();
});
it('should initially be hidden', () => {
const icon = screen.getByTestId('closed');
expect(icon).toBeVisible();
expect(screen.queryByTestId('open')).not.toBeInTheDocument();
});
it('toggles the icon when clicked', async () => {
const toggle = screen.getByRole('button', { name: 'Show password' });
await act(async () => await user.click(toggle));
expect(screen.getByTestId('open')).toBeVisible();
expect(screen.queryByTestId('closed')).not.toBeInTheDocument();
});
it('should initially render input as `type=password`', async () => {
const input = screen.getByLabelText('Password');
expect(input.type).toBe('password');
});
it('should initially render input as `type=text` when toggled', async () => {
const input = screen.getByLabelText('Password');
const toggle = screen.getByRole('button', { name: 'Show password' });
await act(async () => await user.click(toggle));
expect(input.type).toBe('text');
});
it('should retain its value when toggled', async () => {
const input = screen.getByLabelText('Password');
const toggle = screen.getByRole('button', { name: 'Show password' });
await act(async () => await user.type(input, 'pass123'));
await act(async () => await user.click(toggle));
expect(input).toHaveValue('pass123');
});
});
describe('given a PasswordToggleField in a form', () => {
let user: UserEvent;
afterEach(cleanup);
beforeEach(() => {
user = userEvent.setup();
render(
,
);
});
afterEach(cleanup);
it('should reset visibility to hidden after submission', async () => {
const toggle = screen.getByRole('button', { name: 'Show' });
const input = screen.getByLabelText('Password');
await act(() => user.click(toggle));
expect(input.type).toBe('text');
const submitButton = screen.getByText('Submit');
await act(() => user.click(submitButton));
expect(input.type).toBe('password');
});
});
function EyeClosedIcon(props: React.SVGProps) {
return (
);
}
function EyeOpenIcon(props: React.SVGProps) {
return (
);
}