From 240ad90d80318474d2dfc8273415f4d9b9a4f6db Mon Sep 17 00:00:00 2001 From: John Coburn Date: Fri, 8 Oct 2021 16:29:24 -0500 Subject: [PATCH 1/3] skip less things, change radio button test to properly reflect focus flow --- lib/Popover/tests/Popover-test.js | 45 +++++++++++-------- .../tests/RadioButton-ReduxForm-test.js | 8 +++- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/lib/Popover/tests/Popover-test.js b/lib/Popover/tests/Popover-test.js index 54b661c30..3763fa549 100644 --- a/lib/Popover/tests/Popover-test.js +++ b/lib/Popover/tests/Popover-test.js @@ -3,17 +3,25 @@ */ import React, { useState } from 'react'; -import { Interactor } from '@bigtest/interactor'; +import { HTML, Button as ButtonInteractor } from '@folio/stripes-testing'; import { describe, beforeEach, it } from '@bigtest/mocha'; import { expect } from 'chai'; import { mount } from '../../../tests/helpers'; import Button from '../../Button'; -import ButtonInteractor from '../../Button/tests/interactor'; import Popover from '../Popover'; -import PopoverInteractor from './interactor'; + +const PopoverInteractor = HTML.extend('popover') + .selector('[data-test-popover-overlay]') + .actions({ + pressEscape: ({ perform }) => { + return perform((el) => { + return el.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true, keyCode: 27 })); + }); + } + }); const ControlledPopoverHarness = () => { const [open, setOpen] = useState(true); @@ -34,9 +42,9 @@ const ControlledPopoverHarness = () => { }; describe('Popover', () => { - const popover = new PopoverInteractor(); - const button = new ButtonInteractor('#trigger-button'); - const content = new Interactor('#content'); + const popover = PopoverInteractor(); + const button = ButtonInteractor('Toggle'); + const content = HTML('Hello world'); beforeEach(async () => { await mount( @@ -55,11 +63,11 @@ describe('Popover', () => { }); it('Renders a trigger button', () => { - expect(button.isPresent).to.be.true; + button.exists(); }); it('Renders the popover overlay when the trigger is clicked', () => { - expect(popover.isOpen).to.be.true; + popover.exists(); }); describe('Pressing the escape key', () => { @@ -68,11 +76,11 @@ describe('Popover', () => { }); it('closes the popover', () => { - expect(popover.isOpen).to.be.false; + popover.exists(); }); it('focuses the trigger', () => { - expect(button.isFocused).to.be.true; + button.is({ focused: true }); }); }); @@ -98,7 +106,7 @@ describe('Popover', () => { }); it('Renders the content when trigger is clicked', () => { - expect(content.isPresent).to.be.true; + content.exists(); }); it('Has render props passed to the render-prop function', () => { @@ -106,7 +114,7 @@ describe('Popover', () => { }); }); - describe.skip('If the popover is controlled', () => { + describe('If the popover is controlled', () => { beforeEach(async () => { await mount( @@ -116,15 +124,16 @@ describe('Popover', () => { }); it('Renders the popover overlay by default', () => { - expect(popover.isOpen).to.be.true; + popover.exists(); }); it('Closes the popover overlay when the trigger is clicked', () => { - expect(popover.isOpen).to.be.false; + popover.absent(); }); }); describe('If the legacy component API is used', () => { + const button2 = ButtonInteractor('Top Popover3'); beforeEach(async () => { await mount( <> @@ -136,20 +145,20 @@ describe('Popover', () => { ); - await button.click(); + await button2.click(); }); it('Renders the legacy popover and opens the overlay when the toggle is clicked', () => { - expect(popover.isOpen).to.be.true; + popover.exists(); }); describe('When the toggle is clicked again', () => { beforeEach(async () => { - button.click(); + await button2.click(); }); it('Hides the popover overlay', () => { - expect(popover.isOpen).to.be.true; + popover.absent(); }); }); }); diff --git a/lib/RadioButton/tests/RadioButton-ReduxForm-test.js b/lib/RadioButton/tests/RadioButton-ReduxForm-test.js index 0b20e6f9d..754cb3e4d 100644 --- a/lib/RadioButton/tests/RadioButton-ReduxForm-test.js +++ b/lib/RadioButton/tests/RadioButton-ReduxForm-test.js @@ -3,7 +3,7 @@ import { describe, beforeEach, it } from 'mocha'; import { expect } from 'chai'; import { Field } from 'redux-form'; -import { RadioButton as Interactor } from '@folio/stripes-testing'; +import { RadioButton as Interactor, converge } from '@folio/stripes-testing'; import { mountWithContext } from '../../../tests/helpers'; import TestForm from '../../../tests/TestForm'; @@ -25,8 +25,10 @@ describe('RadioButton with Redux Form', () => { onChange={(event) => { output = event.target.checked; }} + validate={value => (value === 'green' ? 'Not ready to eat' : undefined)} type="radio" value="green" + warn={value => (value === 'ripe' ? 'Warning: may be mushy' : undefined)} />
@@ -48,15 +50,17 @@ describe('RadioButton with Redux Form', () => { describe('clicking the label', () => { beforeEach(async () => { + await radioButton1.focus(); await radioButton1.click(); await radioButton1.blur(); + await radioButton2.focus(); }); it('toggles the value', () => expect(output).to.be.true); it('displays the input as checked', () => radioButton1.is({ checked: true })); - it('displays the error text', () => radioButton1.has({ feedbackText: 'Not ready to eat' })); + it('displays the error text', () => converge(() => radioButton1.has({ feedbackText: 'Not ready to eat' }))); it('applies an error class', () => radioButton1.has({ hasError: true })); From 1617be7538066bb5463c99908dfefeda3f293573 Mon Sep 17 00:00:00 2001 From: John Coburn Date: Fri, 8 Oct 2021 16:39:23 -0500 Subject: [PATCH 2/3] depend on github branch temporarily... --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bdf309237..669fe877f 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@bigtest/mocha": "^0.5.0", "@folio/eslint-config-stripes": "^5.2.0", "@folio/stripes-cli": "^2.4.0", - "@folio/stripes-testing": "^4.0.0", + "@folio/stripes-testing": "folio-org/stripes-testing#stcom-880-focus-related-tests", "@mdx-js/loader": "^1.6.22", "@storybook/addon-actions": "^6.3.6", "@storybook/addons": "^6.3.6", From 49fd060aa3cf6d44c0f81124d5bad6f6b8d25e2a Mon Sep 17 00:00:00 2001 From: John Coburn Date: Mon, 11 Oct 2021 15:17:41 -0500 Subject: [PATCH 3/3] un-skip a few others --- .../tests/RadioButtonGroup-ReduxForm-test.js | 70 ++++++++++--------- lib/Select/tests/Select-ReduxForm-test.js | 30 ++++---- .../tests/TextField-ReduxForm-test.js | 49 +++++++------ package.json | 2 +- 4 files changed, 78 insertions(+), 73 deletions(-) diff --git a/lib/RadioButtonGroup/tests/RadioButtonGroup-ReduxForm-test.js b/lib/RadioButtonGroup/tests/RadioButtonGroup-ReduxForm-test.js index 30c6d8dab..ba8c60bb6 100644 --- a/lib/RadioButtonGroup/tests/RadioButtonGroup-ReduxForm-test.js +++ b/lib/RadioButtonGroup/tests/RadioButtonGroup-ReduxForm-test.js @@ -3,72 +3,76 @@ import { describe, beforeEach, it } from '@bigtest/mocha'; import { expect } from 'chai'; import { Field } from 'redux-form'; +import { RadioButtonGroup as RadioButtonGroupInteractor, RadioButton as RadioButtonInteractor } from '@folio/stripes-testing'; + import { mountWithContext } from '../../../tests/helpers'; import TestForm from '../../../tests/TestForm'; import RadioButtonGroup from '../RadioButtonGroup'; import RadioButton from '../../RadioButton'; -import RadioButtonGroupInteractor from './interactor'; +// import RadioButtonGroupInteractor from './interactor'; describe('RadioButtonGroup with Redux Form', () => { - const radioButtonGroup = new RadioButtonGroupInteractor(); + const radioButtonGroup = RadioButtonGroupInteractor('TestField'); beforeEach(async () => { await mountWithContext( - (value === 'no' ? 'testField is invalid' : undefined)} - warn={value => (value === 'yes' ? 'testField has a warning' : undefined)} - > +

Some help text

- - -
+ (value === 'no' ? 'testField is invalid' : undefined)} + warn={value => (value === 'yes' ? 'testField has a warning' : undefined)} + /> + (value === 'no' ? 'testField is invalid' : undefined)} + warn={value => (value === 'yes' ? 'testField has a warning' : undefined)} + /> +
); + await radioButtonGroup.focus('Yes'); }); - it('displays the first option as unselected', () => { - expect(radioButtonGroup.options(0).isChecked).to.be.false; - }); - - it('displays the second option as unselected', () => { - expect(radioButtonGroup.options(1).isChecked).to.be.false; + it('no options are selected', () => { + RadioButtonInteractor({ checked: true }).absent(); }); describe('selecting an option', () => { beforeEach(async () => { - await radioButtonGroup.options(0).clickAndBlur(); + await radioButtonGroup.choose('Yes'); + await radioButtonGroup.blur(); }); it('displays the first option as selected', () => { - expect(radioButtonGroup.options(0).isChecked).to.be.true; + radioButtonGroup.has({ checkedOption: 'Yes' }); }); - it('displays the second option as unselected', () => { - expect(radioButtonGroup.options(1).isChecked).to.be.false; - }); - - it.skip('displays a warning message', () => { - expect(radioButtonGroup.feedbackText).to.equal('testField has a warning'); + it('displays a warning message', () => { + radioButtonGroup.has({ feedbackText: 'testField has a warning' }); }); describe('selecting another option', () => { beforeEach(async () => { - await radioButtonGroup.options(1).clickAndBlur(); - }); - - it('displays the first option as unselected', () => { - expect(radioButtonGroup.options(0).isChecked).to.be.false; + await radioButtonGroup.choose('No'); }); - it('displays the second option as selected', () => { - expect(radioButtonGroup.options(1).isChecked).to.be.true; + it('displays the first option as unselected, second option selected', () => { + radioButtonGroup.has({ checkedOption: 'No' }); + RadioButtonGroupInteractor({ checkedOption: 'Yes' }).absent(); }); it('displays an error message', () => { - expect(radioButtonGroup.feedbackText).to.equal('testField is invalid'); + radioButtonGroup.has({ feedbackText: 'testField is invalid' }); }); }); }); diff --git a/lib/Select/tests/Select-ReduxForm-test.js b/lib/Select/tests/Select-ReduxForm-test.js index 346f4111f..215db3476 100644 --- a/lib/Select/tests/Select-ReduxForm-test.js +++ b/lib/Select/tests/Select-ReduxForm-test.js @@ -1,21 +1,22 @@ import React from 'react'; import { describe, beforeEach, it } from '@bigtest/mocha'; -import { expect } from 'chai'; import { Field } from 'redux-form'; +import { Select as SelectInteractor, including } from '@folio/stripes-testing'; import { mountWithContext } from '../../../tests/helpers'; import TestForm from '../../../tests/TestForm'; import Select from '../Select'; -import SelectInteractor from './interactor'; +// import SelectInteractor from './interactor'; describe('Select with ReduxForm', () => { - const select = new SelectInteractor(); + const select = SelectInteractor('testField'); describe('inputting a value', () => { beforeEach(async () => { await mountWithContext( { }); it('renders a select element', () => { - expect(select.hasSelect).to.be.true; + select.exists(); }); describe('changing the value', () => { beforeEach(async () => { - await select.selectOption('Option 2') - .focusSelect(); + await select.chooseAndBlur('Option 2'); }); it('applies a changed class', () => { - expect(select.hasChangedStyle).to.be.true; + select.has({ className: including('isChanged') }); }); }); }); @@ -49,6 +49,7 @@ describe('Select with ReduxForm', () => { await mountWithContext( { }); beforeEach(async () => { - await select.selectAndBlur('Option 2'); + await select.chooseAndBlur('Option 2'); }); - it.skip('applies an error style', () => { - expect(select.hasErrorStyle).to.be.true; + it('applies an error style', () => { + select.has({ className: including('error') }); }); - it.skip('renders an error message', () => { - expect(select.errorText).to.equal('testField is Invalid'); + it('renders an error message', () => { + select.has({ error: 'testField is Invalid' }); }); }); @@ -80,6 +81,7 @@ describe('Select with ReduxForm', () => { await mountWithContext( { }); beforeEach(async () => { - await select.selectAndBlur('Option 1'); + await select.chooseAndBlur('Option 1'); }); it('applies a valid class', () => { - expect(select.hasValidStyle).to.be.true; + select.has({ className: including('valid') }); }); }); }); diff --git a/lib/TextField/tests/TextField-ReduxForm-test.js b/lib/TextField/tests/TextField-ReduxForm-test.js index df41423d9..fdeaa6459 100644 --- a/lib/TextField/tests/TextField-ReduxForm-test.js +++ b/lib/TextField/tests/TextField-ReduxForm-test.js @@ -1,15 +1,16 @@ import React from 'react'; -import { describe, beforeEach, it } from '@bigtest/mocha'; +import { describe, beforeEach, it } from 'mocha'; import { expect } from 'chai'; import { Field } from 'redux-form'; +import { TextField as TextFieldInteractor, including } from '@folio/stripes-testing'; import { mountWithContext } from '../../../tests/helpers'; import TestForm from '../../../tests/TestForm'; import TextField from '../TextField'; -import TextFieldInteractor from './interactor'; + describe('TextField with ReduxForm', () => { - const textfield = new TextFieldInteractor(); + const textfield = TextFieldInteractor('testField'); describe('using with redux-form', () => { describe('inputting a value', () => { @@ -19,6 +20,7 @@ describe('TextField with ReduxForm', () => { await mountWithContext( { ); }); - it('should render the TextField normally', () => { - expect(textfield.type).to.equal('text'); - }); - describe('changing the value', () => { beforeEach(async () => { - await textfield.fillInput('anything') - .focusInput(); + await textfield.fillIn('anything'); + textfield.focus(); }); it('applies a changed class', () => { - expect(textfield.hasChangedStyle).to.be.true; + textfield.has({ className: including('changed') }); }); - it.skip('renders a clear button', () => { - expect(textfield.hasClearButton).to.be.true; + it('renders a clear button', () => { + textfield.has({ clearButton: true }); }); - describe.skip('clicking the clear button', () => { + describe('clicking the clear button', () => { beforeEach(async () => { - await textfield - .clickClearButton() - .blurInput(); + await textfield.clear(); + textfield.blur(); }); it('clears the field', () => { - expect(textfield.val).to.equal(''); + textfield.has({ value: '' }); }); it('fires the onClearField event', () => { @@ -64,11 +61,12 @@ describe('TextField with ReduxForm', () => { }); }); - describe.skip('inputting an invalid value', () => { + describe('inputting an invalid value', () => { beforeEach(async () => { await mountWithContext( (value === 'invalid' ? 'testField is Invalid' : undefined)} @@ -78,19 +76,20 @@ describe('TextField with ReduxForm', () => { }); beforeEach(async () => { - await textfield.fillAndBlur('invalid'); + await textfield.fillIn('invalid'); }); it('renders an error message', () => { - expect(textfield.inputError).to.be.true; + textfield.has({ error: 'testField is Invalid' }); }); }); - describe.skip('inputting a valid value with validStylesEnabled', () => { + describe('inputting a valid value with validStylesEnabled', () => { beforeEach(async () => { await mountWithContext( { }); beforeEach(async () => { - await textfield.fillAndBlur('valid'); + await textfield.fillIn('valid'); }); it('applies a valid class', () => { - expect(textfield.hasValidStyle).to.be.true; + textfield.has({ className: including('valid') }); }); describe('then removing the text', () => { beforeEach(async () => { - await textfield.fillAndBlur(''); + await textfield.fillIn(''); }); it('renders an error message', () => { - expect(textfield.inputError).to.be.true; + textfield.has({ error: 'testField cannot be blank' }); }); }); }); diff --git a/package.json b/package.json index 669fe877f..bdf309237 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@bigtest/mocha": "^0.5.0", "@folio/eslint-config-stripes": "^5.2.0", "@folio/stripes-cli": "^2.4.0", - "@folio/stripes-testing": "folio-org/stripes-testing#stcom-880-focus-related-tests", + "@folio/stripes-testing": "^4.0.0", "@mdx-js/loader": "^1.6.22", "@storybook/addon-actions": "^6.3.6", "@storybook/addons": "^6.3.6",