breaking into the unknown…

1 Comment

jQuery Slider drag drop with Capybara

We have used jQuery Slider in one of our application, which resize the images within our library when user move it left and right. Since the no of images we can show per page, varies as user moves the slider, we have hooked a ajax call to stop event of slider which reset the per_page value in controller.

The code look as below:

JS code:

    var setupSlider = function () {

        var sl_value = $.cookie("sl_value");
        if (sl_value === undefined) {
            savedSliderValue = 0;
        } else {
            savedSliderValue = parseInt(sl_value);

        $(".size-slider .slider").slider({
            range: "min",
            value: savedSliderValue,
            min: -50,
            max: 0,
            slide: function (event, ui) {
                var fraction = (1 + ui.value / 100),
                        newWidth = originalWidth * fraction;

                $(".slide-library .slide").width(newWidth);
            stop: function (event, ui) {
                var sl_value = $(".size-slider .slider").slider("option", "value");
                $.cookie("sl_value", sl_value);
                var expansion_factor = (0 - ui.value);
                    type: "GET",
                    url: 'desktop.js',
                    data: {
                        expansion_factor: expansion_factor
                    contentType: 'script'

Controller Logic look as Below:

    #the slide_per_page will change on slide resizing. default is 12
    cookies[:slide_per_page] ||= 12
    cookies[:slide_per_page] = params[:expansion_factor].present? ? (12 + (12 * params[:expansion_factor].to_i/50)) : cookies[:slide_per_page]
    #reset page number to 1 as library resizing reinit the pagination
    page_number = params[:expansion_factor].present? ? 1 : current_page
    @library_slides =  Slide.all.paginate(:page => page_number, :per_page => cookies[:slide_per_page])


Now the test case, need to simulate the drag drop behaviour of slider.

Problem :

We are using Capybara which do give drag_to method to simulate drag and drop, but it take argument as source and target i,e you can drop one element on other element, But here there is no such distinction for the Slider as User can drag it anywhere on slider length, left or right.


Capybara internally use Selenium and it have drag_and_drop_by method which simulate drag and drop as offset i,e as X and Y coordinate . So we will extend Capybara to have our custom method.

Well we know the solution, so lets move ahead. Now the question is where to put our code, so that Capybara pick it up and is then available in our Rspec test cases.

All Rspec configuration is loaded from spec_helper.rb file whenever, you run your Rspec tests. My spec_helper.rb file look as below

require 'database_cleaner'
require 'factory_girl_rails'
require 'support/capybara'
require 'warden'
require 'devise'

RSpec.configure do |config|
  config.include Capybara::DSL
  #many more line of configuration...

So you can see that in the third line we have required ‘support/capybara’ (complete path – spec/csupport/capybara) , this file contain capybara specific configuration and initialize Capybara whenever Rspec is run. We will add our extension code at bottom of this file


module CapybaraExtension
  def drag_by(right_by, down_by)
    base.drag_by(right_by, down_by)

module CapybaraSeleniumExtension
  def drag_by(right_by, down_by)
    driver.browser.action.drag_and_drop_by(native, right_by, down_by).perform

::Capybara::Selenium::Node.send :include, CapybaraSeleniumExtension
::Capybara::Node::Element.send :include, CapybaraExtension

So now we have a drag_by method which perfom drag and drop on element with offset.Using it we can now simulate the jQuery Slider behaviour. Finally The Rspec test case look as below

  it "it should reinit the pagination on slide resizing" do
    visit slides_desktop_path(@project)
    #pagination set to 12 and we have around 33 records so including 2,3 and next there should be 3 link. 
    #Note that in pagination previous and 1 is default and so disabled when page loads
    expect(find("div.pagination")).to have_selector(".next_page", :count => 1)
    expect(find("div.pagination")).to have_selector(".previous_page", :count => 1)
    expect(find("div.pagination")).to have_selector("em", :count => 1)
    expect(find("div.pagination em.current").text).to eq("1")
    expect(find("div.pagination")).to have_selector("a", :count => 3)
    first(".ui-slider-handle").drag_by(-50, 0)
    sleep 2
    expect(find("div.pagination")).to have_selector("a", :count => 2)
    first(".ui-slider-handle").drag_by(50, 0)
    sleep 2
    expect(find("div.pagination")).to have_selector("a", :count => 3)

That’s all. Run your test and see it passing 🙂