Module: Guard::Jasmine::Runner

Extended by:
Util
Defined in:
lib/guard/jasmine/runner.rb

Overview

The Jasmine runner handles the execution of the spec through the PhantomJS binary, evaluates the JSON response from the PhantomJS Script guard_jasmine.coffee, writes the result to the console and triggers optional system notifications.

Constant Summary

THRESHOLDS =

Name of the coverage threshold options

[:statements_threshold, :functions_threshold, :branches_threshold, :lines_threshold]

Class Method Summary collapse

Methods included from Util

find_free_server_port, phantomjs_bin_valid?, runner_available?, which

Class Method Details

.any_coverage_threshold?(options) ⇒ Boolean (private)

Do we should check the coverage?

Returns:

  • (Boolean)

    true if any coverage threshold is set



710
711
712
# File 'lib/guard/jasmine/runner.rb', line 710

def any_coverage_threshold?(options)
  THRESHOLDS.any? { |threshold| options[threshold] != 0 }
end

.check_coverage(options) ⇒ Object (private)

Uses the Istanbul text reported to output the result of the last coverage run.

Parameters:

  • options (Hash)

    the options for the coverage



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/guard/jasmine/runner.rb', line 410

def check_coverage(options)
  if any_coverage_threshold?(options)
    coverage = `#{coverage_bin} check-coverage #{ istanbul_coverage_options(options) } #{ coverage_file } 2>&1`
    coverage = coverage.split("\n").grep(/ERROR/).join.sub('ERROR:', '')
    failed   = $? && $?.exitstatus != 0

    if failed
      Formatter.error coverage
      Formatter.notify(coverage, title: 'Code coverage failed', image: :failed, priority: 2) if options[:notification]
    else
      Formatter.success 'Code coverage succeed'
      Formatter.notify('All code is adequately covered with specs', title: 'Code coverage succeed') if options[:notification] && !options[:hide_success]
    end
  end
end

.collect_specs(suites) ⇒ Array<Hash> (private)

Get all specs from the suites and its nested suites.

Parameters:

  • suites (Array<Hash>)

    the suites results

Returns:

  • (Array<Hash>)

    all specs



658
659
660
661
662
663
664
# File 'lib/guard/jasmine/runner.rb', line 658

def collect_specs(suites)
  suites.inject([]) do |specs, suite|
    specs = (specs | suite['specs']) if suite['specs']
    specs = (specs | collect_specs(suite['suites'])) if suite['suites']
    specs
  end
end

.console_for_spec?(spec, options = { }) ⇒ Boolean (private)

Are console logs shown for this spec?

Parameters:

  • spec (Hash)

    the spec

  • options (Hash) (defaults to: { })

    the options

Returns:

  • (Boolean)


534
535
536
537
# File 'lib/guard/jasmine/runner.rb', line 534

def console_for_spec?(spec, options = { })
  spec['logs'] && ((spec['passed'] && options[:console] == :always) ||
    (!spec['passed'] && options[:console] != :never))
end

.console_logs_shown?(suite, passed, options = { }) ⇒ Boolean (private)

Are console logs shown for this suite?

Parameters:

  • suite (Hash)

    the suite

  • passed (Boolean)

    the spec status

  • options (Hash) (defaults to: { })

    the options

Returns:

  • (Boolean)


515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/guard/jasmine/runner.rb', line 515

def console_logs_shown?(suite, passed, options = { })
  # Are console messages displayed?
  console_enabled          = options[:console] == :always || (options[:console] == :failure && !passed)

  # Are there any logs to display at all for this suite?
  logs_for_current_options = suite['specs'].select do |spec|
    spec['logs'] && (options[:console] == :always || (options[:console] == :failure && !spec['passed']))
  end

  any_logs_present = !logs_for_current_options.empty?

  console_enabled && any_logs_present
end

.contains_failed_spec?(suite) ⇒ Boolean (private)

Tests if the given suite has a failing spec underneath.

Parameters:

  • suite (Hash)

    the suite result

Returns:

  • (Boolean)

    the search result



649
650
651
# File 'lib/guard/jasmine/runner.rb', line 649

def contains_failed_spec?(suite)
  collect_specs([suite]).any? { |spec| !spec['passed'] }
end

.coverage_binString (private)

Returns the coverage executable path.

Returns:

  • (String)

    the path



730
731
732
# File 'lib/guard/jasmine/runner.rb', line 730

def coverage_bin
  @coverage_bin ||= which 'istanbul'
end

.coverage_fileString (private)

Get the coverage file to save all coverage data. Creates tmp/coverage if not exists.

Returns:

  • (String)

    the filename to use



739
740
741
# File 'lib/guard/jasmine/runner.rb', line 739

def coverage_file
  File.join(coverage_root, 'coverage.json')
end

.coverage_report_directory(options) ⇒ String (private)

Creates and returns the coverage report directory.

Parameters:

  • options (Hash)

    for the coverage report directory

Returns:

  • (String)

    the coverage report directory



756
757
758
# File 'lib/guard/jasmine/runner.rb', line 756

def coverage_report_directory(options)
  File.expand_path(options[:coverage_html_dir])
end

.coverage_rootString (private)

Create and returns the coverage root directory.

Returns:

  • (String)

    the coverage root



747
748
749
# File 'lib/guard/jasmine/runner.rb', line 747

def coverage_root
  File.expand_path(File.join('tmp', 'coverage'))
end

.description_shown?(passed, spec, options = { }) ⇒ Boolean (private)

Is the description shown for this spec?

Parameters:

  • passed (Boolean)

    the spec status

  • spec (Hash)

    the spec

  • options (Hash) (defaults to: { })

    the options

Returns:

  • (Boolean)


574
575
576
# File 'lib/guard/jasmine/runner.rb', line 574

def description_shown?(passed, spec, options = { })
  specdoc_shown?(passed, options) || console_for_spec?(spec, options) || errors_for_spec?(spec, options)
end

.error_logs_shown?(suite, passed, options = { }) ⇒ Boolean (private)

Are error logs shown for this suite?

Parameters:

  • suite (Hash)

    the suite

  • passed (Boolean)

    the spec status

  • options (Hash) (defaults to: { })

    the options

Returns:

  • (Boolean)


545
546
547
548
549
550
551
552
553
554
555
556
557
# File 'lib/guard/jasmine/runner.rb', line 545

def error_logs_shown?(suite, passed, options = { })
  # Are error messages displayed?
  errors_enabled             = options[:errors] == :always || (options[:errors] == :failure && !passed)

  # Are there any errors to display at all for this suite?
  errors_for_current_options = suite['specs'].select do |spec|
    spec['errors'] && (options[:errors] == :always || (options[:errors] == :failure && !spec['passed']))
  end

  any_errors_present= !errors_for_current_options.empty?

  errors_enabled && any_errors_present
end

.errors_for_spec?(spec, options = { }) ⇒ Boolean (private)

Are errors shown for this spec?

Parameters:

  • spec (Hash)

    the spec

  • options (Hash) (defaults to: { })

    the options

Returns:

  • (Boolean)


563
564
565
566
# File 'lib/guard/jasmine/runner.rb', line 563

def errors_for_spec?(spec, options = { })
  spec['errors'] && ((spec['passed'] && options[:errors] == :always) ||
    (!spec['passed'] && options[:errors] != :never))
end

.evaluate_response(output, file, options) ⇒ Hash (private)

Evaluates the JSON response that the PhantomJS script writes to stdout. The results triggers further notification actions.

Parameters:

  • output (String)

    the JSON output the spec run

  • file (String)

    the file name of the spec

  • options (Hash)

    the options for the execution

Returns:

  • (Hash)

    the suite result



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/guard/jasmine/runner.rb', line 256

def evaluate_response(output, file, options)
  json = output.read
  json = json.encode('UTF-8') if json.respond_to?(:encode)

  begin
    result = MultiJson.decode(json, { max_nesting: false })
    raise 'No response from Jasmine runner' if !result && options[:is_cli]

    if result['error']
      if options[:is_cli]
        raise 'An error occurred in the Jasmine runner'
      else
        notify_runtime_error(result, options)
      end
    elsif result
      result['file'] = file
      notify_spec_result(result, options)
    end

    if result && result['coverage'] && options[:coverage]
      notify_coverage_result(result['coverage'], file, options)
    end

    result

  rescue MultiJson::DecodeError => e
    if e.data == ''
      if options[:is_cli]
        raise 'No response from Jasmine runner'
      else
        Formatter.error('No response from the Jasmine runner!')
      end
    else
      if options[:is_cli]
        raise 'Cannot decode JSON from PhantomJS runner'
      else
        Formatter.error("Cannot decode JSON from PhantomJS runner: #{ e.message }")
        Formatter.error("JSON response: #{ e.data }")
      end
    end
  ensure
    output.close
  end
end

.failed_paths_from(results) ⇒ Array<String> (private)

Returns the failed spec file names.

Parameters:

  • results (Array<Object>)

    the spec runner results

Returns:

  • (Array<String>)

    the list of failed spec files



75
76
77
# File 'lib/guard/jasmine/runner.rb', line 75

def failed_paths_from(results)
  results.map { |r| !r['passed'] ? r['file'] : nil }.compact
end

.file_and_line_number_parts(file) ⇒ Array (private)

Splits the file name into the physical file name and the line number if present. E.g.: 'some_spec.js.coffee:10' -> ['some_spec.js.coffee', 10].

If the line number is missing the second part of the returned array is nil.

Parameters:

  • file (String)

    the spec file

Returns:

  • (Array)

    [file_name, line_number]



220
221
222
223
# File 'lib/guard/jasmine/runner.rb', line 220

def file_and_line_number_parts(file)
  match = file.match(/^(.+?)(?::(\d+))?$/)
  [match[1], match[2].nil? ? nil : match[2].to_i]
end

.format_message(message, short) ⇒ String (private)

Formats a message.

Parameters:

  • message (String)

    the error message

  • short (Boolean)

    show a short version of the message

Returns:

  • (String)

    the cleaned error message



672
673
674
675
676
677
678
# File 'lib/guard/jasmine/runner.rb', line 672

def format_message(message, short)
  if message =~ /(.*?) in http.+?assets\/(.*)\?body=\d+\s\((line\s\d+)/
    short ? $1 : "#{ $1 } in #{ $2 } on #{ $3 }"
  else
    message
  end
end

.generate_html_report(options) ⇒ Object (private)

Uses the Istanbul text reported to output the result of the last coverage run.

Parameters:

  • options (Hash)

    for the HTML report



431
432
433
434
435
# File 'lib/guard/jasmine/runner.rb', line 431

def generate_html_report(options)
  report_directory = coverage_report_directory(options)
  `#{coverage_bin} report --dir #{ report_directory } --root #{ coverage_root } html #{ coverage_file }`
  Formatter.info "Updated HTML report available at: #{ report_directory }/index.html"
end

.generate_summary_reportObject (private)

Uses the Istanbul text-summary reporter to output the summary of all the coverage runs combined.



440
441
442
443
444
445
446
447
448
449
450
# File 'lib/guard/jasmine/runner.rb', line 440

def generate_summary_report
  Formatter.info 'Spec coverage summary:'

  puts ''

  `#{coverage_bin} report --root #{ coverage_root } text-summary #{ coverage_file }`.each_line do |line|
    puts line.sub(/\n$/, '') if line =~ /\)$/
  end

  puts ''
end

.generate_text_report(file, options) ⇒ Object (private)

Uses the Istanbul text reported to output the result of the last coverage run.

Parameters:

  • file (String)

    the file name of the spec

  • options (Hash)

    the options for the execution



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/guard/jasmine/runner.rb', line 386

def generate_text_report(file, options)
  Formatter.info 'Spec coverage details:'

  if file == options[:spec_dir]
    matcher = /[|+]$/
  else
    impl    = file.sub('_spec', '').sub(options[:spec_dir], '')
    matcher = /(-+|All files|% Lines|#{ Regexp.escape(File.basename(impl)) }|#{ File.dirname(impl).sub(/^\//, '') }\/[^\/])/
  end

  puts ''

  `#{coverage_bin} report --root #{ coverage_root } text #{ coverage_file }`.each_line do |line|
    puts line.sub(/\n$/, '') if line =~ matcher
  end

  puts ''
end

.indent(message, level) ⇒ Object (private)

Indent a message.

Parameters:

  • message (String)

    the message

  • level (Number)

    the indention level



621
622
623
# File 'lib/guard/jasmine/runner.rb', line 621

def indent(message, level)
  (' ' * level) + message
end

.istanbul_coverage_options(options) ⇒ String (private)

Converts the options to Istanbul recognized options

Parameters:

  • options (Hash)

    the options for the coverage

Returns:

  • (String)

    the command line options



719
720
721
722
723
724
# File 'lib/guard/jasmine/runner.rb', line 719

def istanbul_coverage_options(options)
  THRESHOLDS.inject([]) do |coverage, name|
    threshold = options[name]
    coverage << (threshold != 0 ? "--#{ name.to_s.sub('_threshold', '') } #{ threshold }" : '')
  end.reject(&:empty?).join(' ')
end

.it_and_describe_lines(file, from, to) ⇒ Object (private)

Returns all lines of the file that are either a 'describe' or a 'it' declaration.

Parameters:

  • file (String)

    the spec file

  • from (Numeric)

    the first line in the range

  • to (Numeric)

    the last line in the range



233
234
235
236
# File 'lib/guard/jasmine/runner.rb', line 233

def it_and_describe_lines(file, from, to)
  File.readlines(file)[from, to].
    select { |x| x =~ /^\s*(it|describe)/ }
end

.jasmine_suite(file, options) ⇒ String (private)

Get the Jasmine test runner URL with the appended suite name that acts as the spec filter.

Parameters:

  • file (String)

    the spec file

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :jasmine_url (String)

    the url of the Jasmine test runner

Returns:

  • (String)

    the Jasmine url



130
131
132
# File 'lib/guard/jasmine/runner.rb', line 130

def jasmine_suite(file, options)
  options[:jasmine_url] + query_string_for_suite(file, options)
end

.notify_coverage_result(coverage, file, options) ⇒ Object (private)

Notification about the coverage of a spec run, success or failure, and some stats.

Parameters:

  • coverage (Hash)

    the coverage hash from the JSON

  • file (String)

    the file name of the spec

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :notification (Boolean)

    show notifications

  • :hide_success (Boolean)

    hide success message notification



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'lib/guard/jasmine/runner.rb', line 358

def notify_coverage_result(coverage, file, options)
  if coverage_bin
    FileUtils.mkdir_p(coverage_root) unless File.exist?(coverage_root)

    update_coverage(coverage, file, options)

    if options[:coverage_summary]
      generate_summary_report
    else
      generate_text_report(file, options)
    end

    check_coverage(options)

    if options[:coverage_html]
      generate_html_report(options)
    end
  else
    Formatter.error('Skipping coverage report: unable to locate istanbul in your PATH')
  end
end

.notify_errors(result, options) ⇒ Object (private)

Show system notifications about the occurred errors.

Parameters:

  • result (Hash)

    the suite result

  • options (Hash)

    the options

Options Hash (options):

  • :max_error_notify (Integer)

    maximum error notifications to show

  • :notification (Boolean)

    show notifications



632
633
634
635
636
637
638
639
640
641
642
# File 'lib/guard/jasmine/runner.rb', line 632

def notify_errors(result, options)
  collect_specs(result['suites']).each_with_index do |spec, index|
    if !spec['passed'] && options[:max_error_notify] > index
      msg = spec['messages'].map { |message| format_message(message, true) }.join(', ')
      Formatter.notify("#{ spec['description'] }: #{ msg }",
                       title:    'Jasmine spec failed',
                       image:    :failed,
                       priority: 2) if options[:notification]
    end
  end
end

.notify_runtime_error(result, options) ⇒ Object (private)

Notification when a system error happens that prohibits the execution of the Jasmine spec.

Parameters:

  • result (Hash)

    the suite result

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :notification (Boolean)

    show notifications



308
309
310
311
312
# File 'lib/guard/jasmine/runner.rb', line 308

def notify_runtime_error(result, options)
  message = "An error occurred: #{ result['error'] }"
  Formatter.error(message)
  Formatter.notify(message, title: 'Jasmine error', image: :failed, priority: 2) if options[:notification]
end

.notify_spec_result(result, options) ⇒ Object (private)

Notification about a spec run, success or failure, and some stats.

Parameters:

  • result (Hash)

    the suite result

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :notification (Boolean)

    show notifications

  • :hide_success (Boolean)

    hide success message notification



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/guard/jasmine/runner.rb', line 322

def notify_spec_result(result, options)
  specs           = result['stats']['specs']
  failures        = result['stats']['failures']
  time            = result['stats']['time']
  specs_plural    = specs == 1 ? '' : 's'
  failures_plural = failures == 1 ? '' : 's'

  Formatter.info("\nFinished in #{ time } seconds")

  message      = "#{ specs } spec#{ specs_plural }, #{ failures } failure#{ failures_plural }"
  full_message = "#{ message }\nin #{ time } seconds"
  passed       = failures == 0

  if passed
    report_specdoc(result, passed, options)
    Formatter.success(message)
    Formatter.notify(full_message, title: 'Jasmine suite passed') if options[:notification] && !options[:hide_success]
  else
    report_specdoc(result, passed, options)
    Formatter.error(message)
    notify_errors(result, options)
    Formatter.notify(full_message, title: 'Jasmine suite failed', image: :failed, priority: 2) if options[:notification]
  end

  Formatter.info("Done.\n")
end

.notify_start_message(paths, options) ⇒ Object (private)

Shows a notification in the console that the runner starts.

Parameters:

  • paths (Array<String>)

    the spec files or directories

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :spec_dir (String)

    the directory with the Jasmine specs



60
61
62
63
64
65
66
67
68
# File 'lib/guard/jasmine/runner.rb', line 60

def notify_start_message(paths, options)
  message = if paths == [options[:spec_dir]]
              'Run all Jasmine suites'
            else
              "Run Jasmine suite#{ paths.size == 1 ? '' : 's' } #{ paths.join(' ') }"
            end

  Formatter.info(message, reset: true)
end

.phantomjs_command(options) ⇒ String (private)

Get the PhantomJS binary and script to execute.

Parameters:

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :phantomjs_bin (String)

    the location of the PhantomJS binary

Returns:

  • (String)

    the command



118
119
120
# File 'lib/guard/jasmine/runner.rb', line 118

def phantomjs_command(options)
  options[:phantomjs_bin] + ' ' + phantomjs_script
end

.phantomjs_scriptString (private)

Get the PhantomJS script that executes the spec and extracts the result from the headless DOM.

Returns:

  • (String)

    the path to the PhantomJS script



139
140
141
# File 'lib/guard/jasmine/runner.rb', line 139

def phantomjs_script
  File.expand_path(File.join(File.dirname(__FILE__), 'phantomjs', 'guard-jasmine.js'))
end

.query_string_for_suite(file, options) ⇒ String (private)

The suite name must be extracted from the spec that will be run.

Parameters:

  • file (String)

    the spec file

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :spec_dir (String)

    the directory with the Jasmine specs

Returns:

  • (String)

    the suite name



151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/guard/jasmine/runner.rb', line 151

def query_string_for_suite(file, options)
  return '' if file == options[:spec_dir]

  query_string = query_string_for_suite_from_line_number(file, options)

  unless query_string
    query_string = query_string_for_suite_from_first_describe(file, options)
  end

  query_string = query_string ? "?spec=#{ query_string }" : ''

  URI.encode(query_string)
end

.query_string_for_suite_from_first_describe(file, options) ⇒ String (private)

The suite name must be extracted from the spec that will be run. This is done by parsing from the head of the spec file until the first describe function is found.

Parameters:

  • file (String)

    the spec file

  • options (Hash)

    the options for the execution

Returns:

  • (String)

    the suite name



202
203
204
205
206
207
208
# File 'lib/guard/jasmine/runner.rb', line 202

def query_string_for_suite_from_first_describe(file, options)
  File.foreach(file) do |line|
    if line =~ /describe\s*[("']+(.*?)["')]+/
      return $1
    end
  end
end

.query_string_for_suite_from_line_number(file, options) ⇒ String (private)

When providing a line number by either the option or by a number directly after the file name the suite is extracted fromt the corresponding line number in the file.

Parameters:

  • file (String)

    the spec file

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :line_number (Fixnum)

    the line number to run

Returns:

  • (String)

    the suite name



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/guard/jasmine/runner.rb', line 174

def query_string_for_suite_from_line_number(file, options)
  file_name, line_number = file_and_line_number_parts(file)
  line_number ||= options[:line_number]

  if line_number
    lines = it_and_describe_lines(file_name, 0, line_number)
    last = lines.pop

    last_indentation = last[/^\s*/].length
    # keep only lines with lower indentation
    lines.delete_if { |x| x[/^\s*/].length >= last_indentation }
    # remove all 'it'
    lines.delete_if { |x| x =~ /^\s*it/ }

    lines << last
    lines.map { |x| spec_title(x) }.join(' ')
  end
end

.report_specdoc(result, passed, options) ⇒ Object (private)

Specdoc like formatting of the result.

Parameters:

  • result (Hash)

    the suite result

  • passed (Boolean)

    status

  • options (Hash)

    the options

Options Hash (options):

  • :console (Symbol)

    options for the console.log output, either :always, :never or :failure



459
460
461
462
463
# File 'lib/guard/jasmine/runner.rb', line 459

def report_specdoc(result, passed, options)
  result['suites'].each do |suite|
    report_specdoc_suite(suite, passed, options)
  end
end

.report_specdoc_errors(spec, options, level) ⇒ Object (private)

Shows the errors for a given spec.

Parameters:

  • spec (Hash)

    the spec result

  • options (Hash)

    the options

  • level (Number)

    the indention level

Options Hash (options):

  • :errors (Symbol)

    options for the errors output, either :always, :never or :failure



602
603
604
605
606
607
608
609
610
611
612
613
614
# File 'lib/guard/jasmine/runner.rb', line 602

def report_specdoc_errors(spec, options, level)
  if spec['errors'] && (options[:errors] == :always || (options[:errors] == :failure && !spec['passed']))
    spec['errors'].each do |error|
      if error['trace']
        error['trace'].each do |trace|
          Formatter.spec_failed(indent("    ➜ Exception: #{ error['msg']  } in #{ trace['file'] } on line #{ trace['line'] }", level))
        end
      else
        Formatter.spec_failed(indent("    ➜ Exception: #{ error['msg']  }", level))
      end
    end
  end
end

.report_specdoc_logs(spec, options, level) ⇒ Object (private)

Shows the logs for a given spec.

Parameters:

  • spec (Hash)

    the spec result

  • options (Hash)

    the options

  • level (Number)

    the indention level

Options Hash (options):

  • :console (Symbol)

    options for the console.log output, either :always, :never or :failure



585
586
587
588
589
590
591
592
593
# File 'lib/guard/jasmine/runner.rb', line 585

def report_specdoc_logs(spec, options, level)
  if spec['logs'] && (options[:console] == :always || (options[:console] == :failure && !spec['passed']))
    spec['logs'].each do |log|
      log.split("\n").each_with_index do |message, index|
        Formatter.info(indent("    #{ index == 0 ? '' : ' ' } #{ message }", level))
      end
    end
  end
end

.report_specdoc_suite(suite, passed, options, level = 0) ⇒ Object (private)

Show the suite result.

Parameters:

  • suite (Hash)

    the suite

  • passed (Boolean)

    status

  • options (Hash)

    the options

  • level (Number) (defaults to: 0)

    the indention level

Options Hash (options):

  • :console (Symbol)

    options for the console.log output, either :always, :never or :failure

  • :focus (Symbol)

    options for focus on failures in the specdoc



474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
# File 'lib/guard/jasmine/runner.rb', line 474

def report_specdoc_suite(suite, passed, options, level = 0)
  # Print the suite description when the specdoc is shown or there are logs to display
  if specdoc_shown?(passed, options) || console_logs_shown?(suite, passed, options) || error_logs_shown?(suite, passed, options)
    Formatter.suite_name((' ' * level) + suite['description']) if passed || options[:focus] && contains_failed_spec?(suite)
  end

  suite['specs'].each do |spec|
    if spec['passed']
      if passed || !options[:focus] || console_for_spec?(spec, options) || errors_for_spec?(spec, options)
        Formatter.success(indent("#{ spec['description'] }", level)) if description_shown?(passed, spec, options)
        report_specdoc_errors(spec, options, level)
        report_specdoc_logs(spec, options, level)
      end
    else
      Formatter.spec_failed(indent("#{ spec['description'] }", level)) if description_shown?(passed, spec, options)
      spec['messages'].each do |message|
        Formatter.spec_failed(indent("#{ format_message(message, false) }", level)) if specdoc_shown?(passed, options)
      end
      report_specdoc_errors(spec, options, level)
      report_specdoc_logs(spec, options, level)
    end
  end

  suite['suites'].each { |s| report_specdoc_suite(s, passed, options, level + 2) } if suite['suites']
end

.response_status_for(results) ⇒ Boolean (private)

Returns the response status for the given result set.

Parameters:

  • results (Array<Object>)

    the spec runner results

Returns:

  • (Boolean)

    whether it has passed or not



84
85
86
# File 'lib/guard/jasmine/runner.rb', line 84

def response_status_for(results)
  results.none? { |r| r.has_key?('error') || !r['passed'] }
end

.run(paths, options = { }) ⇒ Boolean, Array<String>

Run the supplied specs.

Parameters:

  • paths (Array<String>)

    the spec files or directories

  • options (Hash) (defaults to: { })

    the options for the execution

Options Hash (options):

  • :jasmine_url (String)

    the url of the Jasmine test runner

  • :phantomjs_bin (String)

    the location of the PhantomJS binary

  • :timeout (Integer)

    the maximum time in seconds to wait for the spec runner to finish

  • :rackup_config (String)

    custom rackup config to use

  • :notification (Boolean)

    show notifications

  • :hide_success (Boolean)

    hide success message notification

  • :max_error_notify (Integer)

    maximum error notifications to show

  • :specdoc (Symbol)

    options for the specdoc output, either :always, :never

  • :console (Symbol)

    options for the console.log output, either :always, :never or :failure

  • :spec_dir (String)

    the directory with the Jasmine specs

Returns:

  • (Boolean, Array<String>)

    the status of the run and the failed files



38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/guard/jasmine/runner.rb', line 38

def run(paths, options = { })
  return [false, []] if paths.empty?

  notify_start_message(paths, options)

  results = paths.inject([]) do |results, file|
    results << evaluate_response(run_jasmine_spec(file, options), file, options) if File.exist?(file_and_line_number_parts(file)[0])

    results
  end.compact

  [response_status_for(results), failed_paths_from(results)]
end

.run_jasmine_spec(file, options) ⇒ Object (private)

Run the Jasmine spec by executing the PhantomJS script.

Parameters:

  • file (String)

    the path of the spec

  • options (Hash)

    the options for the execution

Options Hash (options):

  • :timeout (Integer)

    the maximum time in seconds to wait for the spec runner to finish



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/guard/jasmine/runner.rb', line 94

def run_jasmine_spec(file, options)
  suite = jasmine_suite(file, options)
  Formatter.info("Run Jasmine suite at #{ suite }")

  arguments = [
    options[:timeout] * 1000,
    options[:specdoc],
    options[:focus],
    options[:console],
    options[:errors],
    options[:junit],
    options[:junit_consolidate],
    "'#{ options[:junit_save_path] }'"
  ]

  IO.popen("#{ phantomjs_command(options) } \"#{ suite }\" #{ arguments.collect { |i| i.to_s }.join(' ')}", 'r:UTF-8')
end

.spec_title(line) ⇒ String (private)

Extracts the title of a 'description' or a 'it' declaration.

Parameters:

  • the (String)

    line content

Returns:

  • (String)

    the extracted title



243
244
245
# File 'lib/guard/jasmine/runner.rb', line 243

def spec_title(line)
  line[/['"](.+?)['"]/, 1]
end

.specdoc_shown?(passed, options = { }) ⇒ Boolean (private)

Is the specdoc shown for this suite?

Parameters:

  • passed (Boolean)

    the spec status

  • options (Hash) (defaults to: { })

    the options

Returns:

  • (Boolean)


505
506
507
# File 'lib/guard/jasmine/runner.rb', line 505

def specdoc_shown?(passed, options = { })
  options[:specdoc] == :always || (options[:specdoc] == :failure && !passed)
end

.update_coverage(coverage, file, options) ⇒ Object (private)

Updates the coverage data with new data for the implementation file. It replaces the coverage data if the file is the spec dir.

Parameters:

  • coverage (Hash)

    the last run coverage data

  • file (String)

    the file name of the spec

  • options (Hash)

    the options for the execution



687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
# File 'lib/guard/jasmine/runner.rb', line 687

def update_coverage(coverage, file, options)
  if file == options[:spec_dir]
    File.write(coverage_file, MultiJson.encode(coverage, { max_nesting: false }))
  else
    if File.exist?(coverage_file)
      impl     = file.sub('_spec', '').sub(options[:spec_dir], '')
      coverage = MultiJson.decode(File.read(coverage_file), { max_nesting: false })

      coverage.each do |coverage_file, data|
        coverage[coverage_file] = data if coverage_file == impl
      end

      File.write(coverage_file, MultiJson.encode(coverage, { max_nesting: false }))
    else
      File.write(coverage_file, MultiJson.encode({ }))
    end
  end
end