Compare commits

..

23 Commits

Author SHA1 Message Date
Pariece McKinney 5c5d295bd5 Public release 3.1.0 2024-10-15 17:05:47 -04:00
Pariece McKinney b274d1f84e Update README.md 2024-10-15 08:23:23 -04:00
aplummer-apple 57427e8c7e Set “Enable Module Verifier” flag to “Yes” only in Release build configuration (#1579) 2024-08-08 18:21:18 -04:00
Louie 6cfb995d83 cleaning up unused strings (#1578) 2024-06-26 10:23:19 -07:00
Pariece McKinney c71d1a56dd Public release 3.0.1 2024-05-08 14:56:11 -04:00
Corey 6c24a7e922 ci: Add code integration testing to repo (#1569) 2024-04-10 14:33:32 -04:00
Pariece McKinney b14e5cfcb0 Public Release 3.0 2024-03-28 19:39:04 -04:00
Louie fee76c2d93 Merge branch 'stable' into main 2023-12-05 14:00:17 -08:00
Louie a07041901b Public Release 2.2.15
Various ORKTextChoiceOther and ORKChoiceOtherViewCell improvements
Bumped version to 2.2.15
Bumped Cocoapod version to 2.2.15
2023-12-05 11:19:31 -08:00
Louie Chatta c370011616 Public Release 2.2.15
- Various ORKTextChoiceOther and ORKChoiceOtherViewCell improvements 
- Bumped version to 2.2.15
- Bumped Cocoapod version to 2.2.15
2023-12-05 11:02:55 -08:00
Pariece McKinney b8f155974c Public release/2.2.12 (#1555)
- Fixed `ORKHealthKitQuantityTypeAnswerFormat`issues
- Improved `ORKReviewStep` initialization
- Introduced a new property called `shouldAutomaticallyAdjustImageTintColor` on `ORKStep` to automatically adopt dark mode version of an image
- Added `ORKSESQuestionResult` to Table View Providers
- ORKCatalog Improvements
- ORKImageSelectionView improvements
- Bumped version to 2.2.12
2023-11-01 18:18:20 -04:00
Pariece McKinney 2fb22a6256 Public release/2.2.10 (#1548)
Fix ORKNavigationContainerView so that it does not call didMoveToWindow from tintColorDidChange
Bumped version to 2.2.10
2023-05-10 17:40:25 -04:00
ronzilla-apple bd60d945bb Public release/2.2.9 (#1541)
* Address issue where there was no option to "Save Results" if task is cancelled (Issue #1536/1540)
* Address an issue where ORKTaskViewController setting tintColor on stepViewController views broke ORKStepViewController's addResult:

* Fixed order issue of tone audiometry steps
* Include missing ORKSpeechRecognition header export (Issue #1534)
* Switch to contentEdgeInsets API in ORKContinueButton to fix an issue where button title wouldn't always fit onscreen (on iOS 16 during long press) (Issue #1533)
* Remove min/max values for ORKCatalog "Numeric with Display Unit"

* xcconfig file updates for version and driving ORKCatalog bundleID
* Addressing compiler warnings
* Added unit tests for init'ing taskViewController with restoration data
* Adding task restoration feature to ORKCatalog's taskViewController presentation
2023-02-13 11:03:48 -08:00
Pariece McKinney 8f58410af9 Release 2.2.8
-Bug fixes and enhancements
-New helper method (ORKViewTintColor) for setting tintColor
-New “displayUnit” property added to ORKAnswerFormat
-Updates in ORKNormalizedReactionTimeResult’s secure coding (breaks compatibility with previous archives).
2022-12-20 11:14:59 -08:00
Louie fc67cc944c Merge pull request #1519 from cbaker6/fixTintColor
Propagate tintColor if window not available
2022-11-29 20:22:37 -08:00
Louie c8473017ec Merge pull request #1531 from ResearchKit/cocoapod_fix
remove deprecated ORKConsent files
2022-11-29 13:21:51 -08:00
Louis Chatta 2bc0033aa8 remove deprecated ORKConsent files 2022-11-29 11:51:42 -08:00
Corey Baker 64a068596b make suggested changes 2022-11-28 15:25:59 -05:00
Corey Baker c10693930d Fix tintColor for ORKWebViewStepViewController buttons 2022-11-15 20:48:25 -05:00
Corey Baker c0f309edf3 only check for tintColor once in viewControllerForStep 2022-11-15 09:16:44 -05:00
Corey Baker b1cc631748 propagate tintColor if window not available 2022-11-14 14:46:02 -05:00
Pariece McKinney a89059f5dc 2.1.1 Release
-Bug fixes
-New ORKAccuracyStroopStep
-AudioStep now allows for audio recording 
-New Styles for ORKDontKnowButton
-General layout and UI improvements
2022-10-26 16:07:45 -07:00
Will 2cc8f9e7d5 Prevent isPasscodeStoredInKeychain crash, correct warnings (#1505)
* fix: prevent isPasscodeStoredInKeychain from causing a crash

* fix: prevent nskeyedunarchiver warnings, add missing classes
2022-06-16 10:55:09 -07:00
27 changed files with 1226 additions and 112 deletions
+26
View File
@@ -0,0 +1,26 @@
name: Build
on:
push:
branches: [ 'main', 'stable' ]
pull_request:
branches: [ 'main', 'stable' ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: macos-14
strategy:
matrix:
destination: ['platform=iOS\ Simulator,OS=17.4,name=iPhone\ 15\ Pro']
scheme: ['ResearchKit']
name: ${{ matrix.scheme }} Unit Tests
steps:
- uses: actions/checkout@v4
- name: Set Xcode Version
run: sudo xcode-select -s /Applications/Xcode_15.3.app
- name: Test
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -verbose -workspace RKWorkspace.xcworkspace -scheme ${{ matrix.scheme }} -destination ${{ matrix.destination }} build test | xcpretty
-1
View File
@@ -1,4 +1,3 @@
![ResearchKit](https://github.com/user-attachments/assets/0384c1a6-ec67-45d3-be68-136a2e4cacff)
ResearchKit Framework
-13
View File
@@ -1,18 +1,5 @@
# ResearchKit Release Notes
## ResearchKit 3.1.1 Release Notes
General bug fixes for the following:
- **ORKMotionActivityPermissionType**
Fixed issue that caused the next button to remain disabled after gaining permission from user.
- **ORKRegistrationStep**
Removed yellow overlay that prevented password entry.
- **ORKdBHLToneAudiometryStep**
Fixed issue that caused the tap button to appear twice.
## ResearchKit 3.1 Release Notes
In addition to general stability and performance improvements, ResearchKit 3.1 includes the following updates:
+1 -18
View File
@@ -11,6 +11,7 @@
isa = PBXAggregateTarget;
buildConfigurationList = 0BC672D92BD9C52D005798AC /* Build configuration list for PBXAggregateTarget "ResearchKitAllTargets" */;
buildPhases = (
0BC672E82BD9C69C005798AC /* ShellScript */,
);
dependencies = (
0BC672E02BD9C541005798AC /* PBXTargetDependency */,
@@ -6017,24 +6018,6 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
0BC672DA2BD9C52D005798AC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
0BC672DC2BD9C52D005798AC /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
3FFF18691829DB1E00167070 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5D000ED12620F27100E5442A /* Project-Debug.xcconfig */;
@@ -43,25 +43,20 @@ static const uint32_t IconDarkTintColor = 0xEF6FD8;
@property (nonatomic, readonly, assign) BOOL canContinue;
@end
@implementation ORKMotionActivityPermissionType {
__weak NSTimer *_checkStatusTimer;
}
@implementation ORKMotionActivityPermissionType
+ (instancetype)new {
return [[ORKMotionActivityPermissionType alloc] init];
}
- (instancetype)init {
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
- (void)cleanUp {
[self _invalidateCheckStatusTimer];
}
- (CMMotionActivityManager *)activityManager {
if (!_activityManager) {
_activityManager = [[CMMotionActivityManager alloc] init];
@@ -110,33 +105,9 @@ static const uint32_t IconDarkTintColor = 0xEF6FD8;
[self.activityManager startActivityUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMMotionActivity * _Nullable activity) {}];
[self.activityManager stopActivityUpdates];
if (self.permissionsStatusUpdateCallback != nil) {
self.permissionsStatusUpdateCallback();
}
if (!_checkStatusTimer) {
__weak typeof(self) weakSelf = self;
_checkStatusTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:weakSelf
selector:@selector(_checkPermissionStatus)
userInfo:nil
repeats:YES];
}
}
- (void)_checkPermissionStatus {
if ([self permissionState] == ORKRequestPermissionsStateConnected && self.permissionsStatusUpdateCallback != nil) {
self.permissionsStatusUpdateCallback();
[self _invalidateCheckStatusTimer];
}
}
- (void)_invalidateCheckStatusTimer {
if (_checkStatusTimer) {
[_checkStatusTimer invalidate];
_checkStatusTimer = nil;
}
}
- (BOOL)isEqual:(id)object {
-1
View File
@@ -69,7 +69,6 @@ ORK_CLASS_AVAILABLE
@property (nonatomic, assign, readonly) BOOL canContinue;
- (void)requestPermission;
- (void)cleanUp;
+ (ORKHealthKitPermissionType *)healthKitPermissionTypeWithSampleTypesToWrite:(nullable NSSet<HKSampleType *> *)sampleTypesToWrite
objectTypesToRead:(nullable NSSet<HKObjectType *> *)objectTypesToRead;
-4
View File
@@ -66,10 +66,6 @@
ORKThrowMethodUnavailableException();
}
- (void)cleanUp {
// left empty for optional subclass override
}
+ (ORKHealthKitPermissionType *)healthKitPermissionTypeWithSampleTypesToWrite:(NSSet<HKSampleType *> *)sampleTypesToWrite objectTypesToRead:(NSSet<HKObjectType *> *)objectTypesToRead {
return [[ORKHealthKitPermissionType alloc] initWithSampleTypesToWrite:sampleTypesToWrite
objectTypesToRead:objectTypesToRead];
+2 -2
View File
@@ -180,14 +180,14 @@ ORK_CLASS_AVAILABLE API_AVAILABLE(ios(11.0), watchos(6.0))
/**
A property that gates automatic tint color image changes based on appearance changes.
The default value for this property is NO.
The default value for this property is NO.
*/
@property (nonatomic) BOOL shouldAutomaticallyAdjustImageTintColor;
/**
A property that determines whether to show progress for this step when presented.
The default is YES.
The default is YES.
*/
@property (nonatomic, assign) BOOL showsProgress;
@@ -23,7 +23,7 @@ PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*] =
SWIFT_VERSION = 5.0
CLANG_STATIC_ANALYZER_MODE = deep
ORK_FRAMEWORK_VERSION_NUMBER = 3.1.1
ORK_FRAMEWORK_VERSION_NUMBER = 3.1
ORK_FRAMEWORK_BUILD_NUMBER = $(ORK_FRAMEWORK_BUILD_NUMBER_CI_$(CI)) // ORK_FRAMEWORK_BUILD_NUMBER_CI_TRUE or ORK_FRAMEWORK_BUILD_NUMBER_CI_
ORK_FRAMEWORK_BUILD_NUMBER_CI_TRUE = $(CI_BUILD_NUMBER)
+7 -2
View File
@@ -73,8 +73,13 @@ static NSArray <ORKFormItem*> *ORKRegistrationFormItems(ORKRegistrationStepOptio
answerFormat.autocapitalizationType = UITextAutocapitalizationTypeNone;
answerFormat.autocorrectionType = UITextAutocorrectionTypeNo;
answerFormat.spellCheckingType = UITextSpellCheckingTypeNo;
answerFormat.textContentType = UITextContentTypeOneTimeCode;
if (@available(iOS 12.0, *)) {
answerFormat.textContentType = UITextContentTypeNewPassword;
} else {
answerFormat.textContentType = UITextContentTypePassword;
}
ORKFormItem *item = [[ORKFormItem alloc] initWithIdentifier:ORKRegistrationFormItemIdentifierPassword
text:ORKLocalizedString(@"PASSWORD_FORM_ITEM_TITLE", nil)
answerFormat:answerFormat
@@ -18,7 +18,7 @@ Present a specific question and collect an answer.
- ``ORKAnswerFormat``
- ``ORKNumericPrecision``
### Height and Weight
### Height, and Weight
- ``ORKHeightAnswerFormat``
- ``ORKWeightAnswerFormat``
@@ -122,20 +122,18 @@ NSString * const ORKdBHLToneAudiometryStepViewAccessibilityIdentifier = @"ORKdBH
- (void)configureStep {
ORKdBHLToneAudiometryStep *dBHLTAStep = [self dBHLToneAudiometryStep];
if (!self.dBHLToneAudiometryContentView) {
self.dBHLToneAudiometryContentView = [[ORKdBHLToneAudiometryContentView alloc] init];
self.activeStepView.activeCustomView = self.dBHLToneAudiometryContentView;
self.activeStepView.customContentFillsAvailableSpace = YES;
[self.activeStepView.navigationFooterView setHidden:YES];
self.dBHLToneAudiometryContentView = [[ORKdBHLToneAudiometryContentView alloc] init];
self.activeStepView.activeCustomView = self.dBHLToneAudiometryContentView;
self.activeStepView.customContentFillsAvailableSpace = YES;
[self.activeStepView.navigationFooterView setHidden:YES];
[self.dBHLToneAudiometryContentView.tapButton addTarget:self action:@selector(tapButtonPressed) forControlEvents:UIControlEventTouchDown];
_audioChannel = dBHLTAStep.earPreference;
_audioGenerator = [self createAudioGeneratorFromHeadphoneType:dBHLTAStep.headphoneType];
_audioGenerator.delegate = self;
[self.dBHLToneAudiometryContentView.tapButton addTarget:self action:@selector(tapButtonPressed) forControlEvents:UIControlEventTouchDown];
_audioChannel = dBHLTAStep.earPreference;
_audioGenerator = [self createAudioGeneratorFromHeadphoneType:dBHLTAStep.headphoneType];
_audioGenerator.delegate = self;
_hapticFeedback = [[UIImpactFeedbackGenerator alloc] initWithStyle: UIImpactFeedbackStyleHeavy];
}
_hapticFeedback = [[UIImpactFeedbackGenerator alloc] initWithStyle: UIImpactFeedbackStyleHeavy];
}
- (void)addObservers {
+1 -3
View File
@@ -55,7 +55,7 @@ static BOOL ORKIsResearchKitClass(Class class) {
+ (NSArray<NSString *> *)_fetchExclusionList {
NSArray<NSString *> *classesToExclude = @[];
#if !ORK_FEATURE_CLLOCATIONMANAGER_AUTHORIZATION
NSArray<NSString *> *locationClasses = @[
@"ORKLocation",
@@ -485,7 +485,6 @@ ORK_MAKE_TEST_INIT(ORKAccuracyStroopStep, (^{ return [[ORKAccuracyStroopStep all
[ORKTouchAbilityPinchStep class],
[ORKTouchAbilitySwipeStep class],
[ORKTouchAbilityTapResult class],
[ORKTouchAbilityTouchTracker class],
[ORKTouchAbilityRotationStep class],
[ORKTouchAbilityLongPressStep class],
[ORKTouchAbilityScrollStep class],
@@ -620,7 +619,6 @@ ORK_MAKE_TEST_INIT(ORKAccuracyStroopStep, (^{ return [[ORKAccuracyStroopStep all
@"ORKTimeIntervalAnswerFormat.defaultInterval",
@"ORKTimeIntervalAnswerFormat.maximumInterval",
@"ORKTimeIntervalAnswerFormat.step",
@"ORKTouchAbilityTouchTracker.delegate",
@"ORKVerificationStep.verificationViewControllerClass",
@"ORKVideoCaptureStep.templateImage",
@"ORKWeightAnswerFormat.useMetricSystem",
@@ -91,18 +91,9 @@ NSString * const ORKRequestPermissionsStepViewAccessibilityIdentifier = @"ORKReq
}
- (void)dealloc {
[self _cleanupPermissionTypes];
[[NSNotificationCenter defaultCenter] removeObserver:self name:ORKRequestPermissionsNotificationCardViewStatusChanged object:nil];
}
- (void)_cleanupPermissionTypes {
ORKRequestPermissionsStep *requestPermissionStep = [self requestPermissionsStep];
for (ORKPermissionType *permissionType in requestPermissionStep.permissionTypes) {
[permissionType cleanUp];
}
}
- (ORKStepResult *)result {
ORKStepResult *parentResult = [super result];
@@ -194,13 +185,9 @@ NSString * const ORKRequestPermissionsStepViewAccessibilityIdentifier = @"ORKReq
NSMutableArray<ORKRequestPermissionView *> *cardViews = [NSMutableArray new];
for (ORKPermissionType *permissionType in requestPermissionStep.permissionTypes) {
ORKRequestPermissionView *cardView = [[ORKRequestPermissionView alloc] initWithIconImage:permissionType.image
title:permissionType.localizedTitle
detailText:permissionType.localizedDetailText];
ORKRequestPermissionView *cardView = [[ORKRequestPermissionView alloc] initWithIconImage:permissionType.image title:permissionType.localizedTitle detailText:permissionType.localizedDetailText];
[cardView updateIconTintColor:permissionType.iconTintColor];
[cardView.requestPermissionButton addTarget:permissionType
action:@selector(requestPermission)
forControlEvents:UIControlEventTouchUpInside];
[cardView.requestPermissionButton addTarget:permissionType action:@selector(requestPermission) forControlEvents:UIControlEventTouchUpInside];
[self permissionStatusUpdatedForPermissionType:permissionType cardView:cardView];
// create the update callback
@@ -19,6 +19,12 @@
51F210FE2B49D04F0072B536 /* TaskListRowConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F210FD2B49D04F0072B536 /* TaskListRowConstants.swift */; };
863CCD821ACF545E009FD3B4 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 863CCD811ACF545E009FD3B4 /* HealthKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
86B89AA51AB2C0A5001626A4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86B89A9C1AB2C0A5001626A4 /* AppDelegate.swift */; };
8C67958C2BC3B09B00DE7286 /* TaskListRowConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F210FD2B49D04F0072B536 /* TaskListRowConstants.swift */; };
9622283924F04DBF0056E74D /* TaskScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622283824F04DBF0056E74D /* TaskScreen.swift */; };
9622283B24F04DFE0056E74D /* CommonElements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622283A24F04DFE0056E74D /* CommonElements.swift */; };
9622283D24F04E6F0056E74D /* AllowScreens.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622283C24F04E6F0056E74D /* AllowScreens.swift */; };
9622283F24F04EBA0056E74D /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622283E24F04EBA0056E74D /* Helpers.swift */; };
9622284124F04EEF0056E74D /* PreSubmissionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9622284024F04EEF0056E74D /* PreSubmissionTests.swift */; };
B125E3D41B1BA47600FF8759 /* tap.aif in Resources */ = {isa = PBXBuildFile; fileRef = B125E3D31B1BA47600FF8759 /* tap.aif */; };
BAAC3FD620C4EF1C0065B9E1 /* ResearchKit.pdf in Resources */ = {isa = PBXBuildFile; fileRef = BAAC3FD520C4EF1C0065B9E1 /* ResearchKit.pdf */; };
BC2A3CD91C58F1CA00DA64B7 /* ResearchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC2A3CD81C58F1CA00DA64B7 /* ResearchKit.framework */; };
@@ -43,6 +49,13 @@
remoteGlobalIDString = 869230BD1AAA890A00BFE11B;
remoteInfo = ORKCatalog;
};
9622282F24F04D780056E74D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 869230B61AAA890A00BFE11B /* Project object */;
proxyType = 1;
remoteGlobalIDString = 869230BD1AAA890A00BFE11B;
remoteInfo = ORKCatalog;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -118,6 +131,14 @@
869230BE1AAA890A00BFE11B /* ORKCatalog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ORKCatalog.app; sourceTree = BUILT_PRODUCTS_DIR; };
86B89A981AB2C0A5001626A4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
86B89A9C1AB2C0A5001626A4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = ORKCatalog/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
9622282A24F04D780056E74D /* ORKCatalogUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ORKCatalogUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
9622282E24F04D780056E74D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9622283824F04DBF0056E74D /* TaskScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskScreen.swift; sourceTree = "<group>"; };
9622283A24F04DFE0056E74D /* CommonElements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonElements.swift; sourceTree = "<group>"; };
9622283C24F04E6F0056E74D /* AllowScreens.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllowScreens.swift; sourceTree = "<group>"; };
9622283E24F04EBA0056E74D /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
9622284024F04EEF0056E74D /* PreSubmissionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreSubmissionTests.swift; sourceTree = "<group>"; };
9632B5D524F093070010818C /* ORKCatalogUI.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ORKCatalogUI.xctestplan; sourceTree = "<group>"; };
B125E3D31B1BA47600FF8759 /* tap.aif */ = {isa = PBXFileReference; lastKnownFileType = file; path = tap.aif; sourceTree = "<group>"; };
BAAC3FD520C4EF1C0065B9E1 /* ResearchKit.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ResearchKit.pdf; sourceTree = "<group>"; };
BAFAD7AA1EFB2907004DEC45 /* fr-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr-CA"; path = fr_CA.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -151,6 +172,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
9622282724F04D780056E74D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -205,6 +233,7 @@
children = (
5116D0BF2677FB780010FAB1 /* ResearchKit-Shared.xcconfig */,
3E39B9E81ABF675000C2ABE5 /* README.md */,
9622282B24F04D780056E74D /* ORKCatalogUITests */,
51AF19782B59F1E500D3B399 /* ORKCatalogTests */,
BC2A3C9E1C58E81500DA64B7 /* Frameworks */,
869230C01AAA890A00BFE11B /* ORKCatalog */,
@@ -216,6 +245,7 @@
isa = PBXGroup;
children = (
869230BE1AAA890A00BFE11B /* ORKCatalog.app */,
9622282A24F04D780056E74D /* ORKCatalogUITests.xctest */,
51AF19772B59F1E400D3B399 /* ORKCatalogTests.xctest */,
);
name = Products;
@@ -244,6 +274,28 @@
path = "Supporting Files";
sourceTree = "<group>";
};
8CDBF98A2AFD9FC800AF767B /* Screens */ = {
isa = PBXGroup;
children = (
9622283824F04DBF0056E74D /* TaskScreen.swift */,
9622283A24F04DFE0056E74D /* CommonElements.swift */,
9622283C24F04E6F0056E74D /* AllowScreens.swift */,
);
path = Screens;
sourceTree = "<group>";
};
9622282B24F04D780056E74D /* ORKCatalogUITests */ = {
isa = PBXGroup;
children = (
8CDBF98A2AFD9FC800AF767B /* Screens */,
9622282E24F04D780056E74D /* Info.plist */,
9622283E24F04EBA0056E74D /* Helpers.swift */,
9622284024F04EEF0056E74D /* PreSubmissionTests.swift */,
9632B5D524F093070010818C /* ORKCatalogUI.xctestplan */,
);
path = ORKCatalogUITests;
sourceTree = "<group>";
};
BC2A3C9E1C58E81500DA64B7 /* Frameworks */ = {
isa = PBXGroup;
children = (
@@ -308,6 +360,24 @@
productReference = 869230BE1AAA890A00BFE11B /* ORKCatalog.app */;
productType = "com.apple.product-type.application";
};
9622282924F04D780056E74D /* ORKCatalogUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9622283624F04D780056E74D /* Build configuration list for PBXNativeTarget "ORKCatalogUITests" */;
buildPhases = (
9622282624F04D780056E74D /* Sources */,
9622282724F04D780056E74D /* Frameworks */,
9622282824F04D780056E74D /* Resources */,
);
buildRules = (
);
dependencies = (
9622283024F04D780056E74D /* PBXTargetDependency */,
);
name = ORKCatalogUITests;
productName = ORKCatalogUITests;
productReference = 9622282A24F04D780056E74D /* ORKCatalogUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -328,6 +398,12 @@
LastSwiftMigration = 1530;
ProvisioningStyle = Automatic;
};
9622282924F04D780056E74D = {
CreatedOnToolsVersion = 11.6;
DevelopmentTeam = "";
ProvisioningStyle = Automatic;
TestTargetID = 869230BD1AAA890A00BFE11B;
};
};
};
buildConfigurationList = 869230B91AAA890A00BFE11B /* Build configuration list for PBXProject "ORKCatalog" */;
@@ -391,6 +467,7 @@
projectRoot = "";
targets = (
869230BD1AAA890A00BFE11B /* ORKCatalog */,
9622282924F04D780056E74D /* ORKCatalogUITests */,
51AF19762B59F1E400D3B399 /* ORKCatalogTests */,
);
};
@@ -418,6 +495,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
9622282824F04D780056E74D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -447,6 +531,19 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
9622282624F04D780056E74D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9622283924F04DBF0056E74D /* TaskScreen.swift in Sources */,
9622283B24F04DFE0056E74D /* CommonElements.swift in Sources */,
8C67958C2BC3B09B00DE7286 /* TaskListRowConstants.swift in Sources */,
9622284124F04EEF0056E74D /* PreSubmissionTests.swift in Sources */,
9622283F24F04EBA0056E74D /* Helpers.swift in Sources */,
9622283D24F04E6F0056E74D /* AllowScreens.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -455,6 +552,11 @@
target = 869230BD1AAA890A00BFE11B /* ORKCatalog */;
targetProxy = 51AF197B2B59F1E500D3B399 /* PBXContainerItemProxy */;
};
9622283024F04D780056E74D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 869230BD1AAA890A00BFE11B /* ORKCatalog */;
targetProxy = 9622282F24F04D780056E74D /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -758,6 +860,68 @@
};
name = Debug;
};
9622283124F04D780056E74D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = ORKCatalogUITests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.HRP.ORKCatalogUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = ORKCatalog;
};
name = Debug;
};
9622283424F04D780056E74D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = ORKCatalogUITests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.HRP.ORKCatalogUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = ORKCatalog;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -788,6 +952,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
9622283624F04D780056E74D /* Build configuration list for PBXNativeTarget "ORKCatalogUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9622283124F04D780056E74D /* Debug */,
9622283424F04D780056E74D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
};
rootObject = 869230B61AAA890A00BFE11B /* Project object */;
@@ -26,9 +26,24 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
shouldUseLaunchSchemeArgsEnv = "YES">
<TestPlans>
<TestPlanReference
reference = "container:ORKCatalogUITests/ORKCatalogUI.xctestplan"
default = "YES">
</TestPlanReference>
</TestPlans>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9622282924F04D780056E74D"
BuildableName = "ORKCatalogUITests.xctest"
BlueprintName = "ORKCatalogUITests"
ReferencedContainer = "container:ORKCatalog.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "869230BD1AAA890A00BFE11B"
BuildableName = "ORKCatalog.app"
BlueprintName = "ORKCatalog"
ReferencedContainer = "container:ORKCatalog.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<TestPlans>
<TestPlanReference
reference = "container:ORKCatalogUITests/ORKCatalogUI.xctestplan"
default = "YES">
</TestPlanReference>
</TestPlans>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9622282924F04D780056E74D"
BuildableName = "ORKCatalogUITests.xctest"
BlueprintName = "ORKCatalogUITests"
ReferencedContainer = "container:ORKCatalog.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "51AF19762B59F1E400D3B399"
BuildableName = "ORKCatalogTests.xctest"
BlueprintName = "ORKCatalogTests"
ReferencedContainer = "container:ORKCatalog.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "869230BD1AAA890A00BFE11B"
BuildableName = "ORKCatalog.app"
BlueprintName = "ORKCatalog"
ReferencedContainer = "container:ORKCatalog.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "869230BD1AAA890A00BFE11B"
BuildableName = "ORKCatalog.app"
BlueprintName = "ORKCatalog"
ReferencedContainer = "container:ORKCatalog.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -126,5 +126,3 @@ extension ResultViewController: ResultProviderDelegate {
present(shareSheet, animated: true, completion: nil)
}
}
@@ -286,5 +286,3 @@ class TaskListViewController: UITableViewController, ORKTaskViewControllerDelega
}
}
@@ -0,0 +1,297 @@
/*
Copyright (c) 2015, Apple Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder(s) nor the names of any contributors
may be used to endorse or promote products derived from this software without
specific prior written permission. No license is granted to the trademarks of
the copyright holders even if such marks are included in this software.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import Foundation
import XCTest
class Helpers: XCTestCase {
let app = XCUIApplication()
let commonElements = CommonElements()
let taskScreen = TaskScreen()
enum SliderTesting {
case slider1
case slider2
case slider3
case slider4
case slider5
case slider6
}
enum SwipeDirection {
case up
case down
case left
case right
case skip
}
//Verify existence of any XCUIElement with an option to tap that element
func verifyElement(_ element: XCUIElement, _ tap: Bool = false) -> Bool {
if element.exists {
if tap && element.isEnabled {
element.tap()
}
return true
}
XCTFail("Unable to confirm \(element) exists")
return false
}
//Verify an element exists based on static text label, option to tap the element after verification
func verifyElementByText(identifier: String, _ tap: Bool = false) -> Bool {
let item = app.staticTexts["\(identifier)"]
if item.exists {
if tap && item.isEnabled {
item.tap()
}
return true
}
XCTFail("Unable to locate Element based on text \(identifier)")
return false
}
//Verify an element matching XCUIElement.ElementType exists based on an identifying string existing in the label or value of the element.
func verifyElementByType(_ elementType: XCUIElement.ElementType, _ identifier: String, _ tap: Bool = false) -> Bool {
let identifyingOptions = ["label", "value"]
for option in identifyingOptions {
let predicate = NSPredicate(format: "\(option) CONTAINS '\(identifier)'")
let target = app.descendants(matching: elementType).element(matching: predicate)
if target.exists {
if tap && target.isEnabled {
target.tap()
}
return true
}
}
XCTFail("Unable to locate Element based on text \(identifier)")
return false
}
//Verify any XCUIElement exists and return that element for assignment, option to tap available
func verifyAndAssign(_ element: XCUIElement, _ tap: Bool = false) -> XCUIElement? {
if element.exists {
if tap && element.isEnabled {
element.tap()
}
return element
}
XCTFail("Unable to confirm \(element) exists")
return nil
}
//Verify an element exists based on static text label, option to tap the element after verification. Element returned for assignment.
func verifyAndAssignByText(_ identifier: String, _ tap: Bool = false) -> XCUIElement? {
let item = app.staticTexts["\(identifier)"]
if item.exists {
if tap && item.isEnabled {
item.tap()
}
return item
}
XCTFail("Unable to locate Element based on text \(identifier)")
return nil
}
//Verify an element matching XCUIElement.ElementType exists based on an identifying string existing in the label or value of the element.
//Element returned for assignment.
func verifyAndAssignByType(_ elementType: XCUIElement.ElementType, _ identifier: String, _ tap: Bool = false) -> XCUIElement? {
let identifyingOptions = ["label", "value"]
for option in identifyingOptions {
let predicate = NSPredicate(format: "\(option) CONTAINS '\(identifier)'")
let target = app.descendants(matching: elementType).element(matching: predicate)
if target.exists {
if tap && target.isEnabled {
target.tap()
}
return target
}
}
XCTFail("Unable to locate Element based on text \(identifier)")
return nil
}
//Enter any ORKCatalog task based on the name of the task and leave without taking action
func launchAndLeave(_ task: String) -> Bool {
XCTAssert(verifyElement(taskScreen.mainTaskScreen))
let currentTask = verifyAndAssignByText(task)!
currentTask.tap()
sleep(1)
guard let cancelButton = commonElements.cancelButton else {
XCTFail("Unable to locate Cancel Button")
return false
}
cancelButton.tap()
sleep(1)
guard let exitButton = commonElements.getExitButton() else {
XCTFail("Unable to locate End Task or Discard Results button")
return false
}
exitButton.tap()
return verifyElement(taskScreen.mainTaskScreen)
}
//Deletes the ORKCatalog app from the simulator instance.
func deleteORKCatalog() {
app.terminate()
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let icon = springboard.icons["ORKCatalog"]
if icon.exists {
icon.press(forDuration: 1.3)
springboard.buttons["Delete App"].tap()
sleep(2)
springboard.buttons["Delete"].tap()
}
}
//Verify that the Navigation bar includes a page count in the form of "pageNumber of pageTotal"
func verifyPageByCount(_ pageNumber: Int, _ pageTotal: Int) -> Bool {
let pageCount = app.navigationBars["\(String(pageNumber)) of \(String(pageTotal))"]
XCTAssert(pageCount.exists)
return true
}
//Return an XCUIElement based on page number of the Scale test
func scaleTitle(_ pageNumber: Int) -> XCUIElement? {
let titles: [Int: String] = [1: "Discrete Scale", 2: "Continuous Scale", 3: "Discrete Vertical Scale", 4: "Continuous Vertical Scale", 5: "Text Scale", 6: "Text Vertical Scale"]
return app.scrollViews.otherElements.staticTexts[titles[pageNumber] ?? "Invalid Page Number"]
}
//Select and interact with sliders in the Scale Question task. Function verifies expected outputs of each slider.
func sliderValuesCheck(_ sliderScreen:SliderTesting, _ screenNum: Int, _ direction:SwipeDirection) -> Bool {
let sliderValues: [SliderTesting:String] = [
.slider1:"8",
.slider3:"8",
.slider4:"4.23",
.slider5:"Above Average",
.slider6:"Above Average"
]
let slider = verifyAndAssignByType(.slider, "Response slider")!
XCTAssert(slider.waitForExistence(timeout: 2), "Unable to locate slider on page \(screenNum)")
XCTAssert(verifyPageByCount(screenNum, 6), "Unable to locate \"\(screenNum) of 6\"")
XCTAssert(scaleTitle(screenNum)!.waitForExistence(timeout: 2), "Unable to locate \(scaleTitle(screenNum)!)")
switch direction {
case .up:
slider.swipeUp()
case .right:
slider.swipeRight()
case .left:
XCTFail("Unexpected Swipe Direction of Left Entered")
case .down:
XCTFail("Unexpected Swipe Direction of Down Entered")
case .skip:
XCTAssert(commonElements.skipButton!.waitForExistence(timeout: 2))
commonElements.skipButton!.tap()
}
if sliderScreen != .slider2 {
XCTAssert(verifyElementByType(.slider, sliderValues[sliderScreen]!))
if commonElements.doneButton!.exists {
commonElements.doneButton!.tap()
return true
}
XCTAssert(commonElements.nextButton!.waitForExistence(timeout: 2))
commonElements.nextButton!.tap()
return true
}
return true
}
//Returns true if sliders behave as expected on any given screen in the Scale Question task.
func sliderScreenCheck(_ sliderScreen:SliderTesting) -> Bool {
XCTAssert(verifyElement(app.scrollViews.otherElements.staticTexts["Scale"]))
XCTAssert(verifyElement(app.scrollViews.otherElements.staticTexts["Your question here."]))
switch sliderScreen {
case .slider1:
XCTAssert(sliderValuesCheck(sliderScreen, 1, .right), "Slider1 Values Check Failed")
case .slider2:
XCTAssert(sliderValuesCheck(sliderScreen, 2, .skip), "Slider2 Values Check Failed")
case .slider3:
XCTAssert(sliderValuesCheck(sliderScreen, 3, .up), "Slider3 Values Check Failed")
case .slider4:
XCTAssert(sliderValuesCheck(sliderScreen, 4, .up), "Slider4 Values Check Failed")
case .slider5:
XCTAssert(sliderValuesCheck(sliderScreen, 5, .right), "Slider5 Values Check Failed")
case .slider6:
XCTAssert(sliderValuesCheck(sliderScreen, 6, .up), "Slider6 Values Check Failed")
}
return true
}
//Monitors for any alerts that may appear and interupt the normal flow of the applicaiton.
func monitorAlerts() {
addUIInterruptionMonitor(withDescription: "Alert") { element in
do {
// Push Notification
let button = element.buttons["Allow"]
let title = element.staticTexts["“ORKCatalog” Would Like to Send You Notifications"]
if title.exists && button.exists {
button.tap()
return true
}
}
do {
// Location
let button = element.buttons["Allow While Using App"]
if button.exists {
button.tap()
return true
}
}
do {
// Microphone
let button = element.buttons["OK"]
let title = element.staticTexts["“ORKCatalog” Would Like to Access the Microphone"]
if title.exists && button.exists {
button.tap()
return true
}
}
return false
}
}
func verifyElementByText(_ text: String, _ tap: Bool = false) -> Bool {
let item = app.staticTexts["\(text)"]
XCTAssert(item.waitForExistence(timeout: 3))
if tap && item.isEnabled {
item.tap()
}
return true
}
}
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
@@ -0,0 +1,46 @@
{
"configurations" : [
{
"id" : "CE2DCF31-672A-4F3F-A538-AF0ADA4806F4",
"name" : "Configuration 1",
"options" : {
}
}
],
"defaultOptions" : {
"codeCoverage" : false,
"targetForVariableExpansion" : {
"containerPath" : "container:ORKCatalog.xcodeproj",
"identifier" : "869230BD1AAA890A00BFE11B",
"name" : "ORKCatalog"
}
},
"testTargets" : [
{
"selectedTests" : [
"PreSubmissionTests\/testAccessActiveTasks()",
"PreSubmissionTests\/testAccessSurveyQuestions()",
"PreSubmissionTests\/testAccessSurveyTasks()",
"PreSubmissionTests\/testImageMultipleChoice()",
"PreSubmissionTests\/testSQPickerWheel()",
"PreSubmissionTests\/testSQSliders()",
"PreSubmissionTests\/testSQTextEntry()",
"PreSubmissionTests\/testWrittenMultipleChoice()"
],
"target" : {
"containerPath" : "container:ORKCatalog.xcodeproj",
"identifier" : "9622282924F04D780056E74D",
"name" : "ORKCatalogUITests"
}
},
{
"target" : {
"containerPath" : "container:ORKCatalog.xcodeproj",
"identifier" : "51AF19762B59F1E400D3B399",
"name" : "ORKCatalogTests"
}
}
],
"version" : 1
}
@@ -0,0 +1,237 @@
/*
Copyright (c) 2015, Apple Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder(s) nor the names of any contributors
may be used to endorse or promote products derived from this software without
specific prior written permission. No license is granted to the trademarks of
the copyright holders even if such marks are included in this software.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import XCTest
class PreSubmissionTests: XCTestCase {
let app = XCUIApplication()
let commonElements = CommonElements()
let allowScreens = AllowScreens()
let helpers = Helpers()
let taskScreen = TaskScreen()
override func setUpWithError() throws {
continueAfterFailure = false
helpers.monitorAlerts()
app.launch()
}
override func tearDownWithError() throws {
}
func testAccessSurveyTasks() throws {
XCTAssert(helpers.verifyElement(taskScreen.mainTaskScreen))
for task in taskScreen.surveyTasks {
XCTAssert(helpers.launchAndLeave(task))
}
}
func testAccessSurveyQuestions() throws {
XCTAssert(allowScreens.triggerAllowScreens())
for task in taskScreen.surveyQuestions {
XCTAssert(helpers.launchAndLeave(task))
}
return
}
func testAccessActiveTasks() throws {
XCTAssert(helpers.verifyElement(taskScreen.mainTaskScreen))
for task in taskScreen.activeTasks {
XCTAssert(helpers.launchAndLeave(task))
}
return
}
func testWrittenMultipleChoice() throws {
XCTAssert(helpers.verifyElement(taskScreen.mainTaskScreen))
let options = ["Choice 1", "Choice 2", "Choice 3", "Other"]
let required = ["Text Choice", "Additional text can go here.", "Your question here."]
XCTAssert(helpers.verifyElementByText("Text Choice Question", true))
for item in required {
XCTAssert(app.tables.staticTexts[item].exists, "Unable to locate the \(item) element.")
}
XCTAssert(helpers.verifyElementByText(options.randomElement()!, true))
XCTAssert(helpers.verifyElementByType(.button, "Done", true))
return
}
func testImageMultipleChoice() throws {
XCTAssert(helpers.verifyElement(taskScreen.mainTaskScreen))
XCTAssert(helpers.verifyElementByText("Image Choice Question", true))
let required = ["Image Choice", "Additional text can go here."]
for item in required {
XCTAssert(helpers.verifyElementByText(item))
}
let square = helpers.verifyAndAssignByType(.button, "Square Shape")!
let circle = helpers.verifyAndAssignByType(.button, "Round Shape")!
square.tap()
XCTAssert(helpers.verifyElementByType(.button, "Next", true))
XCTAssert(helpers.verifyPageByCount(2, 2))
XCTAssert(helpers.verifyElementByType(.button, "Back", true))
XCTAssert(helpers.verifyPageByCount(1, 2))
square.tap()
circle.tap()
XCTAssert(helpers.verifyElementByType(.button, "Next", true))
XCTAssert(helpers.verifyPageByCount(2, 2))
circle.tap()
XCTAssert(helpers.verifyElementByType(.button, "Done", true))
XCTAssert(helpers.verifyElement(taskScreen.mainTaskScreen))
return
}
func testSQPickerWheel() throws {
XCTAssert(helpers.verifyElement(taskScreen.mainTaskScreen))
let dt = helpers.verifyAndAssignByText("Date and Time Question")!
let elementsQuery = app.scrollViews.otherElements.staticTexts
dt.tap()
XCTAssert(elementsQuery["Date and Time"].exists)
XCTAssert(elementsQuery["Additional text can go here."].exists)
XCTAssert(elementsQuery["Your question here."].exists)
let skip = helpers.verifyAndAssignByText("Skip")!
XCTAssert(skip.isEnabled)
let done = helpers.verifyAndAssignByType(.button, "Done")!
XCTAssert(done.isEnabled)
let now = Date()
let formatter = DateFormatter()
formatter.setLocalizedDateFormatFromTemplate("a")
let datetime = formatter.string(from: now)
formatter.setLocalizedDateFormatFromTemplate("MMM dd")
let newDate = Calendar.current.date(byAdding: .day, value: 5, to: now)
let newDateString = formatter.string(from: newDate!)
let firstPredicate = NSPredicate(format: "value BEGINSWITH 'Today'")
let firstPicker = app.pickerWheels.element(matching: firstPredicate)
XCTAssert(firstPicker.isEnabled)
firstPicker.adjust(toPickerWheelValue: newDateString)
let secondPicker = helpers.verifyAndAssignByType(.pickerWheel, "clock")!
XCTAssert((secondPicker.isEnabled))
secondPicker.adjust(toPickerWheelValue: "5")
let thirdPicker = helpers.verifyAndAssignByType(.pickerWheel, "minute")!
XCTAssert(thirdPicker.isEnabled)
thirdPicker.adjust(toPickerWheelValue: "23")
let fourthPredicate = NSPredicate(format: "value CONTAINS '\(datetime)'")
let fourthPicker = app.pickerWheels.element(matching: fourthPredicate)
XCTAssert(fourthPicker.isEnabled)
datetime == "AM" ? fourthPicker.adjust(toPickerWheelValue: "PM") : fourthPicker.adjust(toPickerWheelValue: "AM")
XCTAssert(done.isEnabled)
done.tap()
XCTAssert(helpers.verifyElement(taskScreen.mainTaskScreen))
dt.tap()
skip.tap()
XCTAssert(helpers.verifyElement(taskScreen.mainTaskScreen))
return
}
func testSQSliders() throws {
XCTAssert(helpers.verifyElementByText("Scale Question", true))
XCTAssert(helpers.sliderScreenCheck(.slider1))
XCTAssert(helpers.sliderScreenCheck(.slider2))
XCTAssert(helpers.sliderScreenCheck(.slider3))
XCTAssert(helpers.sliderScreenCheck(.slider4))
XCTAssert(helpers.sliderScreenCheck(.slider5))
XCTAssert(helpers.sliderScreenCheck(.slider6))
XCTAssert(helpers.sliderScreenCheck(.slider1))
XCTAssert(helpers.sliderScreenCheck(.slider2))
XCTAssert(helpers.sliderScreenCheck(.slider3))
XCTAssert(helpers.sliderScreenCheck(.slider4))
XCTAssert(helpers.sliderScreenCheck(.slider5))
XCTAssert(helpers.sliderScreenCheck(.slider6))
XCTAssert(taskScreen.mainTaskScreen.waitForExistence(timeout: 5))
return
}
func testSQTextEntry() throws {
let testString = "The wonderful thing about tiggers is tiggers are wonderful things! Their tops are made out of rubber, their bottoms are made out of springs!"
XCTAssert(helpers.verifyElement(taskScreen.mainTaskScreen))
XCTAssert(helpers.verifyElementByText("Text Question", true))
guard let done = commonElements.doneButton else {
XCTFail("Unable to locate done button")
return
}
XCTAssertFalse(done.isEnabled)
XCTAssert(helpers.verifyElementByText("Text"))
XCTAssert(helpers.verifyElementByText("Additional text can go here."))
let textView = app.textViews.element(boundBy: 0)
XCTAssert(textView.waitForExistence(timeout: 5))
textView.tap()
app.typeText(testString)
XCTAssert(helpers.verifyElementByText("140/280"))
let clear = app.buttons["Clear"]
XCTAssert(clear.waitForExistence(timeout: 3))
clear.tap()
XCTAssert(helpers.verifyElementByText("0/280"))
app.typeText(testString)
XCTAssert(commonElements.doneButton!.firstMatch.exists)
commonElements.doneButton!.firstMatch.tap()
done.tap()
XCTAssert(helpers.verifyElement(taskScreen.mainTaskScreen))
return
}
}
@@ -0,0 +1,91 @@
/*
Copyright (c) 2015, Apple Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder(s) nor the names of any contributors
may be used to endorse or promote products derived from this software without
specific prior written permission. No license is granted to the trademarks of
the copyright holders even if such marks are included in this software.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import Foundation
import XCTest
struct AllowScreens {
let app = XCUIApplication()
let commonElements = CommonElements()
let taskScreen = TaskScreen()
var healthAccessScreen: XCUIElement {
app.navigationBars.staticTexts["Health Access"]
}
var locationAccessAlert: XCUIElement {
app.alerts["Allow “ORKCatalog” to use your location?"]
}
func allowHealthAccess() {
if healthAccessScreen.exists {
app.tables.staticTexts["Turn All Categories On"].tap()
sleep(1)
app.navigationBars["Health Access"].buttons["Allow"].tap()
}
}
func allowLocationServices() {
if locationAccessAlert.exists {
locationAccessAlert.scrollViews.otherElements.buttons["Allow While Using App"].tap()
}
}
func triggerAllowScreens() -> Bool {
let healthTriggers = ["Height Question", "Weight Question"]
XCTAssert(commonElements.verifyElement(taskScreen.mainTaskScreen))
for task in healthTriggers {
let healthTask = app.tables.staticTexts[task]
healthTask.tap()
sleep(3)
allowHealthAccess()
sleep(2)
guard let cancelButton = commonElements.cancelButton else {
XCTFail("Unable to locate Cancel Button")
return false
}
cancelButton.tap()
sleep(1)
guard let exitButton = commonElements.getExitButton() else {
XCTFail("Unable to locate End Task or Discard Results button")
return false
}
exitButton.tap()
XCTAssert(taskScreen.mainTaskScreen.exists)
}
return true
}
}
@@ -0,0 +1,80 @@
/*
Copyright (c) 2015, Apple Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder(s) nor the names of any contributors
may be used to endorse or promote products derived from this software without
specific prior written permission. No license is granted to the trademarks of
the copyright holders even if such marks are included in this software.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import Foundation
import XCTest
struct CommonElements {
let app = XCUIApplication()
var cancelButton: XCUIElement? {
app.navigationBars.buttons["Cancel"]
}
var exitTaskButton: XCUIElement {
getExitButton()!
}
var doneButton: XCUIElement? {
app.buttons["Done"]
}
var nextButton: XCUIElement? {
app.scrollViews.otherElements.staticTexts["Next"]
}
var backButton: XCUIElement? {
app.navigationBars.buttons["Back"]
}
var skipButton: XCUIElement? {
app.scrollViews.otherElements.staticTexts["Skip"]
}
func getExitButton() -> XCUIElement? {
let otherElements = app.sheets.scrollViews.otherElements
if otherElements.buttons["Discard Results"].exists {
return otherElements.buttons["Discard Results"]
}
if otherElements.buttons["End Task"].exists {
return otherElements.buttons["End Task"]
}
return nil
}
func verifyElement(_ element: XCUIElement) -> Bool {
if element.exists {
return true
}
XCTFail("Unable to confrim \(element) exists")
return false
}
}
@@ -0,0 +1,103 @@
/*
Copyright (c) 2015, Apple Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder(s) nor the names of any contributors
may be used to endorse or promote products derived from this software without
specific prior written permission. No license is granted to the trademarks of
the copyright holders even if such marks are included in this software.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import Foundation
import XCTest
struct TaskScreen {
let app = XCUIApplication()
var surveyTasks = [
"Form Survey Example",
"Grouped Form Survey Example",
"Simple Survey Example"
]
var surveyQuestions = [
"Boolean Question",
"Custom Boolean Question",
"Date Question",
"Date and Time Question",
"Height Question",
"Weight Question",
"Image Choice Question",
"Location Question",
"Numeric Question",
"Scale Question",
"Text Question",
"Text Choice Question",
"Time Interval Question",
"Value Picker Choice Question",
"Validated Text Question",
"Image Capture Step",
"Video Capture Step",
"Front Facing Camera Step",
"Wait Step",
"PDF Viewer Step",
"Request Permissions Step"
]
var activeTasks: [String] {
var tasks = [
"Audio",
"Amsler Grid",
"Fitness Check",
"Hole Peg Test",
"PSAT",
"Reaction Time",
"Short Walk",
"Spatial Span Memory",
"Speech Recognition",
"Speech in Noise",
"Stroop",
"Timed Walk with Turn Around",
"Tone Audiometry",
"dBHL Tone Audiometry",
//"Environment SPL Meter",
"Tower of Hanoi",
"Tremor Test",
"Two Finger Tapping Interval",
"Walk Back and Forth",
"Knee Range of Motion",
"Shoulder Range of Motion",
"Trail Making Test"
]
return tasks
}
var mainTaskScreen: XCUIElement {
app.navigationBars["ORKCatalog"].staticTexts["ORKCatalog"]
}
func getCurrentTask(task: String) -> XCUIElement? {
return app.tables.staticTexts[task]
}
}
@@ -4,7 +4,7 @@
// Copyright © 2021 researchkit.org. All rights reserved.
//
ORK_CATALOG_VERSION_NUMBER = 3.1.1
ORK_CATALOG_VERSION_NUMBER = 3.1
ORK_CATALOG_BUILD_NUMBER = $(ORK_CATALOG_BUILD_NUMBER_CI_$(CI)) // ORK_CATALOG_BUILD_NUMBER_CI_TRUE or ORK_CATALOG_BUILD_NUMBER_CI_
ORK_CATALOG_BUILD_NUMBER_CI_TRUE = $(CI_BUILD_NUMBER)