diff --git a/lib/element/appium-locator.js b/lib/element/appium-locator.js index 6caf9e06f6..91539bbcd4 100644 --- a/lib/element/appium-locator.js +++ b/lib/element/appium-locator.js @@ -1,7 +1,7 @@ const {By, RelativeBy} = require('selenium-webdriver'); const LocateElement = require('./locator.js'); -const {AVAILABLE_LOCATORS} = LocateElement; +const LocateStrategy = require('./strategy.js'); class AppiumLocator extends LocateElement { /** @@ -20,11 +20,9 @@ class AppiumLocator extends LocateElement { const elementInstance = LocateElement.createElementInstance(element); - if (elementInstance.locateStrategy === 'id') { - return new By(elementInstance.locateStrategy, elementInstance.selector); - } + LocateStrategy.validate(elementInstance.locateStrategy); - return By[AVAILABLE_LOCATORS[elementInstance.locateStrategy]](elementInstance.selector); + return new By(elementInstance.locateStrategy, elementInstance.selector); } } diff --git a/lib/element/locator-factory.js b/lib/element/locator-factory.js new file mode 100644 index 0000000000..b5532fa097 --- /dev/null +++ b/lib/element/locator-factory.js @@ -0,0 +1,21 @@ +const {By, RelativeBy} = require('selenium-webdriver'); + +const Locator = require('./locator.js'); +const AppiumLocator = require('./appium-locator.js'); + +class NightwatchLocator { + /** + * @param {object|string} element + * @param {boolean} isAppiumClient + * @return {By|RelativeBy} + */ + static create(element, isAppiumClient) { + if (isAppiumClient) { + return AppiumLocator.create(element); + } + + return Locator.create(element); + } +} + +module.exports = NightwatchLocator; diff --git a/lib/element/locator.js b/lib/element/locator.js index bd5dd56a65..089594dbc1 100644 --- a/lib/element/locator.js +++ b/lib/element/locator.js @@ -337,4 +337,3 @@ class NoSuchElementError extends Error { module.exports = LocateElement; module.exports.NoSuchElementError = NoSuchElementError; -module.exports.AVAILABLE_LOCATORS = AVAILABLE_LOCATORS; diff --git a/lib/element/strategy.js b/lib/element/strategy.js index b768f6bc58..27e5110cac 100644 --- a/lib/element/strategy.js +++ b/lib/element/strategy.js @@ -5,10 +5,17 @@ class LocateStrategy { return { ID: 'id', CSS_SELECTOR: 'css selector', - LINK_TEST: 'link text', + LINK_TEXT: 'link text', PARTIAL_LINK_TEXT: 'partial link text', TAG_NAME: 'tag name', - XPATH: 'xpath' + XPATH: 'xpath', + NAME: 'name', + CLASS_NAME: 'class name', + // Appium-specific strategies + ACCESSIBILITY_ID: 'accessibility id', + ANDROID_UIAUTOMATOR: '-android uiautomator', + IOS_PREDICATE_STRING: '-ios predicate string', + IOS_CLASS_CHAIN: '-ios class chain' }; } diff --git a/lib/transport/selenium-webdriver/method-mappings.js b/lib/transport/selenium-webdriver/method-mappings.js index 3a01e22ac8..30dfc8e315 100644 --- a/lib/transport/selenium-webdriver/method-mappings.js +++ b/lib/transport/selenium-webdriver/method-mappings.js @@ -1,6 +1,6 @@ const {WebElement, WebDriver, Origin, By} = require('selenium-webdriver'); const {Locator} = require('../../element'); -const AppiumLocator = require('../../element/appium-locator.js'); +const NightwatchLocator = require('../../element/locator-factory.js'); const {isString} = require('../../utils'); const fs = require('fs'); const cdp = require('./cdp.js'); @@ -356,7 +356,7 @@ module.exports = class MethodMappings { // Elements /////////////////////////////////////////////////////////// async locateSingleElement(element) { - const locator = Locator.create(element); + const locator = NightwatchLocator.create(element, this.transport.api.isAppiumClient()); const webElement = await this.driver.findElement(locator); const elementId = await webElement.getId(); @@ -368,12 +368,7 @@ module.exports = class MethodMappings { }, async locateMultipleElements(element) { - let locator; - if (this.transport.api.isAppiumClient()) { - locator = AppiumLocator.create(element); - } else { - locator = Locator.create(element); - } + const locator = NightwatchLocator.create(element, this.transport.api.isAppiumClient()); const resultValue = await this.driver.findElements(locator); if (Array.isArray(resultValue) && resultValue.length === 0) { diff --git a/lib/utils/locatestrategy.js b/lib/utils/locatestrategy.js index 405d8cf459..65e8862a85 100644 --- a/lib/utils/locatestrategy.js +++ b/lib/utils/locatestrategy.js @@ -5,10 +5,17 @@ class LocateStrategy { return { ID: 'id', CSS_SELECTOR: 'css selector', - LINK_TEST: 'link text', + LINK_TEXT: 'link text', PARTIAL_LINK_TEXT: 'partial link text', TAG_NAME: 'tag name', - XPATH: 'xpath' + XPATH: 'xpath', + NAME: 'name', + CLASS_NAME: 'class name', + // Appium-specific strategies + ACCESSIBILITY_ID: 'accessibility id', + ANDROID_UIAUTOMATOR: '-android uiautomator', + IOS_PREDICATE_STRING: '-ios predicate string', + IOS_CLASS_CHAIN: '-ios class chain' }; } diff --git a/test/apidemos/appium/appiumTest.js b/test/apidemos/appium/appiumTest.js index 059fd0f8dd..2b5e253a8a 100644 --- a/test/apidemos/appium/appiumTest.js +++ b/test/apidemos/appium/appiumTest.js @@ -31,7 +31,9 @@ describe('appium api demo', function () { it('Search for Nightwatch', async function() { app // available globally - .click('id', 'com.app:id/search') + .waitForElementPresent({selector: 'Search Wikipedia', locateStrategy: 'accessibility id'}) + .click('accessibility id', 'Search Wikipedia') + .element('class name', 'android.widget.ImageButton') .sendKeys('id', 'com.app:id/search', 'Nightwatch'); }); }); diff --git a/test/src/apidemos/appium/testAppiumAPI.js b/test/src/apidemos/appium/testAppiumAPI.js index 47dfc7d2b3..2aea0c0594 100644 --- a/test/src/apidemos/appium/testAppiumAPI.js +++ b/test/src/apidemos/appium/testAppiumAPI.js @@ -46,8 +46,8 @@ describe('appium api demos', function () { .addMock({ url: '/wd/hub/session/13521-10219-202/elements', postdata: { - using: 'id', - value: 'com.app:id/search' + using: 'accessibility id', + value: 'Search Wikipedia' }, method: 'POST', response: JSON.stringify({ @@ -56,6 +56,32 @@ describe('appium api demos', function () { value: [{'element-6066-11e4-a52e-4f735466cecf': '0'}] }) }, true, true) + .addMock({ + url: '/wd/hub/session/13521-10219-202/element', + postdata: { + using: 'class name', + value: 'android.widget.ImageButton' + }, + method: 'POST', + response: JSON.stringify({ + status: 0, + state: 'success', + value: {'element-6066-11e4-a52e-4f735466cecf': '1'} + }) + }, true) + .addMock({ + url: '/wd/hub/session/13521-10219-202/elements', + postdata: { + using: 'id', + value: 'com.app:id/search' + }, + method: 'POST', + response: JSON.stringify({ + status: 0, + state: 'success', + value: [{'element-6066-11e4-a52e-4f735466cecf': '2'}] + }) + }, true) .addMock({ url: '/wd/hub/session/13521-10219-202/element/0/click', method: 'POST', @@ -64,7 +90,7 @@ describe('appium api demos', function () { }) }, true) .addMock({ - url: '/wd/hub/session/13521-10219-202/element/0/value', + url: '/wd/hub/session/13521-10219-202/element/2/value', method: 'POST', postdata: JSON.stringify({ text: 'Nightwatch',