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
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.
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 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:
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.
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.
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 });
}