6. WebDriver APIを直接使用するテストシナリオ

このサンプルは、デモアプリケーションをテストする完全なテストシナリオ例です。
テストシナリオの動作は以下のようになります。

初期画面

アプリケーションの最初のページです。
このページで、「トップメニュー」をクリックして表示されるalertで、alertの操作例としています。

「問い合わせ」をクリックして次のアコーディオンメニューの展開へ進みます。

アコーディオンメニューの展開

アコーディオンメニューのような動的にページ内の状態が変化するケースで、表示の完了を待つ例としています。
また、スクリーンキャプチャを使用する例も含まれています。

「ユーザ登録」をクリックして次のページへ遷移します。

ユーザ登録画面

このページは、キー入力とボタン操作を中心とした例です。
特に、GHOST Operatorの特徴でもあるIMEを使用した日本語入力を行っています。

各項目の入力後「送信する」ボタンのクリックで次のページへ遷移します。

ユーザ登録結果画面

このページの表示でテストシナリオは終了します。

このテストシナリオは一連の操作を実行し、最後のユーザ登録結果画面まで到達できるかを確認しています。
仮に、途中で意図しない動作・挙動があれば要所に挿入されているチェックコードにより検出されるか、WebDriverAPIの呼び出しが失敗して例外がスローされるかで、テストの失敗を検出する仕組みです。

全体の処理方法を明確にするために、Seleniumプロジェクトで提供されるWebDriverライブラリを直接使用しているため、比較的簡易なページのテストですが記述量は多くなっています。

const crypto = require('crypto'); const { Builder, Capabilities, Key, until, By } = require("selenium-webdriver"); const fs = require('fs'); const path = require('path'); (async function doTest() { let driver = null; try { console.log("capabilitiesの設定"); const capabilities = new Capabilities() .set("scriptFilter",["\\.html"]) .setBrowserName("safari+G.O"); console.log("WebDriverへ接続"); driver = new Builder() .usingServer("http://192.168.1.147:7000/wd/hub") .withCapabilities(capabilities) .build(); console.log("テスト対象アプリのindex.htmlをロード"); await driver.get("http://dev01.nexaweb.co.jp/GHOST_Operator/demo/index.html"); // 「トップメニュー」のクリックによるalert表示 console.log("「トップメニュー」の表示を待つ"); const titleElm = await driver.wait(until.elementLocated(By.xpath("/html/body/div[1]/header/div/h1")), 3000); console.log("「トップメニュー」をクリックする"); await titleElm.click(); console.log("Alertを待機"); await driver.wait(until.alertIsPresent(), 1000); const alert = await driver.switchTo().alert(); console.log("Alertを閉じる"); await alert.dismiss(); // JavaScriptによる値の取得 console.log("appData変数の値を取得"); const appData = await driver.executeScript('return appData;'); if(appData !== "hello") { throw new Error("appData is not true. appData=" + appData); } // アコーディオンメニューの選択 console.log("「問合せボタン」を検索"); const contactElm = await driver.findElement(By.xpath("/html/body/ul/li[4]/div")); console.log("「問合せボタン」の表示をクリック"); await contactElm.click(); console.log("「ユーザ登録」の表示を待つ"); const userregElm = await driver.wait(until.elementLocated(By.id("userreg", 3000))); await driver.wait(until.elementIsVisible(userregElm),3000); console.log("「ユーザ登録」の表示OK"); // スクリーンキャプチャによるメニュー表示テスト console.log("スクリーンキャプチャのために「ユーザ登録」の安定を待つ"); await driver.sleep(700); console.log("スクリーンキャプチャによるメニュー表示テスト"); const img_b64 = await driver.takeScreenshot(); const hashHex = crypto.createHash('sha256').update(img_b64, 'utf8').digest('hex'); if(hashHex !== "a4cc7834d3f1df879ee9361eb73943735a05792b6a1ed72671692e83ad074bd8") { try { const img_bin = Buffer.from(img_b64, "base64"); // base64エンコードをデコードしてバイナリに変換 const dirName = "images"; const fileName = "snapshot"; const fn = path.normalize(path.format({ dir: dirName, name: fileName, ext: ".png" })); const fnDir = path.dirname(fn); if (!fs.existsSync(fnDir)) fs.mkdirSync(fnDir, {recursive: true}); fs.writeFileSync(fn, img_bin); // 画像をファイルに保存 console.log("キャプチャイメージを保存しました"); } catch(ex) { console.log(ex); } throw new Error("screenshot compare error hash=" + hashHex); } console.log("「ユーザ登録」をクリック"); await userregElm.click(); console.log("ページ遷移を待つ"); await driver.wait(until.urlMatches(/^http.*\/demo\/reg\.html$/),1000); console.log("「お名前」入力欄の表示を待つ"); const nameElm = await driver.wait(until.elementLocated(By.id("name1", 3000))); console.log("「お名前」入力欄をクリック"); await nameElm.click(); await driver.sleep(100); console.log("「お名前」の入力"); await nameElm.sendKeys("[HanjaMode On]yamada",Key.SPACE,Key.RETURN," "); await nameElm.sendKeys("tarou",Key.SPACE,Key.RETURN,"[HanjaMode Off]"); await nameElm.sendKeys(Key.TAB); console.log("「メールアドレス」の入力"); await nameElm.sendKeys("Tarou.Yamada@nexaweb.com",Key.COMMAND,"ac"); await nameElm.sendKeys(Key.TAB); console.log("「メールアドレス(確認用)」の入力"); await nameElm.sendKeys(Key.COMMAND, "v"); await nameElm.sendKeys(Key.TAB); console.log("「電話番号」の入力"); await nameElm.sendKeys("080-4321-8765"); console.log("「性別」を検索"); const genderElms = await driver.findElements(By.name("gender")); console.log("「男性」を選択"); await genderElms[0].click(); console.log("「職業」を検索"); const jobElm = await driver.findElement(By.id("job")); console.log("「職業」をクリック"); await jobElm.click(); console.log("「職業」のセレクタ表示を待つ"); await driver.sleep(500); console.log("「Webデザイナ」を選択"); await driver.actions({bridge: true}) .move({origin: jobElm, x:0, y:-270}) .click() .perform(); console.log("「あなたが学びたいこと」を検索"); const qElms = await driver.findElements(By.name("q1")); console.log("「デザインについて」を選択"); await qElms[0].click(); console.log("「マーケティングについて」を選択"); await qElms[2].click(); console.log("「送信する」を検索"); const buttonElm = await driver.findElement(By.xpath("/html/body/div[2]/form/div/button")); console.log("「送信する」をクリック"); await buttonElm.click(); console.log("ページ遷移を待つ"); await driver.wait(until.urlMatches(/^http.*\/demo\/result\.html\?.*$/),3000); console.log("ページ遷移完了"); console.log("テスト正常終了"); } catch (e) { console.log(e); } finally { if(driver !== null) await driver.quit(); } })();

※ このサンプルの関連ファイルはSeleniumSamples.zipのsec6_webDriverAPIに収められています。