Playwright testing - Select option

Hi,

I am working on setting up automated testing via Playwright. From my understanding, we should be able to leverage Playwright's select option feature. Actions | Playwright

This thread seems to confirm that idea. Issue with Submitting Category and Subcategory Values via Automation in Retool

Is there a correct way to find the correct selector? Everything I've tried via playwright codegen or inspecting the components manually does not seem to work.

@Tess Do you have any insight into this?

Hey @paul.narup, thanks for reaching out, were you able to try out some of the workarounds here?

Let me know if this helps! :pray:

The workaround works fine, that is not the issue.

My question:

Is there a correct way to find the correct selector? Everything I've tried via playwright codegen or inspecting the components manually does not seem to work.

Relates to this piece of that post:

// Ensure the page and selector are correctly loaded
const status = await page.locator('select#status-selector');
// Change the selector to match yours

// Select the option by its value or label
await status.selectOption({ label: 'AwaitingAnalyst' });
// or use { value: 'option_value' }

Make sure to adjust the selector (select#status-selector) to correctly target your select element. You can select by value, label, etc.

This seems to imply we can use Playwright's selectOption feature. My issue is I cannot find the code to replace await page.locator('select#status-selector') with my own locator. Using the Pick Locator and CodeGen features usually return something like getByTestId(Team25--0').getByTestId('WidgetLabelWrapper::FieldLabel') for dropdowns when we explicitly need a select object.

@paul.narup, thanks for clarifying! A couple of follow up questions to better understand what you are trying to do.
Can you share a screenshot of the components in your app or a Loom video? Knowing which components you are using would be really helpful. Also, please describe the exact action you are trying to test. You may need to manually look for the selectors in the browser inspector.

I am using a generic select component.

I am trying to test choosing one of the options, which playwright docs seem to describe as possible using one line: Actions | Playwright. await page.getByLabel('Headquarters (Country)').selectOption({ label: 'United States' });

Using that code results in the following error, which makes me think that due to Retool's generated code, this may not be possible. I really just want to confirm that this is in fact the case since someone from the Retool team previously posted that .selectOption was usable in the thread we both linked.

Error: locator.selectOption: Error: Element is not a <select> element
Call log:
  - waiting for getByLabel('Headquarters (Country)')
    - locator resolved to <input size="1" value="" type="text" role="combobox" autocorrect="off" autocomplete="off" spellcheck="false" aria-expanded="false" class="_input_1pxbh_63" aria-autocomplete="list" id="HeadquartersCountry6--0" placeholder="Select an option" data-testid="Widgets::SelectInput_input"/>
  - attempting select option action
    - waiting for element to be visible and enabled

Your diagnosis is correct. Retool select components are custom solutions that don't use standard HTML tags. As a result, Playwright's selectOption() method cannot be used.

Both solutions you found are effective. The first relies on a simple click on the text that appears after opening the list. The second uses the fact that Retool adds a special SelectOverlay element to the DOM that contains the options.

Here are your solutions in a concise format:

  1. Method using just text: This is a simpler solution that works if the option text (Poland) is unique on the page after the list is opened.
page.getByTestId('Trigger::HeadquartersCountry6--0').click();
await page.getByText('Poland', { exact: true }).click();
  1. Method using the SelectOverlay element: This is a more precise solution. It restricts the text search to the SelectOverlay element, which is useful if the option text might appear in other places on the page as well.
page.getByTestId('Trigger::HeadquartersCountry6--0').click();
await page.getByTestId('SelectOverlay').getByText('Poland', { exact: true }).click();

Thanks for confirming.

For anyone who finds this in the future, just clicking the dropdown then clicking the option was very flaky for me for some reason. I did not try the SelectOverlay so that may work better.

I ended up writing a method that clicks the dropdown, empties any existing value, types the new value (to ensure dropdown option is available), then clicks outside of the dropdown to close it. Probably overkill but not flaky at all.

async selectDropdownOption(
        dropdownLocator: Locator, 
        optionText: string, 
        mainFrameContainer?: Locator
    ): Promise<void> {
        await dropdownLocator.click();
        await dropdownLocator.fill(''); // Clear any existing value
        await dropdownLocator.pressSequentially(optionText, { delay: 25 });
        await this.page.getByRole('listbox').getByRole('option', { name: optionText, exact: true }).click();

        
        // Click outside to close dropdown - use provided container or default to page
        const container = mainFrameContainer || this.page;
        await container.click('body');
        
        await expect(dropdownLocator).toHaveValue(optionText, { timeout: 10000 });
    }