Skip to content

Instantly share code, notes, and snippets.

@jcieslar
Created March 19, 2015 16:33
Show Gist options
  • Save jcieslar/6491810f4fd9fada0ab3 to your computer and use it in GitHub Desktop.
Save jcieslar/6491810f4fd9fada0ab3 to your computer and use it in GitHub Desktop.
Capybara and selectize
page.execute_script("$('.selectize-input input').val('ber')")
sleep 0.5
page.execute_script("$('.selectize-input input').keyup()")
sleep 0.5
page.execute_script("$('.full-name').mousedown()")
# https://github.com/brianreavis/selectize.js/blob/master/src/selectize.js
@thbar
Copy link

thbar commented Apr 23, 2015

Thanks for sharing! Here is my final version:

  def add_tags_to_selectize(parent_selector, with:)
    fail "Array expected" unless with.is_a?(Array)
    selector = "#{parent_selector} + .selectize-control .selectize-input input".to_json.html_safe
    with.each do |tag|
      tag = tag.to_json.html_safe
      # type the data in the partially hidden placeholder
      page.execute_script("$(#{selector}).val(#{tag})")
      # make the selectize dropdown appear
      page.execute_script("$(#{selector}).keyup()")
      # click somewhere else to hide the dropdown and add the tag for good
      page.execute_script("$('body').mousedown()")
    end
  end

@jhirn
Copy link

jhirn commented May 6, 2015

Hey a friend sent me this. I figure I'd share I was able to get my tests to green with this snippet:

  page.execute_script("$('.wrapping-element select')[0].selectize.setValue('#{thing.id}')")

@christiannelson
Copy link

christiannelson commented Dec 23, 2016

This is where I landed... separate helpers for multiple=false and multiple=true selects:

# Select a single item from a selectized select input where multiple=false.
def selectize_single_select(key, value)
  # It may be tempting to combine these into one execute_script, but don't; it will cause failures.
  page.execute_script %Q{ $('.#{key} .selectize-input').click(); }
  page.execute_script %Q{ $('.#{key} .selectize-dropdown-content .option:contains("#{value}")').click(); }
end

# Select one or more items from a selectized select input where multiple=true.
def selectize_multi_select(key, *values)
  values.flatten.each do |value|
    page.execute_script(%{
      $('.#{key} .selectize-input input').val('#{value}');
      $('##{key}').selectize()[0].selectize.createItem();
    })
  end
end

@nicolas-besnard
Copy link

nicolas-besnard commented Jan 5, 2017

My 1 ct contribution:

def selectize_single_select(field, value)
  page.execute_script %Q{ $('.#{field} .selectize-input').click(); }
  page.execute_script %Q{ $('.#{field} .selectize-dropdown-content .option:contains("#{value}")').click(); }
end

def selectize_multi_select(field, *values)
  values.flatten.each do |tag|
    page.execute_script "$('.#{field} .selectize-input').click();"
    page.execute_script %Q{ $('.#{field} .selectize-dropdown-content').children("div:contains('#{tag}')").click(); }
    page.execute_script "$('body').mousedown()"
  end
end

@bunnymatic
Copy link

bunnymatic commented Jan 5, 2017

I came up with this slight mod on @christiannelson's solution. I was unable to use his as written because it is counting on the DOM structure as generated by SimpleForm. My app is using formtastic which does not put a class on the wrapper (instead it uses an id).

I've modified the helpers to make them a bit more generic so they work for both these form builders (and probably others). Instead of depending on the class/id of the field's wrapper class, they count on the fact that selectize builds it's DOM as a sibling of the base form field.

module SelectizeSelectHelper

  def find_selectized_control_js(key)
    %{ $('##{key}.selectized').next('.selectize-control') }.strip
  end

  # Select a single item from a selectized select input where multiple=false given the id for base field
  def selectize_single_select(key, value)
    # It may be tempting to combine these into one execute_script, but don't; it will cause failures.
    page.execute_script %{ #{find_selectized_control_js(key)}.find('.selectize-input').click(); }
    page.execute_script %{ #{find_selectized_control_js(key)}.find(".selectize-dropdown-content .option:contains(\\\"#{value}\\\")").click(); }
  end

  # Select one or more items from a selectized select input where multiple=true.
  def selectize_multi_select(key, *values)
    values.flatten.each do |value|
      page.execute_script(%{
      #{find_selectized_control_js(key)}.find('input').val("#{value}");
      $('##{key}.selectized')[0].selectize.createItem();
    })
    end
  end
end

Note: you'll see the quoting around value. I was having trouble where the "value" send it occasionally had an apostrophe (like O'Reilly). You may adjust the handling of value for your particular data.

@andre1810
Copy link

andre1810 commented Dec 16, 2017

To deselect an item simply use

def deselect_single_select(key, value)
  page.execute_script %Q{ #{find_selectized_control_js(key)}.find('.selectize-input .item:contains("#{value}") .remove').click(); }
end

@jonathanhoskin
Copy link

jonathanhoskin commented May 27, 2018

On large lists of options, the .click() method described above doesn't work, as selectize doesn't populate the fake options collection with all the options for performance reasons.

A potential hack around is to just initialize the selectize with a huge maxOptions:

$(el).selectize({
  maxOptions: 3000
})

@jjinkxy
Copy link

jjinkxy commented Mar 15, 2019

Leaving this here for any one who not only wants to add multiple but also create a new item. This worked for me.

# Example (model: teacher, attribute: student_list):
# fill_in_selectize_multi "teacher_student_list", with: %w(Jo Maren Bobby)

def fill_in_selectize_multi(field, options = {})
  selector = "'##{field}-selectized'"

  values = options[:with]
  values.each do |tag|
    page.execute_script "$(#{selector}).closest('.selectize-control').find(' .selectize-input').click();"
    page.execute_script("$(#{selector}).val('#{tag}')")
    page.execute_script("$(#{selector}).keyup().focus()")
    page.execute_script %{ $(#{selector}).closest('.selectize-control').find(' .selectize-dropdown-content').children("div:contains('#{tag}')").click(); }
    page.execute_script "$('body').mousedown()"
  end

@aleko-812
Copy link

Another version of the same.

  def selectize_single_select(selector, value)
    page.execute_script(%{
      var $select = $('#{ selector }').selectize();
      var selectize = $select[0].selectize;
      selectize.setValue(#{ value });
    })
  end
  def selectize_multi_select(selector, *values)
    page.execute_script(%{
      var $select = $('#{ selector }').selectize();
      var selectize = $select[0].selectize;
      selectize.setValue(#{ values });
    })
  end

To DRY this code a bit one could implement it as one method with extra-parameter. Like:

def fill_in_selectize(selector, multi=false, *values)
  ...
end

@bunnymatic
Copy link

Here's yet another version of the same. I recently did some shuffling of code such that jQuery was not in the global namespace. Instead it's imported as needed. So my test helpers no longer had access to jQuery. This moves the helper code to using vanilla with querySelector{All} instead of the jquery finder helpers.

module SelectizeSelectHelper
  delegate :execute_script, to: :page

  def find_selectized_control_js(key)
    %{ document.querySelector('##{key}.selectized').nextElementSibling }
  end

  def open_selectize_chooser(key)
    execute_script %{ document.querySelector('##{key}').selectize.open(); }
  end

  def close_selectize_chooser(key)
    execute_script %{ document.querySelector('##{key}').selectize.close(); }
  end

  # Select a single item from a selectized select input where multiple=false given the id for base field
  def selectize_single_select(key, value)
    # It may be tempting to combine these into one execute_script, but don't; it will cause failures.
    open_selectize_chooser(key)
    execute_script %{
                   var options = #{find_selectized_control_js(key)}.querySelectorAll('.option');
                   var option = Array.from(options).find((opt) => opt.innerHTML.match(/#{value}/));
                   option.click() }
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment