[自動化測試] Protractor element.click() 注意事項
前幾天 Protractor 的課程終於上完了,有學員就馬上在自己的網站上練習 E2E 的自動化測試。 寫了下方的程式碼1
2
3
4
5
6
7
8
9it('should click the link', async () => {
await browser.waitForAngularEnabled(false);
await browser.get('https://xxx.sample.com/#/');
const link = element(by.className('anticon-down'));
const linkIsEnabled = EC.elementToBeClickable(link);
// 點擊連結之前,先確認連結能被點擊!!!
await browser.wait(linkIsEnabled, 10000);
await link.click();
});
這程式碼看起來合情合理,結果在 link.click()
出現錯誤。 錯誤訊息大概是呈現這樣子。
Failed: unknown error: Element < i class="anticon-down">< / i > is not clickable at point (165, 720). Other element would receive the click: < div class="Loader__foreground" style="display: table; width: 100%; height: 100%; text-align: center; z-index: 20; color: white;">...< /div>
這段的意思是說,我要點的連結的時候,點到其他 DOM 物件了
說明
程式碼看起來很合理,人工操作上也都沒有問題,可是用 Protractor 測試的時候,竟然點到 其他元件,這是什麼情況??
首先要先知道, Protractor 會盡量模擬使用者的行為去操作,以 click 動作來說, Protractor 會模擬使用者操作滑鼠,移動到該座標,點下滑鼠左鍵。 其次,無法點擊有 三種可能
- 連結被 disabled
- 連結看不到
- 連結被其他 DOM 物件蓋住了
因為我們已經使用 ExpectedConditions.elementToBeClickable 確定連結是可以點的,就剩下一個可能,就是連結被其他DOM 物件蓋住了。
所以我合理的推測,網頁載入的時候,有一個看不見的 DOM 物件把畫面蓋住了,而且載入結束後,那個 DOM 物件就會消失。 且根據錯誤訊息,那個 DOM 物件用的 class 是 Loader__foreground
,為了驗證這件事情,我寫了以下程式碼
- ts
1 | it('should print out page source', async () => { |
Bingo ,發現一段神奇的 HTML。 果然有一個 DOM 元件是 z-index: 20 且寬高都 100%。 這表示說,網頁在讀取階段,這個透明的 DOM 元件把畫面都蓋住了。1
2
3
4
5
6
7
8
9< div class="Loader__background"
style="display: block; position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 10;">
< div class="Loader__foreground"
style="display: table; width: 100%; height: 100%; text-align: center; z-index: 20; color: white;">
< div class="Loader__message"
style="display: block; vertical-align: middle; position: fixed; top: 0px; right: 0px; color: rgb(255, 255, 255); background-color: rgb(14, 119, 202); height: 35px; line-height: 35px; font-size: 14px; padding: 0px 30px;">
loading ...< /div>
< /div>
< /div>
可以成功點擊連結的範例
所以我調整程式碼,確定讀取畫面消失後,才點擊連結。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20it('should click the link', async () => {
await browser.waitForAngularEnabled(false);
await browser.get('https://xxx.sample.com/#/');
const link = element(by.className('anticon-down'));
const loader = element(by.className('Loader__foreground'));
// 先等 loader 畫面有出來
await browser.wait(EC.presenceOf(loader), 10000);
// 等待 loader 消失,且連結可以被點擊
const loaderIsOff = EC.stalenessOf(loader);
const linkIsEnabled = EC.elementToBeClickable(link);
await browser.wait(EC.and(loaderIsOff, linkIsEnabled), 10000);
// 點擊連結
await link.click();
// 觀察結果
await browser.sleep(5000);
});
打完收工!!
小結
寫 E2E 真的會有很多鬼打強的情況,這時候,更要仔細閱讀錯誤訊息,因為錯誤訊息其實蠻清楚的,只是要思考一下出現這錯誤訊息的可能原因。
其他小密技(建議不要使用)
如果真的都沒辦法點,又找不到原因,只好使用注入 javascript 點擊 !?1
await browser.executeScript('document.querySelectorAll(\'.anticon-down\')[0].click()');
參考
[Random Element is not clickable at point (x, y) errors]
[WebDriver click() vs JavaScript click()]