Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verify Element Is Within Viewport With Cypress

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?

like image 701
Undistraction Avatar asked Feb 04 '23 16:02

Undistraction


2 Answers

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;
})
like image 119
Undistraction Avatar answered Feb 06 '23 06:02

Undistraction


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.

like image 45
Andrew Leedham Avatar answered Feb 06 '23 05:02

Andrew Leedham