File input element can not work well in IE with Watir 1.8.0 and Ruby187
最近一段时间一直在和同事张逸做项目(Web)相关的regression test,使用的是cucumber(0.10.2)与watir (1.8.0)。总体来讲还算是不错(Ruby开发效率高),但昨天在做文件上传(file upload)相关的测试时,发现了watir的一个问题。代码如下:
def file index @browser.file_field(:index => index) end def fill_file_with_index file_name,index file_full_path = get_file_full_path(file_name) on(UploadDocumentsPage) do file(index).set(file_full_path) end end
函数file定义了UploadDocumentsPage中按照index去查询type为 file类型的dom元素。fill_file_with_index 定义了按照索引(index)找到指定的file元素,并且该元素设置相应值(file_name)。功能很简单,但是在实际运行过程中发现页面在弹出choose file to upload对话框后,并没有去设置相应的文件路径,而是就hold在那里不运行了。简单调试了下发现是卡在了set(file_full_path)这句上。Google未果,于是跟进watir中相应源码:
def set(path_to_file) assert_exists require 'watir/windowhelper' WindowHelper.check_autoit_installed begin Thread.new do sleep 10 # it takes some time for popup to appear system %{ruby -e ' require "win32ole" @autoit = WIN32OLE.new("AutoItX3.Control") time = Time.now while (Time.now - time) < 15 # the loop will wait up to 15 seconds for popup to appear #{POPUP_TITLES.inspect}.each do |popup_title| next unless @autoit.WinWait(popup_title, "", 1) == 1 @autoit.ControlSetText(popup_title, "", "Edit1", #{path_to_file.inspect}) @autoit.ControlSend(popup_title, "", "Button2", "{ENTER}") exit end # each end # while '} end.join(1) rescue raise Watir::Exception::WatirException, "Problem accessing Choose file dialog" end click end end
该方法主要逻辑是:
- 首先检查是否安装了AutoIt(用来模拟鼠标键盘操作的工具),如果没有,抛异常结束。
- 如果有安装,则新开启一个线程,并休眠当前线程10秒(等待pop up对话框),然后使用system 去执行一条命令,该命令在15秒内不断的去轮询标题为“Choose File to Upload”的窗体,一旦找到该窗体,就会自动为该窗体设置相应的值(path_to_file),并调用回车键(Enter)。
- 调用click(弹出Choose File to Upload对话框)
逻辑似乎没有什么问题,但运行起来为什么会出现问题呢?经过无数次puts测试,我们发现问题出现在新线程的sleep 10 # it takes some time for popup to appear 这一句上。当主线程pop up window出现后,新线程的sleep就再也醒不来了….尼玛这是为什么呢?我猜应该是Ruby187的bug。
找到问题后,解决方案也就很容易了,可以自己简单模拟sleep的实现:
def fake_sleep expected_time time_start = Time.now time_end = time_start while(time_end - time_start < expected_time) time_end = Time.now end end
把源码中的sleep 10 替换成fake_sleep 10,跑测试,问题依旧…继续分析发现问题出在join(1)上,先看下join函数的说明:
The calling thread will suspend execution and run <i>thr</i>. Does not
return until <i>thr</i> exits or until <i>limit</i> seconds have passed. If
the time limit expires, <code>nil</code> will be returned, otherwise
<i>thr</i> is returned.
原来新线程执行join(1)后会在1s后会立即返回,而此时线程的sleep 10 还没有结束,后面的代码也就没有机会去执行了…
最终我们修改源码如下:
def set(path_to_file) assert_exists require 'watir/windowhelper' WindowHelper.check_autoit_installed begin Thread.new do system %{ruby -e ' require "win32ole" @autoit = WIN32OLE.new("AutoItX3.Control") time = Time.now sleep 2 while (Time.now - time) < 15 # the loop will wait up to 15 seconds for popup to appear #{POPUP_TITLES.inspect}.each do |popup_title| next if @autoit.WinWait(popup_title, "", 1) == 0 @autoit.ControlSetText(popup_title, "", "Edit1", #{path_to_file.inspect}) @autoit.ControlSend(popup_title, "", "Button2", "{ENTER}") exit end # each end # while '} end.join(1) rescue raise Watir::Exception::WatirException, "Problem accessing Choose file dialog" end click end end
测试通过!