Add table selector with_cols and with_rows expression filters

This commit is contained in:
Thomas Walpole 2019-03-14 18:53:43 -07:00
parent 18c9325fd2
commit b802c87d8a
4 changed files with 162 additions and 13 deletions

View File

@ -1,6 +1,8 @@
# frozen_string_literal: true
require 'capybara/selector/xpath_extensions'
require 'capybara/selector/selector'
Capybara::Selector::FilterSet.add(:_field) do
node_filter(:checked, :boolean) { |node, value| !(value ^ node.checked?) }
node_filter(:unchecked, :boolean) { |node, value| (value ^ node.checked?) }
@ -454,6 +456,46 @@ Capybara.add_selector(:table, locator_type: [String, Symbol]) do
xpath
end
expression_filter(:with_cols, valid_values: [Array]) do |xpath, cols|
raise ArgumentError, 'Columns must be specified as hashes' unless cols.all? { |col| col.is_a? Hash }
col_conditions = cols.map do |col|
col.reduce(nil) do |xp, (header, cell)|
header_xp = XPath.descendant(:th)[XPath.string.n.is(header)]
cell_xp = XPath.descendant(:tr)[header_xp].descendant(:td)
next cell_xp[XPath.string.n.is(cell)] unless xp
table_ancestor = XPath.ancestor(:table)[1]
xp = XPath::Expression.new(:join, table_ancestor, xp)
cell_xp[XPath.string.n.is(cell) & XPath.position.equals(xp.preceding_sibling.count)]
end
end.reduce(:&)
xpath[col_conditions]
end
expression_filter(:with_rows, valid_values: [Array]) do |xpath, rows|
rows_conditions = rows.map do |row|
if row.is_a? Hash
row_conditions = row.map do |header, cell|
header_xp = XPath.ancestor(:table)[1].descendant(:tr)[1].descendant(:th)[XPath.string.n.is(header)]
XPath.descendant(:td)[
XPath.string.n.is(cell) & XPath.position.equals(header_xp.preceding_sibling.count.plus(1))
]
end.reduce(:&)
XPath.descendant(:tr)[row_conditions]
else
row_conditions = row.map do |cell|
XPath.self(:td)[XPath.string.n.is(cell)]
end
row_conditions = row_conditions.reverse.reduce do |cond, cell|
cell[XPath.following_sibling[cond]]
end
XPath.descendant(:tr)[XPath.descendant(:td)[row_conditions]]
end
end.reduce(:&)
xpath[rows_conditions]
end
describe_expression_filters do |caption: nil, **|
" with caption \"#{caption}\"" if caption
end
@ -472,11 +514,7 @@ Capybara.add_selector(:table_row, locator_type: [Array, Hash]) do
end
else
initial_td = XPath.descendant(:td)[XPath.string.n.is(locator.shift)]
tds = locator.reverse.map do |cell|
XPath.following_sibling(:td)[XPath.string.n.is(cell)]
end.reduce do |xp, cell|
xp[cell]
end
tds = locator.reverse.map { |cell| XPath.following_sibling(:td)[XPath.string.n.is(cell)] }.reduce { |xp, cell| xp[cell] }
xpath[initial_td[tds]]
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
module XPath
class Renderer
def join(*expressions)
expressions.join('/')
end
end
end

View File

@ -11,20 +11,70 @@ Capybara::SpecHelper.spec '#has_table?' do
expect(@session).to have_table(:villain_table)
end
it 'should accept rows with column header hashes' do
expect(@session).to have_table('Horizontal Headers', with_rows:
[
{ 'First Name' => 'Vern', 'Last Name' => 'Konopelski', 'City' => 'Everette' },
{ 'First Name' => 'Palmer', 'Last Name' => 'Sawayn', 'City' => 'West Trinidad' }
])
end
it 'should accept rows with partial column header hashses' do
expect(@session).to have_table('Horizontal Headers', with_rows:
[
{ 'First Name' => 'Thomas' },
{ 'Last Name' => 'Sawayn', 'City' => 'West Trinidad' }
])
end
it 'should accept rows with array of cell values' do
expect(@session).to have_table('Horizontal Headers', with_rows:
[
%w[Thomas Walpole Oceanside],
['Ratke', 'Lawrence', 'East Sorayashire']
])
end
it 'should consider order of cells in each row' do
expect(@session).not_to have_table('Horizontal Headers', with_rows:
[
%w[Thomas Walpole Oceanside],
['Lawrence', 'Ratke', 'East Sorayashire']
])
end
it 'should match with vertical headers' do
expect(@session).to have_table('Vertical Headers', with_cols:
[
{ 'First Name' => 'Thomas' },
{ 'First Name' => 'Danilo', 'Last Name' => 'Wilkinson', 'City' => 'Johnsonville' },
{ 'Last Name' => 'Sawayn', 'City' => 'West Trinidad' }
])
end
it "should not match with vertical headers if the columns don't match" do
expect(@session).not_to have_table('Vertical Headers', with_cols:
[
{ 'First Name' => 'Thomas' },
{ 'First Name' => 'Danilo', 'Last Name' => 'Walpole', 'City' => 'Johnsonville' },
{ 'Last Name' => 'Sawayn', 'City' => 'West Trinidad' }
])
end
it 'should be false if the table is not on the page' do
expect(@session).not_to have_table('Monkey')
end
it 'should find row by header and cell values' do
expect(@session.find(:table, 'Vertical')).to have_selector(:table_row, 'First Name' => 'Thomas', 'Last Name' => 'Walpole')
expect(@session.find(:table, 'Vertical')).to have_selector(:table_row, 'Last Name' => 'Walpole')
expect(@session.find(:table, 'Vertical')).not_to have_selector(:table_row, 'First Name' => 'Walpole')
expect(@session.find(:table, 'Horizontal Headers')).to have_selector(:table_row, 'First Name' => 'Thomas', 'Last Name' => 'Walpole')
expect(@session.find(:table, 'Horizontal Headers')).to have_selector(:table_row, 'Last Name' => 'Walpole')
expect(@session.find(:table, 'Horizontal Headers')).not_to have_selector(:table_row, 'First Name' => 'Walpole')
end
it 'should find row by cell values' do
expect(@session.find(:table, 'Vertical')).to have_selector(:table_row, %w[Thomas Walpole])
expect(@session.find(:table, 'Vertical')).not_to have_selector(:table_row, %w[Walpole Thomas])
expect(@session.find(:table, 'Vertical')).not_to have_selector(:table_row, %w[Other])
expect(@session.find(:table, 'Horizontal Headers')).to have_selector(:table_row, %w[Thomas Walpole])
expect(@session.find(:table, 'Horizontal Headers')).not_to have_selector(:table_row, %w[Walpole Thomas])
expect(@session.find(:table, 'Horizontal Headers')).not_to have_selector(:table_row, %w[Other])
end
end
@ -41,4 +91,18 @@ Capybara::SpecHelper.spec '#has_no_table?' do
it 'should be true if the table is not on the page' do
expect(@session).to have_no_table('Monkey')
end
it 'should consider rows' do
expect(@session).to have_no_table('Horizontal Headers', with_rows:
[
{ 'First Name' => 'Thomas', 'City' => 'Los Angeles' }
])
end
it 'should consider columns' do
expect(@session).to have_no_table('Vertical Headers', with_cols:
[
{ 'First Name' => 'Joe' }
])
end
end

View File

@ -63,30 +63,68 @@
</form>
<table>
<caption>Vertical</caption>
<caption>Horizontal Headers</caption>
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>City</th>
</tr>
</thead>
<tbody>
<tr>
<td>Thomas</td>
<td>Walpole</td>
<td>Oceanside</td>
</tr>
<tr>
<td>Danilo</td>
<td>Wilkinson</td>
<td>Johnsonville</td>
</tr>
<tr>
<td>Vern</td>
<td>Konopelski</td>
<td>Everette</td>
</tr>
<tr>
<td>Ratke</td>
<td>Lawrence</td>
<td>East Sorayashire</td>
</tr>
<tr>
<td>Palmer</td>
<td>Sawayn</td>
<td>West Trinidad</td>
</tr>
</tbody>
</table>
<table>
<caption>Horizontal</caption>
<caption>Vertical Headers</caption>
<tr>
<th>First Name</th>
<td>Thomas</td>
<td>Danilo</td>
<td>Vern</td>
<td>Ratke</td>
<td>Palmer</td>
</tr>
<tr>
<th>Last Name</th>
<td>Walpole</td>
<td>Wilkinson</td>
<td>Konopelski</td>
<td>Lawrence</td>
<td>Sawayn</td>
</tr>
<tr>
<th>City</th>
<td>Oceanside</td>
<td>Johnsonville</td>
<td>Everette</td>
<td>East Sorayashire</td>
<td>West Trinidad</td>
</tr>
</table>