e2e protractor test requiring oauth authentication
The key is to use browser.driver.get
instead of browser.get
, and to use browser.driver.sleep(someMilliseconds)
to let angular load at your final destination before using the angular-specific commands.
Here's my working protractor spec that first authorizes to Google and then counts the items in a repeater:
it('allows the user to add new slides', function () { browser.driver.get('http://localhost:3000/editor/?state=%7B"action":"create"%7D'); // at this point my server redirects to google's auth page, so let's log in var emailInput = browser.driver.findElement(by.id('Email')); emailInput.sendKeys('user@googleappsdomain.com'); var passwordInput = browser.driver.findElement(by.id('Passwd')); passwordInput.sendKeys('pa$sWo2d'); //you should not commit this to VCS var signInButton = browser.driver.findElement(by.id('signIn')); signInButton.click(); // we're about to authorize some permissions, but the button isn't enabled for a second browser.driver.sleep(1500); var submitApproveAccess = browser.driver.findElement(by.id('submit_approve_access')); submitApproveAccess.click(); // this nap is necessary to let angular load. browser.driver.sleep(10000); // at this point the protractor functions have something to hook into and // will work as normal! element(by.id('new-slide-dropdown-trigger')).click(); element(by.id('new-text-slide-trigger')).click(); var slideList = element.all(by.repeater('slide in deck.getSlides()')); slideList.then(function(slideElements) { expect(slideElements.length).toEqual(1); });});
I have a Google Auth page object (below) that does the whole job for me.
The key here is "isAngularSite(false);" function witch instructs webdriver if we are entering 'angular' or 'non angular' website. (implementation below).
/* global element, browser, by */'use strict';var GOOGLE_USERNAME = 'my.account@google.com';var GOOGLE_PASSWORD = 'password';var ec = protractor.ExpectedConditions;var Google = function () { this.emailInput = element(by.id('Email')); this.passwordInput = element(by.id('Passwd')); this.nextButton = element(by.id('next')); this.signInButton = element(by.id('signIn')); this.approveAccess = element(by.id('submit_approve_access')); this.loginToGoogle = function () { var self = this; /* Entering non angular site, it instructs webdriver to switch to synchronous mode. At this point I assume we are on google login page */ isAngularSite(false); this.emailInput.sendKeys(GOOGLE_USERNAME); this.nextButton.click(); this.passwordInput.isPresent().then(function () { browser.wait(ec.visibilityOf(self.passwordInput), BROWSER_WAIT).then(function () { self.passwordInput.sendKeys(GOOGLE_PASSWORD); self.signInButton.click(); browser.wait(ec.elementToBeClickable(self.approveAccess), BROWSER_WAIT).then(function () { self.approveAccess.click(); /* Now we are being redirected to our app, switch back to async mode (page with angular) */ isAngularSite(true); }); }); }); }}module.exports = new Google();
--- throw this into protractor.conf.js
onPrepare: function () { global.isAngularSite = function (flag) { console.log('Switching to ' + (flag ? 'Asynchronous' : 'Synchronous') + ' mode.') browser.ignoreSynchronization = !flag; }, global.BROWSER_WAIT = 5000; }
--- that's how you would use it:
it('should login though google', function(done) { mainPage.loginBtn.click(). then(function () { loginPage.connectWithGoogleBtn.click(); googlePage.loginToGoogle(); browser.wait(mainPage.userName.isPresent()). then(function () { expect(mainPage.userName.getText()). toEqual('my.account@google.com'); done(); }); }); });
You should use waitForAngularEnabled
to enable/disable waiting for Angular tasks with protractor. By default it will be enabled. You can use the following:
// do things on your Angular application// go to external oauth pagewaitForAngularEnabled(false)// login on oauth page and redirect to your Angular applicationwaitForAngularEnabled(true)browser.get('/home') // this is a page from your Angular application
waitForAngularEnabled
returns a promise. The browser.get
function blocks until the Angular page is loaded.