在RSpec功能规范(Capybara)中测试索引视图排序顺序

时间:2017-07-24 16:49:24

标签: ruby-on-rails ruby testing rspec capybara

Rails 4.2应用程序的索引视图在其标题处有一个包含排序链接的表。如果用户单击“电子邮件”标题,则记录按电子邮件排序,依此类推。单击排序链接时,将重新加载页面(使用类似q=email+asc的查询字符串)。 AJAX尚未使用。

我写了以下测试。它有效,但我相信应该有更好的方法来测试它。

it "sorts by e-mail when the 'E-mail' column header is clicked", :focus do
  visit "/admin/users"
  expected_id_order = User.order(:email).map(&:id)
  # Once the page is reloaded (the new sort order is applied), the "stale"
  # class will disappear. Note that due to Turbolinks, only the part of
  # the DOM that is modified by a request will have its elements replaced.
  page.execute_script("$('tr.user:first').addClass('stale')")
  within "thead" do
    click_link("E-mail")
  end
  # Force Capybara to wait until the page is reloaded
  expect(page).to have_no_selector("tr.user.stale")
  actual_id_order = page.body.scan(/<tr.+id="user_(.+)".*>/).flatten
  expect(actual_id_order).to eq(expected_id_order)
end

其他详情:

  • <TR>个元素的DOM ID包含其相应记录的DB ID,如<tr class="user" id="user_34">。使用正则表达式提取记录在页面中显示的顺序可能不是最佳解决方案。你能建议一个更好的方法吗?
  • 我不喜欢JavaScript hack(使用JQuery添加stale类然后等到它消失以确保页面被重新加载),但到目前为止我找不到另一种方法来确保Capybara等待直到重新加载页面(应用新的排序顺序)。你能建议一个更好的方法吗?
  • 应用程序使用Devise,因此我们需要创建用户记录才能登录。请注意,为登录目的创建的用户不可避免地出现在我们的测试数据中。

4 个答案:

答案 0 :(得分:2)

最简单的方法是使用控制测试数据的事实,相应地分配字符串然后使用正则表达式来测试用户实际看到的输出而不是特定的标记和ID。

within_table('user_table') do # any method that will scope to the table
  expect(page).to have_text /user3@test\.com.+user2@test\.com.+user1@test\.com/ # initial expected ordering
  click_link("E-mail")
  expect(page).to have_text /user1@test\.com.+user2@test\.com.+user3@test\.com/ # sorted expected ordering
end

Capybara会等,不需要JS hack等等。

答案 1 :(得分:1)

this blog post获取想法,你可以做类似

的事情
# get the index of the first occurrence of each users' email address in the response body
actual_order = User.order(:email).map { |user| page.body.index(user.email) }
# should already be in sorted order
expect(actual_order).to eq(actual_order.sort)

至于必须等待.stale类被删除,我建议修改你的html,在你的th上包含某种“这是当前排序的列”类或者a代码。

<a id="email-sort-link" class="sorted asc">E-Mail</a>

然后你可以等到a#email-sort-link.sorted。通常不喜欢修改代码以方便测试,但这似乎很小,并且允许您以不同方式设置链接的样式以提醒用户当前的排序。

答案 2 :(得分:0)

我有一个特定的案例,我想使用共享示例。所以我根据Thomas Walpole的回答创建了以下内容。

def sort_order_regex(*sort_by_attributes)
  /#{User.order(sort_by_attributes)
         .map { |u| Regexp.quote(u.email) }
         .join(".+")}/
end

RSpec.shared_examples "sort link" do |link_text:, sort_by:|
  # Make sure your factory creates unique values for all sortable attributes
  before(:each) { FactoryGirl.create_list(:user, 3) }

  it "sorts by #{sort_by} when the #{link_text} link is clicked" do
    visit "/admin/users"
    # Check the record's order by matching the order of their e-mails (unique).
    initial_order = sort_order_regex(:first_name, :last_name)
    tested_order = sort_order_regex(sort_by)
    within_table "users_table" do
      expect(page).to have_text(initial_order)
      click_link(link_text, exact: false)
      expect(page).to have_text(tested_order)
    end
  end
end

describe "User CRUD", type: :feature, js: true do
  describe "sorting" do
    include_examples "sort link", link_text: "E-mail", sort_by: :email
    include_examples "sort link", link_text: "Role", sort_by: :role
    include_examples "sort link", link_text: "Country", sort_by: :country
    include_examples "sort link", link_text: "Quotes", sort_by: :quotes_count
  end
  #...
end

答案 3 :(得分:0)

我们可以用CSS东西对其进行测试。

一个例子:

|---------------------|------------------|
|     username        |     email        |
|---------------------|------------------|
|     bot1            | bot1@mail.com    |
|---------------------|------------------|
|     bot2            | bot2@mail.com    |
|---------------------|------------------|

我们的RSpec可以像:

RSpec.describe "returns expected sorted row data" do
  it 'returns expected sorted row data' do
    # the headers column name
    within 'tr:nth-child(1)' do
      expect(page).to have_text 'username password'
    end

    # the second row
    within 'tr:nth-child(2)' do
      expect(page).to have_text 'bot1 bot1@mail.com'
    end

    # the third row
    within 'tr:nth-child(3)' do
      expect(page).to have_text 'bot2 bot2@mail.com'
    end
  end
end