diff --git a/Example/ExampleUITests/ExampleUITests.swift b/Example/ExampleUITests/ExampleUITests.swift index 77f7b8c..b46995a 100644 --- a/Example/ExampleUITests/ExampleUITests.swift +++ b/Example/ExampleUITests/ExampleUITests.swift @@ -537,8 +537,8 @@ final class ExampleUITests: XCTestCase { // Dismiss results view app.swipeDown(velocity: XCUIGestureVelocity.fast) } - - func testMultipleEnableWhenExample() throws { + + func testMultipleEnableWhenExampleWithAllAnswersCorrect() throws { let app = XCUIApplication() app.launch() @@ -546,7 +546,7 @@ final class ExampleUITests: XCTestCase { XCTAssert(multipleEnableWhenButton.waitForExistence(timeout: 2)) multipleEnableWhenButton.tap() - // We will first answer all questions correctly + // Answer all questions correctly XCTAssert(app.tables.staticTexts["Yes"].waitForExistence(timeout: 2)) app.tables.staticTexts["Yes"].tap() XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) @@ -557,7 +557,6 @@ final class ExampleUITests: XCTestCase { XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) app.buttons["Next"].tap() - let integerField = app.textFields.element(boundBy: 0) XCTAssert(integerField.waitForExistence(timeout: 2)) integerField.tap() @@ -565,26 +564,31 @@ final class ExampleUITests: XCTestCase { XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) app.buttons["Next"].tap() - // First result screen appears if at least one answer is correct. - sleep(1) + // First result screen should appear since at least one answer is correct. + sleep(3) XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) app.buttons["Next"].tap() - // Second result screen appears if all answers are correct. - sleep(1) + // Second result screen should appear since all answers are correct. + sleep(3) XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) app.buttons["Next"].tap() // Now the completion screen will appear with a "Done" button that we can tap - sleep(1) + sleep(3) XCTAssert(app.buttons["Done"].waitForExistence(timeout: 2)) app.buttons["Done"].tap() - - // Now we relaunch the survey + } + + func testMultipleEnableWhenExampleWithOneAnswerCorrect() throws { + let app = XCUIApplication() + app.launch() + + let multipleEnableWhenButton = app.collectionViews.buttons["Multiple EnableWhen Expressions"] XCTAssert(multipleEnableWhenButton.waitForExistence(timeout: 2)) multipleEnableWhenButton.tap() - // This time we answer only one question correctly + // Answer only one question correctly XCTAssert(app.tables.staticTexts["Yes"].waitForExistence(timeout: 2)) app.tables.staticTexts["Yes"].tap() XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) @@ -595,6 +599,7 @@ final class ExampleUITests: XCTestCase { XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) app.buttons["Next"].tap() + let integerField = app.textFields.element(boundBy: 0) XCTAssert(integerField.waitForExistence(timeout: 2)) integerField.tap() integerField.typeText("2\n") @@ -602,7 +607,39 @@ final class ExampleUITests: XCTestCase { app.buttons["Next"].tap() // Only one result screen should appear. - sleep(1) + sleep(3) + XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) + app.buttons["Next"].tap() + + // Now the completion screen should appear. + sleep(3) + XCTAssert(app.buttons["Done"].waitForExistence(timeout: 2)) + app.buttons["Done"].tap() + } + + func testMultipleEnableWhenExampleWithNoAnswerCorrect() throws { + let app = XCUIApplication() + app.launch() + + let multipleEnableWhenButton = app.collectionViews.buttons["Multiple EnableWhen Expressions"] + XCTAssert(multipleEnableWhenButton.waitForExistence(timeout: 2)) + multipleEnableWhenButton.tap() + + // Answer all questions incorrectly + XCTAssert(app.tables.staticTexts["Yes"].waitForExistence(timeout: 2)) + app.tables.staticTexts["No"].tap() + XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) + app.buttons["Next"].tap() + + XCTAssert(app.tables.staticTexts["orange"].waitForExistence(timeout: 2)) + app.tables.staticTexts["orange"].tap() + XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) + app.buttons["Next"].tap() + + let integerField = app.textFields.element(boundBy: 0) + XCTAssert(integerField.waitForExistence(timeout: 2)) + integerField.tap() + integerField.typeText("2\n") XCTAssert(app.buttons["Next"].waitForExistence(timeout: 2)) app.buttons["Next"].tap() diff --git a/Sources/FHIRQuestionnaires/Resources/MultipleEnableWhen.json b/Sources/FHIRQuestionnaires/Resources/MultipleEnableWhen.json index 2af1d7d..5a4b9e4 100644 --- a/Sources/FHIRQuestionnaires/Resources/MultipleEnableWhen.json +++ b/Sources/FHIRQuestionnaires/Resources/MultipleEnableWhen.json @@ -110,8 +110,9 @@ "question": "c3fd1ee9-1f0d-4136-887c-f22589bbdc68", "operator": "=", "answerCoding": { + "code": "green", "system": "urn:uuid:2dab6b0b-4518-4c39-9f7f-a9da98e7c2f3", - "code": "green" + "display": "green" } }, { @@ -137,8 +138,9 @@ "question": "c3fd1ee9-1f0d-4136-887c-f22589bbdc68", "operator": "=", "answerCoding": { + "code": "green", "system": "urn:uuid:2dab6b0b-4518-4c39-9f7f-a9da98e7c2f3", - "code": "green" + "display": "green" } }, { @@ -147,6 +149,7 @@ "answerInteger": 12 } ], + "enableBehavior": "all" } ] } diff --git a/Sources/ResearchKitOnFHIR/FHIRToResearchKit/NavigationRules.swift b/Sources/ResearchKitOnFHIR/FHIRToResearchKit/NavigationRules.swift index 0084a88..3bfac8e 100644 --- a/Sources/ResearchKitOnFHIR/FHIRToResearchKit/NavigationRules.swift +++ b/Sources/ResearchKitOnFHIR/FHIRToResearchKit/NavigationRules.swift @@ -56,7 +56,7 @@ extension QuestionnaireItemEnableWhen { let resultSelector = ORKResultSelector(resultIdentifier: enableQuestionId) let predicate: NSPredicate? - // The translation from FHIR to ResearchKit preedicates requires negating the FHIR preedicates as FHIR preedicates activate steps while ResearchKit uses them to skip steps + // The translation from FHIR to ResearchKit predicates requires negating the FHIR predicates as FHIR predicates activate steps while ResearchKit uses them to skip steps switch answer { case .coding(let coding): predicate = try coding.predicate(with: resultSelector, operator: fhirOperator) @@ -90,7 +90,7 @@ extension Coding { return nil } - let expectedAnswer = ValueCoding(code: code, system: system) + let expectedAnswer = ValueCoding(code: code, system: system, display: display?.value?.string) let predicate = ORKResultPredicate.predicateForChoiceQuestionResult( with: resultSelector, diff --git a/Sources/ResearchKitOnFHIR/FHIRToResearchKit/QuestionnaireItem+ResearchKit.swift b/Sources/ResearchKitOnFHIR/FHIRToResearchKit/QuestionnaireItem+ResearchKit.swift index 555d924..cdceeb3 100644 --- a/Sources/ResearchKitOnFHIR/FHIRToResearchKit/QuestionnaireItem+ResearchKit.swift +++ b/Sources/ResearchKitOnFHIR/FHIRToResearchKit/QuestionnaireItem+ResearchKit.swift @@ -241,7 +241,7 @@ extension QuestionnaireItem { let system = valueSet?.compose?.include.first?.system?.value?.url.absoluteString else { continue } - let valueCoding = ValueCoding(code: code, system: system) + let valueCoding = ValueCoding(code: code, system: system, display: display) let choice = ORKTextChoice(text: display, value: valueCoding.rawValue as NSSecureCoding & NSCopying & NSObjectProtocol) choices.append(choice) } @@ -259,15 +259,16 @@ extension QuestionnaireItem { let system = coding.system?.value?.url.absoluteString else { continue } - let valueCoding = ValueCoding(code: code, system: system) + let valueCoding = ValueCoding(code: code, system: system, display: display) let choice = ORKTextChoice(text: display, value: valueCoding.rawValue as NSSecureCoding & NSCopying & NSObjectProtocol) + print(valueCoding.rawValue as NSSecureCoding & NSCopying & NSObjectProtocol) choices.append(choice) } if openChoice { // If the `QuestionnaireItemType` is `open-choice`, allow user to enter in their own free-text answer. let otherChoiceText = NSLocalizedString("Other", comment: "") - let otherChoiceResult = ValueCoding(code: "other", system: "other") + let otherChoiceResult = ValueCoding(code: "other", system: "other", display: otherChoiceText) let otherChoice = ORKTextChoiceOther.choice( withText: otherChoiceText, detailText: nil, diff --git a/Sources/ResearchKitOnFHIR/ResearchKitToFHIR/ORKTaskResult+FHIR.swift b/Sources/ResearchKitOnFHIR/ResearchKitToFHIR/ORKTaskResult+FHIR.swift index e65a4be..8f23d27 100644 --- a/Sources/ResearchKitOnFHIR/ResearchKitToFHIR/ORKTaskResult+FHIR.swift +++ b/Sources/ResearchKitOnFHIR/ResearchKitToFHIR/ORKTaskResult+FHIR.swift @@ -131,6 +131,7 @@ extension ORKTaskResult { if let valueCodingString = answer as? String, let valueCoding = ValueCoding(rawValue: valueCodingString) { let coding = Coding( code: FHIRPrimitive(FHIRString(valueCoding.code)), + display: valueCoding.display.map { FHIRPrimitive(FHIRString($0)) }, system: FHIRPrimitive(FHIRURI(stringLiteral: valueCoding.system)) ) responses += [.coding(coding)] diff --git a/Sources/ResearchKitOnFHIR/ValueCoding/ValueCoding.swift b/Sources/ResearchKitOnFHIR/ValueCoding/ValueCoding.swift index 5b9e555..cc24529 100644 --- a/Sources/ResearchKitOnFHIR/ValueCoding/ValueCoding.swift +++ b/Sources/ResearchKitOnFHIR/ValueCoding/ValueCoding.swift @@ -13,15 +13,19 @@ struct ValueCoding: Equatable, Codable, RawRepresentable { enum CodingKeys: String, CodingKey { case code case system + case display } let code: String let system: String - + let display: String? var rawValue: String { - guard let data = try? JSONEncoder().encode(self) else { + let encoder = JSONEncoder() + encoder.outputFormatting = [.sortedKeys] + + guard let data = try? encoder.encode(self) else { return "{}" } @@ -29,9 +33,10 @@ struct ValueCoding: Equatable, Codable, RawRepresentable { } - init(code: String, system: String) { + init(code: String, system: String, display: String?) { self.code = code self.system = system + self.display = display } init?(rawValue: String) { @@ -47,12 +52,13 @@ struct ValueCoding: Equatable, Codable, RawRepresentable { let values = try decoder.container(keyedBy: CodingKeys.self) self.code = try values.decode(String.self, forKey: .code) self.system = try values.decode(String.self, forKey: .system) + self.display = try values.decodeIfPresent(String.self, forKey: .display) } - func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(code, forKey: .code) try container.encode(system, forKey: .system) + try container.encode(display, forKey: .display) } } diff --git a/Tests/ResearchKitOnFHIRTests/ResearchKitToFHIRTests.swift b/Tests/ResearchKitOnFHIRTests/ResearchKitToFHIRTests.swift index 688f4ec..69e136b 100644 --- a/Tests/ResearchKitOnFHIRTests/ResearchKitToFHIRTests.swift +++ b/Tests/ResearchKitOnFHIRTests/ResearchKitToFHIRTests.swift @@ -183,7 +183,7 @@ final class ResearchKitToFHIRTests: XCTestCase { } func testSingleChoiceResponse() { - let testValue = ValueCoding(code: "testCode", system: "http://biodesign.stanford.edu/test-system") + let testValue = ValueCoding(code: "testCode", system: "http://biodesign.stanford.edu/test-system", display: "Test Code") let choiceResult = ORKChoiceQuestionResult(identifier: "choiceResult") choiceResult.choiceAnswers = [testValue.rawValue as NSSecureCoding & NSCopying & NSObjectProtocol] @@ -198,12 +198,13 @@ final class ResearchKitToFHIRTests: XCTestCase { switch answer { case let .coding(coding): guard let code = coding.code?.value?.string, + let display = coding.display?.value?.string, let system = coding.system?.value?.url.absoluteString else { XCTFail("Could not extract the code and system from the coding.") return } - let valueCoding = ValueCoding(code: code, system: system) + let valueCoding = ValueCoding(code: code, system: system, display: display) XCTAssertEqual(testValue, valueCoding) default: @@ -214,8 +215,8 @@ final class ResearchKitToFHIRTests: XCTestCase { func testMultipleChoiceResponse() { let testValues = [ - ValueCoding(code: "testCode1", system: "http://biodesign.stanford.edu/test-system"), - ValueCoding(code: "testCode2", system: "http://biodesign.stanford.edu/test-system") + ValueCoding(code: "testCode1", system: "http://biodesign.stanford.edu/test-system", display: "Test Code 1"), + ValueCoding(code: "testCode2", system: "http://biodesign.stanford.edu/test-system", display: "Test Code 2") ] let choiceResult = ORKChoiceQuestionResult(identifier: "choiceResult") @@ -240,12 +241,13 @@ final class ResearchKitToFHIRTests: XCTestCase { switch answer { case let .coding(coding): guard let code = coding.code?.value?.string, + let display = coding.display?.value?.string, let system = coding.system?.value?.url.absoluteString else { XCTFail("Could not extract the code and system from the coding.") return } - let valueCoding = ValueCoding(code: code, system: system) + let valueCoding = ValueCoding(code: code, system: system, display: display) XCTAssertEqual(testValues[index], valueCoding) default: