Reputation: 7680
We have a strange behavior when running our cypress tests in a github action. MUI datepicker the datepicker is in readonly mode and we can't enter any date (it's fine in other environments).
Error in Cypress
CypressError: Timed out retrying after 4000ms: cy.clear()
failed because this element is readonly:
<input aria-invalid="false" readonly="" type="text" aria-readonly="true" aria-label="Choose date" class="MuiOutlinedInput-input MuiInputBase-input css-1x5jdmq" value="">
Visually looks the date picker does not have any button (something is going on) :
On other environments, windows/linux, the tests work fine, even though we launch the test in headless mode (all desktops with a UI). The MUI datepicker looks as nice as in MUI documentation (link).
Github action looks like :
on:
workflow_dispatch:
defaults:
run:
working-directory: ic3-test
jobs:
build:
runs-on: ubuntu-latest
container: cypress/included:8.6.0
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install dependencies
run: npm install
working-directory: ic3-test
- name: Cypress run with env
uses: cypress-io/github-action@v2
with:
# headless: true
browser: chrome
record: true
working-directory: ic3-test
The Cypress line that generates the error :
cy.getWidget(widgetId). // this is getting a div with wid = widgetId , works fine
.find("input.MuiInputBase-input")
.clear()
.type(date). // date is a string
Some hints are welcomed
Upvotes: 28
Views: 8180
Reputation: 17879
As of Cypress 13 (2024), you can:
Edit cypress.config.ts
to add:
export default defineConfig({
...
e2e: {
baseUrl: 'http://....',
setupNodeEvents(on) {
on('before:browser:launch', (browser, launchOptions) => {
if (browser.family === 'chromium') {
// running headless chrome in a virtualized environment forces pointer type to default to `NONE`
// to mimic "desktop" environment more correctly we force blink to have `pointer: fine` support
// this allows correct pickers behavior.
// This impact the used DateTimePicker in Material UI (MUI) between DesktopDateTimePicker and MobileDateTimePicker
launchOptions.args.push(
'--blink-settings=primaryPointerType=4'
)
}
return launchOptions
})
},
},
})
Upvotes: -1
Reputation: 19
Add '--blink-settings=primaryPointerType=4'
as a Chrome launch argument. This sets the pointer type to fine
and now the correct DatePicker will be displayed on the UI. Found the solution here
Upvotes: 1
Reputation: 1
Cypress.Commands.add('setDateField', ({ date, label }) => {
//open the datepicker dialog if is mobile
cy.contains(label).siblings('div').click()
cy.get('body').then(($body) => {
if ($body.find('[role=dialog]').length) {
cy.get('[data-testid=PenIcon]').click()
cy.get('[role=dialog]').contains(label).type(date)
cy.get('[role=dialog]').contains('Confirmar').click()
return
}
cy.contains(label).siblings('div').type(date)
})
})
Upvotes: -1
Reputation: 1165
The reason behind this is that Material UI renders the MobileDatePicker
component, since the query @media (pointer: fine)
doesn't match on the headless Chrome used by our Github Actions Workflow. The mobile component only has readonly inputs, therefore it can't be cleared or typed into with .type()
and .clear()
(as opposed to the DesktopDatePicker
component, which has typable and clearable inputs).
Since we don't want to remove the MobileDatePicker
component, we've created a custom command so it checks if the mobile date picker is currently being rendered. If on mobile, the test opens the date picker, clicks on the edit button, so the mobile input view is opened.
And in that input view, the input field is not readonly
anymore. Using that command, no matter if desktop or mobile, the input field can be cleared and typed into.
Cypress.Commands.add(
'chooseDatePicker',
(selector: string, value: string) => {
cy.get('body')
.then(($body) => {
const mobilePickerSelector = `${selector} input[readonly]`;
const isMobile = $body.find(mobilePickerSelector).length > 0;
if (isMobile) {
// The MobileDatePicker component has readonly inputs and needs to
// be opened and clicked on edit so its inputs can be edited
cy.get(mobilePickerSelector)
.click();
cy.get('[role="dialog"] [aria-label="calendar view is open, go to text input view"]')
.click();
cy.get(`[role="dialog"] ${selector}`)
.find('input')
.clear()
.type(value);
cy.contains('[role="dialog"] button', 'OK')
.click();
} else {
cy.get(selector)
.find('input')
.clear()
.type(value);
}
});
},
);
// Usage:
const datePickerValue = '2021-01-03';
cy.chooseDatePicker('[data-testid="my-datepicker"]', datePickerValue);
Upvotes: 8
Reputation: 715
We had the same error when running cypress test in azure devops pipelines. And I think its the same reason, looking at your screenshot of the date picker without any button.
The input which was giving us the error was @mui/lab/DatePicker
.
We found that this component is rendering as @mui/lab/MobileDatePicker
when we ran the cypress tests in azure devops pipelines. It's explained here: docs.
That version not accept direct text input, but opens a dialog to pick/input date, therefore cypress test fails when trying to type into the input.
Our solution was to use @mui/lab/DesktopDatePicker
directly.
Upvotes: 15
Reputation: 4539
Since I haven't seen all the code, I will try to comment as specific as I can, I would like you to review few topics.
1- Examine the properties in the transformed code. Make sure a Property such as helperText={null} is should not set.
2- You might need to install polyfills. For instance for the popper.js (transitive dependency). Although MUI claims that it is (polly) not needed. Technology is advancing every day and there may be changes that they cannot catch.
3- Be sure working right mode. Even test it with Jest. Such window.matchMedia.
Upvotes: 1