Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

show line numbers for spec failures, whoa nelly

  • Loading branch information...
commit 3c4e557517a5ab7c5c296023e85efd759573a516 1 parent 05e7d07
John Bintz johnbintz authored
14 ext/jasmine-webkit-specrunner/specrunner.cpp
@@ -72,7 +72,7 @@ class HeadlessSpecRunner: public QObject
72 72 public slots:
73 73 void log(const QString &msg);
74 74 void specPassed();
75   - void specFailed();
  75 + void specFailed(const QString &specDetail);
76 76 void printName(const QString &name);
77 77 void printResult(const QString &result);
78 78 void finishSuite(const QString &duration, const QString &total, const QString& failed);
@@ -96,6 +96,7 @@ private slots:
96 96 bool consoleNotUsedThisRun;
97 97 QQueue<QString> runnerFiles;
98 98 QString reportFilename;
  99 + QStack<QString> failedSpecs;
99 100
100 101 void red();
101 102 void green();
@@ -201,12 +202,13 @@ void HeadlessSpecRunner::specPassed()
201 202 fflush(stdout);
202 203 }
203 204
204   -void HeadlessSpecRunner::specFailed()
  205 +void HeadlessSpecRunner::specFailed(const QString &specDetail)
205 206 {
206 207 consoleNotUsedThisRun = true;
207 208 didFail = true;
208 209 red();
209 210 std::cout << 'F';
  211 + failedSpecs.push(specDetail);
210 212 clear();
211 213 fflush(stdout);
212 214 }
@@ -292,6 +294,14 @@ void HeadlessSpecRunner::finishSuite(const QString &duration, const QString &tot
292 294 report << qPrintable(total) << "/" << qPrintable(failed) << "/";
293 295 report << (usedConsole ? "T" : "F");
294 296 report << "/" << qPrintable(duration) << "\n";
  297 +
  298 + QString failedSpec;
  299 +
  300 + while (!failedSpecs.isEmpty()) {
  301 + failedSpec = failedSpecs.pop();
  302 + report << qPrintable(failedSpec) << "\n";
  303 + }
  304 +
295 305 reportFH.close();
296 306 }
297 307 }
1  jasmine-headless-webkit.gemspec
@@ -23,4 +23,5 @@ Gem::Specification.new do |s|
23 23 s.add_dependency 'jasmine', '~>1.1.beta'
24 24 s.add_dependency 'coffee-script', '>= 2.2'
25 25 s.add_dependency 'rainbow'
  26 + s.add_dependency 'multi_json'
26 27 end
36 jasmine/jasmine.headless-reporter.coffee
@@ -2,16 +2,42 @@ if !jasmine?
2 2 throw new Error("jasmine not laoded!")
3 3
4 4 class HeadlessReporterResult
5   - constructor: (name) ->
6   - @name = name
  5 + constructor: (@name, @splitName) ->
7 6 @results = []
8 7 addResult: (message) ->
9 8 @results.push(message)
10 9 print: ->
11   - JHW.printName(@name)
  10 + output = @name
  11 + bestChoice = this._findSpecLine()
  12 + output += " (#{bestChoice.file}:#{bestChoice.lineNumber})" if bestChoice.file
  13 +
  14 + JHW.printName(output)
12 15 for result in @results
13 16 do (result) =>
14 17 JHW.printResult(result)
  18 + _findSpecLine: ->
  19 + bestChoice = { accuracy: 0, file: null, lineNumber: null }
  20 +
  21 + for file, lines of SPEC_LINE_NUMBERS
  22 + index = 0
  23 + while newLineNumber = lines[@splitName[index]]
  24 + index++
  25 + lineNumber = newLineNumber
  26 +
  27 + if index > bestChoice.accuracy
  28 + bestChoice = { accuracy: index, file: file, lineNumber: lineNumber }
  29 +
  30 + bestChoice
  31 +
  32 +jasmine.Suite.prototype.getSuiteSplitName = ->
  33 + parts = if @parentSuite then @parentSuite.getSuiteSplitName() else []
  34 + parts.push(@description)
  35 + parts
  36 +
  37 +jasmine.Spec.prototype.getSpecSplitName = ->
  38 + parts = @suite.getSuiteSplitName()
  39 + parts.push(@description)
  40 + parts
15 41
16 42 class jasmine.HeadlessReporter
17 43 constructor: ->
@@ -32,9 +58,9 @@ class jasmine.HeadlessReporter
32 58 if results.passed()
33 59 JHW.specPassed()
34 60 else
35   - JHW.specFailed()
  61 + JHW.specFailed(spec.getSpecSplitName().join('||'))
36 62 @failedCount++
37   - failureResult = new HeadlessReporterResult(spec.getFullName())
  63 + failureResult = new HeadlessReporterResult(spec.getFullName(), spec.getSpecSplitName())
38 64 for result in results.getItems()
39 65 do (result) =>
40 66 if result.type == 'expect' and !result.passed_
52 jasmine/jasmine.headless-reporter.js
@@ -5,16 +5,22 @@
5 5 throw new Error("jasmine not laoded!");
6 6 }
7 7 HeadlessReporterResult = (function() {
8   - function HeadlessReporterResult(name) {
  8 + function HeadlessReporterResult(name, splitName) {
9 9 this.name = name;
  10 + this.splitName = splitName;
10 11 this.results = [];
11 12 }
12 13 HeadlessReporterResult.prototype.addResult = function(message) {
13 14 return this.results.push(message);
14 15 };
15 16 HeadlessReporterResult.prototype.print = function() {
16   - var result, _i, _len, _ref, _results;
17   - JHW.printName(this.name);
  17 + var bestChoice, output, result, _i, _len, _ref, _results;
  18 + output = this.name;
  19 + bestChoice = this._findSpecLine();
  20 + if (bestChoice.file) {
  21 + output += " (" + bestChoice.file + ":" + bestChoice.lineNumber + ")";
  22 + }
  23 + JHW.printName(output);
18 24 _ref = this.results;
19 25 _results = [];
20 26 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -25,8 +31,44 @@
25 31 }
26 32 return _results;
27 33 };
  34 + HeadlessReporterResult.prototype._findSpecLine = function() {
  35 + var bestChoice, file, index, lineNumber, lines, newLineNumber;
  36 + bestChoice = {
  37 + accuracy: 0,
  38 + file: null,
  39 + lineNumber: null
  40 + };
  41 + for (file in SPEC_LINE_NUMBERS) {
  42 + lines = SPEC_LINE_NUMBERS[file];
  43 + index = 0;
  44 + while (newLineNumber = lines[this.splitName[index]]) {
  45 + index++;
  46 + lineNumber = newLineNumber;
  47 + }
  48 + if (index > bestChoice.accuracy) {
  49 + bestChoice = {
  50 + accuracy: index,
  51 + file: file,
  52 + lineNumber: lineNumber
  53 + };
  54 + }
  55 + }
  56 + return bestChoice;
  57 + };
28 58 return HeadlessReporterResult;
29 59 })();
  60 + jasmine.Suite.prototype.getSuiteSplitName = function() {
  61 + var parts;
  62 + parts = this.parentSuite ? this.parentSuite.getSuiteSplitName() : [];
  63 + parts.push(this.description);
  64 + return parts;
  65 + };
  66 + jasmine.Spec.prototype.getSpecSplitName = function() {
  67 + var parts;
  68 + parts = this.suite.getSuiteSplitName();
  69 + parts.push(this.description);
  70 + return parts;
  71 + };
30 72 jasmine.HeadlessReporter = (function() {
31 73 function HeadlessReporter() {
32 74 this.results = [];
@@ -55,9 +97,9 @@
55 97 if (results.passed()) {
56 98 return JHW.specPassed();
57 99 } else {
58   - JHW.specFailed();
  100 + JHW.specFailed(spec.getSpecSplitName().join('||'));
59 101 this.failedCount++;
60   - failureResult = new HeadlessReporterResult(spec.getFullName());
  102 + failureResult = new HeadlessReporterResult(spec.getFullName(), spec.getSpecSplitName());
61 103 _ref = results.getItems();
62 104 _fn = __bind(function(result) {
63 105 if (result.type === 'expect' && !result.passed_) {
27 lib/jasmine/files_list.rb
@@ -19,11 +19,22 @@ class FilesList
19 19 File.expand_path('../../../jasmine/jasmine.headless-reporter.js', __FILE__)
20 20 ]
21 21
  22 + class << self
  23 + def get_spec_line_numbers(file)
  24 + Hash[file.lines.each_with_index.collect { |line, index|
  25 + if description = line[%r{(describe|context|it)[( ]*(["'])(.*)\2}, 3]
  26 + [ description, index + 1 ]
  27 + end
  28 + }.compact]
  29 + end
  30 + end
  31 +
22 32 def initialize(options = {})
23 33 @options = options
24 34 @files = DEFAULT_FILES.dup
25 35 @filtered_files = @files.dup
26 36 @spec_outside_scope = false
  37 + @spec_files = []
27 38 use_config! if config?
28 39
29 40 @code_for_file = {}
@@ -45,6 +56,18 @@ def filtered_files_to_html
45 56 to_html(filtered_files)
46 57 end
47 58
  59 + def spec_file_line_numbers
  60 + @spec_file_line_numbers ||= Hash[@spec_files.collect { |file|
  61 + if File.exist?(file)
  62 + if !(lines = self.class.get_spec_line_numbers(File.read(file))).empty?
  63 + [ file, lines ]
  64 + end
  65 + else
  66 + nil
  67 + end
  68 + }.compact]
  69 + end
  70 +
48 71 private
49 72 def to_html(files)
50 73 coffeescript_run = []
@@ -115,6 +138,10 @@ def use_config!
115 138
116 139 @files += found_files
117 140
  141 + if searches == 'spec_files'
  142 + @spec_files = @files + spec_filter
  143 + end
  144 +
118 145 @filtered_files += (if searches == 'spec_files'
119 146 @spec_outside_scope = ((spec_filter | found_files).sort != found_files.sort)
120 147 spec_filter.empty? ? found_files : (spec_filter || found_files)
8 lib/jasmine/template_writer.rb
... ... @@ -1,4 +1,5 @@
1 1 require 'jasmine/files_list'
  2 +require 'multi_json'
2 3
3 4 module Jasmine
4 5 class TemplateWriter
@@ -11,14 +12,14 @@ def write!(files_list)
11 12 output.unshift([ "specrunner.#{$$}.filter.html", files_list.filtered_files_to_html ]) if files_list.filtered?
12 13
13 14 output.each do |name, files|
14   - File.open(name, 'w') { |fh| fh.print template_for(files) }
  15 + File.open(name, 'w') { |fh| fh.print template_for(files, files_list.spec_file_line_numbers) }
15 16 end
16 17
17 18 output.collect(&:first)
18 19 end
19 20
20 21 private
21   - def template_for(files)
  22 + def template_for(files, spec_lines)
22 23 <<-HTML
23 24 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
24 25 <html>
@@ -32,6 +33,9 @@ def template_for(files)
32 33 } };
33 34 </script>
34 35 #{files.join("\n")}
  36 + <script type="text/javascript">
  37 +SPEC_LINE_NUMBERS = #{MultiJson.encode(spec_lines)};
  38 + </script>
35 39 </head>
36 40 <body>
37 41
63 spec/lib/jasmine/files_list_spec.rb
@@ -206,5 +206,68 @@
206 206 end
207 207 end
208 208 end
  209 +
  210 + describe '.get_spec_line_numbers' do
  211 + let(:line_numbers) do
  212 + described_class.get_spec_line_numbers(file)
  213 + end
  214 +
  215 + context 'coffeescript' do
  216 + let(:file) do
  217 + <<-SPEC
  218 +describe 'test', ->
  219 + context 'yes', ->
  220 + it 'should do something', ->
  221 + "yes"
  222 + SPEC
  223 + end
  224 +
  225 + it 'should get the line numbers' do
  226 + line_numbers['test'].should == 1
  227 + line_numbers['yes'].should == 2
  228 + line_numbers['should do something'].should == 3
  229 + end
  230 + end
  231 +
  232 + context 'javascript' do
  233 + let(:file) do
  234 + <<-SPEC
  235 +describe('test', function() {
  236 + context('yes', function() {
  237 + it('should do something', function() {
  238 +
  239 + });
  240 + });
  241 +});
  242 + SPEC
  243 + end
  244 +
  245 + it 'should get the line numbers' do
  246 + line_numbers['test'].should == 1
  247 + line_numbers['yes'].should == 2
  248 + line_numbers['should do something'].should == 3
  249 + end
  250 + end
  251 + end
  252 +
  253 + describe '#spec_file_line_numbers' do
  254 + include FakeFS::SpecHelpers
  255 +
  256 + before do
  257 + files_list.instance_variable_set(:@spec_files, [
  258 + 'test.coffee',
  259 + 'test2.coffee'
  260 + ])
  261 +
  262 + File.open('test.coffee', 'w') { |fh| fh.print "describe('cat')" }
  263 + File.open('test2.coffee', 'w') { |fh| fh.print "no matches" }
  264 + end
  265 +
  266 + it 'should generate filenames and line number info' do
  267 + files_list.spec_file_line_numbers.should == {
  268 + 'test.coffee' => { 'cat' => 1 }
  269 + }
  270 + end
  271 + end
209 272 end
210 273
10 spec/lib/jasmine/headless/runner_spec.rb
@@ -68,5 +68,15 @@
68 68 it 'should succeed but with javascript error' do
69 69 Jasmine::Headless::Runner.run(:jasmine_config => 'spec/jasmine/success_with_error/success_with_error.yml').should == 1
70 70 end
  71 +
  72 + it 'should fail on one test' do
  73 + Jasmine::Headless::Runner.run(
  74 + :jasmine_config => 'spec/jasmine/failure/failure.yml',
  75 + :report => report
  76 + ).should == 1
  77 +
  78 + report.should be_a_report_containing(1, 1, false)
  79 + report.should contain_a_failing_spec(['failure', 'should fail with error code of 1'])
  80 + end
71 81 end
72 82 end
12 spec/spec_helper.rb
@@ -25,7 +25,17 @@
25 25 end
26 26
27 27 def parts(filename = nil)
28   - @parts ||= File.read(filename).strip.split('/')
  28 + @parts ||= File.readlines(filename).first.strip.split('/')
  29 + end
  30 +end
  31 +
  32 +RSpec::Matchers.define :contain_a_failing_spec do |*parts|
  33 + match do |filename|
  34 + report(filename).include?(parts.join("||")).should be_true
  35 + end
  36 +
  37 + def report(filename)
  38 + @report ||= File.readlines(filename)[1..-1].collect(&:strip)
29 39 end
30 40 end
31 41

0 comments on commit 3c4e557

Please sign in to comment.
Something went wrong with that request. Please try again.