diff --git a/lib/api/web-element/commands/isPresent.js b/lib/api/web-element/commands/isPresent.js new file mode 100644 index 0000000000..2a9cbfc240 --- /dev/null +++ b/lib/api/web-element/commands/isPresent.js @@ -0,0 +1,30 @@ +/** + * 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 Finding Elements 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.5.0 + * @method isPresent + * @memberof ScopedWebElement + * @instance + * @syntax browser.element(selector).isPresent() + * @returns {ScopedValue} A boolean value indicating if the element is present in the DOM. + */ +module.exports.command = function () { + return this.runQueuedCommandScoped('isElementPresent'); +}; diff --git a/lib/transport/selenium-webdriver/method-mappings.js b/lib/transport/selenium-webdriver/method-mappings.js index e91462fef5..aa92c5d27a 100644 --- a/lib/transport/selenium-webdriver/method-mappings.js +++ b/lib/transport/selenium-webdriver/method-mappings.js @@ -1,4 +1,5 @@ const {WebElement, WebDriver, Origin, By, until, Condition} = 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'); @@ -583,6 +584,22 @@ module.exports = class MethodMappings { return value; }, + async isElementPresent(webElement) { + try { + // New element apis contains only webElement argument + const element = await webElement; + + return element instanceof WebElement || element instanceof ShadowRoot; + } catch (error) { + if (error.name === 'NoSuchElementError') { + return false; // Element does not exist + } + throw error; // Re-throw other errors for further handling + } + }, + + + async clearElementValue(webElementOrId) { const element = this.getWebElement(webElementOrId); await element.clear(); diff --git a/test/src/api/commands/web-element/testIsPresent.js b/test/src/api/commands/web-element/testIsPresent.js new file mode 100644 index 0000000000..85439cba35 --- /dev/null +++ b/test/src/api/commands/web-element/testIsPresent.js @@ -0,0 +1,55 @@ +const assert = require('assert'); +const CommandGlobals = require('../../../../lib/globals/commands-w3c.js'); +const common = require('../../../../common.js'); +const Element = common.require('element/index.js'); + +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() for element 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, true, 'Expected element to be present in the DOM'); + }); + + it('test .element().isPresent() for element not present', async function() { + + const resultPromise = this.client.api.element('#signupSecti').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, false, 'Expected element not to be present in the DOM'); + }); + + // Example of a test for an async scenario using .isPresent() + it('test async .element().isPresent() for element present', async function() { + + const result = await this.client.api.element('#signupSection').isPresent(); + assert.strictEqual(result, true, 'Expected element to be present in the DOM using async/await'); + }); + + // Test to ensure .isPresent() correctly handles WebDriver errors + it('test .element().isPresent() handles errors', async function() { + + const resultPromise = this.client.api.element('#badElement').isPresent(); + const result = await resultPromise; + assert.strictEqual(result, false, 'Expected .isPresent() to gracefully handle WebDriver errors'); + }); +}); diff --git a/types/tests/webElement.test-d.ts b/types/tests/webElement.test-d.ts index f179296b6f..4870827a20 100644 --- a/types/tests/webElement.test-d.ts +++ b/types/tests/webElement.test-d.ts @@ -140,6 +140,7 @@ describe('new element() api', function () { expectType>(elem.getProperty('property-name')); expectType>(elem.getAttribute('attrib-name')); expectType>(elem.getValue()); + expectType>(elem.isPresent()); expectType>(elem.isEnabled()); expectType>(elem.getRect()); diff --git a/types/web-element.d.ts b/types/web-element.d.ts index ea28dc8516..205b7b5476 100644 --- a/types/web-element.d.ts +++ b/types/web-element.d.ts @@ -183,9 +183,12 @@ export interface ScopedElement extends Element, PromiseLike { clickAndHold(): Promise; + doubleClick(): Promise; rightClick(): Promise; + + isPresent(): ElementValue; isSelected(): ElementValue;