Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

page.waitForNavigation never resolves #3338

Closed
thetutlage opened this issue Oct 3, 2018 · 10 comments

Comments

@thetutlage
Copy link

@thetutlage thetutlage commented Oct 3, 2018

Steps to reproduce

Tell us about your environment:

  • Puppeteer version: 1.8.0
  • Platform / OS version: MacOSX High Sierra
  • URLs (if applicable):
  • Node.js version: 10.0.0

What steps will reproduce the problem?

const http = require('http')
const puppeteer = require('puppeteer')
const PORT = 3000

/**
 * Dummy HTTP Server
 */
http.createServer((req, res) => {
  if (req.url === '/there') {
  	console.log('Responded')
    res.writeHead(200, { 'content-type': 'text/plain' })
    res.end('reached there')
  } else {
    res.writeHead(200, { 'content-type': 'text/html' })
    res.end(`
      <a href="/there"> Redirect </a>
    `)
  }
}).listen(PORT)

/**
 * Puppeteer script
 */
async function run () {
	const browser = await puppeteer.launch()
	const page = await browser.newPage()
	await page.goto('http://localhost:3000')
	await page.click('a')
	await page.waitForNavigation()

	const text = await page.evaluate(() => document.body.innerText)
	console.log(text)
}

run().then(console.log).catch(console.log)

What is the expected result?
To see the text on console

What happens instead?
page.waitForNavigation is stuck and then times out after 30 seconds

@ntzm

This comment has been minimized.

Copy link
Contributor

@ntzm ntzm commented Oct 3, 2018

You're experiencing a race condition. Try using the example code instead:

const navigationPromise = page.waitForNavigation();
await page.click('a.my-link'); // Clicking the link will indirectly cause a navigation
await navigationPromise; // The navigationPromise resolves after navigation has finished
@vsemozhetbyt

This comment has been minimized.

Copy link
Contributor

@vsemozhetbyt vsemozhetbyt commented Oct 3, 2018

Or you can use this way:

await Promise.all([page.click('a'), page.waitForNavigation()]);
@thetutlage

This comment has been minimized.

Copy link
Author

@thetutlage thetutlage commented Oct 3, 2018

Thanks, and that does work

Is there any better way to avoid this race condition, without wrapping all calls with redirection inside Promise.all?

Also I believe, the race condition will be true for other waitFor functions as-well? For example: page.waitForSelector

@aslushnikov

This comment has been minimized.

Copy link
Collaborator

@aslushnikov aslushnikov commented Oct 4, 2018

Also I believe, the race condition will be true for other waitFor functions as-well? For example: page.waitForSelector

The waitForSelector is about page's state rather then some page events, so there's no race condition there.

The reason waitForNavigation is racy is because there's no direct connection between page.click and the navigation it causes.

Is there any better way to avoid this race condition, without wrapping all calls with redirection inside Promise.all?

This race condition is inherent to the scenario; the Promise.all approach is just a nice trick to handle this kind of race conditions. You can avoid using Promise.all and waitForNavigation using waitForSelector.

Hope this helps.

@aslushnikov aslushnikov closed this Oct 4, 2018
@techsin

This comment has been minimized.

Copy link

@techsin techsin commented Apr 26, 2019

how is there a race condition. would love some explanation.

@techsin

This comment has been minimized.

Copy link

@techsin techsin commented Apr 26, 2019

@sheerun

This comment has been minimized.

Copy link
Contributor

@sheerun sheerun commented Apr 26, 2019

What to do if it's unknown whether click will result in navigation? If I call waitForNavigation and there's no navigation event then test hangs infinitely. Ideally any started network requests would be detected and if there are none waitForNavigation would return immediately

@techsin

This comment has been minimized.

Copy link

@techsin techsin commented Apr 26, 2019

not saying this is ideal solution but you could wrap await in try and catch and if your code is in catch that's running meaning it timed out so you can take steps from there.

@nhhockeyplayer

This comment has been minimized.

Copy link

@nhhockeyplayer nhhockeyplayer commented Jun 11, 2019

I cannot get this to work

            await Promise.all([thePage.click(searchButton), thePage.waitForNavigation()])
            // await thePage.waitForNavigation({ waitUntil: 'load' })
            // thePage.click(searchButton)
            // await navigationPromise

going to try waitForSelector

@techsin

This comment has been minimized.

Copy link

@techsin techsin commented Jun 11, 2019

do this

let wait = thePage.waitForNavigation();
await thePage.click(searchButton);
await wait;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants
You can’t perform that action at this time.