Inputs and Outputs
Inputs and outputs provide a structured way to pass information into and out of tests. When a user initiates a test run, a modal is displayed for providing input values. When multiple tests are run together, the user will not be prompted for inputs that are populated by the output of a previous test in the run. Currently, all inputs and outputs are stored as strings.
Inputs
Defining Inputs
The input method defines an input. input can take several arguments, but
only the identifier is required:
identifier- (required) a name for this input. The input value is available in the run block using this name.title:- a title which is displayed in the UI.description:- a description which is displayed in the UI.type:- controls the type of HTML input element used in the UI. Currently there are 7 possible values:'text'- (default) a regular input field.'textarea'- for a text area input field.'radio'- for a radio button singular selection field.'checkbox'- for a checkbox field. In tests, a checkbox input is represented as an array of the selected values.'select'- for a select input field.'oauth_credentials'- a complex type for storing OAuth2 credentials. When used by a FHIR client, the access token will automatically refresh if possible.'authinfo'- a complex type for storing all the data required for various authorization protocols. See the AuthInfo section below.
default:- default value for the input.optional:- (default: false) whether the input is optional.options:- possible input option formats based on input type.list_options:- options for input formats that require a list of possible values (radio and checkbox). An array of hashes withlabelandvaluekeys.
locked:- (default: false) whether the user can alter the input’s value. Locking an input can force it to use a value from a previous test’s output, or the default value.hidden:- (default: false) hide the input from the UI. Must be used with eitheroptional: trueorlocked: true.enable_when- (optional) adds conditional UI visibility for the input, causing it to be displayed only when the indicated input has the specified value. The value is a hash with keysinput_name(the controlling input’s identifier as a string, e.g.'get_type'forinput :get_type) andvalue(a string matching that input’s current value).
Here is a simple example:
test do
input :url,
title: 'FHIR Server Url',
description: 'The base url for the FHIR server'
run do
# The input's identifier is :url, so its value is available via `url`
assert url.start_with?('https'), 'The server must support https'
end
end
Defining Multiple Inputs
It is possible to define multiple inputs in a single input call, though this
prevents the use of the additional properties listed above. This can be useful when a test
uses inputs that have been defined in a parent or sibling.
test do
input :input1, :input2, :input3, :input4
...
end
Inputs with List Options
For the radio or checkbox input types, a list of options must be provided.
The label is displayed to the user, and the value is the actual value that
is stored when the user selects that option.
test do
input :radio_input_example,
title: 'Example Radio Input',
type: 'radio',
options: {
list_options: [
{
label: 'Radio Option 1',
value: 'option_1'
},
{
label: 'Radio Option 2',
value: 'option_2'
}
]
}
input :checkbox_input_example,
title: 'Example Checkbox Input',
type: 'checkbox',
options: {
list_options: [
{
label: 'Checkbox Option 1',
value: 'option_1'
},
{
label: 'Checkbox Option 2',
value: 'option_2'
}
]
}
run do
if radio_input_example == 'option_1'
# ...
end
if radio_input_example == 'option_2'
# ...
end
if checkbox_input_example.include? 'option_1'
# ...
end
if checkbox_input_example.include? 'option_2'
# ...
end
end
end
It is possible to lock individual checkbox items. In the following example,
Item 1 is not locked, so the user can check and uncheck it freely. Item 2 is
checked because it is included in the default, and the user can not uncheck
it. Item 3 is unchecked because it is not included in the default, and the
user can not check it.
input :locked_checkbox_example,
title: 'Locked Checkbox Input Example',
type: 'checkbox',
default: ['value2'],
options: {
list_options: [
{
label: 'Item 1',
value: 'value1'
},
{
label: 'Item 2 - Locked checked',
value: 'value2',
locked: true
},
{
label: 'Item 3 - Locked unchecked',
value: 'value3',
locked: true
}
]
}
Hidden Inputs
The hidden: property (default: false) can be used to hide an input from the UI.
Hidden inputs must be either optional or locked. If neither is true, an error will be raised.
input :hidden_value,
title: 'Hidden Input',
hidden: true,
optional: true
Ordering Inputs
When a group or suite displays all of its descendants’ inputs, they may be in an
unintuitive order. They can be reordered using input_order.
group do
input_order :input_2, :input_1
test do
input :input_1
end
test do
input :input_2
end
end
Additional Input Instructions
If a developer wants to include additional input instructions, they can define input_instructions
which will be displayed above the inputs.
group do
input_instructions %(
Register Inferno as a standalone application using the following information:
* Redirect URI: `#{SMARTAppLaunch::AppRedirectTest.config.options[:redirect_uri]}`
Enter in the appropriate scope to enable patient-level access to all
relevant resources. If using SMART v2, v2-style scopes must be used. In
addition, support for the OpenID Connect (openid fhirUser), refresh tokens
(offline_access), and patient context (launch/patient) are required.
)
end
Conditional UI visibility for inputs
When a test needs related inputs but only some apply at a time, enable_when controls conditional visibility in the inputs modal only. It does not change how inputs are defined in the DSL or how values are read in run blocks. Use it when several inputs represent alternative paths for the same data—showing every field at once would clutter the modal or confuse users. A typical pattern is a single radio or select choice that reveals only the fields relevant to the selected method.
Usage:
enable_when: { input_name: '<controlling_input>', value: '<string>' }—valueis a string matching the controlling input’s stored value (for example, alist_optionsvalueon a radio or select input).input_nameis the controlling input’s identifier (the symbol name as a string, e.g.'get_type'forinput :get_type).- The controlling input should be a radio or select input (single-valued). Checkbox inputs are not supported as controllers.
- Dependent inputs can use any normal input type (
text,textarea, etc.). - Unlike
hidden:, which always hides an input,enable_whenis dynamic and updates as the user changes the controlling input. - Required-field checks in the modal still apply to inputs that are hidden by
enable_when.
A select input works the same way as radio for the controlling field.
The following example models supplying a FHIR Bundle in one of three mutually exclusive ways: paste JSON, provide a URL, or run a $summary operation (FHIR server URL and patient ID).
group do
id 'conditional_group'
title 'Conditional Inputs Group'
optional
test 'Conditional, optional, empty input test' do
input :get_type, title: 'How to get Bundle', type: 'radio', options: {
list_options: [
{ value: 'copy_paste', label: 'Paste JSON' },
{ value: 'url', label: 'URL to FHIR Bundle' },
{ value: 'summary_op', label: '$summary Operation' }
]
}, default: 'copy_paste'
input :bundle_copy_paste, title: 'Paste JSON', type: 'textarea', optional: true,
enable_when: { input_name: 'get_type', value: 'copy_paste' }
input :bundle_url, title: 'URL to FHIR Bundle', type: 'text', optional: true,
enable_when: { input_name: 'get_type', value: 'url' }
input :fhir_server_url, title: 'FHIR Server URL', type: 'text', optional: true,
enable_when: { input_name: 'get_type', value: 'summary_op' }
input :patient_id, title: 'Patient ID', type: 'text', optional: true,
enable_when: { input_name: 'get_type', value: 'summary_op' }
run { pass }
end
end
In this example:
:get_typeis the controlling radio input.default: 'copy_paste'means the paste field is visible when the modal first opens.:bundle_copy_pasteis shown only whenget_typeis'copy_paste'.:bundle_urlis shown only whenget_typeis'url'.:fhir_server_urland:patient_idboth use the sameenable_whenfor'summary_op'—multiple dependent inputs can share one condition.- All dependent inputs are
optional: trueso hidden fields do not block submitting the modal.
Outputs
Defining Outputs
The output method defines an output. It is used in a test’s definition block
to define which outputs a test provides, and within a test’s run block to
assign a value to an output. Multiple outputs can be defined and assigned at once.
test do
output :value1
output :value2, :value3
run do
output value1: 'ABC'
output value2: 'DEF',
value3: 'GHI'
end
end
test do
# These inputs will automatically get their values from the previous test's
# outputs.
input :value1, :value2, :value3
...
end
output for defining outputs in the API docs
output for assigning values to outputs in the API
docs
Handling Complex Objects
Since inputs and outputs are all stored as strings, special handling is needed
if passing complex objects between tests. This can
generally be handled using JSON serialization. Ruby hashes and arrays, as well
as FHIR model classes, support the to_json method, which turns an object into a JSON
string.
test do
output :complex_object_json
run do
...
output complex_object_json: hash_or_array_or_fhir_resource.to_json
end
end
test do
input :complex_object_json
run do
assert_valid_json(complex_object_json) # For safety
complex_object = JSON.parse(complex_object_json)
...
end
end
AuthInfo
AuthInfo is a complex input
type used to store the information needed to perform a SMART App Launch
authorization workflow, make authorized
FHIR requests, and automatically refresh the authorization when needed. It
supports public, confidential symmetric, confidential asymmetric, and backend
services launches.
Configuring AuthInfo
A single AuthInfo input contains many input fields. These fields are listed in
the AuthInfo
attributes.
Each of these fields can be configured like a regular input using the
components option.
# An auth info that only allows backend services auth
input :backend_services_auth_info,
options: {
components: [
{
name: :auth_type,
default: 'backend_services',
locked: 'true'
}
]
}
# Limit the auth types to those in SMART App Launch v1
input :smart_v1_auth_info,
options: {
components: [
{
name: :auth_type,
list_options: [
{ label: 'Public', value: 'public' },
{ label: 'Confidential Symmetric', value: 'symmetric' }
]
}
]
}
input :smart_auth_info,
options: {
components: [
# This method configures the auth_type component to allow the user to
# select any auth type except backend services
Inferno::DSL::AuthInfo.default_auth_type_component_without_backend_services,
{
name: :requested_scopes,
default: 'launch/patient openid fhirUser offline_access patient/*.rs'
},
{
name: :pkce_support,
default: 'enabled',
locked: true
},
{
name: :pkce_code_challenge_method,
default: 'S256',
locked: true
},
{
name: :use_discovery,
locked: true
}
]
}
AuthInfo also has a mode property which affects how the input is displayed
in the UI. In 'auth' mode, the input will display all of the fields which are
needed to perform an initial authorization workflow. In 'access' mode, the
input will only display the fields needed to make authorized requests and
perform a token refresh.
# Use this to perform an initial authorization workflow
input :auth_info1,
options: {
mode: 'auth'
}
# Use this to make authorized requests
input :auth_info2,
options: {
mode: 'access'
}
Behind the Scenes
Inputs and outputs work as a single key-value store scoped to a test session. The main differences between them are:
- An input’s value can not be changed during a test
- Inputs support additional metadata for display in the UI (title, description, etc.)
Since inputs and outputs form a single key-value store, a value will be overwritten if multiple tests write to the same output. However, each test result stores the input and output values that were present for that particular test.
Merging Behavior (Advanced)
When inputs are defined at multiple levels (e.g., group and test), Inferno merges them. The following rules apply:
locked,hidden,enable_when, andtypeare not inherited when merging input definitions between parent and child.- Other attributes such as
title,description,default, andoptionalare inherited.
This allows different test scopes to override specific input behaviors without affecting others.
Additionally, when inputs are merged with external configuration, type and options are also excluded from automatic merging.
Suggest an improvement
Want to make an change? Contribute an edit for this page on the Inferno Framework GitHub repository.