Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
571 lines (482 sloc) 17.9 KB
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Testing your JavaScript</title>
<!-- metadata -->
<meta name="generator" content="S5" />
<meta name="version" content="S5 1.1" />
<meta name="presdate" content="20110729" />
<meta name="author" content="Dmytrii Nagirniak" />
<meta name="company" content="Approach Excelllence" />
<!-- configuration parameters -->
<meta name="defaultView" content="slideshow" />
<meta name="controlVis" content="hidden" />
<!-- style sheet links -->
<link rel="stylesheet" href="ui/default/slides.css" type="text/css" media="projection" id="slideProj" />
<link rel="stylesheet" href="ui/default/outline.css" type="text/css" media="screen" id="outlineStyle" />
<link rel="stylesheet" href="ui/default/print.css" type="text/css" media="print" id="slidePrint" />
<link rel="stylesheet" href="ui/default/opera.css" type="text/css" media="projection" id="operaFix" />
<link rel="stylesheet" href="assets/preso.css" type="text/css" media="all" />
<link rel="stylesheet" href="assets/highlightjs/styles/github.css" type="text/css" media="all" />
<!-- S5 JS -->
<script src="ui/default/slides.js" type="text/javascript"></script>
<script src="assets/highlightjs/highlight.js" type="text/javascript"></script>
<script src="assets/highlightjs/languages/coffee.js" type="text/javascript"></script>
<script src="assets/highlightjs/languages/javascript.js" type="text/javascript"></script>
<script src="assets/highlightjs/languages/bash.js" type="text/javascript"></script>
<script type="text/javascript">
hljs.initHighlightingOnLoad();
</script>
</head>
<body>
<div class="layout">
<div id="controls"><!-- DO NOT EDIT --></div>
<div id="currentSlide"><!-- DO NOT EDIT --></div>
<div id="header">
</div>
<div id="footer">
<h1>RORO Melbourne / 28 July 2011</h1>
<h2>Testing your JavaScript</h2>
</div>
</div>
<div class="presentation">
<div class="slide">
<div class="lefty">
<h1>Testing your JavaScript</h1>
<h2>Sharing my experience</h2>
<h3>Dmytrii Nagirniak (~ Dima)</h3>
<h4>Believer that Software Development is an <a href="http://www.ApproachE.com">art</a>.</h4>
</div>
<img alt="Testing JavaScript" src="assets/tests-yo.jpg" class="righty" />
</div>
<div class="slide">
<h1>Purpose</h1>
<ul class="lefty">
<li>Get you to <strong>start testing</strong> JavaScript.</li>
<li><strong>Start discussion</strong> about doing it easier.</li>
<li><strong>Contribute</strong> to <code>guard-rails-assets</code>.</li>
</ul>
<img alt="Start testing" src="assets/start-testing.jpg" class="righty" />
</div>
<div class="slide">
<h1>Outline</h1>
<ul class="lefty">
<li><strong>Why</strong> I had to start testing.</li>
<li><strong>Options</strong> I considered in Ruby world.</li>
<li>Quick <strong>set-up</strong> guide.</li>
<li><strong>Examples</strong> of the tests.</li>
</ul>
<img alt="Outline" src="assets/outline.jpg" class="righty" />
</div>
<div class="slide">
<h1>New approach to existing Idea</h1>
<img alt="Idea" src="assets/idea.jpg" class="lefty" />
Volunteered to make Internet a little better world with <strong>usability in mind</strong>.
</div>
<div class="slide">
<h1>Results of outsourcing</h1>
<h3>
<strong>Too generic</strong> implementation that <strong>breaks the world</strong>.
</h3>
<img alt="Frustrated" src="assets/frustration-man.jpg" class="lefty" />
<img alt="Frustratoin ahead" src="assets/frustration.jpg" class="righty" />
</div>
<div class="slide">
<h1>Single file > 1000 LOC</h1>
<strong>Hard to maintain</strong> - all the front-end logic in a <strong>single</strong> file.
<pre class="lefty"><code class="javascript">SBX.common.overlays();
SBX.common.scrolledToBottom();
SBX.common.toggleButtons();
SBX.common.ajax.loading();
SBX.common.ajax.problem();
SBX.common.placeholders();
SBX.common.searchFocus();
SBX.common.notifications();
SBX.common.validation();
SBX.images.showHideInfo("Show","Hide");
SBX.images.optionsTabs();
SBX.images.navigation();
SBX.images.nextImage();
SBX.images.commentsScrolling();
SBX.images.gearEdit();
SBX.images.titleEdit();
SBX.images.tagEdit();
SBX.images.fixIE();
SBX.images.likeButton();
SBX.users.moreUsers();
SBX.users.moreHappenings();
SBX.profile.tooltips();
SBX.profile.gearAutocomplete();
SBX.profile.removeGear();
SBX.upgrade.tooltips();
SBX.upload.createUploader();
SBX.upload.progressBar();
SBX.upload.sortImages();
SBX.settings.colorpickers();
SBX.settings.inPlaceEdit();
SBX.settings.imagePreview();
SBX.settings.liveColorEdit();
</code></pre>
<img alt="Too much stuff in a file" src="assets/matrix.jpg" class="righty" />
</div>
<div class="slide">
<h1>Magical behaviour</h1>
<img alt="Too much magic" src="assets/magic.jpg" />
Too much <strong>magic</strong>.
<pre class="lefty"><code class='javascript'>try {
var data = $.parseJSON(dataR);
} catch (e) {
try {
dataR = dataR.replace(/&lt;\/?pre[^&gt;]*&gt;/igm,&quot;&quot;);
data = $.parseJSON(dataR);
} catch (ex) {
$(form).trigger(&quot;server-error&quot;,[jqXHR, textStatus, ex]);
}
}
</code></pre>
</div>
<div class="slide">
<h1>Unconditional initialisation</h1>
<pre ><code class='javascript'>var moreUsers = function (){
$('.content').bind('on-bottom',function(){ /*...*/ })
},
var moreHappenings = function (){
$('.content').bind('on-bottom',function(){ /*...*/ })
}
/* etc */
// All handlers are executed as expected.
// But the interaction is NOT expected.
$('.content').trigger('on-bottom');
</code></pre>
</div>
<div class="slide">
<h1>How to fix?</h1>
<ul class="lefty">
<li>Integration tests (Capybara >= 1.0).</li>
<li>JavaScript unit testing.</li>
<li>Incremental updates.</li>
</ul>
<img alt="Fixing?" src="assets/thinking.jpg" class="righty" />
</div>
<div class="slide">
<h1>A word on Capybara</h1>
<img alt="Capybara" src="assets/capybara.jpg" class="lefty" />
Using Ruby to <strong>inject JavaScript</strong> for testing.
<br />
A sign that the tool is used the wrong way.
</div>
<div class="slide">
<h1>Considered JS Unit-Testing options</h1>
<ul class="lefty">
<li>
<a href="http://javascriptmvc.com/">JavaScriptMVC</a>
</li>
<li>
<a href="http://docs.jquery.com/Qunit">QUnit</a>
</li>
<li>
<a href="http://pivotal.github.com/jasmine/">Jasmine</a>
</li>
<li>
Selenium (1 and 2) and JsTestDriver
</li>
</ul>
<img alt="Frameworks" src="assets/frameworks.png" class="righty" />
</div>
<div class="slide">
<h1>Tools: JavaScript</h1>
<ul class="lefty">
<li>
<a href="http://jashkenas.github.com/coffee-script/">CoffeeScript</a>
</li>
<li>
<a href="https://github.com/velesin/jasmine-jquery">jasmine-jquery</a>
</li>
<li>
<a href="https://github.com/pivotal/jasmine-ajax">jasmine-ajax</a>
</li>
</ul>
<img alt="DHH and CoffeeScript" src="assets/dhh-coffee.png" class="righty" />
<img alt="Tools" src="assets/tools.jpg" class="righty" />
</div>
<div class="slide">
<h1>Tools: Ruby</h1>
<ul class="lefty">
<li>
<a href="http://johnbintz.github.com/jasmine-headless-webkit/">jasmine-headless-webkit</a>
</li>
<li>
<a href="https://github.com/guard/guard-rails-assets">guard-rails-assets</a>
</li>
<li>
<a href="https://github.com/johnbintz/guard-jasmine-headless-webkit">guard-jasmine-headless-webkit</a>
</li>
</ul>
<img alt="Tools" src="assets/ruby-tools.png" class="righty" />
</div>
<div class="slide">
<h1>Setting everything up</h1>
<img alt="Works On My Machine" src="assets/womm.jpg" />
</div>
<div class="slide">
<h1>Update Gemfile</h1>
<pre><code class='ruby'>
# Gemfile - only shortlist - add yours of course, including execjs
group :development, :test do
gem 'jasmine'
end
group :test do
gem 'guard-coffeescript'
gem 'guard-rails-assets'
gem 'guard-jasmine-headless-webkit'
gem 'jasmine-headless-webkit', :git => 'https://github.com/johnbintz/jasmine-headless-webkit.git' # https://github.com/johnbintz/jasmine-headless-webkit/issues/10#issuecomment-1348568
end
</code></pre>
<pre><code class='bash'>&gt; bundle</code></pre>
</div>
<div class="slide">
<h1>Setup Guard</h1>
<code>bundle exec guard init</code>
<pre><code class='ruby'>
# Guardfile
guard &#39;rails-assets&#39; do
watch %r{^app/assets/.+$}
watch &#39;config/application.rb&#39;
end
guard &#39;coffeescript&#39;, :output =&gt; &#39;spec/javascripts/compiled&#39;, :hide_success =&gt; true do
watch %r{^spec/javascripts/(.+\.coffee)$}
end
guard &#39;jasmine-headless-webkit&#39; do
watch %r{^public/assets/application-(.*)\.js}
watch(%r{^spec/javascripts/compiled/(.*)_spec\..*}) { |m| newest_js_file(&quot;spec/javascripts/compiled/#{m[1]}_spec&quot;) }
end
</code></pre>
</div>
<div class="slide">
<h1>Setup Jasmine</h1>
<div class="lefty">
<code>bundle exec jasmine init</code>
<p>
You are set-up and should be able to <strong>run sample specs</strong>.
</p>
<code>bundle exec rake jasmine:ci</code>
</div>
<img alt="Jasmine" src="assets/jasmine.jpg" class="righty" />
</div>
<div class="slide">
<h1>Setup Jasmine: JavaScript tools</h1>
<pre><code class='bash'>
cd spec/javascripts/helpers/
wget "http://cloud.github.com/downloads/velesin/jasmine-jquery/jasmine-jquery-1.2.0.js"
wget "https://raw.github.com/pivotal/jasmine-ajax/master/lib/mock-ajax.js"
wget "https://raw.github.com/pivotal/jasmine-ajax/master/lib/spec-helper.js"
</code></pre>
<h3>NOTE: You might need to merge <code>spec-helper.js</code> manually.</h3>
</div>
<div class="slide">
<h1>Setup Jasmine: configure file locations</h1>
<code>vim spec/javascripts/support/jasmine.yml</code>
<p>Use wildcard as file names of assets include cache buster.</p>
<pre><code>
src_files:
- public/assets/first-priority-files-*.js
- public/assets/application-*.js
- public/assets/**/*.js
helpers:
- helpers/**/*.js
- compiled/helpers/*.js
spec_files:
- '**/*[sS]pec.js'
</code></pre>
</div>
<div class="slide">
<h1>Done! Use it!</h1>
Run <code>bundle exec guard</code>, move the terminal away and start coding.
<img alt="spec runner example" src="assets/guard-headless-specs.png" />
</div>
<div class="slide">
<h1>Example - namespaces</h1>
<pre><code class='coffee'>
describe &#39;App&#39;, -&gt;
someFunc = -&gt; # empty function
describe &#39;export&#39;, -&gt;
beforeEach -&gt; App.exports &#39;fakeNamespace&#39;, &#39;module&#39;, {someFunc}
afterEach -&gt; delete App.fakeNamespace
it &#39;should register namespace&#39;, -&gt;
expect(App.fakeNamespace).toBeTruthy()
it &#39;should register module with functions&#39;, -&gt;
expect(App.fakeNamespace.module.someFunc).toBe someFunc
</code></pre>
</div>
<div class="slide">
<h1>Example - Rails CSRF with AJAX</h1>
<pre><code class='coffee'>
describe &#39;Rails Ajax support&#39;, -&gt;
beforeEach -&gt;
$(&#39;head&#39;).append &quot;&lt;meta content=&#39;bla&#39; class=&#39;test&#39; name=&#39;csrf-token&#39; /&gt;&quot;
$(&#39;head&#39;).append &#39;&lt;meta content=&quot;authenticity_token&quot; class=&quot;test&quot; name=&quot;csrf-param&quot; /&gt;&#39;
App.utils.rails.init()
afterEach -&gt; $(&#39;.test&#39;).remove()
it &#39;should include CSRF token from page&#39;, -&gt;
$.get &#39;/something&#39;
request = mostRecentAjaxRequest() # notice AJAX is stubbed!
expect(request.url).toMatch /authenticity_token=bla/
</code></pre>
</div>
<div class="slide">
<h1>Example - AJAX + alert + animation</h1>
<pre><code class='coffee'>
describe &#39;comment deletion&#39;, -&gt;
action = null
beforeEach -&gt;
action = App.images.commentActions
action.init()
it &quot;should remove the comment with AJAX&quot;, -&gt;
item = $(&#39;.comments ul &gt; li:first&#39;)
item.find(&#39;a.delete&#39;).click()
mostRecentAjaxRequest().response
status: 200
contentType: &#39;application/json&#39;
responseText: &#39;{&quot;success&quot;: true}&#39;
$.when(item).done -&gt;
expect($(&quot;.comments ul.list&quot;)).not.toContain &quot;&gt; li&quot;
</code></pre>
</div>
<div class="slide">
<h1>Example - AJAX, verifying REAL-ish DOM</h1>
<pre><code class='coffee'>
it &quot;should append comment after submit&quot;, -&gt;
$(&#39;form.comment&#39;).submit()
respond mostRecentAjaxRequest()
expect($(&#39;article.image&#39;)).toContain &#39;.comments ul &gt; li.just-added-fake&#39;
expect($(&#39;article.image&#39;)).not.toContain &#39;.comments ul ul li.just-added-fake&#39;
</code></pre>
</div>
<div class="slide">
<h1>Example - DOM events</h1>
<pre><code class='coffee'>
it &#39;should trigger next on click&#39;, -&gt;
spyOn(nav, &#39;onNext&#39;)
el = $(&#39;a.next:first&#39;).trigger(&#39;click&#39;)
expect(nav.onNext).toHaveBeenCalledWith el[0]
it &#39;should load images on last click&#39;, -&gt;
spyOnEvent $(&#39;.content&#39;), &#39;on-bottom&#39;
$(&#39;a.next:last&#39;).trigger(&#39;click&#39;)
expect(&#39;on-bottom&#39;).toHaveBeenTriggeredOn $(&#39;.content&#39;)
</code></pre>
</div>
<div class="slide">
<h1>Example - Inline <del>Fixtures</del> Factories</h1>
<pre><code class='coffee'>
describe &#39;Imgae Info commenting&#39;, -&gt;
beforeEach -&gt; App.images.commenter.init()
it &quot;should submit comment as AJAX&quot;, -&gt;
setFixtures &quot;
&lt;div id=#{stringInterpolationIsUseful} class=&#39;content commenting&#39;&gt;
etc
&lt;/article&gt;
&lt;/div&gt;
&quot;
$(&#39;form.comment&#39;).submit()
expect(mostRecentAjaxRequest()).toBeTruthy()
</code></pre>
</div>
<div class="slide">
<h1>Example - Generating long HTML</h1>
<pre><code class='coffee'>html = (&quot;
&lt;div id=&#39;more#{i}&#39; class=&#39;content-wrap&#39;&gt;
&lt;div class=&#39;photo&#39;&gt;
&lt;div class=&#39;image&#39;&gt;
&lt;img src=&#39;data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==&#39;/&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&quot; for i in [1,3]).join &quot;\n&quot;
respond = (req) -&gt;
req.response
status: 200
contentType: &#39;text/html&#39;
responseText: html
# Can be used as:
respond mostRecentAjaxRequest()
</code></pre>
</div>
<div class="slide">
<h1>Example - expectations on functions</h1>
<pre><code class='coffee'>it &#39;should not vote when clicking like button twice&#39;, -&gt;
spyOn(liker, &#39;vote&#39;)
$(&#39;.button-like&#39;).click().click().click()
expect(liker.vote.calls.length).toEqual 1
</code></pre>
</div>
<div class="slide">
<h1>Lessons learned: General</h1>
<ul class="lefty">
<li>Front-end dev - <strong>part of the team</strong>.</li>
<li>Good UX/UI is hard.</li>
<li><strong>Rich app</strong> vs <strong>Web Site</strong>.</li>
</ul>
<img alt="Lessons learned" src="assets/lessons-learned.jpg" class="righty" />
</div>
<div class="slide">
<h1>Lessons learned: Technical shit</h1>
<ul class="lefty">
<li>Say NO to <strong>old browsers</strong>.</li>
<li>Watch out for <strong>CoffeeScript</strong> <sup><em>[Python is here]</em></sup>.</li>
<li>Tripple test Rails 3.1 <strong>assets compression</strong>.</li>
<li>Tempting to <strong>overtest</strong>.</li>
</ul>
<img alt="Scratching head" src="assets/head-scratch.jpg" class="righty" />
</div>
<div class="slide">
<h1>Existing problems</h1>
<ul class="lefty">
<li>
<a href="https://github.com/guard/guard-rails-assets">guard-rails-assets</a> is <strong> slow</strong>.
PLZ Contribute (spork?).
</li>
<li><strong>Single page</strong> tests. <strong>Clean up properly</strong>.</li>
<li>Compiled specs in <strong>source control</strong>.</li>
<li>Guard to <strong>compile CoffeeScript</strong> specs.</li>
</ul>
<img alt="Still problems" src="assets/problem.gif" class="righty" />
</div>
<div class="slide">
<h1>Summary</h1>
<ul class="lefty">
<li>More <strong>confidence</strong> in the project.</li>
<li>Enabled <strong>fast feedback</strong>.</li>
<li>Spent <strong>significantly more</strong> time.</li>
<li>Still <strong>finding my way</strong> of testing things.</li>
</ul>
<img alt="Summary" src="assets/summary.jpg" class="righty" />
</div>
<div class="slide">
<h1>Recommendation</h1>
<a href="http://tddjs.com/" class="lefty">
<img alt="Cover of TDDJS book" src="assets/ttdjs.png" />
</a>
This <strong>single</strong> book will give you a great start understanding JavaScript and getting your head around testing.
</div>
<div class="slide">
<h1>Thanks!</h1>
<a href="http://www.ApproachE.com">
<img src="assets/me.jpg" alt="Photo of Dmytrii Nagirniak" />
<img alt="Sweat-out" src="assets/phew.jpg" />
</a>
<ul class="righty">
<li><a href="http://twitter.com/dnagir">@dnagir</a></li>
<li><a href="http://blog.ApproachE.com">blog.ApproachE.com</a></li>
<li><a href="http://github.com/dnagir">github.com/dnagir</a></li>
<li>The rest is at <a href="http://www.ApproachE.com">ApproachE.com</a></li>
</ul>
<p>
This presentation is available at
<br />
<a href="http://dev.approache.com/testing-your-javascript/">dev.ApproachE.com/testing-your-javascript</a>
</p>
</div>
</div>
</body>
</html>