teaspoon 0.7.9 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,65 @@
1
+ module Teaspoon
2
+
3
+ def self.setup(&block)
4
+ Teaspoon.dep("Teaspoon.setup is deprecated, use Teaspoon.configure instead. The /initializer/teaspoon.rb file should be removed, and a new teaspoon_env.rb file should be created by running the install generator.")
5
+ configure(&block)
6
+ end
7
+
8
+ @dep_notified = {}
9
+ def self.dep(message, category = nil)
10
+ return if Teaspoon.configured
11
+ if category
12
+ return if @dep_notified[category]
13
+ @dep_notified[category] = true if category
14
+ end
15
+ puts "WARNING: Deprecated - #{message}"
16
+ end
17
+
18
+ class Configuration
19
+
20
+ def self.context=(*args)
21
+ Teaspoon.dep("the teaspoon context directive is no longer used, remove it from your configuration.")
22
+ end
23
+
24
+ def self.fixture_path=(*args)
25
+ Teaspoon.dep("the teaspoon fixture_path directive has been changed to fixture_paths, which expects an array, please update your configuration.")
26
+ self.fixture_paths = args
27
+ end
28
+
29
+ def self.driver_cli_options=(val)
30
+ Teaspoon.dep("the teaspoon driver_cli_options directive is no longer used, use driver_options instead.")
31
+ self.driver_options = val
32
+ end
33
+
34
+ @coverage_dep_message = <<-MESSAGE
35
+ teaspoon coverage directive has changed and is now more flexible, define coverage using a block instead:
36
+ config.coverage :CI do |coverage|
37
+ coverage.reports = ["html", "text-summary"]
38
+ coverage.output_path = "coverage"
39
+ coverage.statements = 50 # statement threshold required
40
+ coverage.functions = 50 # function threshold required
41
+ coverage.branches = 50 # branch threshold required
42
+ coverage.lines = nil # no line threshold
43
+ end
44
+ > run: teaspoon --coverage=CI --suite=default
45
+ > set: config.use_coverage = "CI"
46
+ MESSAGE
47
+
48
+ for method in %w{coverage coverage_reports coverage_output_dir statements_coverage_threshold functions_coverage_threshold branches_coverage_threshold lines_coverage_threshold}
49
+ define_singleton_method("#{method}=") do |val|
50
+ Teaspoon.dep(@coverage_dep_message, :coverage)
51
+ end
52
+ end
53
+
54
+ class Suite
55
+
56
+ def js_config=(*args)
57
+ Teaspoon.dep("the teaspoon suite js_config directive is no longer used, use the install generator to install the boot partial and customize it instead.", :js_config)
58
+ end
59
+
60
+ def normalize_asset_path=(*args)
61
+ Teaspoon.dep("the teaspoon suite normalize_asset_path directive is no longer used, reopen Teaspoon::Suite and define a normalize_js_extension method instead.", :normalize_asset_path)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,10 @@
1
+ module Teaspoon
2
+ module Drivers
3
+
4
+ autoload :PhantomjsDriver, "teaspoon/drivers/phantomjs_driver"
5
+ autoload :SeleniumDriver, "teaspoon/drivers/selenium_driver"
6
+
7
+ class Base
8
+ end
9
+ end
10
+ end
@@ -26,15 +26,11 @@
26
26
  };
27
27
 
28
28
  Runner.prototype.loadPage = function() {
29
- var method, name, _ref, _results;
30
29
  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);
30
+ var callbacks = this.pageCallbacks();
31
+ for (var name in callbacks) {
32
+ this.page[name] = callbacks[name];
36
33
  }
37
- return _results;
38
34
  };
39
35
 
40
36
  Runner.prototype.waitForResults = function() {
@@ -46,17 +42,16 @@
46
42
  Runner.prototype.fail = function(msg, errno) {
47
43
  if (msg == null) msg = null;
48
44
  if (errno == null) errno = 1;
49
- if (msg) console.log("Error: " + msg);
50
45
 
51
46
  console.log(JSON.stringify({
52
47
  _teaspoon: true,
53
- type: "exception"
48
+ type: "exception",
49
+ message: msg
54
50
  }));
55
51
  phantom.exit(errno);
56
52
  };
57
53
 
58
54
  Runner.prototype.finish = function() {
59
- console.log(" ");
60
55
  phantom.exit(0);
61
56
  };
62
57
 
@@ -71,6 +66,9 @@
71
66
  trace: trace
72
67
  }));
73
68
  _this.errored = true;
69
+ if (/^TeaspoonError: /.test(message || "")) {
70
+ _this.fail("Execution halted.");
71
+ }
74
72
  },
75
73
 
76
74
  onConsoleMessage: function(msg) {
@@ -78,7 +76,7 @@
78
76
  if (_this.errorTimeout) clearTimeout(_this.errorTimeout);
79
77
  if (_this.errored) {
80
78
  _this.errorTimeout = setTimeout((function() {
81
- return _this.fail('Javascript error has cause a timeout.');
79
+ return _this.fail("Javascript error has cause a timeout.");
82
80
  }), 1000);
83
81
  return _this.errored = false;
84
82
  }
@@ -1,46 +1,46 @@
1
- require "teaspoon/runner"
2
- require 'teaspoon/utility'
3
-
4
1
  begin
5
2
  require "phantomjs"
6
3
  rescue LoadError
4
+ # if we can't load phantomjs, assume the cli is installed and in the path
7
5
  end
8
6
 
9
7
  module Teaspoon
10
8
  module Drivers
11
- class PhantomjsDriver < BaseDriver
9
+ class PhantomjsDriver < Base
12
10
  include Teaspoon::Utility
13
11
 
14
- def run_specs(suite, url, cli_options = nil)
15
- runner = Teaspoon::Runner.new(suite)
12
+ def initialize(options = nil)
13
+ options ||= []
14
+ case options
15
+ when Array then @options = options
16
+ when String then @options = options.split(" ")
17
+ when Hash then @options = options.map { |k, v| "--#{k}=#{v}" }
18
+ else raise Teaspoon::UnknownDriverOptions, "Unknown driver options -- supply a string, array or hash"
19
+ end
20
+ end
16
21
 
17
- run(*cli_arguments(url, cli_options)) do |line|
22
+ def run_specs(runner, url)
23
+ run(*driver_options(url)) do |line|
18
24
  runner.process(line) if line && line.strip != ""
19
25
  end
20
-
21
- runner.failure_count
22
26
  end
23
27
 
24
28
  protected
25
29
 
26
30
  def run(*args, &block)
27
- IO.popen([executable, *args]) { |io|
28
- io.each(&block)
29
- }
31
+ IO.popen([executable, *args].join(' ')) { |io| io.each(&block) }
30
32
  end
31
33
 
32
- def cli_arguments(url, cli_options)
33
- [cli_options.to_s.split(" "), script, url].flatten.compact
34
+ def driver_options(url)
35
+ [@options, script, url.shellescape, Teaspoon.configuration.driver_timeout].flatten.compact
34
36
  end
35
37
 
36
38
  def executable
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
39
+ return @executable if @executable
40
+ @executable = which("phantomjs")
41
+ @executable = Phantomjs.path if @executable.blank? && defined?(::Phantomjs)
42
+ return @executable unless @executable.blank?
43
+ raise Teaspoon::MissingDependency, "Could not find PhantomJS. Install phantomjs or try the phantomjs gem."
44
44
  end
45
45
 
46
46
  def script
@@ -1,32 +1,51 @@
1
- require "selenium-webdriver"
2
- require "teaspoon/runner"
1
+ begin
2
+ require "selenium-webdriver"
3
+ rescue LoadError
4
+ STDOUT.print("Could not find Selenium Webdriver. Install the selenium-webdriver gem.\n")
5
+ exit(1)
6
+ end
3
7
 
4
8
  module Teaspoon
5
9
  module Drivers
6
- class SeleniumDriver < BaseDriver
10
+ class SeleniumDriver < Base
11
+
12
+ def initialize(options = nil)
13
+ options ||= {}
14
+ case options
15
+ when Hash then @options = options
16
+ when String then @options = JSON.parse(options)
17
+ else raise Teaspoon::UnknownDriverOptions, "Unknown driver options -- supply a hash or json string"
18
+ end
7
19
 
8
- # note: driver_cli_options which is meant to be used for CLI options to pass into the driver is
9
- # currently ignored. We use the Selenium Ruby binding, so the Selenium command-line options
10
- # aren't used. There are a variety of Selenium options and browser-specific options
11
- # supported by the binding that will take more thought and design to configure cleanly.
12
- def run_specs(suite, url, driver_cli_options = nil)
13
- runner = Teaspoon::Runner.new(suite)
20
+ rescue JSON::ParserError
21
+ raise Teaspoon::UnknownDriverOptions, "Malformed driver options -- supply a hash or json string"
22
+ end
14
23
 
15
- driver = Selenium::WebDriver.for(:firefox)
24
+ def run_specs(runner, url)
25
+ driver = Selenium::WebDriver.for(driver_options[:client_driver])
16
26
  driver.navigate.to(url)
17
27
 
18
- Selenium::WebDriver::Wait.new(timeout: 180, interval: 0.01, message: "Timed out").until do
28
+ Selenium::WebDriver::Wait.new(driver_options).until do
19
29
  done = driver.execute_script("return window.Teaspoon && window.Teaspoon.finished")
20
30
  driver.execute_script("return window.Teaspoon && window.Teaspoon.getMessages() || []").each do |line|
21
31
  runner.process("#{line}\n")
22
32
  end
23
33
  done
24
34
  end
25
-
26
- runner.failure_count
27
35
  ensure
28
36
  driver.quit if driver
29
37
  end
38
+
39
+ protected
40
+
41
+ def driver_options
42
+ @driver_options ||= HashWithIndifferentAccess.new({
43
+ client_driver: :firefox,
44
+ timeout: Teaspoon.configuration.driver_timeout.to_i,
45
+ interval: 0.01,
46
+ message: "Timed out"
47
+ }).merge(@options)
48
+ end
30
49
  end
31
50
  end
32
51
  end
@@ -1,30 +1,50 @@
1
- require File.expand_path(__FILE__, '../../../app/controllers/teaspoon/spec_controller')
1
+ require "teaspoon/environment"
2
+ require "teaspoon/suite"
3
+ require "teaspoon/instrumentation"
2
4
 
3
5
  module Teaspoon
4
6
  class Engine < ::Rails::Engine
5
7
 
6
8
  isolate_namespace Teaspoon
7
9
 
8
- initializer :assets, :group => :all do |app|
9
- # default the root if it's not set
10
- Teaspoon.configuration.root ||= app.root
10
+ initializer :assets, group: :all do |app|
11
+ begin
12
+ Teaspoon::Environment.require_environment
13
+ rescue Teaspoon::EnvironmentNotFound
14
+ # it's ok for this to fail sometimes, like before the initializer is run etc
15
+ end
16
+
17
+ Teaspoon::Engine.default_root_path(app.root) # default the root if it's not set
18
+ Teaspoon::Engine.append_asset_paths(app.config.assets) # append the asset paths from the configuration
19
+ end
20
+
21
+ config.after_initialize do |app|
22
+ Teaspoon::Engine.inject_instrumentation # inject our sprockets hack for instrumenting javascripts
23
+ Teaspoon::Engine.prepend_routes(app) # prepend routes so a catchall doesn't get in the way
24
+ end
11
25
 
12
- # set proper root url
13
- Teaspoon.configuration.context ||= app.config.relative_url_root
26
+ private
14
27
 
15
- # append the asset paths from the configuration
28
+ def self.default_root_path(root)
29
+ Teaspoon.configuration.root ||= root
30
+ end
31
+
32
+ def self.append_asset_paths(assets)
16
33
  Teaspoon.configuration.asset_paths.each do |path|
17
- app.config.assets.paths << Teaspoon.configuration.root.join(path).to_s
34
+ assets.paths << Teaspoon.configuration.root.join(path).to_s
18
35
  end
19
36
  end
20
37
 
21
- config.after_initialize do |app|
22
- # inject our sprockets hack for instrumenting javascripts
38
+ def self.inject_instrumentation
23
39
  Sprockets::Environment.send(:include, Teaspoon::SprocketsInstrumentation)
40
+ end
41
+
42
+ def self.prepend_routes(app)
43
+ return if Teaspoon::Engine.routes.recognize_path('/') rescue nil
44
+ require Teaspoon::Engine.root.join("app/controllers/teaspoon/suite_controller")
24
45
 
25
- # prepend routes so a catchall doesn't get in the way
26
46
  app.routes.prepend do
27
- mount Teaspoon::Engine => Teaspoon.configuration.mount_at
47
+ mount Teaspoon::Engine => Teaspoon.configuration.mount_at, as: "teaspoon"
28
48
  end
29
49
  end
30
50
  end
@@ -8,41 +8,45 @@ module Teaspoon
8
8
  raise "Rails environment not found." unless rails_loaded?
9
9
 
10
10
  require "teaspoon"
11
- require "teaspoon/suite"
12
11
  require "teaspoon/server"
13
- require "teaspoon/exception_handling"
12
+ require "teaspoon/runner"
13
+ require "teaspoon/coverage"
14
+ require "teaspoon/exporter"
14
15
 
15
- configure_from_options(options)
16
+ Teaspoon.configuration.override_from_options(options)
17
+ Teaspoon::ExceptionHandling.add_rails_handling
16
18
  end
17
19
 
18
20
  def self.require_environment(override = nil)
19
- return require_env(File.expand_path(override, Dir.pwd)) if override
21
+ override ||= ENV["TEASPOON_ENV"]
22
+ if override
23
+ override = File.expand_path(override, Dir.pwd)
24
+ ENV["TEASPOON_ENV"] = override
25
+ return require_env(override)
26
+ end
20
27
 
21
28
  standard_environments.each do |filename|
22
29
  file = File.expand_path(filename, Dir.pwd)
23
30
  return require_env(file) if File.exists?(file)
31
+ #file = File.expand_path(filename, File.join(Dir.pwd, '../..'))
32
+ #return require_env(file) if File.exists?(file)
24
33
  end
25
34
 
26
- raise Teaspoon::EnvironmentNotFound
35
+ raise Teaspoon::EnvironmentNotFound, "Unable to load Teaspoon environment in {#{standard_environments.join(', ')}}"
27
36
  end
28
37
 
29
38
  def self.standard_environments
30
39
  ["spec/teaspoon_env.rb", "test/teaspoon_env.rb", "teaspoon_env.rb"]
31
40
  end
32
41
 
33
- def self.configure_from_options(options)
34
- options.each do |key, value|
35
- Teaspoon.configuration.send("#{key.downcase}=", value) if Teaspoon.configuration.respond_to?("#{key.downcase}=")
36
- end
37
- end
42
+ private
38
43
 
39
44
  def self.require_env(file)
40
- require(file)
45
+ ::Kernel.load(file)
41
46
  end
42
47
 
43
48
  def self.rails_loaded?
44
49
  defined?(Rails)
45
50
  end
46
-
47
51
  end
48
52
  end
@@ -1,7 +1,43 @@
1
1
  module Teaspoon
2
- class Failure < Exception; end
3
- class UnknownSuite < Exception; end
4
- class RunnerException < Exception; end
5
- class AssetNotServable < Exception; end
6
- class EnvironmentNotFound < Exception; end
2
+ class Error < Exception; end
3
+ class EnvironmentNotFound < Teaspoon::Error; end
4
+ class MissingDependency < Teaspoon::Error; end
5
+ class ServerException < Teaspoon::Error; end
6
+ class RunnerException < Teaspoon::Error; end
7
+ class ExporterException < Teaspoon::Error; end
8
+ class UnknownFramework < Teaspoon::Error; end
9
+ class UnknownDriver < Teaspoon::Error; end
10
+ class UnknownDriverOptions < Teaspoon::Error; end
11
+ class UnknownFormatter < Teaspoon::Error; end
12
+ class UnknownSuite < Teaspoon::Error; end
13
+ class AssetNotServable < Teaspoon::Error; end
14
+ class Failure < Teaspoon::Error; end
15
+ class DependencyFailure < Teaspoon::Error; end
16
+ class ThresholdNotMet < Teaspoon::Error; end
17
+ class FileNotWritable < Teaspoon::Error; end
18
+
19
+ module ExceptionHandling
20
+
21
+ def self.add_rails_handling
22
+ return unless Teaspoon.configuration.driver == "phantomjs"
23
+
24
+ #Rails.application.config.assets.debug = false # debugging should be off to display errors in the suite_controller
25
+ Rails.application.config.action_dispatch.show_exceptions = true # we want rails to display exceptions
26
+
27
+ # override the render exception method in ActionDispatch to raise a javascript exception
28
+ render_exceptions_with_javascript
29
+ end
30
+
31
+ private
32
+
33
+ def self.render_exceptions_with_javascript
34
+ ActionDispatch::DebugExceptions.class_eval do
35
+ def render_exception(env, exception)
36
+ message = "#{exception.class.name}: #{exception.message}"
37
+ body = "<script>throw Error(#{[message, exception.backtrace].join("\n").inspect})</script>"
38
+ [200, {'Content-Type' => "text/html;", 'Content-Length' => body.bytesize.to_s}, [body]]
39
+ end
40
+ end
41
+ end
42
+ end
7
43
  end
@@ -0,0 +1,52 @@
1
+ module Teaspoon
2
+ class Exporter
3
+ include Teaspoon::Utility
4
+
5
+ def initialize(suite, url, output_path)
6
+ @suite = suite
7
+ @url = url
8
+ @output_path = File.join(File.expand_path(output_path || "export"), @suite.to_s)
9
+ end
10
+
11
+ def export
12
+ Dir.mktmpdir do |temp_path|
13
+ Dir.chdir(temp_path) do
14
+ %x{#{executable} --convert-links --adjust-extension --page-requisites --span-hosts #{@url.shellescape} 2>&1}
15
+ raise Teaspoon::ExporterException, "Unable to export #{@suite} suite." unless $?.exitstatus == 0
16
+ create_export(File.join(temp_path, @url.match(/^http:\/\/([^\/]+).*/)[1]))
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def executable
24
+ return @executable if @executable
25
+ @executable = which("wget")
26
+ return @executable unless @executable.blank?
27
+ raise Teaspoon::MissingDependency, "Could not find wget for exporting."
28
+ end
29
+
30
+ def create_export(path)
31
+ Dir.chdir(path) do
32
+ update_relative_paths
33
+ cleanup_output
34
+ move_output
35
+ end
36
+ end
37
+
38
+ def update_relative_paths
39
+ html = File.read(".#{Teaspoon.configuration.mount_at}/#{@suite}.html")
40
+ File.write("index.html", html.gsub!('"../', '"'))
41
+ end
42
+
43
+ def cleanup_output
44
+ FileUtils.rm_r(Dir["{.#{Teaspoon.configuration.mount_at},robots.txt.html}"])
45
+ end
46
+
47
+ def move_output
48
+ FileUtils.mkdir_p(@output_path)
49
+ FileUtils.mv(Dir["*"], @output_path, force: true)
50
+ end
51
+ end
52
+ end