Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Test Driven Ember.js

Test Driven Ember.js

Ember.js is JavaScript framework for building feature rich and very responsive applications. This session will provide an overview of Ember.js and demonstrate test-driven development of single-page web applications.

In this session, I will present a walkthrough of Ember.js core features. I will showcase a test-driven development of Ember.js application using Mocha. I will also explain Ember.js’ data bindings that allow for creation of views that update automatically in response to model changes. I will demo ease of Ember.js and Rails integration. Finally, I will utilize Ember.js components to create reusable UI elements.

In summary, Ember.js is a great framework for creating complex single-page applications. Attendees will leave the talk with a solid understanding of Ember.js test-driven development process.

AntiTyping

March 18, 2014
Tweet

More Decks by AntiTyping

Other Decks in Programming

Transcript

  1. jQuery everywhere • Low-level DOM modification • Copy and paste

    code • Lack of structure • Maintenance nightmare • Hard to test @AntiTyping
  2. Tools • Node.js • npm • Grunt • Bower •

    Yeoman • Lineman @AntiTyping
  3. Installation • brew install nodejs • npm install -g yo

    • npm install -g generator-ember @AntiTyping
  4. yo ember • mkdir EmberApp && cd $_ • yo

    ember • yo ember:controller create a new web app @AntiTyping
  5. Mocha/Chai • Behavior-driven development framework • Specs for your JavaScript

    code • Write expectations • Uses matchers @AntiTyping
  6. Mocha Suites describe("A suite", function() { var flag; ! beforeEach(function()

    { flag = true; }); ! it("contains spec with an expectation", function() { expect(flag).should.equal(true); }); }); @AntiTyping
  7. Chai Expectations describe("A suite", function() { it("contains spec with an

    expectation", function() { expect(true).to.equal(true); }); }); @AntiTyping
  8. Chai Matchers foo.should.be.a('string'); foo.should.equal('bar'); foo.should.have.length(3); tea.should.have.property('flavors') .with.length(3); ! expect(foo).to.be.a('string'); expect(foo).to.equal('bar');

    expect(foo).to.have.length(3); expect(tea).to.have.property('flavors') .with.length(3); ! assert.typeOf(foo, 'string'); assert.equal(foo, 'bar'); assert.lengthOf(foo, 3) assert.property(tea, 'flavors'); assert.lengthOf(tea.flavors, 3); expect(bar).toThrow(); @AntiTyping
  9. Features •Display list of tasks •Add a new task •Mark

    task as done •Add a new task with a priority •Filter tasks by priority •Search tasks •Task counter @AntiTyping
  10. Install dependencies • rvm install 2.0 • gem install compass

    • brew install nodejs • npm install -g bower • npm install -g yo • npm install -g generator-ember • npm install -g karma @AntiTyping
  11. EmberDo front-end • git clone [email protected]:AntiTyping/EmberDo.git • cd EmberDo •

    npm install • bower install • cd test && bower install && cd .. • grunt server @AntiTyping
  12. User story As a user, I should be able to

    see list of tasks, so I can choose the next task to do ! Scenario: Display list of tasks When I navigate to the task list Then I should see the list of tasks @AntiTyping
  13. Scenario describe("Task List", function() { it('should display list of tasks',

    function() { expect($('tr.task')).to.have.length(3); }); }); @AntiTyping
  14. Model Todos.Todo = DS.Model.extend({ name: DS.attr("string"), priority: DS.attr("string") }); !

    Todos.Todo.FIXTURES = [ {id: 1, name: "Hight priority task", priority: "high"}, {id: 2, name: "Medium priority task", priority: "medium"}, {id: 3, name: "Low priority task", priority: "low"} ]; @AntiTyping
  15. Feature #1 Summary • Scenario • List of tasks (#each)

    • Todos route (TodosRoute) • Todo model • No low level DOM manipulation (#each) @AntiTyping
  16. User Story As a user, I should be able to

    add a new task, so I can update my list of tasks ! Scenario: Add a valid new task When I add a valid new task Then I should see the task in the list ! Scenario: Add an invalid new task When I add an invalid new task Then I should see an error message @AntiTyping
  17. :) Path Scenario describe("Add a new task", function() { describe("when

    the new task is valid", function() { it("should add it to the list", function() { expect($('tr.task:last').text()).to.match(/New task/); expect($('tr.task')).to.have.length(4); }); it('should clear the new task box', function() { expect($('.task-name').val()).to.equal(''); }); it("should not display an error message", function() { expect($('div.alert').hasClass('hide')).to.be.true; }); }); @AntiTyping
  18. :( Path Scenario describe("when the new task is invalid", function()

    { it("should leave the task list unchanged", function() { expect($('tr.task')).to.have.length(3); }); it("should display an error message", function() { expect($('div.alert').hasClass('hide')).to.be.false; }); @AntiTyping
  19. TodosController Todos.TodosController = Ember.ArrayController.extend({ actions: { createTodo: function() { var

    name = this.get('newName'); ! . . . ! var todo = this.store.createRecord('todo', { name: name }); this.set('newName', ''); ! todo.save(); } } }); @AntiTyping
  20. Feature #2 Summary • Scenarios • Added {{input}} • Added

    {{action}} • Dynamic list (#each) @AntiTyping
  21. User Story As a user, I should be able to

    mark tasks as done, so I can keep track of completed work ! Scenario: Mark task as done When I mark a task as done Then the task should be remove from the list ! @AntiTyping
  22. Scenario describe("Marking task as done", function() { it("should remove the

    task from the task list", function() { $('button.js-done:last').click(); expect($('tr.task')).to.have.length(2); }); }); @AntiTyping
  23. removeTodo() Todos.TodoController = Ember.ObjectController.extend({ actions: { removeTodo: function() { var

    todo = this.get('model'); todo.deleteRecord(); todo.save(); } } }); <tbody> {{#each itemController='todo'}} <tr class="task"> . . . <td> <button {{action "removeTodo"}} . . .>Done</button> </td> </tr> {{/each}} </tbody>
  24. User story As a user, I should be able to

    set task priority, so I can keep track of urgent tasks ! Scenario: Add a task with priority When I add task with priority Then the task list should include priorities ! @AntiTyping
  25. {{priority}} {{#each itemController='todo'}} <tr class="task"> <td>{{_view.contentIndex}}</td> <td> {{name}} <span class="priority

    label">{{priority}}</span> </td> <td> <button {{action “removeTodo"}} . . .>Done</button> </td> </tr> {{/each}} @AntiTyping
  26. Property createTodo: function() { var name = this.get('newName'); var priority

    = this.get(‘priority'); ! . . . ! var todo = this.store.createRecord('todo', { name: name, priority: priority }); ! this.set('newName', ''); todo.save(); } @AntiTyping
  27. User Story As a user, I should be filter tasks

    by priority, so I can find hight priority tasks ! Scenario: Priority filter When I select ‘high’ priority filter Then I should see only high priority tasks ! @AntiTyping
  28. Scenario describe("Filter by priority", function() { describe("when high priority is

    selected", function() { it("should display only high priority tasks", function() { $("a.priority:contains('High')").click(); expect($('tr.task')).to.have.length(1); }); }); ! . . . ! describe("when no priority is selected", function() { it("should display all tasks", function() { $("a.priority:contains('All')").click(); expect($('tr.task')).to.have.length(3); }); }); }); @AntiTyping
  29. {{#link-to}} <div class="well" style="max-width: 340px; padding: 8px 0;"> <ul class="nav

    nav-list"> <li class="nav-header">Priority</li> <li> {{#link-to "todos.index" class="priority"}}All{{/link-to}} </li> <li> {{#link-to "todos.high" class="priority"}}High{{/link-to}} </li> <li> {{#link-to "todos.medium" class="priority"}}Medium{{/link-to}} </li> <li> {{#link-to "todos.low" class="priority"}}Low{{/link-to}} </li> </ul> @AntiTyping
  30. Router Todos.Router.map(function() { this.resource('todos', { path: '/' }, function() {

    this.route("high"); this.route("medium"); this.route("low"); }); }); @AntiTyping
  31. Route Todos.TodosHighRoute = Todos.TodosPriorityRoute.extend({ model: function() { return this.filterByPriority('high'); },

    }); ! ! ! Todos.TodosPriorityRoute = Ember.Route.extend({ controllerName: 'Todos', renderTemplate: function(controller) { this.render('todos/index', {controller: controller}); }, filterByPriority: function(priority) { return this.store.filter('todo', function(todo) { return todo.get('priority') == priority; }); } }); !
  32. Summary • Ember abstracts away DOM • Outside in TDD

    • Red-Green-Refactor • Structure development workflow • AngularJS talk @AntiTyping