4

I want to do end to end testing for my angular-firebase application with protractor, but I am using Google OAuth to authenticate users.

The only option for me is for me to use protractor webdriver to login on Google. Not only is this hectic, it's also not safe because I would literally be putting my Google credentials on git.

I would appreciate any advice on how to go about this because it's very imperative for me to test my application and without authentication I can access little.

pinoyyid
  • 21,499
  • 14
  • 64
  • 115
hisabimbola
  • 196
  • 3
  • 14
  • You can put your credentials in a file that isn't added to git and then use `dotenv` from npm to load those into environment variables - That's how I handle things. However I thought of a solution I'm working on at the moment that effectively mocks out oauth providers. They all have the same behaviour, just different implementations of it. I get this 'defeats' the purpose of an E2E test but it's a lot simpler to point at a web page on your local machine that you know will succeed/fail than implement 3rd party site login in your E2E tests – Dan Jul 12 '15 at 16:05
  • Thanks for your comment @DanPantry but one challenge with your solution is that I use circleCI for continuous deployment and when push to master, the app would break because the file would not be available when circleCI is running the test. Any suggestion on how to go about this would be appreciated – hisabimbola Jul 12 '15 at 16:07
  • @DanPantry great to hear that you are already implementing a solution to testing 3rd party site login. If it's open source, I would not mind joining. This is a real problem in javascript that someone has not been able to produce a concrete solution – hisabimbola Jul 12 '15 at 16:11
  • I haven't actually started on it yet but I was thinking of it in the shower last night (all good ideas come in the shower). I'll open up a repository now though [here](https://github.com/lambdaexpression/oauth2-mock) – Dan Jul 12 '15 at 16:13
  • Great, following it now, let me know if you ever need me. Meanwhile any suggestions on how to make the test pass on CircleCI? – hisabimbola Jul 12 '15 at 16:17
  • Unless there's some way of pushing up a configuration file or setting environment variables on CircleCI, I've got nothing. :( – Dan Jul 12 '15 at 16:17

2 Answers2

3

You might want to a look at how https://github.com/pinoyyid/ngGAPI deals with this. This library (discalimer - I'm a contributor) is meant to provide an AngularJS friendly way to integrate with Google APIs and deal with OAuth along the way.

From the README.md...

One of the problems developing applications that access Google Drive is how to achieve headless, end-to-end testing when acquiring an access token generally requires a logged in browser session. ngGAPI deals with this by allowing you to set a refresh token and client secret directly into the configuration, which allows your app to acquire access tokens without being logged in. See This StackOverflow answer for the steps required to get such a refresh token.

// set your own credentials for unattended e2e testing. NB, for security, the credentials should be stored in a separate .js file which is in .gitignore OauthServiceProvider.setTestingRefreshToken(MY_REFRESHTOKEN). OauthServiceProvider.setTestingClientSecret(MY_CLIENTSECRET)

If you are in the situation that need to do server based CI, the trick is to create a sacrificial Google account and generate a refresh token for that. Thus the worst case scenario is that Eve gets to do firebase against an empty account.

Community
  • 1
  • 1
pinoyyid
  • 21,499
  • 14
  • 64
  • 115
  • Why not recommend the credentials are stored in the environment? – Lee Goddard Nov 09 '17 at 12:17
  • 1
    security vs ease of use is a permanent dilemma to which there is no single right answer. In many use cases, including mine, having the credentials stored in a local file is secure enough for my purposes. Since the credentials are long, random strings, they are not memorisable and so would need to be stored somewhere anyway. – pinoyyid Nov 09 '17 at 14:31
  • Same problem here, of course. My best practice is test set the credentials as environment variables at the start of a bash session, and then supply them on the command line if the tests aren't hard-coded to access the environment variables. So often see credentials checked in by mistake.... – Lee Goddard Nov 10 '17 at 10:40
1

Whilst I agree that ideally, you'd want to avoid testing the OAuth2 functionality itself, sometimes it is easier/required to go through those steps.

I use something like this, which makes use of another answer.

Credentials should not be in the code, and I don't even like them in files: I prefer them supplied on the command line, either as arguments or, as in this case, through environment variables.

loginWithGoogle( 
    process.env.PROJ_TEST1_GMAIL_USER,
    process.env.PROJ_TEST1_GMAIL_PASS
)

/**
* Uses the dreaded `sleep` method because finding the password 
* by any css selector tried fails.
* @param {string} username - A Google username.
* @param {string} passphrase - A Google passpharse.
* @return {Promise.<void>} Promise resolved when logged in.
*/
var loginWithGoogle = function (username, passphrase) {
    return selectWindow(1).then( () => {
            return browser.driver.findElement(by.css('[type="email"]'))
            .then( (el) => {
                el.sendKeys( username + protractor.Key.ENTER);
            }).then( () => {
                browser.driver.sleep(1000);
            }).then( () => {
                browser.actions().sendKeys( passphrase + protractor.Key.ENTER ).perform();
            });
    })
}

/**
* Focus the browser to the specified  window.
* [Implementation by and thanks to]{@link http://stackoverflow.com/questions/21700162/protractor-e2e-testing-error-object-object-object-has-no-method-getwindowha}
* @param  {Number} index The 0-based index of the window (eg 0=main, 1=popup)
* @return {webdriver.promise.Promise.<void>} Promise resolved when the index window is focused.
*/
var selectWindow = (index) => {
    browser.driver.wait(function() {
        return browser.driver.getAllWindowHandles().then( (handles) => {
            if (handles.length > index) {
                return true;
            }
        });
    });

    return browser.driver.getAllWindowHandles().then( (handles) => {
        return browser.driver.switchTo().window(handles[index]);
    });
};

One day I should find out why the commented-out code does not run: until then, this does work.

Lee Goddard
  • 10,680
  • 4
  • 46
  • 63