Skip to content

Commit

Permalink
feat: Add data-testid to components. Closes aws#444 (aws#467)
Browse files Browse the repository at this point in the history
  • Loading branch information
jessieweiyi authored Dec 17, 2021
1 parent b66ca3f commit d7244a1
Show file tree
Hide file tree
Showing 111 changed files with 966 additions and 256 deletions.
5 changes: 5 additions & 0 deletions src/components/Alert/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ describe('Alert', () => {

afterEach(cleanup);

it('can be accessed by custom test-id', () => {
const { getByTestId } = render(<Alert type="info" data-testid="info-alert" />);
expect(getByTestId('info-alert')).toBeInTheDocument();
});

alertTypes.forEach((alertType) => {
describe(`with the ${alertType} type`, () => {
it(`should render the ${alertType} styled Alert`, () => {
Expand Down
4 changes: 3 additions & 1 deletion src/components/Alert/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,10 @@ const Alert: FunctionComponent<AlertProps> = ({
[renderAlertBody, mapProps]
);

const testId = props['data-testid'] || props.type;

return (
<Box data-testid={props.type} width="100%">
<Box data-testid={testId} width="100%">
{visible && show && renderAlert(props)}
</Box>
);
Expand Down
7 changes: 7 additions & 0 deletions src/components/Autosuggest/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ describe('Autosuggest', () => {
expect(getByPlaceholderText('input-1')).toHaveValue(preSelectedValue.label);
});

it('can be accessed by custom test-id', () => {
const { getByTestId } = render(
<Autosuggest options={awsServices} placeholder="input-1" data-testid="autosuggest-1" />
);
expect(getByTestId('autosuggest-1')).toBeInTheDocument();
});

describe('Custom Label', () => {
const customOptions = [
{
Expand Down
4 changes: 2 additions & 2 deletions src/components/Autosuggest/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -366,12 +366,12 @@ const AutosuggestBase: FunctionComponent<AutosuggestBaseProps> = ({
},
[inputValue, setInputValue, props]
);

const testId = props['data-testid'] || (props.multiple ? 'multiselect' : 'autosuggest');
return (
<Stack>
<MaterialUIAutocomplete
multiple={props.multiple}
data-testid={props.multiple ? 'multiselect' : 'autosuggest'}
data-testid={testId}
disabled={disabled}
autoHighlight
popupIcon={null}
Expand Down
5 changes: 5 additions & 0 deletions src/components/Badge/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ describe('Badge', () => {
expect(getByText('0')).toBeInTheDocument();
});

it('can be accessed by testid', async () => {
const { getByTestId } = render(<Badge content="some text" data-testid="badge"></Badge>);
expect(getByTestId('badge')).toBeInTheDocument();
});

it('renders accessible component', async () => {
const { container } = render(<Badge content="some text"></Badge>);
const results = await axe(container);
Expand Down
4 changes: 2 additions & 2 deletions src/components/Badge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ export interface BadgeProps {
/**
* A badge is a small color-coded visual element, containing letters or numbers, that you can use to label, categorize or organize items.
*/
const Badge: FunctionComponent<BadgeProps> = ({ color = 'grey', content }) => {
const Badge: FunctionComponent<BadgeProps> = ({ color = 'grey', content, ...props }) => {
const classes = useStyles();
return <Chip className={classes[color]} label={content} />;
return <Chip className={classes[color]} label={content} data-testid={props['data-testid']} />;
};

export default Badge;
8 changes: 5 additions & 3 deletions src/components/BreadcrumbGroup/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ import BreadcrumbGroup from '.';

describe('BreadcrumbGroup', () => {
it('renders the route when provided no items', () => {
const { getByText } = render(
const { getByText, getByTestId } = render(
<MemoryRouter initialEntries={['/current/route/']}>
<BreadcrumbGroup />
</MemoryRouter>
);
expect(getByText('Current')).toBeInTheDocument();
expect(getByText('Route')).toBeInTheDocument();
expect(getByTestId('breadcrumb')).toBeInTheDocument();
});

it('preserves the casing of links when provided no items', () => {
Expand All @@ -50,14 +51,15 @@ describe('BreadcrumbGroup', () => {
{ text: 'first', href: '#first' },
{ text: 'second', href: '#second' },
];
const { getByText, getAllByRole } = render(
const { getByText, getAllByRole, getByTestId } = render(
<MemoryRouter>
<BreadcrumbGroup items={items} />
<BreadcrumbGroup items={items} data-testid="breadcrumb-1" />
</MemoryRouter>
);
expect(getByText('first')).toBeInTheDocument();
expect(getByText('second')).toBeInTheDocument();
expect(getAllByRole('listitem')).toHaveLength(2);
expect(getByTestId('breadcrumb-1')).toBeInTheDocument();
});

it('renders the links', () => {
Expand Down
15 changes: 12 additions & 3 deletions src/components/BreadcrumbGroup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ const matchRoute = (path: string, availableRoutes: RouteProps[]) => {
/**
* The Breadcrumb group is rendered as part of AppLayout on top of main content area to provide page navigation information.
*/
const BreadcrumbGroup = ({ items, rootPath = 'Home', availableRoutes = [] }: BreadcrumbGroupProps) => {
const BreadcrumbGroup = ({ items, rootPath = 'Home', availableRoutes = [], ...props }: BreadcrumbGroupProps) => {
const testId = props['data-testid'] || 'breadcrumb';
if (!items) {
return (
<Route>
Expand All @@ -65,7 +66,11 @@ const BreadcrumbGroup = ({ items, rootPath = 'Home', availableRoutes = [] }: Bre
pathnames.unshift('/');

return (
<MaterialBreadcrumbs separator={<NavigateNextIcon fontSize="small" />} aria-label="Breadcrumb">
<MaterialBreadcrumbs
separator={<NavigateNextIcon fontSize="small" />}
aria-label="Breadcrumb"
data-testid={testId}
>
{pathnames.map((value: string, index: number) => {
const last = index === pathnames.length - 1;
const segment = pathnames.slice(0, index + 1);
Expand Down Expand Up @@ -98,7 +103,11 @@ const BreadcrumbGroup = ({ items, rootPath = 'Home', availableRoutes = [] }: Bre

return (
<Route>
<MaterialBreadcrumbs separator={<NavigateNextIcon fontSize="small" />} aria-label="Breadcrumb">
<MaterialBreadcrumbs
separator={<NavigateNextIcon fontSize="small" />}
aria-label="Breadcrumb"
data-testid={testId}
>
{items.map((item: BreadcrumbGroupItem, idx: number) =>
idx === items.length - 1 ? (
<Typography key={item.text} color="textPrimary">
Expand Down
14 changes: 14 additions & 0 deletions src/components/Button/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ describe('Button', () => {
expect(getByRole('button')).toHaveAttribute('aria-label', props.label);
});

it('can be accessed by custom test-id', () => {
const { getByTestId } = render(<Button data-testid="test-button">test</Button>);
expect(getByTestId('test-button')).toBeInTheDocument();
});

describe('when loading', () => {
it('disables button and renders a loader without icon from props ', () => {
const props = { loading: true, variant, icon: 'add_plus' as ButtonIconType };
Expand Down Expand Up @@ -72,6 +77,15 @@ describe('Button', () => {
expect(results).toHaveNoViolations();
});

it('can be accessed by custom test-id', () => {
const { getByTestId } = render(
<Button {...props} data-testid="test-icon-button">
test
</Button>
);
expect(getByTestId('test-icon-button')).toBeInTheDocument();
});

describe('when loading', () => {
it('disables button and renders loader icon instead of icon from prop', () => {
const props = { loading: true, variant: 'icon' as const, icon: 'add_plus' as ButtonIconType };
Expand Down
10 changes: 9 additions & 1 deletion src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,21 @@ const Button: FunctionComponent<ButtonProps> = ({
children,
type = 'button',
size = 'medium',
...props
}) => {
const styles = useStyles({});
const isDisabled = useMemo(() => disabled || loading, [disabled, loading]);

switch (variant) {
case 'icon':
return (
<IconButton aria-label={label} disabled={isDisabled} onClick={onClick} className={styles.iconButton}>
<IconButton
{...props}
aria-label={label}
disabled={isDisabled}
onClick={onClick}
className={styles.iconButton}
>
{loading ? (
<CircularProgress size={14} className={styles.loadingIcon} />
) : (
Expand All @@ -157,6 +164,7 @@ const Button: FunctionComponent<ButtonProps> = ({
default:
return (
<MaterialButton
{...props}
{...muiButtonProps({
disabled: isDisabled,
onClick,
Expand Down
13 changes: 13 additions & 0 deletions src/components/ButtonDropdown/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import ButtonDropdown from '.';
import { axe } from 'jest-axe';
import { act } from 'react-test-renderer';

describe('ButtonDropdown', () => {
beforeEach(() => jest.clearAllMocks());
Expand Down Expand Up @@ -81,6 +82,18 @@ describe('ButtonDropdown', () => {
expect(handleClickMock).toBeCalled();
});

it('can be accessed by custom test-id', () => {
const props = { content: 'the content', items: [{ text: 'the item' }] };
const { getByTestId } = render(<ButtonDropdown {...props} data-testid="button-dropdown-1" />);
expect(getByTestId('button-dropdown-1')).toBeInTheDocument();

act(() => {
fireEvent.click(getByTestId('button-dropdown-1-button'));
});

expect(getByTestId('button-dropdown-1-menu')).toBeInTheDocument();
});

it('renders accessible component', async () => {
const { container } = render(<ButtonDropdown content="some content" />);
const results = await axe(container);
Expand Down
13 changes: 10 additions & 3 deletions src/components/ButtonDropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import clsx from 'clsx';
import { v4 as uuidv4 } from 'uuid';
import Box from '../../layouts/Box';
import Button from '../Button';
import useUniqueId from '../../hooks/useUniqueId';

export interface ButtonDropdownItem {
/**
Expand Down Expand Up @@ -138,10 +139,14 @@ const ButtonDropdown: FunctionComponent<ButtonDropdownProps> = ({
menuItemClassName,
darkTheme,
onClick,
...props
}) => {
const classes = useStyles();
const menuId = useUniqueId();
const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);

const testId = props['data-testid'] || 'button-dropdown';

const handleClick = useCallback(
(event: React.MouseEvent<HTMLElement>) => {
onClick?.(event);
Expand Down Expand Up @@ -219,20 +224,21 @@ const ButtonDropdown: FunctionComponent<ButtonDropdownProps> = ({
);

return (
<Box className={clsx({ [classes.darkTheme]: darkTheme })}>
<Box className={clsx({ [classes.darkTheme]: darkTheme })} data-testid={testId}>
<Button
variant={variant}
onClick={handleClick}
loading={loading}
disabled={disabled}
data-testid={`${testId}-button`}
aria-haspopup="true"
aria-controls="simple-menu"
aria-controls={menuId}
>
{content} {!disableArrowDropdown && <ArrowDropDown fontSize="small" />}
</Button>

<Menu
id="account-menu"
id={menuId}
anchorEl={anchorEl}
keepMounted
getContentAnchorEl={null}
Expand All @@ -244,6 +250,7 @@ const ButtonDropdown: FunctionComponent<ButtonDropdownProps> = ({
transitionDuration={0}
open={Boolean(anchorEl)}
onClose={handleClose}
data-testid={`${testId}-menu`}
>
{items.map((item: ButtonDropdownItem) =>
item.items ? renderMenuItemWithHeading(item) : renderMenuItem(item)
Expand Down
11 changes: 11 additions & 0 deletions src/components/Card/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ describe('Card', () => {
expect(getByTestId('content')).toBeVisible();
});

it('can be accessed by custom test-id', () => {
const { getByTestId } = render(
<Card {...props} data-testid="card-1">
Content
</Card>
);
expect(getByTestId('card-1')).toBeInTheDocument();
expect(getByTestId('card-1-header')).toBeInTheDocument();
expect(getByTestId('card-1-content')).toBeInTheDocument();
});

it('renders the header using custom titleTypographyProps', () => {
const { container } = render(
<Card
Expand Down
21 changes: 17 additions & 4 deletions src/components/Card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
limitations under the License. *
******************************************************************************************************************** */

import React, { FunctionComponent, ReactNode, MouseEvent } from 'react';
import React, { FunctionComponent, ReactNode, MouseEvent, useMemo } from 'react';
import MUICard from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardHeader, { CardHeaderProps } from '@material-ui/core/CardHeader';
Expand Down Expand Up @@ -83,10 +83,17 @@ const Card: FunctionComponent<CardProps> = ({
onMouseMove,
onMouseOut,
withHover,
...props
}) => {
const styles = useStyles({ withHover });
const content = (
<CardContent>{typeof children === 'string' ? <Text variant="p">{children}</Text> : children}</CardContent>
const testId = props['data-testid'] || 'card';
const content = useMemo(
() => (
<CardContent data-testid={`${testId}-content`}>
{typeof children === 'string' ? <Text variant="p">{children}</Text> : children}
</CardContent>
),
[children, testId]
);
return (
<MUICard
Expand All @@ -95,8 +102,14 @@ const Card: FunctionComponent<CardProps> = ({
onMouseEnter={onMouseEnter}
onMouseMove={onMouseMove}
onMouseOut={onMouseOut}
data-testid={testId}
>
<CardHeader title={title} subheader={subtitle} titleTypographyProps={titleTypographyProps} />
<CardHeader
title={title}
subheader={subtitle}
titleTypographyProps={titleTypographyProps}
data-testid={`${testId}-header`}
/>
{content}
</MUICard>
);
Expand Down
5 changes: 5 additions & 0 deletions src/components/Checkbox/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ describe('Checkbox', () => {
});
});

it('can be accessed by custom test-id', () => {
const { getByTestId } = render(<Checkbox data-testid="checkbox-1" />);
expect(getByTestId('checkbox-1')).toBeInTheDocument();
});

it('renders accessible component', async () => {
const { container } = render(<Checkbox>label</Checkbox>);
const results = await axe(container);
Expand Down
Loading

0 comments on commit d7244a1

Please sign in to comment.