WebdriverIOの click()
は、指定された要素に別の要素が重なってクリック出来ない場合、次のようなメッセージを出力してテストに失敗します。
Element is not clickable at point (x, x). Other element would receive the click: ..."
これは公式ドキュメントでも言及されており、回避方法が紹介されています。WebdriverIOの問題というより、Webdriverの仕様によるもののようです。
To work around this, try to find the overlaying element and remove it via execute command so it doesn't interfere the click. You also can try to scroll to the element yourself using scroll with an offset appropriate for your scenario.
かぶさってる要素を execute
で取り除くか、テストシナリオ内で明示的にスクロールして被らないようにしろ、ということですね。または、 browser.waitUntil()
でかぶさってる要素が消えるのを待つ方法もあります。
スクロールやwaitで解決するならそれでよさそうですが、 execute
で無理やり取り除くくらいなら、対象の要素のclickイベントを直接発火させてしまってもあんまり変わらないのでは……?
ということで、browser.execute()
でやってみたのがこちらです。
やることは単純で、XPathで要素を取得し、clickイベントを発火するスクリプトをブラウザ上で実行するだけです。
async function forceClick(locator) {
const elements = await $(locator)
const selector = elements.selector
browser.execute(function (el) {
(
function(expression, parentElement) {
var r = []
var x = document.evaluate(expression, parentElement ||
document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
for (var i = 0, l = x.snapshotLength; i < l; i++) {
r.push(x.snapshotItem(i))
}
return r
}
)(el)[0].click()
}, selector)
}
forceClick(locator)
こちらは完全に余談ですが、自分はCodeceptJSを使っているので、以下のようなカスタムヘルパを定義して使っています。
class ForceClick extends Helper {
async forceClick(locator) {
const helper = this.helpers['WebDriver']
const elements = await helper._locateClickable(locator)
const selector = elements[0].selector
helper.executeScript(function (el) {
(
function(expression, parentElement) {
var r = []
var x = document.evaluate(expression, parentElement ||
document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
for (var i = 0, l = x.snapshotLength; i < l; i++) {
r.push(x.snapshotItem(i))
}
return r
}
)(el)[0].click()
}, selector)
}
}
module.exports = ForceClick;