diff --git a/apps/smart-forms-app/public/config.json b/apps/smart-forms-app/public/config.json
index 6e664f82a..6c063a838 100644
--- a/apps/smart-forms-app/public/config.json
+++ b/apps/smart-forms-app/public/config.json
@@ -3,5 +3,6 @@
"formsServerUrl": "https://smartforms.csiro.au/api/fhir",
"defaultClientId": "a57d90e3-5f69-4b92-aa2e-2992180863c1",
"launchScopes": "launch openid fhirUser online_access patient/AllergyIntolerance.cs patient/Condition.cs patient/Encounter.r patient/Immunization.cs patient/Medication.r patient/MedicationStatement.cs patient/Observation.cs patient/Patient.r patient/QuestionnaireResponse.crus user/Practitioner.r launch/questionnaire?role=http://ns.electronichealth.net.au/smart/role/new",
- "registeredClientIdsUrl": "https://smartforms.csiro.au/smart-config/config.json"
+ "registeredClientIdsUrl": "https://smartforms.csiro.au/smart-config/config.json",
+ "showDeveloperMessages": true
}
diff --git a/apps/smart-forms-app/src/features/configChecker/utils/config.ts b/apps/smart-forms-app/src/features/configChecker/utils/config.ts
index 671a384c4..e5ad4d320 100644
--- a/apps/smart-forms-app/src/features/configChecker/utils/config.ts
+++ b/apps/smart-forms-app/src/features/configChecker/utils/config.ts
@@ -38,6 +38,11 @@ export interface ConfigFile {
// "https://example.com/fhir": "6cc9bccb-3ae2-40d7-9660-22c99534520b"
// }
registeredClientIdsUrl: string | null;
+
+ // Controls whether developer-focused messages are shown.
+ // Set to false for clinical/production deployments to hide technical messages.
+ // Defaults to true if not specified (shows messages for backward compatibility).
+ showDeveloperMessages?: boolean;
}
export interface AppConfig extends ConfigFile {
@@ -53,6 +58,7 @@ export const FALLBACK_CONFIG: AppConfig = {
launchScopes:
'launch openid fhirUser online_access patient/AllergyIntolerance.cs patient/Condition.cs patient/Encounter.r patient/Immunization.cs patient/Medication.r patient/MedicationStatement.cs patient/Observation.cs patient/Patient.r patient/QuestionnaireResponse.crus user/Practitioner.r launch/questionnaire?role=http://ns.electronichealth.net.au/smart/role/new',
registeredClientIdsUrl: null,
+ showDeveloperMessages: true,
registeredClientIds: null
};
diff --git a/apps/smart-forms-app/src/features/prepopulate/hooks/usePopulate.tsx b/apps/smart-forms-app/src/features/prepopulate/hooks/usePopulate.tsx
index 34062942f..d4cf0118b 100644
--- a/apps/smart-forms-app/src/features/prepopulate/hooks/usePopulate.tsx
+++ b/apps/smart-forms-app/src/features/prepopulate/hooks/usePopulate.tsx
@@ -15,9 +15,10 @@
* limitations under the License.
*/
-import { useState } from 'react';
+import { useState, useContext } from 'react';
import CloseSnackbar from '../../../components/Snackbar/CloseSnackbar.tsx';
import { useSnackbar } from 'notistack';
+import { ConfigContext } from '../../configChecker/contexts/ConfigContext.tsx';
import {
buildForm,
useQuestionnaireResponseStore,
@@ -44,6 +45,7 @@ function usePopulate(spinner: RendererSpinner, onStopSpinner: () => void): void
const [isPopulated, setIsPopulated] = useState(false);
const { enqueueSnackbar } = useSnackbar();
+ const { config } = useContext(ConfigContext);
// Do not run population if spinner purpose is "repopulate"
if (status !== 'prepopulate') {
@@ -127,10 +129,14 @@ function usePopulate(spinner: RendererSpinner, onStopSpinner: () => void): void
onStopSpinner();
if (issues) {
- enqueueSnackbar(
- 'Form partially populated, there might be pre-population issues. View console for details.',
- { action: }
- );
+ // Only show the snackbar message if developer messages are enabled
+ if (config.showDeveloperMessages ?? true) {
+ enqueueSnackbar(
+ 'Form partially populated, there might be pre-population issues. View console for details.',
+ { action: }
+ );
+ }
+ // Always log to console - clinicians won't see this, but developers need it
console.warn(issues);
return;
}
diff --git a/apps/smart-forms-app/src/features/prepopulate/test/usePopulate.test.tsx b/apps/smart-forms-app/src/features/prepopulate/test/usePopulate.test.tsx
index eeb6c7375..fa0b7d699 100644
--- a/apps/smart-forms-app/src/features/prepopulate/test/usePopulate.test.tsx
+++ b/apps/smart-forms-app/src/features/prepopulate/test/usePopulate.test.tsx
@@ -45,6 +45,24 @@ jest.mock('../../../components/Snackbar/CloseSnackbar', () => {
};
});
+// Mock ConfigContext
+const mockConfigContext = {
+ config: {
+ showDeveloperMessages: true
+ },
+ configLoading: false,
+ configValid: true,
+ configError: null,
+ configErrorType: null,
+ currentClientId: 'test-client-id',
+ onSetCurrentClientId: jest.fn()
+};
+
+jest.mock('react', () => ({
+ ...jest.requireActual('react'),
+ useContext: jest.fn(() => mockConfigContext)
+}));
+
// Create mock store functions that will be used in the module mock
const mockSourceQuestionnaire = jest.fn();
const mockSourceResponse = jest.fn();
@@ -373,7 +391,7 @@ describe('usePopulate', () => {
});
});
- it('should handle successful population with issues', async () => {
+ it('should handle successful population with issues (developer messages enabled)', async () => {
const issues = {
resourceType: 'OperationOutcome' as const,
issue: [
@@ -408,13 +426,67 @@ describe('usePopulate', () => {
additionalContext: undefined
});
expect(mockOnStopSpinner).toHaveBeenCalled();
+ // Snackbar should be shown when developer messages are enabled
expect(mockEnqueueSnackbar).toHaveBeenCalledWith(
'Form partially populated, there might be pre-population issues. View console for details.',
{ action: expect.anything() }
);
+ // Console warning should always be shown
+ expect(consoleSpy).toHaveBeenCalledWith(issues);
+
+ consoleSpy.mockRestore();
+ });
+
+ it('should handle successful population with issues (developer messages disabled)', async () => {
+ // Temporarily set showDeveloperMessages to false
+ const originalShowDeveloperMessages = mockConfigContext.config.showDeveloperMessages;
+ mockConfigContext.config.showDeveloperMessages = false;
+
+ const issues = {
+ resourceType: 'OperationOutcome' as const,
+ issue: [
+ {
+ severity: 'warning' as const,
+ code: 'incomplete' as const,
+ diagnostics: 'Some fields could not be populated'
+ }
+ ]
+ };
+ const spinner = createSpinner(true, 'prepopulate');
+
+ mockPopulateQuestionnaire.mockResolvedValue({
+ populateSuccess: true,
+ populateResult: {
+ populatedResponse: mockResponse,
+ issues
+ }
+ });
+
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
+
+ renderHook(() => usePopulate(spinner, mockOnStopSpinner));
+
+ // Wait for async operations
+ await new Promise((resolve) => setTimeout(resolve, 10));
+
+ expect(mockBuildForm).toHaveBeenCalledWith({
+ questionnaire: mockQuestionnaire,
+ questionnaireResponse: mockResponse,
+ terminologyServerUrl: 'https://test-terminology-server.com',
+ additionalContext: undefined
+ });
+ expect(mockOnStopSpinner).toHaveBeenCalled();
+ // Snackbar should NOT be shown when developer messages are disabled
+ expect(mockEnqueueSnackbar).not.toHaveBeenCalledWith(
+ 'Form partially populated, there might be pre-population issues. View console for details.',
+ { action: expect.anything() }
+ );
+ // Console warning should still be shown (developers can still see it)
expect(consoleSpy).toHaveBeenCalledWith(issues);
consoleSpy.mockRestore();
+ // Restore original value
+ mockConfigContext.config.showDeveloperMessages = originalShowDeveloperMessages;
});
});