feat: support element screenshots of elements in a frame in WebDriver BiDi sessions (v9 `switchFrame()`, v10 `context.frame()`)
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 replacementcontext.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 theclip.
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:
- Add the client-side script.
- In both BiDi element functions, use it in place of
getElementRect(). KeepgetElementRect()as the fallback when the script returnsnull. autoElementScroll:scrollElementIntoViewonly scrolls inside the frame. With the'viewport'origin, the top-level page must also show the iframe.- Ignore regions:
images.ts#L621also usesgetElementRect(), so elements in a frame used inignoreprobably have the same offset (not tested). - Tests: unit tests for the script (nested frames, iframe border and padding, scroll), and remove
.skipfromtests/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.captureScreenshotonly accepts a top-level context. - Cross-origin frames:
frameElementisnull, so the script cannot read the iframe position. Possible solution (not tested): BiDibrowsingContext.locateNodeswith thecontextlocator in the parent context gives the iframe element, andscript.callFunctionin 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 exampleresizeDimensions(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
- Không có Dockerfile hay tệp Docker Compose
- Không có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của webdriverio/visual-testing
-
Proposal: a noise-tolerant compare mode with pixelmatch 8's windowSizeCó thể đã có người làm @plum117 đã nhận 3 ngày trước. Đang mởenhancement
webdriverio/visual-testing#1311 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug: the visual service fails on Android Chrome through Appium 3 in the default WebDriver BiDi session (workaround: `wdio:enforceWebDriverClassic`)Có thể đã có người làm @plum117 đã nhận 3 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
webdriverio/visual-testing#1232 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Wait for Screenshot to MatchCó thể đã có người làm @plum117 đã nhận 2 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
webdriverio/visual-testing#690 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Using checkFullPageScreen method, capturing images with duplicate viewport and is not scrolling to the bottom of the pageCó thể đã có người làm @plum117 đã nhận 2 ngày trước. Đang mởhelp wanted
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
webdriverio/visual-testing#125 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của webdriverio/visual-testing
Issue tương tự
-
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 70/100
siyuan-note/siyuan#20517 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
NaturalIntelligence/fast-xml-parser#890 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 3 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
zsviczian/obsidian-excalidraw-plugin#3000 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
pnpm/get.pnpm.io#68 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
nuxt-modules/better-auth#578 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày