I need to access an iframe in playwright that has a name that is automatically generated.
The iframe's name is always prefixed by "__privateStripeFrame" and then a randomly generated number
How i can access the frame with the page.frame({name: }) ?
From the docs it seems like i can't use a regular expression!
The frameSelector doesn't need be specified by the name.
Try an xpath with contains - this works on the W3 sample page:
await page.goto('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe');
await page.frame("//iframe[contains(@title,'W3s')]");
If you want a more general approach - you also have page.frames().
That will return an array of the frames and you can iterate through and find the one you need.
This works for me:
let myFrames = page.frames();
console.log("#frames: " + myFrames.length)
myFrames.map((f) => console.log(f.name()));
(W3S is not the best demo site as there are lots of nested frames - but this outputs the top level frames that have names)
The output:
iframeResult
__tcfapiLocator
__uspapiLocator
__tcfapiLocator
__uspapiLocator
We had the issue of multiple Stripe Elements iframes loading asynchronously and very slowly, so we wound up with this workaround to retry iterating all frames and querying for the card input fields for each, until found or timed out.
Not elegant, but it worked for us.
async function findStripeElementsIframeAsync(page: Page, timeout: number) {
const startTime = Date.now();
let stripeFrame = null;
while (!stripeFrame && Date.now() - startTime < timeout) {
const stripeIframes = await page.locator('iframe[name^=__privateStripeFrame]');
const stripeIframeCount = await stripeIframes.count();
for (let i = 0; i < stripeIframeCount; i++) {
const stripeIFrameElement = await stripeIframes.nth(i).elementHandle();
if (!stripeIFrameElement)
throw 'No Stripe iframe element handle.';
const cf = await stripeIFrameElement.contentFrame();
if (!cf)
throw 'No Stripe iframe content frame.';
// Does this iframe have a CVC input? If so, it's our guy.
// 1 ms timeout did not work, because the selector requires some time to find the element.
try {
await cf.waitForSelector('input[name=cvc]', { timeout: 200 });
stripeFrame = cf;
console.log('Found Stripe iframe with CVC input');
return stripeFrame;
} catch {
// Expected for iframes without this input.
}
}
// Give some time for iframes to load before retrying.
await new Promise(resolve => setTimeout(resolve, 200));
}
return null;
}