Multi-tab flows

Both Puppeteer and Playwright enable us to control multiple browser tabs, albeit in different ways.

Opening tabs directly

If we are looking to open brand new tabs with which to interact, the setup is rather straightforward for both Puppeteer and Playwright.


const { chromium } = require('playwright');
(async () => {
  const browser = await chromium.launch()
  const context = await browser.newContext()

  const pageOne = await context.newPage()
  const pageTwo = await context.newPage()

  await pageOne.goto('https://www.checklyhq.com/')
  await pageTwo.goto('https://playwright.dev/')

  await pageOne.screenshot({ path: 'screenshot-tab-one.png' })
  await pageTwo.screenshot({ path: 'screenshot-tab-two.png' })

  await browser.close()
})()


Run in Checkly

const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch()

  const pageOne = await browser.newPage()
  const pageTwo = await browser.newPage()

  await pageOne.goto('https://www.checklyhq.com/')
  await pageTwo.goto('https://playwright.dev/')

  await pageOne.screenshot({ path: 'screenshot-tab-one.png' })
  await pageTwo.screenshot({ path: 'screenshot-tab-two.png' })

  await browser.close()
})()


Run in Checkly

Controlling tabs that are opened after a click on an element on the page can be trickier. Let’s explore this through an example: navigating to https://checklyhq.com, then opening a new tab by clicking the link to the GitHub-based Checkly Public Roadmap which is found on the page.

By allowing us to wait for the creation of a child tab with page.waitForEvent, Playwright enables us to “catch” it following a click on an element with target="_blank", and then seamlessly interact with any of the currently open tabs.

With Puppeteer we need to follow a different procedure, using page.waitForTarget to grab the new tab once it has been opened.


const { chromium } = require('playwright');
(async () => {
  const browser = await chromium.launch()
  const context = await browser.newContext()
  const page = await context.newPage()

  await page.goto('https://www.checklyhq.com/')

  const [newPage] = await Promise.all([
    context.waitForEvent('page'),
    await page.click('text=Public roadmap')
  ])

  await page.screenshot({ path: 'screenshot-tab-old.png' })

  await newPage.click('#pull-requests-tab')
  await newPage.screenshot({ path: 'screenshot-tab-new.png' })

  await browser.close()
})()


Run in Checkly

const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()

  await page.setViewport({ width: 1440, height: 800 })

  await page.goto('https://www.checklyhq.com/')

  const pageTarget = page.target()

  await page.waitForSelector('#menu_5_drop > li:nth-child(3) > a')
  await page.click('#menu_5_drop > li:nth-child(3) > a')

  await page.screenshot({ path: 'screenshot-tab-old.png' })

  const newTarget = await browser.waitForTarget(target => target.opener() === pageTarget)

  const newPage = await newTarget.page()

  await newPage.waitForSelector('#pull-requests-tab')
  await newPage.click('#pull-requests-tab')
  await newPage.screenshot({ path: 'screenshot-tab-new.png' })

  await browser.close()
})()


Run in Checkly

Note that, if running Puppeteer in headful mode, you will have to manually bring focus to the new tab, either by bringing it to the front or closing the old one:


const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch({ headless: false })
  const page = await browser.newPage()

  await page.setViewport({ width: 1440, height: 800 })

  await page.goto('https://www.checklyhq.com/')

  const pageTarget = page.target()

  await page.waitForSelector('#menu_5_drop > li:nth-child(3) > a')
  await page.click('#menu_5_drop > li:nth-child(3) > a')

  await page.screenshot({ path: 'screenshot-tab-old.png' })

  const newTarget = await browser.waitForTarget(target => target.opener() === pageTarget)

  const newPage = await newTarget.page()
  await newPage.bringToFront()

  await newPage.waitForSelector('#pull-requests-tab')
  await newPage.click('#pull-requests-tab')
  await newPage.screenshot({ path: 'screenshot-tab-new.png' })

  await browser.close()
})()


Run in Checkly

Further reading

  1. Official documentation on Playwright’s multi-tab scenarios