Node WebDriverJS executeScript behaviour - javascript

I have a WebDriverJS Node script with the following in the global scope of the application:
var helperFunctions = {
'getElementEssentials': function get(orig, tmp, level) {
var tmp = tmp || {};
var level = level || 0;
for(var i in orig) {
if(!orig[i]) { continue; }
if(typeof(orig[i]) === 'function' || typeof(orig[i]) === 'object' || level > 1) { continue; }
if(typeof(orig[i]) === 'array') { level++; get(orig[i], tmp, level); continue; }
tmp[i] = orig[i]; continue;
}
return tmp;
}
};
I want to pass this function in to driver.executeScript using something like this:
var evalSelectAll = function(selector, getElementEssentials) {
var els = document.querySelectorAll(selector);
var els2 = [];
for(var i in els) {
els2.push(getElementEssentials(els[i]));
}
var elsStringified = JSON.stringify(els2);
return elsStringified;
};
driver.executeScript(evalSelectAll, selector, helperFunctions.getElementEssentials).then( function(data) {
// things
});
selector relates to a CSS selector expression, so "a" or "div > h1" for example.
My understanding of executeScript is that for the first argument it can accept either a string or a function - a string will be evaluated on the target page, and a function will be executed on the target page, and that all subsequent arguments are additional data?
I get the following error when trying to run this (note that to keep things simple I just passed in a test object of {test: true}:
D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:1643
throw error;
^
UnknownError: {"errorMessage":"'function get(orig, tmp, level) {\r\n\t var tmp = tmp || {};\r\n\t var level = level || 0;\r\n\t for(var i in orig) {\r\n\t if(!orig[i]) { continue; }\r\n\t if(typeof(orig[i]) === 'function' || typeof(orig[i]) === 'object' || level > 1) { continue; }\r\n\t if(typeof(orig[i]) === 'array') { level++; get(orig[i], tmp, level); continue; }\r\n\t tmp[i] = orig[i]; continue;\r\n\t }\r\n\t return tmp;\r\n\t}' is not a function (evaluating 'getElementEssentials({test: true})')","request":{"headers":{"Accept":"application/json; charset=utf-8","Connection":"keep-alive","Content-Length":"903","Content-Type":"application/json;charset=UTF-8","Host":"192.168.0.7:56849"},"httpVersion":"1.1","method":"POST","post":"{\"script\":\"return (function (selector, getElementEssentials) {\\r\\n\\r\\n\\t\\t\\treturn getElementEssentials({test: true});\\r\\n\\r\\n\\t\\t\\tvar els = document.querySelectorAll(selector);\\r\\n\\t\\t\\t\\r\\n\\t\\t\\t/*var els2 = [];\\r\\n\\t\\t\\tfor(var i in els) {\\r\\n\\t\\t\\t\\tels2.push(getElementEssentials(els[i]));\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar elsStringified = JSON.stringify(els2);\\r\\n\\r\\n\\t\\t\\treturn elsStringified;*/\\r\\n\\r\\n\\t\\t}).apply(null, arguments);\",\"args\":[\"h1\",\"function get(orig, tmp, level) {\\r\\n\\t var tmp = tmp || {};\\r\\n\\t var level = level || 0;\\r\\n\\t for(var i in orig) {\\r\\n\\t
if(!orig[i]) { continue; }\\r\\n\\t if(typeof(orig[i]) === 'function' || typeof(orig[i]) === 'object' || level > 1) { continue; }\\r\\n\\t if(typeof(orig[i]) === 'array') { level++; get(orig[i], tmp, level); continue; }\\r\\n\\t tmp[i] = orig[i]; continue;\\r\\n\\t }\\r\\n\\t return tmp;\\r\\n\\t}\"]}","url":"/execute","urlParsed":{"anchor":"","query":"","file":"execute","directory":"/","path":"/execute","relative":"/execute","port":"","host":"","password":"","user":"","userInfo":"","authority":"","protocol":"","source":"/execute","queryKey":{},"chunks":["execute"]},"urlOriginal":"/session/c18c1ff0-0f25-11e4-a90e-a36273e3866b/execute"}}
at new bot.Error (D:\Git\mateserver\node_modules\selenium-webdriver\lib\atoms\error.js:109:18)
at Object.bot.response.checkResponse (D:\Git\mateserver\node_modules\selenium-webdriver\lib\atoms\response.js:106:9)
at D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\webdriver.js:277:20
at D:\Git\mateserver\node_modules\selenium-webdriver\lib\goog\base.js:1243:15
at webdriver.promise.ControlFlow.runInNewFrame_ (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:1539:20)
at notify (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:362:12)
at notifyAll (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:331:7)
at resolve (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:309:7)
at fulfill (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:429:5)
at D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:1406:10
==== async task ====
WebDriver.executeScript()
at webdriver.WebDriver.schedule (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\webdriver.js:268:15)
at webdriver.WebDriver.executeScript (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\webdriver.js:404:15)
at D:\Git\mateserver\main.js:174:11
at D:\Git\mateserver\node_modules\selenium-webdriver\lib\goog\base.js:1243:15
at webdriver.promise.ControlFlow.runInNewFrame_ (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:1539:20)
at notify (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:362:12)
at notifyAll (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:331:7)
at resolve (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:309:7)
at fulfill (D:\Git\mateserver\node_modules\selenium-webdriver\lib\webdriver\promise.js:429:5)
Any thoughts on this? Am I using executeScript correctly? Thanks.
Update: It seems that if I put the get function inside the evalSelectAll function then it works. It seems as though passing functions as arguments to executeScript is not supported. Can anybody confirm this? It would be nice to have the code be more reusable by staying outside the eval function.

Related

Madcap Flare 11 - HTML5 output throws requirejs error - Mismatched anonymous define() module: function(C

I am building the customer manual on frontend using Madcam Flare 11 HTML5 output and referring the below js file but getting the error Uncaught Error: Mismatched anonymous define() module: function(C)
<script src="scripts/helpDoc/WHP/Default.js"></script>
After i load the Madcap library and runtime i am getting the below error. This error comes at very first time when i invoke the madcap flare methods.
require.min.js:13 Uncaught Error: Mismatched anonymous define() module: function () { 'use strict';
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function commonjsRequire () {
throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
}
function unwrapExports (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var es6Promise = createCommonjsModule(function (module, exports) {
/*!
* #overview es6-promise - a tiny implementation of Promises/A+.
* #copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
* #license Licensed under MIT license
* See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
* #version v4.2.5+7f2b526d
*/
(function (global, factory) {
module.exports = factory();
}(commonjsGlobal, (function () { function objectOrFunction(x) {
var type = typeof x;
return x !== null && (type === 'object' || type === 'function');
}
function isFunction(x) {
return typeof x === 'function';
}
var _isArray = void 0;
if (Array.isArray) {
_isArray = Array.isArray;
} else {
_isArray = function (x) {
return Object.prototype.toString.call(x) === '[object Array]';
};
}
var isArray = _isArray;
var len = 0;
var vertxNext = void 0;
var customSchedulerFn = void 0;
var asap = function asap(callback, arg) {
queue[len] = callback;
queue[len + 1] = arg;
len += 2;
if (len === 2) {
// If len is 2, that means that we need to schedule an async flush.
// If additional callbacks are queued before the queue is flushed, they
// will be processed by this flush that we are scheduling.
if (customSchedulerFn) {
customSchedulerFn(flush);
} else {
scheduleFlush();
}
}
};
function setScheduler(scheduleFn) {
customSchedulerFn = scheduleFn;
}
function setAsap(asapFn) {
asap = asapFn;
}
var browserWindow = typeof window !== 'undefined' ? window : undefined;
var browserGlobal = browserWindow || {};
var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
// test for web worker but not in IE10
var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
// node
function useNextTick() {
// node version 0.10.x displays a deprecation warning when nextTick is used recursively
// see https://github.com/cujojs/when/issues/410 for details
return function () {
return process.nextTick(flush);
};
}
// vertx
function useVertxTimer() {
if (typeof vertxNext !== 'undefined') {
return function () {
vertxNext(flush);
};
}
return useSetTimeout();
}
function useMutationObserver() {
var iterations = 0;
var observer = new BrowserMutationObserver(flush);
var node = document.createTextNode('');
observer.observe(node, { characterData: true });
return function () {
node.data = iterations = ++iterations % 2;
};
}
// web worker
function useMessageChannel() {
var channel = new MessageChannel();
channel.port1.onmessage = flush;
return function () {
return channel.port2.postMessage(0);
};
}
function useSetTimeout() {
// Store setTimeout reference so es6-promise will be unaffected by
// other code modifying setTimeout (like sinon.useFakeTimers())
var globalSetTimeout = setTimeout;
return function () {
return globalSetTimeout(flush, 1);
};
}
var queue = new Array(1000);
function flush() {
for (var i = 0; i < len; i += 2) {
var callback = queue[i];
var arg = queue[i + 1];
callback(arg);
queue[i] = undefined;
queue[i + 1] = undefined;
}
len = 0;
}
function attemptVertx() {
try {
var vertx = Function('return this')().require('vertx');
vertxNext = vertx.runOnLoop || vertx.runOnContext;
return useVertxTimer();
} catch (e) {
return useSetTimeout();
}
}
var scheduleFlush = void 0;
// Decide what async method to use to triggering processing of queued callbacks:
if (isNode) {
scheduleFlush = useNextTick();
} else if (BrowserMutationObserver) {
scheduleFlush = useMutationObserver();
} else if (isWorker) {
scheduleFlush = useMessageChannel();
} else if (browserWindow === undefined && typeof commonjsRequire === 'function') {
scheduleFlush = attemptVertx();
} else {
scheduleFlush = useSetTimeout();
}
function then(onFulfillment, onRejection) {
var parent = trequire.min.js:13)
at L (require.min.js:41)
at Object.g [as require] (require.min.js:104)
at requirejs (require.min.js:126)
at MadCap.WebHelp.HelpSystem.LoadLanguage (Default.js:269)
at Default.js:269
at MadCap.WebHelp.HelpSystem.LoadBreakpoints (Default.js:269)
at MadCap.WebHelp.HelpSystem.<anonymous> (Default.js:269)
at XMLHttpRequest.OnreadystatechangeRemote (Default.js:241)

error: Cannot read property 'split' of undefined

I am getting a value as a string from cookie which has multiple values stored in it.
I am separating these values with the use of the split() function, but I am getting an error continuously. Here is my code. It would be a great help if anyone can help me out with this.
var sourcez = jQuery.cookie("Source");
var mediumz = jQuery.cookie("Medium");
function utmze(eutmz) {
var utmz_val = jQuery.cookie("__utmzz");
for (var o = utmz_val, r = o.split("|"), a = 0; (a < r.length); a++) {
var t = r[a].split("=");
if (t[0] == eutmz) {
return t[1];
}
}
}
Make sure that string is not empty , null and undefined before you are performing the split action
function isValidString(input){
if(input != null && input != '' && input != undefined){
return true;
}
return false;
}
if(isValidString(input)){
input.split('=');
}
Make the following changes to avoid the error:
var sourcez = jQuery.cookie("Source");
var mediumz = jQuery.cookie("Medium");
function utmze(eutmz) {
var utmz_val = jQuery.cookie("__utmzz");
for (var o = utmz_val, r = o.split("|"), a = 0; (a < r.length); a++) {
if (typeof r[a] != "undefined") { // Checking if the variable is defined.
var t = r[a].split("=");
if (t[0] == eutmz) {
return t[1];
}
}
}
}

How to call one method from another method in javascript?

I would like to know if the way I have assigned values to the 'Deval' and 'Reval' variables in the 'datevalidate' method is correct?
If you notice my code, I am assigning values by specifying the entire chain pointing to the method like so 'x9.validator.check_element_val()'. Can I make a less explicit call? Since I am trying to access a function outside the immediate lexical scope of these variables, is there some way I can use a closure to better approach this?
Kindly correct my understanding of closures if it is not apt for the current scenario.
var x9 = {} || x9;
x9.validator = {
mode : 1,
check_element_val : function(el){
var returnval = 0;
if (el.value == 0 || el.value == undefined || el.value == null || el.value == ''){
returnval = 0;
}else{
returnval = 1;
}
return returnval;
},
datevalidate : function(mode, dep_el, ret_el){
var returnobj = new Object();
if (mode == 1){
Deval = x9.validator.check_element_val(dep_el);
Reval = x9.validator.check_element_val(ret_el);
if (Deval == 0 || Reval == 0){
returnobj.returnval = false;
}else{
returnobj.returnval = true;
}
}
return JSON.stringify(returnobj);
}
};
Thanks in advance.
Perhaps you would benefit from the Module pattern (see this simple example). You could do something like this:
var x9 = {} || x9;
x9.validator = (function() {
var mode = 1;
var check_element_val = function(el){
var returnval = 0;
if (el.value == 0 || el.value == undefined || el.value == null || el.value == ''){
returnval = 0;
}else{
returnval = 1;
}
return returnval;
};
var datevalidate = function(mode, dep_el, ret_el){
var returnobj = new Object();
if (mode == 1){
Deval = check_element_val(dep_el);
Reval = check_element_val(ret_el);
if (Deval == 0 || Reval == 0){
returnobj.returnval = false;
}else{
returnobj.returnval = true;
}
}
return JSON.stringify(returnobj);
}
return {
mode : mode,
check_element_val : check_element_val,
datevalidate : datevalidate
};
})();
As a side note, I'm not sure what you were trying to do with the mode variable, but be aware that the datevalidate function will not be using it unless you supply it yourself as an argument, for instance:
var validation = x9.validator.datevalidate(x9.validator.mode,someValue1,someValue2);
If you want the function to only use the mode variable from within the validator, remove mode from the argument list. If you want to keep the ability to provide different modes, though, you can write an additional function. For example:
var datevalidate_default_mode = function(dep_el,ret_el) {
return datevalidate(mode, dep_el, ret_el);
};
I think it looks nicer than adding x9.validator.mode to the datevalidate function every time you want to use the default value. And that way you can even remove mode from the module output. Either way, if you're adding a new public function, don't forget to expose it in the module output as well!

Array being reset to undefined after adding items to it

I am trying to construct a hierarchy (tree structure) using JavaScript. For that, I wrote a Node class that represents a node in the tree. When I retrieve the data from the database, it's all being retrieved properly (i.e: the root node has the ParentId as null, it has 3 children that point to it as the parent, and the descendant nodes are set up properly as well...). But when I try to map them to my JavaScript model, the Children property of the root node is ending up being undefined. I do not know how that could be posible even though during runtime, when I output the contents of the Children property in the console I can see the children nodes being added to it. Here's my code:
var Node = function (obj) {
var self = this;
var isDefined = obj != undefined;
self.hasChildren = function () {
return self.Children.length > 0;
};
self.hasParent = function () {
var p = self.ParentId;
return !(p == null || p == undefined || p == 0);
};
self.addChildren = function (objArray) {
if (!$.isArray(self.Children)) {
self.Children = [];
}
for (var i = 0; i < objArray.length; i++) {
self.addChild(objArray[i]);
}
};
self.addChild = function (obj) {
if (obj instanceof Node) {
self.Children.push(obj);
} else {
var n = new Node(obj);
self.Children.push(n);
}
};
self.removeChild = function (n) {
var index = self.Children.indexOf(n);
if (index > -1) {
self.Children.splice(index, 1);
}
};
self.Id = isDefined ? obj.Id : null;
self.ParentId = isDefined ? obj.ParentId : null;
self.Name = isDefined ? obj.Name : '';
self.Children = isDefined ? self.addChildren(obj.Children) : [];
self.TypeId = isDefined ? obj.TypeId : null;
};
The way I thought about the addChildren method, is that I would pass the raw JSON object coming from the server into the constructor of the Node object and then in case it has any children (which essentially have the same properties as the parent), addChildren will be called which will in turn create a new Node for each element. Eventually, the tree will be built recursively.
So where did I go wrong? Why does the Children property end up being undefined?
self.Children = isDefined ? self.addChildren(obj.Children) : [];
You are setting self.Children equal to the return of self.addChildren(). That function has no return.
Here is a couple things I would recommend
function Node(obj) {
// clean constructor moving function definitions to prototype
var self = this;
// ensure that we at least have an object passed in
obj = obj || {};
// default values at the top
self.Id = null;
self.ParentId = null;
self.Name = '';
self.Children = [];
self.TypeId = null;
// fold in data with $.extend, no need to specify each key manually
// third object is to overwrite any added Children as those need to be handled seperately
$.extend(self, obj, { Children : [] });
// if we have children, add them using the addChildren method
if (typeof obj.Children !== undefined && $.isArray(obj.Children)) {
self.addChildren(obj.Children);
}
}
// using prototype to reduce memory footprint
Node.prototype.hasChildren = function () {
return this.Children.length > 0;
};
Node.prototype.hasParent = function () {
var p = this.ParentId;
return !(p == null || p == undefined || p == 0);
};
Node.prototype.addChildren = function (objArray) {
for (var i = 0; i < objArray.length; i++) {
this.addChild(objArray[i]);
}
};
Node.prototype.addChild = function (obj) {
if (obj instanceof Node) {
this.Children.push(obj);
} else {
var n = new Node(obj);
this.Children.push(n);
}
};
Node.prototype.removeChild = function (n) {
var index = this.Children.indexOf(n);
if (index > -1) {
this.Children.splice(index, 1);
}
};
Then I can use this like so:
test = new Node({ Id : "Something", Children : [{ Id : "Interior", Children : [] }] })
Using prototype you reduce the memory footprint and don't create a function reference to each interior function for each Node you create. Each Node one still will reference it's internal data via a this variable.

Is there a javascript serializer for JSON.Net?

I am using Newtonsoft JSON.Net to deserialize an object with PreserveReferencesHandling enabled. jQuery does not support relinking references based on the $ref and $id syntax JSON.Net uses (I don't know if jQuery supports this functionality in any capacity).
I tried using Douglas Crockford's cycle.js but that does not seem to work with my objects, the returned object is identical to the object which got passed in.
I am not incredibly familiar with JSON.Net, but I cannot seem to find any javascript libraries which would serialize (or parse) the JSON their .NET component outputs.
How can I accomplish putting back together object references?
I was looking for a solution to this problem as well, and ended up hacking Douglas Crockford's JSON.retrocycle function. His function does not work for the $ref=some number, but it looks for something like an xpath.
This is my quick and dirty version - don't use this as is - I'm not doing any cleanup, and it probably should be a plugin, but it does the job and is good enough to get going:
function retrocycle(o) {
var self = this;
self.identifiers = [];
self.refs = [];
self.rez = function (value) {
// The rez function walks recursively through the object looking for $ref
// properties. When it finds one that has a value that is a path, then it
// replaces the $ref object with a reference to the value that is found by
// the path.
var i, item, name, path;
if (value && typeof value === 'object') {
if (Object.prototype.toString.apply(value) === '[object Array]') {
for (i = 0; i < value.length; i += 1) {
item = value[i];
if (item && typeof item === 'object') {
path = item.$ref;
if (typeof path === 'string' && path != null) {
//self.refs[parseInt(path)] = {};
value[i] = self.identifiers[parseInt(path)]
} else {
self.identifiers[parseInt(item.$id)] = item;
self.rez(item);
}
}
}
} else {
for (name in value) {
if (typeof value[name] === 'object') {
item = value[name];
if (item) {
path = item.$ref;
if (typeof path === 'string' && path != null) {
//self.refs[parseInt(path)] = {};
value[name] = self.identifiers[parseInt(path)]
} else {
self.identifiers[parseInt(item.$id)] = item;
self.rez(item);
}
}
}
}
}
}
};
self.rez(o);
self.identifiers = [];
}
Use it like this:
$.post("url/function", { ID: params.ID }, function (data) {
retrocycle(data)
// data references should be fixed up now
}, "json");
You would have to write in a double look-up into your js parser. Technically preserving reference handling is to get around circular references, which is to say what would normally cause a stack overflow during parsing.
JSON does not have a native syntax for handling this. Newtonsoft version is a custom implementation, thus parsing the JSON will be a custom implementation.
If you really have to preserve such references, XML may be a better solution. There are some json->xml libraries out there.
Here is one solution for parsing that may be of use, or at least a guide:
https://blogs.oracle.com/sundararajan/entry/a_convention_for_circular_reference
This is my enhanced version of #Dimitri. #Dimitri code sometimes isn't able to rebuild the references. If anyone improves the code, please, tell me.
Regards,
Marco Alves.
if (typeof JSON.retrocycle !== 'function') {
JSON.retrocycle = function retrocycle(o) {
//debugger;
var self = this;
self.identifiers = [];
self.refs = [];
self.buildIdentifiers = function (value) {
//debugger;
if (!value || typeof value !== 'object') {
return;
}
var item;
if (Object.prototype.toString.apply(value) === '[object Array]') {
for (var i = 0; i < value.length; i += 1) {
item = value[i];
if (!item || !item.$id || isNaN(item.$id)) {
if (item) {
self.buildIdentifiers(item);
}
continue;
}
self.identifiers[parseInt(item.$id)] = item;
self.buildIdentifiers(item);
}
return;
}
for (var name in value) {
if (typeof value[name] !== 'object') {
continue;
}
item = value[name];
if (!item || !item.$id || isNaN(item.$id)) {
if (item) {
self.buildIdentifiers(item);
}
continue;
}
self.identifiers[parseInt(item.$id)] = item;
self.buildIdentifiers(item);
}
};
self.rez = function (value) {
// The rez function walks recursively through the object looking for $ref
// properties. When it finds one that has a value that is a path, then it
// replaces the $ref object with a reference to the value that is found by
// the path.
var i, item, name, path;
if (value && typeof value === 'object') {
if (Object.prototype.toString.apply(value) === '[object Array]') {
for (i = 0; i < value.length; i += 1) {
item = value[i];
if (item && typeof item === 'object') {
if (item.$ref)
path = item.$ref;
if (typeof path === 'string' && path != null) {
//self.refs[parseInt(path)] = {};
value[i] = self.identifiers[parseInt(path)];
continue;
}
//self.identifiers[parseInt(item.$id)] = item;
self.rez(item);
}
}
} else {
for (name in value) {
if (typeof value[name] === 'object') {
item = value[name];
if (item) {
path = item.$ref;
if (typeof path === 'string' && path != null) {
//self.refs[parseInt(path)] = {};
value[name] = self.identifiers[parseInt(path)];
continue;
}
//self.identifiers[parseInt(item.$id)] = item;
self.rez(item);
}
}
}
}
}
};
self.buildIdentifiers(o);
self.rez(o);
self.identifiers = []; // Clears the array
};
}

Categories

Resources