Man, I know the struggle. Here is how I solved this:
- First problem was to locate the elements. Locators always change. Each dropdown is attached separately to the HTML and not removed after you close it. This is why you can't just say 'use first occurrence that matches the locator'
- Second problem was actual logic. Sometimes you type, and the location dropdown is not prepopulated. So you have to retry. To be completely honest, this is the ugliest solution in my frameworks, but it does what it supposed to do
class Common {
constructor() {
/**
* Elements
*/
this.$locationDropdownContainer = element.all(by.xpath('//div[contains(@class,"pac-container") and not(contains(@style,"width: 0px")) and not(contains(@style,"display: none"))]')).last();
this.$locationInput = $("#location");
this.$$locationOptions = $$(".pac-container .pac-item .pac-item-query");
}
/**
* Types provided location and selects the first option
* @param criteria
* @param [retry=true] retry on failure
* @returns
*/
async enterLocation(criteria, retry = true) {
log(`enterLocation(${criteria},${retry})`);
await browser.sleep(350);
await sendKeys(this.$locationInput, criteria);
await browser.sleep(400);
try {
await browser.wait(ExpectedConditions.visibilityOf(this.$locationDropdownContainer));
} catch (e) {
await this.$locationInput.click();
try {
await browser.wait(ExpectedConditions.visibilityOf(this.$locationDropdownContainer));
} catch (e) {
if (retry) {
this.enterLocation(criteria, false);
}
}
}
await this.$$locationOptions.get(0).click();
await browser.sleep(350);
}
}
Where sendKeys
is just a custom method for typing, you can use input.sendKeys()
And waitUntilElementHasAttribute
is a method to wait until an element has certain substring in an attribute. In this case, location Dropdown Container (wrapper element) should not have "style" attribute containing "display: none"
As can be seen from JSDoc description I select first option, but it's possible to figure out a way for selecting particular one