Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puppeteer: How to handle multiple tabs?

Scenario: Web form for developer app registration with two part workflow.

Page 1: Fill out developer app details and click on button to create Application ID, which opens, in a new tab...

Page 2: The App ID page. I need to copy the App ID from this page, then close the tab and go back to Page 1 and fill in the App ID (saved from Page 2), then submit the form.

I understand basic usage - how to open Page 1 and click the button which opens Page 2 - but how do I get a handle on Page 2 when it opens in a new tab?

Example:

const puppeteer = require('puppeteer');

(async() => {
    const browser = await puppeteer.launch({headless: false, executablePath: '/Applications/Google Chrome.app'});
    const page = await browser.newPage();

    // go to the new bot registration page
    await page.goto('https://register.example.com/new', {waitUntil: 'networkidle'});

    // fill in the form info
    const form = await page.$('new-app-form');

    await page.focus('#input-appName');
    await page.type('App name here');

    await page.focus('#input-appDescription');
    await page.type('short description of app here');

    await page.click('.get-appId'); //opens new tab with Page 2

    // handle Page 2
    // get appID from Page 2
    // close Page 2

    // go back to Page 1
    await page.focus('#input-appId');
    await page.type(appIdSavedFromPage2);

    // submit the form
    await form.evaluate(form => form.submit());

    browser.close();
})();

Update 2017-10-25

  • The work for Browser.pages has been completed and merged
  • Fixes Emit new Page objects when new tabs created #386 and Request: browser.currentPage() or similar way to access Pages #443.

Still looking for a good usage example.

like image 339
nwxdev Avatar asked Aug 21 '17 23:08

nwxdev


People also ask

How do I open multiple tabs in a puppeteer?

open function to always open "new tabs" on your current page and navigate via history. Navigate to your second page by clicking the button and perform some actions there: await page. click('#button_that_opens_page_2') await page.

How do I get a puppeteer to open in a new tab?

You open a new tab in puppeteer using the newPage() method present in the browser object. const page = await browser. newPage(); Complete code for opening the new tab in the browser.

How much RAM does puppeteer need?

Memory requirements Actors using Puppeteer: at least 1GB of memory. Large and complex sites like Google Maps: at least 4GB for optimal speed and concurrency.

What is headless mode in puppeteer?

Advertisements. By default, Puppeteer executes the test in headless Chromium. This means if we are running a test using Puppeteer, then we won't be able to view the execution in the browser. To enable execution in the headed mode, we have to add the parameter: headless:false in the code.


4 Answers

A new patch has been committed two days ago and now you can use browser.pages() to access all Pages in current browser. Works fine, tried myself yesterday :)

Edit:

An example how to get a JSON value of a new page opened as 'target: _blank' link.

const page = await browser.newPage(); await page.goto(url, {waitUntil: 'load'});  // click on a 'target:_blank' link await page.click(someATag);  // get all the currently open pages as an array let pages = await browser.pages();  // get the last element of the array (third in my case) and do some  // hucus-pocus to get it as JSON... const aHandle = await pages[3].evaluateHandle(() => document.body);  const resultHandle = await pages[3].evaluateHandle(body =>    body.innerHTML, aHandle);  // get the JSON value of the page. let jsonValue = await resultHandle.jsonValue();  // ...do something with JSON 
like image 87
kaiak Avatar answered Sep 16 '22 11:09

kaiak


This will work for you in the latest alpha branch:

const newPagePromise = new Promise(x => browser.once('targetcreated', target => x(target.page()))); await page.click('my-link'); // handle Page 2: you can access new page DOM through newPage object const newPage = await newPagePromise; await newPage.waitForSelector('#appid'); const appidHandle = await page.$('#appid'); const appID = await page.evaluate(element=> element.innerHTML, appidHandle ); newPage.close() [...] //back to page 1 interactions 

Be sure to use the last puppeteer version (from Github master branch) by setting package.json dependency to

"dependencies": {     "puppeteer": "git://github.com/GoogleChrome/puppeteer" }, 

Source: JoelEinbinder @ https://github.com/GoogleChrome/puppeteer/issues/386#issuecomment-343059315

like image 31
tchab Avatar answered Sep 19 '22 11:09

tchab


According to the Official Documentation:

browser.pages()

  • returns: <Promise<Array<Page>>> Promise which resolves to an array of all open pages. Non visible pages, such as "background_page", will not be listed here. You can find them using target.page().

An array of all pages inside the Browser. In case of multiple browser contexts, the method will return an array with all the pages in all browser contexts.

Example Usage:

let pages = await browser.pages();
await pages[0].evaluate(() => { /* ... */ });
await pages[1].evaluate(() => { /* ... */ });
await pages[2].evaluate(() => { /* ... */ });
like image 41
Grant Miller Avatar answered Sep 18 '22 11:09

Grant Miller


In theory, you could override the window.open function to always open "new tabs" on your current page and navigate via history.

Your workflow would then be:

  1. Override the window.open function:

    await page.evaluateOnNewDocument(() => {
      window.open = (url) => {
        top.location = url
      }
    })
    
  2. Go to your first page and perform some actions:

    await page.goto(PAGE1_URL)
    // ... do stuff on page 1
    
  3. Navigate to your second page by clicking the button and perform some actions there:

    await page.click('#button_that_opens_page_2')
    await page.waitForNavigation()
    // ... do stuff on page 2, extract any info required on page 1
    // e.g. const handle = await page.evaluate(() => { ... })
    
  4. Return to your first page:

    await page.goBack()
    // or: await page.goto(PAGE1_URL)
    // ... do stuff on page 1, injecting info saved from page 2
    

This approach, obviously, has its drawbacks, but I find it simplifies multi-tab navigation drastically, which is especially useful if you're running parallel jobs on multiple tabs already. Unfortunately, current API doesn't make it an easy task.

like image 33
krukid Avatar answered Sep 16 '22 11:09

krukid