I’m having trouble with a custom function that uses XPath to find and interact with elements. The same selector works perfectly when I use it directly in my test, but when I move it to a separate function, I get a “Target closed” error.
// Error message I'm getting
Error: Protocol error (Runtime.callFunctionOn): Target closed.
Here’s my helper function file:
// utils.js
module.exports = {};
module.exports.ClickEditButton = async function ClickEditButton(itemText, containerXpath) {
await page.waitForXPath(containerXpath + "/descendant::span[text()='" + itemText + "']/ancestor::div/descendant::button[@data-action='modify']");
const modifyBtn = await page.$x(containerXpath + "/descendant::span[text()='" + itemText + "']/ancestor::div/descendant::button[@data-action='modify']");
await modifyBtn[0].click();
};
And here’s how I’m calling it:
// test.js
const helpers = require('./utils');
test('Check item modification', async() => {
helpers.ClickEditButton("sample item", "//div[@class='content-panel']/div/div/div/div/div")
}, 30000);
When I use a direct approach like this, it works fine:
const helpers = require('./utils');
test('Check item modification', async() => {
await page.$$eval('button[data-action="modify"]', buttons => buttons[0].click());
}, 30000);
Is there something about function scope or async handling that I’m missing here?
hey, i think ur on the right track but make sure to pass the page
obj in your ClickEditButton fn. the error might be due to it not being available in your utils scope. change it to ClickEditButton(page, itemText, containerXpath)
when calling it.
I ran into something very similar last month and spent way too much time debugging it. The root cause is definitely the missing page reference as others mentioned, but there’s another gotcha that might bite you later. Your function is using await
inside but you’re not awaiting the function call in your test - add await
before helpers.ClickEditButton()
. Also, that XPath selector looks pretty fragile with all those nested divs. I’d suggest adding some error handling in your helper function to catch cases where the element isn’t found, maybe wrap the click in a try-catch block. The direct approach works because page is globally available in that test context, but once you move to a separate module that reference gets lost.
The issue stems from your function trying to access the page
object which isn’t in scope within your utils module. You’re essentially calling methods on an undefined variable. I encountered this exact problem when I started modularizing my Puppeteer tests.
You need to modify your function signature to accept the page instance as a parameter. Update your utils.js to receive the page object and then pass it when calling the function from your test file. So your function call should look like helpers.ClickEditButton(page, "sample item", "//div[@class='content-panel']/div/div/div/div/div")
. The “Target closed” error is misleading here - it’s actually throwing because page is undefined in that context, not because the browser target is actually closed.