Table of content
Hi there!
Last week 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 to 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:
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
Create the element:
<!--- app.component.html --->
<div class="body__container--huge">
THIS ONE IS OUT OF VIEWPORT
</div>
Just increase its width by more than 100%.
/* app.component.css */
.body__container--huge {
width: 200%;
}
You will see something like this if you run the app:
npm start
Install Cypress
As I'm using Angular, I can run:
ng add @cypress/schematic
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;
}
});
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);
})
})
})
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
The result is as follows:
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%;
}
}
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);
})
})
})
This will change the resolution to match an iPhone 6 device (375x667). The result is that our test is failing again:
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));
Just run this in the console and voilà:
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 you have learned something new. If you think this might help other people, please hit the like button so that others can read it. ❤️
If you have any thoughts or questions, feel free to leave a comment!
Top comments (0)