import { Freeze } from '@transferwise/icons'; import { mockMatchMedia, render, screen, userEvent } from '../test-utils'; import Button, { ButtonProps } from './Button.resolver'; import { CommonProps } from './Button.types'; mockMatchMedia(); describe('Button', () => { const legacyProps: ButtonProps = { as: 'button', priority: 'primary', size: 'md', type: 'accent', }; const newProps: ButtonProps = { as: 'button', v2: true, priority: 'primary', size: 'md', }; const testId = 'new-button'; it('renders LegacyButton when v2 is false', () => { render(); const button = screen.getByText('Legacy Button'); expect(button).toBeInTheDocument(); expect(button).toHaveClass('btn btn-md np-btn np-btn-md btn-accent btn-priority-1'); }); it('renders the new Button when v2 is true', () => { render( , ); const button = screen.getByTestId('new-button'); expect(button).toBeInTheDocument(); expect(button).toHaveClass('wds-Button wds-Button--medium wds-Button--primary'); }); describe('render as HTML anchor', () => { const href = 'https://example.com'; const name = 'Button as link'; describe('v2', () => { describe('rendered HTMLS element', () => { it('should render as button by default', () => { render(); expect(screen.getByRole('button', { name })).toBeInTheDocument(); }); it('should render as link if href is provided', () => { render( , ); expect(screen.getByRole('link', { name })).toHaveAttribute('href', href); }); it('should render a HTML anchor if `as="a"` is provided', () => { render( , ); expect(screen.getByText(name).closest('a')).toBeInTheDocument(); }); it('should render as a button if `href` is set together with `as="button"`', () => { render( , ); expect(screen.getByRole('button', { name })).toBeInTheDocument(); }); }); describe('HTML attributes', () => { it('should not set `type` if rendered as HTML anchor', () => { render( , ); expect(screen.getByRole('link')).not.toHaveAttribute('type'); }); it('should not set `href` or `target` if rendered as HTML button', () => { render( , ); const button = screen.getByRole('button'); expect(button).not.toHaveAttribute('href'); expect(button).not.toHaveAttribute('target'); }); }); describe('disabled mode', () => { describe('button', () => { it('should not be disabled by default', () => { render(); const button = screen.getByRole('button'); expect(button).toBeEnabled(); expect(button).not.toHaveAttribute('aria-disabled'); }); it('should respect disabled mode and set it only via the `disabled` attribute', () => { render( , ); const button = screen.getByRole('button'); expect(button).toBeDisabled(); expect(button).not.toHaveAttribute('aria-disabled'); }); }); describe('anchor', () => { it('should not be disabled by default', () => { render( , ); const button = screen.getByRole('link'); expect(button).toBeEnabled(); expect(button).not.toHaveAttribute('aria-disabled'); }); it('should respect disabled mode', () => { render( , ); const button = screen.getByRole('link'); expect(button).toHaveAttribute('aria-disabled'); expect(button).toBeEnabled(); expect(button).not.toHaveAttribute('href'); }); }); }); describe('loading mode', () => { describe('button', () => { it('should not be loading by default', () => { render(); const button = screen.getByRole('button'); expect(button).toBeEnabled(); expect(button).not.toHaveAttribute('aria-busy'); }); it('should respect loading mode', () => { render( , ); const button = screen.getByRole('button'); expect(button).toHaveAttribute('aria-busy'); // the `disabled` attribute is not set to keep the button // focusable but aria attribute is defined to make it // announced properly by the assistive tech expect(button).toBeEnabled(); expect(button).toHaveAttribute('aria-disabled'); }); }); describe('anchor', () => { it('should not be loading by default', () => { render( , ); const button = screen.getByRole('link'); expect(button).toBeEnabled(); expect(button).not.toHaveAttribute('aria-busy'); expect(button).not.toHaveAttribute('aria-disabled'); }); it('should respect loading mode', () => { render( , ); const button = screen.getByRole('link'); expect(button).toHaveAttribute('aria-busy'); // the `disabled` attribute is not set to keep the button // focusable but aria attribute is defined to make it // announced properly by the assistive tech expect(button).toBeEnabled(); expect(button).toHaveAttribute('aria-disabled'); expect(button).not.toHaveAttribute('href'); }); }); describe('content id', () => { it('should not set id for content by default', () => { const { container } = render(); expect(container.querySelector('[id$="_content"]')).not.toBeInTheDocument(); }); it('should set id for content by default if `id` is passed as a button prop', () => { const { container } = render( , ); expect(container.querySelector('#myId_content')).toBeInTheDocument(); }); }); }); }); }); it('does not set type when type is in LegacyButtonType', () => { // @ts-expect-error here we intentionally set wrong value to test `type` attr in DOM const legacyTypeProps: ButtonProps = { ...newProps, type: 'accent' }; render( , ); const button = screen.getByTestId('new-button'); expect(button).toBeInTheDocument(); expect(button).not.toHaveAttribute('type'); }); it('sets type when type is not in LegacyButtonType', () => { const buttonTypeProps: ButtonProps = { ...newProps, type: 'submit', }; render( , ); const button = screen.getByTestId('new-button'); expect(button).toBeInTheDocument(); expect(button).toHaveAttribute('type', 'submit'); }); describe('addons', () => { const avatarProp: CommonProps['addonStart'] = { type: 'avatar', value: [{ asset: }], }; const iconProp: CommonProps['addonStart'] = { type: 'icon', value: }; const renderAllPriorities = (props: CommonProps) => render( <> , ); describe('addonsStart', () => { describe('large', () => { it('should not support avatars', () => { renderAllPriorities({ v2: true, size: 'lg', addonStart: avatarProp, }); expect(screen.queryAllByTestId('freeze-icon')).toHaveLength(0); }); it('should not support icons', () => { renderAllPriorities({ v2: true, size: 'lg', addonStart: iconProp, }); expect(screen.queryAllByTestId('freeze-icon')).toHaveLength(0); }); }); describe('medium', () => { it('should support avatar', () => { renderAllPriorities({ v2: true, size: 'md', addonStart: avatarProp, }); expect(screen.getAllByTestId('freeze-icon')).toHaveLength(4); }); it('should support icon', () => { renderAllPriorities({ v2: true, size: 'md', addonStart: iconProp, }); expect(screen.getAllByTestId('freeze-icon')).toHaveLength(4); }); }); describe('small', () => { it('should not support avatar', () => { renderAllPriorities({ v2: true, size: 'sm', addonStart: avatarProp, }); expect(screen.queryAllByTestId('freeze-icon')).toHaveLength(0); }); it('should support icon', () => { renderAllPriorities({ v2: true, size: 'sm', addonStart: iconProp, }); expect(screen.getAllByTestId('freeze-icon')).toHaveLength(4); }); }); }); describe('addonEnd', () => { describe('large', () => { it('should support icon', () => { renderAllPriorities({ v2: true, size: 'lg', addonEnd: iconProp, }); expect(screen.queryAllByTestId('freeze-icon')).toHaveLength(0); }); }); describe('medium', () => { it('should support icon', () => { renderAllPriorities({ v2: true, size: 'sm', addonEnd: iconProp, }); expect(screen.getAllByTestId('freeze-icon')).toHaveLength(4); }); }); describe('small', () => { it('should support icon', () => { renderAllPriorities({ v2: true, size: 'md', addonEnd: iconProp, }); expect(screen.getAllByTestId('freeze-icon')).toHaveLength(4); }); }); }); }); describe('onClick', () => { it('should call the handler when rendered as HTML button', async () => { const onClick = jest.fn(); render( , ); await userEvent.click(screen.getByRole('button')); expect(onClick).toHaveBeenCalledTimes(1); }); it('should call the handler when rendered as HTML anchor', async () => { const onClick = jest.fn(); render( , ); await userEvent.click(screen.getByRole('link')); expect(onClick).toHaveBeenCalledTimes(1); }); }); });