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

Javascript as a programming language - Paris Web

Javascript as a programming language - Paris Web

How to setup up a stable javascript continuous integration environment and why you need it. Through a real life example, the talk explains all the benefits of having a development process that brings real control over javascript codebase. A deep analysis of developer and webapps needs and of the tools that fit those requirements.

Paris Web (Paris, 14 October 2011)
@cedmax
http://cedmax.com

3ca63d4e2f2be0ef47b841e63b564d18?s=128

Marco Cedaro

October 14, 2011
Tweet

Transcript

  1. Javascript As A Programming Language

  2. Javascript As A Programming Language Versioning, Test Driven Development &

    Continuous Integration IS
  3. Javascript As A Programming Language Versioning, Test Driven Development &

    Continuous Integration IS
  4. Hello, who’s speaking?

  5. Hello, who’s speaking? Marco Cedaro @cedmax

  6. They said I am a... Frontend Cowboy Nicola Vitto Jr.

    Javascript Pervert Roberto Felter Perfect Stranger basically anyone else Hello, who’s speaking? Marco Cedaro @cedmax
  7. Actually I am: a Frontend Developer at Spreaker.com Hello, who’s

    speaking? Marco Cedaro @cedmax
  8. Actually I am: a Frontend Developer at Spreaker.com a conference

    organizer with From The Front Hello, who’s speaking? Marco Cedaro @cedmax
  9. Actually I am: a Frontend Developer at Spreaker.com a conference

    organizer with From The Front and a javascript pervert Hello, who’s speaking? Marco Cedaro @cedmax
  10. Bologna, Italy

  11. something in common with Robert Nyman

  12. something in common with Robert Nyman

  13. I believe I can fly

  14. I believe I can fly

  15. I believe I can fly Be brave. Take risks. Nothing

    can substitute experience. Paulo Coelho
  16. once upon a time

  17. we had no control once upon a time

  18. we had no control javascript was the land of the

    brave once upon a time
  19. we had no control javascript was the land of the

    brave we were fearless and unconscious once upon a time
  20. we had no control javascript was the land of the

    brave we were fearless and unconscious we were proud of being like that once upon a time
  21. just like them

  22. kitty hawk

  23. but life goes on

  24. web engineer had a very bad opinion of us once

    upon a time
  25. but it wasn't fair

  26. we didn't have IDEs & tools they did but it

    wasn't fair
  27. we didn't have IDEs & tools they did actually it

    was our own fault but it wasn't fair
  28. we didn't have IDEs & tools they did actually it

    was our own fault we were not ready but it wasn't fair
  29. we didn't have IDEs & tools they did actually it

    was our own fault we were not ready and javascript was neither but it wasn't fair
  30. but, again, life goes on...

  31. ...and on...

  32. and on..

  33. it's not about the language itself

  34. GREAT POWERS...

  35. Frontend developers have to claim their role in development roadmap

    and business strategy
  36. Javascript is a serious business

  37. how serious?

  38. something we can achieve less bandwidth and server load loading

    resources and content when needed
  39. something we can achieve less bandwidth and server load loading

    resources and content when needed performance boosts that can lead to better conversion rates
  40. something we can achieve less bandwidth and server load loading

    resources and content when needed performance boosts that can lead to better conversion rates cross platform development: less maintenance costs
  41. less bandwidth and server load loading resources and content when

    needed performance boosts that can lead to better conversion rates cross platform development: less maintenance costs money
  42. how serious?

  43. this serious

  44. what's missing?

  45. what's missing?

  46. what's missing? If I had nine of my fingers missing

    I wouldn't type any slower. Mitch Hedberg
  47. IDEs & Tools

  48. the attitude

  49. a strategy

  50. the small web agency The designer introduces a slider on

    some websites: ”it’s cool on apple store”. The developer gets a jQuery plugin online
  51. the small web agency The designer introduces a slider on

    some websites: ”it’s cool on apple store”. The developer gets a jQuery plugin online Major release of the most used browser. A small fix has been released, they have to change 5 files in 5 different projects.
  52. the small web agency The designer introduces a slider on

    some websites: ”it’s cool on apple store”. The developer gets a jQuery plugin online Major release of the most used browser. A small fix has been released, they have to change 5 files in 5 different projects. Oh damn! There’s no mouse wheel integration! should they ask for support or should they change the library by themself?
  53. am I the only one or there’s something wrong?

  54. the big corp The client-side architecture has been built on

    the most known and supported framework 2006
  55. the big corp The client-side architecture has been built on

    the most known and supported framework 2006 Everything seems to be fine, except that the well known framework was being replaced by a powerful new one 2008 - 2010
  56. the big corp The client-side architecture has been built on

    the most known and supported framework 2006 Everything seems to be fine, except that the well known framework was being replaced by a powerful new one 2008 - 2010 They were forced to change the whole codebase at once to reduce maintenance and development costs 2011
  57. here we are again

  58. attitude, strategy...

  59. ...and foresight

  60. WARNING!

  61. continuous integration

  62. continuous integration

  63. continuous integration A software development practice where members of a

    team integrate their work frequently [...] to detect integration errors as quickly as possible. Martin Fowler
  64. I Build So Consistently

  65. I Build So Consistently identify

  66. I Build So Consistently identify write a build script

  67. I Build So Consistently share identify write a build script

  68. I Build So Consistently share identify write a build script

    make it continuous
  69. How can we take advantages from a continuous integration process?

  70. rationalize your workflow

  71. deploy stable versions

  72. Separate the codebase

  73. unit test your code

  74. basically

  75. basically

  76. basically ie7

  77. choose your tools

  78. choose your tools

  79. choose your tools A man cannot be too careful in

    the choice of his enemies Oscar Wilde
  80. None
  81. JSHINT

  82. a code quality tool

  83. like Douglas Crockford's JSLint a code quality tool

  84. like Douglas Crockford's JSLint but a code quality tool

  85. like Douglas Crockford's JSLint but customizable a code quality tool

  86. a tool for stupid?

  87. JS HINT

  88. JS TEST DRIVER JS HINT

  89. once upon a time

  90. jsTestDriver

  91. jsTestDriver works from console

  92. jsTestDriver works from console runs a server

  93. jsTestDriver works from console runs a server opens browsers

  94. jsTestDriver works from console runs a server opens browsers runs

    test suites
  95. jsTestDriver works from console runs a server opens browsers runs

    test suites retrieves results in console
  96. build server

  97. build server

  98. build server

  99. how does testing works?

  100. have you seen Lost?

  101. how does it work?

  102. how does it work? write a test

  103. how does it work? write a test let it fail

  104. how does it work? write a test let it fail

    write the code
  105. how does it work? write a test let it fail

    write the code run the test again
  106. how does it work? write a test let it fail

    write the code run the test again refactor
  107. the environment

  108. It's the browser, baby

  109. It's the browser, baby

  110. The curious case of Javascript unit testing Unit testing is

    supposed to test a single atomic “unit” of functionality without dependencies on anything else
  111. The curious case of Javascript unit testing Unit testing is

    supposed to test a single atomic “unit” of functionality without dependencies on anything else This is where you start to run into serious dependency problems due to the interrelation with HTML and CSS
  112. The curious case of Javascript unit testing Unit testing is

    supposed to test a single atomic “unit” of functionality without dependencies on anything else This is where you start to run into serious dependency problems due to the interrelation with HTML and CSS What do you test? Usually how the user interface responds to user input. Actually, the realm of functional testing
  113. keep it real

  114. keep it real

  115. keep it real To all my homies working on the

    9 to 5 Shaggy
  116. #140bytes

  117. I used to work with these guys

  118. _$ = (function() { var registered = {}; return {

    ! ! pub: function(event, memo) { ! ! ! if (registered[event] instanceof Array){ ! ! ! ! var handlers = [].concat(registered[event]); ! ! ! ! for (var i=0, h; (h = handlers[i]); i++){ ! ! ! ! ! h.call(this, memo); ! ! ! ! } ! ! ! } ! ! }, ! ! sub: function(event, handler) { ! ! ! if (typeof registered[event] === "undefined"){ ! ! ! ! registered[event] = []; ! ! ! } ! ! ! registered[event].push(handler); ! ! } }; })();
  119. _$ = (function() { var registered = {}; return {

    ! ! pub: function(event, memo) { ! ! ! if (registered[event] instanceof Array){ ! ! ! ! var handlers = [].concat(registered[event]); ! ! ! ! for (var i=0, h; (h = handlers[i]); i++){ ! ! ! ! ! h.call(this, memo); ! ! ! ! } ! ! ! } ! ! }, ! ! sub: function(event, handler) { ! ! ! if (typeof registered[event] === "undefined"){ ! ! ! ! registered[event] = []; ! ! ! } ! ! ! registered[event].push(handler); ! ! } }; })();
  120. _$.sub("customEvent", function(obj) { ! //DO STUFF }); _$.pub("customEvent"); _$.pub("customEvent", {

    prop : "value" });
  121. _$.sub("customEvent", function(obj) { ! //DO STUFF }); _$.pub("customEvent"); _$.pub("customEvent", {

    prop : "value" });
  122. _$.sub("customEvent", function(obj) { ! //DO STUFF }); _$.pub("customEvent"); _$.pub("customEvent", {

    prop : "value" });
  123. _$ = (function() { var registered = {}; return {

    ! ! pub: function(event, memo) { ! ! ! if (registered[event] instanceof Array){ ! ! ! ! var handlers = [].concat(registered[event]); ! ! ! ! for (var i=0, h; (h = handlers[i]); i++){ ! ! ! ! ! h.call(this, memo); ! ! ! ! } ! ! ! } ! ! }, ! ! sub: function(event, handler) { ! ! ! if (typeof registered[event] === "undefined"){ ! ! ! ! registered[event] = []; ! ! ! } ! ! ! registered[event].push(handler); ! ! } }; })();
  124. _$ = (function (_) { ! return { ! !

    pub: function(a, b, c, d) { ! ! ! for (d=-1, c=[].concat(_[a]); c[++d];) c[d](b) ! ! }, ! ! sub: function(a, b) { ! ! ! (_[a] || (_[a] = [])).push(b) ! ! } ! } })({})
  125. _$ = (function (_) { ! return { ! !

    pub: function(a, b, c, d) { ! ! ! for (d=-1, c=[].concat(_[a]); c[++d];) c[d](b) ! ! }, ! ! sub: function(a, b) { ! ! ! (_[a] || (_[a] = [])).push(b) ! ! } ! } })({}) #140bytes
  126. _$ = (function() { var registered = {}; return {

    ! ! pub: function(event, memo) { ! ! ! if (registered[event] instanceof Array){ ! ! ! ! var handlers = [].concat(registered[event]); ! ! ! ! for (var i=0, h; (h = handlers[i]); i++){ ! ! ! ! ! h.call(this, memo); ! ! ! ! } ! ! ! } ! ! }, ! ! sub: function(event, handler) { ! ! ! if (typeof registered[event] === "undefined"){ ! ! ! ! registered[event] = []; ! ! ! } ! ! ! registered[event].push(handler); ! ! } }; })();
  127. _$ = (function (_) { ! return { ! !

    pub: function(a, b, c, d) { ! ! ! for (d=-1, c=[].concat(_[a]); c[++d];) c[d](b) ! ! }, ! ! sub: function(a, b) { ! ! ! (_[a] || (_[a] = [])).push(b) ! ! } ! } })({}) #140bytes
  128. _$ = (function() { var registered = {}; return {

    ! ! pub: function(event, memo) { ! ! ! if (registered[event] instanceof Array){ ! ! ! ! var handlers = [].concat(registered[event]); ! ! ! ! for (var i=0, h; (h = handlers[i]); i++){ ! ! ! ! ! h.call(this, memo); ! ! ! ! } ! ! ! } ! ! }, ! ! sub: function(event, handler) { ! ! ! if (typeof registered[event] === "undefined"){ ! ! ! ! registered[event] = []; ! ! ! } ! ! ! registered[event].push(handler); ! ! } }; })();
  129. _$ = (function (_) { ! return { ! !

    pub: function(a, b, c, d) { ! ! ! for (d=-1, c=[].concat(_[a]); c[++d];) c[d](b) ! ! }, ! ! sub: function(a, b) { ! ! ! (_[a] || (_[a] = [])).push(b) ! ! } ! } })({}) #140bytes
  130. _$ = (function (_) { ! return { ! !

    pub: function(a, b, c, d) { ! ! ! for (d=-1, c=[].concat(_[a]); c[++d];) c[d](b) ! ! }, ! ! sub: function(a, b) { ! ! ! (_[a] || (_[a] = [])).push(b) ! ! } ! } })({}) #140bytes
  131. [...] testPub: function(){! ! ! ! var a = 0;!

    ! ! _$.sub('testNotify', function(){ a = 1; }); ! ! _$.pub('testNotify'); ! ! assertEquals(1, a); ! }, ! ! ! ! testNotifyWithMemo: function(){!! ! ! var a = 0 ;! ! ! _$.sub('testNotify', function(memo){ ! ! ! ! a = memo.test; ! ! }); ! ! _$.pub('testNotify', {test: 1}); ! ! assertEquals(1, a); ! }, [...]
  132. [...] testPub: function(){! ! ! ! var a = 0;!

    ! ! _$.sub('testNotify', function(){ a = 1; }); ! ! _$.pub('testNotify'); ! ! assertEquals(1, a); ! }, ! ! ! ! testNotifyWithMemo: function(){!! ! ! var a = 0 ;! ! ! _$.sub('testNotify', function(memo){ ! ! ! ! a = memo.test; ! ! }); ! ! _$.pub('testNotify', {test: 1}); ! ! assertEquals(1, a); ! }, [...]
  133. [...] testPub: function(){! ! ! ! var a = 0;!

    ! ! _$.sub('testNotify', function(){ a = 1; }); ! ! _$.pub('testNotify'); ! ! assertEquals(1, a); ! }, ! ! ! ! testNotifyWithMemo: function(){!! ! ! var a = 0 ;! ! ! _$.sub('testNotify', function(memo){ ! ! ! ! a = memo.test; ! ! }); ! ! _$.pub('testNotify', {test: 1}); ! ! assertEquals(1, a); ! }, [...]
  134. [...] testPub: function(){! ! ! ! var a = 0;!

    ! ! _$.sub('testNotify', function(){ a = 1; }); ! ! _$.pub('testNotify'); ! ! assertEquals(1, a); ! }, ! ! ! ! testNotifyWithMemo: function(){!! ! ! var a = 0 ;! ! ! _$.sub('testNotify', function(memo){ ! ! ! ! a = memo.test; ! ! }); ! ! _$.pub('testNotify', {test: 1}); ! ! assertEquals(1, a); ! }, [...]
  135. [...] testPub: function(){! ! ! ! var a = 0;!

    ! ! _$.sub('testNotify', function(){ a = 1; }); ! ! _$.pub('testNotify'); ! ! assertEquals(1, a); ! }, ! ! ! ! testNotifyWithMemo: function(){!! ! ! var a = 0 ;! ! ! _$.sub('testNotify', function(memo){ ! ! ! ! a = memo.test; ! ! }); ! ! _$.pub('testNotify', {test: 1}); ! ! assertEquals(1, a); ! }, [...]
  136. [...] testPub: function(){! ! ! ! var a = 0;!

    ! ! _$.sub('testNotify', function(){ a = 1; }); ! ! _$.pub('testNotify'); ! ! assertEquals(1, a); ! }, ! ! ! ! testNotifyWithMemo: function(){!! ! ! var a = 0 ; ! ! _$.sub('testNotify', function(memo){ ! ! ! ! a = memo.test; ! ! }); ! ! _$.pub('testNotify', {test: 1}); ! ! assertEquals(1, a); ! }, [...]
  137. [...] testPub: function(){! ! ! ! var a = 0;!

    ! ! _$.sub('testNotify', function(){ a = 1; }); ! ! _$.pub('testNotify'); ! ! assertEquals(1, a); ! }, ! ! ! ! testNotifyWithMemo: function(){!! ! ! var a = 0 ;! ! ! _$.sub('testNotify', function(memo){ ! ! ! ! a = memo.test; ! ! }); ! ! _$.pub('testNotify', {test: 1}); ! ! assertEquals(1, a); ! }, [...]
  138. easy, uh?

  139. you will, eventually

  140. JS TEST DRIVER JS HINT

  141. JS TEST DRIVER SINON.JS JS HINT

  142. a mock library

  143. a mock library SPIES a function that records arguments, return

    value, the value of this and exception thrown (if any) for all its calls.
  144. a mock library SPIES a function that records arguments, return

    value, the value of this and exception thrown (if any) for all its calls. STUBS functions (spies) with pre-programmed behavior.
  145. a mock library SPIES a function that records arguments, return

    value, the value of this and exception thrown (if any) for all its calls. STUBS functions (spies) with pre-programmed behavior. MOCKS functions (spies) with pre-programmed behavior (stubs) as well as pre-programmed expectations.
  146. [...] testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! assertTrue(spy.calledWith('SysyemOn')); ! }, ! ! ! ! testMyLibLoggedNotLogged: function(){!! ! ! var stub = sinon.stub(User, 'isLogged');! ! ! stub.returns(true); ! ! //DO STUFF && ASSERTIONS ! ! stub.returns(false); ! ! //DO STUFF && ASSERTIONS ! }, [...]
  147. [...] testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! assertTrue(spy.calledWith('SystemOn')); ! }, ! ! ! ! testMyLibLoggedNotLogged: function(){!! ! ! var stub = sinon.stub(User, 'isLogged');! ! ! stub.returns(true); ! ! //DO STUFF && ASSERTIONS ! ! stub.returns(false); ! ! //DO STUFF && ASSERTIONS ! }, [...] SPY
  148. [...] testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! assertTrue(spy.calledWith('SystemOn')); ! }, ! ! ! ! testMyLibLoggedNotLogged: function(){!! ! ! var stub = sinon.stub(User, 'isLogged');! ! ! stub.returns(true); ! ! //DO STUFF && ASSERTIONS ! ! stub.returns(false); ! ! //DO STUFF && ASSERTIONS ! }, [...] SPY
  149. [...] testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! assertTrue(spy.calledWith('SystemOn')); ! }, ! ! ! ! testMyLibLoggedNotLogged: function(){!! ! ! var stub = sinon.stub(User, 'isLogged');! ! ! stub.returns(true); ! ! //DO STUFF && ASSERTIONS ! ! stub.returns(false); ! ! //DO STUFF && ASSERTIONS ! }, [...] SPY
  150. [...] testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! assertTrue(spy.calledWith('SystemOn')); ! }, ! ! ! ! testMyLibLoggedNotLogged: function(){!! ! ! var stub = sinon.stub(User, 'isLogged');! ! ! stub.returns(true); ! ! //DO STUFF && ASSERTIONS ! ! stub.returns(false); ! ! //DO STUFF && ASSERTIONS ! }, [...] SPY
  151. [...] testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! assertTrue(spy.calledWith('SystemOn')); ! }, ! ! ! ! testMyLibLoggedNotLogged: function(){!! ! ! var stub = sinon.stub(User, 'isLogged');! ! ! stub.returns(true); ! ! //DO STUFF && ASSERTIONS ! ! stub.returns(false); ! ! //DO STUFF && ASSERTIONS ! }, [...] STUB
  152. [...] testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! assertTrue(spy.calledWith('SystemOn')); ! }, ! ! ! ! testMyLibLoggedNotLogged: function(){!! ! ! var stub = sinon.stub(User, 'isLogged');! ! ! stub.returns(true); ! ! //DO STUFF && ASSERTIONS ! ! stub.returns(false); ! ! //DO STUFF && ASSERTIONS ! }, [...] STUB
  153. [...] testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! assertTrue(spy.calledWith('SystemOn')); ! }, ! ! ! ! testMyLibLoggedNotLogged: function(){!! ! ! var stub = sinon.stub(User, 'isLogged');! ! ! stub.returns(true); ! ! //DO STUFF && ASSERTIONS ! ! stub.returns(false); ! ! //DO STUFF && ASSERTIONS ! }, [...] STUB
  154. [...] testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! assertTrue(spy.calledWith('SystemOn')); ! }, ! ! ! ! testMyLibLoggedNotLogged: function(){!! ! ! var stub = sinon.stub(User, 'isLogged');! ! ! stub.returns(true); ! ! //DO STUFF && ASSERTIONS ! ! stub.returns(false); ! ! //DO STUFF && ASSERTIONS ! }, [...] STUB
  155. [...] testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! assertTrue(spy.calledWith('SystemOn')); ! }, [...] MOCK
  156. [...] //testMyLibRegistersToSystemOnEvent: function(){! ! ! ! //var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! //assertTrue(spy.calledWith('SysyemOn')); ! //}, ! ! testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var mock = sinon.mock(_$); ! ! mock.expect('watch').calledWith('SysyemOn'); ! ! //DO STUFF TO INIT YOUR LIB ! ! mock.verify(); ! }, [...] MOCK
  157. [...] //testMyLibRegistersToSystemOnEvent: function(){! ! ! ! //var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! //assertTrue(spy.calledWith('SysyemOn')); ! //}, ! ! testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var mock = sinon.mock(_$); ! ! mock.expect('watch').calledWith('SysyemOn'); ! ! //DO STUFF TO INIT YOUR LIB ! ! mock.verify(); ! }, [...] MOCK
  158. [...] //testMyLibRegistersToSystemOnEvent: function(){! ! ! ! //var spy = sinon.spy(_$,

    'watch');! ! ! //DO STUFF TO INIT YOUR LIB ! ! //assertTrue(spy.calledWith('SysyemOn')); ! //}, ! ! testMyLibRegistersToSystemOnEvent: function(){! ! ! ! var mock = sinon.mock(_$); ! ! mock.expect('watch').calledWith('SysyemOn'); ! ! //DO STUFF TO INIT YOUR LIB ! ! mock.verify(); ! }, [...] MOCK
  159. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  160. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  161. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  162. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  163. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  164. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  165. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  166. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  167. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  168. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  169. [...] testOnSuccessCallback: function(){! ! var server = sinon.useFakeServer(); server.respondWith("GET", "/art/12/comments.json",

    [200, {"Content-Type":"application/json"}, "[{ id:12, text:'Hello'}]"]); ! ! var spy = sinon.spy(); ! ! myLib.getCommentsFor("/art/12", spy); ! ! server.respond(); ! ! assert(spy.calledWith([{ id:12, text:"Hello"}])); ! }, [...] AJAX CALL
  170. JS TEST DRIVER SINON.JS JS HINT

  171. JS TEST DRIVER SINON.JS JS HINT YUI COMPRESSOR

  172. Everyone should be happy

  173. in the wild

  174. in the wild

  175. IN THE WILD In the wild, there is no health

    care. Dwight Schrute (the office)
  176. drawbacks at the beginning

  177. cost of change

  178. LOOKING FORWARD

  179. LOOKING FORWARD

  180. LOOKING FORWARD

  181. LOOKING FORWARD

  182. LOOKING FORWARD

  183. the further we look at, the more control we need

    LOOKING FORWARD
  184. the further we look at, the more control we need

    LOOKING FORWARD
  185. the further we look at, the more control we need

    LOOKING FORWARD let's get ready
  186. the further we look at, the more control we need

    LOOKING FORWARD let's get ready javascript is a programming language
  187. the further we look at, the more control we need

    LOOKING FORWARD let's get ready javascript is a programming language javascript is a serious business.
  188. the further we look at, the more control we need

    LOOKING FORWARD let's get ready javascript is a programming language javascript is a serious business. and, most of all...
  189. javascript kicks asses

  190. javascript kicks asses http://spkr8.com/t/8718 http://cedmax.com @cedmax any question?