Cypress testing a Material-UI datepicker is not working on Github actions
Asked Answered
S

6

28

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).

  1. 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="">

  2. Visually looks the date picker does not have any button (something is going on) :

enter image description here

  1. The console logs show no error or warning

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

Scharaga answered 1/11, 2021 at 15:45 Comment(7)
Can you add the line of code where it is failing ?Unapproachable
It looks like mui-lab uses date functions from external library. Which library is your project using? For example: moment, luxon, dayjs, jalaali, hijri, js-jodaAllsopp
@AlapanDas, not sure if this will help you. See updated questionScharaga
@Allsopp , it's date-fns -> mui.com/components/pickersScharaga
@Scharaga How about you add {force: true} with both type and clear and check how it goes - cy.getWidget(widgetId).find("input.MuiInputBase-input").clear({force: true}).type(date,{force: true})Unapproachable
could you add information about versions of the packages to try to replicate the issueWenger
can you post the code of the component ?Hellkite
M
14

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.

Metaphrase answered 11/11, 2021 at 12:22 Comment(2)
makes sense, we are going to investigate in this direction.Scharaga
And that is because of media queries wright ? now that's a thing to considerHellkite
F
7

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);
Foray answered 16/11, 2021 at 9:35 Comment(2)
Will this work with MUI DateTimePicker too?Hoebart
My cypress test can't find the DateTimePicker element I added the custom data-cy=my-datepicker attribute to.Hoebart
L
1

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.

Liborio answered 10/11, 2021 at 17:54 Comment(0)
P
0

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

Piggy answered 22/8, 2023 at 12:31 Comment(1)
this does not show how to configure cypress, + include link which may go down, not aligning with StackOverflow guidelines. (though the may be a right solution)Brinkley
F
-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)
  })
})
Frederik answered 16/8, 2022 at 20:6 Comment(0)
B
-1

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
            })
        },
    },
})
Brinkley answered 19/5 at 17:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.