https://github.com/GoogleChrome/puppeteer/issues/1361 asked how to determine if a frame is loaded.
@aslushnikov posted a suggestion, but it's unclear if that addresses "frame is loaded".
We should clarify in the docs whether frameattached
or framenavigated
are guaranteed to happen after window.onload
. e.g. where do those events fall in relation to the frames load
event and when using pptr's waitUntil
timings?
@ebidel, @aslushnikov - I would like to take this one, but I'm not sure what is the right way to determine if a frame is loaded...
For me, framenavigated
worked (I got frame.content()
inside this event), but we might want to wait until we have data, in order to prevent flakiness...
One more thing - as a test writer, it is hard to work with event emitter. It would be really nice to wrap this one with a promise. For example, instead of writing:
page.on('framenavigated',frame => do something...)
It would be really nice to have:
const frame = await page.frameNavigated()
WDYT? (I will gladly add a PR)
And, last thing, not exactly the same, but related:
https://github.com/ChromeDevTools/devtools-protocol/issues/42
This one is about switching to iframes. I feel that iFrame solution is not good enough here. In Selenium/Protractor we could just switch to an iFrame context and continue working inside the iFrame seamlessly. Here it would be much more complicated, I guess.
@ebidel, @aslushnikov - I would like to take this one, but I'm not sure what is the right way to determine if a frame is loaded...
@yanivefraim There's no single answer: it's always specific to what exactly webpage is doing and how it's behaving.
Since it's quite an open-ended question, there's not much to put in the documentation.
One more thing - as a test writer, it is hard to work with event emitter. It would be really nice to wrap this one with a promise. For example, instead of writing:
page.on('framenavigated',frame => do something...)It would be really nice to have:
const frame = await page.frameNavigated()
This is easily achievable with a small helper function:
function once(emitter, event) {
return new Promise(resolve => emitter.once(event, resolve));
}
const frame = await once(page, 'framenavigated');
This one is about switching to iframes. I feel that iFrame solution is not good enough here. In Selenium/Protractor we could just switch to an iFrame context and continue working inside the iFrame seamlessly. Here it would be much more complicated, I guess.
You can emulate the "switchFrame" semantics with the frames api that we have in puppeteer; just pass the desired frame anywhere as an argument to the functions that do actions. This worked successfully for us so far:
async function doWork(frame) {
const handle = await frame.waitForSelector('.foo');
// do some other work
}
let currentFrame = page.mainFrame();
await doWork(currentFrame);
currentFrame = frame.childFrames()[0]; // switch to some other frame
await doWork(currentFrame);
currentFrame = frame.parentFrame(); // switch back to parent
Hope this helps.
@yanivefraim,
I found this works nicely:
function waitForIFrameLoad(page, iframeSelector, timeout = 10000) {
// if pageFunction returns a promise, $eval will wait for its resolution
return this.page.$eval(
iFrameSelector,
(el, timeout) => {
const p = new Promise((resolve, reject) => {
el.onload = () => {
resolve()
}
setTimeout(() => {
reject(new Error("Waiting for iframe load has timed out"))
}, timeout)
})
return p
},
timeout,
)
}
const iFrameLoaded = waitForIFrameLoad(page, '#myframe')
doSomethingToTriggerIFrameLoad()
await iFrameLoaded
Most helpful comment
@yanivefraim There's no single answer: it's always specific to what exactly webpage is doing and how it's behaving.
Since it's quite an open-ended question, there's not much to put in the documentation.
This is easily achievable with a small helper function:
You can emulate the "switchFrame" semantics with the frames api that we have in puppeteer; just pass the desired frame anywhere as an argument to the functions that do actions. This worked successfully for us so far:
Hope this helps.