Answer
Scenario: Handling a Custom React Dropdown
Modern React apps use custom dropdown components built with <div>, <ul>, <li> elements — not native <select>. These need a different approach.
Inspect the HTML Structure First
HTML
<!-- Typical custom dropdown HTML -->
<div class="dropdown" aria-expanded="false">
<button class="dropdown-trigger" aria-haspopup="listbox">
Select Country
</button>
<ul class="dropdown-list" role="listbox" hidden>
<li role="option" data-value="US">United States</li>
<li role="option" data-value="UK">United Kingdom</li>
<li role="option" data-value="IN">India</li>
</ul>
</div>
Strategy 1 — Click Trigger, Then Click Option (Most Common)
TypeScript
test('select from custom dropdown', async ({ page }) => {
await page.goto('/register');
// Step 1: Click the dropdown trigger to open it
await page.getByRole('combobox', { name: 'Select Country' }).click();
// OR
await page.locator('.dropdown-trigger').click();
// Step 2: Wait for options to appear
await expect(page.getByRole('listbox')).toBeVisible();
// Step 3: Click the desired option
await page.getByRole('option', { name: 'United States' }).click();
// Step 4: Verify selection
await expect(page.getByRole('combobox')).toHaveText('United States');
});
Strategy 2 — Scope to the Dropdown Container
TypeScript
test('scoped dropdown selection', async ({ page }) => {
// Get the specific dropdown container (important if multiple dropdowns on page)
const countryDropdown = page.locator('[data-testid="country-select"]');
// Open it
await countryDropdown.click();
// Select within the dropdown context
await countryDropdown.getByText('India').click();
await expect(countryDropdown).toContainText('India');
});
Strategy 3 — Material UI / Ant Design / React Select
TypeScript
// React Select (most popular)
test('React Select dropdown', async ({ page }) => {
// The input inside React Select
const selectInput = page.locator('.react-select__control');
await selectInput.click();
// Type to filter options
await page.locator('.react-select__input input').fill('India');
// Click the filtered option
await page.locator('.react-select__option').filter({ hasText: 'India' }).click();
// Verify
await expect(page.locator('.react-select__single-value')).toHaveText('India');
});
// Ant Design Select
test('Ant Design Select', async ({ page }) => {
await page.locator('.ant-select-selector').click();
await page.locator('.ant-select-item-option').filter({ hasText: 'India' }).click();
await expect(page.locator('.ant-select-selection-item')).toHaveText('India');
});
// MUI Select
test('Material UI Select', async ({ page }) => {
await page.locator('#country-select').click();
await page.getByRole('option', { name: 'India' }).click();
await expect(page.locator('#country-select')).toHaveText('India');
});
Strategy 4 — Search/Autocomplete Dropdowns
TypeScript
test('autocomplete dropdown', async ({ page }) => {
const input = page.getByPlaceholder('Search city...');
// Type to trigger suggestions
await input.fill('New Y');
// Wait for suggestions to appear
await expect(page.locator('.suggestions-dropdown')).toBeVisible();
// Click desired suggestion
await page.locator('.suggestion-item').filter({ hasText: 'New York' }).click();
// Verify input shows selected value
await expect(input).toHaveValue('New York');
});
Tips for Handling Custom Dropdowns
- ✓Always check ARIA attributes —
role="listbox",role="option",aria-expanded - ✓Use
getByRole('option')for semantically correct selections - ✓Scope locators to the dropdown container when multiple exist
- ✓Wait for options to appear before clicking
- ✓Check
data-testidattributes — often the most stable selector
