Skip to content

Instantly share code, notes, and snippets.

@codenamev
Last active April 26, 2016 12:19
Show Gist options
  • Save codenamev/3105157 to your computer and use it in GitHub Desktop.
Save codenamev/3105157 to your computer and use it in GitHub Desktop.
RSpec helper and Cucumber step to match table rows with Capybara
# Thanks to Henning Koch: https://makandracards.com/makandra/763-cucumber-step-to-match-table-rows-with-capybara
module ArrayMethods
def find_row(expected_row)
find_index do |row|
expected_row.all? do |expected_column|
first_column = row.find_index do |column|
content = column.content.gsub(/[\r\n\t]+/, ' ').gsub(/[ ]+/, ' ').strip
expected_content = expected_column.gsub(' ', ' ').strip
matching_parts = expected_content.split('*', -1).collect { |part| Regexp.escape(part) }
matching_expression = /\A#{matching_parts.join(".*")}\z/
content =~ matching_expression
end
if first_column.nil?
false
else
row = row[(first_column + 1)..-1]
true
end
end
end
end
end
module Reenhanced
module Matchers
class CheckTable
def initialize(expected_rows, expected_options = {})
@expected_table = expected_rows
@ordered = expected_options.delete(:in_order)
@negation = expected_options.delete(:not_in_table)
end
def matches?(actual_table_rows)
if @negation
actual_table_rows.extend ArrayMethods
@expected_table.none? do |expected_row|
@last_expected_row = expected_row
first_row = actual_table_rows.find_row(expected_row)
end
else
@expected_table.all? do |expected_row|
@last_expected_row = expected_row
actual_table_rows.extend ArrayMethods
first_row = actual_table_rows.find_row(expected_row)
if first_row.nil?
false
else
if @ordered
actual_table_rows = actual_table_rows[(first_row + 1)..-1]
else
actual_table_rows.delete_at(first_row)
end
true
end
end
end
end
def failure_message
"Could not find the following row: #{@last_expected_row.inspect}"
end
def negative_failure_message
"Found the following row: #{@last_expected_row.inspect}"
end
end
def contain_table(rows, in_order = false)
CheckTable.new(rows, in_order: in_order)
end
def not_contain_table(rows, in_order = false)
CheckTable.new(rows, in_order: in_order, not_in_table: true)
end
end
end
ActiveSupport::TestCase.send :include, Reenhanced::Matchers
include Reenhanced::Matchers
# Helper methods for table lookup
def table_exists_with_the_following_rows(rows, options = {})
document = Nokogiri::HTML(page.body)
table = document.xpath('//table//tr').collect { |row| row.xpath('.//th|td') }
in_order = options.delete(:ordered)
table.should contain_table(rows, in_order)
end
def no_table_exists_with_the_following_rows(rows, options = {})
document = Nokogiri::HTML(page.body)
table = document.xpath('//table//tr').collect { |row| row.xpath('.//th|td') }
in_order = options.delete(:ordered)
table.should not_contain_table(rows, in_order)
end
# Thanks to Henning Koch: https://makandracards.com/makandra/763-cucumber-step-to-match-table-rows-with-capybara
module TableStepsHelper
module ArrayMethods
def find_row(expected_row)
find_index do |row|
expected_row.all? do |expected_column|
first_column = row.find_index do |column|
content = column.content.gsub(/[\r\n\t]+/, ' ').gsub(/[ ]+/, ' ').strip
expected_content = expected_column.gsub(' ', ' ').strip
matching_parts = expected_content.split('*', -1).collect { |part| Regexp.escape(part) }
matching_expression = /\A#{matching_parts.join(".*")}\z/
content =~ matching_expression
end
if first_column.nil?
false
else
row = row[(first_column + 1)..-1]
true
end
end
end
end
end
rspec = defined?(RSpec) ? RSpec : Spec
rspec::Matchers.define :contain_table do |*args|
match do |table|
expected_table, unordered = args
expected_table.all? do |expected_row|
@last_expected_row = expected_row
table.extend ArrayMethods
first_row = table.find_row(expected_row)
if first_row.nil?
false
else
if unordered
table.delete_at(first_row)
else
table = table[(first_row + 1)..-1]
end
true
end
end
end
failure_message_for_should do
"Could not find the following row: #{@last_expected_row.inspect}"
end
end
rspec::Matchers.define :not_contain_table do |expected_table|
match do |table|
table.extend ArrayMethods
expected_table.none? do |expected_row|
@last_expected_row = expected_row
first_row = table.find_row(expected_row)
end
end
failure_message_for_should do
"Found the following row: #{@last_expected_row.inspect}"
end
end
def parse_table(table)
if table.is_a?(String)
# multiline string. split it assuming a format like cucumber tables have.
table.split(/\n/).collect do |line|
line.sub!(/^\|/, '')
line.sub!(/\|$/, '')
line.split(/\s*\|\s*/)
end
else
# vanilla cucumber table.
table.raw
end
end
end
Then /^I should( not)? see the following table rows( in any order)?:?$/ do |negate, unordered, expected_table|
extend TableStepsHelper
document = Nokogiri::HTML(page.body)
table = document.xpath('//table//tr').collect { |row| row.xpath('.//th|td') }
expected_table = parse_table(expected_table)
if negate
table.should not_contain_table(expected_table)
else
table.should contain_table(expected_table, unordered)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment