2

I need to wait until the new page is loaded, which was opened in a new tab after clicking on the button. That is, I click on a button, a new page opens (which should load) and on it I click another button. I have some example code, but it doesn't work for some reason:

const page = await browser.newPage(); 
await page.goto('https://twitter.com/amazon/'); 
await page.click('.css-1dbjc4n:nth-child(1) > .css-1dbjc4n > .css-1dbjc4n > .css-901oao > .css-4rbku5',{waitUntil: ['load', 'domcontentloaded', 'networkidle0', 'networkidle2']}); 
const page2 = (await browser.pages())[2]; 
await page2.click('#nav-main > .nav-fill > #nav-xshop-container > #nav-xshop > .nav-a:nth-child(2)');
ggorlen
  • 44,755
  • 7
  • 76
  • 106
Alomen
  • 31
  • 2
  • Please describe "it doesn't work" in more detail. What doesn't work? Maybe try [this answer](https://stackoverflow.com/a/54781467/6243352) as well as [this one](https://stackoverflow.com/questions/52211871/how-to-get-puppeteer-waitfornavigation-working-after-click). – ggorlen Feb 06 '21 at 15:37
  • @ggorlen No, a new page opens there in the same tab. And I have a new page in a new tab. – Alomen Feb 06 '21 at 15:42

1 Answers1

0

If I understand correctly, the problem is detecting when a new tab ("page") has opened and getting the new page object associated with the tab.

There are at least a couple techniques available. One method is promisifying the browser's "targetcreated" event as described here:

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch({headless: false});
  const [page] = await browser.pages(); 
  await page.goto("https://twitter.com/amazon");
  const amzSel = `.css-1dbjc4n:nth-child(1) > .css-1dbjc4n > 
                  .css-1dbjc4n > .css-901oao > .css-4rbku5`;
  await page.waitForSelector(amzSel, {visible: true});
  console.log((await browser.pages()).length); // => 1

  // method 1
  const newPagePromise = new Promise(resolve => 
    browser.once("targetcreated", target => resolve(target.page()))
  );
  await page.click(amzSel);
  const newPage = await newPagePromise;
  // --------

  console.log((await browser.pages()).length); // => 2
  await newPage.waitForSelector("#nav-link-prime", {visible: true});
  await newPage.click("#nav-link-prime");
  const sel = "#prime-header-CTA-announce";
  await newPage.waitForSelector(sel, {visible: true});
  console.log(await newPage.$eval(sel, el => el.innerText.trim())); // => TRY PRIME
  //await browser.close();
})();

Another approach is to use browser.waitForTarget to check when the target's opener() is the previous page target, as described here:

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch({headless: false});
  const [page] = await browser.pages(); 
  await page.goto("https://twitter.com/amazon");
  const amzSel = `.css-1dbjc4n:nth-child(1) > .css-1dbjc4n > 
                  .css-1dbjc4n > .css-901oao > .css-4rbku5`;
  await page.waitForSelector(amzSel, {visible: true});
  console.log((await browser.pages()).length); // => 1

  // method 2
  const pageTarget = page.target();
  await page.click(amzSel);
  const newTarget = await browser.waitForTarget(target =>
    target.opener() === pageTarget
  );
  const newPage = await newTarget.page();
  // --------

  console.log((await browser.pages()).length); // => 2
  await newPage.waitForSelector("#nav-link-prime", {visible: true});
  await newPage.click("#nav-link-prime");
  const sel = "#prime-header-CTA-announce";
  await newPage.waitForSelector(sel, {visible: true});
  console.log(await newPage.$eval(sel, el => el.innerText.trim())); // => TRY PRIME
  //await browser.close();
})();

As an aside, I'm not sure how important/significant this particular Twitter/Amazon example is, but #nav-xshop > .nav-a:nth-child(2) doesn't seem like a reliable selector (it appears to have a race condition between "Best Sellers" and "Prime")--I'd use #nav-link-prime since it's a direct id, if that's what you're looking for.

ggorlen
  • 44,755
  • 7
  • 76
  • 106