Testing Ember apps with Cucumber



THE PROBLEMS


Ember-Testing for developers, not puny mortals

Ember-Testing has limited interaction features

Ember-Testing isn't for integration testing (Rails + Ember)

App has non-Ember pages (login, redirection)

Automated testing is faster than you

The solution suite:

Cucumber/Gherkin

Spreewald

Making Capybara Ember-aware

The readability solution

Scenario: Creating a post displays the new post
  Given I visit New Posts
  And I fill in "Title" with "A new post"
  And I fill in "Author" with "John Doe"
  And I press "Create"
  Then I should see "A new post" within "h1"
  And I should see "John Doe" within "a[rel=author]"    
vs.
test("creating a post displays the new post", function(){
  visit("/posts/new");
  fillIn(".post-title", "A new post");
  fillIn(".post-author", "John Doe");
  click("button.create");
  andThen(function() {
    ok(find("h1:contains('A new post')").length, "The post's title should display");
    ok(find("a[rel=author]:contains('John Doe')").length, "A link to the author should display");
  });
});

The Flexibility Solution

Spreewald: Set of common, elegant cucumber steps

patiently do...end : BEST THING EVARRR


patiently do
  click_link(link)
end
								
e.g.

When /^(?:|I )follow "([^"]*)"$/ do |link|
  patiently do
    click_link(link)
  end
end
								

The biggest problem


Automated tests are faster than you

Ember runloop doesn't always complete before a Cucumber step fires

Capybara becomes Ember-aware



def wait_for_ember_application_to_load
  using_wait_time 20 do
   patiently do
    find ".ember-application"
   end
  end
  wait_for_ember_run_loop_to_complete # Then a miracle happens
end
									

Add jQuery to track Ajax request status



$(function() {
  var body, doc;
  body = $('body');
  doc = $(document);
  doc.ajaxStart(function() {
    return body.addClass('ajax-in-progress').removeClass('ajax-quiet');
  });
  return doc.ajaxStop(function() {
    return body.addClass('ajax-quiet').removeClass('ajax-in-progress');
  });
});

h4x041ng 3mb3r


Dig around; discover:

Ember.run.hasScheduledTimers()
Ember.run.currentRunLoop

...which leads to...



def wait_for_ember_run_loop_to_complete
  2000.times do #this means up to 20 seconds
    return if page.evaluate_script "'undefined' == typeof window.jQuery"
    return if page.evaluate_script "$('body').hasClass('ajax-quiet') && (typeof Ember === 'object') && !Ember.run.hasScheduledTimers() && !Ember.run.currentRunLoop"
    sleep 0.01
  end
end
											

Break it down



jQuery.active === 0 && // Is there a pending Ajax request
(typeof Ember === 'object') && // Does this page have Ember?
!Ember.run.hasScheduledTimers() &&
!Ember.run.currentRunLoop
												    

Now tag your features in Cucumber





AfterStep '@ember-fuckery' do
  wait_for_ember_run_loop_to_complete
end
												

Voila! 


Caveats


Still not perfect, but mo' betta'

Semi-hacky solution

Ember API not public

Links


Spreewald
https://github.com/makandra/spreewald

The gist (gem coming soon)
http://get.pn/ember-cucumber

This presentation
http://trianglegrrl.github.io/ember-cucumber-pres

Alaina Hardie
alaina@precisionnutrition.com
@trianglegrrl