I'm attempting to write some UI tests for a RequireJS-based Backbone application, utilizing FluentAutomation.SeleniumWebDriver and NUnit. The HTML page in question contains a typical data-main attribute for loading the RequireJS module for the application. My struggle is in properly detecting when the application is fully loaded with these tools; the only thing I've gotten to work consistently so far is using an explicit wait in seconds, like so:
I.Open("http://myapp")
.Wait(5)
.Enter("foo").In("input[name=username]")
.Enter("bar").In("input[name=password]")
.Click("button")
.Wait(5)
.Expect.Text("Welcome").In("#welcome");
This is less than ideal -- my test as written above will always take at least 10 seconds to run, when in reality the app might be "ready" much faster than that. What I'd like to be able to do is something like this:
I.Open("http://myapp")
.WaitUntil(() => I.Assert.Exists("input[name=username]"))
.Enter("foo").In("input[name=username]")
.Enter("bar").In("input[name=password]")
.Click("button")
.WaitUntil(() => I.Assert.Exists("#welcome"))
.Expect.Text("Welcome").In("#welcome");
However, this doesn't work -- using WaitUntil here actually seems to prevent the app from loading, for reasons unclear to me, as I simply receive timeout exceptions after the default wait period (30 seconds), stating that it was unable to locate the element in question within that timeframe.
I see that Selenium 2 provides a WebDriverWait for this kind of scenario, and possibly that would work here, but am unsure how I would use this within FluentAutomation (and a quick search of the FluentAutomation code on GitHub doesn't seem to indicate it's in use within the library).
What can I use in FluentAutomation to properly wait for a RequireJS module (or DOM loaded by it) to be ready?
Additional details:
This might not be a RequireJS compatibility problem at all. I've looked further into the app and found that what's happening after the Click("button") is actually a window.location.replace -- not a RequireJS async module load. It's the one place in the app that this is occurring, apparently. So, is a window.location redirect a known scenario that would cause problems with WaitUntil, and is there an alternate approach (aside from a simple Wait(5)) that would properly handle this?
Related
I am using angular translate version 1.x for quite a while.
The use of the $translate service is really easy.
In a controller, you can simply write:
$scope.whatever = $translate('WHATEVER');
But in angular translate 2.x that has changed.
Now the service works async.
The simple and fast to write above statement has now become:
$translate('WHATEVER').then(function (whatever) {
$scope.whatever= whatever;
});
This makes things more complicated.
I don't really understand the need for this.
In most scenario's (?) the translation files will be downloaded once and then getting a translation value should be really fast. Why the need of handling this async?
Can anyone explain why this change was made?
Thanks
You should think the way around:
Who guarantees you, that all language files are loaded in time or at all. And as you're in a browser environment, everything retrieved from somewhere is asynchronous in terms of network speed etc.
So in case one "loading" process hangs, the whole application will be unusable because it is waiting for a correct reply/answer. So, if you do it synchronous, you'll have to wait until everything is loaded - just think of big images or translation files in our case here and maybe these are even unneeded translation files, because you're just using some languages out of the language stack as your personal preference.
Async loading speeds up the whole process of rendering the DOM and giving your "application" already a final state that you can work with, no matter, how many promises haven't been resolved yet.
This is one of the "big" natures of the Web Applications and yes, might cause headaches sometimes. Sometimes you'll ask yourself "why not wait this 500ms until everything is there?", but it could be the case, that one of the webservers is down or just overloaded and then you'll have to wait and wait and wait as a user. Do you always want this?
I'm trying to capture a website using PhantomJS, in particupar I'm using Pageres.
This website has got:
AngularJS
LocalStorage use
AJAX requests to an API
So, I'm testing locally and I'm not getting expected results, sometimes the screenshot will work with errors -rendering part of the contents, sometimes it won't render complete contents.
It really looks like Pagerer gets not enough time to take the screenshot once the site has fully loaded. I already added delay option but it will fail anyways, actually I could said it has worked better with out the delay option.
This is what it should be rendered:
And when it has worked best, this is what I get:
This is my code for rendering:
var pageres = new Pageres({})
.src('fantastica.a2015.mediotiempo.com', ['1440x900'], {delay: 3, crop: false});
pageres.on('warn', function (err,obj) {console.log(err,obj)});
pageres.run(function (err, screenshot) {
screenshot[0].pipe(response);
});
And, (I know there would be MUCH code to explain now) this is JS code being rendered.
Any particular advice?
Be aware of the differences between Phantom versions.
Phantom 1.9.x (which Pageres is using) is a browser engine from quite a few years ago (Chrome 13 is the closest equivalent) and will not render many HTML5 features.
Phantom 2.x is a much more modern webkit engine. But because of: a) because they have not produced a ready-made linux binary; b) some small API changes, projects like CasperJS and Pageres are holding back on supporting it. According a comment in https://github.com/sindresorhus/pageres/issues/77 if you make your own binary, and symlink to it, it works.
Also be aware that SlimerJS is an alternative to PhantomJS, based on Firefox rather than WebKit. There is no similar project based on Blink (to get screenshots how a modern Chrome would render them), but there is TrifleJS for IE. (However the Pageres pages say it is not the goal of the project to support other engines.)
Wait for DOM elements to appear, rather than using a delay.
Ajax calls, delayed loading, etc. make things very hard to predict. So, enter a polling loop, and don't take your screenshot until the DOM element you want in your screenshot is now visible. CasperJS has waitForSelector() for this case. PhantomJS has the slightly lower-level waitFor().
I think pageres would need a bit of hacking to add this functionality.
AngularJS batarang is unusable. It freezes and is unresponsive. How do you figure out where the bottlenecks are? What kind of tools can I use to profile my application to figure out if its my directives or if I have too many watchers?
This problem is perhaps caused, because you broke the watch-apply-digest cycle of the Angular framework by causing digest cycles where they should not be. This can cause Angular to create almost infinite loops watching variables for refresh, thus making your app really slow. But you can confirm that by just disabling Batarang, since the app would be slow by its own.
Anyway, if your app is efficient and only Batarang creates this issue, you can skip using it and start debugging with developer tools. I have also switched to this path various times and I can say it is quite fast (I did not expect it either). To follow this strategy, you have to simply select an element with developer tools and then execute in the JS console :
var scope = angular.element($0).scope()
So, you now have the scope of this element and you can simply check any field you want inside the scope at any time, thus testing the whole watch-apply-digest cycle of Angular.
You can learn more about the watch-apply-digest cycle here
Use case:
I'm writing system tests using Geb/Selenium (so outside of angular).
I want to decorate $http to log all requests/responses at run time.
and here's the catch: without touching the source code.
Before you rush to answer "use $provide#decorator", for example,
http://blog.xebia.com/2014/08/08/extending-angularjs-services-with-the-decorate-method/
That solution for this use case means adding a test hook into production code... that's normally a bad thing I want to avoid if possible.
Update: Geb allows you to run Javascript in the browser window. So just for the heck of it I ran the tutorial code to decorate $http. Unfortunately, it didn't work because apparently you can't re-config the app after it's been loaded. But even if it did work, this brings up another interesting point---I need to override $http before any modules have had a chance to use it.
Since decorating $http service would be the cleanest way of doing this, you can avoid polluting production code by using something like ng-constants and gulp/grunt to only add decoration code for a 'test' environment.
See related Q/A here: How do I configure different environments in Angular.js?
If you are inclined on changing this at runtime(where runtime takes place in a test environment), you may need to go 'closer to the metal' and deal with XMLHttpRequests: Add a "hook" to all AJAX requests on a page
I have an ASP script that generates page content in response to some GET parameters.
Sometimes the page generation takes a bit of time (running database queries etc) and I'd like to display something to the user while the page is loading. What is the standard way of doing this?
I'm not using AJAX on the page at the moment.
Is there a reason you're not using AJAX? I had a similar problem at an internship I did last summer. At first I decided to ignore AJAX, partially due to being lazy and not wanting to have to learn javascript/ajax usage. However, it became increasingly obvious that without ajax, the user experience was being significantly hampered (due to the same sort of thing you're talking about here... a longish server side operation).
If you're in the position to "AJAXify" your application, then I suppose you could add a loading image when the request is initially made, and then replace it with the given content when the asynchronous call returns. Jquery makes this kind of thing pretty easy with its various AJAX facilities and callback functions.
Of course, you're probably already aware of all of this... so please forgive me if I'm just restating the obvious!
You can use the Response.Flush to force something to the browser:
Response.Write("<div id=""preloader"">Loading, please wait...</div>")
Response.Flush()
'long running code...
'long running code...
'long running code...
Response.Write("<script type=""text/javascript"">document.getElementById(""preloader"").style.display = ""none"";</script>")