Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

feat: support element screenshots of elements in a frame in WebDriver BiDi sessions (v9 `switchFrame()`, v10 `context.frame()`)

Đang mở
#1,228 0 bình luận 0 reaction 1 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@plum117 đang làm issue này rồi.

Từ ngày 9/10/2026.

Đánh giá

Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức phù hợp với người mới
50/100
Loại issue
Tính năng
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
typescript
Lĩnh vực
testing-qa

Hướng nghiên cứu

Start with packages/image-comparison-core/src/methods/takeElementScreenshots.ts and screenshots.ts, then inspect images.ts for the ignore-region path. Add unit coverage for nested frames, borders, padding, and scroll, and remove the skip from tests/specs/v10.browsingContexts.spec.ts. Done means the v9 and v10 frame cases produce the same screenshot with 0% mismatch.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Is your feature request related to a problem? Please describe.

In a WebDriver BiDi session (the default for Chrome, Edge and Firefox since WebdriverIO v9), checkElement() / toMatchElementSnapshot() of an element inside an iframe does not work:

  • WebdriverIO v9, frame reached with browser.switchFrame(): the image is wrong. It is moved by the position of the iframe in the page. A baseline made in the same way is also wrong, so the test can pass with a wrong image.
  • WebdriverIO v10: switchFrame() is removed for BiDi sessions, and its replacement context.frame() fails: WebDriver Bidi command "browsingContext.captureScreenshot" failed with error: invalid argument - Invalid input in "clip".

With WebDriver Classic ('wdio:enforceWebDriverClassic': true, Appium, Safari), it works in v9 and v10.

Test: the same box outside a frame (baseline) and inside an iframe at margin: 60px 0 0 30px. Expected: 0% mismatch. Local headless Chrome 154, @wdio/visual-service 10.1.0 (with the WebdriverIO v10 changes of #1227).

How the frame is reached v9.32.0 BiDi v10.0.0-alpha.163 BiDi v9 Classic v10 Classic
browser.switchFrame($('iframe')) 69.587% (image moved by 30/60 px) error: switchFrame removed for BiDi 0% 0%
(await browser.url(url)).frame('iframe') not in v9 error: Invalid input in "clip" not in v9 error: needs BiDi

Reproduction (fixtures tests/fixtures/v10/frame.html and box.html from #1222, on the wdio0-v10-compliance branch):

it('compares the same box outside and inside a frame', async () => {
    await browser.url(boxPage)
    await browser.checkElement(await $('#target'), 'frame-box') // baseline

    // v9
    await browser.url(framePage)
    await browser.switchFrame($('iframe'))
    console.log(await browser.checkElement(await $('#target'), 'frame-box')) // 69.587, expected 0
    await browser.switchFrame(null)

    // v10
    const frame = await (await browser.url(framePage)).frame('iframe')
    console.log(await browser.checkElement(await frame.$('#target'), 'frame-box')) // throws "Invalid input in clip"
})

tests/specs/v10.browsingContexts.spec.ts (#1222) has the v10 case as it.skip.

Root cause

In the BiDi element screenshot (takeElementScreenshots.ts#L46 and #L77):

  • getElementRect() gives the position of the element in the frame document (x 40, y 40 in the test).
  • takeBase64BiDiScreenshot() captures the top-level context (screenshots.ts#L546), with that position as the clip.

The clip misses the element by the position of the iframe (30/60 px).

Describe the solution you'd like

Support element screenshots of elements in frames in BiDi sessions, for v9 switchFrame() and v10 context.frame().

Proposed method (tested outside the service): compute the element rect in the top-level document with a client-side script that runs in the element's own document and adds the content-box position of each parent iframe:

function getElementRectInTopDocument (el, origin) {
    const r = el.getBoundingClientRect()
    let x = r.left, y = r.top, win = el.ownerDocument.defaultView
    while (win !== win.top) {
        const frameEl = win.frameElement // null for a cross-origin frame
        if (!frameEl) return null
        const fr = frameEl.getBoundingClientRect()
        const style = win.parent.getComputedStyle(frameEl)
        x += fr.left + frameEl.clientLeft + parseFloat(style.paddingLeft)
        y += fr.top + frameEl.clientTop + parseFloat(style.paddingTop)
        win = win.parent
    }
    // 'document' origin: add the scroll of the top-level page. 'viewport' origin: no scroll.
    return origin === 'document'
        ? { x: x + win.scrollX, y: y + win.scrollY, width: r.width, height: r.height }
        : { x, y, width: r.width, height: r.height }
}

Run it with element.execute(getElementRectInTopDocument, origin). In v9 (after switchFrame()) and in v10 (context.frame()), element.execute() runs in the frame document (browser.execute() runs in the top-level page in v10).

Result with the clip from this script:

Rect Mismatch
v9 switchFrame() x 70, y 100 (was 40, 40) 0.000% (was 69.587%)
v10 context.frame() x 70, y 100 0.000% (was an error)

Service changes:

  1. Add the client-side script.
  2. In both BiDi element functions, use it in place of getElementRect(). Keep getElementRect() as the fallback when the script returns null.
  3. autoElementScroll: scrollElementIntoView only scrolls inside the frame. With the 'viewport' origin, the top-level page must also show the iframe.
  4. Ignore regions: images.ts#L621 also uses getElementRect(), so elements in a frame used in ignore probably have the same offset (not tested).
  5. Tests: unit tests for the script (nested frames, iframe border and padding, scroll), and remove .skip from tests/specs/v10.browsingContexts.spec.ts.

Describe alternatives you've considered

  • Use WebDriver Classic for these tests ('wdio:enforceWebDriverClassic': true). This works now, but it disables BiDi for the whole session.
  • Capture the frame context directly: not possible, browsingContext.captureScreenshot only accepts a top-level context.
  • Cross-origin frames: frameElement is null, so the script cannot read the iframe position. Possible solution (not tested): BiDi browsingContext.locateNodes with the context locator in the parent context gives the iframe element, and script.callFunction in the parent gives its position. Else keep the current behavior and log a warning.

Additional context

  • Related, closed: #1156 / #1155 (wrong emulation detection inside a frame, which caused blank images with the fallback path on Firefox). From the analysis in #1156, the fallback path (takeScreenshot + crop) has the same frame offset when it is used for other reasons, for example resizeDimensions (not tested).
  • Limit of the method: a part of the element that the iframe does not show (iframe too small or scrolled) is not in the image, because the capture is of the top-level page.
Ngôn ngữ chính
TypeScript
Star
156
Fork
62
Merge trung bình
2 giờ 2 phút
Pull request đã merge (30 ngày)
111

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của webdriverio/visual-testing

Tất cả issue của webdriverio/visual-testing

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.