javascript - Ako použiť Jest/Puppeteer na čakanie, kým bude element odstránený z DOM
Zhrnutie problému: Píšem niekoľko testovacích zostáv (používajúc Jest a Puppeteer) na automatizáciu testov domovskej stránky mojej aplikácie AngularJS. Jeden z testov, ktorý by som chcel automatizovať, simuluje stlačenie tlačidla používateľom na stránke, ktoré maza prvok v DOM. Bohužiaľ, tento prvok sa používa na zobrazovanie veľkého množstva údajov, takže na to, aby mohol byť prvok vymazaný, klient najprv musí poslať POST požiadavku na môj server, aby vymazal údaje z databázy, a až potom môže byť prvok vymazaný z DOM. Celkovo celý tento proces trvá približne jednu alebo dve sekundy. Navyše tento prvok, ktorý sa snažím vymazať, bol do DOM pridaný dynamicky, takže jediný spôsob, ako k nemu mám prístup, je použiť XPath, ktorý identifikuje prvok podľa textu, ktorý obsahuje, namiesto tradičného CSS selektora. Tu je moja otázka: ako môžem použiť API Jest a Puppeteer na napísanie testovacieho kódu, ktorý ČAKÁ na to, kým tento prvok prestane existovať (t. j. opustí DOM).
Tu je prehľad toho, ako vyzerá môj HTML, aby ste mali predstavu o tom, s čím pracujem:
<html>
<body ng-app="myApp" ng-controller="myCtrl">
<!-- Dynamically added div -->
<div>My Data
<table><!-- Displays tons of data --></table>
</div>
<form>
<button type="submit">Delete</button>
</form>
</body>
</html>
Pozadie: Používam Jest (v24.8.0) ako môj testovací rámec. Používam Puppeteer (v1.19.0) na spúšťanie a ovládanie bezhlavého prehliadača Chromium.
Čo som zatiaľ vyskúšal:
Momentálne mám tento kód
test('deleted elem no longer exists', async() => {
elemXPath = '//div[contains(text(), "My Data")]';
// this is a function to pause the
// execution of the test for a given amount of milliseconds
// in order to wait for elem to be removed
await delay(2000);
// This fails because Puppeteer timeouts after 3000
// ms b/c elemXPath no longer exists
const elemExists = await page.waitForXPath(elemXPath, {timeout: 3000}) ? true : false;
expect(elemExists).toBe(false);
});
Mohol by som urobiť niečo takéto:
test('deleted elem no longer exists', async() => {
elemXPath = '//div[contains(text(), "My Data")]';
// wait for elem to be removed
await delay(2000);
try {
var elemExists = await page.waitForXPath(elemXPath, {timeout: 3000}) ? true : false;
} catch(err) {
var elemExists = false
}
expect(elemExists).toBe(false);
});
... ale chcem byť schopný zbaviť sa riadku await delay a nechať test čakať presne až do chvíle, kým prvok nezmizne. Problém s await delay je, že je nespoľahlivý, pretože v závislosti od toho, koľko dát prvok zobrazuje, môže jeho vymazanie trvať viac alebo menej času, než čo špecifikuje await delay.
Záver: Narazil niektorý z vás, hackerov používajúcich Jest/Puppeteer, na podobný problém a pozná nejaké šikovné riešenia?
Odpoveď č. 1
Môžete použiť page.waitForXPath s možnosťou { hidden: true } alebo použiť page.waitForFunction tak, že napíšete funkciu, ktorá otestuje, či prvok neexistuje.
page.waitForXPath s hidden:true
await page.waitForXPath(elemXPath, { hidden: true });
Alternatíva: page.waitForFunction
Alternatívne môžete použiť nasledujúci kód na použitie jednoduchého selektora:
await page.waitForFunction(() => !document.querySelector('#selector-of-element'));
Ak chcete použiť výraz XPath, môžete použiť tento kód:
const elemXPath = '//div[contains(text(), "My Data")]';
await page.waitForFunction(
xpath => !document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue,
{},
elemXPath
);
Toto odovzdá váš selektor funkcii a použije funkciu document.evaluate na spustenie XPath výrazu v kontexte prehliadača.
Odpoveď č. 2
Alebo môžete použiť:
await page.waitForSelector(selector, { hidden: true });
