teaspoon 0.7.8 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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