From dbe5257a9bdba19de6694a2aa39dcfccf5fcaec3 Mon Sep 17 00:00:00 2001 From: John Coburn Date: Mon, 11 Jul 2022 13:03:37 -0500 Subject: [PATCH] Upgrade interactors pt1 (#1857) * filtergroups tests * filterpanesearch tests * upgrade focusLink tests * headline-test-upgrade * upgrade icon tests * update infopopover tests * fix infopopover test * perform the proper action in the test --- lib/ErrorModal/tests/ErrorModal-test.js | 38 ++- lib/ExportCsv/tests/ExportCsv-test.js | 24 +- lib/FilterGroups/tests/FilterGroups-test.js | 274 ++++++------------ .../tests/FilterPaneSearch.js | 39 ++- lib/FocusLink/tests/FocusLinkInteractor.js | 10 + .../tests/FocusLinkWithObjectTarget.js | 15 +- .../tests/FocusLinkWithStringTarget.js | 15 +- lib/FocusLink/tests/FocusLinkWithoutTarget.js | 26 +- lib/Headline/tests/Headline-test.js | 67 ++--- lib/Highlighter/tests/Highlighter-test.js | 15 +- lib/Icon/tests/Icon-test.js | 107 ++++--- lib/InfoPopover/tests/InfoPopover-test.js | 53 ++-- tests/helpers/localInteractors.js | 3 +- 13 files changed, 272 insertions(+), 414 deletions(-) create mode 100644 lib/FocusLink/tests/FocusLinkInteractor.js diff --git a/lib/ErrorModal/tests/ErrorModal-test.js b/lib/ErrorModal/tests/ErrorModal-test.js index 1a4f2dbf9..b3207c2da 100644 --- a/lib/ErrorModal/tests/ErrorModal-test.js +++ b/lib/ErrorModal/tests/ErrorModal-test.js @@ -1,16 +1,22 @@ import React from 'react'; -import { expect } from 'chai'; import sinon from 'sinon'; import { describe, beforeEach, it } from 'mocha'; -import { runAxeTest } from '@folio/stripes-testing'; - +import { runAxeTest, Modal, Button, including, converge } from '@folio/stripes-testing'; +import { RoledHTML } from '../../../tests/helpers/localInteractors'; import { mountWithContext } from '../../../tests/helpers'; import ErrorModal from '../ErrorModal'; -import ErrorModalInteractor from './interactor'; + +const ErrorModalInteractor = Modal.extend('Error modal') + .filters({ + errorText: (el) => el.querySelector('[class^="modalContent--"]').textContent + }) + .actions({ + clickClose: ({ find }, label = 'Close') => find(Button(including(label))).click() + }); describe('ErrorModal', () => { - const errorModal = new ErrorModalInteractor(); + const errorModal = ErrorModalInteractor(); const onClose = sinon.spy(); const label = 'Something went wrong'; @@ -30,29 +36,19 @@ describe('ErrorModal', () => { it('has no axe errors. - ErrorModal', runAxeTest); - it('the label text should be correct', () => { - expect(errorModal.label.text).to.equal(label); - }); + it('the label text should be correct', () => errorModal.has({ title: label })); - it('the content text should be correct', () => { - expect(errorModal.content.text).to.equal(content); - }); + it('the content text should be correct', () => errorModal.has({ errorText: content })); - it('the content tagName should be div', () => { - expect(errorModal.bodyTagName).to.equal('div'); - }); + it('the content tagName should be div', () => RoledHTML({ className: including('modalContent') }).has({ tagName: 'DIV' })); - it('shows correct button label', () => { - expect(errorModal.closeButton.text).to.equal(buttonLabel); - }); + it('shows correct button label', () => Button(buttonLabel).exists()); describe('when clicking the close button', () => { beforeEach(async () => { - await errorModal.closeButton.click(); + await errorModal.clickClose(); }); - it('the onClose callback should be fired', () => { - expect(onClose.called).to.be.true; - }); + it('the onClose callback should be fired', () => converge(() => onClose.called)); }); }); diff --git a/lib/ExportCsv/tests/ExportCsv-test.js b/lib/ExportCsv/tests/ExportCsv-test.js index e8dc37cc6..d8d781fd7 100644 --- a/lib/ExportCsv/tests/ExportCsv-test.js +++ b/lib/ExportCsv/tests/ExportCsv-test.js @@ -1,16 +1,16 @@ import React from 'react'; import { beforeEach, it, describe } from 'mocha'; -import { expect } from 'chai'; +import { Button, Bigtest } from '@folio/stripes-testing'; import { mountWithContext } from '../../../tests/helpers'; -import ButtonInteractor from '../../Button/tests/interactor'; import ExportCsv from '../ExportCsv'; import testData from './export'; +const { Link } = Bigtest; + describe('ExportCSV', () => { - const exportButton = new ButtonInteractor(['data-test-export-csv']); - const exportLink = new ButtonInteractor('a[data-test-exportcsv-link]'); + const exportButton = Button(); beforeEach(async () => { await mountWithContext( @@ -21,25 +21,13 @@ describe('ExportCSV', () => { ); }); - it('renders the exportCSV button', () => { - expect(exportButton.isPresent).to.be.true; - expect(exportButton.isButton).to.be.true; - expect(exportButton.rendersDefault).to.be.true; - }); - - it('renders the exportCSV button label', () => { - expect(exportButton.text).to.equal('Export to CSV'); - }); + it('renders the exportCSV button', () => Button('Export to CSV').exists()); describe('Clicking the button', () => { beforeEach(async () => { await exportButton.click(); }); - it('creates a link node', () => { - expect(exportLink.isPresent).to.be.true; - expect(exportLink.isAnchor).to.be.true; - expect(exportLink.label).to.equal(''); - }); + it('creates a link node', () => Link({ visible: false }).exists()); }); }); diff --git a/lib/FilterGroups/tests/FilterGroups-test.js b/lib/FilterGroups/tests/FilterGroups-test.js index 6b9a78c75..f32343277 100644 --- a/lib/FilterGroups/tests/FilterGroups-test.js +++ b/lib/FilterGroups/tests/FilterGroups-test.js @@ -4,11 +4,9 @@ import { beforeEach, it, } from 'mocha'; -import { expect } from 'chai'; -import { runAxeTest } from '@folio/stripes-testing'; +import { runAxeTest, HTML, Checkbox, Accordion, including, Button } from '@folio/stripes-testing'; import { mountWithContext } from '../../../tests/helpers'; -import FilterGroupInteractor from './interactor'; import { FilterGroupsFirstComponent, FilterGroupsSecondComponent, @@ -17,8 +15,28 @@ import { FilterGroupsFifthComponent, } from './helpers/filterGroupsUsageTest'; +const FilterGroupInteractor = HTML.extend('filter groups') + .selector('[data-test-filter-groups]') + .filters({ + sections: (el) => Array.from(el.querySelectorAll('[class^="filterSetHeader--"] [class^="labelArea--"]')).map((l) => l.textContent), + filterCount: el => el.querySelectorAll('input').length, + }) + .actions({ + check: ({ find }, label) => find(Checkbox(including(label))).click(), + toggle: ({ find }, label) => find(Accordion(including(label))).toggle(), + }); + +const FilterAccordion = Accordion.extend('filter accordion') + .filters({ + filterCount: el => el.querySelectorAll('input').length, + clearButton: Button({ ariaLabel: including('Clear') }).exists() + }) + .actions({ + clickClearButton: ({ find }, label = 'Clear') => find(Button({ ariaLabel: including(label) })).click(), + }); + describe('Filter Group', () => { - const filterGroupInteractor = new FilterGroupInteractor(); + const filterGroup = FilterGroupInteractor(); describe('render Filter Group 1 component', () => { beforeEach(async () => { @@ -27,54 +45,45 @@ describe('Filter Group', () => { ); }); - it('is a very inclusive iquisitor', runAxeTest); - it('render filter group', () => { - expect(filterGroupInteractor.hasControlGroup).to.be.true; - expect(filterGroupInteractor.hasFilterCheckbox).to.be.true; - }); + it('no axe errors: Filter Group', runAxeTest); - it('render checkboxes', () => { - expect(filterGroupInteractor.checkboxes().length).to.equal(3); - }); + it('render filter group', () => Promise.all([ + filterGroup.exists(), + Checkbox().exists(), + ])); - it('render checkbox label', () => { - expect(filterGroupInteractor.checkboxes(0).hasLabelElement).to.be.true; - expect(filterGroupInteractor.checkboxes(0).label).to.equal('Books'); - expect(filterGroupInteractor.checkboxes(0).isChecked).to.be.false; - }); + it('render checkboxes', () => FilterAccordion('Item Types').has({ filterCount: 3 })); - it('has not Clear button', () => { - expect(filterGroupInteractor.hasClearButton).to.be.false; - }); + it('render checkbox label', () => Checkbox('Books').exists()); + + it('has no Clear button', () => FilterAccordion('Item Types').has({ clearButton: false })); describe('check filter', () => { beforeEach(async () => { - await filterGroupInteractor.checkboxes(0).clickInput(); - await filterGroupInteractor.checkboxes(2).clickInput(); + await Checkbox('Books').click(); + await Checkbox('Microfilm').click(); }); - it('checkbox is checked', () => { - expect(filterGroupInteractor.checkboxes(0).isChecked).to.be.true; - expect(filterGroupInteractor.checkboxes(2).isChecked).to.be.true; - }); + it('checkbox is checked', () => Promise.all([ + Checkbox('Books').is({ checked: true }), + Checkbox('DVDs').is({ checked: false }), + Checkbox('Microfilm').is({ checked: true }), + ])); - it('has Clear button', () => { - expect(filterGroupInteractor.hasClearButton).to.be.true; - }); + it('has Clear button', () => FilterAccordion('Item Types').has({ clearButton: true })); describe('clear all filters', () => { beforeEach(async () => { - await filterGroupInteractor.clickClearButton(); + await FilterAccordion('Item Types').clickClearButton(); }); - it('checkbox is checked', () => { - expect(filterGroupInteractor.checkboxes(0).isChecked).to.be.false; - expect(filterGroupInteractor.checkboxes(2).isChecked).to.be.false; - }); + it('checkboxs are unchecked', () => Promise.all([ + Checkbox('Books').is({ checked: false }), + Checkbox('DVDs').is({ checked: false }), + Checkbox('Microfilm').is({ checked: false }), + ])); - it('has Clear button', () => { - expect(filterGroupInteractor.hasClearButton).to.be.false; - }); + it('has Clear button', () => FilterAccordion('Item Types').has({ clearButton: false })); }); }); }); @@ -85,53 +94,35 @@ describe('Filter Group', () => { ); }); - it('render filter group', () => { - expect(filterGroupInteractor.hasControlGroup).to.be.true; - expect(filterGroupInteractor.hasFilterCheckbox).to.be.true; - }); - - it('render checkboxes', () => { - expect(filterGroupInteractor.checkboxes().length).to.equal(5); - }); - - it('render checkbox label', () => { - expect(filterGroupInteractor.checkboxes(0).hasLabelElement).to.be.true; - expect(filterGroupInteractor.checkboxes(0).label).to.equal('Books'); - expect(filterGroupInteractor.checkboxes(0).isChecked).to.be.true; - expect(filterGroupInteractor.checkboxes(2).isChecked).to.be.true; - }); + it('render filter group', () => Promise.all([ + filterGroup.exists(), + Checkbox().exists(), + ])); - it('span wrapper has class for checked checkbox', () => { - expect(filterGroupInteractor.isCheckboxSpanWrapperChecked).to.be.false; - }); + it('render checkboxes', () => filterGroup.has({ filterCount: 5 })); - it('has Clear button', () => { - expect(filterGroupInteractor.hasClearButton).to.be.true; - }); + it('render checkbox label', () => Checkbox('Books').exists()); - describe('check filter', () => { - beforeEach(async () => { - await filterGroupInteractor.checkboxes(0).clickInput(); - await filterGroupInteractor.checkboxes(2).clickInput(); - }); + it('renders correct controls as checked', () => Promise.all([ + Checkbox('Books').is({ checked: true }), + Checkbox('DVDs').is({ checked: false }), + Checkbox('Microfilm').is({ checked: true }), + ])); - it('span wrapper has class for checked checkbox', () => { - expect(filterGroupInteractor.isCheckboxSpanWrapperChecked).to.be.false; - }); - }); + it('has Clear button', () => FilterAccordion('Item Types').has({ clearButton: true })); describe('clear all filters', () => { beforeEach(async () => { - await filterGroupInteractor.clickClearButton(); + await FilterAccordion('Item Types').clickClearButton(); }); - it('span wrapper has class for checked checkbox', () => { - expect(filterGroupInteractor.isCheckboxSpanWrapperChecked).to.be.false; - }); + it('checkboxs are unchecked', () => Promise.all([ + Checkbox('Books').is({ checked: false }), + Checkbox('DVDs').is({ checked: false }), + Checkbox('Microfilm').is({ checked: false }), + ])); - it('has Clear button', () => { - expect(filterGroupInteractor.hasClearButton).to.be.false; - }); + it('has Clear button', () => FilterAccordion('Item Types').has({ clearButton: false })); }); }); @@ -142,34 +133,25 @@ describe('Filter Group', () => { ); }); - it('render filter group', () => { - expect(filterGroupInteractor.hasControlGroup).to.be.true; - expect(filterGroupInteractor.hasFilterCheckbox).to.be.true; - }); + it('render filter group', () => Promise.all([ + filterGroup.exists(), + Checkbox().exists(), + ])); - it('render checkboxes', () => { - expect(filterGroupInteractor.checkboxes().length).to.equal(3); - }); + it('render correct number of checkboxes', () => FilterAccordion('Item Types').has({ filterCount: 3 })); - it('render checkbox label', () => { - expect(filterGroupInteractor.checkboxes(0).hasLabelElement).to.be.true; - expect(filterGroupInteractor.checkboxes(0).label).to.equal('Books'); - expect(filterGroupInteractor.checkboxes(0).isChecked).to.be.true; - }); - - it('filter group heading is', () => { - expect(filterGroupInteractor.filterHeading).to.equal('Item Types'); - }); + it('render checkbox label', () => Promise.all([ + Checkbox('Books').exists(), + Checkbox('DVDs').exists(), + Checkbox('Microfilm').exists() + ])); describe('check filter', () => { beforeEach(async () => { - await filterGroupInteractor.checkboxes(0).clickInput(); + await Checkbox('Books').click(); }); - it('checkbox is checked and has label', () => { - expect(filterGroupInteractor.checkboxes(0).hasLabelElement).to.be.true; - expect(filterGroupInteractor.checkboxes(0).label).to.equal('Books'); - }); + it('filter group has clear button', () => FilterAccordion('Item Types').has({ clearButton: true })); }); }); @@ -180,48 +162,30 @@ describe('Filter Group', () => { ); }); - it('render filter group', () => { - expect(filterGroupInteractor.hasControlGroup).to.be.true; - expect(filterGroupInteractor.hasFilterCheckbox).to.be.true; - }); + it('render filter group', () => Promise.all([ + filterGroup.exists(), + Checkbox().exists(), + ])); - it('render checkboxes', () => { - expect(filterGroupInteractor.checkboxes().length).to.equal(5); - }); + it('render correct number of checkboxes', () => filterGroup.has({ filterCount: 5 })); - it('render checkbox label', () => { - expect(filterGroupInteractor.checkboxes(0).hasLabelElement).to.be.true; - expect(filterGroupInteractor.checkboxes(0).label).to.equal('Books'); - expect(filterGroupInteractor.checkboxes(0).isChecked).to.be.true; - expect(filterGroupInteractor.checkboxes(2).isChecked).to.be.true; - }); + it('renders correct controls as checked according to config', () => Promise.all([ + Checkbox('Books').is({ checked: true }), + Checkbox('DVDs').is({ checked: false }), + Checkbox('Microfilm').is({ checked: true }), + ])); - it('has Clear button', () => { - expect(filterGroupInteractor.hasClearButton).to.be.true; - }); - - it('filter is inside accordion', () => { - expect(filterGroupInteractor.hasFilterAccordion).to.be.true; - }); - - it('filter is expanded', () => { - expect(filterGroupInteractor.isFilterGroupVisible).to.be.true; - }); - - it('filter group heading is', () => { - expect(filterGroupInteractor.filterHeading).to.equal('Item Types'); - }); + it('has Clear button', () => FilterAccordion('Item Types').has({ clearButton: true })); describe('clear all filters', () => { beforeEach(async () => { - await filterGroupInteractor.clickClearButton(); + await FilterAccordion('Item Types').clickClearButton(); }); - it('has Clear button', () => { - expect(filterGroupInteractor.hasClearButton).to.be.true; - }); + it('has Clear button', () => FilterAccordion('Item Types').has({ clearButton: true })); }); }); + describe('render Filter Group 5 component', () => { beforeEach(async () => { await mountWithContext( @@ -229,57 +193,11 @@ describe('Filter Group', () => { ); }); - it('render filter group', () => { - expect(filterGroupInteractor.hasControlGroup).to.be.true; - expect(filterGroupInteractor.hasFilterCheckbox).to.be.true; - }); - - it('render checkboxes', () => { - expect(filterGroupInteractor.checkboxes().length).to.equal(5); - }); - - it('render checkbox label', () => { - expect(filterGroupInteractor.checkboxes(0).hasLabelElement).to.be.true; - expect(filterGroupInteractor.checkboxes(0).label).to.equal('Books'); - expect(filterGroupInteractor.checkboxes(0).isChecked).to.be.true; - expect(filterGroupInteractor.checkboxes(2).isChecked).to.be.true; - }); - - it('has Clear button', () => { - expect(filterGroupInteractor.hasClearButton).to.be.true; - }); - - it('filter is inside accordion', () => { - expect(filterGroupInteractor.hasFilterAccordion).to.be.true; - }); + it('render filter group', () => Promise.all([ + filterGroup.exists(), + Checkbox().exists(), + ])); - it('filter is expanded', () => { - expect(filterGroupInteractor.isFilterGroupVisible).to.be.true; - }); - - it('filter group heading is', () => { - expect(filterGroupInteractor.filterHeading).to.equal('Item Types'); - }); - - describe('check filter', () => { - beforeEach(async () => { - await filterGroupInteractor.checkboxes(0).clickInput(); - }); - - it('checkbox is checked and has label', () => { - expect(filterGroupInteractor.checkboxes(0).hasLabelElement).to.be.true; - expect(filterGroupInteractor.checkboxes(0).label).to.equal('Books'); - }); - }); - - describe('clear all filters', () => { - beforeEach(async () => { - await filterGroupInteractor.clickClearButton(); - }); - - it('has Clear button', () => { - expect(filterGroupInteractor.hasClearButton).to.be.true; - }); - }); + it('render correct number of checkboxes', () => filterGroup.has({ filterCount: 5 })); }); }); diff --git a/lib/FilterPaneSearch/tests/FilterPaneSearch.js b/lib/FilterPaneSearch/tests/FilterPaneSearch.js index e55fa296c..7661965b4 100644 --- a/lib/FilterPaneSearch/tests/FilterPaneSearch.js +++ b/lib/FilterPaneSearch/tests/FilterPaneSearch.js @@ -1,19 +1,26 @@ import React from 'react'; import { describe, beforeEach, it } from 'mocha'; -import { expect } from 'chai'; -import { runAxeTest } from '@folio/stripes-testing'; +import { runAxeTest, HTML } from '@folio/stripes-testing'; import { mount } from '../../../tests/helpers'; import FilterPaneSearch from '../FilterPaneSearch'; -import FilterPaneSearchInteractor from './interactor'; const baseProps = { searchAriaLabel: 'Search', clearSearchId: 'clearSearchId', }; +const FilterPaneSearchInteractor = HTML.extend('filter pane search') + .selector('[class^=headerSearchContainer--]') + .filters({ + searchBox: (el) => !!el.querySelector('[data-test-search-box]'), + searchSelector: (el) => !!el.querySelector('[data-test-search-field-selector]'), + clearButton: (el) => !!el.querySelector('[data-test-clear-search]'), + resultLink: (el) => !!el.querySelector('[data-test-results-link]') + }); + describe('FilterPaneSearch', () => { - const filterPaneSearch = new FilterPaneSearchInteractor(); + const filterPaneSearch = FilterPaneSearchInteractor(); describe('base', async () => { beforeEach(async () => { @@ -22,21 +29,13 @@ describe('FilterPaneSearch', () => { ); }); - it('should render search box', () => { - expect(filterPaneSearch.hasSearchBox).to.be.true; - }); + it('should render search box', () => filterPaneSearch.has({ searchBox: true })); - it('should render clear button', () => { - expect(filterPaneSearch.hasClearButton).to.be.true; - }); + it('should render clear button', () => filterPaneSearch.has({ clearButton: true })); - it('should not render search field selector', () => { - expect(filterPaneSearch.hasSearchFieldSelector).to.be.false; - }); + it('should not render search field selector', () => filterPaneSearch.has({ searchSelector: false })); - it('should not render results link', () => { - expect(filterPaneSearch.hasSearchFieldSelector).to.be.false; - }); + it('should not render results link', () => filterPaneSearch.has({ resultLink: false })); it('has no axe errors', runAxeTest); }); @@ -48,9 +47,7 @@ describe('FilterPaneSearch', () => { ); }); - it('should render search field selector', () => { - expect(filterPaneSearch.hasSearchFieldSelector).to.be.true; - }); + it('should render search field selector', () => () => filterPaneSearch.has({ searchSelector: true })); }); describe('with results', async () => { @@ -60,8 +57,6 @@ describe('FilterPaneSearch', () => { ); }); - it('should render results link', () => { - expect(filterPaneSearch.hasResultsLink).to.be.true; - }); + it('should render results link', () => () => filterPaneSearch.has({ resultLink: true })); }); }); diff --git a/lib/FocusLink/tests/FocusLinkInteractor.js b/lib/FocusLink/tests/FocusLinkInteractor.js new file mode 100644 index 000000000..889e385b7 --- /dev/null +++ b/lib/FocusLink/tests/FocusLinkInteractor.js @@ -0,0 +1,10 @@ +import { + HTML +} from '@folio/stripes-testing'; + +export default HTML.extend('focus link') + .selector('[data-test-focus-link]') + .actions({ + focus: ({ perform }) => perform(el => el.focus()), + click: ({ perform }) => perform(el => el.click()) + }); diff --git a/lib/FocusLink/tests/FocusLinkWithObjectTarget.js b/lib/FocusLink/tests/FocusLinkWithObjectTarget.js index b6d78d6d0..72e159561 100644 --- a/lib/FocusLink/tests/FocusLinkWithObjectTarget.js +++ b/lib/FocusLink/tests/FocusLinkWithObjectTarget.js @@ -1,16 +1,15 @@ import React from 'react'; import { describe, beforeEach, it, afterEach } from 'mocha'; -import { expect } from 'chai'; import sinon from 'sinon'; -import { runAxeTest } from '@folio/stripes-testing'; +import { runAxeTest, converge } from '@folio/stripes-testing'; import { mount } from '../../../tests/helpers'; import FocusLink from '../FocusLink'; -import FocusLinkInteractor from './interactor'; +import FocusLinkInteractor from './FocusLinkInteractor'; const targetObject = { focus: sinon.spy() }; describe('FocusLink with object target', () => { - const focusLink = new FocusLinkInteractor(); + const focusLink = FocusLinkInteractor(); afterEach(() => { targetObject.focus.resetHistory(); @@ -24,9 +23,7 @@ describe('FocusLink with object target', () => { ); }); - it('shoud render link', () => { - expect(focusLink.hasFocusLink).to.be.true; - }); + it('shoud render link', () => focusLink.exists()); it('has no axe errors - FocusLink', runAxeTest); @@ -35,8 +32,6 @@ describe('FocusLink with object target', () => { await focusLink.click(); }); - it('should make the targed focused', () => { - expect(targetObject.focus.called).to.be.true; - }); + it('should make the targed focused', () => converge(() => targetObject.focus.called)); }); }); diff --git a/lib/FocusLink/tests/FocusLinkWithStringTarget.js b/lib/FocusLink/tests/FocusLinkWithStringTarget.js index dbd35bebe..732d35b6f 100644 --- a/lib/FocusLink/tests/FocusLinkWithStringTarget.js +++ b/lib/FocusLink/tests/FocusLinkWithStringTarget.js @@ -1,11 +1,11 @@ import React from 'react'; import { describe, beforeEach, it, afterEach } from 'mocha'; -import { expect } from 'chai'; import sinon from 'sinon'; +import { converge, Keyboard } from '@folio/stripes-testing'; import { mount } from '../../../tests/helpers'; import FocusLink from '../FocusLink'; -import FocusLinkInteractor from './interactor'; +import FocusLinkInteractor from './FocusLinkInteractor'; const testId = 'test'; @@ -29,17 +29,14 @@ describe('FocusLink with string target', () => { focusSpy = sinon.spy(testInput, 'focus'); }); - it('shoud render link', () => { - expect(focusLink.hasFocusLink).to.be.true; - }); + it('shoud render link', () => focusLink.exists()); describe('Press on the Enter button', () => { beforeEach(async () => { - await focusLink.trigger('keypress', { keyCode: 13 }); + await focusLink.focus(); + await Keyboard.pressKey('Enter'); }); - it('should make the targed focused', () => { - expect(focusSpy.called).to.be.true; - }); + it('should make the targed focused', () => converge(() => focusSpy.called)); }); }); diff --git a/lib/FocusLink/tests/FocusLinkWithoutTarget.js b/lib/FocusLink/tests/FocusLinkWithoutTarget.js index 09b97e883..a96428b1b 100644 --- a/lib/FocusLink/tests/FocusLinkWithoutTarget.js +++ b/lib/FocusLink/tests/FocusLinkWithoutTarget.js @@ -1,18 +1,19 @@ import React from 'react'; import { describe, beforeEach, it, afterEach } from 'mocha'; -import { expect } from 'chai'; import sinon from 'sinon'; +import { converge, Keyboard } from '@folio/stripes-testing'; import { mount } from '../../../tests/helpers'; import FocusLink from '../FocusLink'; -import FocusLinkInteractor from './interactor'; +import FocusLinkInteractor from './FocusLinkInteractor'; + const testId = 'test'; const testComponent = 'div'; const targetNextAfter = () =>
; describe('FocusLink', () => { - const focusLink = new FocusLinkInteractor(); + const focusLink = FocusLinkInteractor(); let testInput; let focusSpy; @@ -33,18 +34,15 @@ describe('FocusLink', () => { focusSpy = sinon.spy(testInput, 'focus'); }); - it('shoud render link', () => { - expect(focusLink.hasFocusLink).to.be.true; - }); + it('shoud render link', () => focusLink.exists()); describe('Press on the Enter button', () => { beforeEach(async () => { - await focusLink.trigger('keypress', { keyCode: 13 }); + await focusLink.focus(); + await Keyboard.pressKey('Enter'); }); - it('should make the targed focused', () => { - expect(focusSpy.called).to.be.true; - }); + it('should make the targed focused', () => converge(() => focusSpy.called)); }); }); @@ -70,17 +68,13 @@ describe('FocusLink with targetNextAfter prop', () => { focusSpy = sinon.spy(testNextInput, 'focus'); }); - it('shoud render link', () => { - expect(focusLink.hasFocusLink).to.be.true; - }); + it('shoud render link', () => focusLink.exists()); describe('Clicking on focus link', () => { beforeEach(async () => { await focusLink.click(); }); - it('should make the targed focused', () => { - expect(focusSpy.called).to.be.true; - }); + it('should make the targed focused', () => converge(() => focusSpy.called)); }); }); diff --git a/lib/Headline/tests/Headline-test.js b/lib/Headline/tests/Headline-test.js index 41c6dba28..fa5369975 100644 --- a/lib/Headline/tests/Headline-test.js +++ b/lib/Headline/tests/Headline-test.js @@ -4,14 +4,21 @@ import React from 'react'; import { describe, beforeEach, it } from 'mocha'; -import { expect } from 'chai'; -import { runAxeTest } from '@folio/stripes-testing'; +import { runAxeTest, HTML, including } from '@folio/stripes-testing'; import { mount } from '../../../tests/helpers'; import Headline from '../Headline'; -import HeadlineInteractor, { sizes } from './interactor'; + +const sizes = ['small', 'medium', 'large', 'x-large', 'xx-large']; + +const HeadlineInteractor = HTML.extend('headline') + .selector('[data-test-headline]') + .filters({ + class: (el) => el.className, + tagName: (el) => el.tagName + }); describe('Headline', () => { - const headline = new HeadlineInteractor(); + const headline = HeadlineInteractor(); beforeEach(async () => { await mount( @@ -27,29 +34,17 @@ describe('Headline', () => { ); }); - it('renders passed children', () => { - expect(headline.label).to.equal('My label'); - }); + it('renders passed children', () => HeadlineInteractor('My label').exists()); - it('has a custom class name', () => { - expect(headline.class).to.include('my-custom-class'); - }); + it('has a custom class name', () => headline.has({ class: including('my-custom-class') })); - it('has faded text color class', () => { - expect(headline.isFaded).to.be.true; - }); + it('has faded text color class', () => headline.has({ class: including('isFaded') })); - it('has display flex class', () => { - expect(headline.isFlex).to.be.true; - }); + it('has display flex class', () => headline.has({ class: including('flex') })); - it('has display block class', () => { - expect(headline.isBlock).to.be.true; - }); + it('has display block class', () => headline.has({ class: including('block') })); - it('has a custom tag name of "h1"', () => { - expect(headline.tagName).to.equal('h1'); - }); + it('has a custom tag name of "h1"', () => headline.has({ tagName: 'H1' })); it('has no axe errors', runAxeTest); /** @@ -67,13 +62,9 @@ describe('Headline', () => { ); }); - it(`has a size of ${size}`, () => { - expect(headline[`has-size-${size}`]).to.be.true; - }); + it(`has a size of ${size}`, () => headline.has({ class: including(`size-${size}`) })); - it(`has a margin that matches the size (${size})`, () => { - expect(headline[`has-margin-${size}`]).to.be.true; - }); + it(`has a margin that matches the size (${size})`, () => headline.has({ class: including(`margin-${size}`) })); }); }); @@ -93,9 +84,7 @@ describe('Headline', () => { ); }); - it(`has a margin of ${size}`, () => { - expect(headline[`has-margin-${size}`]).to.be.true; - }); + it(`has a margin of ${size}`, () => headline.has({ class: including(`margin-${size}`) })); }); }); @@ -108,9 +97,7 @@ describe('Headline', () => { await mount(My label); }); - it('Should have a font weight regular class', () => { - expect(headline.hasRegularFontWeight).to.be.true; - }); + it('Should have a font weight regular class', () => headline.has({ class: including('font-weight-regular') })); }); describe('Setting weight="medium"', () => { @@ -118,9 +105,7 @@ describe('Headline', () => { await mount(My label); }); - it('Should have a font weight medium class', () => { - expect(headline.hasMediumFontWeight).to.be.true; - }); + it('Should have a font weight medium class', () => headline.has({ class: including('font-weight-medium') })); }); describe('Setting weight="bold"', () => { @@ -128,9 +113,7 @@ describe('Headline', () => { await mount(My label); }); - it('Should have a font weight bold class', () => { - expect(headline.hasBoldFontWeight).to.be.true; - }); + it('Should have a font weight bold class', () => headline.has({ class: including('font-weight-bold') })); }); describe('Setting weight="black"', () => { @@ -138,8 +121,6 @@ describe('Headline', () => { await mount(My label); }); - it('Should have a font weight black class', () => { - expect(headline.hasBlackFontWeight).to.be.true; - }); + it('Should have a font weight black class', () => headline.has({ class: including('font-weight-black') })); }); }); diff --git a/lib/Highlighter/tests/Highlighter-test.js b/lib/Highlighter/tests/Highlighter-test.js index 4e62a2460..510b1a70a 100644 --- a/lib/Highlighter/tests/Highlighter-test.js +++ b/lib/Highlighter/tests/Highlighter-test.js @@ -3,21 +3,26 @@ */ import React from 'react'; -import { expect } from 'chai'; import { beforeEach, describe, it, } from 'mocha'; +import { HTML } from '@folio/stripes-testing'; import Highlighter from '../Highlighter'; import { mount } from '../../../tests/helpers'; -import HighlighterInteractor from './interactor'; + +const HighlighterInteractor = HTML.extend('highlighter') + .selector('[data-test-highlighter]') + .filters({ + wordCount: (el) => el.querySelectorAll('[data-test-highlighter-mark]').length + }); describe('Highlighter', () => { - const highlighter = new HighlighterInteractor(); + const highlighter = HighlighterInteractor(); describe('if there are any search words', () => { beforeEach(async () => { @@ -29,8 +34,6 @@ describe('Highlighter', () => { ); }); - it('should have highligthed words', () => { - expect(highlighter.highlightedWordCount).to.be.above(0); - }); + it('should have highligthed words', () => highlighter.has({ wordCount: 3 })); }); }); diff --git a/lib/Icon/tests/Icon-test.js b/lib/Icon/tests/Icon-test.js index d853e8d67..1386daf50 100644 --- a/lib/Icon/tests/Icon-test.js +++ b/lib/Icon/tests/Icon-test.js @@ -5,19 +5,48 @@ import React from 'react'; import { beforeEach, it, describe } from 'mocha'; -import { expect } from 'chai'; -import { runAxeTest } from '@folio/stripes-testing'; +import { runAxeTest, HTML, including } from '@folio/stripes-testing'; import { mount } from '../../../tests/helpers'; -import IconInteractor from './interactor'; import icons from '../icons'; import css from '../Icon.css'; import spinnerCss from '../DotSpinner.css'; import Icon from '../Icon'; +// custom bigtest matcher - we may need to move this to a utility file if they accumulate. + +function oneOf(list) { + return { + match(actual) { + return list.includes(actual); + }, + description() { + return `value includes an item from: ${list}`; + } + }; +} + +const IconInteractor = HTML.extend('icon') + .selector('[class*=icon---]') + .filters({ + class: (el) => el.className, + label: (el) => el.textContent, + svg: (el) => !!el.querySelector('svg'), + width: (el) => el.offsetWidth, + height: (el) => el.offsetHeight, + viewBox: (el) => el.querySelector('svg')?.getAttribute('viewBox') || '', + iconClass: (el) => { + return el.querySelector('[data-test-icon-element]') ? + el.querySelector('[data-test-icon-element]').getAttribute('class') : + el.querySelector('svg').getAttribute('class') || ''; + }, + iconRendered: (el) => !!el.querySelector('[data-test-icon-element]') + }); + + describe('Icon', () => { - const icon = new IconInteractor(); + const icon = IconInteractor(); const iconClassName = 'icon-class'; const iconRootClass = 'icon-root-class'; const label = 'My Label'; @@ -54,25 +83,15 @@ describe('Icon', () => { ); }); - it('Renders an tag', () => { - expect(icon.iconElement.isPresent).to.be.true; - }); + it('Renders an tag', () => icon.has({ iconRendered: true })); - it('Renders with default size medium', () => { - expect(icon.isMedium).to.be.true; - }); + it('Renders with default size medium', () => icon.has({ iconClass: including('medium') })); - it('Renders the supplied id on the root element', () => { - expect(icon.id).to.equal('icon-id'); - }); + it('Renders the supplied id on the root element', () => icon.has({ id: 'icon-id' })); - it('Renders the supplied classname on the root element', () => { - expect(icon.className).to.include(iconRootClass); - }); + it('Renders the supplied classname on the root element', () => icon.has({ class: including(iconRootClass) })); - it('Renders the supplied classname on the svg element', () => { - expect(icon.iconElement.className).to.include(iconClassName); - }); + it('Renders the supplied classname on the svg element', () => icon.has({ iconClass: including(iconClassName) })); it('has no axe errors', runAxeTest); }); @@ -85,9 +104,7 @@ describe('Icon', () => { await mount(); }); - it('It should render the default icon', () => { - expect(icon.iconElement.className).to.include('default'); - }); + it('It should render the default icon', () => icon.has({ iconClass: including('default') })); it('has no axe errors', runAxeTest); }); @@ -100,9 +117,7 @@ describe('Icon', () => { await mount(); }); - it('Should include a "iconSpinner" className', () => { - expect(icon.iconElement.className).to.include(spinnerCss.spinner); - }); + it('Should include a "iconSpinner" className', () => icon.has({ iconClass: including(spinnerCss.spinner) })); it('has no axe errors', runAxeTest); }); @@ -118,23 +133,13 @@ describe('Icon', () => { await mount(); }); - it(`Should render a SVG with a className of "${sizeClassName}"`, () => { - expect(icon.iconElement.className).to.include(sizeClassName); - }); + it(`Should render a SVG with a className of "${sizeClassName}"`, () => icon.has({ iconClass: including(sizeClassName) })); - it(`Should have a width of ${sizes[size].width}px`, () => { - // accept both the base and +1 to deal with browser pixel rounding discrepancies - expect(icon.width).to.be.oneOf([sizes[size].width, sizes[size].width + 1]); - }); + it(`Should have a width of ${sizes[size].width}px`, () => icon.has({ width: oneOf([sizes[size].width, sizes[size].width + 1]) })); - it(`Should have a height of ${sizes[size].height}px`, () => { - // accept both the base and +1 to deal with browser pixel rounding discrepancies - expect(icon.height).to.be.oneOf([sizes[size].height, sizes[size].height + 1]); - }); + it(`Should have a height of ${sizes[size].height}px`, () => icon.has({ height: oneOf([sizes[size].height, sizes[size].height + 1]) })); - it('SVG should have a viewBox attribute with the value of "0 0 32 32"', () => { - expect(icon.iconElement.viewBox).to.equal('0 0 32 32'); - }); + it('SVG should have a viewBox attribute with the value of "0 0 32 32"', () => icon.has({ viewBox: '0 0 32 32' })); it('has no axe errors', runAxeTest); }); @@ -153,13 +158,9 @@ describe('Icon', () => { await mount(); }); - it('It should render a svg', () => { - expect(icon.iconElement.isPresent).to.be.true; - }); + it('It should render a svg', () => icon.has({ svg: true })); - it(`SVG should include a className of "${iconClassName}"`, () => { - expect(icon.iconElement.className).to.include(iconClassName); - }); + it(`SVG should include a className of "${iconClassName}"`, () => icon.has({ iconClass: including(iconClassName) })); }); }); @@ -174,9 +175,7 @@ describe('Icon', () => { ); }); - it(`Should render with a string of "${label}"`, () => { - expect(icon.label).to.equal(label); - }); + it(`Should render with a string of "${label}"`, () => icon.has({ label })); it('has no axe errors', runAxeTest); }); @@ -190,9 +189,7 @@ describe('Icon', () => { ); }); - it('Should render an action icon', () => { - expect(icon.isActionIcon).to.be.true; - }); + it('Should render an action icon', () => icon.has({ class: including('action') })); }); /** @@ -212,12 +209,8 @@ describe('Icon', () => { ); }); - it('Should render the custom icon', () => { - expect(icon.iconElement.className).to.include('my-custom-icon'); - }); + it('Should render the custom icon', () => icon.has({ iconClass: including('my-custom-icon') })); - it('SVG should have a viewBox attribute with the value of "0 0 32 32"', () => { - expect(icon.iconElement.viewBox).to.equal('0 0 32 32'); - }); + it('SVG should have a viewBox attribute with the value of "0 0 32 32"', () => icon.has({ viewBox: '0 0 32 32' })); }); }); diff --git a/lib/InfoPopover/tests/InfoPopover-test.js b/lib/InfoPopover/tests/InfoPopover-test.js index 29ee63a86..ca00a6462 100644 --- a/lib/InfoPopover/tests/InfoPopover-test.js +++ b/lib/InfoPopover/tests/InfoPopover-test.js @@ -3,24 +3,25 @@ */ import React from 'react'; -import { Interactor } from '@bigtest/interactor'; import { describe, beforeEach, it } from 'mocha'; -import { expect } from 'chai'; -import { runAxeTest } from '@folio/stripes-testing'; +import { runAxeTest, HTML, Button as ButtonInteractor } from '@folio/stripes-testing'; import Harness from '../../../tests/Harness'; import { mount } from '../../../tests/helpers'; import Button from '../../Button'; -import ButtonInteractor from '../../Button/tests/interactor'; import InfoPopover from '../InfoPopover'; -import InfoPopoverInteractor from './interactor'; + +const InfoPopoverInteractor = HTML.extend('info popover') + .selector('[data-test-popover-overlay]') + .filters({ + button: (el) => !!el.querySelector('button, a') + }); describe('InfoPopover', () => { - const infoPopover = new InfoPopoverInteractor(); - const customTrigger = new ButtonInteractor('#custom-trigger'); - const content = new Interactor('#content'); + const infoPopover = InfoPopoverInteractor(); + const customTrigger = ButtonInteractor({ id: 'custom-trigger' }); beforeEach(async () => { await mount( @@ -33,20 +34,14 @@ describe('InfoPopover', () => { /> ); - await infoPopover.triggerButton.click(); + await ButtonInteractor().click(); }); - it('Renders the default trigger button', () => { - expect(infoPopover.triggerButton.isPresent).to.be.true; - }); + it('Renders the default trigger button', () => ButtonInteractor().exists()); - it('Should render the contents of the info popover', () => { - expect(content.isPresent).to.be.true; - }); + it('Should render the contents of the info popover', () => InfoPopoverInteractor('Here is some content')); - it('Should NOT render a button inside the info popover by default', () => { - expect(infoPopover.button.isPresent).to.be.false; - }); + it('Should NOT render a button inside the info popover by default', () => infoPopover.has({ button: false })); it('should have no axe errors - InfoPopover', runAxeTest); @@ -62,23 +57,19 @@ describe('InfoPopover', () => { ); - await infoPopover.triggerButton.click(); + await ButtonInteractor().click(); }); - it('Should render a button inside the info popover by default', () => { - expect(infoPopover.button.isPresent).to.be.true; - }); + it('Should render a button inside the info popover by default', () => infoPopover.has({ button: true })); it('should have no axe errors - InfoPopover: href prop', runAxeTest); describe('When the hideOnButtonClick-prop is true and the button is clicked', () => { beforeEach(async () => { - await infoPopover.button.click(); + await infoPopover.find(ButtonInteractor()).click(); }); - it('Should close the info popover', () => { - expect(infoPopover.content.isPresent).to.be.false; - }); + it('Should close the info popover', () => infoPopover.absent()); }); }); @@ -102,18 +93,14 @@ describe('InfoPopover', () => { await customTrigger.click(); }); - it('Should render the custom trigger button passed to renderTrigger', () => { - expect(customTrigger.isPresent).to.be.true; - }); + it('Should render the custom trigger button passed to renderTrigger', () => customTrigger.exists()); describe('When the hideOnButtonClick-prop is false and the button is clicked', () => { beforeEach(async () => { - await infoPopover.button.click(); + await infoPopover.perform(el => el.querySelector('a').click()); }); - it('Should remain open', () => { - expect(infoPopover.content.isPresent).to.be.true; - }); + it('Should remain open', () => infoPopover.exists()); }); }); }); diff --git a/tests/helpers/localInteractors.js b/tests/helpers/localInteractors.js index c70f3e47a..8dd09b5f0 100644 --- a/tests/helpers/localInteractors.js +++ b/tests/helpers/localInteractors.js @@ -6,5 +6,6 @@ export const label = HTML.extend('label-interactor') export const RoledHTML = HTML.extend('html plus') .filters({ - role: element => element.getAttribute('role') + role: element => element.getAttribute('role'), + tagName: el => el.tagName });