初めまして、ワッキーです。
私たちは、Seleniumを利用してInternet Exploer(以下、IE)での自動テストや自動処理を行っていましたが、つい先日までSeleniumで動いていたWebサービスが突然動作しないようになってしまいました。
PCの設定変更やIE用Seleniumドライバのバージョン変更を行いましたが、全く同じ設定、同じドライバでも正常に動作完了するPC、動作完了しないPCがあり、安定した環境を構築することができなくなりました。
IEでしか動作しないWebサービスをSeleniumの自動化処理を使ってChrome上で動かす
IEについて調べてみると、既にWindowsの標準ブラウザだったIEはEdgeに置き換わり、IEは2022年6月16日にサポートが完全終了予定となっていました。
ブラウザのテストであればIEで動かすことに意味がありますが、日頃の業務などを自動化したような単なる自動化処理の場合であれば、IE以外のブラウザで動けば目的を達成できますそこでブラウザの主流であるChromeで自動化処理を行おうと考えました。
世の中には色々な理由でシステムを置き換えることができず、未だにIEでしか動作しないWebサービスが存在しています。
この記事は、そんなIEでしか動作しないWebサービスをChromeで動かしてみようという試みです。
そもそもIEでしか動作しないWebサービスがなぜ存在するのか?
一般的なWebサービスはIE以外のブラウザにも対応しているものが殆どで、最近ではIEに非対応なWebサービスも増えてきている状況です。
IEのみ対応とうたっているWebサービスがIE以外で動作を保障しない理由はいくつかあります。
デザイン崩れの問題
一般的にはデザイン崩れの問題が一番に挙げられるかと思います。IEはCSSやHTMLの規格が他のブラウザに比べて遅れているため、互換のある書き方をしないとデザイン崩れが起きてしまいます。 単なるデザイン崩れだけであれば我慢して使えなくもないのですが、ボタンが隠れて押せなかったり、確認すべき事項が見えなくなるようでは実際の運用はできません。
JavaScriptの問題
次にIE上では問題なく動くJavaScriptが他のブラウザではセキュリティ上の理由で動作が許可されておらず、そのようなスクリプトを使ってWebサービスが構築されている場合です。この場合はWebサービスのスクリプトを組み直さない限り、他のブラウザで動かすことはできないためIEでしか動作しないサービスとなってしまいます。
IEでしか動作しないWebサービスをSelenium+Chromeで動かす
必要なもの
-Eclipse for Java
-Java Runtime Environment
-Google Chrome
-ChromeDriver
ドライバーのプロパティとパスを変更する
まずはSeleniumをChromeで動かすためのドライバを変更します。
//IEのWebドライバのパスをセット
System.setProperty("webdriver.ie.driver", "./driver/IEDriverServer.exe");
webDriver = new InternetExplorerDriver();
↓以下のようにパスを変更
// ChromeのWebドライバのパスをセット
System.setProperty("webdriver.chrome.driver", "./driver/chromedriver.exe");
webDriver = new ChromeDriver();
Chromeドライバーでは動作しないonclickを動作させる
onclickとは?
HTMLのonclick属性は、ある要素がユーザーにクリックされた時にクリックイベントとして、onclick属性に指定されたJavaScriptなどの関数を実行するものです。
Javascriptのonclick属性はIE環境では普通に動作しますが、Chrome環境では動作しません。例えば、onclick属性を使ったボタンはIE環境では普通にクリックできますが、Chrome環境ではonclick属性のイベントが無視されてしまうため、ボタンをクリックしても何も起きません。
Chrome環境でonclick属性のスクリプトを動作させるためには、WebDriverのclick関数を呼ぶのではなく、JavascriptExecutor経由でclick関数を呼び出します。
//IE環境で動作するonclickイベント(例:ログインボタン)
<input type="image" name="ImgBtnAuth" src="../aaaaa.jpg" onclick="Login();" language="javascript">
//Chromeで動作させるためにSeleniumスクリプトを変更します。
//IEの場合:
WebElement onclickButton = wait.until(ExpectedConditions.visibilityOfElementLocated(
By.id("onclickButton")));
click(webDriver, onclickButton);
↓
//Chromeの場合:
JavascriptExecutor js = (JavascriptExecutor) webDriver;
WebElement onclickButton = wait.until(ExpectedConditions.visibilityOfElementLocated(
By.id("onclickButton")));
js.executeScript("arguments[0].click()", onclickButton);
JavascriptExecutorを使うことによって、元々IE環境でしか動作しないonclick属性のイベントをChromeでも動かせるようになりました。
aタグで実装されたアコーディオンメニューが開かない
アコーディオンメニューはクリックするたびに開いたり閉じたりするコントロールですが、今回対応したアコーディオンメニューはクリックイベントではなく、href属性にスクリプトが書かれておりクリックイベントでスクリプトが実行されるわけではありませんでした。<a href=javascript>という書き方はIE以外では動作しないため、当然ながらChrome上では動作しませんでした。また、クリックイベントではないので、前述のJavascriptExecutor経由でクリックイベントを呼び出しても、アコーディオンメニューは開閉しませんでした。そこで、アコーディオンメニューのhref属性で行っているstyleのdisplay属性変更をJavascriptExecutorのsetAttributeメソッドを使って同様に処理することにしました。
アコーディオンメニューボタンのコード例
<a href="JavaScript:slideMenu('Menu1');">
<img id="プルダウンリスト1" title="タイトル1" src="../.../Menu1Gazou.gif" border="0">
</a>
function slideMenu(strName){
if (document.getElementById(strName) != null){
if (document.getElementById(strName).style.display == 'none'){
document.getElementById(strName).style.display = "block";
}else{
document.getElementById(strName).style.display = "none";
}
}
}
スクリプトの処理を見ると単純にstyle属性のdisplayプロパティを書き換えているだけです。
JavascriptExecutorのsetAttribute関数を使用して、直接スタイル属性のDisplayプロパティを書き換えてみます。
JavascriptExecutorのsetAttribute関数でスタイルのDisplayプロパティをnoneからblockに変更する
//IEの場合
WebElement onclickButton = wait.until(ExpectedConditions.visibilityOfElementLocated(
By.id("example-div-menuList")));
click(webDriver, onclickButton);
↓
//Chromeの場合
JavascriptExecutor js = (JavascriptExecutor) webDriver;
WebElement onclickbutton = wait.until(ExpectedConditions.visibilityOfElementLocated(
By.id("example-div-menuList")));
js.executeScript("arguments[0].setAttribute('style', 'DISPLAY: block;')", onclickbutton);
JavascriptExecutorのsetAttribute関数を使用することで、アコーディオンメニューを上手く開くことができました。
JavascriptExecutorのsetAttribute関数は、DISPLAYプロパティだけではなく指定した要素に新しい属性を追加したり、既に存在する属性の値を変更したりすることができます。
※setAttribute関数に指定された属性が既に存在している場合、その属性の値が更新されますが、もし存在しなければ属性と値は新規に追加されます。
厄介なwindow.showModalDialog
ChromeやFireFox、Edgeなどのブラウザでは、セキュリティを理由とした仕様により、
window.showModalDialog関数を使用してモーダルダイアログを開くことができません。
※window.showModalDialogは Chrome バージョン43 および Firefox バージョン56 で廃止されました。
本来は改修してマルチブラウザに対応してもらうのが望ましいのですが、色々な理由があってのIEでしか動作しないWebサービス。
今回はSeleniumでwindow.open関数を使用してwindow.showModalDialog関数に近いダイアログの動きを実現してみました。
window.showModalDialog関数とwindow.open関数の違い
- window.openの親・子画面でのデータの受け渡し方法がwindow.showModalDialogと異なっている
- 子画面はwindow.showModalDilaog(uri[,arguments][,options])のargumentsで渡されたパラメータをwindow.dialogArgumentsで受け取ります
- 親画面はreturnVal = window.showModalDilaog()という形で子画面からの結果を受け取ります
- window.openではwindow.dialogArgumentsで受け取れる方法でパラメータを渡せないため、パラメータが不要、もしくはURLパラメータで子画面にパラメータを渡す場合のみ代用が可能です
- window.openではvar window = window.open()という形で受け取れるのはWindowProxyオブジェクトであり子画面が返す結果を受け取ることはできません
window.showModalDialogによる子画面呼び出しのコード例
<input type="submit" value="showModalDialogボタン" onclick="showModalDialog()">
function showModalDialog() {
...
window.showModalDialog("showModalDialog_example.htm", "")
...
}
殆どのブラウザでwindow.showModalDialogは既に廃止されており、クリックしても何の反応もありません。
廃止されたwindow.showModalDialogを無理矢理”復活”させるために、再びJavascriptExecutorの力を借りることにします。
//JavascriptExecutorを使用して、window.showModalDialog関数をwindow.open関数に置き換える
JavascriptExecutor js = (JavascriptExecutor) webDriver;
js.executeScript("window.showModalDialog = window.open");
window.showModalDialogの処理に入る前にJavascriptExecutorでwindow.openに置き換えれば、
Chromeでもwindow.openが呼び出されダイアログを開くことができます。
しかし、前述の通りwindow.openとwindow.showModalDialogのデータの受け渡し方法が異なっているため、引数でのデータ受け渡しが前提のダイアログや、ダイアログの返値を受けて処理が分岐するような処理の場合、window.openを使用しても親・子画面間のデータのやり取ができずにうまく動作しません。
子画面をwindow.showModalDialogを引数のパラメータ付きで呼び出すコード例
<script language="JavaScript">
function openWindow() {
var myArguments.param1 = new Object();
myArguments.param1 = document.all.exampleText.value;
window.showModalDialog("https://www.example.com", myArguements, '');
}
</script>
...
<select id="exampleText">
<option value="a">a</option>
<option value="aa">aa</option>
<option value="aaa">aaa</option>
<option value="aaaa">aaaa</option>
</select>
<button onclick="openWindow();">Open window</button>
...
親画面はwindow.showModalDialog関数の引数argumentsにパラメータをセットして子画面に値を渡します。
呼び出された子画面はwindow.dialogArgumentsプロパティで親画面から渡されたパラメータを取得します。
子画面がwindow.dialogArgumentsプロパティでパラメータを取得するコード例
<script language="JavaScript">
var myArguements = window.dialogArguments;
var aExampleText = myArguements.param1;
</script>
一方でwindow.open関数は引数にargumentsが存在しません。
window.open関数の構文
var window = window.open(url, windowName, [windowFeatures]);
window.open関数で子画面を呼び出す際のコード例
window.open("http://www.example.com/", "example_WindowName", "resizable,scrollbars,status");
この様な違いがあるため、window.showModalDialog関数をパラメータ付きで呼び出さなければならない場合、window.open関数はwindow.showModalDialog関数の代わりになることができません。
パラメータが不要、もしくはURLパラメータ渡しで子画面が呼び出される場合のみ、
window.open関数をwindow.showModalDialog関数の代わりとして使用することが可能となります。
window.open関数にもパラメータを渡す手段は用意されています。
window.open関数を使って親画面から子画面にデータを渡す場合は、window.openerプロパティを使用します。
以上がSeleniumを使ってIEでしか動作しないWebサービスをChromeで動かそうとした際に遭遇した問題点と解決策となります。
やり方としては少々面倒ではありますが、IEでしか動作しないWebサービスがChromeで動くと便利な場面があるのも事実です。
もし同じようにIEでしか動作しないWebサービスをChromeで動かす必要がある方は、ぜひ試してみてください。