diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0086c1bb..a9410b55 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,6 @@ jobs: - name: Validate podspec run: pod lib lint - build_macos14: runs-on: macos-14 strategy: @@ -23,7 +22,9 @@ jobs: - { xcode_version: '14.3.1', simulator: 'name=iPad Air (5th generation),OS=16.4', run_extra_validations: 'false' } - { xcode_version: '14.3.1', simulator: 'name=iPhone 14,OS=16.4', run_extra_validations: 'false' } - { xcode_version: '15.4', simulator: 'name=iPad Air (5th generation),OS=17.5', run_extra_validations: 'false' } - - { xcode_version: '15.4', simulator: 'name=iPhone 15,OS=17.5', run_extra_validations: 'true' } + - { xcode_version: '15.4', simulator: 'name=iPhone 15,OS=17.5', run_extra_validations: 'false' } + - { xcode_version: '16.0', simulator: 'name=iPad Air (5th generation),OS=18.0', run_extra_validations: 'false' } + - { xcode_version: '16.0', simulator: 'name=iPhone 16,OS=18.0', run_extra_validations: 'true' } steps: - name: Checkout Project uses: actions/checkout@v1 diff --git a/Sources/KIF/Additions/CALayer-KIFAdditions.m b/Sources/KIF/Additions/CALayer-KIFAdditions.m index ddc88308..9ea6b2f3 100644 --- a/Sources/KIF/Additions/CALayer-KIFAdditions.m +++ b/Sources/KIF/Additions/CALayer-KIFAdditions.m @@ -41,12 +41,32 @@ - (BOOL)hasAnimations [layer.animationKeys enumerateObjectsUsingBlock:^(NSString *animationKey, NSUInteger idx, BOOL *innerStop) { CAAnimation *animation = [layer animationForKey:animationKey]; - double beginTime = [animation beginTime]; + double completionTime = [animation KIF_completionTime]; - // Ignore long running animations (> 1 minute duration) - if (currentTime >= beginTime && completionTime < currentTime + 60 && currentTime < completionTime) { + // Ignore long running animations (> 1 minute duration), as we don't want to wait on them + if (completionTime > currentTime + 60) { + return; + } + + // If an animation is set to be removed on completion, it must still be in progress if we enumerated it + // This is the default behavior for animations, so we should often hit this codepath. + if ([animation isRemovedOnCompletion]) { result = YES; + } else if ([animation.delegate isKindOfClass:NSClassFromString(@"UIViewAnimationState")]) { + // Use a private property on the private class to determine if the animation state has completed + BOOL animationDidStopSent = [[(NSObject *)animation.delegate valueForKey:@"_animationDidStopSent"] boolValue]; + + if (!animationDidStopSent) { + result = YES; + } + } else if (currentTime > completionTime) { + // Otherwise, use the completion time to determine if the animation has been completed. + // This doesn't seem to always be exactly right however. + result = YES; + } + + if (result) { *innerStop = YES; *stop = YES; } diff --git a/Sources/KIF/Additions/NSObject+KIFSwizzle.m b/Sources/KIF/Additions/NSObject+KIFSwizzle.m index dfcbd978..62d900da 100644 --- a/Sources/KIF/Additions/NSObject+KIFSwizzle.m +++ b/Sources/KIF/Additions/NSObject+KIFSwizzle.m @@ -17,6 +17,9 @@ + (void)swizzleSEL:(SEL)originalSEL withSEL:(SEL)swizzledSEL Method originalMethod = class_getInstanceMethod(class, originalSEL); Method swizzledMethod = class_getInstanceMethod(class, swizzledSEL); + NSAssert(originalMethod != nil, @"The original method for selector '%@' couldn't be found", NSStringFromSelector(originalSEL)); + NSAssert(swizzledMethod != nil, @"The swizzled method for selector '%@' couldn't be found", NSStringFromSelector(swizzledSEL)); + method_exchangeImplementations(originalMethod, swizzledMethod); } diff --git a/Sources/KIF/Additions/UIWindow+KIFSwizzle.m b/Sources/KIF/Additions/UIWindow+KIFSwizzle.m index 9273fae6..919012b5 100644 --- a/Sources/KIF/Additions/UIWindow+KIFSwizzle.m +++ b/Sources/KIF/Additions/UIWindow+KIFSwizzle.m @@ -9,23 +9,31 @@ #import "UIApplication-KIFAdditions.h" #import "NSObject+KIFSwizzle.h" +@interface UIWindow () + +- (instancetype)_initWithFrame:(CGRect)rect debugName:(NSString *)debugName windowScene:(UIWindowScene *)windowScene API_AVAILABLE(ios(13)); + +@end + + @implementation UIWindow (KIFSwizzle) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - [self swizzleSEL:@selector(init) withSEL:@selector(swizzle_init)]; - [self swizzleSEL:@selector(becomeKeyWindow) withSEL:@selector(swizzle_becomeKeyWindow)]; - if (@available(iOS 13.0, *)) { - [self swizzleSEL:@selector(initWithWindowScene:) withSEL:@selector(swizzle_initWithWindowScene:)]; + if (@available(iOS 13, *)) { + [self swizzleSEL:@selector(_initWithFrame:debugName:windowScene:) withSEL:@selector(swizzle__initWithFrame:debugName:windowScene:)]; + } else { + [self swizzleSEL:@selector(init) withSEL:@selector(swizzle_init)]; } + [self swizzleSEL:@selector(becomeKeyWindow) withSEL:@selector(swizzle_becomeKeyWindow)]; }); } -- (instancetype)swizzle_initWithWindowScene:(UIWindowScene *)scene API_AVAILABLE(ios(13)) +- (instancetype)swizzle__initWithFrame:(CGRect)rect debugName:(NSString *)debugName windowScene:(UIWindowScene *)windowScene API_AVAILABLE(ios(13)) { - UIWindow *window = [self swizzle_initWithWindowScene:scene]; + UIWindow *window = [self swizzle__initWithFrame:rect debugName:debugName windowScene:windowScene]; window.layer.speed = [UIApplication sharedApplication].animationSpeed; return window; diff --git a/Sources/KIF/Classes/KIFUITestActor.m b/Sources/KIF/Classes/KIFUITestActor.m index cbabd652..6a1f8ea1 100644 --- a/Sources/KIF/Classes/KIFUITestActor.m +++ b/Sources/KIF/Classes/KIFUITestActor.m @@ -230,9 +230,8 @@ - (void)waitForAnimationsToFinishWithTimeout:(NSTimeInterval)timeout stabilizati [self waitForTimeInterval:maximumWaitingTimeInterval relativeToAnimationSpeed:YES]; } } else { - // Wait for the view to stabilize and give them a chance to start animations before we wait for them. - [self waitForTimeInterval:stabilizationTime relativeToAnimationSpeed:YES]; + [self waitForTimeInterval:MAX(stabilizationTime / [UIApplication sharedApplication].animationSpeed, 0.25) relativeToAnimationSpeed:NO]; maximumWaitingTimeInterval -= stabilizationTime; NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate]; diff --git a/Tests/AccessibilityIdentifierTests.m b/Tests/AccessibilityIdentifierTests.m index b3ff0432..b94becbf 100644 --- a/Tests/AccessibilityIdentifierTests.m +++ b/Tests/AccessibilityIdentifierTests.m @@ -49,14 +49,14 @@ - (void)testWaitingForAbscenceOfViewWithAccessibilityIdentifier - (void)testLongPressingViewWithAccessibilityIdentifier { [tester tapViewWithAccessibilityIdentifier:@"idGreeting"]; - [tester longPressViewWithAccessibilityIdentifier:@"idGreeting" duration:2]; + [tester longPressViewWithAccessibilityIdentifier:@"idGreeting" duration:1]; [tester tapViewWithAccessibilityLabel:@"Select All"]; } - (void)testEnteringTextIntoViewWithAccessibilityIdentifier { [tester tapViewWithAccessibilityIdentifier:@"idGreeting"]; - [tester longPressViewWithAccessibilityIdentifier:@"idGreeting" duration:2]; + [tester longPressViewWithAccessibilityIdentifier:@"idGreeting" duration:1]; [tester waitForAnimationsToFinish]; [tester tapViewWithAccessibilityLabel:@"Select All"]; [tester tapViewWithAccessibilityLabel:@"Cut"]; @@ -77,7 +77,7 @@ - (void)testClearingAndEnteringTextIntoViewWithAccessibilityLabel - (void)testSettingTextIntoViewWithAccessibilityIdentifier { UIView *greetingView = [tester waitForViewWithAccessibilityIdentifier:@"idGreeting"]; - [tester longPressViewWithAccessibilityIdentifier:@"idGreeting" duration:2]; + [tester longPressViewWithAccessibilityIdentifier:@"idGreeting" duration:1]; [tester setText:@"Yo" intoViewWithAccessibilityIdentifier:@"idGreeting"]; [tester expectView:greetingView toContainText:@"Yo"]; [tester setText:@"Hello" intoViewWithAccessibilityIdentifier:@"idGreeting"]; diff --git a/Tests/AccessibilityIdentifierTests_ViewTestActor.m b/Tests/AccessibilityIdentifierTests_ViewTestActor.m index ce96ff3a..240253bf 100644 --- a/Tests/AccessibilityIdentifierTests_ViewTestActor.m +++ b/Tests/AccessibilityIdentifierTests_ViewTestActor.m @@ -49,14 +49,14 @@ - (void)testWaitingForAbscenceOfViewWithAccessibilityIdentifier - (void)testLongPressingViewWithAccessibilityIdentifier { [[viewTester usingIdentifier:@"idGreeting"] tap]; - [[viewTester usingIdentifier:@"idGreeting"] longPressWithDuration:2]; + [[viewTester usingIdentifier:@"idGreeting"] longPressWithDuration:1]; [[viewTester usingLabel:@"Select All"] tap]; } - (void)testEnteringTextIntoViewWithAccessibilityIdentifier { [[viewTester usingIdentifier:@"idGreeting"] tap]; - [[viewTester usingIdentifier:@"idGreeting"] longPressWithDuration:2]; + [[viewTester usingIdentifier:@"idGreeting"] longPressWithDuration:1]; [[viewTester usingLabel:@"Select All"] tap]; [[viewTester usingLabel:@"Cut"] tap]; [[viewTester usingIdentifier:@"idGreeting"] enterText:@"Yo"]; diff --git a/Tests/LongPressTests.m b/Tests/LongPressTests.m index d8e3f8d6..08f8449e 100644 --- a/Tests/LongPressTests.m +++ b/Tests/LongPressTests.m @@ -28,21 +28,21 @@ - (void)afterEach - (void)testLongPressingViewWithAccessibilityLabel { [tester tapViewWithAccessibilityLabel:@"Greeting"]; - [tester longPressViewWithAccessibilityLabel:@"Greeting" duration:2]; + [tester longPressViewWithAccessibilityLabel:@"Greeting" duration:1]; [tester tapViewWithAccessibilityLabel:@"Select All"]; } - (void)testLongPressingViewViewWithTraits { [tester tapViewWithAccessibilityLabel:@"Greeting"]; - [tester longPressViewWithAccessibilityLabel:@"Greeting" value:@"Hello" duration:2]; + [tester longPressViewWithAccessibilityLabel:@"Greeting" value:@"Hello" duration:1]; [tester tapViewWithAccessibilityLabel:@"Select All"]; } - (void)testLongPressingViewViewWithValue { [tester tapViewWithAccessibilityLabel:@"Greeting"]; - [tester longPressViewWithAccessibilityLabel:@"Greeting" value:@"Hello" traits:UIAccessibilityTraitUpdatesFrequently duration:2]; + [tester longPressViewWithAccessibilityLabel:@"Greeting" value:@"Hello" traits:UIAccessibilityTraitUpdatesFrequently duration:1]; [tester tapViewWithAccessibilityLabel:@"Select All"]; } diff --git a/Tests/LongPressTests_ViewTestActor.m b/Tests/LongPressTests_ViewTestActor.m index d472cd38..33461478 100644 --- a/Tests/LongPressTests_ViewTestActor.m +++ b/Tests/LongPressTests_ViewTestActor.m @@ -28,21 +28,21 @@ - (void)afterEach - (void)testLongPressingViewWithAccessibilityLabel { [[viewTester usingLabel:@"Greeting"] tap]; - [[viewTester usingLabel:@"Greeting"] longPressWithDuration:2]; + [[viewTester usingLabel:@"Greeting"] longPressWithDuration:1]; [[viewTester usingLabel:@"Select All"] tap]; } - (void)testLongPressingViewViewWithTraits { [[viewTester usingLabel:@"Greeting"] tap]; - [[[viewTester usingLabel:@"Greeting"] usingValue:@"Hello"] longPressWithDuration:2]; + [[[viewTester usingLabel:@"Greeting"] usingValue:@"Hello"] longPressWithDuration:1]; [[viewTester usingLabel:@"Select All"] tap]; } - (void)testLongPressingViewViewWithValue { [[viewTester usingLabel:@"Greeting"] tap]; - [[[[viewTester usingLabel:@"Greeting"] usingValue:@"Hello"] usingTraits:UIAccessibilityTraitUpdatesFrequently] longPressWithDuration:2]; + [[[[viewTester usingLabel:@"Greeting"] usingValue:@"Hello"] usingTraits:UIAccessibilityTraitUpdatesFrequently] longPressWithDuration:1]; [[viewTester usingLabel:@"Select All"] tap]; } diff --git a/Tests/TypingTests.m b/Tests/TypingTests.m index 2b9f4b25..d1681c02 100644 --- a/Tests/TypingTests.m +++ b/Tests/TypingTests.m @@ -39,7 +39,7 @@ - (void)testMissingFirstResponder - (void)testEnteringTextIntoFirstResponder { [tester tapViewWithAccessibilityLabel:@"Greeting"]; - [tester longPressViewWithAccessibilityLabel:@"Greeting" value:@"Hello" duration:2]; + [tester longPressViewWithAccessibilityLabel:@"Greeting" value:@"Hello" duration:1]; [tester tapViewWithAccessibilityLabel:@"Select All"]; [tester enterTextIntoCurrentFirstResponder:@"Yo"]; [tester waitForViewWithAccessibilityLabel:@"Greeting" value:@"Yo" traits:UIAccessibilityTraitNone]; @@ -53,7 +53,7 @@ - (void)testFailingToEnterTextIntoFirstResponder - (void)testEnteringTextIntoViewWithAccessibilityLabel { [tester tapViewWithAccessibilityLabel:@"Greeting"]; - [tester longPressViewWithAccessibilityLabel:@"Greeting" value:@"Hello" duration:2]; + [tester longPressViewWithAccessibilityLabel:@"Greeting" value:@"Hello" duration:1]; [tester tapViewWithAccessibilityLabel:@"Select All"]; [tester tapViewWithAccessibilityLabel:@"Cut"]; [tester enterText:@"Yo" intoViewWithAccessibilityLabel:@"Greeting"]; @@ -110,7 +110,7 @@ - (void)testClearingALongTextField - (void)testSettingTextIntoViewWithAccessibilityLabel { UIView *greetingView = [tester waitForViewWithAccessibilityLabel:@"Greeting"]; - [tester longPressViewWithAccessibilityLabel:@"Greeting" duration:2]; + [tester longPressViewWithAccessibilityLabel:@"Greeting" duration:1]; [tester setText:@"Yo" intoViewWithAccessibilityLabel:@"Greeting"]; [tester expectView:greetingView toContainText:@"Yo"]; [tester setText:@"Hello" intoViewWithAccessibilityLabel:@"Greeting"]; diff --git a/Tests/TypingTests_ViewTestActor.m b/Tests/TypingTests_ViewTestActor.m index a907ecda..e5ef5f8b 100644 --- a/Tests/TypingTests_ViewTestActor.m +++ b/Tests/TypingTests_ViewTestActor.m @@ -40,7 +40,7 @@ - (void)testMissingFirstResponder - (void)testEnteringTextIntoFirstResponder { [tester tapViewWithAccessibilityLabel:@"Greeting"]; - [[[viewTester usingLabel:@"Greeting"] usingValue:@"Hello"] longPressWithDuration:2]; + [[[viewTester usingLabel:@"Greeting"] usingValue:@"Hello"] longPressWithDuration:1]; [[viewTester usingLabel:@"Select All"] tap]; [[viewTester usingFirstResponder] enterText:@"Yo"]; [[[viewTester usingLabel:@"Greeting"] usingValue:@"Yo"] waitForView]; @@ -54,7 +54,7 @@ - (void)testFailingToEnterTextIntoFirstResponder - (void)testEnteringTextIntoViewWithAccessibilityLabel { [tester tapViewWithAccessibilityLabel:@"Greeting"]; - [[[viewTester usingLabel:@"Greeting"] usingValue:@"Hello"] longPressWithDuration:2]; + [[[viewTester usingLabel:@"Greeting"] usingValue:@"Hello"] longPressWithDuration:1]; [[viewTester usingLabel:@"Select All"] tap]; [[viewTester usingLabel:@"Cut"] tap]; [[viewTester usingLabel:@"Greeting"] enterText:@"Yo"];