Skip to content

Commit

Permalink
test(switch): Redo switch tests in react-testing-library (#386)
Browse files Browse the repository at this point in the history
Co-authored-by: Alex Nicholls <anicholls3@gmail.com>
  • Loading branch information
lychyi and anicholls committed Jan 28, 2020
1 parent ad294d3 commit fd9544f
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 42 deletions.
46 changes: 46 additions & 0 deletions cypress/integration/Switch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as h from '../helpers';

const getSwitch = () => {
return cy.get(`[type="checkbox"]`);
};

describe('Switch', () => {
before(() => {
h.stories.visit();
});
['Default', 'Alert', 'Error'].forEach(story => {
context(`given the '${story}' story is rendered`, () => {
beforeEach(() => {
h.stories.load('Components|Inputs/Switch/React/Top Label', story);
});

it('should pass accessibility checks', () => {
cy.checkA11y();
});

context('when clicked', () => {
beforeEach(() => {
getSwitch().click();
});

it('should be checked', () => {
getSwitch().should('be.checked');
});
});
});
});

context(`given the 'Disabled' story is rendered`, () => {
beforeEach(() => {
h.stories.load('Components|Inputs/Switch/React/Top Label', 'Disabled');
});

it('should pass accessibility checks', () => {
cy.checkA11y();
});

it('should be disabled', () => {
getSwitch().should('be.disabled');
});
});
});
3 changes: 2 additions & 1 deletion jest/setupTests.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Adapter from 'enzyme-adapter-react-16';
import {configure} from 'enzyme';
import {toHaveNoViolations} from 'jest-axe';
import serializer from 'jest-emotion';
import serializer, {matchers} from 'jest-emotion';
import '@testing-library/jest-dom/extend-expect';

expect.addSnapshotSerializer(serializer);
expect.extend(toHaveNoViolations);
expect.extend(matchers);
configure({adapter: new Adapter()});
60 changes: 58 additions & 2 deletions modules/form-field/react/stories/stories_Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
import * as React from 'react';
import {storiesOf} from '@storybook/react';
import withReadme from 'storybook-readme/with-readme';
import {ControlledComponentWrapper} from '../../../../utils/storybook';

import {
ControlledComponentWrapper,
ComponentStatesTable,
permutateProps,
} from '../../../../utils/storybook';
import {StaticStates} from '@workday/canvas-kit-labs-react-core/lib/StaticStates';
import {Switch} from '../../../switch/react/index';
import FormField from '../index';
import README from '../../../switch/react/README.md';
Expand Down Expand Up @@ -90,3 +94,55 @@ storiesOf('Components|Inputs/Switch/React/Left Label', module)
{control(<Switch />)}
</FormField>
));

storiesOf('Components|Inputs/Switch/React/Visual Testing', module)
.addParameters({component: Switch})
.addDecorator(withReadme(README))
.add('States', () => (
<StaticStates>
<ComponentStatesTable
rowProps={permutateProps(
{
checked: [{value: true, label: 'Checked'}, {value: false, label: 'Unchecked'}],
error: [
{value: undefined, label: ''},
{value: Switch.ErrorType.Alert, label: 'Alert'},
{value: Switch.ErrorType.Error, label: 'Error'},
],
},
props => {
if (props.indeterminate && !props.checked) {
return false;
}
return true;
}
)}
columnProps={permutateProps(
{
className: [
{label: 'Default', value: ''},
{label: 'Hover', value: 'hover'},
{label: 'Focus', value: 'focus'},
{label: 'Focus Hover', value: 'focus hover'},
{label: 'Active', value: 'active'},
{label: 'Active Hover', value: 'active hover'},
],
disabled: [{label: '', value: false}, {label: 'Disabled', value: true}],
},
props => {
if (props.disabled && !['', 'hover'].includes(props.className)) {
return false;
}
return true;
}
)}
>
{props => (
<Switch
{...props}
onChange={() => {}} // eslint-disable-line no-empty-function
/>
)}
</ComponentStatesTable>
</StaticStates>
));
3 changes: 0 additions & 3 deletions modules/page-header/react/spec/PageHeader.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import PageHeader from '../lib/PageHeader';
import {mount} from 'enzyme';
import {IconButton} from '@workday/canvas-kit-react-button';
import {exportIcon, fullscreenIcon} from '@workday/canvas-system-icons-web';
import {matchers} from 'jest-emotion';

expect.extend(matchers);

describe('Page Header', () => {
const cb = jest.fn();
Expand Down
2 changes: 2 additions & 0 deletions modules/switch/react/lib/Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ const SwitchCircle = styled('div')<Pick<SwitchProps, 'checked'>>(({checked}) =>
}));

export default class Switch extends React.Component<SwitchProps> {
static ErrorType = ErrorType;

public static defaultProps = {
checked: false,
};
Expand Down
152 changes: 116 additions & 36 deletions modules/switch/react/spec/Switch.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,124 @@
import * as React from 'react';
import {mount} from 'enzyme';
import {render, fireEvent} from '@testing-library/react';
import Switch from '../lib/Switch';

describe('Switch', () => {
const cb = jest.fn();

/**
* Today, this is hardcoded but this could possibly be
* configurable in the future (e.g. role='switch').
* In fact, 'checkbox' probably isn't the correct role
* according to MDN and ARIA
* https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Switch_role
* https://www.w3.org/TR/wai-aria-1.1/#switch
*/
const role = 'checkbox';

afterEach(() => {
cb.mockReset();
});

test('Switch should spread extra props', () => {
const component = mount(<Switch data-propspread="test" onChange={cb} />);
const input = component
.find('input')
// TODO: Standardize on prop spread location (see #150)
.getDOMNode();
expect(input.getAttribute('data-propspread')).toBe('test');
component.unmount();
});

test('Switch creates a unique id for each instance', async () => {
const fragment = mount(
<form>
<Switch checked={true} onChange={jest.fn()} disabled={false} />;
<Switch onChange={jest.fn()} disabled={false} />;
</form>
);

const id1 = fragment
.find('input')
.at(0)
.getDOMNode()
.getAttribute('id');

const id2 = fragment
.find('input')
.at(1)
.getDOMNode()
.getAttribute('id');

expect(id1).not.toEqual(id2);
fragment.unmount();
cb.mockClear();
});

describe('when rendered', () => {
it('should render an input element with `type=checkbox`', () => {
const {getByRole} = render(<Switch onChange={cb} />);
expect(getByRole(role)).toHaveProperty('type', 'checkbox');
});

it('should be unchecked by default', () => {
const {getByRole} = render(<Switch onChange={cb} />);
expect(getByRole(role)).toHaveProperty('checked', false);
});

test('should have a "pointer" cursor', () => {
const {getByRole} = render(<Switch onChange={cb} />);
expect(getByRole(role)).toHaveStyleRule('cursor', 'pointer');
});
});

describe('when rendered with checked=true', () => {
it('should render a checked checkbox input', () => {
const {getByRole} = render(<Switch checked={true} onChange={cb} />);
expect(getByRole(role)).toHaveProperty('checked', true);
});
});

describe('when rendered with an id', () => {
it('should render a checkbox input with that id', () => {
const id = 'switchy';
const {getByRole} = render(<Switch id={id} onChange={cb} />);

expect(getByRole(role)).toHaveAttribute('id', id);
});
});

describe('when rendered without an id', () => {
it('should create a unique id for each instance', () => {
const testIds = ['test1', 'test2'];
const {getByTestId} = render(
<>
<Switch data-testid={testIds[0]} onChange={cb} />
<Switch data-testid={testIds[1]} onChange={cb} />
</>
);

const id1 = getByTestId(testIds[0]).getAttribute('id');
const id2 = getByTestId(testIds[1]).getAttribute('id');

expect(id1).not.toEqual(id2);
});

it('should keep the same unique id if re-rendered', () => {
const {getByRole, rerender} = render(<Switch checked={false} onChange={cb} />);

const uniqueId = getByRole(role).getAttribute('id');
expect(getByRole(role)).toHaveProperty('id', uniqueId);

rerender(<Switch checked={true} onChange={cb} />);

expect(getByRole(role)).toHaveProperty('checked');
expect(getByRole(role)).toHaveProperty('id', uniqueId);
});
});

describe('when rendered with the disabled prop', () => {
it('should render a disabled checkbox input', () => {
const {getByRole} = render(<Switch disabled={true} onChange={cb} />);
expect(getByRole(role)).toHaveProperty('disabled', true);
});
it('should have a "not-allowed" cursor', () => {
const {getByRole} = render(<Switch disabled={true} onChange={cb} />);
expect(getByRole(role)).toHaveStyleRule('cursor', 'not-allowed');
});
});

describe('when rendered with extra, arbitrary props', () => {
it('should apply those extra props to the checkbox input', () => {
const testValue = 'test';

const {getByRole} = render(<Switch data-propspread={testValue} onChange={cb} />);
expect(getByRole(role)).toHaveAttribute('data-propspread', testValue);
});
});

describe('when rendered with an input ref', () => {
it("should set the ref's current property to the checkbox input element", () => {
const ref = React.createRef<HTMLInputElement>();

render(<Switch inputRef={ref} onChange={cb} />);

expect(ref.current).not.toBeNull();
expect(ref.current).toHaveAttribute('role', role);
});
});

describe('when clicked', () => {
it('should call the onChange callback', () => {
const {getByRole} = render(<Switch onChange={cb} />);

fireEvent.click(getByRole(role));

expect(cb).toHaveBeenCalledTimes(1);
});
});
});

0 comments on commit fd9544f

Please sign in to comment.