Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rspec test requires sleep to work

When I run my 'webinar' specs alone they seem to always past, but if I try the whole suite it only passes one of the tests about 50% or the time. I tested this using the same seed each time to see if it had anything to do with the order in which the tests are being executed.

If I slow down my test by putting a sleep in the middle of it then it magically starts passing 100% again. Obviously I don't want to rely on a weak work-around like this and want to figure how to actually fix my problem. require "spec_helper"

require "spec_helper"

describe "ProgramManager::Webinars" do
  let(:program) { create(:program) }
  let(:superuser) { create(:superuser) }

  describe "#index" do
    before { login_as(superuser) }
    let(:delete) { 'Delete' }

    it "displays an edit and destroy link for all webinars" do
      w1, w2, w3 = create(:webinar, program: program), create(:webinar, program: program), create(:webinar, program: program)
      visit program_webinars_path(program)
      [w1, w2, w3].each do |webinar|
        expect(page).to have_link webinar.name, href: edit_program_webinar_path(program, webinar)
        expect(page).to have_link '', href: destroy_warnings_program_webinar_path(program, webinar)
      end
    end

    it "has a link to create a new webinar" do
      visit program_webinars_path(program)
      expect(page).to have_content 'New Webinar'
    end

    it "deletes a webinar", js: true do  #THIS IS THE TEST THAT SOMETIMES FAILS
      webinar = create(:webinar, program: program)
      visit program_webinars_path(program)
      all('.destroy').last.click
      wait_for_ajax
      sleep(1.second)         #THIS IS THE SLEEP THAT FIXES THE FAILURE
      expect { click_link delete }.to change(Webinar, :count).by(-1)
    end
  end

.

FactoryGirl.define do
  factory :webinar do
    program
    name "some name"
    url "some url"
    description "some description"
    speaker "some speaker"
    starts_at Time.now
  end
end

.

FactoryGirl.define do
  factory :program do
    contract
    program_manager factory: :user
    sequence(:name) { |n| "Program-#{n}" }
    description     { "Program description" }
    starts_at       { Time.now }
    ends_at         { Time.now + 10.days }
    color           { "#33f" }
    program_type    { "some program type" }
    verified        { false }
  end
end

.

<div class="col-md-4">
  <%= link_to "<span class='glyphicon glyphicon-plus'></span>".html_safe, new_program_webinar_path(@program), class: 'new-webinar', data: { toggle: 'tooltip', title: 'Add a Webinar' } %>
  <h4>Current Webinars</h4>

  <% if @webinars.empty? %>
    <p>There are currently no webinars to display.</p>
  <% else %>
    <table class="table table-condensed">
      <% @webinars.each do |webinar| %>
        <tr>
          <%= content_tag :td, class: pm_setup_classes(webinar, @webinar) do %>
            <%= link_to "<span class='glyphicon glyphicon-remove'></span>".html_safe, destroy_warnings_program_webinar_path(@program, webinar), class: 'destroy', data: { toggle: 'modal', target: '#ajax-modal' } %>
            <%= link_to webinar.name, edit_program_webinar_path(@program, webinar), class: 'webinar' %>
          <% end %>
        </tr>
      <% end %>
    </table>
  <% end %>

  <%= link_to 'New Webinar', new_program_webinar_path(@program), class: 'btn btn-success btn-block' %>
</div>

.

class ProgramManager::WebinarsController < ProgramManager::ApplicationController
  before_filter :authenticate_user!
  before_filter :webinars

  def new
    @webinar = @webinars.build
    clean_webinars
  end

  def create
    @webinar = @program.webinars.build(params[:webinar])
    clean_webinars
    if @webinar.save
      redirect_to program_webinars_path(@program), success: "Webinar successfully created"
    else
      render :new
    end
  end

  def edit
    @webinar = @program.webinars.find(params[:id])
  end

  def update
    @webinar = @program.webinars.find(params[:id])
    if @webinar.update(params[:webinar])
      redirect_to program_webinars_path(@program), success: "Webinar successfully updated"
    else
      render :edit
    end
  end

  def destroy
    @webinar = @program.webinars.find(params[:id])

    if @webinar.destroy
      redirect_to program_webinars_path(@program), success: "Webinar removed successfully"
    else
      render :index
    end
  end

  def destroy_warnings
    @webinar = @program.webinars.find(params[:id])
    render layout: false
  end

private

  def clean_webinars
    @webinars = @webinars.delete_if(&:new_record?)
  end

  def webinars
    @webinars = @program.webinars
  end
end

I am sorry there is so much code associated with this question. I'm just trying to provide as much info as I can since I have no idea where this bug is from or how to fix it

like image 527
Brian H Avatar asked Jan 23 '14 20:01

Brian H


1 Answers

The problem seemed to ultimately be a javascript fade in. The delete button we are trying to press is on a modal that fades in to alert you of the repercussions of your deletion and asks you to confirm. Our wait_for_ajax() helper waited until all active jQuery connections were resolved. The connections would finish so it would move on to the next line of code which told it to click a link on the delete link. The html had a delete link in it so Capybara can find it, but since it is actively fading in... the click doesn't work and the test fails!

like image 200
Brian H Avatar answered Sep 30 '22 15:09

Brian H