guard-jasmine 1.3.0 → 1.19.2

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.
@@ -5,11 +5,12 @@ require 'guard/jasmine/runner'
5
5
  require 'guard/jasmine/formatter'
6
6
  require 'guard/jasmine/server'
7
7
  require 'guard/jasmine/util'
8
+ require 'guard/jasmine/server'
8
9
 
9
10
  module Guard
10
11
  class Jasmine
11
12
 
12
- # Small helper class to run the Jasmine runner once from the
13
+ # Small helper class to run the Jasmine runner_options once from the
13
14
  # command line. This can be useful to integrate guard-jasmine
14
15
  # into a continuous integration server.
15
16
  #
@@ -20,60 +21,151 @@ module Guard
20
21
 
21
22
  default_task :spec
22
23
 
23
- desc 'spec', 'Run the Jasmine spec runner'
24
+ desc 'spec', 'Run the Jasmine spec runner_options'
24
25
 
25
26
  method_option :server,
26
- :type => :string,
27
- :aliases => '-s',
28
- :default => 'auto',
29
- :desc => 'Server to start, either `auto`, `webrick`, `mongrel`, `thin`, `jasmine_gem` or `none`'
27
+ type: :string,
28
+ aliases: '-s',
29
+ default: 'auto',
30
+ desc: 'Server to start, either `auto`, `webrick`, `mongrel`, `thin`, `unicorn`, `jasmine_gem` or `none`'
30
31
 
31
32
  method_option :port,
32
- :type => :numeric,
33
- :aliases => '-p',
34
- :default => 3001,
35
- :desc => 'Server port to use'
33
+ type: :numeric,
34
+ aliases: '-p',
35
+ desc: 'Server port to use'
36
36
 
37
- method_option :url,
38
- :type => :string,
39
- :aliases => '-u',
40
- :default => 'http://localhost:3001/jasmine',
41
- :desc => 'The url of the Jasmine test runner'
37
+ method_option :server_env,
38
+ type: :string,
39
+ aliases: '-e',
40
+ default: ENV['RAILS_ENV'] || 'test',
41
+ desc: 'The server environment to use, for example `development`, `test` etc.'
42
+
43
+ method_option :server_timeout,
44
+ type: :numeric,
45
+ default: 60,
46
+ desc: 'The number of seconds to wait for the Jasmine spec server'
47
+
48
+ method_option :verbose,
49
+ type: :boolean,
50
+ default: false,
51
+ desc: 'Show the server output in the console'
52
+
53
+ method_option :rackup_config,
54
+ type: :string,
55
+ aliases: '-c',
56
+ desc: 'The rackup config to use (jasminerice only)'
42
57
 
43
58
  method_option :bin,
44
- :type => :string,
45
- :aliases => '-b',
46
- :desc => 'The location of the PhantomJS binary'
59
+ type: :string,
60
+ aliases: '-b',
61
+ desc: 'The location of the PhantomJS binary'
62
+
63
+ method_option :spec_dir,
64
+ type: :string,
65
+ aliases: '-d',
66
+ desc: 'The directory with the Jasmine specs'
67
+
68
+ method_option :line_number,
69
+ type: :numeric,
70
+ aliases: '-l',
71
+ desc: 'The line which identifies the spec to be run'
72
+
73
+ method_option :url,
74
+ type: :string,
75
+ aliases: '-u',
76
+ desc: 'The url of the Jasmine test runner'
77
+
78
+ method_option :mount,
79
+ type: :string,
80
+ aliases: '-m',
81
+ desc: 'The mount point of the Jasmine test runner'
47
82
 
48
83
  method_option :timeout,
49
- :type => :numeric,
50
- :aliases => '-t',
51
- :default => 10000,
52
- :desc => 'The maximum time in milliseconds to wait for the spec runner to finish'
84
+ type: :numeric,
85
+ aliases: '-t',
86
+ default: 60,
87
+ desc: 'The maximum time in seconds to wait for the spec runner to finish'
53
88
 
54
89
  method_option :console,
55
- :type => :string,
56
- :aliases => '-c',
57
- :default => 'failure',
58
- :desc => 'Whether to show console.log statements in the spec runner, either `always`, `never` or `failure`'
90
+ type: :string,
91
+ default: 'failure',
92
+ desc: 'Whether to show console.log statements in the spec runner, either `always`, `never` or `failure`'
59
93
 
60
94
  method_option :errors,
61
- :type => :string,
62
- :aliases => '-x',
63
- :default => 'failure',
64
- :desc => 'Whether to show errors in the spec runner, either `always`, `never` or `failure`'
65
-
66
- method_option :server_env,
67
- :type => :string,
68
- :aliases => '-e',
69
- :default => 'test',
70
- :desc => 'The server environment to use, for example `development`, `test` etc.'
71
-
72
- method_option :spec_dir,
73
- :type => :string,
74
- :aliases => '-d',
75
- :default => 'spec/javascripts',
76
- :desc => 'The directory with the Jasmine specs'
95
+ type: :string,
96
+ default: 'failure',
97
+ desc: 'Whether to show errors in the spec runner, either `always`, `never` or `failure`'
98
+
99
+ method_option :focus,
100
+ type: :boolean,
101
+ aliases: '-f',
102
+ default: true,
103
+ desc: 'Specdoc focus to hide successful tests when at least one test fails'
104
+
105
+ method_option :specdoc,
106
+ type: :string,
107
+ default: :always,
108
+ desc: 'Whether to show successes in the spec runner, either `always`, `never` or `failure`'
109
+
110
+ method_option :coverage,
111
+ type: :boolean,
112
+ default: false,
113
+ desc: 'Whether to enable the coverage support or not'
114
+
115
+ method_option :coverage_html,
116
+ type: :boolean,
117
+ default: false,
118
+ desc: 'Whether to generate html coverage report. Implies --coverage'
119
+
120
+ method_option :coverage_html_dir,
121
+ type: :string,
122
+ default: "./coverage",
123
+ desc: 'Where to save html coverage reports. Defaults to ./coverage. Implies --coverage-html'
124
+
125
+ method_option :coverage_summary,
126
+ type: :boolean,
127
+ default: false,
128
+ desc: 'Whether to generate html coverage summary. Implies --coverage'
129
+
130
+ method_option :ignore_instrumentation,
131
+ type: :string,
132
+ default: "",
133
+ desc: 'Files matching this regex will not be instrumented (e.g. vendor)'
134
+
135
+ method_option :statements_threshold,
136
+ type: :numeric,
137
+ default: 0,
138
+ desc: 'Statements coverage threshold'
139
+
140
+ method_option :functions_threshold,
141
+ type: :numeric,
142
+ default: 0,
143
+ desc: 'Functions coverage threshold'
144
+
145
+ method_option :branches_threshold,
146
+ type: :numeric,
147
+ default: 0,
148
+ desc: 'Branches coverage threshold'
149
+
150
+ method_option :lines_threshold,
151
+ type: :numeric,
152
+ default: 0,
153
+ desc: 'Lines coverage threshold'
154
+
155
+ method_option :junit,
156
+ type: :boolean,
157
+ default: false,
158
+ desc: 'Whether to save jasmine test results in JUnit-compatible xml files'
159
+
160
+ method_option :junit_consolidate,
161
+ type: :boolean,
162
+ default: false,
163
+ desc: 'Whether to save nested describes within the same xml file as their parent'
164
+
165
+ method_option :junit_save_path,
166
+ type: :string,
167
+ default: '',
168
+ desc: 'The directory to save junit xml files into'
77
169
 
78
170
  # Run the Guard::Jasmine::Runner with options from
79
171
  # the command line.
@@ -81,33 +173,56 @@ module Guard
81
173
  # @param [Array<String>] paths the name of the specs to run
82
174
  #
83
175
  def spec(*paths)
84
- paths = [options.spec_dir] if paths.empty?
85
-
86
- runner = {}
87
- runner[:jasmine_url] = options.url
88
- runner[:phantomjs_bin] = options.bin || CLI.which('phantomjs')
89
- runner[:timeout] = options.timeout
90
- runner[:port] = options.port
91
- runner[:server_env] = options.server_env
92
- runner[:spec_dir] = options.spec_dir
93
- runner[:console] = [:always, :never, :failure].include?(options.console.to_sym) ? options.console.to_sym : :failure
94
- runner[:errors] = [:always, :never, :failure].include?(options.errors.to_sym) ? options.errors.to_sym : :failure
95
- runner[:server] = options.server.to_sym
96
-
97
- runner[:notification] = false
98
- runner[:hide_success] = true
99
- runner[:max_error_notify] = 0
100
- runner[:specdoc] = :always
101
-
102
- if CLI.phantomjs_bin_valid?(runner[:phantomjs_bin])
103
- ::Guard::Jasmine::Server.start(runner[:server], runner[:port], runner[:server_env], runner[:spec_dir]) unless runner[:server] == :none
104
-
105
- if CLI.runner_available?(runner[:jasmine_url])
106
- result = ::Guard::Jasmine::Runner.run(paths, runner)
176
+ runner_options = {}
177
+ runner_options[:port] = options.port || CLI.find_free_server_port
178
+ runner_options[:spec_dir] = options.spec_dir || (File.exists?(File.join('spec', 'javascripts')) ? File.join('spec', 'javascripts') : 'spec')
179
+ runner_options[:line_number] = options.line_number
180
+ runner_options[:server] = options.server.to_sym == :auto ? ::Guard::Jasmine::Server.detect_server(runner_options[:spec_dir]) : options.server.to_sym
181
+ runner_options[:server_mount] = options.mount || (defined?(JasmineRails) ? '/specs' : '/jasmine')
182
+ runner_options[:jasmine_url] = options.url || "http://localhost:#{ runner_options[:port] }#{ options.server.to_sym == :jasmine_gem ? '/' : runner_options[:server_mount] }"
183
+ runner_options[:phantomjs_bin] = options.bin || CLI.which('phantomjs')
184
+ runner_options[:timeout] = options.timeout
185
+ runner_options[:verbose] = options.verbose
186
+ runner_options[:server_env] = options.server_env
187
+ runner_options[:server_timeout] = options.server_timeout
188
+ runner_options[:rackup_config] = options.rackup_config
189
+ runner_options[:console] = [:always, :never, :failure].include?(options.console.to_sym) ? options.console.to_sym : :failure
190
+ runner_options[:errors] = [:always, :never, :failure].include?(options.errors.to_sym) ? options.errors.to_sym : :failure
191
+ runner_options[:specdoc] = [:always, :never, :failure].include?(options.specdoc.to_sym) ? options.specdoc.to_sym : :always
192
+ runner_options[:focus] = options.focus
193
+ runner_options[:coverage] = options.coverage || options.coverage_html || options.coverage_summary || options.coverage_html_dir != "./coverage"
194
+ runner_options[:coverage_html] = options.coverage_html || options.coverage_html_dir != "./coverage"
195
+ runner_options[:coverage_html_dir] = options.coverage_html_dir
196
+ runner_options[:coverage_summary] = options.coverage_summary
197
+ runner_options[:ignore_instrumentation] = options.ignore_instrumentation
198
+ runner_options[:statements_threshold] = options.statements_threshold
199
+ runner_options[:functions_threshold] = options.functions_threshold
200
+ runner_options[:branches_threshold] = options.branches_threshold
201
+ runner_options[:lines_threshold] = options.lines_threshold
202
+ runner_options[:notification] = false
203
+ runner_options[:hide_success] = true
204
+ runner_options[:max_error_notify] = 0
205
+ runner_options[:junit] = options.junit
206
+ runner_options[:junit_consolidate] = options.junit_consolidate
207
+ runner_options[:junit_save_path] = options.junit_save_path
208
+ runner_options[:is_cli] = true
209
+
210
+ ::Guard::UI.options = ::Guard::UI.options.merge({ :template => ':message' })
211
+
212
+ paths = [runner_options[:spec_dir]] if paths.empty?
213
+
214
+ if CLI.phantomjs_bin_valid?(runner_options[:phantomjs_bin])
215
+ catch(:task_has_failed) do
216
+ ::Guard::Jasmine::Server.start(runner_options) unless runner_options[:server] == :none
217
+ end
218
+
219
+ if CLI.runner_available?(runner_options)
220
+ result = ::Guard::Jasmine::Runner.run(paths, runner_options)
107
221
  ::Guard::Jasmine::Server.stop
108
222
 
109
223
  Process.exit result.first ? 0 : 1
110
224
  else
225
+ ::Guard::Jasmine::Server.stop
111
226
  Process.exit 2
112
227
  end
113
228
 
@@ -0,0 +1,69 @@
1
+ # coding: utf-8
2
+ require 'tilt'
3
+ require 'childprocess'
4
+ require 'guard/jasmine'
5
+ require 'guard/jasmine/util'
6
+
7
+ # Tilt template to generate coverage instrumented
8
+ # Jasmine specs files.
9
+ #
10
+ class JasmineCoverage < Tilt::Template
11
+ extend ::Guard::Jasmine::Util
12
+
13
+ def prepare
14
+ end
15
+
16
+ # Returns a coverage instrumented JavaScript file
17
+ #
18
+ def evaluate(context, locals)
19
+ return data if !ENV['IGNORE_INSTRUMENTATION'].to_s.empty? && file =~ Regexp.new(ENV['IGNORE_INSTRUMENTATION'])
20
+ return data unless JasmineCoverage.coverage_bin
21
+ return data unless file.include?(JasmineCoverage.app_asset_path)
22
+
23
+ Dir.mktmpdir do |path|
24
+ filename = File.basename(file)
25
+ input = File.join(path, filename).sub /\.js.+/, '.js'
26
+
27
+ File.write input, data
28
+
29
+ result = %x[#{JasmineCoverage.coverage_bin} instrument --embed-source #{input.shellescape}]
30
+
31
+ raise "Could not generate coverage instrumented file for #{ file }" unless $?.exitstatus == 0
32
+
33
+ result.gsub input, file
34
+
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ # Get the absolute path to the projects assets path `/app/assets`.
41
+ #
42
+ # @return [String] the path to the Rails assets
43
+ #
44
+ def self.app_asset_path
45
+ @app_asset_path ||= File.join(Rails.root, 'app', 'assets')
46
+ end
47
+
48
+ # Returns the coverage executable path.
49
+ #
50
+ # @return [String] the path
51
+ #
52
+ def self.coverage_bin
53
+ @coverage_bin ||= which 'istanbul'
54
+ end
55
+
56
+ end
57
+
58
+ if ENV['COVERAGE'] == 'true' and defined?(Rails)
59
+
60
+ # Guard::Jasmine engine to register coverage instrumented
61
+ # Jasmine spec files.
62
+ #
63
+ class GuardJasmineCoverageEngine < ::Rails::Engine
64
+ initializer 'guard-jasmine.initialize' do |app|
65
+ app.assets.register_postprocessor 'application/javascript', JasmineCoverage
66
+ end
67
+ end
68
+
69
+ end
@@ -54,7 +54,7 @@ module Guard
54
54
  # @param [Hash] options the output options
55
55
  #
56
56
  def spec_failed(message, options = { })
57
- ::Guard::UI.info(color(message, ';31'), options)
57
+ ::Guard::UI.info(color(message, ';31'), options)
58
58
  end
59
59
 
60
60
  # Print a red spec failed message to the console.
@@ -63,7 +63,7 @@ module Guard
63
63
  # @param [Hash] options the output options
64
64
  #
65
65
  def suite_name(message, options = { })
66
- ::Guard::UI.info(color(message, ';33'), options)
66
+ ::Guard::UI.info(color(message, ';33'), options)
67
67
  end
68
68
 
69
69
  # Outputs a system notification.
@@ -81,7 +81,7 @@ module Guard
81
81
 
82
82
  # Print a info message to the console.
83
83
  #
84
- # @param [String] test the text to colorize
84
+ # @param [String] text the text to colorize
85
85
  # @param [String] color_code the color code
86
86
  #
87
87
  def color(text, color_code)
@@ -34,7 +34,7 @@ module Guard
34
34
 
35
35
  # Tests if the file is valid.
36
36
  #
37
- # @param [String] file the file
37
+ # @param [String] path the file
38
38
  # @return [Boolean] when the file valid
39
39
  #
40
40
  def jasmine_spec?(path)
@@ -5,11 +5,14 @@ phantom.injectJs 'lib/result.js'
5
5
  # Set default values
6
6
  options =
7
7
  url: phantom.args[0] || 'http://127.0.0.1:3000/jasmine'
8
- timeout: parseInt(phantom.args[1] || 5000)
8
+ timeout: parseInt(phantom.args[1] || 10000)
9
9
  specdoc: phantom.args[2] || 'failure'
10
10
  focus: /true/i.test phantom.args[3]
11
11
  console: phantom.args[4] || 'failure'
12
12
  errors: phantom.args[5] || 'failure'
13
+ junit: /true/i.test phantom.args[6]
14
+ junit_consolidate: /true/i.test phantom.args[7]
15
+ junit_save_path: phantom.args[8] || ''
13
16
 
14
17
  # Create the web page.
15
18
  #
@@ -20,11 +23,13 @@ page = require('webpage').create()
20
23
  currentSpecId = -1
21
24
  logs = {}
22
25
  errors = {}
26
+ resultsKey = "__jr" + Math.ceil(Math.random() * 1000000)
27
+ fs = require("fs")
23
28
 
24
29
  # Catch JavaScript errors
25
30
  #
26
31
  page.onError = (msg, trace) ->
27
- if currentSpecId && currentSpecId isnt -1
32
+ if currentSpecId
28
33
  errors[currentSpecId] ||= []
29
34
  errors[currentSpecId].push({ msg: msg, trace: trace })
30
35
 
@@ -39,72 +44,150 @@ page.onConsoleMessage = (msg, line, source) ->
39
44
 
40
45
  else if /^SPEC_START: (\d+)$/.test(msg)
41
46
  currentSpecId = Number(RegExp.$1)
42
- logs[currentSpecId] = []
43
47
 
44
48
  else
45
- logs[currentSpecId].push(msg) if currentSpecId isnt -1
49
+ logs[currentSpecId] ||= []
50
+ logs[currentSpecId].push(msg)
46
51
 
47
52
  # Initialize the page before the JavaScript is run.
48
53
  #
49
54
  page.onInitialized = ->
55
+ overloadPageEvaluate(page)
56
+ setupWriteFileFunction(page, resultsKey, fs.separator)
57
+
50
58
  page.injectJs 'lib/console.js'
51
59
  page.injectJs 'lib/reporter.js'
60
+ page.injectJs 'lib/junit_reporter.js'
52
61
 
53
- page.evaluate ->
62
+ setupReporters = ->
54
63
  # Attach the console reporter when the document is ready.
55
64
  window.onload = ->
65
+ window.onload = null
56
66
  window.resultReceived = false
57
67
  window.reporter = new ConsoleReporter()
58
- jasmine.getEnv().addReporter(window.reporter)
68
+ if window.jasmine
69
+ jasmine.getEnv().addReporter(new JUnitXmlReporter("%save_path%", "%consolidate%"))
70
+ jasmine.getEnv().addReporter(window.reporter)
71
+
72
+ page.evaluate(setupReporters, {save_path: options.junit_save_path, consolidate: options.junit_consolidate})
73
+
74
+
75
+ getXmlResults = (page, key) ->
76
+ getWindowObj = ->
77
+ window["%resultsObj%"] || {}
78
+ page.evaluate getWindowObj, {resultsObj: key}
79
+
80
+ replaceFunctionPlaceholders= (fn, replacements) ->
81
+ if replacements && typeof replacements == 'object'
82
+ fn = fn.toString()
83
+ for p of replacements
84
+ if replacements.hasOwnProperty(p)
85
+ match = new RegExp("%" + p + "%", "g")
86
+ loop
87
+ fn = fn.replace(match, replacements[p])
88
+ break unless fn.indexOf(match) != -1
89
+ fn
90
+
91
+ overloadPageEvaluate = (page) ->
92
+ page._evaluate = page.evaluate
93
+ page.evaluate = (fn, replacements) ->
94
+ page._evaluate(replaceFunctionPlaceholders(fn, replacements))
95
+ page
96
+
97
+ setupWriteFileFunction= (page,key, path_separator) ->
98
+ saveData = () ->
99
+ window["%resultsObj%"] = {}
100
+ window.fs_path_separator = "%fs_path_separator%"
101
+ window.__phantom_writeFile = (filename, text) ->
102
+ window["%resultsObj%"][filename] = text;
103
+
104
+ page.evaluate saveData, {resultsObj: key, fs_path_separator: path_separator}
59
105
 
60
106
  # Open web page and run the Jasmine test runner
61
107
  #
62
108
  page.open options.url, (status) ->
63
109
  # Avoid that a failed iframe load breaks the runner, see https://github.com/netzpirat/guard-jasmine/pull/19
64
110
  page.onLoadFinished = ->
65
-
66
111
  if status isnt 'success'
67
112
  console.log JSON.stringify({ error: "Unable to access Jasmine specs at #{ options.url }" })
68
113
  phantom.exit()
69
114
  else
70
- done = -> phantom.exit()
71
- waitFor specsReady, done, options.timeout
115
+ waitFor jasmineReady, jasmineAvailable, options.timeout, jasmineMissing
116
+
117
+ # Test if the jasmine has been loaded
118
+ #
119
+ jasmineReady = ->
120
+ page.evaluate -> window.jasmine
121
+
122
+ # Start specs after they are have been loaded
123
+ #
124
+ jasmineAvailable = ->
125
+ waitFor specsReady, specsDone, options.timeout, specsTimedout
126
+
127
+ # Error message for when jasmine never loaded asynchronously
128
+ #
129
+ jasmineMissing = ->
130
+ text = page.evaluate -> document.getElementsByTagName('body')[0]?.innerText
131
+
132
+ if text
133
+ error = """
134
+ The Jasmine reporter is not available!
135
+
136
+ #{ text }
137
+ """
138
+ console.log JSON.stringify({ error: error })
139
+ else
140
+ console.log JSON.stringify({ error: 'The Jasmine reporter is not available!' })
72
141
 
73
142
  # Test if the specs have finished.
74
143
  #
75
144
  specsReady = ->
76
145
  page.evaluate -> window.resultReceived
77
146
 
147
+ # Error message for when specs time out
148
+ #
149
+ specsTimedout = ->
150
+ text = page.evaluate -> document.getElementsByTagName('body')[0]?.innerText
151
+ if text
152
+ error = """
153
+ Timeout waiting for the Jasmine test results!
154
+
155
+ #{ text }
156
+ """
157
+ console.log JSON.stringify({ error: error })
158
+ else
159
+ console.log JSON.stringify({ error: 'Timeout for the Jasmine test results!' })
160
+
161
+ specsDone = ->
162
+ if options.junit == true
163
+ xml_results = getXmlResults(page, resultsKey)
164
+ for filename of xml_results
165
+ if xml_results.hasOwnProperty(filename) && (output = xml_results[filename]) && typeof(output) == 'string'
166
+ fs.write(filename, output, 'w')
167
+
168
+ phantom.exit()
169
+
78
170
  # Wait until the test condition is true or a timeout occurs.
79
171
  #
80
172
  # @param [Function] test the test that returns true if condition is met
81
173
  # @param [Function] ready the action when the condition is fulfilled
82
174
  # @param [Number] timeout the max amount of time to wait in milliseconds
83
175
  #
84
- waitFor = (test, ready, timeout = 5000) ->
85
- start = new Date().getTime()
86
- condition = false
87
-
88
- wait = ->
89
- if (new Date().getTime() - start < timeout) and not condition
90
- condition = test()
176
+ waitFor = (test, ready, timeout = 10000, timeoutFunction) ->
177
+ start = Date.now()
178
+ condition = false
179
+ interval = undefined
180
+
181
+ wait = ->
182
+ if (Date.now() - start < timeout) and not condition
183
+ condition = test()
184
+ else
185
+ clearInterval interval
186
+
187
+ if condition
188
+ ready()
91
189
  else
92
- if not condition
93
- text = page.evaluate -> document.getElementsByTagName('body')[0]?.innerText
94
-
95
- if text
96
- error = """
97
- Timeout waiting for the Jasmine test results!
98
-
99
- #{ text }
100
- """
101
- console.log JSON.stringify({ error: error })
102
- else
103
- console.log JSON.stringify({ error: 'Timeout waiting for the Jasmine test results!' })
104
-
105
- phantom.exit(1)
106
- else
107
- ready()
108
- clearInterval interval
190
+ timeoutFunction()
191
+ phantom.exit(1)
109
192
 
110
- interval = setInterval wait, 250
193
+ interval = setInterval wait, 250