Skip to content

Commit

Permalink
test(toast): Write test for Toasts (#584)
Browse files Browse the repository at this point in the history

Co-Authored-By: Alex Nicholls <anicholls3@gmail.com>

Co-authored-by: Alex Nicholls <anicholls3@gmail.com>
  • Loading branch information
mannycarrera4 and anicholls committed Apr 23, 2020
1 parent 140bdde commit 3e5e595
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 65 deletions.
55 changes: 55 additions & 0 deletions cypress/integration/Toast.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as h from '../helpers';

function getStatusToast() {
return cy.findByRole('status');
}

function getDialogToast() {
return cy.findByRole('dialog');
}

describe('Toast', () => {
before(() => {
h.stories.visit();
});

['Error', 'Success'].forEach(story => {
context(`given the '${story}' story is rendered`, () => {
beforeEach(() => {
h.stories.load('Components|Popups/Toast/React', story);
});

it('should not have any axe errors', () => {
cy.checkA11y();
});
});
});

context(`given the toast with no close icon or action button`, () => {
beforeEach(() => {
h.stories.load('Components|Popups/Toast/React', 'Success');
});

it('should have a role of status', () => {
getStatusToast().should('have.attr', 'role', 'status');
});

it('should have aria live set to polite', () => {
getStatusToast().should('have.attr', 'aria-live', 'polite');
});

it('should have aria atomic set to true', () => {
getStatusToast().should('have.attr', 'aria-atomic', 'true');
});
});

context(`given the toast with a close button and action button`, () => {
beforeEach(() => {
h.stories.load('Components|Popups/Toast/React', 'With action link and close icon');
});

it('should have a role of dialog', () => {
getDialogToast().should('have.attr', 'role', 'dialog');
});
});
});
6 changes: 6 additions & 0 deletions modules/toast/react/lib/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,19 @@ export default class Toast extends React.Component<ToastProps> {
transformOrigin,
...elemProps
} = this.props;

const isInteractive = onClose || onActionClick;

return (
<Popup
width={toastWidth}
transformOrigin={transformOrigin}
padding={PopupPadding.s}
handleClose={onClose}
closeIconSize={IconButtonSize.Small}
role={isInteractive ? 'dialog' : 'status'}
aria-live={isInteractive ? 'off' : 'polite'}
aria-atomic={!isInteractive}
{...elemProps}
>
<ToastContentContainer onClose={onClose}>
Expand Down
3 changes: 3 additions & 0 deletions modules/toast/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
"peerDependencies": {
"react": ">= 16.8 < 17"
},
"devDependencies": {
"@workday/canvas-kit-labs-react-core": "^3.6.0"
},
"dependencies": {
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
Expand Down
77 changes: 77 additions & 0 deletions modules/toast/react/spec/Toast.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as React from 'react';
import {render, fireEvent} from '@testing-library/react';
import {checkIcon} from '@workday/canvas-system-icons-web';
import Toast from '../lib/Toast';

describe('Toast', () => {
const cb = jest.fn();
afterEach(() => {
cb.mockReset();
});

describe('when rendered', () => {
it('should render a Toast with an icon', () => {
const toastMessage = 'Your workbook was successfully processed.';
const {getByTestId} = render(
<Toast icon={checkIcon} data-testid={'myToast'}>
{toastMessage}
</Toast>
);
expect(getByTestId('myToast')).toContainHTML('svg');
});
});

describe('when rendered with children', () => {
it('should render a Toast with a message', () => {
const toastMessage = 'Your workbook was successfully processed.';
const {getByTestId} = render(
<Toast icon={checkIcon} data-testid={'myToast'}>
{toastMessage}
</Toast>
);
expect(getByTestId('myToast')).toHaveTextContent(toastMessage);
});
});

describe('when rendered with a close icon', () => {
it('should call the on close callback', () => {
const toastMessage = 'Your workbook was successfully processed.';
const {container} = render(
<Toast onClose={cb} icon={checkIcon} data-testid={'myToast'}>
{toastMessage}
</Toast>
);
const closeIcon = container.querySelector('[data-close="close"]'); /*? */
fireEvent.click(closeIcon); /*? */
expect(cb).toHaveBeenCalledTimes(1);
});
});

describe('when rendered with an action', () => {
it('should render an action text', () => {
const toastMessage = 'Your workbook was successfully processed.';
const actionText = 'View more details';
const {container} = render(
<Toast onActionClick={cb} actionText={actionText} onClose={cb} icon={checkIcon}>
{toastMessage}
</Toast>
);

expect(container).toHaveTextContent(actionText);
});

it('should call the onActionClick callback', () => {
const toastMessage = 'Your workbook was successfully processed.';
const actionText = 'View more details';
const onCloseCB = jest.fn();
const {getAllByRole} = render(
<Toast onClose={cb} onActionClick={onCloseCB} actionText={actionText}>
{toastMessage}
</Toast>
);

fireEvent.click(getAllByRole('button')[1]);
expect(onCloseCB).toHaveBeenCalledTimes(1);
});
});
});
98 changes: 33 additions & 65 deletions modules/toast/react/stories/stories.tsx
Original file line number Diff line number Diff line change
@@ -1,84 +1,52 @@
/// <reference path="../../../../typings.d.ts" />
import * as React from 'react';
import {storiesOf} from '@storybook/react';
import {action} from '@storybook/addon-actions';
import {exclamationCircleIcon} from '@workday/canvas-system-icons-web';
import withReadme from 'storybook-readme/with-readme';

import {colors} from '../../../core/react';
import Toast from '../index';
import README from '../README.md';

const toastData = {
success: {
icon: undefined,
iconColor: undefined,
messages: [
'Your workbook was successfully processed.',
'Your data has been successfully updated.',
'Your data has been successfully downloaded.',
'View Data',
],
},
error: {
icon: exclamationCircleIcon,
iconColor: colors.cinnamon500,
messages: [
'There was an error with your workbook.',
'You are currently offline.',
'There was an error while your data was being processed.',
'View Error',
],
},
};

class ToastWrapper extends React.Component<{state?: string}> {
public render() {
let toastMeta;

switch (this.props.state) {
case 'Success':
default:
toastMeta = toastData.success;
break;
case 'Error':
toastMeta = toastData.error;
break;
}

const {messages} = toastMeta;

return (
<section>
<Toast icon={toastMeta.icon} iconColor={toastMeta.iconColor}>
{messages[0]}
</Toast>
<Toast icon={toastMeta.icon} iconColor={toastMeta.iconColor}>
{messages[1]}
</Toast>
<Toast
actionText={messages[3]}
onClose={() => console.warn('test')}
onActionClick={() => console.warn('link was clicked')}
icon={toastMeta.icon}
iconColor={toastMeta.iconColor}
>
{messages[2]}
</Toast>
</section>
);
}
}

storiesOf('Components|Popups/Toast/React', module)
.addParameters({component: Toast})
.addDecorator(withReadme(README))
.add('Successful', () => (
.add('Success', () => (
<div className="story">
<ToastWrapper state={'Success'} />
<Toast>Your workbook was successfully processed.</Toast>
</div>
))
.add('Error', () => (
<div className="story">
<ToastWrapper state={'Error'} />
<Toast iconColor={colors.cinnamon500} icon={exclamationCircleIcon}>
There was an error with your workbook.
</Toast>
</div>
))
.add('With close button', () => (
<div className="story">
<Toast onClose={action('close button clicked')}>
Your workbook was successfully processed.
</Toast>
</div>
))

.add('With action link', () => (
<div className="story">
<Toast actionText={'View more details'} onActionClick={action('action button clicked')}>
Your workbook was successfully processed.
</Toast>
</div>
))

.add('With action link and close icon', () => (
<div className="story">
<Toast
onClose={action('close button clicked')}
actionText={'View more details'}
onActionClick={action('action button clicked')}
>
Your workbook was successfully processed.
</Toast>
</div>
));
47 changes: 47 additions & 0 deletions modules/toast/react/stories/stories_visualTesting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/// <reference path="../../../../typings.d.ts" />
/** @jsx jsx */
import {jsx} from '@emotion/core';
import {storiesOf} from '@storybook/react';
import {colors} from '@workday/canvas-kit-react-core';
import {StaticStates} from '@workday/canvas-kit-labs-react-core';
import {action} from '@storybook/addon-actions';
import {ComponentStatesTable} from '../../../../utils/storybook';
import {exclamationCircleIcon} from '@workday/canvas-system-icons-web';
import {Toast} from '../index';

storiesOf('Components|Popups/Toast/React/Visual Testing', module)
.addParameters({
component: Toast,
chromatic: {
disable: false,
},
})
.add('States', () => (
<StaticStates>
<ComponentStatesTable
rowProps={[
{label: 'Default', props: {}},
{label: 'Custom Icon', props: {icon: exclamationCircleIcon}},
{
label: 'Custom Icon Color',
props: {icon: exclamationCircleIcon, iconColor: colors.cinnamon500},
},
{label: 'On Close', props: {onClose: action('close button clicked')}},
{
label: 'With Action Link',
props: {
onActionClick: action('action button clicked'),
actionText: 'View More Details',
},
},
]}
columnProps={[{label: 'Default', props: {}}]}
>
{props => (
<Toast aria-label="Play" {...props}>
Your workbook was successfully processed.
</Toast>
)}
</ComponentStatesTable>
</StaticStates>
));

0 comments on commit 3e5e595

Please sign in to comment.