box-ui-elements
Version:
Box UI Elements
416 lines (350 loc) • 15.3 kB
JavaScript
import * as React from 'react';
import { MemoryRouter, Router } from 'react-router-dom';
// Using fireEvent for all click interactions instead of userEvent because
// userEvent.pointer with right-click doesn't reliably trigger onClick handlers
import { render, screen, fireEvent } from '../../../test-utils/testing-library';
import SidebarNavButton from '../SidebarNavButton';
describe('elements/content-sidebar/SidebarNavButton', () => {
beforeEach(() => {
jest.clearAllMocks();
});
const renderWrapper = ({ children, ref, ...props }, path = '/') =>
render(
<MemoryRouter initialEntries={[path]}>
<SidebarNavButton ref={ref} {...props}>
{children}
</SidebarNavButton>
</MemoryRouter>,
);
test('should render nav button properly', () => {
renderWrapper({
tooltip: 'foo',
sidebarView: 'activity',
children: <span>test button</span>,
});
const button = screen.getByRole('tab');
expect(button).toHaveAttribute('aria-label', 'foo');
expect(button).toHaveAttribute('aria-selected', 'false');
expect(button).toHaveAttribute('aria-controls', 'activity-content');
expect(button).toHaveAttribute('role', 'tab');
expect(button).toHaveAttribute('tabindex', '-1');
expect(button).toHaveAttribute('type', 'button');
expect(button).toHaveAttribute('id', 'activity');
expect(button).toHaveClass('bcs-NavButton');
expect(button).not.toHaveClass('bcs-is-selected');
expect(button).toHaveTextContent('test button');
});
test.each`
isOpen | expected
${true} | ${true}
${false} | ${false}
${undefined} | ${false}
`('should render nav button properly when selected with the sidebar open or closed', ({ expected, isOpen }) => {
const props = {
isOpen,
sidebarView: 'activity',
tooltip: 'foo',
children: <span>test button</span>,
};
renderWrapper(props, '/activity');
const button = screen.getByRole('tab');
if (expected) {
expect(button).toHaveClass('bcs-is-selected');
expect(button).toHaveAttribute('aria-selected', 'true');
expect(button).toHaveAttribute('tabindex', '0');
} else {
expect(button).not.toHaveClass('bcs-is-selected');
expect(button).toHaveAttribute('aria-selected', 'false');
expect(button).toHaveAttribute('tabindex', '-1');
}
});
test.each`
path | expected
${'/'} | ${false}
${'/activity'} | ${true}
${'/activity/'} | ${true}
${'/activity/test'} | ${true}
${'/skills'} | ${false}
`('should reflect active state ($expected) correctly based on active path', ({ expected, path }) => {
renderWrapper(
{
isOpen: true,
sidebarView: 'activity',
tooltip: 'foo',
children: <span>test button</span>,
},
path,
);
const button = screen.getByRole('tab');
if (expected) {
expect(button).toHaveClass('bcs-is-selected');
expect(button).toHaveAttribute('aria-selected', 'true');
expect(button).toHaveAttribute('tabindex', '0');
} else {
expect(button).not.toHaveClass('bcs-is-selected');
expect(button).toHaveAttribute('aria-selected', 'false');
expect(button).toHaveAttribute('tabindex', '-1');
}
});
test('should call onClick with sidebarView when clicked', () => {
const mockOnClick = jest.fn();
const mockSidebarView = 'activity';
renderWrapper({
onClick: mockOnClick,
sidebarView: mockSidebarView,
tooltip: 'test',
children: <span>button</span>,
});
const button = screen.getByText('button');
fireEvent.click(button);
expect(mockOnClick).toBeCalledWith(mockSidebarView);
});
test.each`
isDisabled | expected
${true} | ${true}
${false} | ${false}
${undefined} | ${false}
`('should apply bdl-is-disabled class when isDisabled is $isDisabled', ({ isDisabled, expected }) => {
renderWrapper({
isDisabled,
sidebarView: 'activity',
tooltip: 'Activity',
children: <span>Activity</span>,
});
const button = screen.getByRole('tab');
if (expected) {
expect(button).toHaveClass('bdl-is-disabled');
} else {
expect(button).not.toHaveClass('bdl-is-disabled');
}
});
test.each`
elementId | sidebarView | expectedId | expectedAriaControls
${undefined} | ${'activity'} | ${'activity'} | ${'activity-content'}
${''} | ${'activity'} | ${'activity'} | ${'activity-content'}
${'sidebar'} | ${'activity'} | ${'sidebar_activity'} | ${'sidebar_activity-content'}
${'main'} | ${'skills'} | ${'main_skills'} | ${'main_skills-content'}
`(
'should generate correct id and aria-controls with elementId=$elementId and sidebarView=$sidebarView',
({ elementId, sidebarView, expectedId, expectedAriaControls }) => {
renderWrapper({
elementId,
sidebarView,
tooltip: 'test',
children: <span>test button</span>,
});
const button = screen.getByRole('tab');
expect(button).toHaveAttribute('id', expectedId);
expect(button).toHaveAttribute('aria-controls', expectedAriaControls);
},
);
test('should forward ref to the Button', () => {
const ref = React.createRef();
renderWrapper({
ref,
sidebarView: 'activity',
tooltip: 'test',
children: <span>test button</span>,
});
const button = screen.getByRole('tab');
expect(ref.current).toBe(button);
});
describe('navigation on click', () => {
const mockHistoryPush = jest.fn();
const mockHistoryReplace = jest.fn();
const mockHistory = {
push: mockHistoryPush,
replace: mockHistoryReplace,
listen: jest.fn(),
location: { pathname: '/activity' },
};
const renderWithRouter = (props, history = mockHistory) => {
return render(
<Router history={history}>
<SidebarNavButton sidebarView="activity" tooltip="test" {...props}>
<span>Activity</span>
</SidebarNavButton>
</Router>,
);
};
test('calls onClick handler and history.push on left click when not exact match', () => {
const mockOnClick = jest.fn();
const mockHistoryWithDifferentPath = {
...mockHistory,
location: { pathname: '/activity/versions' },
};
renderWithRouter({ onClick: mockOnClick }, mockHistoryWithDifferentPath);
const button = screen.getByText('Activity');
fireEvent.click(button);
expect(mockOnClick).toBeCalledWith('activity');
expect(mockHistoryPush).toBeCalledWith({
pathname: '/activity',
state: { open: true },
});
expect(mockHistoryReplace).not.toBeCalled();
});
test('calls history.replace on left click when exact match', () => {
const mockOnClick = jest.fn();
renderWithRouter({ onClick: mockOnClick });
const button = screen.getByText('Activity');
fireEvent.click(button);
expect(mockOnClick).toBeCalledWith('activity');
expect(mockHistoryReplace).toBeCalledWith({
pathname: '/activity',
state: { open: true },
});
expect(mockHistoryPush).not.toBeCalled();
});
test('does not call history.push on right click', () => {
const mockOnClick = jest.fn();
renderWithRouter({ onClick: mockOnClick });
const button = screen.getByText('Activity');
fireEvent.click(button, { button: 1 });
expect(mockOnClick).toBeCalledWith('activity');
expect(mockHistoryPush).not.toBeCalled();
expect(mockHistoryReplace).not.toBeCalled();
});
test('does not call history.push on prevented event', () => {
const mockOnClick = jest.fn();
renderWithRouter({ onClick: mockOnClick });
const button = screen.getByText('Activity');
button.addEventListener('click', e => e.preventDefault());
fireEvent.click(button, { button: 0 });
expect(mockOnClick).toBeCalledWith('activity');
expect(mockHistoryPush).not.toBeCalled();
expect(mockHistoryReplace).not.toBeCalled();
});
});
});
describe('elements/content-sidebar/SidebarNavButton - Router Disabled', () => {
beforeEach(() => {
jest.clearAllMocks();
});
const defaultProps = {
routerDisabled: true,
tooltip: 'foo',
sidebarView: 'activity',
internalSidebarNavigation: { sidebar: 'skills' },
};
const renderWithoutRouter = ({ children = <span>test button</span>, ref, ...props }) =>
render(
<SidebarNavButton ref={ref} {...defaultProps} {...props}>
{children}
</SidebarNavButton>,
);
test('should render nav button properly', () => {
renderWithoutRouter({});
const button = screen.getByRole('tab');
expect(button).toHaveAttribute('aria-label', 'foo');
expect(button).toHaveAttribute('aria-selected', 'false');
expect(button).toHaveAttribute('aria-controls', 'activity-content');
expect(button).toHaveAttribute('role', 'tab');
expect(button).toHaveAttribute('tabindex', '-1');
expect(button).toHaveAttribute('type', 'button');
expect(button).toHaveAttribute('id', 'activity');
expect(button).toHaveClass('bcs-NavButton');
expect(button).not.toHaveClass('bcs-is-selected');
expect(button).toHaveTextContent('test button');
});
test.each`
internalSidebarNavigation | expected
${null} | ${false}
${undefined} | ${false}
${{ sidebar: 'skills' }} | ${false}
${{ sidebar: 'activity' }} | ${true}
${{ sidebar: 'activity', versionId: '123' }} | ${true}
`(
'should reflect active state ($expected) correctly based on internal navigation',
({ expected, internalSidebarNavigation }) => {
renderWithoutRouter({
internalSidebarNavigation,
isOpen: true,
});
const button = screen.getByRole('tab');
if (expected) {
expect(button).toHaveClass('bcs-is-selected');
expect(button).toHaveAttribute('aria-selected', 'true');
expect(button).toHaveAttribute('tabindex', '0');
} else {
expect(button).not.toHaveClass('bcs-is-selected');
expect(button).toHaveAttribute('aria-selected', 'false');
expect(button).toHaveAttribute('tabindex', '-1');
}
},
);
test('should call onClick with sidebarView when clicked', () => {
const mockOnClick = jest.fn();
const mockSidebarView = 'activity';
renderWithoutRouter({
onClick: mockOnClick,
sidebarView: mockSidebarView,
});
const button = screen.getByRole('tab');
fireEvent.click(button);
expect(mockOnClick).toBeCalledWith(mockSidebarView);
});
describe('navigation on click', () => {
const mockInternalSidebarNavigationHandler = jest.fn();
test('calls onClick handler and internalSidebarNavigationHandler with replace=false when not exact match', () => {
const mockOnClick = jest.fn();
renderWithoutRouter({
onClick: mockOnClick,
internalSidebarNavigation: { sidebar: 'activity', versionId: '123' },
internalSidebarNavigationHandler: mockInternalSidebarNavigationHandler,
});
const button = screen.getByRole('tab');
fireEvent.click(button);
expect(mockOnClick).toBeCalledWith('activity');
expect(mockInternalSidebarNavigationHandler).toBeCalledWith(
{
sidebar: 'activity',
open: true,
},
false,
);
});
test('calls internalSidebarNavigationHandler with replace=true when exact match', () => {
const mockOnClick = jest.fn();
renderWithoutRouter({
onClick: mockOnClick,
internalSidebarNavigation: { sidebar: 'activity' },
internalSidebarNavigationHandler: mockInternalSidebarNavigationHandler,
});
const button = screen.getByRole('tab');
fireEvent.click(button);
expect(mockOnClick).toBeCalledWith('activity');
expect(mockInternalSidebarNavigationHandler).toBeCalledWith(
{
sidebar: 'activity',
open: true,
},
true,
);
});
test('does not call internalSidebarNavigationHandler on right click', () => {
const mockOnClick = jest.fn();
renderWithoutRouter({
onClick: mockOnClick,
internalSidebarNavigation: { sidebar: 'activity' },
internalSidebarNavigationHandler: mockInternalSidebarNavigationHandler,
});
const button = screen.getByRole('tab');
fireEvent.click(button, { button: 1 });
expect(mockOnClick).toBeCalledWith('activity');
expect(mockInternalSidebarNavigationHandler).not.toBeCalled();
});
test('does not call internalSidebarNavigationHandler on prevented event', () => {
const mockOnClick = jest.fn();
renderWithoutRouter({
onClick: mockOnClick,
internalSidebarNavigation: { sidebar: 'activity' },
internalSidebarNavigationHandler: mockInternalSidebarNavigationHandler,
});
const button = screen.getByRole('tab');
button.addEventListener('click', e => e.preventDefault());
fireEvent.click(button, { button: 0 });
expect(mockOnClick).toBeCalledWith('activity');
expect(mockInternalSidebarNavigationHandler).not.toBeCalled();
});
});
});