0

I have an Angular 9 (9.1.44) application, it utilises Azure B2C via the @azure/msal-angular (^1.0.0) and msal (^1.3.4) NPM packages to get authentication tokens to be used to access a backend service.

The app works fine, and the unit test all pass. However the default protractor e2e test that the angular scaffolding created with the project fails. I have modified the CSS selector in the page object to match my actual page content, but otherwise this it the exact same code as generated by ng new.

When I run the e2e test it fails with this error (stack trace removed for brevity):

    × should display app name the in navigation header
      - Failed: Error while waiting for Protractor to sync with the page: "both angularJS testability and angular testability are undefined.  
This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping.  
See http://git.io/v4gXM for details"

I Think this is due to some combination of the MsalGuard in the routing for the home page causing the browser to redirect to the B2C authentication page before the Angular page is rendered and possibly the HttpInterceptor that will add the B2C token to outgoing calls. But since the browser is closed after the tests fail I cant look at the browser's console logs to check. The home page does not call an external API, but is subject to the Msal route guard.

I think I should be mocking the whole Msal stuff in the e2e tests so I can inject a bearer token and check with mocked API endpoints; I want a UI integration test, not a system integration test here. The problem is I can find no documentation on how to e2e test Angular apps using @azure/msal.

e2e test code:

import { AppPage } from './app.po';
import { browser, logging } from 'protractor';

describe('workspace-project App', () => {
    let page: AppPage;

    beforeEach(() => {
        page = new AppPage();
    });

    it('should display app name the in navigation header', () => {
        page.navigateTo();
        expect(page.getTitleText()).toEqual('ASA Admin');
    });

    afterEach(async () => {
        // Assert that there are no errors emitted from the browser
        const logs = await browser.manage().logs().get(logging.Type.BROWSER);
        expect(logs).not.toContain(jasmine.objectContaining({
            level: logging.Level.SEVERE,
        } as logging.Entry));
    });
});

e2e page object code

import { browser, by, element } from 'protractor';

export class AppPage {
    navigateTo(): Promise<unknown> {
        return browser.get(`browser.baseUrl`) as Promise<unknown>;
    }

    getTitleText(): Promise<string> {
        return element(by.css('.nav-heading')).getText() as Promise<string>;
    }
}

interceptor code:

import {MsalInterceptor} from '@azure/msal-angular';
import {Injectable} from '@angular/core';

@Injectable()
export class AuthenticationInterceptor extends MsalInterceptor {}

I have tried temporarily removing the canActivate: [MsalGuard] from the home page route but this still raises the same error.

Help!

I have looked at Protractor : Make test case wait till authentication is done, Failed: Error while waiting for Protractor to sync with the page” while executing Protractor tests and Protractor: Error while waiting for Protractor to sync with the page: "both angularJS testability and angular testability are undefined but non of the suggested actions have helped.

Pete Stensønes
  • 5,595
  • 2
  • 39
  • 62
  • That error is saying that angular isn't defined on the page. This happens often, especially if you use something like webpack to build your project. It's been such a pain for me to get working that I just default to turning this off and use my own custom waits. Personally, I put this as the very last line in my `onPrepare`. Not sure if that will work for you but all you need to do is `browser.waitForAngularEnabled(false);`. This will tell protractor not to check if angular is defined on the page at all and just start looking for whatever element you are trying to find. – tehbeardedone Aug 13 '20 at 18:14
  • And if you ever need to turn it back on, you just use `browser.waitForAngularEnabled(true);` – tehbeardedone Aug 13 '20 at 18:15
  • I didn't read the other issues you linked before I posted my comments above. You have to make sure to disable the wait before you try to navigate to the page. Try doing it without mocking anything. You also may want to wait for the element to be visible before you try to interact with them. That function should, at the very least, fix the stacktrace you posted above about angular being undefined. To help you figure out the rest we would need to know what is happening after you've disabled the wait for angular. – tehbeardedone Aug 13 '20 at 18:50

0 Answers0