Protractor: Warten Sie nicht auf $ timeout () s?

Erstellt am 16. Okt. 2013  ·  59Kommentare  ·  Quelle: angular/protractor

In unserer App zeigen wir unserem Benutzer "Popup" -Nachrichten an, wenn bestimmte Schaltflächen angeklickt werden. Diese Popup-Meldungen sind nur etwa 20 Sekunden lang sichtbar (danach werden sie durch $ timeout entfernt).

Das Problem ist, dass Protractor zu warten scheint, bis das $ timeout abgeschlossen ist, bevor die Dinge .findElement () und .expect () ausgeführt werden. Wenn jedoch das $ timeout abgelaufen ist, ist die Nachricht verschwunden und das Element wurde nicht gefunden.

Irgendwelche Vorschläge, wie man dies umgeht / löst? (Wir müssen behaupten, dass dem Benutzer die richtige "Popup" -Nachricht angezeigt wird)

Vielen Dank

question

Hilfreichster Kommentar

Plant das Angular-Team, dieses Problem erneut zu lösen, da immer mehr Menschen darauf stoßen? Es ist nicht professionell, nur so zu tun, als gäbe es kein Problem, weil Sie $ timeout durch $ interval ersetzen können, weil Code nicht für Tests geschrieben werden sollte - Tests sollten für Testcode geschrieben werden.

Alle 59 Kommentare

Sie können ptor.ignoreSynchronization , dies kann jedoch an anderer Stelle in Ihrem Test zu Schuppenbildung führen. Alternativ gibt es unter Angular 1.2rc3 jetzt einen interval -Dienst, auf den Protractor nicht warten wird (siehe https://github.com/angular/angular.js/commit/2b5ce84fca7b41fca24707e163ec6af84bc12e83). Sie können ein Intervall festlegen, das 1 Mal wiederholt wird.

Hallo @drhumlen , wir hatten das gleiche Problem und wir kommen zu dem Schluss, dass das Popup definitiv etwas ist, das zum Testen von Einheiten verwendet werden sollte. Also haben wir unser Benachrichtigungssystem verspottet und erwarten, dass der Schein aufgerufen wurde (eventuell mit einigen kritischen Informationen). Wir haben die Benachrichtigung auch getestet, um sicherzustellen, dass nichts kaputt geht.
Hoffe das hilft.

$interval hat den Trick für uns gemacht.

  $interval(function() {
    // ...
  }, duration, 1);

Es funktioniert jetzt großartig. Wir müssen jedoch sehr gewissenhaft sein und sicherstellen, dass das Popup nach jedem Test auf andere Weise entfernt wird (in unserem Fall durch Klicken auf die Schaltfläche "x"):

  afterEach(function() {
    removeLogMessagesFromDOM();
  });

@mackwic : Ich nehme an, es ist möglich, es mit einem Unit-Test für viele Apps zu testen, aber in unserem Fall werden die Fehlermeldungen vom Backend generiert, sodass wir sie im e2e testen müssen.

Danke @juliemr

Es scheint seltsam, dass der Winkelmesser auf $timeout s wartet. Meistens müsste der Benutzer dagegen arbeiten?

Auch dieses Problem konfrontiert und es mit $interval gelöst. Mit einem kleinen Dreh können Sie es genau wie $timeout .

Ich wollte die Speichermethode testen und überprüfen, ob nach dem Speichern eine Erfolgsbenachrichtigung angezeigt wird. Diese Benachrichtigung wurde vom $ timeout-Dienst ausgeblendet. Der Winkelmesser wartete auf $ timeout und überprüfte, ob eine Benachrichtigung sichtbar ist, nachdem sie bereits ausgeblendet wurde.

Vor:

$timeout(function() {
    //hide notification
}, 3000);

Nach:

var intervalCanceller = $interval(function() {
    //hide notification
    $interval.cancel(intervalCanceller);
}, 3000);

Wird dies nicht als gültiges Problem angesehen? Ich bin nur selbst darauf gestoßen. Ich sehe nicht wirklich, dass die Verwendung von $interval anstelle von $timeout eine Lösung ist, eher eine Problemumgehung.

Ich kann das auch nicht zum Laufen bringen, selbst wenn ich von $timeout zu $interval wechsle.

Ich habe einen Toast, der nach einer Auszeit verschwindet. Dies ist der Code, mit dem ich ihn teste:

username = element(select.model("user.username"))
password = element(select.model("user.password"))
loginButton = $('button[type=\'submit\']')
toast = $('.toaster')
# Check the toaster isn't displayed initially
expect(toast.isDisplayed()).toBe(false)  # This passes
username.sendKeys("redders")
password.sendKeys("wrongpassword")
loginButton.click()
toastMessage = $('toast-message')
# Check the toaster is now displayed
expect(toast.isDisplayed()).toBe(true)   # This passes
expect(toastMessage.getText()).toBe("Invalid password")  # This fails

Das entsprechende Markup ist hier (Jade)

.toaster(ng-show="messages.length")
  .toast-message(ng-repeat="message in messages") {{message.body}}

Der Fehler ist, dass toastMessage = $('toast-message') keinen Elementen entspricht. Schlägt dies fehl, weil es zu Beginn aufgerufen wird, wenn .toast-message nicht existiert?

Wurde dieses Problem bereits behoben? Denn die Verwendung von Intervallen anstelle von Timeout ist mehr Hack oder Workarounf als Lösung.

Ich habe das gleiche Problem und browser.ignoreSynchronization = true; hilft nicht. Ich kann Code durch $ interval ersetzen, aber e2e-Tests sollten echten Code testen, nicht den Code, der für sie übernommen wurde.

@ vytautas-pranskunas- klingt so, als gäbe es ein anderes Problem für Sie, wenn ignoreSynchronization nicht funktioniert. Wenn Sie ein reproduzierbares Beispiel bereitstellen können, öffnen Sie eine neue Ausgabe.

Ich habe herausgefunden, wann ignoreSynchronization nicht funktioniert:
Ich habe einen Dienst, der für die Anzeige von Fehlermeldungen verantwortlich ist. Die einmal angezeigte Nachricht bleibt fünf Sekunden lang auf dem Bildschirm, nachdem sie ausgeblendet wurde. Dieser Prozess wird durch $ timeout gesteuert.

Ich habe zwei Szenarien - im ersten ignoreSynchronization funktioniert gut, im zweiten - nicht

1) Ich weiß, dass Nachrichten sofort angezeigt werden sollten, nachdem der Benutzer auf die Schaltfläche geklickt hat, und ich mache expec(message).tobe(...) in diesem Fall ignoreSynchronization erledigt den Job.

2) Ich weiß, dass die Nachricht nach einer beliebigen Zeitspanne angezeigt werden kann (abhängig von der Antwortzeit des Servers) und meine weitere Logik sollte erst ausgeführt werden, nachdem sie angezeigt wurde. In diesem Fall muss ich hier browser.wait(by.(....).isPresent(), n) ignoreSynchronization funktioniert nicht mehr und der Ablauf folgt (ich werde die Ausgabe von browser.wait console.log anzeigen, während ich warte)

waits
waits
waits (element appears)
browser waits freezes for 5 seconds
after element disappears it continue
wait
wait
wait

Wie Sie sehen können, ist das Element nie vorhanden, da das Warten bei Kontakt mit $ timeout einfriert

Ich habe wirklich nicht so viel Zeit, um es im Plunker reproduzierbar zu machen, aber wenn Sie es versuchen, wäre es großartig, weil dieses Szenario ziemlich häufig sein sollte.

Thnaks

Ihr Protokoll ist ohne den Kontext des Codes, der es erstellt hat, wirklich nicht sehr hilfreich. Könnten Sie das teilen?

Ich kann meinen Code aus Datenschutzgründen nicht teilen, aber ich kann Pseudocode anzeigen, um eine Vorstellung zu bekommen
(HTML-Code wird nicht geschrieben, da es eine Schaltfläche und ng-repeat zum Anzeigen von Nachrichten gibt.)

constructor(){
$scope.$on('globalMessageAdded', () => {
            $scope.globalMessages = this.getMessages();
        });
}

callServer(){
//interval here acts like server delay
   $interval(()=> {
     $rootScope.messages.push('test messages');
      this.$rootScope.$broadcast('globalMessageAdded');
   }, 3000, 1);
}

getMessages = () => {
        var newMessages = <ISpaGlobalMessage[]>[];
        _.forEach(this.$rootScope.globalMessages, item => {
            newMessages.push(item);
//because of this timeout browser.wait stops waitng for 5 seconds
            this.$timeout(() => {
                this.remove(item);
                this.$rootScope.$broadcast('globalMessageRemoved');
            }, 5000);
        });

        return newMessages;
    }

und Winkelmesserteil

element('button').click();
browser.ignoreSynchronization = true; //tried with and without it
browser.wait(() => element(by.css('.alert-success')).isPresent(), 10000000);
browser.ignoreSynchronization = false;

other code that should be executed after successful message

browser.wait findet dieses Element nie, da browse.wait keine Überprüfungen auslöst, solange das Element vorhanden ist.

Gibt es Fortschritte oder Gedanken dazu?

Warum kann "browser.ignoreSynchronization = false;" nicht unmittelbar nach browser.wait () festgelegt werden?

Mein Code

        var el = element(by.css('.popup-title.ng-binding'));
        browser.ignoreSynchronization = true; 
        browser.wait(function (){
            return el.isPresent();
        }, 10000);
        var popupMsg = el.getText();
        expect(popupMsg).toEqual('something...');
        browser.ignoreSynchronization = false; // not work, if take out this statement, it works again

Von demselben Problem gebissen zu werden. Das von uns verwendete Toastmodul ist eine generische Komponente für npm, daher können wir es nicht einfach ändern, um $ interval zu verwenden. Ich verstehe auch nicht, warum Winkelmesser auf den einen und nicht auf den anderen warten sollten. Im Idealfall kann dies selbst konfiguriert werden, sodass andere Wartezeiten für die Synchronisierung nicht beeinträchtigt werden.

Wir haben dieses Problem unter angle-ui / bootstrap # 3982 und haben beschlossen, es nicht in $ interval zu tauschen, damit wir hier nachsehen, ob etwas getan werden kann, um das Problem zu beheben.

Vielen Dank.

Ich habe dieses Problem diese Woche mit einer Toast-Benachrichtigungskomponente durchlaufen.

Die Verwendung von $interval anstelle von $timeout ist keine plausible Lösung. Und browser.ignoreSynchronization = true funktioniert auch bei mir nicht.

Irgendwelche Gedanken oder Ideen?

Plant das Angular-Team, dieses Problem erneut zu lösen, da immer mehr Menschen darauf stoßen? Es ist nicht professionell, nur so zu tun, als gäbe es kein Problem, weil Sie $ timeout durch $ interval ersetzen können, weil Code nicht für Tests geschrieben werden sollte - Tests sollten für Testcode geschrieben werden.

+1 Wir brauchen auch eine Lösung.

Einverstanden. Test sollte behoben werden. +1

+1, ist auch auf dieses Problem gestoßen. Vielleicht sollte es eine Möglichkeit geben, zu sagen, dass auf Zeitüberschreitungen bei Winkelmessern nicht gewartet werden sollte.

ignoreSynchronisation funktioniert bei mir nicht, ich denke, es ignoriert zu viele Dinge. Wir brauchen eine Möglichkeit, die Synchronisation von Zeitüberschreitungen zu ignorieren oder eine komplexere API wie browser.waitForTimeouts(); .

+1. brauche eine Lösung. Warum sollten wir hässliche Problemumgehungen mit Intervallen verwenden?

+1

+1, es ist sehr schwierig, mit schuppigen Tests umzugehen. Ich brauche eine Lösung dafür

+1 stimme @ vytautas-pranskunas- zu. Wir sollten nicht verpflichtet sein, den zu testenden Code zu ändern, nur damit der Test selbst funktioniert.

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

Ich denke, viele Leute verwenden browser.ignoreSynchronization = true möglicherweise falsch. Bitte korrigieren Sie mich, wenn ich falsch liege, aber für mich, wenn Sie schreiben:

browser.ignoreSynchronization = true;
expect( elem.getText() ).toEqual( "foo" );
browser.ignoreSynchronization = false;

Dann wird es nicht funktionieren: Dies ist immer noch reines JavaScript und die drei Zeilen werden synchron gelesen. Wenn also die versprochenen getText und expect gelöst sind, ist die Synchronisation bereits wieder aktiviert. Sie könnten das umgehen, indem Sie darauf warten, dass der Winkelmesser das letzte Versprechen löst und die Synchronisation wieder auf den Rückruf von then zurückstellt.

Eine Möglichkeit für das Winkelmesser-Team, Menschen zu helfen, besteht möglicherweise darin, Zugriff auf Methoden zu gewähren, die sich nur dann auf die Synchronisation auswirken, wenn der Versprechen-Puffer fertig ist. Zum Beispiel:

browser.setIgnoreSynchronization( true );
expect( elem.getText() ).toEqual( "foo" );
browser.setIgnoreSynchronization( false );

Wie auch immer, ich habe auch viele Probleme gehabt, weil Protractor auf $ timeouts gewartet hat: In großen Anwendungen gibt es zu viele Nebenwirkungen, seien es einige Tooltips, Modalitäten, Animationen usw. Wir haben beschlossen, die Synchronisierung mit browser.ignoreSynchronization = true; global zu deaktivieren

+1 mit $ interval ist ein Hack, keine Lösung

+1 Dieser Fehler hat mir so viel Elend verursacht. Bitte starten wir einen Dialog, um dies zu beheben. Ich habe mehrere Dutzend Bower- und Npm-Winkelmodule, die an Hunderten (!) Stellen $ timeout verwenden, und ich verwende es 45 Mal in meinem Code. Das Ändern meines Codes ist machbar (und vernünftig), aber das Ändern des Codes von Drittanbietern kommt logistisch völlig außer Frage: Die Kontaktaufnahme und der Versuch, Upstream-Anbieter zu motivieren oder Winkelmodule von Drittanbietern lokal zu forken und einen parallelen Patch-Stream aufrechtzuerhalten, ist ein Alptraumszenario ist nicht akzeptabel, wenn der Fehler bei Protractor und $ timeout liegt, nicht außerhalb von Modulen.

Bitte lassen Sie uns gemeinsam daran arbeiten.

+1

Dieser Fehler führt zu Problemen beim e2e-Test für das folgende Beispielszenario.

1) Füllen Sie die Felder aus und senden Sie ein Formular, das mehrere Ereignisse wie z
* Link zur Statusmeldung, der mit $ timeout angehängt ist und nur wenige Sekunden lang sichtbar ist
* Eine neue Nachricht wird der Nachrichtenliste in einem anderen Widget hinzugefügt
2) Wenn Sie als Nächstes auf den Link zur Statusmeldung klicken, sollte die Nachricht als Popup mit Details geöffnet werden

Wenn wir also versuchen, das Szenario zu automatisieren, können wir das Formular senden. Wenn wir jedoch versuchen, auf den Link zur Statusmeldung zu klicken, muss browser.ignoreSynchronization = true sein. weil der Link an $ timeout angehängt ist.

Der Link ist anklickbar, aber eine Nachricht, die als Popup geöffnet wird, funktioniert nicht.

Als ich Stackoverflow und einige Google-Antworten eincheckte, erfuhr ich, dass es am $ timeout liegt. Also habe ich überprüft, indem ich $ timeout für element entfernt habe, jetzt funktioniert es richtig.

Meine Anfrage ist also jedes Mal, wenn wir kein Element ohne $ timeout haben können. Betrachten Sie also bitte das obige Szenario und haben Sie eine neue Funktion, bei der Winkelmesser das Warten auf $ timeout ignorieren können.

Angesichts des gleichen Problems mussten window.setTimeout und window.clearTimeout verwendet werden, um dies zu vermeiden. Nicht sehr eckig und kann den Digest beeinträchtigen, aber das Intervall $ und das Ignorieren der Synchronisation sind keine Lösung

Wird jemand aus dem Winkelmesserteam auf genau das gleiche Problem reagieren?

+1

Das hat mich verrückt gemacht (Winkelmesser hier), bis ich zu einer schmutzigen Lösung wie folgt kam:

beforeEach(function () { browser.ignoreSynchronization = true; });
afterEach(function () { browser.restart(); });

Funktioniert gut und ist in einem Headless-Browser nicht zu langsam.

+1

Auch hier stößt man auf ein Problem. Meine Tests verliefen reibungslos, bis ich eine Seite besuchte, auf der einige API-Anfragen gestellt und einige WebSockets geöffnet wurden. Zu diesem Zeitpunkt läuft der Winkelmesser jedes Mal ab. Ich habe keine gute Lösung gefunden.

Ich bin @floribon neugierig auf Ihre vorgeschlagenen browser.ignoreSynchronization = true; global deaktivieren . Im Moment verwende ich Seitenobjekte, die normalerweise so aussehen:

describe('something', function() {
  it('should do....', function() {
    var fooPage = new FooPage();
    fooPage.visit();
    var barPage = fooPage.clickCreate();  // does some API call, then redirects to barPage
    // at this point, enough things are happening that it is impossible to interact with barPage.
    // Protractor locks up.
    // barPage makes other API call & opens WebSocket
    barPage.clickBaz();  // nope.  will timeout every time.
  });
});

@benjaminapetersen meine

btn1.click();         // Triggers many things and eventually displays page2
wait.forURL('page2'); // wait for page2 to be loaded before moving on
$('.btn1').click();   // this does some async job and eventually displays btn2
wait.forElement('.btn2');
$('.btn2').click();

Mit zum Beispiel wait.forElement Rückkehr

browser.wait(() => elem.isPresent().then(p => p, () => {}), timeout, 'Not found');
// browser.wait(function() { return elem.isPresent().then(function(p) { return p;} , function() {}); }, timeout, 'Not found');

Die Idee ist, das tatsächliche Benutzerverhalten nachzuahmen: Ich klicke auf ein Element, warte, bis ein anderes sichtbar ist, dann klicke ich darauf usw. Auf diese Weise müssen Sie nicht auf $ timeout warten, es ist schneller und sicherer.

Der Schlüssel hier ist, Fehler aus dem inneren Versprechen zu ignorieren und es noch ein paar Mal zu versuchen, bis das Timeout erreicht ist.

PS: Sie können auch ExpectedConditions dafür verwenden, aber ich hatte einige Probleme damit, wenn Angular den DOM-Knoten neu erstellen würde (zum Beispiel ng-if Bedingungen):

browser.wait(browser.ExpectedConditions.precenseOf(elem), timeout, 'Not found');

@floribon super , danke!

@ Juliemr Kannst du mir bitte helfen, die Toast-Fehlermeldungen in Winkelmesser zu fangen [email protected]

@juliemr Die Verwendung des $ interval-Dienstes zum Aufrufen lang laufender http-Anforderungen funktioniert nicht. Winkelmesser bekommen immer noch Timeout.
Hier ist mein Winkelcode
this. $ interval (() => this. $ http.get(this.prefix (url), config), 0, 1)
Winkelmesser warten immer noch auf den Abschluss der $ http-Aufgabe.
Ich benutze Winkelmesser 5.x und Angualrjs 1.6x.
Kannst du mir helfen?

+1, etwas sollte hinzugefügt werden, um dies zu handhaben.

+1

+1

+1

+1

+1

+1

Diese Spezifikation hat bei mir funktioniert. Vielen Dank! und Prost auf

    // screenshot-spec.js

    // a close button that appears on the md-toast template
    const closeToastButton = $('[data-automation="toast-close"]')
    const cond = protractor.ExpectedConditions
    function waitToastShow() {
        return browser.wait(cond.elementToBeClickable(closeToastButton), 5000)
    }
    function waitToastHide() {
        return browser.wait(cond.invisibilityOf(closeToastButton), 5000)
    }

    screenshot = name => browser.takeScreenshot().then(/* save fn */)

    describe('a suite ... ', () => {
        it('takes screenshots of an md-toast once shown and after hidden', function () {
            // ... actions that launch an md-toast using $mdToast.show({ ... })
            browser.ignoreSynchronization = true
            waitToastShow().then(() => {
                screenshot('toast-showing.png')
                waitToastHide().then(() => {
                    screenshot('toast-hidden.png')
                    browser.ignoreSynchronization = false;
                })
            })
        });
    }

Jungs, wie ich Timeout für einen bestimmten Testfall hinzufügen kann
`
it ('1-sollte sich mit einem an eine E-Mail gesendeten Registrierungscode anmelden', Funktion (erledigt) {

// setTimeout(function () {
            flow.execute(browser.params.getLastEmail)
                .then(function (email) {
                    expect(email.subject)
                        .toEqual('[email protected] submitted feedback');
                    expect(email.headers.to)
                        .toEqual('[email protected]');
                    expect(email.html.includes('User feedback details: accountId: 12345, related To: dashboard, description: ' + D.feedbackMsg + ''))
                        .toEqual(true);
                    console.log(email.html);
                    // done();
                });


    });`
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen