user2010955
user2010955

Reputation: 4011

Angular 7, Protractor, randomly error "both angularJS testability and angular testability are undefined"

I'm randomly getting the error:

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 https://github.com/angular/protractor/issues/2643 for details"

running

$ ng e2e --webdriverUpdate=false --devServerTarget=

In my spec.ts file I have the following 2 tests, the first one always work, the second randomly fail with the above error.

  beforeEach(async () => {
    myPage = new MyPage();
    browser.get('my-page');
  });

  it('should work', async () => {
    console.log('should work');
    expect(true).toBeTruthy();
  });

  it('should display the title', async () => {
    const title = await $('my-title-selector').getText();
    expect(title).toEqual('My-Title');
  });

Here is MyPage PageObject:

import { $, $$ } from 'protractor';

export class MyPage {
  title = $('my-title-selector');
}

Here is my protractor.conf.js

  // Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

const { SpecReporter } = require('jasmine-spec-reporter');

exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    './src/**/*.e2e-spec.ts'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  SELENIUM_PROMISE_MANAGER: false,
  directConnect: true,
  baseUrl: 'http://localhost:4200/',
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function () { }
  },
  onPrepare() {
    require('ts-node').register({
      project: require('path').join(__dirname, './tsconfig.e2e.json')
    });
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
  },
};

Have you any ideas?

Upvotes: 9

Views: 5923

Answers (3)

krotscheck
krotscheck

Reputation: 91

You may be encountering a race condition bug in the Testabilities API outlined here: https://github.com/angular/angular/issues/15743

The TL/DR is that - if you have an APP_INITIALIZER injected that is asynchronous - say loading a config file - there's a chance that protractor will 'check for angular' before angular reports that it exists. The workaround is not to wait for angular, but instead wait for the existence of a DOM element that is rendered by the full angular application.

Here's some sample code:

await browser.wait(ExpectedConditions.urlContains(this.baseUrl));
await browser.waitForAngularEnabled(false);
await browser.wait(ExpectedConditions.visibilityOf($('.main-app-content'), 10000);
await browser.waitForAngularEnabled(true);

Upvotes: 0

DublinDev
DublinDev

Reputation: 2348

Your first test will pass always because it is not interacting with the application and therefore not checking the testability of the angular site. Your second test however does attempt to interact with the application but appears to be timing out because the page has not become testable at that stage. This is also why your tests are passing when waitForAngularEnabled(false) as they no longer check for testability.

There could an issue with your page settling if your the angular code is using $interval or $timeouts in a particular way. Checkout the updates to the main question in the link below and see if they could apply to your project.

How to debug timed out waiting for asynchronous Angular tasks? Failure to find elements on angular page occurring

Upvotes: 0

craig
craig

Reputation: 5016

If you are using async / await (which you are!) you will need to await all promises. So my guess is your beforeEach promise to load the page is not completed and you are looking for a web element that might not have been bootstrapped properly by Protractor.

beforeEach(async () => {
  myPage = new MyPage();
  await browser.get('my-page');   // browser.get returns a webdriver.promise.Promise
});

it('should work', async () => {
  console.log('should work');
  expect(true).toBeTruthy();
});

it('should display the title', async () => {
  const title = await $('my-title-selector').getText();  // <-- this is right, getText returns a webdriver.promise.Promise<string>
  expect(title).toEqual('My-Title');
});

If you are using Protractor 5.4, it is still using the selenium-webdriver control flow / promise library and not native Promises. So the webdriver.promise.Promise is from the selenium-webdriver typings, promise namespace, Promise object. In Protractor 6 (when it is out of beta), this will switch to native Promises.

Hope that helps.

Upvotes: 2

Related Questions