Cypress
是一个非常流行的测试工具,然而实际使用过程中发现一些问题,这里做些记录。
问题发现
在 Cypress
下 click
是非常常用的指令,然而在一些特殊场景下 click
并不能如想象中那般正常工作。
比如现在有一个弹窗,我们需要测试在点击遮罩层时是否可以正常关闭弹窗。
测试代码比较简单:
/// <reference types="cypress" />
context('Actions', () => {
beforeEach(() => {
cy.visit('http://localhost:3300/Modal');
});
it('Override', () => {
cy.get('.mantine-Button-root').click();
cy.get('.mantine-Modal-root').should('exist');
cy.get('.mantine-Modal-overlay').click();
});
});
然后执行 Cypress
,发现一切如想象中那般简单,很顺利就通过了。
然而当往 Model
中填充了一些内容后,却发现突然这里就报错了。
当然,报错是没问题,遮罩层确实被内容遮挡了。问题是刚刚明明也是一样被遮挡,为何就不报错,只是因为内容多了一点就报错,这就很不合适了。
查看文档会发现 click
还支持坐标或位置参数。
然而,并没有什么用,也就是说这个点击位置无关,应该是和 Cypress
判断元素遮挡有关系,看起来 Cypress
遮挡计算还需要优化。
原因排查
排查源码可以发现 Cypress
的 click
会经过一些判定:
if (force !== true) {
// now that we know our element isn't animating its time
// to figure out if it's being covered by another element.
// this calculation is relative from the viewport so we
// only care about fromElViewport coords
$elAtCoords =
options.ensure.notCovered && ensureElIsNotCovered(cy, win, $el, coords.fromElViewport, options, _log, onScroll);
Cypress.ensure.isNotHiddenByAncestors($el, name, _log);
}
其中比较重要的参数是 coords.fromElViewport
,其数值长这样:
{
"top": 0,
"left": 0,
"right": 1000,
"bottom": 660,
"topCenter": 330,
"leftCenter": 500,
"x": 500,
"y": 330
}
注意其中的 x
和 y
,可以认为就是中心点的坐标。
然后 Cypress
会使用该坐标获取该位置最顶层的元素:
const getElementAtPointFromViewport = function (fromElViewport) {
// get the element at point from the viewport based
// on the desired x/y normalized coordinations
let elAtCoords;
elAtCoords = $dom.getElementAtPointFromViewport(win.document, fromElViewport.x, fromElViewport.y);
if (elAtCoords) {
$elAtCoords = $dom.wrap(elAtCoords);
return $elAtCoords;
}
return null;
};
const ensureDescendents = function (fromElViewport) {
// figure out the deepest element we are about to interact
// with at these coordinates
$elAtCoords = getElementAtPointFromViewport(fromElViewport);
debug('elAtCoords', $elAtCoords);
debug('el has pointer-events none?');
ensureElDoesNotHaveCSS($el, 'pointer-events', 'none', name, log);
debug('is descendent of elAtCoords?');
ensureIsDescendent($el, $elAtCoords, name, log);
return $elAtCoords;
};
可以发现这里直接使用 x
和 y
去获取元素,然后和当前目标元素去做了对比。
这也就是为什么 click
有时候可以点,有时候不可以的原因了,简单说就是中心点被遮了就可以点,没被遮就不可以点,还真是简单粗暴