diff --git a/docs/basics.md b/docs/basics.md index c6b7121f3..18850f2c4 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -241,6 +241,13 @@ I.uncheckOption('Subscribe') > [selectOption](/web-api#iselectoption) works with native ` +
+ + + + + diff --git a/test/data/app/view/form/combobox/baseui.php b/test/data/app/view/form/combobox/baseui.php new file mode 100644 index 000000000..a3dbe9ff9 --- /dev/null +++ b/test/data/app/view/form/combobox/baseui.php @@ -0,0 +1,60 @@ + + + + + + Base UI Combobox + + + + +

Base UI Combobox

+
+ +
+ +
+ + + diff --git a/test/helper/webapi.js b/test/helper/webapi.js index 40b51c3c3..0c08c4fb1 100644 --- a/test/helper/webapi.js +++ b/test/helper/webapi.js @@ -949,6 +949,80 @@ export function tests() { }) }) + describe('#fillField - comboboxes', function () { + this.timeout(60000) + + async function open(page, initial) { + const q = initial != null ? `?initial=${encodeURIComponent(initial)}` : '' + await I.amOnPage(`/form/combobox/${page}${q}`) + await I.waitForFunction(() => window.__ready === true, [], 30) + } + + async function outerTitleValue() { + return I.executeScript(() => document.getElementById('outer-title').value) + } + + async function comboInputValue() { + return I.executeScript(() => { + const inputs = [...document.querySelectorAll('input[role="combobox"]')] + return inputs.length ? inputs[inputs.length - 1].value : null + }) + } + + async function visibleOptions() { + return I.executeScript(() => + [...document.querySelectorAll('[role="option"]')] + .filter(o => o.getClientRects().length) + .map(o => o.textContent.trim()), + ) + } + + for (const page of ['baseui', 'baseui-inline']) { + describe(page, () => { + it('types into the combobox and filters its options', async () => { + await open(page) + await I.fillField('Country', 'United') + expect(await comboInputValue()).to.equal('United') + expect(await visibleOptions()).to.deep.equal(['United Kingdom', 'United States']) + }) + + it('submits the option picked after filtering', async () => { + await open(page) + await I.fillField('Country', 'Ukr') + await I.click('Ukraine') + await I.click('#submit') + await I.waitForElement('#result', 15) + expect(await I.grabTextFrom('#result')).to.include('Ukraine') + }) + + it('rewrites a pre-populated value', async () => { + await open(page, 'Portugal') + await I.fillField('Country', 'France') + expect(await comboInputValue()).to.equal('France') + }) + + it('does not leak keystrokes to the outer focused input', async () => { + await open(page) + await I.click('#outer-title') + await I.fillField('Country', 'United') + expect(await outerTitleValue()).to.equal('') + }) + }) + } + + it('fails with a helpful message when the combobox has no text input', async () => { + await I.amOnPage('/form/custom_select') + let message = '' + try { + await I.fillField('Country', 'London') + } catch (e) { + message = e.message + } + expect(message).to.include('no text input') + expect(message).to.include('selectOption') + }) + }) + describe('#clearField', () => { it('should clear a given element', async () => { await I.amOnPage('/form/field') diff --git a/test/unit/locator_test.js b/test/unit/locator_test.js index 9c59216ae..d55b5e205 100644 --- a/test/unit/locator_test.js +++ b/test/unit/locator_test.js @@ -808,4 +808,41 @@ describe('Locator', () => { expect(items[0].getAttribute('id')).to.eql('rename') }) }) + describe('Locator.field', () => { + const comboboxXml = ` + + + + ` + + it('skips the aria-hidden proxy input a label points at', () => { + const comboboxDoc = new DOMParser().parseFromString(comboboxXml, 'text/xml') + const root = xpath.select1('//root', comboboxDoc) + + expect(xpath.select(Locator.field.labelEquals("'Country'"), root)).to.have.length(0) + expect(xpath.select(Locator.field.byName("'country'"), root)).to.have.length(0) + }) + + it('resolves the labelled combobox once the proxy is skipped', () => { + const comboboxDoc = new DOMParser().parseFromString(comboboxXml, 'text/xml') + const root = xpath.select1('//root', comboboxDoc) + + const nodes = xpath.select(Locator.field.labelContains("'Country'"), root) + expect(nodes).to.have.length(1) + expect(nodes[0].getAttribute('id')).to.eql('trigger') + }) + + it('still matches a regular labelled input', () => { + const formXml = ` + + + ` + const formDoc = new DOMParser().parseFromString(formXml, 'text/xml') + const root = xpath.select1('//root', formDoc) + + const nodes = xpath.select(Locator.field.labelEquals("'Name'"), root) + expect(nodes).to.have.length(1) + expect(nodes[0].getAttribute('id')).to.eql('name') + }) + }) })