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

该方法主要逻辑是:

  1. 首先检查是否安装了AutoIt(用来模拟鼠标键盘操作的工具),如果没有,抛异常结束。
  2. 如果有安装,则新开启一个线程,并休眠当前线程10秒(等待pop up对话框),然后使用system 去执行一条命令,该命令在15秒内不断的去轮询标题为“Choose File to Upload”的窗体,一旦找到该窗体,就会自动为该窗体设置相应的值(path_to_file),并调用回车键(Enter)。
  3. 调用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

测试通过!

Posted by Leo 2011年9月03日 17:05