Updates from Sat 14 Mar

- Unforked RKWebView | Nick Lockwood
- [ReactNative] Add integration test stuff | Spencer Ahrens
- [ReactNative] AlertIOS.alert and examples | Eric Vicenti
- [react-packager] Implement image loading i.e. ix('img') -> require('image!img'); | Amjad Masad
- Fixed scrollOffset bug | Nick Lockwood
- [React Native] Update 2048 | Alex Akers
- deepDiffer should support explicitly undefined values | Thomas Aylott
This commit is contained in:
Christopher Chedeau
2015-03-14 10:52:40 -07:00
parent 74899c8ee0
commit 41ae2314ce
52 changed files with 2807 additions and 74 deletions

View File

@@ -23,10 +23,11 @@ var Animation = {
): number {
var nodeHandle = +node.getNodeHandle();
var easingSample = AnimationUtils.evaluateEasingFunction(duration, easing);
RCTAnimationManager.startAnimation(nodeHandle, AnimationUtils.allocateTag(), duration, delay, easingSample, properties);
var tag: number = RCTAnimationManager.startAnimation(nodeHandle, AnimationUtils.allocateTag(), duration, delay, easingSample, properties);
return tag;
},
stopAnimation: function(tag) {
stopAnimation: function(tag: number) {
RCTAnimationManager.stopAnimation(tag);
},
};

View File

@@ -34,7 +34,8 @@ var AnimationMixin = {
var nodeHandle = +ref.getNodeHandle();
var easingSample = AnimationUtils.evaluateEasingFunction(duration, easing);
RCTAnimationManager.startAnimation(nodeHandle, AnimationUtils.allocateTag(), duration, delay, easingSample, properties);
var tag: number = RCTAnimationManager.startAnimation(nodeHandle, AnimationUtils.allocateTag(), duration, delay, easingSample, properties);
return tag;
},
stopAnimation: function(tag: number) {

View File

@@ -0,0 +1,169 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule WebView
*/
'use strict';
var EdgeInsetsPropType = require('EdgeInsetsPropType');
var React = require('React');
var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
var StyleSheet = require('StyleSheet');
var View = require('View');
var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
var keyMirror = require('keyMirror');
var merge = require('merge');
var PropTypes = React.PropTypes;
var RKUIManager = require('NativeModules').RKUIManager;
var RK_WEBVIEW_REF = 'webview';
var WebViewState = keyMirror({
IDLE: null,
LOADING: null,
ERROR: null,
});
var WebView = React.createClass({
propTypes: {
renderErrorView: PropTypes.func.isRequired, // view to show if there's an error
renderLoadingView: PropTypes.func.isRequired, // loading indicator to show
url: PropTypes.string.isRequired,
automaticallyAdjustContentInsets: PropTypes.bool,
contentInset: EdgeInsetsPropType,
onNavigationStateChange: PropTypes.func,
startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load
style: View.propTypes.style,
/**
* Used to locate this view in end-to-end tests.
*/
testID: PropTypes.string,
},
getInitialState: function() {
return {
viewState: WebViewState.IDLE,
lastErrorEvent: null,
startInLoadingState: true,
};
},
componentWillMount: function() {
if (this.props.startInLoadingState) {
this.setState({viewState: WebViewState.LOADING});
}
},
render: function() {
var otherView = null;
if (this.state.viewState === WebViewState.LOADING) {
otherView = this.props.renderLoadingView();
} else if (this.state.viewState === WebViewState.ERROR) {
var errorEvent = this.state.lastErrorEvent;
otherView = this.props.renderErrorView(
errorEvent.domain,
errorEvent.code,
errorEvent.description);
} else if (this.state.viewState !== WebViewState.IDLE) {
console.error("RCTWebView invalid state encountered: " + this.state.loading);
}
var webViewStyles = [styles.container, this.props.style];
if (this.state.viewState === WebViewState.LOADING ||
this.state.viewState === WebViewState.ERROR) {
// if we're in either LOADING or ERROR states, don't show the webView
webViewStyles.push(styles.hidden);
}
var webView =
<RCTWebView
ref={RK_WEBVIEW_REF}
key="webViewKey"
style={webViewStyles}
url={this.props.url}
contentInset={this.props.contentInset}
automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets}
onLoadingStart={this.onLoadingStart}
onLoadingFinish={this.onLoadingFinish}
onLoadingError={this.onLoadingError}
testID={this.props.testID}
/>;
return (
<View style={styles.container}>
{webView}
{otherView}
</View>
);
},
goForward: function() {
RKUIManager.webViewGoForward(this.getWebWiewHandle());
},
goBack: function() {
RKUIManager.webViewGoBack(this.getWebWiewHandle());
},
reload: function() {
RKUIManager.webViewReload(this.getWebWiewHandle());
},
/**
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
*/
updateNavigationState: function(event) {
if (this.props.onNavigationStateChange) {
this.props.onNavigationStateChange(event.nativeEvent);
}
},
getWebWiewHandle: function() {
return this.refs[RK_WEBVIEW_REF].getNodeHandle();
},
onLoadingStart: function(event) {
this.updateNavigationState(event);
},
onLoadingError: function(event) {
event.persist(); // persist this event because we need to store it
console.error("encountered an error loading page", event.nativeEvent);
this.setState({
lastErrorEvent: event.nativeEvent,
viewState: WebViewState.ERROR
});
},
onLoadingFinish: function(event) {
this.setState({
viewState: WebViewState.IDLE,
});
this.updateNavigationState(event);
},
});
var RCTWebView = createReactIOSNativeComponentClass({
validAttributes: merge(ReactIOSViewAttributes.UIView, {
url: true,
}),
uiViewClassName: 'RCTWebView',
});
var styles = StyleSheet.create({
container: {
flex: 1,
},
hidden: {
height: 0,
flex: 0, // disable 'flex:1' when hiding a View
},
});
module.exports = WebView;

View File

@@ -0,0 +1,182 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule WebView
*/
'use strict';
var EdgeInsetsPropType = require('EdgeInsetsPropType');
var React = require('React');
var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
var StyleSheet = require('StyleSheet');
var View = require('View');
var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
var keyMirror = require('keyMirror');
var insetsDiffer = require('insetsDiffer');
var merge = require('merge');
var PropTypes = React.PropTypes;
var { RKWebViewManager } = require('NativeModules');
var RK_WEBVIEW_REF = 'webview';
var WebViewState = keyMirror({
IDLE: null,
LOADING: null,
ERROR: null,
});
var NavigationType = {
click: RKWebViewManager.NavigationType.LinkClicked,
formsubmit: RKWebViewManager.NavigationType.FormSubmitted,
backforward: RKWebViewManager.NavigationType.BackForward,
reload: RKWebViewManager.NavigationType.Reload,
formresubmit: RKWebViewManager.NavigationType.FormResubmitted,
other: RKWebViewManager.NavigationType.Other,
};
var WebView = React.createClass({
statics: {
NavigationType: NavigationType,
},
propTypes: {
renderErrorView: PropTypes.func.isRequired, // view to show if there's an error
renderLoadingView: PropTypes.func.isRequired, // loading indicator to show
url: PropTypes.string.isRequired,
automaticallyAdjustContentInsets: PropTypes.bool,
shouldInjectAJAXHandler: PropTypes.bool,
contentInset: EdgeInsetsPropType,
onNavigationStateChange: PropTypes.func,
startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load
style: View.propTypes.style,
},
getInitialState: function() {
return {
viewState: WebViewState.IDLE,
lastErrorEvent: null,
startInLoadingState: true,
};
},
componentWillMount: function() {
if (this.props.startInLoadingState) {
this.setState({viewState: WebViewState.LOADING});
}
},
render: function() {
var otherView = null;
if (this.state.viewState === WebViewState.LOADING) {
otherView = this.props.renderLoadingView();
} else if (this.state.viewState === WebViewState.ERROR) {
var errorEvent = this.state.lastErrorEvent;
otherView = this.props.renderErrorView(
errorEvent.domain,
errorEvent.code,
errorEvent.description);
} else if (this.state.viewState !== WebViewState.IDLE) {
console.error("RKWebView invalid state encountered: " + this.state.loading);
}
var webViewStyles = [styles.container, this.props.style];
if (this.state.viewState === WebViewState.LOADING ||
this.state.viewState === WebViewState.ERROR) {
// if we're in either LOADING or ERROR states, don't show the webView
webViewStyles.push(styles.hidden);
}
var webView =
<RCTWebView
ref={RK_WEBVIEW_REF}
key="webViewKey"
style={webViewStyles}
url={this.props.url}
shouldInjectAJAXHandler={this.props.shouldInjectAJAXHandler}
contentInset={this.props.contentInset}
automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets}
onLoadingStart={this.onLoadingStart}
onLoadingFinish={this.onLoadingFinish}
onLoadingError={this.onLoadingError}
/>;
return (
<View style={styles.container}>
{webView}
{otherView}
</View>
);
},
goForward: function() {
RKWebViewManager.goForward(this.getWebWiewHandle());
},
goBack: function() {
RKWebViewManager.goBack(this.getWebWiewHandle());
},
reload: function() {
RKWebViewManager.reload(this.getWebWiewHandle());
},
/**
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
*/
updateNavigationState: function(event) {
if (this.props.onNavigationStateChange) {
this.props.onNavigationStateChange(event.nativeEvent);
}
},
getWebWiewHandle: function() {
return this.refs[RK_WEBVIEW_REF].getNodeHandle();
},
onLoadingStart: function(event) {
this.updateNavigationState(event);
},
onLoadingError: function(event) {
event.persist(); // persist this event because we need to store it
console.error("encountered an error loading page", event.nativeEvent);
this.setState({
lastErrorEvent: event.nativeEvent,
viewState: WebViewState.ERROR
});
},
onLoadingFinish: function(event) {
this.setState({
viewState: WebViewState.IDLE,
});
this.updateNavigationState(event);
},
});
var RCTWebView = createReactIOSNativeComponentClass({
validAttributes: merge(ReactIOSViewAttributes.UIView, {
url: true,
contentInset: {diff: insetsDiffer},
automaticallyAdjustContentInsets: true,
shouldInjectAJAXHandler: true
}),
uiViewClassName: 'RCTWebView',
});
var styles = StyleSheet.create({
container: {
flex: 1,
},
hidden: {
height: 0,
flex: 0, // disable 'flex:1' when hiding a View
},
});
module.exports = WebView;

View File

@@ -0,0 +1,264 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
585135371AB3C56F00882537 /* RCTTestModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 585135341AB3C56F00882537 /* RCTTestModule.m */; };
585135381AB3C57000882537 /* RCTTestRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 585135361AB3C56F00882537 /* RCTTestRunner.m */; };
585135391AB3C59A00882537 /* RCTTestRunner.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 585135351AB3C56F00882537 /* RCTTestRunner.h */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
580C376D1AB104AF0015E709 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
585135391AB3C59A00882537 /* RCTTestRunner.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
580C376F1AB104AF0015E709 /* libRCTTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTTest.a; sourceTree = BUILT_PRODUCTS_DIR; };
585135331AB3C56F00882537 /* RCTTestModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTestModule.h; sourceTree = "<group>"; };
585135341AB3C56F00882537 /* RCTTestModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTestModule.m; sourceTree = "<group>"; };
585135351AB3C56F00882537 /* RCTTestRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTestRunner.h; sourceTree = "<group>"; };
585135361AB3C56F00882537 /* RCTTestRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTestRunner.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
580C376C1AB104AF0015E709 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
580C37661AB104AF0015E709 = {
isa = PBXGroup;
children = (
585135331AB3C56F00882537 /* RCTTestModule.h */,
585135341AB3C56F00882537 /* RCTTestModule.m */,
585135351AB3C56F00882537 /* RCTTestRunner.h */,
585135361AB3C56F00882537 /* RCTTestRunner.m */,
580C37701AB104AF0015E709 /* Products */,
);
sourceTree = "<group>";
};
580C37701AB104AF0015E709 /* Products */ = {
isa = PBXGroup;
children = (
580C376F1AB104AF0015E709 /* libRCTTest.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
580C376E1AB104AF0015E709 /* RCTTest */ = {
isa = PBXNativeTarget;
buildConfigurationList = 580C37831AB104AF0015E709 /* Build configuration list for PBXNativeTarget "RCTTest" */;
buildPhases = (
580C376B1AB104AF0015E709 /* Sources */,
580C376C1AB104AF0015E709 /* Frameworks */,
580C376D1AB104AF0015E709 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RCTTest;
productName = RCTTest;
productReference = 580C376F1AB104AF0015E709 /* libRCTTest.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
580C37671AB104AF0015E709 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
580C376E1AB104AF0015E709 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 580C376A1AB104AF0015E709 /* Build configuration list for PBXProject "RCTTest" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 580C37661AB104AF0015E709;
productRefGroup = 580C37701AB104AF0015E709 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
580C376E1AB104AF0015E709 /* RCTTest */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
580C376B1AB104AF0015E709 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
585135371AB3C56F00882537 /* RCTTestModule.m in Sources */,
585135381AB3C57000882537 /* RCTTestRunner.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
580C37811AB104AF0015E709 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../ReactKit/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
580C37821AB104AF0015E709 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../ReactKit/**",
);
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
580C37841AB104AF0015E709 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_LDFLAGS = (
"-ObjC",
"-framework",
XCTest,
);
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
580C37851AB104AF0015E709 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_LDFLAGS = (
"-ObjC",
"-framework",
XCTest,
);
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
580C376A1AB104AF0015E709 /* Build configuration list for PBXProject "RCTTest" */ = {
isa = XCConfigurationList;
buildConfigurations = (
580C37811AB104AF0015E709 /* Debug */,
580C37821AB104AF0015E709 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
580C37831AB104AF0015E709 /* Build configuration list for PBXNativeTarget "RCTTest" */ = {
isa = XCConfigurationList;
buildConfigurations = (
580C37841AB104AF0015E709 /* Debug */,
580C37851AB104AF0015E709 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 580C37671AB104AF0015E709 /* Project object */;
}

View File

@@ -0,0 +1,9 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTBridgeModule.h"
@interface RCTTestModule : NSObject <RCTBridgeModule>
@property (nonatomic, readonly, getter=isDone) BOOL done;
@end

View File

@@ -0,0 +1,14 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTTestModule.h"
@implementation RCTTestModule
- (void)markTestCompleted
{
RCT_EXPORT();
_done = YES;
}
@end

View File

@@ -0,0 +1,14 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import <Foundation/Foundation.h>
@interface RCTTestRunner : NSObject
@property (nonatomic, copy) NSString *script;
- (instancetype)initWithApp:(NSString *)app;
- (void)runTest:(NSString *)moduleName;
- (void)runTest:(NSString *)moduleName initialProps:(NSDictionary *)initialProps expectErrorRegex:(NSRegularExpression *)expectErrorRegex;
- (void)runTest:(NSString *)moduleName initialProps:(NSDictionary *)initialProps expectErrorBlock:(BOOL(^)(NSString *error))expectErrorBlock;
@end

View File

@@ -0,0 +1,64 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTTestRunner.h"
#import "RCTRedBox.h"
#import "RCTRootView.h"
#import "RCTTestModule.h"
#import "RCTUtils.h"
#define TIMEOUT_SECONDS 30
@implementation RCTTestRunner
- (instancetype)initWithApp:(NSString *)app
{
if (self = [super init]) {
_script = [NSString stringWithFormat:@"http://localhost:8081/%@.includeRequire.runModule.bundle?dev=true", app];
}
return self;
}
- (void)runTest:(NSString *)moduleName
{
[self runTest:moduleName initialProps:nil expectErrorBlock:nil];
}
- (void)runTest:(NSString *)moduleName initialProps:(NSDictionary *)initialProps expectErrorRegex:(NSRegularExpression *)errorRegex
{
[self runTest:moduleName initialProps:initialProps expectErrorBlock:^BOOL(NSString *error){
return [errorRegex numberOfMatchesInString:error options:0 range:NSMakeRange(0, [error length])] > 0;
}];
}
- (void)runTest:(NSString *)moduleName initialProps:(NSDictionary *)initialProps expectErrorBlock:(BOOL(^)(NSString *error))expectErrorBlock
{
RCTTestModule *testModule = [[RCTTestModule alloc] init];
RCTRootView *rootView = [[RCTRootView alloc] init];
UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
vc.view = rootView;
rootView.moduleProvider = ^(void){
return @[testModule];
};
rootView.moduleName = moduleName;
rootView.initialProperties = initialProps;
rootView.scriptURL = [NSURL URLWithString:_script];
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
NSString *error = [[RCTRedBox sharedInstance] currentErrorMessage];
while ([date timeIntervalSinceNow] > 0 && ![testModule isDone] && error == nil) {
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date];
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:date];
error = [[RCTRedBox sharedInstance] currentErrorMessage];
}
[[RCTRedBox sharedInstance] dismiss];
if (expectErrorBlock) {
RCTAssert(expectErrorBlock(error), @"Expected an error but got none.");
} else if (error) {
RCTAssert(error == nil, @"RedBox error: %@", error);
} else {
RCTAssert([testModule isDone], @"Test didn't finish within %d seconds", TIMEOUT_SECONDS);
}
}
@end

View File

@@ -0,0 +1,77 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule AlertIOS
* @flow
*/
'use strict';
var NativeModules = require('NativeModulesDeprecated');
var RCTAlertManager = NativeModules.RCTAlertManager;
var DEFAULT_BUTTON_TEXT = 'OK';
var DEFAULT_BUTTON = {
text: DEFAULT_BUTTON_TEXT,
onPress: null,
};
/**
* AlertIOS manages native iOS alerts, option sheets, and share dialogs
*/
class AlertIOS {
/**
* Launches an alert dialog with the specified title and message.
*
* Optionally provide a list of buttons. Tapping any button will fire the
* respective onPress callback and dismiss the alert. By default, the only
* button will be an 'OK' button
*
* The last button in the list will be considered the 'Primary' button and
* it will appear bold.
*
* ```
* AlertIOS.alert(
* 'Foo Title',
* 'My Alert Msg',
* [
* {text: 'Foo', onPress: () => console.log('Foo Pressed!')},
* {text: 'Bar', onPress: () => console.log('Bar Pressed!')},
* ]
* )}
* ```
*/
static alert(
title: ?string,
message: ?string,
buttons: ?Array<{
text: ?string;
onPress: ?Function;
}>
): void {
var callbacks = [];
var buttonsSpec = [];
title = title || '';
message = message || '';
buttons = buttons || [DEFAULT_BUTTON];
buttons.forEach((btn, index) => {
callbacks[index] = btn.onPress;
var btnDef = {};
btnDef[index] = btn.text || DEFAULT_BUTTON_TEXT;
buttonsSpec.push(btnDef);
});
RCTAlertManager.alertWithArgs({
title,
message,
buttons: buttonsSpec,
}, (id) => {
var cb = callbacks[id];
cb && cb();
});
}
}
module.exports = AlertIOS;

View File

@@ -38,7 +38,7 @@ var deepDiffer = function(one: any, two: any): bool {
for (var twoKey in two) {
// The only case we haven't checked yet is keys that are in two but aren't
// in one, which means they are different.
if (one[twoKey] === undefined) {
if (one[twoKey] === undefined && two[twoKey] !== undefined) {
return true;
}
}

View File

@@ -9,6 +9,7 @@ var ReactNative = {
...require('React'),
Animation: require('Animation'),
ActivityIndicatorIOS: require('ActivityIndicatorIOS'),
AlertIOS: require('AlertIOS'),
AppRegistry: require('AppRegistry'),
AppState: require('AppState'),
AppStateIOS: require('AppStateIOS'),
@@ -36,6 +37,7 @@ var ReactNative = {
TouchableOpacity: require('TouchableOpacity'),
TouchableWithoutFeedback: require('TouchableWithoutFeedback'),
View: require('View'),
WebView: require('WebView'),
invariant: require('invariant'),
ix: require('ix'),
};