rspec 0.5.12 → 0.5.13
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +9 -1
- data/README +2 -2
- data/Rakefile +13 -5
- data/examples/custom_formatter.rb +2 -2
- data/examples/file_accessor.rb +18 -0
- data/examples/file_accessor_spec.rb +38 -0
- data/examples/io_processor.rb +8 -0
- data/examples/io_processor_spec.rb +21 -0
- data/lib/spec/api/exceptions.rb +3 -0
- data/lib/spec/api/mocks/message_expectation.rb +45 -29
- data/lib/spec/api/mocks/mock.rb +1 -1
- data/lib/spec/api/mocks/order_group.rb +1 -1
- data/lib/spec/runner.rb +1 -5
- data/lib/spec/runner/formatter.rb +5 -0
- data/lib/spec/runner/formatter/base_text_formatter.rb +79 -0
- data/lib/spec/runner/formatter/html_formatter.rb +156 -0
- data/lib/spec/runner/formatter/progress_bar_formatter.rb +27 -0
- data/lib/spec/runner/formatter/rdoc_formatter.rb +22 -0
- data/lib/spec/runner/formatter/specdoc_formatter.rb +22 -0
- data/lib/spec/runner/option_parser.rb +17 -17
- data/lib/spec/runner/reporter.rb +1 -1
- data/lib/spec/version.rb +1 -1
- data/test/spec/api/helper/arbitrary_predicate_test.rb +38 -38
- data/test/spec/api/helper/diff_test.rb +1 -1
- data/test/spec/api/helper/identity_test.rb +17 -10
- data/test/spec/api/helper/{equality_test.rb → object_equality_test.rb} +15 -29
- data/test/spec/api/helper/regex_matching_test.rb +7 -9
- data/test/spec/api/helper/throwing_test.rb +11 -12
- data/test/spec/api/helper/true_false_special_case_test.rb +15 -17
- data/test/spec/api/helper/typing_test.rb +27 -26
- data/test/spec/api/mocks/mock_arg_constraints_test.rb +1 -1
- data/test/spec/api/mocks/mock_test.rb +45 -11
- data/test/spec/api/mocks/null_object_test.rb +3 -3
- data/test/spec/runner/context_matching_test.rb +2 -2
- data/test/spec/runner/formatter/failure_dump_test.rb +94 -0
- data/test/spec/runner/formatter/html_formatter_test.rb +48 -0
- data/test/spec/runner/formatter/progress_bar_formatter_test.rb +56 -0
- data/test/spec/runner/formatter/rdoc_formatter_test.rb +51 -0
- data/test/spec/runner/formatter/specdoc_formatter_test.rb +57 -0
- data/test/spec/runner/kernel_ext_test.rb +1 -1
- data/test/spec/runner/option_parser_test.rb +22 -12
- data/test/spec/runner/reporter_test.rb +1 -1
- data/test/test_classes.rb +7 -7
- metadata +19 -14
- data/lib/spec/runner/base_text_formatter.rb +0 -77
- data/lib/spec/runner/html_formatter.rb +0 -153
- data/lib/spec/runner/progress_bar_formatter.rb +0 -25
- data/lib/spec/runner/rdoc_formatter.rb +0 -20
- data/lib/spec/runner/specdoc_formatter.rb +0 -20
- data/test/spec/runner/failure_dump_test.rb +0 -92
- data/test/spec/runner/html_formatter_test.rb +0 -47
- data/test/spec/runner/progress_bar_formatter_test.rb +0 -54
- data/test/spec/runner/rdoc_formatter_test.rb +0 -50
- data/test/spec/runner/specdoc_formatter_test.rb +0 -55
data/CHANGES
CHANGED
@@ -1,12 +1,20 @@
|
|
1
1
|
= RSpec Changelog
|
2
2
|
|
3
|
+
== Version 0.5.13
|
4
|
+
This release fixes some subtle bugs in the mock API.
|
5
|
+
|
6
|
+
* Use fully-qualified class name of Exceptions in failure message. Easier to debug that way.
|
7
|
+
* Fixed a bug that caused mocks to yield a one-element array (rather than the element) when one yield arg specified.
|
8
|
+
* Mocks not raise AmbiguousReturnError if an explicit return is used at the same time as an expectation block.
|
9
|
+
* Blocks passed to yielding mocks can now raise without causing mock verification to fail.
|
10
|
+
|
3
11
|
== Version 0.5.12
|
4
12
|
This release adds diff support for failure messages, a HTML formatter plus some other
|
5
13
|
minor enhancements.
|
6
14
|
|
7
15
|
* Added HTML formatter.
|
8
16
|
* Added fail_on_error option to spectask.
|
9
|
-
* Added support for diffing, using the lcs
|
17
|
+
* Added support for diffing, using the diff-lcs Rubygem (#2648).
|
10
18
|
* Remove RSpec on Rails files from backtrace (#4694).
|
11
19
|
* All of RSpec's own tests run successfully after translation with test2spec.
|
12
20
|
* Added --verbose mode for test2spec - useful for debugging when classes fail to translate.
|
data/README
CHANGED
@@ -8,12 +8,12 @@ Then you must install the following gems:
|
|
8
8
|
* webgen
|
9
9
|
* RedCloth
|
10
10
|
* syntax
|
11
|
-
* lcs
|
11
|
+
* diff-lcs
|
12
12
|
|
13
13
|
Note that RSpec itself - once built - doesn't have any dependencies outside the Ruby core
|
14
14
|
and stdlib - with a few exceptions:
|
15
15
|
|
16
|
-
* The spec command line uses lcs
|
16
|
+
* The spec command line uses diff-lcs when --diff is specified.
|
17
17
|
* The test2spec command line uses ParseTree and RubyInline.
|
18
18
|
* The Spec::Rake::SpecTask needs RCov if RCov is enabled in the task.
|
19
19
|
|
data/Rakefile
CHANGED
@@ -32,8 +32,12 @@ Spec::Rake::SpecTask.new('failing_examples') do |t|
|
|
32
32
|
t.spec_files = FileList['failing_examples/**/*_spec.rb']
|
33
33
|
end
|
34
34
|
|
35
|
+
require 'rbconfig'
|
36
|
+
windows = Config::CONFIG['target_os'] == 'mswin32'
|
35
37
|
Rake::TestTask.new do |t|
|
36
|
-
|
38
|
+
tests = FileList['test/**/*_test.rb']
|
39
|
+
tests.exclude 'test/spec/test_to_spec/*.rb' if windows
|
40
|
+
t.test_files = tests
|
37
41
|
t.verbose = true
|
38
42
|
end
|
39
43
|
|
@@ -44,21 +48,25 @@ Rcov::RcovTask.new do |t|
|
|
44
48
|
end
|
45
49
|
|
46
50
|
desc 'Translate our own tests to specs'
|
47
|
-
task :test2spec do
|
51
|
+
task :test2spec => :create_test2spec_dir do
|
48
52
|
rm_rf 'spec/translated'
|
49
53
|
`bin/test2spec --force --template spec/test2spec.erb --specdir spec/translated test`
|
50
54
|
# Remove the spec translations that we don't care about.
|
51
55
|
rm 'spec/translated/spec/test_to_spec/sexp_transformer_assertion_spec.rb'
|
52
56
|
rm 'spec/translated/spec/test_to_spec/sexp_transformer_spec.rb'
|
53
57
|
end
|
58
|
+
task :create_test2spec_dir do
|
59
|
+
mkdir_p 'doc/output/tools' unless File.exist? 'doc/output/tools'
|
60
|
+
end
|
54
61
|
|
55
62
|
desc 'Runs all RSpec specs - translated with test2spec from our own tests'
|
56
63
|
Spec::Rake::SpecTask.new('test2spec_test' => :test2spec) do |t|
|
57
64
|
t.spec_files = FileList['spec/**/*_spec.rb']
|
58
|
-
t.
|
65
|
+
t.spec_opts = ["--format", "html", "--diff"]
|
66
|
+
t.out = 'doc/output/tools/rspec_specs.html'
|
59
67
|
end
|
60
68
|
|
61
|
-
desc 'Generate HTML documentation'
|
69
|
+
desc 'Generate HTML documentation for website'
|
62
70
|
task :webgen => :test2spec do
|
63
71
|
Dir.chdir 'doc' do
|
64
72
|
output = nil
|
@@ -155,7 +163,7 @@ task :tag do
|
|
155
163
|
end
|
156
164
|
|
157
165
|
desc "Build the website with rdoc and rcov, but do not publish it"
|
158
|
-
task :website => [:clobber, :rcov_verify, :webgen, :failing_examples_with_html, :examples_specdoc, :rdoc]
|
166
|
+
task :website => [:clobber, :rcov_verify, :webgen, :failing_examples_with_html, :test2spec_test, :examples_specdoc, :rdoc]
|
159
167
|
|
160
168
|
task :verify_user do
|
161
169
|
raise "RUBYFORGE_USER environment variable not set!" unless ENV['RUBYFORGE_USER']
|
@@ -1,8 +1,8 @@
|
|
1
|
-
require 'spec/runner/base_text_formatter'
|
1
|
+
require 'spec/runner/formatter/base_text_formatter'
|
2
2
|
|
3
3
|
# Example of a custom formatter. Run me with:
|
4
4
|
# bin/spec examples -r examples/custom_formatter.rb -f CustomFormatter
|
5
|
-
class CustomFormatter < Spec::Runner::BaseTextFormatter
|
5
|
+
class CustomFormatter < Spec::Runner::Formatter::BaseTextFormatter
|
6
6
|
def add_context(name, first)
|
7
7
|
@output << "\n" if first
|
8
8
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class FileAccessor
|
2
|
+
def open_and_handle_with(pathname, processor)
|
3
|
+
pathname.open do |io|
|
4
|
+
processor.process(io)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
if __FILE__ == $0
|
10
|
+
require File.dirname(__FILE__) + '/io_processor'
|
11
|
+
require 'pathname'
|
12
|
+
|
13
|
+
accessor = FileAccessor.new
|
14
|
+
io_processor = IoProcessor.new
|
15
|
+
file = Pathname.new ARGV[0]
|
16
|
+
|
17
|
+
accessor.open_and_handle_with(file, io_processor)
|
18
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/spec'
|
2
|
+
require File.dirname(__FILE__) + '/file_accessor'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
context "A FileAccessor" do
|
6
|
+
# This sequence diagram illustrates what this spec specifies.
|
7
|
+
#
|
8
|
+
# +--------------+ +----------+ +-------------+
|
9
|
+
# | FileAccessor | | Pathname | | IoProcessor |
|
10
|
+
# +--------------+ +----------+ +-------------+
|
11
|
+
# | | |
|
12
|
+
# open_and_handle_with | | |
|
13
|
+
# -------------------->| | open | |
|
14
|
+
# | |--------------->| | |
|
15
|
+
# | | io | | |
|
16
|
+
# | |<...............| | |
|
17
|
+
# | | | process(io) |
|
18
|
+
# | |---------------------------------->| |
|
19
|
+
# | | | | |
|
20
|
+
# | |<..................................| |
|
21
|
+
# | | |
|
22
|
+
#
|
23
|
+
specify "should open a file and pass it to the processor's process method" do
|
24
|
+
# This is the primary actor
|
25
|
+
accessor = FileAccessor.new
|
26
|
+
|
27
|
+
# These are the primary actor's neighbours, which we mock.
|
28
|
+
file = mock "Pathname"
|
29
|
+
io_processor = mock "IoProcessor"
|
30
|
+
|
31
|
+
io = StringIO.new "whatever"
|
32
|
+
file.should_receive(:open).and_yield io
|
33
|
+
io_processor.should_receive(:process).with(io)
|
34
|
+
|
35
|
+
accessor.open_and_handle_with(file, io_processor)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/spec'
|
2
|
+
require File.dirname(__FILE__) + '/io_processor'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
context "An IoProcessor" do
|
6
|
+
setup do
|
7
|
+
@processor = IoProcessor.new
|
8
|
+
end
|
9
|
+
|
10
|
+
specify "should raise nothing when the file is exactly 32 bytes" do
|
11
|
+
lambda {
|
12
|
+
@processor.process(StringIO.new("z"*32))
|
13
|
+
}.should_not_raise
|
14
|
+
end
|
15
|
+
|
16
|
+
specify "should raise an exception when the file length is less than 32 bytes" do
|
17
|
+
lambda {
|
18
|
+
@processor.process(StringIO.new("z"*31))
|
19
|
+
}.should_raise(DataTooShort)
|
20
|
+
end
|
21
|
+
end
|
data/lib/spec/api/exceptions.rb
CHANGED
@@ -4,12 +4,12 @@ module Spec
|
|
4
4
|
# Represents the expection of the reception of a message
|
5
5
|
class MessageExpectation
|
6
6
|
|
7
|
-
def initialize(mock_name, expectation_ordering, expected_from, sym,
|
7
|
+
def initialize(mock_name, expectation_ordering, expected_from, sym, method_block)
|
8
8
|
@mock_name = mock_name
|
9
9
|
@expected_from = expected_from
|
10
10
|
@sym = sym
|
11
|
-
@method_block =
|
12
|
-
@
|
11
|
+
@method_block = method_block
|
12
|
+
@return_block = lambda {}
|
13
13
|
@received_count = 0
|
14
14
|
@expected_received_count = 1
|
15
15
|
@args_expectation = ArgumentExpectation.new([:any_args])
|
@@ -67,39 +67,54 @@ module Spec
|
|
67
67
|
end
|
68
68
|
|
69
69
|
# This method is called when a method is invoked on a mock
|
70
|
-
def
|
70
|
+
def invoke(args, block)
|
71
71
|
|
72
72
|
handle_order_constraint
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
73
|
+
|
74
|
+
begin
|
75
|
+
Kernel::raise @exception_to_raise.new unless @exception_to_raise.nil?
|
76
|
+
Kernel::throw @symbol_to_throw unless @symbol_to_throw.nil?
|
77
|
+
|
78
|
+
if !@method_block.nil?
|
79
|
+
return invoke_method_block(args)
|
80
|
+
elsif !@args_to_yield.nil?
|
81
|
+
return invoke_with_yield(block)
|
82
|
+
else
|
83
|
+
return invoke_return_block(args, block)
|
79
84
|
end
|
85
|
+
ensure
|
80
86
|
@received_count += 1
|
81
|
-
return result
|
82
87
|
end
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
88
|
+
end
|
89
|
+
|
90
|
+
def invoke_method_block(args)
|
91
|
+
begin
|
92
|
+
@method_block.call(*args)
|
93
|
+
rescue Spec::Api::ExpectationNotMetError => detail
|
94
|
+
Kernel::raise Spec::Api::MockExpectationError, "Call expectation violated with: " + detail
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def invoke_with_yield(block)
|
99
|
+
if block.nil?
|
100
|
+
Kernel::raise Spec::Api::MockExpectationError, "Expected block to be passed"
|
94
101
|
end
|
102
|
+
if @args_to_yield.length != block.arity
|
103
|
+
Kernel::raise Spec::Api::MockExpectationError, "Wrong arity of passed block. Expected #{@args_to_yield.size}"
|
104
|
+
end
|
105
|
+
block.call *@args_to_yield
|
106
|
+
end
|
95
107
|
|
108
|
+
def invoke_return_block(args, block)
|
96
109
|
args << block unless block.nil?
|
97
|
-
|
98
|
-
value = @block.call(*args)
|
110
|
+
value = @return_block.call(*args)
|
99
111
|
|
100
|
-
|
101
|
-
|
102
|
-
|
112
|
+
if @consecutive
|
113
|
+
index = [@received_count, value.size-1].min
|
114
|
+
value[index]
|
115
|
+
else
|
116
|
+
value
|
117
|
+
end
|
103
118
|
end
|
104
119
|
|
105
120
|
def with(*args)
|
@@ -170,11 +185,12 @@ module Spec
|
|
170
185
|
self
|
171
186
|
end
|
172
187
|
|
173
|
-
def return(value=nil
|
188
|
+
def return(value=nil, &return_block)
|
189
|
+
Kernel::raise AmbiguousReturnError unless @method_block.nil?
|
174
190
|
return self unless @and_seen
|
175
191
|
@and_seen = false
|
176
192
|
@consecutive = value.instance_of? Array
|
177
|
-
@
|
193
|
+
@return_block = block_given? ? return_block : lambda { value }
|
178
194
|
end
|
179
195
|
|
180
196
|
def raise(exception=Exception)
|
data/lib/spec/api/mocks/mock.rb
CHANGED
@@ -35,7 +35,7 @@ module Spec
|
|
35
35
|
|
36
36
|
def method_missing(sym, *args, &block)
|
37
37
|
if expectation = find_matching_expectation(sym, *args)
|
38
|
-
expectation.
|
38
|
+
expectation.invoke(args, block)
|
39
39
|
else
|
40
40
|
begin
|
41
41
|
# act as null object if method is missing and we ignore them. return value too!
|
data/lib/spec/runner.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec/runner/formatter'
|
1
2
|
require 'spec/runner/instance_exec'
|
2
3
|
require 'spec/runner/context'
|
3
4
|
require 'spec/runner/specification'
|
@@ -6,9 +7,4 @@ require 'spec/runner/context_runner'
|
|
6
7
|
require 'spec/runner/option_parser'
|
7
8
|
require 'spec/runner/backtrace_tweaker'
|
8
9
|
require 'spec/runner/reporter'
|
9
|
-
require 'spec/runner/base_text_formatter'
|
10
|
-
require 'spec/runner/progress_bar_formatter'
|
11
|
-
require 'spec/runner/rdoc_formatter'
|
12
|
-
require 'spec/runner/specdoc_formatter'
|
13
|
-
require 'spec/runner/html_formatter'
|
14
10
|
require 'spec/runner/spec_matcher'
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Spec
|
2
|
+
module Runner
|
3
|
+
module Formatter
|
4
|
+
# Baseclass for text-based formatters. Can in fact be used for
|
5
|
+
# non-text based ones too - just ignore the +output+ constructor
|
6
|
+
# argument.
|
7
|
+
class BaseTextFormatter
|
8
|
+
def initialize(output, dry_run=false)
|
9
|
+
@dry_run = dry_run
|
10
|
+
@output = output
|
11
|
+
end
|
12
|
+
|
13
|
+
# This method is invoked before any specs are run, right after
|
14
|
+
# they have all been collected. This can be useful for special
|
15
|
+
# formatters that need to provide progress on feedback (graphical ones)
|
16
|
+
#
|
17
|
+
# This method will only be invoked once, and the next one to be invoked
|
18
|
+
# is #add_context
|
19
|
+
def start(spec_count)
|
20
|
+
end
|
21
|
+
|
22
|
+
# This method is invoked at the beginning of the execution of each context.
|
23
|
+
# +name+ is the name of the context and +first+ is true if it is the
|
24
|
+
# first context - otherwise it's false.
|
25
|
+
#
|
26
|
+
# The next method to be invoked after this is #spec_started
|
27
|
+
def add_context(name, first)
|
28
|
+
end
|
29
|
+
|
30
|
+
# This method is invoked right before a spec is executed.
|
31
|
+
# The next method to be invoked after this one is one of #spec_failed
|
32
|
+
# or #spec_passed.
|
33
|
+
def spec_started(name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# This method is invoked when a spec fails, i.e. an exception occurred
|
37
|
+
# inside it (such as a failed should or other exception). +name+ is the name
|
38
|
+
# of the specification. +counter+ is the sequence number of the failure
|
39
|
+
# (starting at 1) and +failure+ is the associated Failure object.
|
40
|
+
def spec_failed(name, counter, failure)
|
41
|
+
end
|
42
|
+
|
43
|
+
# This method is invoked when a spec passes. +name+ is the name of the
|
44
|
+
# specification.
|
45
|
+
def spec_passed(name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# This method is invoked after all of the specs have executed. The next method
|
49
|
+
# to be invoked after this one is #dump_failure (once for each failed spec),
|
50
|
+
def start_dump
|
51
|
+
end
|
52
|
+
|
53
|
+
# Dumps detailed information about a spec failure.
|
54
|
+
# This method is invoked for each failed spec after all specs have run. +counter+ is the sequence number
|
55
|
+
# of the associated spec. +failure+ is a Failure object, which contains detailed
|
56
|
+
# information about the failure.
|
57
|
+
def dump_failure(counter, failure)
|
58
|
+
@output << "\n"
|
59
|
+
@output << counter.to_s << ")\n"
|
60
|
+
@output << "#{failure.header}\n"
|
61
|
+
@output << "#{failure.message}\n"
|
62
|
+
@output << "#{failure.backtrace}\n"
|
63
|
+
@output.flush
|
64
|
+
end
|
65
|
+
|
66
|
+
# This method is invoked at the very end.
|
67
|
+
def dump_summary(duration, spec_count, failure_count)
|
68
|
+
return if @dry_run
|
69
|
+
@output << "\n"
|
70
|
+
@output << "Finished in " << (duration).to_s << " seconds\n\n"
|
71
|
+
@output << "#{spec_count} specification#{'s' unless spec_count == 1}, "
|
72
|
+
@output << "#{failure_count} failure#{'s' unless failure_count == 1}"
|
73
|
+
@output << "\n"
|
74
|
+
@output.flush
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module Spec
|
2
|
+
module Runner
|
3
|
+
module Formatter
|
4
|
+
class HtmlFormatter < BaseTextFormatter
|
5
|
+
def initialize(output, dry_run=false)
|
6
|
+
super
|
7
|
+
@current_count = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def start(spec_count)
|
11
|
+
@spec_count = spec_count
|
12
|
+
|
13
|
+
@output.puts HEADER
|
14
|
+
@output.flush
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_context(name, first)
|
18
|
+
unless first
|
19
|
+
@output.puts " </ul>"
|
20
|
+
@output.puts "</div>"
|
21
|
+
end
|
22
|
+
@output.puts "<div class=\"context\">"
|
23
|
+
@output.puts " <div>#{name}</div>"
|
24
|
+
@output.puts " <ul>"
|
25
|
+
end
|
26
|
+
|
27
|
+
def start_dump
|
28
|
+
@output.puts " </ul>"
|
29
|
+
@output.puts "</div>"
|
30
|
+
@output.flush
|
31
|
+
end
|
32
|
+
|
33
|
+
def spec_started(name)
|
34
|
+
@current_spec = name
|
35
|
+
@current_count += 1
|
36
|
+
end
|
37
|
+
|
38
|
+
def spec_passed(name)
|
39
|
+
@output.puts "<li class=\"spec passed\">#{escape(@current_spec)}</li>"
|
40
|
+
end
|
41
|
+
|
42
|
+
def spec_failed(name, counter, failure)
|
43
|
+
@output.puts "<li class=\"spec failed\" onclick=\"toggle('failure_#{counter}');return false;\">"
|
44
|
+
@output.puts " <div>#{escape(@current_spec)}</div>"
|
45
|
+
@output.puts " <div class=\"failure\" id=\"failure_#{counter}\" style=\"display:none\">"
|
46
|
+
@output.puts " <div><pre>#{escape(failure.header)}</pre></div>" unless failure.header == ""
|
47
|
+
@output.puts " <div><pre>#{escape(failure.message)}</pre></div>" unless failure.message == ""
|
48
|
+
@output.puts " <div><pre>#{escape(failure.backtrace)}</pre></div>" unless failure.backtrace == ""
|
49
|
+
@output.puts " </div>"
|
50
|
+
@output.puts "</li>"
|
51
|
+
@output.flush
|
52
|
+
end
|
53
|
+
|
54
|
+
def escape(string)
|
55
|
+
string.gsub(/&/n, '&').gsub(/\"/n, '"').gsub(/>/n, '>').gsub(/</n, '<')
|
56
|
+
end
|
57
|
+
|
58
|
+
def dump_failure(counter, failure)
|
59
|
+
# @output << "\n"
|
60
|
+
# @output << counter.to_s << ")\n"
|
61
|
+
# @output << "#{failure.header}\n"
|
62
|
+
# @output << "#{failure.message}\n"
|
63
|
+
# @output << "#{failure.backtrace}\n"
|
64
|
+
# @output.flush
|
65
|
+
end
|
66
|
+
|
67
|
+
def dump_summary(duration, spec_count, failure_count)
|
68
|
+
@output << "</body>"
|
69
|
+
@output << "</html>"
|
70
|
+
@output.flush
|
71
|
+
end
|
72
|
+
|
73
|
+
HEADER = <<-HEADER
|
74
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
75
|
+
<!DOCTYPE html
|
76
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
77
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
78
|
+
|
79
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
80
|
+
<head>
|
81
|
+
<title>RSpec results</title>
|
82
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
83
|
+
<meta http-equiv="Content-Script-Type" content="text/javascript" />
|
84
|
+
<style type="text/css">
|
85
|
+
body {
|
86
|
+
font-size: 10pt;
|
87
|
+
font: "lucida grande";
|
88
|
+
width: 85%;
|
89
|
+
}
|
90
|
+
|
91
|
+
ul {
|
92
|
+
padding-left: 8px;
|
93
|
+
}
|
94
|
+
|
95
|
+
li.passed {
|
96
|
+
background-color: #DDFFDD;
|
97
|
+
}
|
98
|
+
|
99
|
+
li {
|
100
|
+
list-style-type: none;
|
101
|
+
margin: 0;
|
102
|
+
}
|
103
|
+
|
104
|
+
li.failed {
|
105
|
+
background-color: #FFBBBB;
|
106
|
+
font-weight: bold;
|
107
|
+
}
|
108
|
+
|
109
|
+
li.failed:hover {
|
110
|
+
color: #FFFFFF;
|
111
|
+
background-color: #FF0000;
|
112
|
+
}
|
113
|
+
|
114
|
+
li.failed .failure {
|
115
|
+
font-weight: normal;
|
116
|
+
font-size: 9pt;
|
117
|
+
}
|
118
|
+
|
119
|
+
div.context {
|
120
|
+
padding:4px;
|
121
|
+
border:1px solid #000000;
|
122
|
+
margin-top:4px;
|
123
|
+
}
|
124
|
+
|
125
|
+
</style>
|
126
|
+
<script type="text/javascript">
|
127
|
+
// <![CDATA[
|
128
|
+
|
129
|
+
function toggle( id ) {
|
130
|
+
if ( document.getElementById )
|
131
|
+
elem = document.getElementById( id );
|
132
|
+
else if ( document.all )
|
133
|
+
elem = eval( "document.all." + id );
|
134
|
+
else
|
135
|
+
return false;
|
136
|
+
|
137
|
+
elemStyle = elem.style;
|
138
|
+
|
139
|
+
if ( elemStyle.display != "block" ) {
|
140
|
+
elemStyle.display = "block"
|
141
|
+
} else {
|
142
|
+
elemStyle.display = "none"
|
143
|
+
}
|
144
|
+
|
145
|
+
return true;
|
146
|
+
}
|
147
|
+
// ]]>
|
148
|
+
</script>
|
149
|
+
|
150
|
+
</head>
|
151
|
+
<body>
|
152
|
+
HEADER
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|