Appiumを用いたモバイルネイティブアプリVRT(Visual Regression Testing)環境を構築する

これまで、iOSネイティブアプリのE2Eテスト実現のために、Appium+TypeScript環境を構築してきました。
最後に、これまで構築してきた環境にVRT環境を追加します。

webdriverioにのサービスに`wdio-image-comparison-service`という画像回帰テストのサービス（プラグイン的な立ち位置）があります。しかし、こちらのサービスはWebブラウザ向けであり、モバイルネイティブアプリでは動作しません。

そこで、筆者は[wdio-native-app-compare](https://github.com/wswebcreation/wdio-native-app-compare)を導入し、モバイルネイティブアプリ上でのVRT環境を構築しました。

# 前提
これまで構築してきたAppium+TypeScript環境についてはこちらの記事をご参考ください。

https://qiita.com/5enxia/items/3e84b3eaaf54fd8e5f00

# 環境構築

## VRT(Visual Regression Testing)の導入

### 1. `wdio-native-app-compare`のインストール
モバイルネィティブアプリ向けの画像比較ツールである[wdio-native-app-compare](https://github.com/wswebcreation/wdio-native-app-compare)をインストールします。

```sh
npm i wdio-native-app-compare-service
```

### 2. `wdio.conf.ts`の更新
`wdio.conf.ts`内の配列プロパティ`services`に以下を追加します。
```ts
[
            'native-app-compare',
            // The options
            {
                // Mandatory
                baselineFolder: join(process.cwd(), './baseline/'),
                screenshotPath: join(process.cwd(), '.tmp/'),
                // Optional
                // See Options for more options
                //..
                // Some options, see the docs for more
                imageNameFormat: '{tag}-{deviceName}-{platformName}-{platformVersion}',
                savePerInstance: true,
                autoSaveBaseline: true,
                blockOutStatusBar: true,
                blockOutToolBar: true,
                blockOutNavigationBar: true,
                isHybridApp: true,
            },
        ] 
```

# テストシナリオの更新
テストシナリオを以下のコードに書き換えます。
メソッドの詳細については、[こちら](https://github.com/wswebcreation/wdio-native-app-compare/blob/master/docs/OPTIONS.md)をご参考ください。

```ts
describe('Hello World!', () => {
    it('should display Hello, World!', async () => {
        const el = await $("~textField")
        
        const word = "Hello, World!"
        await el.setValue(word)

        await driver.saveScreen('page')
        await driver.saveElement(el, 'el')
    });

    it('should no diff Element!', async () => {
        const el = await $("~textField")
        const result = await driver.compareElement(el, 'el')
        expect(result.misMatchPercentage).toEqual(0)
    });

    it('should no diff Page!', async () => {
        const result = await driver.compareScreen('page')
        expect(result.misMatchPercentage).toEqual(0)
    });
});

```

# テストの実行
```sh
npx wdio run text/wdio.conf.ts
```

以上です。

# 追加編：`wdio-native-app-compare`のメソッドに型を追加する
`wdio-native-app-compare`packageには型定義がないため、コーディングの際に補完が働きません。そこで追加編では、`wdio-native-app-compare`のメソッドに型情報を追加します。

## 1. `tsconfig.json`の更新
`tsconfig.json`に型定義ファイルのパスを追加します。

`compilerOptions`に配列型の`typeRoots`プロパティを追加し、配列の第0要素に`"./src/@types"`をおきます。

```json
{
    "compilerOptions": {
        ...
        "typeRoots": [
            "./src/@types"
        ]
    }
}
```

2. 型定義ファイル`wdio.d.ts`の作成

`tsconfig.json`に設定したパスを作成します。
作成するパスは、`tsconfig.json`が存在するディレクトリから見た相対パスになります。

```sh
mkdir -p ./src/@types
```

次に型定義ファイルを作成します。

```sh
cd ./src/@types
touch wdio.d.ts
```

3. `wdio.d.ts`の編集

[wdio-native-app-compareのドキュメント](https://github.com/wswebcreation/wdio-native-app-compare/blob/master/docs/OPTIONS.md)を参考にinterfaceを定義します。

```ts
// Ref: https://github.com/wswebcreation/wdio-native-app-compare
// Ref: https://github.com/wswebcreation/wdio-native-app-compare/issues/18 

declare module 'wdio-native-app-compare-service';

/**
 * The save methods return an object like this:
 */
interface SaveResult {
    fileName: string
    folders: {
        actual: string
    }
}

/**
 * The compare methods return an object like this:
 */
interface CompareResult {
    fileName: string
    folders: {
        actual: string
        baseline: string,
        diff: string
    }
    /**
     * @param {number} 0.00-100.00(%)
     */
    misMatchPercentage: number
}

declare namespace WebdriverIO {
    interface Browser {
        /**
         * With this method you can create a screenshot of an element in the view. You'll need to provide the following options
         * @param {WebdriverIO.Element} elemnt Provide the element, so NOT the selector, like for example
         * @param {string} tag The tag is the part that is used for determining the image name when it is saved
         * @param {object?} options You can provide an object with https://github.com/wswebcreation/wdio-native-app-compare/blob/master/docs/OPTIONS.md#method-options, see options for details
         * 
         * Provide the element, so NOT the selector, like for example
         * @example
         * browser.saveElement(browser.element('\~your-accessibility-id'), 'name-of-your-file');
         * @example
         * browser.saveElement($('\~your-accessibility-id'), 'name-of-your-file');
         */
        saveElement: (elemnt: WebdriverIO.Element, tag: string, options?: object) => SaveResult

        /**
         * With this method you can create a screenshot of the view. You'll need to provide the following options
         * @param {string} tag The tag is the part that is used for determining the image name when it is saved
         * @param {object?} options You can provide an object with https://github.com/wswebcreation/wdio-native-app-compare/blob/master/docs/OPTIONS.md#method-options, see options for details
         * 
         * @example
         * browser.saveScreen('name-of-your-file');
         */
        saveScreen: (tag: string) => SaveResult

        /**
         * With this method you can compare a screenshot of an element in the view with a baseline image. It will return a mismatch percentage between the actual element screenshot and the baseline. You'll need to provide the following options
         * @param {WebdriverIO.Element} elemnt Provide the element, so NOT the selector, like for example
         * @param {string} tag The tag is the part that is used for determining the image name when it is saved
         * @param {object?} options You can provide an object with https://github.com/wswebcreation/wdio-native-app-compare/blob/master/docs/OPTIONS.md#method-options, see options for details
         * 
         * @example
         * browser.compareElement(browser.element('\~your-accessibility-id'), 'name-of-your-file');
         * @example
         * browser.compareElement($('\~your-accessibility-id'), 'name-of-your-file');
         */
        compareElement: (elemnt: WebdriverIO.Element, tag: string) => CompareResult

        /**
         * With this method you can create a screenshot of the view. You'll need to provide the following options
         * @param {string} tag The tag is the part that is used for determining the image name when it is saved
         * @param {object?} options You can provide an object with https://github.com/wswebcreation/wdio-native-app-compare/blob/master/docs/OPTIONS.md#method-options, see options for details
         * 
         * @example
         * browser.compareScreen('name-of-your-file');
         */
        compareScreen: (tag: string) => CompareResult
    }
}

```

以上です。


# 参考文献
- [TypeScript](https://webdriver.io/docs/typescript/)
- [wdio-native-app-compare\+typescript](https://github.com/wswebcreation/wdio-native-app-compare/issues/18)
- [wdio-image-comparison-service](https://webdriver.io/docs/wdio-image-comparison-service/)
- [webdriverio](https://gitter.im/webdriverio/webdriverio)
- [wdio-native-app-compare](https://github.com/wswebcreation/wdio-native-app-compare)