Skip to content

Commit

Permalink
chore(Dropdown next): add testing (#8640)
Browse files Browse the repository at this point in the history
* add integration tests, issue with keypress tests

* use shared mocks

* fix integration tests

* temporarily remove onOpenChange test

* default behavior tests, small changes
  • Loading branch information
Dominik-Petrik authored Feb 2, 2023
1 parent d557fbd commit c29744e
Show file tree
Hide file tree
Showing 20 changed files with 698 additions and 0 deletions.
12 changes: 12 additions & 0 deletions packages/react-core/src/components/Menu/__mocks__/Menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { MenuProps } from '../Menu';

export const Menu = ({ className, isPlain, isScrollable, style, onSelect, ...props }: MenuProps) => (
<>
<div className={className} data-testid="menu-mock" {...props}></div>
<div onClick={onSelect}>{'Mock item'}</div>
<p>{`isPlain: ${isPlain}`}</p>
<p>{`isScrollable: ${isScrollable}`}</p>
<p>{`minWidth: ${style?.['--pf-c-menu--MinWidth']}`}</p>
</>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import React from 'react';

export const MenuContent = ({ children }) => <div>{children}</div>;
12 changes: 12 additions & 0 deletions packages/react-core/src/components/Menu/__mocks__/MenuGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { MenuGroupProps } from '../MenuGroup';

export const MenuGroup = ({ className, children, label, labelHeadingLevel }: MenuGroupProps) => (
<>
<div className={className} data-testid="menu-group-mock">
{children}
</div>
<p>{`label: ${label}`}</p>
<p>{`labelHeadingLevel: ${labelHeadingLevel}`}</p>
</>
);
12 changes: 12 additions & 0 deletions packages/react-core/src/components/Menu/__mocks__/MenuItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { MenuItemProps } from '../MenuItem';

export const MenuItem = ({ className, children, description, itemId }: MenuItemProps) => (
<>
<div className={className} data-testid="menu-item-mock">
{children}
</div>
<p>{`description: ${description}`}</p>
<p>{`itemId: ${itemId}`}</p>
</>
);
10 changes: 10 additions & 0 deletions packages/react-core/src/components/Menu/__mocks__/MenuList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import { MenuListProps } from '../MenuList';

export const MenuList = ({ className, children }: MenuListProps) => (
<>
<div className={className} data-testid="menu-list-mock">
{children}
</div>
</>
);
5 changes: 5 additions & 0 deletions packages/react-core/src/components/Menu/__mocks__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './Menu';
export * from './MenuContent';
export * from './MenuGroup';
export * from './MenuItem';
export * from './MenuList';
11 changes: 11 additions & 0 deletions packages/react-core/src/helpers/Popper/__mocks__/Popper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import { PopperProps } from '../Popper';

export const Popper = ({ popper, zIndex, isVisible, trigger }: PopperProps) => (
<>
<div>{popper}</div>
<p>{`zIndex: ${zIndex}`}</p>
<p>{`isOpen: ${isVisible}`}</p>
<div>{trigger}</div>
</>
);
1 change: 1 addition & 0 deletions packages/react-core/src/helpers/Popper/__mocks__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Popper';
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import React from 'react';
import { Dropdown } from '../../Dropdown';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

jest.mock('../../../../components/Menu');

jest.mock('../../../../helpers/Popper/Popper');

const toggle = (ref: React.RefObject<any>) => <button ref={ref}>Dropdown</button>;

const dropdownChildren = <div>Dropdown children</div>;

test('renders dropdown', () => {
render(
<div data-testid="dropdown">
<Dropdown toggle={toggleRef => toggle(toggleRef)}>{dropdownChildren}</Dropdown>
</div>
);

expect(screen.getByTestId('dropdown').children[0]).toBeVisible();
});

test('passes children', () => {
render(<Dropdown toggle={toggleRef => toggle(toggleRef)}>{dropdownChildren}</Dropdown>);

expect(screen.getByText('Dropdown children')).toBeVisible();
});

test('renders passed toggle element', () => {
render(<Dropdown toggle={toggleRef => toggle(toggleRef)}>{dropdownChildren}</Dropdown>);

expect(screen.getByRole('button', { name: 'Dropdown' })).toBeVisible();
});

test('passes no class name by default', () => {
render(
<Dropdown isOpen={true} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

expect(screen.getByTestId('menu-mock')).not.toHaveClass();
});

test('passes custom class name', () => {
render(
<Dropdown className="custom-class" isOpen={true} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

expect(screen.getByTestId('menu-mock')).toHaveClass('custom-class');
});

test('does not pass isPlain to Menu by default', () => {
render(
<Dropdown isOpen={true} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

expect(screen.getByText('isPlain: undefined')).toBeVisible();
});

test('passes isPlain to Menu', () => {
render(
<Dropdown isPlain isOpen={true} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

expect(screen.getByText('isPlain: true')).toBeVisible();
});

test('does not pass isScrollable to Menu by default', () => {
render(
<Dropdown isOpen={true} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

expect(screen.getByText('isScrollable: undefined')).toBeVisible();
});

test('passes isScrollable to Menu', () => {
render(
<Dropdown isScrollable isOpen={true} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

expect(screen.getByText('isScrollable: true')).toBeVisible();
});

test('does not pass minWidth to Menu by default', () => {
render(
<Dropdown isOpen={true} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

expect(screen.getByText('minWidth: undefined')).toBeVisible();
});

test('passes minWidth to Menu', () => {
render(
<Dropdown minWidth="100px" isOpen={true} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

expect(screen.getByText('minWidth: 100px')).toBeVisible();
});

test('passes default zIndex to popper', () => {
render(<Dropdown toggle={toggleRef => toggle(toggleRef)}>{dropdownChildren}</Dropdown>);

expect(screen.getByText('zIndex: 9999')).toBeVisible();
});

test('passes zIndex to popper', () => {
render(
<Dropdown zIndex={100} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

expect(screen.getByText('zIndex: 100')).toBeVisible();
});

test('does not pass isOpen to popper by default', () => {
render(<Dropdown toggle={toggleRef => toggle(toggleRef)}>{dropdownChildren}</Dropdown>);

expect(screen.getByText('isOpen: undefined')).toBeVisible();
});

test('passes isOpen to popper', () => {
render(
<Dropdown isOpen toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

expect(screen.getByText('isOpen: true')).toBeVisible();
});

/* no default tests for callback props
since there is no way to test that the
function doesn`t get passed */

test('passes onSelect callback', async () => {
const user = userEvent.setup();

const onSelect = jest.fn();
render(
<Dropdown onSelect={onSelect} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

const trigger = await screen.findByText('Mock item');
await user.click(trigger);

expect(onSelect).toBeCalledTimes(1);
});

test('onOpenChange is called when passed and user clicks outside of dropdown', async () => {
const user = userEvent.setup();
const onOpenChange = jest.fn();

render(
<Dropdown isOpen={true} onOpenChange={onOpenChange} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

const dropdown = screen.getByRole('button', { name: 'Dropdown' });
await user.click(dropdown);
await user.click(document.body);

expect(onOpenChange).toBeCalledTimes(1);
});

test('onOpenChange is called when passed and user presses tab key', async () => {
const user = userEvent.setup();
const onOpenChange = jest.fn();

render(
<Dropdown isOpen={true} onOpenChange={onOpenChange} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

//focus dropdown
const dropdown = screen.getByRole('button', { name: 'Dropdown' });
await user.click(dropdown);
await user.keyboard('{Tab}');

expect(onOpenChange).toBeCalledTimes(1);
});

test('onOpenChange is called when passed and user presses esc key', async () => {
const user = userEvent.setup();
const onOpenChange = jest.fn();

render(
<Dropdown isOpen={true} onOpenChange={onOpenChange} toggle={toggleRef => toggle(toggleRef)}>
{dropdownChildren}
</Dropdown>
);

//focus dropdown
const dropdown = screen.getByRole('button', { name: 'Dropdown' });
await user.click(dropdown);
await user.keyboard('{Escape}');

expect(onOpenChange).toBeCalledTimes(1);
});

test('match snapshot', () => {
const { asFragment } = render(
<Dropdown
ouiaId={'dropdown'}
isOpen
isScrollable
isPlain
className={'customClass'}
toggle={toggleRef => toggle(toggleRef)}
>
{dropdownChildren}
</Dropdown>
);

expect(asFragment()).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { DropdownGroup } from '../../Dropdown';
import { render, screen } from '@testing-library/react';
import React from 'react';

jest.mock('../../../../components/Menu');

const dropdownGroupChildren = <div>Dropdown Group children</div>;

test('renders dropdown group', () => {
render(
<div data-testid="dropdown-group">
<DropdownGroup>{dropdownGroupChildren}</DropdownGroup>
</div>
);

expect(screen.getByTestId('dropdown-group').children[0]).toBeVisible();
});

test('passes children', () => {
render(<DropdownGroup>{dropdownGroupChildren}</DropdownGroup>);

expect(screen.getByText('Dropdown Group children')).toBeVisible();
});

test('passes no class name by default', () => {
render(<DropdownGroup>{dropdownGroupChildren}</DropdownGroup>);

expect(screen.getByTestId('menu-group-mock')).not.toHaveClass();
});

test('passes custom class name to MenuGroup', () => {
render(<DropdownGroup className="custom-class">{dropdownGroupChildren}</DropdownGroup>);

expect(screen.getByTestId('menu-group-mock')).toHaveClass('custom-class');
});

test('passes no label by default', () => {
render(<DropdownGroup>{dropdownGroupChildren}</DropdownGroup>);

expect(screen.getByText('label: undefined')).toBeVisible();
});

test('passes custom label to MenuGroup', () => {
render(<DropdownGroup label="Test label">{dropdownGroupChildren}</DropdownGroup>);

expect(screen.getByText('label: Test label')).toBeVisible();
});

test('passes h1 as labelHeadingLevel to MenuGroup by default', () => {
render(<DropdownGroup>{dropdownGroupChildren}</DropdownGroup>);

expect(screen.getByText('labelHeadingLevel: h1')).toBeVisible();
});

test('passes custom labelHeadingLevel to MenuGroup', () => {
render(<DropdownGroup labelHeadingLevel="h2">{dropdownGroupChildren}</DropdownGroup>);

expect(screen.getByText('labelHeadingLevel: h2')).toBeVisible();
});

test('matches snapshot', () => {
const { asFragment } = render(
<DropdownGroup className="custom-class" label="Test label" labelHeadingLevel="h2">
{dropdownGroupChildren}
</DropdownGroup>
);

expect(asFragment()).toMatchSnapshot();
});
Loading

0 comments on commit c29744e

Please sign in to comment.