Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Abierto
#1,228 0 comentarios 0 reacciones 1 asignado Ver en GitHub

Los mantenedores suelen responder en 1 día

@plum117 ya está trabajando en esto.

Desde el 9/10/2026.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
50/100
Tipo de issue
Nueva funcionalidad
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
typescript
Área
testing-qa

Línea de trabajo

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.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.
Lenguaje dominante
TypeScript
Estrellas
156
Forks
62
Merge medio
2 h 2 min
PR fusionados (30 d)
111

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de webdriverio/visual-testing

Todos los issues de webdriverio/visual-testing

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.