show line numbers for spec failures, whoa nelly
This commit is contained in:
parent
05e7d07d40
commit
3c4e557517
@ -72,7 +72,7 @@ public:
|
||||
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:
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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_
|
||||
|
@ -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_) {
|
||||
|
@ -19,11 +19,22 @@ module Jasmine
|
||||
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 @@ module Jasmine
|
||||
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 @@ module Jasmine
|
||||
|
||||
@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)
|
||||
|
@ -1,4 +1,5 @@
|
||||
require 'jasmine/files_list'
|
||||
require 'multi_json'
|
||||
|
||||
module Jasmine
|
||||
class TemplateWriter
|
||||
@ -11,14 +12,14 @@ module Jasmine
|
||||
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 @@ module Jasmine
|
||||
} };
|
||||
</script>
|
||||
#{files.join("\n")}
|
||||
<script type="text/javascript">
|
||||
SPEC_LINE_NUMBERS = #{MultiJson.encode(spec_lines)};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -206,5 +206,68 @@ describe Jasmine::FilesList do
|
||||
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
|
||||
|
||||
|
@ -68,5 +68,15 @@ describe Jasmine::Headless::Runner do
|
||||
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
|
||||
|
@ -25,7 +25,17 @@ RSpec::Matchers.define :be_a_report_containing do |total, fails, used_console|
|
||||
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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user