Testing Ember apps with Cucumber – THE PROBLEMS – The solution suite:



Testing Ember apps with Cucumber – THE PROBLEMS – The solution suite:

0 0


ember-cucumber-pres

Toronto EmberJS Meetup - Presentation on testing Ember with Cucumber

On Github trianglegrrl / ember-cucumber-pres

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
The gist (gem coming soon)
This presentation
Alaina Hardie