fix js:test for OSX, plugins, and dynamic manifest

no more maintaining spec/javascripts/supports/specs.js to tell the
runner which specs to run. instead, the runner is built from an erb that
determines the list of specs to run from the filesystem. this new way is
also plugin friendly by maintaining a plugin symlink to include plugin
specs in the spec/coffeescripts tree.

second, give the runner page to the phantomjs execution as a file:///
path rather than just a local path. the latter worked on the build
machine, but failed with the phantomjs installed viw macbrew.

third, clean up/out some slightly broken specs.

 * ENVSpec fails when the specs are reordered by the dynamic globbing,
   but is no longer necessary anyways (since ENV is global now)
 * the 'specs/helpers/' path in TemplateSpec doesn't work with the new
   setup, but just 'helpers/' does and is what it should have been using
   before anyways.
 * asyncTest is not reliable. once the specs started running in a
   slightly different order (thanks to the initial point), they started
   failing with asyncTest race conditions. use fakeTimers and
   fakeServers instead when appropriate.
 * found and fixed a minor bug in BackoffPoller while converting/fixing
   its asyncTests

test-plan:
  - run rake js:test; should run completely as before
  - add a plugin with a spec in spec_canvas/coffeescripts then run rake
    js:test again; the plugin's spec should be executed.

Change-Id: I9ce5a038829a9e747df26d878ce86dbb7da8c384
Reviewed-on: https://gerrit.instructure.com/9411
Reviewed-by: Jon Jensen <jon@instructure.com>
Tested-by: Hudson <hudson@instructure.com>
This commit is contained in:
Jacob Fugal 2012-03-13 17:15:35 -06:00
parent fbf9aab150
commit aba524e43f
13 changed files with 147 additions and 125 deletions

6
.gitignore vendored
View File

@ -54,7 +54,10 @@ public/doc/api/*
.yardoc/*
spec/javascripts/*Spec.js
spec/javascripts/*/*Spec.js
spec/javascripts/helpers/*
spec/javascripts/*/*/*Spec.js
spec/javascripts/helpers/
spec/javascripts/plugins/
spec/javascripts/runner.html
public/optimized
!node_modules
chromedriver.log
@ -62,3 +65,4 @@ public/plugins/
app/coffeescripts/plugins/
app/views/jst/plugins/
app/stylesheets/plugins/
spec/coffeescripts/plugins/

View File

@ -33,7 +33,7 @@ define [
initialDelay: false
# we'll abort after about 10 minutes
baseInterval: 2000
maxAttempts: 11
maxAttempts: 12
backoffFactor: 1.6
loader.start()

View File

@ -73,7 +73,7 @@ define [
return @poll() if not @initialDelay
else
@nextInterval = parseInt(@nextInterval * @backoffFactor)
return @stop() if @attempts > @maxAttempts
return @stop() if @attempts >= @maxAttempts
@running = setTimeout @poll, @nextInterval

View File

@ -1,9 +1,10 @@
def maintain_plugin_symlinks(relative_path)
Dir.glob("vendor/plugins/*/#{relative_path}").each do |plugin_dir|
Dir.mkdir("#{relative_path}/plugins") unless File.exists?("#{relative_path}/plugins")
plugin = plugin_dir.gsub(%r{^vendor/plugins/(.*)/#{relative_path}$}, '\1')
source = "#{relative_path}/plugins/#{plugin}"
target = "#{relative_path.gsub(%r{[^/]+}, '..')}/../#{plugin_dir}"
def maintain_plugin_symlinks(local_path, plugin_path=nil)
plugin_path ||= local_path
Dir.glob("vendor/plugins/*/#{plugin_path}").each do |plugin_dir|
Dir.mkdir("#{local_path}/plugins") unless File.exists?("#{local_path}/plugins")
plugin = plugin_dir.gsub(%r{^vendor/plugins/(.*)/#{plugin_path}$}, '\1')
source = "#{local_path}/plugins/#{plugin}"
target = "#{local_path.gsub(%r{[^/]+}, '..')}/../#{plugin_dir}"
unless File.symlink?(source) && File.readlink(source) == target
File.unlink(source) if File.exists?(source)
File.symlink(target, source)
@ -15,3 +16,4 @@ maintain_plugin_symlinks('public')
maintain_plugin_symlinks('app/coffeescripts')
maintain_plugin_symlinks('app/views/jst')
maintain_plugin_symlinks('app/stylesheets')
maintain_plugin_symlinks('spec/coffeescripts', 'spec_canvas/coffeescripts')

View File

@ -8,7 +8,8 @@ namespace :js do
Rake::Task['js:generate'].invoke
end
puts "--> executing phantomjs tests"
phantomjs_output = `phantomjs spec/javascripts/support/qunit/test.js spec/javascripts/runner.html`
`erb spec/javascripts/runner.html.erb > spec/javascripts/runner.html`
phantomjs_output = `phantomjs spec/javascripts/support/qunit/test.js file:///#{Dir.pwd}/spec/javascripts/runner.html`
exit_status = $?.exitstatus
puts phantomjs_output
raise "PhantomJS tests failed" if exit_status != 0
@ -35,8 +36,13 @@ namespace :js do
# files that don't map to any source file anymore
FileUtils.rm_rf('public/javascripts/compiled')
FileUtils.rm_rf('public/javascripts/jst')
FileUtils.rm_rf('public/plugins/*/javascripts/compiled')
FileUtils.rm_rf('public/plugins/*/javascripts/jst')
Dir.glob('spec/javascripts/**/*Spec.js') do |compiled_spec|
FileUtils.rm_f(compiled_spec)
end
Dir.glob('public/plugins/*/javascripts') do |plugin_dir|
FileUtils.rm_rf(plugin_dir + '/compiled')
FileUtils.rm_rf(plugin_dir + '/javascripts/jst')
end
puts "--> Pre-compiling all handlebars templates"
Rake::Task['jst:compile'].invoke

View File

@ -20,17 +20,22 @@ define [
appendTarget: @fixture.find('#customList')
@list.open()
@lis = @fixture.find('.customListItem')
@clock = sinon.useFakeTimers()
teardown: ->
@clock.restore()
@fixture.detach()
test 'should open and close', ->
@list.close()
@clock.tick 1
equal @list.wrapper.is(':visible'), false, 'starts hidden'
@list.open()
@clock.tick 1
equal @list.wrapper.is(':visible'), true, 'displays on open'
asyncTest 'should remove and add the first item', 2, ->
test 'should remove and add the first item', ->
# store original length to compare to later
originalLength = @list.targetList.children().length
@ -40,44 +45,45 @@ define [
# this next click should get ignored because the previous element is animating
simulateClick( @lis[1] )
setTimeout =>
expectedLength = originalLength - 1
equal @list.pinned.length, expectedLength, 'only one item should have been removed'
simulateClick( @lis[0] )
equal @list.pinned.length, originalLength, 'item should be restored'
start()
, 300
@clock.tick 300
expectedLength = originalLength - 1
equal @list.pinned.length, expectedLength, 'only one item should have been removed'
simulateClick( @lis[0] )
@clock.tick 300
equal @list.pinned.length, originalLength, 'item should be restored'
test 'should cancel pending add request on remove', ->
# Add one that doesn't exist
el = jQuery @lis[16]
@list.add(16, el)
@clock.tick 300
ok @list.requests.add[16], 'create an "add" request'
# then immediately remove it before the request has time to come back
item = @list.pinned.findBy 'id', 16
@list.remove item, el
@clock.tick 300
equal @list.requests.add[16], undefined, 'delete "add" request'
test 'should cancel pending remove request on add', ->
el = jQuery @lis[1]
item = @list.pinned.findBy('id', 1)
@list.remove(item, el)
@clock.tick 300
ok @list.requests.remove[1], 'create a "remove" request'
@list.add 1, el
@clock.tick 300
equal @list.requests.remove[1], undefined, 'delete "remove" request'
asyncTest 'should reset', 2, ->
test 'should reset', ->
originalLength = @list.targetList.children().length
simulateClick @lis[0]
@clock.tick 300
ok originalLength isnt @list.targetList.children().length, 'length should be different'
setTimeout =>
ok originalLength isnt @list.targetList.children().length, 'length should be different'
@list.reset()
length = @list.targetList.children().length
equal length, originalLength, 'targetList items restored'
start()
, 300
@list.reset()
length = @list.targetList.children().length
equal length, originalLength, 'targetList items restored'

View File

@ -1,16 +0,0 @@
define ['require'], (require) ->
module 'ENV'
asyncTest 'simple', ->
require ['ENV'], (env1) ->
env1.thing1 = 3
require ['ENV'], (env2) ->
env2.thing2 = 4
equal env1.thing1, 3
equal env1.thing2, 4
equal env2.thing1, 3
equal env2.thing2, 4
strictEqual env1, env2
start()

View File

@ -1,5 +1,5 @@
define [
'specs/helpers/testTemplate'
'helpers/testTemplate'
'compiled/Template'
], (_,Template) ->

View File

@ -12,7 +12,15 @@ require [
@$holder = $('<table />').appendTo(document.body)
@timeBlockList = new TimeBlockList(@$holder)
# fakeTimer'd because the tests with failed validations add an error box
# that is faded in. if we don't tick past the fade-in, other unrelated
# tests that use fake timers fail.
@clock = sinon.useFakeTimers((new Date()).valueOf())
teardown: ->
# tick past any remaining errorBox fade-ins
@clock.tick 250
@clock.restore()
@$holder.detach()
test "should init properly", ->

View File

@ -2,8 +2,6 @@ require [
'compiled/helpDialog'
'vendor/jquery.ba-tinypubsub'
'helpers/fakeENV'
'helpers/ajax_mocks/help_links'
'helpers/ajax_mocks/api/v1/courses'
], (helpDialog)->
@ -14,23 +12,39 @@ require [
ie: true
version: 8
module 'HelpDialog Static methods'
module 'HelpDialog',
test 'init', 1, ->
setup: ->
@clock = sinon.useFakeTimers()
@server = sinon.fakeServer.create()
@server.respondWith '/help_links', '[]'
@server.respondWith '/api/v1/courses.json', '[]'
teardown: ->
@server.restore()
# if we don't close it after each test, subsequent tests get messed up.
# additionally, closing it starts an animation, so tick past that.
if helpDialog.$dialog?
helpDialog.$dialog.dialog('close') #cleanup
@clock.tick 200
@clock.restore()
# reset the shared object
helpDialog.dialogInited = false
helpDialog.teacherFeedbackInited = false
test 'init', ->
$tester = $('<a class="help_dialog_trigger" />').appendTo('body')
helpDialog.initTriggers()
$tester.click()
ok $('.ui-dialog-content').is(':visible'), "help dialog appears when you click 'help' link"
module 'HelpDialog'
asyncTest 'teacher feedback', 1, ->
$(helpDialog).bind 'ready', ->
helpDialog.switchTo "#teacher_feedback"
setTimeout ->
ok helpDialog.$dialog.find('#teacher-feedback-body').is(':visible'), "textarea shows up"
helpDialog.$dialog.dialog('close') #cleanup
start()
, 101
test 'teacher feedback', ->
helpDialog.open()
@server.respond()
helpDialog.switchTo "#teacher_feedback"
@clock.tick 200
ok helpDialog.$dialog.find('#teacher-feedback-body').is(':visible'), "textarea shows up"

View File

@ -5,70 +5,91 @@ define ['compiled/util/BackoffPoller'], (BackoffPoller)->
@ran_callback = false
@callback = =>
@ran_callback = true
@clock = sinon.useFakeTimers()
@server = sinon.fakeServer.create()
@server.respondWith 'fixtures/ok.json', '{"status":"ok"}'
asyncTest 'should keep polling when it gets a "continue"', ->
teardown: ->
@clock.restore()
@server.restore()
test 'should keep polling when it gets a "continue"', ->
poller = new BackoffPoller 'fixtures/ok.json', ->
'continue'
, backoffFactor: 1, baseInterval: 10, maxAttempts: 100
poller.start().then(@callback)
setTimeout =>
ok poller.running, "poller should be running"
poller.stop(false)
start()
, 100
# let the first interval expire, and then respond to the request
@clock.tick 10
@server.respond()
asyncTest 'should reset polling when it gets a "reset"', ->
ok poller.running, "poller should be running"
poller.stop(false)
test 'should reset polling when it gets a "reset"', ->
poller = new BackoffPoller 'fixtures/ok.json', ->
'reset'
, backoffFactor: 1, baseInterval: 10, maxAttempts: 100
poller.start().then(@callback)
setTimeout =>
ok poller.running, "poller should be running"
ok poller.attempts <= 1, "counter should be reset" # either zero or one, depending on whether we're waiting for a timeout or an ajax call
poller.stop(false)
start()
, 100
# let the first interval expire, and then respond to the request
@clock.tick 10
@server.respond()
asyncTest 'should stop polling when it gets a "stop"', ->
ok poller.running, "poller should be running"
ok poller.attempts <= 1, "counter should be reset" # either zero or one, depending on whether we're waiting for a timeout or an ajax call
poller.stop(false)
test 'should stop polling when it gets a "stop"', ->
count = 0
poller = new BackoffPoller 'fixtures/ok.json', ->
if count++ > 3 then 'stop' else 'continue'
, backoffFactor: 1, baseInterval: 10
poller.start().then(@callback)
setTimeout =>
ok not poller.running, "poller should be stopped"
ok @ran_callback, "poller should have run callbacks"
start()
, 100
# let the four 'continue' intervals expire, responding after each
for i in [0...4]
@clock.tick 10
@server.respond()
asyncTest 'should abort polling when it hits maxAttempts', ->
ok poller.running, "poller should be running"
# let the final 'stop' interval expire, and then respond to the request
@clock.tick 10
@server.respond()
ok not poller.running, "poller should be stopped"
ok @ran_callback, "poller should have run callbacks"
test 'should abort polling when it hits maxAttempts', ->
poller = new BackoffPoller 'fixtures/ok.json', ->
'continue'
, backoffFactor: 1, baseInterval: 10, maxAttempts: 3
poller.start().then(@callback)
setTimeout =>
ok not poller.running, "poller should be stopped"
ok not @ran_callback, "poller should not have run callbacks"
start()
, 100
# let the first two intervals expire, responding after each
for i in [0...2]
@clock.tick 10
@server.respond()
asyncTest 'should abort polling when it gets anything else', ->
count = 0
ok poller.running, "poller should be running"
# let the final interval expire, and then respond to the request
@clock.tick 10
@server.respond()
ok not poller.running, "poller should be stopped"
ok not @ran_callback, "poller should not have run callbacks"
test 'should abort polling when it gets anything else', ->
poller = new BackoffPoller 'fixtures/ok.json', ->
'omgwtfbbq'
, baseInterval: 10
poller.start().then(@callback)
setTimeout =>
ok not poller.running, "poller should be stopped"
ok not @ran_callback, "poller should not have run callbacks"
start()
, 100
# let the interval expire, and then respond to the request
@clock.tick 10
@server.respond()
ok not poller.running, "poller should be stopped"
ok not @ran_callback, "poller should not have run callbacks"

View File

@ -61,12 +61,6 @@
name: 'jqueryui',
location: 'vendor/jqueryui'
},
{
name: 'specs',
location: '../../spec/javascripts',
main: 'support/specs',
lib: './'
},
{
name: 'helpers',
location: '../../spec/javascripts/helpers',
@ -78,7 +72,11 @@
location: '../../spec/javascripts/support',
main: 'support',
lib: './'
}
},
{
name: 'spec',
location: '../../spec'
},
]
};
</script>
@ -86,7 +84,7 @@
<script>
// don't run specs until they are all loaded
stop();
require(['specs'], function() {
require(<%= Dir["spec/javascripts/**/*Spec.js"].map{ |file| file.sub(/\.js$/, '') }.inspect %>, function() {
// run specs once they've all loaded
start();
});

View File

@ -1,21 +0,0 @@
require([
'specs/TemplateSpec',
'specs/CustomListSpec',
'specs/invokerSpec',
'specs/class/cacheSpec',
'specs/xhr/remoteSelectSpec',
'specs/jQuery.instructureMiscPluginsSpec',
'specs/userNamePartsSpec',
'specs/objectCollectionSpec',
'specs/helpDialogSpec',
'specs/paginatedListSpec',
'specs/util/BackoffPollerSpec',
'specs/jQuery.instructureJqueryPatchesSpec',
'specs/ENVSpec',
'specs/calendar/TimeBlockListManagerSpec',
'specs/calendar/TimeBlockListSpec',
'specs/calendar/TimeBlockRowSpec',
'specs/util/processMigrationItemSelectionsSpec',
'specs/util/processItemSelectionsSpec'
]);