start integrating flowerbox tests

This commit is contained in:
John Bintz 2012-03-13 13:20:57 -04:00
parent f0dd789e86
commit 5ac54f8414
22 changed files with 347 additions and 211 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
.tmp/
chromedriver.log
node_modules/
*.gem *.gem
*.rbc *.rbc
.bundle .bundle

View File

@ -2,3 +2,7 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in attentive.gemspec # Specify your gem's dependencies in attentive.gemspec
gemspec gemspec
gem 'flowerbox', :path => '../flowerbox'
gem 'flowerbox-delivery', :path => '../flowerbox-delivery'
gem 'guard'

33
Guardfile Normal file
View File

@ -0,0 +1,33 @@
require 'guard'
require 'guard/guard'
require 'flowerbox'
class ::Guard::FlowerboxUnit < ::Guard::Guard
def run_all
::Flowerbox.run('spec/javascripts')
end
def run_on_change(files)
run_all
end
end
class ::Guard::FlowerboxIntegration < ::Guard::Guard
def run_all
::Flowerbox.run('js-features')
end
def run_on_change(files)
run_all
end
end
guard 'flowerbox-unit' do
watch(%r{^spec/javascripts})
end
guard 'flowerbox-integration' do
watch(%r{^js-features})
end

View File

@ -0,0 +1,13 @@
Feature: Presentation Timer
Background:
Given I have a presentation counter
Scenario: Render
When I render the counter
Then the counter should show the time "00:00"
And the counter should not be running
Scenario: Start the timer
When I start the timer
Then the counter should be running

View File

@ -0,0 +1,13 @@
Flowerbox.configure do |c|
c.test_with :cucumber
c.run_with :chrome
c.asset_paths << "lib/assets/javascripts"
c.spec_patterns << "**/*.js*"
c.spec_patterns << "*.js*"
c.spec_patterns << "**/*.feature"
c.spec_patterns << "*.feature"
c.report_with :verbose
end

View File

@ -0,0 +1,5 @@
#= require attentive/presentation_timer
#
Flowerbox.Given /I have a presentation counter/, ->
@timer = new Attentive.PresentationTimer()
@timer.render()

View File

@ -0,0 +1,7 @@
Flowerbox.Then /^the counter (should|should not) be running$/, (state) ->
switch state
when 'should'
@expect(@timer).toBeRunning()
when 'should not'
@expect(@timer).not.toBeRunning()

View File

@ -0,0 +1,8 @@
Flowerbox.Then /^the counter (should|should not) show the time "([^"]+)"$/, (state, time) ->
content = @timer.ensureEl().innerHTML
switch state
when 'should'
@expect(content).toEqual(time)
when 'should not'
@expect(content).not.toEqual(time)

View File

@ -0,0 +1,3 @@
Flowerbox.Then /^it should count up$/, ->
@pending()

View File

@ -0,0 +1,3 @@
Flowerbox.When /^I render the counter$/, ->
@timer.render()

View File

@ -0,0 +1,3 @@
Flowerbox.When /^I start the timer$/, ->
@timer.start()

View File

@ -0,0 +1,3 @@
Flowerbox.When /^I wait (\d+) seconds?$/, (secs) ->
Flowerbox.pause(Number(secs) * 1000)

View File

@ -0,0 +1,7 @@
Flowerbox.World ->
@addMatchers(
toBeRunning: () ->
@message = "Expected #{@notMessage} be running"
@actual._runner?
)

View File

@ -1,212 +1,5 @@
#= require attentive/presentation
#= require attentive/slide
#= require attentive/presentation_timer
#= require underscore #= require underscore
#
class PresentationTimer
constructor: ->
@time = 0
@el = document.createElement('div')
@el.classList.add('timer')
this.render()
render: ->
@el.innerHTML = this.formattedTime()
start: ->
@_runner = this.runner()
@el.classList.add('running')
runner: ->
setTimeout(
=>
this.render()
@time += 1
this.runner() if @_runner?
, 1000
)
stop: ->
clearTimeout(@_runner)
@el.classList.remove('running')
@_runner = null
reset: ->
this.stop()
@time = 0
this.render()
toggle: ->
if @_runner?
this.stop()
else
this.start()
toggleVisible: ->
@el.classList.toggle('hide')
isVisible: ->
!@el.classList.contains('hide')
hide: ->
@el.classList.add('hide')
formattedTime: ->
minute = "00#{Math.floor(@time / 60)}".slice(-2)
second = "00#{@time % 60}".slice(-2)
"#{minute}:#{second}"
class Slide
@fromList: (list) ->
result = (new Slide(slide) for slide in list)
constructor: (@dom) ->
recalculate: =>
@dom.style['width'] = "#{window.innerWidth}px"
currentMarginTop = Number(@dom.style['marginTop'].replace(/[^\d\.]/g, ''))
height = (window.innerHeight - @dom.querySelector('.content').clientHeight) / 2
if height != currentMarginTop
@dom.style['marginTop'] = "#{height}px"
true
activate: =>
@dom.classList.add('active')
deactivate: =>
@dom.classList.remove('active')
class this.Attentive
@setup: (identifier) ->
starter = ->
setTimeout(
->
(new Attentive(identifier)).start()
, 250
)
window.addEventListener('DOMContentLoaded', starter, false)
constructor: (@identifier) ->
@length = @allSlides().length
@priorSlide = null
@initialRender = true
@timer = new PresentationTimer()
@timer.hide()
@currentWindowHeight = null
document.querySelector('body').appendChild(@timer.el)
bodyClassList: ->
@_bodyClassList ||= document.querySelector('body').classList
allSlides: ->
@_allSlides ||= Slide.fromList(@slidesViewer().querySelectorAll('.slide'))
slidesViewer: ->
@_slidesViewer ||= document.querySelector(@identifier)
start: ->
if !this.isFile()
window.addEventListener('popstate', @handlePopState, false)
document.addEventListener('click', @handleClick, false)
document.addEventListener('keydown', @handleKeyDown, false)
window.addEventListener('resize', _.throttle(@calculate, 500), false)
imageWait = null
imageWait = =>
wait = false
for slide in @allSlides()
for img in slide.dom.getElementsByTagName('img')
wait = true if !img.complete
if wait
setTimeout(imageWait, 100)
else
this.advanceTo(this.slideFromLocation())
imageWait()
slideFromLocation: ->
value = if this.isFile()
location.hash
else
location.pathname
Number(value.substr(1))
handlePopState: (e) =>
this.advanceTo(if e.state then e.state.index else this.slideFromLocation())
handleClick: (e) =>
this.advance() if e.target.tagName != 'A'
handleKeyDown: (e) =>
switch e.keyCode
when 72
this.advanceTo(0)
when 37
this.advance(-1)
when 39, 32
this.advance()
when 220
@timer.reset()
when 84
if e.shiftKey
@timer.toggleVisible()
else
@timer.toggle() if @timer.isVisible()
advance: (offset = 1) =>
this.advanceTo(Math.max(Math.min(@currentSlide + offset, @length - 1), 0))
isFile: => location.href.slice(0, 4) == 'file'
advanceTo: (index) =>
@priorSlide = @currentSlide
@currentSlide = index || 0
this.calculate()
if this.isFile()
location.hash = @currentSlide
else
history.pushState({ index: @currentSlide }, '', @currentSlide)
calculate: =>
if @currentWindowHeight != window.innerHeight
recalculate = true
times = 3
while recalculate and times > 0
recalculate = false
times -= 1
for slide in @allSlides()
recalculate = true if slide.recalculate()
@currentWindowHeight = window.innerHeight
@slidesViewer().style['width'] = "#{window.innerWidth * @allSlides().length}px"
this.align()
getCurrentSlide: =>
@allSlides()[@currentSlide]
align: =>
@allSlides()[@priorSlide].deactivate() if @priorSlide
this.getCurrentSlide().activate()
@slidesViewer().style['left'] = "-#{@currentSlide * window.innerWidth}px"
if @initialRender
@bodyClassList().remove('loading')
@initialRender = false
@currentWindowHeight = null
this.calculate()

View File

@ -0,0 +1,137 @@
if !Attentive? then Attentive = {}
class Attentive.Presentation
@setup: (identifier) ->
starter = ->
setTimeout(
->
(new Attentive.Presentation(identifier)).start()
, 250
)
window.addEventListener('DOMContentLoaded', starter, false)
constructor: (@identifier) ->
@length = @allSlides().length
@priorSlide = null
@initialRender = true
@timer = new Attentive.PresentationTimer()
@timer.hide()
@currentWindowHeight = null
document.querySelector('body').appendChild(@timer.el)
bodyClassList: ->
@_bodyClassList ||= document.querySelector('body').classList
allSlides: ->
@_allSlides ||= Attentive.Slide.fromList(@slidesViewer().querySelectorAll('.slide'))
slidesViewer: ->
@_slidesViewer ||= document.querySelector(@identifier)
start: ->
if !this.isFile()
window.addEventListener('popstate', @handlePopState, false)
@timer.render()
document.addEventListener('click', @handleClick, false)
document.addEventListener('keydown', @handleKeyDown, false)
window.addEventListener('resize', _.throttle(@calculate, 500), false)
imageWait = null
imageWait = =>
wait = false
for slide in @allSlides()
for img in slide.dom.getElementsByTagName('img')
wait = true if !img.complete
if wait
setTimeout(imageWait, 100)
else
this.advanceTo(this.slideFromLocation())
imageWait()
slideFromLocation: ->
value = if this.isFile()
location.hash
else
location.pathname
Number(value.substr(1))
handlePopState: (e) =>
this.advanceTo(if e.state then e.state.index else this.slideFromLocation())
handleClick: (e) =>
this.advance() if e.target.tagName != 'A'
handleKeyDown: (e) =>
switch e.keyCode
when 72
this.advanceTo(0)
when 37
this.advance(-1)
when 39, 32
this.advance()
when 220
@timer.reset()
when 84
if e.shiftKey
@timer.toggleVisible()
else
@timer.toggle() if @timer.isVisible()
advance: (offset = 1) =>
this.advanceTo(Math.max(Math.min(@currentSlide + offset, @length - 1), 0))
isFile: => location.href.slice(0, 4) == 'file'
advanceTo: (index) =>
@priorSlide = @currentSlide
@currentSlide = index || 0
this.calculate()
if this.isFile()
location.hash = @currentSlide
else
history.pushState({ index: @currentSlide }, '', @currentSlide)
calculate: =>
if @currentWindowHeight != window.innerHeight
recalculate = true
times = 3
while recalculate and times > 0
recalculate = false
times -= 1
for slide in @allSlides()
recalculate = true if slide.recalculate()
@currentWindowHeight = window.innerHeight
@slidesViewer().style['width'] = "#{window.innerWidth * @allSlides().length}px"
this.align()
getCurrentSlide: =>
@allSlides()[@currentSlide]
align: =>
@allSlides()[@priorSlide].deactivate() if @priorSlide
this.getCurrentSlide().activate()
@slidesViewer().style['left'] = "-#{@currentSlide * window.innerWidth}px"
if @initialRender
@bodyClassList().remove('loading')
@initialRender = false
@currentWindowHeight = null
this.calculate()

View File

@ -0,0 +1,59 @@
if !Attentive? then Attentive = {}
class Attentive.PresentationTimer
constructor: ->
@time = 0
@el = null
render: ->
@ensureEl().innerHTML = this.formattedTime()
ensureEl: ->
if !@el
@el = document.createElement('div')
@el.classList.add('timer')
@el
start: ->
@_runner = this.runner()
@ensureEl().classList.add('running')
runner: ->
setTimeout(
=>
this.render()
@time += 1
this.runner() if @_runner?
, 1000
)
stop: ->
clearTimeout(@_runner)
@ensureEl().classList.remove('running')
@_runner = null
reset: ->
this.stop()
@time = 0
this.render()
toggle: ->
if @_runner?
this.stop()
else
this.start()
toggleVisible: ->
@ensureEl().classList.toggle('hide')
isVisible: ->
!@ensureEl().classList.contains('hide')
hide: ->
@ensureEl().classList.add('hide')
formattedTime: ->
minute = "00#{Math.floor(@time / 60)}".slice(-2)
second = "00#{@time % 60}".slice(-2)
"#{minute}:#{second}"

View File

@ -0,0 +1,24 @@
if !Attentive? then Attentive = {}
class Attentive.Slide
@fromList: (list) ->
result = (new Attentive.Slide(slide) for slide in list)
constructor: (@dom) ->
recalculate: =>
@dom.style['width'] = "#{window.innerWidth}px"
currentMarginTop = Number(@dom.style['marginTop'].replace(/[^\d\.]/g, ''))
height = (window.innerHeight - @dom.querySelector('.content').clientHeight) / 2
if height != currentMarginTop
@dom.style['marginTop'] = "#{height}px"
true
activate: =>
@dom.classList.add('active')
deactivate: =>
@dom.classList.remove('active')

View File

@ -20,6 +20,8 @@ module Attentive
require 'coffee_script' require 'coffee_script'
require 'sass' require 'sass'
Tilt::CoffeeScriptTemplate.default_bare = true
# make sure pygments is ready before starting a new thread # make sure pygments is ready before starting a new thread
Pygments.highlight("attentive") Pygments.highlight("attentive")

2
skel/.gitignore vendored
View File

@ -0,0 +1,2 @@
.sass-cache/

View File

@ -1,4 +1,4 @@
#= require attentive #= require attentive
# #
Attentive.setup('#slides') Attentive.Presentation.setup('#slides')

View File

@ -0,0 +1,5 @@
#= require attentive/presentation_timer
describe 'Attentive.PresentationTimer', ->
describe '#render', ->
it 'should do something', ->

View File

@ -0,0 +1,9 @@
Flowerbox.configure do |c|
c.test_with :jasmine
c.run_with :node
c.asset_paths << "lib/assets/javascripts"
c.report_with :verbose
end