rspec 0.5.12 → 0.5.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/CHANGES +9 -1
  2. data/README +2 -2
  3. data/Rakefile +13 -5
  4. data/examples/custom_formatter.rb +2 -2
  5. data/examples/file_accessor.rb +18 -0
  6. data/examples/file_accessor_spec.rb +38 -0
  7. data/examples/io_processor.rb +8 -0
  8. data/examples/io_processor_spec.rb +21 -0
  9. data/lib/spec/api/exceptions.rb +3 -0
  10. data/lib/spec/api/mocks/message_expectation.rb +45 -29
  11. data/lib/spec/api/mocks/mock.rb +1 -1
  12. data/lib/spec/api/mocks/order_group.rb +1 -1
  13. data/lib/spec/runner.rb +1 -5
  14. data/lib/spec/runner/formatter.rb +5 -0
  15. data/lib/spec/runner/formatter/base_text_formatter.rb +79 -0
  16. data/lib/spec/runner/formatter/html_formatter.rb +156 -0
  17. data/lib/spec/runner/formatter/progress_bar_formatter.rb +27 -0
  18. data/lib/spec/runner/formatter/rdoc_formatter.rb +22 -0
  19. data/lib/spec/runner/formatter/specdoc_formatter.rb +22 -0
  20. data/lib/spec/runner/option_parser.rb +17 -17
  21. data/lib/spec/runner/reporter.rb +1 -1
  22. data/lib/spec/version.rb +1 -1
  23. data/test/spec/api/helper/arbitrary_predicate_test.rb +38 -38
  24. data/test/spec/api/helper/diff_test.rb +1 -1
  25. data/test/spec/api/helper/identity_test.rb +17 -10
  26. data/test/spec/api/helper/{equality_test.rb → object_equality_test.rb} +15 -29
  27. data/test/spec/api/helper/regex_matching_test.rb +7 -9
  28. data/test/spec/api/helper/throwing_test.rb +11 -12
  29. data/test/spec/api/helper/true_false_special_case_test.rb +15 -17
  30. data/test/spec/api/helper/typing_test.rb +27 -26
  31. data/test/spec/api/mocks/mock_arg_constraints_test.rb +1 -1
  32. data/test/spec/api/mocks/mock_test.rb +45 -11
  33. data/test/spec/api/mocks/null_object_test.rb +3 -3
  34. data/test/spec/runner/context_matching_test.rb +2 -2
  35. data/test/spec/runner/formatter/failure_dump_test.rb +94 -0
  36. data/test/spec/runner/formatter/html_formatter_test.rb +48 -0
  37. data/test/spec/runner/formatter/progress_bar_formatter_test.rb +56 -0
  38. data/test/spec/runner/formatter/rdoc_formatter_test.rb +51 -0
  39. data/test/spec/runner/formatter/specdoc_formatter_test.rb +57 -0
  40. data/test/spec/runner/kernel_ext_test.rb +1 -1
  41. data/test/spec/runner/option_parser_test.rb +22 -12
  42. data/test/spec/runner/reporter_test.rb +1 -1
  43. data/test/test_classes.rb +7 -7
  44. metadata +19 -14
  45. data/lib/spec/runner/base_text_formatter.rb +0 -77
  46. data/lib/spec/runner/html_formatter.rb +0 -153
  47. data/lib/spec/runner/progress_bar_formatter.rb +0 -25
  48. data/lib/spec/runner/rdoc_formatter.rb +0 -20
  49. data/lib/spec/runner/specdoc_formatter.rb +0 -20
  50. data/test/spec/runner/failure_dump_test.rb +0 -92
  51. data/test/spec/runner/html_formatter_test.rb +0 -47
  52. data/test/spec/runner/progress_bar_formatter_test.rb +0 -54
  53. data/test/spec/runner/rdoc_formatter_test.rb +0 -50
  54. 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-diff Rubygem (#2648).
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-diff
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-diff when --diff is specified.
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
- t.test_files = FileList['test/**/*_test.rb']
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.options = ['--diff']
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,8 @@
1
+ class DataTooShort < StandardError; end
2
+
3
+ class IoProcessor
4
+ # Does some fancy stuff unless the length of +io+ is shorter than 32
5
+ def process(io)
6
+ raise DataTooShort if io.read.length < 32
7
+ end
8
+ 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
@@ -5,5 +5,8 @@ module Spec
5
5
 
6
6
  class MockExpectationError < StandardError
7
7
  end
8
+
9
+ class AmbiguousReturnError < StandardError
10
+ end
8
11
  end
9
12
  end
@@ -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, block)
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 = block
12
- @block = proc {}
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 verify_message(args, block)
70
+ def invoke(args, block)
71
71
 
72
72
  handle_order_constraint
73
-
74
- unless @method_block.nil?
75
- begin
76
- result = @method_block.call(*args)
77
- rescue Spec::Api::ExpectationNotMetError => detail
78
- Kernel::raise Spec::Api::MockExpectationError, "Call expectation violated with: " + detail
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
- Kernel::raise @exception_to_raise.new unless @exception_to_raise.nil?
85
- Kernel::throw @symbol_to_throw unless @symbol_to_throw.nil?
86
- unless @args_to_yield.nil?
87
- if block.nil?
88
- Kernel::raise Spec::Api::MockExpectationError, "Expected block to be passed"
89
- end
90
- if @args_to_yield.length != block.arity
91
- Kernel::raise Spec::Api::MockExpectationError, "Wrong arity of passed block. Expected #{@args_to_yield.size}"
92
- end
93
- block.call @args_to_yield
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
- @received_count += 1
98
- value = @block.call(*args)
110
+ value = @return_block.call(*args)
99
111
 
100
- return value unless @consecutive
101
-
102
- value[[@received_count, value.size].min - 1]
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,&block)
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
- @block = block_given? ? block : proc { value }
193
+ @return_block = block_given? ? return_block : lambda { value }
178
194
  end
179
195
 
180
196
  def raise(exception=Exception)
@@ -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.verify_message(args, block)
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!
@@ -6,7 +6,7 @@ module Spec
6
6
  end
7
7
 
8
8
  def register(expectation)
9
- @ordering << expectation
9
+ @ordering << expectation
10
10
  end
11
11
 
12
12
  def ready_for?(expectation)
@@ -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,5 @@
1
+ require 'spec/runner/formatter/base_text_formatter'
2
+ require 'spec/runner/formatter/progress_bar_formatter'
3
+ require 'spec/runner/formatter/rdoc_formatter'
4
+ require 'spec/runner/formatter/specdoc_formatter'
5
+ require 'spec/runner/formatter/html_formatter'
@@ -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, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
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