Selenium IDE スニペット


はじめに

SeleniumIDEのレコーディング機能による操作の自動記録は大変便利です。ここから 自動化の第一歩を踏み出す人は結構多いのではないかと思います。

使っていて、「ちょっとこんなことができないかな~」と思った時 SeleniumIDEはそもそもJavaScriptでできている というのを思い出してもらうと、解決のカギになるかもしれません。

タイムスタンプを取得する

たとえば、

  • スクリーンショットのファイル名をユニークにしたい
  • テストデータをユニークにしたい

といった場合、タイムスタンプが使えると便利です。

はじめの一歩

ちょっと調べてみると JavaScriptを使えばいいらしいとわかります

html
<tr>
  <td>storeEval</td>
  <td>var d = new Date(); d.getTime();</td>
  <td>timestamp</td>
</tr>
<tr>
    <td>echo</td>
    <td>${timestamp}</td>
    <td></td>
</tr>

できた! が、これっていつなんだ?フォーマット変換しよう

html
<tr>
  <td>storeEval</td>
  <td>
  var date = new Date();
  var yyyy = date.getFullYear();
  var mm = date.getMonth() + 1;
  var dd = date.getDate();
  var hh = date.getHours();
  var mi = date.getMinutes();
  var ss = date.getSeconds();
  storedVars.yyyymmddhhmin = yyyy + '' + mm + '' + dd + '-' + hh + '' + mi + '' + ss;
  </td>
  <td>timestamp</td>
</tr>
<tr>
    <td>echo</td>
    <td>${timestamp}</td>
    <td></td>
</tr>

ガタガタになっているから前にゼロを入れて桁を揃えよう

html
<tr>
  <td>storeEval</td>
  <td>
  var date = new Date();
  var yyyy = date.getFullYear();
  var mm = ("0" + (date.getMonth() + 1)).slice(-2);
  var dd = ("0" + date.getDate()).slice(-2);
  var hh = ("0" + date.getHours()).slice(-2);
  var mi = ("0" + date.getMinutes()).slice(-2);
  var ss = ("0" + date.getSeconds()).slice(-2);
  storedVars.yyyymmddhhmin = yyyy + '' + mm + '' + dd + '-' + hh + '' + mi + '' + ss;
  </td>
  <td>timestamp</td>
</tr>
<tr>
    <td>echo</td>
    <td>${timestamp}</td>
    <td></td>
</tr>

しかし長いな短くならない?

html
<tr>
  <td>storeEval</td>
  <td>
 function fm(n){
   return ("0" + n).slice(-2);
  }
   var date = new Date();
   var yyyy = date.getFullYear();
   var mm = fm(date.getMonth() + 1);
   var dd = fm(date.getDate());
   var hh = fm(date.getHours());
   var mi = fm(date.getMinutes());
   var ss = fm(date.getSeconds());
   storedVars.yyyymmddhhmin = yyyy + '' + mm + '' + dd + '-' + hh + '' + mi +'' + ss;
  </td>
  <td>timestamp</td>
</tr>
<tr>
    <td>echo</td>
    <td>${timestamp}</td>
    <td></td>
</tr>

途中の変数もいらないんじゃ?

html
<tr>
  <td>storeEval</td>
  <td>
 function fm(n){
   return ("0" + n).slice(-2);
  }
   var date = new Date();
   date.getFullYear()+ '' + fm(date.getMonth() + 1)+ ''+ fm(date.getDate())+ '-' + fm(date.getHours())+ ''+ fm(date.getMinutes()) 
  </td>
  <td>timestamp</td>
</tr>
<tr>
    <td>echo</td>
    <td>${timestamp}</td>
    <td></td>
</tr>

タイムスタンプだから使いたい時に取得しないと値が変わらないよね?
でも、毎回これ書くのは大変だから関数で定義しておこう

html
<tr>
  <td>getEval</td>
  <td>
 function fm(n){
   return ("0" + n).slice(-2);
  }
  function get_timestamp(){
   var date = new Date();
   return date.getFullYear()+ '' + fm(date.getMonth() + 1)+ ''+ fm(date.getDate())+ '-' + fm(date.getHours())+ ''+ fm(date.getMinutes())+''+fm(dt.getSeconds())
} 
  </td>
  <td></td>
</tr>
<tr>
    <td>echo</td>
    <td>javascript{get_timestamp()}</td>
    <td></td>
</tr>

いやいやそこじゃ呼び出せないから

html
<tr>
  <td>getEval</td>
  <td>
  storedVars.get_timestamp = function (){
   function fm(n){
     return ("0" + n).slice(-2);
    }
    var dt = new Date();
    return dt.getFullYear()+ '' + fm(dt.getMonth() + 1)+ ''+ fm(dt.getDate())+ '-' + fm(dt.getHours())+ ''+ fm(dt.getMinutes())+''+fm(dt.getSeconds());
   } 
  </td>
  <td></td>
</tr>
<tr>
    <td>echo</td>
    <td>javascript{storedVars.get_timestamp()}</td>
    <td></td>
</tr>

できたけど、短くなった気がしないね・・・

実は

フォーマットを気にしないのであればこんな方法も

html
<tr>
 <td>storeEval</td>
 <td>var td = new Log;td._formatDate(new Date());</td>
 <td>timestamp</td>
</tr>
<tr>
  <td>echo</td>
  <td>${timestamp}</td>
  <td></td>
</tr>

もう、newすらいらない方法も

html
<tr>
    <td>storeEval</td>
    <td>FileUtils.getTimeStamp()</td>
    <td>timestamp</td>
</tr>
<tr>
    <td>echo</td>
    <td>${timestamp}</td>
    <td></td>
</tr>

値の反映について

storeEval を使った場合 最後の式の評価値が 第3パラメータにセットされるので 式内で storedVarsにセットする必要はないです

html
<tr>
 <td>storeEval</td>
 <td>
     var td = new Log;
     td._formatDate(new Date());
  </td>
 <td>timestamp</td>
</tr>

getEvalを使う場合は式内でstoredVarsにセットする必要があります

html
<tr>
 <td>getEval</td>
 <td>
    var td = new Log;
    storedVars.timestamp = td._formatDate(new Date());
  </td>
 <td></td>
</tr>

StoreValueで取得した値を使って待つ

最近はページ遷移をしないで画面の内容が変わるのも増えてきました。
ページ内のJavaScriptで処理をしている訳ですが、処理が終わる前に操作を行うわけにはいかないのでタイミングをとる必要があります。

そんな時に使えるコマンドが WaitForXXXX です。

たとえば、事前に取得した値と比較して操作をチェックします

html
<tr>
    <td>storeValue</td>
    <td>//*[@class=&quot;in_text&quot;]</td>
    <td>compval</td>
</tr>
<tr>
    <td>click</td>
    <td>//input[@id=&quot;addbtn&quot;]</td>
    <td></td>
</tr>
<tr>
    <td>waitForValue</td>
    <td>//tr[2]//input[@class=&quot;in_detail&quot;]</td>
    <td>${compval}</td>
</tr>

htmlソース内のコメントを取得する

xPath式を使ってコメントを取得しています。

html
<td>getEval</td>
<td>
    var doc =this.browserbot.getCurrentWindow().document;
    var xpath="//comment()"; 
    var nodes = doc.evaluate(xpath, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    alert(nodes.snapshotItem(0).textContent); 
</td>
<td></td>

画面内の複数の要素を取得したい場合は、直接evaluate関数を呼び出せばすべてを取得することができます。
findElement関数だと、画面上の最初に見つかった要素のみが返され、複数の要素を取得することができません。

ローカルストレージに保存

取得した値を永続化してみる

html
<tr>
  <td>getEval</td>
  <td>
    var win = selenium.page().getCurrentWindow();
    storedVars.lsg ={
    set : function(key, obj){
        win.localStorage.setItem(key, JSON.stringify(obj));
    },

    get : function(key){
        return JSON.parse(win.localStorage.getItem(key));
    }
    };
  </td>
  <td></td>
</tr>
<tr>
  <td>getEval</td>
  <td>
    storedVars.lsg.set("price","19800");
  </td>
  <td></td>
</tr>
<tr>
  <td>getEval</td>
  <td>
    alert(storedVars.lsg.get("price"));
  </td>
  <td></td>
</tr>

テスト対象ページ側のローカルストレージに保存しているのに注意が必要です。

Ajaxを使う

SeleniumIDEから webAPIを呼び出してブラウザの表示内容と比較すると行ったテストケースだと
開発側に ブラウザから別途 APIを呼び出すフロントエンドを作ってもらい
実画面と、フロントエンドを経由して取得したAPIの結果を比較するという方法もあります。

が、webAPIなのであれば直接呼び出して値を取得する方法もある訳です。

html
<tr>
 <td>getEval</td>
 <td>
     storedVars.ajax= function(url){
     var req = new Ajax.Request(
          url, {
               method : 'GET',
               asynchronous: false
          } 
     );
     return req.transport.responseText;
     }
 </td>
 <td></td>
</tr>
<tr>
 <td>getEval</td>
 <td>
     var URL='http://xxxxxxxxxx';
     var results = storedVars.ajax(URL);
 </td>
 <td></td>
</tr>

IDEから使う場合 非同期処理にしてしまうとコントロールできなくなると思われるので同期処理にしています。

テキスト状態のHTMLSRCをDOMオブジェクトに変換

html
<tr>
 <td>getEval</td>
 <td>
storedVars.FUNC.createHTMLDocument=function(src) {
  var doctype = document.implementation.createDocumentType('html','-//W3C//DTD HTML 4.01//EN','http://www.w3.org/TR/html4/strict.dtd');
  var doc = document.implementation.createDocument(null, 'html', doctype);
  var range = document.createRange();
  range.selectNodeContents(document.documentElement);
  var content = doc.adoptNode(range.createContextualFragment(src));
  doc.documentElement.appendChild(content);
  return doc;
 </td>
 <td></td>
</tr>

テキスト形式で取得したHTMLソースをDOMに変換しています。どういうシーンで必要だったのか思い出せないので、メモ代わりに載せておきます。

おわりに

過去にどこかで使ったものを備忘録的に載せましたが、どれもこれも使いどころを考える必要があるばかりになってしまいました。。。
特に JavaScriptを使っているところは FireFoxのバージョンアップに伴い動かなくなる(なっている)可能性もあるのでご注意ください。