diff --git a/lib/api/element-commands/check.js b/lib/api/element-commands/check.js
new file mode 100644
index 000000000..e985f74f6
--- /dev/null
+++ b/lib/api/element-commands/check.js
@@ -0,0 +1,64 @@
+const BaseElementCommand = require('./_baseElementCommand.js');
+
+/**
+ * Will check, by clicking, on a checkbox or radio input if it is not already checked.
+ *
+ * @example
+ * module.exports = {
+ * demoTest(browser) {
+ * browser.check('input[type=checkbox]:not(:checked)');
+ *
+ * browser.check('input[type=checkbox]:not(:checked)', function(result) {
+ * console.log('Check result', result);
+ * });
+ *
+ * // with explicit locate strategy
+ * browser.check('css selector', 'input[type=checkbox]:not(:checked)');
+ *
+ * // with selector object - see https://nightwatchjs.org/guide#element-properties
+ * browser.check({
+ * selector: 'input[type=checkbox]:not(:checked)',
+ * index: 1,
+ * suppressNotFoundErrors: true
+ * });
+ *
+ * browser.check({
+ * selector: 'input[type=checkbox]:not(:checked)',
+ * timeout: 2000 // overwrite the default timeout (in ms) to check if the element is present
+ * });
+ * },
+ *
+ * demoTestAsync: async function(browser) {
+ * const result = await browser.check('input[type=checkbox]:not(:checked)');
+ * console.log('Check result', result);
+ * }
+ * }
+ *
+ * @method check
+ * @syntax .check(selector, [callback])
+ * @syntax .check(using, selector, [callback])
+ * @syntax browser.element(selector).check()
+ * @param {string} [using] The locator strategy to use. See [W3C Webdriver - locator strategies](https://www.w3.org/TR/webdriver/#locator-strategies)
+ * @param {string} selector The CSS/Xpath selector used to locate the element.
+ * @param {function} [callback] Optional callback function to be called when the command finishes.
+ * @api protocol.elementinteraction
+ */
+class CheckElement extends BaseElementCommand {
+ get extraArgsCount() {
+ return 0;
+ }
+
+ get elementProtocolAction() {
+ return 'checkElement';
+ }
+
+ static get isTraceable() {
+ return true;
+ }
+
+ async protocolAction() {
+ return this.executeProtocolAction(this.elementProtocolAction);
+ }
+}
+
+module.exports = CheckElement;
diff --git a/lib/api/element-commands/uncheck.js b/lib/api/element-commands/uncheck.js
new file mode 100644
index 000000000..888f455a3
--- /dev/null
+++ b/lib/api/element-commands/uncheck.js
@@ -0,0 +1,64 @@
+const BaseElementCommand = require('./_baseElementCommand.js');
+
+/**
+ * Will uncheck, by clicking, on a checkbox or radio input if it is not already unchecked.
+ *
+ * @example
+ * module.exports = {
+ * demoTest(browser) {
+ * browser.uncheck('input[type=checkbox]:checked)');
+ *
+ * browser.uncheck('input[type=checkbox]:checked)', function(result) {
+ * console.log('Check result', result);
+ * });
+ *
+ * // with explicit locate strategy
+ * browser.uncheck('css selector', 'input[type=checkbox]:checked)');
+ *
+ * // with selector object - see https://nightwatchjs.org/guide#element-properties
+ * browser.uncheck({
+ * selector: 'input[type=checkbox]:checked)',
+ * index: 1,
+ * suppressNotFoundErrors: true
+ * });
+ *
+ * browser.uncheck({
+ * selector: 'input[type=checkbox]:checked)',
+ * timeout: 2000 // overwrite the default timeout (in ms) to check if the element is present
+ * });
+ * },
+ *
+ * demoTestAsync: async function(browser) {
+ * const result = await browser.uncheck('input[type=checkbox]:checked)');
+ * console.log('Check result', result);
+ * }
+ * }
+ *
+ * @method check
+ * @syntax .uncheck(selector, [callback])
+ * @syntax .uncheck(using, selector, [callback])
+ * @syntax browser.element(selector).uncheck()
+ * @param {string} [using] The locator strategy to use. See [W3C Webdriver - locator strategies](https://www.w3.org/TR/webdriver/#locator-strategies)
+ * @param {string} selector The CSS/Xpath selector used to locate the element.
+ * @param {function} [callback] Optional callback function to be called when the command finishes.
+ * @api protocol.elementinteraction
+ */
+class UncheckElement extends BaseElementCommand {
+ get extraArgsCount() {
+ return 0;
+ }
+
+ get elementProtocolAction() {
+ return 'uncheckElement';
+ }
+
+ static get isTraceable() {
+ return true;
+ }
+
+ async protocolAction() {
+ return this.executeProtocolAction(this.elementProtocolAction);
+ }
+}
+
+module.exports = UncheckElement;
diff --git a/lib/api/web-element/commands/check.js b/lib/api/web-element/commands/check.js
new file mode 100644
index 000000000..cb91ffc46
--- /dev/null
+++ b/lib/api/web-element/commands/check.js
@@ -0,0 +1,28 @@
+/**
+ * Will check, by clicking, on a checkbox or radio input if it is not already checked.
+ * The element is scrolled into view if it is not already pointer-interactable. See the WebDriver specification for element interactability.
+ *
+ * For more info on working with DOM elements in Nightwatch, refer to the Finding & interacting with DOM Elements guide page.
+ *
+ * @example
+ * export default {
+ * demoTest(browser: NightwatchAPI): void {
+ * browser.element('input[type=checkbox]:not(:checked)').check();
+ * browser.element('input[type=radio]:not(:checked)').check();
+ * },
+ * async demoTestAsync(browser: NightwatchAPI): Promise {
+ * await browser.element('input[type=checkbox]:not(:checked)').check();
+ * await browser.element('input[type=radio]:not(:checked)').check();
+ * },
+ * }
+ *
+ * @since 3.6.4
+ * @method check
+ * @memberof ScopedWebElement
+ * @instance
+ * @syntax browser.element(selector).check()
+ * @returns {ScopedWebElement}
+ */
+module.exports.command = function () {
+ return this.runQueuedCommand('checkElement');
+};
diff --git a/lib/api/web-element/commands/uncheck.js b/lib/api/web-element/commands/uncheck.js
new file mode 100644
index 000000000..5bab1354d
--- /dev/null
+++ b/lib/api/web-element/commands/uncheck.js
@@ -0,0 +1,26 @@
+/**
+ * Will uncheck, by clicking, on a checkbox or radio input if it is not already unchecked.
+ * The element is scrolled into view if it is not already pointer-interactable. See the WebDriver specification for element interactability.
+ *
+ * For more info on working with DOM elements in Nightwatch, refer to the Finding & interacting with DOM Elements guide page.
+ *
+ * @example
+ * export default {
+ * demoTest(browser: NightwatchAPI): void {
+ * browser.element('input[type=checkbox]:checked)').check();
+ * },
+ * async demoTestAsync(browser: NightwatchAPI): Promise {
+ * await browser.element('input[type=checkbox]:checked)').check();
+ * },
+ * }
+ *
+ * @since 3.6.4
+ * @method uncheck
+ * @memberof ScopedWebElement
+ * @instance
+ * @syntax browser.element(selector).check()
+ * @returns {ScopedWebElement}
+ */
+module.exports.command = function () {
+ return this.runQueuedCommand('uncheckElement');
+};
diff --git a/lib/transport/selenium-webdriver/method-mappings.js b/lib/transport/selenium-webdriver/method-mappings.js
index 0f10c8f72..5843598a3 100644
--- a/lib/transport/selenium-webdriver/method-mappings.js
+++ b/lib/transport/selenium-webdriver/method-mappings.js
@@ -606,6 +606,42 @@ module.exports = class MethodMappings {
return this.methods.session.setElementValue.call(this, webElementOrId, modifiedValue);
},
+ async checkElement(webElementOrId) {
+ const element = await this.getWebElement(webElementOrId);
+ const elementType = await element.getAttribute('type');
+ const checkableTypes = ['checkbox', 'radio'];
+
+ if (!checkableTypes.includes(elementType)) {
+ throw new Error('must be an input element with type attribute \'checkbox\' or \'radio\'');
+ }
+
+ const value = await element.isSelected();
+
+ if (!value) {
+ await element.click();
+ }
+
+ return null;
+ },
+
+ async uncheckElement(webElementOrId) {
+ const element = await this.getWebElement(webElementOrId);
+ const elementType = await element.getAttribute('type');
+ const checkableTypes = ['checkbox', 'radio'];
+
+ if (!checkableTypes.includes(elementType)) {
+ throw new Error('must be an input element with type attribute \'checkbox\' or \'radio\'');
+ }
+
+ const value = await element.isSelected();
+
+ if (value) {
+ await element.click();
+ }
+
+ return null;
+ },
+
async setElementValue(webElementOrId, value) {
if (Array.isArray(value)) {
value = value.join('');
diff --git a/test/src/api/commands/element/testCheck.js b/test/src/api/commands/element/testCheck.js
new file mode 100644
index 000000000..8d9d925f5
--- /dev/null
+++ b/test/src/api/commands/element/testCheck.js
@@ -0,0 +1,137 @@
+const assert = require('assert');
+const MockServer = require('../../../../lib/mockserver.js');
+const CommandGlobals = require('../../../../lib/globals/commands.js');
+
+describe('.check()', function () {
+ beforeEach(function(done) {
+ CommandGlobals.beforeEach.call(this, done);
+ });
+
+ afterEach(function(done) {
+ CommandGlobals.afterEach.call(this, done);
+ });
+
+ it('client.check() will click unselected checkbox', function (done) {
+ MockServer.addMock({
+ 'url': '/wd/hub/session/1352110219202/element/0/click',
+ 'response': {
+ sessionId: '1352110219202',
+ status: 0
+ }
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: false
+ })
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: 'checkbox'
+ })
+ });
+
+ this.client.api.check('css selector', '#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ }).check('#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ });
+
+ this.client.start(done);
+ });
+
+ it('client.check() will click unselected radio input', function (done) {
+ MockServer.addMock({
+ 'url': '/wd/hub/session/1352110219202/element/0/click',
+ 'response': {
+ sessionId: '1352110219202',
+ status: 0
+ }
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: false
+ })
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: 'radio'
+ })
+ });
+
+ this.client.api.check('css selector', '#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ }).check('#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ });
+
+ this.client.start(done);
+ });
+
+ it('client.check() will not click selected checkbox', function (done) {
+ MockServer.addMock({
+ url: '/wd/hub/session/1352110219202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: true
+ })
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: 'checkbox'
+ })
+ });
+
+ this.client.api.check('css selector', '#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ }).check('#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ });
+
+ this.client.start(done);
+ });
+
+ it('client.check() will not click selected radio input', function (done) {
+ MockServer.addMock({
+ url: '/wd/hub/session/1352110219202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: true
+ })
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: 'checkbox'
+ })
+ });
+
+ this.client.api.check('css selector', '#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ }).check('#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ });
+
+ this.client.start(done);
+ });
+});
diff --git a/test/src/api/commands/element/testUncheck.js b/test/src/api/commands/element/testUncheck.js
new file mode 100644
index 000000000..82374e6e1
--- /dev/null
+++ b/test/src/api/commands/element/testUncheck.js
@@ -0,0 +1,109 @@
+const assert = require('assert');
+const MockServer = require('../../../../lib/mockserver.js');
+const CommandGlobals = require('../../../../lib/globals/commands.js');
+
+describe('.uncheck()', function () {
+ beforeEach(function(done) {
+ CommandGlobals.beforeEach.call(this, done);
+ });
+
+ afterEach(function(done) {
+ CommandGlobals.afterEach.call(this, done);
+ });
+
+ it('client.uncheck() will uncheck selected checkbox input', function (done) {
+ MockServer.addMock({
+ 'url': '/wd/hub/session/1352110219202/element/0/click',
+ 'response': {
+ sessionId: '1352110219202',
+ status: 0
+ }
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: true
+ })
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: 'checkbox'
+ })
+ });
+
+ this.client.api.uncheck('css selector', '#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ }).uncheck('#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ });
+
+ this.client.start(done);
+ });
+
+ it('client.uncheck() will uncheck selected radio input', function (done) {
+ MockServer.addMock({
+ 'url': '/wd/hub/session/1352110219202/element/0/click',
+ 'response': {
+ sessionId: '1352110219202',
+ status: 0
+ }
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: true
+ })
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: 'radio'
+ })
+ });
+
+ this.client.api.uncheck('css selector', '#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ }).uncheck('#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ });
+
+ this.client.start(done);
+ });
+
+ it('client.uncheck() will not click unselected checkbox element', function (done) {
+ MockServer.addMock({
+ url: '/wd/hub/session/1352110219202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: false
+ })
+ }).addMock({
+ url: '/wd/hub/session/1352110219202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ sessionId: '1352110219202',
+ status: 0,
+ value: 'checkbox'
+ })
+ });
+
+ this.client.api.uncheck('css selector', '#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ }).uncheck('#weblogin', function callback(result) {
+ assert.strictEqual(result.status, 0);
+ });
+
+ this.client.start(done);
+ });
+});
diff --git a/test/src/api/commands/web-element/testCheck.js b/test/src/api/commands/web-element/testCheck.js
new file mode 100644
index 000000000..941632f97
--- /dev/null
+++ b/test/src/api/commands/web-element/testCheck.js
@@ -0,0 +1,201 @@
+const assert = require('assert');
+const {WebElement} = require('selenium-webdriver');
+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');
+
+describe('element().check() command', function() {
+ before(function (done) {
+ CommandGlobals.beforeEach.call(this, done);
+ });
+
+ after(function (done) {
+ CommandGlobals.afterEach.call(this, done);
+ });
+
+ it('test .element().check() will check checkbox if not selected', async function () {
+ let nCallsToClick = 0;
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/click',
+ method: 'POST',
+ response: JSON.stringify({
+ value: null
+ }),
+ onRequest(_) {
+ nCallsToClick++;
+ }
+ }, true);
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ value: false
+ })
+ }, true);
+
+ // For returning 'checkbox' from getAttribute for type attribute
+ MockServer.addMock({
+ url: '/session/13521-10219-202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ value: 'checkbox'
+ })
+ }, true);
+
+ const resultPromise = await this.client.api.element('#signupSection').check();
+
+ // Click command should have been used one time to uncheck
+ assert.strictEqual(nCallsToClick, 1);
+
+ // neither an instance of Element or Promise, but an instance of ScopedWebElement.
+ assert.strictEqual(resultPromise instanceof Element, false);
+ assert.strictEqual(typeof resultPromise.find, 'undefined');
+ assert.strictEqual(resultPromise instanceof Promise, false);
+
+ const result = await resultPromise;
+ assert.strictEqual(result instanceof WebElement, true);
+ assert.strictEqual(await result.getId(), '0');
+ });
+
+ it('test .element().check() will check radio input if not selected', async function () {
+ let nCallsToClick = 0;
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/click',
+ method: 'POST',
+ response: JSON.stringify({
+ value: null
+ }),
+ onRequest(_) {
+ nCallsToClick++;
+ }
+ }, true);
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ value: false
+ })
+ }, true);
+
+ // For returning 'radio' from getAttribute for type attribute
+ MockServer.addMock({
+ url: '/session/13521-10219-202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ value: 'radio'
+ })
+ }, true);
+
+ const resultPromise = await this.client.api.element('#signupSection').check();
+
+ // Click command should have been used one time to uncheck
+ assert.strictEqual(nCallsToClick, 1);
+
+ // neither an instance of Element or Promise, but an instance of ScopedWebElement.
+ assert.strictEqual(resultPromise instanceof Element, false);
+ assert.strictEqual(typeof resultPromise.find, 'undefined');
+ assert.strictEqual(resultPromise instanceof Promise, false);
+
+ const result = await resultPromise;
+ assert.strictEqual(result instanceof WebElement, true);
+ assert.strictEqual(await result.getId(), '0');
+ });
+
+ it('test .element().check() will not check radio input if type not a checkbox or radio input', async function () {
+ let nCallsToClick = 0;
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/click',
+ method: 'POST',
+ response: JSON.stringify({
+ value: null
+ }),
+ onRequest(_) {
+ nCallsToClick++;
+ }
+ }, true);
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ value: false
+ })
+ }, true);
+
+ // For returning 'submit' from getAttribute for type attribute
+ MockServer.addMock({
+ url: '/session/13521-10219-202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ value: 'submit'
+ })
+ }, true);
+
+ const resultPromise = await this.client.api.element('#signupSection').check();
+
+ // Click command should have been used one time to uncheck
+ assert.strictEqual(nCallsToClick, 0);
+
+ // neither an instance of Element or Promise, but an instance of ScopedWebElement.
+ assert.strictEqual(resultPromise instanceof Element, false);
+ assert.strictEqual(typeof resultPromise.find, 'undefined');
+ assert.strictEqual(resultPromise instanceof Promise, false);
+
+ const result = await resultPromise;
+ assert.strictEqual(result instanceof WebElement, true);
+ assert.strictEqual(await result.getId(), '0');
+ });
+
+ it('test .element().check() will not click if checked already', async function () {
+ let nCallsToClick = 0;
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/click',
+ method: 'POST',
+ response: JSON.stringify({
+ value: null
+ }),
+ onRequest(_) {
+ nCallsToClick++;
+ }
+ }, true);
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ value: true
+ })
+ }, true);
+
+ // For returning 'radio' from getAttribute for type attribute
+ MockServer.addMock({
+ url: '/session/13521-10219-202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ value: 'radio'
+ })
+ }, true);
+
+ const resultPromise = await this.client.api.element('#signupSection').check();
+
+ // Click command should not have been executed since element is unchecked already
+ assert.strictEqual(nCallsToClick, 0);
+
+ // neither an instance of Element or Promise, but an instance of ScopedWebElement.
+ assert.strictEqual(resultPromise instanceof Element, false);
+ assert.strictEqual(typeof resultPromise.find, 'undefined');
+ assert.strictEqual(resultPromise instanceof Promise, false);
+
+ const result = await resultPromise;
+ assert.strictEqual(result instanceof WebElement, true);
+ assert.strictEqual(await result.getId(), '0');
+ });
+});
+
diff --git a/test/src/api/commands/web-element/testUncheck.js b/test/src/api/commands/web-element/testUncheck.js
new file mode 100644
index 000000000..23942a777
--- /dev/null
+++ b/test/src/api/commands/web-element/testUncheck.js
@@ -0,0 +1,201 @@
+const assert = require('assert');
+const {WebElement} = require('selenium-webdriver');
+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');
+
+describe('element().uncheck() command', function() {
+ before(function (done) {
+ CommandGlobals.beforeEach.call(this, done);
+ });
+
+ after(function (done) {
+ CommandGlobals.afterEach.call(this, done);
+ });
+
+ it('test .element().uncheck() will uncheck checkbox if selected', async function () {
+ let nCallsToClick = 0;
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/click',
+ method: 'POST',
+ response: JSON.stringify({
+ value: null
+ }),
+ onRequest(_) {
+ nCallsToClick++;
+ }
+ }, true);
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ value: true
+ })
+ }, true);
+
+ // For returning 'checkbox' from getAttribute for type attribute
+ MockServer.addMock({
+ url: '/session/13521-10219-202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ value: 'checkbox'
+ })
+ }, true);
+
+ const resultPromise = await this.client.api.element('#signupSection').uncheck();
+
+ // Click command should have been used one time to uncheck
+ assert.strictEqual(nCallsToClick, 1);
+
+ // neither an instance of Element or Promise, but an instance of ScopedWebElement.
+ assert.strictEqual(resultPromise instanceof Element, false);
+ assert.strictEqual(typeof resultPromise.find, 'undefined');
+ assert.strictEqual(resultPromise instanceof Promise, false);
+
+ const result = await resultPromise;
+ assert.strictEqual(result instanceof WebElement, true);
+ assert.strictEqual(await result.getId(), '0');
+ });
+
+ it('test .element().uncheck() will uncheck radio input if selected', async function () {
+ let nCallsToClick = 0;
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/click',
+ method: 'POST',
+ response: JSON.stringify({
+ value: null
+ }),
+ onRequest(_) {
+ nCallsToClick++;
+ }
+ }, true);
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ value: true
+ })
+ }, true);
+
+ // For returning 'radio' from getAttribute for type attribute
+ MockServer.addMock({
+ url: '/session/13521-10219-202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ value: 'radio'
+ })
+ }, true);
+
+ const resultPromise = await this.client.api.element('#signupSection').uncheck();
+
+ // Click command should have been used one time to uncheck
+ assert.strictEqual(nCallsToClick, 1);
+
+ // neither an instance of Element or Promise, but an instance of ScopedWebElement.
+ assert.strictEqual(resultPromise instanceof Element, false);
+ assert.strictEqual(typeof resultPromise.find, 'undefined');
+ assert.strictEqual(resultPromise instanceof Promise, false);
+
+ const result = await resultPromise;
+ assert.strictEqual(result instanceof WebElement, true);
+ assert.strictEqual(await result.getId(), '0');
+ });
+
+ it('test .element().uncheck() will not uncheck if not a checkbox', async function () {
+ let nCallsToClick = 0;
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/click',
+ method: 'POST',
+ response: JSON.stringify({
+ value: null
+ }),
+ onRequest(_) {
+ nCallsToClick++;
+ }
+ }, true);
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ value: true
+ })
+ }, true);
+
+ // For returning 'submit' from getAttribute for type attribute
+ MockServer.addMock({
+ url: '/session/13521-10219-202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ value: 'submit'
+ })
+ }, true);
+
+ const resultPromise = await this.client.api.element('#signupSection').uncheck();
+
+ // Click command should not have been fired
+ assert.strictEqual(nCallsToClick, 0);
+
+ // neither an instance of Element or Promise, but an instance of ScopedWebElement.
+ assert.strictEqual(resultPromise instanceof Element, false);
+ assert.strictEqual(typeof resultPromise.find, 'undefined');
+ assert.strictEqual(resultPromise instanceof Promise, false);
+
+ const result = await resultPromise;
+ assert.strictEqual(result instanceof WebElement, true);
+ assert.strictEqual(await result.getId(), '0');
+ });
+
+ it('test .element().uncheck() will not click if unchecked already', async function () {
+ let nCallsToClick = 0;
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/click',
+ method: 'POST',
+ response: JSON.stringify({
+ value: null
+ }),
+ onRequest(_) {
+ nCallsToClick++;
+ }
+ }, true);
+
+ MockServer.addMock({
+ url: '/session/13521-10219-202/element/0/selected',
+ method: 'GET',
+ response: JSON.stringify({
+ value: false
+ })
+ }, true);
+
+ // For returning 'checkbox' from getAttribute for type attribute
+ MockServer.addMock({
+ url: '/session/13521-10219-202/execute/sync',
+ method: 'POST',
+ response: JSON.stringify({
+ value: 'checkbox'
+ })
+ }, true);
+
+ const resultPromise = await this.client.api.element('#signupSection').uncheck();
+
+ // Click command should not have been executed since element is unchecked already
+ assert.strictEqual(nCallsToClick, 0);
+
+ // neither an instance of Element or Promise, but an instance of ScopedWebElement.
+ assert.strictEqual(resultPromise instanceof Element, false);
+ assert.strictEqual(typeof resultPromise.find, 'undefined');
+ assert.strictEqual(resultPromise instanceof Promise, false);
+
+ const result = await resultPromise;
+ assert.strictEqual(result instanceof WebElement, true);
+ assert.strictEqual(await result.getId(), '0');
+ });
+});
+
diff --git a/types/index.d.ts b/types/index.d.ts
index 566162646..64b5c4fe3 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -2587,6 +2587,57 @@ export interface ClientCommands extends ChromiumClientCommands {
}
export interface ElementCommands {
+ /**
+ * Will check, click, on an unchecked checkbox or radio input if not already checked.
+ *
+ * @example
+ * module.exports = {
+ * demoTest(browser) {
+ * browser.check('input[type=checkbox]:not(:checked)');
+ *
+ * browser.check('input[type=checkbox]:not(:checked)', function(result) {
+ * console.log('Check result', result);
+ * });
+ *
+ * // with explicit locate strategy
+ * browser.check('css selector', 'input[type=checkbox]:not(:checked)');
+ *
+ * // with selector object - see https://nightwatchjs.org/guide#element-properties
+ * browser.check({
+ * selector: 'input[type=checkbox]:not(:checked)',
+ * index: 1,
+ * suppressNotFoundErrors: true
+ * });
+ *
+ * browser.check({
+ * selector: 'input[type=checkbox]:not(:checked)',
+ * timeout: 2000 // overwrite the default timeout (in ms) to check if the element is present
+ * });
+ * },
+ *
+ * demoTestAsync: async function(browser) {
+ * const result = await browser.check('input[type=checkbox]:not(:checked)');
+ * console.log('Check result', result);
+ * }
+ * }
+ *
+ * @see https://nightwatchjs.org/api/check.html
+ */
+ check(
+ selector: Definition,
+ callback?: (
+ this: NightwatchAPI,
+ result: NightwatchCallbackResult
+ ) => void
+ ): Awaitable;
+ check(
+ using: LocateStrategy,
+ selector: Definition,
+ callback?: (
+ this: NightwatchAPI,
+ result: NightwatchCallbackResult
+ ) => void
+ ): Awaitable;
/**
* Clear a textarea or a text input element's value.
* Starting with v1.1 `clearValue()` will wait for the element to be present (until the specified timeout).
@@ -4521,6 +4572,60 @@ export interface ElementCommands {
result: NightwatchCallbackResult
) => void
): Awaitable;
+
+ /**
+ * Will uncheck, click, on a checked checkbox or radio input if not already unchecked.
+ *
+ * @example
+ * module.exports = {
+ * demoTest(browser) {
+ * browser.uncheck('input[type=checkbox]:checked)');
+ *
+ * browser.uncheck('input[type=checkbox]:checked)', function(result) {
+ * console.log('Check result', result);
+ * });
+ *
+ * // with explicit locate strategy
+ * browser.uncheck('css selector', 'input[type=checkbox]:checked)');
+ *
+ * // with selector object - see https://nightwatchjs.org/guide#element-properties
+ * browser.uncheck({
+ * selector: 'input[type=checkbox]:checked)',
+ * index: 1,
+ * suppressNotFoundErrors: true
+ * });
+ *
+ * browser.uncheck({
+ * selector: 'input[type=checkbox]:checked)',
+ * timeout: 2000 // overwrite the default timeout (in ms) to check if the element is present
+ * });
+ * },
+ *
+ * demoTestAsync: async function(browser) {
+ * const result = await browser.uncheck('input[type=checkbox]:checked)');
+ * console.log('Check result', result);
+ * }
+ * }
+ *
+ * Will uncheck, click, on a checked checkbox or radio input if not already unchecked.
+ *
+ * @see https://nightwatchjs.org/api/uncheck.html
+ */
+ uncheck(
+ selector: Definition,
+ callback?: (
+ this: NightwatchAPI,
+ result: NightwatchCallbackResult
+ ) => void
+ ): Awaitable;
+ uncheck(
+ using: LocateStrategy,
+ selector: Definition,
+ callback?: (
+ this: NightwatchAPI,
+ result: NightwatchCallbackResult
+ ) => void
+ ): Awaitable;
}
export interface AppiumCommands {
diff --git a/types/tests/elementCommands.test-d.ts b/types/tests/elementCommands.test-d.ts
index 581b76022..e20b8b6a4 100644
--- a/types/tests/elementCommands.test-d.ts
+++ b/types/tests/elementCommands.test-d.ts
@@ -1,6 +1,66 @@
import { expectType } from 'tsd';
import { JSON_WEB_OBJECT, NightwatchSizeAndPosition, ElementResult, NightwatchAPI, NightwatchCallbackResult, ElementGlobal } from '..';
+//
+// .check
+//
+describe('check command demo', function () {
+ test('demo test', function () {
+ browser
+ .url('https://www.selenium.dev/selenium/web/formPage.html')
+ .waitForElementVisible('#checkbox-with-label')
+ .check('#checkbox-with-label', function (result) {
+ expectType(this);
+ expectType>(result);
+ })
+ .expect.element('#checkbox-with-label').to.be.selected;
+
+ // Should not uncheck the checkbox if .check is rerun on the same element
+ browser
+ .check('#checkbox-with-label')
+ .expect.element('#checkbox-with-label').to.be.selected
+ });
+
+ test('async demo test', async function (browser) {
+ const result = await browser
+ .url('https://www.selenium.dev/selenium/web/formPage.html')
+ .waitForElementVisible('#checkbox-with-label')
+ .check('#checkbox-with-label');
+ expectType(result);
+ });
+});
+
+//
+// .uncheck
+//
+describe('uncheck command demo', function () {
+ test('demo test', function () {
+ browser
+ .url('https://www.selenium.dev/selenium/web/formPage.html')
+ .waitForElementVisible('#checkbox-with-label')
+ .click('#checkbox-with-label')
+ .assert.selected('#checkbox-with-label')
+ .uncheck('#checkbox-with-label', function (result) {
+ expectType(this);
+ expectType>(result);
+ })
+ .expect.element('#checkbox-with-label').to.not.be.selected;
+
+ // Should not check the checkbox if .check is rerun on the same element
+ browser
+ .uncheck('#checkbox-with-label')
+ .expect.element('#checkbox-with-label').to.not.be.selected
+ });
+
+ test('async demo test', async function (browser) {
+ const result = await browser
+ .url('https://www.selenium.dev/selenium/web/formPage.html')
+ .waitForElementVisible('#checkbox-with-label')
+ .uncheck('#checkbox-with-label');
+ expectType(result);
+ });
+});
+
//
// .clearValue
//
diff --git a/types/tests/webElement.test-d.ts b/types/tests/webElement.test-d.ts
index e7a3d0ff6..b530c42d1 100644
--- a/types/tests/webElement.test-d.ts
+++ b/types/tests/webElement.test-d.ts
@@ -175,6 +175,8 @@ describe('new element() api', function () {
expectType>(elem.click());
expectType>(elem.clear());
+ expectType>(elem.check());
+ expectType>(elem.uncheck());
expectType>(elem.sendKeys('something', 1));
expectType>(elem.update('something', 1));
expectType>(elem.setValue('something', 1));
diff --git a/types/web-element.d.ts b/types/web-element.d.ts
index a5e2c7786..bde0cc4c1 100644
--- a/types/web-element.d.ts
+++ b/types/web-element.d.ts
@@ -157,6 +157,9 @@ export interface ScopedElement extends Element, PromiseLike {
clear(): Promise;
+ check(): Promise;
+ uncheck(): Promise;
+
sendKeys(...keys: E): Promise;
submit(): Promise;