I'm using react-select in my application for implementing CSS styleable dropdowns. Also, I'm using Playwright for automatic testing want to be able to select an option from one of these dropdowns using Playwright. How to?
You can set the id for both the react-select as well as the input field that react-select supports that allows you to search for an option by text. For instance I did the following:
<Select id={"myReactSelectId"} inputId={"myReactSelectInputId"} options={options} autofocus={true}/>
Then with Playwright you can use:
const selectOption = async (page: Page, optionText: string) => {
await expect(page.locator("input[id='myReactSelectInputId']")).toBeFocused();
await page.locator("input[id='myReactSelectInputId']").fill(optionText);
await this.page.keyboard.down("Tab");
}
And voila your react-select has now updated to the desired option.
Note: I am using react-select 5.7.3
By trial and error, I came up with this.
import { Page, ElementHandle } from "@playwright/test"
export default async function reactSelect(page: Page, instanceId: string, optionText: string) {
const innerDetailWithKnownId = await page.waitForSelector(`#react-select-${id}-live-region`)
const select = await parentElement(innerDetailWithKnownId)
await select!.click()
const option = await page.waitForSelector(`#react-select-${instanceId}-listbox div:text('${optionText}')`)
await option.scrollIntoViewIfNeeded()
await option.click()
}
function parentElement(element: ElementHandle<any>) {
return element.$("xpath=..")
}
Here instanceId
should match the value you used as instanceId
for the actual react-select in your JSX code.
Here's the solution I come up with using "react-select": "5.4.0"
.
Note that you need to place an id my_react_select
on your select for this to work
const selectOption = async (page: Page, optionText: string) => {
await page.locator('#my_react_select').click()
await page.locator(`.react-select__option:text("${optionText}")`).click()
}
Here's my solution using C# and react-select 3.2.0 - hopefully the markup hasn't changed too much in recent versions, I expect the solution would be similar for TypeScript implementation.
public enum TextMatchType
{
Exact,
StartsWith,
Contains,
EndsWith
}
protected async Task SelectReactSelectOption(string selectId, string optionText, TextMatchType matchType = TextMatchType.Exact)
{
// Locate and click the container to open the dropdown
var containerSelector = $"div[class*='-container']:has(input#{selectId})";
await page.Locator(containerSelector).ClickAsync();
// Wait for the menu to appear
var menuSelector = $"{containerSelector} >> div[class$='-menu']";
await page.WaitForSelectorAsync(menuSelector);
// Construct the option selector
var optionSelector = $"{menuSelector} >> div[class*='-option']";
var optionLocator = page.Locator(optionSelector);
// Apply text matching based on the specified type
ILocator matchedOption = matchType switch
{
TextMatchType.StartsWith => optionLocator.Filter(new() { HasTextRegex = new($"^{Regex.Escape(optionText)}") }),
TextMatchType.Contains => optionLocator.Filter(new() { HasText = optionText }),
TextMatchType.EndsWith => optionLocator.Filter(new() { HasTextRegex = new($"{Regex.Escape(optionText)}$") }),
_ => optionLocator.Filter(new() { HasTextRegex = new($"^{Regex.Escape(optionText)}$") })
};
// Click the matched option
await matchedOption.ClickAsync();
// Wait for a short time to ensure the selection is registered
await page.WaitForTimeoutAsync(100);
}
Using react-select 5.x myself
I always prefer to use role locators.
await page.getByRole('combobox', { name: 'your_label_name' }).click();
await page.getByRole('option').filter({ hasText: 'your_option' }).click();
If your select component already has a value you might have to force the click to ignore the value div placed above it.
.click({ force: true });
You can also consider using keyboard actions.
await page.getByRole('combobox', { name: 'your_label_name' }).click();
await page.keyboard.press('ArrowDown');
// Type a filter value or keep pressing arrowdown until you get to the value you need
await page.keyboard.press('Space'); // Or enter
© 2022 - 2024 — McMap. All rights reserved.