DrCord
DrCord

Reputation: 3955

Puppeteer - How to interact with new window or force new window into same instance

I am using Puppeteer for testing in non-headless mode with Chromium. I have a part of a test where it clicks on a node on a webpage and that opens a new window. I am unable to interact with that new window. I have tried browser.pages(), unfortunately it only yields interaction with the pre-existing tabs in the original Chromium instance, not the new window.

example:

let pages = await browser.pages();
let newWindow = pages[pages.length - 1];
// try to find element on newWindow - fails
// last element in pages is still window that click was made in to open new window

I have found event: 'targetcreated' in the Puppeteer API but have been unable to figure out how to use it to access a new window.

I have also tried using browser.targets() to get the new instance but have not been successful.

I would like to know either how to interact with a new window instance or how to force all new windows to open in the same instance so they can be interacted with via browser.pages().

I don't believe this is a duplicate of the linked question about a similar subject of detecting when a new tab is opened as I am not just trying to detect when a new tab is opened but also to access the new tab/window within the test that opened the new tab/window.

Upvotes: 2

Views: 15294

Answers (2)

Adam Leppek
Adam Leppek

Reputation: 1

I know this is an old question, but I think there is another way to solve this: As soon as the new window or tab is opened, a new "target" is created in the browser context, but it doesn't show up immediately. So you have to wait for it, using browser.waitForTarget() (https://pptr.dev/api/puppeteer.browser.waitfortarget). You can find the specific page you want using the callback function you provide.

After you find the target, you can run target.page() (https://pptr.dev/api/puppeteer.target.page) to convert it into a Page that you can use.

Upvotes: 0

DrCord
DrCord

Reputation: 3955

I was able to solve this with an event listener for targetCreated, which multiple users were able to help me setup. The additional part that wasn't immediately obvious that I had to figure out myself was how to use that event listener to actually access the new page in the test that created it. I did so using lodash with a global.pages variable that the event listener adds any new pages to, see code below:

package.json

{
"name": "workflow-tests",
  "version": "1.0.0",
  "description": "Workflow tests",
  "main": "index.js",
  "scripts": {
    "test": "mocha test/bootstrap.js --recursive test"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "chai": "^4.1.0",
    "mocha": "^3.4.2"
  },
  "dependencies": {
    "puppeteer": "^1.0.0"
  }
}

test/bootstrap.js:

const puppeteer = require('puppeteer');
const { expect } = require('chai');
const _ = require('lodash');
const globalVariables = _.pick(global, ['browser', 'expect', 'pages']);

// puppeteer options
const opts = {
    headless: false,
    slowMo: 100,
    timeout: 10000
};

// expose variables
before (async function () {
    global.expect = expect;
    global.browser = await puppeteer.launch(opts);
    global.pages = await global.browser.pages();
    // console.log('global.pages.length', global.pages.length);

    // Event listener for taargetCreated events (new pages/popups)
    // adds the new page to the global.pages variable so it can be accessed immediately in the test that created it
    global.browser.on('targetcreated', async () => {
        // console.log('New Tab Created');
        global.pages = await global.browser.pages();
        // console.log('global.pages.length', global.pages.length);
    });
});

// close browser and reset global variables
after (function () {
    browser.close();
    global.browser = globalVariables.browser;
    global.expect = globalVariables.expect;
    global.pages = globalVariables.pages;
});

test/workflow1.js - pseudo code/example test that can access popup that is created:

describe('Workflow tests', function () {
    let page = null;
    this.timeout(60000);

    it('should access new window after clicking opens it', () => {
        return new Promise(async (resolve, reject) => {
            page = global.pages[0];
            await page.setViewport({ width: 1500, height: 1000 });
            await page.goto('https://system.netsuite.com/pages/customerlogin.jsp');

            // click something that opens a new window or use this test/example new window opener
            window.open('http://www.example.com', '_blank');

            // targetCreated event listener in test.bootstrap.js activated and sets global.pages to all open windows in instance
            // new page/popup is last item in global.pages array
            let popup = global.pages[global.pages.length - 1];

            // do things in the new window - #addr2 is an example selector from my instance
            await popup.waitForSelector('#addr2');
            await popup.click('#addr2');
            await popup.keyboard.type("popup test typing! :D");
            await popup.keyboard.press('Enter');
            resolve();
        })
    })
});

These tests would run with the command npm test. As these tests use async they require a certain version of node, I believe it is any node >= 8.9.1. I tried to run these tests on an earlier version of node and they did not work.

Upvotes: 2

Related Questions