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


はじめに

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

結論

W3C(HTML側)での文献を見つけることができなかったのですが、ECMAのドキュメントによると、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年は入力時点で入りませんでした。
確かに書かれている仕様と一致しています!!

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

対処方法

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

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

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

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

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

Selenium Webdriver(Ruby)での検証

ブラウザとSeleniumは以下のバージョンを使用しました。

  • Chrome(88.0.4324.150)
  • selenium-webdriver (3.142.7)
date.html
<!DOCTYPE html>
<html>
  <body>
    <form>
      <input type="date" id="date"/>
    </form>
  </body>
</html>
Gemfile
source 'https://rubygems.org'

gem 'selenium-webdriver'

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

NGケース1:ハイフン(-)なしの日付をsendkeys

  • 入力値:20210131
  • 結果:202101-03-01
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ケース2:ハイフン(-)区切りの日付をsendkeys

  • 入力値:20210131
  • 結果:202101-03-01
 :
ele.send_keys('2021-01-31')
puts ele.attribute('value') # => 202101-03-01

NGケース3:スラッシュ(/)区切りの日付をsendkeys

  • 入力値:2021/01/31
  • 結果:202101-03-01
 :
ele.send_keys('2021/01/31')
puts ele.attribute('value') # => 202101-03-01

NGケース4:先頭に0を1つ付与した値をsendkeys

  • 入力値:020210131
  • 結果:20210-12-01
 :
ele.send_keys('020210131')
puts ele.attribute('value') # => 20210-12-01

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

以下、入力値はいずれも 2021/01/31 となり、意図通りの結果になります。

OKケース1:先頭に0を2つ付与した値をsendkeys

  • 入力値:0020210131
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ケース2:ハイフン(-)区切りの日付の先頭に0を2つ付与した値をsendkeys

  • 入力値:002021-01-31
ele.send_keys('002021-01-31')
puts ele.attribute('value') # => 2021/01/31

OKケース3:Javascriptでvalueを入力

  • 入力値:2021-01-31
driver.execute_script('document.getElementById("date").value="2021-01-31";')
puts ele.attribute('value') #=> 2021-01-31

OKケース4:inputタグにmax属性を追加して、ハイフン(-)なしの日付をsendkeys

  • HTMLのinputタグにmax属性を追加して、値は"2999-12-31"とする
    • max属性:入力値の最大値を定義する
date_max.html
<!DOCTYPE html>
<html>
  <body>
    <form>
      <input type="date" id="date_max" max="2999-12-31"/>
    </form>
  </body>
</html>
  • 入力値:20210131
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ケース5:inputタグにmax属性を追加して、スラッシュ(/)区切りの日付をsendkeys

  • 入力値:2021/01/31
ele = driver.find_element(:css, '#date_max')
ele.send_keys('2021/01/31')
puts ele.attribute('value') #=> 2021-01-31