Repository: nightwatchjs/nightwatch (js)

Issue:
moveTo doesn't take null in first parameter, but should do it according to documentation
Hi! 👋 
      
Firstly, thanks for your work on this project! 🙂

Today I used [patch-package](https://github.com/ds300/patch-package) to patch `nightwatch@2.3.7` for the project I'm working on.

<!-- <html>
<body>
<!--StartFragment-->
my test:
var agentName = process.env.AGENT_NAME;
var agentSession = Number(process.env.SESSION_IDX);
var agentSessionName = process.env.SESSION_NAME;
var agentNumber = process.env.AGENT_NUM;

client.include("SetTestExpectation_alert")

client.rtcEvent("Start", 'global')
    .rtcProgress("Start")
    .pause(agentNumber * 500);
var sec = 1000;
var roomUrl = "some_roomUrl"

client
    .rtcInfo("process.env.RTC_RUN_COUNT " + process.env.RTC_RUN_COUNT)
    .rtcInfo("RTC agent start - agent: %s room: %s", agentName, roomUrl)
    .url(roomUrl)
    .waitForElementVisible('#input-user-name', 120 * sec)
    .pause(300) //wait for page render 
    .setValue('#input-user-name', agentName.replace(/-/g, ''))
    .pause(300)
    .click(".MuiButtonBase-root.MuiButton-root")
    .pause(5000)
    .useXpath()
    .waitForElementVisible("//*[text()='Join Now']", 120 * sec)
    .pause(300)
    .click("//*[text()='Join Now']")
    .rtcProgress("In  " + roomUrl)

client
    .rtcSetNetwork({
        outgoing: {
            packetloss: 5,
            jitter: 50,
            latency: 200,
            bitrate: 5000
        },
        incoming: {
            packetloss: 6,
            jitter: 50,
            latency: 200,
            bitrate: 6000
        }
    });

client
    .pause(30 * sec)
    .useCss()
    .moveTo(null, 110, 10)
    .pause(30 * sec)

client
    .useXpath()
    .waitForElementVisible("//*[text()='Disconnect']", 60 * sec)
    .pause(300)
    .click("//*[text()='Disconnect']")
    .pause(15 * sec)
    
    LOGS:
→ Running command: moveTo (, 110, 10)
--
→ Error
Error while running .moveTo() protocol action: Unknown element: null

<!--EndFragment-->
</body>
</html> -->

Here is the diff that solved my problem:

```diff
diff --git a/node_modules/nightwatch/lib/api/_loaders/static.js b/node_modules/nightwatch/lib/api/_loaders/static.js
index 092bf05..18a42d3 100644
--- a/node_modules/nightwatch/lib/api/_loaders/static.js
+++ b/node_modules/nightwatch/lib/api/_loaders/static.js
@@ -133,11 +133,15 @@ module.exports = class StaticAssert {
         const {passed, expected, actual, message, stackTrace} = assertion;
         const elapsedTime = new Date() - startTime;
 
+        // Muly
+        const stackTraceEx = this.commandQueue.currentNode && this.commandQueue.currentNode.stackTrace ?
+        this.commandQueue.currentNode.stackTrace : stackTrace;
+
         this.runner = new AssertionRunner({
           passed,
           err: {
             expected, actual
-          }, message, calleeFn: assertFn, abortOnFailure, stackTrace, reporter, elapsedTime
+          }, message, calleeFn: assertFn, abortOnFailure, stackTraceEx, reporter, elapsedTime
         });
 
         return this.runner.run();
diff --git a/node_modules/nightwatch/lib/transport/selenium-webdriver/method-mappings.js b/node_modules/nightwatch/lib/transport/selenium-webdriver/method-mappings.js
index 83efe39..b63991e 100644
--- a/node_modules/nightwatch/lib/transport/selenium-webdriver/method-mappings.js
+++ b/node_modules/nightwatch/lib/transport/selenium-webdriver/method-mappings.js
@@ -1,6 +1,6 @@
 const {WebElement, Origin, By} = require('selenium-webdriver');
 const {Locator} = require('../../element');
-const {isString} = require('../../utils');
+const {isString,isNull} = require('../../utils');
 const fs = require('fs');
 const cdp = require('./cdp.js');
 
@@ -26,6 +26,10 @@ module.exports = class MethodMappings {
       return new WebElement(this.driver, webElementOrString);
     }
 
+    if (isNull(webElementOrString)) {
+      return new WebElement(this.driver, '');
+    }
+
     throw new Error(`Unknown element: ${webElementOrString}`);
   }
 
diff --git a/node_modules/nightwatch/lib/utils/index.js b/node_modules/nightwatch/lib/utils/index.js
index 18d1f35..990539a 100644
--- a/node_modules/nightwatch/lib/utils/index.js
+++ b/node_modules/nightwatch/lib/utils/index.js
@@ -32,7 +32,8 @@ const PrimitiveTypes = {
   BOOLEAN: 'boolean',
   NUMBER: 'number',
   STRING: 'string',
-  UNDEFINED: 'undefined'
+  UNDEFINED: 'undefined',
+  NULL: null
 };
 
 class Utils {
@@ -68,6 +69,10 @@ class Utils {
     return typeof value == PrimitiveTypes.UNDEFINED;
   }
 
+  static isNull(value) {
+    return value === PrimitiveTypes.NULL;
+  }
+
   static isDefined(value) {
     return !Utils.isUndefined(value);
   }
```

<em>This issue body was [partially generated by patch-package](https://github.com/ds300/patch-package/issues/296).</em>


Pull request description:
Fixes/move to command params
## Changes
- consider optional parameters in `moveTo` protocol command
- fix test for `moveTo` protocol command

## Impact
- fixes #3404 


Patch:
diff --git a/lib/api/protocol/moveTo.js b/lib/api/protocol/moveTo.js
index 17f3c554..cb2015d5 100644
--- a/lib/api/protocol/moveTo.js
+++ b/lib/api/protocol/moveTo.js
@@ -1,3 +1,4 @@
+const {Origin} = require('selenium-webdriver');
 const ProtocolAction = require('./_base-action.js');
 
 /**
@@ -19,8 +20,28 @@ const ProtocolAction = require('./_base-action.js');
  * @param {function} [callback] Optional callback function to be called when the command finishes.
  * @api protocol.useractions
  */
-module.exports = class Session extends ProtocolAction {
-  command(webElementId, xoffset, yoffset, callback) {
+module.exports = class Command extends ProtocolAction {
+
+  command(webElementId, xoffset, yoffset, callback = function() {}) {
+   
+    if (arguments.length >= 3 && typeof arguments[0] === 'string' || arguments[0] === null) {
+      if (webElementId === null) {
+        webElementId = Origin.POINTER;
+      } else {
+        ProtocolAction.validateElementId(webElementId);
+      }
+     
+    } else if (arguments.length >= 2 && typeof arguments[0] === 'number') {
+      webElementId = Origin.POINTER;
+      xoffset = arguments[0];
+      yoffset = arguments[1];
+      callback = arguments.length === 3 ? arguments[2] : callback;
+      
+    } else {
+      throw new Error('Invalid Parameters for moveTo Command');
+    }
+
     return this.transportActions.moveTo(webElementId, xoffset, yoffset, callback);
+    
   }
 };
diff --git a/lib/transport/selenium-webdriver/method-mappings.js b/lib/transport/selenium-webdriver/method-mappings.js
index 83efe391..3292b92e 100644
--- a/lib/transport/selenium-webdriver/method-mappings.js
+++ b/lib/transport/selenium-webdriver/method-mappings.js
@@ -792,6 +792,7 @@ module.exports = class MethodMappings {
         async moveTo(origin, x, y, duration, options = {}) {
           switch (origin) {
             case Origin.POINTER:
+              break;
             case Origin.VIEWPORT:
               break;
 


Test patch:
diff --git a/test/src/api/protocol/testMoveTo.js b/test/src/api/protocol/testMoveTo.js
index 3440fe97..85ccad58 100644
--- a/test/src/api/protocol/testMoveTo.js
+++ b/test/src/api/protocol/testMoveTo.js
@@ -2,20 +2,63 @@ const assert = require('assert');
 const Globals = require('../../../lib/globals.js');
 
 describe('browser commands', function() {
+  let args;
   before(function() {
-    Globals.protocolBefore();
+    Globals.protocolBefore.call(this);
+    
+  });
+
+  beforeEach(function() {
+    this.client.transport.Actions.session.moveTo = function(opts) {
+      args = opts.args;
+
+      return Promise.resolve(null);
+    };
+  });
+
+  afterEach(function() {
+    args = null;
   });
 
   it('testMoveTo', function () {
 
-    return Globals.protocolTest({
-      assertion: function(opts) {
-        assert.strictEqual(opts.method, 'POST');
-        assert.strictEqual(opts.path, '/session/1352110219202/moveto');
-        assert.deepStrictEqual(opts.data, {element: 'testElement', xoffset: 0, yoffset: 1});
-      },
+    return Globals.runProtocolTest({
+      assertion: function() {},
       commandName: 'moveTo',
       args: ['testElement', 0, 1]
+    }, this.client).then(_ => {
+      assert.deepStrictEqual(args, ['testElement', 0, 1]);
+    });
+  });
+
+  it('testMoveTo - no webElementId', function() {
+
+    return Globals.runProtocolTest({
+      assertion: function() {},
+      commandName: 'moveTo',
+      args: [2, 1]
+    }, this.client).then(_ => {
+      assert.deepStrictEqual(args, ['pointer', 2, 1]);
+    });
+  });
+
+  it('testMoveTo - null webElementId', function() {
+    return Globals.runProtocolTest({
+      assertion: function() {},
+      commandName: 'moveTo',
+      args: [null, 2, 1]
+    }, this.client).then(_ => {
+      assert.deepStrictEqual(args, ['pointer', 2, 1]);
     });
   });
+    
+  it('testMove - invalid parameters', function(done) {
+    Globals.runProtocolTest({
+      assertion: function() {},
+      commandName: 'moveTo',
+      args: ['test-element']
+    }, this.client)
+      .then(_ => done(new Error('should result into error')))
+      .catch(err => done());
+  });
 });