webentwicklung-frage-antwort-db.com.de

Wie man ein Element dann und dann ein anderes im Winkelmesser ausführt

Dieser folgende Code verhält sich zufällig, manchmal funktioniert er einwandfrei und manchmal gibt er einen Fehler wie diesen aus. Stale Element Reference Exception

was ich will, ist, dass ich dies zuerst ausführen lassen möchte

element(by.id('FiltItemTransDocNo')).sendKeys(grno);

nach oben möchte ich, dass dies unten ausgeführt wird

element.all(by.name('chkGrd')).first().click();

ich habe es auf diese Weise versucht, aber es scheint nicht zu funktionieren

element(by.id('FiltItemTransDocNo')).sendKeys(grno).then(function(el){
     element.all(by.name('chkGrd')).first().click();
});

hilf mir dabei

ich habe das Bild angehängt, das ich dem Feld "Purchase Rquisition" sende. Entsprechend habe ich ein Ergebnis erstellt, das nur ein Ergebnis anzeigt und auf das ich klicken möchte. Wenn ich die Bedingung für die Sichtbarkeit setze, ist es immer truename__, dann führe ich zu demselben Problem  enter image description here

7
Rao

Ein kurzer Hinweis zu sendKeys und e1

Ein kurzer Hinweis, dass sendKeys kein WebElement oder ElementFinder zurückgibt. Dies bedeutet, dass e1 im obigen Beispiel wahrscheinlich undefiniert ist.

Veraltete Elemente und das DOM ändern sich

Kurze Anmerkung zu den Annahmen: Bei der Antwort wird davon ausgegangen, dass das Senden von Text an einen Filter die Zeilenanzahl oder die Anzahl der Elemente auf dem Bildschirm ändert. Wenn nach dem Senden des Textes die gleiche Anzahl von Elementen auf dem Bildschirm angezeigt wird, funktioniert dies nicht. Ich würde Florents Kommentar unten auf den veralteten Referenzfehler ansehen.

Veraltete Elemente treten normalerweise auf, wenn sich das DOM geändert hat. Wenn Sie eine strukturelle Anweisung in Angular verwenden, ändert sich das DOM, wenn Sie * ngFor oder * ngIf verwenden. Meine Vermutung ist, dass, nachdem Sie das Element gefiltert haben, die Elemente im DOM während oder vor dem eigentlichen Ändern des DOM basierend auf Ihrem Filter abgerufen werden. Dies würde zu einem veralteten referenzierten Webelement führen. In meinem folgenden Beispiel verwende ich async/waitit bei deaktiviertem Kontrollfluss.

Explizites Warten während des Filters

Sie können explizit einen Ruhezustand festlegen, damit das DOM aktualisiert wird, bevor Sie einen Aufruf zum Anklicken des ersten Elements tätigen. Dies kann möglicherweise zu flockigen Tests führen, da das Zeitlimit aufgrund der Umgebung, in der Sie ausgeführt werden, unbekannt ist.

it('should do something', async () => {
  const filterItem = element(by.id('FiltItemTransDocNo'));
  await filterItem.sendKeys(grno);
  await browser.sleep(1000);  // or some other waits
  await element.all(by.name('chkGrd')).first().click();
});

Zeilen für element.all vergleichen

Alternativ können Sie eine Vergleichsprüfung für die Anzahl der element.all-Elemente durchführen, die Sie vor und nach dem Klick haben, und erst dann weitergehen, wenn Dinge aktualisiert werden.

it('should do something', async () => {
  const filterItem = element(by.id('FiltItemTransDocNo'));
  const table = element.all(by.name('chkGrd'));
  const length = await table.count();

  await filterItem.sendKeys(grno);

  // After the filter keys are sent, check to see if the current table
  // count is not equal to the `table.count()`.
  let updated = false;
  await browser.wait(async () => {
    updated = length !== await table.count();
    return updated;
  }, 5000);

  // So if we use the entire 5 seconds and the count has not changed,
  // we should probably fail before clicking on stuff.
  expect(updated).toBeTruthy();

  // now we can click on the next element.
  await element.all(by.name('chkGrd')).first().click();
});

Warum funktioniert der Aufruf von length !== await table.count()? Dies liegt daran, dass die Tabelle ein Versprechen darstellt, das Web-Element zu erhalten. Wenn Sie die count-Methode aufrufen, führt sie die Aktion aus, indem Sie zuerst das Webelement auflösen. Dies kann möglicherweise unterschiedlich sein, je nachdem, ob sich das DOM ändert. Wir vergleichen dann die aktuelle Anzahl mit der vorherigen.

Stellen Sie sicher, dass Sie async/await verwenden

In Ihrer Konfigurationsdatei müssen Sie angeben, dass Sie sich nicht im Kontrollfluss befinden:

exports.config = {
    // In this case, I plan to use a Selenium standalone server
    // at http://127.0.0.1:4444/wd/hub. You could also use other 
    // methods like direct connect or starting it up with 'local'.
    seleniumAddress: 'http://127.0.0.1:4444/wd/hub',

    // Required flag to tell Protractor you do not want to use the
    // control flow. This means that you will have to either chain
    // your promises or async / await your actions. Historically
    // jasminewd node module would resolve promises for you. This
    // package will no longer be used in future releases since the
    // control flow is deprecated.
    Selenium_PROMISE_MANAGER: false,

    // The rest of your config...
}

Hoffentlich hilft das.

6
cnishina

es gibt mehrere Lösungen für die Referenz von veralteten Elementen.

Zuerst:

    let filterItem = element(by.id('FiltItemTransDocNo'));
    browser.wait(ExpectedConditions.visibilityOf(filterItem), 5000, 'Element is not visible.');
    filterItem.sendKeys('some random text');

    let elementToClick = element.all(by.name('chkGrd')).first();
    browser.wait(ExpectedConditions.elementToBeClickable(elementToClick), 5000, 'Element is not clickable.');
    elementToClick.click();

Sie können sie auch verketten:

browser.wait(ExpectedConditions.visibilityOf(filterItem), 5000, 'Element is not visible.').then( () => {
                filterItem.sendKeys('some random text');

                browser.wait(ExpectedConditions.elementToBeClickable(elementToClick), 5000, 'Element is not clickable.').then( () => {
                    elementToClick.click();
                });
            });

Oder die zweite Möglichkeit, das Element bei einem veralteten Fehler zu aktualisieren:

let filterItem = element(by.id('FiltItemTransDocNo'));

            try {
                filterItem.sendKeys('some random text');
            } catch (e) {
                if (e instanceof StaleElementReferenceError) {
                    filterItem = element(by.id('FiltItemTransDocNo'));
                    filterItem.sendKeys('text');
                }
            }
2
Infern0

Wie ich bereits in einer antwort auf eine andere Frage erläutert habe , ändern sich Protractor und die von ihm verwendeten Tools, um native JavaScript-Versprechen zu verwenden, mit der Erwartung, dass jeder seinen Code zu einem vollständig asynchronen Codierstil migrieren wird. Leider ist dies nicht einfach, da Sie den Code für den alten Stil nicht mit dem neuen Stil kombinieren können. Zuerst sollten Sie also sicherstellen, dass die gesamte Suite asynchron geschrieben ist und Sie Selenium_PROMISE_MANAGER: false in Ihrer Konfiguration festgelegt haben.

Was passiert als Antwort auf die sendKeys? Wenn dies einen AJAX - Aufruf auslöst und die Antwort auf diesen Aufruf das DOM ändert, müssen Sie wahrscheinlich tun, was @cnishina vorschlägt, und das DOM abfragen, um zu warten, dass die Änderung landet. Wenn jedoch nur Angular clientseitige Änderungen auftreten, sollte dieser Code funktionieren:

it('should do something', async () => {
  await element(by.id('FiltItemTransDocNo')).sendKeys(grno);
  await element.all(by.name('chkGrd')).first().click();
});

Beide element-Aufrufe synchronisieren sich mit Angular, um sicherzustellen, dass die Aktualisierungen von Angular abgeschlossen sind, bevor die Locators fortfahren. Sie sollten also in Bezug auf Angular OK sein. 

2
Old Pro