Protractor tests broke with no apparent reason: “T

2019-07-22 12:55发布

问题:

My Protractor tests are broken since last Monday (2018-03-26) and I don't know why. Last time they worked (and successfully passed) was Friday before that -- 2018-03-23.

How do I debug this issue? I tried so many things and ran out of ideas...

Error output

$ npm run e2e

> myproj-spa@0.0.0 pree2e C:\W\cp\myproj\myproj.SPA
> webdriver-manager update --standalone false --gecko false

[12:54:20] I/update - chromedriver: file exists C:\W\cp\myproj\myproj.SPA\node_modules\protractor\node_modules\webdriver-manager\selenium\chromedriver_2.37.zip
[12:54:20] I/update - chromedriver: unzipping chromedriver_2.37.zip
[12:54:21] I/update - chromedriver: chromedriver_2.37.exe up to date

> myproj-spa@0.0.0 e2e C:\W\cp\myproj\myproj.SPA
> protractor

(node:13492) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.
[12:54:27] I/launcher - Running 1 instances of WebDriver
[12:54:27] I/direct - Using ChromeDriver directly...

DevTools listening on ws://127.0.0.1:12867/devtools/browser/b73e368c-ac7f-407a-890d-24fbb6f0c4c9
[11912:8644:0404/125435.389:ERROR:shader_disk_cache.cc(238)] Failed to create shader cache entry: -2
[11912:8644:0404/125435.390:ERROR:shader_disk_cache.cc(238)] Failed to create shader cache entry: -2
[11912:8644:0404/125435.390:ERROR:shader_disk_cache.cc(238)] Failed to create shader cache entry: -2
Jasmine started
2018-04-04T12:54:40.566 [WARNING] http://localhost:13403/#/ - WebSocket connection to 'ws://localhost:13403/sockjs-node/534/0y0ajoq5/websocket' failed: WebSocket is closed before the connection is established.


Reusable Component Workbench
    × overriding non-zero value with zero value should not erase input-number field
    - Failed: Timed out waiting for asynchronous Angular tasks to finish after 11 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
    While waiting for element with locator - Locator: By(css selector, *[id="inputNumber1"])
        (Session info: chrome=64.0.3282.140)
        (Driver info: chromedriver=2.37.540470 (e522d04694c7ebea4ba8821272dbef4f9b818c91),platform=Windows NT 6.1.7601 SP1 x86_64)
        at Object.checkLegacyResponse (C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\error.js:546:15)
        at parseHttpResponse (C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\http.js:509:13)
        at doSend.then.response (C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\http.js:441:30)
        at <anonymous>
        at process._tickCallback (internal/process/next_tick.js:188:7)
    From: Task: Protractor.waitForAngular() - Locator: By(css selector, *[id="inputNumber1"])
        at Driver.schedule (C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\webdriver.js:807:17)
        at ProtractorBrowser.executeAsyncScript_ (C:\W\cp\myproj\myproj.SPA\node_modules\protractor\built\browser.js:425:28)
        at angularAppRoot.then (C:\W\cp\myproj\myproj.SPA\node_modules\protractor\built\browser.js:456:33)
        at ManagedPromise.invokeCallback_ (C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\promise.js:1376:14)
        at TaskQueue.execute_ (C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\promise.js:3084:14)
        at TaskQueue.executeNext_ (C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\promise.js:3067:27)
        at asyncRun (C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\promise.js:2927:27)
        at C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\promise.js:668:7
        at <anonymous>
        at process._tickCallback (internal/process/next_tick.js:188:7)Error
        at ElementArrayFinder.applyAction_ (C:\W\cp\myproj\myproj.SPA\node_modules\protractor\built\element.js:459:27)
        at ElementArrayFinder.(anonymous function).args [as getAttribute] (C:\W\cp\myproj\myproj.SPA\node_modules\protractor\built\element.js:91:29)
        at ElementFinder.(anonymous function).args [as getAttribute] (C:\W\cp\myproj\myproj.SPA\node_modules\protractor\built\element.js:831:22)
        at Function.Page.getInputText (C:\W\cp\myproj\myproj.SPA\e2e\dom-wrappers\page.ts:75:22)
        at InputNumberWrapper.get [as text] (C:\W\cp\myproj\myproj.SPA\e2e\dom-wrappers\input-number.wrapper.ts:11:21)
        at UserContext.<anonymous> (C:\W\cp\myproj\myproj.SPA\e2e\e2e-workbench-page.e2e-spec.ts:16:43)
        at new ManagedPromise (C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\promise.js:1077:7)
        at ControlFlow.promise (C:\W\cp\myproj\myproj.SPA\node_modules\selenium-webdriver\lib\promise.js:2505:12)
    From: Task: Run fit("overriding non-zero value with zero value should not erase input-number field") in control flow
    From asynchronous test:
    Error
        at Suite.<anonymous> (C:\W\cp\myproj\myproj.SPA\e2e\e2e-workbench-page.e2e-spec.ts:15:5)
        at Object.<anonymous> (C:\W\cp\myproj\myproj.SPA\e2e\e2e-workbench-page.e2e-spec.ts:4:1)
        at Module._compile (module.js:635:30)
        at Module.m._compile (C:\W\cp\myproj\myproj.SPA\node_modules\ts-node\src\index.ts:392:23)
        at Module._extensions..js (module.js:646:10)
        at Object.require.extensions.(anonymous function) [as .ts] (C:\W\cp\myproj\myproj.SPA\node_modules\ts-node\src\index.ts:395:12)

What I tried

❗ Downgraded the webdriver-manager to the previous version (2.36, and 2.35):

webdriver-manager update --standalone false --gecko false --versions.chrome 2.36


❗ Checked out the code passed the tests before (proven by CI server) to a fresh repo clone on another machine.


❗ Downgraded Google Chrome browser to the previous version Version 64.0.3282.140 (Official Build) (64-bit).


❗ Upgraded all NPM packages in the application to latest and greatest version (see package.json below).


The issue still persists.

Interestingly, the authentication code works (the one from protractor.conf.js). It's Protractor code that fails later on...

Source Code

e2e-workbench-page.e2e-spec.ts

import { E2EWorkbenchPageWrapper } from './dom-wrappers/e2e-workbench-page.wrapper';
import { Page } from './dom-wrappers/page';

describe('Reusable Component Workbench', function () {

    let workbenchPage: E2EWorkbenchPageWrapper;

    beforeAll(() => {
        workbenchPage = new E2EWorkbenchPageWrapper();
        workbenchPage.navigateTo();
    });

    afterEach(() => Page.standardAfterEach());

    fit(`overriding non-zero value with zero value should not erase input-number field`, () => {
        expect(workbenchPage.inputNumber1.text).toBe('', 'Input number should be blank by default');

        workbenchPage.inputNumber1.setText('8');

        workbenchPage.inputNumber1.setText('0');

        expect(workbenchPage.inputNumber1.text).toBe('0.0000', 'Input number should have a properly formatted value.');
    });

});

e2e-workbench-page.wrapper.ts

import { promise as wdpromise } from 'selenium-webdriver';

import { InputNumberWrapper } from './input-number.wrapper';
import { Page } from './page';

export class E2EWorkbenchPageWrapper {

    navigateTo(): wdpromise.Promise<any> {
        return Page.navigateToRelativeAngularRoute('e2e-workbench');
    }

    get inputNumber1(): InputNumberWrapper {
        return new InputNumberWrapper(Page.getInputById('inputNumber1'));
    }

}

input-number.wrapper.ts

import { by, ElementFinder } from 'protractor';
import { promise as wdpromise } from 'selenium-webdriver';

import { Page } from './page';

export class InputNumberWrapper {

    constructor(private _elementFinder: ElementFinder) { }

    get text(): wdpromise.Promise<string> {
        return Page.getInputText(this.inputElementFinder);
    }

    setText(text: string): wdpromise.Promise<void> {
        return Page.setInputText(this.inputElementFinder, text);
    }

    focus(): wdpromise.Promise<void> {
        return Page.focusOnNonButtonElement(this.inputElementFinder);
    }

    private get inputElementFinder(): ElementFinder {
        return this._elementFinder.all(by.css('.form-control')).get(0);
    }
}

HTML of the page against which the tests are executed

<html class=""><head>
<body>
<myproj-root _nghost-c0="" ng-version="4.4.6">

<!-- Some html... -->

<div _ngcontent-c0="">
    <router-outlet _ngcontent-c0=""></router-outlet><myproj-e2e-workbench class=""><div class="row">
    <div class="col-md-3">
        <input-number class="col-md-6" id="inputNumber1"><div class="input-group has-error">

        <input type="text" class="form-control ng-invalid">
    </div></input-number>
    </div>
    <div class="col-md-3">
        <input-number class="col-md-6" id="inputNumber2"><div class="input-group">

        <input type="text" class="form-control">
        </div></input-number>
    </div>
</div>

<!-- Rest of html -->

</body></html>

package.json

{
  "name": "myproj-spa",
  "version": "0.0.0",
  "license": "MIT",
  "angular-cli": {},
  "scripts": {
    "ng": "ng",
    "start": "ng serve --port 13403 --proxy-config proxy.config.json",
    "start-prodish": "ng serve -e prod --port 13403 --proxy-config proxy.config.json",
    "start-prodish-for-e2e": "ng serve -e e2e --port 13403 --proxy-config proxy.config.json",
    "lint": "tslint \"src/**/*.ts\"",
    "test": "ng test",
    "pree2e": "webdriver-manager update --standalone false --gecko false",
    "e2e": "protractor"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^5.2.9",
    "@angular/cdk": "^5.2.4",
    "@angular/common": "^5.2.9",
    "@angular/compiler": "^5.2.9",
    "@angular/core": "^5.2.9",
    "@angular/forms": "^5.2.9",
    "@angular/http": "^5.2.9",
    "@angular/material": "^5.2.4",
    "@angular/platform-browser": "^5.2.9",
    "@angular/platform-browser-dynamic": "^5.2.9",
    "@angular/router": "^5.2.9",
    "alertifyjs": "^1.11.1",
    "bootstrap": "3.3.7",
    "core-js": "^2.5.4",
    "date-fns": "^1.28.5",
    "fancybox": "^3.0.1",
    "font-awesome": "^4.7.0",
    "jquery": "^3.3.1",
    "ngx-ssrs-reportviewer": "^1.0.2",
    "rxjs": "5.5.2",
    "ts-helpers": "^1.1.2",
    "zone.js": "^0.8.24"
  },
  "devDependencies": {
    "@angular/cli": "^1.7.3",
    "@angular/compiler-cli": "^5.2.9",
    "@types/date-fns": "^2.6.0",
    "@types/jasmine": "2.8.6",
    "@types/node": "^9.6.1",
    "jasmine-core": "^3.1.0",
    "jasmine-spec-reporter": "^4.2.1",
    "karma": "^2.0.0",
    "karma-chrome-launcher": "^2.2.0",
    "karma-cli": "^1.0.1",
    "karma-coverage-istanbul-reporter": "^1.4.2",
    "karma-jasmine": "^1.1.0",
    "karma-jasmine-html-reporter": "^1.0.0",
    "protractor": "^5.3.0",
    "protractor-jasmine2-html-reporter": "0.0.7",
    "ts-node": "^5.0.1",
    "tslint": "^5.7.0",
    "typescript": "^2.8.1"
  }
}

回答1:

Before performing any action, Protractor waits until there are no pending asynchronous tasks in your Angular application. This means that all timeouts and http requests are finished. Source: angular/protractor documentation

There was some new code a teammate has added recently (which I was not aware of). That code was doing some setInterval() stuff which prevented Protractor from detecting Angular's "readyness" to work.