selectOption() funciona solo en elementos nativos: llamalo en un React Select o un combobox de Material UI construido con divs y Playwright lanza un error. Los dropdowns personalizados necesitan un enfoque diferente: clic para abrir, luego clic en la opción por rol o texto visible. Este artículo cubre los selects nativos, el patrón clic-para-abrir para componentes personalizados, dropdowns con filtrado por escritura, navegación por teclado, la estructura de clases específica de React Select, multi-select, y la aserción que confirma que algo fue realmente seleccionado en lugar de solo clickeado.
Elementos nativos
El caso más simple: un elemento HTML estándar.
<label for="country">Country</label>
<select id="country">
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="de">Germany</option>
<option value="pl">Poland</option>
</select>// Seleccionar por texto visible
await page.getByLabel('Country').selectOption('Germany');
// Seleccionar por atributo value
await page.getByLabel('Country').selectOption({ value: 'de' });
// Seleccionar por índice (base 0 — evitarlo, es frágil)
await page.getByLabel('Country').selectOption({ index: 2 });
// Verificar la selección
await expect(page.getByLabel('Country')).toHaveValue('de');
// Seleccionar múltiples (para <select multiple>)
await page.getByLabel('Tags').selectOption(['playwright', 'typescript']);selectOption() funciona solo en elementos . Para dropdowns personalizados necesitas un enfoque diferente.
Componentes dropdown personalizados
La mayoría de las apps web modernas usan componentes dropdown personalizados (React Select, Headless UI, Material UI) en lugar del nativo. Se ven como dropdowns pero funcionan diferente por dentro.
Patrón 1: clic para abrir, luego clic en la opción
El patrón más común de dropdown personalizado:
// Dropdown personalizado: clic para abrir, clic para seleccionar
await page.getByRole('combobox', { name: 'Country' }).click();
await page.getByRole('option', { name: 'Germany' }).click();
// Verificar
await expect(page.getByRole('combobox', { name: 'Country' })).toHaveText('Germany');Si el dropdown usa el rol listbox:
await page.getByRole('button', { name: 'Country' }).click();
await page.getByRole('listbox').getByRole('option', { name: 'Germany' }).click();Patrón 2: escribir para filtrar, luego seleccionar
Muchos dropdowns personalizados soportan búsqueda y filtrado:
// Escribir para filtrar
await page.getByRole('combobox', { name: 'Country' }).fill('Ger');
// Esperar a que aparezcan los resultados filtrados
await page.getByRole('option', { name: 'Germany' }).waitFor();
// Clic en la opción
await page.getByRole('option', { name: 'Germany' }).click();Patrón 3: navegación por teclado
Algunos dropdowns están controlados por teclado:
await page.getByLabel('Country').focus();
await page.keyboard.press('ArrowDown'); // Abrir dropdown
await page.keyboard.press('ArrowDown'); // Moverse a la primera opción
await page.keyboard.press('ArrowDown'); // Moverse a la segunda opción
await page.keyboard.press('Enter'); // SeleccionarO escribir para saltar a las opciones que coinciden:
await page.getByLabel('Country').focus();
await page.keyboard.type('G'); // Saltar a Germany
await page.keyboard.press('Enter');Inspeccionar la implementación real del dropdown
Antes de escribir el test, inspecciona el elemento en las DevTools:
1. Clic derecho en el dropdown → Inspeccionar
2. Verificar el tipo de elemento: ¿es 3. Si es personalizado, interactúa con él manualmente y observa los cambios en el DOM 4. Verificar el atributo El enfoque correcto de testing depende enteramente de la implementación. No asumas. React Select es una de las librerías de dropdown más usadas. Tiene comportamiento específico: React Select usa clases CSS específicas. Si tu versión genera nombres de clase diferentes, adáptalo. Prefiere Inputs de fecha con dropdowns separados para mes, día y año: Para Solo funciona en Después de abrir un dropdown que carga opciones de forma asíncrona, espera a que la opción sea visible antes de hacer clic. Es frágil: una nueva opción agregada antes de la tuya rompe el test. Usa texto o valor en su lugar. Verifica que el valor seleccionado es correcto, no solo que el clic no lanzó un error. o un ?
role o los roles ARIAReact Select (una librería común)
// Combobox de React Select
const dropdown = page.locator('.react-select__control');
await dropdown.click();
// Escribir para filtrar
await dropdown.locator('input').fill('Ger');
await page.locator('.react-select__option', { hasText: 'Germany' }).click();
// Verificar
await expect(page.locator('.react-select__single-value')).toHaveText('Germany');
// Limpiar la selección
await page.locator('.react-select__clear-indicator').click();getByRole cuando sea posible:// Mejor — usa roles semánticos
await page.getByRole('combobox', { name: 'Country' }).click();
await page.getByRole('option', { name: 'Germany' }).click();Dropdowns multi-select
// <select multiple> nativo
await page.getByLabel('Skills').selectOption(['playwright', 'typescript', 'cicd']);
// Multi-select personalizado: clic en múltiples opciones
await page.getByRole('button', { name: 'Skills' }).click();
await page.getByRole('option', { name: 'Playwright' }).click();
await page.getByRole('option', { name: 'TypeScript' }).click();
await page.getByRole('option', { name: 'CI/CD' }).click();
// Cerrar el dropdown
await page.keyboard.press('Escape');
// Verificar las selecciones
await expect(page.getByTestId('selected-skills')).toContainText('Playwright');
await expect(page.getByTestId('selected-skills')).toContainText('TypeScript');Dropdowns de fecha (selectores de día, mes, año)
await page.getByLabel('Month').selectOption('June');
await page.getByLabel('Day').selectOption('15');
await page.getByLabel('Year').selectOption('1990');:// Funciona en todos los navegadores
await page.getByLabel('Date of birth').fill('1990-06-15');
// Verificar
await expect(page.getByLabel('Date of birth')).toHaveValue('1990-06-15');Verificar las opciones del dropdown
// Verificar que todas las opciones están presentes
const options = await page.getByLabel('Country').locator('option').allTextContents();
expect(options).toContain('Germany');
expect(options).toContain('Poland');
// Verificar que una opción específica está deshabilitada
await expect(page.getByRole('option', { name: 'Out of stock item' })).toBeDisabled();
// Verificar la cantidad de opciones
const optionCount = await page.getByLabel('Category').locator('option').count();
expect(optionCount).toBeGreaterThan(0);Errores comunes
Usar selectOption() en un dropdown personalizado
. Los dropdowns personalizados requieren interacciones de clic o teclado.No esperar a que aparezcan las opciones
Seleccionar por índice
No verificar qué muestra el dropdown después de la selección