剧作家 Select 帧动态名称

Playwright Select frame with dynamic name

我需要在 playwright 中访问一个具有自动生成名称的 iframe。

iframe 的名称总是以“__privateStripeFrame”为前缀,然后是一个随机生成的数字

如何使用 page.frame({name: }) 访问框架?

从文档看来我不能使用正则表达式!

frameSelector不需要指定名称。 尝试使用 containsxpath - 这适用于 W3 示例页面:

    await page.goto('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe');
    await page.frame("//iframe[contains(@title,'W3s')]");

如果您想要更通用的方法 - 您还有 page.frames()。 这将 return 一组帧,您可以遍历并找到您需要的帧。

这对我有用:

    let myFrames = page.frames();
    console.log("#frames: " + myFrames.length)
    myFrames.map((f) => console.log(f.name()));

(W3S 不是最好的演示站点,因为有很多嵌套框架 - 但它会输出具有名称的顶级框架)

输出:

iframeResult

__tcfapiLocator

__uspapiLocator

__tcfapiLocator

__uspapiLocator

我们遇到了多个 Stripe Elements iframe 异步加载且加载速度非常慢的问题,因此我们最终采用此解决方法来重试迭代所有框架并为每个框架查询卡片输入字段,直到找到或超时。

不优雅,但对我们有用。

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;
}