-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fi 2962 execute outputters #541
Changes from 14 commits
288794a
09916bf
c6259e1
6e91e59
dc863a5
92c931a
47fed6d
49f9587
7576474
c3d682f
ab6723a
e07fdd7
590be67
eef78ab
1fb7e4a
b7aadbd
86b000b
65a177c
fcd2a1c
7ae50ec
f7f0d74
334867f
4e1d4ce
5770bb8
d8b1052
5618f4e
df5f0b2
aedfe06
3d8ff0f
96fa788
de121a3
157b3c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,4 +36,5 @@ group :test do | |
gem 'simplecov-cobertura' | ||
gem 'webmock' | ||
gem 'factory_bot' | ||
end | ||
end | ||
gem "tty-spinner", "~> 0.9.3" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,22 @@ | ||
require 'pastel' | ||
require 'active_support' | ||
require 'dry/inflector' | ||
require_relative '../../utils/verify_runnable' | ||
require_relative '../../utils/persist_inputs' | ||
require_relative 'execute/console_outputter' | ||
|
||
Dir[File.join(__dir__, 'execute', '*_outputter.rb')].each { |outputter| require outputter } | ||
|
||
module Inferno | ||
module CLI | ||
class Execute | ||
include ::Inferno::Utils::VerifyRunnable | ||
include ::Inferno::Utils::PersistInputs | ||
|
||
INFLECTOR = Dry::Inflector.new do |inflections| | ||
inflections.acronym 'JSON' | ||
end | ||
|
||
OUTPUTTER_WHITELIST = %w[console plain json quiet].freeze | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is all fine, but a Hash mapping the option to the outputter class would be a lot simpler. I don't think being dynamic buys us much here, since we would need to alter code here, as well as documentation when adding new outputters anyway. |
||
attr_accessor :options | ||
|
||
def self.suppress_output | ||
|
@@ -79,8 +86,14 @@ def print_help_and_exit | |
end | ||
|
||
def outputter | ||
# TODO: swap outputter based on options | ||
@outputter ||= Inferno::CLI::Execute::ConsoleOutputter.new | ||
unless OUTPUTTER_WHITELIST.include? options[:outputter] | ||
raise StandardError, | ||
"Unrecognized outputter #{options[:outputter]}" | ||
end | ||
|
||
@outputter ||= INFLECTOR.constantize( | ||
"Inferno::CLI::Execute::#{INFLECTOR.camelize(options[:outputter])}Outputter" | ||
).new | ||
end | ||
|
||
def selected_runnables | ||
|
@@ -155,7 +168,6 @@ def create_params(test_session, runnable) | |
end | ||
|
||
def dispatch_job(test_run) | ||
# TODO: move suppression to outputter? better suppression? | ||
if options[:verbose] | ||
Jobs.perform(Jobs::ExecuteTestRun, test_run.id, force_synchronous: true) | ||
else | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,15 @@ | ||
require 'pastel' | ||
require_relative '../../web/serializers/test_run' | ||
require_relative '../../web/serializers/result' | ||
require 'tty-spinner' | ||
require_relative 'json_outputter' | ||
|
||
module Inferno | ||
module CLI | ||
class Execute | ||
# @private | ||
class ConsoleOutputter | ||
COLOR = Pastel.new | ||
class ConsoleOutputter < JSONOutputter | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless I'm missing something, I don't think it makes sense for this to inherit from JSONOutputter. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It inherits |
||
CHECKMARK = "\u2713".freeze | ||
BAR = ('=' * 80).freeze | ||
SPINNER = TTY::Spinner.new('Running tests [:spinner]', format: :bouncing_ball, clear: true, output: $stdout) | ||
|
||
def print_start_message(options) | ||
puts '' | ||
|
@@ -19,9 +19,9 @@ def print_start_message(options) | |
end | ||
|
||
def print_around_run(_options) | ||
puts 'Running tests. This may take a while...' | ||
# TODO: spinner/progress bar | ||
SPINNER.auto_spin | ||
yield | ||
SPINNER.stop('done!') | ||
end | ||
|
||
def print_results(options, results) | ||
|
@@ -44,14 +44,18 @@ def print_results(options, results) | |
def print_end_message(options); end | ||
|
||
def print_error(options, exception) | ||
puts COLOR.red "Error: #{exception.full_message}" | ||
puts color.red "Error: #{exception.full_message}" | ||
verbose_print(options, exception.backtrace&.join('\n')) | ||
end | ||
|
||
# private | ||
|
||
def verbose_print(options, *args) | ||
print(COLOR.dim(*args)) if options[:verbose] | ||
print(color.dim(*args)) if options[:verbose] | ||
end | ||
|
||
def color | ||
@color ||= Pastel.new(enabled: $stdout.tty?) | ||
end | ||
|
||
def verbose_puts(options, *args) | ||
|
@@ -103,21 +107,21 @@ def format_outputs(result) | |
def format_result(result) # rubocop:disable Metrics/CyclomaticComplexity | ||
case result.result | ||
when 'pass' | ||
COLOR.bold.green(CHECKMARK, ' pass') | ||
color.bold.green(CHECKMARK, ' pass') | ||
when 'fail' | ||
COLOR.bold.red 'X fail' | ||
color.bold.red 'X fail' | ||
when 'skip' | ||
COLOR.yellow '* skip' | ||
color.yellow '* skip' | ||
when 'omit' | ||
COLOR.blue '* omit' | ||
color.blue '* omit' | ||
when 'error' | ||
COLOR.magenta 'X error' | ||
color.magenta 'X error' | ||
when 'wait' | ||
COLOR.bold '. wait' | ||
color.bold '. wait' | ||
when 'cancel' | ||
COLOR.red 'X cancel' | ||
color.red 'X cancel' | ||
when 'running' | ||
COLOR.bold '- running' | ||
color.bold '- running' | ||
else | ||
raise StandardError.new, "Unrecognized result #{result.result}" | ||
end | ||
|
@@ -130,19 +134,6 @@ def verbose_print_json_results(options, results) | |
verbose_puts(options, serialize(results)) | ||
verbose_puts(options, BAR) | ||
end | ||
|
||
def serialize(entity) | ||
case entity.class.to_s | ||
when 'Array' | ||
JSON.pretty_generate(entity.map { |item| JSON.parse serialize(item) }) | ||
when lambda { |x| | ||
defined?(x.constantize) && defined?("Inferno::Web::Serializers::#{x.split('::').last}".constantize) | ||
} | ||
"Inferno::Web::Serializers::#{entity.class.to_s.split('::').last}".constantize.render(entity) | ||
else | ||
raise StandardError, "CLI does not know how to serialize #{entity.class}" | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
require_relative '../../web/serializers/test_run' | ||
require_relative '../../web/serializers/result' | ||
|
||
module Inferno | ||
module CLI | ||
class Execute | ||
# @private | ||
class JSONOutputter | ||
def print_start_message(_options); end | ||
|
||
def print_around_run(_options, &) | ||
yield | ||
end | ||
|
||
def print_results(_options, results) | ||
puts serialize(results) | ||
end | ||
|
||
def print_end_message(_options); end | ||
|
||
def print_error(_options, exception) | ||
puts exception.to_json | ||
end | ||
|
||
def serialize(entity) | ||
case entity.class.to_s | ||
when 'Array' | ||
JSON.pretty_generate(entity.map { |item| JSON.parse serialize(item) }) | ||
when lambda { |x| | ||
defined?(x.constantize) && defined?("Inferno::Web::Serializers::#{x.split('::').last}".constantize) | ||
} | ||
"Inferno::Web::Serializers::#{entity.class.to_s.split('::').last}".constantize.render(entity) | ||
else | ||
raise StandardError, "CLI does not know how to serialize #{entity.class}" | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
require_relative 'console_outputter' | ||
|
||
module Inferno | ||
module CLI | ||
class Execute | ||
# @private | ||
class PlainOutputter < ConsoleOutputter | ||
# override to disable spinner | ||
def print_around_run(_options) | ||
puts 'Running tests. This may take a while...' | ||
yield | ||
end | ||
|
||
def print_error(_options, exception) | ||
puts "Error: #{exception.full_message(highlight: false)}" | ||
puts exception.backtrace&.join('\n') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this different from the console outputter version? Also, doesn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Plain outputter needs But the backtrace part was redundant so I'm removing that. |
||
end | ||
|
||
def color | ||
@color ||= Pastel.new(enabled: false) | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
module Inferno | ||
module CLI | ||
class Execute | ||
# @private | ||
class QuietOutputter | ||
# rubocop:disable Lint/UnusedMethodArgument | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't disable this rule. Prefix the arguments with underscore if they're not used. |
||
def print_start_message(options); end | ||
|
||
def print_around_run(options, &) | ||
yield | ||
end | ||
|
||
def print_results(options, results); end | ||
|
||
def print_end_message(options); end | ||
|
||
def print_error(options, exception) | ||
puts "Error: #{exception.full_message}" if options[:verbose] | ||
end | ||
# rubocop:enable Lint/UnusedMethodArgument | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
require_relative '../../../../../lib/inferno/apps/cli/execute/json_outputter' | ||
require_relative 'outputter_spec' | ||
|
||
RSpec.describe Inferno::CLI::Execute::JSONOutputter do # rubocop:disable RSpec/FilePath | ||
let(:instance) { described_class.new } | ||
let(:options) { { outputter: 'json', verbose: false } } | ||
|
||
include_examples 'outputter_spec', described_class | ||
|
||
describe '#serialize' do | ||
let(:test_results) { create_list(:result, 2) } | ||
|
||
it 'handles an array of test results without raising exception' do | ||
expect { instance.serialize(test_results) }.to_not raise_error(StandardError) | ||
end | ||
|
||
it 'returns JSON' do | ||
expect { JSON.parse(instance.serialize(test_results)) }.to_not raise_error(StandardError) | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would have to go in the gemspec.