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://dev.tobesoft.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 {
            // base64エンコードをデコードしてバイナリに変換
            const img_bin = Buffer.from(img_b64, "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に収められています。