Skip to content

Commit

Permalink
Add isPresent command to new Element API. (#4216)
Browse files Browse the repository at this point in the history
Co-authored-by: Priyansh Garg <[email protected]>
  • Loading branch information
dikwickley and garg3133 committed Aug 12, 2024
1 parent 5f0ed3e commit 3a83dda
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 5 deletions.
32 changes: 32 additions & 0 deletions lib/api/web-element/commands/isPresent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Checks if an element is present in the DOM.
*
* This command is useful for verifying the presence of elements that may not be visible or interactable.
*
* For more information on working with DOM elements in Nightwatch, refer to the <a href="https://nightwatchjs.org/guide/working-with-page-elements/finding-elements.html">Finding Elements</a> guide page.
*
* @example
* describe('isPresent Demo', function() {
* it('test isPresent', function(browser) {
* browser.element('#search')
* .isPresent()
* .assert.equals(true);
* });
*
* it('test async isPresent', async function(browser) {
* const result = await browser.element('#search').isPresent();
* browser.assert.equal(result, true);
* });
* });
*
* @since 3.7.1
* @method isPresent
* @memberof ScopedWebElement
* @instance
* @syntax browser.element(selector).isPresent()
* @returns {ScopedValue<boolean>} A boolean value indicating if the element is present in the DOM.
*/

module.exports.command = function () {
return this.runQueuedCommandScoped('isElementPresent', {suppressNotFoundErrors: true});
};
18 changes: 15 additions & 3 deletions lib/api/web-element/scoped-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ class ScopedWebElement {
return true;
}

get suppressNotFoundErrors() {
return this._suppressNotFoundErrors;
}

constructor(selector = 'html', parentElement, nightwatchInstance) {
this.nightwatchInstance = nightwatchInstance;
this.parentScopedElement = parentElement;
Expand Down Expand Up @@ -157,7 +161,7 @@ class ScopedWebElement {
return webElements[index];
}

async findElementAction({parentElement, condition, index, timeout, retryInterval, abortOnFailure, suppressNotFoundErrors}) {
async findElementAction({parentElement, condition, index, timeout, retryInterval, abortOnFailure}) {
const createAction = () => async ({args}) => {
if ((args[0] instanceof Promise) && !args[0]['@nightwatch_element']) {
args[0] = await args[0];
Expand All @@ -172,7 +176,7 @@ class ScopedWebElement {

return await this.findElement({parentElement, selector: condition, index, timeout, retryInterval});
} catch (error) {
if (suppressNotFoundErrors) {
if (this.suppressNotFoundErrors) {
return null;
}

Expand Down Expand Up @@ -241,8 +245,12 @@ class ScopedWebElement {
return resolve(condition);
}

if (suppressNotFoundErrors) {
this._suppressNotFoundErrors = true;
}

const webElement = await this.findElementAction({
parentElement, condition, index, timeout, retryInterval, abortOnFailure, suppressNotFoundErrors
parentElement, condition, index, timeout, retryInterval, abortOnFailure
});

resolve(webElement);
Expand Down Expand Up @@ -273,6 +281,10 @@ class ScopedWebElement {
}

createNode(commandName, args) {
if (args[0]?.suppressNotFoundErrors) {
this._suppressNotFoundErrors = true;
}

const createAction = (actions, webElement) => function () {
if (isFunction(commandName)) {
return commandName(webElement, ...args).then((result) => {
Expand Down
4 changes: 2 additions & 2 deletions lib/core/treenode.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ class TreeNode {
err.abortOnFailure = err.abortOnFailure || err.abortOnFailure === undefined;
}

let errorName = err.name !== 'Error' ? `[${err.name}] ` : '';
let originalError = `${errorName}${err.message}`;
const errorName = err.name !== 'Error' ? `[${err.name}] ` : '';
const originalError = `${errorName}${err.message}`;

if (this.stackTrace && Utils.shouldReplaceStack(err)) {
err.stack = this.stackTrace;
Expand Down
8 changes: 8 additions & 0 deletions lib/transport/selenium-webdriver/method-mappings.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const {WebElement, WebDriver, Origin, By, until, Condition, Key} = require('selenium-webdriver');
const {ShadowRoot} = require('selenium-webdriver/lib/webdriver');
const {Locator} = require('../../element');
const NightwatchLocator = require('../../element/locator-factory.js');
const {isString} = require('../../utils');
Expand Down Expand Up @@ -583,6 +584,13 @@ module.exports = class MethodMappings {
return value;
},

async isElementPresent(webElement) {
// webElement would be a Promise in case of new Element API.
const element = await webElement;

return element instanceof WebElement || element instanceof ShadowRoot;
},

async clearElementValue(webElementOrId) {
const element = this.getWebElement(webElementOrId);
await element.clear();
Expand Down
6 changes: 6 additions & 0 deletions test/sampletests/isPresent/elementNotPresent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
describe('test', function () {
test('test isPresent', async (browser) => {
browser
.element('#wrong').isPresent().assert.equals(false);
});
});
109 changes: 109 additions & 0 deletions test/src/api/commands/web-element/testIsPresent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
const assert = require('assert');
const {WebElement} = require('selenium-webdriver');
const path = require('path');
const MockServer = require('../../../../lib/mockserver.js');
const CommandGlobals = require('../../../../lib/globals/commands-w3c.js');
const common = require('../../../../common.js');
const Element = common.require('element/index.js');
const Utils = common.require('./utils');
const NightwatchClient = common.require('index.js');
const {settings} = common;

describe('element().isPresent() command', function() {
before(function (done) {
CommandGlobals.beforeEach.call(this, done);

});

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

it('test .element().isPresent() present', async function() {
const resultPromise = this.client.api.element('#signupSection').isPresent();
assert.strictEqual(resultPromise instanceof Element, false);
assert.strictEqual(typeof resultPromise.find, 'undefined');

assert.strictEqual(resultPromise instanceof Promise, false);
assert.strictEqual(typeof resultPromise.then, 'function');

const result = await resultPromise;
assert.strictEqual(result instanceof WebElement, false);
assert.strictEqual(result, true);

});

it('test .element().isPresent() not present', async function() {
const resultPromise = this.client.api.element('#wrong').isPresent();
assert.strictEqual(resultPromise instanceof Element, false);
assert.strictEqual(typeof resultPromise.find, 'undefined');

assert.strictEqual(resultPromise instanceof Promise, false);
assert.strictEqual(typeof resultPromise.then, 'function');

const result = await resultPromise;
assert.strictEqual(result instanceof WebElement, false);
assert.strictEqual(result, false);

});

it('test .element().find().isPresent() present', async function() {
const resultPromise = this.client.api.element('#signupSection').find('#helpBtn').isPresent();
assert.strictEqual(resultPromise instanceof Element, false);
assert.strictEqual(typeof resultPromise.find, 'undefined');

assert.strictEqual(resultPromise instanceof Promise, false);
assert.strictEqual(typeof resultPromise.then, 'function');

const result = await resultPromise;
assert.strictEqual(result instanceof WebElement, false);
assert.strictEqual(result, true);
});

it('test .element().find().isPresent() not present', async function() {
const resultPromise = this.client.api.element('#signupSection').find('#wrong').isPresent();
assert.strictEqual(resultPromise instanceof Element, false);
assert.strictEqual(typeof resultPromise.find, 'undefined');

assert.strictEqual(resultPromise instanceof Promise, false);
assert.strictEqual(typeof resultPromise.then, 'function');

const result = await resultPromise;
assert.strictEqual(result instanceof WebElement, false);
assert.strictEqual(result, false);
});

it('test .element().isPresent() suppresses NoSuchElementError', async function() {
MockServer.addMock({
url: '/session/13521-10219-202/elements',
method: 'POST',
postdata: JSON.stringify({using: 'css selector', value: '#wrong'}),
response: JSON.stringify({
value: []
})
});

let globalReporterCalled = false;

const globals = {
reporter(results) {
globalReporterCalled = true;
if (Object.prototype.hasOwnProperty.call(results, 'lastError')) {
assert.notStrictEqual(results.lastError.name, 'NoSuchElementError');
}
},
waitForConditionTimeout: 100
};
const testsPath = [
path.join(__dirname, '../../../../sampletests/isPresent/elementNotPresent.js')
];

await NightwatchClient.runTests(testsPath, settings({
globals,
output_folder: 'output',
selenium_host: null
}));

assert.strictEqual(globalReporterCalled, true);
});
});

0 comments on commit 3a83dda

Please sign in to comment.