Repository: nightwatchjs/nightwatch
Pull request #4035 (2024-02-22 12:57:58)

issue #4028 fix : added fallback for clearValue command
Add a fallback for `clearValue` command where Nightwatch will now try to delete the text in an input field by sending backspaces if the text is not deleted by the clear command directly.

This fallback will also work for commands like `setValue`, `updateValue`, etc. which clears the text before entering new text.

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/nightwatchjs/nightwatch?pullRequest=4035) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/nightwatchjs/nightwatch?pullRequest=4035) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/nightwatchjs/nightwatch?pullRequest=4035) it.</sub>
github-actions[bot]: 
## Status
* ❌ No modified files found in the **types** directory.
Please make sure to include types for any changes you have made. Thank you!.
FireNdIce3: > We'd also need to have some unit tests written for this PR. You can try to explore how the tests are written in Nightwatch and ask if you don't understand anything or need any help.

Can you please guide me where to explore regarding writing tests?
Thanks
FireNdIce3: I am confused why they are not strictly equal
Can you please help with this?
garg3133: > I am confused why they are not strictly equal
Can you please help with this?

Two arrays are never strictly equal (being strictly equal would mean that both refer to the same memory location). Use can use `deepStrictEqual` instead.
FireNdIce3: I looked through the verbose logs and indeed keys are going in an array
But I am still unsure what's wrong with the test
FireNdIce3: hi the tests are working fine as of now.. is there anything that's left?
thanks

FireNdIce3: @garg3133 the linting issues has been fixed 
garg3133: @FireNdIce3 Can you remove the formatting changes you've made in the test file? Your PR should not contain any changes that are not related to the issue at hand, it makes it really difficult to review the PR.
FireNdIce3: @garg3133 sorry for that..
will do the required changes 
garg3133: No need to be sorry. Are you looking into some other issues as well?
FireNdIce3: hi @garg3133 
I have updated the test and its passing 
Can you please review again when you get the time 
Thanks
github-actions[bot]: 
## Status
* ❌ No modified files found in the **types** directory.
Please make sure to include types for any changes you have made. Thank you!.

Patch:
diff --git a/lib/transport/selenium-webdriver/method-mappings.js b/lib/transport/selenium-webdriver/method-mappings.js
index bb915573..0f10c8f7 100644
--- a/lib/transport/selenium-webdriver/method-mappings.js
+++ b/lib/transport/selenium-webdriver/method-mappings.js
@@ -587,6 +587,16 @@ module.exports = class MethodMappings {
           const element = this.getWebElement(webElementOrId);
           await element.clear();
 
+          try {
+            const value = await element.getProperty('value');
+            if (isString(value) && value.length > 0) {
+              const backArr = Array(value.length).fill(Key.BACK_SPACE);
+              await element.sendKeys(...backArr);
+            }
+          } catch {
+            // silent catch
+          }
+
           return null;
         },
 
@@ -607,7 +617,7 @@ module.exports = class MethodMappings {
 
           // clear Element value
           try {
-            await element.clear();
+            await this.methods.session.clearElementValue.call(this, webElementOrId);
           } catch (err) {
             if (err.name !== 'InvalidElementStateError') {
               throw err;


Test patch:
diff --git a/test/src/api/commands/element/testClearValue.js b/test/src/api/commands/element/testClearValue.js
index 3f4498f6..9d22938e 100644
--- a/test/src/api/commands/element/testClearValue.js
+++ b/test/src/api/commands/element/testClearValue.js
@@ -1,6 +1,7 @@
 const assert = require('assert');
 const MockServer  = require('../../../../lib/mockserver.js');
 const Nightwatch = require('../../../../lib/nightwatch.js');
+const {Key} = require('selenium-webdriver');
 
 describe('clearValue', function() {
 
@@ -118,6 +119,114 @@ describe('clearValue', function() {
     });
   });
 
+  it('client.clearValue() with webdriver protocol - fallback sending backspace keys', function (done) {
+    Nightwatch.initClient({
+      selenium: {
+        version2: false,
+        start_process: false,
+        host: null
+      },
+      output: false,
+      silent: false,
+      webdriver: {
+        host: 'localhost',
+        start_process: false
+      }
+    }).then((client) => {
+      let sendKeysMockCalled = false;
+      let callbackResultValue;
+  
+      MockServer
+        .addMock({
+          url: '/session/13521-10219-202/element/0/clear',
+          response: {value: null}
+        }, true)
+        .addMock({
+          url: '/session/13521-10219-202/element/0/property/value',
+          method: 'GET',
+          response: {value: 'sample'}
+        }, true)
+        .addMock({
+          url: '/session/13521-10219-202/element/0/value',
+          method: 'POST',
+          response: {value: null},
+          onRequest(_, requestData) {
+            assert.strictEqual(requestData.text, Array(6).fill(Key.BACK_SPACE).join(''));
+            sendKeysMockCalled = true;
+          }
+        }, true);
+
+      client.api.clearValue('css selector', '#signupSection', function (result) {
+        callbackResultValue = result.value;
+      });
+  
+      client.start(function (err) {
+        try {
+          assert.strictEqual(err, undefined);
+          assert.strictEqual(callbackResultValue, null);
+          // ensure fallback is working (backspaces are sent to element)
+          assert.strictEqual(sendKeysMockCalled, true);
+          done();
+        } catch (e) {
+          done(e);
+        }
+      });
+    });
+  });
+
+  it('client.clearValue() with webdriver protocol - fallback sending nothing when element cleared correctly', function (done) {
+    Nightwatch.initClient({
+      selenium: {
+        version2: false,
+        start_process: false,
+        host: null
+      },
+      output: false,
+      silent: false,
+      webdriver: {
+        host: 'localhost',
+        start_process: false
+      }
+    }).then((client) => {
+      let sendKeysMockCalled = false;
+      let callbackResultValue;
+  
+      MockServer
+        .addMock({
+          url: '/session/13521-10219-202/element/0/clear',
+          response: {value: null}
+        }, true)
+        .addMock({
+          url: '/session/13521-10219-202/element/0/property/value',
+          method: 'GET',
+          response: {value: ''}
+        }, true)
+        .addMock({
+          url: '/session/13521-10219-202/element/0/value',
+          method: 'POST',
+          response: {value: null},
+          onRequest() {
+            sendKeysMockCalled = true;
+          }
+        }, true);
+
+      client.api.clearValue('css selector', '#signupSection', function (result) {
+        callbackResultValue = result.value;
+      });
+  
+      client.start(function (err) {
+        try {
+          assert.strictEqual(err, undefined);
+          assert.strictEqual(callbackResultValue, null);
+          assert.strictEqual(sendKeysMockCalled, false);
+          done();
+        } catch (e) {
+          done(e);
+        }
+      });
+    });
+  });
+
   it('client.clearValue() with webdriver protocol - element not found', function(done) {
     Nightwatch.initClient({
       selenium: {
@@ -163,4 +272,4 @@ describe('clearValue', function() {
       client.start(done);
     });
   });
-});
+});
\ No newline at end of file
diff --git a/test/src/api/commands/web-element/testClear.js b/test/src/api/commands/web-element/testClear.js
index 01c5399a..8fda24f2 100644
--- a/test/src/api/commands/web-element/testClear.js
+++ b/test/src/api/commands/web-element/testClear.js
@@ -1,5 +1,5 @@
 const assert = require('assert');
-const {WebElement} = require('selenium-webdriver');
+const {WebElement, Key} = require('selenium-webdriver');
 const MockServer  = require('../../../../lib/mockserver.js');
 const CommandGlobals = require('../../../../lib/globals/commands-w3c.js');
 const common = require('../../../../common.js');
@@ -73,4 +73,73 @@ describe('element().clear() command', function () {
     assert.strictEqual(result instanceof WebElement, true);
     assert.strictEqual(await result.getId(), '0');
   });
+
+  it('test .element().clear() with fallback sending keys', async function() {
+    let sendKeysMockCalled = false;
+
+    MockServer
+      .addMock({
+        url: '/session/13521-10219-202/element/0/clear',
+        method: 'POST',
+        response: JSON.stringify({
+          value: null
+        })
+      }, true)
+      .addMock({
+        url: '/session/13521-10219-202/element/0/property/value',
+        method: 'GET',
+        response: {value: 'sample'}
+      }, true)
+      .addMock({
+        url: '/session/13521-10219-202/element/0/value',
+        method: 'POST',
+        response: {value: null},
+        onRequest(_, requestData) {
+          assert.strictEqual(requestData.text, Array(6).fill(Key.BACK_SPACE).join(''));
+          sendKeysMockCalled = true;
+        }
+      }, true);
+
+    const resultPromise = this.client.api.element('#signupSection').clear();
+
+    const result = await resultPromise;
+    assert.strictEqual(result instanceof WebElement, true);
+    assert.strictEqual(await result.getId(), '0');
+
+    assert.strictEqual(sendKeysMockCalled, true);
+  });
+
+  it('test .element().clear() with fallback not sending keys when clear working correctly', async function() {
+    let sendKeysMockCalled = false;
+
+    MockServer
+      .addMock({
+        url: '/session/13521-10219-202/element/0/clear',
+        method: 'POST',
+        response: JSON.stringify({
+          value: null
+        })
+      }, true)
+      .addMock({
+        url: '/session/13521-10219-202/element/0/property/value',
+        method: 'GET',
+        response: {value: ''}
+      }, true)
+      .addMock({
+        url: '/session/13521-10219-202/element/0/value',
+        method: 'POST',
+        response: {value: null},
+        onRequest() {
+          sendKeysMockCalled = true;
+        }
+      }, true);
+
+    const resultPromise = this.client.api.element('#signupSection').clear();
+
+    const result = await resultPromise;
+    assert.strictEqual(result instanceof WebElement, true);
+    assert.strictEqual(await result.getId(), '0');
+
+    assert.strictEqual(sendKeysMockCalled, false);
+  });
 });
diff --git a/test/src/api/commands/web-element/testSetValue.js b/test/src/api/commands/web-element/testSetValue.js
index 561cc6cb..00110a73 100644
--- a/test/src/api/commands/web-element/testSetValue.js
+++ b/test/src/api/commands/web-element/testSetValue.js
@@ -1,5 +1,5 @@
 const assert = require('assert');
-const {WebElement} = require('selenium-webdriver');
+const {WebElement, Key} = require('selenium-webdriver');
 const MockServer  = require('../../../../lib/mockserver.js');
 const CommandGlobals = require('../../../../lib/globals/commands-w3c.js');
 const common = require('../../../../common.js');
@@ -151,4 +151,68 @@ describe('element().setValue() command', function () {
     assert.strictEqual(result instanceof WebElement, true);
     assert.strictEqual(await result.getId(), '9');
   });
+
+  it('test .element().setValue() with clear fallback sending keys', async function() {
+    let sendKeysMockCalled = 0;
+
+    MockServer
+      .addMock({
+        url: '/session/13521-10219-202/elements',
+        postdata: {
+          using: 'css selector',
+          value: 'input[name=q]'
+        },
+        method: 'POST',
+        response: JSON.stringify({
+          value: [{'element-6066-11e4-a52e-4f735466cecf': '9'}]
+        })
+      }, true)
+      .addMock({
+        url: '/session/13521-10219-202/element/9/clear',
+        method: 'POST',
+        postdata: {},
+        response: JSON.stringify({
+          value: null
+        })
+      }, true)
+      .addMock({
+        url: '/session/13521-10219-202/element/9/property/value',
+        method: 'GET',
+        response: {value: 'sample'}
+      }, true)
+      .addMock({
+        url: '/session/13521-10219-202/element/9/value',
+        method: 'POST',
+        response: {value: null},
+        onRequest(_, requestData) {
+          assert.strictEqual(requestData.text, Array(6).fill(Key.BACK_SPACE).join(''));
+          sendKeysMockCalled++;
+        }
+      }, true)
+      .addMock({
+        url: '/session/13521-10219-202/element/9/value',
+        method: 'POST',
+        postdata: {
+          text: 'nightwatch',
+          value: [
+            'n', 'i', 'g', 'h', 't', 'w', 'a', 't', 'c', 'h'
+          ]
+        },
+        response: JSON.stringify({
+          value: null
+        }),
+        onRequest(_, requestData) {
+          assert.strictEqual(requestData.text, 'nightwatch');
+          sendKeysMockCalled++;
+        }
+      }, true);
+
+    const resultPromise = this.client.api.element('input[name=q]').setValue('nightwatch');
+
+    const result = await resultPromise;
+    assert.strictEqual(result instanceof WebElement, true);
+    assert.strictEqual(await result.getId(), '9');
+
+    assert.strictEqual(sendKeysMockCalled, 2);
+  });
 });
diff --git a/test/src/api/protocol/testElementCommands.js b/test/src/api/protocol/testElementCommands.js
index a916d011..933bfbe5 100644
--- a/test/src/api/protocol/testElementCommands.js
+++ b/test/src/api/protocol/testElementCommands.js
@@ -512,13 +512,20 @@ describe('element actions', function () {
         },
         commandName: 'elementIdValue',
         args: ['TEST_ELEMENT', 'test', function(result) {
+          // accounting for fallback for clearElement as well.
+          assert.strictEqual(assertions.length, 4);
+
           assert.strictEqual(assertions[0].command, 'clearElement');
           assert.ok(assertions[0].args.id instanceof WebElement);
 
-          assert.strictEqual(assertions[1].command, 'sendKeysToElement');
-          assert.strictEqual(assertions[1].args.text, 'test');
-          assert.deepStrictEqual(assertions[1].args.value, ['t', 'e', 's', 't']);
-          assert.ok(assertions[1].args.id instanceof WebElement);
+          // fallback for clearElement
+          assert.strictEqual(assertions[1].command, 'getElementProperty');
+          assert.strictEqual(assertions[2].command, 'sendKeysToElement');
+
+          assert.strictEqual(assertions[3].command, 'sendKeysToElement');
+          assert.strictEqual(assertions[3].args.text, 'test');
+          assert.deepStrictEqual(assertions[3].args.value, ['t', 'e', 's', 't']);
+          assert.ok(assertions[3].args.id instanceof WebElement);
 
           assert.deepStrictEqual(result, {value: null, status: 0});
         }]