diff --git a/common/lib/.gitignore b/common/lib/.gitignore
new file mode 100644
index 0000000000..bf6b783416
--- /dev/null
+++ b/common/lib/.gitignore
@@ -0,0 +1 @@
+*/jasmine_test_runner.html
diff --git a/common/lib/xmodule/jasmine_test_runner.html.erb b/common/lib/xmodule/jasmine_test_runner.html.erb
new file mode 100644
index 0000000000..edf4b43c2c
--- /dev/null
+++ b/common/lib/xmodule/jasmine_test_runner.html.erb
@@ -0,0 +1,44 @@
+
+
+
+ Jasmine Test Runner
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <% for src in js_source %>
+
+ <% end %>
+
+
+ <% for src in js_specs %>
+
+ <% end %>
+
+
+
+
+
+
+
+
diff --git a/common/lib/xmodule/xmodule/js/fixtures/problem.html b/common/lib/xmodule/xmodule/js/fixtures/problem.html
index f77ece7845..525b4323b7 100644
--- a/common/lib/xmodule/xmodule/js/fixtures/problem.html
+++ b/common/lib/xmodule/xmodule/js/fixtures/problem.html
@@ -1 +1,7 @@
-
+
\ No newline at end of file
diff --git a/common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee b/common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
index 107930c3b1..120a0fad33 100644
--- a/common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
+++ b/common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
@@ -8,25 +8,43 @@ describe 'Problem', ->
MathJax.Hub.getAllJax.andReturn [@stubbedJax]
window.update_schematics = ->
+ # Load this function from spec/helper.coffee
+ # Note that if your test fails with a message like:
+ # 'External request attempted for blah, which is not defined.'
+ # this msg is coming from the stubRequests function else clause.
+ jasmine.stubRequests()
+
+ # note that the fixturesPath is set in spec/helper.coffee
loadFixtures 'problem.html'
+
spyOn Logger, 'log'
spyOn($.fn, 'load').andCallFake (url, callback) ->
$(@).html readFixtures('problem_content.html')
callback()
- jasmine.stubRequests()
describe 'constructor', ->
- beforeEach ->
- @problem = new Problem 1, "problem_1", "/problem/url/"
- it 'set the element', ->
- expect(@problem.el).toBe '#problem_1'
+ it 'set the element from html', ->
+ @problem999 = new Problem ("
+
+ ")
+ expect(@problem999.element_id).toBe 'problem_999'
+
+ it 'set the element from loadFixtures', ->
+ @problem1 = new Problem($('.xmodule_display'))
+ expect(@problem1.element_id).toBe 'problem_1'
describe 'bind', ->
beforeEach ->
spyOn window, 'update_schematics'
MathJax.Hub.getAllJax.andReturn [@stubbedJax]
- @problem = new Problem 1, "problem_1", "/problem/url/"
+ @problem = new Problem($('.xmodule_display'))
it 'set mathjax typeset', ->
expect(MathJax.Hub.Queue).toHaveBeenCalled()
@@ -38,7 +56,7 @@ describe 'Problem', ->
expect($('section.action input:button')).toHandleWith 'click', @problem.refreshAnswers
it 'bind the check button', ->
- expect($('section.action input.check')).toHandleWith 'click', @problem.check
+ expect($('section.action input.check')).toHandleWith 'click', @problem.check_fd
it 'bind the reset button', ->
expect($('section.action input.reset')).toHandleWith 'click', @problem.reset
@@ -60,7 +78,7 @@ describe 'Problem', ->
describe 'render', ->
beforeEach ->
- @problem = new Problem 1, "problem_1", "/problem/url/"
+ @problem = new Problem($('.xmodule_display'))
@bind = @problem.bind
spyOn @problem, 'bind'
@@ -86,9 +104,13 @@ describe 'Problem', ->
it 're-bind the content', ->
expect(@problem.bind).toHaveBeenCalled()
+ describe 'check_fd', ->
+ xit 'should have specs written for this functionality', ->
+ expect(false)
+
describe 'check', ->
beforeEach ->
- @problem = new Problem 1, "problem_1", "/problem/url/"
+ @problem = new Problem($('.xmodule_display'))
@problem.answers = 'foo=1&bar=2'
it 'log the problem_check event', ->
@@ -98,30 +120,34 @@ describe 'Problem', ->
it 'submit the answer for check', ->
spyOn $, 'postWithPrefix'
@problem.check()
- expect($.postWithPrefix).toHaveBeenCalledWith '/modx/1/problem_check', 'foo=1&bar=2', jasmine.any(Function)
+ expect($.postWithPrefix).toHaveBeenCalledWith '/problem/Problem1/problem_check',
+ 'foo=1&bar=2', jasmine.any(Function)
describe 'when the response is correct', ->
it 'call render with returned content', ->
- spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) -> callback(success: 'correct', contents: 'Correct!')
+ spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) ->
+ callback(success: 'correct', contents: 'Correct!')
@problem.check()
expect(@problem.el.html()).toEqual 'Correct!'
describe 'when the response is incorrect', ->
it 'call render with returned content', ->
- spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) -> callback(success: 'incorrect', contents: 'Correct!')
+ spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) ->
+ callback(success: 'incorrect', contents: 'Incorrect!')
@problem.check()
- expect(@problem.el.html()).toEqual 'Correct!'
+ expect(@problem.el.html()).toEqual 'Incorrect!'
describe 'when the response is undetermined', ->
it 'alert the response', ->
spyOn window, 'alert'
- spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) -> callback(success: 'Number Only!')
+ spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) ->
+ callback(success: 'Number Only!')
@problem.check()
expect(window.alert).toHaveBeenCalledWith 'Number Only!'
describe 'reset', ->
beforeEach ->
- @problem = new Problem 1, "problem_1", "/problem/url/"
+ @problem = new Problem($('.xmodule_display'))
it 'log the problem_reset event', ->
@problem.answers = 'foo=1&bar=2'
@@ -131,7 +157,8 @@ describe 'Problem', ->
it 'POST to the problem reset page', ->
spyOn $, 'postWithPrefix'
@problem.reset()
- expect($.postWithPrefix).toHaveBeenCalledWith '/modx/1/problem_reset', { id: 1 }, jasmine.any(Function)
+ expect($.postWithPrefix).toHaveBeenCalledWith '/problem/Problem1/problem_reset',
+ { id: 'i4x://edX/101/problem/Problem1' }, jasmine.any(Function)
it 'render the returned content', ->
spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) ->
@@ -141,7 +168,7 @@ describe 'Problem', ->
describe 'show', ->
beforeEach ->
- @problem = new Problem 1, "problem_1", "/problem/url/"
+ @problem = new Problem($('.xmodule_display'))
@problem.el.prepend ''
describe 'when the answer has not yet shown', ->
@@ -150,12 +177,14 @@ describe 'Problem', ->
it 'log the problem_show event', ->
@problem.show()
- expect(Logger.log).toHaveBeenCalledWith 'problem_show', problem: 1
+ expect(Logger.log).toHaveBeenCalledWith 'problem_show',
+ problem: 'i4x://edX/101/problem/Problem1'
it 'fetch the answers', ->
spyOn $, 'postWithPrefix'
@problem.show()
- expect($.postWithPrefix).toHaveBeenCalledWith '/modx/1/problem_show', jasmine.any(Function)
+ expect($.postWithPrefix).toHaveBeenCalledWith '/problem/Problem1/problem_show',
+ jasmine.any(Function)
it 'show the answers', ->
spyOn($, 'postWithPrefix').andCallFake (url, callback) ->
@@ -220,7 +249,7 @@ describe 'Problem', ->
describe 'save', ->
beforeEach ->
- @problem = new Problem 1, "problem_1", "/problem/url/"
+ @problem = new Problem($('.xmodule_display'))
@problem.answers = 'foo=1&bar=2'
it 'log the problem_save event', ->
@@ -230,7 +259,8 @@ describe 'Problem', ->
it 'POST to save problem', ->
spyOn $, 'postWithPrefix'
@problem.save()
- expect($.postWithPrefix).toHaveBeenCalledWith '/modx/1/problem_save', 'foo=1&bar=2', jasmine.any(Function)
+ expect($.postWithPrefix).toHaveBeenCalledWith '/problem/Problem1/problem_save',
+ 'foo=1&bar=2', jasmine.any(Function)
it 'alert to the user', ->
spyOn window, 'alert'
@@ -240,7 +270,7 @@ describe 'Problem', ->
describe 'refreshMath', ->
beforeEach ->
- @problem = new Problem 1, "problem_1", "/problem/url/"
+ @problem = new Problem($('.xmodule_display'))
$('#input_example_1').val 'E=mc^2'
@problem.refreshMath target: $('#input_example_1').get(0)
@@ -250,7 +280,7 @@ describe 'Problem', ->
describe 'updateMathML', ->
beforeEach ->
- @problem = new Problem 1, "problem_1", "/problem/url/"
+ @problem = new Problem($('.xmodule_display'))
@stubbedJax.root.toMathML.andReturn ''
describe 'when there is no exception', ->
@@ -270,7 +300,7 @@ describe 'Problem', ->
describe 'refreshAnswers', ->
beforeEach ->
- @problem = new Problem 1, "problem_1", "/problem/url/"
+ @problem = new Problem($('.xmodule_display'))
@problem.el.html '''
@@ -293,3 +323,6 @@ describe 'Problem', ->
it 'serialize all answers', ->
@problem.refreshAnswers()
expect(@problem.answers).toEqual "input_1_1=one&input_1_2=two"
+
+
+
diff --git a/common/lib/xmodule/xmodule/js/spec/helper.coffee b/common/lib/xmodule/xmodule/js/spec/helper.coffee
new file mode 100644
index 0000000000..f34aee21d9
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/spec/helper.coffee
@@ -0,0 +1,76 @@
+jasmine.getFixtures().fixturesPath = 'xmodule/js/fixtures'
+
+jasmine.stubbedMetadata =
+ slowerSpeedYoutubeId:
+ id: 'slowerSpeedYoutubeId'
+ duration: 300
+ normalSpeedYoutubeId:
+ id: 'normalSpeedYoutubeId'
+ duration: 200
+ bogus:
+ duration: 100
+
+jasmine.stubbedCaption =
+ start: [0, 10000, 20000, 30000]
+ text: ['Caption at 0', 'Caption at 10000', 'Caption at 20000', 'Caption at 30000']
+
+jasmine.stubRequests = ->
+ spyOn($, 'ajax').andCallFake (settings) ->
+ if match = settings.url.match /youtube\.com\/.+\/videos\/(.+)\?v=2&alt=jsonc/
+ settings.success data: jasmine.stubbedMetadata[match[1]]
+ else if match = settings.url.match /static\/subs\/(.+)\.srt\.sjson/
+ settings.success jasmine.stubbedCaption
+ else if settings.url.match /.+\/problem_get$/
+ settings.success html: readFixtures('problem_content.html')
+ else if settings.url == '/calculate' ||
+ settings.url.match(/.+\/goto_position$/) ||
+ settings.url.match(/event$/) ||
+ settings.url.match(/.+\/problem_(check|reset|show|save)$/)
+ # do nothing
+ else
+ throw "External request attempted for #{settings.url}, which is not defined."
+
+jasmine.stubYoutubePlayer = ->
+ YT.Player = -> jasmine.createSpyObj 'YT.Player', ['cueVideoById', 'getVideoEmbedCode',
+ 'getCurrentTime', 'getPlayerState', 'getVolume', 'setVolume', 'loadVideoById',
+ 'playVideo', 'pauseVideo', 'seekTo']
+
+jasmine.stubVideoPlayer = (context, enableParts, createPlayer=true) ->
+ enableParts = [enableParts] unless $.isArray(enableParts)
+
+ suite = context.suite
+ currentPartName = suite.description while suite = suite.parentSuite
+ enableParts.push currentPartName
+
+ for part in ['VideoCaption', 'VideoSpeedControl', 'VideoVolumeControl', 'VideoProgressSlider']
+ unless $.inArray(part, enableParts) >= 0
+ spyOn window, part
+
+ loadFixtures 'video.html'
+ jasmine.stubRequests()
+ YT.Player = undefined
+ context.video = new Video 'example', '.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId'
+ jasmine.stubYoutubePlayer()
+ if createPlayer
+ return new VideoPlayer(video: context.video)
+
+spyOn(window, 'onunload')
+
+# Stub Youtube API
+window.YT =
+ PlayerState:
+ UNSTARTED: -1
+ ENDED: 0
+ PLAYING: 1
+ PAUSED: 2
+ BUFFERING: 3
+ CUED: 5
+
+# Stub jQuery.cookie
+$.cookie = jasmine.createSpy('jQuery.cookie').andReturn '1.0'
+
+# Stub jQuery.qtip
+$.fn.qtip = jasmine.createSpy 'jQuery.qtip'
+
+# Stub jQuery.scrollTo
+$.fn.scrollTo = jasmine.createSpy 'jQuery.scrollTo'
diff --git a/common/static/coffee/src/xmodule.coffee b/common/lib/xmodule/xmodule/js/src/xmodule.coffee
similarity index 100%
rename from common/static/coffee/src/xmodule.coffee
rename to common/lib/xmodule/xmodule/js/src/xmodule.coffee
diff --git a/rakefile b/rakefile
index 4f1c15321f..c05f06b9d6 100644
--- a/rakefile
+++ b/rakefile
@@ -3,6 +3,8 @@ require 'tempfile'
require 'net/http'
require 'launchy'
require 'colorize'
+require 'erb'
+require 'tempfile'
# Build Constants
REPO_ROOT = File.dirname(__FILE__)
@@ -79,6 +81,31 @@ def django_for_jasmine(system, django_reload)
end
end
+def template_jasmine_runner(lib)
+ coffee_files = Dir["#{lib}/**/js/**/*.coffee", "common/static/coffee/src/**/*.coffee"]
+ if !coffee_files.empty?
+ sh("coffee -c #{coffee_files.join(' ')}")
+ end
+ phantom_jasmine_path = File.expand_path("common/test/phantom-jasmine")
+ common_js_root = File.expand_path("common/static/js")
+ common_coffee_root = File.expand_path("common/static/coffee/src")
+
+ # Get arrays of spec and source files, ordered by how deep they are nested below the library
+ # (and then alphabetically) and expanded from a relative to an absolute path
+ spec_glob = File.join("#{lib}", "**", "spec", "**", "*.js")
+ src_glob = File.join("#{lib}", "**", "src", "**", "*.js")
+ js_specs = Dir[spec_glob].sort_by {|p| [p.split('/').length, p]} .map {|f| File.expand_path(f)}
+ js_source = Dir[src_glob].sort_by {|p| [p.split('/').length, p]} .map {|f| File.expand_path(f)}
+
+ template = ERB.new(File.read("#{lib}/jasmine_test_runner.html.erb"))
+ template_output = "#{lib}/jasmine_test_runner.html"
+ File.open(template_output, 'w') do |f|
+ f.write(template.result(binding))
+ end
+ yield File.expand_path(template_output)
+end
+
+
def report_dir_path(dir)
return File.join(REPORT_DIR, dir.to_s)
end
@@ -126,22 +153,6 @@ end
end
task :pylint => "pylint_#{system}"
- desc "Open jasmine tests in your default browser"
- task "browse_jasmine_#{system}" do
- django_for_jasmine(system, true) do |jasmine_url|
- Launchy.open(jasmine_url)
- puts "Press ENTER to terminate".red
- $stdin.gets
- end
- end
-
- desc "Use phantomjs to run jasmine tests from the console"
- task "phantomjs_jasmine_#{system}" do
- phantomjs = ENV['PHANTOMJS_PATH'] || 'phantomjs'
- django_for_jasmine(system, false) do |jasmine_url|
- sh("#{phantomjs} common/test/phantom-jasmine/lib/run_jasmine_test.coffee #{jasmine_url}")
- end
- end
end
$failed_tests = 0
@@ -210,6 +221,23 @@ TEST_TASK_DIRS = []
end
end
end
+
+ desc "Open jasmine tests for #{system} in your default browser"
+ task "browse_jasmine_#{system}" do
+ django_for_jasmine(system, true) do |jasmine_url|
+ Launchy.open(jasmine_url)
+ puts "Press ENTER to terminate".red
+ $stdin.gets
+ end
+ end
+
+ desc "Use phantomjs to run jasmine tests for #{system} from the console"
+ task "phantomjs_jasmine_#{system}" do
+ phantomjs = ENV['PHANTOMJS_PATH'] || 'phantomjs'
+ django_for_jasmine(system, false) do |jasmine_url|
+ sh("#{phantomjs} common/test/phantom-jasmine/lib/run_jasmine_test.coffee #{jasmine_url}")
+ end
+ end
end
desc "Reset the relational database used by django. WARNING: this will delete all of your existing users"
@@ -245,6 +273,22 @@ Dir["common/lib/*"].select{|lib| File.directory?(lib)}.each do |lib|
sh("nosetests #{lib}")
end
+ desc "Open jasmine tests for #{lib} in your default browser"
+ task "browse_jasmine_#{lib}" do
+ template_jasmine_runner(lib) do |f|
+ sh("python -m webbrowser -t 'file://#{f}'")
+ puts "Press ENTER to terminate".red
+ $stdin.gets
+ end
+ end
+
+ desc "Use phantomjs to run jasmine tests for #{lib} from the console"
+ task "phantomjs_jasmine_#{lib}" do
+ phantomjs = ENV['PHANTOMJS_PATH'] || 'phantomjs'
+ template_jasmine_runner(lib) do |f|
+ sh("#{phantomjs} common/test/phantom-jasmine/lib/run_jasmine_test.coffee #{f}")
+ end
+ end
end
task :report_dirs