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'); }); });