Search code examples
ruby-on-railscapybararspec-rails

find method is not waiting for elements that will appear from a json file


Currently, i have this test:

spec/features/cardx/cardx_spec.rb

it 'can edit cardx with the form' do
  product = FactoryGirl.create(:product)
  store = FactoryGirl.create(:store)
  click_link(class: 'link-record-edit')
  page.find(class: 'record-form')
  fill_in 'field-start-date', with: Date.tomorrow.to_s
  page.find(id: 'cardx-edit-form').fill_in 'field-end-date', with: (Date.tomorrow + 7).to_s
  page.find(id: 'cardx-edit-form').select(product.name, from: { id: 'field-product' })
  page.find(id: 'cardx-edit-form').select(store.name, from: { id: 'field-store' })
  page.find(id: 'cardx-edit-form').click_button(t('button.save'))
  changed = Cardx.find(@cardx.id)
  result = changed.start_date == Date.tomorrow && changed.end_date == (Date.tomorrow + 7) && changed.product.name == product.name && changed.store.name == store.name
  expect(result).to eq(true)
end

This test is supposed to check if i can submit a form, but this form is being rendered and added to the DOM via an AJAX request that happens when i click on a link in the page. This render is coming via a JSON file. When I'm using the browser normally as a user, everything works, but when i run this test (even when i set max_timeout with ridiculous values like 50 seconds), it immediately fails saying that it cannot find 'record-form' element. Isn't find supposed to wait until max_timeout runs out?

What apparently happens in my test is that it isn't trying to load and render the JSON file with the form code. Is there a solution to this problem?

EDIT: Code I have so far

javascript that loads form

$(document).on('click', '.link-record-edit', function(e) {
    //diferentiate between multiple searches on page
    setRecord(this);

    //set selected id
    record[record_type].selected_id = $(this).attr('data-id');

    //mark selected row
    markSelectedRow();

    if(record_type !== "default") {
       //if in tab, open modal
      $(record[record_type].form_placeholder).modal();
    }

    //get ID for edition
    (loadForm || console.error)(e);

    e.preventDefault();
  });

function loadForm(ev, callback) {
  //validate
  if (typeof record[record_type].path_edit === 'undefined') {
    return console.error("No definition for 'record[record_type].path_edit' on your page's js");
  }

  target = [];
  _event = null;
  if(typeof event === 'undefined' || event === null) {
    _event = ev;
  } else {
    _event = event;
  }
  if(_event.srcElement !== undefined && _event.srcElement.className !== undefined) {
    target = _event.srcElement.className.split(" ");
  } else if(_event.target !== undefined && _event.target.className !== undefined) {
    target = _event.target.className.split(" ");
  }

  //show form block
  $form_placeholder = "";
  if(record_type === "default") {

    //is nome cases, with tabs, preselect first tab
    $('[role="tabpanel"] a:first').tab('show');

    //if selected in default list, empty tab placeholders
    $('.tab-pane [data-placeholder="true"]').html('');

    //add sidebar form
    $('.width-switcher').filter(':visible').removeClass('col-sm-12').addClass('col-sm-6');
    $('.width-switcher:hidden').fadeIn('fast');
    $form_placeholder = record[record_type].form_placeholder;

  } else if(target.indexOf("link-record-edit") > -1 || target.indexOf("link-record-new") > -1) {
    $form_placeholder = record[record_type].form_placeholder + ' .modal-content';
  }

  //last case cenario
  if(record[record_type].inline_placeholder != null && $($form_placeholder).length == 0) {
    $form_placeholder = record[record_type].form_placeholder;
  }

  loadSpinner($form_placeholder);


  //enable/disable tabs
  if(record[record_type].selected_id > 0 && record_type === "default") {
      $(".nav-tabs").show();
  } else if(record_type === "default") {
      $(".nav-tabs").hide();
  }

  // Remove temporary buttons from hooks
  $('.temporary').remove();

  // pass var to form of view/edit mode
  $edit_mode = $('.editor-mode.active').length;

  //get form data and place it on placeholder
  $.ajax({
    url: record[record_type].path_edit.replace(':id', record[record_type].selected_id),
    type: "GET",
    dataType: "html",
    data: { parent_id: record[record_type].parent_id, edit_mode: $edit_mode },
    success: function(result) {
      //add form
      $($form_placeholder).fadeOut(100, function() {
        $(this).html(result).fadeIn(400, function() {
          //as it fades in, set title and prettify form objects

          //change title
          setFormTitle();

          //refresh scripts for forms
          formPluginRegresh();

          //new html on page, redistribute record types attributes
          distributeRecordAttributes();

          removeSpinner();

          //callback if anyone waits
          if(callback != null) { callback(); }
        });
      });
    },
    error: function(jqXHR, textStatus, errorThrown) {
      showMessage('error', $.parseJSON(jqXHR.responseText));
    }
  });
}

function update_inline_combos() {
  //case inline, update dropdownlist
  if($(record[record_type].form_placeholder).parents('.overflow').length > 0) {
    //check existance of drops
    drops = $('.input-group[data-record="'+record_type+'"]').find('> select');

    //fill in new data
    $.ajax({
        url: record[record_type].path_list_drop,
        type: "GET",
        dataType: "html",
        success: function(result) {
          $.each(drops, function(index, value) {
            //element's id
            $id = $(value).attr('id');

            //add new data with :id, :name
            setBoot('#'+$id, result);
          });
        },
        error: function(jqXHR, textStatus, errorThrown) {
          showMessage('error', $.parseJSON(jqXHR.responseText));
        }
    });
  }
}

HTML I have

index.html

<div class="col-sm-6 col-xs-12 width-switcher not-visible">
        <div class="box-container">
            <div class="box-header">
                <span class="title" id="edit-cardx-title">&nbsp;</span>

                <!-- Split button -->
                <button class="btn btn-xs btn-default pull-right close-width-switcher" type="button">
                    <i class="fa fa-close"></i>
                </button>
            </div>

            <div class="box-content no-padding">
                <div id="placeholder-form-cardx">
                    <!-- FORM PLACEHOLDER -->
                </div>
            </div>
        </div>
    </div>

_form.etml (partial that will be put on placeholder)

<%= render partial: 'partials/form_objects/form_title', format: :erb, locals: { optional: true } %>

<%= simple_form_for(@cardx, remote: true,
    url: url_for(action: 'update_or_create', controller: 'cardxes', format: 'json'),
    html: { method: 'post', id: 'cardx-edit-form' },
    authenticity_token: true)  do |f| %>

    <%= f.error_notification %>
    <%= f.hidden_field :id %>

    <div class="form-inputs form-group">
        <div class="col-lg-2 col-md-3 col-sm-4 control-label">
            <%=t("label.start_date")%>
        </div>
        <div class="col-lg-10 col-md-9 col-sm-8">
            <%= f.input :start_date, as: :string,
            input_html: {class: "date-picker day-datepicker", id: "field-start-date"},
            label: false, required: true, class: "form-control" %>
        </div>
    </div>

    <div class="form-inputs form-group">
        <div class="col-lg-2 col-md-3 col-sm-4 control-label">
            <%=t("label.end_date")%>
        </div>
        <div class="col-lg-10 col-md-9 col-sm-8">
            <%= f.input :end_date, as: :string,
            input_html: {class: "date-picker day-datepicker", id: "field-end-date"},
            label: false, required: true, class: "form-control" %>
        </div>
    </div>
    <!--
    <div class="form-inputs form-group">
        <div class="col-lg-2 col-md-3 col-sm-4 control-label">
            <%#=t("label.end_date")%>
        </div>
        <div class="col-lg-10 col-md-9 col-sm-8">
          <%= f.collection_select :store_id,
              Store.to_collection.list, :id, :name,
              { include_blank: t("form.choose") },
              { class: "required" } %>
        </div>
    </div>
    -->
    <div class="form-inputs form-group">
        <div class="col-lg-2 col-md-3 col-sm-4 control-label">
            <%=t("label.store.one")%>
        </div>
        <div class="col-lg-10 col-md-9 col-sm-8">
          <%= f.collection_select :store_id,
              Store.to_collection.list, :id, :name,
              { include_blank: t("form.choose") },
              { class: "required selectpicker", id: "field-store" } %>
        </div>
    </div>

    <div class="form-inputs form-group">
        <div class="col-lg-2 col-md-3 col-sm-4 control-label">
            <%=t("label.product.one")%>
        </div>
        <div class="col-lg-10 col-md-9 col-sm-8">
          <%= f.collection_select :product_id,
              Product.to_collection.list, :id, :name,
              { include_blank: t("form.choose"), required_field: true },
              { class: "selectpicker required", id: "field-product" } %>
        </div>
    </div>

    <script>
    $(function() {
      $('.selectpicker').selectpicker({
    
        liveSearch: true,
        liveSearchNormalize: true,
    
        size: 6,
    
        width: '100%',
        dropupAuto: true,
        actionsBox: false
    
      });
    });
    </script>

    <%= render partial: "partials/form_objects/form_actions",
               format: :erb,
               locals: { f: f, record: @cardx } %>

<% end %>

Thank you in advance

EDIT:

I'm using this test (for simplicity) to detect whether the form is loaded and i get ' Unable to find css "#cardx-edit-form" '

it 'show edit form when a cardex record is clicked' do
      page.find('.link-record-edit', match: :first).click
      page.find('#cardx-edit-form', wait: 50)
      expect(page).to have_selector('#cardx-edit-form')
    end

Solution

  • The fact that your test isn't tagged with js: true metadata, and that no waiting behavior is happening points to the probability that you're using the rack_test driver for this test. The rack_test driver doesn't support JS and hence doesn't do any waiting either (since without JS support there are no asynchronous actions to wait for). You should be able to confirm that from the stacktrace of your error which will probably reference files with 'capybara/rack_test' in their name. See - https://github.com/teamcapybara/capybara#drivers and https://github.com/teamcapybara/capybara#using-capybara-with-rspec