Cypress's visible
matcher treats an element as visible based on a variety of factors, however it doesn't take the viewport into account, so an element that is scrolled off-screen is still treated as visible.
I need to test that a link to an on-page anchor is functioning correctly. Once the link is clicked, the page scrolls to the element with the id as defined in the href of the link (example/#some-id
).
How can verify that the element is within the viewport?
I've cobbled together the following commands which appear to work so far, but amazed there isn't on out-of-box solution:
Cypress.Commands.add('topIsWithinViewport', { prevSubject: true }, subject => {
const windowInnerWidth = Cypress.config(`viewportWidth`);
const bounding = subject[0].getBoundingClientRect();
const rightBoundOfWindow = windowInnerWidth;
expect(bounding.top).to.be.at.least(0);
expect(bounding.left).to.be.at.least(0);
expect(bounding.right).to.be.lessThan(rightBoundOfWindow);
return subject;
})
Cypress.Commands.add('isWithinViewport', { prevSubject: true }, subject => {
const windowInnerWidth = Cypress.config(`viewportWidth`);
const windowInnerHeight = Cypress.config(`viewportHeight`);
const bounding = subject[0].getBoundingClientRect();
const rightBoundOfWindow = windowInnerWidth;
const bottomBoundOfWindow = windowInnerHeight;
expect(bounding.top).to.be.at.least(0);
expect(bounding.left).to.be.at.least(0);
expect(bounding.right).to.be.lessThan(rightBoundOfWindow);
expect(bounding.bottom).to.be.lessThan(bottomBoundOfWindow);
return subject;
})
I did a little refactoring on Undistracted's approach if anyone is interested:
Cypress.Commands.add('isWithinViewport', { prevSubject: true }, (subject) => {
const rect = subject[0].getBoundingClientRect();
expect(rect.top).to.be.within(0, window.innerHeight);
expect(rect.right).to.be.within(0, window.innerWidth);
expect(rect.bottom).to.be.within(0, window.innerHeight);
expect(rect.left).to.be.within(0, window.innerWidth);
return subject;
});
Cypress.Commands.add('isOutsideViewport', { prevSubject: true }, (subject) => {
const rect = subject[0].getBoundingClientRect();
expect(rect.top).not.to.be.within(0, window.innerHeight);
expect(rect.right).not.to.be.within(0, window.innerWidth);
expect(rect.bottom).not.to.be.within(0, window.innerHeight);
expect(rect.left).not.to.be.within(0, window.innerWidth);
return subject;
});
Uses window.innerWidth
and window.innerHeight
in case you have used cy.viewport
before calling. Also uses .within
to facilitate the outside addition.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With