使用 Cypress 验证元素是否在视口内

问题描述 投票:0回答:5

Cypress 的

visible
匹配器根据多种因素将元素视为可见,但它没有考虑视口,因此滚动到屏幕外的元素仍被视为可见。

我需要测试页面锚点的链接是否正常工作。单击链接后,页面将滚动到具有链接 href 中定义的 id 的元素 (

example/#some-id
)。

如何验证元素是否在视口内?

javascript testing cypress end-to-end
5个回答
16
投票

我已经拼凑了以下命令,到目前为止这些命令似乎有效,但令人惊讶的是没有开箱即用的解决方案:

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;
})

3
投票

如果有人感兴趣的话,我对 Undistracted 的方法做了一些重构:

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;
});

如果您在致电之前使用过

window.innerWidth
,请使用
window.innerHeight
cy.viewport
。还使用
.within
来方便外部添加。


0
投票

这段代码对我有用

cy.get(element).eq(index).then(($el) => {
        const bottom = Cypress.$(cy.state('window')).height()
        console.log("element", $el[0])
        const rect = $el[0].getBoundingClientRect()
        const isInViewport = (
            rect.top >= -2 &&
            rect.left >= 0 &&
            rect.bottom <= bottom &&
            rect.right <= Cypress.$(cy.state('window')).width()
        )
        expect(isInViewport).to.be.true
    })

0
投票

迄今为止最优雅的方式

function isElementFullyVisible($el) {
    const rect = $el[0].getBoundingClientRect();
    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
}

// Usage
const $element = $('#yourElementId'); // Replace with your selector
console.log(isElementFullyVisible($element));

0
投票

如果元素不在视口约束内,所有其他解决方案都会立即失败。

我们进行了测试,在滚动到元素时进行这些视口断言,这需要使用片状

cy.wait()
命令。

因此有下面的解决方案:它们等待元素满足视口条件(就像通常的 Cypress 断言一样),并且只会在常规 cypress 超时后失败。这样就避免了任何

cy.wait()

我们通常在 cypress 自定义命令前加上“custom”前缀(以明确它是自定义 cypress 命令),但当然您可以根据需要更改函数名称。

// add to your cypress/support/commands.js file
// usage is as expected:
//
// use this to assert only the top of the element is within the viewport:
// cy.get('target-element').customTopIsWithinViewport();
//
// use this to assert that the entire element is within the viewport:
// cy.get('target-element').customIsEntirelyWithinViewport();

Cypress.Commands.add('customTopIsWithinViewport', { prevSubject: true }, (subject) => {
  return cy.wrap(subject).should(($el) => {
    const rect = $el[0].getBoundingClientRect();
    const windowInnerWidth = Cypress.config('viewportWidth');
    const windowInnerHeight = Cypress.config('viewportHeight');

    expect(rect.top, 'Element top').to.be.within(0, windowInnerHeight);
    expect(rect.right, 'Element right').to.be.within(0, windowInnerWidth);
    expect(rect.left, 'Element left').to.be.within(0, windowInnerWidth);
  });
});

Cypress.Commands.add('customIsEntirelyWithinViewport', { prevSubject: true }, subject => {
  
  return cy.wrap(subject).should(($el) => {
    const rect = $el[0].getBoundingClientRect();
    const windowInnerWidth = Cypress.config('viewportWidth');
    const windowInnerHeight = Cypress.config('viewportHeight');

    expect(rect.top, 'Element top').to.be.at.least(0);
    expect(rect.bottom, 'Element bottom').to.be.at.most(windowInnerHeight);
    expect(rect.left, 'Element left').to.be.at.least(0);
    expect(rect.right, 'Element right').to.be.at.most(windowInnerWidth);
  });
  
})
© www.soinside.com 2019 - 2024. All rights reserved.