The following Node.js script attempts to use the Playwright Library for browser automation (not Playwright Test) to scrape some data from a local website and log the output to the terminal. The script throws the error: locator.innerText: Target closed
, which points to the rows
definition.
The script succeeds when the line await context.close();
is commented out, but then the terminal session hangs which is undesirable.
Why is the failure thrown and how to fix it?
import { firefox } from 'playwright';
(async () => {
const browser = await firefox.launch({ headless: true });
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(
'https://example.com'
);
const rows = await page
.locator('.row')
.filter({ hasText: 'some text that some rows have' })
.all();
rows.forEach(async (row) => {
const time = await row.locator('.time').innerText();
const heading = await row.getByText('some common heading').innerText();
console.log(`${time} ${heading}`);
});
// When context is closed via the following uncommented line then the script fails with
// the error `locator.innerText: Target closed`.
// When the following uncommented line is commented out then the script logs the
// expected output, but the terminal session hangs because the browser is still open.
await context.close();
})();
The error happens because the forEach
loop does not actually wait for completion of asynchronous operations inside it and context.close()
is called faster.
You can't use async/await
with forEach
. Instead, try changing it to for...of
:
for (const row of rows) {
const time = await row.locator('.time').innerText();
const heading = await row.getByText('some common heading').innerText();
console.log(`${time} ${heading}`);
}
Also, if you want to close the terminal session, you should call browser.close()
instead of context.close()
.