From 13db9cb9cef1e8cda927a933e2b6ce15eb1c908f Mon Sep 17 00:00:00 2001 From: Alyssa Wang Date: Fri, 25 Aug 2023 19:16:41 -0400 Subject: [PATCH 1/8] FI-2094: Improve tooltip a11y (#386) * update notistack * add focus to icons with tooltips * update snackbar styles * add copy tooltip * add tooltip to header icon --------- Co-authored-by: Alyssa Wang --- client/src/components/App/App.tsx | 21 +++++-- client/src/components/Header/Header.tsx | 9 ++- .../RequestDetailModal/RequestDetailModal.tsx | 2 +- .../TestSuite/TestSuiteDetails/ResultIcon.tsx | 57 ++++++++++++++----- .../TestGroupListItem/TestGroupListItem.tsx | 2 +- .../TestListItem/RequestList.tsx | 13 ++++- .../TestListItem/TestListItem.tsx | 9 ++- package-lock.json | 33 +++++------ package.json | 2 +- 9 files changed, 106 insertions(+), 42 deletions(-) diff --git a/client/src/components/App/App.tsx b/client/src/components/App/App.tsx index 28407f69c..e77059783 100644 --- a/client/src/components/App/App.tsx +++ b/client/src/components/App/App.tsx @@ -1,13 +1,21 @@ -import { SnackbarProvider } from 'notistack'; import React, { FC, useEffect } from 'react'; import { RouterProvider } from 'react-router-dom'; +import { Theme } from '@mui/material/styles'; +import { SnackbarProvider } from 'notistack'; import { getTestSuites } from '~/api/TestSuitesApi'; import { router } from '~/components/App/Router'; import { TestSuite } from '~/models/testSuiteModels'; import { useAppStore } from '~/store/app'; import { useTestSessionStore } from '~/store/testSession'; import SnackbarCloseButton from 'components/_common/SnackbarCloseButton'; -import lightTheme from '~/styles/theme'; +import { makeStyles } from 'tss-react/mui'; + +const useStyles = makeStyles<{ height: string }>()((theme: Theme, { height }) => ({ + container: { + marginBottom: height, + zIndex: `${theme.zIndex.snackbar} !important`, + }, +})); const App: FC = () => { const footerHeight = useAppStore((state) => state.footerHeight); @@ -18,6 +26,10 @@ const App: FC = () => { const setWindowIsSmall = useAppStore((state) => state.setWindowIsSmall); const testRunInProgress = useTestSessionStore((state) => state.testRunInProgress); + const { classes } = useStyles({ + height: testRunInProgress ? `${72 + footerHeight}px` : `${footerHeight}px`, + }); + // Update UI on window resize useEffect(() => { window.addEventListener('resize', handleResize); @@ -57,9 +69,8 @@ const App: FC = () => { horizontal: 'right', }} action={(id) => } - style={{ - marginBottom: testRunInProgress ? `${72 + footerHeight}px` : `${footerHeight}px`, - zIndex: lightTheme.zIndex.snackbar, + classes={{ + containerAnchorOriginBottomRight: classes.container, }} > diff --git a/client/src/components/Header/Header.tsx b/client/src/components/Header/Header.tsx index 6884c5df3..08c0a9d21 100644 --- a/client/src/components/Header/Header.tsx +++ b/client/src/components/Header/Header.tsx @@ -8,6 +8,7 @@ import { useAppStore } from '~/store/app'; import useStyles from './styles'; import icon from '~/images/inferno_icon.png'; import lightTheme from '~/styles/theme'; +import CustomTooltip from '../_common/CustomTooltip'; export interface HeaderProps { suiteId?: string; @@ -63,7 +64,13 @@ const Header: FC = ({ ) : ( - Inferno logo + + Inferno logo + )} diff --git a/client/src/components/RequestDetailModal/RequestDetailModal.tsx b/client/src/components/RequestDetailModal/RequestDetailModal.tsx index aead3f9bc..b387a1680 100644 --- a/client/src/components/RequestDetailModal/RequestDetailModal.tsx +++ b/client/src/components/RequestDetailModal/RequestDetailModal.tsx @@ -65,7 +65,7 @@ const RequestDetailModal: FC = ({ {request?.url} {request?.url && ( - + void copyTextClick(request.url)}> diff --git a/client/src/components/TestSuite/TestSuiteDetails/ResultIcon.tsx b/client/src/components/TestSuite/TestSuiteDetails/ResultIcon.tsx index 30c2539c3..47360e507 100644 --- a/client/src/components/TestSuite/TestSuiteDetails/ResultIcon.tsx +++ b/client/src/components/TestSuite/TestSuiteDetails/ResultIcon.tsx @@ -27,7 +27,11 @@ const ResultIcon: FC = ({ result, isRunning }) => { if (isRunning && result?.test_run_id !== testRunId) { return ( - + ); } else if (result) { @@ -36,6 +40,8 @@ const ResultIcon: FC = ({ result, isRunning }) => { return ( @@ -45,6 +51,8 @@ const ResultIcon: FC = ({ result, isRunning }) => { return ( @@ -54,6 +62,8 @@ const ResultIcon: FC = ({ result, isRunning }) => { return ( @@ -63,6 +73,8 @@ const ResultIcon: FC = ({ result, isRunning }) => { return ( @@ -71,13 +83,20 @@ const ResultIcon: FC = ({ result, isRunning }) => { case 'omit': return ( - + ); case 'error': return ( @@ -86,26 +105,38 @@ const ResultIcon: FC = ({ result, isRunning }) => { case 'wait': return ( - + ); default: return ( - + + + ); } } else { return ( - + + + ); } }; diff --git a/client/src/components/TestSuite/TestSuiteDetails/TestGroupListItem/TestGroupListItem.tsx b/client/src/components/TestSuite/TestSuiteDetails/TestGroupListItem/TestGroupListItem.tsx index fd587be04..9105c9863 100644 --- a/client/src/components/TestSuite/TestSuiteDetails/TestGroupListItem/TestGroupListItem.tsx +++ b/client/src/components/TestSuite/TestSuiteDetails/TestGroupListItem/TestGroupListItem.tsx @@ -126,7 +126,7 @@ const TestGroupListItem: FC = ({ data-testid={`${testGroup.id}-summary`} aria-controls={`${testGroup.id}-detail`} className={classes.accordionSummary} - expandIcon={view === 'run' && } + expandIcon={view === 'run' && } > diff --git a/client/src/components/TestSuite/TestSuiteDetails/TestListItem/RequestList.tsx b/client/src/components/TestSuite/TestSuiteDetails/TestListItem/RequestList.tsx index e410f1d39..d3675d715 100644 --- a/client/src/components/TestSuite/TestSuiteDetails/TestListItem/RequestList.tsx +++ b/client/src/components/TestSuite/TestSuiteDetails/TestListItem/RequestList.tsx @@ -69,7 +69,13 @@ const RequestList: FC = ({ requests, resultId, updateRequest, if (request.result_id !== resultId) { return ( - + ); } @@ -150,8 +156,9 @@ const RequestList: FC = ({ requests, resultId, updateRequest, = ({ data-testid={`${test.id}-summary`} aria-controls={`${test.id}-detail`} role={view === 'report' ? 'region' : 'button'} - expandIcon={view === 'run' && } + expandIcon={ + view === 'run' && ( + + + + ) + } className={classes.accordionSummary} onKeyDown={(e) => { if (view !== 'report' && e.key === 'Enter') { diff --git a/package-lock.json b/package-lock.json index 2efbf67dc..1add0b165 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@mui/material": "^5.11.13", "history": "^5.3.0", "js-yaml": "^4.1.0", - "notistack": "^2.0.8", + "notistack": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.8.1", @@ -11817,6 +11817,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/goober": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz", + "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -17558,31 +17566,24 @@ } }, "node_modules/notistack": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/notistack/-/notistack-2.0.8.tgz", - "integrity": "sha512-/IY14wkFp5qjPgKNvAdfL5Jp6q90+MjgKTPh4c81r/lW70KeuX6b9pE/4f8L4FG31cNudbN9siiFS5ql1aSLRw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.1.tgz", + "integrity": "sha512-ntVZXXgSQH5WYfyU+3HfcXuKaapzAJ8fBLQ/G618rn3yvSzEbnOB8ZSOwhX+dAORy/lw+GC2N061JA0+gYWTVA==", "dependencies": { "clsx": "^1.1.0", - "hoist-non-react-statics": "^3.3.0" + "goober": "^2.0.33" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/notistack" }, "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "@mui/material": "^5.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } } }, "node_modules/npm-run-path": { diff --git a/package.json b/package.json index 6dca570eb..3beac0ced 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "@mui/material": "^5.11.13", "history": "^5.3.0", "js-yaml": "^4.1.0", - "notistack": "^2.0.8", + "notistack": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.8.1", From 0452e643a4561af09245131026598a95b630697e Mon Sep 17 00:00:00 2001 From: Dylan Hall Date: Mon, 28 Aug 2023 16:59:17 -0400 Subject: [PATCH 2/8] FI-2035: Improve error handling for validator errors (#379) * FI-2035: improve error handling for validator errors * FI-2035: refactor resource_is_valid? and check for non-json responses * FI-2035: missed doco param --- lib/inferno/dsl/fhir_validation.rb | 63 +++++++++++++++++++----- lib/inferno/exceptions.rb | 10 ++++ spec/inferno/dsl/fhir_validation_spec.rb | 39 +++++++++++++++ 3 files changed, 101 insertions(+), 11 deletions(-) diff --git a/lib/inferno/dsl/fhir_validation.rb b/lib/inferno/dsl/fhir_validation.rb index 33aef90cd..7f1ae81c2 100644 --- a/lib/inferno/dsl/fhir_validation.rb +++ b/lib/inferno/dsl/fhir_validation.rb @@ -116,17 +116,26 @@ def exclude_message(&block) def resource_is_valid?(resource, profile_url, runnable) profile_url ||= FHIR::Definitions.resource_definition(resource.resourceType).url - outcome = FHIR::OperationOutcome.new(JSON.parse(validate(resource, profile_url))) + outcome, http_status = validate(resource, profile_url, runnable) - message_hashes = outcome.issue&.map { |issue| message_hash_from_issue(issue, resource) } || [] + message_hashes = message_hashes_from_outcome(outcome, resource, profile_url) - message_hashes.concat(additional_validation_messages(resource, profile_url)) + message_hashes + .each { |message_hash| runnable.add_message(message_hash[:type], message_hash[:message]) } - filter_messages(message_hashes) + unless http_status == 200 + raise Inferno::Exceptions::ErrorInValidatorException, + 'Error occurred in the validator. Review Messages tab or validator service logs for more information.' + end message_hashes - .each { |message_hash| runnable.add_message(message_hash[:type], message_hash[:message]) } .none? { |message_hash| message_hash[:type] == 'error' } + rescue Inferno::Exceptions::ErrorInValidatorException + raise + rescue StandardError => e + runnable.add_message('error', e.message) + raise Inferno::Exceptions::ErrorInValidatorException, + 'Error occurred in the validator. Review Messages tab or validator service logs for more information.' end # @private @@ -134,6 +143,17 @@ def filter_messages(message_hashes) message_hashes.reject! { |message| exclude_message.call(Entities::Message.new(message)) } if exclude_message end + # @private + def message_hashes_from_outcome(outcome, resource, profile_url) + message_hashes = outcome.issue&.map { |issue| message_hash_from_issue(issue, resource) } || [] + + message_hashes.concat(additional_validation_messages(resource, profile_url)) + + filter_messages(message_hashes) + + message_hashes + end + # @private def message_hash_from_issue(issue, resource) { @@ -171,12 +191,33 @@ def issue_message(issue, resource) # # @param resource [FHIR::Model] # @param profile_url [String] - # @return [String] the body of the validation response - def validate(resource, profile_url) - Faraday.new( - url, - params: { profile: profile_url } - ).post('validate', resource.source_contents).body + # @param runnable [Inferno::Entities::Test] + # @return [[Array(FHIR::OperationOutcome, Number)] the validation response and HTTP status code + def validate(resource, profile_url, runnable) + begin + response = Faraday.new( + url, + params: { profile: profile_url } + ).post('validate', resource.source_contents) + rescue StandardError => e + runnable.add_message('error', e.message) + raise Inferno::Exceptions::ErrorInValidatorException, "Unable to connect to validator at #{url}." + end + outcome = operation_outcome_from_validator_response(response.body, runnable) + + [outcome, response.status] + end + + # @private + def operation_outcome_from_validator_response(response, runnable) + if response.start_with? '{' + FHIR::OperationOutcome.new(JSON.parse(response)) + else + runnable.add_message('error', "Validator Response: #{response}") + raise Inferno::Exceptions::ErrorInValidatorException, + 'Validator response was an unexpected format. '\ + 'Review Messages tab or validator service logs for more information.' + end end end diff --git a/lib/inferno/exceptions.rb b/lib/inferno/exceptions.rb index dc3cc8e3b..bcd059594 100644 --- a/lib/inferno/exceptions.rb +++ b/lib/inferno/exceptions.rb @@ -39,6 +39,16 @@ def result end end + class ErrorInValidatorException < TestResultException + # This extends TestResultException instead of RuntimeError + # to bypass printing the stack trace in the UI. + # (The stack trace of this exception may not be useful, + # instead the message should point to where in the validator an error occurred) + def result + 'error' + end + end + class ParentNotLoadedException < RuntimeError def initialize(klass, id) super("No #{klass.name.demodulize} found with id '#{id}'") diff --git a/spec/inferno/dsl/fhir_validation_spec.rb b/spec/inferno/dsl/fhir_validation_spec.rb index 717edd159..0458e9d17 100644 --- a/spec/inferno/dsl/fhir_validation_spec.rb +++ b/spec/inferno/dsl/fhir_validation_spec.rb @@ -130,6 +130,45 @@ end end + context 'with error from validator' do + let(:error_outcome) do + { + resourceType: 'OperationOutcome', + issue: [ + { + severity: 'fatal', + code: 'structure', + diagnostics: 'Validator still warming up... Please wait', + details: { + text: 'Validator still warming up... Please wait' + } + } + ] + }.to_json + end + + it 'throws ErrorInValidatorException when validator not ready yet' do + stub_request(:post, "#{validation_url}/validate?profile=#{profile_url}") + .with(body: resource_string) + .to_return(status: 503, body: error_outcome) + + expect do + validator.resource_is_valid?(resource, profile_url, runnable) + end.to raise_error(Inferno::Exceptions::ErrorInValidatorException) + expect(runnable.messages.first[:message]).to include('Validator still warming up... Please wait') + end + + it 'throws ErrorInValidatorException for non-JSON response' do + stub_request(:post, "#{validation_url}/validate?profile=#{profile_url}") + .with(body: resource_string) + .to_return(status: 500, body: 'Internal Server Error') + + expect do + validator.resource_is_valid?(resource, profile_url, runnable) + end.to raise_error(Inferno::Exceptions::ErrorInValidatorException) + end + end + it 'posts the resource with primitive extensions intact' do stub_request(:post, "#{validation_url}/validate?profile=#{profile_url}") .with(body: resource_string) From 516a1abb24cd6a822f36022ae4535554795fe6e5 Mon Sep 17 00:00:00 2001 From: Emily Michaud <59289146+emichaud998@users.noreply.github.com> Date: Wed, 30 Aug 2023 11:56:15 -0400 Subject: [PATCH 3/8] Fix outdated documentation on Advanced Test Features page of framework documentation (#385) --- docs/advanced-test-features/serving-http-requests.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/advanced-test-features/serving-http-requests.md b/docs/advanced-test-features/serving-http-requests.md index 1498794ed..5fc095301 100644 --- a/docs/advanced-test-features/serving-http-requests.md +++ b/docs/advanced-test-features/serving-http-requests.md @@ -63,15 +63,15 @@ class MyTestSuite < Inferno::TestSuite id :my_test_suite my_html = File.read(File.join(__dir__, 'my_html.html')) - my_html_route_handler = proc { [200, { 'Content-Type' => 'text/html' }, [html]] } + my_html_route_handler = proc { [200, { 'Content-Type' => 'text/html' }, [my_html]] } - # Serve an html page at INFERNO_PATH/my_test_suite/custom/my_html_page + # Serve an html page at INFERNO_PATH/custom/my_test_suite/my_html_page route :get, '/my_html_page', my_html_route_handler my_jwks = File.read(File.join(__dir__, 'my_jwks.json')) my_jwks_route_handler = proc { [200, { 'Content-Type' => 'application/json' }, [my_jwks]] } - # Serve a JSON file at INFERNO_PATH/my_test_suite/custom/.well-known/jwks.json + # Serve a JSON file at INFERNO_PATH/custom/my_test_suite/.well-known/jwks.json route :get, '/.well-known/jwks.json', my_jwks_route_handler end ``` From ec2d182743c295a5ac72b3fbe5ce43eaf58892cc Mon Sep 17 00:00:00 2001 From: Alisa Wallace <140003092+alisawallace@users.noreply.github.com> Date: Thu, 31 Aug 2023 09:25:36 -0700 Subject: [PATCH 4/8] FI-2041: Custom suites with no ids now throw standard error (#387) * Custom suites with no ids now throw standard error Original bug: a critical error would result when running custom test suites with either a) a blank id field (e.g., id = '') b) no id field and the console output did not provide meaningful information to the source of the error. These conditions are now checked for and will throw a standard error with a specific message upon startup * Resolving linter failures * Resolving linter failures * Add suite name to error output Co-authored-by: Stephen MacVicar * Combining suite ID checks into single condition --------- Co-authored-by: Stephen MacVicar --- Gemfile.lock | 1 + lib/inferno/config/boot/suites.rb | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 4fdb00693..c761682d5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -325,6 +325,7 @@ PLATFORMS arm64-darwin-21 arm64-darwin-22 x86_64-darwin-20 + x86_64-darwin-22 x86_64-linux DEPENDENCIES diff --git a/lib/inferno/config/boot/suites.rb b/lib/inferno/config/boot/suites.rb index b698b3e55..0e621b774 100644 --- a/lib/inferno/config/boot/suites.rb +++ b/lib/inferno/config/boot/suites.rb @@ -25,5 +25,13 @@ end ObjectSpace.each_object(TracePoint, &:disable) + + Inferno::Entities::TestSuite.descendants.each do |descendant| + # When ID not assigned in custom test suites, Runnable.id will return default ID + # equal to the custom test suite's parent class name + if descendant.id.blank? || descendant.id == 'Inferno::Entities::TestSuite' + raise StandardError, "Error initializing test suite #{descendant.name}: test suite ID is not set" + end + end end end From 08ec136ed81cc42ea04c36b3e1800301ba763d71 Mon Sep 17 00:00:00 2001 From: Stephen MacVicar Date: Sat, 9 Sep 2023 13:32:48 -0400 Subject: [PATCH 5/8] FI-2156: Dependabot updates (#390) * bump puma * bump activesupport * update commonmarker --- Gemfile.lock | 4 ++-- docs/Gemfile.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c761682d5..5a960c04c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -30,7 +30,7 @@ PATH GEM remote: https://rubygems.org/ specs: - activesupport (6.1.7.3) + activesupport (6.1.7.6) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -217,7 +217,7 @@ GEM byebug (~> 11.0) pry (~> 0.13.0) public_suffix (4.0.7) - puma (5.6.6) + puma (5.6.7) nio4r (~> 2.0) racc (1.7.1) rack (2.2.6.4) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index e5cf68001..5295c70ee 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -14,7 +14,7 @@ GEM execjs coffee-script-source (1.11.1) colorator (1.1.0) - commonmarker (0.23.9) + commonmarker (0.23.10) concurrent-ruby (1.2.2) dnsruby (1.61.9) simpleidn (~> 0.1) From 8d88911760446750b98babae177bef01886b76bd Mon Sep 17 00:00:00 2001 From: Alisa Wallace <140003092+alisawallace@users.noreply.github.com> Date: Mon, 11 Sep 2023 08:15:25 -0700 Subject: [PATCH 6/8] FI-2086: fix errors on webpack shutdown (#389) * Changed from system call to exec call in main * Replaced npm start command with direct webpack invocation Because npm is not a process manager, it does not propagate SIGINTs to the scripts it starts (webpack in this case) - thus, doing Ctrl+C on the Foreman process will kill the npm process but not the webpack process. Replacing the "npm run start" command with whatever is listed under "start" in package.json bypasses this signal handling discrepancy. * Removed commented out code --- Procfile | 2 +- lib/inferno/apps/cli/main.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Procfile b/Procfile index 5f35ca5da..4433c1748 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,3 @@ web: bundle exec puma worker: bundle exec sidekiq -r ./worker.rb -webpack: npm run start +webpack: ./node_modules/.bin/webpack serve --config ./webpack.config.js --mode=development diff --git a/lib/inferno/apps/cli/main.rb b/lib/inferno/apps/cli/main.rb index 07e5368e5..ad7f1c353 100644 --- a/lib/inferno/apps/cli/main.rb +++ b/lib/inferno/apps/cli/main.rb @@ -36,7 +36,7 @@ def start command = "rerun \"#{command}\" --background" end - system command + exec command end desc 'suites', 'List available test suites' From 3b094281f423eab47ccbbe8f0e59ea1ee45c88c5 Mon Sep 17 00:00:00 2001 From: Stephen MacVicar Date: Tue, 12 Sep 2023 08:19:28 -0400 Subject: [PATCH 7/8] Release 0.4.19 (#391) * bump puma in gemspec * bump activesupport in gemspec * bump version * update changelog * fix changelog item formatting --- CHANGELOG.md | 19 +++++++++++++++++++ Gemfile.lock | 20 ++++++++++---------- inferno_core.gemspec | 4 ++-- lib/inferno/version.rb | 2 +- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0df4663d..f175a2ed8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +# 0.4.19 +* FI-2053: Fix inputs dialog overflow by @AlyssaWang in + https://github.com/inferno-framework/inferno-core/pull/382 +* FI-2038: Prevent modal close on edit by @AlyssaWang in + https://github.com/inferno-framework/inferno-core/pull/383 +* FI-2094: Improve tooltip a11y by @AlyssaWang in + https://github.com/inferno-framework/inferno-core/pull/386 +* FI-2035: Improve error handling for validator errors by @dehall in + https://github.com/inferno-framework/inferno-core/pull/379 +* FI-2070: Inferno Framework Documentation Advanced Test Features Information + Fix by @emichaud998 in + https://github.com/inferno-framework/inferno-core/pull/385 +* FI-2041: Custom suites with no ids now throw standard error by @alisawallace + in https://github.com/inferno-framework/inferno-core/pull/387 +* FI-2156: Dependabot updates by @Jammjammjamm in + https://github.com/inferno-framework/inferno-core/pull/390 +* FI-2086: fix errors on webpack shutdown by @alisawallace in + https://github.com/inferno-framework/inferno-core/pull/389 + # 0.4.18 * Fix a bug which could prevent some test results from appearing until the page is reloaded. diff --git a/Gemfile.lock b/Gemfile.lock index 5a960c04c..7f3f435a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,8 @@ PATH remote: . specs: - inferno_core (0.4.18) - activesupport (~> 6.1) + inferno_core (0.4.19) + activesupport (~> 6.1.7.5) base62-rb (= 0.3.1) blueprinter (= 0.25.2) dotenv (~> 2.7) @@ -19,7 +19,7 @@ PATH oj (= 3.11.0) pry pry-byebug - puma (~> 5.3) + puma (~> 5.6.7) rake (~> 13.0) sequel (~> 5.42.0) sidekiq (~> 6.5.6) @@ -178,7 +178,7 @@ GEM kramdown (2.4.0) rexml method_source (1.0.0) - mime-types (3.5.0) + mime-types (3.5.1) mime-types-data (~> 3.2015) mime-types-data (3.2023.0808) minitest (5.18.0) @@ -192,11 +192,11 @@ GEM mustermann (= 1.1.2) netrc (0.11.0) nio4r (2.5.9) - nokogiri (1.15.3-arm64-darwin) + nokogiri (1.15.4-arm64-darwin) racc (~> 1.4) - nokogiri (1.15.3-x86_64-darwin) + nokogiri (1.15.4-x86_64-darwin) racc (~> 1.4) - nokogiri (1.15.3-x86_64-linux) + nokogiri (1.15.4-x86_64-linux) racc (~> 1.4) oauth2 (1.4.11) faraday (>= 0.17.3, < 3.0) @@ -282,9 +282,9 @@ GEM simplecov (~> 0.19) simplecov-html (0.12.3) simplecov_json_formatter (0.1.3) - sqlite3 (1.6.3-arm64-darwin) - sqlite3 (1.6.3-x86_64-darwin) - sqlite3 (1.6.3-x86_64-linux) + sqlite3 (1.6.5-arm64-darwin) + sqlite3 (1.6.5-x86_64-darwin) + sqlite3 (1.6.5-x86_64-linux) strings (0.2.1) strings-ansi (~> 0.2) unicode-display_width (>= 1.5, < 3.0) diff --git a/inferno_core.gemspec b/inferno_core.gemspec index e3fb21184..b49d64cf8 100644 --- a/inferno_core.gemspec +++ b/inferno_core.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |spec| spec.description = 'Inferno Core is an open source tool for testing data exchanges enabled by the FHIR standand' spec.homepage = 'https://github.com/inferno-framework/inferno-core' spec.license = 'Apache-2.0' - spec.add_runtime_dependency 'activesupport', '~> 6.1' + spec.add_runtime_dependency 'activesupport', '~> 6.1.7.5' spec.add_runtime_dependency 'base62-rb', '0.3.1' spec.add_runtime_dependency 'blueprinter', '0.25.2' spec.add_runtime_dependency 'dotenv', '~> 2.7' @@ -28,7 +28,7 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'oj', '3.11.0' spec.add_runtime_dependency 'pry' spec.add_runtime_dependency 'pry-byebug' - spec.add_runtime_dependency 'puma', '~> 5.3' + spec.add_runtime_dependency 'puma', '~> 5.6.7' spec.add_runtime_dependency 'rake', '~> 13.0' spec.add_runtime_dependency 'sequel', '~> 5.42.0' spec.add_runtime_dependency 'sidekiq', '~> 6.5.6' diff --git a/lib/inferno/version.rb b/lib/inferno/version.rb index 0f6f1b3fd..3d5ed52cf 100644 --- a/lib/inferno/version.rb +++ b/lib/inferno/version.rb @@ -1,4 +1,4 @@ module Inferno # Standard patterns for gem versions: https://guides.rubygems.org/patterns/ - VERSION = '0.4.18'.freeze + VERSION = '0.4.19'.freeze end From 44da696bdb75db1635452d159d77db841a5b8500 Mon Sep 17 00:00:00 2001 From: Alyssa Wang Date: Tue, 12 Sep 2023 10:39:27 -0400 Subject: [PATCH 8/8] FI-2077: Improve colors and contrasts for a11y (#392) * convert input colors to secondary * bold modal action text * add focus border to buttons * update colors for tabs and buttons --------- Co-authored-by: Alyssa Wang --- .../components/InputsModal/InputCheckboxGroup.tsx | 1 + .../components/InputsModal/InputOAuthCredentials.tsx | 1 + .../src/components/InputsModal/InputRadioGroup.tsx | 2 +- client/src/components/InputsModal/InputTextArea.tsx | 1 + client/src/components/InputsModal/InputTextField.tsx | 1 + client/src/components/InputsModal/InputsModal.tsx | 4 +++- client/src/components/InputsModal/styles.tsx | 4 ++-- .../components/PresetsSelector/PresetsSelector.tsx | 4 ++-- .../RequestDetailModal/RequestDetailModal.tsx | 1 + .../TestSuite/TestSuiteDetails/ResultIcon.tsx | 4 ++-- .../TestSuiteDetails/TestListItem/TestListItem.tsx | 6 +++++- client/src/components/_common/ActionModal.tsx | 1 + client/src/components/_common/CustomTab.tsx | 2 +- .../_common/SelectionPanel/RadioSelection.tsx | 9 ++++++++- .../src/components/_common/SelectionPanel/styles.tsx | 5 +++++ client/src/index.css | 12 ++++++++++++ 16 files changed, 47 insertions(+), 11 deletions(-) diff --git a/client/src/components/InputsModal/InputCheckboxGroup.tsx b/client/src/components/InputsModal/InputCheckboxGroup.tsx index de65941f1..203ae7bb4 100644 --- a/client/src/components/InputsModal/InputCheckboxGroup.tsx +++ b/client/src/components/InputsModal/InputCheckboxGroup.tsx @@ -108,6 +108,7 @@ const InputCheckboxGroup: FC = ({ control={ = ({ value={oAuthCredentials[field.name as keyof OAuthCredentials]} className={classes.inputField} variant="standard" + color="secondary" fullWidth onChange={(event) => { const value = event.target.value; diff --git a/client/src/components/InputsModal/InputRadioGroup.tsx b/client/src/components/InputsModal/InputRadioGroup.tsx index 929826e5d..a784b38d9 100644 --- a/client/src/components/InputsModal/InputRadioGroup.tsx +++ b/client/src/components/InputsModal/InputRadioGroup.tsx @@ -66,7 +66,7 @@ const InputRadioGroup: FC = ({ {requirement.options?.list_options?.map((option, i) => ( } + control={} label={option.label} key={`radio-button-${i}`} /> diff --git a/client/src/components/InputsModal/InputTextArea.tsx b/client/src/components/InputsModal/InputTextArea.tsx index 4ebd01074..807f42f6e 100644 --- a/client/src/components/InputsModal/InputTextArea.tsx +++ b/client/src/components/InputsModal/InputTextArea.tsx @@ -25,6 +25,7 @@ const InputTextArea: FC = ({ requirement, index, inputsMap, id={`requirement${index}_input`} className={classes.inputField} variant="standard" + color="secondary" fullWidth label={} helperText={requirement.description} diff --git a/client/src/components/InputsModal/InputTextField.tsx b/client/src/components/InputsModal/InputTextField.tsx index 2374b8feb..841354628 100644 --- a/client/src/components/InputsModal/InputTextField.tsx +++ b/client/src/components/InputsModal/InputTextField.tsx @@ -30,6 +30,7 @@ const InputTextField: FC = ({ id={`requirement${index}_input`} className={classes.inputField} variant="standard" + color="secondary" fullWidth label={} helperText={requirement.description} diff --git a/client/src/components/InputsModal/InputsModal.tsx b/client/src/components/InputsModal/InputsModal.tsx index ad65e0764..97e94cef4 100644 --- a/client/src/components/InputsModal/InputsModal.tsx +++ b/client/src/components/InputsModal/InputsModal.tsx @@ -349,6 +349,7 @@ const InputsModal: FC = ({ input: classes.serialInput, }, }} + color="secondary" fullWidth multiline data-testid="serial-input" @@ -399,7 +400,7 @@ const InputsModal: FC = ({ color="secondary" data-testid="cancel-button" onClick={() => closeModal()} - sx={{ mr: 1 }} + sx={{ mr: 1, fontWeight: 'bold' }} > Cancel @@ -409,6 +410,7 @@ const InputsModal: FC = ({ disableElevation onClick={submitClicked} disabled={missingRequiredInput || invalidInput} + sx={{ fontWeight: 'bold' }} > Submit diff --git a/client/src/components/InputsModal/styles.tsx b/client/src/components/InputsModal/styles.tsx index c6327a761..75279782e 100644 --- a/client/src/components/InputsModal/styles.tsx +++ b/client/src/components/InputsModal/styles.tsx @@ -13,7 +13,7 @@ export default makeStyles()((theme: Theme) => ({ color: theme.palette.common.grayDarkest, }, '& label.Mui-focused': { - color: theme.palette.common.orangeDarkest, + color: theme.palette.secondary.main, }, '& label.Mui-disabled': { color: theme.palette.common.gray, @@ -36,7 +36,7 @@ export default makeStyles()((theme: Theme) => ({ margin: '8px 0', borderColor: theme.palette.common.grayLight, '&:focus-within': { - borderColor: theme.palette.primary.main, + borderColor: theme.palette.secondary.main, }, }, serialInput: { diff --git a/client/src/components/PresetsSelector/PresetsSelector.tsx b/client/src/components/PresetsSelector/PresetsSelector.tsx index 37645f195..94672b5ba 100644 --- a/client/src/components/PresetsSelector/PresetsSelector.tsx +++ b/client/src/components/PresetsSelector/PresetsSelector.tsx @@ -109,7 +109,7 @@ const PresetsSelector: FC = ({ presets, testSessionId, getSes diff --git a/client/src/components/TestSuite/TestSuiteDetails/ResultIcon.tsx b/client/src/components/TestSuite/TestSuiteDetails/ResultIcon.tsx index 47360e507..e516c5fec 100644 --- a/client/src/components/TestSuite/TestSuiteDetails/ResultIcon.tsx +++ b/client/src/components/TestSuite/TestSuiteDetails/ResultIcon.tsx @@ -53,7 +53,7 @@ const ResultIcon: FC = ({ result, isRunning }) => { @@ -64,7 +64,7 @@ const ResultIcon: FC = ({ result, isRunning }) => { diff --git a/client/src/components/TestSuite/TestSuiteDetails/TestListItem/TestListItem.tsx b/client/src/components/TestSuite/TestSuiteDetails/TestListItem/TestListItem.tsx index 9546e7d73..7fb97afb1 100644 --- a/client/src/components/TestSuite/TestSuiteDetails/TestListItem/TestListItem.tsx +++ b/client/src/components/TestSuite/TestSuiteDetails/TestListItem/TestListItem.tsx @@ -217,7 +217,11 @@ const TestListItem: FC = ({ TransitionProps={{ unmountOnExit: true }} onClick={handleAccordionClick} onKeyDown={(e) => { - if (e.key === 'Enter' || e.key === ' ') { + if (e.key === 'Enter') { + // Don't open/close accordion on enter + setTabIndex(findPopulatedTabIndex()); + } + if (e.key === ' ') { handleAccordionClick(); } }} diff --git a/client/src/components/_common/ActionModal.tsx b/client/src/components/_common/ActionModal.tsx index 45533a81f..9eb82fac4 100644 --- a/client/src/components/_common/ActionModal.tsx +++ b/client/src/components/_common/ActionModal.tsx @@ -31,6 +31,7 @@ const ActionModal: FC = ({ modalVisible, message, cancelTestRu disableElevation onClick={cancelTestRun} data-testid="cancel-button" + sx={{ fontWeight: 'bold' }} > Cancel diff --git a/client/src/components/_common/CustomTab.tsx b/client/src/components/_common/CustomTab.tsx index 507bc4d6c..e59a070f9 100644 --- a/client/src/components/_common/CustomTab.tsx +++ b/client/src/components/_common/CustomTab.tsx @@ -10,7 +10,7 @@ interface CustomTabProps { const CustomTab = styled((props: CustomTabProps) => )( ({ theme }) => ({ pointerEvents: 'auto', - fontWeight: 'bolder', + fontWeight: 'bold', '&:hover, :focus-within': { color: theme.palette.common.grayDarkest, }, diff --git a/client/src/components/_common/SelectionPanel/RadioSelection.tsx b/client/src/components/_common/SelectionPanel/RadioSelection.tsx index 8b6bff6af..bf9f6673f 100644 --- a/client/src/components/_common/SelectionPanel/RadioSelection.tsx +++ b/client/src/components/_common/SelectionPanel/RadioSelection.tsx @@ -3,6 +3,7 @@ import { Box, FormControl, FormControlLabel, FormLabel, Radio, RadioGroup } from import HelpOutlineOutlinedIcon from '@mui/icons-material/HelpOutlineOutlined'; import { RadioOption, RadioOptionSelection } from '~/models/selectionModels'; import CustomTooltip from '~/components/_common/CustomTooltip'; +import useStyles from '~/components/_common/SelectionPanel/styles'; export interface RadioSelectionProps { options: RadioOption[]; @@ -13,6 +14,7 @@ const RadioSelection: FC = ({ options, setSelections: setParentSelections, }) => { + const { classes } = useStyles(); const initialSelectedRadioOptions: RadioOptionSelection[] = options.map((option) => ({ // just grab the first to start // perhaps choices should be persisted in the URL to make it easy to share specific options @@ -39,7 +41,12 @@ const RadioSelection: FC = ({ return ( {options.map((option, i) => ( - + {option.title || option.id} {option.description && ( diff --git a/client/src/components/_common/SelectionPanel/styles.tsx b/client/src/components/_common/SelectionPanel/styles.tsx index 2c2e69625..9b7d2b5bf 100644 --- a/client/src/components/_common/SelectionPanel/styles.tsx +++ b/client/src/components/_common/SelectionPanel/styles.tsx @@ -2,6 +2,11 @@ import { Theme } from '@mui/material/styles'; import { makeStyles } from 'tss-react/mui'; export default makeStyles()((theme: Theme) => ({ + label: { + '& label.Mui-focused': { + color: theme.palette.common.orangeDarkest, + }, + }, optionsList: { display: 'flex', flexDirection: 'column', diff --git a/client/src/index.css b/client/src/index.css index 046864a33..981b5373a 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -22,4 +22,16 @@ body { overscroll-behavior-y: none; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; +} + +/* Keyboard-only focus indicator */ +button:focus-visible { + border: 1px solid #707070; +} + +/* Darken button backgrounds on hover on text styled buttons */ +button:hover { + &.MuiButton-text { + background-color: #cbd5df60; + } } \ No newline at end of file