protractor expect currenturl fails - angularjs

I'm trying to get an e2e test running against my local server and test that the resulting url (after a navigational button has been clicked) is the correct result. However the resulting url is always false.
My code is shown below:
HTML:
//http://localhost/#/current_Page
<html>
<head><title></title></head>
<body>
//should change the current url to
//http://localhost/#/new_page
<button class="button" ng-click="change_page()">Change Page</button>
</html>
TEST CODE:
var protractor = require('protractor');
require('protractor/jasminewd');
describe('Tests', function() {
var ptor;
describe('Test 1', function() {
var ptor = protractor.getInstance();
ptor.get('#/current_page');
it('change page and current url', function() {
ptor.findElement(protractor.By.className('.button').click().then(function() {
expect(ptor.currentUrl()).toContain('#/new_page');
});
});
}, 30000);
});
The issue is the current url after clicking the button remains #/current_url and does not change to the expected result #/new_page.
Does anyone know where I have gone wrong?

After search for the answer to this question I figured it out myself
The current url does not fail, I was not waiting for the promise to return to angular. The ammended code below shows where I had gone wrong
var protractor = require('protractor');
require('protractor/jasminewd');
describe('Tests', function() {
var ptor;
describe('Test 1', function() {
var ptor = protractor.getInstance();
ptor.get('#/current_page');
it('change page and current url', function() {
ptor.findElement(protractor.By.className('.button').click().then(function() {
ptor.waitForAngular();
expect(ptor.currentUrl()).toContain('#/new_page');
});
});
}, 30000);
});
This then waits for angular to route to the new page and update any bindings and then proceeds to check the expected result which is now what I would expect it to be.
Please be advised that this does not solve all issues relating to unexpected getCurrentUrl() results. if using driver.findElement() you may need to refer to JulieMR's answer to this question
I hope this helps someone stuck on this issue.

In Protractor 1.5.0 protractor.getInstance(); isn't working anymore, so you have to use browser instead.
var protractor = require('protractor');
require('protractor/jasminewd');
describe('Tests', function() {
describe('Test 1', function() {
browser.get('#/current_page');
it('change page and current url', function() {
ptor.findElement(protractor.By.className('.button').click().then(function() {
browser.waitForAngular();
expect(browser.getCurrentUrl()).toContain('#/new_page');
});
});
}, 30000);
});

You can also write a custom expected condition to wait for current url to being equal a desired one. Besides, use browser and element notations:
browser.get("#/current_page");
it("change page and current url", function() {
element(by.css(".button")).click();
browser.wait(urlChanged("#/new_page")), 5000);
});
where urlChanged is:
var urlChanged = function(url) {
return function () {
return browser.getCurrentUrl().then(function(actualUrl) {
return actualUrl.indexOf(url) >= 0;
});
};
};
Or, a Protractor>=4.0.0 solution and the urlContains expected condition:
element(by.css(".button")).click();
var EC = protractor.ExpectedConditions;
browser.wait(EC.urlContains("#/new_page"), 5000);

Related

AngularJS - scenario.js code

In the application building tutorial on angularjs.org, step-8, testing part, what does the following lines of code mean-
element.all(by.css('.phones li a')).first().click();
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
Thanks in advance!
PS:
The exact URL is- https://docs.angularjs.org/tutorial/step_08 and the code file (scenarios.js) is-
'use strict';
// Angular E2E Testing Guide:
// https://docs.angularjs.org/guide/e2e-testing
describe('PhoneCat Application', function() {
describe('phoneList', function() {
beforeEach(function() {
browser.get('index.html');
});
it('should filter the phone list as a user types into the search box', function() {
var phoneList = element.all(by.repeater('phone in $ctrl.phones'));
var query = element(by.model('$ctrl.query'));
expect(phoneList.count()).toBe(20);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(8);
});
it('should be possible to control phone order via the drop-down menu', function() {
var queryField = element(by.model('$ctrl.query'));
var orderSelect = element(by.model('$ctrl.orderProp'));
var nameOption = orderSelect.element(by.css('option[value="name"]'));
var phoneNameColumn = element.all(by.repeater('phone in $ctrl.phones').column('phone.name'));
function getNames() {
return phoneNameColumn.map(function(elem) {
return elem.getText();
});
}
queryField.sendKeys('tablet'); // Let's narrow the dataset to make the assertions shorter
expect(getNames()).toEqual([
'Motorola XOOM\u2122 with Wi-Fi',
'MOTOROLA XOOM\u2122'
]);
nameOption.click();
expect(getNames()).toEqual([
'MOTOROLA XOOM\u2122',
'Motorola XOOM\u2122 with Wi-Fi'
]);
});
it('should render phone specific links', function() {
var query = element(by.model('$ctrl.query'));
query.sendKeys('nexus');
element.all(by.css('.phones li a')).first().click();
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
});
});
});
It is testing of the routing to /phones/nexus-s.
It is written in Protractor.
The first line reads the DOM and finds all the .phones li a css rules. It then takes only the first one and calls click() on it.
element.all(by.css('.phones li a')).first().click();
The second line expects the output of the function browser.getLocationAbsUrl() to be the string /phone/nexus-s
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
So all in all the test framework clicks a button and expects it to be routed to a new page.

Testing component that opens md-dialog

I am trying to write a unit test for an Angular component that opens a dialog, but am unable to do so because I cannot trigger the closing of the dialog.
How can I cause the md dialog to resolve from the test case?
I have created a repository with a basic example where the problem can be reproduced, and copied the central bits below. There is an index.html to manually verify that the code is working, a test case that displays the problem and an example of how the tests are written in the md code.
Repository - https://github.com/gseabrook/md-dialog-test-issue
The component is extremely basic
angular
.module('test', ['ngMaterial'])
.component('dialogTest', {
template: '<button ng-click="showDialog()">Show Dialog</button>',
controller: function($scope, $mdDialog) {
var self = this;
$scope.showDialog = function() {
self.dialogOpen = true;
var confirm = $mdDialog.confirm()
.title('Dialog title')
.ok('OK')
.cancel('Cancel');
$mdDialog.show(confirm).then(function(result) {
self.dialogOpen = false;
}, function() {
self.dialogOpen = false;
});
}
}
});
And the test is also very simple
it("should open then close the dialog", function() {
var controller = element.controller("dialogTest");
expect(controller.dialogOpen).toEqual(undefined);
expect(element.find('button').length).toEqual(1);
element.find('button').triggerHandler('click');
expect(controller.dialogOpen).toBeTruthy();
rootScope.$apply();
material.flushInterimElement();
element.find('button').eq(2).triggerHandler('click');
rootScope.$apply();
material.flushInterimElement();
expect(controller.dialogOpen).toBeFalsy();
});
I managed to resolve the issue by setting the root element as the problem seemed to be related to element being compiled in the test being unconnected with the root element that angular-material appended the dialog too.
I've updated the github repository with the full code, but the important bits are
beforeEach(module(function($provide) {
rootElem = angular.element("<div></div>")
$provide.value('$rootElement', rootElem);
}));
beforeEach(inject(function(_$rootScope_, _$compile_, $mdDialog, _$material_) {
...
element = getCompiledElement();
angular.element(window.document.body).append(rootElem);
angular.element(rootElem).append(element);
}));

How to say protractor to wait till page loads?

My application taking some to page the login page.So protractor trying to enter the user name before page loads.So i need to say protractor to wait till the login page loads.
Could you please help me what command I need to use for this and where I need to use?(Please modify my code to add the wait command)
PFB for my onPrepare and beforeeach function
onPrepare: function() {
browser.driver.manage().window().maximize();
jasmine.getEnv().addReporter(new HtmlReporter({
})
}
beforeEach(function() {
browser.get('https://accounts.google.com/');
//browser.manage().timeouts().pageLoadTimeout(30000);
//browser.manage().timeouts().implicitlyWait(5000);
//browser.sleep( 10000 );
//browser.waitForAngular();
});
I used those commented functions,but it didn't work for me.
Please guide me.thanks in advance.
Assuming you want to click the Get Started button, wait for the button to become clickable:
var EC = protractor.ExpectedConditions;
var getStarted = element(by.css('button[title="Get started"]'));
browser.wait(EC.elementToBeClickable(getStarted), 5000);
getStarted.click();
Use the wait command: browser.driver.wait(condition, timeout);
onPrepare: function() {
browser.driver.manage().window().maximize();
jasmine.getEnv().addReporter(new HtmlReporter({
});
}
beforeEach(function() {
browser.driver.get('https://accounts.google.com/');
return browser.driver.wait(function() {
return browser.driver.getCurrentUrl().then(function (url) {
return url.indexOf('https://accounts.google.com/') > -1;
});
}, 5000);
//whatever you want after
});
you can wait for any condition like that.
Also, google f.e is not an angular page, so you should use:
browser.driver.get instead of protractors browser.get,
or it will wait for angular on the page.

protractor get url after click()

i'm new with protractor.. i need you'r help..
my code go like this..
describe('Protractor Demo Charts', function () {
var url = 'https://angularjs.org/';
it('should get the value of attribute d', function () {
browser.get(url);
element(by.css('.btn-warning')).click().then(function(text){
expect(browser.getCurrentUrl()).toContain('0BxgtL8yFJbacQmpCc1NMV3d5dnM');
}
);
});
});
my problem is that browser.getCurrentUrl() still return me the base url (the page that i came from 'https://angularjs.org/' )
how can i get the new Url (the URL AFTER the click )?
May you should wait until the page has been loaded.
Try this way :
describe('Protractor Demo Charts', function () {
var url = 'https://angularjs.org/';
it('should get the value of attribute d', function () {
browser.get(url);
browser.sleep(2000);
$('.btn-warning').click();
expect(browser.getCurrentUrl()).toContain('0BxgtL8yFJbacQmpCc1NMV3d5dnM');
});
});
From the doc:
Protractor will ensure that commands will automatically run in sync. For example, in the following code, element(by.model(...)).click() will run before browser2.$('.css').click():
browser.get('http://www.angularjs.org');
browser2.get('http://localhost:1234');
browser.sleep(5000);
element(by.model(...)).click();
browser2.$('.css').click();

How to run an ordered test

I want to
fill out name and password input fields
click login button
wait for page to load
click a button
How can I accomplish this? I realize promises are going to be involved. This is what I have tried:
conf.js
var env = require('./environment.js');
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['test/e2e/app/test-spec.js'],
baseUrl: env.baseUrl,
onPrepare: function() {
console.log("base url is: " + env.baseUrl);
browser.driver.get(env.baseUrl + '#/login');
element(by.model('my.username')).sendKeys('username');
element(by.model('my.password')).sendKeys('password');
console.log("clicking login button...");
element(by.css('button.md-primary.md-button.md-default-theme')).click();
// Login takes some time, so wait until it's done.
// For the test app's login, we know it's done when it redirects to
// /#/home.html.
browser.driver.wait(function() {
return browser.driver.getCurrentUrl().then(function(url) {
console.log("read current url as" + url);
console.log("returning...");
return /\/#\/home\//.test(url);
});
}, 10000);
}
};
Then a test.
test-spec.js
describe('Click button on home page', function() {
it('should click the button', function() {
console.log("looking for button on home page")
element(by.css('button.md-fab.md-accent.md-primary.md-button.md-default-theme')).click();
});
});
And for reference
environment.js
// Common configuration files with defaults plus overrides from environment vars
var webServerDefaultPort = 9002;
module.exports = {
// The address of a running selenium server.
seleniumAddress:
(process.env.SELENIUM_URL || 'http://localhost:4444/wd/hub'),
// Capabilities to be passed to the webdriver instance.
capabilities: {
'browserName':
(process.env.TEST_BROWSER_NAME || 'chrome'),
'version':
(process.env.TEST_BROWSER_VERSION || 'ANY')
},
// Default http port to host the web server
webServerDefaultPort: webServerDefaultPort,
// A base URL for your application under test.
baseUrl:
'http://' + (process.env.HTTP_HOST || 'localhost') +
':' + (process.env.HTTP_PORT || webServerDefaultPort)
};
Outputs
Using the selenium server at http://localhost:4444/wd/hub
[launcher] Running 1 instances of WebDriver
base url is: http://localhost:9002
clicking login button...
looking for button on home page
I never see "read current url as" which tells me the promise is not executing and my test executes too early.
So the Error I get makes sense because the test ran too early:
NoSuchElementError: No element found using locator: By.cssSelector("button.md-> fab.md-accent.md-primary.md-button.md-default-theme")
Put the wait() call into beforeEach():
describe('Click button on home page', function() {
beforeEach(function () {
browser.wait(function() {
return browser.getCurrentUrl().then(function(url) {
console.log("read current url as" + url);
console.log("returning...");
return /\/#\/home\//.test(url);
});
}, 10000);
});
it('should click the button', function() {
console.log("looking for button on home page")
element(by.css('button.md-fab.md-accent.md-primary.md-button.md-default-theme')).click();
});
});
Or, you can wait for the element on the home page to become visible:
describe('Click button on home page', function() {
var scope = {};
beforeEach(function () {
var EC = protractor.ExpectedConditions;
scope.defaultTheme = element(by.css('button.md-fab.md-accent.md-primary.md-button.md-default-theme'));
browser.wait(EC.visibilityOf(scope.defaultTheme), 10000);
});
it('should click the button', function() {
console.log("looking for button on home page");
scope.defaultTheme.click();
});
});
After tinkering around I had the most success with the following setup:
beforeAll() used to login
sendkeys() failed to function for me inside of OnPrepare(). I shortened OnPrepare() down to a simple redirect to login page.
I set config.js to use firefox because I had problems getting click() to function in chrome.
Use Jasmine2 (jasmine1 is default)
Use a series of browser.wait to workout the timing. You can wait for an element to exist (one that only exists on page2 per-se)
browser.waitForAngular() performs similar duties. I had less success with this feature
Protractor configuration file documentation
test-spec.js
describe('Click button on home page', function() {
beforeAll(function () {
element(by.model('my.username')).sendKeys('username');
element(by.model('my.password')).sendKeys('password');
element(by.id('loginButton')).click();
browser.wait(function() {
return browser.driver.getCurrentUrl().then(function(url) {
var result = url.indexOf("/#/home") > -1;//wait for url to change to this
return result;
});
}, 10000);
});
it('should click the button', function() {
console.log("executing test...");
browser.ignoreSynchronization = true;//Yea I had to use this here too...I dunno why this is required
element(by.id('someButton')).click();
browser.wait(function() {
console.log('inside wait for vm.firstName');
return browser.isElementPresent(by.model('vm.firstName'));
}, 10000).then(function(){
//rinse and repeat with more waits as pages change
});
expect(true).toBe(true);
});
});
conf.js
var env = require('./environment.js');
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['test/e2e/app/test-spec.js'],
baseUrl: env.baseUrl,
capabilities: {
browserName: 'firefox'
},
framework: 'jasmine2',
onPrepare: function() {
browser.driver.get(env.baseUrl + '#/login');
}
};
environment.js
no change

Resources