Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(switch): Redo switch tests in react-testing-library #386

Merged
merged 30 commits into from
Jan 28, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b75ed3c
test: Move jest-emotion matchers to global setup
lychyi Dec 27, 2019
15701e2
test(switch): Redo tests with RTL
lychyi Dec 27, 2019
0369ad0
test(switch): Fix tests
lychyi Dec 27, 2019
eeb610a
test(switch): Remove old tests
lychyi Dec 27, 2019
fad6c15
test(switch): Organize tests, add style tests
lychyi Dec 28, 2019
2a368e0
test(switch): Clean up extra afterEach and cleanup
lychyi Dec 28, 2019
c718440
test(switch): Adjust a couple test names
lychyi Jan 2, 2020
f1af44b
test(switch): Add cypress tests
lychyi Jan 3, 2020
242daf5
test(switch): Add static state table for vizreg
lychyi Jan 3, 2020
c279eca
Merge branch 'master' into switch-tests
lychyi Jan 3, 2020
172b4cf
test(switch): Convert state table to maps
lychyi Jan 3, 2020
bda17c9
test(switch): Remove style tests for error states
lychyi Jan 3, 2020
2614174
test(switch): Decouple act/assert from tests
lychyi Jan 3, 2020
992a4ca
Merge branch 'master' of github.com:Workday/canvas-kit into switch-tests
lychyi Jan 8, 2020
4f9d770
test(switch): Remove unnecessary code/test
lychyi Jan 8, 2020
017392f
Merge branch 'master' of github.com:Workday/canvas-kit into switch-tests
lychyi Jan 13, 2020
0e1a9dc
test(switch): Simplify and align Cypress tests
lychyi Jan 14, 2020
bda5e6a
test(switch): Organize and improve tests
lychyi Jan 14, 2020
5b24c60
test(switch): Move style specs to other blocks
lychyi Jan 14, 2020
592dec2
Merge branch 'master' into switch-tests
lychyi Jan 14, 2020
4b48608
test(switch): Add hover to visual testing
lychyi Jan 15, 2020
62f78d9
test(switch): Combine input ref tests
lychyi Jan 15, 2020
0d2b99f
test(switch): Fix typo in test desc
lychyi Jan 17, 2020
0d33e9f
test(switch): Remove focus/active state when disabled
lychyi Jan 17, 2020
df9c776
Merge branch 'master' into switch-tests
lychyi Jan 22, 2020
8e1d247
test(switch): Add StaticStatesTable
lychyi Jan 22, 2020
9f76dc7
Merge branch 'master' into switch-tests
lychyi Jan 23, 2020
d8d06e1
Merge branch 'master' into switch-tests
anicholls Jan 24, 2020
14dde09
test(switch): Use new ComponentState wrapper
lychyi Jan 25, 2020
5cd6033
Merge branch 'master' into switch-tests
lychyi Jan 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions cypress/integration/Switch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as h from '../helpers';

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

describe('Non-disabled 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 not have any axe errors', () => {
cy.checkA11y();
});

context('when the tab key is pressed', () => {
it('should be the focused item on the page', () => {
cy.tab();
getSwitch().should('have.focus');
lychyi marked this conversation as resolved.
Show resolved Hide resolved
});
});

context('when the switch is clicked', () => {
it('should be toggled', () => {
getSwitch()
lychyi marked this conversation as resolved.
Show resolved Hide resolved
.invoke('prop', 'checked')
.as('checked');
getSwitch().click();
getSwitch().should(this.checked ? 'not.be.checked' : 'be.checked');
lychyi marked this conversation as resolved.
Show resolved Hide resolved
getSwitch().click();
getSwitch().should(this.checked ? 'be.checked' : 'not.be.checked');
});
});
});
});
});

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

it('should not have any axe errors', () => {
cy.checkA11y();
});
it('should be disabled', () => {
getSwitch().should('be.disabled');
lychyi marked this conversation as resolved.
Show resolved Hide resolved
});
it('should not toggle when clicked', () => {
getSwitch()
.invoke('prop', 'checked')
.as('beforeClick');
getSwitch().click({force: true});
getSwitch()
.invoke('prop', 'checked')
.as('afterClick');
expect(this.beforeClick).to.eq(this.afterClick);
});
});
});
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()});
56 changes: 56 additions & 0 deletions modules/form-field/react/stories/stories_Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import {storiesOf} from '@storybook/react';
import withReadme from 'storybook-readme/with-readme';
import {ControlledComponentWrapper} from '../../../../utils/storybook';

import styled from '@emotion/styled';
import {Switch} from '../../../switch/react/index';
import FormField from '../index';
import README from '../../../switch/react/README.md';
import {StaticStates} from '@workday/canvas-kit-labs-react-core/lib/StaticStates';
import {ErrorType} from '@workday/canvas-kit-react-common';

const control = (child: React.ReactNode) => (
<ControlledComponentWrapper controlledProp={ControlledComponentWrapper.ControlledProp.Checked}>
Expand All @@ -17,6 +20,20 @@ const control = (child: React.ReactNode) => (
const hintText = 'Helpful text goes here.';
const hintId = 'error-desc-id';

const Table = styled('table')({
width: '100%',
thead: {
textAlign: 'left',
paddingBottom: 16,
},
'td, th': {
minWidth: 100,
paddingBottom: 16,
paddingRight: 16,
textAlign: 'left',
},
});

storiesOf('Components|Inputs/Switch/React/Top Label', module)
.addParameters({component: Switch})
.addDecorator(withReadme(README))
Expand Down Expand Up @@ -90,3 +107,42 @@ storiesOf('Components|Inputs/Switch/React/Left Label', module)
{control(<Switch />)}
</FormField>
));

storiesOf('Components|Inputs/Switch/React/Visual Testing', module)
lychyi marked this conversation as resolved.
Show resolved Hide resolved
lychyi marked this conversation as resolved.
Show resolved Hide resolved
.addParameters({component: Switch})
.addDecorator(withReadme(README))
.add('States', () => (
<StaticStates>
<h3>Focus/Active states should all render with the same blue focus outline</h3>
<Table>
<thead>
<tr>
<th>Error Type</th>
<th>No Style</th>
<th>Focus Style</th>
<th>Active Style</th>
</tr>
</thead>
<tbody>
<tr>
<td>No Error (Default)</td>
<td>{control(<Switch />)}</td>
<td>{control(<Switch className="focus" />)}</td>
<td>{control(<Switch className="active" />)}</td>
</tr>
<tr>
<td>Alert</td>
<td>{control(<Switch error={ErrorType.Alert} />)}</td>
<td>{control(<Switch error={ErrorType.Alert} className="focus" />)}</td>
<td>{control(<Switch error={ErrorType.Alert} className="active" />)}</td>
</tr>
<tr>
<td>Error</td>
<td>{control(<Switch error={ErrorType.Error} />)}</td>
<td>{control(<Switch error={ErrorType.Error} className="focus" />)}</td>
<td>{control(<Switch error={ErrorType.Error} className="active" />)}</td>
</tr>
</tbody>
</Table>
</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: 1 addition & 1 deletion modules/switch/react/lib/Switch.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import styled from '@emotion/styled';
import uuid from 'uuid/v4';
import {styled} from '@workday/canvas-kit-labs-react-core';
import {ErrorType, focusRing, mouseFocusBehavior} from '@workday/canvas-kit-react-common';
import {borderRadius, colors, inputColors, depth, spacing} from '@workday/canvas-kit-react-core';

Expand Down
194 changes: 166 additions & 28 deletions modules/switch/react/spec/Switch.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,182 @@
import * as React from 'react';
import {mount} from 'enzyme';
import {render, fireEvent} from '@testing-library/react';
import canvas from '@workday/canvas-kit-react-core';
import {ErrorType} from '@workday/canvas-kit-react-common';
import Switch from '../lib/Switch';

// Implementation details of the switch. These are meant to be helpers.

// The switch track
const getBackground = (container: HTMLElement) => {
return container.querySelector('div')!.querySelector('div');
};

// The switch thumb
const getCircle = (container: HTMLElement) => {
lychyi marked this conversation as resolved.
Show resolved Hide resolved
return container
.querySelector('div')!
.querySelector('div')!
.querySelector('div');
};

const expectedBoxShadow = (c1: string, c2: string, c3: string) => {
return `0 0 0 2px ${c1}, 0 0 0 4px ${c2}, 0 0 0 5px ${c3}`;
};

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

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

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();
it('should be unchecked by default', () => {
lychyi marked this conversation as resolved.
Show resolved Hide resolved
const {getByRole} = render(<Switch onChange={cb} />);
expect(getByRole('checkbox')).toHaveProperty('checked', false);
});

it('should apply extra props to the input element', () => {
const testId = 'test';
const {container} = render(<Switch data-testid="test" onChange={cb} />);
expect(container.querySelector('input')).toHaveAttribute('data-testid', testId);
lychyi marked this conversation as resolved.
Show resolved Hide resolved
});

it('should be checked if the checked prop is true', () => {
const {getByRole} = render(<Switch checked={true} onChange={cb} />);
expect(getByRole('checkbox')).toHaveProperty('checked', true);
});

it('should be disabled if the disabled prop is true', () => {
const {getByRole} = render(<Switch disabled={true} onChange={cb} />);
expect(getByRole('checkbox')).toHaveProperty('disabled', true);
});

it('should have a reference to the input', () => {
const inputRef = React.createRef<HTMLInputElement>();

render(<Switch inputRef={inputRef} onChange={cb} />);
expect(inputRef.current!.tagName.toLowerCase()).toBe('input');
});

it('should have a unique id by default', () => {
const {getByRole} = render(<Switch disabled={true} onChange={cb} />);
expect(getByRole('checkbox')).toHaveProperty('disabled', true);
lychyi marked this conversation as resolved.
Show resolved Hide resolved
});

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

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

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

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

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>
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 = fragment
.find('input')
.at(0)
.getDOMNode()
.getAttribute('id');
expect(getByTestId(testIds[0]).id).not.toBe(getByTestId(testIds[1]).id);
});

it('should fire the onChange event handler when clicked', () => {
const cb = jest.fn();
const {getByRole} = render(<Switch checked={true} onChange={cb} />);

fireEvent.click(getByRole('checkbox'));
fireEvent.click(getByRole('checkbox'));
expect(cb).toHaveBeenCalledTimes(2);
});

it('should be a `type` of "checkbox"', () => {
const {getByRole} = render(<Switch onChange={cb} />);
expect(getByRole('checkbox')).toHaveProperty('type', 'checkbox');
});

it('should have a `tabIndex` of 0', () => {
lychyi marked this conversation as resolved.
Show resolved Hide resolved
const {getByRole} = render(<Switch onChange={cb} />);
expect(getByRole('checkbox')).toHaveProperty('tabIndex', 0);
});

describe('Switch styles', () => {
describe('When disabled', () => {
test('the input element should have a "not-allowed" cursor when disabled', () => {
const {getByRole} = render(<Switch disabled={true} onChange={cb} />);
expect(getByRole('checkbox')).toHaveStyleRule('cursor', 'not-allowed');
});

test('the background element should have a pale background color when disabled', () => {
const {container} = render(<Switch disabled={true} onChange={cb} />);
const backgroundElement = getBackground(container);
expect(backgroundElement).toHaveStyleRule('background-color', canvas.colors.soap400);
});
});

describe('When not disabled', () => {
test('the input element should have a "pointer" cursor when not disabled', () => {
const {getByRole} = render(<Switch onChange={cb} />);
expect(getByRole('checkbox')).toHaveStyleRule('cursor', 'pointer');
});

test('the background element should have a non-neutral background when not disabled and checked', () => {
const {container} = render(<Switch checked onChange={cb} />);
const backgroundElement = getBackground(container);
expect(backgroundElement).toHaveStyleRule('background-color', canvas.colors.blueberry500);
});

test('the background element should have a neutral background color when not disabled and unhecked', () => {
const {container} = render(<Switch checked={false} onChange={cb} />);
const backgroundElement = getBackground(container);
expect(backgroundElement).toHaveStyleRule('background-color', canvas.colors.licorice200);
});

test('the circle element should be to the very left (`translateX(0)`) when unchecked', () => {
const {container} = render(<Switch checked={false} disabled={true} onChange={cb} />);
const circleElement = getCircle(container);
expect(circleElement).toHaveStyleRule('transform', 'translateX(0)');
lychyi marked this conversation as resolved.
Show resolved Hide resolved
});

test('the circle element should be to the very right (`translateX(spacing.s)`) when unchecked', () => {
lychyi marked this conversation as resolved.
Show resolved Hide resolved
const {container} = render(<Switch checked onChange={cb} />);
const circleElement = getCircle(container);
expect(circleElement).toHaveStyleRule('transform', `translateX(${canvas.spacing.s})`);
});
});

describe('When in error state', () => {
test('the input should have an error-colored border when the error type is "error"', () => {
const {getByRole} = render(<Switch checked onChange={cb} error={ErrorType.Error} />);
const bs = expectedBoxShadow(
canvas.colors.frenchVanilla100,
canvas.inputColors.error.border,
'transparent'
);

expect(getByRole('checkbox')).toHaveStyleRule('box-shadow', bs, {
target: ' ~ div:first-of-type',
});
});

const id2 = fragment
.find('input')
.at(1)
.getDOMNode()
.getAttribute('id');
test('the input should have an alert-colored border when the error type is "alert"', () => {
const {getByRole} = render(<Switch checked onChange={cb} error={ErrorType.Alert} />);
const bs = expectedBoxShadow(
canvas.colors.frenchVanilla100,
canvas.inputColors.warning.border,
canvas.colors.cantaloupe600
);

expect(id1).not.toEqual(id2);
fragment.unmount();
expect(getByRole('checkbox')).toHaveStyleRule('box-shadow', bs, {
lychyi marked this conversation as resolved.
Show resolved Hide resolved
target: ' ~ div:first-of-type',
});
});
});
});
});