From 17fd17036418dc143136b111e1a7c7195c9940d6 Mon Sep 17 00:00:00 2001 From: Alyssa Wang Date: Wed, 13 Nov 2024 20:29:52 -0500 Subject: [PATCH 1/4] add child selector to input css rules (#537) --- client/src/components/InputsModal/styles.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/InputsModal/styles.tsx b/client/src/components/InputsModal/styles.tsx index 320228919..69e2d6c63 100644 --- a/client/src/components/InputsModal/styles.tsx +++ b/client/src/components/InputsModal/styles.tsx @@ -13,13 +13,13 @@ export default makeStyles()((theme: Theme) => ({ fontWeight: 600, color: theme.palette.common.grayDarkest, }, - '& label.Mui-focused': { + '& > label.Mui-focused': { color: theme.palette.secondary.main, }, - '& label.Mui-disabled': { + '& > label.Mui-disabled': { color: theme.palette.common.gray, }, - '& label.Mui-error': { + '& > label.Mui-error': { color: theme.palette.error.main, }, }, From af1e650225140e87cd873d5b52f6f0511bacc7e0 Mon Sep 17 00:00:00 2001 From: Shaumik-Ashraf Date: Fri, 15 Nov 2024 12:18:34 -0500 Subject: [PATCH 2/4] Fi 2962 execute outputters (#541) * add quiet outputter * add plain outputter * impl json outputters and outputter option * debug * spec and cop * keep job silencer in execute; add spinner * write outputter_spec and json_outputter_spec * spec plain and quiet outputters * cop * add in-code documentation * move tty-spinner gem to gemspec * replace OUTPUTTERS_WHITELIST with OUTPUTTERS hash * remove redundant backtrace print * fix quiet outputter unused variables * cop * debug * fix docs * remove serialize standard error; use demodulize * factorize serializer and remove JSONOutputter from ConsoleOutputter ancestors * create serialize_spec, rspec, and cop * yank spinner * rm tty-spinner gem * cop --- lib/inferno/apps/cli/execute.rb | 21 ++++++--- .../apps/cli/execute/console_outputter.rb | 47 +++++++------------ .../apps/cli/execute/json_outputter.rb | 28 +++++++++++ .../apps/cli/execute/plain_outputter.rb | 18 +++++++ .../apps/cli/execute/quiet_outputter.rb | 22 +++++++++ lib/inferno/apps/cli/execute/serialize.rb | 21 +++++++++ lib/inferno/apps/cli/main.rb | 16 ++++++- .../cli/execute/console_outputter_spec.rb | 13 +---- .../cli/execute/json_outputter_spec.rb | 6 +++ spec/inferno/cli/execute/outputter_spec.rb | 33 +++++++++++++ .../cli/execute/plain_outputter_spec.rb | 20 ++++++++ .../cli/execute/quiet_outputter_spec.rb | 26 ++++++++++ spec/inferno/cli/execute/serialize_spec.rb | 18 +++++++ spec/inferno/cli/execute_spec.rb | 43 +++++------------ 14 files changed, 253 insertions(+), 79 deletions(-) create mode 100644 lib/inferno/apps/cli/execute/json_outputter.rb create mode 100644 lib/inferno/apps/cli/execute/plain_outputter.rb create mode 100644 lib/inferno/apps/cli/execute/quiet_outputter.rb create mode 100644 lib/inferno/apps/cli/execute/serialize.rb create mode 100644 spec/inferno/cli/execute/json_outputter_spec.rb create mode 100644 spec/inferno/cli/execute/outputter_spec.rb create mode 100644 spec/inferno/cli/execute/plain_outputter_spec.rb create mode 100644 spec/inferno/cli/execute/quiet_outputter_spec.rb create mode 100644 spec/inferno/cli/execute/serialize_spec.rb diff --git a/lib/inferno/apps/cli/execute.rb b/lib/inferno/apps/cli/execute.rb index a57049b59..3a03d10e8 100644 --- a/lib/inferno/apps/cli/execute.rb +++ b/lib/inferno/apps/cli/execute.rb @@ -1,9 +1,8 @@ -require 'pastel' require 'active_support' require_relative '../../utils/verify_runnable' require_relative '../../utils/persist_inputs' -require_relative 'execute/console_outputter' -require_relative '../../result_summarizer' + +Dir[File.join(__dir__, 'execute', '*_outputter.rb')].each { |outputter| require outputter } module Inferno module CLI @@ -11,6 +10,13 @@ class Execute include ::Inferno::Utils::VerifyRunnable include ::Inferno::Utils::PersistInputs + OUTPUTTERS = { + 'console' => Inferno::CLI::Execute::ConsoleOutputter, + 'plain' => Inferno::CLI::Execute::PlainOutputter, + 'json' => Inferno::CLI::Execute::JSONOutputter, + 'quiet' => Inferno::CLI::Execute::QuietOutputter + }.freeze + attr_accessor :options def self.suppress_output @@ -88,8 +94,12 @@ def print_help_and_exit end def outputter - # TODO: swap outputter based on options - @outputter ||= Inferno::CLI::Execute::ConsoleOutputter.new + unless OUTPUTTERS.key? options[:outputter] + raise StandardError, + "Unrecognized outputter #{options[:outputter]}" + end + + @outputter ||= OUTPUTTERS[options[:outputter]].new end def all_selected_groups_and_tests @@ -164,7 +174,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 diff --git a/lib/inferno/apps/cli/execute/console_outputter.rb b/lib/inferno/apps/cli/execute/console_outputter.rb index 66658579e..6235f1abb 100644 --- a/lib/inferno/apps/cli/execute/console_outputter.rb +++ b/lib/inferno/apps/cli/execute/console_outputter.rb @@ -1,16 +1,16 @@ require 'pastel' -require_relative '../../web/serializers/test_run' -require_relative '../../web/serializers/result' +require_relative 'serialize' module Inferno module CLI class Execute # @private class ConsoleOutputter - COLOR = Pastel.new CHECKMARK = "\u2713".freeze BAR = ('=' * 80).freeze + include Serialize + def print_start_message(options) puts '' puts BAR @@ -23,7 +23,6 @@ def print_start_message(options) def print_around_run(_options) puts 'Running tests. This may take a while...' - # TODO: spinner/progress bar yield end @@ -46,15 +45,18 @@ def print_results(options, results) def print_end_message(options); end - def print_error(options, exception) - puts COLOR.red "Error: #{exception.full_message}" - verbose_print(options, exception.backtrace&.join('\n')) + def print_error(_options, exception) + puts color.red "Error: #{exception.full_message}" 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) @@ -106,21 +108,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 @@ -133,19 +135,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 diff --git a/lib/inferno/apps/cli/execute/json_outputter.rb b/lib/inferno/apps/cli/execute/json_outputter.rb new file mode 100644 index 000000000..4691bd660 --- /dev/null +++ b/lib/inferno/apps/cli/execute/json_outputter.rb @@ -0,0 +1,28 @@ +require_relative 'serialize' + +module Inferno + module CLI + class Execute + # @private + class JSONOutputter + include Serialize + + 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 + end + end + end +end diff --git a/lib/inferno/apps/cli/execute/plain_outputter.rb b/lib/inferno/apps/cli/execute/plain_outputter.rb new file mode 100644 index 000000000..eb0b2f94d --- /dev/null +++ b/lib/inferno/apps/cli/execute/plain_outputter.rb @@ -0,0 +1,18 @@ +require_relative 'console_outputter' + +module Inferno + module CLI + class Execute + # @private + class PlainOutputter < ConsoleOutputter + def print_error(_options, exception) + puts "Error: #{exception.full_message(highlight: false)}" + end + + def color + @color ||= Pastel.new(enabled: false) + end + end + end + end +end diff --git a/lib/inferno/apps/cli/execute/quiet_outputter.rb b/lib/inferno/apps/cli/execute/quiet_outputter.rb new file mode 100644 index 000000000..a76f76ae5 --- /dev/null +++ b/lib/inferno/apps/cli/execute/quiet_outputter.rb @@ -0,0 +1,22 @@ +module Inferno + module CLI + class Execute + # @private + class QuietOutputter + 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 + end + end + end +end diff --git a/lib/inferno/apps/cli/execute/serialize.rb b/lib/inferno/apps/cli/execute/serialize.rb new file mode 100644 index 000000000..f843fb071 --- /dev/null +++ b/lib/inferno/apps/cli/execute/serialize.rb @@ -0,0 +1,21 @@ +require 'active_support' +require_relative '../../web/serializers/test_run' +require_relative '../../web/serializers/result' + +module Inferno + module CLI + class Execute + # @private + module Serialize + def serialize(entity) + case entity.class.to_s + when 'Array' + JSON.pretty_generate(entity.map { |item| JSON.parse serialize(item) }) + else + Inferno::Web::Serializers.const_get(entity.class.to_s.demodulize).render(entity) + end + end + end + end + end +end diff --git a/lib/inferno/apps/cli/main.rb b/lib/inferno/apps/cli/main.rb index 1a08db1c4..5b926b28c 100644 --- a/lib/inferno/apps/cli/main.rb +++ b/lib/inferno/apps/cli/main.rb @@ -74,6 +74,10 @@ def version You can view suite ids with: `bundle exec inferno suites` + You can select an output format with the `--outputter` option. Current outputters + are console (default), plain, quiet, and json. JSON-formatted output will copy + Inferno's REST API: https://inferno-framework.github.io/inferno-core/api-docs/#/Result. + Examples: (These examples only work from within the inferno_core directory). @@ -88,6 +92,12 @@ def version patient_id:1234321 \ --tests 1.01 1.02` => Run specific tests from suite + + `bundle exec inferno execute --suite dev_validator \ + --inputs "url:https://hapi.fhir.org/baseR4" \ + patient_id:1234321 \ + --outputter json` + => Outputs test results in JSON END_OF_HELP desc 'execute', 'Run Inferno tests in command line' long_desc EXECUTE_HELP, wrap: false @@ -97,7 +107,7 @@ def version desc: 'Test suite id to run or to select groups and tests from', banner: 'id' option :suite_options, - aliases: ['-u'], # NOTE: -o will be for outputter + aliases: ['-u'], type: :hash, desc: 'Suite options' option :groups, @@ -116,6 +126,10 @@ def version aliases: ['-i'], type: :hash, desc: 'Inputs (i.e: --inputs=foo:bar goo:baz)' + option :outputter, + aliases: ['-o'], + default: 'console', + desc: 'Select an outputter format: console | plain | json | quiet' option :verbose, aliases: ['-v'], type: :boolean, diff --git a/spec/inferno/cli/execute/console_outputter_spec.rb b/spec/inferno/cli/execute/console_outputter_spec.rb index b82d95b38..7fda528bc 100644 --- a/spec/inferno/cli/execute/console_outputter_spec.rb +++ b/spec/inferno/cli/execute/console_outputter_spec.rb @@ -1,20 +1,11 @@ require_relative '../../../../lib/inferno/apps/cli/execute/console_outputter' +require_relative 'outputter_spec' RSpec.describe Inferno::CLI::Execute::ConsoleOutputter do let(:instance) { described_class.new } let(:options) { { verbose: true } } - 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 valid JSON' do - expect { JSON.parse(instance.serialize(test_results)) }.to_not raise_error(StandardError) - end - end + include_examples 'outputter_spec', described_class describe '#verbose_print' do it 'outputs when verbose is true' do diff --git a/spec/inferno/cli/execute/json_outputter_spec.rb b/spec/inferno/cli/execute/json_outputter_spec.rb new file mode 100644 index 000000000..5b0127b1e --- /dev/null +++ b/spec/inferno/cli/execute/json_outputter_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../../../lib/inferno/apps/cli/execute/json_outputter' +require_relative 'outputter_spec' + +RSpec.describe Inferno::CLI::Execute::JSONOutputter do + include_examples 'outputter_spec', described_class +end diff --git a/spec/inferno/cli/execute/outputter_spec.rb b/spec/inferno/cli/execute/outputter_spec.rb new file mode 100644 index 000000000..25243b5cf --- /dev/null +++ b/spec/inferno/cli/execute/outputter_spec.rb @@ -0,0 +1,33 @@ +RSpec.shared_examples 'outputter_spec' do |outputter_class| + it 'responds to print_start_message' do + expect(outputter_class.new).to respond_to(:print_start_message) + end + + it 'responds to print_around_run' do + expect(outputter_class.new).to respond_to(:print_around_run) + end + + it 'method print_around_run yields' do + expect do + expect { |b| outputter_class.new.print_around_run({}, &b) }.to yield_control + end.to output(/.?/).to_stdout_from_any_process # required to prevent output in rspec + end + + it 'responds to print_results' do + expect(outputter_class.new).to respond_to(:print_results) + end + + it 'responds to print_end_message' do + expect(outputter_class.new).to respond_to(:print_end_message) + end + + it 'responds to print_error' do + expect(outputter_class.new).to respond_to(:print_error) + end + + it 'returns an object whose print_error does not raise exception nor exit' do + expect do + expect { outputter_class.new.print_error({}, StandardError.new('my error')) }.to_not raise_error + end.to output(/.?/).to_stdout # required to prevent output in rspec + end +end diff --git a/spec/inferno/cli/execute/plain_outputter_spec.rb b/spec/inferno/cli/execute/plain_outputter_spec.rb new file mode 100644 index 000000000..5384214af --- /dev/null +++ b/spec/inferno/cli/execute/plain_outputter_spec.rb @@ -0,0 +1,20 @@ +require_relative '../../../../lib/inferno/apps/cli/execute/plain_outputter' +require_relative 'outputter_spec' + +RSpec.describe Inferno::CLI::Execute::PlainOutputter do + let(:instance) { described_class.new } + let(:results) { create_list(:result, 2) } + let(:options) { { outputter: 'plain', verbose: true } } + + include_examples 'outputter_spec', described_class + + it 'never outputs a color code' do + expect do + instance.print_start_message(options) + instance.print_around_run(options) { ' ' } + instance.print_results(options, results) + instance.print_end_message(options) + # instance.print_error(options, StandardError.new('Mock Error')) + end.to_not output(/\033/).to_stdout + end +end diff --git a/spec/inferno/cli/execute/quiet_outputter_spec.rb b/spec/inferno/cli/execute/quiet_outputter_spec.rb new file mode 100644 index 000000000..439688d7c --- /dev/null +++ b/spec/inferno/cli/execute/quiet_outputter_spec.rb @@ -0,0 +1,26 @@ +require_relative '../../../../lib/inferno/apps/cli/execute/quiet_outputter' +require_relative 'outputter_spec' + +RSpec.describe Inferno::CLI::Execute::QuietOutputter do + let(:instance) { described_class.new } + let(:results) { create_list(:result, 2) } + + include_examples 'outputter_spec', described_class + + it 'never outputs when verbose is false' do + options = { outputter: 'quiet', verbose: false } + + expect do + instance.print_start_message(options) + instance.print_around_run(options) { ' ' } + instance.print_results(options, results) + instance.print_end_message(options) + instance.print_error(options, StandardError.new('Mock Error')) + end.to_not output(/./).to_stdout + end + + it 'outputs error when verbose is true' do + options = { outputter: 'quiet', verbose: true } + expect { instance.print_error(options, StandardError.new('Mock Error')) }.to output.to_stdout + end +end diff --git a/spec/inferno/cli/execute/serialize_spec.rb b/spec/inferno/cli/execute/serialize_spec.rb new file mode 100644 index 000000000..097bae132 --- /dev/null +++ b/spec/inferno/cli/execute/serialize_spec.rb @@ -0,0 +1,18 @@ +require_relative '../../../../lib/inferno/apps/cli/execute/serialize' + +RSpec.describe Inferno::CLI::Execute::Serialize do + let(:dummy_class) { Class.new { include Inferno::CLI::Execute::Serialize } } + let(:instance) { dummy_class.new } + + 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 diff --git a/spec/inferno/cli/execute_spec.rb b/spec/inferno/cli/execute_spec.rb index 9460fd4da..37969ce64 100644 --- a/spec/inferno/cli/execute_spec.rb +++ b/spec/inferno/cli/execute_spec.rb @@ -26,37 +26,16 @@ end describe '#outputter' do - it 'returns an object that responds to print_start_message' do - expect(instance.outputter).to respond_to(:print_start_message) - end - - it 'returns an object that responds to print_around_run' do - expect(instance.outputter).to respond_to(:print_around_run) - end - - it 'returns an object whose print_around_run yields' do - expect do - expect { |b| instance.outputter.print_around_run({}, &b) }.to yield_control - end.to output(/.?/).to_stdout # required to prevent output in rspec - end - - it 'returns an object that responds to print_results' do - expect(instance.outputter).to respond_to(:print_results) - end - - it 'returns an object that responds to print_end_message' do - expect(instance.outputter).to respond_to(:print_end_message) - end - - it 'returns an object that responds to print_error' do - expect(instance.outputter).to respond_to(:print_error) - end - - it 'returns an object whose print_error does not raise exception nor exit' do - allow(instance).to receive(:options).and_return({}) - expect do - expect { instance.outputter.print_error({}, StandardError.new('my error')) }.to_not raise_error - end.to output(/.?/).to_stdout # required to prevent output in rspec + { + 'console' => Inferno::CLI::Execute::ConsoleOutputter, + 'plain' => Inferno::CLI::Execute::PlainOutputter, + 'json' => Inferno::CLI::Execute::JSONOutputter, + 'quiet' => Inferno::CLI::Execute::QuietOutputter + }.each do |selected_outputter, expected_class| + it "returns #{expected_class} given '#{selected_outputter}'" do + allow(instance).to receive(:options).and_return({ outputter: selected_outputter }) + expect(instance.outputter).to be_an_instance_of expected_class + end end end @@ -268,7 +247,7 @@ .to_return(status: 200, body: FHIR::Patient.new({ name: { given: 'Smith' } }).to_json) expect do - expect { instance.run({ suite:, inputs:, verbose: true }) } + expect { instance.run({ suite:, inputs:, outputter: 'plain', verbose: true }) } .to raise_error(an_instance_of(SystemExit).and(having_attributes(status: 0))) end.to output(/.+/).to_stdout end From 050f4e8a013bdcd255b80b3ae068cf87bec32cbe Mon Sep 17 00:00:00 2001 From: Alyssa Wang Date: Wed, 20 Nov 2024 12:45:49 -0500 Subject: [PATCH 3/4] use input options for auth selection if they exist (#560) --- .../InputsModal/AuthTypeSelector.tsx | 58 +++++++++---------- .../components/InputsModal/InputAccess.tsx | 2 +- .../src/components/InputsModal/InputAuth.tsx | 2 +- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/client/src/components/InputsModal/AuthTypeSelector.tsx b/client/src/components/InputsModal/AuthTypeSelector.tsx index 922687c74..6979e209e 100644 --- a/client/src/components/InputsModal/AuthTypeSelector.tsx +++ b/client/src/components/InputsModal/AuthTypeSelector.tsx @@ -1,55 +1,55 @@ import React, { FC } from 'react'; -import { TestInput } from '~/models/testSuiteModels'; +import { InputOption, TestInput } from '~/models/testSuiteModels'; import InputCombobox from './InputCombobox'; export interface InputAccessProps { - requirement: TestInput; + input: TestInput; index: number; inputsMap: Map; setInputsMap: (map: Map, edited?: boolean) => void; } -const AuthTypeSelector: FC = ({ - requirement, - index, - inputsMap, - setInputsMap, -}) => { - const selectorSettings = requirement.options?.components - ? requirement.options?.components[0] +const AuthTypeSelector: FC = ({ input, index, inputsMap, setInputsMap }) => { + const selectorSettings = input.options?.components + ? input.options?.components[0] : // Default auth type settings { name: 'auth_type', default: 'public', }; + const selectorOptions: InputOption[] = + input.options?.components?.find((component) => component.name === 'auth_type')?.options + ?.list_options || + ([ + { + label: 'Public', + value: 'public', + }, + { + label: 'Confidential Symmetric', + value: 'symmetric', + }, + { + label: 'Confidential Asymmetric', + value: 'asymmetric', + }, + { + label: 'Backend Services', + value: 'backend_services', + }, + ] as InputOption[]); + const selectorModel: TestInput = { name: 'auth_type', type: 'select', title: 'Auth Type', - description: requirement.description, + description: input.description, default: selectorSettings.default || 'public', optional: selectorSettings.optional, locked: selectorSettings.locked, options: { - list_options: [ - { - label: 'Public', - value: 'public', - }, - { - label: 'Confidential Symmetric', - value: 'symmetric', - }, - { - label: 'Confidential Asymmetric', - value: 'asymmetric', - }, - { - label: 'Backend Services', - value: 'backend_services', - }, - ], + list_options: selectorOptions, }, }; diff --git a/client/src/components/InputsModal/InputAccess.tsx b/client/src/components/InputsModal/InputAccess.tsx index 7c2b6bdac..cc321c82e 100644 --- a/client/src/components/InputsModal/InputAccess.tsx +++ b/client/src/components/InputsModal/InputAccess.tsx @@ -126,7 +126,7 @@ const InputAccess: FC = ({ requirement, index, inputsMap, setI )} = ({ requirement, index, inputsMap, setInput )} Date: Thu, 21 Nov 2024 08:11:00 -0500 Subject: [PATCH 4/4] FI-3475: Unit testing improvements (#559) * add runnable helpers * update resume_test_route to support unit testing tests with no parent * update runnable specs * update template for runnable helper --- inferno_core.gemspec | 3 +- lib/inferno/apps/cli/templates/Gemfile.tt | 4 ++ .../%library_name%/patient_group_spec.rb.tt | 13 +----- .../apps/cli/templates/spec/spec_helper.rb.tt | 2 +- lib/inferno/dsl/resume_test_route.rb | 2 +- lib/inferno/spec_support.rb | 8 ++++ spec/inferno/entities/test_group_spec.rb | 2 + spec/inferno/entities/test_suite_spec.rb | 1 + spec/runnable_context.rb | 43 +++++++++++++++++++ spec/runnable_helper.rb | 11 +++++ spec/spec_helper.rb | 2 +- 11 files changed, 75 insertions(+), 16 deletions(-) create mode 100644 spec/runnable_context.rb create mode 100644 spec/runnable_helper.rb diff --git a/inferno_core.gemspec b/inferno_core.gemspec index d0ae92b94..18d139e12 100644 --- a/inferno_core.gemspec +++ b/inferno_core.gemspec @@ -56,7 +56,8 @@ Gem::Specification.new do |spec| 'lib/inferno/public/assets.json', 'spec/support/factory_bot.rb', Dir['spec/factories/**/*.rb'], - Dir['spec/fixtures/**/*.rb'] + Dir['spec/fixtures/**/*.rb'], + Dir['spec/*.rb'] ].flatten spec.bindir = 'bin' diff --git a/lib/inferno/apps/cli/templates/Gemfile.tt b/lib/inferno/apps/cli/templates/Gemfile.tt index 382378ec4..65d5b3a1d 100644 --- a/lib/inferno/apps/cli/templates/Gemfile.tt +++ b/lib/inferno/apps/cli/templates/Gemfile.tt @@ -7,3 +7,7 @@ gemspec group :development, :test do gem 'debug' end + +group :test do + gem 'rack-test' +end \ No newline at end of file diff --git a/lib/inferno/apps/cli/templates/spec/%library_name%/patient_group_spec.rb.tt b/lib/inferno/apps/cli/templates/spec/%library_name%/patient_group_spec.rb.tt index 58c462d7c..262a8050f 100644 --- a/lib/inferno/apps/cli/templates/spec/%library_name%/patient_group_spec.rb.tt +++ b/lib/inferno/apps/cli/templates/spec/%library_name%/patient_group_spec.rb.tt @@ -1,8 +1,6 @@ RSpec.describe <%= module_name %>::PatientGroup do - let(:suite) { Inferno::Repositories::TestSuites.new.find('<%= test_suite_id %>') } + let(:suite_id) { '<%= test_suite_id %>' } let(:group) { suite.groups[1] } - let(:session_data_repo) { Inferno::Repositories::SessionData.new } - let(:test_session) { repo_create(:test_session, test_suite_id: '<%= test_suite_id %>') } let(:url) { 'http://example.com/fhir' } let(:success_outcome) do { @@ -25,15 +23,6 @@ RSpec.describe <%= module_name %>::PatientGroup do } end - def run(runnable, inputs = {}) - test_run_params = { test_session_id: test_session.id }.merge(runnable.reference_hash) - test_run = Inferno::Repositories::TestRuns.new.create(test_run_params) - inputs.each do |name, value| - session_data_repo.save(test_session_id: test_session.id, name: name, value: value, type: 'text') - end - Inferno::TestRunner.new(test_session: test_session, test_run: test_run).run(runnable) - end - describe 'read test' do let(:test) { group.tests.first } let(:patient_id) { 'abc123' } diff --git a/lib/inferno/apps/cli/templates/spec/spec_helper.rb.tt b/lib/inferno/apps/cli/templates/spec/spec_helper.rb.tt index f30b83fca..5ef8a0b9e 100644 --- a/lib/inferno/apps/cli/templates/spec/spec_helper.rb.tt +++ b/lib/inferno/apps/cli/templates/spec/spec_helper.rb.tt @@ -127,7 +127,7 @@ Inferno::Utils::Migration.new.run require 'inferno' Inferno::Application.finalize! -require Inferno::SpecSupport::FACTORY_BOT_SUPPORT_PATH +Inferno::SpecSupport.require_helpers FactoryBot.definition_file_paths = [ Inferno::SpecSupport::FACTORY_PATH diff --git a/lib/inferno/dsl/resume_test_route.rb b/lib/inferno/dsl/resume_test_route.rb index c1b44ffdb..0f8937b6f 100644 --- a/lib/inferno/dsl/resume_test_route.rb +++ b/lib/inferno/dsl/resume_test_route.rb @@ -72,7 +72,7 @@ def find_test(waiting_result) # @private def resume_ui_at_id(test_run, test) - test_run.test_suite_id || test_run.test_group_id || test.parent.id + test_run.test_suite_id || test_run.test_group_id || test.parent&.id || test.id end # @private diff --git a/lib/inferno/spec_support.rb b/lib/inferno/spec_support.rb index 85725c78a..f1f8b8ce7 100644 --- a/lib/inferno/spec_support.rb +++ b/lib/inferno/spec_support.rb @@ -5,5 +5,13 @@ module Inferno module SpecSupport FACTORY_BOT_SUPPORT_PATH = File.expand_path('../../spec/support/factory_bot', __dir__).freeze FACTORY_PATH = File.expand_path('../../spec/factories', __dir__).freeze + REQUEST_HELPER_PATH = File.expand_path('../../spec/request_helper', __dir__).freeze + RUNNABLE_HELPER_PATH = File.expand_path('../../spec/runnable_helper', __dir__).freeze + + def self.require_helpers + require FACTORY_BOT_SUPPORT_PATH + require RUNNABLE_HELPER_PATH + require REQUEST_HELPER_PATH + end end end diff --git a/spec/inferno/entities/test_group_spec.rb b/spec/inferno/entities/test_group_spec.rb index 090bbfc7b..2182ca9db 100644 --- a/spec/inferno/entities/test_group_spec.rb +++ b/spec/inferno/entities/test_group_spec.rb @@ -1,4 +1,6 @@ RSpec.describe Inferno::Entities::TestGroup do + let(:suite_id) { 'basic' } + describe '.group' do let!(:subgroup_class) { Class.new(described_class) } let!(:group_class) { Class.new(described_class) } diff --git a/spec/inferno/entities/test_suite_spec.rb b/spec/inferno/entities/test_suite_spec.rb index 56c138a71..80d0efd22 100644 --- a/spec/inferno/entities/test_suite_spec.rb +++ b/spec/inferno/entities/test_suite_spec.rb @@ -1,5 +1,6 @@ RSpec.describe Inferno::Entities::TestSuite do let!(:suite_class) { Class.new(described_class) } + let(:suite_id) { 'basic' } describe '.group' do let!(:group_class) { Class.new(Inferno::Entities::TestGroup) } diff --git a/spec/runnable_context.rb b/spec/runnable_context.rb new file mode 100644 index 000000000..78e2e5761 --- /dev/null +++ b/spec/runnable_context.rb @@ -0,0 +1,43 @@ +RSpec.shared_context('when testing a runnable') do + let(:suite) { Inferno::Repositories::TestSuites.new.find(suite_id) } + let(:session_data_repo) { Inferno::Repositories::SessionData.new } + let(:validation_url) { "#{ENV.fetch('FHIR_RESOURCE_VALIDATOR_URL')}/validate" } + let(:test_session) { repo_create(:test_session, test_suite_id: suite_id) } + + before do + allow(described_class).to receive(:suite).and_return(suite) if described_class.parent.nil? + rescue NameError + raise StandardError, "No suite id defined. Add `let(:suite_id) { 'your_suite_id' }` to the spec" + end + + def run(runnable, inputs = {}) + test_run_params = { test_session_id: test_session.id }.merge(runnable.reference_hash) + test_run = Inferno::Repositories::TestRuns.new.create(test_run_params) + inputs.each do |name, value| + session_data_repo.save( + test_session_id: test_session.id, + name:, + value:, + type: runnable.config.input_type(name) + ) + end + + Inferno::TestRunner.new(test_session:, test_run:).run(runnable) + end + + # depth-first search looking for a runnable with a runtime id + # (prefixed with the ancestor suite / group ids) that ends + # with the provided suffix. It can be the test's id if unique, or + # can include some ancestor context if needed to identify the + # correct test. The first matching test found will be returned. + def find_test(runnable, id_suffix) + return runnable if runnable.id.ends_with?(id_suffix) + + runnable.children.each do |entity| + found = find_test(entity, id_suffix) + return found unless found.nil? + end + + nil + end +end diff --git a/spec/runnable_helper.rb b/spec/runnable_helper.rb new file mode 100644 index 000000000..cbe6ae6ff --- /dev/null +++ b/spec/runnable_helper.rb @@ -0,0 +1,11 @@ +require_relative 'runnable_context' + +RSpec.configure do |config| + config.define_derived_metadata do |metadata| + if metadata[:described_class].present? && metadata[:described_class].is_a?(Inferno::DSL::Runnable) + metadata[:runnable] = true + end + end + + config.include_context 'when testing a runnable', runnable: true +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ceb3784c0..f82674f72 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -139,7 +139,7 @@ Inferno::Utils::Migration.new.run Inferno::Application.finalize! -require_relative 'support/factory_bot' +Inferno::SpecSupport.require_helpers RSpec::Matchers.define_negated_matcher :exclude, :include require 'fhir_client'