angular / protractor

E2E test framework for Angular apps
http://www.protractortest.org
MIT License
8.74k stars 2.31k forks source link

When authentication app redirects to another app - Cannot read property ‘$$testability’ of undefined #4753

Open Jag-MW opened 6 years ago

Jag-MW commented 6 years ago

Hello all, Protractor version: 5.1.2 Thanks for your time! Recently our application changed to support central authentication much like accounts.google.com which then redirects to corresponding app. Until then our tests were very reliable and no issues. Since then we keep getting 'Cannot read property ‘$$testability’ of undefined' at least 50% of the times.

I was looking around and I found this solution, I did a quick test and it seems to work.

element.click(); //Action which causes redirect browser.driver.get('about:blank'); return browser.get(browser.baseUrl);

Suggested solution is to go to blank page after the action which causes redirect and then go to your baseURL. Did anyone use this or any other work around? Is there any official guidance for such scenarios?

Thanks Jag

IgorSasovets commented 6 years ago

Hi, @Jag-MW ! Can you provide relevant test case and configuration file?

Jag-MW commented 6 years ago

Hi Igor, I cannot provide a working example as the site is behind a firewall, it is a simple login app, then it redirects to a different depending upon the user logging in. Following is the conf file. `'use strict'; const DEFAULT_TIMEOUT_INTERVAL = 180000;

let yargs = require('yargs'); let config; let logger = require('@xxxxxx/xxxxxx'); var sauceConnection, sauceConnectLauncher;

yargs.alias('s', 'seleniumLocation') // selenium location: 'saucelabs', or 'local' .alias('t', 'target') // target environment to test against: 'dev' or 'local' .alias('b', 'build') // build number from travis, used when e2e tests are kicked off by a build and identifies the build in saucelabs .alias('k', 'sauceKey') //the saucelabs key value required for running tests .alias ('u', 'sauceUser') //the user associated with the saucelabs account .boolean('proxy') //proxy: if true connects to saucelabs using SauceConnect .alias('proxy',['p']) .describe('proxy', 'Whether to connect via Sauce Connect');

//Checking to see if we have a selenium location, user and sauce key value if (yargs.argv.seleniumLocation === 'sauceLabs' && (!yargs.argv.sauceKey || !yargs.argv.sauceUser)) { logger.info('seleniumLocation:', yargs.argv.seleniumLocation,'sauceKey:', yargs.argv.sauceKey, 'sauceUser:', yargs.argv.sauceUser) process.exit(1) }

config = {

// Protractor waits for the page to be loaded and the new URL to appear before continuing (Default timeout: 10 seconds)
getPageTimeout: DEFAULT_TIMEOUT_INTERVAL,

// Before performing any action, Protractor asks Angular to wait until the page is synchronized (Default timeout: 11 seconds)
allScriptsTimeout: DEFAULT_TIMEOUT_INTERVAL,
beforeLaunch: function() {
    if (yargs.argv.proxy)   { 
        sauceConnectLauncher = require('sauce-connect-launcher');
        return new Promise(function (resolve, reject) {
            sauceConnectLauncher({
                username: yargs.argv.sauceUser,
                accessKey: yargs.argv.sauceKey,
            }, function (err, sauceConnectProcess) {
                if (err) {
                    return reject(err);
                }
                sauceConnection = sauceConnectProcess;
                logger.info("Connection Established");
                resolve();
            });
        });
    }
},
onPrepare: function() {
    var SpecReporter = require('jasmine-spec-reporter');
    jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'all'}));
},
afterLaunch: function(exitCode) {
    if (yargs.argv.proxy) {
        sauceConnection.close();
    }
},
capabilities: {
    'name': 'xxxxxx-automation-latestchrome',
    'browserName':'chrome',
    'version': 'latest',
    'platform': 'Windows 7',
    'chromeOptions': {
        args: ['--lang=en',
                '--windows-size=1600,1600'],
        'prefs': {
            'credentials_enable_service': false,
            'profile': {
                'password_manager_enabled': false
            }
        }
    }
},

suites:{
    accountInfo: 'specs/xxxxxx.js',
    personalization: 'specs/xxxxxx.js'
},

jasmineNodeOpts: {
    // Timeouts from Jasmine: If a spec (an 'it' block) takes longer than the Jasmine timeout (30000 msec) for any reason
    defaultTimeoutInterval: DEFAULT_TIMEOUT_INTERVAL,
    showColors: true
},

sauceBuild: yargs.argv.build

};

if(yargs.argv.seleniumLocation === 'sauceLabs') { config.sauceUser = yargs.argv.sauceUser; config.sauceKey = yargs.argv.sauceKey; };

switch(yargs.argv.target) {

case 'dev':
    config.baseUrl = 'http://xxxxxx.xxxxxx.com';
    global.userEmail = 'automation@xxxxxx.com';
    global.userPassword = 'xxxxxx';
    break;
case 'latest':
    config.baseUrl = 'https://xxxxxxxxxx.net';
    global.userEmail = 'xxxxxxx@xxxx.com';
    global.userPassword = 'xxxxxx';
    break;
case 'staging':
    config.baseUrl = 'https://xxxxxxxxxx';
    global.userEmail = 'xxxx@xxxxx.com';
    global.userPassword = 'xxxxxxx';
    break;

};

exports.config = config;`

IgorSasovets commented 6 years ago

You can provide example of test case from any app but with same logic as yours. It could help me to investigate this problem. I'll try to provide test case example with redirect to another app. And as I realised, target application opens on the same page, doesn't it?

Jag-MW commented 6 years ago

Thanks a lot, Igor! Here is a test case - As you can see we are just using login and then go about testing the app, as we decoupled login from the entire app so that each module can live on it's own, we started seeing these issues. Hope I provided what you asked for. `var App2 = require('./app2.e2e.page.js'), LoginApp = require('../login/login.e2e.page.js'), EC = protractor.ExpectedConditions;

describe('App 2 Tests (common for all browser sizes)', () => { 'use strict';

let page = new App2();
let login = new LoginPage();

beforeAll(() => {
    login.login();
    browser.wait(EC.stalenessOf(page.containerLoader));
    browser.wait(EC.elementToBeClickable(page.mwAccMenuBtn), 15000, 'Unable to find account menu');
});
afterAll(() => {
    //reset();
    login.logout();
});
it('should allow user to cancel after finding matching companies', () => {
    // Verify clicking on cancel button clears the find textbox
    page.mAccMenuBtn.click();
    browser.wait(EC.visibilityOf(page.mAccMenuInput), 15000, 'Unable to find acct menu input');
    page.mwAccMenuInput.sendKeys('FW_E2E_Company_10');
    page.mAccMenuInputCancel.click();
    expect(page.mAccMenuInput.getText()).toBe('');
});

});`

IgorSasovets commented 6 years ago

@Jag-MW , take a look at tests examples. There you can find tests for simple angular2 app with redirect after login. Hope that it'll help you to resolve your problem. Anyway, I'm waiting for your feedback)

Jag-MW commented 6 years ago

Hi @IgorSasovets Thanks a lot! I tried similar to what was suggested, my tests fail just like before with ‘$$testability’ of undefined , the Application under test is using Angular 1.7, not Angular 2.0. Not sure if that is playing a role. The workaround I am using even though not elegant but working more reliably. Thank you for all the help and looking into it.

IgorSasovets commented 6 years ago

If we're talking about angular version, sometimes it can cause troubles. I faced with such issues. In general, I tried to choose similar example to yours case, but it's not always helps) There might be some problems with waiting strategy, but it's just my assumption. I'm happy that you've solved issue and it works as expected.

qiyigg commented 6 years ago

I don't know whether I understand your problem or not. But if your login page is not written in Angular and you uses Protractor methods (e.g. click) to interactive with page elements, it will cause the waiting problem, since every Protractor method will wait for angular to be stable, but you don't have angular in your page. You can disable angular waiting by browser.waitForAngularEnabled(false), and change it back when you go back to angular page.

Your problem might more complicated than that since I saw you have a different error. But first step is to make sure whether you had the situation I mentioned above.