teaspoon 0.7.9 → 0.8.0

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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +382 -260
  3. data/app/assets/javascripts/teaspoon-angular.js +108 -26241
  4. data/app/assets/javascripts/teaspoon-jasmine.js +103 -2642
  5. data/app/assets/javascripts/teaspoon-mocha.js +109 -5416
  6. data/app/assets/javascripts/teaspoon-qunit.js +107 -2255
  7. data/app/assets/javascripts/teaspoon-teaspoon.js +0 -1
  8. data/app/assets/javascripts/teaspoon/angular.coffee +3 -1
  9. data/app/assets/javascripts/teaspoon/base/hook.coffee +21 -0
  10. data/app/assets/javascripts/teaspoon/base/reporters/html.coffee +26 -14
  11. data/app/assets/javascripts/teaspoon/base/reporters/html/progress_view.coffee +1 -1
  12. data/app/assets/javascripts/teaspoon/base/reporters/html/template.coffee +3 -3
  13. data/app/assets/javascripts/teaspoon/base/teaspoon.coffee +10 -1
  14. data/app/assets/javascripts/teaspoon/jasmine.coffee +3 -1
  15. data/app/assets/javascripts/teaspoon/mocha.coffee +3 -1
  16. data/app/assets/javascripts/teaspoon/mocha/reporters/html.coffee +1 -1
  17. data/app/assets/javascripts/teaspoon/qunit.coffee +3 -1
  18. data/app/assets/javascripts/teaspoon/qunit/reporters/html.coffee +1 -1
  19. data/app/assets/javascripts/teaspoon/teaspoon.coffee +0 -1
  20. data/app/assets/stylesheets/teaspoon.css +12 -8
  21. data/app/controllers/teaspoon/suite_controller.rb +32 -0
  22. data/app/views/teaspoon/suite/_body.html.erb +0 -0
  23. data/app/views/teaspoon/suite/_boot.html.erb +4 -0
  24. data/app/views/teaspoon/suite/_boot_require_js.html.erb +19 -0
  25. data/app/views/teaspoon/{spec/suites.html.erb → suite/index.html.erb} +6 -7
  26. data/app/views/teaspoon/suite/show.html.erb +19 -0
  27. data/bin/teaspoon +1 -1
  28. data/config/routes.rb +14 -4
  29. data/lib/generators/teaspoon/install/POST_INSTALL +2 -2
  30. data/lib/generators/teaspoon/install/install_generator.rb +22 -11
  31. data/lib/generators/teaspoon/install/templates/_body.html.erb +0 -0
  32. data/lib/generators/teaspoon/install/templates/_boot.html.erb +4 -0
  33. data/lib/generators/teaspoon/install/templates/jasmine/env.rb +11 -0
  34. data/lib/generators/teaspoon/install/templates/jasmine/env_comments.rb +182 -0
  35. data/lib/generators/teaspoon/install/templates/jasmine/spec_helper.coffee +8 -6
  36. data/lib/generators/teaspoon/install/templates/jasmine/spec_helper.js +8 -7
  37. data/lib/generators/teaspoon/install/templates/mocha/env.rb +11 -0
  38. data/lib/generators/teaspoon/install/templates/mocha/env_comments.rb +182 -0
  39. data/lib/generators/teaspoon/install/templates/mocha/spec_helper.coffee +13 -13
  40. data/lib/generators/teaspoon/install/templates/mocha/spec_helper.js +13 -13
  41. data/lib/generators/teaspoon/install/templates/qunit/env.rb +11 -0
  42. data/lib/generators/teaspoon/install/templates/qunit/env_comments.rb +182 -0
  43. data/lib/generators/teaspoon/install/templates/qunit/test_helper.coffee +6 -5
  44. data/lib/generators/teaspoon/install/templates/qunit/test_helper.js +6 -5
  45. data/lib/tasks/teaspoon.rake +9 -2
  46. data/lib/teaspoon.rb +4 -6
  47. data/lib/teaspoon/command_line.rb +116 -134
  48. data/lib/teaspoon/configuration.rb +144 -66
  49. data/lib/teaspoon/console.rb +70 -37
  50. data/lib/teaspoon/coverage.rb +42 -15
  51. data/lib/teaspoon/deprecated.rb +65 -0
  52. data/lib/teaspoon/drivers/base.rb +10 -0
  53. data/lib/teaspoon/drivers/phantomjs/runner.js +9 -11
  54. data/lib/teaspoon/drivers/phantomjs_driver.rb +21 -21
  55. data/lib/teaspoon/drivers/selenium_driver.rb +32 -13
  56. data/lib/teaspoon/engine.rb +32 -12
  57. data/lib/teaspoon/environment.rb +16 -12
  58. data/lib/teaspoon/exceptions.rb +41 -5
  59. data/lib/teaspoon/exporter.rb +52 -0
  60. data/lib/teaspoon/formatters/base.rb +171 -0
  61. data/lib/teaspoon/formatters/clean_formatter.rb +2 -4
  62. data/lib/teaspoon/formatters/documentation_formatter.rb +60 -0
  63. data/lib/teaspoon/formatters/dot_formatter.rb +12 -90
  64. data/lib/teaspoon/formatters/json_formatter.rb +36 -0
  65. data/lib/teaspoon/formatters/junit_formatter.rb +51 -32
  66. data/lib/teaspoon/formatters/modules/report_module.rb +76 -0
  67. data/lib/teaspoon/formatters/pride_formatter.rb +23 -27
  68. data/lib/teaspoon/formatters/snowday_formatter.rb +7 -11
  69. data/lib/teaspoon/formatters/swayze_or_oprah_formatter.rb +88 -64
  70. data/lib/teaspoon/formatters/tap_formatter.rb +18 -27
  71. data/lib/teaspoon/formatters/tap_y_formatter.rb +35 -45
  72. data/lib/teaspoon/formatters/teamcity_formatter.rb +69 -31
  73. data/lib/teaspoon/instrumentation.rb +33 -33
  74. data/lib/teaspoon/result.rb +2 -1
  75. data/lib/teaspoon/runner.rb +40 -28
  76. data/lib/teaspoon/server.rb +23 -25
  77. data/lib/teaspoon/suite.rb +52 -72
  78. data/lib/teaspoon/utility.rb +3 -14
  79. data/lib/teaspoon/version.rb +1 -1
  80. data/spec/dummy/app/assets/javascripts/integration/integration_spec.coffee +3 -0
  81. data/spec/dummy/app/assets/javascripts/integration/spec_helper.coffee +2 -0
  82. data/spec/dummy/config/application.rb +3 -0
  83. data/spec/features/console_reporter_spec.rb +48 -18
  84. data/spec/features/hooks_spec.rb +23 -41
  85. data/spec/features/html_reporter_spec.rb +38 -21
  86. data/spec/features/install_generator_spec.rb +34 -20
  87. data/spec/features/instrumentation_spec.rb +3 -2
  88. data/spec/fixtures/coverage.json +243 -0
  89. data/spec/javascripts/fixtures/_body.html.erb +1 -0
  90. data/spec/javascripts/jasmine_helper.coffee +1 -1
  91. data/spec/javascripts/teaspoon/base/fixture_spec.coffee +4 -4
  92. data/spec/javascripts/teaspoon/base/reporters/html_spec.coffee +9 -10
  93. data/spec/javascripts/teaspoon/mocha/reporters/html_mspec.coffee +0 -6
  94. data/spec/javascripts/teaspoon/phantomjs/runner_spec.coffee +5 -6
  95. data/spec/javascripts/turbolinks_helper.coffee +1 -1
  96. data/spec/spec_helper.rb +3 -4
  97. data/spec/teaspoon/command_line_spec.rb +139 -23
  98. data/spec/teaspoon/configuration_spec.rb +164 -46
  99. data/spec/teaspoon/console_spec.rb +142 -47
  100. data/spec/teaspoon/coverage_spec.rb +98 -28
  101. data/spec/teaspoon/drivers/base_spec.rb +5 -0
  102. data/spec/teaspoon/drivers/phantomjs_driver_spec.rb +32 -14
  103. data/spec/teaspoon/drivers/selenium_driver_spec.rb +32 -24
  104. data/spec/teaspoon/engine_spec.rb +8 -5
  105. data/spec/teaspoon/environment_spec.rb +56 -33
  106. data/spec/teaspoon/exceptions_spec.rb +57 -0
  107. data/spec/teaspoon/exporter_spec.rb +96 -0
  108. data/spec/teaspoon/formatters/base_spec.rb +259 -0
  109. data/spec/teaspoon/formatters/clean_formatter_spec.rb +37 -0
  110. data/spec/teaspoon/formatters/documentation_formatter_spec.rb +127 -0
  111. data/spec/teaspoon/formatters/dot_formatter_spec.rb +52 -56
  112. data/spec/teaspoon/formatters/json_formatter_spec.rb +77 -0
  113. data/spec/teaspoon/formatters/junit_formatter_spec.rb +72 -35
  114. data/spec/teaspoon/formatters/pride_formatter_spec.rb +37 -0
  115. data/spec/teaspoon/formatters/snowday_formatter_spec.rb +35 -0
  116. data/spec/teaspoon/formatters/tap_formatter_spec.rb +29 -81
  117. data/spec/teaspoon/formatters/tap_y_formatter_spec.rb +31 -141
  118. data/spec/teaspoon/formatters/teamcity_formatter_spec.rb +99 -42
  119. data/spec/teaspoon/instrumentation_spec.rb +44 -44
  120. data/spec/teaspoon/result_spec.rb +37 -0
  121. data/spec/teaspoon/runner_spec.rb +70 -59
  122. data/spec/teaspoon/server_spec.rb +34 -52
  123. data/spec/teaspoon/suite_spec.rb +42 -188
  124. data/spec/teaspoon_env.rb +39 -28
  125. data/vendor/assets/javascripts/{angular-scenario-1.0.5.js → angular/1.0.5.js} +0 -0
  126. data/vendor/assets/javascripts/{angular-scenario-1.0.5.MIT-LICENSE → angular/MIT-LICENSE} +0 -0
  127. data/vendor/assets/javascripts/{jasmine-1.3.1.js → jasmine/1.3.1.js} +0 -0
  128. data/vendor/assets/javascripts/jasmine/2.0.0.js +2412 -0
  129. data/vendor/assets/javascripts/{jasmine-1.3.1.MIT.LICENSE → jasmine/MIT.LICENSE} +0 -0
  130. data/vendor/assets/javascripts/{mocha-1.10.0.js → mocha/1.10.0.js} +1 -0
  131. data/vendor/assets/javascripts/mocha/1.17.1.js +5813 -0
  132. data/vendor/assets/javascripts/{mocha-1.10.1.MIT.LICENSE → mocha/MIT.LICENSE} +0 -0
  133. data/vendor/assets/javascripts/{qunit-1.12.0.js → qunit/1.12.0.js} +1 -1
  134. data/vendor/assets/javascripts/qunit/1.14.0.js +2288 -0
  135. data/vendor/assets/javascripts/{qunit-1.12.0.MIT.LICENSE → qunit/MIT.LICENSE} +0 -0
  136. data/vendor/assets/javascripts/support/chai.js +827 -385
  137. data/vendor/assets/javascripts/support/jasmine-jquery-1.7.0.js +720 -0
  138. data/vendor/assets/javascripts/support/jasmine-jquery-2.0.0.js +812 -0
  139. data/vendor/assets/javascripts/support/sinon-chai.js +17 -0
  140. data/vendor/assets/javascripts/support/sinon.js +1138 -643
  141. metadata +57 -36
  142. data/app/controllers/teaspoon/spec_controller.rb +0 -38
  143. data/app/helpers/teaspoon/spec_helper.rb +0 -36
  144. data/app/views/teaspoon/spec/_require_js.html.erb +0 -21
  145. data/app/views/teaspoon/spec/_standard.html.erb +0 -4
  146. data/app/views/teaspoon/spec/runner.html.erb +0 -19
  147. data/lib/generators/teaspoon/install/templates/env.rb +0 -38
  148. data/lib/generators/teaspoon/install/templates/jasmine/initializer.rb +0 -64
  149. data/lib/generators/teaspoon/install/templates/mocha/initializer.rb +0 -64
  150. data/lib/generators/teaspoon/install/templates/qunit/initializer.rb +0 -64
  151. data/lib/teaspoon/check_coverage.rb +0 -33
  152. data/lib/teaspoon/drivers/base_driver.rb +0 -10
  153. data/lib/teaspoon/exception_handling.rb +0 -18
  154. data/lib/teaspoon/formatters/base_formatter.rb +0 -63
  155. data/spec/dummy/config/initializers/teaspoon.rb +0 -41
  156. data/spec/teaspoon/check_coverage_spec.rb +0 -50
  157. data/spec/teaspoon/formatters/base_formatter_spec.rb +0 -45
  158. data/vendor/assets/javascripts/support/chai.MIT.LICENSE +0 -22
  159. data/vendor/assets/javascripts/support/expect.MIT.LICENSE +0 -22
  160. data/vendor/assets/javascripts/support/jasmine-jquery.MIT.LICENSE +0 -20
  161. data/vendor/assets/javascripts/support/jasmine-jquery.js +0 -659
  162. data/vendor/assets/javascripts/support/sinon-chai.MIT-ISH.LICENSE +0 -13
  163. data/vendor/assets/javascripts/support/sinon.BSD.LICENSE +0 -27
@@ -1,77 +1,152 @@
1
1
  require "singleton"
2
2
 
3
3
  module Teaspoon
4
+
5
+ autoload :Formatters, "teaspoon/formatters/base"
6
+ autoload :Drivers, "teaspoon/drivers/base"
7
+
4
8
  class Configuration
5
9
  include Singleton
6
10
 
7
- cattr_accessor :mount_at, :context, :root, :asset_paths, :fixture_path, :suites, :driver_cli_options
8
- @@mount_at = "/teaspoon"
9
- @@context = nil # will default to Rails #relative_url_root
10
- @@root = nil # will default to Rails.root
11
- @@asset_paths = ["spec/javascripts", "spec/javascripts/stylesheets", "test/javascripts", "test/javascripts/stylesheets"]
12
- @@fixture_path = "spec/javascripts/fixtures"
13
- @@suites = {"default" => proc{}}
14
- @@driver_cli_options = nil
11
+ # CONTRIBUTORS:
12
+ # If you add a configuration option you should do the following before it will be considered for merging.
13
+ # - think about if it should be a suite, coverage, or global configuration
14
+ # - write specs for it, and add it to existing specs in spec/teaspoon/configuration_spec.rb
15
+ # - add it to the readme so it's documented
16
+ # - add it to the command_line.rb if appropriate (_only_ if it's appropriate)
17
+ # - add it to ENV_OVERRIDES if it can be overridden from ENV
18
+ # - add it to the initializers in /lib/generators/install/templates so it's documented there as well
19
+
20
+ cattr_accessor :mount_at, :root, :asset_paths, :fixture_paths
21
+ @@mount_at = "/teaspoon"
22
+ @@root = nil # will default to Rails.root
23
+ @@asset_paths = ["spec/javascripts", "spec/javascripts/stylesheets", "test/javascripts", "test/javascripts/stylesheets"]
24
+ @@fixture_paths = ["spec/javascripts/fixtures", "test/javascripts/fixtures"]
15
25
 
16
26
  # console runner specific
17
- cattr_accessor :driver, :server_timeout, :server_port, :fail_fast, :formatters, :suppress_log, :color, :coverage, :coverage_reports, :coverage_output_dir, :server, :statements_coverage_threshold, :functions_coverage_threshold, :branches_coverage_threshold, :lines_coverage_threshold
18
- @@driver = "phantomjs"
19
- @@server = nil
20
- @@server_port = nil
21
- @@server_timeout = 20
22
- @@fail_fast = true
23
- @@formatters = "dot"
24
- @@suppress_log = false
25
- @@color = true
26
- @@coverage = false
27
- @@coverage_reports = nil
28
- @@coverage_output_dir = "coverage"
29
- @@statements_coverage_threshold = nil
30
- @@functions_coverage_threshold = nil
31
- @@branches_coverage_threshold = nil
32
- @@lines_coverage_threshold = nil
27
+
28
+ cattr_accessor :driver, :driver_options, :driver_timeout, :server, :server_port, :server_timeout, :fail_fast,
29
+ :formatters, :color, :suppress_log,
30
+ :use_coverage
31
+ @@driver = "phantomjs"
32
+ @@driver_options = nil
33
+ @@driver_timeout = 180
34
+ @@server = nil
35
+ @@server_port = nil
36
+ @@server_timeout = 20
37
+ @@fail_fast = true
38
+
39
+ @@formatters = ["dot"]
40
+ @@color = true
41
+ @@suppress_log = false
42
+
43
+ @@use_coverage = nil
44
+
45
+ # options that can be specified in the ENV
46
+
47
+ ENV_OVERRIDES = {
48
+ boolean: %w(FAIL_FAST SUPPRESS_LOG COLOR),
49
+ integer: %w(DRIVER_TIMEOUT SERVER_TIMEOUT),
50
+ string: %w(DRIVER DRIVER_OPTIONS SERVER SERVER_PORT FORMATTERS USE_COVERAGE)
51
+ }
52
+
53
+ # suite configurations
54
+
55
+ cattr_accessor :suite_configs
56
+ @@suite_configs = {"default" => {block: proc{}}}
57
+
58
+ def self.suite(name = :default, &block)
59
+ @@suite_configs[name.to_s] = {block: block, instance: Suite.new(&block)}
60
+ end
33
61
 
34
62
  class Suite
35
- attr_accessor :matcher, :helper, :stylesheets, :javascripts, :no_coverage, :boot_partial, :js_config, :hooks
63
+
64
+ FRAMEWORKS = {
65
+ jasmine: ["1.3.1", "2.0.0"],
66
+ mocha: ["1.10.0", "1.17.1"],
67
+ qunit: ["1.12.0", "1.14.0"],
68
+ angular: ["1.0.5"],
69
+ }
70
+
71
+ attr_accessor :matcher, :helper, :javascripts, :stylesheets,
72
+ :boot_partial, :body_partial,
73
+ :no_coverage,
74
+ :hooks
36
75
 
37
76
  def initialize
38
- @matcher = "{spec/javascripts,app/assets}/**/*_spec.{js,js.coffee,coffee}"
39
- @helper = "spec_helper"
40
- @javascripts = ["teaspoon-jasmine"]
41
- @stylesheets = ["teaspoon"]
42
- @no_coverage = [%r{/lib/ruby/gems/}, %r{/vendor/assets/}, %r{/support/}, %r{/(.+)_helper.}]
43
- @boot_partial = nil
44
- @js_config = {}
45
-
46
- @hooks = Hash.new {|h, k| h[k] = [] }
47
-
48
- default = Teaspoon.configuration.suites["default"]
49
- self.instance_eval(&default) if default
77
+ @matcher = "{spec/javascripts,app/assets}/**/*_spec.{js,js.coffee,coffee}"
78
+ @helper = "spec_helper"
79
+ @javascripts = ["jasmine/1.3.1", "teaspoon-jasmine"]
80
+ @stylesheets = ["teaspoon"]
81
+
82
+ @boot_partial = "boot"
83
+ @body_partial = "body"
84
+
85
+ @no_coverage = [%r{/lib/ruby/gems/}, %r{/vendor/assets/}, %r{/support/}, %r{/(.+)_helper.}]
86
+
87
+ @hooks = Hash.new{ |h, k| h[k] = [] }
88
+
89
+ default = Teaspoon.configuration.suite_configs["default"]
90
+ self.instance_eval(&default[:block]) if default
50
91
  yield self if block_given?
51
92
  end
52
93
 
53
- def use_require=(val) # todo: deprecated in version 0.7.4
54
- puts "Deprecation Notice: use_require will be removed, set config.boot_partial to 'require_js' instead."
55
- self.boot_partial = 'require_js' if val
94
+ def use_framework(name, version = nil)
95
+ name = name.to_sym
96
+ version ||= FRAMEWORKS[name].last if FRAMEWORKS[name]
97
+ unless FRAMEWORKS[name] && FRAMEWORKS[name].include?(version)
98
+ message = "Unknown framework \"#{name}\""
99
+ message += " with version #{version} -- available versions #{FRAMEWORKS[name].join(", ")}" if FRAMEWORKS[name] && version
100
+ raise Teaspoon::UnknownFramework, message
101
+ end
102
+
103
+ @javascripts = [[name, version].join("/"), "teaspoon-#{name}"]
104
+ case name.to_sym
105
+ when :qunit
106
+ @matcher = "{test/javascripts,app/assets}/**/*_test.{js,js.coffee,coffee}"
107
+ @helper = "test_helper"
108
+ else
109
+ end
56
110
  end
111
+ alias_method :use_framework=, :use_framework
57
112
 
58
113
  def hook(group = :default, &block)
59
114
  @hooks[group.to_s] << block
60
115
  end
61
116
  end
62
117
 
63
- def self.root=(path)
64
- @@root = Pathname.new(path.to_s) if path.present?
118
+ # coverage configurations
119
+
120
+ cattr_accessor :coverage_configs
121
+ @@coverage_configs = {"default" => {block: proc{}}}
122
+
123
+ def self.coverage(name = :default, &block)
124
+ @@coverage_configs[name.to_s] = {block: block, instance: Coverage.new(&block)}
65
125
  end
66
126
 
67
- def self.suite(name = :default, &block)
68
- @@suites[name.to_s] = block
127
+ class Coverage
128
+ attr_accessor :reports, :output_path,
129
+ :statements, :functions, :branches, :lines
130
+
131
+ def initialize
132
+ @reports = ["text-summary"]
133
+ @output_path = "coverage"
134
+
135
+ @statements = nil
136
+ @functions = nil
137
+ @branches = nil
138
+ @lines = nil
139
+
140
+ default = Teaspoon.configuration.coverage_configs["default"]
141
+ self.instance_eval(&default[:block]) if default
142
+ yield self if block_given?
143
+ end
69
144
  end
70
145
 
71
- def self.coverage_reports
72
- return ["text-summary"] if @@coverage_reports.blank?
73
- return @@coverage_reports if @@coverage_reports.is_a?(Array)
74
- @@coverage_reports.to_s.split(/,\s?/)
146
+ # custom getters / setters
147
+
148
+ def self.root=(path)
149
+ @@root = Pathname.new(path.to_s) if path.present?
75
150
  end
76
151
 
77
152
  def self.formatters
@@ -79,29 +154,32 @@ module Teaspoon
79
154
  return @@formatters if @@formatters.is_a?(Array)
80
155
  @@formatters.to_s.split(/,\s?/)
81
156
  end
82
- end
83
157
 
84
- autoload :Formatters, "teaspoon/formatters/base_formatter"
85
- autoload :Drivers, "teaspoon/drivers/base_driver"
158
+ # override from env or options
86
159
 
87
- mattr_accessor :configuration
88
- @@configuration = Configuration
160
+ def self.override_from_options(options)
161
+ options.each { |k, v| override(k, v) }
162
+ end
89
163
 
90
- def self.setup
91
- yield @@configuration
92
- override_from_env
164
+ def self.override_from_env(env)
165
+ ENV_OVERRIDES[:boolean].each { |o| override(o, env[o] == "true") if env[o].present? }
166
+ ENV_OVERRIDES[:integer].each { |o| override(o, env[o].to_i) if env[o].present? }
167
+ ENV_OVERRIDES[:string].each { |o| override(o, env[o]) if env[o].present? }
168
+ end
169
+
170
+ def self.override(config, value)
171
+ setter = "#{config.to_s.downcase}="
172
+ send(setter, value) if respond_to?(setter)
173
+ end
93
174
  end
94
175
 
95
- private
176
+ mattr_accessor :configured, :configuration
177
+ @@configured = false
178
+ @@configuration = Configuration
96
179
 
97
- def self.override_from_env
98
- %w(FAIL_FAST SUPPRESS_LOG COLOR COVERAGE).each do |directive|
99
- next unless ENV[directive].present?
100
- @@configuration.send("#{directive.downcase}=", ENV[directive] == "true")
101
- end
102
- %w(DRIVER DRIVER_CLI_OPTIONS SERVER SERVER_TIMEOUT SERVER_PORT FORMATTERS COVERAGE_REPORTS COVERAGE_OUTPUT_DIR).each do |directive|
103
- next unless ENV[directive].present?
104
- @@configuration.send("#{directive.downcase}=", ENV[directive])
105
- end
180
+ def self.configure
181
+ yield @@configuration
182
+ @@configured = true
183
+ @@configuration.override_from_env(ENV)
106
184
  end
107
185
  end
@@ -1,49 +1,62 @@
1
- require 'open-uri'
2
- require 'teaspoon/environment'
1
+ require "teaspoon/environment"
3
2
 
4
3
  module Teaspoon
5
4
  class Console
6
5
 
7
- def initialize(options = nil, files = [])
8
- @options = options || {}
6
+ def initialize(options = {})
7
+ @options = options
9
8
  @suites = {}
10
- @files = []
11
-
12
9
  Teaspoon::Environment.load(@options)
13
10
 
14
- start_server
15
- resolve(files)
11
+ @server = start_server
12
+ rescue Teaspoon::ServerException => e
13
+ abort(e.message)
16
14
  end
17
15
 
18
- def execute(options = {}, files = [])
19
- @options = @options.merge(options) if options.present?
20
- resolve(files)
16
+ def failures?
17
+ !execute
18
+ end
21
19
 
22
- failure_count = 0
23
- suites.each do |suite|
24
- STDOUT.print "Teaspoon running #{suite} suite at #{url(suite)}\n" unless Teaspoon.configuration.suppress_log
25
- failure_count += run_specs(suite, @options[:driver_cli_options] || Teaspoon.configuration.driver_cli_options)
26
- end
27
- failure_count > 0
20
+ def execute(options = {})
21
+ execute_without_handling(options)
28
22
  rescue Teaspoon::Failure
29
- true
30
- rescue Teaspoon::RunnerException
31
- true
23
+ false
24
+ rescue Teaspoon::Error => e
25
+ abort(e.message)
32
26
  end
33
27
 
34
- def run_specs(suite, driver_cli_options = nil)
35
- url = url(suite)
36
- url += url.include?("?") ? "&" : "?"
37
- url += "reporter=Console"
38
- driver.run_specs(suite, url, driver_cli_options)
28
+ def execute_without_handling(options = {})
29
+ @options.merge!(options)
30
+ @suites = {}
31
+ resolve(@options[:files])
32
+
33
+ 0 == suites.inject(0) do |failures, suite|
34
+ export(suite) if @options.include?(:export)
35
+ failures += run_specs(suite)
36
+ log("") # empty line for space
37
+ failures
38
+ end
39
+ end
40
+
41
+ def run_specs(suite)
42
+ raise Teaspoon::UnknownSuite, "Unknown suite: \"#{suite}\"" unless Teaspoon.configuration.suite_configs[suite.to_s]
43
+ log("Teaspoon running #{suite} suite at #{base_url_for(suite)}")
44
+ runner = Teaspoon::Runner.new(suite)
45
+ driver.run_specs(runner, url_for(suite))
46
+ raise Teaspoon::Failure if Teaspoon.configuration.fail_fast && runner.failure_count > 0
47
+ runner.failure_count
48
+ end
49
+
50
+ def export(suite)
51
+ raise Teaspoon::UnknownSuite, "Unknown suite: \"#{suite}\"" unless Teaspoon.configuration.suite_configs[suite.to_s]
52
+ log("Teaspoon exporting #{suite} suite at #{base_url_for(suite)}")
53
+ Teaspoon::Exporter.new(suite, url_for(suite, false), @options[:export]).export
39
54
  end
40
55
 
41
56
  protected
42
57
 
43
- def resolve(files)
44
- return if files.length == 0
45
- @suites = {}
46
- @files = files
58
+ def resolve(files = [])
59
+ return if files.blank?
47
60
  files.uniq.each do |path|
48
61
  if result = Teaspoon::Suite.resolve_spec_for(path)
49
62
  suite = @suites[result[:suite]] ||= []
@@ -53,30 +66,50 @@ module Teaspoon
53
66
  end
54
67
 
55
68
  def start_server
56
- @server = Teaspoon::Server.new
57
- @server.start
69
+ log("Starting the Teaspoon server...")
70
+ server = Teaspoon::Server.new
71
+ server.start
72
+ server
58
73
  end
59
74
 
60
75
  def suites
61
76
  return [@options[:suite]] if @options[:suite].present?
62
77
  return @suites.keys if @suites.present?
63
- Teaspoon.configuration.suites.keys
78
+ Teaspoon.configuration.suite_configs.keys
64
79
  end
65
80
 
66
81
  def driver
67
- @driver ||= Teaspoon::Drivers.const_get("#{Teaspoon.configuration.driver.to_s.camelize}Driver").new
82
+ return @driver if @driver
83
+ klass = "#{Teaspoon.configuration.driver.to_s.camelize}Driver"
84
+ @driver = Teaspoon::Drivers.const_get(klass).new(Teaspoon.configuration.driver_options)
85
+ rescue NameError
86
+ raise Teaspoon::UnknownDriver, "Unknown driver: \"#{Teaspoon.configuration.driver}\""
87
+ end
88
+
89
+ def base_url_for(suite)
90
+ ["#{@server.url}#{Teaspoon.configuration.mount_at}", suite].join('/')
91
+ end
92
+
93
+ def url_for(suite, console = true)
94
+ url = [base_url_for(suite), filter(suite)].compact.join('?')
95
+ url += "#{(url.include?("?") ? "&" : "?")}reporter=Console" if console
96
+ url
68
97
  end
69
98
 
70
99
  def filter(suite)
71
100
  parts = []
72
101
  parts << "grep=#{URI::encode(@options[:filter])}" if @options[:filter].present?
73
- (@suites[suite] || @files).flatten.each { |file| parts << "file[]=#{URI::encode(file)}" }
102
+ (@suites[suite] || @options[:files] || []).flatten.each { |file| parts << "file[]=#{URI::encode(file)}" }
74
103
  "#{parts.join('&')}" if parts.present?
75
104
  end
76
105
 
77
- def url(suite)
78
- base_url = ["#{@server.url}#{Teaspoon.configuration.mount_at}", suite].join('/')
79
- [base_url, filter(suite)].compact.join('?')
106
+ def log(str, force = false)
107
+ STDOUT.print("#{str}\n") if force || !Teaspoon.configuration.suppress_log
108
+ end
109
+
110
+ def abort(message = nil)
111
+ log(message, true) if message
112
+ exit(1)
80
113
  end
81
114
  end
82
115
  end
@@ -1,36 +1,63 @@
1
1
  module Teaspoon
2
2
  class Coverage
3
- include Teaspoon::Utility
4
3
 
5
- def initialize(data, suite_name)
6
- @data = data
4
+ def initialize(suite_name, config_name, data)
7
5
  @suite_name = suite_name
6
+ @data = data
7
+ @executable = Teaspoon::Instrumentation.executable
8
+ @config = coverage_configuration(config_name.to_s)
8
9
  end
9
10
 
10
- def reports
11
- Dir.mktmpdir do |path|
12
- input = File.join(path, 'coverage.json')
13
- File.open(input, 'w') { |file| file.write(@data.to_json) }
11
+ def generate_reports(&block)
12
+ input_path do |input|
14
13
  results = []
15
- for format in Teaspoon.configuration.coverage_reports
14
+ for format in @config.reports
16
15
  result = generate_report(input, format)
17
16
  results << result if ["text", "text-summary"].include?(format.to_s)
18
17
  end
19
- Teaspoon::CheckCoverage.new(input).check_coverage
20
- "\n#{results.join("\n\n")}\n"
18
+ block.call(results.join("\n\n")) unless results.blank?
19
+ end
20
+ end
21
+
22
+ def check_thresholds(&block)
23
+ args = threshold_args
24
+ return if args.blank?
25
+ input_path do |input|
26
+ result = %x{#{@executable} check-coverage #{args.join(" ")} #{input.shellescape} 2>&1}
27
+ return if $?.exitstatus == 0
28
+ result = result.scan(/ERROR: .*$/).join("\n").gsub("ERROR: ", "")
29
+ block.call(result) unless result.blank?
21
30
  end
22
31
  end
23
32
 
24
33
  private
25
34
 
35
+ def coverage_configuration(name)
36
+ config = Teaspoon.configuration.coverage_configs[name]
37
+ raise Teaspoon::UnknownCoverage, "Unknown coverage configuration \"#{name}\"" unless config.present?
38
+ config[:instance] ||= Teaspoon::Configuration::Coverage.new(&config[:block])
39
+ end
40
+
41
+ def input_path(&block)
42
+ Dir.mktmpdir do |temp_path|
43
+ input_path = File.join(temp_path, "coverage.json")
44
+ File.open(input_path, "w") { |f| f.write(@data.to_json) }
45
+ block.call(input_path)
46
+ end
47
+ end
48
+
26
49
  def generate_report(input, format)
27
- result = %x{#{executable} report #{format} #{input.shellescape} --dir #{File.join(Teaspoon.configuration.coverage_output_dir, @suite_name)}}
28
- raise "Could not generate coverage report for #{format}" unless $?.exitstatus == 0
29
- result.gsub("Done", "").gsub("Using reporter [#{format}]", "").strip
50
+ output_path = File.join(@config.output_path, @suite_name)
51
+ result = %x{#{@executable} report #{format} #{input.shellescape} --dir #{output_path} 2>&1}
52
+ return result.gsub("Done", "").gsub("Using reporter [#{format}]", "").strip if $?.exitstatus == 0
53
+ raise Teaspoon::DependencyFailure, "Could not generate coverage report for #{format}"
30
54
  end
31
55
 
32
- def executable
33
- @executable ||= istanbul()
56
+ def threshold_args
57
+ %w{statements functions branches lines}.map do |assert|
58
+ threshold = @config.send(:"#{assert}")
59
+ "--#{assert}=#{threshold}" if threshold
60
+ end.compact
34
61
  end
35
62
  end
36
63
  end