Paris Web - Javascript as a programming language

Preview:

Citation preview

Javascript As A Programming Language

Javascript As A Programming Language

Versioning, Test Driven Development & Continuous Integration

IS

Javascript As A Programming Language

Versioning, Test Driven Development & Continuous Integration

IS

Hello, who’s speaking?

Hello, who’s speaking?

Marco Cedaro @cedmax

They said I am a...

Frontend Cowboy Nicola Vitto Jr.

Javascript PervertRoberto Felter

Perfect Strangerbasically anyone else

Hello, who’s speaking?

Marco Cedaro @cedmax

Actually I am:

a Frontend Developer at Spreaker.com

Hello, who’s speaking?

Marco Cedaro @cedmax

Actually I am:

a Frontend Developer at Spreaker.com

a conference organizer with From The Front

Hello, who’s speaking?

Marco Cedaro @cedmax

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

Bologna, Italy

something in common with Robert Nyman

something in common with Robert Nyman

I believe I can fly

I believe I can fly

I believe I can fly

Be brave. Take risks. Nothing can substitute

experience. Paulo Coelho

once upon a time

we had no control

once upon a time

we had no control

javascript was the land of the brave

once upon a time

we had no control

javascript was the land of the brave

we were fearless and unconscious

once upon a time

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

just like them

kitty hawk

but life goes on

web engineer

had a very bad

opinion of us

once upon a time

but it wasn't fair

we didn't have IDEs & tools they did

but it wasn't fair

we didn't have IDEs & tools they did

actually it was our own fault

but it wasn't fair

we didn't have IDEs & tools they did

actually it was our own fault

we were not ready

but it wasn't fair

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

but, again, life goes on...

...and on...

and on..

it's not about the language itself

GREAT POWERS...

Frontend developers have to claim their rolein development roadmap and

business strategy

Javascript is a serious business

how serious?

something we can achieve

less bandwidth and server load loading

resources and content when needed

something we can achieve

less bandwidth and server load loading

resources and content when needed

performance boosts that can lead to better

conversion rates

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

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

how serious?

this serious

what's missing?

what's missing?

what's missing?

If I had nine of my fingers missing I wouldn't type any

slower.Mitch Hedberg

IDEs & Tools

the attitude

a strategy

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

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.

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?

am I the only one or there’s something wrong?

the big corp

The client-side architecture has been

built on the most known and supported

framework

2006

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

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

here we are again

attitude, strategy...

...and foresight

WARNING!

continuous integration

continuous integration

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

I Build So Consistently

I Build So Consistently

identify

I Build So Consistently

identify

write a build script

I Build So Consistently

share

identify

write a build script

I Build So Consistently

share

identify

write a build script

make it continuous

How can we take advantages from a

continuous integration process?

rationalize your workflow

deploy stable versions

Separate the codebase

unit test your code

basically

basically

basically

ie7

choose your tools

choose your tools

choose your tools

A man cannot be too careful in the choice of his enemies

Oscar Wilde

JSHINT

a code quality tool

like Douglas Crockford's JSLint

a code quality tool

like Douglas Crockford's JSLint

but

a code quality tool

like Douglas Crockford's JSLint

but

customizable

a code quality tool

a tool for stupid?

JS HINT

JS TEST DRIVER

JS HINT

once upon a time

jsTestDriver

jsTestDriver

works from console

jsTestDriver

works from console

runs a server

jsTestDriver

works from console

runs a server

opens browsers

jsTestDriver

works from console

runs a server

opens browsers

runs test suites

jsTestDriver

works from console

runs a server

opens browsers

runs test suites

retrieves results in console

build server

build server

build server

how does testing works?

have you seen Lost?

how does it work?

how does it work?

write a test

how does it work?

write a test let it fail

how does it work?

write a test let it fail write the code

how does it work?

write a test let it fail write the code

run the test again

how does it work?

write a test let it fail write the code

run the test again

refactor

the environment

It's the browser, baby

It's the browser, baby

The curious case of Javascript unit testing

Unit testing is supposed to test a

single atomic “unit” of functionality without

dependencies on anything else

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

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

keep it real

keep it real

keep it real

To all my homies working on the 9 to 5

Shaggy

#140bytes

I used to work with these guys

_$ = (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);! ! }};

})();

_$ = (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);! ! }};

})();

_$.sub("customEvent", function(obj) {! //DO STUFF});

_$.pub("customEvent");_$.pub("customEvent", { prop : "value" });

_$.sub("customEvent", function(obj) {! //DO STUFF});

_$.pub("customEvent");_$.pub("customEvent", { prop : "value" });

_$.sub("customEvent", function(obj) {! //DO STUFF});

_$.pub("customEvent");_$.pub("customEvent", { prop : "value" });

_$ = (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);! ! }};

})();

_$ = (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)! ! }! }})({})

_$ = (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

_$ = (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);! ! }};

})();

_$ = (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

_$ = (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);! ! }};

})();

_$ = (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

_$ = (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

[...]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);! },[...]

[...]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);! },[...]

[...]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);! },[...]

[...]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);! },[...]

[...]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);! },[...]

[...]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);! },[...]

[...]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);! },[...]

easy, uh?

you will, eventually

JS TEST DRIVER

JS HINT

JS TEST DRIVERSINON.JS

JS HINT

a mock library

a mock library

SPIESa function that records

arguments, return value, the value of this and exception thrown (if any) for all its calls.

a mock library

SPIESa function that records

arguments, return value, the value of this and exception thrown (if any) for all its calls.

STUBSfunctions (spies) with

pre-programmed behavior.

a mock library

SPIESa function that records

arguments, return value, the value of this and exception thrown (if any) for all its calls.

STUBSfunctions (spies) with

pre-programmed behavior.

MOCKSfunctions (spies) with

pre-programmed behavior (stubs) as well

as pre-programmed expectations.

[...]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

! },[...]

[...]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

[...]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

[...]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

[...]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

[...]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

[...]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

[...]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

[...]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

[...]testMyLibRegistersToSystemOnEvent: function(){! !

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

MOCK

[...]//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

[...]//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

[...]//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

[...]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

[...]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

[...]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

[...]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

[...]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

[...]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

[...]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

[...]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

[...]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

[...]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

[...]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

JS TEST DRIVERSINON.JS

JS HINT

JS TEST DRIVERSINON.JS

JS HINT

YUI COMPRESSOR

Everyone should be happy

in the wild

in the wild

IN THE WILD

In the wild, there is no health care.

Dwight Schrute (the office)

drawbacks

at the beginning

cost of change

LOOKING FORWARD

LOOKING FORWARD

LOOKING FORWARD

LOOKING FORWARD

LOOKING FORWARD

the further we look at,the more control we need

LOOKING FORWARD

the further we look at,the more control we need

LOOKING FORWARD

the further we look at,the more control we need

LOOKING FORWARD

let's get ready

the further we look at,the more control we need

LOOKING FORWARD

let's get ready

javascript is a programming language

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.

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...

javascript kicks asses

Recommended