Protractor - Issue in using one function value from another function - javascript

Hi I am new to protractor, I have below two functions, the first function is returning a promise, which I want to use in 2nd function to retrieve value from it and use it.
getColumnNumber(columnName) {
this.eh.waitForElement(this.GridsEleMap.get("HeaderTableHeaderRow"));
var colEle = this.GridsEleMap.get("HeaderTableHeaderRow").all(by.xpath(".//td//div[contains(#class,'text-content')]"));
return colEle.getText().then(function (text) {
var actIndex = text.indexOf(columnName) + 1;
logger.info("Column Index:" + actIndex);
});
}
clickRowElementUsingRowTextAndColumnName(rowText, columnName) {
var ele = this.GridsEleMap.get("BodyTable");
return this.getColumnNumber(columnName).then(function (result) {
logger.info("Text:" + result);
var cellEle = ele.all(by.xpath(".//tr//td[" + result + "]//div[#class='virtualLink']"));
logger.info("Result:" + cellEle);
return cellEle.filter(function (elem) {
browser.actions().mouseMove(elem).perform();
browser.sleep(50);
return elem.getText().then(function (text) {
return text.trim() === rowText.trim();
});
}).each(function (element) {
browser.actions().mouseMove(element).perform();
element.click();
browser.sleep(10*1000);
});
});
Whenever I am trying to use "result" object of then function applied on first function in clickRowElementUsingRowTextAndColumnName, its value is coming as undefined. Please help me with it.
I have to pass this result value to form a xpath of particular column index and perform operation on it.

You should return properly the value from the first function.
You can try the following code for example:
getColumnNumber(columnName) {
...
return colEle.getText().then(function (text) {
return text.indexOf(columnName) + 1;
});
}
If you see, it returns the actIndex.
Pay also attention that you have few code not chained properly, all protractor methods return promises which need to be chained in order to be sure to keep the flow sync.
Then, just as suggestion, try to avoid the use of xpath locators.
They are unreadable and they bring to a decrease of performances.

Related

how to pass data from one function to another function angularjs

I am filtering data while searching in my application. I am facing the issue to write if condition to check the function selected data and second function filter data.
I have 5 side menus in my application. If I click any one of that, I need to store that value in a variable.
I have another function, I am looping the results there.But I need to check already stored value, and the current function's response value is same or not.
$scope.toggleTab = function (id) {
$scope.categoryId = id;
}
And my another function is,
$scope.getTags = function () {
var keywords_regex = new RegExp($scope.search.split(' ').map(function (i) { return '(?=.*' + i + ')'; }).join(''), 'i');
return $scope.getData.filter(function (data) {
return !!data.match(keywords_regex);
}).map(function (data) {
return data;
});
}
In this map function I need to check the result like this,
}).map(function (data) {
if( selectedData == data.id){
return data;
}
});
How can I do this, I used $rootScope. But When it came inside loop, the result has changed..

How to return an element from element.all each

I am trying to return an element from a list of elements and then perform some other actions like click, drag and drop etc. I tried following code but getting error saying nodes.click() not a function
var nodes = vectorpo.yfilesCanvasMain.all(
by.css("g text[fill-opacity='1']")
)
.each(function (eachnode, index) {
eachnode.getText().then(function (text) {
if (text == 'hello world') {
return eachnode;
}
})
});
nodes.click();
As per your problem statement, You are interested in filtering out the array of elements and want to perform some action on filtered list of elements.
As i stated problem and understood correctly then function 'each' is not suitable for this because 'each' function meant for iterating each element of ElementArrayFinder object hence Promise is returned and resolved after calling each elementFinder. 'each' function is used to solve different typr of problems.
So, what is the right approach to address problem you mentioned?
ElementArrayFinder class also provide function 'filter'. This function is meant for filtering out this list of elements. Therefore 'filter' function doesn't return any promise, it returns object of ElementArrayFinder after applying filter condition defined inside it. Refer following code snippet applied for the code you share.
vectorpo.yfilesCanvasMain.all(by.css("g text[fill-opacity='1']")).filter(eachNode, index){
return eachNode.getText().then(function(text){
return text === 'hello world';
});
}).first().click();
Code before first().click() will again return object of ElementArrayFinder which satisfy the condition of element with 'hello world' text. That's why we used first() to get the first element from ElementArrayFinder object.
ElementArrayFinder.each() only can return null, if you stick to use each() to archive your goal, you can do as following:
var found, node;
vectorpo.yfilesCanvasMain.all(
by.css("g text[fill-opacity='1']")
)
.each(function (eachnode, index) {
// if had found the wanted node, end this loop in advance
if (found) return;
eachnode.getText().then(function (text) {
if (text == 'hello world') {
node = eachnode; // assign wanted node to outside variable `node`
found = true; // change flag: found to true
}
})
})
.then(function() {
// the function inside this `then()` will only be executed when above `each()` complete
// if the ouside variable is not null or undefined,
// means find wanted node, then click
if (node) node.click();
});
Even you can archive same goal through each(), but it's more complex than filter(), I'd recommend to use filter()
Write return nodeIndex just after closing the if statement
function getNodeIndex () {
var nodeIndex
var nodes = vectorpo.yfilesCanvasMain.all(by.css("g text[fill-opacity='1']"));
return nodes.each(function (eachNode, index) {
// for (var i = 0; i < noOfNodes.length; i++) { //Somereason if I use for loop the i is always shows as length of the element when I try to print in below if statement
eachNode.getText().then(function (text) {
if (text == 'hello world') {
nodeIndex = index;
console.log("printing index value is " + nodeIndex);
//the nodeIndex is printing correct value
}
return nodeIndex;
});
})
PS : I am using my mobile browser to type this answer so the indentation might not look ok.
After reviewed few other options I tried the following method. I am trying to find the index value of the selected element and want to return that value to my call function. But when I try to output the returned value I am getting null
function getNodeIndex () {
var nodeIndex
var nodes = vectorpo.yfilesCanvasMain.all(by.css("g text[fill-opacity='1']"));
return nodes.each(function (eachNode, index) {
// for (var i = 0; i < noOfNodes.length; i++) { //Somereason if I use for loop the i is always shows as length of the element when I try to print in below if statement
eachNode.getText().then(function (text) {
if (text == 'hello world') {
nodeIndex = index;
console.log("printing index value is " + nodeIndex);
//the nodeIndex is printing correct value
}
})
return nodeIndex;
})
And in another script I used following script
getNodeIndex().then(function(value){
console.log("printing node index after retrieving in function ", value)
})
Here the value is printing as null. Please let me know what am I missing here.
Most of your code is correct, but a wrong on return.
A tiny change as following can fix your problem.
return nodes.each(function (eachNode, index) {
eachNode.getText().then(function (text) {
if (text == 'hello world') {
nodeIndex = index;
console.log("printing index value is " + nodeIndex);
}
});
})
.then(function(){
return nodeIndex;
})
Rather than returning eachnode, we can append eachnode to an ElementFinder[] array and return that. Then we will need to loop through the array to click each one.
// Make a reusable function
async function reusableFunction() {
var nodes = []; // Change 1: Initialize local array
vectorpo.yfilesCanvasMain
.all(by.css("g text[fill-opacity='1']"))
.each(function(eachnode, index) {
eachnode
.getText()
.then(function(text) {
if (text === 'hello world') {
nodes.push(eachnode); // Change 2: add the node to an array
}
})
});
return nodes; // Change 3: return
}
var nodeArray = await reusableFunction(); // Change 4: function call
// Change 5: since we return an array of nodes,
// we need to loop through the array to click each one
nodeArray.forEach(function(node) {
node.click();
});

Return Function Arguments from another Function

I am trying to streamline some code but am hitting errors when I am converting something to a function. I am effectively trying to create one function that takes a single input, and then returns three things that end up as three arguments for another function.
Note: the final function that will take three arguments is a method titled md.use()
The original (working) code is as follows:
md.use(require('markdown-it-container'), 'warning', {
render: function (tokens, idx) {
var m = tokens[idx].info;
if (tokens[idx].nesting === 1) {
return '<aside class="warning">' + md.utils.escapeHtml(m[0]);
} else {
return '</aside>\n';
}
}
});
My attempt at streamlining it / making it reusable is:
function aside(name) {
return [require('markdown-it-container'), name, {
render: function (tokens, idx) {
var m = tokens[idx].info;
if (tokens[idx].nesting === 1) {
return '<aside class="' + name + '">' + md.utils.escapeHtml(m[0]);
} else {
return '</aside>\n';
}
}
}]
}
md.use.apply(null, aside('warning'));
This creates the following error when I try to build:
TypeError: Cannot read property 'block' of null
at Function.container_plugin (/Users/Paul/Development/shins/node_modules/markdown-it-container/index.js:138:6)
at MarkdownIt.use (/Users/Paul/Development/shins/node_modules/markdown-it/lib/index.js:496:10)
In your first attempt, the call to use() is direct through the md instance, hence invoked in the context of md (this === md).
In your second attempt, you're using md.use.apply(null) that's invoking use() without its original context (which is the md instance).
Try this instead:
md.use.apply(md, aside('warning'));
See MDN

return value from onClick

I wrote a method that helps to upload images to page.There is a text area with and input area to upload file with id=imgFileUploadQwersergsd
function forumImageUpload() {
if (!($("input#imgFileUploadQwersergsd").length)) {
$("div").last().append("<input id='imgFileUploadQwersergsd' type='file' data-url='/Images' style='display:none;' />");
}
var uploader = $("input#imgFileUploadQwersergsd");
$("div").last().append("<button id='ok' onClick =\"returnString('imgFileUploadQwersergsd')\">" + "Ok" + "</button>");
returnString method return the uploaded file name:
function returnString(stringValue) {
var imgSrc = document.getElementById(stringValue).value();
var pos = imgSrc.lastIndexOf("\\");
if (pos != -1) {
imgSrc = imgSrc.substr(pos);
}
return imgSrc;
}
My question is how can I use the return value from returŅ‚String method further in code?
Simply assign a variable to the result:
var result = returnString("someString");
Edit:
Just noticed: var imgSrc = document.getElementById(stringValue).value();, .value is not a method, it's a property, remove the ().
var imgSrc = document.getElementById(stringValue).value;
The button is obviously clicked only when the user decides to click it. Therefore, there is no way you can predict when to execute code that you want to run when the user click the button. Fortunately, as you've found out, javascript allows you to intercept the button click via the onclick event.
Which means, the only way to use the return value of returnString() is from inside the onclick handler.
Say for example you have a function that wants to use the result of returnString(). Lets call it doSomething() for this example. The first obvious way to use the return value of returnString() is simply:
$("div").
last().
append(
"<button id='ok' onClick =\"doSomething(returnString('imgFileUploadQwersergsd'))\">" +
"Ok" +
"</button>"
);
But my is that ugly. What with the multiple quoting hell and a string longer than the page width. Not to mention that if you want to do any additional processing with the result you'd end up having to maintain javascript code inside a string:
"<button id='ok' onClick =\"" +
"var x = returnString('imgFileUploadQwersergsd');" +
"if (x.match(somePattern)) {" +
" doSomething(x);" +
"}" +
"\">"
That's barely readable and is a syntax error waiting to happen. Just.. no.. don't do this.
Instead you can create the HTML in string form and let jQuery parse that for you and do javascript in javascript:
$("div").last().append("<button id='ok'>OK</button>");
$("#ok").click(function(){
// from previous example:
var x = returnString('imgFileUploadQwersergsd');
if (x.match(somePattern)) {
doSomething(x);
}
});
See, no more quoting hell and much easier to read and maintain.
But wait, what if you still want to return form that click function to use the result of doSomething()? What if, for example, you're trying to do something like this:
function forumImageUpload() {
// ...
var result;
$("#ok").click(function(){
var x = returnString('imgFileUploadQwersergsd');
if (x.match(somePattern)) {
result = doSomething(x);
}
});
doSomethingElse(result); // DOES NOT WORK!
}
This wouldn't work because when doSomethingElse is called the button have not been clicked yet. The solution is to move any and all code that need to use the result of returnString or doSomething to inside the event handler:
function forumImageUpload() {
// ...
$("#ok").click(function(){
var result;
var x = returnString('imgFileUploadQwersergsd');
if (x.match(somePattern)) {
result = doSomething(x);
doSomethingElse(result); // WORKS!
}
});
}
But wait, you say, what if you want forumImageUpload to return the value? Something like this maybe:
function forumImageUpload() {
// ...
$("#ok").click(function(){
var result;
var x = returnString('imgFileUploadQwersergsd');
if (x.match(somePattern)) {
result = doSomething(x);
return doSomethingElse(result); // WONT WORK
}
});
}
var y = forumImageUpload();
doYetAnotherThing(y); // DOES NOT WORK!
The way to handle a situation like this is to let forumImageUpload accept a callback and move all code that wants to use the result inside the callback:
function forumImageUpload(callback) {
// ...
$("#ok").click(function(){
var result;
var x = returnString('imgFileUploadQwersergsd');
if (x.match(somePattern)) {
result = doSomething(x);
result = doSomethingElse(result);
if (callback) {
callback(result); // execute a callback instead of returning
}
}
});
}
forumImageUpload(function(y){
// move code that needs to use the result in here:
doYetAnotherThing(y); // WORKS!
});
The above is basically how you handle asynchronous code which covers everything from onclick events to ajax calls to setTimeout. Get comfortable passing functions to functions in javascript. You'll be doing it a lot.
var imgSrc = document.getElementById(stringValue).value;

using multiple return statements in JavaScript

I am trying to use multiple returns but just keep breaking the code. I have tried a few examples, but cant find the right combination.
How can I combine these two return statements into one?
$(".bar").popover({
content:
function (){
return $(this).data('dataObj').status;
return $(this).data('dataObj').timeline;
}
});
Use
function (){
return $(this).data('dataObj');
}
OR
function (){
// return an array
return [ $(this).data('dataObj').status, $(this).data('dataObj').timeline ]
}
OR
function (){
// return a associative array
return { "status": $(this).data('dataObj').status, "timeline": $(this).data('dataObj').timeline }
}
And process the components in the caller.
Update
The content parameter for popover needs a string as argument, you can do this:
function (){
return $(this).data('dataObj').status + " " + $(this).data('dataObj').timeline;
}
Putting aside this specific case, where the plugin demands a certain type of return value (apparently a string in this case), you can't really... A return statement terminates the function. What you'll have to do is return an object (or an array) containing those two values -
var status = $(this).data('dataObj').status;
var timeline = $(this).data('dataObj').timeline;
return [status,timeline];
Or
var status = $(this).data('dataObj').status;
var timeline = $(this).data('dataObj').timeline;
var returnObj = {'status':status, 'timeline':timeline};
return returnObj;
You can return objext ir array containig those two items
$(".bar").popover({
content:
function (){
return
{
status: $(this).data('dataObj').status;
timeline: $(this).data('dataObj').timeline;
}
}
});
Try returning an array with .status and .timeline as elements.
Ok Lix was faster.

Categories

Resources