teaspoon 0.7.8 → 0.7.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -5
  3. data/app/assets/javascripts/teaspoon-angular.js +19 -27
  4. data/app/assets/javascripts/teaspoon-jasmine.js +19 -27
  5. data/app/assets/javascripts/teaspoon-mocha.js +19 -28
  6. data/app/assets/javascripts/teaspoon-qunit.js +41 -15
  7. data/app/assets/javascripts/teaspoon/base/reporters/html/failure_view.coffee +1 -1
  8. data/app/assets/javascripts/teaspoon/base/reporters/html/spec_view.coffee +1 -1
  9. data/app/assets/javascripts/teaspoon/base/reporters/html/suite_view.coffee +1 -1
  10. data/app/assets/javascripts/teaspoon/base/teaspoon.coffee +12 -7
  11. data/app/assets/javascripts/teaspoon/qunit/reporters/html.coffee +1 -1
  12. data/app/assets/stylesheets/teaspoon.css +1 -1
  13. data/app/helpers/teaspoon/spec_helper.rb +2 -1
  14. data/lib/generators/teaspoon/install/templates/jasmine/spec_helper.coffee +4 -0
  15. data/lib/generators/teaspoon/install/templates/jasmine/spec_helper.js +4 -0
  16. data/lib/generators/teaspoon/install/templates/mocha/spec_helper.coffee +6 -2
  17. data/lib/generators/teaspoon/install/templates/mocha/spec_helper.js +4 -0
  18. data/lib/generators/teaspoon/install/templates/qunit/test_helper.coffee +4 -0
  19. data/lib/generators/teaspoon/install/templates/qunit/test_helper.js +4 -0
  20. data/lib/teaspoon/configuration.rb +4 -3
  21. data/lib/teaspoon/coverage.rb +3 -2
  22. data/lib/teaspoon/drivers/phantomjs/runner.js +108 -0
  23. data/lib/teaspoon/drivers/phantomjs_driver.rb +25 -7
  24. data/lib/teaspoon/engine.rb +5 -0
  25. data/lib/teaspoon/environment.rb +2 -4
  26. data/lib/teaspoon/formatters/base_formatter.rb +9 -6
  27. data/lib/teaspoon/formatters/dot_formatter.rb +3 -2
  28. data/lib/teaspoon/formatters/pride_formatter.rb +48 -0
  29. data/lib/teaspoon/formatters/snowday_formatter.rb +20 -0
  30. data/lib/teaspoon/server.rb +9 -3
  31. data/lib/teaspoon/suite.rb +3 -3
  32. data/lib/teaspoon/version.rb +1 -1
  33. data/spec/dummy/config/initializers/teaspoon.rb +1 -1
  34. data/spec/javascripts/teaspoon/base/teaspoon_spec.coffee +4 -4
  35. data/spec/javascripts/teaspoon/other/erb_spec.js.coffee.erb +4 -0
  36. data/spec/javascripts/teaspoon/phantomjs/runner_spec.coffee +1 -1
  37. data/spec/teaspoon/configuration_spec.rb +2 -1
  38. data/spec/teaspoon/coverage_spec.rb +1 -1
  39. data/spec/teaspoon/drivers/phantomjs_driver_spec.rb +28 -14
  40. data/spec/teaspoon/environment_spec.rb +2 -3
  41. data/spec/teaspoon/formatters/base_formatter_spec.rb +3 -2
  42. data/spec/teaspoon/formatters/pride_formatter_spec.rb +0 -0
  43. data/spec/teaspoon/suite_spec.rb +5 -0
  44. data/vendor/assets/javascripts/support/bind-poly.js +23 -0
  45. metadata +10 -17
  46. data/lib/teaspoon/drivers/phantomjs/runner.coffee +0 -68
@@ -6,7 +6,7 @@ class Teaspoon.Reporters.HTML.FailureView extends Teaspoon.Reporters.BaseView
6
6
 
7
7
  build: ->
8
8
  super("spec")
9
- html = """<h1 class="teaspoon-clearfix"><a href="#{@spec.link}">#{@spec.fullDescription}</a></h1>"""
9
+ html = """<h1 class="teaspoon-clearfix"><a href="#{@spec.link}">#{@htmlSafe(@spec.fullDescription)}</a></h1>"""
10
10
  for error in @spec.errors()
11
11
  html += """<div><strong>#{@htmlSafe(error.message)}</strong><br/>#{@htmlSafe(error.stack || "Stack trace unavailable")}</div>"""
12
12
  @el.innerHTML = html
@@ -13,7 +13,7 @@ class Teaspoon.Reporters.HTML.SpecView extends Teaspoon.Reporters.BaseView
13
13
  classes = ["spec"]
14
14
  classes.push("state-pending") if @spec.pending
15
15
  super(classes.join(" "))
16
- @el.innerHTML = """<a href="#{@spec.link}">#{@spec.description}</a>"""
16
+ @el.innerHTML = """<a href="#{@spec.link}">#{@htmlSafe(@spec.description)}</a>"""
17
17
  @parentView = @buildParent()
18
18
  @parentView.append(@el)
19
19
 
@@ -12,7 +12,7 @@ class Teaspoon.Reporters.HTML.SuiteView extends Teaspoon.Reporters.BaseView
12
12
 
13
13
  build: ->
14
14
  super("suite")
15
- @el.innerHTML = """<h1><a href="#{@suite.link}">#{@suite.description}</a></h1>"""
15
+ @el.innerHTML = """<h1><a href="#{@suite.link}">#{@htmlSafe(@suite.description)}</a></h1>"""
16
16
  @parentView = @buildParent()
17
17
  @parentView.append(@el)
18
18
 
@@ -16,13 +16,18 @@ class @Teaspoon
16
16
  @messages: []
17
17
 
18
18
  @execute: ->
19
- if @defer
20
- @defer = false
19
+ if Teaspoon.defer
20
+ Teaspoon.defer = false
21
21
  return
22
- @started = true
22
+ Teaspoon.reload() if Teaspoon.started
23
+ Teaspoon.started = true
23
24
  new Teaspoon.Runner()
24
25
 
25
26
 
27
+ @reload: ->
28
+ window.location.reload()
29
+
30
+
26
31
  @onWindowLoad: (method) ->
27
32
  originalOnload = window.onload
28
33
  window.onload = ->
@@ -32,7 +37,7 @@ class @Teaspoon
32
37
 
33
38
  @resolveDependenciesFromParams: (all = []) ->
34
39
  deps = []
35
- return all if (paths = @location.search.match(/[\?&]file(\[\])?=[^&\?]*/gi)) == null
40
+ return all if (paths = Teaspoon.location.search.match(/[\?&]file(\[\])?=[^&\?]*/gi)) == null
36
41
 
37
42
  for path in paths
38
43
  parts = decodeURIComponent(path.replace(/\+/g, " ")).match(/\/(.+)\.(js|js.coffee|coffee)$/i)
@@ -43,13 +48,13 @@ class @Teaspoon
43
48
 
44
49
 
45
50
  @log: ->
46
- @messages.push(arguments[0])
51
+ Teaspoon.messages.push(arguments[0])
47
52
  try console.log(arguments...)
48
53
  catch e
49
54
  throw new Error("Unable to use console.log for logging")
50
55
 
51
56
 
52
57
  @getMessages: ->
53
- messages = @messages
54
- @messages = []
58
+ messages = Teaspoon.messages
59
+ Teaspoon.messages = []
55
60
  messages
@@ -65,7 +65,7 @@ class Teaspoon.Reporters.HTML.FailureView extends Teaspoon.Reporters.HTML.Failur
65
65
 
66
66
  build: ->
67
67
  super("spec")
68
- html = """<h1 class="teaspoon-clearfix"><a href="#{@spec.link}">#{@spec.fullDescription}</a></h1>"""
68
+ html = """<h1 class="teaspoon-clearfix"><a href="#{@spec.link}">#{@htmlSafe(@spec.fullDescription)}</a></h1>"""
69
69
  for error in @spec.errors()
70
70
  html += """<div><strong>#{error.message}</strong><br/>#{@htmlSafe(error.stack || "Stack trace unavailable")}</div>"""
71
71
  @el.innerHTML = html
@@ -63,7 +63,7 @@ html[xmlns] #teaspoon-interface .teaspoon-clearfix {
63
63
  color: #000;
64
64
  }
65
65
  #teaspoon-title a:hover {
66
- text-decoration: underline;
66
+ border-bottom: 1px solid #000;
67
67
  }
68
68
 
69
69
  /* Progress indicator
@@ -30,6 +30,7 @@ module Teaspoon::SpecHelper
30
30
  def asset_src(dep, instrument = false)
31
31
  params = "?body=1"
32
32
  params << "&instrument=1" if instrument && @suite && @suite.instrument_file?(dep.pathname.to_s)
33
- "#{Rails.application.config.assets.prefix}/#{dep.logical_path}#{params}"
33
+
34
+ "#{Teaspoon.configuration.context}#{Rails.application.config.assets.prefix}/#{dep.logical_path}#{params}"
34
35
  end
35
36
  end
@@ -3,6 +3,10 @@
3
3
  # require support/sinon
4
4
  # require support/your-support-file
5
5
  #
6
+ # PhantomJS (Teaspoons default driver) doesn't have support for Function.prototype.bind, which has caused confusion. Use
7
+ # this polyfill to avoid the confusion.
8
+ #= require support/bind-poly
9
+ #
6
10
  # Deferring execution
7
11
  # If you're using CommonJS, RequireJS or some other asynchronous library you can defer execution. Call Teaspoon.execute()
8
12
  # after everything has been loaded. Simple example of a timeout:
@@ -3,6 +3,10 @@
3
3
  // require support/sinon
4
4
  // require support/your-support-file
5
5
  //
6
+ // PhantomJS (Teaspoons default driver) doesn't have support for Function.prototype.bind, which has caused confusion. Use
7
+ // this polyfill to avoid the confusion.
8
+ //= require support/bind-poly
9
+ //
6
10
  // Deferring execution
7
11
  // If you're using CommonJS, RequireJS or some other asynchronous library you can defer execution. Call Teaspoon.execute()
8
12
  // after everything has been loaded. Simple example of a timeout:
@@ -4,6 +4,10 @@
4
4
  # require support/chai
5
5
  # require support/your-support-file
6
6
  #
7
+ # PhantomJS (Teaspoons default driver) doesn't have support for Function.prototype.bind, which has caused confusion. Use
8
+ # this polyfill to avoid the confusion.
9
+ #= require support/bind-poly
10
+ #
7
11
  # Deferring execution
8
12
  # If you're using CommonJS, RequireJS or some other asynchronous library you can defer execution. Call Teaspoon.execute()
9
13
  # after everything has been loaded. Simple example of a timeout:
@@ -25,8 +29,8 @@
25
29
  # For more information see: http://chaijs.com/guide/styles
26
30
  # Examples:
27
31
  #
28
- # window.assert = chai.assert()
29
- # window.expect = chai.expect()
32
+ # window.assert = chai.assert
33
+ # window.expect = chai.expect
30
34
  # window.should = chai.should()
31
35
  #
32
36
  # For more information: http://github.com/modeset/teaspoon
@@ -4,6 +4,10 @@
4
4
  // require support/chai
5
5
  // require support/your-support-file
6
6
  //
7
+ // PhantomJS (Teaspoons default driver) doesn't have support for Function.prototype.bind, which has caused confusion. Use
8
+ // this polyfill to avoid the confusion.
9
+ //= require support/bind-poly
10
+ //
7
11
  // Deferring execution
8
12
  // If you're using CommonJS, RequireJS or some other asynchronous library you can defer execution. Call Teaspoon.execute()
9
13
  // after everything has been loaded. Simple example of a timeout:
@@ -2,6 +2,10 @@
2
2
  # require support/sinon
3
3
  # require support/your-support-file
4
4
  #
5
+ # PhantomJS (Teaspoons default driver) doesn't have support for Function.prototype.bind, which has caused confusion. Use
6
+ # this polyfill to avoid the confusion.
7
+ #= require support/bind-poly
8
+ #
5
9
  # Deferring execution
6
10
  # If you're using CommonJS, RequireJS or some other asynchronous library you can defer execution. Call Teaspoon.execute()
7
11
  # after everything has been loaded. Simple example of a timeout:
@@ -2,6 +2,10 @@
2
2
  // require support/sinon
3
3
  // require support/your-support-file
4
4
  //
5
+ // PhantomJS (Teaspoons default driver) doesn't have support for Function.prototype.bind, which has caused confusion. Use
6
+ // this polyfill to avoid the confusion.
7
+ //= require support/bind-poly
8
+ //
5
9
  // Deferring execution
6
10
  // If you're using CommonJS, RequireJS or some other asynchronous library you can defer execution. Call Teaspoon.execute()
7
11
  // after everything has been loaded. Simple example of a timeout:
@@ -4,9 +4,10 @@ module Teaspoon
4
4
  class Configuration
5
5
  include Singleton
6
6
 
7
- cattr_accessor :mount_at, :root, :asset_paths, :fixture_path, :suites, :driver_cli_options
7
+ cattr_accessor :mount_at, :context, :root, :asset_paths, :fixture_path, :suites, :driver_cli_options
8
8
  @@mount_at = "/teaspoon"
9
- @@root = nil # will default to Rails.root if left unset
9
+ @@context = nil # will default to Rails #relative_url_root
10
+ @@root = nil # will default to Rails.root
10
11
  @@asset_paths = ["spec/javascripts", "spec/javascripts/stylesheets", "test/javascripts", "test/javascripts/stylesheets"]
11
12
  @@fixture_path = "spec/javascripts/fixtures"
12
13
  @@suites = {"default" => proc{}}
@@ -50,7 +51,7 @@ module Teaspoon
50
51
  end
51
52
 
52
53
  def use_require=(val) # todo: deprecated in version 0.7.4
53
- puts "Deprecation Notice: use_require will be removed, specify 'require_js' for config.boot_partial instead."
54
+ puts "Deprecation Notice: use_require will be removed, set config.boot_partial to 'require_js' instead."
54
55
  self.boot_partial = 'require_js' if val
55
56
  end
56
57
 
@@ -2,8 +2,9 @@ module Teaspoon
2
2
  class Coverage
3
3
  include Teaspoon::Utility
4
4
 
5
- def initialize(data)
5
+ def initialize(data, suite_name)
6
6
  @data = data
7
+ @suite_name = suite_name
7
8
  end
8
9
 
9
10
  def reports
@@ -23,7 +24,7 @@ module Teaspoon
23
24
  private
24
25
 
25
26
  def generate_report(input, format)
26
- result = %x{#{executable} report #{format} #{input.shellescape} --dir #{Teaspoon.configuration.coverage_output_dir}}
27
+ result = %x{#{executable} report #{format} #{input.shellescape} --dir #{File.join(Teaspoon.configuration.coverage_output_dir, @suite_name)}}
27
28
  raise "Could not generate coverage report for #{format}" unless $?.exitstatus == 0
28
29
  result.gsub("Done", "").gsub("Using reporter [#{format}]", "").strip
29
30
  end
@@ -0,0 +1,108 @@
1
+ (function() {
2
+ var system = require("system");
3
+ var webpage = require("webpage");
4
+
5
+ this.Runner = (function() {
6
+
7
+ function Runner() {
8
+ this.url = system.args[1];
9
+ this.timeout = parseInt(system.args[2] || 180) * 1000;
10
+
11
+ var _this = this;
12
+ this.waitForResults = function() { Runner.prototype.waitForResults.apply(_this, arguments) };
13
+ }
14
+
15
+ Runner.prototype.run = function() {
16
+ this.initPage();
17
+ this.loadPage();
18
+ };
19
+
20
+ Runner.prototype.initPage = function() {
21
+ this.page = webpage.create();
22
+ this.page.viewportSize = {
23
+ width: 800,
24
+ height: 800
25
+ };
26
+ };
27
+
28
+ Runner.prototype.loadPage = function() {
29
+ var method, name, _ref, _results;
30
+ this.page.open(this.url);
31
+ _ref = this.pageCallbacks();
32
+ _results = [];
33
+ for (name in _ref) {
34
+ method = _ref[name];
35
+ _results.push(this.page[name] = method);
36
+ }
37
+ return _results;
38
+ };
39
+
40
+ Runner.prototype.waitForResults = function() {
41
+ if ((new Date().getTime() - this.start) >= this.timeout) this.fail("Timed out");
42
+ var finished = this.page.evaluate(function() { return window.Teaspoon && window.Teaspoon.finished });
43
+ finished ? this.finish() : setTimeout(this.waitForResults, 200);
44
+ };
45
+
46
+ Runner.prototype.fail = function(msg, errno) {
47
+ if (msg == null) msg = null;
48
+ if (errno == null) errno = 1;
49
+ if (msg) console.log("Error: " + msg);
50
+
51
+ console.log(JSON.stringify({
52
+ _teaspoon: true,
53
+ type: "exception"
54
+ }));
55
+ phantom.exit(errno);
56
+ };
57
+
58
+ Runner.prototype.finish = function() {
59
+ console.log(" ");
60
+ phantom.exit(0);
61
+ };
62
+
63
+ Runner.prototype.pageCallbacks = function() {
64
+ var _this = this;
65
+ return {
66
+ onError: function(message, trace) {
67
+ console.log(JSON.stringify({
68
+ _teaspoon: true,
69
+ type: "error",
70
+ message: message,
71
+ trace: trace
72
+ }));
73
+ _this.errored = true;
74
+ },
75
+
76
+ onConsoleMessage: function(msg) {
77
+ console.log(msg);
78
+ if (_this.errorTimeout) clearTimeout(_this.errorTimeout);
79
+ if (_this.errored) {
80
+ _this.errorTimeout = setTimeout((function() {
81
+ return _this.fail('Javascript error has cause a timeout.');
82
+ }), 1000);
83
+ return _this.errored = false;
84
+ }
85
+ },
86
+
87
+ onLoadFinished: function(status) {
88
+ if (_this.start) return;
89
+ _this.start = new Date().getTime();
90
+ var defined = _this.page.evaluate(function() {
91
+ return window.Teaspoon;
92
+ });
93
+ if (!(status === "success" && defined)) {
94
+ _this.fail("Failed to load: " + _this.url);
95
+ return;
96
+ }
97
+ _this.waitForResults();
98
+ }
99
+ };
100
+ };
101
+
102
+ return Runner;
103
+
104
+ })();
105
+
106
+ new Runner().run();
107
+
108
+ }).call(this);
@@ -1,18 +1,20 @@
1
- require "phantomjs"
2
1
  require "teaspoon/runner"
3
2
  require 'teaspoon/utility'
4
3
 
4
+ begin
5
+ require "phantomjs"
6
+ rescue LoadError
7
+ end
8
+
5
9
  module Teaspoon
6
10
  module Drivers
7
11
  class PhantomjsDriver < BaseDriver
8
12
  include Teaspoon::Utility
9
13
 
10
- def run_specs(suite, url, driver_cli_options = nil)
14
+ def run_specs(suite, url, cli_options = nil)
11
15
  runner = Teaspoon::Runner.new(suite)
12
16
 
13
- Phantomjs.instance_variable_set(:@path, executable)
14
- # Phantomjs.run takes the command-line args as an array, so if we need to pass in switches/flags, need to split on space
15
- Phantomjs.run(*([driver_cli_options && driver_cli_options.split(" "), script, url].flatten.compact)) do |line|
17
+ run(*cli_arguments(url, cli_options)) do |line|
16
18
  runner.process(line) if line && line.strip != ""
17
19
  end
18
20
 
@@ -21,12 +23,28 @@ module Teaspoon
21
23
 
22
24
  protected
23
25
 
26
+ def run(*args, &block)
27
+ IO.popen([executable, *args]) { |io|
28
+ io.each(&block)
29
+ }
30
+ end
31
+
32
+ def cli_arguments(url, cli_options)
33
+ [cli_options.to_s.split(" "), script, url].flatten.compact
34
+ end
35
+
24
36
  def executable
25
- @executable ||= which('phantomjs')
37
+ executable ||= which('phantomjs')
38
+ executable = Phantomjs.path if executable.blank? && defined?(::Phantomjs)
39
+ if executable.blank?
40
+ STDOUT.print("Could not find PhantomJS. Install phantomjs or try the phantomjs gem.")
41
+ exit(1)
42
+ end
43
+ executable
26
44
  end
27
45
 
28
46
  def script
29
- File.expand_path("../phantomjs/runner.coffee", __FILE__)
47
+ File.expand_path("../phantomjs/runner.js", __FILE__)
30
48
  end
31
49
  end
32
50
  end
@@ -1,3 +1,5 @@
1
+ require File.expand_path(__FILE__, '../../../app/controllers/teaspoon/spec_controller')
2
+
1
3
  module Teaspoon
2
4
  class Engine < ::Rails::Engine
3
5
 
@@ -7,6 +9,9 @@ module Teaspoon
7
9
  # default the root if it's not set
8
10
  Teaspoon.configuration.root ||= app.root
9
11
 
12
+ # set proper root url
13
+ Teaspoon.configuration.context ||= app.config.relative_url_root
14
+
10
15
  # append the asset paths from the configuration
11
16
  Teaspoon.configuration.asset_paths.each do |path|
12
17
  app.config.assets.paths << Teaspoon.configuration.root.join(path).to_s
@@ -4,10 +4,8 @@ module Teaspoon
4
4
  module Environment
5
5
 
6
6
  def self.load(options = {})
7
- unless rails_loaded?
8
- require_environment(options[:environment])
9
- raise "Rails environment not found." unless rails_loaded?
10
- end
7
+ require_environment(options[:environment])
8
+ raise "Rails environment not found." unless rails_loaded?
11
9
 
12
10
  require "teaspoon"
13
11
  require "teaspoon/suite"
@@ -1,13 +1,15 @@
1
1
  module Teaspoon
2
2
  module Formatters
3
3
 
4
- autoload :DotFormatter, 'teaspoon/formatters/dot_formatter'
5
4
  autoload :CleanFormatter, 'teaspoon/formatters/clean_formatter'
6
- autoload :TapYFormatter, 'teaspoon/formatters/tap_y_formatter'
7
- autoload :TapFormatter, 'teaspoon/formatters/tap_formatter'
5
+ autoload :DotFormatter, 'teaspoon/formatters/dot_formatter'
6
+ autoload :JunitFormatter, 'teaspoon/formatters/junit_formatter'
7
+ autoload :PrideFormatter, 'teaspoon/formatters/pride_formatter'
8
+ autoload :SnowdayFormatter, 'teaspoon/formatters/snowday_formatter'
8
9
  autoload :SwayzeOrOprahFormatter, 'teaspoon/formatters/swayze_or_oprah_formatter'
10
+ autoload :TapFormatter, 'teaspoon/formatters/tap_formatter'
11
+ autoload :TapYFormatter, 'teaspoon/formatters/tap_y_formatter'
9
12
  autoload :TeamcityFormatter, 'teaspoon/formatters/teamcity_formatter'
10
- autoload :JunitFormatter, 'teaspoon/formatters/junit_formatter'
11
13
 
12
14
  class BaseFormatter
13
15
 
@@ -52,8 +54,9 @@ module Teaspoon
52
54
  private
53
55
 
54
56
  def log_coverage(data)
55
- return if data.blank? || suppress_logs?
56
- STDOUT.print(Teaspoon::Coverage.new(data).reports)
57
+ return if data.blank?
58
+ report_output = Teaspoon::Coverage.new(data, @suite_name).reports
59
+ STDOUT.print(report_output) unless suppress_logs?
57
60
  end
58
61
  end
59
62
  end