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()
...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
Caveats
Still not perfect, but mo' betta'
Semi-hacky solution
Ember API not public
Links
Spreewald
The gist (gem coming soon)
This presentation
Alaina Hardie