DEV Community

Cover image for Avoid horizontal scroll with Cypress
Ismael Ramos
Ismael Ramos

Posted on • Updated on • Originally published at ismaelramos.dev

Avoid horizontal scroll with Cypress

Hi there!

The other day I was working styling some elements and when I opened my dev tools console and looked into the mobile view,
a horizontal scroll showed up. I thought, here we go again. So an idea came to my mind and I want to show you here, how I test with Cypress if there is an element out of the viewport.

In the first place, I want to leave the Github repo where you can find all the code that I'm going to explain now. I'm going to use Angular (v13) to create the sample application, but you can choose whatever you like most.

Let's get down to business:

1 — Create the horizontal scroll

If you have already a horizontal scroll caused by elements outside the viewport, skip this step.
If not, create an app, and add the element like this:

The app:

ng new cypress-element-out-of-viewport
Enter fullscreen mode Exit fullscreen mode

Create the element:

<!--- app.component.html --->
<div class="body__container--huge">
  THIS ONE IS OUT OF VIEWPORT
</div>
Enter fullscreen mode Exit fullscreen mode

Just increase its width by more than 100%.

/* app.component.css */
.body__container--huge {
  width: 200%;
}
Enter fullscreen mode Exit fullscreen mode

You will see something like this if you run the app:

npm start
Enter fullscreen mode Exit fullscreen mode

gif showing up an horizontal scroll

2 — Install Cypress

As I'm using Angular, I can run:

ng add @cypress/schematic
Enter fullscreen mode Exit fullscreen mode

If not, more info here on how to install it.
Once you've done it, we only need to modify our first test a little bit to catch this buggy element.

And all the magic below. In the support index file, we need to create a function that returns an array with all the elements. All the credits to Chris Coyier for this article and his marvellous function:

// cypress/support/index.ts
Cypress.on('window:before:load', (win: any) => {
  win.getElementsOutOfViewport = () => {
    var elements = [];
    var all = win.document.getElementsByTagName("*"), i = 0,
      rect, docWidth = win.document.documentElement.offsetWidth;
    for (; i < all.length; i++) {
      rect = all[i].getBoundingClientRect();
      if (rect.right > docWidth || rect.left < 0) {
        elements.push(all[i]);
      }
    }
    return elements;
  }
});
Enter fullscreen mode Exit fullscreen mode

We are also telling Cypress to store in the window object a function to calculate the elements that are outside the viewport, which name is 'getElementsOutOfViewport'. That way, we can call it in the spec file like this:

// cypress/integration/spec.ts
describe('My First Test', () => {
  it('Visits the initial project page', () => {
    cy.visit('/')
    cy.window().then((win: any) => {
        const outOfViewportElements = win.getElementsOutOfViewport();
        assert.equal(outOfViewportElements.length, 0);
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

Here we are asserting that no elements out of the viewport exist otherwise, the test will fail.

To execute the test, you can type:

npm run cypress:open
Enter fullscreen mode Exit fullscreen mode

The result is as follows:

capture of the test failing

3 — Test different viewport sizes

Until here, I've been keeping things simple, but this kind of issue can happen in one resolution but not in other. At least, I think we should test 3 different sizes. Cypress can modify the viewport size very easily.

First, we are going to create a css rule to have horizontal scroll only in mobile. Replace the previous code with this:

/* app.component.css */
@media (max-width: 576px) {
  .body__container--huge {
    width: 200%;
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, let's change the viewport size during the test execution:

// cypress/integration/spec.ts
describe('My First Test', () => {
  it('Visits the initial project page', () => {
    cy.visit('/')
    cy.viewport('iphone-6') // <----- new line!
    cy.window().then((win: any) => {
        const outOfViewportElements = win.getElementsOutOfViewport();
        assert.equal(outOfViewportElements.length, 0);
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

This will change the resolution to match an iPhone 6 device (375x667). The result is that our test is failing again:
capture of the test failing in mobile

Finally, I would like to point out a very useful inline javascript function (from here) which, allow us to show the elements while we are developing:

javascript:(function(d){var w=d.documentElement.offsetWidth,t=d.createTreeWalker(d.body,NodeFilter.SHOW_ELEMENT),b;while(t.nextNode()){b=t.currentNode.getBoundingClientRect();if(b.right>w||b.left<0){t.currentNode.style.setProperty('outline','1px dotted red','important');console.log(t.currentNode);}};}(document));
Enter fullscreen mode Exit fullscreen mode

Just run this in the console and voilà:

capture of the inline function and the elements outside

4 — Conclusion

With this kind of test I think we can forget about uploading to a production environment something wrong, but now that you are here, I want to know what do you think? Do you like this approach? Do you even test this kind of issue? Any suggestions?

Also, remember you have the repo with all the code here.

I hope this can help someone and, thanks for reading! If you've liked it, share it!

ismaelramos.dev

Discussion (0)