back to the frontjavascript tdd is between us
Who are we?Marco Cedaro                  Luca Lischettijavascript pirate, arr        8-bit lover and super heroSpreaker Fr...
Who are we?Marco Cedaro                                  Luca Lischettijavascript pirate, arr                        8-bit...
Who are we?Marco Cedaro                                      Luca Lischettijavascript pirate, arr                         ...
I believe I can fly
Fearless & Unconscious
Fearless & Unconscious
But Life Goes On
TDD is about control
and about DESIGN too
The curious case of JavaScript unit testing
Unit Test and Functional Test
Pro/Cons     test                consistency               controlinteraction                against     execution       t...
Thats the browsers,               baby
Unit testing issupposed to test asingle atomic “unit”   of functionality       without dependencies on   anything else
Unit testing is                         This is where yousupposed to test a                          start to run intosing...
Unit testing is                         This is where you     What do you test?supposed to test a                         ...
How does it work?
have you seen LOST?
Write a new test
Write a new testRun test & let it fail
Write a new testRun test & let it failWrite the code
Write a new testRun test & let it failWrite the codeRun test & let it succeed
Write a new testRun test & let it failWrite the codeRun test & let it succeedRefactor your code
Coding
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
pub.. What?
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$.watch("customEvent", function(obj) {! //DO STUFF});_$.notify("customEvent");_$.notify("customEvent", { prop : "value" });
_$.watch("customEvent", function(obj) {! //DO STUFF});_$.notify("customEvent");_$.notify("customEvent", { prop : "value" });
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$.watch("customEvent", function(obj) {! //DO STUFF});_$.notify("customEvent");_$.notify("customEvent", { prop : "value" });
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function (_) {   return {       notify: function (a, b, c, d) {          for (d = -1, c = [].concat(_[a]); c[++d];) ...
[...]    testNotify: function(){!                           !!   ! var a = 0;!!   ! _$.watch(testNotify, function(){ a = 1...
[...]    testNotify: function(){!                           !!   ! var a = 0;!!   ! _$.watch(testNotify, function(){ a = 1...
[...]    testNotify: function(){!                           !!   ! var a = 0;!!   ! _$.watch(testNotify, function(){ a = 1...
[...]    testNotify: function(){!                           !!   ! var a = 0;!!   ! _$.watch(testNotify, function(){ a = 1...
[...]    testNotify: function(){!                           !!   ! var a = 0;!!   ! _$.watch(testNotify, function(){ a = 1...
[...]    testNotify: function(){!                           !!   ! var a = 0;!!   ! _$.watch(testNotify, function(){ a = 1...
[...]    testNotify: function(){!                           !!   ! var a = 0;!!   ! _$.watch(testNotify, function(){ a = 1...
[...]!   testNotifyWithMultipleWhatches: function(){!                                               !!   ! var a = 0, b = ...
[...]!   testNotifyWithMultipleWhatches: function(){!                                               !!   ! var a = 0, b = ...
[...]!   testNotifyWithMultipleWhatches: function(){!                                               !!   ! var a = 0, b = ...
[...]    testNotifyWithMultipleWhatchesNested: function(){!!!   ! var a = 0, b = 0, c=0;!!   ! _$.watch(testNotify, functi...
[...]    testNotifyWithMultipleWhatchesNested: function(){!!!   ! var a = 0, b = 0, c=0;!!   ! _$.watch(testNotify, functi...
[...]    testNotifyWithMultipleWhatchesNested: function(){!!!   ! var a = 0, b = 0, c=0;!!   ! _$.watch(testNotify, functi...
[...]    testNotifyWithMultipleWhatchesNested: function(){!!!   ! var a = 0, b = 0, c=0;!!   ! _$.watch(testNotify, functi...
[...]    testNotifyWithMultipleWhatchesNested: function(){!!!   ! var a = 0, b = 0, c=0;!!   ! _$.watch(testNotify, functi...
[...]    testNotifyWithMultipleWhatchesNested: function(){!!!   ! var a = 0, b = 0, c=0;!!   ! _$.watch(testNotify, functi...
[...]    testNotifyWithMultipleWhatchesNested: function(){!!!   ! var a = 0, b = 0, c=0;!!   ! _$.watch(testNotify, functi...
[...]    testNotifyWithMultipleWhatchesNested: function(){!!!   ! var a = 0, b = 0, c=0;!!   ! _$.watch(testNotify, functi...
[...]    testNotifyWithMultipleWhatchesNested: function(){!!!   ! var a = 0, b = 0, c=0;!!   ! _$.watch(testNotify, functi...
[...]    testNotifyWithMultipleWhatchesNested: function(){!!!   ! var a = 0, b = 0, c=0;!!   ! _$.watch(testNotify, functi...
Theres no parachute Theres a major bug, lets take another look at the code...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
Theres no parachute How would you write a test to check it?
[...]!   testNotifyWithMultipleWhatches: function(){!                                               !!   ! var a = 0, b = ...
[...]!   testWhatchWithoutHandler: function(){! !!   ! var a = 0, b = 0;!!   ! _$.watch(testNotify, function(memo){ a = me...
_$ = (function() {   var registered = {};    return {!   !   notify: function(event, memo) {!   !   ! if (registered[event...
_$    = (function (_) {     return {         notify: function (a, b, c, d) {             for (d = -1, c = [].concat(_[a]);...
Spy, Stub & Mock
Fake objects & methods
Sinon.js
Spies   a function thatrecords arguments,  return value, the  value of this andexception thrown (if any) for all its calls.
Spies                       Stub   a function that         functions (spies) withrecords arguments,           pre-programm...
Spies                       Stub                    Mock   a function that         functions (spies) with   functions (spi...
[...]    testMyLibRegistersToSystemOnEvent: function(){!!!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO INIT YO...
[...]    testMyLibRegistersToSystemOnEvent: function(){!!!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO INIT YO...
[...]    "test MyLib Registers To SystemOn Event": function(){! !!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO...
[...]    testMyLibRegistersToSystemOnEvent: function(){!!!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO INIT YO...
[...]    testMyLibRegistersToSystemOnEvent: function(){!!!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO INIT YO...
[...]    testMyLibRegistersToSystemOnEvent: function(){!!!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO INIT YO...
[...]    testMyLibRegistersToSystemOnEvent: function(){!!!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO INIT YO...
[...]    testMyLibRegistersToSystemOnEvent: function(){!!!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO INIT YO...
[...]    testMyLibRegistersToSystemOnEvent: function(){!!!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO INIT YO...
[...]    testMyLibRegistersToSystemOnEvent: function(){!!!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO INIT YO...
[...]    testMyLibRegistersToSystemOnEvent: function(){!!!   ! var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO INIT YO...
[...]    //testMyLibRegistersToSystemOnEvent: function(){! !!   ! //var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO IN...
[...]    //testMyLibRegistersToSystemOnEvent: function(){! !!   ! //var spy = sinon.spy(_$, watch);!!   ! //DO STUFF TO IN...
What are we going to do
svn co https://svn.dsgn.it/jstddcheatconfigdistlibstoolsworkshopbuild.shbuild.batreadme.txt
svn co https://svn.dsgn.it/jstddcheat                                           /config/config                              ...
svn co https://svn.dsgn.it/jstddcheat                                             /config/config                            ...
svn co https://svn.dsgn.it/jstddcheat                                                /config/config                         ...
svn co https://svn.dsgn.it/jstddcheat                                                /config/config                         ...
svn co https://svn.dsgn.it/jstddcheat                                                        /config/config                 ...
svn co https://svn.dsgn.it/jstddcheat                                                        /config/config                 ...
A simple modular event driven app  a javascript code highlighter with 3 components:         syntax highlighter         sel...
First step - syntax  watch a SRC_LOADED event with the memo {file:{{FILE_SRC}}}  highlight (wrap in a span with the keyword...
Second step - append watch previous SRC_READY create a div[id="src_container"] and fill it with the source append to docume...
Third step - find  watch to SRC_READY  create a div[id=form] and append inside of it       input[type=text][id=search]     ...
Where are we now?
Costs of change
I Build So Consistently
Identify                         Build           I Build So ConsistentlyShare               make it Continuous
Back To The Front - Javascript Test Driven Development is between us (workshop)
Back To The Front - Javascript Test Driven Development is between us (workshop)
Back To The Front - Javascript Test Driven Development is between us (workshop)
Back To The Front - Javascript Test Driven Development is between us (workshop)
Upcoming SlideShare
Loading in...5
×

Back To The Front - Javascript Test Driven Development is between us (workshop)

797

Published on

Javascript & browsers have been for years a complex and unsafe environment for a web developer, now we have the right tools to gain control on what we are distributing in our web applications. During the workshop you will learn first-hand basic Javascript Test Driven Development practices including testing, refactoring and related agile practices such as continuous integration and pair programming.
presented at italian Back To The Front conference /w @sirLisko

Published in: Technology, Business
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
797
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
19
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Back To The Front - Javascript Test Driven Development is between us (workshop)

    1. 1. back to the frontjavascript tdd is between us
    2. 2. Who are we?Marco Cedaro Luca Lischettijavascript pirate, arr 8-bit lover and super heroSpreaker Frontend Developer (almost) Shazam Frontend Developer
    3. 3. Who are we?Marco Cedaro Luca Lischettijavascript pirate, arr 8-bit lover and super heroSpreaker Frontend Developer (almost) Shazam Frontend Developer The content of this workshop do not necessarily reflect the opinion of authors employers
    4. 4. Who are we?Marco Cedaro Luca Lischettijavascript pirate, arr 8-bit lover and super heroSpreaker Frontend Developer (almost) Shazam Frontend Developer The content of this workshop do not necessarily reflect the opinion of authors employers Authors employers are not responsible in any way of authors bad coding and funny spoken english
    5. 5. I believe I can fly
    6. 6. Fearless & Unconscious
    7. 7. Fearless & Unconscious
    8. 8. But Life Goes On
    9. 9. TDD is about control
    10. 10. and about DESIGN too
    11. 11. The curious case of JavaScript unit testing
    12. 12. Unit Test and Functional Test
    13. 13. Pro/Cons test consistency controlinteraction against execution test over between external time integration codebase libraries changes
    14. 14. Thats the browsers, baby
    15. 15. Unit testing issupposed to test asingle atomic “unit” of functionality without dependencies on anything else
    16. 16. Unit testing is This is where yousupposed to test a start to run intosingle atomic “unit” serious dependency of functionality problems due to the without interrelation HTML dependencies on and CSS anything else
    17. 17. Unit testing is This is where you What do you test?supposed to test a start to run into Usually how the usersingle atomic “unit” serious dependency interface responds of functionality problems due to the to user input. without interrelation HTML Actually, the realm of dependencies on and CSS functional testing anything else
    18. 18. How does it work?
    19. 19. have you seen LOST?
    20. 20. Write a new test
    21. 21. Write a new testRun test & let it fail
    22. 22. Write a new testRun test & let it failWrite the code
    23. 23. Write a new testRun test & let it failWrite the codeRun test & let it succeed
    24. 24. Write a new testRun test & let it failWrite the codeRun test & let it succeedRefactor your code
    25. 25. Coding
    26. 26. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    27. 27. pub.. What?
    28. 28. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    29. 29. _$.watch("customEvent", function(obj) {! //DO STUFF});_$.notify("customEvent");_$.notify("customEvent", { prop : "value" });
    30. 30. _$.watch("customEvent", function(obj) {! //DO STUFF});_$.notify("customEvent");_$.notify("customEvent", { prop : "value" });
    31. 31. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    32. 32. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    33. 33. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    34. 34. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    35. 35. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    36. 36. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    37. 37. _$.watch("customEvent", function(obj) {! //DO STUFF});_$.notify("customEvent");_$.notify("customEvent", { prop : "value" });
    38. 38. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    39. 39. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    40. 40. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    41. 41. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    42. 42. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    43. 43. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    44. 44. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    45. 45. _$ = (function (_) { return { notify: function (a, b, c, d) { for (d = -1, c = [].concat(_[a]); c[++d];) c[d](b); }, watch: function (a, b) { (_[a] || (_[a] = [])).push(b); } }})({});
    46. 46. [...] testNotify: function(){! !! ! var a = 0;!! ! _$.watch(testNotify, function(){ a = 1; });! ! _$.notify(testNotify);! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! },[...]
    47. 47. [...] testNotify: function(){! !! ! var a = 0;!! ! _$.watch(testNotify, function(){ a = 1; });! ! _$.notify(testNotify);! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! },[...]
    48. 48. [...] testNotify: function(){! !! ! var a = 0;!! ! _$.watch(testNotify, function(){ a = 1; });! ! _$.notify(testNotify);! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! },[...]
    49. 49. [...] testNotify: function(){! !! ! var a = 0;!! ! _$.watch(testNotify, function(){ a = 1; });! ! _$.notify(testNotify);! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! },[...]
    50. 50. [...] testNotify: function(){! !! ! var a = 0;!! ! _$.watch(testNotify, function(){ a = 1; });! ! _$.notify(testNotify);! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! },[...]
    51. 51. [...] testNotify: function(){! !! ! var a = 0;!! ! _$.watch(testNotify, function(){ a = 1; });! ! _$.notify(testNotify);! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! },[...]
    52. 52. [...] testNotify: function(){! !! ! var a = 0;!! ! _$.watch(testNotify, function(){ a = 1; });! ! _$.notify(testNotify);! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! },[...]
    53. 53. [...]! testNotifyWithMultipleWhatches: function(){! !! ! var a = 0, b = 0;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.watch(testNotify, function(memo){ b = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(1, b);! },[...]
    54. 54. [...]! testNotifyWithMultipleWhatches: function(){! !! ! var a = 0, b = 0;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.watch(testNotify, function(memo){ b = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(1, b);! },[...]
    55. 55. [...]! testNotifyWithMultipleWhatches: function(){! !! ! var a = 0, b = 0;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.watch(testNotify, function(memo){ b = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(1, b);! },[...]
    56. 56. [...] testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch(testNotify, function(memo){ a = memo.test;! ! ! _$.watch(testNotify, function(memo){! ! ! ! if (b<2){ b = memo.test; } else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]
    57. 57. [...] testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch(testNotify, function(memo){ a = memo.test;! ! ! _$.watch(testNotify, function(memo){! ! ! ! if (b<2){ b = memo.test; } else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]
    58. 58. [...] testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch(testNotify, function(memo){ a = memo.test;! ! ! _$.watch(testNotify, function(memo){! ! ! ! if (b<2){ b = memo.test; } else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]
    59. 59. [...] testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch(testNotify, function(memo){ a = memo.test;! ! ! _$.watch(testNotify, function(memo){! ! ! ! if (b<2){ b = memo.test; } else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]
    60. 60. [...] testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch(testNotify, function(memo){ a = memo.test;! ! ! _$.watch(testNotify, function(memo){! ! ! ! if (b<2){ b = memo.test; }! ! ! ! else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]
    61. 61. [...] testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch(testNotify, function(memo){ a = memo.test;! ! ! _$.watch(testNotify, function(memo){! ! ! ! if (b<2){ b = memo.test; } else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]
    62. 62. [...] testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch(testNotify, function(memo){ a = memo.test;! ! ! _$.watch(testNotify, function(memo){! ! ! ! if (b<2){ b = memo.test; } else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]
    63. 63. [...] testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch(testNotify, function(memo){ a = memo.test;! ! ! _$.watch(testNotify, function(memo){! ! ! ! if (b<2){ b = memo.test; } else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]
    64. 64. [...] testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch(testNotify, function(memo){ a = memo.test;! ! ! _$.watch(testNotify, function(memo){! ! ! ! if (b<2){ b = memo.test; } else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]
    65. 65. [...] testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch(testNotify, function(memo){ a = memo.test;! ! ! _$.watch(testNotify, function(memo){! ! ! ! if (b<2){ b = memo.test; } else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify(testNotify, {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]
    66. 66. Theres no parachute Theres a major bug, lets take another look at the code...
    67. 67. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    68. 68. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! } };})();
    69. 69. Theres no parachute How would you write a test to check it?
    70. 70. [...]! testNotifyWithMultipleWhatches: function(){! !! ! var a = 0, b = 0;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.watch(testNotify, function(memo){ b = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(1, b);! },[...]
    71. 71. [...]! testWhatchWithoutHandler: function(){! !! ! var a = 0, b = 0;!! ! _$.watch(testNotify, function(memo){ a = memo.test; });! ! _$.watch(testNotify);! ! _$.watch(testNotify, function(memo){ b = memo.test; });! ! _$.notify(testNotify, {test: 1});! ! assertEquals(1, a);! ! assertEquals(1, b);! },[...]
    72. 72. _$ = (function() { var registered = {}; return {! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (handler) { if (typeof registered[event] === "undefined"){ ! ! ! ! registered[event] = []; ! ! ! } ! ! ! registered[event].push(handler); }! ! } };})();
    73. 73. _$ = (function (_) { return { notify: function (a, b, c, d) { for (d = -1, c = [].concat(_[a]); c[++d];) c[d](b); }, watch: function (a, b) { if(b)(_[a] || (_[a] = [])).push(b); } }})({});
    74. 74. Spy, Stub & Mock
    75. 75. Fake objects & methods
    76. 76. Sinon.js
    77. 77. Spies a function thatrecords arguments, return value, the value of this andexception thrown (if any) for all its calls.
    78. 78. Spies Stub a function that functions (spies) withrecords arguments, pre-programmed return value, the behavior. value of this andexception thrown (if any) for all its calls.
    79. 79. Spies Stub Mock a function that functions (spies) with functions (spies) withrecords arguments, pre-programmed pre-programmed return value, the behavior. behavior (stubs) as value of this and well as pre-exception thrown (if programmed any) for all its calls. expectations.
    80. 80. [...] 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! },[...]
    81. 81. [...] 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! },[...]
    82. 82. [...] "test MyLib Registers To SystemOn Event": 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! },[...]
    83. 83. [...] 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! },[...]
    84. 84. [...] 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! },[...]
    85. 85. [...] 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! },[...]
    86. 86. [...] 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! },[...]
    87. 87. [...] 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! },[...]
    88. 88. [...] 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! },[...]
    89. 89. [...] 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! },[...]
    90. 90. [...] testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, watch);!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith(SystemOn));! },[...]
    91. 91. [...] //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();! },[...]
    92. 92. [...] //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();! },[...]
    93. 93. What are we going to do
    94. 94. svn co https://svn.dsgn.it/jstddcheatconfigdistlibstoolsworkshopbuild.shbuild.batreadme.txt
    95. 95. svn co https://svn.dsgn.it/jstddcheat /config/config browsers.propdist build.xmllibs default.proptoolsworkshopbuild.shbuild.batreadme.txt
    96. 96. svn co https://svn.dsgn.it/jstddcheat /config/config browsers.propdist /libs/ build.xmllibs base.js default.proptools sinon.jsworkshopbuild.shbuild.batreadme.txt
    97. 97. svn co https://svn.dsgn.it/jstddcheat /config/config browsers.propdist /libs/ build.xmllibs base.js default.proptools /tools/ sinon.jsworkshop antbuild.sh browserbuild.bat jslintreadme.txt JsTestDriver
    98. 98. svn co https://svn.dsgn.it/jstddcheat /config/config browsers.propdist /libs/ build.xmllibs base.js default.proptools /tools/ sinon.jsworkshop antbuild.sh browserbuild.bat jslintreadme.txt JsTestDriver /workshop/ append find syntax
    99. 99. svn co https://svn.dsgn.it/jstddcheat /config/config browsers.propdist /libs/ build.xmllibs base.js default.proptools /tools/ sinon.jsworkshop antbuild.sh browserbuild.bat jslint /append/readme.txt JsTestDriver src append.js /workshop/ test append append.test.js find jsTestDriver.conf syntax
    100. 100. svn co https://svn.dsgn.it/jstddcheat /config/config browsers.propdist /libs/ build.xmllibs base.js default.proptools /tools/ sinon.jsworkshop antbuild.sh browserbuild.bat jslint /append/readme.txt JsTestDriver src append.js /workshop/ test append append.test.js find jsTestDriver.conf syntax build append | sh build.sh append
    101. 101. A simple modular event driven app a javascript code highlighter with 3 components: syntax highlighter selected word highlighter finder
    102. 102. First step - syntax watch a SRC_LOADED event with the memo {file:{{FILE_SRC}}} highlight (wrap in a span with the keyword itself as classname) function => <span class="function">function</span> var, if ... else, for, return, ... notify SRC_READY with the memo {file:{{EDITED_SRC}}}
    103. 103. Second step - append watch previous SRC_READY create a div[id="src_container"] and fill it with the source append to document llisten dblclick event and notify SRC_HIGHLIGHT with this memo: {keyword:{{HIGHLIGHTED_WORD}}} manage the SRC_HIGHLIGHT notification <span class="highlight">{{HIGHLIGHTED_KEYWORKD}}</span>
    104. 104. Third step - find watch to SRC_READY create a div[id=form] and append inside of it input[type=text][id=search] input[type=button][id=submitBtn][value=Search] append the div to the document on click on submit notify a SRC_HIGHLIGHT event with memo {keyword:"{{SEARCHED_TEXT}}"}
    105. 105. Where are we now?
    106. 106. Costs of change
    107. 107. I Build So Consistently
    108. 108. Identify Build I Build So ConsistentlyShare make it Continuous
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×