Skip to content

Commit

Permalink
Fix visible commands for Appium. (nightwatchjs#3566)
Browse files Browse the repository at this point in the history
  • Loading branch information
garg3133 authored and harshit-bs committed Mar 16, 2023
1 parent 5ef19c6 commit 65b2055
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 15 deletions.
18 changes: 17 additions & 1 deletion lib/core/client.js
Expand Up @@ -10,6 +10,7 @@ const Transport = require('../transport');
const Element = require('../element');
const ApiLoader = require('../api');
const ElementGlobal = require('../api/_loaders/element-global.js');
const Factory = require('../transport/factory.js');
const {isAndroid, isIos} = require('../utils/mobile');

const {LocateStrategy, Locator} = Element;
Expand Down Expand Up @@ -76,7 +77,7 @@ class NightwatchAPI {
return false;
}

return this.platformName.toLowerCase() === platform.toLowerCase();
return this.platformName.toLowerCase() === platform.toLowerCase();
}

isIOS() {
Expand Down Expand Up @@ -114,6 +115,21 @@ class NightwatchAPI {
isOpera() {
return this.capabilities.browserName === Browser.OPERA;
}

isAppiumClient() {
if (this.options.selenium && this.options.selenium.use_appium) {
return true;
}

// Handle BrowserStack case
// (BrowserStack always returns platformName in capabilities)
const isMobile = this.__isPlatformName('android') || this.__isPlatformName('ios');
if (Factory.usingBrowserstack(this.options) && isMobile) {
return true;
}

return false;
}
}

class NightwatchClient extends EventEmitter {
Expand Down
31 changes: 31 additions & 0 deletions lib/element/appium-locator.js
@@ -0,0 +1,31 @@
const {By, RelativeBy} = require('selenium-webdriver');

const LocateElement = require('./locator.js');
const {AVAILABLE_LOCATORS} = LocateElement;

class AppiumLocator extends LocateElement {
/**
* @param {object|string} element
* @return {By|RelativeBy}
*/
static create(element) {
if (!element) {
throw new Error(`Error while trying to locate element: missing element definition; got: "${element}".`);
}

const byInstance = LocateElement.locateInstanceOfBy(element);
if (byInstance !== null) {
return byInstance;
}

const elementInstance = LocateElement.createElementInstance(element);

if (elementInstance.locateStrategy === 'id') {
return new By(elementInstance.locateStrategy, elementInstance.selector);
}

return By[AVAILABLE_LOCATORS[elementInstance.locateStrategy]](elementInstance.selector);
}
}

module.exports = AppiumLocator;
32 changes: 27 additions & 5 deletions lib/element/locator.js
Expand Up @@ -3,7 +3,7 @@ const Element = require('./index.js');
const ElementsByRecursion = require('./locate/elements-by-recursion.js');
const SingleElementByRecursion = require('./locate/single-element-by-recursion.js');

const availableLocators = {
const AVAILABLE_LOCATORS = {
'css selector': 'css',
'id': 'id',
'link text': 'linkText',
Expand All @@ -17,13 +17,28 @@ const availableLocators = {
class LocateElement {
/**
* @param {object|string} element
* @return {*}
* @return {By|RelativeBy}
*/
static create(element) {
if (!element) {
throw new Error(`Error while trying to locate element: missing element definition; got: "${element}".`);
}

const byInstance = LocateElement.locateInstanceOfBy(element);
if (byInstance !== null) {
return byInstance;
}

const elementInstance = LocateElement.createElementInstance(element);

return By[AVAILABLE_LOCATORS[elementInstance.locateStrategy]](elementInstance.selector);
}

/**
* @param {object} element
* @return {By|RelativeBy|null}
*/
static locateInstanceOfBy(element) {
if (element instanceof By) {
return element;
}
Expand All @@ -36,6 +51,14 @@ class LocateElement {
return element.value;
}

return null;
}

/**
* @param {object|string} element
* @return {Element}
*/
static createElementInstance(element) {
if (typeof element != 'object' && typeof element != 'string') {
throw new Error(`Invalid element definition type; expected string or object, but got: ${typeof element}.`);
}
Expand All @@ -50,9 +73,7 @@ class LocateElement {
selector = element;
}

const elementInstance = Element.createFromSelector(selector, strategy);

return By[availableLocators[elementInstance.locateStrategy]](elementInstance.selector);
return Element.createFromSelector(selector, strategy);
}

get api() {
Expand Down Expand Up @@ -316,3 +337,4 @@ class NoSuchElementError extends Error {

module.exports = LocateElement;
module.exports.NoSuchElementError = NoSuchElementError;
module.exports.AVAILABLE_LOCATORS = AVAILABLE_LOCATORS;
17 changes: 13 additions & 4 deletions lib/transport/selenium-webdriver/method-mappings.js
@@ -1,5 +1,6 @@
const {WebElement, WebDriver, Origin, By} = require('selenium-webdriver');
const {Locator} = require('../../element');
const AppiumLocator = require('../../element/appium-locator.js');
const {isString} = require('../../utils');
const fs = require('fs');
const cdp = require('./cdp.js');
Expand Down Expand Up @@ -367,7 +368,12 @@ module.exports = class MethodMappings {
},

async locateMultipleElements(element) {
const locator = Locator.create(element);
let locator;
if (this.transport.api.isAppiumClient()) {
locator = AppiumLocator.create(element);
} else {
locator = Locator.create(element);
}
const resultValue = await this.driver.findElements(locator);

if (Array.isArray(resultValue) && resultValue.length === 0) {
Expand Down Expand Up @@ -531,11 +537,14 @@ module.exports = class MethodMappings {
return `/element/${id}/location_in_view`;
},

async isElementDisplayed(webElementOrId) {
isElementDisplayed(webElementOrId) {
if (this.transport.api.isAppiumClient()) {
return `/element/${webElementOrId}/displayed`;
}

const element = this.getWebElement(webElementOrId);
const result = await element.isDisplayed();

return result;
return element.isDisplayed();
},

async isElementEnabled(webElementOrId) {
Expand Down
25 changes: 25 additions & 0 deletions test/lib/nocks.js
Expand Up @@ -138,6 +138,18 @@ module.exports = {
return this;
},

appiumElementFound() {
nock('http://localhost:10195')
.post('/wd/hub/session/1352110219202/elements', {'using': 'id', 'value': 'com.app:id/web-login'})
.reply(200, {
status: 0,
state: 'success',
value: [{'element-6066-11e4-a52e-4f735466cecf': '0'}]
});

return this;
},

click() {
nock('http://localhost:10195')
.post('/wd/hub/session/1352110219202/element/0/click')
Expand Down Expand Up @@ -370,6 +382,19 @@ module.exports = {
return this;
},

appiumElementVisible() {
nock('http://localhost:10195')
.get('/wd/hub/session/1352110219202/element/0/displayed')
.reply(200, {
status: 0,
sessionId: '1352110219202',
value: true,
state: 'success'
});

return this;
},

notVisible(times) {
var mock = nock('http://localhost:10195')
.post('/wd/hub/session/1352110219202/execute/sync');
Expand Down
84 changes: 82 additions & 2 deletions test/src/api/commands/element/testIsVisible.js
Expand Up @@ -12,13 +12,21 @@ describe('isVisible', function () {
url: '/wd/hub/session/1352110219202/execute/sync',
method: 'POST'
});
MockServer.removeMock({
url: '/wd/hub/session/1352110219202/elements',
method: 'POST'
});
MockServer.removeMock({
url: '/wd/hub/session/1352110219202/element/999/displayed',
method: 'GET'
});
});

after(function (done) {
CommandGlobals.afterEach.call(this, done);
});

it('client.isVisible()', function (done) {
it('client.isVisible() [visible]', function (done) {
MockServer.addMock({
url: '/wd/hub/session/1352110219202/execute/sync',
method: 'POST',
Expand All @@ -38,7 +46,7 @@ describe('isVisible', function () {
this.client.start(done);
});

it('client.isVisible()', function (done) {
it('client.isVisible() [not visible]', function (done) {
MockServer.addMock({
url: '/wd/hub/session/1352110219202/execute/sync',
method: 'POST',
Expand All @@ -57,4 +65,76 @@ describe('isVisible', function () {

this.client.start(done);
});

it('client.isVisible() [visible] -- appium', function (done) {
MockServer
.addMock({
url: '/wd/hub/session/1352110219202/elements',
postdata: {
using: 'id',
value: 'com.app:id/weblogin'
},
method: 'POST',
response: JSON.stringify({
status: 0,
state: 'success',
value: [{'element-6066-11e4-a52e-4f735466cecf': '999'}]
})
})
.addMock({
url: '/wd/hub/session/1352110219202/element/999/displayed',
method: 'GET',
response: JSON.stringify({
value: true
})
});

// Make appium client
this.client.api.options.selenium.use_appium = true;

this.client.api.isVisible('id', 'com.app:id/weblogin', function callback(result) {
assert.strictEqual(result.value, true);
}).isVisible({selector: 'com.app:id/weblogin', locateStrategy: 'id'}, function callback(result) {
assert.strictEqual(result.value, true);
});

this.client.start(done);
});

it('client.isVisible() [not visible] -- appium', function (done) {
MockServer
.addMock({
url: '/wd/hub/session/1352110219202/elements',
postdata: {
using: 'id',
value: 'com.app:id/weblogin'
},
method: 'POST',
response: JSON.stringify({
status: 0,
state: 'success',
value: [{'element-6066-11e4-a52e-4f735466cecf': '999'}]
})
})
.addMock({
url: '/wd/hub/session/1352110219202/element/999/displayed',
method: 'GET',
response: JSON.stringify({
value: false
})
});

// Make appium client
this.client.api.options.selenium.use_appium = true;

assert.strictEqual(this.client.api.isAppiumClient(), true);

this.client.api.isVisible('id', 'com.app:id/weblogin', function callback(result) {
assert.strictEqual(result.value, false);
}).isVisible({selector: 'com.app:id/weblogin', locateStrategy: 'id'}, function callback(result) {
assert.strictEqual(result.value, false);
});

this.client.start(done);
});
});
42 changes: 42 additions & 0 deletions test/src/api/commands/element/testWaitForElementVisible.js
Expand Up @@ -336,4 +336,46 @@ describe('waitForElementVisible', function () {
});
});

it('client.waitForElementVisible() success with appium client', function () {
MockServer
.addMock({
url: '/wd/hub/session/1352110219202/elements',
postdata: {
using: 'id',
value: 'com.app:id/web-login'
},
method: 'POST',
response: JSON.stringify({
status: 0,
state: 'success',
value: [{'element-6066-11e4-a52e-4f735466cecf': '99'}]
})
}, undefined, true)
.addMock({
url: '/wd/hub/session/1352110219202/element/99/displayed',
method: 'GET',
response: JSON.stringify({
value: true
})
}, undefined, true);

// Make appium client
this.client.api.options.selenium.use_appium = true;

assert.strictEqual(this.client.api.isAppiumClient(), true);

this.client.api.waitForElementVisible('id', 'com.app:id/web-login', function callback(result, instance) {
assert.strictEqual(instance.elementId, '99');
assert.strictEqual(result.value, true);
}).waitForElementVisible({selector: 'com.app:id/web-login', locateStrategy: 'id'}, function callback(result, instance) {
assert.strictEqual(instance.elementId, '99');
assert.strictEqual(result.value, true);
});

return this.client.start(function (err) {
if (err) {
throw err;
}
});
});
});

0 comments on commit 65b2055

Please sign in to comment.