Extraer texto de una lista de elementos de la página, filtrar una respuesta de API por estado y construir conjuntos de datos de prueba usan los mismos cuatro métodos de JavaScript: map, filter, find y forEach. El error más común en Playwright específicamente es llamar a forEach con un callback async: dispara promesas sin esperar, así que las aserciones se ejecutan antes de que lleguen los datos. Esta guía cubre cada método con los patrones que aparecen en suites de tests reales, incluyendo cómo Promise.all con map reemplaza a forEach para el trabajo asíncrono con elementos.
Arrays: un repaso de un minuto
Un array es una lista ordenada de valores:
const usuarios = ['Alice', 'Bob', 'Charlie'];
const precios = [12.99, 5.50, 29.00, 8.75];
const casosDePrueba = [
{ input: 'valido@email.com', debepasar: true },
{ input: 'no-es-un-email', debepasar: false },
{ input: '', debepasar: false },
];Los elementos se acceden por índice (comenzando en 0):
console.log(usuarios[0]); // 'Alice'
console.log(usuarios[2]); // 'Charlie'
console.log(usuarios.length); // 3forEach: hacer algo con cada ítem
forEach ejecuta una función una vez por cada ítem del array. Úsalo cuando quieres hacer algo con cada ítem pero no necesitas recibir un resultado de vuelta.
const nombres = ['alice', 'bob', 'charlie'];
nombres.forEach((nombre) => {
console.log(`Testeando usuario: ${nombre}`);
});
// Salida:
// Testeando usuario: alice
// Testeando usuario: bob
// Testeando usuario: charlieEn tests de Playwright
const urlsLogin = [
'/en/login',
'/ru/login',
'/es/login',
];
loginUrls.forEach(async (url) => {
await page.goto(url);
await expect(page.locator('h1')).toBeVisible();
});map: transformar cada ítem
map crea un nuevo array aplicando una función a cada ítem. El array original no cambia.
Piénsalo así: "por cada ítem, devuélveme algo diferente."
const precios = [10, 20, 30];
const conDescuento = precios.map((precio) => precio * 0.9);
console.log(conDescuento); // [9, 18, 27]
console.log(precios); // [10, 20, 30] ← sin cambiosEjemplo práctico: extraer texto de una lista de elementos
A menudo obtienes un array de locators de Playwright y necesitas extraer el texto de cada uno:
// Obtener todos los textos de celdas de tabla
const celdas = await page.locator('table tbody td').all();
const textos = await Promise.all(
celdas.map((celda) => celda.textContent())
);
// textos = ['Alice', '28', 'Admin', 'Bob', '34', 'User', ...]Construir datos de prueba con map
const idsUsuario = [1, 2, 3, 4, 5];
const usuariosDePrueba = idsUsuario.map((id) => ({
id,
username: `user_${id}`,
email: `user${id}@test.com`,
rol: id === 1 ? 'admin' : 'member',
}));
/*
[
{ id: 1, username: 'user_1', email: 'user1@test.com', rol: 'admin' },
{ id: 2, username: 'user_2', email: 'user2@test.com', rol: 'member' },
...
]
*/Transformar datos de respuesta de API
// La API devuelve datos crudos
const usuariosApi = [
{ first_name: 'Alice', last_name: 'Smith', is_active: 1 },
{ first_name: 'Bob', last_name: 'Jones', is_active: 0 },
];
// Transformarlos para que coincidan con lo que muestra la UI
const usuariosMostrados = usuariosApi.map((u) => ({
nombre: `${u.first_name} ${u.last_name}`,
activo: u.is_active === 1,
}));
// [{ nombre: 'Alice Smith', activo: true }, { nombre: 'Bob Jones', activo: false }]filter: quedarte solo con lo que necesitás
filter crea un nuevo array que contiene solo los ítems que pasan una condición. Los que no la pasan se eliminan.
const precios = [5, 12, 3, 25, 8, 40, 1];
const caros = precios.filter((precio) => precio > 10);
console.log(caros); // [12, 25, 40]
console.log(precios); // [5, 12, 3, 25, 8, 40, 1] ← sin cambiosFiltrar casos de prueba
Acá es donde filter brilla en la automatización de tests. Cuando tienes una lista grande de casos de prueba, puedes dividirlos:
const todosCasos = [
{ input: 'valido@email.com', debepasar: true },
{ input: 'otro@test.org', debepasar: true },
{ input: 'no-es-un-email', debepasar: false },
{ input: '', debepasar: false },
{ input: 'falta@', debepasar: false },
];
const casosValidos = todosCasos.filter((tc) => tc.debepasar);
const casosInvalidos = todosCasos.filter((tc) => !tc.debepasar);
// casosValidos tiene 2 ítems, casosInvalidos tiene 3Filtrar resultados de API
// Todas las órdenes de la API
const ordenes = [
{ id: 1, estado: 'completada', monto: 99 },
{ id: 2, estado: 'pendiente', monto: 45 },
{ id: 3, estado: 'completada', monto: 12 },
{ id: 4, estado: 'cancelada', monto: 75 },
];
// Quedarse solo con las órdenes completadas para verificación
const ordenesCompletadas = ordenes.filter((o) => o.estado === 'completada');
// [{ id: 1, ... }, { id: 3, ... }]Filtrar elementos de la página por texto
// Todas las filas de una tabla
const filas = await page.locator('table tbody tr').all();
// Solo las filas que contienen 'Admin'
const filasAdmin = [];
for (const fila of filas) {
const texto = await fila.textContent();
if (texto?.includes('Admin')) {
filasAdmin.push(fila);
}
}find: obtener la primera coincidencia
find devuelve el primer ítem que pasa una condición. Si nada coincide, devuelve undefined. A diferencia de filter, deja de buscar en cuanto encuentra una coincidencia.
const usuarios = [
{ id: 1, nombre: 'Alice', rol: 'admin' },
{ id: 2, nombre: 'Bob', rol: 'member' },
{ id: 3, nombre: 'Carol', rol: 'admin' },
];
const primerAdmin = usuarios.find((u) => u.rol === 'admin');
// { id: 1, nombre: 'Alice', rol: 'admin' }
const usuarioInexistente = usuarios.find((u) => u.nombre === 'Dave');
// undefinedEncontrar datos de prueba por ID
const productos = [
{ id: 'PROD-001', nombre: 'Laptop', precio: 999 },
{ id: 'PROD-002', nombre: 'Mouse', precio: 29 },
{ id: 'PROD-003', nombre: 'Escritorio', precio: 349 },
];
const productoObjetivo = productos.find((p) => p.id === 'PROD-002');
// { id: 'PROD-002', nombre: 'Mouse', precio: 29 }Siempre verificar si es undefined
const producto = productos.find((p) => p.id === 'PROD-999');
if (!producto) {
throw new Error('Datos de prueba no encontrados: PROD-999');
}
// Ahora es seguro usar producto
await page.fill('[data-testid="buscar"]', producto.nombre);filter.
Combinarlos
El poder real viene de encadenarlos:
const ordenesApi = [
{ id: 1, usuario: 'alice', estado: 'completada', monto: 150, items: 3 },
{ id: 2, usuario: 'bob', estado: 'pendiente', monto: 50, items: 1 },
{ id: 3, usuario: 'alice', estado: 'completada', monto: 200, items: 5 },
{ id: 4, usuario: 'carol', estado: 'cancelada', monto: 75, items: 2 },
{ id: 5, usuario: 'alice', estado: 'pendiente', monto: 30, items: 1 },
];
// Obtener el monto total de las órdenes completadas de Alice
const totalAlice = ordenesApi
.filter((o) => o.usuario === 'alice' && o.estado === 'completada')
.map((o) => o.monto)
.reduce((suma, monto) => suma + monto, 0);
// 150 + 200 = 350O para verificación:
// Verificar que todos los precios de productos visibles son mayores a cero
const locatorsPrecios = await page.locator('[data-testid="precio-producto"]').all();
const precios = await Promise.all(
locatorsPrecios.map(async (el) => {
const texto = await el.textContent();
return parseFloat(texto?.replace('$', '') ?? '0');
})
);
const tienePrecioNegativo = precios.some((p) => p <= 0);
expect(tienePrecioNegativo).toBe(false);Referencia rápida
| Método | Qué devuelve | Usalo cuando |
|--------|-------------|--------------|
| forEach | Nada (undefined) | Querés efectos secundarios (logging, acciones) |
| map | Nuevo array, misma longitud | Querés transformar cada ítem |
| filter | Nuevo array, igual o más corto | Querés un subconjunto de ítems |
| find | Un ítem o undefined | Querés la primera coincidencia |
Errores comunes
Usar map cuando quieres forEach
// Mal: map es para devolver valores, no para efectos secundarios
usuarios.map((usuario) => {
console.log(usuario.nombre); // funciona pero es innecesario
});
// Bien
usuarios.forEach((usuario) => {
console.log(usuario.nombre);
});Olvidar que find puede devolver undefined
// Esto va a fallar si no se encuentra el usuario
const usuario = usuarios.find((u) => u.id === 99);
usuario.nombre; // TypeError: Cannot read properties of undefinedMutar el array original con map
Si haces usuarios.map((u) => { u.rol = 'admin'; return u; }), mutaste los objetos originales, no solo creaste nuevos. Crea nuevos objetos en su lugar:
// Bien: crea nuevos objetos
usuarios.map((u) => ({ ...u, rol: 'admin' }));Nota sobre async en Playwright
Cuando el callback es async (lo que pasa constantemente en Playwright), envuelve con Promise.all:
// Esto no funciona: forEach ignora las promesas
celdas.forEach(async (celda) => {
const texto = await celda.textContent(); // se dispara y se olvida
});
// Esto funciona: espera todas las promesas
const textos = await Promise.all(
celdas.map(async (celda) => await celda.textContent())
);Este es el error de "async en arrays" más común en Playwright. Ante la duda, usa Promise.all con map.