I have the component below that loads a list of countries from an API. It uses the list to populate the options under a select HTML element.
import { CountryDto } from '@/lib/models';
import { CountryService } from '@/services';
import { Select } from 'flowbite-react';
import React, { ChangeEvent, ChangeEventHandler, useEffect, useMemo, useState } from 'react';
type CountrySelectorProps = {
value?: string;
onChange?: ChangeEventHandler<HTMLSelectElement>;
id?: string;
name?: string;
isRequired?: boolean;
isDisabled?: boolean;
textAsValue?: boolean;
selectedCountry?: CountryDto;
"data-testid"?: string
};
const CountrySelector = ({
value,
onChange,
id,
name,
isRequired,
isDisabled,
textAsValue,
selectedCountry,
"data-testid": dataTestId
}: CountrySelectorProps) => {
const [countries, setCountries] = useState<CountryDto[]>([]);
const [selectedValue, setSelectedValue] = useState<string>(value || '');
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
setIsLoading(true);
CountryService.getCountries().then(result => {
if (result.success) {
setCountries(result.data || []);
}
setIsLoading(false);
}).catch(() => {
setIsLoading(false);
});
}, []);
useEffect(() => {
setSelectedValue(value || (selectedCountry ? (textAsValue ? selectedCountry.name : selectedCountry.iso2Code) : '') || '');
}, [value, selectedCountry, textAsValue]);
const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
const selectedCountryValue = event.target.value;
setSelectedValue(selectedCountryValue);
onChange && onChange(event);
};
return (
<Select
id={id}
name={name}
value={selectedValue}
onChange={handleChange}
required={isRequired}
disabled={isDisabled || isLoading}
data-testid={dataTestId}
>
{isLoading ? (
<option value="">Loading...</option>
) : (
<>
<option value="">{selectedCountry ? selectedCountry.name : 'Select a country'}</option>
{countries.map(country => (
<option
key={country.iso2Code}
value={textAsValue ? country.name as string : country.iso2Code as string}
>
{country.name}
</option>
))}
</>
)}
</Select>
);
};
export default CountrySelector;
The component works as expected on the UI. One of the rendered options is
<option value="France">France</option>
The parent component uses the CountrySelector as below
<CountrySelector
value={companyBillingDetails.companyBillingCountry}
data-testid="companyBillingCountry"
onChange={event =>
setCompanyBillingDetails({
...companyBillingDetails,
companyBillingCountry: event.target.value,
})
}
isRequired
textAsValue
isDisabled={isSubmitting}
/>
My test is as follows:
test('should handle new client billing details', async ({ page }) => {
// Fill out billing details
// Wait for country selector to load and be interactable
await page.waitForSelector('[data-testid="companyBillingCountry"]:not([disabled])', { timeout: 15000 });
// Select country
await page.selectOption('[data-testid="companyBillingCountry"]', 'France');
// Submit the form
const submitButton = await page.getByTestId('submit-job-button');
await submitButton.click();
// Check for success toast
const successToast = await page.getByText('Job submitted successfully');
expect(successToast).toBeDefined();
});
But I got the below error after running it
Error: page.selectOption: Test timeout of 30000ms exceeded.
Call log:
- waiting for locator('[data-testid="companyBillingCountry"]')
- locator resolved to <select required="" data-testid="companyBillingCountry" class="block w-full border disabled:cursor-not-allowed disabled:opacity-50 border-gray-300 bg-gray-50 text-gray-900 focus:border-cyan-500 focus:ring-cyan-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-cyan-500 dark:focus:ring-cyan-500 p-2.5 text-sm rounded-lg">…</select>
- attempting select option action
2 × waiting for element to be visible and enabled
- did not find some options
- retrying select option action
- waiting 20ms
2 × waiting for element to be visible and enabled
- did not find some options
- retrying select option action
- waiting 100ms
55 × waiting for element to be visible and enabled
- did not find some options
- retrying select option action
- waiting 500ms
133 |
134 | // Select country
> 135 | await page.selectOption('[data-testid="companyBillingCountry"]', 'France');
| ^
136 |
I can't seem to find why the test wouldn't select the France
option
You don't need to use waitForSelector
because of auto-waiting https://playwright.dev/docs/actionability
Try to find with label, text and with { force: true }
to bypass auto-wait.
// for value
await page.getByTestId("companyBillingCountry").selectOption("France");
// for label
await page.getByTestId("companyBillingCountry").selectOption({ label: "France" });
And maybe printing innerHTML()
of that locator would help.