Cypress 踩坑記

2023-05-17 06:00:49

Cypress 是一個非常流行的測試工具,然而實際使用過程中發現一些問題,這裡做些記錄。

問題發現

Cypressclick 是非常常用的指令,然而在一些特殊場景下 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 遮擋計算還需要優化。

原因排查

排查原始碼可以發現 Cypressclick 會經過一些判定:

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
}

注意其中的 xy,可以認為就是中心點的座標。

然後 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;
};

可以發現這裡直接使用 xy 去獲取元素,然後和當前目標元素去做了對比。

這也就是為什麼 click 有時候可以點,有時候不可以的原因了,簡單說就是中心點被遮了就可以點,沒被遮就不可以點,還真是簡單粗暴