Compare commits
No commits in common. "master" and "gh-pages" have entirely different histories.
4
.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
.DS_Store
|
||||
.sass-cache/
|
||||
_export/
|
||||
_site/
|
9
Gemfile
@ -1,9 +0,0 @@
|
||||
source :rubygems
|
||||
|
||||
gem 'attentive', :path => '../attentive'
|
||||
gem 'rack-livereload'
|
||||
|
||||
gem 'guard'
|
||||
gem 'guard-livereload'
|
||||
|
||||
gem 'thin'
|
172
Gemfile.lock
@ -1,172 +0,0 @@
|
||||
PATH
|
||||
remote: ../attentive
|
||||
specs:
|
||||
attentive (0.0.1)
|
||||
backbone-rails
|
||||
coffee-script
|
||||
compass (~> 0.12.rc)
|
||||
haml
|
||||
naturalsort (~> 1.1.1)
|
||||
nokogiri
|
||||
pygments.rb
|
||||
rack (~> 1.4.0)
|
||||
rdiscount
|
||||
selenium-webdriver
|
||||
sinatra
|
||||
sprockets
|
||||
sprockets-sass
|
||||
sprockets-vendor_gems
|
||||
thor
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
actionmailer (3.2.2)
|
||||
actionpack (= 3.2.2)
|
||||
mail (~> 2.4.0)
|
||||
actionpack (3.2.2)
|
||||
activemodel (= 3.2.2)
|
||||
activesupport (= 3.2.2)
|
||||
builder (~> 3.0.0)
|
||||
erubis (~> 2.7.0)
|
||||
journey (~> 1.0.1)
|
||||
rack (~> 1.4.0)
|
||||
rack-cache (~> 1.1)
|
||||
rack-test (~> 0.6.1)
|
||||
sprockets (~> 2.1.2)
|
||||
activemodel (3.2.2)
|
||||
activesupport (= 3.2.2)
|
||||
builder (~> 3.0.0)
|
||||
activerecord (3.2.2)
|
||||
activemodel (= 3.2.2)
|
||||
activesupport (= 3.2.2)
|
||||
arel (~> 3.0.2)
|
||||
tzinfo (~> 0.3.29)
|
||||
activeresource (3.2.2)
|
||||
activemodel (= 3.2.2)
|
||||
activesupport (= 3.2.2)
|
||||
activesupport (3.2.2)
|
||||
i18n (~> 0.6)
|
||||
multi_json (~> 1.0)
|
||||
addressable (2.2.6)
|
||||
arel (3.0.2)
|
||||
backbone-rails (0.9.1)
|
||||
rails (>= 3.0.0)
|
||||
blankslate (2.1.2.4)
|
||||
builder (3.0.0)
|
||||
childprocess (0.3.1)
|
||||
ffi (~> 1.0.6)
|
||||
chunky_png (1.2.5)
|
||||
coffee-script (2.2.0)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.2.0)
|
||||
compass (0.12.1)
|
||||
chunky_png (~> 1.2)
|
||||
fssm (>= 0.2.7)
|
||||
sass (~> 3.1)
|
||||
daemons (1.1.8)
|
||||
em-websocket (0.3.6)
|
||||
addressable (>= 2.1.1)
|
||||
eventmachine (>= 0.12.9)
|
||||
erubis (2.7.0)
|
||||
eventmachine (0.12.10)
|
||||
execjs (1.3.0)
|
||||
multi_json (~> 1.0)
|
||||
ffi (1.0.11)
|
||||
fssm (0.2.8.1)
|
||||
guard (1.0.0)
|
||||
ffi (>= 0.5.0)
|
||||
thor (~> 0.14.6)
|
||||
guard-livereload (0.4.0)
|
||||
em-websocket (>= 0.2.0)
|
||||
guard (>= 0.10.0)
|
||||
multi_json (~> 1.0.3)
|
||||
haml (3.1.4)
|
||||
hike (1.2.1)
|
||||
i18n (0.6.0)
|
||||
journey (1.0.3)
|
||||
json (1.6.5)
|
||||
mail (2.4.4)
|
||||
i18n (>= 0.4.0)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
mime-types (1.18)
|
||||
multi_json (1.0.4)
|
||||
naturalsort (1.1.1)
|
||||
nokogiri (1.5.2)
|
||||
polyglot (0.3.3)
|
||||
pygments.rb (0.2.7)
|
||||
rubypython (~> 0.5.3)
|
||||
rack (1.4.1)
|
||||
rack-cache (1.2)
|
||||
rack (>= 0.4)
|
||||
rack-livereload (0.3.4)
|
||||
rack
|
||||
rack-protection (1.2.0)
|
||||
rack
|
||||
rack-ssl (1.3.2)
|
||||
rack
|
||||
rack-test (0.6.1)
|
||||
rack (>= 1.0)
|
||||
rails (3.2.2)
|
||||
actionmailer (= 3.2.2)
|
||||
actionpack (= 3.2.2)
|
||||
activerecord (= 3.2.2)
|
||||
activeresource (= 3.2.2)
|
||||
activesupport (= 3.2.2)
|
||||
bundler (~> 1.0)
|
||||
railties (= 3.2.2)
|
||||
railties (3.2.2)
|
||||
actionpack (= 3.2.2)
|
||||
activesupport (= 3.2.2)
|
||||
rack-ssl (~> 1.3.2)
|
||||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (~> 0.14.6)
|
||||
rake (0.9.2.2)
|
||||
rdiscount (1.6.8)
|
||||
rdoc (3.12)
|
||||
json (~> 1.4)
|
||||
rubypython (0.5.3)
|
||||
blankslate (>= 2.1.2.3)
|
||||
ffi (~> 1.0.7)
|
||||
rubyzip (0.9.6.1)
|
||||
sass (3.1.15)
|
||||
selenium-webdriver (2.20.0)
|
||||
childprocess (>= 0.2.5)
|
||||
ffi (~> 1.0)
|
||||
multi_json (~> 1.0)
|
||||
rubyzip
|
||||
sinatra (1.3.2)
|
||||
rack (~> 1.3, >= 1.3.6)
|
||||
rack-protection (~> 1.2)
|
||||
tilt (~> 1.3, >= 1.3.3)
|
||||
sprockets (2.1.2)
|
||||
hike (~> 1.2)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sprockets-sass (0.7.0)
|
||||
sprockets (~> 2.0)
|
||||
tilt (~> 1.1)
|
||||
sprockets-vendor_gems (0.1.1)
|
||||
thin (1.3.1)
|
||||
daemons (>= 1.0.9)
|
||||
eventmachine (>= 0.12.6)
|
||||
rack (>= 1.0.0)
|
||||
thor (0.14.6)
|
||||
tilt (1.3.3)
|
||||
treetop (1.4.10)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.32)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
attentive!
|
||||
guard
|
||||
guard-livereload
|
||||
rack-livereload
|
||||
thin
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 43 B |
Before Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 26 KiB |
@ -1,4 +0,0 @@
|
||||
#= require attentive
|
||||
#
|
||||
Attentive.Presentation.setup('#slides')
|
||||
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
@ -1,43 +0,0 @@
|
||||
@import 'attentive';
|
||||
|
||||
$color: #d4d2bf;
|
||||
|
||||
body, html {
|
||||
background-color: $color;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
font-family: Acme, sans-serif;
|
||||
}
|
||||
|
||||
div.highlight {
|
||||
background-color: adjust-lightness($color, -10);
|
||||
}
|
||||
|
||||
.style-image-80-percent {
|
||||
img {
|
||||
height: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
#slides {
|
||||
@include transition-duration(0.3s);
|
||||
}
|
||||
|
||||
#intro {
|
||||
width: 90%;
|
||||
|
||||
@include clearfix;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
width: 30%;
|
||||
@include float-left;
|
||||
}
|
||||
|
||||
div {
|
||||
width: 70%;
|
||||
@include float-right;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 43 B After Width: | Height: | Size: 43 B |
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
1492
index.html
Normal file
@ -1,9 +0,0 @@
|
||||
require 'rack-livereload'
|
||||
|
||||
Attentive.configure do |c|
|
||||
c.title = "Tea Time: A Beginner's Guide to Jasmine"
|
||||
c.middleware << Rack::LiveReload
|
||||
|
||||
#c.export_size = '1280x1024'
|
||||
end
|
||||
|
@ -1,146 +0,0 @@
|
||||
!SLIDE
|
||||
<div id="intro">
|
||||
<img src="assets/john-drinking-tea.png" />
|
||||
<div>
|
||||
<h1>Tea Time</h1>
|
||||
<h2>A Beginner's Guide to JavaScript Testing using Jasmine</h2>
|
||||
<h3>By John Bintz</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
!SLIDE
|
||||
# Automated testing is important
|
||||
|
||||
!SLIDE
|
||||
# Why is it important?
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/checklist.png" />
|
||||
|
||||
!SLIDE
|
||||
# Fortunately, we're beyond that nowadays
|
||||
|
||||
!SLIDE even-larger
|
||||
``` ruby
|
||||
describe MyCoolWebsite do
|
||||
let(:website) { described_class.new }
|
||||
|
||||
describe '#cool_method' do
|
||||
subject { website.cool_method }
|
||||
|
||||
let(:oh_yeah) { [ double_cool ] }
|
||||
let(:double_cool) { 'double cool' }
|
||||
|
||||
before do
|
||||
website.stubs(:whoa_cool).
|
||||
returns(oh_yeah)
|
||||
end
|
||||
|
||||
it { should == double_cool }
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# But there's more to web apps than Ruby nowadays...
|
||||
|
||||
!SLIDE even-larger
|
||||
``` html
|
||||
<img src="normal.gif"
|
||||
onmouseover="this.src='hover.gif'"
|
||||
onmouseout="this.src='normal.gif'" />
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` javascript
|
||||
function showMyCoolTitle(title, length) {
|
||||
if (length == null) { length = 0; }
|
||||
|
||||
if (length <= title.length) {
|
||||
document.title =
|
||||
title.substr(0, length);
|
||||
|
||||
length++;
|
||||
|
||||
setTimeout(function() {
|
||||
showMyCoolTitle(title, length);
|
||||
}, 75);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` javascript
|
||||
window.onload = function() {
|
||||
showMyCoolTitle(
|
||||
"My cool website! Whoaaaaa!"
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# jQuery
|
||||
|
||||
!SLIDE
|
||||
# Backbone.js
|
||||
|
||||
!SLIDE
|
||||
# Sprockets and RequireJS
|
||||
|
||||
!SLIDE
|
||||
# Automated testing is important
|
||||
|
||||
!SLIDE even-larger
|
||||
``` ruby
|
||||
describe MyCoolWebsite do
|
||||
let(:website) { described_class.new }
|
||||
|
||||
describe '#cool_method' do
|
||||
subject { website.cool_method }
|
||||
|
||||
let(:oh_yeah) { [ double_cool ] }
|
||||
let(:double_cool) { 'double cool' }
|
||||
|
||||
before do
|
||||
website.stubs(:whoa_cool).
|
||||
returns(oh_yeah)
|
||||
end
|
||||
|
||||
it { should == double_cool }
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'MyCoolWebsiteView', ->
|
||||
beforeEach ->
|
||||
@website = new MyCoolWebsiteView()
|
||||
|
||||
describe '#coolMethod', ->
|
||||
doubleCool = 'double cool'
|
||||
ohYeah = [ doubleCool ]
|
||||
|
||||
beforeEach ->
|
||||
@website.whoaCool = -> ohYeah
|
||||
|
||||
it 'should be double cool', ->
|
||||
expect(@website.coolMethod()).
|
||||
toEqual(doubleCool)
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Jasmine
|
||||
|
||||
!SLIDE
|
||||
# BDD unit testing framework for JavaScript
|
||||
|
||||
!SLIDE
|
||||
# Platform independent
|
||||
|
||||
!SLIDE
|
||||
# Easily extended
|
||||
|
||||
!SLIDE
|
||||
# Very easy to learn!
|
||||
|
@ -1,27 +0,0 @@
|
||||
!SLIDE
|
||||
# Follow along!
|
||||
|
||||
!SLIDE
|
||||
## [johnbintz.github.com/tea-time](http://johnbintz.github.com/tea-time/)
|
||||
|
||||
!SLIDE
|
||||
# No need to install anything right now
|
||||
|
||||
!SLIDE
|
||||
# [tryjasmine.com](http://tryjasmine.com/)
|
||||
|
||||
!SLIDE
|
||||
# Specs on the left
|
||||
|
||||
!SLIDE
|
||||
# Code under test on the right
|
||||
|
||||
!SLIDE
|
||||
# Write code in CoffeeScript
|
||||
|
||||
!SLIDE
|
||||
# Ready?
|
||||
|
||||
!SLIDE
|
||||
# Let's go!
|
||||
|
@ -1,62 +0,0 @@
|
||||
!SLIDE
|
||||
# `describe`
|
||||
|
||||
!SLIDE
|
||||
# Describes a thing or a behavior of a thing
|
||||
|
||||
!SLIDE
|
||||
# Let's describe...
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/relevant-cat.jpg" />
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
# cat behavior descriptions go here
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Something that cats do...
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/cat-meow.jpg" />
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
# description of the
|
||||
# meow behavior goes here
|
||||
```
|
||||
|
||||
!SLIDE larger
|
||||
# John behavior #1
|
||||
## Use Ruby-style indicators for instance- and class-level methods
|
||||
|
||||
``` coffeescript
|
||||
describe 'John', ->
|
||||
describe 'spec definitions', ->
|
||||
it 'should look like you did it in RSpec', ->
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Describe how we expect a cat to meow
|
||||
|
||||
!SLIDE
|
||||
# `it`
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
it 'should meow correctly', ->
|
||||
# expectation of a cat meowing
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# We have the description...
|
||||
|
||||
!SLIDE
|
||||
# Now let's add the expectations!
|
||||
|
@ -1,91 +0,0 @@
|
||||
!SLIDE
|
||||
# `expect`
|
||||
|
||||
!SLIDE
|
||||
# What should we get as an output?
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
it 'should meow correctly', ->
|
||||
expect(cat.meow()).toEqual('meow')
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Wait, we need a cat.
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
it 'should meow correctly', ->
|
||||
cat = new Cat()
|
||||
|
||||
expect(cat.meow()).toEqual('meow')
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
# code-under-test
|
||||
|
||||
class @Cat
|
||||
meow: ->
|
||||
```
|
||||
|
||||
!SLIDE larger
|
||||
``` javascript
|
||||
// safety wrapper to prevent global pollution
|
||||
(function() {
|
||||
this.Cat = (function() {
|
||||
function Cat() {}
|
||||
Cat.prototype.meow = function() {};
|
||||
return Cat;
|
||||
})();
|
||||
})(this) // this is window in a browser
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Run it!
|
||||
|
||||
!SLIDE even-larger
|
||||
```
|
||||
1 spec, 1 failure
|
||||
|
||||
Expected undefined to equal 'meow'.
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Make it meow!
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
class @Cat
|
||||
meow: -> "meow"
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
```
|
||||
1 spec, 0 failures
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Here's what you should have meow...
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
# spec
|
||||
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
it 'should meow correctly', ->
|
||||
expect(cat.meow()).toEqual('meow')
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
# code-under-test
|
||||
|
||||
class @Cat
|
||||
meow: -> "meow"
|
||||
```
|
@ -1,124 +0,0 @@
|
||||
!SLIDE
|
||||
# What if the cat meows differently based on certain states?
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/hungry-cat.jpg" />
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/cat-carrier.jpg" />
|
||||
|
||||
!SLIDE
|
||||
# Nested `describe`
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
describe 'hungry', ->
|
||||
# Cat#meow expectation for when
|
||||
# the cat is hungry
|
||||
|
||||
describe 'going to the vet', ->
|
||||
# Cat#meow expectation for when
|
||||
# the cat knows it's vet time
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'hungry', ->
|
||||
it 'should be a mournful meow', ->
|
||||
cat = new Cat()
|
||||
cat.state = -> Cat.HUNGRY
|
||||
# ...just like cat.stubs(:state)
|
||||
|
||||
expect(cat.meow()).toEqual("meeeyaow")
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'going to the vet', ->
|
||||
it 'should be an evil meow', ->
|
||||
cat = new Cat()
|
||||
cat.state = -> Cat.VET_PSYCHIC
|
||||
# ...just like the one above
|
||||
|
||||
expect(cat.meow()).toEqual("raowwww")
|
||||
```
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/wet-cat.jpg" />
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
cat = new Cat()
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` ruby
|
||||
before do
|
||||
@cat = Cat.new
|
||||
end
|
||||
|
||||
it 'should be a mournful meow' do
|
||||
@cat.stubs(:state).returns(Cat::HUNGRY)
|
||||
|
||||
@cat.meow.should == "meeyaow"
|
||||
end
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` ruby
|
||||
before -> it -> after
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` ruby
|
||||
before do
|
||||
@instance_variable = "yes"
|
||||
end
|
||||
|
||||
it "is in same context as before block" do
|
||||
@instance_variable.should == "yes"
|
||||
end
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
beforeEach -> it -> afterEach
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
beforeEach ->
|
||||
@instanceVariable = "yes"
|
||||
|
||||
it "should be in the same context", ->
|
||||
expect(@instanceVariable).toEqual("yes")
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
beforeEach ->
|
||||
@cat = new Cat()
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'hungry', ->
|
||||
it 'should be a mournful meow', ->
|
||||
@cat.state = -> Cat.HUNGRY
|
||||
|
||||
expect(@cat.meow()).toEqual("meeeyaow")
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'going to the vet', ->
|
||||
it 'should be an evil meow', ->
|
||||
@cat.state = -> Cat.VET_PSYCHIC
|
||||
|
||||
expect(@cat.meow()).toEqual("raowwww")
|
||||
```
|
||||
|
@ -1,130 +0,0 @@
|
||||
!SLIDE
|
||||
# A little semantics game...
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
describe 'hungry', ->
|
||||
# cat codes
|
||||
|
||||
describe 'going to the vet', ->
|
||||
# moar cat codes
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# This works, but it can be clearer
|
||||
|
||||
!SLIDE even-larger
|
||||
``` ruby
|
||||
describe Cat do
|
||||
describe '#meow' do
|
||||
describe 'hungry' do
|
||||
# cat codes
|
||||
end
|
||||
|
||||
describe 'going to the vet' do
|
||||
# moar cat codes
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# `context`
|
||||
|
||||
!SLIDE
|
||||
# Description of different states for a test
|
||||
|
||||
!SLIDE even-larger
|
||||
``` ruby
|
||||
alias :context :describe
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` ruby
|
||||
describe Cat do
|
||||
let(:cat) { described_class.new }
|
||||
|
||||
# save describe for
|
||||
# things or behaviors...
|
||||
describe '#meow' do
|
||||
subject { cat.meow }
|
||||
|
||||
# use context to describe states
|
||||
context 'hungry' do
|
||||
# cat codes
|
||||
end
|
||||
|
||||
context 'going to the vet' do
|
||||
# moar cat codes
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Jasmine doesn't have `context`
|
||||
|
||||
!SLIDE
|
||||
# However...
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
this.context = this.describe
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
this.context = this.describe
|
||||
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
context 'hungry', ->
|
||||
# cat codes
|
||||
|
||||
context 'going to the vet', ->
|
||||
# moar cat codes
|
||||
```
|
||||
|
||||
!SLIDE larger
|
||||
``` coffeescript
|
||||
this.context = this.describe
|
||||
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
beforeEach ->
|
||||
@cat = new Cat()
|
||||
|
||||
context 'hungry', ->
|
||||
it 'should be a mournful meow', ->
|
||||
@cat.state = -> Cat.HUNGRY
|
||||
|
||||
expect(@cat.meow()).toEqual("meeeyaow")
|
||||
|
||||
context 'going to the vet', ->
|
||||
it 'should be an evil meow', ->
|
||||
@cat.state = -> Cat.VET_PSYCHIC
|
||||
|
||||
expect(@cat.meow()).toEqual("raowwww")
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
class @Cat
|
||||
@HUNGRY = 'hungry'
|
||||
@VET_PSYCHIC = 'vet psychic'
|
||||
|
||||
meow: ->
|
||||
switch this.state()
|
||||
when Cat.HUNGRY
|
||||
"meeeyaow"
|
||||
when Cat.VET_PSYCHIC
|
||||
"raowwww"
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
```
|
||||
2 spec, 0 failures
|
||||
```
|
||||
|
@ -1,60 +0,0 @@
|
||||
!SLIDE
|
||||
# Matchers
|
||||
|
||||
!SLIDE even-larger
|
||||
``` ruby
|
||||
cat.meow.should == "meow"
|
||||
cat.should be_a_kind_of(Cat)
|
||||
cat.should_not be_hungry
|
||||
# => cat.hungry?.should == false
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
expect(cat.meow()).toEqual("meow")
|
||||
expect(cat.constructor).toEqual(Cat)
|
||||
expect(cat.isHungry()).not.toBeTruthy()
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
# Lots of built in matchers
|
||||
|
||||
``` coffeescript
|
||||
toEqual(object)
|
||||
toBeTruthy()
|
||||
toBeFalsy()
|
||||
toBeGreaterThan()
|
||||
toBeLessThan()
|
||||
toBeUndefined()
|
||||
toContain()
|
||||
toMatch()
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
expect(cat.isHungry()).not.toBeTruthy()
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Create your own matchers!
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
MyMatchers =
|
||||
toBeHungry: ->
|
||||
return @actual.isHungry() == true
|
||||
|
||||
beforeEach ->
|
||||
this.addMatchers(MyMatchers)
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
beforeEach ->
|
||||
@cat = new Cat()
|
||||
|
||||
it 'should not be hungry', ->
|
||||
expect(@cat).not.toBeHungry()
|
||||
```
|
||||
|
@ -1,380 +0,0 @@
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe
|
||||
it
|
||||
expect
|
||||
toSomething()
|
||||
beforeEach
|
||||
afterEach
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Jasmine == unit testing
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/synergize.jpg" />
|
||||
|
||||
!SLIDE
|
||||
# No, this isn't a talk about integration testing
|
||||
|
||||
!SLIDE
|
||||
# Testing the *right* things in your JavaScript unit tests
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/spaghetti.jpg" />
|
||||
|
||||
!SLIDE
|
||||
# John behavior #2
|
||||
## Mock, stub, and spy on anything that should be handled in an integration test
|
||||
|
||||
``` coffeescript
|
||||
describe 'John', ->
|
||||
describe 'spec definitions', ->
|
||||
it 'should keep unit tests as focused as possible', ->
|
||||
```
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/beer-cat.jpg" />
|
||||
|
||||
!SLIDE even-larger
|
||||
``` gherkin
|
||||
Feature: Cat Behaviors
|
||||
Scenario: Hungry cats meow a certain way
|
||||
Given I have a cat
|
||||
And the cat is hungry
|
||||
When the cat meows
|
||||
Then the meow should be a "meeyaow"
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
class @Cat
|
||||
@FOOD_THRESHOLD = 20
|
||||
@HUNGRY = 'hungry'
|
||||
|
||||
constructor: (@foodLevel = 30) ->
|
||||
|
||||
meow: ->
|
||||
switch this.state()
|
||||
when Cat.HUNGRY
|
||||
"meeyaow"
|
||||
|
||||
state: ->
|
||||
if @foodLevel < Cat.FOOD_THRESHOLD
|
||||
Cat.HUNGRY
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
context 'hungry', ->
|
||||
it 'should be a mournful meow', ->
|
||||
cat = new Cat()
|
||||
cat.foodLevel = 15
|
||||
|
||||
expect(cat.meow()).
|
||||
toEqual("meeeyaow")
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# A perfectly cromulent test
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
class @Cat
|
||||
meow: ->
|
||||
switch this.state()
|
||||
# ^^^ dependent code executed
|
||||
when Cat.HUNGRY
|
||||
"meeyaow"
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Why make your unit tests fragile?
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
cat.foodLevel = 15
|
||||
# do we care about food level?
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'Cat', ->
|
||||
describe '#meow', ->
|
||||
context 'hungry', ->
|
||||
it 'should be a mournful meow', ->
|
||||
cat = new Cat()
|
||||
cat.state = -> Cat.HUNGRY
|
||||
# ^^^ we just want a hungry cat
|
||||
|
||||
expect(cat.meow()).
|
||||
toEqual("meeeyaow")
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Instance Stubs in JavaScript
|
||||
## Just replace the method on the instance
|
||||
|
||||
``` coffeescript
|
||||
class @Cat
|
||||
state: ->
|
||||
# cat codes
|
||||
|
||||
cat = new Cat()
|
||||
cat.state = -> "whatever"
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Stubs just return something when called
|
||||
|
||||
!SLIDE
|
||||
# Mocks expect to be called
|
||||
|
||||
!SLIDE
|
||||
# Test fails if all mocks are not called
|
||||
|
||||
!SLIDE
|
||||
# Jasmine blurs the line a little
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/spy-cat.jpg" />
|
||||
|
||||
!SLIDE
|
||||
# Spies work like mocks, but with additional abilities
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/flying-cat.jpg" />
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
class @Cat
|
||||
vocalProcessor: (speech) =>
|
||||
if this.isAirborne()
|
||||
this.modifyForAirborne(speech)
|
||||
else
|
||||
this.modifyForGround(speech)
|
||||
```
|
||||
|
||||
!SLIDE larger
|
||||
``` coffeescript
|
||||
describe 'Cat#vocalProcessor', ->
|
||||
speech = "speech"
|
||||
|
||||
beforeEach ->
|
||||
@cat = new Cat()
|
||||
|
||||
context 'airborne', ->
|
||||
beforeEach ->
|
||||
spyOn(@cat, 'modifyForAirborne')
|
||||
@cat.isAirborne = -> true
|
||||
|
||||
it 'should be modified for flight', ->
|
||||
@cat.vocalProcessor(speech)
|
||||
expect(@cat.modifyForAirborne).
|
||||
toHaveBeenCalledWith(speech)
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
# `spyOn` replaces a method on an instance with a spy method
|
||||
|
||||
``` coffeescript
|
||||
spyOn(@cat, 'modifyForAirborne')
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Can return a value, run code, run the original code, or just wait to be called
|
||||
|
||||
!SLIDE
|
||||
# Two basic ways to make sure a spy is called
|
||||
|
||||
!SLIDE even-larger
|
||||
## `toHaveBeenCalledWith(args...)`
|
||||
### Called least once with the given parameters
|
||||
|
||||
``` coffeescript
|
||||
expect(@cat.modifyForAirborne).
|
||||
toHaveBeenCalledWith(speech)
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
## `toHaveBeenCalled()`
|
||||
### Just called, no parameter check
|
||||
|
||||
``` coffeescript
|
||||
expect(@cat.modifyForAirborne).
|
||||
toHaveBeenCalled()
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
# Instance Mocks/Spies in JavaScript
|
||||
## Use `spyOn`/`toHaveBeenCalled` matchers
|
||||
|
||||
``` coffeescript
|
||||
class @Cat
|
||||
state: ->
|
||||
# cat codes
|
||||
|
||||
cat = new Cat()
|
||||
spyOn(cat, 'state')
|
||||
expect(cat.state).
|
||||
toHaveBeenCalled()
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# `spyOn` works great with class-level stubs and mocks, too
|
||||
|
||||
!SLIDE larger
|
||||
``` coffeescript
|
||||
class @Cat
|
||||
@generateFurColor: (base) ->
|
||||
# magicks to make a fur color given a base
|
||||
|
||||
regrowFur: (damagedHairs) ->
|
||||
for follicle in damagedHairs
|
||||
follicle.regrow(
|
||||
Cat.generateFurColor(this.baseColor)
|
||||
)
|
||||
```
|
||||
|
||||
!SLIDE larger
|
||||
``` coffeescript
|
||||
Cat.generateFurColor = ->
|
||||
"whoops i nuked this method for every other test"
|
||||
```
|
||||
|
||||
!SLIDE larger
|
||||
``` coffeescript
|
||||
describe 'Cat#regrowFur', ->
|
||||
color = 'color'
|
||||
|
||||
beforeEach ->
|
||||
@cat = new Cat()
|
||||
@follicle = { regrow: -> null }
|
||||
@follicles = [ follicle ]
|
||||
|
||||
spyOn(Cat, 'generateFurColor').
|
||||
# ^^^ original replaced when done
|
||||
andReturn(color)
|
||||
spyOn(@follicle, 'regrow')
|
||||
|
||||
it 'should regrow', ->
|
||||
@cat.regrowFur(@follicles)
|
||||
|
||||
expect(@follicle.regrow).
|
||||
toHaveBeenCalledWith(color)
|
||||
```
|
||||
|
||||
!SLIDE larger
|
||||
# Class Stubs in JavaScript
|
||||
## Use `spyOn` to generate stubs so that the original code is replaced after the test
|
||||
|
||||
``` coffeescript
|
||||
class this.Cat
|
||||
@injectPsychicPowers: (cat) ->
|
||||
# cat codes
|
||||
|
||||
spyOn(Cat, 'injectPsychicPowers').andReturn(psychicCat)
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# John behavior #3
|
||||
## If you have too many mocks/stubs/contexts, your code is too complex
|
||||
|
||||
``` coffeescript
|
||||
describe 'John', ->
|
||||
describe 'spec definitions', ->
|
||||
it 'should obey the Law of Demeter as much as possible', ->
|
||||
it 'should not smell too funny', ->
|
||||
```
|
||||
|
||||
!SLIDE smaller
|
||||
``` coffeescript
|
||||
describe 'Cat#fetch', ->
|
||||
object = null
|
||||
|
||||
context 'a mouse', ->
|
||||
beforeEach ->
|
||||
object = new Mouse()
|
||||
|
||||
context 'fast mouse', ->
|
||||
it 'should wear down the mouse', ->
|
||||
# who
|
||||
|
||||
context 'slow mouse', ->
|
||||
it 'should deliver a present to you', ->
|
||||
# cares
|
||||
|
||||
context 'a ball', ->
|
||||
beforeEach ->
|
||||
object = new Ball()
|
||||
|
||||
context 'ball is bouncing', ->
|
||||
it 'should cause the cat to leap', ->
|
||||
# this
|
||||
|
||||
context 'ball is rolling', ->
|
||||
it 'should cause the cat to slide on the floor', ->
|
||||
# test
|
||||
|
||||
context 'a red dot', ->
|
||||
laser = null
|
||||
|
||||
beforeEach ->
|
||||
laser = new Laser()
|
||||
|
||||
context 'laser out of batteries', ->
|
||||
it 'should not activate', ->
|
||||
# is
|
||||
|
||||
context 'laser functioning', ->
|
||||
it 'should activate, driving the cat insane', ->
|
||||
# huge and unmaintainable and silly
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Sometimes you just need a big blob of unit tests
|
||||
|
||||
!SLIDE larger
|
||||
``` coffeescript
|
||||
# fast and focused!
|
||||
|
||||
describe 'Cat#respondsTo', ->
|
||||
beforeEach ->
|
||||
@cat = new Cat()
|
||||
|
||||
context 'successes', ->
|
||||
it 'should respond', ->
|
||||
for request in [ 'kitty kitty',
|
||||
'pookums',
|
||||
'hisshead' ]
|
||||
expect(@cat.respondsTo(request)).
|
||||
toBeTruthy()
|
||||
```
|
||||
|
||||
!SLIDE larger
|
||||
``` gherkin
|
||||
# slow and synergistic!
|
||||
|
||||
Scenario Outline: Successful responsiveness
|
||||
Given I have a cat
|
||||
When I call it with "<request>"
|
||||
Then the cat should respond
|
||||
|
||||
Examples:
|
||||
| request |
|
||||
| kitty kitty |
|
||||
| pookums |
|
||||
| hisshead |
|
||||
```
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/balancing-cat.jpg" />
|
||||
|
||||
!SLIDE
|
||||
# Find what works best for you and stick with it
|
||||
|
||||
!SLIDE
|
||||
## ...until you get sick of it, of course...
|
||||
|
@ -1,33 +0,0 @@
|
||||
!SLIDE
|
||||
# Using it in your project
|
||||
|
||||
!SLIDE
|
||||
## [github.com/pivotal/jasmine-gem](http://github.com/pivotal/jasmine-gem)
|
||||
|
||||
!SLIDE
|
||||
# Starts a Rack server for running Jasmine against your code
|
||||
|
||||
!SLIDE
|
||||
# Really easy to plug into an existing Rails project
|
||||
|
||||
!SLIDE
|
||||
# Want to make that run fast?
|
||||
|
||||
!SLIDE
|
||||
# Use PhantomJS or `jasmine-headless-webkit`
|
||||
|
||||
!SLIDE
|
||||
# Fast code running in a real browser
|
||||
|
||||
!SLIDE
|
||||
# Evergreen
|
||||
|
||||
!SLIDE
|
||||
# Jasminerice
|
||||
|
||||
!SLIDE
|
||||
# Node.js
|
||||
|
||||
!SLIDE
|
||||
# Pick your favorite!
|
||||
|
@ -1,141 +0,0 @@
|
||||
!SLIDE
|
||||
# Some miscellaneous hints and tips
|
||||
|
||||
!SLIDE
|
||||
# Testing jQuery
|
||||
|
||||
!SLIDE
|
||||
# Mocking and stubbing `$.fn` calls
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
this.containerWaiter = ->
|
||||
$('#container').
|
||||
addClass('wait').
|
||||
append('<div class="waiting" />')
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
$.fn.makeWait = ->
|
||||
$(this).
|
||||
addClass('wait').
|
||||
append('<div class="waiting" />')
|
||||
this
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
this.containerWaiter = ->
|
||||
$('#container').makeWait()
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# `jquery-jasmine`
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'container', ->
|
||||
beforeEach ->
|
||||
setFixtures('<div id="container" />')
|
||||
|
||||
it 'should make it wait', ->
|
||||
containerWaiter()
|
||||
|
||||
expect($('#container')).
|
||||
toHaveClass('wait')
|
||||
expect($('#container')).
|
||||
toContain('div.waiting')
|
||||
```
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/wet-cat.jpg" />
|
||||
|
||||
!SLIDE image-80-percent
|
||||
<img src="assets/spaghetti.jpg" />
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe '$.fn.makeWait', ->
|
||||
it 'should make wait', ->
|
||||
$div = $('<div />')
|
||||
$div.makeWait()
|
||||
|
||||
expect($div).toHaveClass('wait')
|
||||
expect($div).toContain('div.waiting')
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
describe 'container', ->
|
||||
beforeEach ->
|
||||
setFixtures('<div id="container" />')
|
||||
spyOn($.fn, 'makeWait')
|
||||
|
||||
it 'should make it wait', ->
|
||||
containerWaiter()
|
||||
expect($.fn.makeWait).
|
||||
toHaveBeenCalled()
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# No longer testing jQuery, just testing for our code
|
||||
|
||||
!SLIDE
|
||||
# Animations and other time-dependent things
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
class @Cat
|
||||
constructor: ->
|
||||
@mood = "happy"
|
||||
|
||||
pet: ->
|
||||
setTimeout(
|
||||
-> @mood = "angry"
|
||||
, 500
|
||||
)
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Do you really need to test the `setTimeout`?
|
||||
|
||||
!SLIDE even-larger
|
||||
``` coffeescript
|
||||
class Cat
|
||||
constructor: ->
|
||||
@mood = "happy"
|
||||
|
||||
pet: -> setTimeout(@makeAngry, 500)
|
||||
|
||||
makeAngry: => @mood = "angry"
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Use Jasmine's `waitsFor` and `runs`
|
||||
|
||||
!SLIDE larger
|
||||
``` coffeescript
|
||||
describe 'cat moods', ->
|
||||
it 'should change moods', ->
|
||||
cat = new Cat()
|
||||
|
||||
# we want to know the cat's current mood
|
||||
currentMood = cat.mood
|
||||
|
||||
# start petting the cat
|
||||
runs -> cat.pet()
|
||||
|
||||
# wait one second for the cat's mood to change
|
||||
waitsFor(
|
||||
->
|
||||
cat.mood != currentMood
|
||||
, "Cat changed its mood",
|
||||
1000
|
||||
)
|
||||
|
||||
# expect the inevitable
|
||||
runs ->
|
||||
expect(cat.mood).toEqual('angry')
|
||||
```
|
||||
|
@ -1,48 +0,0 @@
|
||||
!SLIDE
|
||||
# So that's pretty much it.
|
||||
|
||||
!SLIDE even-larger
|
||||
# Basic parts of Jasmine unit tests
|
||||
|
||||
``` coffeescript
|
||||
describe
|
||||
it
|
||||
expect
|
||||
toSomething()
|
||||
beforeEach
|
||||
afterEach
|
||||
```
|
||||
|
||||
!SLIDE even-larger
|
||||
# Mocking and stubbing
|
||||
|
||||
``` coffeescript
|
||||
direct method replacement
|
||||
spyOn()
|
||||
toHaveBeenCalled()
|
||||
toHaveBeenCalledWith()
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
# Running Jasmine in your project
|
||||
|
||||
!SLIDE even-larger
|
||||
# Hints and tips for JavaScript testing
|
||||
|
||||
``` coffeescript
|
||||
waitsFor()
|
||||
runs()
|
||||
```
|
||||
|
||||
!SLIDE
|
||||
## [Jasmine documentation](http://pivotal.github.com/jasmine/)
|
||||
|
||||
!SLIDE
|
||||
## [johnbintz.github.com/tea-time](http://johnbintz.github.com/tea-time/)
|
||||
|
||||
!SLIDE
|
||||
# Any questions?
|
||||
|
||||
!SLIDE
|
||||
# Thank you!
|
||||
|
@ -1,2 +0,0 @@
|
||||
%link{:href => 'http://fonts.googleapis.com/css?family=Acme', :rel => 'stylesheet', :type => 'text/css'}/
|
||||
|