import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { axe } from 'jest-axe';
import { Spinner } from '@leafygreen-ui/loading-indicator';
import { getTestUtils } from '../testing';
import { ButtonProps } from '../types';
import Button from '..';
const className = 'test-button-class';
const title = 'Test button title';
const child = 'Button child';
function renderButton(props: ButtonProps = {}) {
const utils = render(
// @ts-ignore React17 types
,
);
const { getButton, isDisabled } = getTestUtils();
const button = getButton();
return { ...utils, button, isDisabled };
}
describe('packages/button', () => {
describe('a11y', () => {
test('does not have basic accessibility issues when rendered as a button', async () => {
const { container } = renderButton({ children: child });
const results = await axe(container);
expect(results).toHaveNoViolations();
});
test('does not have basic accessibility issues when rendered as an anchor tag', async () => {
const { container } = renderButton({
href: 'https://mongodb.design',
children: child,
});
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});
describe('rendering', () => {
test(`renders "${className}" in the component's markup`, () => {
const { button } = renderButton({
className,
});
expect(button.closest(`.${className}`)).toBeVisible();
});
test(`renders "${child}" as the button's textContent`, () => {
const { button } = renderButton({
children: child,
});
expect(button.textContent).toBe(child);
});
test(`renders spinner when isLoading is true`, () => {
const { getByTestId } = renderButton({
isLoading: true,
loadingIndicator: ,
});
expect(getByTestId('lg-button-spinner')).toBeVisible();
});
test(`does not loadingText when isLoading is false`, () => {
const loadingText = 'loading text';
const { queryByText } = renderButton({
isLoading: false,
loadingText,
});
expect(queryByText(loadingText)).toBeNull();
});
test(`renders loadingText when isLoading is true`, () => {
const loadingText = 'loading text';
const { getByText } = renderButton({
isLoading: true,
loadingIndicator: ,
loadingText,
});
expect(getByText(loadingText)).toBeVisible();
});
test(`renders "${title}" as the button title`, () => {
const { button } = renderButton({
title,
});
expect(button.title).toBe(title);
});
test(`renders with aria-disabled attribute but not disabled attribute when disabled prop is set`, () => {
const { button, isDisabled } = renderButton({
disabled: true,
});
expect(isDisabled()).toBeTruthy();
expect(button.getAttribute('disabled')).toBeFalsy();
});
test(`renders a button with the "button" type by default`, () => {
const { button } = renderButton();
expect((button as HTMLButtonElement).type).toBe('button');
});
test(`renders a button with the given type when one is set`, () => {
const { button } = renderButton({
type: 'submit',
});
expect((button as HTMLButtonElement).type).toBe('submit');
});
test(`renders inside of a \`button\` tag by default`, () => {
const { button } = renderButton();
expect(button.tagName.toLowerCase()).toBe('button');
});
test(`renders component inside of an \`a\` tag when "href" prop is set`, () => {
const { button } = renderButton({
href: 'http://mongodb.design',
});
expect(button.tagName.toLowerCase()).toBe('a');
});
test(`renders component inside of \`button\` tag when "href" prop is undefined`, () => {
const { button } = renderButton({
// @ts-expect-error - by identifying an `href` property, TS expects it to be a string
href: undefined,
});
expect(button.tagName.toLowerCase()).toBe('button');
});
test(`renders a button as another HTML element if the "as" prop is set`, () => {
const { container, button } = renderButton({
as: 'div',
});
expect(container.querySelector('button')).not.toBeInTheDocument();
expect(button.tagName.toLowerCase()).toBe('div');
});
test(`does not render the disabled attribute for a disabled link`, () => {
const { button, isDisabled } = renderButton({
href: 'http://mongodb.design',
disabled: true,
});
expect(button).not.toHaveAttribute('disabled');
expect(isDisabled()).toBe(true);
});
test('renders additional attributes not explicitly defined in props', () => {
const { button } = renderButton({
tabIndex: 0,
});
expect(button).toHaveAttribute('tabindex', '0');
});
});
describe('interaction', () => {
test('fires the onClick handler once when clicked', () => {
const onClick = jest.fn();
const { button } = renderButton({
onClick,
});
fireEvent.click(button);
expect(onClick).toHaveBeenCalledTimes(1);
});
test('does not fire onClick handler when disabled', () => {
const onClick = jest.fn();
const { button } = renderButton({
disabled: true,
onClick,
});
fireEvent.click(button);
expect(onClick).toHaveBeenCalledTimes(0);
});
test('does not fire onClick handler with ENTER key after focusing when disabled', () => {
const onClick = jest.fn();
const { button } = renderButton({
disabled: true,
onClick,
});
button.focus();
userEvent.keyboard('[Enter]');
expect(onClick).toHaveBeenCalledTimes(0);
});
test('does not fire onClick handler with SPACE bar after focusing when disabled', () => {
const onClick = jest.fn();
const { button } = renderButton({
disabled: true,
onClick,
});
button.focus();
userEvent.keyboard('[Space]');
expect(onClick).toHaveBeenCalledTimes(0);
});
test('does not fire onClick handler when loading', () => {
const onClick = jest.fn();
const { button } = renderButton({
isLoading: true,
onClick,
});
fireEvent.click(button);
expect(onClick).toHaveBeenCalledTimes(0);
});
test('does not fire onClick handler on disabled anchor', () => {
const onClick = jest.fn();
const { button } = renderButton({
disabled: true,
as: 'a',
onClick,
});
fireEvent.click(button);
expect(onClick).toHaveBeenCalledTimes(0);
});
test('does not fire onClick handler on disabled anchor set by "href"', () => {
const onClick = jest.fn();
const href = 'https://mongodb.design';
const { button } = renderButton({
disabled: true,
href,
onClick,
});
fireEvent.click(button);
expect(onClick).toHaveBeenCalledTimes(0);
});
test('href attribute exists on a link', () => {
const href = 'https://mongodb.design';
const { button } = renderButton({
href,
});
expect(button).toHaveAttribute('href', href);
});
test('does not invoke a forms submit handler when disabled', () => {
const onSubmit = jest.fn();
render(
,
);
const button = screen.getByRole('button');
fireEvent.click(button);
expect(onSubmit).not.toHaveBeenCalled();
});
});
/* eslint-disable jest/no-disabled-tests*/
describe.skip('types behave as expected', () => {
test('does not throw an error when no children are passed to the component', () => {