Protractor: Winkelmesser schlägt fehl, wenn die App mit ngUpgrade gebootet wird

Erstellt am 16. Mai 2017  ·  51Kommentare  ·  Quelle: angular/protractor

Ich arbeite an der Migration einer AngularJS (1.6)-App zu Angular (4) und habe jetzt eine Hybridanwendung, die mit NgUpgrade gebootet wurde. Dies scheint meine Winkelmesser-Tests vollständig gebrochen zu haben.

Verzeihen Sie, wenn ich das nur falsch mache, aber ich kann keine Antwort finden, warum dies nicht funktioniert.

Der Fehler, den ich erhalte, ist:

Fehlgeschlagen: Zeitüberschreitung beim Warten auf den Abschluss asynchroner Angular-Aufgaben nach 11 Sekunden. Dies kann daran liegen, dass die aktuelle Seite keine Angular-Anwendung ist. Weitere Informationen finden Sie in den FAQ: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting -for-angular

Beim Warten auf Element mit Locator - Locator: By(css selector, .toggle-summary-button)

Änderungen an Hybrid-Apps

Die Anwendung scheint gut zu laufen, und sowohl AngularJS- als auch Angular-Komponenten funktionieren wie erwartet. Die Änderungen, die ich im Bootstrapping-Prozess vorgenommen habe, sind:

1 ng-app aus dem HTML-Tag entfernt:

<html lang="en" *ng-app="myapp">

2 AppModule (@NgModule) usw. hinzugefügt.

3 Verwendet NgUpgrade, um die App zu booten:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { UpgradeModule } from '@angular/upgrade/static';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
  const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
  upgrade.bootstrap(document.body, ['myapp'], {strictDi: true});
});

Winkelmessertests

Basierend auf dem obigen Fehler scheint das Problem mit dem zusammenzuhängen, was Protractor tut, wenn es auf Angular wartet. Ich habe einen beforeEach-Block, der die Login-Seite lädt, die Details ausfüllt und sich anmeldet. Seltsamerweise öffnet er immer noch die Seite und gibt Text in das Benutzernamensfeld ein, aber dann geht es nicht weiter.

Ich habe versucht, ohne Erfolg:

  • Hinzufügen von "rootElement: 'body'" zu meiner Winkelmesser-Konfigurationsdatei
  • Hinzufügen von "ng12Hybrid: true" zu meiner Winkelmesser-Konfigurationsdatei - ich erhalte eine Meldung, dass es nicht mehr benötigt werden sollte, da es automatisch erkennt.
  • Erhöhen der Einstellung allScriptsTimeout von 11000 auf 60000 und es wird immer noch eine Zeitüberschreitung durchgeführt.
  • Deaktivieren der Einstellung waitForAngularEnabled. Dies löst das Problem mit den Login-Feldern, aber dann funktioniert keiner meiner http-Mocks und die Tests schlagen fehl.

Hilfreichster Kommentar

Hallo - wir arbeiten an einer Änderung, damit Sie konfigurieren können, auf welche Aufgaben Protractor wartet, die Ihnen bei der Bewältigung des oben genannten Problems helfen kann. Dies ist eine umfassende Änderung, die Zone.js und Angular betrifft und keine spezifische ETA hat (wird in der Größenordnung von Wochen liegen). Werde hier über den Fortschritt berichten.

Alle 51 Kommentare

Ich hatte ein ähnliches Problem, das durch Umschließen dieses Upgrade-Codes wie folgt gelöst wurde:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { UpgradeModule } from '@angular/upgrade/static';
import { AppModule } from './app.module';

angular.element(document).ready(() => {
  platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
    const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
    upgrade.bootstrap(document.body, ['myapp'], {strictDi: true});
  });
});

Und in der Konfiguration verwende ich keines davon:

//ng12Hybrid: true,
//useAllAngular2AppRoots: true,
//rootElement: 'body',

Könnten Sie einen Blick auf https://github.com/angular/protractor/issues/4234 werfen und sehen, ob Sie das gleiche Problem haben? Außerdem wird in ngUpgrade $interval Protractor jetzt dort blockieren, wo es vorher nicht war (https://github.com/angular/angular/issues/16349), aber ich arbeite daran.

@Evilweed - danke für den Vorschlag, das scheint es leider nicht für mich zu lösen.

@heathkit - Ich habe beim Durchsuchen des gesamten Projekts ein langes $intervall gefunden, aber selbst nach dem Entfernen besteht das Problem weiterhin. Ich habe auch alle Verwendungen von $interval oder $timeout in der gesamten App auskommentiert und es schlägt immer noch mit dem gleichen Fehler fehl. Wissen Sie, ob es Möglichkeiten zum Debuggen gibt und herauszufinden, was blockiert werden könnte, wenn es ein Problem mit etwas Asynchronem ist?

Kannst du deinen kompletten Fehler posten? Es soll zeigen, welche Aufgaben in der Fehlermeldung anstehen, aber möglicherweise fehlen einige. Der Fehler zeigt beispielsweise noch keine ausstehenden Aufgaben von der Angular-Seite an. Wenn Sie im Angular-Teil Ihrer App über lange Zeit asynchrone Aufgaben ausführen, müssen Sie diese außerhalb der Angular-Zone ausführen, um den Winkelmesser nicht zu blockieren. (Siehe https://github.com/angular/protractor/blob/master/docs/timeouts.md)

Nochmals vielen Dank für die Antwort. Die Fehlerdetails finden Sie unten. Ein paar Dinge zu beachten:

  • Meine Testdatei hat einen beforeAll Block, der die Benutzernamen- und Passwortfelder für die Anmeldung ausfüllen soll. Es füllt das Benutzernamensfeld erfolgreich aus, aber dann läuft eine Zeitüberschreitung in waitForAngular und das Passwortfeld wird nicht ausgefüllt. Darum geht es in der ersten Hälfte des Fehlers. Die zweite Hälfte ist der eigentliche fehlgeschlagene Test (was sinnvoll ist, da die Anmeldung fehlgeschlagen ist und das Element nicht vorhanden ist).

  • Der Fehler deutet darauf hin, dass das Problem von einem HTTP-Aufruf herrühren könnte, also möglicherweise nicht $interval oder $timeout.

  • Auf der Angular-Seite habe ich im Moment nicht viel, es ist fast alles noch AngularJS. Es kommt nicht so weit, die neue Angular-Komponente anzuzeigen, weil es nicht gelingt, sich anzumelden (also bezweifle ich, dass der http-Aufruf mit der Angular 4-Komponente zu tun hat).

  Message:
    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, *[name="password"])
  Stack:
    ScriptTimeoutError: asynchronous script timeout: result was not received in 11 seconds
      (Session info: chrome=58.0.3029.110)
      (Driver info: chromedriver=2.29.461585,platform=Mac OS X 10.12.4 x86_64) (WARNING: The server did not provide any stacktrace information)
    Command duration or timeout: 11.07 seconds
    Build info: version: '3.4.0', revision: 'unknown', time: 'unknown'
    System info: host: 'Matt.lan', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.12.4', java.version: '1.8.0_111'
    Driver info: org.openqa.selenium.chrome.ChromeDriver
    Capabilities [{applicationCacheEnabled=false, rotatable=false, mobileEmulationEnabled=false, networkConnectionEnabled=false, chrome={chromedriverVersion=2.29.461585, takesHeapSnapshot=true, pageLoadStrategy=normal, databaseEnabled=false, handlesAlerts=true, hasTouchScreen=false, version=58.0.3029.110, platform=MAC, browserConnectionEnabled=false, nativeEvents=true, acceptSslCerts=true, locationContextEnabled=true, webStorageEnabled=true, browserName=chrome, takesScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true, unexpectedAlertBehaviour=}]
    Session ID: 40deafceb49b72b1c0188ad0895c1179
        at Object.checkLegacyResponse (/Users/matt/web-app/node_modules/selenium-webdriver/lib/error.js:505:15)
        at parseHttpResponse (/Users/matt/web-app/node_modules/selenium-webdriver/lib/http.js:509:13)
        at doSend.then.response (/Users/matt/web-app/node_modules/selenium-webdriver/lib/http.js:440:13)
        at process._tickCallback (internal/process/next_tick.js:109:7)
    From: Task: Protractor.waitForAngular() - Locator: By(css selector, *[name="password"])
        at thenableWebDriverProxy.schedule (/Users/matt/web-app/node_modules/selenium-webdriver/lib/webdriver.js:816:17)
        at ProtractorBrowser.executeAsyncScript_ (/Users/matt/web-app/node_modules/protractor/lib/browser.ts:608:24)
        at angularAppRoot.then (/Users/matt/web-app/node_modules/protractor/lib/browser.ts:642:23)
        at ManagedPromise.invokeCallback_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1366:14)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
        at TaskQueue.executeNext_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2953:27)
        at asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2813:27)
        at /Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:676:7
        at process._tickCallback (internal/process/next_tick.js:109:7)Error
        at ElementArrayFinder.applyAction_ (/Users/matt/web-app/node_modules/protractor/lib/element.ts:482:23)
        at ElementArrayFinder.(anonymous function).args [as sendKeys] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:96:21)
        at ElementFinder.(anonymous function).args [as sendKeys] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:873:14)
        at /Users/matt/web-app/e2e-tests/modules/authentication.js:83:38
        at ManagedPromise.invokeCallback_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1366:14)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
        at TaskQueue.executeNext_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2953:27)
        at asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2813:27)
        at /Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:676:7
        at process._tickCallback (internal/process/next_tick.js:109:7)
    From: Task: Run beforeAll in control flow
        at Object.<anonymous> (/Users/matt/web-app/node_modules/jasminewd2/index.js:94:19)
    From asynchronous test: 
    Error
        at Suite.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:28:3)
        at Object.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:7:1)
        at Module._compile (module.js:571:32)
        at Object.Module._extensions..js (module.js:580:10)
        at Module.load (module.js:488:32)
        at tryModuleLoad (module.js:447:12)
  Message:
    Failed: No element found using locator: By(css selector, .toggle-asset-summary-button)
  Stack:
    NoSuchElementError: No element found using locator: By(css selector, .toggle-asset-summary-button)
        at elementArrayFinder.getWebElements.then (/Users/matt/web-app/node_modules/protractor/lib/element.ts:851:17)
        at ManagedPromise.invokeCallback_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1366:14)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
        at TaskQueue.executeNext_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2953:27)
        at asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2813:27)
        at /Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:676:7
        at process._tickCallback (internal/process/next_tick.js:109:7)Error
        at ElementArrayFinder.applyAction_ (/Users/matt/web-app/node_modules/protractor/lib/element.ts:482:23)
        at ElementArrayFinder.(anonymous function).args [as click] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:96:21)
        at ElementFinder.(anonymous function).args [as click] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:873:14)
        at Page.showAssetSummaryComponent (/Users/matt/web-app/e2e-tests/specs/home/page-object.js:289:31)
        at Object.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:63:12)
        at /Users/matt/web-app/node_modules/jasminewd2/index.js:112:25
        at new ManagedPromise (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1067:7)
        at ControlFlow.promise (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2396:12)
        at schedulerExecute (/Users/matt/web-app/node_modules/jasminewd2/index.js:95:18)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
    From: Task: Run fit("should open asset summary component") in control flow
        at Object.<anonymous> (/Users/matt/web-app/node_modules/jasminewd2/index.js:94:19)
        at /Users/matt/web-app/node_modules/jasminewd2/index.js:64:48
        at ControlFlow.emit (/Users/matt/web-app/node_modules/selenium-webdriver/lib/events.js:62:21)
        at ControlFlow.shutdown_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2565:10)
        at shutdownTask_.MicroTask (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2490:53)
        at MicroTask.asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2619:9)
    From asynchronous test: 
    Error
        at Suite.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:62:5)
        at Suite.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:60:3)
        at Object.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:7:1)

Genau das gleiche – mit einer frischen neuen 'Quickstart'-Angular4-Konfiguration und einem sehr einfachen Winkelmesser-Test. Im Nicht-Hybrid-Modus funktioniert alles gut.

Failed: Timed out waiting for asynchronous Angular tasks to finish after 50 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, #loginModal h4)

Ich sehe dieses Problem immer noch.
Verwendung: @angular/upgrade 4.2.2
das den Fix von @heathkit (https://github.com/angular/angular/pull/16836) enthält, um die Testability-API stabil zu machen, aber es scheint immer noch nicht zu funktionieren

Wenn jemand ein Repository freigeben könnte, das das Problem zeigt, werde ich es weiter untersuchen.

Was ich bisher entdeckt habe

Nach einigem Suchen habe ich eine der Ursachen für das Timeout entdeckt, das ich sehe (oder eine davon, ich vermute, es ist nicht die einzige). Es stellt sich heraus, dass eine der Abhängigkeiten, die ich verwende, window.setTimeout() aufruft, was die Synchronisierung von Protractor verhindert (in meinem Fall ist es
Winkelmoment .

Dies lief gut, wenn meine App nur AngularJS lief, verursachte jedoch beim Bootstrapping als Hybrid-App ein Timeout-Problem (vermutlich aufgrund der Art und Weise, wie sie in Zonen ausgeführt wird).

Beispiel-Repository

Ich habe ein Beispiel-Repository unter https://github.com/mattwilson1024/angular-hybrid erstellt . Es ist eine Hybrid-App (NgUpgrade) mit den folgenden Teilen:

  • Die äußerste Seite (die CharactersPage) wird von AngularJS (1.6) bereitgestellt.
  • Die Seite enthält CharacterListComponent, eine herabgestufte Angular (4)-Komponente.
  • Die CharacterListComponent verwendet eine weitere Angular 4-Komponente, die CharacterDetail-Komponente.

Der Master-Zweig enthält die funktionierende App und einen einzelnen Winkelmesser-Test, der kein Problem besteht.

Der Winkelmoment-Zweig fügt eine Abhängigkeit von der angular-moment Bibliothek hinzu und verwendet seine am-time-ago Direktive, um der Seite ein Label hinzuzufügen, das anzeigt, wie viele Jahre seit der ersten Episode vergangen sind. Die App selbst läuft einwandfrei, aber der Winkelmessertest kann jetzt nicht synchronisiert werden.

Auszeiten

Um dies zu bestätigen, habe ich versucht, dem AngularJS-Controller verschiedene Arten von Timeouts hinzuzufügen, mit den folgenden Ergebnissen. Dies bestätigte, dass $interval funktioniert, $timeout, setInterval oder setTimeout jedoch nicht.

function sayHello() { console.log('Hello, world'); }

$interval(sayHello, 30000); // Succeeds (with Angular 4.2.0 and later - see https://github.com/angular/angular/pull/16836)
$timeout(sayHello, 30000); // Fails - Timed out waiting for asynchronous Angular tasks to finish after 11 seconds
setInterval(sayHello, 30000); // Fails - Timed out waiting for asynchronous Angular tasks to finish after 11 seconds
setTimeout(sayHello, 300000); // Fails - Timed out waiting for asynchronous Angular tasks to finish after 11 seconds

Nächste Schritte

Nachdem ich es geschafft habe, aufzuspüren, was passiert, bin ich mir immer noch nicht sicher, was die Lösung ist. Ich verstehe, dass es notwendig sein kann, bestimmte Codeteile "außerhalb der Angular-Zone" auszuführen, obwohl ich nicht sicher bin, wie dies von AngularJS-Segmenten aus geht, in denen ich keine TypeScript-Klasse habe, in die die Zone usw.

Selbst dann, wenn der Problemcode in einer Bibliothek eines Drittanbieters auftritt (wie in meinem Beispiel mit Winkelmoment) - was wäre der Ansatz, um dieses Problem zu umgehen, um die Tests wie vor dem Upgrade auf a zum Laufen zu bringen? Hybrid-Setup?

Zonen werden im Fenster festgelegt, sodass Sie NgZone nicht injizieren müssen. Sie können in AngularJS so außerhalb der Angular-Zone laufen

if (Zone && Zone.current.name === 'angular') {
  let angularZone = Zone.current;
  Zone.parent.run(() => {
    $timeout(() => {
       // Stuff here is run outside the Angular zone and will not be change detected
       angularZone.run(() => {
         // Inside the zone and will be change detected.
       });
    }, 500);
  });
}

Sie haben Recht, dass jede Verwendung von setTimeout oder setInterval, auch im Code von Drittanbietern, die Stabilität von Winkelmesser blockieren und Zeitüberschreitungen verursachen kann. Wir fügen Angular und AngularJS die Aufgabenverfolgung hinzu, die das Debuggen dieser Probleme erleichtern sollte, indem wir Ihnen zeigen, woher die asynchrone Aufgabe stammt, bei der das Zeitlimit von Protractor überschritten wurde. Realistischerweise wird es ein paar Monate dauern, bis wir das in Protractor bekommen.

Was den Winkel angeht, ist es möglicherweise die beste Option, eine PR an sie zu senden, die sie wie oben gezeigt zonenbewusst macht. Andernfalls können Sie waitForAngular selektiv deaktivieren und stattdessen browser.wait und ExpectedConditions verwenden. Wir arbeiten an einer flexibleren waitForAngular-API, mit der Sie steuern können, auf welche Aufgaben Sie warten, aber das ist noch ein weiter Weg.

In unserer Situation können Tests generisch (ohne Angular-Sync) geschrieben werden ( browser.ignoreSynchronization = true mit ECs usw.), aber scheitern, wenn browser.ignoreSynchronization = false; mit "Error while waiting for Protractor to sync with the page: "Cannot read property '$$testability' of undefined"

Dies macht im Grunde unsere gesamte automatisierte E2E-Testsuite zunichte.

Wenn ich mit Protractor 5.1.2 ng12Hybrid: true setze, erhalte ich die Meldung:

Sie haben ng12Hybrid eingestellt. Ab Protractor 4.1.0 kann Protractor automatisch ableiten, wenn Sie eine ngUpgrade-App verwenden (solange ng1 geladen ist, bevor Sie platformBrowserDynamic() aufrufen), und dieses Flag wird für die meisten Benutzer nicht mehr benötigt

Offensichtlich ist die Dokumentation von angle.io hier also veraltet: https://angular.io/guide/upgrade#e2e -tests

Berühmte letzte Worte aus demselben Dokument:

E2E-Tests beschäftigen sich nicht wirklich mit der internen Struktur der Anwendungskomponenten. Das bedeutet auch, dass, obwohl Sie das Projekt während des Upgrades einiges ändern, die E2E-Testsuite mit nur geringfügigen Änderungen weiterhin bestehen sollte. Sie haben das Verhalten der Anwendung aus Sicht des Benutzers nicht geändert.

Angesichts dieser Aussage auf angle.io:

@heathkit Vielen Dank für den Tipp zum Ausführen innerhalb und außerhalb von Zonen, aber um ehrlich zu sein, suchen wir nach einer Lösung, die mit unserem vorhandenen AngularJS-Code so wie er ist, ausgeführt wird. Wir verfügen über eine umfangreiche Protractor-Suite, die auf impliziter AngularJS-Synchronisierung basiert. Unser Ziel ist es, unser AngularJS mit ngUpgrade zu Angular zu migrieren, aber dieses Problem, dass unsere vorhandene Protractor-Suite nicht im Hybrid-Upgrade-Modus ausgeführt werden kann, hat unsere Initiative vereitelt. Eine vernünftige Lösung wäre jetzt sehr willkommen.

Wenn wir etwas grundlegend falsch machen, würden wir gerne wissen, was.

Stehe vor dem gleichen Problem! :0

Das gleiche hier zu sehen: AngularJS 1.6 -> Angular 4.2.4

Kennt jemand einen Workaround, wie groß er auch sein mag? Das heißt, ohne auf 1.6 zurückzukehren.

@quinw68 - @heathkit schlägt in seiner vorherigen Antwort in diesem Thread eine Methode zum Ausführen des Codes außerhalb der Angular-Zone vor. Ich denke, dies bedeutet Änderungen am Quellcode Ihrer App, damit sie funktioniert, und kann sogar davon abhängen, dass Bibliotheken von Drittanbietern Unterstützung hinzufügen. Bitte korrigieren Sie mich, falls ich falsch liege.

Ein anderer mir bekannter „Workaround“ besteht darin, Ihre vorhandene App so zu behandeln, als wäre sie keine AngularJS-App mehr. Dies bedeutet, dass Sie Ihre Winkelmesser-Tests ändern müssen, damit sie nicht mehr von Natur aus auf Angular warten, sondern explizit auf das Vorhandensein von Elementen auf der Seite prüfen, bevor sie ihren Zustand abfragen. Je nach Größe Ihrer App kann dies eine entmutigende Aufgabe sein.

Hoffentlich hilft das.

Soweit ich das Problem verstehe, ist dies nicht ngUpgrade-spezifisch, sondern eher Angular-spezifisch. Ich meine, selbst in einer reinen Angular-App (2+) führt ein langes setTimeout (entweder direkt oder über eine Drittanbieterbibliothek) zu einer Zeitüberschreitung des Winkelmessers.

@heathkit , kannst du das bestätigen oder habe ich etwas übersehen? Wirft ngUpgrade den Winkelmesser aus oder verschlimmert es die Dinge in irgendeiner Weise?

@gkalpak Sie haben Recht, dass dies eher ein Angular-spezifisches Problem ist, aber die Leute stoßen darauf, wenn sie ngUpgrade mit einer großen AngularJS-App verwenden.

In AngularJS wartete Protractor nur auf asynchrone Aufgaben, die mit $browser.defer() gestartet wurden – im Grunde nur $timeout und $http Aufrufe im Anwendungscode. Sobald jemand mit der Verwendung von ngUpgrade beginnt, werden seine bestehenden Protractor-Tests aufgrund langer setTimeout Aufrufe (möglicherweise von Bibliotheken von Drittanbietern) ablaufen, wo dies zuvor nicht der Fall war. Das Problem besteht also hauptsächlich darin, dass ngUpgrade wie beabsichtigt funktioniert und jetzt die AngularJS-App und die Winkelmesser-Tests plötzlich der Angular (2+)-Semantik unterliegen.

Wie würde man das also für eine reine Angular-App lösen? Was würde man tun, wenn eine Angular-Bibliothek eines Drittanbieters ein langes setTimeout , was zu einer Zeitüberschreitung bei Protractor führt?

@vikerman Ich habe gehört, dass Sie bald ein neues Teammitglied auf die weisen Sie es Ihnen zu, bis es an Bord ist.

Wir haben keine wirklich gute Antwort für den Fall, dass eine Drittanbieter-App ein langes Timeout festlegt. Im Moment können Sie nur waitForAngular deaktivieren und sich auf ExpectedConditions verlassen, aber das ist nicht ideal. Ich arbeite daran, Protractor eine Funktion hinzuzufügen, die den Leuten mehr Kontrolle darüber gibt, auf welche Makrotasks ihre Tests warten, aber das ist noch ein weiter Weg.

@heathkit @vikerman Irgendein Update dazu bitte?

Das Projekt, an dem ich arbeite, hatte dieses Problem zuvor, aber sie haben die kontinuierliche Abfragefunktion so umschlossen, dass sie außerhalb der Winkelzone ausgeführt wird, sodass browser.waitForAngular();

Zunächst vielen Dank an das Protractor-Team/die Mitwirkenden . Protractor war die Grundlage für meine App, da sie über Dutzende von Releases über 3 Jahre hinweg nur sehr wenige Produktionsprobleme hatte, über 200 Dienste, Anweisungen, Komponenten und Controller mit etwa 400 e2e-Protractor-Tests.

Dies ist eine gute Erinnerung daran, wie sehr ich mich auf Protractor verlasse, aber dieses Problem hat mein Angular-Upgrade gestoppt, nachdem ich 2 Wochen nur dem Bootstrapping von ngUpgrade und den Zusatzanforderungen gewidmet hatte. Das ist erledigt, aber jetzt können die e2e-Tests nicht ausgeführt werden und die Bereitstellung wird (noch) ohne sichtbaren Weg nach vorne gestoppt.

Dank der tollen Infos von @mattwilson1024 habe ich sichergestellt, dass $timeout , setTimeout , setInterval in unserer App nicht verwendet werden.

Aber ich habe festgestellt, dass angular-ui-bootstrap (v.2.5.6) und ui-select (v0.19.8) $timeout . Ich benutze beide Bibliotheken ausgiebig. Anscheinend verlassen sich auch viele andere auf sie - angle-ui-bootstrap - 400.000 npm Downloads/Monat, ui-select 100k+/Monat.

angular-ui-bootstrap hat im Jahr 2015 ein Problem erstellt, um $timeout durch $interval zu ersetzen, speziell aufgrund dieses Problems, hat aber leider entschieden, dass "dieser Fehler im Winkelmesser und nicht hier behoben werden sollte. Dies wird geschlossen." Außerdem bietet angular-ui-bootstrap keine Updates mehr in Liu ihrer Angular-Version @ng-bootstrap/ng-bootstrap, die sich in der Beta befindet.

Ich bin überrascht, wenn ich stark genutzte Angular-Bibliotheken sehe, die die Gültigkeit von Protractor nicht gewährleisten. Darin stimme ich $timeout Abhängigkeiten ausfindig gemacht habe und möglicherweise, anstatt nur ngGrade einzubeziehen und anzufangen, Elemente für ein Upgrade auszuwählen, wenn möglich, ich haben wahrscheinlich eine Abhängigkeit zum Upgrade oder zur Migration von diesen Drittanbieterelementen zusammen mit dem Hinzufügen von ngUpgrade. Pfui.

Ich recherchiere weiterhin für meine App (bestimmte Täter, die die Zeitüberschreitungen verursachen, wie ich vorankomme usw.) und werde alles zurückmelden, was für andere von Nutzen sein könnte.

Wo ich $timeout gefunden habe:
angle-ui-bootstrap v2.5.6

  • UibAlertController
  • UibCarouselController
  • UibDatepickerPopupController
  • UibTypeaheadController
  • UibTooltip

ui-auswahl

  • uiAuswählen
  • uiSelectMultiple
  • uiSelectSingle
  • uiSelectSort
  • uisOpenClose

angle-resource $resource injiziert und verwendet $timeout
Winkelfilter ( $window.setTimeout )

Hallo - wir arbeiten an einer Änderung, damit Sie konfigurieren können, auf welche Aufgaben Protractor wartet, die Ihnen bei der Bewältigung des oben genannten Problems helfen kann. Dies ist eine umfassende Änderung, die Zone.js und Angular betrifft und keine spezifische ETA hat (wird in der Größenordnung von Wochen liegen). Werde hier über den Fortschritt berichten.

Ich habe das gleiche Problem, wenn ich meine [email protected] mit NgUpgrade auf [email protected] boote.
Vielen Dank für die obigen Kommentare, insbesondere @mattwilson1024 und @tonybranfort , die mir helfen, das Problem besser zu verstehen.

Ich habe in meinem Fall eine Lösung gefunden, indem ich browser.ignoreSynchronization = true/false; um den Code, der das Zeitlimit überschreitet, hinzugefügt habe.

Zum Beispiel hat einer meiner Tests eine einfache Auswahl in einer Gruppe von Optionsfeldern durchgeführt und erwartet, dass die richtige Option ausgewählt ist:

it('should be able to pick option', function () {
        expect(myPage.optionSelected()).toBe(false);
        myPage.pickOption('Option 1');
        expect(myPage.anyOptionSelected()).toBe(true);
        expect(myPage.getSelectedOption()).toBe('Option 1');
});

Als ich versuche, diesen Code zu debuggen, habe ich festgestellt, dass im letzten Schritt eine Zeitüberschreitung aufgetreten ist. Ich nehme an, das zugrunde liegende Problem ist ein Toolip von angle-ui-bootstrap mit der Verwendung von $timeout: https://github.com/angular-ui/bootstrap/blob/master/src/tooltip/tooltip.js

In diesem Test gibt es jedoch keine asynchronen Aufrufe. Also füge ich am Anfang einfach ignoreSynchronization hinzu und setze es am Ende zurück:

it('should be able to pick a font', function () {
        browser.ignoreSynchronization = true;  // tell protractor do not waitForAngular
        expect(myPage.optionSelected()).toBe(false);
        myPage.PickOption('Option 1');
        expect(myPage.anyOptionSelected()).toBe(true);
        expect(myPage.getSelectedOption()).toBe('Option 1');
        browser.ignoreSynchronization = false;  // reset the flag
});

Ich weiß nicht, ob dies der empfohlene Weg ist oder ob die Tests dadurch sprunghafter werden. Aber für Tests, die keine asynchronen Aufrufe erfordern, aber dennoch unter dem Zeitüberschreitungsproblem leiden, sollten sie behoben werden.

Hallo @vikerman - Irgendein Update dazu? https://github.com/angular/protractor/issues/4290#issuecomment -337094393

@vikerman Gibt es dazu ein Update?

Hat jemand einen guten Rat, wie ich diese langen Laufzeiten finden kann, damit ich sie aus meiner Anwendung entfernen kann?

Von : Schiffbrüchiger(n?) auf der Insel AngularJS gestrandet von Protractor Issue #4290
An : @vikerman @juliemr

Ahoi! Könnten wir ein Update zu diesem Problem bekommen? Das letzte Update am 16. Oktober war eine ETA in der "Reihenfolge von Wochen". Oder schließen Sie dieses Problem, damit wir fortfahren können.

Dies sind die Flöße, die ich derzeit in Betracht ziehe:

  • Ersetzen Sie alle angle-ui-bootstrap / ui-select . Dies ist für mich von Bedeutung. Es muss sowieso getan werden, aber meine Sorge ist, dass etwas anderes auftaucht und ich nach dieser Arbeit immer noch bei AngularJS hängen bleibe. Gelernte Lektion: Umwickeln Sie Komponenten von Drittanbietern mit meinen eigenen Komponenten.
  • waitForAngularEnabled(false) für alle Tests, wie von @vladotesanovic vorgeschlagen. Ich bin skeptisch gegenüber einer ganzen App, aber ich habe sie nicht getestet. Ich benutze waitForAngularEnabled jetzt nur in einer Situation und nur für einen ganz bestimmten Fall.
  • Aktualisieren Sie die gesamte App auf Angular statt auf AngularJS/Angular parallel inkrementelle Migration: Nein, nur keine Option mit meinen Ressourcen.
  • Migrieren Sie zu Vue oder React anstelle von Angular. Ganz oben auf der Liste, insbesondere wenn man bedenkt, dass beide mit AngularJs koexistieren können.
  • Wechseln Sie zu Puppenspieler und lassen Sie alle Winkelmesser-Tests fallen - Dies ist wahrscheinlich eine strategische Richtung.
  • ODER ein Fix von #4290 und ich kann die paar Zeilen auskommentieren, die erforderlich sind, um mit der Verwendung von ngUpgrade zu beginnen :)

Unser Team ist dabei, unsere komplexe Anwendung zu aktualisieren, und Winkelmesser-Tests schlagen beim Bootstrapping mit NgUpgrade fehl. Wir mussten unsere Änderungen rückgängig machen, weil wir jetzt nicht alle fehlgeschlagenen Tests verarbeiten können, die falsch positiv sind.

Wo stehen wir bei diesem Problem? Ich mache mir Sorgen, dass ich zu einem anderen Testframework wechseln muss, damit wir unsere Pläne für ein Upgrade von Angular beibehalten können.

@tonybranfort : In genau demselben Boot wie du... Wir verlassen uns stark auf ui-select und angular-ui-bootstrap - dies ist ein Hauptblocker für unser Upgrade. Anscheinend haben Sie bei diesem Problem die Führung übernommen. Bitte halten Sie uns auf dem Laufenden! 😁

Wir haben das gleiche Problem. Kann uns bitte jemand vom Winkelmesser dazu auf den neuesten Stand bringen?

Wir sind auf halbem Weg beim Upgrade unserer App von Angular V1 und hassen es, diese Ursache zu haben, ist, zu V1 zurückzukehren und die gesamte Arbeit, die bisher für das Upgrade auf Angular V5 geleistet wurde, aufzugeben.

Wir werfen hier eine weitere Stimme ein. Wir sind dabei, unsere App auf Angular zu aktualisieren, und verwenden derzeit eine stückweise Anwendung von waitForAngularEnabled(false) , um diese zu verabschieden, ohne die gesamte Suite einem größeren Risiko auszusetzen. Es ist jedoch außerordentlich unideal, daher wäre eine bessere Lösung sehr zu begrüßen.

Wir haben gerade auch dieses Problem angesprochen, insbesondere wenn es um den $interval-Service geht. Manchmal wartet der Winkelmesser darauf und manchmal nicht - das Muster habe ich noch nicht gefunden.

Langzeitbearbeitung: anscheinend hängt es davon ab, wann das Intervall aufgerufen wird (in der Zone oder nicht)

Hallo @vikerman -

Gleiches Problem hier.

@vikerman gibt es ein Update oder eine Problemumgehung?

Wir stoßen auf die gleichen Probleme. Wäre toll, wenn es diesbezüglich Fortschritte geben würde.

Leider habe ich das gleiche Problem. Sind diesbezüglich Fortschritte in Sicht?

Wir fügen Angular und AngularJS die Aufgabenverfolgung hinzu, die das Debuggen dieser Probleme erleichtern sollte, indem wir Ihnen zeigen, woher die asynchrone Aufgabe stammt, bei der das Zeitlimit von Protractor überschritten wurde.

@heathkit Ist das schon irgendwo gemacht? es fällt uns schwer, diese Stellen in unserer Codebasis zu identifizieren.

Außerdem wäre es schön, eine Dokumentation zu haben, auf die der Winkelmesser genau wartet. Dieser scheint veraltet zu sein: https://www.protractortest.org/#/system -setup. es wird nur erwähnt, $timeout durch $interval zu ersetzen. Nichts über setTimeout oder setInterval.
einige Dokumente sind hier: http://www.protractortest.org/#/timeouts. aber keine hinweise wie man sie erkennt...

Ich denke, dies wird Ihnen helfen, Ihre Tests "Winkelhybrid + Winkelmesser" durchzuführen. Es hat mir geholfen. Überprüfen Sie den Link: https://github.com/ui-router/angular-hybrid/issues/39#issuecomment -309057814

Wir sind ein wenig vorangekommen, um zu verstehen, was mit diesem Problem vor sich geht. Wir scheinen das gleiche zu haben. Es scheint, dass der aktualisierte anglejs Watcher/Digest-Zyklus etwas anders funktioniert und einige unendliche Digest-Zyklusschleifen ermöglichen kann. Wir haben 1 bei einem von uns verwendeten Drittanbieter gefunden und untersuchen den nächsten. Aber im Grunde hat der Watcher eine Änderung an dem beobachteten Ding verursacht, den Watcher ausgelöst usw. Um zu testen, ob Sie das gleiche Problem haben, öffnen Sie in Chrome einfach die Entwicklungstools, gehen Sie zu Leistung, zeichnen Sie ein paar Sekunden auf und Überprüfen Sie, ob Sie häufig NgZone-Anrufe tätigen. Wenn Sie dies tun, haben Sie irgendwo in Ihrem Code einen unendlichen Digest-Zyklus. Wenn Sie sich die Anrufe genauer ansehen, können Sie die Ursache dafür finden

Sie können auch die lokale Datei zone.js ein wenig ändern und Konsolenprotokolle hinzufügen, in denen Timeout-/Intervallaufgaben gepatcht werden, um zu sehen, was sie aufruft. Einfacher Weg, dies zu tun:

try {
  throw new Error("track");
} catch(err) {
  console.debug(err);
}

Bearbeiten: So macht es Spaß, Code auf dem Handy zu formatieren!

@vikerman Hast du dazu ein Update? Es wäre toll, wenn Sie Ihre Meinung dazu sagen könnten, wie schnell dies behoben wird. Danke.

Sie können auch die lokale Datei zone.js ein wenig ändern und Konsolenprotokolle hinzufügen, in denen Timeout-/Intervallaufgaben gepatcht werden, um zu sehen, was sie aufruft. Einfacher Weg, dies zu tun:

try {
  throw new Error("track");
} catch(err) {
  console.debug(err);
}

Bearbeiten: So macht es Spaß, Code auf dem Handy zu formatieren!

Wo würden Sie diesen Code platzieren, um die Timeouts/Intervalle zu verfolgen?

@maurycyg patchTimer -> patchMethod- Funktion sollte den Zweck erfüllen

Dieser Thread ist entmutigend zu sehen. Gibt es dazu ein Update?

Eine mögliche Problemumgehung, um $timeout besteht darin, es mit $interval zu überschreiben (für Bibliotheken von Drittanbietern).
Der folgende Code hat das Zeitüberschreitungsproblem für mich behoben (Browserprüfung für alle Fälle hinzugefügt). In meinem Fall wurde das Problem durch Angular-Bootstrap-Komponenten verursacht, die $timeout mit 0-Verzögerung unendlich aufgerufen haben.

app.config(function($provide) {
    function isUnderProtractor() {
        var nav = window.navigator;
        var lowercasedUserAgent = nav.userAgent;
        return !lowercasedUserAgent || lowercasedUserAgent.contains('protractor');
    }

    if (isUnderE2ETest()) {
        $provide.decorator('$timeout', ['$interval', '$delegate', function($interval, $delegate) {
            var newTimeout = function(fn, delay, invokeApply, pass) {
                if (!delay)
                    return $interval(fn, delay, 1, invokeApply, pass);
                else
                    return $delegate(fn, delay, invokeApply, pass);
            };
            newTimeout.cancel = $delegate.cancel;
            return newTimeout;
        }]);
    }
}

Ab zone.js v0.8.9 können Sie auswählen, welche Web-API-Module Sie patchen möchten, um den durch das Patchen dieser Module verursachten Overhead zu reduzieren.
https://github.com/angular/zone.js/blob/master/MODULE.md

Datei zone-flags.ts mit Inhalt unten hinzufügen
(window as any).__Zone_disable_timers = true;

Importieren Sie es in polyfills.ts vor zone.js

import './zone-flags';
import 'zone.js/dist/zone';  // Included with Angular CLI.

Es schien, dass die obige Lösung mit dem Deaktivieren von Zonen-Timern mit Obserables (z. B. asyncValidators) durcheinander gebracht wird. Die Lösung, die für mich endlich funktioniert hat, ist das Affen-Patching von setTimeout.

platformBrowserDynamic().bootstrapModule(AppModule)
.then((moduleRef: NgModuleRef<AppModule>) => {
  const ngZone: NgZone = moduleRef.injector.get(NgZone);
  const originalSetTimeout = window.setTimeout;
  (window as any).setTimeout = function setTimeout(fn, ms) {
    return ngZone.runOutsideAngular(() => {
      return originalSetTimeout(() => {
        ngZone.run(fn);
      }, ms);
    });
  };
})

Vielleicht liegt das Problem nicht im Winkelmesser selbst, sondern im Anwendungscode.

Mit dem Übergang von Angular JS, das einen Digest-Zyklus verwendet, um Änderungen zu verfolgen, zu Hybrid-App mit Angular2+, die zone.js verwendet, um Änderungen zu erkennen, begann Protractor mit Zeitüberschreitungen zu fallen.

Der Winkelmesser startet die nächste Testaktion nur, wenn die Anwendung stabil ist. Stabilität im Fall einer Hybrid-App bedeutet Stabilität des Angular JS-Teils der Anwendung und des Angular2+-Teils der Anwendung. Beide Teile müssen stabil sein.

Angular JS Stabilität wird durch $$testability.whenStable . Die Stabilität von Angular 2+ wird durch window.getAngularTestability(...).whenStable . Sie können den Protractor-Code debuggen und sich selbst davon überzeugen. Fügen Sie dazu die debugger; Anweisung zur Datei waitForAngular _\node_modules\protractor\built\clientsidescripts.js_ hinzu;
Sie werden feststellen, dass die Stabilität der Angular2+-Zone nie auftritt. Aus Stabilitätsgründen müssen alle Mikrotasks und Makrotasks abgeschlossen werden.

Jetzt verstehen wir das Problem. Angular 2+-Teil der Anwendung ist immer instabil und daher wartet Protractor auf unbestimmte Zeit, bis er stabil wird und mit Timeouts abfällt. Die Winkelstabilität von JS im Fall einer Hybrid-App sollte sich nicht geändert haben und hat keinen Einfluss auf die Zeitüberschreitungen des Winkelmessers. Daraus können wir schließen, dass es notwendig ist, die Instabilität der Angular2+-Zone zu beheben und dann beginnt der Winkelmesser wie erwartet zu arbeiten.

Erster Reparaturversuch:
Da die Zeit knapp war und ich alles auf einmal reparieren wollte, schrieb ich Code, der die Aufgabenplanung aus der Angular2+-Zone herausnahm.

    $provide.decorator("$animateCss", ["$delegate", createDecorator]);
    $provide.decorator("$http", ["$delegate", createDecorator]);
    $provide.decorator("$timeout", ["$delegate", createDecorator]);
    $provide.decorator("$interval", ["$delegate", createDecorator]);
    $provide.decorator("$q", ["$delegate", createDecorator]);
    $provide.decorator("$xhrFactory", ["$delegate", createDecorator]);
    $provide.decorator("$templateRequest", ["$delegate", createDecorator]);
    $provide.decorator("$httpBackend", ["$delegate", createDecorator]);

    $provide.decorator("$animate", ["$delegate", createDecorator]);
    $provide.decorator("$rootScope", ["$delegate", createDecorator]);
    $provide.decorator("$templateCache", ["$delegate", createDecorator]);
    $provide.decorator("$browser", ["$delegate", createDecorator]);

    function runInRootZone(fn, ...args) {
        if (window.Zone.current.parent) {
            return window.Zone.current.parent.run(() => runInRootZone(fn, ...args));
        }

        return fn(...args);
    }

    function decorate(decorator, $delegate) {

        const keys = Object.keys($delegate);
        for (let i = 0; i < keys.length; i++) {

            const key = keys[i];
            if (angular.isFunction($delegate[key])) {
                decorator[key] = createDecoratorForFunctionLikeService($delegate[key]);
            } else {
                if (decorator[key] !== $delegate[key]) {
                    decorator[key] = $delegate[key];
                }
            }
        }
    }

    function createDecoratorForFunctionLikeService($delegate) {

        function decorator(...args) {
            return runInRootZone($delegate, ...args);
        }

        decorate(decorator, $delegate);

        return decorator;
    }

    function createDecoratorForService($delegate) {

        decorate($delegate, $delegate);

        return $delegate;
    }

    function createDecorator($delegate) {
        if (angular.isFunction($delegate)) {
            return createDecoratorForFunctionLikeService($delegate);
        }
        return createDecoratorForService($delegate);
    }

Obwohl sich dieser Ansatz als praktikabel erwiesen hat, hat dieser Ansatz, wie die Zeit gezeigt hat, seine eigenen Nachteile. Der Hauptgrund ist, dass es unmöglich ist, zu bestimmen, welche Zone (Root oder Angular) für einen bestimmten Teil der Hybrid-App sein wird. Für den normalen Betrieb erfordert Angular2+, dass es sich immer in der Angular-Zone befindet, und Dekorateure können bewirken, dass sich die Angular2+-Komponenten in der Root-Zone befinden. ngUpgrade impliziert, dass die Angular-Zone den größten Teil der Anwendung abdeckt.

Um dies zu vermeiden, gab es einen zweiten Fixversuch mit einer tieferen Analyse der Gründe, dem Verlassen der Dekorateure und der Rückkehr zum ursprünglichen Problem.

Jetzt müssen wir verstehen, welcher Code die Zone instabil macht. Dazu hat zone.js spezielle Dienstprogramme, wie _TaskTrackingZoneSpec_ (_ \ node_modules \ zone.js \ dist \ Task-tracking.js_), die die Aufgaben im Bereich verfolgen können und wo sie erstellt wurden ( creationLocation ). Speichern Sie außerdem zu Beginn des Angular2+-Teils der App die Angular-Zone (window as any).a9Zone = (upgradeModule as any).ngZone._inner; und fügen Sie diesen Code irgendwo hinzu

$rootScope.$watch(function () {
    if (window.a9Zone) {
        console.log("Angular2+ task count:", window.a9Zone._zoneDelegate._taskCounts);

        // Optional.
        window.getAllAngularTestabilities().forEach(_ => {
            _.taskTrackingZone.macroTasks.forEach(t => {
                console.log(t, t.creationLocation);
            });
        });
    }
});

Suchen Sie daher in der Anwendung die Stellen, an denen sich die Microtasks und Macrotasks für längere Zeit in der Zone befinden (_microTasks.lenght! == 0 && macroTasks.lenght! == 0_).

Dann müssen Sie diesen Code aus der Angular-Zone holen. Um dies zu tun, packen Sie den Code in ngZone.runOutsideAngular . Gehen Sie jedoch sehr vorsichtig vor und stellen Sie sicher, dass die Root-Zone nicht dort angezeigt wird, wo sie nicht benötigt wird.

Nachdem die Winkelzone stabil geworden ist, fällt der Winkelmesser nicht mehr mit Zeitüberschreitungen .

Hoffe das hilft.

Ich habe mir einen temporären Hack ausgedacht, um dieses Problem zu lösen, indem ich die Funktion waitForAngular mit der folgenden Logik überschreibe.

onPrepare: function() {

            var script = "var callback = arguments[arguments.length - 1];" +
                "angular.element(document.querySelector(\"body\")).injector()"+
                ".get(\"$browser\").notifyWhenNoOutstandingRequests(callback)");

            browser.waitForAngular = function() {
                return browser.executeAsyncScript(script)
            };
}
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen