I have a SPA application on stack ASP MVC + AngularJS and I\'d like to test the UI. For now I\'m trying Selenium with PhantomJS and WebKit drivers.
This is a sample
If you're using AngularJS then using Protractor is a good idea.
If you use protractor you can use it's waitForAngular() method which will wait for http requests to complete. It's still good practise to wait for elements to be displayed before acting on them, depending on your language and implementation it might look this in a synchronous language
WebDriverWait wait = new WebDriverWait(webDriver, timeoutInSeconds);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id<locator>));
Or in JS you can use wait method which executes a function until it returns true
browser.wait(function () {
return browser.driver.isElementPresent(elementToFind);
});
I did the following code and it helped me for the async race condition failures.
$window._docReady = function () {
var phase = $scope.$root.$$phase;
return $http.pendingRequests.length === 0 && phase !== '$apply' && phase !== '$digest';
}
Now in selenium PageObject model, you can wait for
Object result = ((RemoteWebDriver) driver).executeScript("return _docReady();");
return result == null ? false : (Boolean) result;
Here is an example for how to wait on Angular if you're using WebDriverJS. Originally I thought you had to create a custom condition, but wait
accepts any function.
// Wait for Angular to Finish
function angularReady(): any {
return $browser.executeScript("return (window.angular !== undefined) && (angular.element(document).injector() !== undefined) && (angular.element(document).injector().get('$http').pendingRequests.length === 0)")
.then(function(angularIsReady) {
return angularIsReady === true;
});
}
$browser.wait(angularReady, 5000).then(...);
Sadly this doesn't work with PhantomJS because of CSP (content-security-policy) and unsafe-eval
. Can't wait for headless Chrome 59 on Windows.
For my particular problem with the HTML page containing iframes and developed with AnglularJS the following trick saved me a lot of time: In the DOM I clearly saw that there is an iframe which wraps all the content. So following code supposed to work:
driver.switchTo().frame(0);
waitUntilVisibleByXPath("//h2[contains(text(), 'Creative chooser')]");
But it was not working and told me something like "Cannot switch to frame. Window was closed". Then I modified the code to:
driver.switchTo().defaultContent();
driver.switchTo().frame(0);
waitUntilVisibleByXPath("//h2[contains(text(), 'Creative chooser')]");
After this everything went smoothly. So evidently Angular was mangling something with iframes and just after loading the page when you expect that driver is focused on default content it was focused by some already removed by Angular frame. Hope this may help some of you.
If you don't want to make the entire switch to Protractor but you do want to wait for Angular I recommend using Paul Hammants ngWebDriver (Java). It's based on protractor but you don't have to make the switch.
I fixed the problem by writing an actions class in which I waited for Angular (using ngWebDriver's waitForAngularRequestsToFinish()) before carrying out the actions (click, fill, check etc.).
For a code snippet see my answer to this question
If your web app is indeed created with Angular as you say, the best way to do end-to-end testing is with Protractor.
Internally, Protractor uses its own waitForAngular
method, to ensure Protractor waits automatically until Angular has finished modifying the DOM.
Thus, in the normal case, you would never need to write an explicit wait
in your test cases: Protractor does that for you.
You can look at the Angular Phonecat tutorial to learn how to set up Protractor.
If you want to use Protractor seriously, you will want to adopt pageobjects. If you want an example of that have a look at my page object test suite for the Angular Phonecat.
With Protractor you write your tests in Javascript (Protractor is indeed based on Node), and not in C# -- but in return Protractor handles all waiting for you.