import React from 'react';
import { fireEvent } from '@testing-library/react';
import { vi } from 'vitest';
import { PasswordInput as CorePasswordInput } from '@shoptet/ui-core-web';
import { render } from '../../../../vitest/render';
import { PasswordInput } from './PasswordInput';
describe('PasswordInput auto-hide on focus leave', () => {
it('hides the password again when focus leaves the component', () => {
const { getByLabelText, getByRole } = render();
const input = getByLabelText('Password') as HTMLInputElement;
const toggle = getByRole('button');
fireEvent.click(toggle);
expect(input.type).toBe('text');
fireEvent.blur(input, { relatedTarget: document.body });
expect(input.type).toBe('password');
expect(toggle.getAttribute('aria-pressed')).toBe('false');
});
it('keeps the password revealed while focus moves between the input and the toggle', () => {
const { getByLabelText, getByRole } = render();
const input = getByLabelText('Password') as HTMLInputElement;
const toggle = getByRole('button');
fireEvent.click(toggle);
fireEvent.blur(toggle, { relatedTarget: input });
expect(input.type).toBe('text');
fireEvent.blur(input, { relatedTarget: toggle });
expect(input.type).toBe('text');
});
it('lets the toggle hide the password while the input is focused', () => {
const { getByLabelText, getByRole } = render();
const input = getByLabelText('Password') as HTMLInputElement;
const toggle = getByRole('button');
fireEvent.click(toggle);
expect(input.type).toBe('text');
fireEvent.blur(input, { relatedTarget: toggle });
fireEvent.click(toggle);
expect(input.type).toBe('password');
});
it('does not change a controlled revealed state itself, only requests the change', () => {
const onRevealedChange = vi.fn();
const { getByLabelText } = render(
{}} revealed onRevealedChange={onRevealedChange} />
);
const input = getByLabelText('Password') as HTMLInputElement;
fireEvent.blur(input, { relatedTarget: document.body });
expect(input.type).toBe('text');
expect(onRevealedChange).toHaveBeenCalledWith(false);
});
});
describe('core PasswordInput revealed state', () => {
it('keeps the input hidden when revealed is controlled and the change is not echoed back', () => {
const onRevealedChange = vi.fn();
const { getByLabelText, getByRole } = render(
);
fireEvent.click(getByRole('button'));
expect((getByLabelText('Password') as HTMLInputElement).type).toBe('password');
expect(onRevealedChange).toHaveBeenCalledWith(true);
});
it('toggles the input type when uncontrolled', () => {
const onRevealedChange = vi.fn();
const { getByLabelText, getByRole } = render(
);
const input = getByLabelText('Password') as HTMLInputElement;
fireEvent.click(getByRole('button'));
expect(input.type).toBe('text');
expect(onRevealedChange).toHaveBeenNthCalledWith(1, true);
fireEvent.click(getByRole('button'));
expect(input.type).toBe('password');
expect(onRevealedChange).toHaveBeenNthCalledWith(2, false);
});
it('lets an explicit disabled prop override the auto-enable', () => {
const { getByLabelText, getByRole } = render(
);
expect((getByRole('button') as HTMLButtonElement).disabled).toBe(true);
fireEvent.click(getByRole('button'));
expect((getByLabelText('Password') as HTMLInputElement).type).toBe('password');
});
});
describe('core PasswordInput toggle enablement', () => {
it('disables the toggle while the field is empty', () => {
const { getByRole } = render(
);
expect((getByRole('button') as HTMLButtonElement).disabled).toBe(true);
});
it('enables the toggle once the field has a value', () => {
const { getByRole } = render(
);
expect((getByRole('button') as HTMLButtonElement).disabled).toBe(false);
});
it('re-disables the toggle when the field is cleared', () => {
const { getByLabelText, getByRole } = render(
);
expect((getByRole('button') as HTMLButtonElement).disabled).toBe(false);
fireEvent.change(getByLabelText('Password'), { target: { value: '' } });
expect((getByRole('button') as HTMLButtonElement).disabled).toBe(true);
});
it('defaults the toggle accessible label from the dictionary', () => {
const { getByRole } = render(
);
expect(getByRole('button').getAttribute('aria-label')).toBe('Show password');
});
});