diff --git a/tests/ReactTests/ios/ReactTests/AppDelegate.m b/tests/ReactTests/ios/ReactTests/AppDelegate.m index b4c5c53a..bf4e4fc5 100644 --- a/tests/ReactTests/ios/ReactTests/AppDelegate.m +++ b/tests/ReactTests/ios/ReactTests/AppDelegate.m @@ -19,30 +19,8 @@ #import "AppDelegate.h" #import "RCTRootView.h" -static NSString * const RealmReactEnableChromeDebuggingKey = @"RealmReactEnableChromeDebugging"; -static NSString * const RCTDevMenuKey = @"RCTDevMenu"; - @implementation AppDelegate -- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - - // Check if this default is explicitly set, otherwise just leave the settings as is. - if ([defaults objectForKey:RealmReactEnableChromeDebuggingKey]) { - NSMutableDictionary *settings = [([defaults dictionaryForKey:RCTDevMenuKey] ?: @{}) mutableCopy]; - NSMutableDictionary *domain = [[defaults volatileDomainForName:NSArgumentDomain] mutableCopy]; - - settings[@"executorClass"] = [defaults boolForKey:RealmReactEnableChromeDebuggingKey] ? @"RCTWebSocketExecutor" : @"RCTContextExecutor"; - domain[RCTDevMenuKey] = settings; - - // Re-register the arguments domain (highest precedent and volatile) with our new overridden settings. - [defaults removeVolatileDomainForName:NSArgumentDomain]; - [defaults setVolatileDomain:domain forName:NSArgumentDomain]; - } - - return YES; -} - - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation; diff --git a/tests/ReactTests/ios/ReactTestsTests/RealmReactTests.m b/tests/ReactTests/ios/ReactTestsTests/RealmReactTests.m index f4d0e323..d1e25cdf 100644 --- a/tests/ReactTests/ios/ReactTestsTests/RealmReactTests.m +++ b/tests/ReactTests/ios/ReactTestsTests/RealmReactTests.m @@ -19,34 +19,71 @@ #import "RealmJSTests.h" #import "Base/RCTJavaScriptExecutor.h" #import "Base/RCTBridge.h" +#import "Modules/RCTDevMenu.h" +@import ObjectiveC; @import RealmReact; extern void JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(JSGlobalContextRef ctx, bool includesNativeCallStack); -static id s_currentJavaScriptExecutor; - @interface RealmReactTests : RealmJSTests @end +@interface RealmReactChromeTests : RealmReactTests +@end + @implementation RealmReactTests -+ (XCTestSuite *)defaultTestSuite { - NSNotification *notification = [self waitForNotification:RCTJavaScriptDidLoadNotification]; - RCTBridge *bridge = notification.userInfo[@"bridge"]; ++ (void)load { + // Swap the [RCTDevMenu init] method with [NSObject init] in order to disable RCTDevMenu completely. + IMP init = class_getMethodImplementation([NSObject class], @selector(init)); + class_replaceMethod([RCTDevMenu class], @selector(init), init, NULL); +} - if (!bridge) { - NSLog(@"No RCTBridge provided by RCTJavaScriptDidLoadNotification"); ++ (Class)executorClass { + return NSClassFromString(@"RCTContextExecutor"); +} + ++ (NSString *)classNameSuffix { + return @""; +} + ++ (id)currentExecutor { + Class executorClass = [self executorClass]; + if (!executorClass) { + NSLog(@"%@: Executor class not found", self); exit(1); } - // Wait for the RCTDevMenu to handle this notification and act upon it. - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + static RCTBridge *s_bridge; + if (!s_bridge.valid) { + NSNotification *notification = [self waitForNotification:RCTJavaScriptDidLoadNotification];; + s_bridge = notification.userInfo[@"bridge"]; - s_currentJavaScriptExecutor = [bridge valueForKey:@"javaScriptExecutor"]; + if (!s_bridge) { + NSLog(@"No RCTBridge provided by RCTJavaScriptDidLoadNotification"); + exit(1); + } + } + + if (s_bridge.executorClass != executorClass) { + s_bridge.executorClass = executorClass; + [s_bridge reload]; + + // The [RCTBridge reload] method does a dispatch_async that we must run before trying again. + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + return [self currentExecutor]; + } + + return [s_bridge valueForKey:@"javaScriptExecutor"]; +} + ++ (XCTestSuite *)defaultTestSuite { + id executor = [self currentExecutor]; // FIXME: Remove this nonsense once the crashes go away when a test fails! - JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(s_currentJavaScriptExecutor, false); + JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(executor, false); if (ctx) { JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(ctx, false); } @@ -59,8 +96,16 @@ static id s_currentJavaScriptExecutor; exit(1); } - XCTestSuite *suite = [super defaultTestSuite]; + NSString *nameSuffix = [self classNameSuffix]; + if (nameSuffix.length) { + NSMutableDictionary *renamedTestCaseNames = [[NSMutableDictionary alloc] init]; + for (NSString *name in testCaseNames) { + renamedTestCaseNames[[name stringByAppendingString:nameSuffix]] = testCaseNames[name]; + } + testCaseNames = renamedTestCaseNames; + } + XCTestSuite *suite = [super defaultTestSuite]; for (XCTestSuite *testSuite in [self testSuitesFromDictionary:testCaseNames]) { [suite addTest:testSuite]; } @@ -95,12 +140,13 @@ static id s_currentJavaScriptExecutor; } + (id)invokeMethod:(NSString *)method inModule:(NSString *)module error:(NSError * __strong *)outError { + id executor = [self currentExecutor]; module = [NSString stringWithFormat:@"realm-tests/%@.js", module]; __block BOOL condition = NO; __block id result; - [s_currentJavaScriptExecutor executeJSCall:module method:method arguments:@[] callback:^(id json, NSError *error) { + [executor executeJSCall:module method:method arguments:@[] callback:^(id json, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ condition = YES; result = json; @@ -117,8 +163,15 @@ static id s_currentJavaScriptExecutor; } - (void)invokeMethod:(NSString *)method { + NSString *module = NSStringFromClass(self.class); + NSString *suffix = [self.class classNameSuffix]; + + if (suffix.length && [module hasSuffix:suffix]) { + module = [module substringToIndex:(module.length - suffix.length)]; + } + NSError *error; - [self.class invokeMethod:method inModule:NSStringFromClass(self.class) error:&error]; + [self.class invokeMethod:method inModule:module error:&error]; if (error) { // TODO: Parse and use localizedFailureReason info once we can source map the failure location in JS. @@ -127,3 +180,15 @@ static id s_currentJavaScriptExecutor; } @end + +@implementation RealmReactChromeTests + ++ (Class)executorClass { + return NSClassFromString(@"RCTWebSocketExecutor"); +} + ++ (NSString *)classNameSuffix { + return @"_Chrome"; +} + +@end