@patternfly/elements
Version:
PatternFly Elements
1,099 lines (1,098 loc) • 72.8 kB
JavaScript
import { expect, html, aTimeout, nextFrame } from '@open-wc/testing';
import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js';
import { PfSelect } from '../pf-select.js';
import { sendKeys } from '@web/test-runner-commands';
import { a11ySnapshot, querySnapshot } from '@patternfly/pfe-tools/test/a11y-snapshot.js';
import { clickElementAtCenter, clickElementAtOffset } from '@patternfly/pfe-tools/test/utils.js';
async function holdShift() {
await sendKeys({ down: 'Shift' });
}
async function releaseShift() {
await sendKeys({ up: 'Shift' });
}
async function holdCtrl() {
await sendKeys({ down: 'Control' });
}
async function releaseCtrl() {
await sendKeys({ up: 'Control' });
}
function press(key) {
return async function () {
await sendKeys({ press: key });
};
}
/**
* Compare selection to an array of strings
* @param element pf-select
* @returns a list of values of each selected option
*/
function getSelectedOptionValues(element) {
return Array.from(element.querySelectorAll('[selected]'), x => x.value);
}
// a11yShapshot does not surface the options
function getVisibleOptionValues(element) {
return element.options.filter(x => !x.hidden).map(x => x.value);
}
// a11yShapshot does not surface the options
function getActiveOption(element) {
return element.options.find(x => x.active);
}
/**
* NOTE because of the copy-to-shadow-root shtick in ActivedescendantController,
* we can't just pick an option (from light dom);
* @param element pf-select
* @param index item index
*/
async function clickItemAtIndex(element, index) {
const itemHeight = 44;
await clickElementAtOffset(element, [
10,
element.offsetHeight + (itemHeight * (index + 1)) - itemHeight / 2,
], {
allowOutOfBounds: true,
});
}
describe('<pf-select>', function () {
it('imperatively instantiates', function () {
expect(document.createElement('pf-select')).to.be.an.instanceof(PfSelect);
});
it('should upgrade', async function () {
expect(await createFixture(html `<pf-select></pf-select>`))
.to.be.an.instanceOf(customElements.get('pf-select'))
.and
.to.be.an.instanceOf(PfSelect);
});
describe('with accessible-label attribute and 3 items', function () {
let element;
const updateComplete = () => element.updateComplete;
const focus = () => element.focus;
beforeEach(async function () {
element = await createFixture(html `
<pf-select accessible-label="label">
<pf-option value="1">1</pf-option>
<pf-option value="2">2</pf-option>
<pf-option value="3">3</pf-option>
</pf-select>`);
});
it('passes aXe audit', async function () {
await expect(element).to.be.accessible();
});
it('labels the combobox with the accessible-label attribuet', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
name: 'label',
});
});
it('does not have redundant role', async function () {
expect(element.shadowRoot?.firstElementChild).to.not.contain('[role="button"]');
});
it('sets aria-setsize="3" and aria-posinset on items', function () {
element.options.forEach((option, i) => {
expect(option).to.have.attr('aria-setsize', '3');
expect(option).to.have.attr('aria-posinset', `${i + 1}`);
});
});
describe('focus()', function () {
beforeEach(focus);
beforeEach(updateComplete);
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
it('labels the listbox with the accessible-label attribute', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'listbox',
name: 'label',
});
});
it('focuses on the first item', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
describe('Space', function () {
beforeEach(press(' '));
beforeEach(updateComplete);
it('selects option 1', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
]);
});
it('exposes selection to assistive technology', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
value: '1',
});
});
});
});
});
});
describe('with `placeholder` attribute and 3 items', function () {
let element;
const updateComplete = () => element.updateComplete;
const focus = () => element.focus;
beforeEach(async function () {
element = await createFixture(html `
<pf-select placeholder="placeholder">
<pf-option value="1">1</pf-option>
<pf-option value="2">2</pf-option>
<pf-option value="3">3</pf-option>
</pf-select>`);
});
it('passes aXe audit', async function () {
await expect(element).to.be.accessible();
});
it('labels the combobox with the placeholder attribute', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
name: 'placeholder',
});
});
it('does not have redundant role', async function () {
expect(element.shadowRoot?.firstElementChild).to.not.contain('[role="button"]');
});
it('sets aria-setsize="4" and aria-posinset on items', function () {
element.options.forEach((option, i) => {
expect(option).to.have.attr('aria-setsize', '4');
expect(option).to.have.attr('aria-posinset', `${i + 1}`);
});
});
describe('focus()', function () {
beforeEach(focus);
beforeEach(updateComplete);
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
it('labels the listbox with the placeholder attribute', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'listbox',
name: 'placeholder',
});
});
it('focuses on the placeholder item', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('placeholder');
});
describe('Space', function () {
beforeEach(press(' '));
beforeEach(updateComplete);
it('selects nothing', async function () {
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('does not close the listbox nothing', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([]);
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('focuses on the first item', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
describe('Space', function () {
beforeEach(press(' '));
beforeEach(updateComplete);
it('selects option 1', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
]);
});
it('exposes selection to assistive technology', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
value: '1',
});
});
});
});
});
});
});
});
describe('with 3 items and associated <label> elements', function () {
let element;
const updateComplete = () => element.updateComplete;
const focus = () => element.focus;
beforeEach(async function () {
element = await createFixture(html `
<pf-select id="select">
<pf-option value="1">1</pf-option>
<pf-option value="2">2</pf-option>
<pf-option value="3">3</pf-option>
</pf-select>
<label for="select">label1</label>
<label for="select">label2</label>
`);
});
it('passes aXe audit', async function () {
await expect(element).to.be.accessible();
});
it('does not have redundant role', async function () {
expect(element.shadowRoot?.firstElementChild).to.not.contain('[role="button"]');
});
it('labels the combobox with the label elements', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
name: 'label1label2',
});
});
it('sets aria-setsize="3" and aria-posinset on items', function () {
element.options.forEach((option, i) => {
expect(option).to.have.attr('aria-setsize', '3');
expect(option).to.have.attr('aria-posinset', `${i + 1}`);
});
});
describe('focus()', function () {
beforeEach(focus);
beforeEach(updateComplete);
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
it('labels the listbox with the label elements', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'listbox',
name: 'label1label2',
});
});
it('focuses on the first item', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
describe('Space', function () {
beforeEach(press(' '));
beforeEach(updateComplete);
it('selects option 1', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
]);
});
it('exposes selection to assistive technology', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
value: '1',
});
});
});
});
});
});
describe('in a deep shadow root', function () {
let element;
const focus = () => element.focus();
const updateComplete = () => element.updateComplete;
beforeEach(async function () {
const fixture = await createFixture(html `
<shadow-root>
<template shadowrootmode="open">
<shadow-root>
<template shadowrootmode="open">
<pf-select variant="single"
accessible-label="Choose a number"
placeholder="Choose a number">
<pf-option value="1">1</pf-option>
<pf-option value="2">2</pf-option>
<pf-option value="3">3</pf-option>
<pf-option value="4">4</pf-option>
<pf-option value="5">5</pf-option>
<pf-option value="6">6</pf-option>
<pf-option value="7">7</pf-option>
<pf-option value="8">8</pf-option>
</pf-select>
</template>
</shadow-root>
</template>
</shadow-root>`);
function attachShadowRoots(root) {
root?.querySelectorAll('template[shadowrootmode]').forEach(template => {
const mode = template.getAttribute('shadowrootmode');
const shadowRoot = template.parentElement?.attachShadow?.({ mode });
shadowRoot?.appendChild(template.content);
template.remove();
attachShadowRoots(shadowRoot);
});
}
attachShadowRoots(document);
const select = fixture.shadowRoot?.firstElementChild?.shadowRoot?.querySelector('pf-select');
if (select) {
element = select;
await element?.updateComplete;
}
else {
throw new Error('no element!');
}
});
describe('expanding', function () {
beforeEach(focus);
beforeEach(press('Enter'));
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('remains expanded', function () {
expect(element.expanded).to.be.true;
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('remains expanded', function () {
expect(element.expanded).to.be.true;
});
describe('Space', function () {
beforeEach(press(' '));
beforeEach(updateComplete);
it('closes', function () {
expect(element.expanded).to.be.false;
});
it('sets value', function () {
expect(element.value).to.equal('2');
});
});
});
});
});
});
describe('with 8 items', function () {
let element;
const updateComplete = () => element.updateComplete;
const focus = () => element.focus();
beforeEach(async function () {
element = await createFixture(html `
<pf-select>
<pf-option value="1">1</pf-option>
<pf-option value="2">2</pf-option>
<pf-option value="3">3</pf-option>
<pf-option value="4">4</pf-option>
<pf-option value="5">5</pf-option>
<pf-option value="6">6</pf-option>
<pf-option value="7">7</pf-option>
<pf-option value="8">8</pf-option>
</pf-select>`);
});
it('does not pass aXe audit', async function () {
await expect(element).to.not.be.accessible();
});
it('sets aria-setsize and aria-posinset on items', function () {
element.options.forEach((option, i) => {
expect(option).to.have.attr('aria-setsize', '8');
expect(option).to.have.attr('aria-posinset', `${i + 1}`);
});
});
describe('click combobox button', function () {
beforeEach(() => clickElementAtCenter(element));
beforeEach(updateComplete);
it('does not pass aXe audit', async function () {
await expect(element).to.not.be.accessible();
});
it('expands the listbox', async function () {
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('focuses on the first item', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
describe('Tab', function () {
beforeEach(press('Tab'));
it('does not focus the combobox button', async function () {
expect(await a11ySnapshot()).to.not.have.axTreeFocusedNode;
});
});
});
describe('focus()', function () {
beforeEach(focus);
beforeEach(updateComplete);
it('focuses on the combobox button', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axRole('combobox');
});
it('does not expand the listbox', async function () {
expect(element.expanded).to.be.false;
expect(await a11ySnapshot()).to.not.axContainRole('listbox');
});
describe('Enter', function () {
beforeEach(press('Enter'));
beforeEach(updateComplete);
it('expands the listbox', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('focuses on the first item', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
});
describe('Space', function () {
beforeEach(function () {
document.body.style.height = '8000px';
});
afterEach(function () {
document.body.style.height = 'initial';
});
beforeEach(press(' '));
beforeEach(updateComplete);
beforeEach(() => aTimeout(300));
it('expands the listbox', async function () {
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('focuses on the first item', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
it('does not scroll the screen', function () {
expect(window.scrollY).to.equal(0);
});
});
describe('Home', function () {
beforeEach(press('Home'));
beforeEach(updateComplete);
it('expands the listbox', async function () {
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('focuses on option 1', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
});
describe('End', function () {
beforeEach(press('End'));
beforeEach(updateComplete);
it('expands the listbox', async function () {
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('focuses on option 8', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('8');
});
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('expands the listbox', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('focuses on the first item', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
describe('ArrowUp', function () {
beforeEach(press('ArrowUp'));
beforeEach(updateComplete);
it('focuses on the last option', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('8');
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('focuses on option 1', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
});
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('focuses on option 2', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('2');
});
describe('ArrowUp', function () {
beforeEach(press('ArrowUp'));
beforeEach(updateComplete);
it('focuses on option 1', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('focuses on option 3', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('3');
});
describe('Enter', function () {
beforeEach(press('Enter'));
beforeEach(updateComplete);
it('selects option 3', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'3',
]);
});
it('exposes selection to assistive technology', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
value: '3',
});
});
it('hides the listbox', async function () {
expect(element.expanded).to.be.false;
expect(await a11ySnapshot()).to.not.axContainRole('listbox');
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('expands the listbox', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
describe('Home', function () {
beforeEach(press('Home'));
beforeEach(updateComplete);
it('focuses on option 1', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
describe('Home', function () {
beforeEach(press('Home'));
beforeEach(updateComplete);
it('focuses on option 1', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode.to.have.axName('1');
});
});
});
});
});
});
describe('Enter', function () {
beforeEach(press('Enter'));
beforeEach(updateComplete);
it('selects option 2', function () {
expect(getSelectedOptionValues(element)).to.deep.equal(['2']);
});
it('exposes selection to assistive technology', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
value: '2',
});
});
});
});
describe('Space', function () {
beforeEach(press(' '));
beforeEach(updateComplete);
it('hides the listbox', async function () {
expect(element.expanded).to.be.false;
expect(await a11ySnapshot()).to.not.axContainRole('listbox');
});
it('focuses the combobox toggle', async function () {
expect(await a11ySnapshot())
.axTreeFocusedNode
.to.have.axRole('combobox');
});
it('selects option 1', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
]);
});
it('exposes selection to assistive technology', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
value: '1',
});
});
});
describe('Tab', function () {
beforeEach(press('Tab'));
beforeEach(nextFrame);
beforeEach(updateComplete);
it('hides the listbox', async function () {
expect(element.expanded).to.be.false;
expect(await a11ySnapshot()).to.not.axContainRole('listbox');
});
it('does not focus on the combobox button', async function () {
expect(await a11ySnapshot()).to.not.have.axTreeFocusedNode;
});
});
describe('Shift+Tab', function () {
beforeEach(holdShift);
beforeEach(press('Tab'));
beforeEach(releaseShift);
beforeEach(updateComplete);
it('hides the listbox', async function () {
expect(element.expanded).to.be.false;
expect(await a11ySnapshot()).to.not.axContainRole('listbox');
});
it('focuses the button', async function () {
expect(await a11ySnapshot())
.axTreeFocusedNode
.to.have.axRole('combobox');
});
});
describe('Escape', function () {
beforeEach(press('Escape'));
beforeEach(nextFrame);
beforeEach(updateComplete);
it('hides the listbox', async function () {
expect(element.expanded).to.be.false;
expect(await a11ySnapshot()).to.not.axContainRole('listbox');
});
it('focuses the button', async function () {
expect(await a11ySnapshot())
.axTreeFocusedNode
.to.have.axRole('combobox');
});
});
});
});
});
});
describe('<pf-select variant="checkbox">', function () {
let element;
let items;
const updateComplete = () => element.updateComplete;
const focus = () => element.focus();
beforeEach(async function () {
element = await createFixture(html `
<pf-select variant="checkbox"
placeholder="placeholder"
accessible-label="Check it out">
<pf-option value="1">1</pf-option>
<pf-option value="2">2</pf-option>
<pf-option value="3">3</pf-option>
<pf-option value="4">4</pf-option>
<pf-option value="5">5</pf-option>
<pf-option value="6">6</pf-option>
<pf-option value="7">7</pf-option>
<pf-option value="8">8</pf-option>
</pf-select>`);
items = Array.from(element.querySelectorAll('pf-option'));
});
it('is accessible', async function () {
await expect(element).to.be.accessible();
});
it('does not have redundant role', async function () {
expect(element.shadowRoot?.firstElementChild).to.not.contain('[role="button"]');
});
describe('focus()', function () {
beforeEach(focus);
beforeEach(updateComplete);
describe('Enter', function () {
beforeEach(press('Enter'));
beforeEach(updateComplete);
it('expands the listbox', async function () {
expect(element.expanded).to.be.true;
const snapshot = await a11ySnapshot();
expect(snapshot.children?.at(1)).to.be.ok;
expect(snapshot.children?.at(1)?.role).to.equal('listbox');
});
it('should NOT use checkbox role for options', async function () {
const snapshot = await a11ySnapshot();
expect(snapshot.children?.at(1)?.children?.filter(x => x.role === 'checkbox')?.length)
.to.equal(0);
});
});
describe('Space', function () {
beforeEach(press(' '));
beforeEach(updateComplete);
it('expands the listbox', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('expands the listbox', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('focuses the placeholder', async function () {
expect(await a11ySnapshot()).to.have.axTreeFocusedNode.to.have.axName('placeholder');
});
describe('Shift+Tab', function () {
beforeEach(holdShift);
beforeEach(press('Tab'));
beforeEach(releaseShift);
beforeEach(updateComplete);
it('hides the listbox', async function () {
expect(element.expanded).to.be.false;
expect(await a11ySnapshot()).to.not.axContainRole('listbox');
});
it('focuses the button', async function () {
expect(await a11ySnapshot())
.axTreeFocusedNode
.to.have.axRole('combobox');
});
});
describe('Tab', function () {
beforeEach(press('Tab'));
beforeEach(nextFrame);
beforeEach(updateComplete);
// a little extra sleep to de-flake this test
beforeEach(nextFrame);
beforeEach(updateComplete);
it('closes', function () {
expect(element.expanded).to.be.false;
});
it('hides the listbox', async function () {
const snapshot = await a11ySnapshot();
const listbox = snapshot.children?.find(x => x.role === 'listbox');
expect(listbox).to.be.undefined;
});
});
describe('Escape', function () {
beforeEach(press('Escape'));
beforeEach(updateComplete);
it('closes', function () {
expect(element.expanded).to.be.false;
});
it('hides the listbox', async function () {
const snapshot = await a11ySnapshot();
expect(snapshot.children?.at(1)).to.be.undefined;
});
it('focuses the button', async function () {
const snapshot = await a11ySnapshot();
const focused = querySnapshot(snapshot, { focused: true });
expect(focused?.role).to.equal('combobox');
});
});
describe('Ctrl-A', function () {
beforeEach(holdCtrl);
beforeEach(press('A'));
beforeEach(releaseCtrl);
beforeEach(updateComplete);
it('selects all', function () {
expect(element.selected.length).to.equal(items.length);
});
it('remains expanded', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
describe('Ctrl-A', function () {
beforeEach(holdCtrl);
beforeEach(press('A'));
beforeEach(releaseCtrl);
beforeEach(updateComplete);
it('deselects all', function () {
expect(element.selected.length).to.equal(0);
});
it('remains expanded', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
});
});
describe('Space', function () {
it('does not select anything', function () {
expect(element.selected).to.deep.equal([]);
});
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
describe('Space', function () {
beforeEach(press(' '));
beforeEach(updateComplete);
it('selects option 1', function () {
// because the placeholder was focused
expect(getSelectedOptionValues(element)).to.deep.equal(['1']);
});
it('remains expanded', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('focuses option 2', async function () {
const snapshot = await a11ySnapshot();
expect(snapshot).to.have.axQuery({
focused: true,
name: '2',
});
});
describe('Enter', function () {
beforeEach(press('Enter'));
beforeEach(updateComplete);
it('adds option 2 to selection', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
'2',
]);
});
it('remains expanded', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
});
});
describe('holding Shift', function () {
beforeEach(holdShift);
afterEach(releaseShift);
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(nextFrame);
it('adds option 2 to selection', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
'2',
]);
});
describe('Enter', function () {
beforeEach(press('Enter'));
beforeEach(updateComplete);
it('makes no change', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
'2',
]);
});
beforeEach(updateComplete);
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));
beforeEach(updateComplete);
it('adds option 3 to the selected list', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
'2',
'3',
]);
});
describe('ArrowUp', function () {
beforeEach(press('Enter'));
beforeEach(updateComplete);
it('makes no change to selection', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
'2',
'3',
]);
});
});
});
});
});
});
});
});
describe('clicking the first item', function () {
beforeEach(() => clickElementAtCenter(items.at(0)));
beforeEach(updateComplete);
it('selects option 1', function () {
// because the placeholder was focused
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
]);
});
it('remains expanded', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
describe('holding Shift', function () {
beforeEach(holdShift);
afterEach(releaseShift);
describe('clicking the 7th item', function () {
beforeEach(() => clickElementAtCenter(items.at(6)));
it('remains expanded', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('selects items 1-7', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'1',
'2',
'3',
'4',
'5',
'6',
'7',
]);
});
describe('releasing Shift', function () {
beforeEach(releaseShift);
describe('clicking 6th item', function () {
beforeEach(() => clickElementAtCenter(items.at(5)));
it('deselects item 6', function () {
expect(getSelectedOptionValues(element)).to.not.contain('6');
});
describe('holding Shift', function () {
beforeEach(holdShift);
describe('clicking 2nd item', function () {
beforeEach(() => clickElementAtCenter(items.at(1)));
it('deselects items 2-6', function () {
expect(getSelectedOptionValues(element)).to.deep.equal(['1', '7']);
});
});
});
});
});
});
});
});
});
});
});
describe('<pf-select variant="typeahead">', function () {
let element;
const label = 'label';
const placeholder = 'placeholder';
const updateComplete = () => element.updateComplete;
const focus = () => element.focus();
beforeEach(async function () {
element = await createFixture(html `
<pf-select variant="typeahead">
<pf-option>Blue</pf-option>
<pf-option>Green</pf-option>
<pf-option>Magenta</pf-option>
<pf-option>Orange</pf-option>
<pf-option>Purple</pf-option>
<pf-option>Periwinkle</pf-option>
<pf-option>Pink</pf-option>
<pf-option>Red</pf-option>
<pf-option>Yellow</pf-option>
</pf-select>`);
});
beforeEach(nextFrame);
it('does not have redundant role', async function () {
expect(element.shadowRoot?.firstElementChild).to.not.contain('[role="button"]');
});
it('labels the combobox input with the first option', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
name: 'Blue',
});
});
describe('with an `accessible-label` attribute', function () {
beforeEach(function () {
element.setAttribute('accessible-label', label);
});
beforeEach(nextFrame);
it('passes aXe audit', async function () {
await expect(element).to.be.accessible();
});
it('does not have redundant role', async function () {
expect(element.shadowRoot?.firstElementChild).to.not.contain('[role="button"]');
});
it('labels the combobox with the label', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
name: label,
});
});
it('labels the toggle button with the label', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'button',
name: label,
});
});
describe('show()', function () {
beforeEach(() => element.show());
it('labels the listbox with the placeholder attribute', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'listbox',
name: 'label',
});
});
});
});
describe('with a `placeholder` attribute', function () {
beforeEach(function () {
element.setAttribute('placeholder', placeholder);
});
beforeEach(nextFrame);
it('passes aXe audit', async function () {
await expect(element).to.be.accessible();
});
it('lists the placeholder as first among the options', function () {
expect(element.options.at(0)).to.have.text(placeholder);
});
it('does not have redundant role', async function () {
expect(element.shadowRoot?.firstElementChild).to.not.contain('[role="button"]');
});
it('labels the combobox with the placeholder', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'combobox',
name: placeholder,
});
});
it('labels the toggle button with the placeholder', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'button',
name: placeholder,
});
});
describe('show()', function () {
beforeEach(() => element.show());
it('labels the listbox with the placeholder attribute', async function () {
expect(await a11ySnapshot()).to.axContainQuery({
role: 'listbox',
name: 'placeholder',
});
});
});
});
describe('clicking the toggle button', function () {
beforeEach(async function () {
await clickElementAtOffset(element, [-10, -10]);
});
it('shows the listbox', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('focuses the toggle button', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainQuery({
focused: true,
role: 'button',
});
});
});
describe('clicking the combobox input', function () {
beforeEach(async function () {
await clickElementAtOffset(element, [10, 10]);
});
it('shows the listbox', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainRole('listbox');
});
it('focuses the combobox', async function () {
expect(element.expanded).to.be.true;
expect(await a11ySnapshot()).to.axContainQuery({
focused: true,
role: 'combobox',
});
});
describe('clicking option 1', function () {
beforeEach(async function () {
await clickItemAtIndex(element, 0);
});
it('selects option 1', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'Blue',
]);
});
it('closes the listbox', async function () {
expect(await a11ySnapshot()).to.not.axContainRole('listbox');
});
});
describe('clicking option 2', function () {
beforeEach(async function () {
await clickItemAtIndex(element, 1);
});
it('selects option 1', function () {
expect(getSelectedOptionValues(element)).to.deep.equal([
'Green',
]);
});
it('closes the listbox', async function () {
expect(await a11ySnapshot()).to.not.axContainRole('listbox');
});
});
});
describe('focus()', function () {
beforeEach(focus);
beforeEach(updateComplete);
it('focuses the combobox input', async function () {
expect(await a11ySnapshot()).axTreeFocusedNode
.to.have.axRole('combobox')
.and
.to.have.axName('Blue');
});
describe('"r"', function () {
beforeEach(press('r'));
beforeEach(updateComplete);
it('only shows options that start with "r" or "R"', async function () {
expect(getVisibleOptionValues(element)).to.deep.equal([
'Red',
]);
});
});
describe('Space', function () {
beforeEach(function () {
document.body.style.height = '8000px';
});
afterEach(function () {
document.body.style.height = 'initial';
});
beforeEach(press(' '));
beforeEach(updateComplete);
beforeEach(() => aTimeout(300));
it('does not expand the listbox', async function () {
expect(await a11ySnapshot()).to.not.axContainRole('listbox');
});
it('does not scroll the screen', function () {
expect(window.scrollY).to.equal(0);
});
});
describe('ArrowDown', function () {
beforeEach(press('ArrowDown'));