How to automate react-select with Playwright
Asked Answered
T

5

5

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?

Trephine answered 1/11, 2021 at 10:0 Comment(3)
Have you tried react selectors in playwright playwright.dev/docs/selectors#react-selectors ?Huntress
Read about briefly. Would they help here?Trephine
dead link @YurySemikhatskyKarakalpak
C
4

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

Collier answered 21/7, 2023 at 17:10 Comment(0)
T
2

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.

Trephine answered 1/11, 2021 at 10:0 Comment(0)
E
0

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()
}
Elin answered 24/1, 2023 at 10:53 Comment(0)
E
0

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);
    }
Electromagnet answered 2/7 at 5:39 Comment(0)
C
0

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
Coccidioidomycosis answered 4/10 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.