input[type="date"]をSeleniumで操作する際の注意点


# はじめに
`<input type="date"/>`の入力をSelenium Webdriverの`send_keys`で入力しようとしたところ、意図しない値が入力されたため詳しく調べてみました。
「なるほど！そうだったのか！！」を調査結果を添えて解説していきます。 

# 結論
W3C(HTML側)での文献を見つけることができなかったのですが、[ECMAのドキュメント](https://262.ecma-international.org/5.1/#sec-15.9.1.1)によると、***275760年9月13日まで入力できる***そうです。

```
Time Values and Time Range

(中略)

Time is measured in ECMAScript in milliseconds since 01 January,　1970 UTC. 
In time values leap seconds are ignored.
It is assumed that there are exactly 86,400,000 milliseconds per day.
ECMAScript Number values can represent all integers from
–9,007,199,254,740,992 to 9,007,199,254,740,992;
this range suffices to measure times to millisecond precision
for any instant that is within approximately 285,616 years,
either forward or backward, from 01 January, 1970 UTC.

The actual range of times supported by ECMAScript Date objects is
slightly smaller: exactly –100,000,000 days to 100,000,000 days
measured relative to midnight at the beginning of 01 January, 1970 UTC.
This gives a range of 8,640,000,000,000,000 milliseconds
to either side of 01 January, 1970 UTC.
```

275760年を入力してみると **275760/09/13** までしか表示されません。
また、275761年は入力時点で入りませんでした。
確かに書かれている仕様と一致しています！！
![スクリーンショット 2021-02-11 14.51.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19778/e8781fd6-012f-1b17-4672-4359706a3f1a.png)

つまり... **西暦は4桁ではなく6桁** で扱う仕様になっていると
そこで以下、３つの対処方法を考えました。

## 対処方法
* １. 入力値を `<input type="date"/>` の仕様に対応した値にする
    * 西暦を6桁として入力する(0で先頭を埋める)
* ２. Javascriptでvalue値を入力する
* ３. inputタグにmax属性を追加する
    * 入力を受け付ける日付を西暦2999年までに定義する

個人的には、３が一番スッキリですが、それぞれの方法に対する私の見解を書いておきます。

１または２は、テストコード側で対応できます。
３はアプリケーション側のビュー(HTML)を変更する必要がありますが、テストコードは違和感がないものになります。

１はユーザーのマニュアル操作とは異なっており、仕様を理解していない人が見たら意味不明なテストコードになってしまいます。
２はテストコードとしても違和感なくHTML側の修正も不要ですが、その箇所だけJavascriptの要素が加わります。



以下、SeleniumWebDriver(Ruby)で検証をしたコードを載せておきます。

# Selenium Webdriver(Ruby)での検証
ブラウザとSeleniumは以下のバージョンを使用しました。

* Chrome(88.0.4324.150)
* selenium-webdriver (3.142.7)

```html:date.html
<!DOCTYPE html>
<html>
  <body>
    <form>
      <input type="date" id="date"/>
    </form>
  </body>
</html>
```

```ruby:Gemfile
source 'https://rubygems.org'

gem 'selenium-webdriver'
```

## 意図しない入力値になるケース

### NGケース１：ハイフン(-)なしの日付をsendkeys
* 入力値**：20210131**
* 結果**：202101-03-01**

```ruby
require 'selenium-webdriver'
  
driver = Selenium::WebDriver.for :chrome
driver.get('file://[date.htmlの絶対パス]')

ele = driver.find_element(:css, '#date')
ele.send_keys('20210131')
puts ele.attribute('value') # => 202101-03-01
```

### NGケース２：ハイフン(-)区切りの日付をsendkeys
* 入力値**：20210131**
* 結果**：202101-03-01**

```ruby
 :
ele.send_keys('2021-01-31')
puts ele.attribute('value') # => 202101-03-01
```

### NGケース３：スラッシュ(/)区切りの日付をsendkeys
* 入力値**：2021/01/31**
* 結果**：202101-03-01**

```ruby
 :
ele.send_keys('2021/01/31')
puts ele.attribute('value') # => 202101-03-01
```

### NGケース４：先頭に0を1つ付与した値をsendkeys
* 入力値**：020210131**
* 結果**：20210-12-01**

```ruby
 :
ele.send_keys('020210131')
puts ele.attribute('value') # => 20210-12-01
```


## 意図した入力値になるケース
以下、入力値はいずれも **2021/01/31** となり、意図通りの結果になります。

### OKケース１：先頭に0を2つ付与した値をsendkeys
* 入力値**：0020210131**

```ruby
require 'selenium-webdriver'
  
driver = Selenium::WebDriver.for :chrome
driver.get('file://[date.htmlの絶対パス]')

ele = driver.find_element(:css, '#date')
ele.send_keys('0020210131')
puts ele.attribute('value') # => 2021/01/31
```

### OKケース２：ハイフン(-)区切りの日付の先頭に0を2つ付与した値をsendkeys
* 入力値**：002021-01-31** 

```ruby
ele.send_keys('002021-01-31')
puts ele.attribute('value') # => 2021/01/31
```

### OKケース３：Javascriptでvalueを入力
* 入力値**：2021-01-31**

```ruby
driver.execute_script('document.getElementById("date").value="2021-01-31";')
puts ele.attribute('value') #=> 2021-01-31
```

### OKケース４：inputタグにmax属性を追加して、ハイフン(-)なしの日付をsendkeys
* HTMLのinputタグにmax属性を追加して、値は"2999-12-31"とする
    * max属性：入力値の最大値を定義する

```html:date_max.html
<!DOCTYPE html>
<html>
  <body>
    <form>
      <input type="date" id="date_max" max="2999-12-31"/>
    </form>
  </body>
</html>
```

* 入力値**：20210131**

```ruby
require 'selenium-webdriver'
  
driver = Selenium::WebDriver.for :chrome
driver.get('file://[date_max.htmlの絶対パス]')

ele = driver.find_element(:css, '#date_max')
ele.send_keys('20210131')
puts ele.attribute('value') #=> 2021-01-31
```

### OKケース５：inputタグにmax属性を追加して、スラッシュ(/)区切りの日付をsendkeys
* 入力値**：2021/01/31**

```ruby
ele = driver.find_element(:css, '#date_max')
ele.send_keys('2021/01/31')
puts ele.attribute('value') #=> 2021-01-31
```