Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- `have_fields_date_range` matcher for date range filter fields
- `within_table_footer` finder for table footer row
- `click_table_scope` action for clicking table scope links
- `table_header_selector` selector now accepts text and options (sortable, sort_direction, column)
- `have_table_header` matcher for table header columns
- `find_table_header` finder for table header columns
- `click_table_header` action for clicking sortable table headers
- `find_action_item` finder for action item elements
- `have_action_item_link` matcher for action item links (with optional href)
- `within_action_item_dropdown` finder for action item dropdown menu
- `have_status_tag` matcher for status tag elements

### Changed
- `within_sidebar` now scopes within the sidebar section directly using `ancestor`
- `have_table_scope` now accepts an optional title as first positional argument and `selected:` keyword arg

## [0.3.3] - 2020-04-17
### Changed
- `batch_action_selector`, `click_batch_action` finds element by link text
Expand Down
12 changes: 12 additions & 0 deletions lib/capybara/active_admin/actions/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ def select_table_row(id: nil, index: nil)
selector = %(input[id^="batch_action_item_"])
find_all(selector, minimum: index + 1)[index].click
end

# @param text [String] exact text of the scope to click.
def click_table_scope(text)
selector = "#{table_scopes_container_selector} > #{table_scope_selector}"
page.find(selector, exact_text: text).click
end

# @param text [String] column header text.
# @param options [Hash] options passed to find_table_header.
def click_table_header(text, options = {})
find_table_header(text, options).find('a').click
end
end
end
end
Expand Down
21 changes: 16 additions & 5 deletions lib/capybara/active_admin/finders/layout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ def within_tab_body
end

def within_sidebar(title, exact: nil)
selector = sidebar_selector

within(selector) do
within_panel(title, exact: exact) { yield }
end
opts = Util.options_with_text(title, exact: exact)
sidebar = page.find("#{sidebar_selector} .sidebar_section #{panel_title_selector}", **opts).ancestor('.sidebar_section')
within(sidebar) { yield }
end

def within_panel(title, exact: nil)
Expand All @@ -35,6 +33,19 @@ def within_panel(title, exact: nil)
def within_modal_dialog
within(modal_dialog_selector) { yield }
end

# @param title [String] action item link text.
# @param exact [Boolean] whether to match the title exactly (default true).
# @return [Capybara::Node::Element] the found action item element.
def find_action_item(title, exact: true)
opts = exact ? { exact_text: title } : { text: title }
page.find(action_item_selector, **opts)
end

# @yield within action item dropdown menu list.
def within_action_item_dropdown
within("#{action_item_selector} .dropdown_menu_list_wrapper") { yield }
end
end
end
end
Expand Down
17 changes: 17 additions & 0 deletions lib/capybara/active_admin/finders/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,23 @@ def find_table_row(id: nil, index: nil)
find_all(selector, minimum: index + 1)[index]
end

# @yield within table>tfoot>tr
def within_table_footer
within('tfoot > tr') { yield }
end

# @param text [String] column header text.
# @param options [Hash]
# @option column [String, nil] column name override (defaults to text).
# @option sortable [Boolean] whether the column is sortable.
# @option sort_direction [String, nil] sort direction ('asc' or 'desc').
# @return [Capybara::Node::Element] the found table header element.
def find_table_header(text, options = {})
selector = table_header_selector(text, options)
opts = options.except(:column, :sortable, :sort_direction).merge(exact_text: text)
find(selector, **opts)
end

# @yield within table>tbody>tr>td
def within_table_cell(name)
cell = find_table_cell(name)
Expand Down
19 changes: 19 additions & 0 deletions lib/capybara/active_admin/matchers/form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@ def have_has_many_fields_for(association_name, options = {})
have_selector(selector, **options)
end

# @param label [String] label text of the date range filter.
# @param options [Hash]
# @option from [String, nil] expected value of the "from" field.
# @option to [String, nil] expected value of the "to" field.
# @option exact [Boolean, nil] whether to match field values exactly.
# @example
# expect(page).to have_fields_date_range('Created At', from: '2020-01-01', to: '2020-12-31')
#
def have_fields_date_range(label, options = {})
exact = options[:exact]
satisfy do |actual|
expect(actual).to have_selector('div.filter_date_range label', text: label)
container = actual.find('div.filter_date_range label', text: label).ancestor('div.filter_date_range')
base_name = container[:id].gsub(/_input\z/, '')
expect(container).to have_field("#{base_name}_gteq_datetime", with: options[:from].to_s, exact: exact)
expect(container).to have_field("#{base_name}_lteq_datetime", with: options[:to].to_s, exact: exact)
end
end

# @param text [String] button title
# @param options [Hash]
# @option selector [String, nil] optional selector to append
Expand Down
26 changes: 26 additions & 0 deletions lib/capybara/active_admin/matchers/layout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,32 @@ def have_batch_action(title, exact: true)
opts = Util.options_with_text(title, exact: exact)
have_selector(selector, **opts)
end

# @param title [String] action item link text.
# @param exact [Boolean] whether to match the title exactly (default true).
# @param href [String, nil] expected href attribute of the link.
# @param options [Hash] additional options passed to have_selector.
# @example
# expect(page).to have_action_item_link('New User')
# expect(page).to have_action_item_link('New User', href: new_admin_user_path)
#
def have_action_item_link(title, exact: true, href: nil, **options)
opts = exact ? { exact_text: title } : { text: title }
opts.merge!(options)
selector = "#{action_item_selector} > a"
selector += "[href=\"#{href}\"]" if href.present?
have_selector(selector, **opts)
end

# @param type [String, Symbol] status tag type (e.g. :yes, :no, :warning).
# @param options [Hash] additional options passed to have_selector (e.g. exact_text:).
# @example
# expect(page).to have_status_tag(:yes)
# expect(page).to have_status_tag(:error, exact_text: 'DOWN')
#
def have_status_tag(type, **options)
have_selector("span.status_tag.#{type}", **options)
end
end
end
end
Expand Down
34 changes: 26 additions & 8 deletions lib/capybara/active_admin/matchers/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,34 @@ def have_table_scopes(options = {})
have_selector("#{table_scopes_container_selector} > #{table_scope_selector}", **options)
end

# @param options [Hash]
# @option exact_text [String] title of scope
# @option counter [Integer,String,nil] counter value in brackets (nil if skipped)
# @option selected [Boolean] is scope active (default false)
def have_table_scope(options = {})
active = options.delete(:active)
# @param title [String, nil] exact title of scope to match.
# @param selected [Boolean] whether to match selected (active) scope only (default false).
# @param options [Hash] additional options passed to have_selector.
def have_table_scope(title = nil, selected: false, **options)
selector = "#{table_scopes_container_selector} > #{table_scope_selector}"
selector = active ? "#{selector}.selected" : "#{selector}:not(.selected)"
if title.nil?
selector = selected ? "#{selector}.selected" : "#{selector}:not(.selected)"
have_selector(selector, **options)
else
selector = selected ? "#{selector}.selected" : selector
have_selector(selector, exact_text: title.to_s, **options)
end
end

have_selector(selector, **options)
# @param text [String] column header text.
# @param options [Hash]
# @option column [String, nil] column name override (defaults to text).
# @option sortable [Boolean] whether the column is sortable.
# @option sort_direction [String, nil] sort direction ('asc' or 'desc').
# @example
# expect(page).to have_table_header('Full Name')
# expect(page).to have_table_header('Full Name', sortable: true)
# expect(page).to have_table_header('Full Name', sort_direction: :asc)
#
def have_table_header(text, options = {})
selector = table_header_selector(text, options)
opts = options.except(:column, :sortable, :sort_direction).merge(exact_text: text)
have_selector(selector, **opts)
end
end
end
Expand Down
15 changes: 13 additions & 2 deletions lib/capybara/active_admin/selectors/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,20 @@ def table_row_selector(record_id = nil)
%(tbody > tr[id$="_#{record_id}"])
end

# @param text [String] column header text.
# @param options [Hash]
# @option column [String, nil] column name override (defaults to text).
# @option sortable [Boolean] whether column is sortable.
# @option sort_direction [String, nil] sort direction ('asc' or 'desc').
# @return selector.
def table_header_selector
'thead > tr > th.col'
def table_header_selector(text = nil, options = {})
return 'thead > tr > th.col' if text.nil?

column = (options[:column] || text).to_s.tr(' ', '_').downcase
selector = "th.col.col-#{column}"
selector += '.sortable' if options[:sortable]
selector += ".sorted-#{options[:sort_direction].to_s.downcase}" if options[:sort_direction].present?
"thead > tr > #{selector}"
end

# @param column [String, nil] column name.
Expand Down
Loading