#背景
クリックできる UI要素 A
があって、A
をクリックするとその場にポップアップメニューが開く、というUIは広く使われている。
ところが、「A
のクリックを拾ってメニューを出す」部分がオレオレロジックで実装されており、A
自体が表示された後、しばらく待ってから click
イベントを飛ばさないとメニューが開かない、ということがたまにある。ない? あったんや!
この場合、「しばらく」のタイミングをテキトーに決めうってしまうと、必ず他の環境でテストがこけるので、「メニューが開くまで A
のクリックを試行する」ようなテストコードを書きたい。要するに:
while (!exists('#menu')) {
click('#A');
sleep(100); // さすがに busy loop はあかん
}
のようなことがしたい。
#つらみ
Nightwatch.js では、「あるUI要素が存在するまで待つ」関数は提供されている: .waitForElementPresent()
ところが、この API は:
- 指定されたDOM要素がみつかれば、なら次のことをする
- 指定のタイムアウト時間までみつからなければ テストがこける
という仕様になっており、failure handler に相当するものをこちらで教えてあげることができない。大変つらい。ばーかばーか。
これをどうにかする方法を探し始めるとそれだけで半日ムダになるので、せめて1時間ぐらいのムダですむことを期待してここにメモを残す。
#方針
もう少し低レベルの API である .element()
を使って、存在を判定する。
#実装
module.exports = {
menu: function(client) {
function waitForMyMenu(andThen) {
// "#A" のクリックをトリガーに "#menu" が現れるまで、100ms 間隔で trial & error
client
.click('#A')
// "#menu" が見つかるか
.element('css selector', '#menu', function(e) {
if (e.status < 0) { // e.status < 0 なら見つからなかった
// 100ms まってリトライ
client.pause(100, function() { waitForMyMenu(andThen) })
} else {
// console.log(e); // かくにんよかった!
andThen();
}
});
}
waitForMyMenu(function() {
// ここに、本来やりたかった "#menu" に対するテスト
...
});
}
}
.element()
で存在を判定し、なければ少し (100ms) 待ってから、再帰でリトライしている。
当然だが、リトライ回数があまりにも多くなると、stack overflow する。てえるこおる除去は甘え。そこまでぐるぐる回っちゃったらテスト失敗でいいんじゃないかな! 5回ぐらいで打ち切りたい時は適当にカウンターを仕込もう。