Skip to content

Commit

Permalink
very basic react_select v2 plugin implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
twalpole committed Oct 3, 2018
1 parent f455b6f commit b052cec
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 7 deletions.
11 changes: 5 additions & 6 deletions lib/capybara/node/pluginify.rb
Expand Up @@ -6,15 +6,14 @@ module Pluginify
def self.prepended(mod)
mod.public_instance_methods.each do |method_name|
define_method method_name do |*args, **options|
plugin_name = options.delete(:using) { |_using| session_options.default_plugin[method_name] }
if plugin_name
plugin_names = Array(options.delete(:using) { |_using| session_options.default_plugin[method_name] })
plugin_names.reduce(false) do |memo, plugin_name|
plugin = Capybara.plugins[plugin_name]
raise ArgumentError, "Plugin not loaded: #{plugin_name}" unless plugin
raise NoMethodError, "Action not implemented in plugin: #{plugin_name}:#{method_name}" unless plugin.respond_to?(method_name)
plugin.send(method_name, self, *args, **options)
else
super(*args, **options)
end

memo || plugin.send(method_name, self, *args, **options)
end || super(*args, **options)
end
end
end
Expand Down
52 changes: 52 additions & 0 deletions lib/capybara/plugins/react_select.rb
@@ -0,0 +1,52 @@
# frozen_string_literal: true

module Capybara
module Plugins
class ReactSelect
def select(scope, value, **options)
sel = find_react_select(scope, value, options)
sel.click.assert_matches_selector(:css, '.select__control--is-focused')
scope.find(:react_select_option, value).click
end

def unselect(scope, value, **options)
select = find_react_select(scope, value, options)
raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select.has_css?('.select__value-container--is-multi')

scope.find(:css, '.select__multi-value', text: value).find(:css, '.select__multi-value__remove').click
end

private

def find_react_select(scope, value, from: nil, **options)
if from
scope.find(:react_select, from, options.merge(visible: false))
else
select = scope.find(:option, value, options).ancestor(:css, 'select', visible: false)
select.find(:xpath, XPath.next_sibling(:span)[XPath.attr(:class).contains_word('react-select')][XPath.attr(:class).contains_word('react-select-container')])
end
end
end
end
end

Capybara.add_selector(:react_select) do
xpath do |locator, **_options|
XPath.css('.select__control')[
XPath.following_sibling(:input)[XPath.attr(:name) == locator].or(
XPath.following_sibling(:div)[XPath.child(:input)[XPath.attr(:name) == locator]]
)
]
end
end

Capybara.add_selector(:react_select_option) do
xpath do |locator|
xpath = XPath.anywhere(:div)[XPath.attr(:class).contains_word('select__menu')]
xpath = xpath.descendant(:div)[XPath.attr(:class).contains_word('select__option')]
xpath = xpath[XPath.string.n.is(locator.to_s)] unless locator.nil?
xpath
end
end

Capybara.register_plugin(:react_select, Capybara::Plugins::ReactSelect.new)
1 change: 1 addition & 0 deletions lib/capybara/plugins/select2.rb
Expand Up @@ -12,6 +12,7 @@ def select(scope, value, **options)
def unselect(scope, value, **options)
select2 = find_select2(scope, value, options)
raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select2.has_css?('.select2-selection--multiple')

select2.click
option = scope.find(:select2_option, value)
option[:"aria-selected"] == 'true' ? option.click : select2.click
Expand Down
43 changes: 42 additions & 1 deletion lib/capybara/spec/session/plugin_spec.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true

require 'capybara/plugins/select2'
require 'capybara/plugins/react_select'

Capybara::SpecHelper.spec 'Plugin', requires: [:js] do
Capybara::SpecHelper.spec 'Plugin', requires: [:js], focus_: true do
before do
@session.visit('https://select2.org/appearance')
end
Expand Down Expand Up @@ -83,4 +84,44 @@
@session.select 'Miss', from: 'Title', using: nil
expect(@session.find_field('Title').value).to eq('Miss')
end

context 'with react select 2' do
before do
@session.visit('https://react-select.com/home')
end

it 'should select an option' do
@session.select 'Red', from: 'color', using: :react_select
expect(@session).to have_field('color', type: 'hidden', with: 'red')
end

it 'should remain selected if called twice on a single select' do
@session.select 'Blue', from: 'color', using: :react_select
@session.select 'Blue', from: 'color', using: :react_select
expect(@session).to have_field('color', type: 'hidden', with: 'blue')
end

it 'should work with multiple select' do
@session.within @session.first(:css, 'div.basic-multi-select') do
@session.select 'Green', from: 'colors', using: :react_select
@session.select 'Silver', from: 'colors', using: :react_select
expect(@session).to have_field('colors', with: 'green', type: 'hidden')
expect(@session).to have_field('colors', with: 'silver', type: 'hidden')
end
end

it 'should unselect an option' do
@session.within @session.first(:css, 'div.basic-multi-select') do
@session.select 'Green', from: 'colors', using: :react_select
expect(@session).to have_field('colors', with: 'green', type: 'hidden')
@session.unselect 'Green', from: 'colors', using: :react_select
expect(@session).to have_no_field('colors', with: 'green', type: 'hidden')
end
end

it 'should work with name' do
@session.select 'Purple', from: 'color', using: :react_select
expect(@session).to have_css('input[type=hidden][name=color]', visible: false) { |el| el.value == 'purple' }
end
end
end

0 comments on commit b052cec

Please sign in to comment.