Protractor loop: while until some text in text field is appeared - javascript

I'm trying to realize a loop with "while' construction for scrolling down a table with loading dynamic data until some text like "n of N items displayed
(scroll for more)" changes to text like "N of N items displayed" in label element. New data are loaded by blocks in 30 rows scrolling to the bottom of table. I'm trying to realize a loop:
var Needed_text = 'needed_text';
var rows = element.all(by.xpath('table'));
var text = element(by.xpath('element').getText();
while(expect((text).not.toEqual(Needed_text)){
last_row = rows.last();
row_count_number = rows.count(rows);
browser.actions().mouseMove(last_row).perform();
browser.sleep(3000);
text = element(by.xpath('element').getText();
});
All a time when I'm trying to realize something like that I face error :
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap
out of memory
Trying to fix this bug with changing parameters max_old_space and etc. didn't help me. How can i rebuild this loop for scrolling?

First of all you need to declare a boolean variable that will change once you get the desired label. And then, you look for that element inside the while loop, but in order to check the desired text you will have to resolve the promise. Something like this:
var bool = true;
while (bool) {
element(by.xpath('element').getText().then(function(resolvedText){
if (resolvedText == NeededText){
do all you need here;
bool = false;
}
})
}

if your logic is correct below code should work.
getText() method returns a promise and it needs to be resolved .
element(by.xpath('element').getText().then(function(text){
while(expect((text).not.toEqual(Needed_text)){
last_row = rows.last();
row_count_number = rows.count(rows);
browser.actions().mouseMove(last_row).perform();
browser.sleep(3000);
text = element(by.xpath('element').getText();
});
});

Related

Protractor shows variable value as "undefined"

We are defining variables from the elements on one page of the website, clicking on the edit button, which is opening the next page. In this page we need to assert that the data captured on the earlier page matches the data shown on the 2nd page. Our problem is, once the test moves to the 2nd page, it fails to recall the variables that we defined on the 1st page. below is our code snippets:
it ('Student ID Validation', function(){
// get rows
var rows = tableData_Dashboard.all(by.tagName("tr"));
// get cell values
var cells = rows.all(by.tagName("td"));
var Student_ID = cells.get(0).getText().then(function(SID){
console.log(SID);
});
Edit_Button_1.click();
browser.sleep(2000);
expect(Student_ID_on_Reg_Page.getAttribute('value')).toEqual(Student_ID);
after execution, we get the following error
Message:
Expected '123456' to equal undefined.
We were suspecting that it may be due to asynchronization, but that is not the case. the test moves to page 2 after it stores the variable from page 1, so we are at a loss why this is happening. How can we fix this and use the variables for assertion purpose?
The problem is that you've specified the then() callback where you just log the value but don't return it:
var Student_ID = cells.get(0).getText().then(function(SID){
console.log(SID);
});
As nothing is returned, Student_ID would become a promise which would resolve into undefined.
You either need a return:
var Student_ID = cells.get(0).getText().then(function(SID){
console.log(SID);
return SID;
});
Or, remove the custom callback completely:
var Student_ID = cells.get(0).getText();
actually, the following part is causing the problem. Once we removed this part, the test is working fine.
.then(function(SID){
console.log(SID);
});

Returning empty string on a input that has a value

I have a date input in my page, which I'm using Daterangepicker framework to populate it.
Here is the code of how I start my page!
$(function(){
startSelectors();
var variaveis = returnInputVars();
var rede = variaveis[0];
var codLoja = variaveis[1];
var period = variaveis[2];
console.log('1.'+rede+' 2.'+codLoja+' 3.'+period);
});
function returnInputVars(){
var rede = $("#dropdown-parceria").val();
var codLoja = $("#dropdown-loja").val();
var periodo = $("#datepicker-range").val();
return [rede, codLoja, periodo];
};
The function startSelectors() is set to start my datepicker and other fields, which is working perfectly. After it, I create a var called "variaveis" to fill
with the values of each field because I will use then later (this functions also works perfectly at other scripts of my page).
Running the page, my console returns this:
The funny thing is, if I type at the console this, the value is shown, just while starting the script is does not work!
Anybody experienced something like this?
***UPDATE
Adding this script to my start function:
console.log($("#datepicker-range"));
The value is shown, but the second console.log don't:
EDIT 1. FIDDLE (Suggested by #halleron)
To ensure things are loaded in the correct order, it is useful to apply a page sniffer code snippet that will scan the page continuously until a condition is met, or until a preset counter limit is reached (to prevent strain on browser memory). Below is an example of what I typically use that would fit your scenario.
I think because you are dealing with asynchronous loading, you can't have a global variable that holds the values in a global scope without an interval to detect when it can be used. Otherwise, it will attempt to read the variable when it is not yet ready.
You can invoke functions anywhere you like. But I would keep all of your variables contained within the page_sniffer_2017() because that is a controlled environment where you know that everything successfully loaded and you know that the variables are ready to be accessed without error.
That way, regardless of connection speed, your functions will only fire when ready and your code will flow, sequentially, in the right order.
Within the ajax success options, always add a class to the body of the document that you can search on to determine if it has finished loading.
$(document).ready(function() {
page_sniffer_2017();
});
function page_sniffer_2017() {
var counter = 0;
var imgScanner = setInterval(function() {
if ($("#datepicker-range").length > 0 && $("#datepicker-range").val().length && jQuery('body').hasClass('date-picker-successfully-generated')) {
var periodoDatepicker = $("#datepicker-range").val(); // ok
console.log(periodoDatepicker); // ok
var variaveis = returnInputVars(replaceDate(periodoDatepicker)); // ok
console.log(variaveis[0], variaveis[1], variaveis[2]);
//startNewSelectors(variaveis);
// start ajax call
generateData(variaveis[0], variaveis[1], variaveis[2]);
clearInterval(imgScanner);
} else {
//var doNothing = "";
counter++;
if (counter === 100) {
console.log(counter);
clearInterval(imgScanner);
}
}
}, 50);
}

keep changed style by javascript after showing flash error message in ruby on rails

I have a div part in my HTML(erb), whose display is "none" at first. Then I change its style to "block" detecting the input values of datetimepicker.
I have succeeded to change the style, but the style reverses to "none" if the form gets flash error message shown after validation.
Is there any way to keep the style changed even after error message shows up.
Here is my code of javascript.
$('.datetimepicker').on('dp.change', function(e) {
var x = document.getElementById("from").value;
var y = document.getElementById("to").value;
var date_y = Date.parse(y);
var date_x_day = Date.parse(x) + (1 * 86400000);
if (date_y > date_x_day) {
$('#hotel').fadeIn(500);
} else {
$('#hotel').fadeOut(500);
}
});
I tried to put the line below after "$('#hotel').fadeIn(500); " but it doesn't work.
document.getElementById('hotel').style.display="block";
Could anyone tells me the best way??
So you want to show the hotels after selecting the date, but after the form submission the page will render with a date value and dp.change never get's triggered.
You can extract that anonymous function and call it right after the page renders.
function handleHotelVisibility(){
// method body;
}
$(function(){
handleHotelVisibility();
$('.datetimepicker').on('dp.change', handleHotelVisibility);
});

Javascript coding help. Skipping a certain part of code

I'm trying to code a web application and I have encountered a slight issue.
In the top part of the code there is a moving item around the screen. I called that item to randomly move around the screen in the beginning of my code. But then when I click a button its suppose to stop. So what I did was I created a function for when it's clicked, and it captures the x,y,z into a local variable in the function. I then entered these fixed x,y,z values into the item so it remains stationary, but for some reason I think its being overwritten by the top code and still keeps on moving. Is there any way to skip a line of code in the top section of a code when a specific function is made to run?
Code I'm talking about
function motionUpdate()
{
xvalues= //huge code which is obtained from device sensors
yvalues= //huge code which is obtained from device sensors
zvalues= //huge code which is obtained from device sensors
//There are two objects that move id1 and id2. When clicking on button id2 should stop
ui.elememenTranslate(xvalues,yvalues,zvalues,"id1") //moves according to x,y,z location
ui.elememenTranslate(xvalues,yvalues,zvalues,"id2")
}
self.Click = function()
{
var localX = xvalues;
var localY = yvalues;
var localZ = yvalues;
ui.elememenTranslate(xvalues,yvalues,zvalues,"id2")
}
Use a global variable as a condition for the code to run. Example:
var run_code = true;
Then in the code:
function motionUpdate()
{
if(run_code) // if run_code is true, else skip this part
{
....
}
}
In other part of code under certain condition, set as required:
run_code = false;
Once set, above code will be skipped.
Expanding on Ashad Shanto's comment, you can use a flag to save if the button has been clicked.
// Save if the button was clicked
var id2IsStopped = false;
function motionUpdate(){
...
ui.elememenTranslate(xvalues,yvalues,zvalues,"id1") //moves according to x,y,z location
// Don't run translate if the button was clicked
if(!id2IsStopped){
ui.elememenTranslate(xvalues,yvalues,zvalues,"id2")
}
}
self.Click = function(){
...
ui.elememenTranslate(xvalues,yvalues,zvalues,"id2");
// Record that the button was clicked
id2IsStopped = true;
}
This will record the clicked button and stop running the translation on id2. However, if you want to toggle id2's movement, you just need to toggle the value of the flag:
// Previously: id2IsStopped = true;
id2IsStopped = !id2IsStopped;

Highlighting current selected textfield - best approach

I am trying to achieve an effect like this on mobile (ios + android):
http://i.imgur.com/6zaTdRd.png
Where the currently selected textfield has a blue tinted icon + underlining
So my framework lacks any support for grey scaling a bitmap image of any sort so I need to swap between two images to achieve this effect.
My current implementation looks like this:
Please note this for the Titanium Alloy MVC framework but I'm guessing the basic logic should be similar.
I listen for blur/focus events to toggle current image
$.firstNameField.addEventListener('focus', function(e){
swapImages($.firstNameField.getParent());
});
$.lastNameField.addEventListener('focus', function(e){
swapImages($.lastNameField.getParent());
});
and then I swap images like so:
/**
* Swaps between the images (..._0 and ..._1) of an ImageView nested in a
TableRow
* ..._0 Greyscale image
* ..._0 Colour image
* #param e current TableViewRow
*/
function swapImages(e){
var imagePathSplit = (e.children[0].image).split('_');
var newImagePath = null;
if(imagePathSplit[1] == "0.png")
newImagePath = imagePathSplit[0] + "_1.png";
else
newImagePath = imagePathSplit[0] + "_0.png";
e.children[0].image = newImagePath;
return;
}
This doesn't look that great especially since I need a lot more fields with this functionality, I also want to implement tabbing (using Return key = NEXT) between the fields which will further balloon to increase 1 more event listener per field.
How would something like this be done ideally? I can think of one way of just creating the fields in code in array form which should help simplify matters (no more looking too far for Parent/Children, but that would still end up using quite a bit of listeners for switching right?
EDIT: Forgot to add how I have the textFields setup:
<TableView id="paypalTable">
<TableViewSection>
<TableViewRow id="firstNameView" class="tableRow">
<ImageView id="firstNameIcon" class="textFieldIcon"/>
<TextField id="firstNameField" class="textField"/>
</TableViewRow>
I tried something similar in one of my projects. Although I had an Alloy project I had to use a classic approach to get my desired behaviour.
In my controller:
var textFields = [];
var yourTextFieldsArray = [];
for (var i = 0; i < yourTextFieldsArray; i++) {
//Set the selected state to false initially. Maybe you need another command for it.
textFieldIsSelected[i] = false;
//create your new view
textFields[i] = Ti.UI.createView({
top : topValues[i],
width : Titanium.UI.FILL,
height : height,
id : i + 1,
touchEnabled : true
});
textFields[i].addEventListener('click', function(e) {
//Check the source id
if (e.source.id - 1 > -1) {
//use your function swapImages(e.source.id). Notice the slightly different parameter since you do not need the complete event.
swapImages(e.source.id);
}
}
function swapImages(id){
//Check the the path variable as you did
var imagePathSplit = (textFields[i-1].image).split('_');
var newImagePath = null;
if(imagePathSplit[1] == "0.png")
newImagePath = imagePathSplit[0] + "_1.png";
else
newImagePath = imagePathSplit[0] + "_0.png";
textFields[i-1].image = newImagePath;
}
This approach lets you use the same event listener for every property.
Please notice that my ids start at 1 and not at 0. This is because I had to implement such a behaviour for images and ImageViews do not accept id=0. My guess is that TextViews don't do it either so you should stick with it. Further notice that you need to decrement the id to get the corresponding object in the textFields Array. Otherwise you would get an out of bounds error.
You should create one more event listener for your NEXT event. Implement it in the same way as the first eventListener. Maybe the code is not perfect because I wrote it from my memory. If there are any questions left feel free to ask in the comments.

Categories