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

fix: codeInput implement specs, code improvement and new readme examples #1538

Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
053b560
fix: codeInput accessibility, a few code improvements and readme exam…
blakmetall May 11, 2020
a00572d
fix: labelId prop types definition
blakmetall May 11, 2020
3520c69
fix: removed accessibility integration due to we have a couple of gra…
blakmetall May 12, 2020
77d4262
feat: codeInput specs
blakmetall May 13, 2020
0bf8de0
fix: margin-bottom offset updated to 12px
blakmetall May 13, 2020
ec45092
feat: codeInput integration tests
blakmetall May 14, 2020
c9fb5bc
fix: uncomment temporarily disabled integration tests
blakmetall May 14, 2020
f8c8181
fix: name prop removed; one integration test removed, one test spec u…
blakmetall May 14, 2020
4e5ebfe
fix: spec tests and integration tests update
blakmetall May 14, 2020
9a64726
fix: codeInput spec method rename and styled updates for readOnly inputs
blakmetall May 15, 2020
592f558
fix: styles improvement; negative margin removed; readme example removed
blakmetall May 15, 2020
fdeaeca
fix: removed margins under input elements to close the gap between th…
blakmetall May 15, 2020
119a800
fix: spec updates needed
blakmetall May 15, 2020
df37d1a
styles: fix some changes
May 16, 2020
3dde628
fix: some tests
May 16, 2020
5befb0a
styles: some tests
May 16, 2020
a71d0a8
Merge branch 'master' into fix-codeinput-component-improve-accesibili…
TahimiLeonBravo May 19, 2020
8be6e96
fix: update specs and tests
blakmetall May 20, 2020
a70000e
fix: click and focus handler events update, and a few changes
blakmetall May 21, 2020
4d59ee8
Merge branch 'master' into fix-codeinput-component-improve-accesibili…
LeandroTorresSicilia May 22, 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
105 changes: 105 additions & 0 deletions integration/specs/CodeInput/codeInput-1.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
const PageCodeInput = require('../../../src/components/CodeInput/pageObject');
const { DELETE_KEY, TAB_KEY } = require('../../constants');

const CODEINPUT = '#codeinput-1';

describe('CodeInput base example', () => {
beforeAll(() => {
browser.url('/#!/CodeInput/1');
});
beforeEach(() => {
browser.refresh();
const component = $(CODEINPUT);
component.waitForExist();
});
it('should have focus on the first input on load', () => {
This conversation was marked as resolved.
Show resolved Hide resolved
const codeInput = new PageCodeInput(CODEINPUT);
expect(codeInput.getFocusedIndex()).toBe(0);
});
it('should change focus to the second input when the first input its filled with a number', () => {
const codeInput = new PageCodeInput(CODEINPUT);
browser.keys('2');
expect(codeInput.getFocusedIndex()).toBe(1);
});
it('should remove value from first input when first input is selected, has a value and "Backspace" key is pressed', () => {
const codeInput = new PageCodeInput(CODEINPUT);
browser.keys('2');
expect(codeInput.getInputValueAtIndex(0)).toBe('2');
browser.keys(DELETE_KEY);
expect(codeInput.getInputValueAtIndex(0)).toBe('');
});
it('should return focus to the first input and clear his value when the first input has a value, the second input is empty, is focused and "Backspace" key is pressed', () => {
const codeInput = new PageCodeInput(CODEINPUT);
browser.keys('2');
expect(codeInput.getInputValueAtIndex(0)).toBe('2');
expect(codeInput.getFocusedIndex()).toBe(1);
browser.keys(DELETE_KEY);
expect(codeInput.getInputValueAtIndex(0)).toBe('');
expect(codeInput.getFocusedIndex()).toBe(0);
});
it('should keep focus on the first input when input is empty and "Backspace" key is pressed', () => {
const codeInput = new PageCodeInput(CODEINPUT);
expect(codeInput.getFocusedIndex()).toBe(0);
expect(codeInput.getInputValueAtIndex(0)).toBe('');
browser.keys(DELETE_KEY);
expect(codeInput.getFocusedIndex()).toBe(0);
});
it('should keep focus on the last input when the last input value is filled with a number', () => {
const codeInput = new PageCodeInput(CODEINPUT);
browser.keys('2');
browser.keys('2');
browser.keys('2');
browser.keys('2');
expect(codeInput.getFocusedIndex()).toBe(3);
});
it('should keep focus in the current input when we type a letter', () => {
const codeInput = new PageCodeInput(CODEINPUT);
expect(codeInput.getFocusedIndex()).toBe(0);
browser.keys('A');
expect(codeInput.getFocusedIndex()).toBe(0);
});
it('should keep value empty in the current input when we type a letter', () => {
const codeInput = new PageCodeInput(CODEINPUT);
expect(codeInput.getFocusedIndex()).toBe(0);
This conversation was marked as resolved.
Show resolved Hide resolved
browser.keys('A');
expect(codeInput.getInputValueAtIndex(0)).toBe('');
});
it('should keep current input focus when unfocused inputs are clicked', () => {
const codeInput = new PageCodeInput(CODEINPUT);
expect(codeInput.getFocusedIndex()).toBe(0);
codeInput.clickInputByIndex(2);
This conversation was marked as resolved.
Show resolved Hide resolved
expect(codeInput.getFocusedIndex()).toBe(0);
});
it('should keep focus on the second input when any input is clicked and the first input has a number already', () => {
const codeInput = new PageCodeInput(CODEINPUT);
browser.keys('2');
expect(codeInput.getFocusedIndex()).toBe(1);
codeInput.clickInputByIndex(0);
This conversation was marked as resolved.
Show resolved Hide resolved
expect(codeInput.getFocusedIndex()).toBe(1);
});
it('should leave focus on all inputs when "Tab" key is pressed', () => {
const codeInput = new PageCodeInput(CODEINPUT);
expect(codeInput.getFocusedIndex()).toBe(0);
browser.keys(TAB_KEY);
expect(codeInput.getFocusedIndex()).toBe(undefined);
});
it('should return focus to the active input when "Tab" key is pressed and then any of the non active inputs is clicked', () => {
const codeInput = new PageCodeInput(CODEINPUT);
expect(codeInput.getFocusedIndex()).toBe(0);
browser.keys(TAB_KEY);
expect(codeInput.getFocusedIndex()).toBe(undefined);
codeInput.clickInputByIndex(3);
expect(codeInput.getFocusedIndex()).toBe(0);
});
it('should have all inputs filled when we type 4 numbers in a row and codeInput has a length of 4', () => {
const codeInput = new PageCodeInput(CODEINPUT);
browser.keys('1');
browser.keys('2');
browser.keys('3');
browser.keys('4');
expect(codeInput.getInputValueAtIndex(0)).toBe('1');
expect(codeInput.getInputValueAtIndex(1)).toBe('2');
expect(codeInput.getInputValueAtIndex(2)).toBe('3');
expect(codeInput.getInputValueAtIndex(3)).toBe('4');
});
});
21 changes: 21 additions & 0 deletions integration/specs/CodeInput/codeInput-9.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const PageCodeInput = require('../../../src/components/CodeInput/pageObject');

const CODEINPUT = '#codeinput-9';

describe('CodeInput readOnly example', () => {
beforeAll(() => {
browser.url('/#!/CodeInput/9');
});
beforeEach(() => {
browser.refresh();
const component = $(CODEINPUT);
component.waitForExist();
});
it('should keep inputs unfocused when codeInput is disabled and inputs are clicked', () => {
const codeInput = new PageCodeInput(CODEINPUT);
expect(codeInput.getFocusedIndex()).toBe(undefined);
codeInput.clickInputByIndex(1);
codeInput.clickInputByIndex(2);
expect(codeInput.getFocusedIndex()).toBe(undefined);
});
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"@fortawesome/free-solid-svg-icons": "^5.3.1",
"@fortawesome/react-fontawesome": "^0.1.2",
"@stripe/stripe-js": "^1.3.0",
"@testing-library/react-hooks": "^3.2.1",
"@wdio/allure-reporter": "^5.18.6",
"@wdio/cli": "^5.7.15",
"@wdio/jasmine-framework": "^5.7.13",
Expand Down
167 changes: 167 additions & 0 deletions src/components/CodeInput/__test__/codeInput.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import React from 'react';
import { mount } from 'enzyme';
import CodeInput from '../';
import InputItems from '../inputItems';
import InputItem from '../inputItem';
import HelpText from '../../Input/styled/helpText';
import StyledTextError from '../../Input/styled/errorText';
import { StyledLabel } from '../styled';

import useFocusedIndexState from '../hooks/useFocusedIndexState';
import usePreviousIndex from '../hooks/usePreviousIndex';
import useValueState from '../hooks/useValueState';
import getNormalizedValue from '../helpers/getNormalizedValue';
import getNumbersFromClipboard from '../helpers/getNumbersFromClipboard';

jest.mock('../hooks/useFocusedIndexState', () => jest.fn(() => 0));
jest.mock('../hooks/usePreviousIndex', () => jest.fn(() => undefined));
jest.mock('../hooks/useValueState', () => jest.fn(() => ['', '', '', '']));
jest.mock('../helpers/getNormalizedValue', () => jest.fn(() => ''));
jest.mock('../helpers/getNumbersFromClipboard', () => jest.fn(() => ''));

describe('<CodeInput />', () => {
it('should have value length as 6 when length is 6', () => {
useValueState.mockImplementation(() => ['', '', '', '', '', '']);
const component = mount(<CodeInput length={6} />);
expect(component.find(InputItems).prop('value').length).toBe(6);
});
it('should have label rendered if label is sent as param', () => {
const component = mount(<CodeInput label="label-test" />);
expect(component.find(StyledLabel).exists()).toBe(true);
});
it('should not have label rendered when label is emtpy', () => {
const component = mount(<CodeInput />);
expect(component.find(StyledLabel).exists()).toBe(false);
});
it('should have bottomHelpText rendered if is sent as param', () => {
const component = mount(<CodeInput bottomHelpText="help-text-test" />);
expect(component.find(HelpText).exists()).toBe(true);
});
it('should not have bottomHelpText rendered when bottomHelpText is empty', () => {
const component = mount(<CodeInput />);
expect(component.find(HelpText).exists()).toBe(false);
});
it('should have error rendered if is sent as param', () => {
const component = mount(<CodeInput error="error-test" />);
expect(component.find(StyledTextError).exists()).toBe(true);
});
it('should not have error rendered if is not sent as param', () => {
const component = mount(<CodeInput />);
expect(component.find(StyledTextError).exists()).toBe(false);
});
it('should keep isActive as true on active input whenever any other input is clicked', () => {
const component = mount(<CodeInput />);
const input0 = component.find(InputItem).at(0);
const input2 = component.find(InputItem).at(2);
input2.simulate('click');
expect(input0.prop('isActive')).toBe(true);
expect(input2.prop('isActive')).toBe(false);
});
it('should set isActive as true over the second input when first input is filled', () => {
const component = mount(<CodeInput />);
useValueState.mockImplementation(() => ['1', '', '', '']);
useFocusedIndexState.mockImplementation(() => 1);
component.setProps({ value: '1' });
expect(
component
.find(InputItem)
.at(0)
.prop('isActive'),
).toBe(false);
expect(
component
.find(InputItem)
.at(1)
.prop('isActive'),
).toBe(true);
});
it('should keep isActive as true on the last input when all inputs are filled an last input changes', () => {
const component = mount(<CodeInput />);
useValueState.mockImplementation(() => ['1', '2', '3', '4']);
useFocusedIndexState.mockImplementation(() => 3);
component.setProps({ value: '1234' });
expect(
component
.find(InputItem)
.at(3)
.prop('isActive'),
).toBe(true);
});
it('should call onChange with the number value when input value changes and is number', () => {
getNormalizedValue.mockImplementation(() => '3');
const onChangeFn = jest.fn();
const component = mount(<CodeInput onChange={onChangeFn} />);
const inputIndex = 0;
component
.find(InputItems)
.props()
.onChange('3', inputIndex);
expect(onChangeFn).toHaveBeenCalledWith('3');
});
it('should not call onChange when input value changes and is not a number', () => {
getNormalizedValue.mockImplementation(() => '');
const onChangeFn = jest.fn();
const component = mount(<CodeInput onChange={onChangeFn} />);
const inputIndex = 0;
component
.find(InputItems)
.props()
.onChange('A', inputIndex);
expect(onChangeFn).not.toHaveBeenCalled();
});
it('should call getNumbersFromClipboard with the copy and pasted value', () => {
getNumbersFromClipboard.mockClear();
const component = mount(<CodeInput />);
component
.find(InputItem)
.at(0)
.props()
.onPaste({
clipboardData: {
getData: () => '12345abcdfe',
},
});
expect(getNumbersFromClipboard).toHaveBeenCalledWith('12345abcdfe');
});
it('should call onChange with numbers only when we copy and paste a mixed text and numbers string', () => {
getNumbersFromClipboard.mockImplementation(() => '12345');
const onChangeFn = jest.fn();
const component = mount(<CodeInput onChange={onChangeFn} length={5} />);
component
.find(InputItem)
.at(0)
.props()
.onPaste({
clipboardData: {
getData: () => '12345abcdfe',
},
});
expect(onChangeFn).toHaveBeenCalledWith('12345');
});
it('should call getNormalizedValue with the right values when value changes', () => {
useValueState.mockImplementation(() => ['1', '2', '', '']);
const component = mount(<CodeInput value="12" />);
const inputIndex = 2;
component
.find(InputItems)
.props()
.onChange('3', inputIndex);
expect(getNormalizedValue).toHaveBeenCalledWith('3', 2, ['1', '2', '', '']);
});
it('should call useValueState with the right value and length', () => {
useValueState.mockReset();
mount(<CodeInput value="1" length={4} />);
expect(useValueState).toHaveBeenCalledWith('1', 4);
});
it('should call useFocusedIndexState with the right value and length', () => {
useValueState.mockReset();
mount(<CodeInput value="6" length={3} />);
expect(useValueState).toHaveBeenCalledWith('6', 3);
});
it('should call usePreviousIndex with the right focusedIndex value', () => {
usePreviousIndex.mockReset();
useFocusedIndexState.mockImplementation(() => 2);
mount(<CodeInput value="12" />);
expect(usePreviousIndex).toHaveBeenCalledWith(2);
});
});
84 changes: 84 additions & 0 deletions src/components/CodeInput/__test__/inputItem.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react';
import { mount } from 'enzyme';
import { DELETE_KEY } from '../../../libs/constants';
import InputItem from '../inputItem';
import { StyledInput } from '../styled';

describe('<InputItem />', () => {
it('should call onChange event with the right value and index when input changes', () => {
const onChangeFn = jest.fn();
const component = mount(<InputItem index={0} onChange={onChangeFn} />);
component
.find(StyledInput)
.props()
.onChange({ target: { value: 2 } });
expect(onChangeFn).toHaveBeenCalledWith(2, 0);
});
it('should call onChange event with empty value when DELETE_KEY is pressed, current input value is empty and current index is greater than 0', () => {
const onChangeFn = jest.fn();
const component = mount(<InputItem index={1} onChange={onChangeFn} />);
component
.find(StyledInput)
.props()
.onKeyDown({ keyCode: DELETE_KEY });
expect(onChangeFn).toHaveBeenCalledWith('', 0);
});
it('should not call onChange when DELETE_KEY is pressed, current input value is empty and current index is 0', () => {
const onChangeFn = jest.fn();
const component = mount(<InputItem index={0} onChange={onChangeFn} />);
component
.find(StyledInput)
.props()
.onKeyDown({ keyCode: DELETE_KEY });
expect(onChangeFn).not.toHaveBeenCalled();
});
it('should call onClick event with the right value input is clicked', () => {
const onClickFn = jest.fn();
const component = mount(<InputItem index={2} value="2" onClick={onClickFn} />);
component
.find(StyledInput)
.props()
.onClick({ target: { value: '2' } });
expect(onClickFn).toHaveBeenCalledWith({ target: { value: '2' } });
});
it('should call onFocus event with the right value and index when input is focused', () => {
const onFocusFn = jest.fn();
const component = mount(<InputItem index={0} value="2" onFocus={onFocusFn} />);
component
.find(StyledInput)
.props()
.onFocus({ target: { value: '2' } });
expect(onFocusFn).toHaveBeenCalledWith({ target: { value: '2' } }, 0);
});
it('should call onBlur event with the right value when input leaves focus', () => {
const onBlurFn = jest.fn();
const component = mount(<InputItem index={0} value="2" onBlur={onBlurFn} />);
component
.find(StyledInput)
.props()
.onBlur({ target: { value: '2' } });
expect(onBlurFn).toHaveBeenCalledWith({ target: { value: '2' } });
});
it('should call onKeyDown event with the right value when key is pressed', () => {
const onKeyDownFn = jest.fn();
const component = mount(<InputItem index={0} value="2" onKeyDown={onKeyDownFn} />);
component
.find(StyledInput)
.props()
.onKeyDown({ keyCode: DELETE_KEY });
expect(onKeyDownFn).toHaveBeenCalledWith({ keyCode: DELETE_KEY });
});
it('should call onPaste event with the right value when string is pasted inside input', () => {
const onPasteFn = jest.fn();
const component = mount(<InputItem index={0} value="2" onPaste={onPasteFn} />);
component
.find(StyledInput)
.props()
.onPaste({
clipboardData: '1234',
});
expect(onPasteFn).toHaveBeenCalledWith({
clipboardData: '1234',
});
});
});
Loading