Tengo un simple trozo de código.
describe('My First Puppeeteer Test', () => { it('Should launch the browser', async function() { const browser = await puppeteer.launch({ headless: false}) const page = await browser.newPage() await page.goto('https://github.com/login') await page.type('#login_field', testLogin) await page.type('#password', testPassword) await page.click('[name="commit"]') await page.waitForNavigation() let [element] = await page.$x('//h3[@class="text-normal"]') let helloText = await page.evaluate(element => element.textContent, element); console.log(helloText); browser.close(); }) })Todo funcionó antes, pero hoy recibo un error + mi stacktrace:
Error: la evaluación falló: TypeError: no se pueden leer las propiedades de undefined (leyendo 'textContent') en titiritero_evaluación_script : 1:21 en ExecutionContext._evaluateInternal (node_modules\puppeteer\lib\cjs\puppeteer\common\ExecutionContext.js:221:19) en processTicksAndRejections (node:internal/process/task_queues:96:5) en async ExecutionContext.evaluate (node_modules\puppeteer\lib\cjs\puppeteer\common\ExecutionContext.js:110:16) en async Context. (pruebas\ejemplo.pruebas.js:16:22)
¿Cómo puedo resolver esto?
Atentamente
Si bien no he probado el código debido al inicio de sesión y asumo que sus selectores son correctos, el problema principal es casi seguro que
await page.click('[name="commit"]') await page.waitForNavigation()crea una condición de carrera. Los documentos aclaran:
Tenga en cuenta que si
click()desencadena un evento de navegación y hay una promesapage.waitForNavigation()separada para ser resuelta, puede terminar con una condición de carrera que produce resultados inesperados. El patrón correcto para hacer clic y esperar la navegación es el siguiente:const [response] = await Promise.all([ page.waitForNavigation(waitOptions), page.click(selector, clickOptions), ]);
Como punto adicional, probablemente sea mejor hacer waitForXPath en lugar de $x , aunque esto parece menos probable que sea la raíz del problema. No olvide await todas las promesas como browser.close() .
const puppeteer = require("puppeteer"); let browser; (async () => { browser = await puppeteer.launch({headless: true}); const [page] = await browser.pages(); await page.goto('https://github.com/login'); await page.type('#login_field', testLogin); await page.type('#password', testPassword); // vvvvvvvvvvv await Promise.all([ page.click('[name="commit"]'), page.waitForNavigation(), ]); const el = await page.waitForXPath('//h3[@class="text-normal"]'); // ^^^^^^^^^^^^ //const el = await page.waitForSelector("h3.text-normal"); // ..or const text = await el.evaluate(el => el.textContent); console.log(text); //await browser.close(); //^^^^^ missing await, or use finally as below })() .catch(err => console.error(err)) .finally(() => browser?.close()) ; Además, si está utilizando Jest, una vez que todo funcione, es posible que desee mover el browser y la administración de la page a los beforeEach / afterEach o beforeAll / afterAll . Es más rápido usar la misma instancia de navegador para todos los casos de prueba, y las páginas se pueden abrir y cerrar antes o después de cada caso.