Reputation: 19039
I'm looking for a simpler way to handle clicking on links which open new pages (like target="_blank" anchor tags).
Here handle means:
Tell us about your environment:
I've looked at related issues:
https://github.com/GoogleChrome/puppeteer/issues/386 https://github.com/GoogleChrome/puppeteer/issues/3535 https://github.com/GoogleChrome/puppeteer/issues/978 and more
What steps will reproduce the problem?
I've included the code snippet below
I'm trying to:
Get the object for the new page when clicking on a link opens a new tab. (The links are dynamically generated, capturing href might not be the most elegant way)
Wait till the new page loads (with timeout). I'd like it if you can use page.waitForNavigation for consistency
close the tab and return the earlier tab to continue further operations
Please include code that reproduces the issue.
// as referenced here on #386 : https://github.com/GoogleChrome/puppeteer/issues/386#issuecomment-425109457
const getNewPageWhenLoaded = async () => {
return new Promise(x =>
global.browser.on('targetcreated', async target => {
if (target.type() === 'page') {
const newPage = await target.page();
const newPagePromise = new Promise(y =>
newPage.once('domcontentloaded', () => y(newPage))
);
const isPageLoaded = await newPage.evaluate(
() => document.readyState
);
return isPageLoaded.match('complete|interactive')
? x(newPage)
: x(newPagePromise);
}
})
);
};
const newPagePromise = getNewPageWhenLoaded();
await page.click('my-link'); // or just do await page.evaluate(() => window.open('https://www.example.com/'));
const newPage = await newPagePromise;
What is the expected result?
An easier and consistent way to handle new tabs
What happens instead?
The developer has to write what looks like plumbing (internal/ low level) commands.
Usage of waitForTarget might simplify this, but I've not been able to get the predicate to return the right types. Here's my non-functional code
private async getNewPageWhenLoaded() {
const newTarget = await this._browser.waitForTarget(async (target) => {
const newPage = await target.page();
await newPage.waitForNavigation(this._optionsNavigation);
// const newPagePromise = new Promise(() => newPage.once('load', () => x(newPage)));
return await newPage.evaluate("true");
});
return await newTarget.page();
}
// elsewhere in the code
const newPagePromise = this.getNewPageWhenLoaded();
await resultItem.element.click();
const newPage = <Page>await newPagePromise;
//I get the following error
DevTools listening on ws://127.0.0.1:31984/devtools/browser/bf86648d-d52d-42d8-a392-629bf96211d4
(node:5564) UnhandledPromiseRejectionWarning: Error: Navigation failed because browser has disconnected!
at CDPSession.LifecycleWatcher._eventListeners.helper.addEventListener (<path-to-my-project>\node_modules\puppeteer\lib\FrameManager.js:1181:107)
at CDPSession.emit (events.js:182:13)
at CDPSession._onClosed (<path-to-my-project>\node_modules\puppeteer\lib\Connection.js:231:10)
at Connection._onMessage (<path-to-my-project>\node_modules\puppeteer\lib\Connection.js:103:19)
at WebSocketTransport._ws.addEventListener.event (<path-to-my-project>\node_modules\puppeteer\lib\WebSocketTransport.js:41:24)
at WebSocket.onMessage (<path-to-my-project>\node_modules\ws\lib\event-target.js:120:16)
at WebSocket.emit (events.js:182:13)
at Receiver.receiverOnMessage (<path-to-my-project>\node_modules\ws\lib\websocket.js:741:20)
at Receiver.emit (events.js:182:13)
at Receiver.dataMessage (<path-to-my-project>\node_modules\ws\lib\receiver.js:417:14)
-- ASYNC --
at Frame.<anonymous> (<path-to-my-project>\node_modules\puppeteer\lib\helper.js:144:27)
at Page.waitForNavigation (<path-to-my-project>\node_modules\puppeteer\lib\Page.js:644:49)
at Page.<anonymous> (<path-to-my-project>\node_modules\puppeteer\lib\helper.js:145:23)
at newTarget._browser.waitForTarget (<path-to-my-project>\pageObjects\MyPage.js:104:27)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:5564) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:5564) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:5564) UnhandledPromiseRejectionWarning: TimeoutError: Navigation Timeout Exceeded: 300000ms exceeded
at Promise.then (<path-to-my-project>\node_modules\puppeteer\lib\FrameManager.js:1276:21)
-- ASYNC --
at Frame.<anonymous> (<path-to-my-project>\node_modules\puppeteer\lib\helper.js:144:27)
at Page.waitForNavigation (<path-to-my-project>\node_modules\puppeteer\lib\Page.js:644:49)
at Page.<anonymous> (<path-to-my-project>\node_modules\puppeteer\lib\helper.js:145:23)
at newTarget._browser.waitForTarget (<path-to-my-project>\pageObjects\MyPage.js:104:27)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:5564) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
Note: issue I've created on github: https://github.com/GoogleChrome/puppeteer/issues/3718
Upvotes: 6
Views: 6127
Reputation: 157
first run the click function first of all and remove "global" inside the promise and declaring browser as constant outside the promise
const browser = await puppeteer.launch();
await page.click('my-link');
const getNewPageWhenLoaded = async () => {
return new Promise(x =>
browser.on('targetcreated', async target => {
if (target.type() === 'page') {
const newPage = await target.page();
const newPagePromise = new Promise(y =>
newPage.once('domcontentloaded', () => y(newPage))
);
const isPageLoaded = await newPage.evaluate(
() => document.readyState
);
return isPageLoaded.match('complete|interactive')
? x(newPage)
: x(newPagePromise);
}
})
);
};
const newPagePromise = getNewPageWhenLoaded();
const newPage = await newPagePromise;
Upvotes: 6