I’m working on an automated test with Java + Maven + Selenium WebDriver + Serenity BDD + Cucumber.
The test needs to click the "Download" button of the most recent projection file in a table that is rendered inside an iframe.
Problem
At runtime, my step fails with this assertion:
No se encontró la tabla bodyArchivos ni en el DOM principal ni en ninguno de los iframes de la página. Revisa que la URL sea la de 'Archivos de resultados de proyecciones' y que el id sea exactamente 'bodyArchivos'.
Serenity output (Cucumber scenario):
Descargar y validar archivo de proyección ya existente
\-----------------------------------------------------------------------
23:33:03.045 ERROR \[ main\] : Test failed at step: Y descargo el archivo de proyección más reciente
23:33:03.045 ERROR \[ main\] : No se encontró la tabla bodyArchivos ni en el DOM principal ni en ninguno de los iframes de la página. Revisa que la URL sea la de 'Archivos de resultados de proyecciones' y que el id sea exactamente 'bodyArchivos'.
Failed scenarios:
.../archivos_proyeccion.feature:24 # Descargar y validar archivo de proyección ya existente
java.lang.AssertionError: No se encontró la tabla bodyArchivos ni en el DOM principal ni en ninguno de los iframes de la página. Revisa que la URL sea la de 'Archivos de resultados de proyecciones' y que el id sea exactamente 'bodyArchivos'.
at org.af.gestion.mi.pension.stepdefinitions.ArchivosProyeccionStepDefinitions.cambiarAFrameConTablaArchivos(ArchivosProyeccionStepDefinitions.java:407)
at org.af.gestion.mi.pension.stepdefinitions.ArchivosProyeccionStepDefinitions.descargoArchivoMasReciente(ArchivosProyeccionStepDefinitions.java:202)
at ?.descargo el archivo de proyección más reciente(.../archivos_proyeccion.feature:27)
So my helper method cannot find tbody#bodyArchivos either in the main DOM or in any iframe, but in the browser DevTools the element does exist.
Step definition that fails
@Cuando("descargo el archivo de proyección más reciente")
public void descargoArchivoMasReciente() {
WebDriver driver = getDriver();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(40));
// 1) Switch to the frame (or main DOM) that contains the table
cambiarAFrameConTablaArchivos(driver);
// 2) Wait for the table in the CURRENT context
WebElement tabla = wait.until(
ExpectedConditions.presenceOfElementLocated(By.id("bodyArchivos"))
);
// 3) Get the first row (most recent file) and its download button
WebElement fila = tabla.findElement(By.cssSelector("tr:first-child"));
WebElement botonDescarga = fila.findElement(By.cssSelector("td:nth-child(3) > button"));
// Click using JS in case of overlays
((JavascriptExecutor) driver).executeScript("arguments[0].click();", botonDescarga);
// 4) Go back to main content for the next steps
driver.switchTo().defaultContent();
// (code that waits for the downloaded .csv is omitted)
}
Helper method to switch into the iframe
/**
* Looks for tbody#bodyArchivos either in the main DOM or inside any iframe.
* If found, it leaves the driver already switched to the correct context.
* Otherwise it throws an AssertionError.
*/
private void cambiarAFrameConTablaArchivos(WebDriver driver) {
// Always start from the main document
driver.switchTo().defaultContent();
System.out.println("[DescargaCSV] URL actual: " + driver.getCurrentUrl());
// 1) First try in the main DOM (no iframe)
List<WebElement> tablasEnPrincipal = driver.findElements(By.id("bodyArchivos"));
if (!tablasEnPrincipal.isEmpty()) {
System.out.println("[DescargaCSV] ✔ Tabla bodyArchivos encontrada en el DOM principal (sin iframe).");
return;
}
// 2) If not in main DOM, look into iframes
List<WebElement> iframes = driver.findElements(By.tagName("iframe"));
System.out.println("[DescargaCSV] iframes encontrados en la página: " + iframes.size());
if (iframes.isEmpty()) {
throw new AssertionError(
"No se encontró la tabla bodyArchivos ni en el DOM principal ni en iframes (no hay iframes en la página)."
);
}
// 3) Try each iframe by index
for (int i = 0; i < iframes.size(); i++) {
try {
driver.switchTo().defaultContent();
driver.switchTo().frame(i);
List<WebElement> tablas = driver.findElements(By.id("bodyArchivos"));
if (!tablas.isEmpty()) {
System.out.println("[DescargaCSV] ✔ Tabla bodyArchivos encontrada en iframe index=" + i);
return; // stay in this iframe
} else {
System.out.println("[DescargaCSV] iframe index=" + i + " no contiene bodyArchivos");
}
} catch (NoSuchFrameException e) {
System.out.println("[DescargaCSV] NoSuchFrameException en iframe index=" + i);
} catch (StaleElementReferenceException e) {
System.out.println("[DescargaCSV] StaleElementReference en iframe index=" + i +
" → ignoring and continuing with the next one");
}
}
// 4) If we get here, the table was not found anywhere
throw new AssertionError(
"No se encontró la tabla bodyArchivos ni en el DOM principal ni en ninguno de los iframes de la página. " +
"Revisa que la URL sea la de 'Archivos de resultados de proyecciones' y que el id sea exactamente 'bodyArchivos'."
);
}
HTML structure (iframe + table)
From the Elements tab in DevTools, the page has this iframe:
<iframe
name="ifrcontent"
id="ifrcontent"
src="/PortalAccesoWeb/jsp/templates/blank.jsp"
class="min-width: 500px;"
width="100%"
height="450px"
frameborder="0"
allowtransparency="yes">
</iframe>
When I expand that iframe in DevTools, I see that its document loads another URL and contains the form and the table I need:
<div class="col-md-12">
<div class="card-table">
<h5 class="text-center text-white">Archivos de resultados de proyecciones</h5>
</div>
<div>
<table class="table mb-0">
<caption class="text-end pb-0">Últimos archivos de resultados</caption>
<thead>
<tr class="tdtituloAz mx-2">
<th class="align-middle">Archivo</th>
<th class="align-middle text-center">Fecha fin procesamiento</th>
<th class="align-middle text-center">Descargar</th>
</tr>
</thead>
<tbody id="bodyArchivos">
<tr class="trTablaImpar">
<td class="align-middle">ProyeccionPensionISSSTE_260925_resultado_58.csv</td>
<td class="align-middle text-center">26/09/2025</td>
<td class="align-middle text-center">
<button class="btn btn-secondary btn-sm" type="button"
onclick="descargarProyeccion('ProyeccionPensionISSSTE_260925_resultado_58.csv');">
<i class="fa fa-file-excel-o"></i>
</button>
</td>
</tr>
<!-- several more <tr> rows ... -->
</tbody>
</table>
</div>
</div>
Browser console output
In the browser console I tried:
document.querySelectorAll("iframe")
document.querySelectorAll("iframe").length
and I get:
- NodeList []
- 0
I also tried:
let frame = window.frames[0];
frame.document.getElementById("bodyArchivos");
and I get:
Uncaught TypeError: Cannot read properties of undefined (reading 'document')
At the same time, in the Console I see a JavaScript error coming from the application:
Uncaught ReferenceError: setUpArchivos is not defined at onload (archivos.do:48)
What I have already tried
- Iterating all iframes by index (driver.switchTo().frame(i)) and searching for By.id("bodyArchivos") in each one.
- Looking for the table in the main DOM before switching to frames.
- Confirming in DevTools that the id really is bodyArchivos and that the table is inside ifrcontent.
- Running the test in Edge and Chrome with the same result.
Question
What am I doing wrong when trying to locate tbody#bodyArchivos inside this iframe?
- Is there something special about this iframe because its src is /PortalAccesoWeb/jsp/templates/blank.jsp but internally it loads /ProyeccionPensionWeb/archivos.do?
- Could the JavaScript error (setUpArchivos is not defined) prevent the iframe document from being attached to window.frames / document.querySelectorAll("iframe") from Selenium’s point of view?
- Do I need a different strategy to switch into this iframe (for example by name or WebElement, or waiting for the inner document to be fully loaded) so that WebDriver can see tbody#bodyArchivos?enter image description hereenter image description here
1 Reply 1
// 2) Wait for the table in the CURRENT context
WebElement tabla = wait.until(
ExpectedConditions.presenceOfElementLocated(By.id("bodyArchivos"))
);
You are waiting for the element after you've already thrown an exception that it doesn't exist. Move the wait inside your helper.
Explore related questions
See similar questions with these tags.