1

Maybe it's an easy one, but my Cypress tests fail to test locked scrolling functionality.

So I have this React modal component which sets document.body.style.overflow = 'hidden' upon mount (client side only)

I have the following Cypress's test suite:

  it('page cant scroll when modal is open', () => {
    cy.get('[data-testid="button"]')
      .first()
      .click();

    cy.window().scrollTo('bottom');
    
    // scroll should be disabled
    cy.window()
      .its('scrollY')
      .should('equal', 0);

    // cy.get('[data-testid="modal"]')
    //   .scrollTo('bottom')
    //   .its('scrollY')
    //   .should('not.equal', 0);
  });

Error message: Timed out retrying after 4000ms: expected 802 to equal 0

Looks like Cypress is able to scroll the window even though this not possible when manually scrolling the page.

Any idea what the issue could be?

OP's edit: Scrolling the page programmatically still works (window.scrollTo()). The overflow property hides the scrollbar but this doesn't mean the scroll functionality is actually blocked (beyond a simple user interaction)

I was advised to use another approach for testing the scroll freeze through pagedown and pageup key navigation. Using the mentioned keys will be blocked on the actual page and user won't be able to scroll.

  it('page cant scroll when modal is open', () => {
    cy.get('[data-testid="button"]')
      .first()
      .click();

    cy.get('body').type('{pagedown}');

    cy.window()
      .its('scrollY')
      .should('equal', 0);

    cy.get('[data-testid="overlay"]').click();

    cy.get('body').type('{pagedown}');

    cy.window()
      .its('scrollY')
      .should('not.equal', 0);
  });

As with the previous code example, it makes sense for this snippet to throw error as well. Timed out retrying after 4000ms: expected 46 to equal 0.

Richard Matsen
  • 20,671
  • 3
  • 43
  • 77
pollx
  • 643
  • 9
  • 21
  • So when the modal opens users see a blocked scrolling behavior. Then why do you test the scrolling behavior? When you are writing your cypress tests you need to test what users see. – hurricane Mar 02 '21 at 16:04
  • @hurricane, because you test whether the logic you've implemented works correctly, no? – pollx Mar 02 '21 at 16:08

2 Answers2

2

It seems to me that testing that expect($el.document.body.style.overflow).to.eq('hidden') is not very useful because you already know that to be the case. You say

I have this React modal component which sets document.body.style.overflow = 'hidden' upon mount

Are you testing the React Modal component or how it interacts with the app?


Taking a look at the Material-UI Modal (Simple Modal demo), when I click on the "Open Modal" button it applies the same style to body

<body dir="ltr" style="padding-right: 16px; overflow: hidden;">

Visually the scrollbar has gone when the modal is open.

The best approach I found was to measure widths before and after opening the modal. The scrollbar is actually attached to the html element, so this is what I measured (relative to the window)

cy.visit('https://material-ui.com/components/modal/');

cy.window().then(win => {
  const htmlWidth = Cypress.$('html')[0].scrollWidth;
  const scrollBarWidth = win.innerWidth - htmlWidth;
  expect(scrollBarWidth).to.be.gt(0);                 // scrollbar is present
})

cy.contains('button', 'Open Modal').click();          // open the modal

cy.window().then(win => {
  const htmlWidth = Cypress.$('html')[0].scrollWidth;
  const scrollBarWidth = win.innerWidth - htmlWidth;
  expect(scrollBarWidth).to.be.eq(0);                 // scrollbar is absent
})
Richard Matsen
  • 20,671
  • 3
  • 43
  • 77
  • Thank you for your answer! A page width seems like quite a reasonable measurment to test in this case! – pollx Mar 04 '21 at 08:06
  • Hey, just to mention that the test won't work on local Mac due to the automatic scroll bar behaviour based on mouse or trackpad. In other words the scroll bar is shown only upon user interaction, Cypress can't detect its width. – pollx Mar 10 '21 at 15:28
  • You can probably overcome that by applying `.focus()` - "upon user interaction" is a bit vague. – Richard Matsen Mar 11 '21 at 07:19
1

You can disable a scroll behavior on a web page by setting the overflow property to hidden like so window.document.body.style.overflow = 'hidden'.

Assuming the React modal component sets document.body.style.overflow = 'hidden' as you mentioned previously.

You could assert that scrolling is not enabled with one-liner like so:

cy.window().then(($el) => expect($el.document.body.style.overflow).to.eq('hidden'));
Manuel Abascal
  • 5,616
  • 5
  • 35
  • 68
  • Thanks for your reply! Althought changing the overflow style from visible to hiden is what my Modal component does, it didn't corss my mind to test against this. I kept on insiting to prove the scroll y offset would equal 0. – pollx Mar 02 '21 at 15:44
  • Looks like `window.scrollTo()` works smoothly even if `overflow:hidden` is set. It wouldn't work though if both the html and body elements have styles updated to `{overflow:hidden, height: 100%}` which isn't most pleasant solution imo. – pollx Mar 02 '21 at 15:48
  • @pollx happy to help, does my solution works for you? It wasn't clear to me in your comment. I agree this is not the most pleasant solution. It doesn't come to mind another solution at the moment. Maybe you can abstract this `one-liner` in a custom command? So you don't litter your code base with it? I'll post another answer if I think another solution – Manuel Abascal Mar 02 '21 at 16:01
  • 1
    your solution works good for my case, thank you! – pollx Mar 02 '21 at 16:06
  • 1
    Awesome, so my next suggestion will be to add it as a custom command so you can abstract this assertion. Name it `cy.isWindowScrollable()` or something along those lines. If my solution helps you, I'll appreciate it if you accept & upvote my answer! – Manuel Abascal Mar 02 '21 at 16:08