読者です 読者をやめる 読者になる 読者になる
スポンサーリンク

UWSCでIEを自動操作し,回帰テスト/JavaScript実行/ファイル保存 などができるライブラリ

javascript UWSC IEの自動操作 ブラウザ

※これより新しいバージョンがリリースされています。
http://language-and-engineering.hatenablog.jp/entry/20090826/p1

下記の「IEを自動操作するライブラリ」の改良版ができた。

UWSCで,IEを自動操作するためのライブラリ (ファイルアップロードも自動化できる)
http://language-and-engineering.hatenablog.jp/entry/20090729/p1

IEで

  • ファイルのアップロード・ダウンロード (←selenium等の他ツールでは不可能
  • 簡易な回帰テスト
  • 表示中のWebページに対するJavaScript実行

を含むようなオートメーションが可能。


まず,新ライブラリを利用したコードを掲載する。

次に,ライブラリそのものを掲載する。

こんなコードが書ける

use_google.uws

call .\IEManipulation.uws


// IEを起動
_ie = IE.new()
IE.show( _ie )

// Googleを開く
IE.jump( _ie, "http://www.google.co.jp/" )

// 検索
IE.type( _ie, "q", "ゴッゴル" )
IE.click( _ie, "all" )
IE.click_and_wait( _ie, "btnG" )

// 関連キーワードを取得
first_result_link = _ie.document.getElementById( "res" ).getElementsByTagName("a").Item(0)
msgbox( first_result_link.innerText )
IE.click_and_wait( _ie, first_result_link )

click_and_waitなどの関数の引数には

  • "btnG"のようにDOM IDの文字列を渡す事もできるし,
  • first_result_link のように,要素オブジェクトをそのまま渡す事もできる。

※ポリモーフィズム/オーバーロードに近い事をやろうとしている。

コーディングの際に考える事は少なくて済む。両方とも同一のメソッドでよい。



2サンプル目,JavaScriptを利用するケース:

use_js.uws

call .\IEManipulation.uws


// IEを起動
_ie = IE.new()
IE.show( _ie )

// Googleを開く
IE.jump_with_js( _ie, "http://www.google.co.jp/" )

// JSコードをブラウザ側で実行
IE.export_js( _ie, "alert('hoge')" )

// JSコードの実行結果をUWSC側で取得
elem = IE.import_js( _ie, "document.body" )
msgbox( elem.innerHTML )


// 値をテストしてみる
IE.type( _ie, "q", "hoge" )
IE.assert_value( _ie, "q", "hoge" )
//IE.assert_value( _ie, "q", "hogee" ) // これはfailedとして実行停止になる

// 要素が存在するかテスト
IE.wait_for_element_present( _ie, "q", 5 )
IE.wait_for_element_present( _ie, "aaaaaa", 5 )

jumpのかわりにjump_with_jsでURLを開くと,そのページにはJavaScriptを注入できる。

  • export_jsで,ブラウザ側へJSコードを輸出してもよいし,
  • import_jsで,UWSC側へJSコードの実行結果を輸入することもできる。

UWSCを使って,Webアプリケーションのかんたんな回帰テストを行なう事も可能。

  • assert_valueで要素のvalueを検査
  • assert_textで要素のinnerTextを検査

成功したらそのまま動作が進む。こけたらそこでエラーメッセージを出し,実行を止める。


wait_for_element_presentは,いまどきのWebサイトを表示する際には必須。

  • setTimeoutとかで遅延して要素が読み込まれる場合とか
  • Ajaxで非同期にページの一部が書き換わる場合とか

などで,ある要素が現れるまでポーズしてくれる。ポーズの秒数を具体的に指定しなくて済む。(タイムアウト秒数は指定するが。)



3サンプル目,ファイルのダウンロード:

use_dl.uws

call .\IEManipulation.uws

_ie = IE.new()
IE.show( _ie )

IE.jump( _ie, "http://cakeforge.org/frs/download.php/730/cake_1.3-dev.zip/donation=false" )

// ファイルへのリンク
IE.click( _ie, _ie.document.getElementsByTagName("a").Item(2) )

// ダウンロードダイアログを処理
IE.save_downloaded_file( _ie )

msgbox("ダウンロード完了")

保存のダイアログに自動でEnterキーが押される。

デフォルトのダウンロードフォルダ(デスクトップとか)にファイルが保存される。


ライブラリ本体

ライブラリはclass化されている。

UWSCでのクラスはインスタンス化できないので,staticメソッドだけを持つようなclassだが,それでも

_ie = IE.new()

みたいな記法が可能になって,その分ついて行きやすい。



IEManipulation.uws

//
//  IEを自動操作するためのライブラリ ver 1.1
//


class IE

  // -------------------- 制御 --------------------


  // 新規IEオブジェクトを作成して返す
  function new()
    result = createOLEobj("InternetExplorer.Application")
  fend


  // 起動中のIEを見えるように
  procedure show( browser )
    browser.visible = True
    wid = hndtoid( browser.hwnd )
    acw( wid )
    pause( browser )
  fend


  // IEがビジー状態の間待ちます
  procedure wait( browser )
    repeat
      sleep( 0.1 )
    until ( ! browser.busy ) and ( browser.readystate = 4 )
    pause( browser )
  fend


  // URLにジャンプ
  procedure jump( browser, url )
    browser.navigate( url )
    wait( browser )
  fend


  // ポーズ
  procedure pause( browser )
    sleep( 0.2 )
  fend


  // 要素が出現するまで待ちます
  procedure wait_for_element_present( browser, dom_id, timeout_sec )
    interval_sec = 0.2
    total_wait_sec = 0
    loop_flag = True

    while loop_flag
    
      // 要素は現れたか
      ifb browser.document.getElementById( dom_id ) = Nothing then
        // 出現していないのでスリープ
        sleep( interval_sec )
        total_wait_sec = total_wait_sec + interval_sec
      else
        // 出現したのでループ終了
        loop_flag = false
      endif
    
      // タイムアウトか
      ifb total_wait_sec > timeout_sec then
        msgbox( "element '" + dom_id + "' did not appear." )
        exitexit
      endif
      
    wend
  
  fend

  // -------------------- DOM操作 --------------------


  // IDが渡された場合はDOM要素にして返します
  function to_elem( browser, locator )

    ifb VarType( locator ) = 8 then
      // 変数の型が文字列の場合はDOM IDとみなす
      result = gid( browser, locator )
    else
      // それ以外の場合はスルー
      result = locator
    endif

      // VarTypeのヘルプ:http://msdn.microsoft.com/ja-jp/library/cc392346.aspx
  fend


  // $
  function gid( browser, dom_id )
    result = browser.document.getElementById( dom_id )
  fend


  // 入力
  procedure type( browser, locator, str )
    elem = to_elem( browser, locator )
    elem.value = str
    pause( browser )
  fend


  // クリック
  procedure click( browser, locator )
    elem = to_elem( browser, locator )
    elem.click
    pause( browser )
  fend


  // クリックして待機
  procedure click_and_wait( browser, locator )
    click( browser, locator )
    wait( browser )
  fend


  // 文言ベースでセレクトボックスを選択
  procedure select_by_label( browser, locator, label )
    elem = to_elem( browser, locator )
    for i = 0 to elem.options.length - 1
      // 文言が一致するか
      ifb elem.options[ i ].innerText = label then
        elem.options[ i ].selected = True
      endif
    next
    pause( browser )
  fend


  // 値ベースでセレクトボックスを選択
  procedure select_by_value( browser, locator, val )
    elem = to_elem( browser, locator )
    for i = 0 to elem.options.length - 1
      // 値が一致するか
      ifb elem.options[ i ].Value = val then
        elem.options[ i ].selected = True
      endif
    next
    pause( browser )
  fend


  // indexベースでセレクトボックスを選択
  procedure select_by_index( browser, locator, index )
    elem = to_elem( browser, locator )
    elem.options[ index ].selected = True
    pause( browser )
  fend


  // ファイルアップロード
  // DOM IDではなくnameで要素を指定するので注意
  procedure file_upload( browser, post_name, file_path )
    IESetData( browser, file_path, post_name )
    pause( browser )
  fend


  // -------------------- JavaScriptの制御 --------------------


  // URLにジャンプし,WebページにJSコードを注入する
  procedure jump_with_js( browser, url )
    
    IE.jump( browser, url )
    IE.create_js_proxy( browser )
    
  fend


  // Webページ中にJS経由用のオブジェクトを生成して返す
  procedure create_js_proxy( browser )
    doc = browser.document
    
    TextBlock js_proxy_code
    
// UWSCからコード注入するためのオブジェクト
document._uwsc_proxy = {
  global : this,
  _window : window,
  eval_code : function( str ){
    try{
      return eval( str );
    }catch(e){
      return null;
    }
    
  }
};
    
    endTextBlock
    
    // 生成
    elem_s = doc.createElement("script")
    elem_s.text = js_proxy_code;
    elem_s.type = "text/javascript";
    
    // 注入
    doc.getElementsByTagName("head").Item(0).appendChild( elem_s );

  fend


  // 文字列をJSコードとしてブラウザ側で評価
  procedure export_js( browser, str_jscode )
    browser.document._uwsc_proxy.eval_code( str_jscode )
  fend


  // 文字列をJSコードとして評価した結果をUWSC側へ読み込み
  function import_js( browser, str_jscode )
    // いったん文字列をブラウザ側にexportし,その結果をUWSC側にimport
    result = browser.document._uwsc_proxy.eval_code( str_jscode )
  fend


  // -------------------- ファイルダウンロード用 --------------------


  // ダイアログが現れるまで待機
  procedure wait_for_dialog( dialog_title, timeout_sec )
    interval_sec = 0.2
    total_wait_sec = 0
    loop_flag = True

    while loop_flag
    
      // ダイアログは現れたか
      ifb getid( dialog_title, "#32770", -1 ) > -1 then
        loop_flag = false
      else
        sleep( interval_sec )
        total_wait_sec = total_wait_sec + interval_sec
      endif
    
      // タイムアウトか
      ifb total_wait_sec > timeout_sec then
        msgbox( "dialog '" + dialog_title + "' did not appear." )
        exitexit
      endif
      
    wend
      
  fend
  
  
  // ダイアログにキーを送信
  procedure send_dialog( dialog_title, key_code )
    // 出現を待つ
    wait_for_dialog( dialog_title, 10 )
    sleep(1)
    
    // キー押下
    id = getid( dialog_title, "#32770", -1 )
    sckey( id, key_code )
  fend
  
  
  // ファイルのダウンロードダイアログが出たときに,ダウンロード+保存を実行
  procedure save_downloaded_file( browser )
    sleep(2)
    IE.send_dialog( "ファイルのダウンロード", vk_s )
    IE.send_dialog( "名前を付けて保存", vk_return )
  fend


  // -------------------- テスト実行用 --------------------


  // 要素の値を検証
  procedure assert_value( browser, locator, val_expected )
  
    elem = to_elem( browser, locator )
    val_real = elem.value
    
    assert( val_expected, val_real )
  
  fend


  // 要素内の文字列を検証
  procedure assert_text( browser, locator, val_expected )
  
    elem = to_elem( browser, locator )
    val_real = elem.innerText
    
    assert( val_expected, val_real )
  
  fend


  // 文字列同士を比較
  procedure assert( val_expected, val_real )
  
    ifb val_expected = val_real then
      // OK
    else
      msgbox( "actual value '" + val_real + "' did not match '" + val_expected + "'" )
      
      // スクリプトを強制終了
      exitexit
    endif
  
  fend


endclass

補足

自動テストならまずはSeleniumだが,ファイルのアップロード/ダウンロードはできない。

Selenium IDEだとFirefox限定になってしまう。

UWSCなら,IEで自動化できる。

そういう経緯で,回帰テストのために採用。


関連する記事:

UWSCのマクロで,IEを起動して自動操作するサンプルコード
http://language-and-engineering.hatenablog.jp/entry/20140204/controlIeBrowser...


JScript / VBScript (WSH)で,IEを自動操作しよう
http://language-and-engineering.hatenablog.jp/entry/20090713/p1


IE AutoTester で,UIの回帰テストを完全自動化
http://language-and-engineering.hatenablog.jp/entry/20090922/p1