javascript - Bagaimana untuk menggunakan Jest/Puppeteer untuk menunggu sehingga satu elemen telah dikeluarkan dari DOM
Ringkasan masalah: Saya sedang menulis beberapa suite ujian (menggunakan Jest dan Puppeteer) untuk mengautomasikan ujian halaman utama aplikasi AngularJS saya. Salah satu ujian yang ingin saya automasikan ialah pengguna menekan butang pada halaman yang memadam elemen dalam DOM. Malangnya, elemen ini digunakan untuk memaparkan sebilangan besar data, jadi untuk elemen itu dipadamkan, klien terlebih dahulu perlu membuat permintaan POST ke pelayan saya untuk memadam data dari pangkalan data, dan hanya selepas itu elemen boleh dipadamkan daripada DOM. Secara keseluruhan, keseluruhan proses ini mengambil masa kira-kira satu atau dua saat. Tambah pula, elemen yang saya cuba padam ini ditambahkan secara dinamik ke dalam DOM, jadi satu-satunya cara saya boleh mengakses elemen tersebut adalah dengan menggunakan XPath yang mengenal pasti elemen itu berdasarkan teks yang terkandung, bukan menggunakan pemilih CSS tradisional. Sekarang, ini soalan saya: bagaimana saya boleh menggunakan Api Jest dan Puppeteer untuk menulis beberapa kod ujian yang MENUNGGU elemen ini tidak lagi wujud (iaitu, meninggalkan DOM).
Berikut adalah gambaran keseluruhan tentang rupa HTML saya supaya anda mempunyai idea tentang apa yang saya sedang kerjakan:
<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>
Latar Belakang: Saya menggunakan Jest (v24.8.0) sebagai rangka kerja ujian saya. Saya menggunakan Puppeteer (v1.19.0) untuk memulakan dan mengawal pelayar Chromium tanpa kepala.
Apa yang telah saya cuba setakat ini:
Pada masa ini, saya mempunyai kod ini
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);
});
Saya boleh melakukan sesuatu seperti ini:
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);
});
... tetapi saya mahu dapat menghapuskan baris await delay saya dan hanya membiarkan ujian menunggu dengan tepat sehingga elemen itu hilang. Masalah dengan await delay ialah ia tidak boleh dipercayai kerana bergantung pada berapa banyak data yang dipaparkan oleh elemen itu, ia mungkin mengambil masa lebih atau kurang untuk dipadamkan daripada yang ditentukan oleh await delay.
Kesimpulan: Adakah mana-mana daripada kamu penggodam Jest/Puppeteer pernah menghadapi masalah seperti ini sebelum ini dan tahu sebarang penyelesaian yang pintar?
Jawapan #1
Anda boleh menggunakan page.waitForXPath dengan pilihan { hidden: true } atau menggunakan page.waitForFunction untuk ini dengan menulis fungsi yang menguji jika elemen itu tidak wujud.
page.waitForXPath dengan hidden:true
await page.waitForXPath(elemXPath, { hidden: true });
Alternatif: page.waitForFunction
Sebagai alternatif, anda boleh menggunakan kod berikut untuk menggunakan pemilih mudah:
await page.waitForFunction(() => !document.querySelector('#selector-of-element'));
Jika anda ingin menggunakan ungkapan XPath, anda boleh menggunakan kod ini:
const elemXPath = '//div[contains(text(), "My Data")]';
await page.waitForFunction(
xpath => !document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue,
{},
elemXPath
);
Ini menghantar pemilih anda kepada fungsi dan menggunakan fungsi document.evaluate untuk menjalankan ungkapan XPath di dalam konteks pelayar.
Jawapan #2
Atau anda boleh menggunakan:
await page.waitForSelector(selector, { hidden: true });
