Skip to content

Commit 5f4abf2

Browse files
Copilotsenid231
andcommitted
Add have_fields_date_range, within_table_footer, and other helpers
Agent-Logs-Url: https://github.com/activeadmin-plugins/capybara_active_admin/sessions/ba3cc7b3-4229-4aa9-b458-e4fee87e4c66 Co-authored-by: senid231 <8393857+senid231@users.noreply.github.com>
1 parent 4dfbd59 commit 5f4abf2

8 files changed

Lines changed: 146 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
### Added
10+
- `have_fields_date_range` matcher for date range filter fields
11+
- `within_table_footer` finder for table footer row
12+
- `click_table_scope` action for clicking table scope links
13+
- `table_header_selector` selector now accepts text and options (sortable, sort_direction, column)
14+
- `have_table_header` matcher for table header columns
15+
- `find_table_header` finder for table header columns
16+
- `click_table_header` action for clicking sortable table headers
17+
- `find_action_item` finder for action item elements
18+
- `have_action_item_link` matcher for action item links (with optional href)
19+
- `within_action_item_dropdown` finder for action item dropdown menu
20+
- `have_status_tag` matcher for status tag elements
21+
22+
### Changed
23+
- `within_sidebar` now scopes within the sidebar section directly using `ancestor`
24+
- `have_table_scope` now accepts an optional title as first positional argument and `selected:` keyword arg
25+
926
## [0.3.3] - 2020-04-17
1027
### Changed
1128
- `batch_action_selector`, `click_batch_action` finds element by link text

lib/capybara/active_admin/actions/table.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ def select_table_row(id: nil, index: nil)
1616
selector = %(input[id^="batch_action_item_"])
1717
find_all(selector, minimum: index + 1)[index].click
1818
end
19+
20+
# @param text [String] exact text of the scope to click.
21+
def click_table_scope(text)
22+
selector = "#{table_scopes_container_selector} > #{table_scope_selector}"
23+
page.find(selector, exact_text: text).click
24+
end
25+
26+
# @param text [String] column header text.
27+
# @param options [Hash] options passed to find_table_header.
28+
def click_table_header(text, options = {})
29+
find_table_header(text, options).find('a').click
30+
end
1931
end
2032
end
2133
end

lib/capybara/active_admin/finders/layout.rb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ def within_tab_body
1616
end
1717

1818
def within_sidebar(title, exact: nil)
19-
selector = sidebar_selector
20-
21-
within(selector) do
22-
within_panel(title, exact: exact) { yield }
23-
end
19+
opts = Util.options_with_text(title, exact: exact)
20+
sidebar = page.find("#{sidebar_selector} .sidebar_section #{panel_title_selector}", **opts).ancestor('.sidebar_section')
21+
within(sidebar) { yield }
2422
end
2523

2624
def within_panel(title, exact: nil)
@@ -35,6 +33,19 @@ def within_panel(title, exact: nil)
3533
def within_modal_dialog
3634
within(modal_dialog_selector) { yield }
3735
end
36+
37+
# @param title [String] action item link text.
38+
# @param exact [Boolean] whether to match the title exactly (default true).
39+
# @return [Capybara::Node::Element] the found action item element.
40+
def find_action_item(title, exact: true)
41+
opts = exact ? { exact_text: title } : { text: title }
42+
page.find(action_item_selector, **opts)
43+
end
44+
45+
# @yield within action item dropdown menu list.
46+
def within_action_item_dropdown
47+
within("#{action_item_selector} .dropdown_menu_list_wrapper") { yield }
48+
end
3849
end
3950
end
4051
end

lib/capybara/active_admin/finders/table.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,23 @@ def find_table_row(id: nil, index: nil)
3434
find_all(selector, minimum: index + 1)[index]
3535
end
3636

37+
# @yield within table>tfoot>tr
38+
def within_table_footer
39+
within('tfoot > tr') { yield }
40+
end
41+
42+
# @param text [String] column header text.
43+
# @param options [Hash]
44+
# @option column [String, nil] column name override (defaults to text).
45+
# @option sortable [Boolean] whether the column is sortable.
46+
# @option sort_direction [String, nil] sort direction ('asc' or 'desc').
47+
# @return [Capybara::Node::Element] the found table header element.
48+
def find_table_header(text, options = {})
49+
selector = table_header_selector(text, options)
50+
opts = options.except(:column, :sortable, :sort_direction).merge(exact_text: text)
51+
find(selector, **opts)
52+
end
53+
3754
# @yield within table>tbody>tr>td
3855
def within_table_cell(name)
3956
cell = find_table_cell(name)

lib/capybara/active_admin/matchers/form.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,25 @@ def have_has_many_fields_for(association_name, options = {})
3333
have_selector(selector, **options)
3434
end
3535

36+
# @param label [String] label text of the date range filter.
37+
# @param options [Hash]
38+
# @option from [String, nil] expected value of the "from" field.
39+
# @option to [String, nil] expected value of the "to" field.
40+
# @option exact [Boolean, nil] whether to match field values exactly.
41+
# @example
42+
# expect(page).to have_fields_date_range('Created At', from: '2020-01-01', to: '2020-12-31')
43+
#
44+
def have_fields_date_range(label, options = {})
45+
exact = options[:exact]
46+
satisfy do |actual|
47+
expect(actual).to have_selector('div.filter_date_range label', text: label)
48+
container = actual.find('div.filter_date_range label', text: label).ancestor('div.filter_date_range')
49+
base_name = container[:id].gsub(/_input\z/, '')
50+
expect(container).to have_field("#{base_name}_gteq_datetime", with: options[:from].to_s, exact: exact)
51+
expect(container).to have_field("#{base_name}_lteq_datetime", with: options[:to].to_s, exact: exact)
52+
end
53+
end
54+
3655
# @param text [String] button title
3756
# @param options [Hash]
3857
# @option selector [String, nil] optional selector to append

lib/capybara/active_admin/matchers/layout.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,32 @@ def have_batch_action(title, exact: true)
4444
opts = Util.options_with_text(title, exact: exact)
4545
have_selector(selector, **opts)
4646
end
47+
48+
# @param title [String] action item link text.
49+
# @param exact [Boolean] whether to match the title exactly (default true).
50+
# @param href [String, nil] expected href attribute of the link.
51+
# @param options [Hash] additional options passed to have_selector.
52+
# @example
53+
# expect(page).to have_action_item_link('New User')
54+
# expect(page).to have_action_item_link('New User', href: new_admin_user_path)
55+
#
56+
def have_action_item_link(title, exact: true, href: nil, **options)
57+
opts = exact ? { exact_text: title } : { text: title }
58+
opts.merge!(options)
59+
selector = "#{action_item_selector} > a"
60+
selector += "[href=\"#{href}\"]" if href.present?
61+
have_selector(selector, **opts)
62+
end
63+
64+
# @param type [String, Symbol] status tag type (e.g. :yes, :no, :warning).
65+
# @param options [Hash] additional options passed to have_selector (e.g. exact_text:).
66+
# @example
67+
# expect(page).to have_status_tag(:yes)
68+
# expect(page).to have_status_tag(:error, exact_text: 'DOWN')
69+
#
70+
def have_status_tag(type, **options)
71+
have_selector("span.status_tag.#{type}", **options)
72+
end
4773
end
4874
end
4975
end

lib/capybara/active_admin/matchers/table.rb

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,34 @@ def have_table_scopes(options = {})
6060
have_selector("#{table_scopes_container_selector} > #{table_scope_selector}", **options)
6161
end
6262

63-
# @param options [Hash]
64-
# @option exact_text [String] title of scope
65-
# @option counter [Integer,String,nil] counter value in brackets (nil if skipped)
66-
# @option selected [Boolean] is scope active (default false)
67-
def have_table_scope(options = {})
68-
active = options.delete(:active)
63+
# @param title [String, nil] exact title of scope to match.
64+
# @param selected [Boolean] whether to match selected (active) scope only (default false).
65+
# @param options [Hash] additional options passed to have_selector.
66+
def have_table_scope(title = nil, selected: false, **options)
6967
selector = "#{table_scopes_container_selector} > #{table_scope_selector}"
70-
selector = active ? "#{selector}.selected" : "#{selector}:not(.selected)"
68+
if title.nil?
69+
selector = selected ? "#{selector}.selected" : "#{selector}:not(.selected)"
70+
have_selector(selector, **options)
71+
else
72+
selector = selected ? "#{selector}.selected" : selector
73+
have_selector(selector, exact_text: title.to_s, **options)
74+
end
75+
end
7176

72-
have_selector(selector, **options)
77+
# @param text [String] column header text.
78+
# @param options [Hash]
79+
# @option column [String, nil] column name override (defaults to text).
80+
# @option sortable [Boolean] whether the column is sortable.
81+
# @option sort_direction [String, nil] sort direction ('asc' or 'desc').
82+
# @example
83+
# expect(page).to have_table_header('Full Name')
84+
# expect(page).to have_table_header('Full Name', sortable: true)
85+
# expect(page).to have_table_header('Full Name', sort_direction: :asc)
86+
#
87+
def have_table_header(text, options = {})
88+
selector = table_header_selector(text, options)
89+
opts = options.except(:column, :sortable, :sort_direction).merge(exact_text: text)
90+
have_selector(selector, **opts)
7391
end
7492
end
7593
end

lib/capybara/active_admin/selectors/table.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,20 @@ def table_row_selector(record_id = nil)
2222
%(tbody > tr[id$="_#{record_id}"])
2323
end
2424

25+
# @param text [String] column header text.
26+
# @param options [Hash]
27+
# @option column [String, nil] column name override (defaults to text).
28+
# @option sortable [Boolean] whether column is sortable.
29+
# @option sort_direction [String, nil] sort direction ('asc' or 'desc').
2530
# @return selector.
26-
def table_header_selector
27-
'thead > tr > th.col'
31+
def table_header_selector(text = nil, options = {})
32+
return 'thead > tr > th.col' if text.nil?
33+
34+
column = (options[:column] || text).to_s.tr(' ', '_').downcase
35+
selector = "th.col.col-#{column}"
36+
selector += '.sortable' if options[:sortable]
37+
selector += ".sorted-#{options[:sort_direction].to_s.downcase}" if options[:sort_direction].present?
38+
"thead > tr > #{selector}"
2839
end
2940

3041
# @param column [String, nil] column name.

0 commit comments

Comments
 (0)