きなこもち.net

.NET Framework × UiPath,Orchestrator × Azure × AWS × Angularなどの忘備録

Angular Material × Selenium × DatePickerのカレンダーを操作するサンプル

目的

SeleniumのSample.
Angular MaterialのDatePickerで日付を選択する。   値を直接Inputできない場合を想定。

背景

DatePickerをSeleniumで操作しようとしたとき、テキストボックスに日付が直接入力できれば問題ないが、システムによっては、直接入力ができないケースがある。そういった場合、カレンダーを使って日付を選択する必要がある。
同じ月の日付の選択であればまだしも、別の月、年の日付を選択しようとした場合、カレンダーUIによって操作性が異なる。今回は、汎用的なカレンダー操作をできるようにするためのサンプルを目指して作成した。

本題

コード

public class AngularMaterialDatePicker : IAngularMaterialDatePicker
{
    public void SetDatePicker(DateTime targetDateTime)
    {
        using (var driver = new ChromeDriver())
        {
            driver.Navigate().GoToUrl("https://material.angular.io/components/datepicker/overview");
            var targetPath = "//*[@id=\"datepicker-overview\"]/div/div[2]/datepicker-overview-example/mat-form-field/div/div[1]/div[2]/mat-datepicker-toggle/button";
            var picker = driver.FindElement(By.XPath(targetPath));
            picker.Click();
            var delta = (targetDateTime - new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day)).Days;
            if (delta > 0) 
                this.SendArrowRightKey(driver, delta);
            else if (delta < 0) 
                this.SendArrowLeftKey(driver, Math.Abs(delta));
            driver.SwitchTo().ActiveElement().Click();
            driver.Close();
        }
    }

    public void SendArrowLeftKey(ChromeDriver driver, int delta)
    {
        for (int i = 0; i < delta; i++)
        {
            driver.SwitchTo().ActiveElement().SendArrowLeftKey();
        }
    }

    public void SendArrowRightKey(ChromeDriver driver, int delta)
    {
        for (int i = 0; i < delta; i++)
        {
            driver.SwitchTo().ActiveElement().SendArrowRightKey();
        }
    }
}

Step1

Angular MateriaのDatePickerのページにアクセスする。

driver.Navigate().GoToUrl("https://material.angular.io/components/datepicker/overview");

Step2

DatePickerのカレンダーボタンをクリックする。この時、IDが付与されていなかったため、XPathを使ってエレメントを特定した。

var picker = driver.FindElement(By.XPath(targetPath));
picker.Click();

Step3

現在の日付から目的の日付の差を計算する。(targetDateTime - new DateTime.Now).Days;とすると、今日の日付が計算されないため、日付までの情報をつかって新しいDatetimeインスタンスを生成した。

var delta = (targetDateTime - new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day)).Days;

Step4

計算した差の回数だけ矢印ボタンを押下して日付を移動させる。この時、カレンダーの月が変わると、HTMLが変更される。そのため、ページが変わったタイミングでエレメントの再検索が必要となる。ページが変わることを計算すると実装が複雑になるため、今回は、矢印ボタンを押下するごとに現在アクティブなエレメントの情報を取得する方法を採用した。
差が負の値の場合は、未来の日付を意味するため、→キーを送信する。その反対は、過去日付なので、←ボタンを送信する。キーを送信する前には、現在アクティブなエレメントを毎回取得する。

    public void SendArrowLeftKey(ChromeDriver driver, int delta)
    {
        for (int i = 0; i < delta; i++)
        {
            driver.SwitchTo().ActiveElement().SendArrowLeftKey();
        }
    }

    public void SendArrowRightKey(ChromeDriver driver, int delta)
    {
        for (int i = 0; i < delta; i++)
        {
            driver.SwitchTo().ActiveElement().SendArrowRightKey();
        }
    }

上記メソッドの呼び出しは以下のようにした。

var delta = (targetDateTime - new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day)).Days;
if (delta > 0) 
    this.SendArrowRightKey(driver, delta);
else if (delta < 0) 
    this.SendArrowLeftKey(driver, Math.Abs(delta));
driver.SwitchTo().ActiveElement().Click();
driver.Close();