From f2b4b61eddbb1dd425cee3a84366c9bcf32ed97a Mon Sep 17 00:00:00 2001 From: Vadim Laletin Date: Mon, 20 Jul 2026 11:12:23 +0200 Subject: [PATCH 01/11] Make behavioral utils shared --- .../src/test/aboriginalFormUtils.tsx | 155 +----------------- .../src/test/behavioralTestUtils.tsx | 144 ++++++++++++++++ 2 files changed, 148 insertions(+), 151 deletions(-) create mode 100644 apps/smart-forms-app/src/test/behavioralTestUtils.tsx diff --git a/apps/smart-forms-app/src/test/aboriginalFormUtils.tsx b/apps/smart-forms-app/src/test/aboriginalFormUtils.tsx index 7489d604f..419b489d3 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormUtils.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormUtils.tsx @@ -1,155 +1,8 @@ -import { - BaseRenderer, - buildForm, - RendererThemeProvider, - useQuestionnaireResponseStore, - useQuestionnaireStore, - useRendererQueryClient -} from '@aehrc/smart-forms-renderer'; - +import type { BehavioralTestWrapperProps } from './behavioralTestUtils'; +import { BehavioralTestWrapper } from './behavioralTestUtils'; import aboriginalForm from '../data/resources/Questionnaire/Questionnaire-AboriginalTorresStraitIslanderHealthCheckAssembled-0.4.0.json'; import type { Questionnaire } from 'fhir/r4'; -import { QueryClientProvider } from '@tanstack/react-query'; -import type { Patient } from 'fhir/r4'; -import { populateQuestionnaire } from '@aehrc/sdc-populate'; -import { useEffect, useState } from 'react'; -import { inAppExtract, type InAppExtractOutput } from '@aehrc/sdc-template-extract'; -import Button from '@mui/material/Button'; - -export const terminologyServerUrl = 'https://r4.ontoserver.csiro.au/fhir'; - -export type RequestDefinition = { - urlPrefix: string; - params?: Record; - responseBody: any; -}; - -interface AboriginalFormProps { - patient?: Patient; - requestDefinitions?: RequestDefinition[]; - onExtractResult?: (extractResult: InAppExtractOutput) => void; -} - -export function AboriginalForm(props: AboriginalFormProps) { - return ( - - ); -} - -interface BuildFormWrapperWithPopulateProps extends AboriginalFormProps { - questionnaire: Questionnaire; -} - -function BuildFormWrapperWithPopulate(props: BuildFormWrapperWithPopulateProps) { - const { questionnaire, patient, requestDefinitions } = props; - const queryClient = useRendererQueryClient(); - - const [isPopulating, setIsPopulating] = useState(false); - - useEffect(() => { - const load = async () => { - setIsPopulating(true); - - if (patient && requestDefinitions) { - const result = await populateQuestionnaire({ - questionnaire: questionnaire, - patient: patient, - fetchResourceCallback: buildFetchResourceCallback(requestDefinitions), - fetchResourceRequestConfig: { sourceServerUrl: 'http://mock.example' } - }); - - const { populateSuccess, populateResult } = result; - if (!populateSuccess || !populateResult) { - setIsPopulating(false); - return; - } - - const { populatedResponse, populatedContext } = populateResult; - - await buildForm({ - questionnaire: questionnaire, - questionnaireResponse: populatedResponse, - terminologyServerUrl, - additionalContext: { - patient: patient, - ...populatedContext - } - }); - } else { - await buildForm({ - questionnaire: questionnaire, - terminologyServerUrl - }); - } - - setIsPopulating(false); - }; - - load(); - }, [questionnaire, patient, requestDefinitions]); - - if (isPopulating) { - return
Loading...
; - } - - return ( - - - - - - - ); -} - -function buildFetchResourceCallback(requestDefinitions: RequestDefinition[]) { - return async (url: string) => { - const requestUrl = url; - const [path, queryString] = requestUrl.split('?'); - - const searchParams = new URLSearchParams(queryString ?? ''); - const paramsObject: Record = {}; - searchParams.forEach((value, key) => { - paramsObject[key] = value; - }); - - const match = requestDefinitions.find((def) => { - if (!path.startsWith(def.urlPrefix)) { - return false; - } - - if (!def.params) { - return true; - } - - return Object.entries(def.params).every(([key, value]) => paramsObject[key] === value); - }); - - if (match) { - return Promise.resolve(match.responseBody); - } - - return Promise.resolve({}); - }; -} - -function SaveControl({ - onExtractResult -}: { - onExtractResult?: (extractResult: InAppExtractOutput) => void; -}) { - const qr = useQuestionnaireResponseStore.use.updatableResponse(); - const q = useQuestionnaireStore.use.sourceQuestionnaire(); - - return ( - - ); +export function AboriginalForm(props: Omit) { + return ; } diff --git a/apps/smart-forms-app/src/test/behavioralTestUtils.tsx b/apps/smart-forms-app/src/test/behavioralTestUtils.tsx new file mode 100644 index 000000000..69ed4348b --- /dev/null +++ b/apps/smart-forms-app/src/test/behavioralTestUtils.tsx @@ -0,0 +1,144 @@ +import { + BaseRenderer, + buildForm, + RendererThemeProvider, + useQuestionnaireResponseStore, + useQuestionnaireStore, + useRendererQueryClient +} from '@aehrc/smart-forms-renderer'; + +import type { Patient, Questionnaire } from 'fhir/r4'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { populateQuestionnaire } from '@aehrc/sdc-populate'; +import { useEffect, useState } from 'react'; +import { inAppExtract, type InAppExtractOutput } from '@aehrc/sdc-template-extract'; +import Button from '@mui/material/Button'; + +export const terminologyServerUrl = 'https://r4.ontoserver.csiro.au/fhir'; + +export type RequestDefinition = { + urlPrefix: string; + params?: Record; + responseBody: any; +}; + +export interface BehavioralTestWrapperProps { + patient?: Patient; + requestDefinitions?: RequestDefinition[]; + onExtractResult?: (extractResult: InAppExtractOutput) => void; + questionnaire: Questionnaire; +} + +export function BehavioralTestWrapper(props: BehavioralTestWrapperProps) { + const { questionnaire, patient, requestDefinitions } = props; + const queryClient = useRendererQueryClient(); + + const [isPopulating, setIsPopulating] = useState(false); + + useEffect(() => { + const load = async () => { + setIsPopulating(true); + + if (patient && requestDefinitions) { + const result = await populateQuestionnaire({ + questionnaire: questionnaire, + patient: patient, + fetchResourceCallback: buildFetchResourceCallback(requestDefinitions), + fetchResourceRequestConfig: { sourceServerUrl: 'http://mock.example' } + }); + + const { populateSuccess, populateResult } = result; + if (!populateSuccess || !populateResult) { + setIsPopulating(false); + return; + } + + const { populatedResponse, populatedContext } = populateResult; + + await buildForm({ + questionnaire: questionnaire, + questionnaireResponse: populatedResponse, + terminologyServerUrl, + additionalContext: { + patient: patient, + ...populatedContext + } + }); + } else { + await buildForm({ + questionnaire: questionnaire, + terminologyServerUrl + }); + } + + setIsPopulating(false); + }; + + load(); + }, [questionnaire, patient, requestDefinitions]); + + if (isPopulating) { + return
Loading...
; + } + + return ( + + + + + + + ); +} + +function buildFetchResourceCallback(requestDefinitions: RequestDefinition[]) { + return async (url: string) => { + const requestUrl = url; + const [path, queryString] = requestUrl.split('?'); + + const searchParams = new URLSearchParams(queryString ?? ''); + const paramsObject: Record = {}; + searchParams.forEach((value, key) => { + paramsObject[key] = value; + }); + + const match = requestDefinitions.find((def) => { + if (!path.startsWith(def.urlPrefix)) { + return false; + } + + if (!def.params) { + return true; + } + + return Object.entries(def.params).every(([key, value]) => paramsObject[key] === value); + }); + + if (match) { + return Promise.resolve(match.responseBody); + } + + return Promise.resolve({}); + }; +} + +function SaveControl({ + onExtractResult +}: { + onExtractResult?: (extractResult: InAppExtractOutput) => void; +}) { + const qr = useQuestionnaireResponseStore.use.updatableResponse(); + const q = useQuestionnaireStore.use.sourceQuestionnaire(); + + return ( + + ); +} From f0c48fff639b17256e18917491c453f22ce47866 Mon Sep 17 00:00:00 2001 From: Vadim Laletin Date: Mon, 20 Jul 2026 11:20:18 +0200 Subject: [PATCH 02/11] Add minimal working behavioral test for gpccmp --- .../src/test/behavioralTestUtils.tsx | 7 +- ...ronicConditionManagementPlanAssembled.json | 4238 +++++++++++++++++ .../src/test/gpccmp/population.test.tsx | 57 + apps/smart-forms-app/vitest.config.ts | 2 +- 4 files changed, 4301 insertions(+), 3 deletions(-) create mode 100644 apps/smart-forms-app/src/test/gpccmp/data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json create mode 100644 apps/smart-forms-app/src/test/gpccmp/population.test.tsx diff --git a/apps/smart-forms-app/src/test/behavioralTestUtils.tsx b/apps/smart-forms-app/src/test/behavioralTestUtils.tsx index 69ed4348b..25e1fd592 100644 --- a/apps/smart-forms-app/src/test/behavioralTestUtils.tsx +++ b/apps/smart-forms-app/src/test/behavioralTestUtils.tsx @@ -38,12 +38,15 @@ export function BehavioralTestWrapper(props: BehavioralTestWrapperProps) { useEffect(() => { const load = async () => { setIsPopulating(true); + if (requestDefinitions && !patient) { + throw new Error('Patient must be provided when request definitions are provided'); + } - if (patient && requestDefinitions) { + if (patient) { const result = await populateQuestionnaire({ questionnaire: questionnaire, patient: patient, - fetchResourceCallback: buildFetchResourceCallback(requestDefinitions), + fetchResourceCallback: buildFetchResourceCallback(requestDefinitions ?? []), fetchResourceRequestConfig: { sourceServerUrl: 'http://mock.example' } }); diff --git a/apps/smart-forms-app/src/test/gpccmp/data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json b/apps/smart-forms-app/src/test/gpccmp/data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json new file mode 100644 index 000000000..6d5e16dd5 --- /dev/null +++ b/apps/smart-forms-app/src/test/gpccmp/data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json @@ -0,0 +1,4238 @@ +{ + "resourceType" : "Questionnaire", + "id" : "GPChronicConditionManagementPlan", + "meta" : { + "profile" : ["http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-render", + "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-pop-exp"] + }, + "text" : { + "status" : "extensions", + "div" : "

Generated Narrative: Questionnaire GPChronicConditionManagementPlan

Structure\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n
LinkIDTextCardinalityTypeFlagsDescription & Constraints\"doco\"
\".\"\".\" GPChronicConditionManagementPlanGP Chronic Condition Management PlanQuestionnairehttp://www.health.gov.au/assessments/GPChronicConditionManagementPlan#0.1.0
\".\"\".\"\".\" containernull0..1group
\".\"\".\"\".\"\".\" patientPatient details0..1group
\".\"\".\"\".\"\".\"\".\" patient-instructionsThis form has been prefilled with information that was available from the patient's health record. Upon saving, the form will be stored, but additional information entered will not be used to update the patient's health record. Changes to patient demographic information should be made in the source system.0..1display
\".\"\".\"\".\"\".\"\".\" patient-consentConsent0..1group
Consent given to proceed with plan after discussion of the purpose, benefits, process and costs0..1boolean
\".\"\".\"\".\"\".\"\".\" patient-nameName0..1string\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" patient-preferrednamePreferred name0..1string\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" patient-preferredpronounsPreferred pronouns0..1choice\"icon\"/\"icon\"/Value Set: Australian Pronouns
\".\"\".\"\".\"\".\"\".\" patient-dobDate of birth0..1date\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" patient-ageAge0..1integer\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" patient-sexatbirthSex assigned at birth0..1choice\"icon\"/\"icon\"/Value Set: Biological Sex
\".\"\".\"\".\"\".\"\".\" patient-genderidentityGender identity0..1choice\"icon\"/\"icon\"/Value Set: Gender Identity Response
\".\"\".\"\".\"\".\"\".\" patient-firstnationsstatusAboriginal and/or Torres Strait Islander status0..1choice\"icon\"/\"icon\"/Value Set: Australian Indigenous Status
\".\"\".\"\".\"\".\"\".\" patient-ctgRegistered for Closing the Gap PBS Co-payment Measure (CTG)0..1boolean\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" patient-myagedcareMy Aged Care0..1group
\".\"\".\"\".\"\".\"\".\"\".\" patient-myagedcare-registeredRegistered for My Aged Care0..1choice\"icon\"/\"icon\"/Value Set: Yes/No/Pending
\".\"\".\"\".\"\".\"\".\"\".\" patient-myagedcare-numberMy Aged Care Number0..1stringEnable When: patient-myagedcare-registered = Yes (v2 Y/N Indicator#Y)
\".\"\".\"\".\"\".\"\".\"\".\" patient-myagedcare-commentComment0..1string
\".\"\".\"\".\"\".\"\".\" patient-ndisNational Disability Insurance Scheme0..1group
\".\"\".\"\".\"\".\"\".\"\".\" patient-ndis-registeredRegistered for NDIS0..1choice\"icon\"/\"icon\"/Value Set: Yes/No/Pending
\".\"\".\"\".\"\".\"\".\"\".\" patient-ndis-numberNDIS Number0..1stringEnable When: patient-ndis-registered = Yes (v2 Y/N Indicator#Y)
\".\"\".\"\".\"\".\"\".\"\".\" patient-ndis-commentComment0..1string
\".\"\".\"\".\"\".\"\".\" patient-medicareMedicare card number0..*group\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\" patient-medicare-numberNumber0..1string
\".\"\".\"\".\"\".\"\".\"\".\" patient-medicare-referencenumberReference number0..1string
\".\"\".\"\".\"\".\"\".\"\".\" patient-medicare-expiryExpiry0..1string
\".\"\".\"\".\"\".\"\".\" patient-contactContact information0..1group
\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-homephoneHome phone0..*string\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-mobilephoneMobile phone0..*string\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-emailEmail0..*string\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-homeaddressHome address0..1group\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-homeaddress-nofixedaddressNo fixed address0..1boolean
\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-homeaddress-detailsHome address0..*group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-homeaddress-details-streetaddressStreet address0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-homeaddress-details-cityCity0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-homeaddress-details-stateState0..1choiceValue Set: Australian States and Territories
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-homeaddress-details-postcodePostcode0..1string
\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-postaladdressPostal address0..*group\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-postaladdress-purposePurpose of use0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-postaladdress-streetaddressStreet address0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-postaladdress-cityCity0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-postaladdress-stateState0..1choiceValue Set: Australian States and Territories
\".\"\".\"\".\"\".\"\".\"\".\"\".\" patient-contact-postaladdress-postcodePostcode0..1string
\".\"\".\"\".\"\".\"\".\" patient-contactsCarers and key contacts0..*group
\".\"\".\"\".\"\".\"\".\"\".\" patient-contacts-preferredPreferred contact0..1boolean
\".\"\".\"\".\"\".\"\".\"\".\" patient-contacts-roleRole0..*open-choiceOptions: 6 options
\".\"\".\"\".\"\".\"\".\"\".\" patient-contacts-nameName0..1string
\".\"\".\"\".\"\".\"\".\"\".\" patient-contacts-phonePhone0..*string
\".\"\".\"\".\"\".\"\".\"\".\" patient-contacts-emailEmail0..*string
\".\"\".\"\".\"\".\"\".\"\".\" patient-contacts-relationshipRelationship to patient0..1string
\".\"\".\"\".\"\".\"\".\"\".\" patient-contacts-presentPresent at appointment0..1choice\"icon\"/\"icon\"/Value Set: Yes/No
\".\"\".\"\".\"\".\"\".\" patient-additionalinformationAdditional information0..1text
\".\"\".\"\".\"\".\" practitionerPractitioner details0..1group
\".\"\".\"\".\"\".\"\".\" practitioner-instructionsThis form has been prefilled with information that was available from the patient's health record. Upon saving, the form will be stored, but additional information entered will not be used to update the patient's health record. Changes to practitioner information should be made in the source system.0..1display
\".\"\".\"\".\"\".\"\".\" practitioner-nameName0..1string\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" practitioner-phonePhone0..*string\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" practitioner-emailEmail0..*string\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" practitioner-medicareprovidernumberMedicare provider number0..*string\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" practitioner-clinicdetailsClinic details0..1group\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\" practitioner-clinicdetails-nameName0..1string\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\" practitioner-clinicdetails-addressAddress0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" practitioner-clinicdetails-address-streetaddressStreet address0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\" practitioner-clinicdetails-address-cityCity0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\" practitioner-clinicdetails-address-stateState0..1choiceValue Set: Australian States and Territories
\".\"\".\"\".\"\".\"\".\"\".\"\".\" practitioner-clinicdetails-address-postcodePostcode0..1string
\".\"\".\"\".\"\".\"\".\"\".\" practitioner-clinicdetails-addresstextAddress0..1string
\".\"\".\"\".\"\".\" clinicaldetailsClinical details0..1group
\".\"\".\"\".\"\".\"\".\" clinicaldetails-instructionsThis form has been prefilled with information that was available from the patient's health record. Upon saving, the form will be stored, but additional information entered will not be used to update the patient's health record.0..1display
\".\"\".\"\".\"\".\"\".\" clinicaldetails-problemsdiagnosesProblems/Diagnoses0..1group
\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-problemsdiagnoses-recordedproblemsRecorded problems/diagnoses0..*group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-problemsdiagnoses-recordedproblems-conditionCondition0..1open-choice\"icon\"/\"icon\"/Value Set: Clinical Condition
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-problemsdiagnoses-recordedproblems-clinicalstatusClinical status0..1choiceValue Set: Condition Clinical Status Codes
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-problemsdiagnoses-recordedproblems-onsetdateOnset date0..1date\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-problemsdiagnoses-recordedproblems-abatementdateAbatement date0..1date
\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-problemsdiagnoses-newproblemsNew problems/diagnoses0..*group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-problemsdiagnoses-newproblems-conditionCondition0..1open-choiceValue Set: Clinical Condition
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-problemsdiagnoses-newproblems-onsetdateOnset date0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-problemsdiagnoses-newproblems-commentComment0..1string
\".\"\".\"\".\"\".\"\".\" clinicaldetails-allergiesAdverse reaction risks0..1group
\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-allergies-recordedallergiesRecorded adverse reaction risks0..*group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-allergies-recordedallergies-substanceSubstance0..1open-choice\"icon\"/\"icon\"/Value Set: Adverse Reaction Agent
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-allergies-recordedallergies-statusStatus0..1choiceValue Set: AllergyIntolerance Clinical Status Codes
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-allergies-recordedallergies-manifestationManifestation0..*open-choice\"icon\"/\"icon\"/Value Set: Clinical Finding
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-allergies-recordedallergies-commentComment0..1text
\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-allergies-newallergiesNew adverse reaction risks0..*group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-allergies-newallergies-substanceSubstance0..1open-choiceValue Set: Adverse Reaction Agent
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-allergies-newallergies-manifestationManifestation0..*open-choiceValue Set: Clinical Finding
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-allergies-newallergies-commentComment0..1text
\".\"\".\"\".\"\".\"\".\" clinicaldetails-medicationsMedications (current)0..1group
\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-recordedmedicationsRecorded medications0..*group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-recordedmedications-medicationMedication0..1open-choice\"icon\"/\"icon\"/Value Set: Australian Medication
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-recordedmedications-statusStatus0..1choiceValue Set: Medication Statement Status Limited
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-recordedmedications-dosageDosage0..1text
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-recordedmedications-indicationIndication0..*open-choice\"icon\"/\"icon\"/Value Set: Medication Reason Taken
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-recordedmedications-commentComment0..1text
\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-newmedicationsNew medications0..*group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-newmedications-medicationMedication0..1open-choiceValue Set: Australian Medication
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-newmedications-dosageDosage0..1text
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-newmedications-indicationIndication0..*open-choiceValue Set: Medication Reason Taken
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-medications-newmedications-commentComment0..1text
\".\"\".\"\".\"\".\"\".\" clinicaldetails-observationsObservations0..1group
\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-instructionsThe tabled observations will display the most recent results available from the patient record. New observations may be added.0..1display
\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingridObservations0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-heightHeight0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-height-lastresultLast result0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-height-lastresultvaluenull0..1decimal\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-height-lastresultdatenull0..1date\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-height-newresultvalueNew result0..1decimal
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-height-newresultvalue-unitcm0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-height-newresultdateNew result date0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-weightWeight0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-weight-lastresultLast result0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-weight-lastresultvaluenull0..1decimal\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-weight-lastresultdatenull0..1date\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-weight-newresultvalueNew result0..1decimal
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-weight-newresultvalue-unitkg0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-weight-newresultdateNew result date0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-bmiBMI (calculated)0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-bmi-lastresultLast result0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-bmi-lastresultvaluenull0..1decimal\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-bmi-newresultvalueNew result0..1decimal\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-bmi-newresult-unitkg/m20..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-waistcircumferenceWaist circumference0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-waistcircumference-lastresultLast result0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-waistcircumference-lastresultvaluenull0..1decimal\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-waistcircumference-lastresultdatenull0..1date\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-waistcircumference-newresultvalueNew result0..1decimal
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-waistcircumference-newresultvalue-unitcm0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-waistcircumference-newdateNew result date0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulseratePulse rate0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserate-lastresultLast result0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserate-lastresultvaluenull0..1decimal\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserate-lastresultdatenull0..1date\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserate-newresultvalueNew result0..1integer
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserate-newresultvalue-unit/min0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserate-newresultdateNew result date0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserhythmPulse rhythm0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserhythm-lastresultLast result0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserhythm-lastresultvaluenull0..1choice\"icon\"/Value Set: Pulse Rhythm
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserhythm-lastresultdatenull0..1date\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserhythm-newresultvalueNew result0..1choiceValue Set: Pulse Rhythm
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-pulserhythm-newresultdateNew result date0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-oxygensaturationOxygen saturation0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-oxygensaturation-lastresultLast result0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-oxygensaturation-lastresultvaluenull0..1decimal\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-oxygensaturation-lastresultdatenull0..1date\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-oxygensaturation-newresultvalueNew result0..1integer
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-oxygensaturation-newresultvalue-unit%0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-maingrid-oxygensaturation-newresultdateNew result date0..1date
\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgridBlood pressure0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgrid-bpBlood pressure0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgrid-bp-lastresultLast result0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgrid-bp-lastresultvaluesystolicnull0..1decimal\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgrid-bp-lastresultvaluediastolicnull0..1decimal\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgrid-bp-lastresultdatenull0..1date\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgrid-bp-newresultsystolicSystolic0..1integer
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgrid-bp-newresultsystolic-unitmm Hg0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgrid-bp-newresultdiastolicDiastolic0..1integer
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgrid-bp-newresultdiastolic-unitmm Hg0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-bpgrid-bp-newresultdateDate performed0..1date
\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegridSubstance use0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-smokingstatusSmoking status0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-smokingstatus-laststatusLast status0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-smokingstatus-lastresultvaluenull0..1choice\"icon\"/Value Set: Smoking Status
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-smokingstatus-lastresultdatenull0..1date\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-smokingstatus-newresultvalueNew status0..1choiceValue Set: Smoking Status
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-smokingstatus-newresultdateNew status date0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-smokingstatusgrid-smokingstatus-newresultcommentComment0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-alcoholstatusAlcohol consumption status0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-alcoholstatus-laststatusLast status0..1display
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-alcoholstatus-lastresultvaluenull0..1choice\"icon\"/Value Set: Alcohol Intake Status
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-alcoholstatus-lastresultdatenull0..1date\"icon\"/
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-alcoholstatus-newresultvalueNew status0..1choiceValue Set: Alcohol Intake Status
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-alcoholstatus-newresultdateNew status date0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-substanceusegrid-alcoholstatus-newresultcommentComment0..1string
\".\"\".\"\".\"\".\"\".\" clinicaldetails-observations-additionalinformationAdditional information0..1text
\".\"\".\"\".\"\".\" planPlan0..1group
\".\"\".\"\".\"\".\"\".\" plan-typeNew plan or a review of an existing plan?0..1choice\"icon\"/\"icon\"/Options: 2 options
\".\"\".\"\".\"\".\"\".\"\".\" plan-type-instructionsAutoselected as 'Review' if a plan has been completed in the last 12 months, otherwise 'New'.0..1display\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" plan-lastcompleteddateDate of most recent plan or review0..1date\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" plan-inprogressIncomplete draft plan already exists?0..1boolean\"icon\"/\"icon\"/
\".\"\".\"\".\"\".\"\".\" plan-conditionsConditions addressed0..*group
\".\"\".\"\".\"\".\"\".\"\".\" plan-conditions-conditionCondition0..1open-choiceValue Set: Clinical Condition
\".\"\".\"\".\"\".\"\".\"\".\" plan-conditions-onsetdateOnset date0..1date
\".\"\".\"\".\"\".\"\".\"\".\" plan-conditions-commentsComments0..1text
\".\"\".\"\".\"\".\"\".\" plan-goalstasksGoals and tasks0..*group
\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-problemneedProblems/Needs0..*open-choiceValue Set: Clinical Condition
\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-goalsettingGoal setting0..*group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-goalsetting-goalsGoals0..1text
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-goalsetting-initiatorInitiator0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-goalsetting-targetdateTarget date0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-goalsetting-statusStatus0..1choiceValue Set: Goal Status Limited
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-goalsetting-commentComment0..1string
\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-interventionsactionsInterventions and actions0..*group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-interventionsactions-interventionsactionsInterventions/Actions0..1open-choiceValue Set: Procedure
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-interventionsactions-ownerOwner0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-interventionsactions-targetdateTarget date0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-interventionsactions-statusStatus0..1choiceValue Set: Actions Status
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-interventionsactions-commentComment0..1string
\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-servicestreatmentsServices and treatments0..*group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-servicestreatments-servicestreatmentsRequired services and treatments0..1open-choiceValue Set: Service Type
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-servicestreatments-activityActivity0..1open-choiceValue Set: Procedure
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-servicestreatments-providerProvider0..1string
\".\"\".\"\".\"\".\"\".\"\".\"\".\" plan-goalstasks-details-servicestreatments-commentComment0..1string
\".\"\".\"\".\"\".\"\".\" notesNotes0..1group
\".\"\".\"\".\"\".\"\".\"\".\" notes-additionalcommentsAdditional notes or comments0..*text
\".\"\".\"\".\"\".\"\".\" completionCompletion0..1group
\".\"\".\"\".\"\".\"\".\"\".\" completion-consentforsharingConsent given for sharing of information with relevant healthcare providers0..1boolean
\".\"\".\"\".\"\".\"\".\"\".\" completion-reviewReview0..1group
\".\"\".\"\".\"\".\"\".\"\".\"\".\" completion-review-appointmentstatusAppointment status0..1choice\"icon\"/\"icon\"/Options: 2 options
\".\"\".\"\".\"\".\"\".\"\".\"\".\" completion-review-dateDate0..1date
\".\"\".\"\".\"\".\"\".\"\".\"\".\" completion-review-commentComment0..1string
\".\"\".\"\".\"\".\"\".\"\".\" completion-copyofferedPatient has been offered a copy of this plan0..1choiceOptions: 3 options
\".\"\".\"\".\"\".\"\".\"\".\" completion-commentComment0..1string

\"doco\" Documentation for this format

Options Sets

Answer options for patient-contacts-role

  • http://snomed.info/sct#133932002
  • http://snomed.info/sct#394619001
  • http://snomed.info/sct#1620171000168100
  • http://terminology.hl7.org/CodeSystem/v2-0131#C
  • http://terminology.hl7.org/CodeSystem/v2-0131#CP
  • http://terminology.hl7.org/CodeSystem/v2-0131#N

Answer options for plan-type

  • New
  • Review

Answer options for completion-review-appointmentstatus

  • http://hl7.org/fhir/appointmentstatus#booked ("Booked")
  • http://hl7.org/fhir/appointmentstatus#proposed ("Proposed")

Answer options for completion-copyoffered

  • Yes, copy provided
  • Yes, copy to be provided at a later date
  • Yes, but declined

Contained Resources


ValueSet #YesNoPending

This ValueSet requires the Code system Supplement GP CCMP Expanded Yes No Indicator Supplement.

This value set contains 3 concepts

SystemCodeDisplay (en)Definition
http://terminology.hl7.org/CodeSystem/v2-0532\u00a0\u00a0YYesYes
http://terminology.hl7.org/CodeSystem/v2-0532\u00a0\u00a0NNoNo
http://terminology.hl7.org/CodeSystem/v2-0532\u00a0\u00a0NAVPendingtemporarily unavailable

ValueSet #YesNo

Expansion based on codesystem expandedYes-NoIndicator v3.0.0 (CodeSystem)

This value set contains 2 concepts

SystemCodeDisplay (en)Definition
http://terminology.hl7.org/CodeSystem/v2-0532\u00a0\u00a0YYesYes
http://terminology.hl7.org/CodeSystem/v2-0532\u00a0\u00a0NNoNo

ValueSet #pulse-rhythm-1

Expansion based on SNOMED CT Australian Edition edition 31-Jan 2026

This value set contains 4 concepts

SystemCodeDisplay (en)
http://snomed.info/sct\u00a0\u00a0271636001Pulse regular
http://snomed.info/sct\u00a0\u00a061086009Pulse irregular
http://snomed.info/sct\u00a0\u00a0271637005Pulse irregularly irregular
http://snomed.info/sct\u00a0\u00a0271638000Pulse regularly irregular

ValueSet #MedicationStatementStatusLimited

Expansion based on codesystem Medication status codes v4.0.1 (CodeSystem)

This value set contains 4 concepts

SystemCodeDisplayDefinition
http://hl7.org/fhir/CodeSystem/medication-statement-status\u00a0\u00a0activeActiveThe medication is still being taken.
http://hl7.org/fhir/CodeSystem/medication-statement-status\u00a0\u00a0completedCompletedThe medication is no longer being taken.
http://hl7.org/fhir/CodeSystem/medication-statement-status\u00a0\u00a0stoppedStoppedActions implied by the statement have been permanently halted, before all of them occurred. This should not be used if the statement was entered in error.
http://hl7.org/fhir/CodeSystem/medication-statement-status\u00a0\u00a0on-holdOn HoldActions implied by the statement have been temporarily halted, but are expected to continue later. May also be called 'suspended'.

ValueSet #GoalStatusLimited

This ValueSet requires the Code system Supplement GP CCMP Goal Status Supplement.

This value set contains 3 concepts

SystemCodeDisplay (en)Definition
http://hl7.org/fhir/goal-status\u00a0\u00a0activeActiveThe goal is being sought actively.
http://hl7.org/fhir/goal-status\u00a0\u00a0completedCompletedThe goal is no longer being sought.
http://hl7.org/fhir/goal-status\u00a0\u00a0cancelledWithdrawnThe goal has been abandoned.

ValueSet #ActionsStatus

This ValueSet requires the Code system Supplement GP CCMP Care Plan Activity Status Supplement.

This value set contains 6 concepts

SystemCodeDisplay (en)Definition
http://hl7.org/fhir/care-plan-activity-status\u00a0\u00a0not-startedPlannedCare plan activity is planned but no action has yet been taken.
http://hl7.org/fhir/care-plan-activity-status\u00a0\u00a0in-progressIn ProgressCare plan activity has been started but is not yet complete.
http://hl7.org/fhir/care-plan-activity-status\u00a0\u00a0on-holdOn HoldCare plan activity was started but has temporarily ceased with an expectation of resumption at a future time.
http://hl7.org/fhir/care-plan-activity-status\u00a0\u00a0completedCompletedCare plan activity has been completed (more or less) as planned.
http://hl7.org/fhir/care-plan-activity-status\u00a0\u00a0stoppedStoppedThe planned care plan activity has been ended prior to completion after the activity was started.
http://hl7.org/fhir/care-plan-activity-status\u00a0\u00a0cancelledCancelledThe planned care plan activity has been withdrawn.
" + }, + "contained" : [{ + "resourceType" : "ValueSet", + "id" : "YesNoPending", + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/valueset-supplement", + "valueCanonical" : "https://gpccmp.csiro.au/ig/CodeSystem/GPCCMPExpandedYesNoIndicatorSupplement|0.1.0" + }], + "url" : "https://gpccmp.csiro.au/ig/ValueSet/YesNoPending", + "name" : "YesNoPending", + "title" : "Yes/No/Pending", + "status" : "draft", + "experimental" : false, + "description" : "Concepts for Yes, No and Pending", + "compose" : { + "include" : [{ + "system" : "http://terminology.hl7.org/CodeSystem/v2-0532", + "version" : "3.0.0", + "concept" : [{ + "code" : "Y", + "display" : "Yes" + }, + { + "code" : "N", + "display" : "No" + }, + { + "code" : "NAV", + "display" : "Pending" + }] + }] + }, + "expansion" : { + "identifier" : "urn:uuid:105bb5ba-72a4-43a2-ab99-6859a21463a8", + "timestamp" : "2026-02-17T10:59:10+10:00", + "total" : 3, + "offset" : 0, + "parameter" : [{ + "name" : "displayLanguage", + "valueCode" : "en" + }, + { + "name" : "count", + "valueInteger" : 1000 + }, + { + "name" : "offset", + "valueInteger" : 0 + }, + { + "name" : "excludeNested", + "valueBoolean" : false + }, + { + "name" : "used-codesystem", + "valueUri" : "http://terminology.hl7.org/CodeSystem/v2-0532|3.0.0" + }, + { + "name" : "used-supplement", + "valueUri" : "https://gpccmp.csiro.au/ig/CodeSystem/GPCCMPExpandedYesNoIndicatorSupplement|0.1.0" + }], + "contains" : [{ + "system" : "http://terminology.hl7.org/CodeSystem/v2-0532", + "code" : "Y", + "display" : "Yes" + }, + { + "system" : "http://terminology.hl7.org/CodeSystem/v2-0532", + "code" : "N", + "display" : "No" + }, + { + "system" : "http://terminology.hl7.org/CodeSystem/v2-0532", + "code" : "NAV", + "display" : "Pending" + }] + } + }, + { + "resourceType" : "ValueSet", + "id" : "YesNo", + "url" : "https://gpccmp.csiro.au/ig/ValueSet/YesNo", + "name" : "YesNo", + "title" : "Yes/No", + "status" : "draft", + "experimental" : false, + "description" : "Concepts for Yes and No", + "compose" : { + "include" : [{ + "system" : "http://terminology.hl7.org/CodeSystem/v2-0532", + "version" : "3.0.0", + "concept" : [{ + "code" : "Y", + "display" : "Yes" + }, + { + "code" : "N", + "display" : "No" + }] + }] + }, + "expansion" : { + "identifier" : "urn:uuid:65f54ac5-483f-4d55-a6eb-43c1588ba934", + "timestamp" : "2026-02-17T11:03:51+10:00", + "total" : 2, + "offset" : 0, + "parameter" : [{ + "name" : "displayLanguage", + "valueCode" : "en" + }, + { + "name" : "count", + "valueInteger" : 1000 + }, + { + "name" : "offset", + "valueInteger" : 0 + }, + { + "name" : "excludeNested", + "valueBoolean" : false + }, + { + "name" : "used-codesystem", + "valueUri" : "http://terminology.hl7.org/CodeSystem/v2-0532|3.0.0" + }], + "contains" : [{ + "system" : "http://terminology.hl7.org/CodeSystem/v2-0532", + "code" : "Y", + "display" : "Yes" + }, + { + "system" : "http://terminology.hl7.org/CodeSystem/v2-0532", + "code" : "N", + "display" : "No" + }] + } + }, + { + "resourceType" : "ValueSet", + "id" : "pulse-rhythm-1", + "url" : "https://gpccmp.csiro.au/ig/ValueSet/pulse-rhythm-1", + "name" : "PulseRhythm", + "title" : "Pulse Rhythm", + "status" : "draft", + "experimental" : false, + "description" : "The Pulse Rhythm value set includes values that may be used to represent the pulse rhythm of an individual.", + "compose" : { + "include" : [{ + "system" : "http://snomed.info/sct", + "concept" : [{ + "code" : "271636001", + "display" : "Pulse regular" + }, + { + "code" : "61086009", + "display" : "Pulse irregular" + }, + { + "code" : "271637005", + "display" : "Pulse irregularly irregular" + }, + { + "code" : "271638000", + "display" : "Pulse regularly irregular" + }] + }] + }, + "expansion" : { + "identifier" : "urn:uuid:a544a2bc-fc6b-45e8-bbaa-4472eedaba72", + "timestamp" : "2026-02-09T11:26:29+10:00", + "total" : 4, + "offset" : 0, + "parameter" : [{ + "name" : "displayLanguage", + "valueCode" : "en" + }, + { + "name" : "count", + "valueInteger" : 1000 + }, + { + "name" : "offset", + "valueInteger" : 0 + }, + { + "name" : "excludeNested", + "valueBoolean" : false + }, + { + "name" : "system-version", + "valueUri" : "http://snomed.info/sct|http://snomed.info/sct/32506021000036107" + }, + { + "name" : "used-codesystem", + "valueUri" : "http://snomed.info/sct|http://snomed.info/sct/32506021000036107/version/20260131" + }, + { + "name" : "version", + "valueUri" : "http://snomed.info/sct|http://snomed.info/sct/32506021000036107/version/20260131" + }], + "contains" : [{ + "system" : "http://snomed.info/sct", + "code" : "271636001", + "display" : "Pulse regular" + }, + { + "system" : "http://snomed.info/sct", + "code" : "61086009", + "display" : "Pulse irregular" + }, + { + "system" : "http://snomed.info/sct", + "code" : "271637005", + "display" : "Pulse irregularly irregular" + }, + { + "system" : "http://snomed.info/sct", + "code" : "271638000", + "display" : "Pulse regularly irregular" + }] + } + }, + { + "resourceType" : "ValueSet", + "id" : "MedicationStatementStatusLimited", + "url" : "https://gpccmp.csiro.au/ig/ValueSet/MedicationStatementStatusLimited", + "name" : "MedicationStatementStatusLimited", + "title" : "Medication Statement Status Limited", + "status" : "draft", + "experimental" : false, + "description" : "This value set includes the minimal set of codes to represent the status of a medication statement (i.e., active, completed, stopped and on-hold).", + "compose" : { + "include" : [{ + "system" : "http://hl7.org/fhir/CodeSystem/medication-statement-status", + "version" : "4.0.1", + "concept" : [{ + "code" : "active" + }, + { + "code" : "completed" + }, + { + "code" : "stopped" + }, + { + "code" : "on-hold" + }] + }] + }, + "expansion" : { + "identifier" : "urn:uuid:59fe5ac1-65bf-4606-8c2a-0a55fba1d064", + "timestamp" : "2025-08-25T15:53:32+10:00", + "total" : 4, + "offset" : 0, + "parameter" : [{ + "name" : "count", + "valueInteger" : 1000 + }, + { + "name" : "offset", + "valueInteger" : 0 + }, + { + "name" : "excludeNested", + "valueBoolean" : false + }, + { + "name" : "used-codesystem", + "valueUri" : "http://hl7.org/fhir/CodeSystem/medication-statement-status|4.0.1" + }], + "contains" : [{ + "system" : "http://hl7.org/fhir/CodeSystem/medication-statement-status", + "code" : "active", + "display" : "Active" + }, + { + "system" : "http://hl7.org/fhir/CodeSystem/medication-statement-status", + "code" : "completed", + "display" : "Completed" + }, + { + "system" : "http://hl7.org/fhir/CodeSystem/medication-statement-status", + "code" : "stopped", + "display" : "Stopped" + }, + { + "system" : "http://hl7.org/fhir/CodeSystem/medication-statement-status", + "code" : "on-hold", + "display" : "On Hold" + }] + } + }, + { + "resourceType" : "ValueSet", + "id" : "GoalStatusLimited", + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/valueset-supplement", + "valueCanonical" : "https://gpccmp.csiro.au/ig/CodeSystem/GPCCMPGoalStatusSupplement|0.1.0" + }], + "url" : "https://gpccmp.csiro.au/ig/ValueSet/GoalStatusLimited", + "name" : "GoalStatusLimited", + "title" : "Goal Status Limited", + "status" : "draft", + "experimental" : false, + "description" : "This value set includes the minimal set of codes to represent the status of a goal (i.e., active, completed, and withdrawn).", + "compose" : { + "include" : [{ + "system" : "http://hl7.org/fhir/goal-status", + "version" : "4.0.1", + "concept" : [{ + "code" : "active", + "display" : "Active" + }, + { + "code" : "completed", + "display" : "Completed" + }, + { + "code" : "cancelled", + "display" : "Withdrawn" + }] + }] + }, + "expansion" : { + "identifier" : "urn:uuid:fff81a46-8231-4929-b534-3b0e07d0c502", + "timestamp" : "2026-05-25T13:11:30+10:00", + "total" : 3, + "offset" : 0, + "parameter" : [{ + "name" : "displayLanguage", + "valueCode" : "en" + }, + { + "name" : "count", + "valueInteger" : 1000 + }, + { + "name" : "offset", + "valueInteger" : 0 + }, + { + "name" : "excludeNested", + "valueBoolean" : false + }, + { + "name" : "used-codesystem", + "valueUri" : "http://hl7.org/fhir/goal-status|4.0.1" + }, + { + "name" : "used-supplement", + "valueUri" : "https://gpccmp.csiro.au/ig/CodeSystem/GPCCMPGoalStatusSupplement|0.1.0" + }], + "contains" : [{ + "system" : "http://hl7.org/fhir/goal-status", + "code" : "active", + "display" : "Active" + }, + { + "system" : "http://hl7.org/fhir/goal-status", + "code" : "completed", + "display" : "Completed" + }, + { + "system" : "http://hl7.org/fhir/goal-status", + "code" : "cancelled", + "display" : "Withdrawn" + }] + } + }, + { + "resourceType" : "ValueSet", + "id" : "ActionsStatus", + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/valueset-supplement", + "valueCanonical" : "https://gpccmp.csiro.au/ig/CodeSystem/GPCCMPCarePlanActivityStatusSupplement|0.1.0" + }], + "url" : "https://gpccmp.csiro.au/ig/ValueSet/ActionsStatus", + "name" : "ActionsStatus", + "title" : "Actions Status", + "status" : "draft", + "experimental" : false, + "description" : "This value set includes values to represent the status of a care plan action. It uses canonical status codes for FHIR resources for applicability to different resource types that may represent an action.", + "compose" : { + "include" : [{ + "system" : "http://hl7.org/fhir/care-plan-activity-status", + "version" : "4.0.1", + "concept" : [{ + "code" : "not-started", + "display" : "Planned" + }, + { + "code" : "in-progress", + "display" : "In Progress" + }, + { + "code" : "on-hold", + "display" : "On Hold" + }, + { + "code" : "completed", + "display" : "Completed" + }, + { + "code" : "stopped", + "display" : "Stopped" + }, + { + "code" : "cancelled", + "display" : "Cancelled" + }] + }] + }, + "expansion" : { + "identifier" : "urn:uuid:81e33f47-20e7-444c-9001-d2844bb0eb00", + "timestamp" : "2026-05-25T13:11:30+10:00", + "total" : 6, + "offset" : 0, + "parameter" : [{ + "name" : "displayLanguage", + "valueCode" : "en" + }, + { + "name" : "count", + "valueInteger" : 1000 + }, + { + "name" : "offset", + "valueInteger" : 0 + }, + { + "name" : "excludeNested", + "valueBoolean" : false + }, + { + "name" : "used-codesystem", + "valueUri" : "http://hl7.org/fhir/care-plan-activity-status|4.0.1" + }, + { + "name" : "used-supplement", + "valueUri" : "https://gpccmp.csiro.au/ig/CodeSystem/GPCCMPCarePlanActivityStatusSupplement|0.1.0" + }], + "contains" : [{ + "system" : "http://hl7.org/fhir/care-plan-activity-status", + "code" : "not-started", + "display" : "Planned" + }, + { + "system" : "http://hl7.org/fhir/care-plan-activity-status", + "code" : "in-progress", + "display" : "In Progress" + }, + { + "system" : "http://hl7.org/fhir/care-plan-activity-status", + "code" : "on-hold", + "display" : "On Hold" + }, + { + "system" : "http://hl7.org/fhir/care-plan-activity-status", + "code" : "completed", + "display" : "Completed" + }, + { + "system" : "http://hl7.org/fhir/care-plan-activity-status", + "code" : "stopped", + "display" : "Stopped" + }, + { + "system" : "http://hl7.org/fhir/care-plan-activity-status", + "code" : "cancelled", + "display" : "Cancelled" + }] + } + }], + "extension" : [{ + "extension" : [{ + "url" : "name", + "valueCoding" : { + "system" : "http://hl7.org/fhir/uv/sdc/CodeSystem/launchContext", + "code" : "patient" + } + }, + { + "url" : "type", + "valueCode" : "Patient" + }, + { + "url" : "description", + "valueString" : "The patient that is to be used to pre-populate the form. This is the subject of the form." + }], + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext" + }, + { + "extension" : [{ + "url" : "name", + "valueCoding" : { + "system" : "http://hl7.org/fhir/uv/sdc/CodeSystem/launchContext", + "code" : "user" + } + }, + { + "url" : "type", + "valueCode" : "Practitioner" + }, + { + "url" : "description", + "valueString" : "The practitioner user that is to be used to pre-populate the form. This is the user that is filling out the form." + }], + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext" + }, + { + "extension" : [{ + "url" : "name", + "valueCoding" : { + "system" : "http://hl7.org/fhir/uv/sdc/CodeSystem/launchContext", + "code" : "encounter" + } + }, + { + "url" : "type", + "valueCode" : "Encounter" + }, + { + "url" : "description", + "valueString" : "The encounter that is to be used to pre-populate the form. This is the encounter during which the form is being filled out." + }], + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext" + }, + { + "extension" : [{ + "url" : "name", + "valueCoding" : { + "system" : "https://gpccmp.csiro.au/ig/CodeSystem/LaunchContextExtended", + "code" : "gpccmppractitionerrole" + } + }, + { + "url" : "type", + "valueCode" : "PractitionerRole" + }, + { + "url" : "description", + "valueString" : "The practitioner role that is to be used to pre-populate the form. This is the practitioner role of the user that is filling out the form." + }], + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext" + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "GPCCMPLatest", + "language" : "application/x-fhir-query", + "expression" : "QuestionnaireResponse?questionnaire=http://www.health.gov.au/assessments/GPChronicConditionManagementPlan&_count=1&_sort=-authored&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "GPCCMPLatestCompletedAmended", + "language" : "application/x-fhir-query", + "expression" : "QuestionnaireResponse?questionnaire=http://www.health.gov.au/assessments/GPChronicConditionManagementPlan&status=completed,amended&_count=1&_sort=-authored&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "PractitionerRoleLocation", + "language" : "application/x-fhir-query", + "expression" : "PractitionerRole?_id={{%gpccmppractitionerrole.id}}&_include=PractitionerRole:location" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "Condition", + "language" : "application/x-fhir-query", + "expression" : "Condition?patient={{%patient.id}}&category=http://terminology.hl7.org/CodeSystem/condition-category|problem-list-item" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "AllergyIntolerance", + "language" : "application/x-fhir-query", + "expression" : "AllergyIntolerance?patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "MedicationStatement", + "language" : "application/x-fhir-query", + "expression" : "MedicationStatement?patient={{%patient.id}}&status=active&_include=MedicationStatement:medication" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBodyHeight", + "language" : "application/x-fhir-query", + "expression" : "Observation?code=8302-2&_sort=-date&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBodyWeight", + "language" : "application/x-fhir-query", + "expression" : "Observation?code=29463-7&_sort=-date&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsWaistCircumference", + "language" : "application/x-fhir-query", + "expression" : "Observation?code=8280-0&_sort=-date&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsPulseRate", + "language" : "application/x-fhir-query", + "expression" : "Observation?code=78564009&_sort=-date&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsPulseRhythm", + "language" : "application/x-fhir-query", + "expression" : "Observation?code=364095004&_sort=-date&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsOxygenSaturation", + "language" : "application/x-fhir-query", + "expression" : "Observation?code=2708-6&_sort=-date&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBloodPressure", + "language" : "application/x-fhir-query", + "expression" : "Observation?code=85354-9&_sort=-date&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsSmokingStatus", + "language" : "application/x-fhir-query", + "expression" : "Observation?code=1747861000168109&_sort=-date&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsAlcoholStatus", + "language" : "application/x-fhir-query", + "expression" : "Observation?code=897148007&_sort=-date&patient={{%patient.id}}" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ClinicLocation", + "language" : "text/fhirpath", + "expression" : "%PractitionerRoleLocation.entry.resource.ofType(Location)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "HomeAddressNoFixedAddress", + "language" : "text/fhirpath", + "expression" : "repeat(item).where(linkId='patient-contact-homeaddress-nofixedaddress').answer.value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "age", + "language" : "text/fhirpath", + "expression" : "repeat(item).where(linkId='patient-age').answer.value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "weight", + "language" : "text/fhirpath", + "expression" : "repeat(item).where(linkId='clinicaldetails-observations-maingrid-weight-newresultvalue').answer.value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "height", + "language" : "text/fhirpath", + "expression" : "repeat(item).where(linkId='clinicaldetails-observations-maingrid-height-newresultvalue').answer.value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "medicationsFromContained", + "language" : "text/fhirpath", + "expression" : "%MedicationStatement.entry.resource.contained.ofType(Medication).where(id in %MedicationStatement.entry.resource.medication.select(reference.replace('#', '')))" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "medicationsFromRef", + "language" : "text/fhirpath", + "expression" : "%MedicationStatement.entry.resource.ofType(Medication).where(id in %MedicationStatement.entry.resource.medication.select(reference.replace('Medication/', '')))" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBodyHeightLatest", + "language" : "text/fhirpath", + "expression" : "%ObsBodyHeight.entry.resource.where(status = 'final' or status = 'amended' or status = 'corrected').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBodyWeightLatest", + "language" : "text/fhirpath", + "expression" : "%ObsBodyWeight.entry.resource.where(status = 'final' or status = 'amended' or status = 'corrected').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsWaistCircumferenceLatest", + "language" : "text/fhirpath", + "expression" : "%ObsWaistCircumference.entry.resource.where(status = 'final' or status = 'amended' or status = 'corrected').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsPulseRateLatest", + "language" : "text/fhirpath", + "expression" : "%ObsPulseRate.entry.resource.where(status = 'final' or status = 'amended' or status = 'corrected').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsPulseRhythmLatest", + "language" : "text/fhirpath", + "expression" : "%ObsPulseRhythm.entry.resource.where(status = 'final' or status = 'amended' or status = 'corrected').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsOxygenSaturationLatest", + "language" : "text/fhirpath", + "expression" : "%ObsOxygenSaturation.entry.resource.where(status = 'final' or status = 'amended' or status = 'corrected').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBloodPressureLatest", + "language" : "text/fhirpath", + "expression" : "%ObsBloodPressure.entry.resource.where(status = 'final' or status = 'amended' or status = 'corrected').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsSmokingStatusLatest", + "language" : "text/fhirpath", + "expression" : "%ObsSmokingStatus.entry.resource.where(status = 'final' or status = 'amended' or status = 'corrected').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsAlcoholStatusLatest", + "language" : "text/fhirpath", + "expression" : "%ObsAlcoholStatus.entry.resource.where(status = 'final' or status = 'amended' or status = 'corrected').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBodyHeightValue", + "language" : "text/fhirpath", + "expression" : "%ObsBodyHeightLatest.value.where(exists(system='http://unitsofmeasure.org' and code='cm')).value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBodyHeightDateString", + "language" : "text/fhirpath", + "expression" : "%ObsBodyHeightLatest.effective.toString().substring(0,10)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBodyHeightDateFormatted", + "language" : "text/fhirpath", + "expression" : "%ObsBodyHeightDateString.substring(8,2).toInteger().toString() + ' ' + %ObsBodyHeightDateString.substring(5,2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + %ObsBodyHeightDateString.substring(0,4)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBodyWeightValue", + "language" : "text/fhirpath", + "expression" : "%ObsBodyWeightLatest.value.where(exists(system='http://unitsofmeasure.org' and code='kg')).value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBodyWeightDateString", + "language" : "text/fhirpath", + "expression" : "%ObsBodyWeightLatest.effective.toString().substring(0,10)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBodyWeightDateFormatted", + "language" : "text/fhirpath", + "expression" : "%ObsBodyWeightDateString.substring(8,2).toInteger().toString() + ' ' + %ObsBodyWeightDateString.substring(5,2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + %ObsBodyWeightDateString.substring(0,4)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsWaistCircumferenceValue", + "language" : "text/fhirpath", + "expression" : "%ObsWaistCircumferenceLatest.value.where(exists(system='http://unitsofmeasure.org' and code='cm')).value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsWaistCircumferenceDateString", + "language" : "text/fhirpath", + "expression" : "%ObsWaistCircumferenceLatest.effective.toString().substring(0,10)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsWaistCircumferenceDateFormatted", + "language" : "text/fhirpath", + "expression" : "%ObsWaistCircumferenceDateString.substring(8,2).toInteger().toString() + ' ' + %ObsWaistCircumferenceDateString.substring(5,2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + %ObsWaistCircumferenceDateString.substring(0,4)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsPulseRateValue", + "language" : "text/fhirpath", + "expression" : "%ObsPulseRateLatest.value.where(exists(system='http://unitsofmeasure.org' and code='/min')).value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsPulseRateDateString", + "language" : "text/fhirpath", + "expression" : "%ObsPulseRateLatest.effective.toString().substring(0,10)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsPulseRateDateFormatted", + "language" : "text/fhirpath", + "expression" : "%ObsPulseRateDateString.substring(8,2).toInteger().toString() + ' ' + %ObsPulseRateDateString.substring(5,2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + %ObsPulseRateDateString.substring(0,4)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsPulseRhythmValue", + "language" : "text/fhirpath", + "expression" : "%ObsPulseRhythmLatest.value.coding.where(system='http://snomed.info/sct').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsPulseRhythmDateString", + "language" : "text/fhirpath", + "expression" : "%ObsPulseRhythmLatest.effective.toString().substring(0,10)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsPulseRhythmDateFormatted", + "language" : "text/fhirpath", + "expression" : "%ObsPulseRhythmDateString.substring(8,2).toInteger().toString() + ' ' + %ObsPulseRhythmDateString.substring(5,2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + %ObsPulseRhythmDateString.substring(0,4)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsOxygenSaturationValue", + "language" : "text/fhirpath", + "expression" : "%ObsOxygenSaturationLatest.value.where(exists(system='http://unitsofmeasure.org' and code='%')).value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsOxygenSaturationString", + "language" : "text/fhirpath", + "expression" : "%ObsOxygenSaturationLatest.effective.toString().substring(0,10)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsOxygenSaturationDateFormatted", + "language" : "text/fhirpath", + "expression" : "%ObsOxygenSaturationString.substring(8,2).toInteger().toString() + ' ' + %ObsOxygenSaturationString.substring(5,2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + %ObsOxygenSaturationString.substring(0,4)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsSmokingStatusValue", + "language" : "text/fhirpath", + "expression" : "%ObsSmokingStatusLatest.value.coding.where(system='http://snomed.info/sct').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsSmokingStatusDateString", + "language" : "text/fhirpath", + "expression" : "%ObsSmokingStatusLatest.effective.toString().substring(0,10)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsSmokingStatusDateFormatted", + "language" : "text/fhirpath", + "expression" : "%ObsSmokingStatusDateString.substring(8,2).toInteger().toString() + ' ' + %ObsSmokingStatusDateString.substring(5,2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + %ObsSmokingStatusDateString.substring(0,4)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsAlcoholStatusValue", + "language" : "text/fhirpath", + "expression" : "%ObsAlcoholStatusLatest.value.coding.where(system='http://snomed.info/sct').first()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsAlcoholStatusDateString", + "language" : "text/fhirpath", + "expression" : "%ObsAlcoholStatusLatest.effective.toString().substring(0,10)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsAlcoholStatusDateFormatted", + "language" : "text/fhirpath", + "expression" : "%ObsAlcoholStatusDateString.substring(8,2).toInteger().toString() + ' ' + %ObsAlcoholStatusDateString.substring(5,2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + %ObsAlcoholStatusDateString.substring(0,4)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBloodPressureValue", + "language" : "text/fhirpath", + "expression" : "%ObsBloodPressureLatest.component.where(code.coding.exists(code='8480-6')).value.value.round(0).toString() + ' / ' + %ObsBloodPressureLatest.component.where(code.coding.exists(code='8462-4')).value.value.round(0).toString()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBloodPressureDateString", + "language" : "text/fhirpath", + "expression" : "%ObsBloodPressureLatest.effective.toString().substring(0,10)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/variable", + "valueExpression" : { + "name" : "ObsBloodPressureDateFormatted", + "language" : "text/fhirpath", + "expression" : "%ObsBloodPressureDateString.substring(8,2).toInteger().toString() + ' ' + %ObsBloodPressureDateString.substring(5,2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + %ObsBloodPressureDateString.substring(0,4)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/artifact-versionAlgorithm", + "valueCoding" : { + "system" : "http://hl7.org/fhir/version-algorithm", + "code" : "semver" + } + }], + "url" : "http://www.health.gov.au/assessments/GPChronicConditionManagementPlan", + "version" : "0.1.0", + "name" : "GPChronicConditionManagementPlan", + "title" : "GP Chronic Condition Management Plan", + "status" : "draft", + "experimental" : false, + "subjectType" : ["Patient"], + "date" : "2026-06-10", + "publisher" : "AEHRC CSIRO", + "contact" : [{ + "name" : "AEHRC CSIRO", + "telecom" : [{ + "system" : "url", + "value" : "http://example.org/example-publisher" + }] + }], + "description" : "GP Chronic Condition Management Plan", + "copyright" : "Copyright © 2026 Australian Government Department of Health, Disability and Ageing. All rights reserved.\nThis material is published for evaluation and local testing only, pending selection of a final licence. You may clone, build, and run it locally/personally to evaluate the IG. No other rights are granted — including no redistribution, modification for distribution, sublicensing, or commercial/production use. Final licence terms will be published and will supersede this notice.\n", + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "tab-container" + }] + } + }], + "linkId" : "container", + "type" : "group", + "repeats" : false, + "item" : [{ + "linkId" : "patient", + "text" : "Patient details", + "type" : "group", + "repeats" : false, + "item" : [{ + "linkId" : "patient-instructions", + "text" : "This form has been prefilled with information that was available from the patient's health record. Upon saving, the form will be stored, but additional information entered will not be used to update the patient's health record. Changes to patient demographic information should be made in the source system.", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-xhtml", + "valueString" : "
\n

This form has been prefilled with information that was available from the patient's health record. Upon saving, the form will be stored, but additional information entered will not be used to update the patient's health record. Changes to patient demographic information should be made in the source system.

" + }] + }, + "type" : "display" + }, + { + "linkId" : "patient-consent", + "text" : "Consent", + "type" : "group", + "repeats" : false, + "item" : [{ + "linkId" : "patient-consent-consentforplan", + "text" : "Consent given to proceed with plan after discussion of the purpose, benefits, process and costs", + "type" : "boolean", + "repeats" : false + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "(%patient.name.where(use='official').select((given.join(' ') | family).join(' ') | text) | %patient.name.select((given.join(' ') | family).join(' ') | text)).first()" + } + }], + "linkId" : "patient-name", + "text" : "Name", + "type" : "string", + "repeats" : false, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "(%patient.name.where(use='usual').select((given.join(' ') | family).join(' ') | text)).first()" + } + }], + "linkId" : "patient-preferredname", + "text" : "Preferred name", + "type" : "string", + "repeats" : false, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%patient.extension.where(exists(url='http://hl7.org/fhir/StructureDefinition/individual-pronouns') and extension.where(url='period').value.end.empty()).extension.where(url='value').value.coding" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "patient-preferredpronouns", + "text" : "Preferred pronouns", + "type" : "choice", + "repeats" : false, + "readOnly" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/australian-pronouns-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%patient.birthDate" + } + }], + "linkId" : "patient-dob", + "text" : "Date of birth", + "type" : "date", + "repeats" : false, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(today().toString().select(substring(5,2) & substring(8,2)).toInteger() > %patient.birthDate.toString().select(substring(5,2) & substring(8,2)).toInteger(), today().toString().substring(0,4).toInteger() - %patient.birthDate.toString().substring(0,4).toInteger(), today().toString().substring(0,4).toInteger() - %patient.birthDate.toString().substring(0,4).toInteger() - 1)" + } + }], + "linkId" : "patient-age", + "text" : "Age", + "type" : "integer", + "repeats" : false, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%patient.extension.where(exists(url='http://hl7.org/fhir/StructureDefinition/individual-recordedSexOrGender' and extension.where(exists(url='type' and value.coding.code='1515311000168102')) and extension.where(url='effectivePeriod').value.end.empty())).extension.where(url='value').value.coding" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "patient-sexatbirth", + "text" : "Sex assigned at birth", + "type" : "choice", + "repeats" : false, + "readOnly" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/biological-sex-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%patient.extension.where(exists(url='http://hl7.org/fhir/StructureDefinition/individual-genderIdentity') and extension.where(url='period').value.end.empty()).extension.where(url='value').value.coding" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "patient-genderidentity", + "text" : "Gender identity", + "type" : "choice", + "repeats" : false, + "readOnly" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/gender-identity-response-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%patient.extension.where(url='http://hl7.org.au/fhir/StructureDefinition/indigenous-status').value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "patient-firstnationsstatus", + "text" : "Aboriginal and/or Torres Strait Islander status", + "type" : "choice", + "repeats" : false, + "readOnly" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/australian-indigenous-status-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%patient.extension.where(url='http://hl7.org.au/fhir/StructureDefinition/closing-the-gap-registration').value" + } + }], + "linkId" : "patient-ctg", + "text" : "Registered for Closing the Gap PBS Co-payment Measure (CTG)", + "type" : "boolean", + "repeats" : false, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%age >= 50" + } + }], + "linkId" : "patient-myagedcare", + "text" : "My Aged Care", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "radio-button" + }] + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation", + "valueCode" : "horizontal" + }], + "linkId" : "patient-myagedcare-registered", + "text" : "Registered for My Aged Care", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "#YesNoPending" + }, + { + "linkId" : "patient-myagedcare-number", + "text" : "My Aged Care Number", + "type" : "string", + "enableWhen" : [{ + "question" : "patient-myagedcare-registered", + "operator" : "=", + "answerCoding" : { + "system" : "http://terminology.hl7.org/CodeSystem/v2-0136", + "code" : "Y" + } + }], + "repeats" : false + }, + { + "linkId" : "patient-myagedcare-comment", + "text" : "Comment", + "type" : "string", + "repeats" : false + }] + }, + { + "linkId" : "patient-ndis", + "text" : "National Disability Insurance Scheme", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "radio-button" + }] + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation", + "valueCode" : "horizontal" + }], + "linkId" : "patient-ndis-registered", + "text" : "Registered for NDIS", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "#YesNoPending" + }, + { + "linkId" : "patient-ndis-number", + "text" : "NDIS Number", + "type" : "string", + "enableWhen" : [{ + "question" : "patient-ndis-registered", + "operator" : "=", + "answerCoding" : { + "system" : "http://terminology.hl7.org/CodeSystem/v2-0136", + "code" : "Y" + } + }], + "repeats" : false + }, + { + "linkId" : "patient-ndis-comment", + "text" : "Comment", + "type" : "string", + "repeats" : false + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext", + "valueExpression" : { + "name" : "MedicareNumberArray", + "language" : "text/fhirpath", + "expression" : "%patient.identifier.where(type.coding.exists(system='http://terminology.hl7.org/CodeSystem/v2-0203' and code='MC'))" + } + }], + "linkId" : "patient-medicare", + "text" : "Medicare card number", + "type" : "group", + "repeats" : true, + "readOnly" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%MedicareNumberArray.value.substring(0,10)" + } + }, + { + "extension" : [{ + "url" : "key", + "valueId" : "constraint-regex-medicarecardnumber-1" + }, + { + "url" : "severity", + "valueCode" : "warning" + }, + { + "url" : "expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "matches('^[0-9]{10}$')" + } + }, + { + "url" : "human", + "valueString" : "Medicare card number must be 10 digits" + }], + "url" : "http://hl7.org/fhir/StructureDefinition/targetConstraint" + }], + "linkId" : "patient-medicare-number", + "text" : "Number", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%MedicareNumberArray.value.substring(10,1)" + } + }, + { + "extension" : [{ + "url" : "key", + "valueId" : "constraint-regex-medicarecardnumber-2" + }, + { + "url" : "severity", + "valueCode" : "warning" + }, + { + "url" : "expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "matches('^[0-9]{1}$')" + } + }, + { + "url" : "human", + "valueString" : "Medicare card reference number must be 1 digit" + }], + "url" : "http://hl7.org/fhir/StructureDefinition/targetConstraint" + }], + "linkId" : "patient-medicare-referencenumber", + "text" : "Reference number", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%MedicareNumberArray.period.end.toString()" + } + }], + "linkId" : "patient-medicare-expiry", + "text" : "Expiry", + "type" : "string", + "repeats" : false + }] + }, + { + "linkId" : "patient-contact", + "text" : "Contact information", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%patient.telecom.where(system = 'phone' and use = 'home').value" + } + }], + "linkId" : "patient-contact-homephone", + "text" : "Home phone", + "type" : "string", + "repeats" : true, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%patient.telecom.where(system = 'phone' and use = 'mobile').value" + } + }], + "linkId" : "patient-contact-mobilephone", + "text" : "Mobile phone", + "type" : "string", + "repeats" : true, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%patient.telecom.where(all(system = 'email' and (use.empty() or use = 'home'))).value" + } + }], + "linkId" : "patient-contact-email", + "text" : "Email", + "type" : "string", + "repeats" : true, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext", + "valueExpression" : { + "name" : "HomeAddressArray", + "language" : "text/fhirpath", + "expression" : "%patient.address.where(all(use='home' and (type.empty() or type!='postal')))" + } + }], + "linkId" : "patient-contact-homeaddress", + "text" : "Home address", + "type" : "group", + "repeats" : false, + "readOnly" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%HomeAddressArray.extension('http://hl7.org.au/fhir/StructureDefinition/no-fixed-address').value" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "check-box" + }] + } + }], + "linkId" : "patient-contact-homeaddress-nofixedaddress", + "text" : "No fixed address", + "type" : "boolean", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%HomeAddressNoFixedAddress.empty() or %HomeAddressNoFixedAddress = false" + } + }], + "linkId" : "patient-contact-homeaddress-details", + "text" : "Home address", + "_text" : { + "extension" : [{ + "url" : "https://smartforms.csiro.au/ig/StructureDefinition/QuestionnaireItemTextHidden", + "valueBoolean" : true + }] + }, + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%HomeAddressArray.select(line.join(', '))" + } + }], + "linkId" : "patient-contact-homeaddress-details-streetaddress", + "text" : "Street address", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%HomeAddressArray.city" + } + }], + "linkId" : "patient-contact-homeaddress-details-city", + "text" : "City", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%HomeAddressArray.state" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "patient-contact-homeaddress-details-state", + "text" : "State", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/australian-states-territories-2" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%HomeAddressArray.postalCode" + } + }, + { + "extension" : [{ + "url" : "key", + "valueId" : "constraint-regex-postcode" + }, + { + "url" : "severity", + "valueCode" : "warning" + }, + { + "url" : "expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "matches('^[0-9]{4}$')" + } + }, + { + "url" : "human", + "valueString" : "Postcode must be 4 digits" + }], + "url" : "http://hl7.org/fhir/StructureDefinition/targetConstraint" + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/entryFormat", + "valueString" : "####" + }], + "linkId" : "patient-contact-homeaddress-details-postcode", + "text" : "Postcode", + "type" : "string", + "repeats" : false + }] + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext", + "valueExpression" : { + "name" : "PostalAddressArray", + "language" : "text/fhirpath", + "expression" : "%patient.address.where(type='postal')" + } + }, + { + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%PostalAddressArray.exists()" + } + }], + "linkId" : "patient-contact-postaladdress", + "text" : "Postal address", + "type" : "group", + "repeats" : true, + "readOnly" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%PostalAddressArray.use" + } + }], + "linkId" : "patient-contact-postaladdress-purpose", + "text" : "Purpose of use", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%PostalAddressArray.select(line.join(', '))" + } + }], + "linkId" : "patient-contact-postaladdress-streetaddress", + "text" : "Street address", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%PostalAddressArray.city" + } + }], + "linkId" : "patient-contact-postaladdress-city", + "text" : "City", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%PostalAddressArray.state" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "patient-contact-postaladdress-state", + "text" : "State", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/australian-states-territories-2" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%PostalAddressArray.postalCode" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/regex", + "valueString" : "matches('^[0-9]{4}$')" + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/entryFormat", + "valueString" : "####" + }], + "linkId" : "patient-contact-postaladdress-postcode", + "text" : "Postcode", + "type" : "string", + "repeats" : false + }] + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext", + "valueExpression" : { + "name" : "ContactsArray", + "language" : "text/fhirpath", + "expression" : "%patient.contact.where(relationship.coding.exists(code = 'C' or code = '394619001' or code = '133932002' or code = '1620171000168100' or code = 'CP' or code = 'N'))" + } + }], + "linkId" : "patient-contacts", + "text" : "Carers and key contacts", + "type" : "group", + "repeats" : true, + "item" : [{ + "linkId" : "patient-contacts-preferred", + "text" : "Preferred contact", + "type" : "boolean", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ContactsArray.relationship.coding.where(exists(code = 'C' or code = '394619001' or code = '133932002' or code = '1620171000168100' or code = 'CP' or code = 'N'))" + } + }], + "linkId" : "patient-contacts-role", + "text" : "Role", + "type" : "open-choice", + "repeats" : true, + "answerOption" : [{ + "valueCoding" : { + "system" : "http://snomed.info/sct", + "code" : "133932002" + } + }, + { + "valueCoding" : { + "system" : "http://snomed.info/sct", + "code" : "394619001" + } + }, + { + "valueCoding" : { + "system" : "http://snomed.info/sct", + "code" : "1620171000168100" + } + }, + { + "valueCoding" : { + "system" : "http://terminology.hl7.org/CodeSystem/v2-0131", + "code" : "C" + } + }, + { + "valueCoding" : { + "system" : "http://terminology.hl7.org/CodeSystem/v2-0131", + "code" : "CP" + } + }, + { + "valueCoding" : { + "system" : "http://terminology.hl7.org/CodeSystem/v2-0131", + "code" : "N" + } + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ContactsArray.name.select((given.join(' ') | family).join(' ') | text)" + } + }], + "linkId" : "patient-contacts-name", + "text" : "Name", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ContactsArray.telecom.where(system = 'phone').value" + } + }], + "linkId" : "patient-contacts-phone", + "text" : "Phone", + "type" : "string", + "repeats" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ContactsArray.telecom.where(system = 'email').value" + } + }], + "linkId" : "patient-contacts-email", + "text" : "Email", + "type" : "string", + "repeats" : true + }, + { + "linkId" : "patient-contacts-relationship", + "text" : "Relationship to patient", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "radio-button" + }] + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation", + "valueCode" : "horizontal" + }], + "linkId" : "patient-contacts-present", + "text" : "Present at appointment", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "#YesNo" + }] + }, + { + "linkId" : "patient-additionalinformation", + "text" : "Additional information", + "type" : "text", + "repeats" : false + }] + }, + { + "linkId" : "practitioner", + "text" : "Practitioner details", + "type" : "group", + "repeats" : false, + "item" : [{ + "linkId" : "practitioner-instructions", + "text" : "This form has been prefilled with information that was available from the patient's health record. Upon saving, the form will be stored, but additional information entered will not be used to update the patient's health record. Changes to practitioner information should be made in the source system.", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-xhtml", + "valueString" : "
\n

This form has been prefilled with information that was available from the patient's health record. Upon saving, the form will be stored, but additional information entered will not be used to update the patient's health record. Changes to practitioner information should be made in the source system.

" + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "(%user.name.where(use='official').select((given.join(' ') | family).join(' ') | text) | %user.name.select((given.join(' ') | family).join(' ') | text)).first()" + } + }], + "linkId" : "practitioner-name", + "text" : "Name", + "type" : "string", + "repeats" : false, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%gpccmppractitionerrole.telecom.where(system = 'phone').value" + } + }], + "linkId" : "practitioner-phone", + "text" : "Phone", + "type" : "string", + "repeats" : true, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%gpccmppractitionerrole.telecom.where(system = 'email').value" + } + }], + "linkId" : "practitioner-email", + "text" : "Email", + "type" : "string", + "repeats" : true, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%gpccmppractitionerrole.identifier.where(system = 'http://ns.electronichealth.net.au/id/medicare-provider-number').value" + } + }], + "linkId" : "practitioner-medicareprovidernumber", + "text" : "Medicare provider number", + "type" : "string", + "repeats" : true, + "readOnly" : true + }, + { + "linkId" : "practitioner-clinicdetails", + "text" : "Clinic details", + "type" : "group", + "repeats" : false, + "readOnly" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ClinicLocation.name" + } + }], + "linkId" : "practitioner-clinicdetails-name", + "text" : "Name", + "type" : "string", + "repeats" : false, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ClinicLocation.address.text.empty()" + } + }], + "linkId" : "practitioner-clinicdetails-address", + "text" : "Address", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ClinicLocation.address.select(line.join(', '))" + } + }], + "linkId" : "practitioner-clinicdetails-address-streetaddress", + "text" : "Street address", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ClinicLocation.address.city" + } + }], + "linkId" : "practitioner-clinicdetails-address-city", + "text" : "City", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ClinicLocation.address.state" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "practitioner-clinicdetails-address-state", + "text" : "State", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/australian-states-territories-2" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ClinicLocation.address.postalCode" + } + }, + { + "extension" : [{ + "url" : "key", + "valueId" : "constraint-regex-postcode" + }, + { + "url" : "severity", + "valueCode" : "warning" + }, + { + "url" : "expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "matches('^[0-9]{4}$')" + } + }, + { + "url" : "human", + "valueString" : "Postcode must be 4 digits" + }], + "url" : "http://hl7.org/fhir/StructureDefinition/targetConstraint" + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/entryFormat", + "valueString" : "####" + }], + "linkId" : "practitioner-clinicdetails-address-postcode", + "text" : "Postcode", + "type" : "string", + "repeats" : false + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ClinicLocation.address.line.empty() and %ClinicLocation.address.text.exists()" + } + }, + { + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ClinicLocation.address.text" + } + }], + "linkId" : "practitioner-clinicdetails-addresstext", + "text" : "Address", + "type" : "string", + "repeats" : false + }] + }] + }, + { + "linkId" : "clinicaldetails", + "text" : "Clinical details", + "type" : "group", + "repeats" : false, + "item" : [{ + "linkId" : "clinicaldetails-instructions", + "text" : "This form has been prefilled with information that was available from the patient's health record. Upon saving, the form will be stored, but additional information entered will not be used to update the patient's health record.", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-xhtml", + "valueString" : "
\n

This form has been prefilled with information that was available from the patient's health record. Upon saving, the form will be stored, but additional information entered will not be used to update the patient's health record.

" + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-collapsible", + "valueCode" : "default-open" + }], + "linkId" : "clinicaldetails-problemsdiagnoses", + "text" : "Problems/Diagnoses", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "https://smartforms.csiro.au/ig/StructureDefinition/GroupHideAddItemButton", + "valueBoolean" : true + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "gtable" + }] + } + }, + { + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext", + "valueExpression" : { + "name" : "ConditionArray", + "language" : "text/fhirpath", + "expression" : "%Condition.entry.resource.where(verificationStatus.coding.all(code.empty() or code='confirmed'))" + } + }], + "linkId" : "clinicaldetails-problemsdiagnoses-recordedproblems", + "text" : "Recorded problems/diagnoses", + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }, + { + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 30, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }, + { + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ConditionArray.code.select((coding.where(system='http://snomed.info/sct') | coding.where(system!='http://snomed.info/sct').first() | text ).first())" + } + }], + "linkId" : "clinicaldetails-problemsdiagnoses-recordedproblems-condition", + "text" : "Condition", + "type" : "open-choice", + "repeats" : false, + "readOnly" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/clinical-condition-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }, + { + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ConditionArray.clinicalStatus.coding" + } + }], + "linkId" : "clinicaldetails-problemsdiagnoses-recordedproblems-clinicalstatus", + "text" : "Clinical status", + "type" : "choice", + "repeats" : false, + "readOnly" : false, + "answerValueSet" : "http://hl7.org/fhir/ValueSet/condition-clinical|4.0.1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ConditionArray.onset.ofType(dateTime).toString().substring(0,10).toDate()" + } + }], + "linkId" : "clinicaldetails-problemsdiagnoses-recordedproblems-onsetdate", + "text" : "Onset date", + "type" : "date", + "repeats" : false, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ConditionArray.abatement.ofType(dateTime).toString().substring(0,10).toDate()" + } + }], + "linkId" : "clinicaldetails-problemsdiagnoses-recordedproblems-abatementdate", + "text" : "Abatement date", + "type" : "date", + "repeats" : false, + "readOnly" : false + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "gtable" + }] + } + }], + "linkId" : "clinicaldetails-problemsdiagnoses-newproblems", + "text" : "New problems/diagnoses", + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }, + { + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 35, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }], + "linkId" : "clinicaldetails-problemsdiagnoses-newproblems-condition", + "text" : "Condition", + "type" : "open-choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/clinical-condition-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 25, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }], + "linkId" : "clinicaldetails-problemsdiagnoses-newproblems-onsetdate", + "text" : "Onset date", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 40, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }], + "linkId" : "clinicaldetails-problemsdiagnoses-newproblems-comment", + "text" : "Comment", + "type" : "string", + "repeats" : false + }] + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-collapsible", + "valueCode" : "default-open" + }], + "linkId" : "clinicaldetails-allergies", + "text" : "Adverse reaction risks", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext", + "valueExpression" : { + "name" : "AllergyIntoleranceArray", + "language" : "text/fhirpath", + "expression" : "%AllergyIntolerance.entry.resource.where(clinicalStatus.coding.exists(code='active')).where(verificationStatus.coding.all(code.empty() or code='confirmed'))" + } + }, + { + "url" : "https://smartforms.csiro.au/ig/StructureDefinition/GroupHideAddItemButton", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-allergies-recordedallergies", + "text" : "Recorded adverse reaction risks", + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%AllergyIntoleranceArray.code.select((coding.where(system='http://snomed.info/sct') | coding.where(system!='http://snomed.info/sct').first() | text ).first())" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "clinicaldetails-allergies-recordedallergies-substance", + "text" : "Substance", + "type" : "open-choice", + "repeats" : false, + "readOnly" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/adverse-reaction-agent-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%AllergyIntoleranceArray.clinicalStatus.coding" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "clinicaldetails-allergies-recordedallergies-status", + "text" : "Status", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "http://hl7.org/fhir/ValueSet/allergyintolerance-clinical|4.0.1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%AllergyIntoleranceArray.reaction.manifestation.select((coding.where(system='http://snomed.info/sct') | coding.where(system!='http://snomed.info/sct').first() | text ).first())" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "clinicaldetails-allergies-recordedallergies-manifestation", + "text" : "Manifestation", + "type" : "open-choice", + "repeats" : true, + "readOnly" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/clinical-finding-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%AllergyIntoleranceArray.note[0].text" + } + }], + "linkId" : "clinicaldetails-allergies-recordedallergies-comment", + "text" : "Comment", + "type" : "text", + "repeats" : false + }] + }, + { + "linkId" : "clinicaldetails-allergies-newallergies", + "text" : "New adverse reaction risks", + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "clinicaldetails-allergies-newallergies-substance", + "text" : "Substance", + "type" : "open-choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/adverse-reaction-agent-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "clinicaldetails-allergies-newallergies-manifestation", + "text" : "Manifestation", + "type" : "open-choice", + "repeats" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/clinical-finding-1" + }, + { + "linkId" : "clinicaldetails-allergies-newallergies-comment", + "text" : "Comment", + "type" : "text", + "repeats" : false + }] + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-collapsible", + "valueCode" : "default-open" + }], + "linkId" : "clinicaldetails-medications", + "text" : "Medications (current)", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemPopulationContext", + "valueExpression" : { + "name" : "MedicationStatementArray", + "language" : "text/fhirpath", + "expression" : "%MedicationStatement.entry.resource.ofType(MedicationStatement)" + } + }, + { + "url" : "https://smartforms.csiro.au/ig/StructureDefinition/GroupHideAddItemButton", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-medications-recordedmedications", + "text" : "Recorded medications", + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }, + { + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%MedicationStatementArray.medication.reference.replace('#', '') in %medicationsFromContained.id, %medicationsFromContained.where(id = %MedicationStatementArray.medication.reference.replace('#', '')).code.select((coding.where(system='http://snomed.info/sct') | coding.where(system!='http://snomed.info/sct').first() | text ).first()), iif(%MedicationStatementArray.medication.reference.replace('Medication/', '') in %medicationsFromRef.id , %medicationsFromRef.where(id = %MedicationStatementArray.medication.reference.replace('Medication/', '')).code.select((coding.where(system='http://snomed.info/sct') | coding.where(system!='http://snomed.info/sct').first() | text ).first()), %MedicationStatementArray.medication.select((coding.where(system='http://snomed.info/sct') | coding.where(system!='http://snomed.info/sct').first() | text ).first())))" + } + }], + "linkId" : "clinicaldetails-medications-recordedmedications-medication", + "text" : "Medication", + "type" : "open-choice", + "repeats" : false, + "readOnly" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/australian-medication-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%MedicationStatementArray.status" + } + }], + "linkId" : "clinicaldetails-medications-recordedmedications-status", + "text" : "Status", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "#MedicationStatementStatusLimited" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%MedicationStatementArray.dosage.text" + } + }], + "linkId" : "clinicaldetails-medications-recordedmedications-dosage", + "text" : "Dosage", + "type" : "text", + "repeats" : false, + "readOnly" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }, + { + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%MedicationStatementArray.reasonCode.select((coding.where(system='http://snomed.info/sct') | coding.where(system!='http://snomed.info/sct').first() | text ).first())" + } + }], + "linkId" : "clinicaldetails-medications-recordedmedications-indication", + "text" : "Indication", + "type" : "open-choice", + "repeats" : true, + "readOnly" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/medication-reason-taken-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%MedicationStatementArray.note.text" + } + }], + "linkId" : "clinicaldetails-medications-recordedmedications-comment", + "text" : "Comment", + "type" : "text", + "repeats" : false, + "readOnly" : false + }] + }, + { + "linkId" : "clinicaldetails-medications-newmedications", + "text" : "New medications", + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "clinicaldetails-medications-newmedications-medication", + "text" : "Medication", + "type" : "open-choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/australian-medication-1" + }, + { + "linkId" : "clinicaldetails-medications-newmedications-dosage", + "text" : "Dosage", + "type" : "text", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "clinicaldetails-medications-newmedications-indication", + "text" : "Indication", + "type" : "open-choice", + "repeats" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/medication-reason-taken-1" + }, + { + "linkId" : "clinicaldetails-medications-newmedications-comment", + "text" : "Comment", + "type" : "text", + "repeats" : false + }] + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-collapsible", + "valueCode" : "default-open" + }], + "linkId" : "clinicaldetails-observations", + "text" : "Observations", + "type" : "group", + "repeats" : false, + "item" : [{ + "linkId" : "clinicaldetails-observations-instructions", + "text" : "The tabled observations will display the most recent results available from the patient record. New observations may be added.", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-xhtml", + "valueString" : "

The tabled observations will display the most recent results available from the patient record. New observations may be added.

" + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "grid" + }] + } + }], + "linkId" : "clinicaldetails-observations-maingrid", + "text" : "Observations", + "_text" : { + "extension" : [{ + "url" : "https://smartforms.csiro.au/ig/StructureDefinition/QuestionnaireItemTextHidden", + "valueBoolean" : true + }] + }, + "type" : "group", + "repeats" : false, + "item" : [{ + "linkId" : "clinicaldetails-observations-maingrid-height", + "text" : "Height", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-style", + "valueString" : "text-align: left;" + }], + "linkId" : "clinicaldetails-observations-maingrid-height-lastresult", + "text" : "Last result", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsBodyHeightValue.exists() and %ObsBodyHeightDateFormatted.exists(), %ObsBodyHeightValue.round(0).toString() + ' cm ( ' + %ObsBodyHeightDateFormatted + ' )', 'Not available')" + } + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsBodyHeightValue.round(0)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "cm" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-height-lastresultvalue", + "type" : "decimal", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsBodyHeightLatest.effective.toString().substring(0,10).toDate()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-maingrid-height-lastresultdate", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "cm" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-height-newresultvalue", + "text" : "New result", + "type" : "decimal", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "unit" + }] + } + }], + "linkId" : "clinicaldetails-observations-maingrid-height-newresultvalue-unit", + "text" : "cm", + "type" : "display" + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%resource.repeat(item).where(linkId='clinicaldetails-observations-maingrid-height-newresultvalue').answer.value.exists(), today())" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-height-newresultdate", + "text" : "New result date", + "type" : "date", + "repeats" : false + }] + }, + { + "linkId" : "clinicaldetails-observations-maingrid-weight", + "text" : "Weight", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-style", + "valueString" : "text-align: left;" + }], + "linkId" : "clinicaldetails-observations-maingrid-weight-lastresult", + "text" : "Last result", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsBodyWeightValue.exists() and %ObsBodyWeightDateFormatted.exists(), %ObsBodyWeightValue.round(1).toString() + ' kg ( ' + %ObsBodyWeightDateFormatted + ' )', 'Not available')" + } + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsBodyWeightValue.round(1)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "kg" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-weight-lastresultvalue", + "type" : "decimal", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsBodyWeightLatest.effective.toString().substring(0,10).toDate()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-maingrid-weight-lastresultdate", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "kg" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-weight-newresultvalue", + "text" : "New result", + "type" : "decimal", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "unit" + }] + } + }], + "linkId" : "clinicaldetails-observations-maingrid-weight-newresultvalue-unit", + "text" : "kg", + "type" : "display" + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%resource.repeat(item).where(linkId='clinicaldetails-observations-maingrid-weight-newresultvalue').answer.value.exists(), today())" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-weight-newresultdate", + "text" : "New result date", + "type" : "date", + "repeats" : false + }] + }, + { + "linkId" : "clinicaldetails-observations-maingrid-bmi", + "text" : "BMI (calculated)", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-style", + "valueString" : "text-align: left;" + }], + "linkId" : "clinicaldetails-observations-maingrid-bmi-lastresult", + "text" : "Last result", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsBodyWeightValue.exists() and %ObsBodyHeightValue.exists() and %ObsBodyHeightValue > 0, (%ObsBodyWeightValue/((%ObsBodyHeightValue/100).power(2))).round(1).toString() + ' kg/m2', 'Not available')" + } + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsBodyWeightValue.exists() and %ObsBodyHeightValue.exists() and %ObsBodyHeightValue > 0, (%ObsBodyWeightValue/((%ObsBodyHeightValue/100).power(2))).round(1), {})" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "kg/m2" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-bmi-lastresultvalue", + "type" : "decimal", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "description" : "BMI calculation", + "language" : "text/fhirpath", + "expression" : "iif(%weight.exists() and %height.exists() and %height > 0, (%weight/((%height/100).power(2))).round(1), {})" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "kg/m2" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-bmi-newresultvalue", + "text" : "New result", + "type" : "decimal", + "repeats" : false, + "readOnly" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "unit" + }] + } + }], + "linkId" : "clinicaldetails-observations-maingrid-bmi-newresult-unit", + "text" : "kg/m2", + "type" : "display" + }] + }] + }, + { + "linkId" : "clinicaldetails-observations-maingrid-waistcircumference", + "text" : "Waist circumference", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-style", + "valueString" : "text-align: left;" + }], + "linkId" : "clinicaldetails-observations-maingrid-waistcircumference-lastresult", + "text" : "Last result", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsWaistCircumferenceValue.exists() and %ObsWaistCircumferenceDateFormatted.exists(), %ObsWaistCircumferenceValue.round(0).toString() + ' cm ( ' + %ObsWaistCircumferenceDateFormatted + ' )', 'Not available')" + } + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsWaistCircumferenceValue.round(0)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "cm" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-waistcircumference-lastresultvalue", + "type" : "decimal", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsWaistCircumferenceLatest.effective.toString().substring(0,10).toDate()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-maingrid-waistcircumference-lastresultdate", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "cm" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-waistcircumference-newresultvalue", + "text" : "New result", + "type" : "decimal", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "unit" + }] + } + }], + "linkId" : "clinicaldetails-observations-maingrid-waistcircumference-newresultvalue-unit", + "text" : "cm", + "type" : "display" + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%resource.repeat(item).where(linkId='clinicaldetails-observations-maingrid-waistcircumference-newresultvalue').answer.value.exists(), today())" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-waistcircumference-newdate", + "text" : "New result date", + "type" : "date", + "repeats" : false + }] + }, + { + "linkId" : "clinicaldetails-observations-maingrid-pulserate", + "text" : "Pulse rate", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-style", + "valueString" : "text-align: left;" + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserate-lastresult", + "text" : "Last result", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsPulseRateValue.exists() and %ObsPulseRateDateFormatted.exists(), %ObsPulseRateValue.round(0).toString() + ' /min ( ' + %ObsPulseRateDateFormatted + ' )', 'Not available')" + } + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsPulseRateValue.round(0)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "/min" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserate-lastresultvalue", + "type" : "decimal", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsPulseRateLatest.effective.toString().substring(0,10).toDate()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserate-lastresultdate", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "/min" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserate-newresultvalue", + "text" : "New result", + "type" : "integer", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "unit" + }] + } + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserate-newresultvalue-unit", + "text" : "/min", + "type" : "display" + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%resource.repeat(item).where(linkId='clinicaldetails-observations-maingrid-pulserate-newresultvalue').answer.value.exists(), today())" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserate-newresultdate", + "text" : "New result date", + "type" : "date", + "repeats" : false + }] + }, + { + "linkId" : "clinicaldetails-observations-maingrid-pulserhythm", + "text" : "Pulse rhythm", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-style", + "valueString" : "text-align: left;" + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserhythm-lastresult", + "text" : "Last result", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsPulseRhythmValue.exists() and %ObsPulseRhythmDateFormatted.exists(), %ObsPulseRhythmValue.display + ' ( ' + %ObsPulseRhythmDateFormatted + ' )', 'Not available')" + } + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsPulseRhythmValue" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserhythm-lastresultvalue", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "#pulse-rhythm-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsPulseRhythmLatest.effective.toString().substring(0,10).toDate()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserhythm-lastresultdate", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserhythm-newresultvalue", + "text" : "New result", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "#pulse-rhythm-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%resource.repeat(item).where(linkId='clinicaldetails-observations-maingrid-pulserhythm-newresultvalue').answer.value.exists(), today())" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-pulserhythm-newresultdate", + "text" : "New result date", + "type" : "date", + "repeats" : false + }] + }, + { + "linkId" : "clinicaldetails-observations-maingrid-oxygensaturation", + "text" : "Oxygen saturation", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-style", + "valueString" : "text-align: left;" + }], + "linkId" : "clinicaldetails-observations-maingrid-oxygensaturation-lastresult", + "text" : "Last result", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsOxygenSaturationValue.exists() and %ObsOxygenSaturationDateFormatted.exists(), %ObsOxygenSaturationValue.round(0).toString() + ' % ( ' + %ObsOxygenSaturationDateFormatted + ' )', 'Not available')" + } + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsOxygenSaturationValue.round(0)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-oxygensaturation-lastresultvalue", + "type" : "decimal", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsOxygenSaturationLatest.effective.toString().substring(0,10).toDate()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-maingrid-oxygensaturation-lastresultdate", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-oxygensaturation-newresultvalue", + "text" : "New result", + "type" : "integer", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "unit" + }] + } + }], + "linkId" : "clinicaldetails-observations-maingrid-oxygensaturation-newresultvalue-unit", + "text" : "%", + "type" : "display" + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%resource.repeat(item).where(linkId='clinicaldetails-observations-maingrid-oxygensaturation-newresultvalue').answer.value.exists(), today())" + } + }], + "linkId" : "clinicaldetails-observations-maingrid-oxygensaturation-newresultdate", + "text" : "New result date", + "type" : "date", + "repeats" : false + }] + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "grid" + }] + } + }], + "linkId" : "clinicaldetails-observations-bpgrid", + "text" : "Blood pressure", + "_text" : { + "extension" : [{ + "url" : "https://smartforms.csiro.au/ig/StructureDefinition/QuestionnaireItemTextHidden", + "valueBoolean" : true + }] + }, + "type" : "group", + "repeats" : false, + "item" : [{ + "linkId" : "clinicaldetails-observations-bpgrid-bp", + "text" : "Blood pressure", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-style", + "valueString" : "text-align: left;" + }], + "linkId" : "clinicaldetails-observations-bpgrid-bp-lastresult", + "text" : "Last result", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsBloodPressureValue.exists() and %ObsBloodPressureDateFormatted.exists(), %ObsBloodPressureValue + ' mm Hg ( ' + %ObsBloodPressureDateFormatted + ' )', 'Not available')" + } + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsBloodPressureLatest.component.where(code.coding.exists(code='8480-6')).value.value.round(0)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "mm[Hg]" + } + }], + "linkId" : "clinicaldetails-observations-bpgrid-bp-lastresultvaluesystolic", + "type" : "decimal", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsBloodPressureLatest.component.where(code.coding.exists(code='8462-4')).value.value.round(0)" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "mm[Hg]" + } + }], + "linkId" : "clinicaldetails-observations-bpgrid-bp-lastresultvaluediastolic", + "type" : "decimal", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsBloodPressureLatest.effective.toString().substring(0,10).toDate()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-bpgrid-bp-lastresultdate", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "mm[Hg]" + } + }], + "linkId" : "clinicaldetails-observations-bpgrid-bp-newresultsystolic", + "text" : "Systolic", + "type" : "integer", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "unit" + }] + } + }], + "linkId" : "clinicaldetails-observations-bpgrid-bp-newresultsystolic-unit", + "text" : "mm Hg", + "type" : "display" + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-unit", + "valueCoding" : { + "system" : "http://unitsofmeasure.org", + "code" : "mm[Hg]" + } + }], + "linkId" : "clinicaldetails-observations-bpgrid-bp-newresultdiastolic", + "text" : "Diastolic", + "type" : "integer", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "unit" + }] + } + }], + "linkId" : "clinicaldetails-observations-bpgrid-bp-newresultdiastolic-unit", + "text" : "mm Hg", + "type" : "display" + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%resource.repeat(item).where(linkId='clinicaldetails-observations-bpgrid-bp-newresultsystolic').answer.value.exists() or %resource.repeat(item).where(linkId='clinicaldetails-observations-bpgrid-bp-newresultdiastolic').answer.value.exists(), today())" + } + }], + "linkId" : "clinicaldetails-observations-bpgrid-bp-newresultdate", + "text" : "Date performed", + "type" : "date", + "repeats" : false + }] + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "grid" + }] + } + }], + "linkId" : "clinicaldetails-observations-substanceusegrid", + "text" : "Substance use", + "_text" : { + "extension" : [{ + "url" : "https://smartforms.csiro.au/ig/StructureDefinition/QuestionnaireItemTextHidden", + "valueBoolean" : true + }] + }, + "type" : "group", + "repeats" : false, + "item" : [{ + "linkId" : "clinicaldetails-observations-substanceusegrid-smokingstatus", + "text" : "Smoking status", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-style", + "valueString" : "text-align: left;" + }], + "linkId" : "clinicaldetails-observations-substanceusegrid-smokingstatus-laststatus", + "text" : "Last status", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsSmokingStatusValue.exists() and %ObsSmokingStatusDateFormatted.exists(), %ObsSmokingStatusValue.display + ' ( ' + %ObsSmokingStatusDateFormatted + ' )', 'Not available')" + } + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsSmokingStatusValue" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-substanceusegrid-smokingstatus-lastresultvalue", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/smoking-status-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsSmokingStatusLatest.effective.toString().substring(0,10).toDate()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-substanceusegrid-smokingstatus-lastresultdate", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "clinicaldetails-observations-substanceusegrid-smokingstatus-newresultvalue", + "text" : "New status", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/smoking-status-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%resource.repeat(item).where(linkId='clinicaldetails-observations-substanceusegrid-smokingstatus-newresultvalue').answer.value.exists(), today())" + } + }], + "linkId" : "clinicaldetails-observations-substanceusegrid-smokingstatus-newresultdate", + "text" : "New status date", + "type" : "date", + "repeats" : false + }, + { + "linkId" : "clinicaldetails-observations-smokingstatusgrid-smokingstatus-newresultcomment", + "text" : "Comment", + "type" : "string", + "repeats" : false + }] + }, + { + "linkId" : "clinicaldetails-observations-substanceusegrid-alcoholstatus", + "text" : "Alcohol consumption status", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/rendering-style", + "valueString" : "text-align: left;" + }], + "linkId" : "clinicaldetails-observations-substanceusegrid-alcoholstatus-laststatus", + "text" : "Last status", + "_text" : { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%ObsAlcoholStatusValue.exists() and %ObsAlcoholStatusDateFormatted.exists(), %ObsAlcoholStatusValue.display + ' ( ' + %ObsAlcoholStatusDateFormatted + ' )', 'Not available')" + } + }] + }, + "type" : "display" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsAlcoholStatusValue" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-substanceusegrid-alcoholstatus-lastresultvalue", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/alcohol-intake-status-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%ObsAlcoholStatusLatest.effective.toString().substring(0,10).toDate()" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", + "valueBoolean" : true + }], + "linkId" : "clinicaldetails-observations-substanceusegrid-alcoholstatus-lastresultdate", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "clinicaldetails-observations-substanceusegrid-alcoholstatus-newresultvalue", + "text" : "New status", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/alcohol-intake-status-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%resource.repeat(item).where(linkId='clinicaldetails-observations-substanceusegrid-alcoholstatus-newresultvalue').answer.value.exists(), today())" + } + }], + "linkId" : "clinicaldetails-observations-substanceusegrid-alcoholstatus-newresultdate", + "text" : "New status date", + "type" : "date", + "repeats" : false + }, + { + "linkId" : "clinicaldetails-observations-substanceusegrid-alcoholstatus-newresultcomment", + "text" : "Comment", + "type" : "string", + "repeats" : false + }] + }] + }] + }, + { + "linkId" : "clinicaldetails-observations-additionalinformation", + "text" : "Additional information", + "type" : "text", + "repeats" : false + }] + }, + { + "linkId" : "plan", + "text" : "Plan", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "radio-button" + }] + } + }, + { + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "iif(%GPCCMPLatestCompletedAmended.entry.resource.exists(authored > (today() - 12 months)), 'Review', 'New')" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation", + "valueCode" : "horizontal" + }], + "linkId" : "plan-type", + "text" : "New plan or a review of an existing plan?", + "type" : "choice", + "repeats" : false, + "answerOption" : [{ + "valueString" : "New" + }, + { + "valueString" : "Review" + }], + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-display-category", + "code" : "instructions" + }] + } + }], + "linkId" : "plan-type-instructions", + "text" : "Autoselected as 'Review' if a plan has been completed in the last 12 months, otherwise 'New'.", + "type" : "display", + "repeats" : false + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%GPCCMPLatestCompletedAmended.entry.resource.authored.toString().substring(0,10).toDate()" + } + }], + "linkId" : "plan-lastcompleteddate", + "text" : "Date of most recent plan or review", + "type" : "date", + "repeats" : false, + "readOnly" : true + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression", + "valueExpression" : { + "language" : "text/fhirpath", + "expression" : "%GPCCMPLatest.entry.resource.where(status='in-progress').exists()" + } + }], + "linkId" : "plan-inprogress", + "text" : "Incomplete draft plan already exists?", + "type" : "boolean", + "repeats" : false, + "readOnly" : true + }, + { + "linkId" : "plan-conditions", + "text" : "Conditions addressed", + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "plan-conditions-condition", + "text" : "Condition", + "type" : "open-choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/clinical-condition-1" + }, + { + "linkId" : "plan-conditions-onsetdate", + "text" : "Onset date", + "type" : "date", + "repeats" : false + }, + { + "linkId" : "plan-conditions-comments", + "text" : "Comments", + "type" : "text", + "repeats" : false + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-collapsible", + "valueCode" : "default-open" + }], + "linkId" : "plan-goalstasks", + "text" : "Goals and tasks", + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "plan-goalstasks-problemneed", + "text" : "Problems/Needs", + "type" : "open-choice", + "repeats" : true, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/clinical-condition-1" + }, + { + "linkId" : "plan-goalstasks-details-goalsetting", + "text" : "Goal setting", + "type" : "group", + "repeats" : true, + "item" : [{ + "linkId" : "plan-goalstasks-details-goalsetting-goals", + "text" : "Goals", + "type" : "text", + "repeats" : false + }, + { + "linkId" : "plan-goalstasks-details-goalsetting-initiator", + "text" : "Initiator", + "type" : "string", + "repeats" : false + }, + { + "linkId" : "plan-goalstasks-details-goalsetting-targetdate", + "text" : "Target date", + "type" : "date", + "repeats" : false + }, + { + "linkId" : "plan-goalstasks-details-goalsetting-status", + "text" : "Status", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "#GoalStatusLimited" + }, + { + "linkId" : "plan-goalstasks-details-goalsetting-comment", + "text" : "Comment", + "type" : "string", + "repeats" : false + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "gtable" + }] + } + }], + "linkId" : "plan-goalstasks-details-interventionsactions", + "text" : "Interventions and actions", + "_text" : { + "extension" : [{ + "url" : "https://smartforms.csiro.au/ig/StructureDefinition/QuestionnaireItemTextHidden", + "valueBoolean" : true + }] + }, + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 25, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "plan-goalstasks-details-interventionsactions-interventionsactions", + "text" : "Interventions/Actions", + "type" : "open-choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/procedure-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 20, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }], + "linkId" : "plan-goalstasks-details-interventionsactions-owner", + "text" : "Owner", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 15, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }], + "linkId" : "plan-goalstasks-details-interventionsactions-targetdate", + "text" : "Target date", + "type" : "date", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 15, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "drop-down" + }] + } + }], + "linkId" : "plan-goalstasks-details-interventionsactions-status", + "text" : "Status", + "type" : "choice", + "repeats" : false, + "answerValueSet" : "#ActionsStatus" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 25, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }], + "linkId" : "plan-goalstasks-details-interventionsactions-comment", + "text" : "Comment", + "type" : "string", + "repeats" : false + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "gtable" + }] + } + }], + "linkId" : "plan-goalstasks-details-servicestreatments", + "text" : "Services and treatments", + "_text" : { + "extension" : [{ + "url" : "https://smartforms.csiro.au/ig/StructureDefinition/QuestionnaireItemTextHidden", + "valueBoolean" : true + }] + }, + "type" : "group", + "repeats" : true, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 23, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "plan-goalstasks-details-servicestreatments-servicestreatments", + "text" : "Required services and treatments", + "type" : "open-choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/service-type-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 23, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "autocomplete" + }] + } + }], + "linkId" : "plan-goalstasks-details-servicestreatments-activity", + "text" : "Activity", + "type" : "open-choice", + "repeats" : false, + "answerValueSet" : "https://healthterminologies.gov.au/fhir/ValueSet/procedure-1" + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 23, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }], + "linkId" : "plan-goalstasks-details-servicestreatments-provider", + "text" : "Provider", + "type" : "string", + "repeats" : false + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-width", + "valueQuantity" : { + "value" : 31, + "system" : "http://unitsofmeasure.org", + "code" : "%" + } + }], + "linkId" : "plan-goalstasks-details-servicestreatments-comment", + "text" : "Comment", + "type" : "string", + "repeats" : false + }] + }] + }, + { + "linkId" : "notes", + "text" : "Notes", + "type" : "group", + "item" : [{ + "linkId" : "notes-additionalcomments", + "text" : "Additional notes or comments", + "type" : "text", + "repeats" : true + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-collapsible", + "valueCode" : "default-open" + }], + "linkId" : "completion", + "text" : "Completion", + "type" : "group", + "repeats" : false, + "item" : [{ + "linkId" : "completion-consentforsharing", + "text" : "Consent given for sharing of information with relevant healthcare providers", + "type" : "boolean", + "repeats" : false + }, + { + "linkId" : "completion-review", + "text" : "Review", + "type" : "group", + "repeats" : false, + "item" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "radio-button" + }] + } + }, + { + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation", + "valueCode" : "horizontal" + }], + "linkId" : "completion-review-appointmentstatus", + "text" : "Appointment status", + "type" : "choice", + "repeats" : false, + "answerOption" : [{ + "valueCoding" : { + "system" : "http://hl7.org/fhir/appointmentstatus", + "code" : "booked", + "display" : "Booked" + } + }, + { + "valueCoding" : { + "system" : "http://hl7.org/fhir/appointmentstatus", + "code" : "proposed", + "display" : "Proposed" + } + }] + }, + { + "linkId" : "completion-review-date", + "text" : "Date", + "type" : "date", + "repeats" : false + }, + { + "linkId" : "completion-review-comment", + "text" : "Comment", + "type" : "string", + "repeats" : false + }] + }, + { + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/questionnaire-item-control", + "code" : "radio-button" + }] + } + }], + "linkId" : "completion-copyoffered", + "text" : "Patient has been offered a copy of this plan", + "type" : "choice", + "repeats" : false, + "answerOption" : [{ + "valueString" : "Yes, copy provided" + }, + { + "valueString" : "Yes, copy to be provided at a later date" + }, + { + "valueString" : "Yes, but declined" + }] + }, + { + "linkId" : "completion-comment", + "text" : "Comment", + "type" : "string", + "repeats" : false + }] + }] + }] + }] +} \ No newline at end of file diff --git a/apps/smart-forms-app/src/test/gpccmp/population.test.tsx b/apps/smart-forms-app/src/test/gpccmp/population.test.tsx new file mode 100644 index 000000000..969c13829 --- /dev/null +++ b/apps/smart-forms-app/src/test/gpccmp/population.test.tsx @@ -0,0 +1,57 @@ +import type { Patient, Questionnaire } from 'fhir/r4'; +import type { BehavioralTestWrapperProps } from '../behavioralTestUtils'; +import { BehavioralTestWrapper } from '../behavioralTestUtils'; +import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; +import { vi } from 'vitest'; +import { render, waitFor } from '@testing-library/react'; +import { getBirthDateForAge, getInputText, selectTab } from '../testUtils'; + +export const patient: Patient = { + resourceType: 'Patient', + id: 'patient-123', + name: [ + { + use: 'official', + family: 'John', + given: ['Snow'] + }, + { + use: 'usual', + given: ['Johnny'] + } + ], + birthDate: getBirthDateForAge(33), + gender: 'male' +}; + +function GpccmpForm(props: Omit) { + return ; +} + +vi.mock('fhirclient', () => ({ + client: () => ({ + request: vi.fn(() => Promise.resolve({})) + }) +})); + +beforeAll(() => { + globalThis.ResizeObserver = class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} + }; +}); + +describe('Population workflow for', () => { + test('Patient details', async () => { + const { container } = render(); + + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + await selectTab(container, 'Patient details'); + + const patientAge = await getInputText(container, 'Age'); + expect(patientAge).toBe('33'); + }); +}); diff --git a/apps/smart-forms-app/vitest.config.ts b/apps/smart-forms-app/vitest.config.ts index cc2fb60e5..a49179337 100644 --- a/apps/smart-forms-app/vitest.config.ts +++ b/apps/smart-forms-app/vitest.config.ts @@ -8,7 +8,7 @@ export default defineConfig({ globals: true, testTimeout: 40000, environment: 'jsdom', - include: ['src/test/aboriginalForm*.test.tsx'], // Only include this specific test file + include: ['src/test/aboriginalForm*.test.tsx', 'src/test/gpccmp/*.test.tsx'], // Only include this specific test file exclude: ['**/e2e/**', '**/node_modules/**'], coverage: { provider: 'v8', From 10fecf15bd7c2d05b2846ea2677021a51d763883 Mon Sep 17 00:00:00 2001 From: Roman P Date: Fri, 31 Jul 2026 13:16:03 +0300 Subject: [PATCH 03/11] Add test for enableWhen, enableWhenBehavior, calculation --- .../src/test/gpccmp/calculation.test.tsx | 368 ++++++++++++++++++ .../test/gpccmp/conditionsEnableWhen.test.tsx | 94 +++++ .../conditionsEnableWhenBehavior.test.tsx | 111 ++++++ 3 files changed, 573 insertions(+) create mode 100644 apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx create mode 100644 apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx create mode 100644 apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx diff --git a/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx b/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx new file mode 100644 index 000000000..962647a90 --- /dev/null +++ b/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx @@ -0,0 +1,368 @@ +import type { Questionnaire } from 'fhir/r4'; +import type { BehavioralTestWrapperProps } from '../behavioralTestUtils'; +import { BehavioralTestWrapper } from '../behavioralTestUtils'; +import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; +import { render, waitFor } from '@testing-library/react'; +import { + inputDecimal, + selectTab, + inputInteger, + findByLinkIdOrLabel, + getInputText, + chooseSelectOption +} from '../testUtils'; + +function GpccmpForm(props: Omit) { + return ; +} + +beforeAll(() => { + globalThis.ResizeObserver = class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} + }; +}); + +describe('Observation Calculation', () => { + test('height new result date', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const observationsContainer = await findByLinkIdOrLabel(container, 'Observations'); + const dateFieldValueBefore = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-height-newresultdate' + ); + expect(dateFieldValueBefore).toBe(''); + + await inputDecimal( + observationsContainer, + 'clinicaldetails-observations-maingrid-height-newresultvalue', + 170.55 + ); + const dateFieldValueAfter = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-height-newresultdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); + + test('weight new result date', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const observationsContainer = await findByLinkIdOrLabel(container, 'Observations'); + const dateFieldValueBefore = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-weight-newresultdate' + ); + expect(dateFieldValueBefore).toBe(''); + + await inputDecimal( + observationsContainer, + 'clinicaldetails-observations-maingrid-weight-newresultvalue', + 70.55 + ); + const dateFieldValueAfter = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-weight-newresultdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); + + test('BMI calculation', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const observationsContainer = await findByLinkIdOrLabel(container, 'Observations'); + await inputDecimal( + observationsContainer, + 'clinicaldetails-observations-maingrid-height-newresultvalue', + 170.55 + ); + await inputDecimal( + observationsContainer, + 'clinicaldetails-observations-maingrid-weight-newresultvalue', + 70.32 + ); + const bmiFieldValue = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-bmi-newresultvalue' + ); + expect(bmiFieldValue).toBe('24.2'); + }); + + test('Waist circumference new result date', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const observationsContainer = await findByLinkIdOrLabel(container, 'Observations'); + const dateFieldValueBefore = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-waistcircumference-newdate' + ); + expect(dateFieldValueBefore).toBe(''); + + await inputDecimal( + observationsContainer, + 'clinicaldetails-observations-maingrid-waistcircumference-newresultvalue', + 80.55 + ); + const dateFieldValueAfter = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-waistcircumference-newdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); + + test('Pulse rate new result date', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const observationsContainer = await findByLinkIdOrLabel(container, 'Observations'); + const dateFieldValueBefore = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-pulserate-newresultdate' + ); + expect(dateFieldValueBefore).toBe(''); + + await inputInteger( + observationsContainer, + 'clinicaldetails-observations-maingrid-pulserate-newresultvalue', + 70 + ); + const dateFieldValueAfter = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-pulserate-newresultdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); + + test('Pulse rhythm new result date', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const observationsContainer = await findByLinkIdOrLabel(container, 'Observations'); + const dateFieldValueBefore = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-pulserhythm-newresultdate' + ); + expect(dateFieldValueBefore).toBe(''); + + await chooseSelectOption( + observationsContainer, + 'clinicaldetails-observations-maingrid-pulserhythm-newresultvalue', + 'Pulse regular' + ); + const dateFieldValueAfter = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-pulserhythm-newresultdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); + + test('Oxygen saturation new result date', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const observationsContainer = await findByLinkIdOrLabel(container, 'Observations'); + const dateFieldValueBefore = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-oxygensaturation-newresultdate' + ); + expect(dateFieldValueBefore).toBe(''); + + await inputDecimal( + observationsContainer, + 'clinicaldetails-observations-maingrid-oxygensaturation-newresultvalue', + 20 + ); + const dateFieldValueAfter = await getInputText( + observationsContainer, + 'clinicaldetails-observations-maingrid-oxygensaturation-newresultdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); +}); + +describe('Blood pressure calculations', () => { + test('Systolic pressure date performed', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const bloodPressureContainer = await findByLinkIdOrLabel(container, 'Blood pressure'); + const dateFieldValueBefore = await getInputText( + bloodPressureContainer, + 'clinicaldetails-observations-bpgrid-bp-newresultdate' + ); + expect(dateFieldValueBefore).toBe(''); + + await inputDecimal( + bloodPressureContainer, + 'clinicaldetails-observations-bpgrid-bp-newresultsystolic', + 120 + ); + const dateFieldValueAfter = await getInputText( + bloodPressureContainer, + 'clinicaldetails-observations-bpgrid-bp-newresultdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); + + test('Diastolic pressure date performed', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const bloodPressureContainer = await findByLinkIdOrLabel(container, 'Blood pressure'); + const dateFieldValueBefore = await getInputText( + bloodPressureContainer, + 'clinicaldetails-observations-bpgrid-bp-newresultdate' + ); + expect(dateFieldValueBefore).toBe(''); + + await inputDecimal( + bloodPressureContainer, + 'clinicaldetails-observations-bpgrid-bp-newresultdiastolic', + 80 + ); + const dateFieldValueAfter = await getInputText( + bloodPressureContainer, + 'clinicaldetails-observations-bpgrid-bp-newresultdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); + + test('Systolic and diastolic date performed', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const bloodPressureContainer = await findByLinkIdOrLabel(container, 'Blood pressure'); + const dateFieldValueBefore = await getInputText( + bloodPressureContainer, + 'clinicaldetails-observations-bpgrid-bp-newresultdate' + ); + expect(dateFieldValueBefore).toBe(''); + + await inputDecimal( + bloodPressureContainer, + 'clinicaldetails-observations-bpgrid-bp-newresultsystolic', + 120 + ); + await inputDecimal( + bloodPressureContainer, + 'clinicaldetails-observations-bpgrid-bp-newresultdiastolic', + 80 + ); + const dateFieldValueAfter = await getInputText( + bloodPressureContainer, + 'clinicaldetails-observations-bpgrid-bp-newresultdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); +}); + +describe('Substance use calculations', () => { + test('Smoking new status date', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const substanceUseContainer = await findByLinkIdOrLabel(container, 'Substance use'); + const dateFieldValueBefore = await getInputText( + substanceUseContainer, + 'clinicaldetails-observations-substanceusegrid-smokingstatus-newresultdate' + ); + expect(dateFieldValueBefore).toBe(''); + + await chooseSelectOption( + substanceUseContainer, + 'clinicaldetails-observations-substanceusegrid-smokingstatus-newresultvalue', + 'Current smoker' + ); + const dateFieldValueAfter = await getInputText( + substanceUseContainer, + 'clinicaldetails-observations-substanceusegrid-smokingstatus-newresultdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); + + test('Alcohol consumption new status date', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Clinical details'); + const substanceUseContainer = await findByLinkIdOrLabel(container, 'Substance use'); + const dateFieldValueBefore = await getInputText( + substanceUseContainer, + 'clinicaldetails-observations-substanceusegrid-alcoholstatus-newresultdate' + ); + expect(dateFieldValueBefore).toBe(''); + await chooseSelectOption( + substanceUseContainer, + 'clinicaldetails-observations-substanceusegrid-alcoholstatus-newresultvalue', + 'Current drinker' + ); + const dateFieldValueAfter = await getInputText( + substanceUseContainer, + 'clinicaldetails-observations-substanceusegrid-alcoholstatus-newresultdate' + ); + const today = new Date(); + const formattedDate = `${String(today.getDate()).padStart(2, '0')}/${String(today.getMonth() + 1).padStart(2, '0')}/${today.getFullYear()}`; + expect(dateFieldValueAfter).toBe(formattedDate); + }); +}); diff --git a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx b/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx new file mode 100644 index 000000000..99367ffd2 --- /dev/null +++ b/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx @@ -0,0 +1,94 @@ +import type { Questionnaire } from 'fhir/r4'; +import type { BehavioralTestWrapperProps } from '../behavioralTestUtils'; +import { BehavioralTestWrapper } from '../behavioralTestUtils'; +import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; +import { vi } from 'vitest'; +import { render, waitFor } from '@testing-library/react'; +import { inputInteger, checkRadioOption, findByLinkIdOrLabel, inputText } from '../testUtils'; + +function GpccmpForm(props: Omit) { + return ; +} + +vi.mock('fhirclient', () => ({ + client: () => ({ + request: vi.fn(() => Promise.resolve({})) + }) +})); + +beforeAll(() => { + globalThis.ResizeObserver = class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} + }; +}); + +describe('My Aged Care question', () => { + test('for yes', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details')); + await inputInteger(container, 'Age', 51); + + await checkRadioOption(container, 'Registered for My Aged Care', 'Yes'); + await inputInteger(container, 'My Aged Care Number', 1234567890); + await inputText(container, 'Comment', 'This is a comment'); + }); + + test('for no', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details')); + await inputInteger(container, 'Age', 51); + await checkRadioOption(container, 'Registered for My Aged Care', 'No'); + await expect( + async () => await findByLinkIdOrLabel(container, 'My Aged Care Number') + ).rejects.toThrow(); + await inputText(container, 'Comment', 'This is a comment'); + }); + + test('for Pending', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details')); + await inputInteger(container, 'Age', 51); + await checkRadioOption(container, 'Registered for My Aged Care', 'Pending'); + await expect( + async () => await findByLinkIdOrLabel(container, 'My Aged Care Number') + ).rejects.toThrow(); + await inputText(container, 'Comment', 'This is a comment'); + }); +}); + +describe('National Disability Insurance Scheme question', () => { + test('for yes', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details')); + + await inputInteger(container, 'Age', 24); + await checkRadioOption(container, 'Registered for NDIS', 'Yes'); + + await inputInteger(container, 'NDIS Number', 1234567890); + await inputText(container, 'Comment', 'This is a comment'); + }); + + test('for no', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details')); + + await inputInteger(container, 'Age', 24); + await checkRadioOption(container, 'Registered for NDIS', 'No'); + + await expect(async () => await findByLinkIdOrLabel(container, 'NDIS Number')).rejects.toThrow(); + await inputText(container, 'Comment', 'This is a comment'); + }); + + test('for Pending', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details')); + + await inputInteger(container, 'Age', 24); + await checkRadioOption(container, 'Registered for NDIS', 'Pending'); + + await expect(async () => await findByLinkIdOrLabel(container, 'NDIS Number')).rejects.toThrow(); + await inputText(container, 'Comment', 'This is a comment'); + }); +}); diff --git a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx b/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx new file mode 100644 index 000000000..27c645ca5 --- /dev/null +++ b/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx @@ -0,0 +1,111 @@ +import type { Questionnaire } from 'fhir/r4'; +import type { BehavioralTestWrapperProps } from '../behavioralTestUtils'; +import { BehavioralTestWrapper } from '../behavioralTestUtils'; +import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; +import { render, waitFor } from '@testing-library/react'; +import { + chooseSelectOption, + selectTab, + inputInteger, + checkRadioOption, + findByLinkIdOrLabel, + inputText, + checkCheckBox +} from '../testUtils'; + +function GpccmpForm(props: Omit) { + return ; +} + +beforeAll(() => { + globalThis.ResizeObserver = class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} + }; +}); + +//Patient details +describe('My Aged Care boundary values', () => { + test('for patients over 50 years of age', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + await inputInteger(container, 'Age', 51); + + await checkRadioOption(container, 'Registered for My Aged Care', 'Yes'); + await inputInteger(container, 'My Aged Care Number', 1234567890); + await inputText(container, 'Comment', 'This is a comment'); + }); + + test('for patients under 50 years of age', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + await inputInteger(container, 'Age', 49); + await expect( + async () => await findByLinkIdOrLabel(container, 'My Aged Care') + ).rejects.toThrow(); + }); + + test('for patients aged 50 years', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + await inputInteger(container, 'Age', 50); + + await checkRadioOption(container, 'Registered for My Aged Care', 'Yes'); + await inputInteger(container, 'My Aged Care Number', 1234567890); + await inputText(container, 'Comment', 'This is a comment'); + }); +}); + +describe('Home Address', () => { + test('for patients with a home address', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + await inputText(container, 'Street address', '123 Main St'); + await inputText(container, 'City', 'Sydney'); + await chooseSelectOption(container, 'State', 'New South Wales'); + await inputText(container, 'Postcode', '2000'); + }); + + test('for patients without a home address', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await checkCheckBox(container, 'No fixed address'); + await expect( + async () => await findByLinkIdOrLabel(container, 'Street address') + ).rejects.toThrow(); + + await expect(async () => await findByLinkIdOrLabel(container, 'City')).rejects.toThrow(); + + await expect(async () => await findByLinkIdOrLabel(container, 'Postcode')).rejects.toThrow(); + }); +}); + +//Practitioner details + +describe('Clinic Address', () => { + test('clinic address', async () => { + const { container } = render(); + await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { + timeout: 10000 + }); + + await selectTab(container, 'Practitioner details'); + const addressContainer = await findByLinkIdOrLabel(container, 'Address'); + await inputText(addressContainer, 'Street address', '123 Main St'); + await inputText(addressContainer, 'City', 'Sydney'); + await chooseSelectOption(addressContainer, 'State', 'New South Wales'); + await inputText(addressContainer, 'Postcode', '2000'); + }); +}); From 62360d60aa9c9ac362a24aa21e0eb0f9109a5cbe Mon Sep 17 00:00:00 2001 From: Alex Pavlushkin Date: Mon, 24 Aug 2026 10:50:21 +0600 Subject: [PATCH 04/11] Update .gitignore to include .scratch and ensure proper formatting --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 07317a3b4..29985a355 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ changelog_guide.md .DS_STORE # GitHub copilot instructions -.github/copilot-instructions.md \ No newline at end of file +.github/copilot-instructions.md + +.scratch \ No newline at end of file From 9a3bf0c2fce3e046b3acf0b8a26ebd6f373c02f4 Mon Sep 17 00:00:00 2001 From: Alex Pavlushkin Date: Mon, 24 Aug 2026 11:27:18 +0600 Subject: [PATCH 05/11] Add vitest mock for fhirclient in calculation and conditionsEnableWhenBehavior tests --- apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx | 7 +++++++ .../src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx b/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx index 962647a90..4b7320fe8 100644 --- a/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx +++ b/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx @@ -2,6 +2,7 @@ import type { Questionnaire } from 'fhir/r4'; import type { BehavioralTestWrapperProps } from '../behavioralTestUtils'; import { BehavioralTestWrapper } from '../behavioralTestUtils'; import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; +import { vi } from 'vitest'; import { render, waitFor } from '@testing-library/react'; import { inputDecimal, @@ -16,6 +17,12 @@ function GpccmpForm(props: Omit) { return ; } +vi.mock('fhirclient', () => ({ + client: () => ({ + request: vi.fn(() => Promise.resolve({})) + }) +})); + beforeAll(() => { globalThis.ResizeObserver = class ResizeObserver { observe() {} diff --git a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx b/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx index 27c645ca5..adef619f3 100644 --- a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx +++ b/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx @@ -2,6 +2,7 @@ import type { Questionnaire } from 'fhir/r4'; import type { BehavioralTestWrapperProps } from '../behavioralTestUtils'; import { BehavioralTestWrapper } from '../behavioralTestUtils'; import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; +import { vi } from 'vitest'; import { render, waitFor } from '@testing-library/react'; import { chooseSelectOption, @@ -17,6 +18,12 @@ function GpccmpForm(props: Omit) { return ; } +vi.mock('fhirclient', () => ({ + client: () => ({ + request: vi.fn(() => Promise.resolve({})) + }) +})); + beforeAll(() => { globalThis.ResizeObserver = class ResizeObserver { observe() {} From 5e3870eff06b1e0e9feb4b20350af9c914668ee4 Mon Sep 17 00:00:00 2001 From: Alex Pavlushkin Date: Mon, 24 Aug 2026 11:39:38 +0600 Subject: [PATCH 06/11] Add initial implementation of questionnaire test toolkit with README, package.json, and TypeScript configuration --- packages/questionnaire-test-toolkit/README.md | 60 +++++++++++++++ .../questionnaire-test-toolkit/package.json | 44 +++++++++++ .../questionnaire-test-toolkit/src/index.ts | 73 +++++++++++++++++++ .../questionnaire-test-toolkit/tsconfig.json | 17 +++++ 4 files changed, 194 insertions(+) create mode 100644 packages/questionnaire-test-toolkit/README.md create mode 100644 packages/questionnaire-test-toolkit/package.json create mode 100644 packages/questionnaire-test-toolkit/src/index.ts create mode 100644 packages/questionnaire-test-toolkit/tsconfig.json diff --git a/packages/questionnaire-test-toolkit/README.md b/packages/questionnaire-test-toolkit/README.md new file mode 100644 index 000000000..7496eeded --- /dev/null +++ b/packages/questionnaire-test-toolkit/README.md @@ -0,0 +1,60 @@ +# @aehrc/questionnaire-test-toolkit + +Behavioural test toolkit for FHIR Questionnaire forms: DOM interaction helpers plus a harness +that mounts a questionnaire in the renderer, optionally pre-populated from a patient. + +Extracted from `apps/smart-forms-app/src/test/` so that a form's test suite can live beside the +form rather than inside the application. Consumed by both the Aboriginal and Torres Strait +Islander Health Check suite and the GP CCMP suite. + +## Status: private + +Not published to npm. This is a deliberate decision, not an oversight — publishing means a public +name, semver commitments, a changelog and a named owner, which is an organisational commitment +rather than a repository refactor. + +The interface is nonetheless designed as though it will be published, so that the decision stays +cheap to reverse: no imports of private module paths, no reach into application source, and a +contract stated explicitly in `src/index.ts`. + +## The contract + +`src/index.ts` **is** the public API. Everything re-exported there may be relied upon by a form's +test suite. Anything else is internal: which file a helper lives in, how it locates elements, and +any function added to a source file without being re-exported from `index.ts`. + +Re-exports are named individually rather than `export *`, so adding a helper to a source file is +not the same act as promising it to consumers. + +Consume it as `@aehrc/questionnaire-test-toolkit`, never by relative path — that keeps a later move +to a separate repository a path rewrite rather than a rebuild. + +## Consumed as TypeScript source + +`main` and `exports` point at `src/index.ts`. There is no build step: the package is test-only, and +every consumer already runs its tests through a TypeScript transform (Vitest via esbuild, Jest via +`ts-jest`). A `tsup` build and a release workflow can be added later without changing the +interface. + +Jest consumers need `@aehrc` to stay transformable — the app's `transformIgnorePatterns` +(`/node_modules/(?!(@aehrc|@fontsource)/)`) already provides this, which is part of why the package +carries the `@aehrc` scope while private. + +## Notes for maintainers + +**Seven helpers are not exercised by any suite in this repository.** `inputFile`, `inputTime`, +`inputReference`, `inputUrl`, `chooseQuantityOption`, `inputOpenChoiceOtherText` and +`getAnswerRecursiveByLabel` are unused by both the Aboriginal and GP CCMP suites. They are kept +because this file began as a copy of +`packages/smart-forms-renderer/src/stories/testUtils.ts`, where the storybook stories do use them, +and because the toolkit is meant to be general rather than shaped around its current two tenants. +Treat them as untested surface. + +**`terminologyServerUrl` is probably dead.** `BehavioralTestWrapper` passes it to `buildForm`, but +every suite mocks `fhirclient` to return `{}` for all requests, so no terminology call reaches the +network and the value has no observable effect. It is exported unchanged for now because removing +it would be a behaviour change; verify before relying on it. + +**A near-duplicate exists.** `packages/smart-forms-renderer/src/stories/testUtils.ts` is a longer +copy of the same helpers, despite a comment in the original asserting the two are identical. +Merging them is worthwhile and deliberately out of scope here. diff --git a/packages/questionnaire-test-toolkit/package.json b/packages/questionnaire-test-toolkit/package.json new file mode 100644 index 000000000..ad00386b4 --- /dev/null +++ b/packages/questionnaire-test-toolkit/package.json @@ -0,0 +1,44 @@ +{ + "name": "@aehrc/questionnaire-test-toolkit", + "version": "0.1.0", + "private": true, + "description": "Behavioural test toolkit for FHIR Questionnaire forms: DOM interaction helpers and a renderer harness.", + "repository": { + "type": "git", + "url": "git+https://github.com/aehrc/smart-forms.git" + }, + "author": "AEHRC", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/aehrc/smart-forms/issues" + }, + "homepage": "https://github.com/aehrc/smart-forms#readme", + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": "./src/index.ts" + }, + "files": [ + "src" + ], + "dependencies": { + "@aehrc/sdc-populate": "^4.7.1", + "@aehrc/sdc-template-extract": "^1.0.15", + "@aehrc/smart-forms-renderer": "^1.4.0", + "@mui/material": "^7.1.1", + "@tanstack/react-query": "^5.90.5", + "@testing-library/dom": "^10.4.0", + "@testing-library/user-event": "^14.6.1", + "fhirpath": "^4.10.1" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "vitest": "^3.2.4" + }, + "devDependencies": { + "@types/fhir": "^0.0.41", + "@types/react": "^18.3.18", + "typescript": "^5.9.3" + } +} diff --git a/packages/questionnaire-test-toolkit/src/index.ts b/packages/questionnaire-test-toolkit/src/index.ts new file mode 100644 index 000000000..47d1e9552 --- /dev/null +++ b/packages/questionnaire-test-toolkit/src/index.ts @@ -0,0 +1,73 @@ +/* + * Copyright 2025 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) ABN 41 687 119 230. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This module is the package's contract. Everything re-exported here is public API and may be +// relied upon by a form's test suite. Anything not listed here — including the split of helpers +// across files, and any function added to those files without being re-exported below — is an +// internal detail and may change without notice. +// +// Re-exports are named deliberately rather than `export *`, so that adding a helper to a source +// file is not the same act as promising it to consumers. + +// -- Harness --------------------------------------------------------------------------------- + +export { BehavioralTestWrapper, terminologyServerUrl } from './behavioralTestUtils'; +export type { BehavioralTestWrapperProps, RequestDefinition } from './behavioralTestUtils'; + +// -- Entering answers ----------------------------------------------------------------------- + +export { + inputText, + inputDate, + inputDateTime, + inputTime, + inputDecimal, + inputInteger, + inputUrl, + inputFile, + inputReference, + inputOpenChoiceOtherText +} from './testUtils'; + +// -- Choosing from options ------------------------------------------------------------------ + +export { + checkCheckBox, + checkCheckboxOption, + checkRadioOption, + chooseSelectOption, + chooseQuantityOption +} from './testUtils'; + +// -- Reading rendered state ----------------------------------------------------------------- + +export { + getInputText, + getRadioValue, + getSelectText, + getCqfText, + getAnswerRecursiveByLabel, + getVisibleTab +} from './testUtils'; + +// -- Locating elements ---------------------------------------------------------------------- + +export { findByLinkIdOrLabel, findAllByLinkIdOrLabel, selectTab } from './testUtils'; + +// -- Extraction and misc -------------------------------------------------------------------- + +export { invokeExtract, getBirthDateForAge } from './testUtils'; diff --git a/packages/questionnaire-test-toolkit/tsconfig.json b/packages/questionnaire-test-toolkit/tsconfig.json new file mode 100644 index 000000000..5683436d9 --- /dev/null +++ b/packages/questionnaire-test-toolkit/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "resolveJsonModule": true, + "sourceMap": true, + "noEmit": true + }, + "include": ["src"] +} From 9bddbcdc3737a80c0e20212d477770e6b3bd2e22 Mon Sep 17 00:00:00 2001 From: Alex Pavlushkin Date: Mon, 24 Aug 2026 15:13:17 +0600 Subject: [PATCH 07/11] Refactor tests to utilize @aehrc/questionnaire-test-toolkit - Updated imports in various test files to replace local test utilities with the new toolkit. - Introduced behavioral test constants and types for better organization and clarity. - Added a comprehensive test utility file to encapsulate common testing functions. - Ensured all references to previous utility functions are replaced with toolkit equivalents. - Updated package dependencies to include @aehrc/questionnaire-test-toolkit. --- apps/smart-forms-app/package.json | 1 + .../test/aboriginalFormCalculation.test.tsx | 2 +- ...originalFormCompletedFor0To1Years.test.tsx | 2 +- ...riginalFormCompletedFor12To6Years.test.tsx | 2 +- ...iginalFormCompletedFor24To13Years.test.tsx | 2 +- ...iginalFormCompletedFor49To25Years.test.tsx | 2 +- ...aboriginalFormCompletedFor51Years.test.tsx | 2 +- ...originalFormCompletedFor5To1Years.test.tsx | 2 +- .../test/aboriginalFormConditions.test.tsx | 2 +- .../test/aboriginalFormExtraction.test.tsx | 5 +- .../src/test/aboriginalFormIntegrationData.ts | 2 +- .../test/aboriginalFormPopulation.test.tsx | 2 +- .../src/test/aboriginalFormUtils.tsx | 4 +- .../src/test/gpccmp/calculation.test.tsx | 6 +- .../test/gpccmp/conditionsEnableWhen.test.tsx | 11 ++- .../conditionsEnableWhenBehavior.test.tsx | 6 +- .../src/test/gpccmp/population.test.tsx | 6 +- package-lock.json | 67 ++++++++++++--- .../questionnaire-test-toolkit/package.json | 16 ++-- .../src/behavioralTestConstants.ts | 18 ++++ .../src/behavioralTestTypes.ts | 32 +++++++ .../src}/behavioralTestUtils.tsx | 83 ++++++++++--------- .../questionnaire-test-toolkit/src/index.ts | 5 +- .../src}/testUtils.ts | 24 ++++-- 24 files changed, 211 insertions(+), 93 deletions(-) create mode 100644 packages/questionnaire-test-toolkit/src/behavioralTestConstants.ts create mode 100644 packages/questionnaire-test-toolkit/src/behavioralTestTypes.ts rename {apps/smart-forms-app/src/test => packages/questionnaire-test-toolkit/src}/behavioralTestUtils.tsx (63%) rename {apps/smart-forms-app/src/test => packages/questionnaire-test-toolkit/src}/testUtils.ts (96%) diff --git a/apps/smart-forms-app/package.json b/apps/smart-forms-app/package.json index e433c7192..17fecae0f 100644 --- a/apps/smart-forms-app/package.json +++ b/apps/smart-forms-app/package.json @@ -74,6 +74,7 @@ "zustand": "^5.0.14" }, "devDependencies": { + "@aehrc/questionnaire-test-toolkit": "^0.1.0", "@jest/globals": "^30.3.0", "@playwright/test": "^1.60.0", "@sentry/cli": "^2.57.0", diff --git a/apps/smart-forms-app/src/test/aboriginalFormCalculation.test.tsx b/apps/smart-forms-app/src/test/aboriginalFormCalculation.test.tsx index ad7a1d1d6..b1c88d990 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormCalculation.test.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormCalculation.test.tsx @@ -9,7 +9,7 @@ import { inputDecimal, getVisibleTab, checkRadioOption -} from './testUtils.ts'; +} from '@aehrc/questionnaire-test-toolkit'; import { AboriginalForm } from './aboriginalFormUtils.tsx'; vi.mock('fhirclient', () => ({ diff --git a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor0To1Years.test.tsx b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor0To1Years.test.tsx index b013edc05..3d28438cc 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor0To1Years.test.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor0To1Years.test.tsx @@ -14,7 +14,7 @@ import { inputDecimal, getVisibleTab, inputDateTime -} from './testUtils.ts'; +} from '@aehrc/questionnaire-test-toolkit'; import { AboriginalForm } from './aboriginalFormUtils.tsx'; vi.mock('fhirclient', () => ({ diff --git a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor12To6Years.test.tsx b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor12To6Years.test.tsx index bdb93f613..a1bdfbcdf 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor12To6Years.test.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor12To6Years.test.tsx @@ -14,7 +14,7 @@ import { inputDecimal, getVisibleTab, inputDateTime -} from './testUtils.ts'; +} from '@aehrc/questionnaire-test-toolkit'; import { AboriginalForm } from './aboriginalFormUtils.tsx'; vi.mock('fhirclient', () => ({ diff --git a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor24To13Years.test.tsx b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor24To13Years.test.tsx index 4f14d7af9..15614ac36 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor24To13Years.test.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor24To13Years.test.tsx @@ -14,7 +14,7 @@ import { inputDecimal, getVisibleTab, inputDateTime -} from './testUtils.ts'; +} from '@aehrc/questionnaire-test-toolkit'; import { AboriginalForm } from './aboriginalFormUtils.tsx'; vi.mock('fhirclient', () => ({ diff --git a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor49To25Years.test.tsx b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor49To25Years.test.tsx index 061767191..e80a97ac3 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor49To25Years.test.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor49To25Years.test.tsx @@ -14,7 +14,7 @@ import { inputDecimal, getVisibleTab, inputDateTime -} from './testUtils.ts'; +} from '@aehrc/questionnaire-test-toolkit'; import { AboriginalForm } from './aboriginalFormUtils.tsx'; vi.mock('fhirclient', () => ({ diff --git a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor51Years.test.tsx b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor51Years.test.tsx index ce61c7b94..6b83042af 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor51Years.test.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor51Years.test.tsx @@ -14,7 +14,7 @@ import { inputDecimal, getVisibleTab, inputDateTime -} from './testUtils.ts'; +} from '@aehrc/questionnaire-test-toolkit'; import { AboriginalForm } from './aboriginalFormUtils.tsx'; vi.mock('fhirclient', () => ({ diff --git a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor5To1Years.test.tsx b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor5To1Years.test.tsx index 5beb506b8..72739ba78 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormCompletedFor5To1Years.test.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormCompletedFor5To1Years.test.tsx @@ -14,7 +14,7 @@ import { inputDecimal, getVisibleTab, inputDateTime -} from './testUtils.ts'; +} from '@aehrc/questionnaire-test-toolkit'; import { AboriginalForm } from './aboriginalFormUtils.tsx'; vi.mock('fhirclient', () => ({ diff --git a/apps/smart-forms-app/src/test/aboriginalFormConditions.test.tsx b/apps/smart-forms-app/src/test/aboriginalFormConditions.test.tsx index c5a925bd5..2d253a86b 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormConditions.test.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormConditions.test.tsx @@ -11,7 +11,7 @@ import { findByLinkIdOrLabel, checkCheckBox, checkCheckboxOption -} from './testUtils.ts'; +} from '@aehrc/questionnaire-test-toolkit'; import { AboriginalForm } from './aboriginalFormUtils.tsx'; vi.mock('fhirclient', () => ({ diff --git a/apps/smart-forms-app/src/test/aboriginalFormExtraction.test.tsx b/apps/smart-forms-app/src/test/aboriginalFormExtraction.test.tsx index bc0e5ffa3..2f14e0f86 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormExtraction.test.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormExtraction.test.tsx @@ -1,6 +1,7 @@ import { vi, beforeAll } from 'vitest'; import { render, waitFor } from '@testing-library/react'; -import { AboriginalForm, terminologyServerUrl } from './aboriginalFormUtils'; +import { AboriginalForm } from './aboriginalFormUtils'; +import { terminologyServerUrl } from '@aehrc/questionnaire-test-toolkit'; import { nonSnomedCondition, patient, @@ -30,7 +31,7 @@ import { selectTab, chooseSelectOption, checkRadioOption -} from './testUtils'; +} from '@aehrc/questionnaire-test-toolkit'; import { FhirResource, MedicationStatement } from 'fhir/r4'; vi.mock('fhirclient', async () => { diff --git a/apps/smart-forms-app/src/test/aboriginalFormIntegrationData.ts b/apps/smart-forms-app/src/test/aboriginalFormIntegrationData.ts index c225b8f17..e733292c3 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormIntegrationData.ts +++ b/apps/smart-forms-app/src/test/aboriginalFormIntegrationData.ts @@ -8,7 +8,7 @@ import type { Patient, QuestionnaireResponse } from 'fhir/r4'; -import { getBirthDateForAge } from './testUtils'; +import { getBirthDateForAge } from '@aehrc/questionnaire-test-toolkit'; export const patient: Patient = { resourceType: 'Patient', diff --git a/apps/smart-forms-app/src/test/aboriginalFormPopulation.test.tsx b/apps/smart-forms-app/src/test/aboriginalFormPopulation.test.tsx index 381301f86..51ad6d67a 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormPopulation.test.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormPopulation.test.tsx @@ -12,7 +12,7 @@ import { findAllByLinkIdOrLabel, getSelectText, getRadioValue -} from './testUtils.ts'; +} from '@aehrc/questionnaire-test-toolkit'; import { aboutTheHealthCheckInProgressQuestionnaireResponse, aboutTheHealthCheckQuestionnaireResponse, diff --git a/apps/smart-forms-app/src/test/aboriginalFormUtils.tsx b/apps/smart-forms-app/src/test/aboriginalFormUtils.tsx index 419b489d3..13357844a 100644 --- a/apps/smart-forms-app/src/test/aboriginalFormUtils.tsx +++ b/apps/smart-forms-app/src/test/aboriginalFormUtils.tsx @@ -1,5 +1,5 @@ -import type { BehavioralTestWrapperProps } from './behavioralTestUtils'; -import { BehavioralTestWrapper } from './behavioralTestUtils'; +import type { BehavioralTestWrapperProps } from '@aehrc/questionnaire-test-toolkit'; +import { BehavioralTestWrapper } from '@aehrc/questionnaire-test-toolkit'; import aboriginalForm from '../data/resources/Questionnaire/Questionnaire-AboriginalTorresStraitIslanderHealthCheckAssembled-0.4.0.json'; import type { Questionnaire } from 'fhir/r4'; diff --git a/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx b/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx index 4b7320fe8..68048372a 100644 --- a/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx +++ b/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx @@ -1,6 +1,6 @@ import type { Questionnaire } from 'fhir/r4'; -import type { BehavioralTestWrapperProps } from '../behavioralTestUtils'; -import { BehavioralTestWrapper } from '../behavioralTestUtils'; +import type { BehavioralTestWrapperProps } from '@aehrc/questionnaire-test-toolkit'; +import { BehavioralTestWrapper } from '@aehrc/questionnaire-test-toolkit'; import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; import { vi } from 'vitest'; import { render, waitFor } from '@testing-library/react'; @@ -11,7 +11,7 @@ import { findByLinkIdOrLabel, getInputText, chooseSelectOption -} from '../testUtils'; +} from '@aehrc/questionnaire-test-toolkit'; function GpccmpForm(props: Omit) { return ; diff --git a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx b/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx index 99367ffd2..4627694c4 100644 --- a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx +++ b/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx @@ -1,10 +1,15 @@ import type { Questionnaire } from 'fhir/r4'; -import type { BehavioralTestWrapperProps } from '../behavioralTestUtils'; -import { BehavioralTestWrapper } from '../behavioralTestUtils'; +import type { BehavioralTestWrapperProps } from '@aehrc/questionnaire-test-toolkit'; +import { BehavioralTestWrapper } from '@aehrc/questionnaire-test-toolkit'; import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; import { vi } from 'vitest'; import { render, waitFor } from '@testing-library/react'; -import { inputInteger, checkRadioOption, findByLinkIdOrLabel, inputText } from '../testUtils'; +import { + inputInteger, + checkRadioOption, + findByLinkIdOrLabel, + inputText +} from '@aehrc/questionnaire-test-toolkit'; function GpccmpForm(props: Omit) { return ; diff --git a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx b/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx index adef619f3..18dc8f8df 100644 --- a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx +++ b/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx @@ -1,6 +1,6 @@ import type { Questionnaire } from 'fhir/r4'; -import type { BehavioralTestWrapperProps } from '../behavioralTestUtils'; -import { BehavioralTestWrapper } from '../behavioralTestUtils'; +import type { BehavioralTestWrapperProps } from '@aehrc/questionnaire-test-toolkit'; +import { BehavioralTestWrapper } from '@aehrc/questionnaire-test-toolkit'; import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; import { vi } from 'vitest'; import { render, waitFor } from '@testing-library/react'; @@ -12,7 +12,7 @@ import { findByLinkIdOrLabel, inputText, checkCheckBox -} from '../testUtils'; +} from '@aehrc/questionnaire-test-toolkit'; function GpccmpForm(props: Omit) { return ; diff --git a/apps/smart-forms-app/src/test/gpccmp/population.test.tsx b/apps/smart-forms-app/src/test/gpccmp/population.test.tsx index 969c13829..f3268b732 100644 --- a/apps/smart-forms-app/src/test/gpccmp/population.test.tsx +++ b/apps/smart-forms-app/src/test/gpccmp/population.test.tsx @@ -1,10 +1,10 @@ import type { Patient, Questionnaire } from 'fhir/r4'; -import type { BehavioralTestWrapperProps } from '../behavioralTestUtils'; -import { BehavioralTestWrapper } from '../behavioralTestUtils'; +import type { BehavioralTestWrapperProps } from '@aehrc/questionnaire-test-toolkit'; +import { BehavioralTestWrapper } from '@aehrc/questionnaire-test-toolkit'; import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; import { vi } from 'vitest'; import { render, waitFor } from '@testing-library/react'; -import { getBirthDateForAge, getInputText, selectTab } from '../testUtils'; +import { getBirthDateForAge, getInputText, selectTab } from '@aehrc/questionnaire-test-toolkit'; export const patient: Patient = { resourceType: 'Patient', diff --git a/package-lock.json b/package-lock.json index a03a2ed39..84e3ed739 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,6 +78,7 @@ "zustand": "^5.0.14" }, "devDependencies": { + "@aehrc/questionnaire-test-toolkit": "^0.1.0", "@jest/globals": "^30.3.0", "@playwright/test": "^1.60.0", "@sentry/cli": "^2.57.0", @@ -1568,6 +1569,10 @@ "integrity": "sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==", "dev": true }, + "node_modules/@aehrc/questionnaire-test-toolkit": { + "resolved": "packages/questionnaire-test-toolkit", + "link": true + }, "node_modules/@aehrc/sdc-assemble": { "resolved": "packages/sdc-assemble", "link": true @@ -12430,7 +12435,6 @@ "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -12503,7 +12507,6 @@ "version": "14.6.1", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", - "dev": true, "license": "MIT", "engines": { "node": ">=12", @@ -12568,8 +12571,7 @@ "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -15109,7 +15111,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, "dependencies": { "dequal": "^2.0.3" } @@ -19027,8 +19028,7 @@ "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==" }, "node_modules/dom-converter": { "version": "0.2.0", @@ -25507,7 +25507,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, "bin": { "lz-string": "bin/bin.js" } @@ -31180,7 +31179,6 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -31194,7 +31192,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, "engines": { "node": ">=10" }, @@ -31205,8 +31202,7 @@ "node_modules/pretty-format/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/pretty-time": { "version": "1.1.0", @@ -39117,6 +39113,53 @@ "url": "https://github.com/sponsors/wooorm" } }, + "packages/questionnaire-test-toolkit": { + "name": "@aehrc/questionnaire-test-toolkit", + "version": "0.1.0", + "license": "Apache-2.0", + "dependencies": { + "@aehrc/sdc-populate": "^4.7.1", + "@aehrc/sdc-template-extract": "^1.0.15", + "@testing-library/user-event": "^14.6.1", + "fhirpath": "^4.10.1" + }, + "devDependencies": { + "@aehrc/smart-forms-renderer": "^1.4.0", + "@tanstack/react-query": "^5.90.5", + "@testing-library/dom": "^10.4.0", + "@types/fhir": "^0.0.41", + "@types/react": "^18.3.18", + "typescript": "^5.9.3", + "vitest": "^3.2.4" + }, + "peerDependencies": { + "@aehrc/smart-forms-renderer": "^1.4.0", + "@tanstack/react-query": "^5.90.5", + "@testing-library/dom": "^10.4.0", + "react": "^18.0.0 || ^19.0.0" + } + }, + "packages/questionnaire-test-toolkit/node_modules/@types/fhir": { + "version": "0.0.41", + "resolved": "https://registry.npmjs.org/@types/fhir/-/fhir-0.0.41.tgz", + "integrity": "sha512-MAQAFufNZBZ6V0F94Nhknmmh/E3iMXFK4n/L8RkSNjKtOJnvaAJERivNOj35VVx9VCQBJbE0BHSzikfBahoRhA==", + "dev": true, + "license": "MIT" + }, + "packages/questionnaire-test-toolkit/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "packages/sdc-assemble": { "name": "@aehrc/sdc-assemble", "version": "2.0.2", diff --git a/packages/questionnaire-test-toolkit/package.json b/packages/questionnaire-test-toolkit/package.json index ad00386b4..0fad7a342 100644 --- a/packages/questionnaire-test-toolkit/package.json +++ b/packages/questionnaire-test-toolkit/package.json @@ -25,20 +25,22 @@ "dependencies": { "@aehrc/sdc-populate": "^4.7.1", "@aehrc/sdc-template-extract": "^1.0.15", - "@aehrc/smart-forms-renderer": "^1.4.0", - "@mui/material": "^7.1.1", - "@tanstack/react-query": "^5.90.5", - "@testing-library/dom": "^10.4.0", "@testing-library/user-event": "^14.6.1", "fhirpath": "^4.10.1" }, "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "vitest": "^3.2.4" + "@aehrc/smart-forms-renderer": "^1.4.0", + "@tanstack/react-query": "^5.90.5", + "@testing-library/dom": "^10.4.0", + "react": "^18.0.0 || ^19.0.0" }, "devDependencies": { + "@aehrc/smart-forms-renderer": "^1.4.0", + "@tanstack/react-query": "^5.90.5", + "@testing-library/dom": "^10.4.0", "@types/fhir": "^0.0.41", "@types/react": "^18.3.18", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vitest": "^3.2.4" } } diff --git a/packages/questionnaire-test-toolkit/src/behavioralTestConstants.ts b/packages/questionnaire-test-toolkit/src/behavioralTestConstants.ts new file mode 100644 index 000000000..14a7fcd4d --- /dev/null +++ b/packages/questionnaire-test-toolkit/src/behavioralTestConstants.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2025 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) ABN 41 687 119 230. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const terminologyServerUrl = 'https://r4.ontoserver.csiro.au/fhir'; diff --git a/packages/questionnaire-test-toolkit/src/behavioralTestTypes.ts b/packages/questionnaire-test-toolkit/src/behavioralTestTypes.ts new file mode 100644 index 000000000..17054075f --- /dev/null +++ b/packages/questionnaire-test-toolkit/src/behavioralTestTypes.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) ABN 41 687 119 230. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Patient, Questionnaire } from 'fhir/r4'; +import type { InAppExtractOutput } from '@aehrc/sdc-template-extract'; + +export type RequestDefinition = { + urlPrefix: string; + params?: Record; + responseBody: unknown; +}; + +export interface BehavioralTestWrapperProps { + patient?: Patient; + requestDefinitions?: RequestDefinition[]; + onExtractResult?: (extractResult: InAppExtractOutput) => void; + questionnaire: Questionnaire; +} diff --git a/apps/smart-forms-app/src/test/behavioralTestUtils.tsx b/packages/questionnaire-test-toolkit/src/behavioralTestUtils.tsx similarity index 63% rename from apps/smart-forms-app/src/test/behavioralTestUtils.tsx rename to packages/questionnaire-test-toolkit/src/behavioralTestUtils.tsx index 25e1fd592..bf05f5362 100644 --- a/apps/smart-forms-app/src/test/behavioralTestUtils.tsx +++ b/packages/questionnaire-test-toolkit/src/behavioralTestUtils.tsx @@ -1,3 +1,20 @@ +/* + * Copyright 2025 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) ABN 41 687 119 230. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { BaseRenderer, buildForm, @@ -6,46 +23,37 @@ import { useQuestionnaireStore, useRendererQueryClient } from '@aehrc/smart-forms-renderer'; - -import type { Patient, Questionnaire } from 'fhir/r4'; import { QueryClientProvider } from '@tanstack/react-query'; import { populateQuestionnaire } from '@aehrc/sdc-populate'; import { useEffect, useState } from 'react'; import { inAppExtract, type InAppExtractOutput } from '@aehrc/sdc-template-extract'; -import Button from '@mui/material/Button'; - -export const terminologyServerUrl = 'https://r4.ontoserver.csiro.au/fhir'; - -export type RequestDefinition = { - urlPrefix: string; - params?: Record; - responseBody: any; -}; - -export interface BehavioralTestWrapperProps { - patient?: Patient; - requestDefinitions?: RequestDefinition[]; - onExtractResult?: (extractResult: InAppExtractOutput) => void; - questionnaire: Questionnaire; -} - +import { terminologyServerUrl } from './behavioralTestConstants'; +import type { BehavioralTestWrapperProps, RequestDefinition } from './behavioralTestTypes'; + +/** + * Mounts a Questionnaire in the renderer for behaviour and integration tests. + * + * When a patient is supplied, the questionnaire is populated first. Resource requests made by + * population are resolved from requestDefinitions, which keeps the test independent of a FHIR + * server. The rendered response can be extracted through the Save button exposed by the wrapper. + */ export function BehavioralTestWrapper(props: BehavioralTestWrapperProps) { const { questionnaire, patient, requestDefinitions } = props; const queryClient = useRendererQueryClient(); - const [isPopulating, setIsPopulating] = useState(false); useEffect(() => { const load = async () => { setIsPopulating(true); + if (requestDefinitions && !patient) { throw new Error('Patient must be provided when request definitions are provided'); } if (patient) { const result = await populateQuestionnaire({ - questionnaire: questionnaire, - patient: patient, + questionnaire, + patient, fetchResourceCallback: buildFetchResourceCallback(requestDefinitions ?? []), fetchResourceRequestConfig: { sourceServerUrl: 'http://mock.example' } }); @@ -57,19 +65,18 @@ export function BehavioralTestWrapper(props: BehavioralTestWrapperProps) { } const { populatedResponse, populatedContext } = populateResult; - await buildForm({ - questionnaire: questionnaire, + questionnaire, questionnaireResponse: populatedResponse, terminologyServerUrl, additionalContext: { - patient: patient, + patient, ...populatedContext } }); } else { await buildForm({ - questionnaire: questionnaire, + questionnaire, terminologyServerUrl }); } @@ -77,7 +84,7 @@ export function BehavioralTestWrapper(props: BehavioralTestWrapperProps) { setIsPopulating(false); }; - load(); + void load(); }, [questionnaire, patient, requestDefinitions]); if (isPopulating) { @@ -96,32 +103,30 @@ export function BehavioralTestWrapper(props: BehavioralTestWrapperProps) { function buildFetchResourceCallback(requestDefinitions: RequestDefinition[]) { return async (url: string) => { - const requestUrl = url; - const [path, queryString] = requestUrl.split('?'); - + const [path, queryString] = url.split('?'); const searchParams = new URLSearchParams(queryString ?? ''); const paramsObject: Record = {}; searchParams.forEach((value, key) => { paramsObject[key] = value; }); - const match = requestDefinitions.find((def) => { - if (!path.startsWith(def.urlPrefix)) { + const match = requestDefinitions.find((definition) => { + if (!path.startsWith(definition.urlPrefix)) { return false; } - if (!def.params) { + if (!definition.params) { return true; } - return Object.entries(def.params).every(([key, value]) => paramsObject[key] === value); + return Object.entries(definition.params).every(([key, value]) => paramsObject[key] === value); }); if (match) { - return Promise.resolve(match.responseBody); + return match.responseBody; } - return Promise.resolve({}); + return {}; }; } @@ -134,14 +139,14 @@ function SaveControl({ const q = useQuestionnaireStore.use.sourceQuestionnaire(); return ( - + ); } diff --git a/packages/questionnaire-test-toolkit/src/index.ts b/packages/questionnaire-test-toolkit/src/index.ts index 47d1e9552..8fa07dcdb 100644 --- a/packages/questionnaire-test-toolkit/src/index.ts +++ b/packages/questionnaire-test-toolkit/src/index.ts @@ -25,8 +25,9 @@ // -- Harness --------------------------------------------------------------------------------- -export { BehavioralTestWrapper, terminologyServerUrl } from './behavioralTestUtils'; -export type { BehavioralTestWrapperProps, RequestDefinition } from './behavioralTestUtils'; +export { BehavioralTestWrapper } from './behavioralTestUtils'; +export { terminologyServerUrl } from './behavioralTestConstants'; +export type { BehavioralTestWrapperProps, RequestDefinition } from './behavioralTestTypes'; // -- Entering answers ----------------------------------------------------------------------- diff --git a/apps/smart-forms-app/src/test/testUtils.ts b/packages/questionnaire-test-toolkit/src/testUtils.ts similarity index 96% rename from apps/smart-forms-app/src/test/testUtils.ts rename to packages/questionnaire-test-toolkit/src/testUtils.ts index cd20928af..05636e42e 100644 --- a/apps/smart-forms-app/src/test/testUtils.ts +++ b/packages/questionnaire-test-toolkit/src/testUtils.ts @@ -21,8 +21,9 @@ // 2. Prevent it from showing up in typedoc in the documentation site, which will affect docs search import { evaluate } from 'fhirpath'; -import type { Mock } from 'storybook/internal/test'; -import { fireEvent, screen, userEvent, waitFor } from 'storybook/internal/test'; +import type { Mock } from 'vitest'; +import { fireEvent, screen, waitFor } from '@testing-library/dom'; +import userEvent from '@testing-library/user-event'; import { questionnaireResponseStore } from '@aehrc/smart-forms-renderer'; import { act } from 'react'; import type { ExtractResult, InAppExtractOutput } from '@aehrc/sdc-template-extract'; @@ -461,9 +462,14 @@ export async function invokeExtract( fireEvent.click(button); }); - await waitFor(() => expect(onExtractResultMock.mock.lastCall).toBeDefined(), { - timeout: 5000 - }); + await waitFor( + () => { + if (!onExtractResultMock.mock.lastCall) { + throw new Error('Expected onExtractResult to be called'); + } + }, + { timeout: 5000 } + ); const lastCall = onExtractResultMock.mock.lastCall; if (!lastCall) { @@ -474,9 +480,13 @@ export async function invokeExtract( } function getExtractResultBundle(extractResultOutput: InAppExtractOutput) { - expect(extractResultOutput.extractSuccess).toBe(true); + if (!extractResultOutput.extractSuccess) { + throw new Error('Expected extraction to succeed'); + } const extractResult = extractResultOutput.extractResult as ExtractResult; - expect(extractResultIsOperationOutcome(extractResult)).toBe(false); + if (extractResultIsOperationOutcome(extractResult)) { + throw new Error('Expected extraction not to return an OperationOutcome'); + } return extractResult.extractedBundle; } From 561ba593551f0b05e3bbb0fd92900ee4969c8a00 Mon Sep 17 00:00:00 2001 From: Alex Pavlushkin Date: Mon, 24 Aug 2026 15:51:35 +0600 Subject: [PATCH 08/11] Add tests for GPCCMP form calculations and conditions - Implement tests for observation calculations including height, weight, BMI, waist circumference, pulse rate, rhythm, oxygen saturation, and blood pressure. - Add tests for enabling conditions based on user input for My Aged Care and NDIS questions. - Create tests for boundary values related to age and address presence. - Introduce a population workflow test to validate patient details. - Set up testing environment with Vitest and configure TypeScript for the GPCCMP form. - Include necessary dependencies in package.json and package-lock.json for testing and development. --- .prettierignore | 3 + apps/smart-forms-app/vitest.config.ts | 2 +- forms/gpccmp/README.md | 76 +++++++++++++++++++ forms/gpccmp/package.json | 40 ++++++++++ forms/gpccmp/questionnaire/PROVENANCE.md | 71 +++++++++++++++++ ...ronicConditionManagementPlanAssembled.json | 0 .../gpccmp/test}/calculation.test.tsx | 17 +---- .../test}/conditionsEnableWhen.test.tsx | 17 +---- .../conditionsEnableWhenBehavior.test.tsx | 17 +---- .../gpccmp/test}/population.test.tsx | 17 +---- forms/gpccmp/test/setup.ts | 27 +++++++ forms/gpccmp/tsconfig.json | 19 +++++ forms/gpccmp/vitest.config.ts | 27 +++++++ package-lock.json | 49 ++++++++++++ package.json | 1 + 15 files changed, 318 insertions(+), 65 deletions(-) create mode 100644 forms/gpccmp/README.md create mode 100644 forms/gpccmp/package.json create mode 100644 forms/gpccmp/questionnaire/PROVENANCE.md rename {apps/smart-forms-app/src/test/gpccmp/data/resources/Questionnaire => forms/gpccmp/questionnaire}/Questionnaire-GPChronicConditionManagementPlanAssembled.json (100%) rename {apps/smart-forms-app/src/test/gpccmp => forms/gpccmp/test}/calculation.test.tsx (97%) rename {apps/smart-forms-app/src/test/gpccmp => forms/gpccmp/test}/conditionsEnableWhen.test.tsx (89%) rename {apps/smart-forms-app/src/test/gpccmp => forms/gpccmp/test}/conditionsEnableWhenBehavior.test.tsx (90%) rename {apps/smart-forms-app/src/test/gpccmp => forms/gpccmp/test}/population.test.tsx (76%) create mode 100644 forms/gpccmp/test/setup.ts create mode 100644 forms/gpccmp/tsconfig.json create mode 100644 forms/gpccmp/vitest.config.ts diff --git a/.prettierignore b/.prettierignore index de5a65c09..ee25ee340 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,3 +8,6 @@ storybook-static *.js *.config.* *.html + +# Assembled questionnaire snapshots are artifacts, not source — see questionnaire/PROVENANCE.md +forms/*/questionnaire/*.json diff --git a/apps/smart-forms-app/vitest.config.ts b/apps/smart-forms-app/vitest.config.ts index a49179337..cc2fb60e5 100644 --- a/apps/smart-forms-app/vitest.config.ts +++ b/apps/smart-forms-app/vitest.config.ts @@ -8,7 +8,7 @@ export default defineConfig({ globals: true, testTimeout: 40000, environment: 'jsdom', - include: ['src/test/aboriginalForm*.test.tsx', 'src/test/gpccmp/*.test.tsx'], // Only include this specific test file + include: ['src/test/aboriginalForm*.test.tsx'], // Only include this specific test file exclude: ['**/e2e/**', '**/node_modules/**'], coverage: { provider: 'v8', diff --git a/forms/gpccmp/README.md b/forms/gpccmp/README.md new file mode 100644 index 000000000..903c349b1 --- /dev/null +++ b/forms/gpccmp/README.md @@ -0,0 +1,76 @@ +# @aehrc/gpccmp-form + +The GP Chronic Condition Management Plan questionnaire and the behavioural test suite that +exercises it. + +``` +questionnaire/ the assembled Questionnaire, plus PROVENANCE.md +test/ four behavioural suites and the shared setup file +``` + +## Running + +```sh +npm test # jsdom +npm run test-headed # real chromium via VITEST_HEADED +``` + +The four `packages/*` libraries must be built first — from the repository root: + +```sh +npm run build-all-deps-first-run +``` + +## Why it lives outside `packages/` + +`packages/` means "publishable SDC library". A form is content, not a library. The `forms/` prefix +is also sized for a sibling: the Aboriginal and Torres Strait Islander Health Check suite is +expected to follow, and the boundary is meant to be an obvious `git subtree` split rather than a +negotiated one. + +Three invariants keep that split mechanical, and each is cheap to hold and expensive to retrofit: + +- every `@aehrc` dependency is consumed by semver range, never by relative path; +- the test toolkit's interface stays publishable — no private module paths, no reach into + application source; +- `questionnaire/PROVENANCE.md` stays current, so the form remains reproducible by someone who has + never seen this repository. + +## The questionnaire is a snapshot + +`questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json` is the *output* of a +`$assemble` over modular questionnaires that are not in this repository. Assembling from those +sources at build time would be strictly better and the machinery already exists in +`packages/sdc-assemble`, but it is blocked on obtaining them. Until they arrive this package is +permanently a snapshot consumer. + +The artifact is `status: draft` at version `0.1.0`. Every hard-coded `linkId` in `test/` is a +contract with something explicitly not final, so a failing lookup after a refresh is a renamed item +rather than a regression. Do not reformat the file and do not add a second copy beside it — read +`questionnaire/PROVENANCE.md` before touching it. + +## Known failures + +Five of the twenty-five tests fail, and have since before the suite moved here. This is the +expected state: compare against it test by test before concluding that anything new has broken. + +| Test | Cause | +|---|---| +| `calculation` › Substance use calculations › *Smoking new status date* | ValueSet — `…substanceusegrid-smokingstatus-newresultvalue`, `https://healthterminologies.gov.au/fhir/ValueSet/smoking-status-1` | +| `calculation` › Substance use calculations › *Alcohol consumption new status date* | ValueSet — `…substanceusegrid-alcoholstatus-newresultvalue`, `https://healthterminologies.gov.au/fhir/ValueSet/alcohol-intake-status-1` | +| `conditionsEnableWhenBehavior` › Home Address › *for patients with a home address* | ValueSet — `State`, `https://healthterminologies.gov.au/fhir/ValueSet/australian-states-territories-2` | +| `conditionsEnableWhenBehavior` › Clinic Address › *clinic address* | ValueSet — `State`, same | +| `conditionsEnableWhenBehavior` › Home Address › *for patients without a home address* | **Substantive, untriaged** — see below | + +The four ValueSet failures share one cause: `test/setup.ts` mocks `fhirclient` to resolve `{}` for +every request, so a `choice` item backed by an external `answerValueSet` renders with no options +and therefore no input element at all. They cannot pass as written. The fix is valid `$expand` +responses; a working example exists in `packages/smart-forms-renderer/.storybook/preview.tsx`, and +it already covers the states-and-territories ValueSet the two `State` assertions need. + +The fifth is different. At `test/conditionsEnableWhenBehavior.test.tsx:70`, after checking *No +fixed address*, the test expects `Street address` to have left the DOM; the lookup resolves +instead. The group `patient-contact-homeaddress-details` is gated by an `enableWhenExpression` +(`%HomeAddressNoFixedAddress.empty() or %HomeAddressNoFixedAddress = false`), not a plain +`enableWhen`. Label ambiguity was ruled out: three items carry the text `Street address`, but in +this render state only the home one is in the DOM. Candidate renderer bug. diff --git a/forms/gpccmp/package.json b/forms/gpccmp/package.json new file mode 100644 index 000000000..8b0bf7dae --- /dev/null +++ b/forms/gpccmp/package.json @@ -0,0 +1,40 @@ +{ + "name": "@aehrc/gpccmp-form", + "version": "0.1.0", + "private": true, + "description": "GP Chronic Condition Management Plan questionnaire and its behavioural test suite.", + "repository": { + "type": "git", + "url": "git+https://github.com/aehrc/smart-forms.git" + }, + "author": "AEHRC", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/aehrc/smart-forms/issues" + }, + "homepage": "https://github.com/aehrc/smart-forms#readme", + "type": "module", + "scripts": { + "test": "TZ=Australia/Sydney vitest run --config ./vitest.config.ts", + "test-headed": "TZ=Australia/Sydney VITEST_HEADED=1 vitest run --config ./vitest.config.ts", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@aehrc/questionnaire-test-toolkit": "^0.1.0", + "@aehrc/smart-forms-renderer": "^1.4.0", + "@playwright/test": "^1.60.0", + "@tanstack/react-query": "^5.90.5", + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.3.2", + "@types/fhir": "^0.0.41", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", + "@vitest/browser": "^3.2.4", + "fhirclient": "^2.6.3", + "jsdom": "^29.0.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "typescript": "^5.9.3", + "vitest": "^3.2.4" + } +} diff --git a/forms/gpccmp/questionnaire/PROVENANCE.md b/forms/gpccmp/questionnaire/PROVENANCE.md new file mode 100644 index 000000000..c8a8625a4 --- /dev/null +++ b/forms/gpccmp/questionnaire/PROVENANCE.md @@ -0,0 +1,71 @@ +# Provenance — Questionnaire-GPChronicConditionManagementPlanAssembled.json + +This file is a **snapshot**, not a source. It is the output of a `Questionnaire/$assemble` over +modular questionnaires that are not in this repository. Nothing here can rebuild it; this record +exists so that someone who has never seen this repository knows where it came from and how to +replace it. + +## What the artifact says about itself + +| Field | Value | +|---|---| +| `url` | `http://www.health.gov.au/assessments/GPChronicConditionManagementPlan` | +| `version` | `0.1.0` (`versionAlgorithm` = `semver`) | +| `status` | **`draft`** | +| `experimental` | `false` | +| `date` | `2026-06-10` | +| `publisher` | AEHRC CSIRO | +| `copyright` | © 2026 Australian Government Department of Health, Disability and Ageing — "published for evaluation and local testing only, pending selection of a final licence" | +| Size | 636 KB, 1 root item | + +Related canonicals it depends on live under `https://gpccmp.csiro.au/ig/` (the GP CCMP +implementation guide) and `https://healthterminologies.gov.au/` (NCTS). + +`status: draft` is the load-bearing fact. Every hard-coded `linkId` in `../test/` — +`clinicaldetails-observations-maingrid-height-newresultdate` and its kin — is a contract with an +artifact that is explicitly not final. + +## How it entered this repository + +| | | +|---|---| +| Commit | `f0c48fff` — "Add minimal working behavioral test for gpccmp" | +| Committed | 2026-07-20 | +| By | Vadim Laletin | +| Original path | `apps/smart-forms-app/src/test/gpccmp/data/resources/Questionnaire/` | +| Moved here | Phase 2 of the GP CCMP extraction, via `git mv` (history follows the rename) | + +The file arrived in a single commit, already assembled. **Not recorded, and not recoverable from +this repository:** the exact retrieval URL, the retrieval date, the versions of the modular +sub-questionnaires that went into the assembly, and which `$assemble` implementation ran. Anyone +who learns any of these should add them here. + +## Refresh procedure + +There is no automated refresh. To replace this snapshot: + +1. **Obtain the modular sources.** They are not in this repository and may not be CSIRO's to hand + over. Ask the GP CCMP IG maintainers (`https://gpccmp.csiro.au/ig/`) for the root questionnaire + plus every `subQuestionnaire` it references, at a stated version. +2. **Assemble.** `packages/sdc-assemble` implements `$assemble`; the repository also runs an + assemble service (`services/`, `push-assemble-image.sh`). Either can produce the artifact — + `assembleQuestionnaire` in the app's `src/utils/assemble.ts` shows the call shape. +3. **Replace this file in place,** keeping the filename. The four test files import it by relative + path, so nothing else needs to change. +4. **Update the table above** — new `version`, new `date`, the sub-questionnaire versions used, and + the date you retrieved them. +5. **Re-run `npm test` in `forms/gpccmp`** and compare test by test against the expected state in + [`../README.md`](../README.md) → *Known failures*. Expect `linkId` drift: a failing + `findByLinkIdOrLabel` after a refresh is a renamed item, not a regression. + +Do **not** add a second copy alongside this one. The application already carries the Aboriginal and +Torres Strait Islander Health Check at both `0.1.0` and `0.4.0` — 700 KB and 1.07 MB side by side. +Without a refresh procedure you do not update fixtures, you collect them; that is the specific +outcome this record exists to prevent. + +## Target state + +Assembling from modular sources at build time is strictly better than storing the output, and the +machinery already exists in `packages/sdc-assemble`. It is blocked only on obtaining the sources. +If they never arrive, this package is permanently a snapshot consumer and this file is the whole of +its provenance. diff --git a/apps/smart-forms-app/src/test/gpccmp/data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json b/forms/gpccmp/questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json similarity index 100% rename from apps/smart-forms-app/src/test/gpccmp/data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json rename to forms/gpccmp/questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json diff --git a/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx b/forms/gpccmp/test/calculation.test.tsx similarity index 97% rename from apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx rename to forms/gpccmp/test/calculation.test.tsx index 68048372a..54412a7e0 100644 --- a/apps/smart-forms-app/src/test/gpccmp/calculation.test.tsx +++ b/forms/gpccmp/test/calculation.test.tsx @@ -1,8 +1,7 @@ import type { Questionnaire } from 'fhir/r4'; import type { BehavioralTestWrapperProps } from '@aehrc/questionnaire-test-toolkit'; import { BehavioralTestWrapper } from '@aehrc/questionnaire-test-toolkit'; -import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; -import { vi } from 'vitest'; +import gpccmpForm from '../questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; import { render, waitFor } from '@testing-library/react'; import { inputDecimal, @@ -17,20 +16,6 @@ function GpccmpForm(props: Omit) { return ; } -vi.mock('fhirclient', () => ({ - client: () => ({ - request: vi.fn(() => Promise.resolve({})) - }) -})); - -beforeAll(() => { - globalThis.ResizeObserver = class ResizeObserver { - observe() {} - unobserve() {} - disconnect() {} - }; -}); - describe('Observation Calculation', () => { test('height new result date', async () => { const { container } = render(); diff --git a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx b/forms/gpccmp/test/conditionsEnableWhen.test.tsx similarity index 89% rename from apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx rename to forms/gpccmp/test/conditionsEnableWhen.test.tsx index 4627694c4..023794311 100644 --- a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhen.test.tsx +++ b/forms/gpccmp/test/conditionsEnableWhen.test.tsx @@ -1,8 +1,7 @@ import type { Questionnaire } from 'fhir/r4'; import type { BehavioralTestWrapperProps } from '@aehrc/questionnaire-test-toolkit'; import { BehavioralTestWrapper } from '@aehrc/questionnaire-test-toolkit'; -import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; -import { vi } from 'vitest'; +import gpccmpForm from '../questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; import { render, waitFor } from '@testing-library/react'; import { inputInteger, @@ -15,20 +14,6 @@ function GpccmpForm(props: Omit) { return ; } -vi.mock('fhirclient', () => ({ - client: () => ({ - request: vi.fn(() => Promise.resolve({})) - }) -})); - -beforeAll(() => { - globalThis.ResizeObserver = class ResizeObserver { - observe() {} - unobserve() {} - disconnect() {} - }; -}); - describe('My Aged Care question', () => { test('for yes', async () => { const { container } = render(); diff --git a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx b/forms/gpccmp/test/conditionsEnableWhenBehavior.test.tsx similarity index 90% rename from apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx rename to forms/gpccmp/test/conditionsEnableWhenBehavior.test.tsx index 18dc8f8df..d8f84596c 100644 --- a/apps/smart-forms-app/src/test/gpccmp/conditionsEnableWhenBehavior.test.tsx +++ b/forms/gpccmp/test/conditionsEnableWhenBehavior.test.tsx @@ -1,8 +1,7 @@ import type { Questionnaire } from 'fhir/r4'; import type { BehavioralTestWrapperProps } from '@aehrc/questionnaire-test-toolkit'; import { BehavioralTestWrapper } from '@aehrc/questionnaire-test-toolkit'; -import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; -import { vi } from 'vitest'; +import gpccmpForm from '../questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; import { render, waitFor } from '@testing-library/react'; import { chooseSelectOption, @@ -18,20 +17,6 @@ function GpccmpForm(props: Omit) { return ; } -vi.mock('fhirclient', () => ({ - client: () => ({ - request: vi.fn(() => Promise.resolve({})) - }) -})); - -beforeAll(() => { - globalThis.ResizeObserver = class ResizeObserver { - observe() {} - unobserve() {} - disconnect() {} - }; -}); - //Patient details describe('My Aged Care boundary values', () => { test('for patients over 50 years of age', async () => { diff --git a/apps/smart-forms-app/src/test/gpccmp/population.test.tsx b/forms/gpccmp/test/population.test.tsx similarity index 76% rename from apps/smart-forms-app/src/test/gpccmp/population.test.tsx rename to forms/gpccmp/test/population.test.tsx index f3268b732..ebb5d63b1 100644 --- a/apps/smart-forms-app/src/test/gpccmp/population.test.tsx +++ b/forms/gpccmp/test/population.test.tsx @@ -1,8 +1,7 @@ import type { Patient, Questionnaire } from 'fhir/r4'; import type { BehavioralTestWrapperProps } from '@aehrc/questionnaire-test-toolkit'; import { BehavioralTestWrapper } from '@aehrc/questionnaire-test-toolkit'; -import gpccmpForm from './data/resources/Questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; -import { vi } from 'vitest'; +import gpccmpForm from '../questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; import { render, waitFor } from '@testing-library/react'; import { getBirthDateForAge, getInputText, selectTab } from '@aehrc/questionnaire-test-toolkit'; @@ -28,20 +27,6 @@ function GpccmpForm(props: Omit) { return ; } -vi.mock('fhirclient', () => ({ - client: () => ({ - request: vi.fn(() => Promise.resolve({})) - }) -})); - -beforeAll(() => { - globalThis.ResizeObserver = class ResizeObserver { - observe() {} - unobserve() {} - disconnect() {} - }; -}); - describe('Population workflow for', () => { test('Patient details', async () => { const { container } = render(); diff --git a/forms/gpccmp/test/setup.ts b/forms/gpccmp/test/setup.ts new file mode 100644 index 000000000..172fcdcad --- /dev/null +++ b/forms/gpccmp/test/setup.ts @@ -0,0 +1,27 @@ +import { beforeEach, vi } from 'vitest'; +import { destroyForm } from '@aehrc/smart-forms-renderer'; + +// Every request resolves to an empty object, so any item backed by an external answerValueSet +// renders without options. Without this mock, getValueSetPromise throws `client is not a +// function`, buildForm rejects, and the form never leaves `Loading...`. +vi.mock('fhirclient', () => ({ + client: () => ({ + request: vi.fn(() => Promise.resolve({})) + }) +})); + +// jsdom has no ResizeObserver; the renderer's layout components require one. +globalThis.ResizeObserver = class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} +}; + +// The renderer keeps form state in module-level stores that outlive an unmounted component, so +// every test in a file shares them. buildForm happens to overwrite all of them but +// formChangesHistory, which makes the isolation an accident of renderer internals rather than +// something these tests ask for. destroyForm is the documented lifecycle — the application calls +// it before building a new form — so call it here and stop depending on the accident. +beforeEach(() => { + destroyForm(); +}); diff --git a/forms/gpccmp/tsconfig.json b/forms/gpccmp/tsconfig.json new file mode 100644 index 000000000..79871a8de --- /dev/null +++ b/forms/gpccmp/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "resolveJsonModule": true, + "sourceMap": true, + "noEmit": true, + "types": ["vitest/globals"] + }, + "include": ["test", "vitest.config.ts"] +} diff --git a/forms/gpccmp/vitest.config.ts b/forms/gpccmp/vitest.config.ts new file mode 100644 index 000000000..922bf0faf --- /dev/null +++ b/forms/gpccmp/vitest.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + optimizeDeps: { + include: ['react/jsx-dev-runtime'] + }, + test: { + globals: true, + testTimeout: 40000, + environment: 'jsdom', + setupFiles: ['./test/setup.ts'], + include: ['test/*.test.tsx'], + exclude: ['**/node_modules/**'], + browser: { + enabled: !!process.env.VITEST_HEADED, + provider: 'playwright', + headless: !process.env.VITEST_HEADED, + ui: false, + viewport: { width: 1280, height: 800 }, // Force desktop view + instances: [ + { + browser: 'chromium' + } + ] + } + } +}); diff --git a/package-lock.json b/package-lock.json index 84e3ed739..1752629af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "workspaces": [ "apps/smart-forms-app", "packages/*", + "forms/*", "services/*", "deployment/forms-server/*", "deployment/ehr-proxy/*", @@ -1563,12 +1564,60 @@ "node": ">=10.0.0" } }, + "forms/gpccmp": { + "name": "@aehrc/gpccmp-form", + "version": "0.1.0", + "license": "Apache-2.0", + "devDependencies": { + "@aehrc/questionnaire-test-toolkit": "^0.1.0", + "@aehrc/smart-forms-renderer": "^1.4.0", + "@playwright/test": "^1.60.0", + "@tanstack/react-query": "^5.90.5", + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.3.2", + "@types/fhir": "^0.0.41", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", + "@vitest/browser": "^3.2.4", + "fhirclient": "^2.6.3", + "jsdom": "^29.0.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "typescript": "^5.9.3", + "vitest": "^3.2.4" + } + }, + "forms/gpccmp/node_modules/@types/fhir": { + "version": "0.0.41", + "resolved": "https://registry.npmjs.org/@types/fhir/-/fhir-0.0.41.tgz", + "integrity": "sha512-MAQAFufNZBZ6V0F94Nhknmmh/E3iMXFK4n/L8RkSNjKtOJnvaAJERivNOj35VVx9VCQBJbE0BHSzikfBahoRhA==", + "dev": true, + "license": "MIT" + }, + "forms/gpccmp/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/@adobe/css-tools": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.2.tgz", "integrity": "sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==", "dev": true }, + "node_modules/@aehrc/gpccmp-form": { + "resolved": "forms/gpccmp", + "link": true + }, "node_modules/@aehrc/questionnaire-test-toolkit": { "resolved": "packages/questionnaire-test-toolkit", "link": true diff --git a/package.json b/package.json index 19cf6cd6b..7a614bb38 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "workspaces": [ "apps/smart-forms-app", "packages/*", + "forms/*", "services/*", "deployment/forms-server/*", "deployment/ehr-proxy/*", From 41b4faf4ba2b4a08bde368d3d8cfc301dd9a6eb6 Mon Sep 17 00:00:00 2001 From: Alex Pavlushkin Date: Mon, 24 Aug 2026 16:34:53 +0600 Subject: [PATCH 09/11] Add CI workflow for Vitest GP CCMP Behaviour Tests and update README with test suite details --- .github/workflows/vitest_gpccmp.yml | 31 ++++++++ forms/gpccmp/README.md | 72 ++++++++++++------ forms/gpccmp/questionnaire/PROVENANCE.md | 8 +- .../conditionsEnableWhenBehavior.test.tsx | 32 ++++++-- forms/gpccmp/test/setup.ts | 45 ++++++++++- forms/gpccmp/test/terminology/PROVENANCE.md | 47 ++++++++++++ .../terminology/alcohol-intake-status-1.json | 51 +++++++++++++ .../australian-states-territories-2.json | 76 +++++++++++++++++++ .../test/terminology/smoking-status-1.json | 51 +++++++++++++ 9 files changed, 376 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/vitest_gpccmp.yml create mode 100644 forms/gpccmp/test/terminology/PROVENANCE.md create mode 100644 forms/gpccmp/test/terminology/alcohol-intake-status-1.json create mode 100644 forms/gpccmp/test/terminology/australian-states-territories-2.json create mode 100644 forms/gpccmp/test/terminology/smoking-status-1.json diff --git a/.github/workflows/vitest_gpccmp.yml b/.github/workflows/vitest_gpccmp.yml new file mode 100644 index 000000000..4fe1cfb46 --- /dev/null +++ b/.github/workflows/vitest_gpccmp.yml @@ -0,0 +1,31 @@ +name: Vitest GP CCMP Behaviour Tests + +on: [workflow_dispatch, push, workflow_call] + +jobs: + vitest-gpccmp-tests: + name: Vitest Questionnaire Behaviour Tests - GP CCMP + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - name: Use Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - name: Install dependencies + run: npm ci + + - name: Build workspace packages (monorepo dependencies) + run: | + npm run build -w packages/sdc-assemble + npm run build -w packages/sdc-populate + npm run build -w packages/sdc-template-extract + npm run build -w packages/smart-forms-renderer + + - name: Typecheck + run: npm run typecheck -w forms/gpccmp + + - name: Run vitest tests + run: npm run test -w forms/gpccmp diff --git a/forms/gpccmp/README.md b/forms/gpccmp/README.md index 903c349b1..bc5871d45 100644 --- a/forms/gpccmp/README.md +++ b/forms/gpccmp/README.md @@ -49,28 +49,50 @@ contract with something explicitly not final, so a failing lookup after a refres rather than a regression. Do not reformat the file and do not add a second copy beside it — read `questionnaire/PROVENANCE.md` before touching it. -## Known failures - -Five of the twenty-five tests fail, and have since before the suite moved here. This is the -expected state: compare against it test by test before concluding that anything new has broken. - -| Test | Cause | -|---|---| -| `calculation` › Substance use calculations › *Smoking new status date* | ValueSet — `…substanceusegrid-smokingstatus-newresultvalue`, `https://healthterminologies.gov.au/fhir/ValueSet/smoking-status-1` | -| `calculation` › Substance use calculations › *Alcohol consumption new status date* | ValueSet — `…substanceusegrid-alcoholstatus-newresultvalue`, `https://healthterminologies.gov.au/fhir/ValueSet/alcohol-intake-status-1` | -| `conditionsEnableWhenBehavior` › Home Address › *for patients with a home address* | ValueSet — `State`, `https://healthterminologies.gov.au/fhir/ValueSet/australian-states-territories-2` | -| `conditionsEnableWhenBehavior` › Clinic Address › *clinic address* | ValueSet — `State`, same | -| `conditionsEnableWhenBehavior` › Home Address › *for patients without a home address* | **Substantive, untriaged** — see below | - -The four ValueSet failures share one cause: `test/setup.ts` mocks `fhirclient` to resolve `{}` for -every request, so a `choice` item backed by an external `answerValueSet` renders with no options -and therefore no input element at all. They cannot pass as written. The fix is valid `$expand` -responses; a working example exists in `packages/smart-forms-renderer/.storybook/preview.tsx`, and -it already covers the states-and-territories ValueSet the two `State` assertions need. - -The fifth is different. At `test/conditionsEnableWhenBehavior.test.tsx:70`, after checking *No -fixed address*, the test expects `Street address` to have left the DOM; the lookup resolves -instead. The group `patient-contact-homeaddress-details` is gated by an `enableWhenExpression` -(`%HomeAddressNoFixedAddress.empty() or %HomeAddressNoFixedAddress = false`), not a plain -`enableWhen`. Label ambiguity was ruled out: three items carry the text `Street address`, but in -this render state only the home one is in the DOM. Candidate renderer bug. +## Terminology + +Three of the questionnaire's `choice` items are backed by external `answerValueSet`s. `test/setup.ts` +mocks `fhirclient` so the suite makes no network calls, and answers `$expand` from the real +expansions vendored in `test/terminology/` — see that directory's `PROVENANCE.md` for the server, +the retrieval date, and how to refresh them. + +Anything not in that directory still resolves to `{}`, which renders a `choice` item with no options +and therefore no input element at all. A test that suddenly cannot find a select is usually a +questionnaire that gained a ValueSet the fixtures do not cover. + +### The suite runs offline, and enforces it + +`fhirclient` is the only HTTP client in the dependency chain — the renderer, `sdc-populate` and +`sdc-template-extract` pull in no `axios`, `undici` or `node-fetch` — so mocking it is enough to +take the suite off the network. Verified by instrumenting `fetch`, `XMLHttpRequest` and +`node:http`/`node:https` across a full run: zero egress. + +`test/setup.ts` then makes `fetch` and `XMLHttpRequest.open` throw, so it stays that way. Without +that, a renderer that started calling `fetch` directly would quietly reach the real Ontoserver in +CI — slow, flaky, and green for the wrong reason. If you hit +`Network access from a test`, add the response to `test/terminology/` or mock the caller; do not +relax the guard. + +## Two items that only population can reach + +`patient-contact-homeaddress` is `readOnly: true` in the questionnaire, so everything under it — +including the `No fixed address` checkbox — renders with `pointer-events: none`. The checkbox +carries an initialExpression reading the `no-fixed-address` extension off the patient's home +address, which is the design: the flag comes from the patient record, not from the user. + +So `conditionsEnableWhenBehavior` › *for patients without a home address* renders with a `Patient` +that has that extension rather than clicking anything. Tests that need this state must populate; +clicking is a silent no-op. + +Note that its sibling, *for patients with a home address*, still types into that same read-only +group. It passes because the toolkit's `inputText` uses `fireEvent.change`, which ignores +`readOnly`. That is a pre-existing weakness in the test, not in the renderer — it asserts an +interaction a real user cannot perform. Left alone here; worth revisiting when the toolkit's input +helpers are next touched. + +## Running the suite in CI + +`.github/workflows/vitest_gpccmp.yml` builds the four `packages/*` libraries, typechecks this +package, and runs the suite on every push. It is green — all 25 tests pass, in roughly 140 seconds +on a warm checkout. Treat any failure as a real regression; there is no expected-failure list to +compare against any more. diff --git a/forms/gpccmp/questionnaire/PROVENANCE.md b/forms/gpccmp/questionnaire/PROVENANCE.md index c8a8625a4..8a66cdf08 100644 --- a/forms/gpccmp/questionnaire/PROVENANCE.md +++ b/forms/gpccmp/questionnaire/PROVENANCE.md @@ -54,9 +54,11 @@ There is no automated refresh. To replace this snapshot: path, so nothing else needs to change. 4. **Update the table above** — new `version`, new `date`, the sub-questionnaire versions used, and the date you retrieved them. -5. **Re-run `npm test` in `forms/gpccmp`** and compare test by test against the expected state in - [`../README.md`](../README.md) → *Known failures*. Expect `linkId` drift: a failing - `findByLinkIdOrLabel` after a refresh is a renamed item, not a regression. +5. **Re-run `npm test` in `forms/gpccmp`.** All 25 tests pass today, so any failure is a real one. + Expect `linkId` drift: a failing `findByLinkIdOrLabel` after a refresh is a renamed item, not a + regression. A `choice` item that renders with no options means the refresh introduced an + `answerValueSet` the fixtures in [`../test/terminology/`](../test/terminology/PROVENANCE.md) do + not cover. Do **not** add a second copy alongside this one. The application already carries the Aboriginal and Torres Strait Islander Health Check at both `0.1.0` and `0.4.0` — 700 KB and 1.07 MB side by side. diff --git a/forms/gpccmp/test/conditionsEnableWhenBehavior.test.tsx b/forms/gpccmp/test/conditionsEnableWhenBehavior.test.tsx index d8f84596c..6cda829cd 100644 --- a/forms/gpccmp/test/conditionsEnableWhenBehavior.test.tsx +++ b/forms/gpccmp/test/conditionsEnableWhenBehavior.test.tsx @@ -1,4 +1,4 @@ -import type { Questionnaire } from 'fhir/r4'; +import type { Patient, Questionnaire } from 'fhir/r4'; import type { BehavioralTestWrapperProps } from '@aehrc/questionnaire-test-toolkit'; import { BehavioralTestWrapper } from '@aehrc/questionnaire-test-toolkit'; import gpccmpForm from '../questionnaire/Questionnaire-GPChronicConditionManagementPlanAssembled.json'; @@ -9,8 +9,7 @@ import { inputInteger, checkRadioOption, findByLinkIdOrLabel, - inputText, - checkCheckBox + inputText } from '@aehrc/questionnaire-test-toolkit'; function GpccmpForm(props: Omit) { @@ -55,6 +54,25 @@ describe('My Aged Care boundary values', () => { }); }); +const noFixedAddressPatient: Patient = { + resourceType: 'Patient', + id: 'patient-no-fixed-address', + name: [{ use: 'official', family: 'Doe', given: ['Jane'] }], + birthDate: '1990-01-01', + gender: 'female', + address: [ + { + use: 'home', + extension: [ + { + url: 'http://hl7.org.au/fhir/StructureDefinition/no-fixed-address', + valueBoolean: true + } + ] + } + ] +}; + describe('Home Address', () => { test('for patients with a home address', async () => { const { container } = render(); @@ -67,13 +85,17 @@ describe('Home Address', () => { await inputText(container, 'Postcode', '2000'); }); + // `No fixed address` cannot be ticked by hand: its group `patient-contact-homeaddress` is + // `readOnly: true`, so the checkbox renders with `pointer-events: none` and a click is a no-op. + // The item carries an initialExpression reading the `no-fixed-address` extension off the + // patient's home address, so population is the only way this flag is ever set — and the only + // way to reach the state this test is about. test('for patients without a home address', async () => { - const { container } = render(); + const { container } = render(); await waitFor(() => expect(container.innerHTML).toContain('Patient details'), { timeout: 10000 }); - await checkCheckBox(container, 'No fixed address'); await expect( async () => await findByLinkIdOrLabel(container, 'Street address') ).rejects.toThrow(); diff --git a/forms/gpccmp/test/setup.ts b/forms/gpccmp/test/setup.ts index 172fcdcad..e172bc7c3 100644 --- a/forms/gpccmp/test/setup.ts +++ b/forms/gpccmp/test/setup.ts @@ -1,15 +1,52 @@ import { beforeEach, vi } from 'vitest'; import { destroyForm } from '@aehrc/smart-forms-renderer'; +import smokingStatus from './terminology/smoking-status-1.json'; +import alcoholIntakeStatus from './terminology/alcohol-intake-status-1.json'; +import australianStatesTerritories from './terminology/australian-states-territories-2.json'; -// Every request resolves to an empty object, so any item backed by an external answerValueSet -// renders without options. Without this mock, getValueSetPromise throws `client is not a -// function`, buildForm rejects, and the form never leaves `Loading...`. +// The renderer resolves external answerValueSets through fhirclient. Left unmocked, +// getValueSetPromise throws `client is not a function`, buildForm rejects, and the form never +// leaves `Loading...`. Mocked to `{}`, a choice item renders with no options and therefore no +// input element at all — so the expansions below are what make those items selectable. +// See terminology/PROVENANCE.md for where they came from and how to refresh them. +const expansions: Record = { + 'https://healthterminologies.gov.au/fhir/ValueSet/smoking-status-1': smokingStatus, + 'https://healthterminologies.gov.au/fhir/ValueSet/alcohol-intake-status-1': alcoholIntakeStatus, + 'https://healthterminologies.gov.au/fhir/ValueSet/australian-states-territories-2': + australianStatesTerritories +}; + +// getValueSetPromise splits any absolute `…/ValueSet/$expand?url=…` into a server URL and a +// ValueSet URL, then requests `ValueSet/$expand?url={valueSetUrl}` — optionally with `|version` +// rewritten to `&version=`. Match on the ValueSet URL alone; the version is not pinned by the +// questionnaire and the fixtures hold one expansion each. vi.mock('fhirclient', () => ({ client: () => ({ - request: vi.fn(() => Promise.resolve({})) + request: vi.fn(({ url }: { url: string }) => { + const valueSetUrl = url.replace(/^ValueSet\/\$expand\?url=/, '').split('&version=')[0]; + return Promise.resolve(expansions[valueSetUrl] ?? {}); + }) }) })); +// Nothing in this suite may reach the network. `fhirclient` is the only HTTP client in the +// dependency chain and it is mocked above, so this guard is not what makes the suite offline — it +// is what keeps it that way. Without it, a renderer that started calling `fetch` directly, or a new +// test that forgot a mock, would quietly talk to the real Ontoserver in CI: slow, flaky, and green +// for the wrong reason. Failing loudly at the call site names the culprit. +function blockNetwork(label: string) { + return (...args: unknown[]) => { + const target = typeof args[0] === 'string' ? args[0] : JSON.stringify(args[0]); + throw new Error( + `Network access from a test: ${label} ${target}. This suite must run offline — add the ` + + `response to test/terminology/ or mock the caller. See test/terminology/PROVENANCE.md.` + ); + }; +} + +globalThis.fetch = blockNetwork('fetch') as unknown as typeof fetch; +XMLHttpRequest.prototype.open = blockNetwork('XMLHttpRequest.open'); + // jsdom has no ResizeObserver; the renderer's layout components require one. globalThis.ResizeObserver = class ResizeObserver { observe() {} diff --git a/forms/gpccmp/test/terminology/PROVENANCE.md b/forms/gpccmp/test/terminology/PROVENANCE.md new file mode 100644 index 000000000..bfc2d52a7 --- /dev/null +++ b/forms/gpccmp/test/terminology/PROVENANCE.md @@ -0,0 +1,47 @@ +# Vendored ValueSet expansions + +The renderer resolves every external `answerValueSet` through a terminology server. These tests must +not make network calls, so `../setup.ts` mocks `fhirclient` and answers `$expand` requests from the +files in this directory. + +Each file is the response of a real `$expand`, so the codes and display strings are the ones a user +would see. Nothing here is hand-written — a `choice` item whose options were invented would let a +test pass against terminology that does not exist. + +## How they were retrieved + +| File | ValueSet | Version | +|---|---|---| +| `smoking-status-1.json` | `https://healthterminologies.gov.au/fhir/ValueSet/smoking-status-1` | 1.0.0 | +| `alcohol-intake-status-1.json` | `https://healthterminologies.gov.au/fhir/ValueSet/alcohol-intake-status-1` | 1.0.0 | +| `australian-states-territories-2.json` | `https://healthterminologies.gov.au/fhir/ValueSet/australian-states-territories-2` | 2.0.2 | + +- Server: `https://r4.ontoserver.csiro.au/fhir` — the same server the suite names in + `@aehrc/questionnaire-test-toolkit`'s `terminologyServerUrl`. +- Retrieved: 2026-08-24. +- Request: `GET {server}/ValueSet/$expand?url={valueset url}`, `Accept: application/fhir+json`. + +To refresh one: + +```sh +curl -s -H 'Accept: application/fhir+json' \ + 'https://r4.ontoserver.csiro.au/fhir/ValueSet/$expand?url=https://healthterminologies.gov.au/fhir/ValueSet/smoking-status-1' \ + | jq 'del(.expansion.identifier, .expansion.timestamp)' > smoking-status-1.json +``` + +## The one edit made to each response + +`expansion.identifier` and `expansion.timestamp` are stripped. Both change on every request, so +keeping them would make the fixture look modified whenever it was refreshed. Everything else, +including the `copyright` element, is as the server returned it — the ADHA and SNOMED CT terms +require that statement to travel with every copy. + +## What this does not do + +The mock answers `$expand` for these three ValueSets and returns `{}` for anything else, which is +what the mock did for everything before these files existed. Adding a `choice` item backed by a +fourth external ValueSet will render it with no options; add its expansion here. + +The expansions are a snapshot. SNOMED CT is versioned — `smoking-status-1` expanded against the +`20260731` Australian edition — so a refresh can legitimately change displays and break a test that +selects by display text. That is the fixture doing its job. diff --git a/forms/gpccmp/test/terminology/alcohol-intake-status-1.json b/forms/gpccmp/test/terminology/alcohol-intake-status-1.json new file mode 100644 index 000000000..47b02c058 --- /dev/null +++ b/forms/gpccmp/test/terminology/alcohol-intake-status-1.json @@ -0,0 +1,51 @@ +{ + "resourceType": "ValueSet", + "url": "https://healthterminologies.gov.au/fhir/ValueSet/alcohol-intake-status-1", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "urn:oid:1.2.36.1.2001.1004.201.10265" + } + ], + "version": "1.0.0", + "name": "AlcoholIntakeStatus", + "title": "Alcohol Intake Status", + "status": "active", + "experimental": false, + "copyright": "Copyright © 2022 Australian Digital Health Agency - All rights reserved. Except for the material identified below, this content is licensed under a Creative Commons Attribution 4.0 International License. See https://creativecommons.org/licenses/by/4.0/. \n\nThis resource includes SNOMED Clinical Terms™ (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO). All rights reserved. SNOMED CT®, was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO. \n\nThe rights to use and implement or implementation of SNOMED CT content are limited to the extent it is necessary to allow for the end use of this material. No further rights are granted in respect of the International Release and no further use of any SNOMED CT content by any other party is permitted. \n\nAll copies of this resource must include this copyright statement and all information contained in this statement.", + "expansion": { + "total": 4, + "parameter": [ + { + "name": "used-codesystem", + "valueUri": "http://snomed.info/sct|http://snomed.info/sct/32506021000036107/version/20260731" + }, + { + "name": "version", + "valueUri": "http://snomed.info/sct|http://snomed.info/sct/32506021000036107/version/20260731" + } + ], + "contains": [ + { + "system": "http://snomed.info/sct", + "code": "783261004", + "display": "Lifetime non-drinker" + }, + { + "system": "http://snomed.info/sct", + "code": "82581004", + "display": "Former drinker" + }, + { + "system": "http://snomed.info/sct", + "code": "219006", + "display": "Current drinker" + }, + { + "system": "http://snomed.info/sct", + "code": "228276006", + "display": "Occasional drinker" + } + ] + } +} diff --git a/forms/gpccmp/test/terminology/australian-states-territories-2.json b/forms/gpccmp/test/terminology/australian-states-territories-2.json new file mode 100644 index 000000000..9e5fcc5fa --- /dev/null +++ b/forms/gpccmp/test/terminology/australian-states-territories-2.json @@ -0,0 +1,76 @@ +{ + "resourceType": "ValueSet", + "url": "https://healthterminologies.gov.au/fhir/ValueSet/australian-states-territories-2", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "urn:oid:1.2.36.1.2001.1004.201.10026" + } + ], + "version": "2.0.2", + "name": "AustralianStatesAndTerritories", + "title": "Australian States and Territories", + "status": "active", + "experimental": false, + "copyright": "Copyright © 2018 Australian Digital Health Agency - All rights reserved. Except for the material identified below, this content is licensed under a Creative Commons Attribution 4.0 International License. See https://creativecommons.org/licenses/by/4.0/. \n\nThis resource includes material that is based on Australian Institute of Health and Welfare material. \n\nAll copies of this resource must include this copyright statement and all information contained in this statement.", + "expansion": { + "total": 9, + "parameter": [ + { + "name": "used-codesystem", + "valueUri": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1|1.1.3" + }, + { + "name": "version", + "valueUri": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1|1.1.3" + } + ], + "contains": [ + { + "system": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1", + "code": "ACT", + "display": "Australian Capital Territory" + }, + { + "system": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1", + "code": "NSW", + "display": "New South Wales" + }, + { + "system": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1", + "code": "NT", + "display": "Northern Territory" + }, + { + "system": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1", + "code": "OTHER", + "display": "Other territories" + }, + { + "system": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1", + "code": "QLD", + "display": "Queensland" + }, + { + "system": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1", + "code": "SA", + "display": "South Australia" + }, + { + "system": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1", + "code": "TAS", + "display": "Tasmania" + }, + { + "system": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1", + "code": "VIC", + "display": "Victoria" + }, + { + "system": "https://healthterminologies.gov.au/fhir/CodeSystem/australian-states-territories-1", + "code": "WA", + "display": "Western Australia" + } + ] + } +} diff --git a/forms/gpccmp/test/terminology/smoking-status-1.json b/forms/gpccmp/test/terminology/smoking-status-1.json new file mode 100644 index 000000000..1519b7667 --- /dev/null +++ b/forms/gpccmp/test/terminology/smoking-status-1.json @@ -0,0 +1,51 @@ +{ + "resourceType": "ValueSet", + "url": "https://healthterminologies.gov.au/fhir/ValueSet/smoking-status-1", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "urn:oid:1.2.36.1.2001.1004.201.10244" + } + ], + "version": "1.0.0", + "name": "SmokingStatus", + "title": "Smoking Status", + "status": "active", + "experimental": false, + "copyright": "Copyright © 2022 Australian Digital Health Agency - All rights reserved. This content is licensed under a Creative Commons Attribution 4.0 International License. See https://creativecommons.org/licenses/by/4.0/. \n\nThis resource includes SNOMED Clinical Terms™ (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO). All rights reserved. SNOMED CT®, was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO. \n\nThe rights to use and implement or implementation of SNOMED CT content are limited to the extent it is necessary to allow for the end use of this material. No further rights are granted in respect of the International Release and no further use of any SNOMED CT content by any other party is permitted. \n\nAll copies of this resource must include this copyright statement and all information contained in this statement.", + "expansion": { + "total": 4, + "parameter": [ + { + "name": "used-codesystem", + "valueUri": "http://snomed.info/sct|http://snomed.info/sct/32506021000036107/version/20260731" + }, + { + "name": "version", + "valueUri": "http://snomed.info/sct|http://snomed.info/sct/32506021000036107/version/20260731" + } + ], + "contains": [ + { + "system": "http://snomed.info/sct", + "code": "428041000124106", + "display": "Occasional tobacco smoker" + }, + { + "system": "http://snomed.info/sct", + "code": "8517006", + "display": "Former smoker" + }, + { + "system": "http://snomed.info/sct", + "code": "266919005", + "display": "Lifetime non-smoker" + }, + { + "system": "http://snomed.info/sct", + "code": "77176002", + "display": "Current smoker" + } + ] + } +} From 1b4c41f8357c12a6a4919c4b89bede24e46c48cb Mon Sep 17 00:00:00 2001 From: Alex Pavlushkin Date: Mon, 24 Aug 2026 16:46:10 +0600 Subject: [PATCH 10/11] Update CI workflow to trigger on pull requests and enhance README with CI runtime details --- .github/workflows/vitest_gpccmp.yml | 2 +- forms/gpccmp/README.md | 6 ++++-- forms/gpccmp/test/setup.ts | 4 +++- forms/gpccmp/test/terminology/PROVENANCE.md | 8 ++++---- .../gpccmp/test/terminology/alcohol-intake-status-1.json | 1 + .../test/terminology/australian-states-territories-2.json | 1 + forms/gpccmp/test/terminology/smoking-status-1.json | 1 + 7 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/vitest_gpccmp.yml b/.github/workflows/vitest_gpccmp.yml index 4fe1cfb46..5b70f3580 100644 --- a/.github/workflows/vitest_gpccmp.yml +++ b/.github/workflows/vitest_gpccmp.yml @@ -1,6 +1,6 @@ name: Vitest GP CCMP Behaviour Tests -on: [workflow_dispatch, push, workflow_call] +on: [workflow_dispatch, push, pull_request, workflow_call] jobs: vitest-gpccmp-tests: diff --git a/forms/gpccmp/README.md b/forms/gpccmp/README.md index bc5871d45..a40a2e60c 100644 --- a/forms/gpccmp/README.md +++ b/forms/gpccmp/README.md @@ -93,6 +93,8 @@ helpers are next touched. ## Running the suite in CI `.github/workflows/vitest_gpccmp.yml` builds the four `packages/*` libraries, typechecks this -package, and runs the suite on every push. It is green — all 25 tests pass, in roughly 140 seconds -on a warm checkout. Treat any failure as a real regression; there is no expected-failure list to +package, and runs the suite on every push and pull request. A warm local verification on 2026-08-24 +ran all 25 tests in 152.56–195.63 seconds; the four builds, typecheck, and tests took +181.51–230.76 seconds in total, excluding `npm ci`. GitHub Actions reports the authoritative CI +runtime on each run. Treat any failure as a real regression; there is no expected-failure list to compare against any more. diff --git a/forms/gpccmp/test/setup.ts b/forms/gpccmp/test/setup.ts index e172bc7c3..dad3bc57a 100644 --- a/forms/gpccmp/test/setup.ts +++ b/forms/gpccmp/test/setup.ts @@ -9,7 +9,9 @@ import australianStatesTerritories from './terminology/australian-states-territo // leaves `Loading...`. Mocked to `{}`, a choice item renders with no options and therefore no // input element at all — so the expansions below are what make those items selectable. // See terminology/PROVENANCE.md for where they came from and how to refresh them. -const expansions: Record = { +type TimestampedExpansion = { expansion: { timestamp: string } }; + +const expansions: Record = { 'https://healthterminologies.gov.au/fhir/ValueSet/smoking-status-1': smokingStatus, 'https://healthterminologies.gov.au/fhir/ValueSet/alcohol-intake-status-1': alcoholIntakeStatus, 'https://healthterminologies.gov.au/fhir/ValueSet/australian-states-territories-2': diff --git a/forms/gpccmp/test/terminology/PROVENANCE.md b/forms/gpccmp/test/terminology/PROVENANCE.md index bfc2d52a7..829a26f34 100644 --- a/forms/gpccmp/test/terminology/PROVENANCE.md +++ b/forms/gpccmp/test/terminology/PROVENANCE.md @@ -26,14 +26,14 @@ To refresh one: ```sh curl -s -H 'Accept: application/fhir+json' \ 'https://r4.ontoserver.csiro.au/fhir/ValueSet/$expand?url=https://healthterminologies.gov.au/fhir/ValueSet/smoking-status-1' \ - | jq 'del(.expansion.identifier, .expansion.timestamp)' > smoking-status-1.json + | jq 'del(.expansion.identifier)' > smoking-status-1.json ``` ## The one edit made to each response -`expansion.identifier` and `expansion.timestamp` are stripped. Both change on every request, so -keeping them would make the fixture look modified whenever it was refreshed. Everything else, -including the `copyright` element, is as the server returned it — the ADHA and SNOMED CT terms +`expansion.identifier` is stripped because it changes on every request. `expansion.timestamp` is +retained: FHIR R4 requires it, and it records when this particular snapshot was produced. Everything +else, including the `copyright` element, is as the server returned it — the ADHA and SNOMED CT terms require that statement to travel with every copy. ## What this does not do diff --git a/forms/gpccmp/test/terminology/alcohol-intake-status-1.json b/forms/gpccmp/test/terminology/alcohol-intake-status-1.json index 47b02c058..8bbc0e2df 100644 --- a/forms/gpccmp/test/terminology/alcohol-intake-status-1.json +++ b/forms/gpccmp/test/terminology/alcohol-intake-status-1.json @@ -14,6 +14,7 @@ "experimental": false, "copyright": "Copyright © 2022 Australian Digital Health Agency - All rights reserved. Except for the material identified below, this content is licensed under a Creative Commons Attribution 4.0 International License. See https://creativecommons.org/licenses/by/4.0/. \n\nThis resource includes SNOMED Clinical Terms™ (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO). All rights reserved. SNOMED CT®, was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO. \n\nThe rights to use and implement or implementation of SNOMED CT content are limited to the extent it is necessary to allow for the end use of this material. No further rights are granted in respect of the International Release and no further use of any SNOMED CT content by any other party is permitted. \n\nAll copies of this resource must include this copyright statement and all information contained in this statement.", "expansion": { + "timestamp": "2026-08-24T20:39:36+10:00", "total": 4, "parameter": [ { diff --git a/forms/gpccmp/test/terminology/australian-states-territories-2.json b/forms/gpccmp/test/terminology/australian-states-territories-2.json index 9e5fcc5fa..b8a9a03cd 100644 --- a/forms/gpccmp/test/terminology/australian-states-territories-2.json +++ b/forms/gpccmp/test/terminology/australian-states-territories-2.json @@ -14,6 +14,7 @@ "experimental": false, "copyright": "Copyright © 2018 Australian Digital Health Agency - All rights reserved. Except for the material identified below, this content is licensed under a Creative Commons Attribution 4.0 International License. See https://creativecommons.org/licenses/by/4.0/. \n\nThis resource includes material that is based on Australian Institute of Health and Welfare material. \n\nAll copies of this resource must include this copyright statement and all information contained in this statement.", "expansion": { + "timestamp": "2026-08-24T20:39:37+10:00", "total": 9, "parameter": [ { diff --git a/forms/gpccmp/test/terminology/smoking-status-1.json b/forms/gpccmp/test/terminology/smoking-status-1.json index 1519b7667..c27e5bc0b 100644 --- a/forms/gpccmp/test/terminology/smoking-status-1.json +++ b/forms/gpccmp/test/terminology/smoking-status-1.json @@ -14,6 +14,7 @@ "experimental": false, "copyright": "Copyright © 2022 Australian Digital Health Agency - All rights reserved. This content is licensed under a Creative Commons Attribution 4.0 International License. See https://creativecommons.org/licenses/by/4.0/. \n\nThis resource includes SNOMED Clinical Terms™ (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO). All rights reserved. SNOMED CT®, was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO. \n\nThe rights to use and implement or implementation of SNOMED CT content are limited to the extent it is necessary to allow for the end use of this material. No further rights are granted in respect of the International Release and no further use of any SNOMED CT content by any other party is permitted. \n\nAll copies of this resource must include this copyright statement and all information contained in this statement.", "expansion": { + "timestamp": "2026-08-24T20:39:35+10:00", "total": 4, "parameter": [ { From 7e64a22f32129b36660392738b8541e8876d931d Mon Sep 17 00:00:00 2001 From: Alex Pavlushkin Date: Mon, 24 Aug 2026 17:01:16 +0600 Subject: [PATCH 11/11] Update Dockerfile to use Node.js 20 and ensure questionnaire test toolkit is included in the build process --- apps/smart-forms-app/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/smart-forms-app/Dockerfile b/apps/smart-forms-app/Dockerfile index be4951f84..c24775826 100644 --- a/apps/smart-forms-app/Dockerfile +++ b/apps/smart-forms-app/Dockerfile @@ -1,7 +1,7 @@ # Dockerfile # Step 1: Use a Node.js image to build the app -FROM --platform=$BUILDPLATFORM node:18 AS builder +FROM --platform=$BUILDPLATFORM node:20 AS builder # Set working directory inside the container WORKDIR /app @@ -21,6 +21,7 @@ COPY packages/sdc-assemble/package*.json ./packages/sdc-assemble/ COPY packages/sdc-populate/package*.json ./packages/sdc-populate/ COPY packages/sdc-template-extract/package*.json ./packages/sdc-template-extract/ COPY packages/smart-forms-renderer/package*.json ./packages/smart-forms-renderer/ +COPY packages/questionnaire-test-toolkit/package*.json ./packages/questionnaire-test-toolkit/ COPY apps/smart-forms-app/package*.json ./apps/smart-forms-app/ # Install all workspace dependencies from the root @@ -31,6 +32,7 @@ COPY packages/sdc-assemble/ ./packages/sdc-assemble/ COPY packages/sdc-populate/ ./packages/sdc-populate/ COPY packages/sdc-template-extract/ ./packages/sdc-template-extract/ COPY packages/smart-forms-renderer/ ./packages/smart-forms-renderer/ +COPY packages/questionnaire-test-toolkit/ ./packages/questionnaire-test-toolkit/ COPY apps/smart-forms-app/ ./apps/smart-forms-app/ # Build packages in dependency order (mirrors deploy_app.yml)