javascript - Cómo usar Jest/Puppeteer para esperar hasta que un elemento haya sido eliminado del DOM
Resumen del problema: Estoy escribiendo varios conjuntos de pruebas (usando Jest y Puppeteer) para automatizar pruebas de la página de inicio de mi aplicación AngularJS. Una de las pruebas que me gustaría automatizar es que un usuario presione un botón en la página que elimina un elemento en el DOM. Desafortunadamente, este elemento se utiliza para mostrar una gran cantidad de datos, por lo que, para que el elemento sea eliminado, el cliente primero necesita hacer una solicitud POST a mi servidor para eliminar los datos de la base de datos, y solo entonces se puede eliminar el elemento del DOM. En general, todo este proceso tarda aproximadamente uno o dos segundos. Además, este elemento que estoy intentando eliminar fue agregado dinámicamente al DOM, por lo que la única manera de poder acceder al elemento es usando un XPath que identifica el elemento por el texto que contiene, en lugar de un selector CSS tradicional. Ahora, aquí está mi pregunta: ¿cómo puedo emplear las APIs de Jest y Puppeteer para escribir código de prueba que ESPERE a que este elemento deje de existir (es decir, salga del DOM)?
Aquí hay una visión general de cómo se ve mi HTML para que tengas una idea de con qué estoy trabajando:
<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>
Antecedentes: Estoy usando Jest (v24.8.0) como mi framework de pruebas. Estoy usando Puppeteer (v1.19.0) para iniciar y controlar un navegador Chromium sin cabeza.
Lo que he intentado hasta ahora:
Actualmente, tengo este código
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);
});
Podría hacer algo así:
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);
});
... pero quiero poder deshacerme de mi línea await delay y simplemente hacer que la prueba espere precisamente hasta que el elemento desaparezca. El problema con await delay es que no es confiable, ya que dependiendo de cuántos datos esté mostrando el elemento, puede tardar más o menos tiempo en eliminarse de lo que await delay especifica.
Conclusión: ¿Alguno de ustedes, hackers de Jest/Puppeteer, se ha encontrado con un problema como este antes y conoce alguna solución ingeniosa?
Respuesta #1
Puedes usar page.waitForXPath con la opción { hidden: true } o usar page.waitForFunction para esto escribiendo una función que verifique si el elemento no existe.
page.waitForXPath con hidden:true
await page.waitForXPath(elemXPath, { hidden: true });
Alternativa: page.waitForFunction
Alternativamente, puedes usar el siguiente código para usar un selector simple:
await page.waitForFunction(() => !document.querySelector('#selector-of-element'));
Si quieres usar una expresión XPath, puedes usar este código:
const elemXPath = '//div[contains(text(), "My Data")]';
await page.waitForFunction(
xpath => !document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue,
{},
elemXPath
);
Esto pasa tu selector a la función y utiliza la función document.evaluate para ejecutar la expresión XPath dentro del contexto del navegador.
Respuesta #2
O puedes usar:
await page.waitForSelector(selector, { hidden: true });
