Search code examples
twitter-bootstrapunit-testingember.jsember-cliember-qunit

ember-cli unit test - controller opens bootstrap modal


Trying to write a unit test for my ember controller. It just changes a property and then opens a bootstrap modal. Having difficulty figuring out how to test that the modal actually gets opened. Not sure if this even belongs in a unit test or integration test. If it's not in my unit test it seems like it will be difficult to determine code coverage later down the line. Bootstrap version: 3.3.1, ember-cli version 0.1.5, node 0.10.33. Here is what I've tried to no avail:

1.

test('loginClick() opens modal', function(){
  var controller = this.subject();
  $('#login-modal').on('show.bs.modal', function(){
    equal(true, true, "the show.bs.modal event fired");
  });

  controller.send('loginClick', 'anything');
});

no assertion error

2.

test('loginClick() opens modal', function(){
  var controller = this.subject();
  andThen(function(){
    controller.send('loginClick', 'anything');
    stop();

    Ember.run.later(function(){
        start();
        equal($('#login-modal').hasClass('in'), true, "has the 'in' class");
    }, 500);
  });
});

andThen is not defined

Here is the controller:

loginClick: function(param){
  this.set('provider', param);//facebook or google

  $('#login-modal')
    .modal();
}

Any other suggestions or best practices on how to test this kind of thing will be appreciated.

p.s. Also tried adding this before click:

$.support.transition = false;

per someone's suggestion, but it does not disable the modal transition.


Solution

  • I had the same problem. I’m not sure this is the best solution, but I solved it by registering an async test helper before calling App.injectTestHelpers():

    Ember.Test.registerAsyncHelper 'waitForModalOpen', (app, modal) ->
      # If using QUnit < 1.16, you need to add stop().
      #stop()
      Ember.Test.promise (resolve, reject) ->
        modal.on 'shown.bs.modal', ->
          resolve()
          # If using QUnit < 1.16, you need to add start().
          #start()
    

    I then call it after clicking the button and before the assertions:

    modal = find '#testModal'
    click '#openModal'
    waitForModalOpen modal
    andThen ->
      strictEqual modal.attr('aria-hidden'), 'false', 'modal should be visible'
      strictEqual modal.hasClass('in'), true, 'modal should have .in class'
    

    Here is a JS Bin testcase. QUnit 1.16 supports returning promises from tests, so with this version, calling stop() and start() is not needed anymore: QUnit will wait for the andThen() promise to resolve.

    Edit: Use in ember-cli

    The ember-cli documentation has a section about writing your own test helpers.

    Create the helper as /tests/helpers/wait-for-modal-open.js:

    import Ember from "ember";
    
    export default Ember.Test.registerAsyncHelper('waitForModalOpen', function(app, modal) {
      return Ember.Test.promise(function(resolve, reject) {
        return modal.on('shown.bs.modal', function() {
          return resolve();
        });
      });
    });
    

    Then, add this line in /tests/helpers/start-app.js:

    import waitForModalOpen from './wait-for-modal-open';
    

    You also have to add "waitForModalOpen" in the "predef" array in /tests/.jshintrc to avoid JSHint errors.

    Finally, create the test as a file in /tests/integration:

    import Ember from "ember";
    import { test } from 'ember-qunit';
    import startApp from '../helpers/start-app';
    var App;
    
    module('Bootstrap Modal Open', {
      setup: function() {
        App = startApp();
        return visit('/');
      },
      teardown: function() {
        Ember.run(App, App.destroy);
      }
    });
    
    test('clicking the button should open a modal', function() {
      var modal;
      modal = find('#testModal');
      click('#openModal');
      waitForModalOpen(modal);
      return andThen(function() {
        strictEqual(modal.attr('aria-hidden'), 'false', 'modal should be visible');
        return strictEqual(modal.hasClass('in'), true, 'modal should have .in class');
      });
    });