Support Promise-based async tests from Xcode tests

We already supported this for React Native and Node test runners. Now we support returning promises from tests everywhere.
This commit is contained in:
Scott Kyle 2016-06-30 15:28:45 -07:00
parent b1db10e177
commit 56a0b903f9
4 changed files with 65 additions and 17 deletions

View File

@ -27,5 +27,6 @@
- (JSValue *)loadModuleFromURL:(NSURL *)url error:(NSError **)error;
- (JSValue *)loadJSONFromURL:(NSURL *)url error:(NSError **)error;
- (JSValue *)loadGlobalModule:(NSString *)name relativeToURL:(NSURL *)url error:(NSError **)error;
@end

View File

@ -195,6 +195,18 @@ static NSString * const RJSModuleLoaderErrorDomain = @"RJSModuleLoaderErrorDomai
BOOL isDirectory;
if ([fileManager fileExistsAtPath:moduleURL.path isDirectory:&isDirectory] && isDirectory) {
NSURL *packageURL = [moduleURL URLByAppendingPathComponent:@"package.json"];
NSDictionary *package;
if ([fileManager fileExistsAtPath:packageURL.path]) {
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:packageURL options:0 error:&error];
package = data ? [NSJSONSerialization JSONObjectWithData:data options:0 error:&error] : nil;
NSAssert(package, @"%@", error);
}
moduleURL = [moduleURL URLByAppendingPathComponent:package[@"main"] ?: @"index.js"];
return [self loadModuleFromURL:moduleURL error:error];
}

View File

@ -31,12 +31,23 @@
+ (XCTestSuite *)defaultTestSuite {
XCTestSuite *suite = [super defaultTestSuite];
JSContext *context = [[JSContext alloc] init];
// We need a JS context from a UIWebView so it has setTimeout, Promise, etc.
UIWebView *webView = [[UIWebView alloc] init];
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
RJSModuleLoader *moduleLoader = [[RJSModuleLoader alloc] initWithContext:context];
NSURL *realmURL = [[NSBundle bundleForClass:self] URLForResource:@"index" withExtension:@"js" subdirectory:@"lib"];
NSURL *scriptURL = [[NSBundle bundleForClass:self] URLForResource:@"index" withExtension:@"js" subdirectory:@"js"];
NSError *error;
// The ES6 global Promise constructor was added in iOS 8.
if (![context[@"Promise"] isObject]) {
JSValue *promiseModule = [moduleLoader loadGlobalModule:@"es6-promise" relativeToURL:scriptURL error:&error];
NSAssert(promiseModule, @"%@", error);
context[@"Promise"] = promiseModule[@"Promise"];
}
// Create Realm constructor in the JS context.
RJSInitializeInContext(context.JSGlobalContextRef);
@ -73,25 +84,46 @@
JSContext *context = testObject.context;
context.exception = nil;
[testObject invokeMethod:@"runTest" withArguments:@[NSStringFromClass(self.class), method]];
JSValue *promise = [testObject invokeMethod:@"runTest" withArguments:@[NSStringFromClass(self.class), method]];
JSValue *exception = context.exception;
if (exception) {
JSValue *message = [exception hasProperty:@"message"] ? exception[@"message"] : exception;
NSString *source = [exception hasProperty:@"sourceURL"] ? [exception[@"sourceURL"] toString] : nil;
NSUInteger line = [exception hasProperty:@"line"] ? [exception[@"line"] toUInt32] - 1 : 0;
NSURL *sourceURL = nil;
if (context.exception) {
[self recordException:context.exception];
return;
}
if (source) {
NSString *path = [NSString pathWithComponents:@[[@(__FILE__) stringByDeletingLastPathComponent], @"..", @"js", source.lastPathComponent]];
sourceURL = [NSURL URLWithString:path];
}
if ([promise isObject]) {
XCTestExpectation *expectation = [self expectationWithDescription:@"Promise resolved or rejected"];
[self recordFailureWithDescription:message.description
inFile:sourceURL ? sourceURL.absoluteString : @(__FILE__)
atLine:sourceURL ? line : __LINE__
expected:YES];
JSValue *onFulfilled = [JSValue valueWithObject:^() {
[expectation fulfill];
} inContext:context];
JSValue *onRejected = [JSValue valueWithObject:^(JSValue *error) {
[self recordException:error];
[expectation fulfill];
} inContext:context];
[promise invokeMethod:@"then" withArguments:@[onFulfilled, onRejected]];
[self waitForExpectationsWithTimeout:5.0 handler:NULL];
}
}
- (void)recordException:(JSValue *)exception {
JSValue *message = [exception hasProperty:@"message"] ? exception[@"message"] : exception;
NSString *source = [exception hasProperty:@"sourceURL"] ? [exception[@"sourceURL"] toString] : nil;
NSUInteger line = [exception hasProperty:@"line"] ? [exception[@"line"] toUInt32] - 1 : 0;
NSURL *sourceURL = nil;
if (source) {
NSString *path = [NSString pathWithComponents:@[[@(__FILE__) stringByDeletingLastPathComponent], @"..", @"js", source.lastPathComponent]];
sourceURL = [NSURL URLWithString:path];
}
[self recordFailureWithDescription:message.description
inFile:sourceURL ? sourceURL.absoluteString : @(__FILE__)
atLine:sourceURL ? line : __LINE__
expected:YES];
}
@end

View File

@ -1,5 +1,8 @@
{
"name": "realm-tests",
"version": "0.0.1",
"private": true
"private": true,
"dependencies": {
"es6-promise": "^3.2.1"
}
}