From 6284e36849aa38f274e392c0c91740633f4057f1 Mon Sep 17 00:00:00 2001 From: Sergey Ilyevsky Date: Sun, 4 Jun 2017 09:58:28 +0300 Subject: [PATCH 1/9] Added animatedDisplayLink idling resource --- detox/ios/Detox.xcodeproj/project.pbxproj | 8 ++ detox/ios/Detox/ReactNativeHeaders.h | 2 + detox/ios/Detox/ReactNativeSupport.m | 5 +- .../WXAnimatedDisplayLinkIdlingResource.h | 14 +++ .../WXAnimatedDisplayLinkIdlingResource.m | 34 ++++++ detox/test/e2e/l-animations.js | 11 ++ detox/test/index.ios.js | 1 + detox/test/package.json | 7 +- detox/test/src/Screens/AnimationsScreen.js | 110 ++++++++++++++++++ detox/test/src/Screens/index.js | 4 +- 10 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 detox/ios/Detox/WXAnimatedDisplayLinkIdlingResource.h create mode 100644 detox/ios/Detox/WXAnimatedDisplayLinkIdlingResource.m create mode 100644 detox/test/e2e/l-animations.js create mode 100644 detox/test/src/Screens/AnimationsScreen.js diff --git a/detox/ios/Detox.xcodeproj/project.pbxproj b/detox/ios/Detox.xcodeproj/project.pbxproj index 45226f5050..756c44e94c 100644 --- a/detox/ios/Detox.xcodeproj/project.pbxproj +++ b/detox/ios/Detox.xcodeproj/project.pbxproj @@ -72,6 +72,8 @@ 39CEFCDB1E34E91B00A09124 /* DetoxUserNotificationDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CEFCDA1E34E91B00A09124 /* DetoxUserNotificationDispatcher.swift */; }; 468731A51E6C6D0500F151BE /* EarlGrey+Detox.h in Headers */ = {isa = PBXBuildFile; fileRef = 468731A31E6C6D0500F151BE /* EarlGrey+Detox.h */; }; 468731A61E6C6D0500F151BE /* EarlGrey+Detox.m in Sources */ = {isa = PBXBuildFile; fileRef = 468731A41E6C6D0500F151BE /* EarlGrey+Detox.m */; }; + A7F76A151ED33DE500FFE77E /* WXAnimatedDisplayLinkIdlingResource.h in Headers */ = {isa = PBXBuildFile; fileRef = A7F76A131ED33DE500FFE77E /* WXAnimatedDisplayLinkIdlingResource.h */; }; + A7F76A161ED33DE500FFE77E /* WXAnimatedDisplayLinkIdlingResource.m in Sources */ = {isa = PBXBuildFile; fileRef = A7F76A141ED33DE500FFE77E /* WXAnimatedDisplayLinkIdlingResource.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -217,6 +219,8 @@ 39CEFCDA1E34E91B00A09124 /* DetoxUserNotificationDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetoxUserNotificationDispatcher.swift; sourceTree = ""; }; 468731A31E6C6D0500F151BE /* EarlGrey+Detox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "EarlGrey+Detox.h"; sourceTree = ""; }; 468731A41E6C6D0500F151BE /* EarlGrey+Detox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "EarlGrey+Detox.m"; sourceTree = ""; }; + A7F76A131ED33DE500FFE77E /* WXAnimatedDisplayLinkIdlingResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WXAnimatedDisplayLinkIdlingResource.h; sourceTree = ""; }; + A7F76A141ED33DE500FFE77E /* WXAnimatedDisplayLinkIdlingResource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WXAnimatedDisplayLinkIdlingResource.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -353,6 +357,8 @@ 394767CA1DBF98D900D72256 /* WXJSTimerObservationIdlingResource.m */, 394767CB1DBF98D900D72256 /* WXRunLoopIdlingResource.h */, 394767CC1DBF98D900D72256 /* WXRunLoopIdlingResource.m */, + A7F76A131ED33DE500FFE77E /* WXAnimatedDisplayLinkIdlingResource.h */, + A7F76A141ED33DE500FFE77E /* WXAnimatedDisplayLinkIdlingResource.m */, ); name = ReactNativeSupport; sourceTree = ""; @@ -400,6 +406,7 @@ 391FA5E91E7FD96D0056F82F /* GREYIdlingResourcePrettyPrint.h in Headers */, 39A34C711E30F10D00BEBB59 /* DetoxAppDelegateProxy.h in Headers */, 397EC9B51E7EDE0B00D5F2BB /* EarlGreyStatistics.h in Headers */, + A7F76A151ED33DE500FFE77E /* WXAnimatedDisplayLinkIdlingResource.h in Headers */, 394767AE1DBF987E00D72256 /* DetoxManager.h in Headers */, 468731A51E6C6D0500F151BE /* EarlGrey+Detox.h in Headers */, 394767CD1DBF98D900D72256 /* ReactNativeHeaders.h in Headers */, @@ -631,6 +638,7 @@ 394767B51DBF987E00D72256 /* TestRunner.m in Sources */, 394767D31DBF98D900D72256 /* WXJSTimerObservationIdlingResource.m in Sources */, 394767C01DBF98A700D72256 /* GREYCondition+Detox.m in Sources */, + A7F76A161ED33DE500FFE77E /* WXAnimatedDisplayLinkIdlingResource.m in Sources */, 394767D11DBF98D900D72256 /* WXJSDisplayLinkIdlingResource.m in Sources */, 39A34C721E30F10D00BEBB59 /* DetoxAppDelegateProxy.m in Sources */, 394767B11DBF987E00D72256 /* MethodInvocation.m in Sources */, diff --git a/detox/ios/Detox/ReactNativeHeaders.h b/detox/ios/Detox/ReactNativeHeaders.h index 37abfb8968..e47c0f014d 100644 --- a/detox/ios/Detox/ReactNativeHeaders.h +++ b/detox/ios/Detox/ReactNativeHeaders.h @@ -23,6 +23,8 @@ typedef void (^RN_RCTJavaScriptCallback)(id json, NSError *error); + (id)currentBridge; - (void)requestReload; - (id) uiManager; +- (id)moduleForName:(NSString *)moduleName; +- (id)moduleForClass:(Class)moduleClass; @property (nonatomic, readonly, getter=isLoading) BOOL loading; @property (nonatomic, readonly, getter=isValid) BOOL valid; diff --git a/detox/ios/Detox/ReactNativeSupport.m b/detox/ios/Detox/ReactNativeSupport.m index 57f54b37a7..f799c203be 100644 --- a/detox/ios/Detox/ReactNativeSupport.m +++ b/detox/ios/Detox/ReactNativeSupport.m @@ -14,6 +14,7 @@ #import "WXRunLoopIdlingResource.h" #import "WXJSDisplayLinkIdlingResource.h" #import "WXJSTimerObservationIdlingResource.h" +#import "WXAnimatedDisplayLinkIdlingResource.h" @import ObjectiveC; @import Darwin; @@ -84,8 +85,10 @@ void setupForTests() // m = class_getInstanceMethod(cls, NSSelectorFromString(@"addToRunLoop:")); // orig_addToRunLoop = (void(*)(id, SEL, NSRunLoop*))method_getImplementation(m); // method_setImplementation(m, (IMP)swz_addToRunLoop); - + [[GREYUIThreadExecutor sharedInstance] registerIdlingResource:[WXJSTimerObservationIdlingResource new]]; + + [[GREYUIThreadExecutor sharedInstance] registerIdlingResource:[WXAnimatedDisplayLinkIdlingResource new]]; } @implementation ReactNativeSupport diff --git a/detox/ios/Detox/WXAnimatedDisplayLinkIdlingResource.h b/detox/ios/Detox/WXAnimatedDisplayLinkIdlingResource.h new file mode 100644 index 0000000000..28c91e1b2a --- /dev/null +++ b/detox/ios/Detox/WXAnimatedDisplayLinkIdlingResource.h @@ -0,0 +1,14 @@ +// +// WXAnimatedDisplayLinkIdlingResource.h +// Detox +// +// Created by Sergey Ilyevsky on 22/05/2017. +// Copyright © 2017 Wix. All rights reserved. +// + +#import +#import + +@interface WXAnimatedDisplayLinkIdlingResource : NSObject + +@end diff --git a/detox/ios/Detox/WXAnimatedDisplayLinkIdlingResource.m b/detox/ios/Detox/WXAnimatedDisplayLinkIdlingResource.m new file mode 100644 index 0000000000..1c0dd88df4 --- /dev/null +++ b/detox/ios/Detox/WXAnimatedDisplayLinkIdlingResource.m @@ -0,0 +1,34 @@ +// +// WXAnimatedDisplayLinkIdlingResource.m +// Detox +// +// Created by Sergey Ilyevsky on 22/05/2017. +// Copyright © 2017 Wix. All rights reserved. +// + +#import "WXAnimatedDisplayLinkIdlingResource.h" +#import "ReactNativeHeaders.h" + +@implementation WXAnimatedDisplayLinkIdlingResource { + id _bridge; +} + +- (NSString *)idlingResourceName +{ + return NSStringFromClass([self class]); +} + +- (NSString *)idlingResourceDescription +{ + return @"Monitors CADisplayLink objects created by React Native AnimatedAnimated"; +} + +- (BOOL)isIdleNow +{ + id bridge = [NSClassFromString(@"RCTBridge") valueForKey:@"currentBridge"]; + id animatedModule = [bridge moduleForClass:NSClassFromString(@"RCTNativeAnimatedModule")]; + id displayLink = [animatedModule valueForKeyPath:@"_nodesManager._displayLink"]; + return displayLink == nil; +} + +@end diff --git a/detox/test/e2e/l-animations.js b/detox/test/e2e/l-animations.js new file mode 100644 index 0000000000..56a9b91d89 --- /dev/null +++ b/detox/test/e2e/l-animations.js @@ -0,0 +1,11 @@ +describe.only('Animations', () => { + beforeEach(async () => { + await device.reloadReactNative(); + await element(by.label('Animations')).tap(); + }); + + it('should find element', async () => { + await expect(element(by.id('UniqueId_AnimationsScreen_testedText'))).toBeVisible(); + }); + +}); \ No newline at end of file diff --git a/detox/test/index.ios.js b/detox/test/index.ios.js index f779e37f25..a993078da8 100644 --- a/detox/test/index.ios.js +++ b/detox/test/index.ios.js @@ -69,6 +69,7 @@ class example extends Component { {this.renderScreenButton('Stress', Screens.StressScreen)} {this.renderScreenButton('Switch Root', Screens.SwitchRootScreen)} {this.renderScreenButton('Timeouts', Screens.TimeoutsScreen)} + {this.renderScreenButton('Animations', Screens.AnimationsScreen)} ); } diff --git a/detox/test/package.json b/detox/test/package.json index 8486c1d7ef..e88f882272 100644 --- a/detox/test/package.json +++ b/detox/test/package.json @@ -30,7 +30,12 @@ "type": "ios.none", "name": "iPhone 7 Plus" } + }, + "session": { + "server": "ws://localhost:8099", + "sessionId": "test" } + } } - \ No newline at end of file + diff --git a/detox/test/src/Screens/AnimationsScreen.js b/detox/test/src/Screens/AnimationsScreen.js new file mode 100644 index 0000000000..54eeb84219 --- /dev/null +++ b/detox/test/src/Screens/AnimationsScreen.js @@ -0,0 +1,110 @@ +import React, { Component } from 'react'; +import { + Text, + View, + Animated, + Button +} from 'react-native'; +import _ from 'lodash'; + +class AnimationStartingRightAway extends Component { + constructor(props) { + super(props); + + this._faidInValue = new Animated.Value(0); + + this.state = { + renderTestedText: false + }; + } + + componentDidMount() { + Animated.timing(this._faidInValue, { + toValue: 1, + duration: 400, + useNativeDriver: true + }).start(() => this.setState({renderTestedText: true})); + } + + render() { + return ( + + + Fading in text + + {(() => { if(this.state.renderTestedText) return ( + + Tested text + + )})()} + + ); + } +} + +export default class AnimationsScreen extends Component { + constructor(props) { + super(props); + + this.testCases = [ + AnimationStartingRightAway + ]; + + this.state = { + selectedDriver: undefined, + currentTestComponent: undefined + }; + } + + _renderDriverSelection() { + return ( + + { + _.map(['js', 'native'], (driver) => { + return ( +