Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

show line numbers for spec failures, whoa nelly

  • Loading branch information...
commit 3c4e557517a5ab7c5c296023e85efd759573a516 1 parent 05e7d07
@johnbintz johnbintz authored
View
14 ext/jasmine-webkit-specrunner/specrunner.cpp
@@ -72,7 +72,7 @@ class HeadlessSpecRunner: public QObject
public slots:
void log(const QString &msg);
void specPassed();
- void specFailed();
+ void specFailed(const QString &specDetail);
void printName(const QString &name);
void printResult(const QString &result);
void finishSuite(const QString &duration, const QString &total, const QString& failed);
@@ -96,6 +96,7 @@ private slots:
bool consoleNotUsedThisRun;
QQueue<QString> runnerFiles;
QString reportFilename;
+ QStack<QString> failedSpecs;
void red();
void green();
@@ -201,12 +202,13 @@ void HeadlessSpecRunner::specPassed()
fflush(stdout);
}
-void HeadlessSpecRunner::specFailed()
+void HeadlessSpecRunner::specFailed(const QString &specDetail)
{
consoleNotUsedThisRun = true;
didFail = true;
red();
std::cout << 'F';
+ failedSpecs.push(specDetail);
clear();
fflush(stdout);
}
@@ -292,6 +294,14 @@ void HeadlessSpecRunner::finishSuite(const QString &duration, const QString &tot
report << qPrintable(total) << "/" << qPrintable(failed) << "/";
report << (usedConsole ? "T" : "F");
report << "/" << qPrintable(duration) << "\n";
+
+ QString failedSpec;
+
+ while (!failedSpecs.isEmpty()) {
+ failedSpec = failedSpecs.pop();
+ report << qPrintable(failedSpec) << "\n";
+ }
+
reportFH.close();
}
}
View
1  jasmine-headless-webkit.gemspec
@@ -23,4 +23,5 @@ Gem::Specification.new do |s|
s.add_dependency 'jasmine', '~>1.1.beta'
s.add_dependency 'coffee-script', '>= 2.2'
s.add_dependency 'rainbow'
+ s.add_dependency 'multi_json'
end
View
36 jasmine/jasmine.headless-reporter.coffee
@@ -2,16 +2,42 @@ if !jasmine?
throw new Error("jasmine not laoded!")
class HeadlessReporterResult
- constructor: (name) ->
- @name = name
+ constructor: (@name, @splitName) ->
@results = []
addResult: (message) ->
@results.push(message)
print: ->
- JHW.printName(@name)
+ output = @name
+ bestChoice = this._findSpecLine()
+ output += " (#{bestChoice.file}:#{bestChoice.lineNumber})" if bestChoice.file
+
+ JHW.printName(output)
for result in @results
do (result) =>
JHW.printResult(result)
+ _findSpecLine: ->
+ bestChoice = { accuracy: 0, file: null, lineNumber: null }
+
+ for file, lines of SPEC_LINE_NUMBERS
+ index = 0
+ while newLineNumber = lines[@splitName[index]]
+ index++
+ lineNumber = newLineNumber
+
+ if index > bestChoice.accuracy
+ bestChoice = { accuracy: index, file: file, lineNumber: lineNumber }
+
+ bestChoice
+
+jasmine.Suite.prototype.getSuiteSplitName = ->
+ parts = if @parentSuite then @parentSuite.getSuiteSplitName() else []
+ parts.push(@description)
+ parts
+
+jasmine.Spec.prototype.getSpecSplitName = ->
+ parts = @suite.getSuiteSplitName()
+ parts.push(@description)
+ parts
class jasmine.HeadlessReporter
constructor: ->
@@ -32,9 +58,9 @@ class jasmine.HeadlessReporter
if results.passed()
JHW.specPassed()
else
- JHW.specFailed()
+ JHW.specFailed(spec.getSpecSplitName().join('||'))
@failedCount++
- failureResult = new HeadlessReporterResult(spec.getFullName())
+ failureResult = new HeadlessReporterResult(spec.getFullName(), spec.getSpecSplitName())
for result in results.getItems()
do (result) =>
if result.type == 'expect' and !result.passed_
View
52 jasmine/jasmine.headless-reporter.js
@@ -5,16 +5,22 @@
throw new Error("jasmine not laoded!");
}
HeadlessReporterResult = (function() {
- function HeadlessReporterResult(name) {
+ function HeadlessReporterResult(name, splitName) {
this.name = name;
+ this.splitName = splitName;
this.results = [];
}
HeadlessReporterResult.prototype.addResult = function(message) {
return this.results.push(message);
};
HeadlessReporterResult.prototype.print = function() {
- var result, _i, _len, _ref, _results;
- JHW.printName(this.name);
+ var bestChoice, output, result, _i, _len, _ref, _results;
+ output = this.name;
+ bestChoice = this._findSpecLine();
+ if (bestChoice.file) {
+ output += " (" + bestChoice.file + ":" + bestChoice.lineNumber + ")";
+ }
+ JHW.printName(output);
_ref = this.results;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -25,8 +31,44 @@
}
return _results;
};
+ HeadlessReporterResult.prototype._findSpecLine = function() {
+ var bestChoice, file, index, lineNumber, lines, newLineNumber;
+ bestChoice = {
+ accuracy: 0,
+ file: null,
+ lineNumber: null
+ };
+ for (file in SPEC_LINE_NUMBERS) {
+ lines = SPEC_LINE_NUMBERS[file];
+ index = 0;
+ while (newLineNumber = lines[this.splitName[index]]) {
+ index++;
+ lineNumber = newLineNumber;
+ }
+ if (index > bestChoice.accuracy) {
+ bestChoice = {
+ accuracy: index,
+ file: file,
+ lineNumber: lineNumber
+ };
+ }
+ }
+ return bestChoice;
+ };
return HeadlessReporterResult;
})();
+ jasmine.Suite.prototype.getSuiteSplitName = function() {
+ var parts;
+ parts = this.parentSuite ? this.parentSuite.getSuiteSplitName() : [];
+ parts.push(this.description);
+ return parts;
+ };
+ jasmine.Spec.prototype.getSpecSplitName = function() {
+ var parts;
+ parts = this.suite.getSuiteSplitName();
+ parts.push(this.description);
+ return parts;
+ };
jasmine.HeadlessReporter = (function() {
function HeadlessReporter() {
this.results = [];
@@ -55,9 +97,9 @@
if (results.passed()) {
return JHW.specPassed();
} else {
- JHW.specFailed();
+ JHW.specFailed(spec.getSpecSplitName().join('||'));
this.failedCount++;
- failureResult = new HeadlessReporterResult(spec.getFullName());
+ failureResult = new HeadlessReporterResult(spec.getFullName(), spec.getSpecSplitName());
_ref = results.getItems();
_fn = __bind(function(result) {
if (result.type === 'expect' && !result.passed_) {
View
27 lib/jasmine/files_list.rb
@@ -19,11 +19,22 @@ class FilesList
File.expand_path('../../../jasmine/jasmine.headless-reporter.js', __FILE__)
]
+ class << self
+ def get_spec_line_numbers(file)
+ Hash[file.lines.each_with_index.collect { |line, index|
+ if description = line[%r{(describe|context|it)[( ]*(["'])(.*)\2}, 3]
+ [ description, index + 1 ]
+ end
+ }.compact]
+ end
+ end
+
def initialize(options = {})
@options = options
@files = DEFAULT_FILES.dup
@filtered_files = @files.dup
@spec_outside_scope = false
+ @spec_files = []
use_config! if config?
@code_for_file = {}
@@ -45,6 +56,18 @@ def filtered_files_to_html
to_html(filtered_files)
end
+ def spec_file_line_numbers
+ @spec_file_line_numbers ||= Hash[@spec_files.collect { |file|
+ if File.exist?(file)
+ if !(lines = self.class.get_spec_line_numbers(File.read(file))).empty?
+ [ file, lines ]
+ end
+ else
+ nil
+ end
+ }.compact]
+ end
+
private
def to_html(files)
coffeescript_run = []
@@ -115,6 +138,10 @@ def use_config!
@files += found_files
+ if searches == 'spec_files'
+ @spec_files = @files + spec_filter
+ end
+
@filtered_files += (if searches == 'spec_files'
@spec_outside_scope = ((spec_filter | found_files).sort != found_files.sort)
spec_filter.empty? ? found_files : (spec_filter || found_files)
View
8 lib/jasmine/template_writer.rb
@@ -1,4 +1,5 @@
require 'jasmine/files_list'
+require 'multi_json'
module Jasmine
class TemplateWriter
@@ -11,14 +12,14 @@ def write!(files_list)
output.unshift([ "specrunner.#{$$}.filter.html", files_list.filtered_files_to_html ]) if files_list.filtered?
output.each do |name, files|
- File.open(name, 'w') { |fh| fh.print template_for(files) }
+ File.open(name, 'w') { |fh| fh.print template_for(files, files_list.spec_file_line_numbers) }
end
output.collect(&:first)
end
private
- def template_for(files)
+ def template_for(files, spec_lines)
<<-HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
@@ -32,6 +33,9 @@ def template_for(files)
} };
</script>
#{files.join("\n")}
+ <script type="text/javascript">
+SPEC_LINE_NUMBERS = #{MultiJson.encode(spec_lines)};
+ </script>
</head>
<body>
View
63 spec/lib/jasmine/files_list_spec.rb
@@ -206,5 +206,68 @@
end
end
end
+
+ describe '.get_spec_line_numbers' do
+ let(:line_numbers) do
+ described_class.get_spec_line_numbers(file)
+ end
+
+ context 'coffeescript' do
+ let(:file) do
+ <<-SPEC
+describe 'test', ->
+ context 'yes', ->
+ it 'should do something', ->
+ "yes"
+ SPEC
+ end
+
+ it 'should get the line numbers' do
+ line_numbers['test'].should == 1
+ line_numbers['yes'].should == 2
+ line_numbers['should do something'].should == 3
+ end
+ end
+
+ context 'javascript' do
+ let(:file) do
+ <<-SPEC
+describe('test', function() {
+ context('yes', function() {
+ it('should do something', function() {
+
+ });
+ });
+});
+ SPEC
+ end
+
+ it 'should get the line numbers' do
+ line_numbers['test'].should == 1
+ line_numbers['yes'].should == 2
+ line_numbers['should do something'].should == 3
+ end
+ end
+ end
+
+ describe '#spec_file_line_numbers' do
+ include FakeFS::SpecHelpers
+
+ before do
+ files_list.instance_variable_set(:@spec_files, [
+ 'test.coffee',
+ 'test2.coffee'
+ ])
+
+ File.open('test.coffee', 'w') { |fh| fh.print "describe('cat')" }
+ File.open('test2.coffee', 'w') { |fh| fh.print "no matches" }
+ end
+
+ it 'should generate filenames and line number info' do
+ files_list.spec_file_line_numbers.should == {
+ 'test.coffee' => { 'cat' => 1 }
+ }
+ end
+ end
end
View
10 spec/lib/jasmine/headless/runner_spec.rb
@@ -68,5 +68,15 @@
it 'should succeed but with javascript error' do
Jasmine::Headless::Runner.run(:jasmine_config => 'spec/jasmine/success_with_error/success_with_error.yml').should == 1
end
+
+ it 'should fail on one test' do
+ Jasmine::Headless::Runner.run(
+ :jasmine_config => 'spec/jasmine/failure/failure.yml',
+ :report => report
+ ).should == 1
+
+ report.should be_a_report_containing(1, 1, false)
+ report.should contain_a_failing_spec(['failure', 'should fail with error code of 1'])
+ end
end
end
View
12 spec/spec_helper.rb
@@ -25,7 +25,17 @@
end
def parts(filename = nil)
- @parts ||= File.read(filename).strip.split('/')
+ @parts ||= File.readlines(filename).first.strip.split('/')
+ end
+end
+
+RSpec::Matchers.define :contain_a_failing_spec do |*parts|
+ match do |filename|
+ report(filename).include?(parts.join("||")).should be_true
+ end
+
+ def report(filename)
+ @report ||= File.readlines(filename)[1..-1].collect(&:strip)
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.