ProtractorJS Timeout on clicking element, any suggestions? - javascript

The Problem
I have a simple 'New Booking' button I wish to click. As shown below
<button _ngcontent-c9="" class="mat-menu-item" mat-menu-item="" role="menuitem" routerlinkactive="menu-highlight-item-left" tabindex="0" ng-reflect-router-link="/booking,create" ng-reflect-router-link-active="menu-highlight-item-left" aria-disabled="false">
New Booking
<div class="mat-menu-ripple mat-ripple" matripple="" ng-reflect-disabled="false" ng-reflect-trigger="[object HTMLButtonElement]">
</div>
</button>
Something to Note
This is a Material 2 Angular 5 Application, so every page uses and functions off of Angular.
In the past, the code I will provide further down had worked 100% every single time I ran the test without fail, which leads me to believe something else in the source code may be what causing this issue. However no additional load/spinners or popups are in the way
It has absolutely no unique attributes whatsoever, no surrounding
divs with IDs to attach to or any other way of grabbing the element.
However using my knowledge of XPath, despite the problem I managed
to find a unique locator which I believe works, as shown below.
var btnNewBooking = element(by.xpath("//*[text()='New Booking']"));
I know for a fact this should grab the element, because when I query the xpath through the ChromeDevTools it highlights the element.
The Code that originally worked
var btnNewBooking = element(by.xpath("//*[text()='New Booking']"));
btnNewBooking.click();
The Exception produced from the ProtractorJS Console
Failed: Timed out waiting for asynchronous Angular tasks to finish after 11 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
While waiting for element with locator - Locator: By(xpath, //*[text()='New Booking'])
What i've tried so far?
I've tried using Protractors built in Expected Conditions from the API Library they have provided this includes all of these:
1) Waiting Until the Presence of the element
var EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($('#abc')), 5000);
2) Waiting Until the Visibility of the element
EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to be visible on the dom.
browser.wait(EC.visibilityOf($('#abc')), 5000);
3) Waiting until the clickability of the element
var EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to be clickable.
browser.wait(EC.elementToBeClickable($('#abc')), 5000);
I've also tried using Angular's own waits such as:
browser.WaitForAngular();
and also
browser.WaitForAngularEnabled();

The click() method returns a promise, if you need some documentation on that you can surely read the following: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
The Protractor/WebDriverJS "Control Flow" takes care of settling these promises. However, my team has been struggling with the synchronization not being perfect.
A solution to this issue is the actually use EC to await for the element and then settle the promise of the click by chaining
.then()
Check out this stack overflow answer: Protractor, when should I use then() after a click()
Even better?
We use the new async functions to await promises in our protractor tests. This has enabled us to refactor much of the logic and ensure a proper controlled flow. This link describes well in the Fork in the road section why a "serial" code (executes one step at a time) is the outcome of using async/await: https://ponyfoo.com/articles/understanding-javascript-async-await

You should try disabling Protractor to wait for being synchronous with Angular. This can be done by browser.ignoreSynchronisation. Set it to truein your conf.js file.

Related

Call function without actually executing it

So I have such a weird question and I don't know if this is possible, but I will give it a shot anyways.
We have implemented OneTrust, which is a third-party cookie consent vendor and it works great and all, but we have one small hiccup that we are trying to resolve.
So within the below function:
toggleVideo: function (videoWrapper, src, cover, type, element) {
var videoElement = video.buildVideo(src, type);
// We build out video-player element
videoWrapper.html(videoElement);
// We define our variables
let onetrust = window.OneTrust;
let onetrust_obj = window.ONETRUST_MODULE;
let target = videoWrapper.html(videoElement).children('iframe');
console.log(onetrust.Close());
// Now we wait and observe for a src attribute and then show the video.
onetrust_obj.src_observer(target, function() {
video.toggle_show(videoWrapper, cover);
});
},
We have an <iframe> element that when clicked play, will wait for consent to execute - The problem is that it needs to "refresh" OneTrust so that it can change the data-src to src attribute (This is all handled using OneTrust JS, so I have no control).
When I add in the console.log(onetrust.Close());, it works just as intended and resumes playing the video when consent is given, the downfall is that it outputs an error in the console. If I remove it, the videos will not play after consent is given.
I don't want to actually execute the onetrust.Close() method as it will close the banner.
OneTrust doesn't have a proper way to "Refresh" their initialization, the techs told me that this was a one-off case, where they don't even know how to handle it.
My questions:
Is there a way that I can properly call onetrust.Close() (Seems to be the only call that actually engages the video to play after) without actually executing it?
If not, is there a way that I can somehow similarly log it, but not actually log it in the console?
Thanks all!
Strange one, may be a race-condition issue so making your code run in the next procedural iteration may resolve the issue - this can be done by adding a setTimeout with no timer value.
setTimeout(() => {
onetrust_obj.src_observer(target, function() {
video.toggle_show(videoWrapper, cover);
});
});
Alternatively, it may be worth digging into the onetrust.Close() method to see if there are any public utilities that may help 'refresh' the context you are working in.
Another idea would be to see what happens after if you ran the onetrust_obj.src_observer code block again.
EDIT: I would like to be clear that I'm just trying to help resolve debugging this, without seeing a working environment it's difficult to offer suggestions 😄

Changes in cache are not saved without async/await

I have a react-native application, it uses react-navigation. There is a functional component with a handler for button click.
I recently had a problem with async/await. I called async method in a non-async method and it did not work as I expected. I debugged it a little and I found out that the async method is called and does everything it should but after that the changes are lost.
The non-async method looked like this:
const handleDone = () => {
api.events.removeEventFromCache(eventId);
navigation.navigate(routes.Events);
};
When the method is called, an object is removed from cache and user is navigated to another screen. api.events.removeEventFromCache(eventId) got called and finished successfully and I even check the cache to see that the object was removed. The thing is that after the navigation.navigate(routes.Events) it is suddenly still in the cache.
Adding async/await keyword solved the problem but I do not really understand why:
const handleDone = async () => {
await api.events.removeEventFromCache(eventId);
navigation.navigate(routes.Events);
};
I though it would do everything without waiting for the result but why did the result disappear? It is not a question about the order of executing and waiting for the result. I do not really care about the result, I just want it to be done.
This is the log made without the keywords:
--> in cache now 3
remove the event from cache
navigate to events
cache length before remove 3
--> in cache now 3
cache length set 2
cache length checked 2
--> in cache now 3
A log with the keywords:
--> in cache now 3
remove the event from cache
cache length before remove 3
cache length set 2
cache length checked 2
navigate to events
--> in cache now 2
Yes, there is a difference in execution but my question is about the result in cache.
When you log the output before and after navigation, you are logging in 2 different contexts.
To explain this, lets say you have a cache object cache from which you wish to remove the event.
The way your code without the keywords executes is as follows:
cache is loaded by the api method to be edited
navigation method executes and it is going to send a copy of the current cache to the next screen and discard the previous.
cache-copy is created and dispatched by the navigation method.
You api method is currently still working with the cache object and not cache-copy.
cache is edited by the api method but is then discarded as the new screen is now using the cache-copy object.
In the second scenario:
The api method receives cache
The event is removed from cache
The navigation method receives the updated cache and creates cache-copy
cache-copy now has the updates list of events
The important thing to note is where and when exactly the cache-copy object is being created. If it is created before the event is removed, the code will work just fine.
Lets say, your navigation method executes the exact instant when the api method has removes the event, your code will run as expected even if async/await isn't used.
async/await is just working as expected. When managing promises, you could have two options:
//Using promises
const handleDone = () => {
api.events.removeEventFromCache(eventId).then(() => {
navigation.navigate(routes.Events);
});//You can manage failure with .catch()
};
and using async/await just as you posted, it waits until the promise is executed, it doesn't stop everything itself. Also, it is a good practice to wrap it inside a try/catch block in case the Promise fails.
The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
That means, when you call api.events.removeEventFromCache(eventId) it won't be completed immediately, so you either have to use one of both options.

How Can I wait to an element to appears using protractor and my file conf?

I'm trying to using protractor to wait to my input 'Username' to appears and then insert value on it. How Can I do it?
browser.get('http://localhost:5555/#');
var login = browser.driver.findElement(by.id('Username'));
Use Expected Conditions to wait for certain conditions, i.e. for an element to be present or visible. Use sendKeys to fill an input.
var login = element(by.id('Username'));
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf(login)).then(function() {
login.sendKeys('myuser');
});
This belongs in your spec, not your config.
If your's is an Angular App and if you are doing everything right , Then You need not wait for the Element or Page to Load.
Protractor does it for you. Refer API doc for WaitForAngular
Also check this you are interested. Wrote more info on my blog Protractor Over Selenium
I would assume couple of things
using shortcut version of element(by.id())
setting variables to let, instead var
provide timeout for wait, or it will be endless, and will fail only on test timeout (wasting time for wait)
provide wait failed error message (3rd parameter in wait function) - better readability on failures
No need to put sendKeys() to callback, protractor controlFlow will execute commands in correct order even without this.
Here is code example:
let loginField = $('#Username');
let EC = protractor.ExpectedConditions;
browser.wait(EC.visibilityOf(loginField), 3000, 'Login field should be visible before entering text');
loginField.sendKeys('myuser');

What are the inner workings of the Selenium waitFor mechanism?

I am trying to customize the behavior of Selenium's click command, (via user-extentions.js), by intercepting calls to doClick(locator). Basically I need to delay click actions whenever our application's "busy indicator" is being displayed.
(Now the standard answer for this kind of thing is to insert a waitFor into the script for those situations. Indeed, we currently have zillions of them throughout our scripts. I'm trying to eliminate those.)
Detecting the page element is the trivial part. The tricky part is getting the script to actually wait. My promising looking, but failed attempt looks like this:
var nativeClick = Selenium.prototype.doClick;
Selenium.prototype.doClick = function(locator) {
this.doWaitForCondition("!selenium.browserbot.findElementOrNull('busy-indicator')", 5000);
return nativeClick.call(this, locator);
}
The doWaitForCondition gets called before every click, but it does not wait when the condition evaluates to false. nativeClick always gets called immediately, and so no delay is introduced. I suspect that the doWaitForCondition function doesn't actually do any waiting per se, but rather establishes the conditions for it within the command execution loop. And in this case the click command is already in play, and I'm trying to run a command within a command.
Can somebody shed some light on how Selenium command execution and waitFor works, or offer suggestions on how this might be done?
I have finally solved this. And with an approach that is much better than trying to intercept click processing in its various forms. My refined goal is: to delay execution of script command completion when our application is "busy".
How Selenium command processing works:
Upon completion, each selenium command returns an ActionResult object, (see ActionHandler.prototype.execute). The terminationCondition attribute on this object is a function that determines when it is okay for selenium to proceed to the next command, (TestLoop.prototype.continueTestWhenConditionIsTrue). Basically, selenium repeatedly executes the condition function until it yields true. The result object it quite trivial:
function ActionResult(terminationCondition) {
this.terminationCondition = terminationCondition;
}
Customizing it:
I want to delay execution any time myAppIsBusy() returns true. Of course all of the standard delays need to remain in place as well, like waiting for page loads, and explicit waitFor conditions as scripted. The solution is to redefine the selenium result object in my user-extensions.js, as follows:
function ActionResult(terminationCondition) {
this.terminationCondition = function() {
// a null terminationCondition means okay to continue
return (!terminationCondition || terminationCondition()) && !myAppIsBusy();
}
}
The great thing is that this is at a low enough level that it works for the IDE, as well as for RC.
Note that this does not affect Accessor or Assert command types, which return different result objects. But that should be fine, because those commands don't effect the state of the application.
Well, a look at the java drivers com.thoughtworks.selenium.Wait class reveals this:
public void wait(String message, long timeoutInMilliseconds, long intervalInMilliseconds) {
long start = System.currentTimeMillis();
long end = start + timeoutInMilliseconds;
while (System.currentTimeMillis() < end) {
if (until()) return;
try {
Thread.sleep(intervalInMilliseconds);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
throw new WaitTimedOutException(message);
}
I am not to deep into selenium but I excpect that every waitXXX Method points to this.
So, Selenium is working with Thread.sleep(). While this might not look like an ideal solution it shows at least that you cant make it worse by using Thread.sleep() on your own if neccessary. ;-)

Event handling in Dojo

Taking Jeff Atwood's advice, I decided to use a JavaScript library for the very basic to-do list application I'm writing. I picked the Dojo toolkit, version 1.1.1. At first, all was fine: the drag-and-drop code I wrote worked first time, you can drag tasks on-screen to change their order of precedence, and each drag-and-drop operation calls an event handler that sends an AJAX call to the server to let it know that order has been changed.
Then I went to add in the email tracking functionality. Standard stuff: new incoming emails have a unique ID number attached to their subject line, all subsequent emails about that problem can be tracked by simply leaving that ID number in the subject when you reply. So, we have a list of open tasks, each with their own ID number, and each of those tasks has a time-ordered list of associated emails. I wanted the text of those emails to be available to the user as they were looking at their list of tasks, so I made each task box a Dijit "Tree" control - top level contains the task description, branches contain email dates, and a single "leaf" off of each of those branches contains the email text.
First problem: I wanted the tree view to be fully-collapsed by default. After searching Google quite extensively, I found a number of solutions, all of which seemed to be valid for previous versions of Dojo but not the one I was using. I eventually figured out that the best solution would seem to be to have a event handler called when the Tree control had loaded that simply collapsed each branch/leaf. Unfortunately, even though the Tree control had been instantiated and its "startup" event handler called, the branches and leaves still hadn't loaded (the data was still being loaded via an AJAX call). So, I modified the system so that all email text and Tree structure is added server-side. This means the whole fully-populated Tree control is available when its startup event handler is called.
So, the startup event handler fully collapses the tree. Next, I couldn't find a "proper" way to have nice formatted text for the email leaves. I can put the email text in the leaf just fine, but any HTML gets escaped out and shows up in the web page. Cue more rummaging around Dojo's documentation (tends to be out of date, with code and examples for pre-1.0 versions) and Google. I eventually came up with the solution of getting JavaScript to go and read the SPAN element that's inside each leaf node and un-escape the escaped HTML code in it's innerHTML. I figured I'd put code to do this in with the fully-collapse-the-tree code, in the Tree control's startup event handler.
However... it turns out that the SPAN element isn't actually created until the user clicks on the expando (the little "+" symbol in a tree view you click to expand a node). Okay, fair enough - I'll add the re-formatting code to the onExpand() event handler, or whatever it's called. Which doesn't seem to exist. I've searched to documentation, I've searched Google... I'm quite possibly mis-understanding Dojo's "publish/subscribe" event handling system, but I think that mainly because there doesn't seem to be any comprehensive documentation for it anywhere (like, where do I find out what events I can subscribe to?).
So, in the end, the best solution I can come up with is to add an onClick event handler (not a "Dojo" event, but a plain JavaScript event that Dojo knows nothing about) to the expando node of each Tree branch that re-formats the HTML inside the SPAN element of each leaf. Except... when that is called, the SPAN element still doesn't exist (sometimes - other times it's been cached, just to further confuse you). Therefore, I have the event handler set up a timer that periodically calls a function that checks to see if the relevant SPAN element has turned up yet before then re-formatting it.
// An event handler called whenever a "email title" tree node is expanded.
function formatTreeNode(nodeID) {
if (dijit.byId(nodeID).getChildren().length != 0) {
clearInterval(nodeUpdateIntervalID);
messageBody = dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML
if (messageBody.indexOf("<b>Message text:</b>") == -1) {
messageBody = messageBody.replace(/>/g, ">");
messageBody = messageBody.replace(/</g, "<");
messageBody = messageBody.replace(/&/g, "&");
dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML = "<b>Message text:</b><div style=\"font-family:courier\">"+messageBody+"</div>";
}
}
}
// An event handler called when a tree node has been set up - we changed the default fully-expanded to fully-collapsed.
function setupTree(theTree) {
dijit.byId("tree-"+theTree).rootNode.collapse();
messageNode = dijit.byId("tree-"+theTree).rootNode.getChildren();
for (pl = 0; pl < messageNode.length; pl++) {
messageNode[pl].collapse();
messageNode[pl].expandoNode.onclick = eval("nodeUpdateIntervalID = setInterval(\"formatTreeNode('"+messageNode[pl].id+"')\",200); formatTreeNode('"+messageNode[pl].id+"');");
}
}
The above has the feel of a truly horrible hack, and I feel sure I must have taken a wrong turn somewhere early on in my thought process. Can someone please tell me:
The correct way to go about putting nicely-formatted text inside a Dojo/Dijit Tree control.
The correct way to handle Dojo events, like where I can figure out what events are available for me to subscribe to.
A better JavaScript library to use (can I do what I want to with JQuery and avoid the all-around-the-houses approach seen above?).
PS: If you're naming a software project, give thought to its name's uniqueness in Google - I'm sure searching for "Dojo" documentation in Google would be easier without all the martial arts results getting in the way.
PPS: Firefox spellchecker knows how to spell "Atwood", correcting me when I put two 'T's instead of one. Is Jeff just that famous now?
I assume that you followed the dijit.Tree and dojo.data in Dojo 1.1 tutorial which directed you to pass the data to the tree control using a data store. That had me banging my head of a brick wall for a while.
Its not really a great approach and the alternative is not really well documented. You need to create a use model instead. I have included an example below of a tree model that I created for displaying the structure of an LDAP directory.
You will find the default implementation of the model in your dojo distribution at ./dijit/_tree/model.js. The comments should help you understand the functions supported by the model.
The IDirectoryService class the code below are stubs for server-side Java POJOs generated by Direct Web Remoting (DWR). I highly recommend DWR if you going to be doing a lot of client-server interaction.
dojo.declare("LDAPDirectoryTreeModel", [ dijit.tree.model ], {
getRoot : function(onItem) {
IDirectoryService.getRoots( function(roots) {
onItem(roots[0])
});
},
mayHaveChildren : function(item) {
return true;
},
getChildren : function(parentItem, onComplete) {
IDirectoryService.getChildrenImpl(parentItem, onComplete);
},
getIdentity : function(item) {
return item.dn;
},
getLabel : function(item) {
return item.rdn;
}
});
And here is an extract from the my JSP page where I created the model and used it to populate the tree control.
<div
dojoType="LDAPDirectoryTreeModel"
jsid="treeModel"
id="treeModel">
</div>
<div
jsid="tree"
id="tree"
dojoType="dijit.Tree" model="treeModel"
labelAttr="name"
label="${directory.host}:${directory.port}">
</div>

Categories

Resources