sspec-core 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.yardopts +8 -0
- data/Changelog.md +2232 -0
- data/LICENSE.md +26 -0
- data/README.md +384 -0
- data/exe/rspec +4 -0
- data/lib/rspec/autorun.rb +3 -0
- data/lib/rspec/core.rb +185 -0
- data/lib/rspec/core/backtrace_formatter.rb +65 -0
- data/lib/rspec/core/bisect/coordinator.rb +62 -0
- data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
- data/lib/rspec/core/bisect/fork_runner.rb +134 -0
- data/lib/rspec/core/bisect/server.rb +61 -0
- data/lib/rspec/core/bisect/shell_command.rb +126 -0
- data/lib/rspec/core/bisect/shell_runner.rb +73 -0
- data/lib/rspec/core/bisect/utilities.rb +58 -0
- data/lib/rspec/core/configuration.rb +2289 -0
- data/lib/rspec/core/configuration_options.rb +233 -0
- data/lib/rspec/core/drb.rb +113 -0
- data/lib/rspec/core/dsl.rb +98 -0
- data/lib/rspec/core/example.rb +653 -0
- data/lib/rspec/core/example_group.rb +885 -0
- data/lib/rspec/core/example_status_persister.rb +235 -0
- data/lib/rspec/core/filter_manager.rb +231 -0
- data/lib/rspec/core/flat_map.rb +20 -0
- data/lib/rspec/core/formatters.rb +269 -0
- data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
- data/lib/rspec/core/formatters/base_formatter.rb +70 -0
- data/lib/rspec/core/formatters/base_text_formatter.rb +75 -0
- data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
- data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
- data/lib/rspec/core/formatters/console_codes.rb +68 -0
- data/lib/rspec/core/formatters/deprecation_formatter.rb +223 -0
- data/lib/rspec/core/formatters/documentation_formatter.rb +70 -0
- data/lib/rspec/core/formatters/exception_presenter.rb +508 -0
- data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
- data/lib/rspec/core/formatters/helpers.rb +110 -0
- data/lib/rspec/core/formatters/html_formatter.rb +153 -0
- data/lib/rspec/core/formatters/html_printer.rb +414 -0
- data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
- data/lib/rspec/core/formatters/json_formatter.rb +102 -0
- data/lib/rspec/core/formatters/profile_formatter.rb +68 -0
- data/lib/rspec/core/formatters/progress_formatter.rb +29 -0
- data/lib/rspec/core/formatters/protocol.rb +182 -0
- data/lib/rspec/core/formatters/snippet_extractor.rb +134 -0
- data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
- data/lib/rspec/core/hooks.rb +624 -0
- data/lib/rspec/core/invocations.rb +87 -0
- data/lib/rspec/core/memoized_helpers.rb +554 -0
- data/lib/rspec/core/metadata.rb +499 -0
- data/lib/rspec/core/metadata_filter.rb +255 -0
- data/lib/rspec/core/minitest_assertions_adapter.rb +31 -0
- data/lib/rspec/core/mocking_adapters/flexmock.rb +31 -0
- data/lib/rspec/core/mocking_adapters/mocha.rb +57 -0
- data/lib/rspec/core/mocking_adapters/null.rb +14 -0
- data/lib/rspec/core/mocking_adapters/rr.rb +31 -0
- data/lib/rspec/core/mocking_adapters/rspec.rb +32 -0
- data/lib/rspec/core/notifications.rb +521 -0
- data/lib/rspec/core/option_parser.rb +309 -0
- data/lib/rspec/core/ordering.rb +158 -0
- data/lib/rspec/core/output_wrapper.rb +29 -0
- data/lib/rspec/core/pending.rb +165 -0
- data/lib/rspec/core/profiler.rb +34 -0
- data/lib/rspec/core/project_initializer.rb +48 -0
- data/lib/rspec/core/project_initializer/.rspec +1 -0
- data/lib/rspec/core/project_initializer/spec/spec_helper.rb +100 -0
- data/lib/rspec/core/rake_task.rb +168 -0
- data/lib/rspec/core/reporter.rb +257 -0
- data/lib/rspec/core/ruby_project.rb +53 -0
- data/lib/rspec/core/runner.rb +199 -0
- data/lib/rspec/core/sandbox.rb +37 -0
- data/lib/rspec/core/set.rb +54 -0
- data/lib/rspec/core/shared_context.rb +55 -0
- data/lib/rspec/core/shared_example_group.rb +269 -0
- data/lib/rspec/core/shell_escape.rb +49 -0
- data/lib/rspec/core/test_unit_assertions_adapter.rb +30 -0
- data/lib/rspec/core/version.rb +9 -0
- data/lib/rspec/core/warnings.rb +40 -0
- data/lib/rspec/core/world.rb +275 -0
- metadata +257 -0
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            module RSpec
         | 
| 2 | 
            +
              module Core
         | 
| 3 | 
            +
                module Formatters
         | 
| 4 | 
            +
                  # @api private
         | 
| 5 | 
            +
                  # Formatter for providing message output as a fallback when no other
         | 
| 6 | 
            +
                  # profiler implements #message
         | 
| 7 | 
            +
                  class FallbackMessageFormatter
         | 
| 8 | 
            +
                    Formatters.register self, :message
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    def initialize(output)
         | 
| 11 | 
            +
                      @output = output
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    # @private
         | 
| 15 | 
            +
                    attr_reader :output
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    # @api public
         | 
| 18 | 
            +
                    #
         | 
| 19 | 
            +
                    # Used by the reporter to send messages to the output stream.
         | 
| 20 | 
            +
                    #
         | 
| 21 | 
            +
                    # @param notification [MessageNotification] containing message
         | 
| 22 | 
            +
                    def message(notification)
         | 
| 23 | 
            +
                      output.puts notification.message
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
| @@ -0,0 +1,110 @@ | |
| 1 | 
            +
            RSpec::Support.require_rspec_core "shell_escape"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RSpec
         | 
| 4 | 
            +
              module Core
         | 
| 5 | 
            +
                module Formatters
         | 
| 6 | 
            +
                  # Formatters helpers.
         | 
| 7 | 
            +
                  module Helpers
         | 
| 8 | 
            +
                    # @private
         | 
| 9 | 
            +
                    SUB_SECOND_PRECISION = 5
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    # @private
         | 
| 12 | 
            +
                    DEFAULT_PRECISION = 2
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    # @api private
         | 
| 15 | 
            +
                    #
         | 
| 16 | 
            +
                    # Formats seconds into a human-readable string.
         | 
| 17 | 
            +
                    #
         | 
| 18 | 
            +
                    # @param duration [Float, Fixnum] in seconds
         | 
| 19 | 
            +
                    # @return [String] human-readable time
         | 
| 20 | 
            +
                    #
         | 
| 21 | 
            +
                    # @example
         | 
| 22 | 
            +
                    #    format_duration(1) #=>  "1 minute 1 second"
         | 
| 23 | 
            +
                    #    format_duration(135.14) #=> "2 minutes 15.14 seconds"
         | 
| 24 | 
            +
                    def self.format_duration(duration)
         | 
| 25 | 
            +
                      precision = case
         | 
| 26 | 
            +
                                  when duration < 1 then    SUB_SECOND_PRECISION
         | 
| 27 | 
            +
                                  when duration < 120 then  DEFAULT_PRECISION
         | 
| 28 | 
            +
                                  when duration < 300 then  1
         | 
| 29 | 
            +
                                  else                  0
         | 
| 30 | 
            +
                                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                      if duration > 60
         | 
| 33 | 
            +
                        minutes = (duration.round / 60).to_i
         | 
| 34 | 
            +
                        seconds = (duration - minutes * 60)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                        "#{pluralize(minutes, 'minute')} #{pluralize(format_seconds(seconds, precision), 'second')}"
         | 
| 37 | 
            +
                      else
         | 
| 38 | 
            +
                        pluralize(format_seconds(duration, precision), 'second')
         | 
| 39 | 
            +
                      end
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    # @api private
         | 
| 43 | 
            +
                    #
         | 
| 44 | 
            +
                    # Formats seconds to have 5 digits of precision with trailing zeros
         | 
| 45 | 
            +
                    # removed if the number is less than 1 or with 2 digits of precision if
         | 
| 46 | 
            +
                    # the number is greater than zero.
         | 
| 47 | 
            +
                    #
         | 
| 48 | 
            +
                    # @param float [Float]
         | 
| 49 | 
            +
                    # @return [String] formatted float
         | 
| 50 | 
            +
                    #
         | 
| 51 | 
            +
                    # @example
         | 
| 52 | 
            +
                    #    format_seconds(0.000006) #=> "0.00001"
         | 
| 53 | 
            +
                    #    format_seconds(0.020000) #=> "0.02"
         | 
| 54 | 
            +
                    #    format_seconds(1.00000000001) #=> "1"
         | 
| 55 | 
            +
                    #
         | 
| 56 | 
            +
                    # The precision used is set in {Helpers::SUB_SECOND_PRECISION} and
         | 
| 57 | 
            +
                    # {Helpers::DEFAULT_PRECISION}.
         | 
| 58 | 
            +
                    #
         | 
| 59 | 
            +
                    # @see #strip_trailing_zeroes
         | 
| 60 | 
            +
                    def self.format_seconds(float, precision=nil)
         | 
| 61 | 
            +
                      return '0' if float < 0
         | 
| 62 | 
            +
                      precision ||= (float < 1) ? SUB_SECOND_PRECISION : DEFAULT_PRECISION
         | 
| 63 | 
            +
                      formatted = "%.#{precision}f" % float
         | 
| 64 | 
            +
                      strip_trailing_zeroes(formatted)
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    # @api private
         | 
| 68 | 
            +
                    #
         | 
| 69 | 
            +
                    # Remove trailing zeros from a string.
         | 
| 70 | 
            +
                    #
         | 
| 71 | 
            +
                    # Only remove trailing zeros after a decimal place.
         | 
| 72 | 
            +
                    # see: http://rubular.com/r/ojtTydOgpn
         | 
| 73 | 
            +
                    #
         | 
| 74 | 
            +
                    # @param string [String] string with trailing zeros
         | 
| 75 | 
            +
                    # @return [String] string with trailing zeros removed
         | 
| 76 | 
            +
                    def self.strip_trailing_zeroes(string)
         | 
| 77 | 
            +
                      string.sub(/(?:(\..*[^0])0+|\.0+)$/, '\1')
         | 
| 78 | 
            +
                    end
         | 
| 79 | 
            +
                    private_class_method :strip_trailing_zeroes
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    # @api private
         | 
| 82 | 
            +
                    #
         | 
| 83 | 
            +
                    # Pluralize a word based on a count.
         | 
| 84 | 
            +
                    #
         | 
| 85 | 
            +
                    # @param count [Fixnum] number of objects
         | 
| 86 | 
            +
                    # @param string [String] word to be pluralized
         | 
| 87 | 
            +
                    # @return [String] pluralized word
         | 
| 88 | 
            +
                    def self.pluralize(count, string)
         | 
| 89 | 
            +
                      "#{count} #{string}#{'s' unless count.to_f == 1}"
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    # @api private
         | 
| 93 | 
            +
                    # Given a list of example ids, organizes them into a compact, ordered list.
         | 
| 94 | 
            +
                    def self.organize_ids(ids)
         | 
| 95 | 
            +
                      grouped = ids.inject(Hash.new { |h, k| h[k] = [] }) do |hash, id|
         | 
| 96 | 
            +
                        file, id = Example.parse_id(id)
         | 
| 97 | 
            +
                        hash[file] << id
         | 
| 98 | 
            +
                        hash
         | 
| 99 | 
            +
                      end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                      grouped.sort_by(&:first).map do |file, grouped_ids|
         | 
| 102 | 
            +
                        grouped_ids = grouped_ids.sort_by { |id| id.split(':').map(&:to_i) }
         | 
| 103 | 
            +
                        id = Metadata.id_from(:rerun_file_path => file, :scoped_id => grouped_ids.join(','))
         | 
| 104 | 
            +
                        ShellEscape.conditionally_quote(id)
         | 
| 105 | 
            +
                      end
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
            end
         | 
| @@ -0,0 +1,153 @@ | |
| 1 | 
            +
            RSpec::Support.require_rspec_core "formatters/base_text_formatter"
         | 
| 2 | 
            +
            RSpec::Support.require_rspec_core "formatters/html_printer"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module RSpec
         | 
| 5 | 
            +
              module Core
         | 
| 6 | 
            +
                module Formatters
         | 
| 7 | 
            +
                  # @private
         | 
| 8 | 
            +
                  class HtmlFormatter < BaseFormatter
         | 
| 9 | 
            +
                    Formatters.register self, :start, :example_group_started, :start_dump,
         | 
| 10 | 
            +
                                        :example_started, :example_passed, :example_failed,
         | 
| 11 | 
            +
                                        :example_pending, :dump_summary
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def initialize(output)
         | 
| 14 | 
            +
                      super(output)
         | 
| 15 | 
            +
                      @failed_examples = []
         | 
| 16 | 
            +
                      @example_group_number = 0
         | 
| 17 | 
            +
                      @example_number = 0
         | 
| 18 | 
            +
                      @header_red = nil
         | 
| 19 | 
            +
                      @printer = HtmlPrinter.new(output)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    def start(notification)
         | 
| 23 | 
            +
                      super
         | 
| 24 | 
            +
                      @printer.print_html_start
         | 
| 25 | 
            +
                      @printer.flush
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    def example_group_started(notification)
         | 
| 29 | 
            +
                      super
         | 
| 30 | 
            +
                      @example_group_red = false
         | 
| 31 | 
            +
                      @example_group_number += 1
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                      @printer.print_example_group_end unless example_group_number == 1
         | 
| 34 | 
            +
                      @printer.print_example_group_start(example_group_number,
         | 
| 35 | 
            +
                                                         notification.group.description,
         | 
| 36 | 
            +
                                                         notification.group.parent_groups.size)
         | 
| 37 | 
            +
                      @printer.flush
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    def start_dump(_notification)
         | 
| 41 | 
            +
                      @printer.print_example_group_end
         | 
| 42 | 
            +
                      @printer.flush
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    def example_started(_notification)
         | 
| 46 | 
            +
                      @example_number += 1
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    def example_passed(passed)
         | 
| 50 | 
            +
                      @printer.move_progress(percent_done)
         | 
| 51 | 
            +
                      @printer.print_example_passed(passed.example.description, passed.example.execution_result.run_time)
         | 
| 52 | 
            +
                      @printer.flush
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    def example_failed(failure)
         | 
| 56 | 
            +
                      @failed_examples << failure.example
         | 
| 57 | 
            +
                      unless @header_red
         | 
| 58 | 
            +
                        @header_red = true
         | 
| 59 | 
            +
                        @printer.make_header_red
         | 
| 60 | 
            +
                      end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                      unless @example_group_red
         | 
| 63 | 
            +
                        @example_group_red = true
         | 
| 64 | 
            +
                        @printer.make_example_group_header_red(example_group_number)
         | 
| 65 | 
            +
                      end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                      @printer.move_progress(percent_done)
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                      example = failure.example
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                      exception = failure.exception
         | 
| 72 | 
            +
                      message_lines = failure.fully_formatted_lines(nil, RSpec::Core::Notifications::NullColorizer)
         | 
| 73 | 
            +
                      exception_details = if exception
         | 
| 74 | 
            +
                                            {
         | 
| 75 | 
            +
                                              # drop 2 removes the description (regardless of newlines) and leading blank line
         | 
| 76 | 
            +
                                              :message => message_lines.drop(2).join("\n"),
         | 
| 77 | 
            +
                                              :backtrace => failure.formatted_backtrace.join("\n"),
         | 
| 78 | 
            +
                                            }
         | 
| 79 | 
            +
                                          end
         | 
| 80 | 
            +
                      extra = extra_failure_content(failure)
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                      @printer.print_example_failed(
         | 
| 83 | 
            +
                        example.execution_result.pending_fixed,
         | 
| 84 | 
            +
                        example.description,
         | 
| 85 | 
            +
                        example.execution_result.run_time,
         | 
| 86 | 
            +
                        @failed_examples.size,
         | 
| 87 | 
            +
                        exception_details,
         | 
| 88 | 
            +
                        (extra == "") ? false : extra
         | 
| 89 | 
            +
                      )
         | 
| 90 | 
            +
                      @printer.flush
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    def example_pending(pending)
         | 
| 94 | 
            +
                      example = pending.example
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                      @printer.make_header_yellow unless @header_red
         | 
| 97 | 
            +
                      @printer.make_example_group_header_yellow(example_group_number) unless @example_group_red
         | 
| 98 | 
            +
                      @printer.move_progress(percent_done)
         | 
| 99 | 
            +
                      @printer.print_example_pending(example.description, example.execution_result.pending_message)
         | 
| 100 | 
            +
                      @printer.flush
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    def dump_summary(summary)
         | 
| 104 | 
            +
                      @printer.print_summary(
         | 
| 105 | 
            +
                        summary.duration,
         | 
| 106 | 
            +
                        summary.example_count,
         | 
| 107 | 
            +
                        summary.failure_count,
         | 
| 108 | 
            +
                        summary.pending_count
         | 
| 109 | 
            +
                      )
         | 
| 110 | 
            +
                      @printer.flush
         | 
| 111 | 
            +
                    end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                  private
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                    # If these methods are declared with attr_reader Ruby will issue a
         | 
| 116 | 
            +
                    # warning because they are private.
         | 
| 117 | 
            +
                    # rubocop:disable Style/TrivialAccessors
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                    # The number of the currently running example_group.
         | 
| 120 | 
            +
                    def example_group_number
         | 
| 121 | 
            +
                      @example_group_number
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                    # The number of the currently running example (a global counter).
         | 
| 125 | 
            +
                    def example_number
         | 
| 126 | 
            +
                      @example_number
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
                    # rubocop:enable Style/TrivialAccessors
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    def percent_done
         | 
| 131 | 
            +
                      result = 100.0
         | 
| 132 | 
            +
                      if @example_count > 0
         | 
| 133 | 
            +
                        result = (((example_number).to_f / @example_count.to_f * 1000).to_i / 10.0).to_f
         | 
| 134 | 
            +
                      end
         | 
| 135 | 
            +
                      result
         | 
| 136 | 
            +
                    end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                    # Override this method if you wish to output extra HTML for a failed
         | 
| 139 | 
            +
                    # spec. For example, you could output links to images or other files
         | 
| 140 | 
            +
                    # produced during the specs.
         | 
| 141 | 
            +
                    def extra_failure_content(failure)
         | 
| 142 | 
            +
                      RSpec::Support.require_rspec_core "formatters/html_snippet_extractor"
         | 
| 143 | 
            +
                      backtrace = (failure.exception.backtrace || []).map do |line|
         | 
| 144 | 
            +
                        RSpec.configuration.backtrace_formatter.backtrace_line(line)
         | 
| 145 | 
            +
                      end
         | 
| 146 | 
            +
                      backtrace.compact!
         | 
| 147 | 
            +
                      @snippet_extractor ||= HtmlSnippetExtractor.new
         | 
| 148 | 
            +
                      "    <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(backtrace)}</code></pre>"
         | 
| 149 | 
            +
                    end
         | 
| 150 | 
            +
                  end
         | 
| 151 | 
            +
                end
         | 
| 152 | 
            +
              end
         | 
| 153 | 
            +
            end
         | 
| @@ -0,0 +1,414 @@ | |
| 1 | 
            +
            require 'erb'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RSpec
         | 
| 4 | 
            +
              module Core
         | 
| 5 | 
            +
                module Formatters
         | 
| 6 | 
            +
                  # @private
         | 
| 7 | 
            +
                  class HtmlPrinter
         | 
| 8 | 
            +
                    include ERB::Util # For the #h method.
         | 
| 9 | 
            +
                    def initialize(output)
         | 
| 10 | 
            +
                      @output = output
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def print_html_start
         | 
| 14 | 
            +
                      @output.puts HTML_HEADER
         | 
| 15 | 
            +
                      @output.puts REPORT_HEADER
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    def print_example_group_end
         | 
| 19 | 
            +
                      @output.puts "  </dl>"
         | 
| 20 | 
            +
                      @output.puts "</div>"
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    def print_example_group_start(group_id, description, number_of_parents)
         | 
| 24 | 
            +
                      @output.puts "<div id=\"div_group_#{group_id}\" class=\"example_group passed\">"
         | 
| 25 | 
            +
                      @output.puts "  <dl #{indentation_style(number_of_parents)}>"
         | 
| 26 | 
            +
                      @output.puts "  <dt id=\"example_group_#{group_id}\" class=\"passed\">#{h(description)}</dt>"
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    def print_example_passed(description, run_time)
         | 
| 30 | 
            +
                      formatted_run_time = "%.5f" % run_time
         | 
| 31 | 
            +
                      @output.puts "    <dd class=\"example passed\">" \
         | 
| 32 | 
            +
                        "<span class=\"passed_spec_name\">#{h(description)}</span>" \
         | 
| 33 | 
            +
                        "<span class='duration'>#{formatted_run_time}s</span></dd>"
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    # rubocop:disable Metrics/ParameterLists
         | 
| 37 | 
            +
                    def print_example_failed(pending_fixed, description, run_time, failure_id,
         | 
| 38 | 
            +
                                             exception, extra_content)
         | 
| 39 | 
            +
                      # rubocop:enable Metrics/ParameterLists
         | 
| 40 | 
            +
                      formatted_run_time = "%.5f" % run_time
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                      @output.puts "    <dd class=\"example #{pending_fixed ? 'pending_fixed' : 'failed'}\">"
         | 
| 43 | 
            +
                      @output.puts "      <span class=\"failed_spec_name\">#{h(description)}</span>"
         | 
| 44 | 
            +
                      @output.puts "      <span class=\"duration\">#{formatted_run_time}s</span>"
         | 
| 45 | 
            +
                      @output.puts "      <div class=\"failure\" id=\"failure_#{failure_id}\">"
         | 
| 46 | 
            +
                      if exception
         | 
| 47 | 
            +
                        @output.puts "        <div class=\"message\"><pre>#{h(exception[:message])}</pre></div>"
         | 
| 48 | 
            +
                        @output.puts "        <div class=\"backtrace\"><pre>#{h exception[:backtrace]}</pre></div>"
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
                      @output.puts extra_content if extra_content
         | 
| 51 | 
            +
                      @output.puts "      </div>"
         | 
| 52 | 
            +
                      @output.puts "    </dd>"
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    def print_example_pending(description, pending_message)
         | 
| 56 | 
            +
                      @output.puts "    <dd class=\"example not_implemented\">" \
         | 
| 57 | 
            +
                        "<span class=\"not_implemented_spec_name\">#{h(description)} " \
         | 
| 58 | 
            +
                        "(PENDING: #{h(pending_message)})</span></dd>"
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    def print_summary(duration, example_count, failure_count, pending_count)
         | 
| 62 | 
            +
                      totals = String.new(
         | 
| 63 | 
            +
                        "#{example_count} example#{'s' unless example_count == 1}, "
         | 
| 64 | 
            +
                      )
         | 
| 65 | 
            +
                      totals << "#{failure_count} failure#{'s' unless failure_count == 1}"
         | 
| 66 | 
            +
                      totals << ", #{pending_count} pending" if pending_count > 0
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                      formatted_duration = "%.5f" % duration
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                      @output.puts "<script type=\"text/javascript\">" \
         | 
| 71 | 
            +
                        "document.getElementById('duration').innerHTML = \"Finished in " \
         | 
| 72 | 
            +
                        "<strong>#{formatted_duration} seconds</strong>\";</script>"
         | 
| 73 | 
            +
                      @output.puts "<script type=\"text/javascript\">" \
         | 
| 74 | 
            +
                        "document.getElementById('totals').innerHTML = \"#{totals}\";</script>"
         | 
| 75 | 
            +
                      @output.puts "</div>"
         | 
| 76 | 
            +
                      @output.puts "</div>"
         | 
| 77 | 
            +
                      @output.puts "</body>"
         | 
| 78 | 
            +
                      @output.puts "</html>"
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    def flush
         | 
| 82 | 
            +
                      @output.flush
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                    def move_progress(percent_done)
         | 
| 86 | 
            +
                      @output.puts "    <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
         | 
| 87 | 
            +
                      @output.flush
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    def make_header_red
         | 
| 91 | 
            +
                      @output.puts "    <script type=\"text/javascript\">makeRed('rspec-header');</script>"
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    def make_header_yellow
         | 
| 95 | 
            +
                      @output.puts "    <script type=\"text/javascript\">makeYellow('rspec-header');</script>"
         | 
| 96 | 
            +
                    end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                    def make_example_group_header_red(group_id)
         | 
| 99 | 
            +
                      @output.puts "    <script type=\"text/javascript\">" \
         | 
| 100 | 
            +
                                   "makeRed('div_group_#{group_id}');</script>"
         | 
| 101 | 
            +
                      @output.puts "    <script type=\"text/javascript\">" \
         | 
| 102 | 
            +
                                   "makeRed('example_group_#{group_id}');</script>"
         | 
| 103 | 
            +
                    end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    def make_example_group_header_yellow(group_id)
         | 
| 106 | 
            +
                      @output.puts "    <script type=\"text/javascript\">" \
         | 
| 107 | 
            +
                                   "makeYellow('div_group_#{group_id}');</script>"
         | 
| 108 | 
            +
                      @output.puts "    <script type=\"text/javascript\">" \
         | 
| 109 | 
            +
                                   "makeYellow('example_group_#{group_id}');</script>"
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                  private
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                    def indentation_style(number_of_parents)
         | 
| 115 | 
            +
                      "style=\"margin-left: #{(number_of_parents - 1) * 15}px;\""
         | 
| 116 | 
            +
                    end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                    REPORT_HEADER = <<-EOF
         | 
| 119 | 
            +
            <div class="rspec-report">
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            <div id="rspec-header">
         | 
| 122 | 
            +
              <div id="label">
         | 
| 123 | 
            +
                <h1>RSpec Code Examples</h1>
         | 
| 124 | 
            +
              </div>
         | 
| 125 | 
            +
             | 
| 126 | 
            +
              <div id="display-filters">
         | 
| 127 | 
            +
                <input id="passed_checkbox"  name="passed_checkbox"  type="checkbox" checked="checked" onchange="apply_filters()" value="1" /> <label for="passed_checkbox">Passed</label>
         | 
| 128 | 
            +
                <input id="failed_checkbox"  name="failed_checkbox"  type="checkbox" checked="checked" onchange="apply_filters()" value="2" /> <label for="failed_checkbox">Failed</label>
         | 
| 129 | 
            +
                <input id="pending_checkbox" name="pending_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="3" /> <label for="pending_checkbox">Pending</label>
         | 
| 130 | 
            +
              </div>
         | 
| 131 | 
            +
             | 
| 132 | 
            +
              <div id="summary">
         | 
| 133 | 
            +
                <p id="totals"> </p>
         | 
| 134 | 
            +
                <p id="duration"> </p>
         | 
| 135 | 
            +
              </div>
         | 
| 136 | 
            +
            </div>
         | 
| 137 | 
            +
             | 
| 138 | 
            +
             | 
| 139 | 
            +
            <div class="results">
         | 
| 140 | 
            +
            EOF
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                    GLOBAL_SCRIPTS = <<-EOF
         | 
| 143 | 
            +
             | 
| 144 | 
            +
            function addClass(element_id, classname) {
         | 
| 145 | 
            +
              document.getElementById(element_id).className += (" " + classname);
         | 
| 146 | 
            +
            }
         | 
| 147 | 
            +
             | 
| 148 | 
            +
            function removeClass(element_id, classname) {
         | 
| 149 | 
            +
              var elem = document.getElementById(element_id);
         | 
| 150 | 
            +
              var classlist = elem.className.replace(classname,'');
         | 
| 151 | 
            +
              elem.className = classlist;
         | 
| 152 | 
            +
            }
         | 
| 153 | 
            +
             | 
| 154 | 
            +
            function moveProgressBar(percentDone) {
         | 
| 155 | 
            +
              document.getElementById("rspec-header").style.width = percentDone +"%";
         | 
| 156 | 
            +
            }
         | 
| 157 | 
            +
             | 
| 158 | 
            +
            function makeRed(element_id) {
         | 
| 159 | 
            +
              removeClass(element_id, 'passed');
         | 
| 160 | 
            +
              removeClass(element_id, 'not_implemented');
         | 
| 161 | 
            +
              addClass(element_id,'failed');
         | 
| 162 | 
            +
            }
         | 
| 163 | 
            +
             | 
| 164 | 
            +
            function makeYellow(element_id) {
         | 
| 165 | 
            +
              var elem = document.getElementById(element_id);
         | 
| 166 | 
            +
              if (elem.className.indexOf("failed") == -1) {  // class doesn't includes failed
         | 
| 167 | 
            +
                if (elem.className.indexOf("not_implemented") == -1) { // class doesn't include not_implemented
         | 
| 168 | 
            +
                  removeClass(element_id, 'passed');
         | 
| 169 | 
            +
                  addClass(element_id,'not_implemented');
         | 
| 170 | 
            +
                }
         | 
| 171 | 
            +
              }
         | 
| 172 | 
            +
            }
         | 
| 173 | 
            +
             | 
| 174 | 
            +
            function apply_filters() {
         | 
| 175 | 
            +
              var passed_filter = document.getElementById('passed_checkbox').checked;
         | 
| 176 | 
            +
              var failed_filter = document.getElementById('failed_checkbox').checked;
         | 
| 177 | 
            +
              var pending_filter = document.getElementById('pending_checkbox').checked;
         | 
| 178 | 
            +
             | 
| 179 | 
            +
              assign_display_style("example passed", passed_filter);
         | 
| 180 | 
            +
              assign_display_style("example failed", failed_filter);
         | 
| 181 | 
            +
              assign_display_style("example not_implemented", pending_filter);
         | 
| 182 | 
            +
             | 
| 183 | 
            +
              assign_display_style_for_group("example_group passed", passed_filter);
         | 
| 184 | 
            +
              assign_display_style_for_group("example_group not_implemented", pending_filter, pending_filter || passed_filter);
         | 
| 185 | 
            +
              assign_display_style_for_group("example_group failed", failed_filter, failed_filter || pending_filter || passed_filter);
         | 
| 186 | 
            +
            }
         | 
| 187 | 
            +
             | 
| 188 | 
            +
            function get_display_style(display_flag) {
         | 
| 189 | 
            +
              var style_mode = 'none';
         | 
| 190 | 
            +
              if (display_flag == true) {
         | 
| 191 | 
            +
                style_mode = 'block';
         | 
| 192 | 
            +
              }
         | 
| 193 | 
            +
              return style_mode;
         | 
| 194 | 
            +
            }
         | 
| 195 | 
            +
             | 
| 196 | 
            +
            function assign_display_style(classname, display_flag) {
         | 
| 197 | 
            +
              var style_mode = get_display_style(display_flag);
         | 
| 198 | 
            +
              var elems = document.getElementsByClassName(classname)
         | 
| 199 | 
            +
              for (var i=0; i<elems.length;i++) {
         | 
| 200 | 
            +
                elems[i].style.display = style_mode;
         | 
| 201 | 
            +
              }
         | 
| 202 | 
            +
            }
         | 
| 203 | 
            +
             | 
| 204 | 
            +
            function assign_display_style_for_group(classname, display_flag, subgroup_flag) {
         | 
| 205 | 
            +
              var display_style_mode = get_display_style(display_flag);
         | 
| 206 | 
            +
              var subgroup_style_mode = get_display_style(subgroup_flag);
         | 
| 207 | 
            +
              var elems = document.getElementsByClassName(classname)
         | 
| 208 | 
            +
              for (var i=0; i<elems.length;i++) {
         | 
| 209 | 
            +
                var style_mode = display_style_mode;
         | 
| 210 | 
            +
                if ((display_flag != subgroup_flag) && (elems[i].getElementsByTagName('dt')[0].innerHTML.indexOf(", ") != -1)) {
         | 
| 211 | 
            +
                  elems[i].style.display = subgroup_style_mode;
         | 
| 212 | 
            +
                } else {
         | 
| 213 | 
            +
                  elems[i].style.display = display_style_mode;
         | 
| 214 | 
            +
                }
         | 
| 215 | 
            +
              }
         | 
| 216 | 
            +
            }
         | 
| 217 | 
            +
            EOF
         | 
| 218 | 
            +
                    # rubocop:enable LineLength
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                    GLOBAL_STYLES = <<-EOF
         | 
| 221 | 
            +
            #rspec-header {
         | 
| 222 | 
            +
              background: #65C400; color: #fff; height: 4em;
         | 
| 223 | 
            +
            }
         | 
| 224 | 
            +
             | 
| 225 | 
            +
            .rspec-report h1 {
         | 
| 226 | 
            +
              margin: 0px 10px 0px 10px;
         | 
| 227 | 
            +
              padding: 10px;
         | 
| 228 | 
            +
              font-family: "Lucida Grande", Helvetica, sans-serif;
         | 
| 229 | 
            +
              font-size: 1.8em;
         | 
| 230 | 
            +
              position: absolute;
         | 
| 231 | 
            +
            }
         | 
| 232 | 
            +
             | 
| 233 | 
            +
            #label {
         | 
| 234 | 
            +
              float:left;
         | 
| 235 | 
            +
            }
         | 
| 236 | 
            +
             | 
| 237 | 
            +
            #display-filters {
         | 
| 238 | 
            +
              float:left;
         | 
| 239 | 
            +
              padding: 28px 0 0 40%;
         | 
| 240 | 
            +
              font-family: "Lucida Grande", Helvetica, sans-serif;
         | 
| 241 | 
            +
            }
         | 
| 242 | 
            +
             | 
| 243 | 
            +
            #summary {
         | 
| 244 | 
            +
              float:right;
         | 
| 245 | 
            +
              padding: 5px 10px;
         | 
| 246 | 
            +
              font-family: "Lucida Grande", Helvetica, sans-serif;
         | 
| 247 | 
            +
              text-align: right;
         | 
| 248 | 
            +
            }
         | 
| 249 | 
            +
             | 
| 250 | 
            +
            #summary p {
         | 
| 251 | 
            +
              margin: 0 0 0 2px;
         | 
| 252 | 
            +
            }
         | 
| 253 | 
            +
             | 
| 254 | 
            +
            #summary #totals {
         | 
| 255 | 
            +
              font-size: 1.2em;
         | 
| 256 | 
            +
            }
         | 
| 257 | 
            +
             | 
| 258 | 
            +
            .example_group {
         | 
| 259 | 
            +
              margin: 0 10px 5px;
         | 
| 260 | 
            +
              background: #fff;
         | 
| 261 | 
            +
            }
         | 
| 262 | 
            +
             | 
| 263 | 
            +
            dl {
         | 
| 264 | 
            +
              margin: 0; padding: 0 0 5px;
         | 
| 265 | 
            +
              font: normal 11px "Lucida Grande", Helvetica, sans-serif;
         | 
| 266 | 
            +
            }
         | 
| 267 | 
            +
             | 
| 268 | 
            +
            dt {
         | 
| 269 | 
            +
              padding: 3px;
         | 
| 270 | 
            +
              background: #65C400;
         | 
| 271 | 
            +
              color: #fff;
         | 
| 272 | 
            +
              font-weight: bold;
         | 
| 273 | 
            +
            }
         | 
| 274 | 
            +
             | 
| 275 | 
            +
            dd {
         | 
| 276 | 
            +
              margin: 5px 0 5px 5px;
         | 
| 277 | 
            +
              padding: 3px 3px 3px 18px;
         | 
| 278 | 
            +
            }
         | 
| 279 | 
            +
             | 
| 280 | 
            +
            dd .duration {
         | 
| 281 | 
            +
              padding-left: 5px;
         | 
| 282 | 
            +
              text-align: right;
         | 
| 283 | 
            +
              right: 0px;
         | 
| 284 | 
            +
              float:right;
         | 
| 285 | 
            +
            }
         | 
| 286 | 
            +
             | 
| 287 | 
            +
            dd.example.passed {
         | 
| 288 | 
            +
              border-left: 5px solid #65C400;
         | 
| 289 | 
            +
              border-bottom: 1px solid #65C400;
         | 
| 290 | 
            +
              background: #DBFFB4; color: #3D7700;
         | 
| 291 | 
            +
            }
         | 
| 292 | 
            +
             | 
| 293 | 
            +
            dd.example.not_implemented {
         | 
| 294 | 
            +
              border-left: 5px solid #FAF834;
         | 
| 295 | 
            +
              border-bottom: 1px solid #FAF834;
         | 
| 296 | 
            +
              background: #FCFB98; color: #131313;
         | 
| 297 | 
            +
            }
         | 
| 298 | 
            +
             | 
| 299 | 
            +
            dd.example.pending_fixed {
         | 
| 300 | 
            +
              border-left: 5px solid #0000C2;
         | 
| 301 | 
            +
              border-bottom: 1px solid #0000C2;
         | 
| 302 | 
            +
              color: #0000C2; background: #D3FBFF;
         | 
| 303 | 
            +
            }
         | 
| 304 | 
            +
             | 
| 305 | 
            +
            dd.example.failed {
         | 
| 306 | 
            +
              border-left: 5px solid #C20000;
         | 
| 307 | 
            +
              border-bottom: 1px solid #C20000;
         | 
| 308 | 
            +
              color: #C20000; background: #FFFBD3;
         | 
| 309 | 
            +
            }
         | 
| 310 | 
            +
             | 
| 311 | 
            +
             | 
| 312 | 
            +
            dt.not_implemented {
         | 
| 313 | 
            +
              color: #000000; background: #FAF834;
         | 
| 314 | 
            +
            }
         | 
| 315 | 
            +
             | 
| 316 | 
            +
            dt.pending_fixed {
         | 
| 317 | 
            +
              color: #FFFFFF; background: #C40D0D;
         | 
| 318 | 
            +
            }
         | 
| 319 | 
            +
             | 
| 320 | 
            +
            dt.failed {
         | 
| 321 | 
            +
              color: #FFFFFF; background: #C40D0D;
         | 
| 322 | 
            +
            }
         | 
| 323 | 
            +
             | 
| 324 | 
            +
             | 
| 325 | 
            +
            #rspec-header.not_implemented {
         | 
| 326 | 
            +
              color: #000000; background: #FAF834;
         | 
| 327 | 
            +
            }
         | 
| 328 | 
            +
             | 
| 329 | 
            +
            #rspec-header.pending_fixed {
         | 
| 330 | 
            +
              color: #FFFFFF; background: #C40D0D;
         | 
| 331 | 
            +
            }
         | 
| 332 | 
            +
             | 
| 333 | 
            +
            #rspec-header.failed {
         | 
| 334 | 
            +
              color: #FFFFFF; background: #C40D0D;
         | 
| 335 | 
            +
            }
         | 
| 336 | 
            +
             | 
| 337 | 
            +
             | 
| 338 | 
            +
            .backtrace {
         | 
| 339 | 
            +
              color: #000;
         | 
| 340 | 
            +
              font-size: 12px;
         | 
| 341 | 
            +
            }
         | 
| 342 | 
            +
             | 
| 343 | 
            +
            a {
         | 
| 344 | 
            +
              color: #BE5C00;
         | 
| 345 | 
            +
            }
         | 
| 346 | 
            +
             | 
| 347 | 
            +
            /* Ruby code, style similar to vibrant ink */
         | 
| 348 | 
            +
            .ruby {
         | 
| 349 | 
            +
              font-size: 12px;
         | 
| 350 | 
            +
              font-family: monospace;
         | 
| 351 | 
            +
              color: white;
         | 
| 352 | 
            +
              background-color: black;
         | 
| 353 | 
            +
              padding: 0.1em 0 0.2em 0;
         | 
| 354 | 
            +
            }
         | 
| 355 | 
            +
             | 
| 356 | 
            +
            .ruby .keyword { color: #FF6600; }
         | 
| 357 | 
            +
            .ruby .constant { color: #339999; }
         | 
| 358 | 
            +
            .ruby .attribute { color: white; }
         | 
| 359 | 
            +
            .ruby .global { color: white; }
         | 
| 360 | 
            +
            .ruby .module { color: white; }
         | 
| 361 | 
            +
            .ruby .class { color: white; }
         | 
| 362 | 
            +
            .ruby .string { color: #66FF00; }
         | 
| 363 | 
            +
            .ruby .ident { color: white; }
         | 
| 364 | 
            +
            .ruby .method { color: #FFCC00; }
         | 
| 365 | 
            +
            .ruby .number { color: white; }
         | 
| 366 | 
            +
            .ruby .char { color: white; }
         | 
| 367 | 
            +
            .ruby .comment { color: #9933CC; }
         | 
| 368 | 
            +
            .ruby .symbol { color: white; }
         | 
| 369 | 
            +
            .ruby .regex { color: #44B4CC; }
         | 
| 370 | 
            +
            .ruby .punct { color: white; }
         | 
| 371 | 
            +
            .ruby .escape { color: white; }
         | 
| 372 | 
            +
            .ruby .interp { color: white; }
         | 
| 373 | 
            +
            .ruby .expr { color: white; }
         | 
| 374 | 
            +
             | 
| 375 | 
            +
            .ruby .offending { background-color: gray; }
         | 
| 376 | 
            +
            .ruby .linenum {
         | 
| 377 | 
            +
              width: 75px;
         | 
| 378 | 
            +
              padding: 0.1em 1em 0.2em 0;
         | 
| 379 | 
            +
              color: #000000;
         | 
| 380 | 
            +
              background-color: #FFFBD3;
         | 
| 381 | 
            +
            }
         | 
| 382 | 
            +
            EOF
         | 
| 383 | 
            +
             | 
| 384 | 
            +
                    HTML_HEADER = <<-EOF
         | 
| 385 | 
            +
            <!DOCTYPE html>
         | 
| 386 | 
            +
            <html lang='en'>
         | 
| 387 | 
            +
            <head>
         | 
| 388 | 
            +
              <title>RSpec results</title>
         | 
| 389 | 
            +
              <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
         | 
| 390 | 
            +
              <meta http-equiv="Expires" content="-1" />
         | 
| 391 | 
            +
              <meta http-equiv="Pragma" content="no-cache" />
         | 
| 392 | 
            +
              <style type="text/css">
         | 
| 393 | 
            +
              body {
         | 
| 394 | 
            +
                margin: 0;
         | 
| 395 | 
            +
                padding: 0;
         | 
| 396 | 
            +
                background: #fff;
         | 
| 397 | 
            +
                font-size: 80%;
         | 
| 398 | 
            +
              }
         | 
| 399 | 
            +
              </style>
         | 
| 400 | 
            +
              <script type="text/javascript">
         | 
| 401 | 
            +
                // <![CDATA[
         | 
| 402 | 
            +
            #{GLOBAL_SCRIPTS}
         | 
| 403 | 
            +
                // ]]>
         | 
| 404 | 
            +
              </script>
         | 
| 405 | 
            +
              <style type="text/css">
         | 
| 406 | 
            +
            #{GLOBAL_STYLES}
         | 
| 407 | 
            +
              </style>
         | 
| 408 | 
            +
            </head>
         | 
| 409 | 
            +
            <body>
         | 
| 410 | 
            +
            EOF
         | 
| 411 | 
            +
                  end
         | 
| 412 | 
            +
                end
         | 
| 413 | 
            +
              end
         | 
| 414 | 
            +
            end
         |