Playwright: Interacting with Select Dropdown
Select dropdowns are common, but they are rarely all the same. Some allow one choice, some allow many, some filter as you type, and some include clear buttons or long scrollable lists. That is why dropdowns are a useful control to practice with in Playwright.
This guide uses the Codemo select dropdown playground to cover the main interaction patterns you are likely to automate: standard selection, multiple selection, defaults, long lists, searchable dropdowns, and clearable values.
Playground - General UI - Select Dropdown
Click here to practice select dropdown interactions in the Codemo Playground.
Why Test Select Dropdowns?
Dropdown tests help confirm that:
- users can open the control and choose the expected option
- selected values are shown correctly in the UI
- default values are preserved on load
- long lists remain usable
- searchable dropdowns filter options correctly
- clear buttons really reset the selection
- multiple selection works correctly
Basic Select Dropdown HTML
Before interacting with a select dropdown in Playwright, it's helpful to understand the underlying HTML structure. A basic select dropdown looks like this:
<label for="fruits">Choose a fruit:</label>
<select id="fruits" name="fruits">
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="cherry">Cherry</option>
</select>
Finding Dropdowns In Playwright
The most useful locators for this page are getByRole("combobox"), getByRole("option") to interact with the dropdown trigger and its options, and selectOption on the select element by label, value, or array of values.
selectOption only works on the underlying <select> elementimport { test, expect } from "@playwright/test"
test("selects an option from a dropdown", async ({ page }) => {
await page.getByRole("combobox").first().click()
await page.getByRole("option", { name: "Cherry" }).click()
// Using selectOption by value, label, and array of values
await page.getByLabel("Choose a fruit").selectOption("banana")
await page.getByLabel("Choose a fruit").selectOption({ label: "Cherry" })
await page.getByLabel("Choose a fruit").selectOption(["apple", "banana", "cherry"])
})
getByRole("combobox")for the dropdown triggergetByRole("option", { name: "Banana" })for the option listselectOptionon the select element by label, value, or array of values
Testing Standard Selection
The simplest scenario is selecting one option from a dropdown and checking that the selected value is correctly reflected in the UI.
selected: true while asserting the option's selection state (an attribute that is usually set by aria-selected), to verify that the correct options are selected in the dropdown.test("dropdown should register a standard selection", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("combobox", { name: "Choose an option" }).click()
await page.getByRole("option", { name: "Banana" }).click()
await expect(page.getByRole("option", { name: "Banana", selected: true })).toBeVisible()
await expect(page.getByText("Selected: Banana")).toBeVisible()
})
Testing Multiple Selection
This test ensures that multiple selections are correctly registered and displayed in the UI.
test("dropdown should support multiple selections", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("combobox", { name: "Choose options" }).click()
await page.getByRole("option", { name: "Red" }).click()
await page.getByRole("option", { name: "Blue" }).click()
await expect(page.getByRole("option", { name: "Red", selected: true })).toBeVisible()
await expect(page.getByRole("option", { name: "Blue", selected: true })).toBeVisible()
await expect(page.getByText("Selected: Red, Blue")).toBeVisible()
})
Testing Default Values
The default selection should be correctly reflected in the dropdown, validating that the initial value is properly set and displayed in the UI.
test("dropdown should keep its default selection", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("combobox", { name: "Default select" }).click()
await expect(page.getByRole("option", { name: "Option 1 (Default)", selected: true })).toBeVisible()
await expect(page.getByRole("option", { name: "Option 2", selected: false })).toBeVisible()
await expect(page.getByRole("option", { name: "Option 3", selected: false })).toBeVisible()
await expect(page.getByText("Default value: Option 1 (Default)")).toBeVisible()
})
Testing Long Lists
Long dropdown menus often reveal scrolling or rendering problems. This playground includes a 50-item list to simulate that case.
test("dropdown should handle a long list", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("combobox", { name: "Choose from many" }).click()
await page.getByRole("option", { name: "Item 50" }).click()
await expect(page.getByRole("combobox", { name: "Choose from many" })).toHaveText("Item 50")
await expect(page.getByText("Selected: Item 50")).toBeVisible()
})
Testing Searchable Dropdowns
This is one of the most practical patterns because searchable dropdowns behave more like a combination of input and menu.
exact: true to ensure that the option selection is precise and not partially matched by other similar options.test("dropdown should filter searchable options", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("button", { name: "Type to search" }).click()
await page.getByPlaceholder("Search").fill("java")
await page.getByRole("option", { name: "Java", exact: true }).click()
await expect(page.getByRole("button", { name: "Java" })).toBeVisible()
await expect(page.getByText("Selected: Java")).toBeVisible()
})
Testing Clearable Values
Clearable dropdowns allow users to remove their selection, which is an important interaction to test.
.clear-icon (. is the class selector prefix for classes in CSS selectors) and clicking it to remove the selected value.test("dropdown should clear the selected value", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("button", { name: "Option A" })
.locator(".clear-icon")
.click()
await expect(page.getByRole("button", { name: "Choose and clear" })).toBeVisible()
await expect(page.getByText("Selected: (cleared)")).toBeVisible()
})
Practice With Codemo Playground
Codemo provides an interactive select dropdown playground where you can practice dropdown behavior one scenario at a time.
Try these challenges:
- Select Banana in the standard case and confirm the footer updates.
- Select Red and Blue in the multiple-select case.
- Verify that the default case loads with
Option 1 (Default)already selected. - Select
Item 50from the long-list case. - Search for
JavaScriptin the searchable case. - Select and then clear a value in the clearable case.
Complete Test Example
import { test, expect } from "@playwright/test"
test.describe("Playground - General UI - Select Dropdown", () => {
test("dropdown should register a standard selection", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("combobox", { name: "Choose an option" }).click()
await page.getByRole("option", { name: "Banana" }).click()
await expect(page.getByRole("option", { name: "Banana", selected: true })).toBeVisible()
await expect(page.getByText("Selected: Banana")).toBeVisible()
})
test("dropdown should support multiple selections", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("combobox", { name: "Choose options" }).click()
await page.getByRole("option", { name: "Red" }).click()
await page.getByRole("option", { name: "Blue" }).click()
await expect(page.getByRole("option", { name: "Red", selected: true })).toBeVisible()
await expect(page.getByRole("option", { name: "Blue", selected: true })).toBeVisible()
await expect(page.getByText("Selected: Red, Blue")).toBeVisible()
})
test("dropdown should keep its default selection", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("combobox", { name: "Default select" }).click()
await expect(page.getByRole("option", { name: "Option 1 (Default)", selected: true })).toBeVisible()
await expect(page.getByRole("option", { name: "Option 2", selected: false })).toBeVisible()
await expect(page.getByRole("option", { name: "Option 3", selected: false })).toBeVisible()
await expect(page.getByText("Default value: Option 1 (Default)")).toBeVisible()
})
test("dropdown should handle a long list", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("combobox", { name: "Choose from many" }).click()
await page.getByRole("option", { name: "Item 50" }).click()
await expect(page.getByRole("combobox", { name: "Choose from many" })).toHaveText("Item 50")
await expect(page.getByText("Selected: Item 50")).toBeVisible()
})
test("dropdown should filter searchable options", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("button", { name: "Type to search" }).click()
await page.getByPlaceholder("Search").fill("java")
await page.getByRole("option", { name: "Java", exact: true }).click()
await expect(page.getByRole("button", { name: "Java" })).toBeVisible()
await expect(page.getByText("Selected: Java")).toBeVisible()
})
test("dropdown should clear the selected value", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/select-dropdown/")
await page.getByRole("button", { name: "Option A" }).locator(".clear-icon").click()
await expect(page.getByRole("button", { name: "Choose and clear" })).toBeVisible()
await expect(page.getByText("Selected: (cleared)")).toBeVisible()
})
})
Key Takeaways
- Use
getByRole("combobox")andgetByRole("option")for most dropdown interactions. - Use
selected: trueto target the currently selected option in searchable dropdowns. - Use
exact: trueto ensure precise option selection in searchable dropdowns. - Use class selectors (e.g.,
.clear-icon) to interact with clear buttons in clearable dropdowns.