Capybara integration tests with jquery.selectize
Asked Answered
A

7

10

How can I write a capybara integration test with a form using jquery.selectize?

I'd like to test a user entering a couple of values.

Augusto answered 29/4, 2014 at 17:55 Comment(4)
Explanations for -1s are always welcome.Catima
yeah, it's always annoying if you don't have a comment for not really obvious downvote =\ Probably, we should propose something like minusing reputation for downvoting without a commentIgal
There has been discussion on thisCatima
I would imagine it has to do with the lack of detail in the post. Regardless, this is a question I'm struggling with at the moment.Directions
P
6

Most of the answers here change stuff from beneath the covers and aren't the same as the users interaction. Here is a version which is close to the what the user does.

# waits for the text to show up in autocomplete and then selects it
def selectize_select(selectize_class, text)
  find("#{selectize_class} .selectize-input input").native.send_keys(text) #fill the input text
  find(:xpath, "//div[@data-selectable and contains(., '#{text}')]").click #wait for the input and then click on it
  # convert css selector to xpath selector using Nokogiri::CSS.xpath_for
end
Pronunciation answered 17/5, 2015 at 15:32 Comment(0)
M
5

I created a helper that I mix in to my capybara feature specs:

module PageSteps
  def fill_in_selectized(key, *values)
    values.flatten.each do |value|
      page.execute_script(%{
        $('.#{key} .selectize-input input').val('#{value}');
        $('##{key}').selectize()[0].selectize.createItem();
      })
    end
  end
end

And here's an example of how it is used:

# Single value
fill_in_selectized('candidate_offices', 'Santa Monica')

# Multiple values
fill_in_selectized('candidate_offices', ['San Francisco', 'Santa Monica'])

The first parameter is a "key" and works in our application given our markup. Depending on your markup, you might need a little tweak. This requires a Javascript enabled capybara driver (we use poltergeist).

Mullane answered 30/7, 2014 at 21:32 Comment(0)
E
2

None of the above worked reliably for me in 2022, here's what I use now:

def selectize_select(option_name, from:)
  page.execute_script <<~JS
    const id = $('label:contains(#{from.to_json})').attr('for').replace(/-selectized$/, '')
    const selectize = $(`#${id}`).selectize({})[0].selectize;
    selectize.setValue(selectize.search(#{option_name.to_json}).items[0].id);
  JS
end

It matches on the Label and option names instead of input ID and option values:

selectize_select 'Capybara', from: 'Your favorite animal'
Excurvature answered 17/5, 2022 at 19:59 Comment(0)
A
1

The API allows it but the options need to be added first before the value can be set:

var selectize = $('.selector')[0].selectize
selectize.addOptions([{text: 'Hello', value: 'Hello'}, {text: 'World', value: 'World'}])
selectize.setValue(['Hello', 'World'])
Augusto answered 30/4, 2014 at 20:44 Comment(2)
Having searched far-and-wide for answers, this route was the best for me. The biggest downside I see to this approach is that it bypasses my Ajax loading. But, to test that selectize is working, and that values are passing to the form, this worked for me!Floorwalker
One caveat, if you're initializing selectize with valueField and labelField, your addOptions hash needs to be modified to match your incoming data.Floorwalker
G
1

This is my helper I mix in with my feature specs. It tweaks and extends Christian's answer.

  • It addresses both select and text input types
  • It reads like the regular Capybara fill_in method
  • It can accept any identifier that works for find_field to find the input. So, you can use the label's text, the element's id or the element's name to find the element.

The only assumption this code makes is that your text input has a name attribute (which, of course, the Rails input helpers add automatically)

def selectize(key, with:)
  field = page.find_field(key, visible: false)
  case field.tag_name
  when "select"
    page.execute_script(%{
      $("select[name='#{field["name"]}']")
        .selectize()[0].selectize.setValue(#{Array(with)});
    })
  else
    Array(with).each do |value|
      page.execute_script(%{
        $("input[name='#{field["name"]}']")
          .next()
          .find(".selectize-input input").val('#{value}')
          .end()
          .prev()
          .selectize()[0].selectize.createItem();
      })
    end
  end
end

Example usage:

selectize "Single-choice field", with: "Only option"
selectize "Multi-choice field", with: ["Option A", "Option B"]
Goal answered 9/12, 2014 at 4:6 Comment(0)
A
0

selectize uses all key events (keydown, keypress, keyup) to provide a great UI, but doesn't seem to provide an easy way to set data from javascript.

One solution is to use the syn.js library to trigger the right key events. Here's a helper that works:

def fill_in_selectize_area selector, options
  # Syn appears to require an id, so assign a temporary one
  @selectize_unique_id ||= 0
  unique_id = "temp_selectize_id_#{@selectize_unique_id +=1}"
  with = options.fetch(:with)
  page.execute_script %Q{
    var selectize = $(#{selector.to_json})[0].selectize;
    var type = #{with.to_json}.join(selectize.settings.delimiter) + '\t';
    selectize.$control_input.attr('id', #{unique_id.to_json});
    Syn.click({}, #{unique_id.to_json}).delay().type(type);
  }
  # make sure that it worked *and* that it's finished:
  page.should have_content with.join('×') << '×' 
end

# example use:
fill_in_selectize_area '[name="blog[tags]"]', with: ['awesome subject', 'other tag']

Note that the delay is needed because focussing on the input isn't instant.

Augusto answered 29/4, 2014 at 17:55 Comment(1)
Difficult not to find a -1 on this answer insulting. Dislike the question if you like, but assuming it is a valid question, this can only be a good answer, no?Catima
S
0

I had various methods for filling in selectize input, but none of them worked with the case when options where loaded dynamically based on user input. It was needed because select otherwise would have few thousands of options and the page would load quite slow.

So here's the version that finally worked (it's based on most voted answer in this thread but with improved CSS selectors)

def fill_in_selectize(key, options = {})
    # fill the input text
    find("##{key} + .selectize-control .selectize-input input").native.send_keys(options[:with])
    # wait for the input and then click on it
    find(:xpath, "//div[@data-selectable and contains(., '#{options[:with]}')]").click
  end
Sepia answered 9/1 at 20:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.