WinAppDriverでテスト自動化：操作別テストコード

WinAppDriverを使って、WindowsアプリのUIテストを自動化する。
環境構築手順や各操作の記述方法を以下にまとめる。

## 環境
* Windows 10（開発者モード有効）
* Visual Studio 2019

## インストール
1. WinAppDriverのインストーラをダウンロードする。  
   https://github.com/microsoft/WinAppDriver/releases
2. WinAppDriverをインストールする。
3. Windows 10の開発者モードを有効にする。
4. WinAppDriver.exeを実行する。  
   C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe

## テストコード作成
1. Visual Studioで単体テストプロジェクトを作成する。
2. Nugetパッケージマネージャーで「Appium.WebDriver」をインストールする。
3. テストコードを作成する。  
※注意: Appiumのバージョンによって、書き方が異なる。ネット上のサンプルプログラムの多くは3系である。Nugetパッケージマネージャーでインストールされるのは4系であるので、4系の書き方をすること。

## テスト自動化
作成したテストコード
https://github.com/ysk-hello/WinAppDriverSample
### Setup
```csharp:3系
var appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app", @"C:\Ysk\WinAppDriverTest\WinAppDriverTest\bin\Debug\WinAppDriverTest.exe");
_session = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), appCapabilities);
```
```csharp:4系
var options = new AppiumOptions();
options.AddAdditionalCapability("app", @"C:\Ysk\WinAppDriverTest\WinAppDriverTest\bin\Debug\WinAppDriverTest.exe");
_session = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), options);
```

### 操作別
#### 左クリック
panelを左クリックすると、背景色が黄色に変わる。
![click.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64683/6c200fe7-0a0c-26cc-37aa-9ec48161deb7.gif)

```csharp:4系
var panel = _session.FindElementByAccessibilityId("panel1");
var actions = new Actions(_session);
actions.Click(panel);       // panel1をクリック
actions.Perform();      // 実行するために必要
```
#### ダブルクリック
```csharp:4系
var panel = _session.FindElementByAccessibilityId("panel1");
var actions = new Actions(_session);
actions.DoubleClick(panel);     // panel1をダブルクリック
actions.Perform();
```
#### 右クリック
panelを右クリックしてコンテキストメニューを表示する。
![contextmenu.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64683/5998980f-b5b0-2dce-0db8-ef20a4882879.gif)

```csharp:4系
var panel = _session.FindElementByAccessibilityId("panel1");
var actions = new Actions(_session);
actions.ContextClick(panel);    // panel1を右クリック
actions.Perform();

actions.Click(_session.FindElementByName("Clear"));
actions.Perform();      // コンテキストメニューのClearをクリック
```
#### マウス操作
panel上をクリックしながらマウスカーソルを移動する。
![mouse.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64683/6d588e63-b0f9-e445-281d-75c4c2738af3.gif)

```csharp:4系
var panel = _session.FindElementByAccessibilityId("panel2");

for (int i = 0; i < 10; i++)
{
    var actions = new Actions(_session);
    actions.MoveToElement(panel, i * 10, i * 10);       // マウスカーソルを移動
    actions.Click();        // クリック
    actions.Perform();

    Thread.Sleep(3000);
}
```
#### キー操作
textBoxに文字列を入力する。
![key.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64683/86ba6b55-4c58-5f35-4985-99effbfdb9d1.gif)

```csharp:4系
var textBox = _session.FindElementByAccessibilityId("textBox1");

var actions = new Actions(_session);
actions.MoveToElement(textBox);     // textBox1にマウスカーソルを移動
actions.SendKeys("test");       // キーボードで"test"を入力
actions.Perform();

Thread.Sleep(3000);
```
#### スクリーンショット
起動した画面、デスクトップ全体のスクリーンショットをとる。

```csharp:4系
var form = _session.FindElementByAccessibilityId("Form1");

// 起動するまで待つ
Thread.Sleep(1000);

var shot = form.GetScreenshot();
shot.SaveAsFile("form.png", ScreenshotImageFormat.Png);     // スクリーンショットを保存

var options = new AppiumOptions();
options.AddAdditionalCapability("app", "Root");
var deskSession = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), options);

shot = deskSession.GetScreenshot();
shot.SaveAsFile("desktop.png", ScreenshotImageFormat.Png);     // デスクトップ全体のスクリーンショットを保存
```

### その他
#### 起動済みのアプリにアタッチ
起動済みの画面にアタッチする。

```csharp:4系
var options = new AppiumOptions();
options.AddAdditionalCapability("app", "Root");
var deskSession = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), options);     // デスクトップセッション

var window = deskSession.FindElementByName("Form1");
var windowHandle = window.GetAttribute("NativeWindowHandle");
windowHandle = (int.Parse(windowHandle)).ToString("x"); // Convert to Hex

var winOptions = new AppiumOptions();
winOptions.AddAdditionalCapability("appTopLevelWindow", windowHandle);
var session = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), winOptions);      // Form1のセッション

var panel = session.FindElementByAccessibilityId("panel1");     // panel1

var actions = new Actions(session);
actions.Click(panel);       // panel1をクリック
actions.Perform();

Thread.Sleep(3000);
```
#### 希望の操作を自動化するために
* キー操作、マウス操作を使って実現できないか検討する。  
例: ウィンドウの最小化: Windowsキー + ↓キー (Keys.Meta + Keys.Down)