UI Automation

This article is the last one in 2013. In the rails project we use capybara to do UI testing. Like click a button/link or fill the form. But i will show how to use capybara without rails in UI automation today.

Sometimes we have to repeat trivial works and these require user to interactive with browser. For instance, upload file to server or input necessary data.

Install below gems and Firefox

1
2
gem install capybara
gem install selenium-webdriver

In orer to use capybara without rails. You need to require capybara manually. In default capybara will launch rack server, change run_server to false. Because we assume your server is running. Use selenium as driver.

1
2
3
4
5
6
7
8
require 'capybara'
require 'capybara/dsl'

Capybara.run_server = false
Capybara.current_driver = :selenium

# it will open the browser to 127.0.0.1:8888/admin
Capybara.app_host = 'http://127.0.0.1:8888/admin'

With capybara DSL, you can operate UI easily. Below code shows fill id and password field with admin then click login button.

1
2
3
4
5
6
def login
  visit('/')
  fill_in 'userNameTxtField', :with => 'admin'
  fill_in 'passwordTxtField', :with => 'admin'
  find('#loginBtn').click
end

In case you have to wait specified element show in page or element class changed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def wait_util_find(cssString)
  puts "start waiting until find " << cssString
  until page.has_selector?(cssString) do
    puts "waiting..." << cssString
    sleep 1
  end
end

def wait_element_enable(cssString)
  elem = find(cssString)
  while elem[:class].include?('disabled') do
    puts "waiting..."
    sleep 5
  end
end

# If you need to find multiple elements, you can use page.all
# find() method can't find multiple elements. It causes exception.
def wait_something(cssString)
  while page.all(:css, cssString).length > 1
    puts "waiting..."
    sleep 5
  end
end

How to attach file? In capybara official document, you can use attach_file to attach file easily. But i found it doesn’t work in HTML5 input file tag with multiple like

1
<input id="upload" name="upload-btn" type="file" multiple>

The capybara always show can’t find the element.

1
attach_file 'upload-btn', '/upload/1.zip' # it won't work.

Then i think i could click the button then select the file myself.

1
2
3
4
5
def upload
  find('#upload').click # click upload button 

  select_file # select files from disk.
end

But here is the problem, selenium driver can’t control the native file selection window. There’s no way to select files in file selection window. After few days survey, i solved it with rautomation.

1
gem install rautomation

After the file selection window popups, the running program is stoped. It means select_file method won’t be executed. I think it’s because the native window popups and require user to interactive. So i change capybara click method to selenium method and select_file can be executed.

1
2
3
4
5
6
7
8
9
10
require `rautomation`

def upload
  # find('#upload').click 

  upload_elem = page.driver.browser.all(:xpath, '//input[@name="upload-btn"]')[1]
  upload_elem.send_keys "gogo"

  select_file
end

The next step is how to select files in native window. The first step is find the native window. I use a simple way to do it. Compare the window number before click upload button and after.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def upload
  before = RAutomation::Window.windows.map(&:hwnd)

  upload_elem = page.driver.browser.all(:xpath, '//input[@name="upload-btn"]')[1]
  upload_elem.send_keys "gogo"

  after = RAutomation::Window.windows.map(&:hwnd)

  select_file(before, after)
end

def select_file(before, after)
  file_select_window = nil
  candidate = after - before
  case candidate.length
    when 0
      nil
    when 1
      file_select_window = RAutomation::Window.new(:hwnd => candidate[0])
    else
      select_from_other_criteria(candidate)
  end
end

If the candidate length equals 1, that candidate will be the file selection window. There should be better way to find the native window. I try to find window by window’s title but it won’t work. Maybe it’s because my OS or Firebox is Chinese.
After you get the file_select_window, set the file path to text_field and click the button.

1
2
3
4
5
6
def select_file(before, after)
  ...

  file_select_window.text_field.set '/upload/1.zip'
  file_select_window.button.click
end

Comments