omnitest 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.gitmodules +0 -0
  4. data/.groc.json +7 -0
  5. data/.rspec +6 -0
  6. data/.rubocop.yml +5 -0
  7. data/.rubocop_todo.yml +47 -0
  8. data/.travis.yml +12 -0
  9. data/.yardopts +3 -0
  10. data/Gemfile +26 -0
  11. data/README.md +341 -0
  12. data/Rakefile +33 -0
  13. data/appveyor.yml +9 -0
  14. data/bin/omnidoc +5 -0
  15. data/bin/omnitask +5 -0
  16. data/bin/omnitest +5 -0
  17. data/bower.json +21 -0
  18. data/doc-src/index.md.tt +341 -0
  19. data/doc-src/project_sets.md.tt +31 -0
  20. data/doc-src/usage/crosstask.md.tt +86 -0
  21. data/doc-src/usage/omnitest.md.tt +87 -0
  22. data/features/bootstrapping.feature +25 -0
  23. data/features/cloning.feature +32 -0
  24. data/features/fixtures/configs/omnitest_sample.yaml +11 -0
  25. data/features/fixtures/configs/skeptic_empty.yaml +12 -0
  26. data/features/fixtures/configs/skeptic_hello_world.yaml +10 -0
  27. data/features/show.feature +38 -0
  28. data/features/states.feature +40 -0
  29. data/features/step_definitions/sdk_steps.rb +22 -0
  30. data/features/support/env.rb +9 -0
  31. data/lib/omnitest.rb +211 -0
  32. data/lib/omnitest/cli.rb +297 -0
  33. data/lib/omnitest/command.rb +103 -0
  34. data/lib/omnitest/command/generate.rb +29 -0
  35. data/lib/omnitest/command/generators/code2doc.rb +79 -0
  36. data/lib/omnitest/command/generators/dashboard.rb +148 -0
  37. data/lib/omnitest/command/generators/documentation.rb +119 -0
  38. data/lib/omnitest/command/list.rb +62 -0
  39. data/lib/omnitest/command/project_action.rb +26 -0
  40. data/lib/omnitest/command/scenario_action.rb +20 -0
  41. data/lib/omnitest/command/show.rb +148 -0
  42. data/lib/omnitest/command/task.rb +27 -0
  43. data/lib/omnitest/command/test.rb +41 -0
  44. data/lib/omnitest/configuration.rb +53 -0
  45. data/lib/omnitest/documentation_generator.rb +68 -0
  46. data/lib/omnitest/project.rb +100 -0
  47. data/lib/omnitest/project_logger.rb +273 -0
  48. data/lib/omnitest/project_set.rb +47 -0
  49. data/lib/omnitest/reporters.rb +27 -0
  50. data/lib/omnitest/reporters/hash_reporter.rb +32 -0
  51. data/lib/omnitest/reporters/json_reporter.rb +12 -0
  52. data/lib/omnitest/reporters/markdown_reporter.rb +26 -0
  53. data/lib/omnitest/reporters/yaml_reporter.rb +12 -0
  54. data/lib/omnitest/run_action.rb +44 -0
  55. data/lib/omnitest/version.rb +3 -0
  56. data/lib/omnitest/workflow.rb +5 -0
  57. data/mkdocs.yml +8 -0
  58. data/omnitest.gemspec +39 -0
  59. data/omnitest.yaml +5 -0
  60. data/resources/assets/angular/angular.min.js +217 -0
  61. data/resources/assets/angular/angular.min.js.map +8 -0
  62. data/resources/assets/angular/json-formatter.min.css +6 -0
  63. data/resources/assets/angular/json-formatter.min.js +7 -0
  64. data/resources/assets/angular/ng-table.map +1 -0
  65. data/resources/assets/angular/ng-table.min.css +3 -0
  66. data/resources/assets/angular/ng-table.min.js +3 -0
  67. data/resources/assets/angular/ui-bootstrap-tpls.min.js +10 -0
  68. data/resources/assets/bootstrap/bootstrap.min.css +9 -0
  69. data/resources/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  70. data/resources/assets/fonts/glyphicons-halflings-regular.svg +229 -0
  71. data/resources/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  72. data/resources/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  73. data/resources/assets/pygments/autumn.css +58 -0
  74. data/resources/assets/pygments/borland.css +46 -0
  75. data/resources/assets/pygments/bw.css +34 -0
  76. data/resources/assets/pygments/colorful.css +61 -0
  77. data/resources/assets/pygments/default.css +62 -0
  78. data/resources/assets/pygments/emacs.css +61 -0
  79. data/resources/assets/pygments/friendly.css +61 -0
  80. data/resources/assets/pygments/fruity.css +69 -0
  81. data/resources/assets/pygments/github.css +61 -0
  82. data/resources/assets/pygments/manni.css +61 -0
  83. data/resources/assets/pygments/monokai.css +64 -0
  84. data/resources/assets/pygments/murphy.css +61 -0
  85. data/resources/assets/pygments/native.css +69 -0
  86. data/resources/assets/pygments/pastie.css +60 -0
  87. data/resources/assets/pygments/perldoc.css +58 -0
  88. data/resources/assets/pygments/tango.css +69 -0
  89. data/resources/assets/pygments/trac.css +59 -0
  90. data/resources/assets/pygments/vim.css +69 -0
  91. data/resources/assets/pygments/vs.css +33 -0
  92. data/resources/assets/pygments/zenburn.css +1 -0
  93. data/resources/assets/style.css +56 -0
  94. data/resources/code_sample.tt +2 -0
  95. data/resources/generators/dashboard/files/dashboard.html.tt +51 -0
  96. data/resources/generators/dashboard/files/dashboard.js +26 -0
  97. data/resources/generators/dashboard/templates/_test_report.html.haml +91 -0
  98. data/resources/generators/todo/templates/todo.md.tt +6 -0
  99. data/resources/generators/todo/todo_template.rb +1 -0
  100. data/samples/.gitignore +2 -0
  101. data/samples/_markdown.md +5 -0
  102. data/samples/bootstrap.sh +2 -0
  103. data/samples/clone.sh +2 -0
  104. data/samples/code2doc.sh +5 -0
  105. data/samples/default_bootstrap.rb +7 -0
  106. data/samples/detect.sh +2 -0
  107. data/samples/exec.sh +2 -0
  108. data/samples/omnitest.yaml +24 -0
  109. data/samples/omnitest_simple.yaml +8 -0
  110. data/samples/scripts/bootstrap +3 -0
  111. data/samples/show.sh +4 -0
  112. data/samples/skeptic.yaml +13 -0
  113. data/samples/skeptic_simple.yaml +9 -0
  114. data/samples/test.sh +2 -0
  115. data/samples/tests/omnitest/validators.rb +23 -0
  116. data/samples/verify.sh +3 -0
  117. data/scripts/bootstrap.ps1 +7 -0
  118. data/scripts/run_script.sh +4 -0
  119. data/skeptic.yaml +26 -0
  120. data/spec/fabricators/project_fabricator.rb +19 -0
  121. data/spec/fabricators/scenario_fabricator.rb +6 -0
  122. data/spec/fabricators/test_manifest_fabricator.rb +41 -0
  123. data/spec/fabricators/validator_fabricator.rb +12 -0
  124. data/spec/fixtures/factorial.py +18 -0
  125. data/spec/fixtures/omnitest.yaml +11 -0
  126. data/spec/fixtures/skeptic.yaml +16 -0
  127. data/spec/fixtures/src-doc/_scenario.md.erb +1 -0
  128. data/spec/fixtures/src-doc/quine.md.erb +20 -0
  129. data/spec/omnitest/cli_spec.rb +38 -0
  130. data/spec/omnitest/configuration_spec.rb +25 -0
  131. data/spec/omnitest/documentation_generator_spec.rb +59 -0
  132. data/spec/omnitest/file_finder_spec.rb +21 -0
  133. data/spec/omnitest/project_spec.rb +65 -0
  134. data/spec/omnitest_spec.rb +13 -0
  135. data/spec/spec_helper.rb +32 -0
  136. data/spec/thor_spy.rb +66 -0
  137. data/tests/omnitest/bootstrap_validations.rb +7 -0
  138. data/tests/omnitest/show_validations.rb +22 -0
  139. data/yard_macros.rb +25 -0
  140. metadata +470 -0
@@ -0,0 +1,148 @@
1
+ require 'json'
2
+ require 'tilt'
3
+ require 'haml'
4
+ require 'omnitest/reporters'
5
+
6
+ module Omnitest
7
+ module Command
8
+ class Generate
9
+ class Dashboard < Thor::Group
10
+ include Thor::Actions
11
+ include Omnitest::Core::FileSystem
12
+ module Helpers
13
+ include Omnitest::Core::Util::String
14
+ # include Padrino::Helpers::RenderHelpers # requires sinatra-compatible render method
15
+ include Padrino::Helpers::TagHelpers
16
+ include Padrino::Helpers::OutputHelpers
17
+ include Padrino::Helpers::AssetTagHelpers
18
+
19
+ def projects
20
+ Omnitest.projects.map do |project|
21
+ slugify(project.name)
22
+ end
23
+ end
24
+
25
+ def results
26
+ rows = []
27
+ grouped_scenarios = Omnitest.scenarios.group_by { |scenario| [scenario.suite, scenario.name] }
28
+ grouped_scenarios.each do |(suite, name), scenarios|
29
+ row = {
30
+ slug_prefix: slugify(suite, name),
31
+ suite: suite,
32
+ scenario: name
33
+ }
34
+ Omnitest.projects.each do |project|
35
+ scenario = scenarios.find { |s| s.psychic.name == project.name }
36
+ row[slugify(project.name)] = scenario.status_description
37
+ end
38
+ rows << row
39
+ end
40
+ rows
41
+ end
42
+
43
+ def as_json(data)
44
+ JSON.dump(data)
45
+ rescue => e
46
+ JSON.dump(to_utf(data))
47
+ end
48
+
49
+ def to_utf(data)
50
+ Hash[
51
+ data.collect do |k, v|
52
+ if v.respond_to?(:collect)
53
+ [k, to_utf(v)]
54
+ elsif v.respond_to?(:encoding)
55
+ [k, v.dup.encode('UTF-8')]
56
+ else
57
+ [k, v]
58
+ end
59
+ end
60
+ ]
61
+ end
62
+
63
+ def status(status, msg = nil, _color = :cyan)
64
+ "<strong>#{status}</strong> <em>#{msg}</em>"
65
+ end
66
+
67
+ def bootstrap_color(color)
68
+ bootstrap_classes = {
69
+ green: 'success',
70
+ cyan: 'primary',
71
+ red: 'danger',
72
+ yellow: 'warning'
73
+ }
74
+ bootstrap_classes.key?(color) ? bootstrap_classes[color] : color
75
+ end
76
+ end
77
+
78
+ include Helpers
79
+
80
+ class_option :destination, default: 'reports/'
81
+ class_option :code_style, default: 'github'
82
+
83
+ def self.source_root
84
+ Omnitest::Reporters::GENERATORS_DIR
85
+ end
86
+
87
+ def report_name
88
+ @report_name ||= self.class.name.downcase.split('::').last
89
+ end
90
+
91
+ def add_framework_to_source_root
92
+ source_paths.map do | path |
93
+ path << "/#{report_name}"
94
+ end
95
+ end
96
+
97
+ def set_destination_root
98
+ self.destination_root = options[:destination]
99
+ end
100
+
101
+ def setup
102
+ @tabs = {}
103
+ @tabs['Dashboard'] = 'dashboard.html'
104
+ Omnitest.update_config!(options)
105
+ Omnitest.setup
106
+ end
107
+
108
+ def create_spy_reports
109
+ reports = Omnitest::Skeptic::Spies.reports[:dashboard]
110
+ reports.each do | report_class |
111
+ if report_class.respond_to? :tab_name
112
+ @active_tab = report_class.tab_name
113
+ @tabs[@active_tab] = report_class.tab_target
114
+ else
115
+ @active_tab = nil
116
+ end
117
+ report_class.tabs = @tabs
118
+ invoke report_class, args, options
119
+ end if reports
120
+ end
121
+
122
+ def copy_assets
123
+ directory Omnitest::Reporters::ASSETS_DIR, 'assets'
124
+ end
125
+
126
+ def copy_base_structure
127
+ @active_tab = 'Dashboard'
128
+ directory 'files', '.'
129
+ end
130
+
131
+ def create_results_json
132
+ create_file 'matrix.json', as_json(results)
133
+ end
134
+
135
+ def create_test_reports
136
+ template_file = find_in_source_paths('templates/_test_report.html.haml')
137
+ template = Tilt.new(template_file)
138
+ Omnitest.scenarios.each do |scenario|
139
+ @scenario = scenario
140
+ add_file "details/#{scenario.slug}.html" do
141
+ template.render(self)
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,119 @@
1
+ require 'json'
2
+ require 'omnitest/reporters'
3
+
4
+ module Omnitest
5
+ module Command
6
+ class Generate
7
+ class Documentation < Thor::Group
8
+ include Thor::Actions
9
+ include Omnitest::Core::FileSystem
10
+ include Omnitest::Psychic::Code2Doc::CodeHelper
11
+ include Omnitest::Psychic::Code2Doc::SnippetHelper
12
+
13
+ BUILTIN_GENERATORS = Dir["#{Omnitest::Reporters::GENERATORS_DIR}/*"].select { |f| File.directory? f }
14
+
15
+ attr_reader :projects, :project, :project_name, :project_basedir
16
+
17
+ class << self
18
+ def generators
19
+ BUILTIN_GENERATORS + Dir['tests/omnitest/generators/*'].select { |f| File.directory? f }
20
+ end
21
+
22
+ def generator_names
23
+ generators.map { |d| File.basename d }
24
+ end
25
+
26
+ def generator_not_found(generator)
27
+ s = "ERROR: No generator named #{generator}, available generators: "
28
+ s << generator_names.join(', ')
29
+ end
30
+
31
+ def source_root
32
+ Omnitest::RESOURCES_DIR
33
+ end
34
+ end
35
+
36
+ argument :project_regexp, default: 'all'
37
+ argument :scenario_regexp, default: 'all'
38
+ class_option :source, default: 'doc-src/', desc: 'Source folder with documentation templates'
39
+ class_option :destination, default: 'docs/', desc: 'Destination for generated documentation'
40
+ class_option :template, desc: 'The name or location of a custom generator template'
41
+ class_option :scope, desc: 'Whether the template should be applied once (global), per project, or per scenario',
42
+ enum: %w(global project scenario), default: 'global'
43
+ class_option :failed, type: :boolean, desc: 'Only list tests that failed / passed'
44
+ class_option :skipped, type: :boolean, desc: 'Only list tests that were skipped / executed'
45
+ class_option :samples, type: :boolean, desc: 'Only list tests that have sample code / do not have sample code'
46
+ class_option :travis, type: :boolean, desc: "Enable/disable delegation to travis-build, if it's available"
47
+ def setup
48
+ Omnitest.update_config!(options)
49
+ Omnitest.setup
50
+ end
51
+
52
+ def set_source_and_destination
53
+ unless options[:template] || File.exist?(options[:source])
54
+ abort 'Either the --source directory must exist, or --template must be specified'
55
+ end
56
+
57
+ if options[:template]
58
+ generator = self.class.generators.find { |d| File.basename(d) == options[:template] }
59
+ abort self.class.generator_not_found(generator) if generator.nil?
60
+ source_paths << generator
61
+ else
62
+ source_paths << Pathname(options[:source]).expand_path
63
+ end
64
+
65
+ self.destination_root = options[:destination]
66
+ end
67
+
68
+ def apply_template
69
+ if options[:template]
70
+ generator_script = "#{options[:template]}_template.rb"
71
+ apply(generator_script)
72
+ else
73
+ case options[:scope]
74
+ when 'global'
75
+ @projects = Omnitest.filter_projects(project_regexp)
76
+ process_directory
77
+ when 'project'
78
+ Omnitest.filter_projects(project_regexp).each do | project |
79
+ bind_project_variables(project)
80
+ process_directory
81
+ end
82
+ when 'scenario'
83
+ Omnitest.scenarios(project_regexp, scenario_regexp).each do | scenario |
84
+ bind_scenario_variables(scenario)
85
+ process_directory
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def scenario_output_snippet(project_regex, scenario_regex, opts = {})
94
+ scenario = Omnitest.scenarios(project_regex, scenario_regex).first
95
+ fail "Output is not available for #{scenario_name} because that scenario does not exist" unless scenario
96
+ fail "Output is not available for #{scenario_name} because it has not been executed" unless scenario.result
97
+ snippetize_output(scenario.result, opts)
98
+ end
99
+
100
+ def process_directory
101
+ directory Pathname(options[:source]).expand_path, Pathname(options[:destination]).expand_path
102
+ end
103
+
104
+ def bind_project_variables(project)
105
+ @project = project
106
+ @project_name = project.name
107
+ @project_basedir = project.basedir
108
+ end
109
+
110
+ def bind_scenario_variables(scenario)
111
+ bind_project_variables(scenario.project)
112
+ @scenario = scenario
113
+ @scenario_name = scenario.name
114
+ @scenario_slug = scenario.slug
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,62 @@
1
+ require 'omnitest/reporters'
2
+ module Omnitest
3
+ module Command
4
+ class List < Omnitest::Command::Base
5
+ include Omnitest::Reporters
6
+
7
+ def call
8
+ setup
9
+ @reporter = Omnitest::Reporters.reporter(options[:format], shell)
10
+ tests = parse_subcommand(args.shift, args.shift)
11
+
12
+ table = [header_row]
13
+ table += tests.map do | scenario |
14
+ row(scenario)
15
+ end
16
+ print_table(table)
17
+ end
18
+
19
+ private
20
+
21
+ def header_row
22
+ row = []
23
+ row << colorize('Suite', :green)
24
+ row << colorize('Scenario', :green)
25
+ row << colorize('Project', :green)
26
+ row << colorize('Status', :green)
27
+ row << colorize('Source', :green) if options[:source]
28
+ row
29
+ end
30
+
31
+ def row(scenario)
32
+ row = []
33
+ row << color_pad(scenario.suite)
34
+ row << color_pad(scenario.name)
35
+ row << color_pad(scenario.psychic.name)
36
+ row << format_status(scenario)
37
+ if options[:source]
38
+ source_file = scenario.absolute_source_file ? scenario.source_file : colorize('<No code sample>', :red)
39
+ row << source_file
40
+ end
41
+ row
42
+ end
43
+
44
+ def print_table(*args)
45
+ @reporter.print_table(*args)
46
+ end
47
+
48
+ def colorize(string, *args)
49
+ return string unless @reporter.respond_to? :set_color
50
+ @reporter.set_color(string, *args)
51
+ end
52
+
53
+ def color_pad(string)
54
+ string + colorize('', :white)
55
+ end
56
+
57
+ def format_status(scenario)
58
+ colorize(scenario.status_description, scenario.status_color)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,26 @@
1
+ require 'benchmark'
2
+
3
+ module Omnitest
4
+ module Command
5
+ class ProjectAction < Omnitest::Command::Base
6
+ include RunAction
7
+
8
+ # Invoke the command.
9
+ def call
10
+ banner "Starting Omnitest (v#{Omnitest::VERSION})"
11
+ elapsed = Benchmark.measure do
12
+ setup
13
+ project_regex = args.shift
14
+ if %w(task workflow).include? action # a bit hacky...
15
+ argument = project_regex
16
+ project_regex = args.shift
17
+ args.unshift argument
18
+ end
19
+ projects = select_projects(project_regex, options)
20
+ run_action(projects, action, options[:concurrency], *args)
21
+ end
22
+ banner "Omnitest is finished. #{Core::Util.duration(elapsed.real)}"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ require 'benchmark'
2
+
3
+ module Omnitest
4
+ module Command
5
+ class ScenarioAction < Omnitest::Command::Base
6
+ include RunAction
7
+
8
+ # Invoke the command.
9
+ def call
10
+ banner "Starting Omnitest (v#{Omnitest::VERSION})"
11
+ elapsed = Benchmark.measure do
12
+ setup
13
+ scenarios = parse_subcommand(args.shift, args.shift)
14
+ run_action(scenarios, action, options[:concurrency])
15
+ end
16
+ banner "Omnitest is finished. #{Core::Util.duration(elapsed.real)}"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,148 @@
1
+ require 'omnitest/reporters'
2
+
3
+ module Omnitest
4
+ module Command
5
+ class Show < Omnitest::Command::Base # rubocop:disable Metrics/ClassLength
6
+ include Omnitest::Reporters
7
+ include Omnitest::Core::Util::String
8
+ include Omnitest::Core::FileSystem
9
+
10
+ def call
11
+ @indent_level = 0
12
+ setup
13
+ @reporter = Omnitest::Reporters.reporter(options[:format], shell)
14
+ scenarios = parse_subcommand(args.shift, args.shift)
15
+
16
+ scenarios.each do | scenario |
17
+ status_color = scenario.status_color.to_sym
18
+ status(scenario.slug, colorize(scenario.status_description, status_color), status_color)
19
+ indent do
20
+ status('Test suite', scenario.suite)
21
+ status('Test scenario', scenario.name)
22
+ status('Project', scenario.psychic.name)
23
+ source_file = if scenario.code_sample
24
+ Core::FileSystem.relativize(scenario.absolute_source_file, Dir.pwd)
25
+ else
26
+ colorize('<No code sample>', :red)
27
+ end
28
+ status('Source', source_file)
29
+ display_source(scenario)
30
+ display_execution_result(scenario)
31
+ display_validations(scenario)
32
+ display_spy_data(scenario)
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def reformat(string)
40
+ return if string.nil? || string.empty?
41
+
42
+ indent do
43
+ string.gsub(/^/, indent)
44
+ end
45
+ end
46
+
47
+ def indent
48
+ if block_given?
49
+ @indent_level += 2
50
+ result = yield
51
+ @indent_level -= 2
52
+ result
53
+ else
54
+ ' ' * @indent_level
55
+ end
56
+ end
57
+
58
+ def display_source(test)
59
+ return if !options[:source] || !test.source?
60
+
61
+ shell.say test.highlighted_code
62
+ end
63
+
64
+ def display_execution_result(test)
65
+ return if test.result.nil? || test.result.execution_result.nil?
66
+
67
+ execution_result = test.result.execution_result
68
+ status 'Execution result'
69
+ indent do
70
+ status('Exit Status', execution_result.exitstatus)
71
+ status 'Stdout'
72
+ say reformat(execution_result.stdout)
73
+ status 'Stderr'
74
+ say reformat(execution_result.stderr)
75
+ end
76
+ end
77
+
78
+ def display_validations(test)
79
+ return if test.validations.nil?
80
+
81
+ status 'Validations'
82
+ indent do
83
+ test.validations.each do | name, validation |
84
+ status(name, indicator(validation))
85
+ indent do
86
+ status 'Error message', validation.error if validation.error
87
+ unless !options[:source] || !validation.error_source?
88
+ status 'Validator source'
89
+ say highlight(validation.error_source, language: 'ruby')
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def display_spy_data(test)
97
+ return if test.spy_data.nil?
98
+
99
+ status 'Data from spies'
100
+ indent do
101
+ test.spy_data.each do |_spy, data|
102
+ indent do
103
+ data.each_pair do |k, v|
104
+ status(k, v)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ def say(msg)
112
+ shell.say msg if msg
113
+ end
114
+
115
+ def status(status, msg = nil, color = :cyan, colwidth = 50)
116
+ msg = yield if block_given?
117
+ shell.say(indent)
118
+ status = shell.set_color("#{status}:", color, true)
119
+ # The built-in say_status is right-aligned, we want left-aligned
120
+ shell.say format("%-#{colwidth}s %s", status, msg).rstrip
121
+ end
122
+
123
+ def print_table(*args)
124
+ @reporter.print_table(*args)
125
+ end
126
+
127
+ def colorize(string, *args)
128
+ return string unless @reporter.respond_to? :set_color
129
+ @reporter.set_color(string, *args)
130
+ end
131
+
132
+ def color_pad(string)
133
+ string + colorize('', :white)
134
+ end
135
+
136
+ def indicator(validation)
137
+ case validation.status
138
+ when :passed
139
+ colorize("\u2713 Passed", :green)
140
+ when :failed
141
+ colorize('x Failed', :red)
142
+ else
143
+ colorize(validation.status, :yellow)
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end