Skip to content

Commit a5f80b6

Browse files
committed
integration test scaffolding
1 parent 9a39770 commit a5f80b6

File tree

17 files changed

+840
-3
lines changed

17 files changed

+840
-3
lines changed

App/Constants/AppConstants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ module.exports = keyMirror({
1111
NAVBAR_UPDATE: null,
1212
POST_LIST_UPDATED: null,
1313
POST_ADDED: null,
14-
FOLLOW_LIST_UPDATED: null
14+
FOLLOW_LIST_UPDATED: null,
15+
TEST_COMPONENT_ROUTE: null,
1516
});

App/Platform/StatusBar.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ var {
66
var StatusBar = {
77
setNetworkActive: function(active) {
88
StatusBarIOS.setNetworkActivityIndicatorVisible(active);
9+
},
10+
11+
setHidden: function(hidden) {
12+
StatusBarIOS.setHidden(hidden);
913
}
1014
};
1115

App/Root.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var Launch = require('./Root/Launch');
1010
var LoggedOut = require('./Root/LoggedOut');
1111
var LoggedIn = require('./Root/LoggedIn');
1212
var Launcher = require('./Root/Launcher');
13+
var TestRunner = require('./Root/TestRunner');
1314

1415
var AppActions = require('./Actions/AppActions');
1516
var CurrentUserStore = require('./Stores/CurrentUserStore');
@@ -64,6 +65,8 @@ var Root = React.createClass({
6465
},
6566

6667
renderContent: function() {
68+
if (this.state.routeUnderTest) return null;
69+
6770
var routeStack = this.state.routeStack;
6871
if(this.state.user.isLoggedIn()) {
6972
return(<LoggedIn ref="current" routeStack={routeStack} />);
@@ -78,6 +81,14 @@ var Root = React.createClass({
7881
if (!this.state.user || !this.state.environment) {
7982
return(<Launch ref="current" />);
8083
}
84+
else if (this.state.environment.data.name === 'test') {
85+
return (
86+
<View style={{flex:1}}>
87+
<TestRunner routeUnderTest={this.state.routeUnderTest}/>
88+
{this.renderContent()}
89+
</View>
90+
);
91+
}
8192
else {
8293
return this.renderContent();
8394
}

App/Root/Launcher.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ var Launcher = {
3131
// root.setState({loading: false});
3232
//});
3333
break;
34+
case AppConstants.TEST_COMPONENT_ROUTE:
35+
var TestComponents = require("../Root/TestComponents");
36+
action.routeUnderTest.component = TestComponents.find(action.routeUnderTest.component);
37+
rootComponent.setState({routeUnderTest: action.routeUnderTest});
38+
break;
3439
default:
3540
break;
3641
}

App/Root/TestComponents.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
var components = {
2+
'Components/SimpleListItem': require("../Components/SimpleListItem")
3+
};
4+
5+
var TestComponents = {
6+
find(name) {
7+
return components[name];
8+
}
9+
};
10+
11+
module.exports = TestComponents;

App/Root/TestRunner.js

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
var React = require('react-native');
2+
3+
var {
4+
Text,
5+
View,
6+
StyleSheet,
7+
TouchableOpacity,
8+
NativeModules,
9+
} = React;
10+
11+
var client = require('../Api/HTTPClient');
12+
var Dispatcher = require('../Dispatcher');
13+
14+
var TestRunnerManager = require('react-native').NativeModules.TestRunnerManager;
15+
var DevMenu = require('react-native').NativeModules.DevMenu;
16+
17+
var StatusBar = require('../Platform/StatusBar');
18+
19+
var TestRunner = React.createClass({
20+
componentDidMount: function() {
21+
StatusBar.setHidden(true);
22+
},
23+
24+
resetApp: function() {
25+
TestRunnerManager.reset(function() {
26+
console.log("reset")
27+
});
28+
},
29+
30+
showDevMenu: function() {
31+
DevMenu.show();
32+
},
33+
34+
hookConsole: function() {
35+
global.console = {
36+
log: function(...args) {
37+
client.post("test/console.json", {level: 'log', arguments: args});
38+
},
39+
error: function(...args) {
40+
client.post("test/console.json", {level: 'error', arguments: args});
41+
},
42+
warn: function(...args) {
43+
client.post("test/console.json", {level: 'warn', arguments: args});
44+
}
45+
}
46+
},
47+
48+
bootstrapApp: function() {
49+
client.get("test/bootstrap.json", null,
50+
(error, response) => {
51+
if (error) {
52+
console.log("BOOTSTRAP ERROR");
53+
alert('"BOOTSTRAP ERROR"');
54+
}
55+
else {
56+
this.hookConsole();
57+
if (response.actions) {
58+
for (var i=0; i<response.actions.length; i++) {
59+
var options = response.actions[i].options || {};
60+
options.actionType = response.actions[i].type;
61+
62+
switch(options.actionType) {
63+
default:
64+
Dispatcher.dispatch(options);
65+
}
66+
}
67+
}
68+
}
69+
});
70+
},
71+
72+
onLoadedTest: function(component) {
73+
if (this.props.routeUnderTest && this.props.routeUnderTest.setState) {
74+
component.setState(this.props.routeUnderTest.setState);
75+
}
76+
},
77+
78+
renderRouteUnderTest: function() {
79+
if (!this.props.routeUnderTest) return null;
80+
81+
var Component = this.props.routeUnderTest.component;
82+
var testProps = this.props.routeUnderTest.passProps || {};
83+
return (
84+
<Component ref={this.onLoadedTest} {...testProps} />
85+
);
86+
},
87+
88+
render: function() {
89+
var containerStyle = (this.props.routeUnderTest ? styles.withComponent : styles.noComponent);
90+
return (
91+
<View style={containerStyle}>
92+
{this.renderRouteUnderTest()}
93+
<View style={styles.bar}>
94+
<TouchableOpacity onPress={this.resetApp}>
95+
<Text style={styles.action}>
96+
ResetTest
97+
</Text>
98+
</TouchableOpacity>
99+
<TouchableOpacity onPress={this.bootstrapApp}>
100+
<Text style={styles.action}>
101+
Bootstrap
102+
</Text>
103+
</TouchableOpacity>
104+
<TouchableOpacity onPress={this.showDevMenu}>
105+
<Text style={styles.action}>
106+
DevMenu
107+
</Text>
108+
</TouchableOpacity>
109+
</View>
110+
</View>
111+
)
112+
}
113+
});
114+
115+
var styles = StyleSheet.create({
116+
withComponent: {
117+
flex: 1
118+
},
119+
noComponent: {
120+
height: 10
121+
},
122+
bar: {
123+
height: 10,
124+
backgroundColor: 'red',
125+
justifyContent: 'center',
126+
flexDirection: 'row'
127+
},
128+
action: {
129+
marginHorizontal: 4,
130+
fontSize: 10
131+
}
132+
});
133+
134+
module.exports = TestRunner;

ios/Sample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
684860A3F12E466838065EED /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A565388F7CDBDB0548B5DC8D /* libPods.a */; };
2525
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
2626
D339AEF21BAA809100CFA651 /* libRNKeyboardEvents.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D339AEF01BAA808800CFA651 /* libRNKeyboardEvents.a */; };
27+
D36840121BEA726800A9B3F4 /* TestRunnerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D36840111BEA726800A9B3F4 /* TestRunnerManager.m */; };
2728
D374ADC51BA929C500453203 /* libRNKeychain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D374ADC41BA929BA00453203 /* libRNKeychain.a */; };
2829
D374ADD31BA9336E00453203 /* EnvironmentManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D374ADD21BA9336E00453203 /* EnvironmentManager.m */; };
2930
/* End PBXBuildFile section */
@@ -139,6 +140,7 @@
139140
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
140141
A565388F7CDBDB0548B5DC8D /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
141142
D339AEE11BAA808800CFA651 /* RNKeyboardEvents.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNKeyboardEvents.xcodeproj; path = "../node_modules/react-native-keyboardevents/RNKeyboardEvents.xcodeproj"; sourceTree = "<group>"; };
143+
D36840111BEA726800A9B3F4 /* TestRunnerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestRunnerManager.m; path = Sample/TestRunnerManager.m; sourceTree = "<group>"; };
142144
D36DCD9F1BD4A92600B3EB2F /* Staging.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Staging.xcconfig; sourceTree = "<group>"; };
143145
D36DCDA01BD4A92600B3EB2F /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
144146
D374ADB51BA929B900453203 /* RNKeychain.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNKeychain.xcodeproj; path = "../node_modules/react-native-keychain/RNKeychain.xcodeproj"; sourceTree = "<group>"; };
@@ -246,6 +248,7 @@
246248
13B07FAE1A68108700A75B9A /* Sample */ = {
247249
isa = PBXGroup;
248250
children = (
251+
D36840111BEA726800A9B3F4 /* TestRunnerManager.m */,
249252
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
250253
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
251254
13B07FB01A68108700A75B9A /* AppDelegate.m */,
@@ -636,6 +639,7 @@
636639
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
637640
D374ADD31BA9336E00453203 /* EnvironmentManager.m in Sources */,
638641
13B07FC11A68108700A75B9A /* main.m in Sources */,
642+
D36840121BEA726800A9B3F4 /* TestRunnerManager.m in Sources */,
639643
);
640644
runOnlyForDeploymentPostprocessing = 0;
641645
};

ios/Sample/AppDelegate.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
4747

4848

4949
#ifdef TEST_ENVIRONMENT
50-
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
50+
jsCodeLocation = [NSURL URLWithString:@"http://localhost:9091/index.ios.bundle?platform=ios"];
5151
#else
5252
#if TARGET_IPHONE_SIMULATOR
5353
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];

ios/Sample/TestRunnerManager.m

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// TestRunnerManager.m
3+
// Tasker
4+
//
5+
// Created by Brian Leonard on 8/9/15.
6+
// Copyright (c) 2015 TaskRabbit. All rights reserved.
7+
//
8+
9+
#import "RCTBridgeModule.h"
10+
#import "RCTBridge.h"
11+
12+
@interface TestRunnerManager : NSObject <RCTBridgeModule>
13+
14+
@end
15+
16+
17+
@implementation TestRunnerManager
18+
19+
RCT_EXPORT_MODULE()
20+
21+
RCT_EXPORT_METHOD(reset:(RCTResponseSenderBlock)callback)
22+
{
23+
// delete all data in documents directory
24+
NSString *folderPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
25+
NSError *error = nil;
26+
for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:&error]) {
27+
// If you want to skip certain files
28+
if ([file containsString:@".skip"]) {
29+
// but be sure to clear them out if applicable from your js code
30+
continue;
31+
}
32+
33+
// otherwise, delete
34+
[[NSFileManager defaultManager] removeItemAtPath:[folderPath stringByAppendingPathComponent:file] error:&error];
35+
}
36+
37+
// reload the app
38+
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil userInfo:nil];
39+
40+
callback(@[]);
41+
}
42+
43+
@end

package.json

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,24 @@
44
"private": true,
55
"scripts": {
66
"start": "node_modules/react-native/packager/packager.sh",
7+
"test": "npm run mocha-test test",
8+
"mocha-test": "mocha --recursive --compilers js:babel/register",
79
"compile:test": "TARGET=test node tasks/compile.js",
810
"compile:staging": "TARGET=staging node tasks/compile.js",
911
"install:staging": "PHONE=true TARGET=staging node tasks/compile.js"
1012
},
13+
"babel": {
14+
"whitelist": [
15+
"es6.modules",
16+
"es6.classes",
17+
"es6.arrowFunctions",
18+
"es6.constants",
19+
"es6.spread",
20+
"es6.blockScoping",
21+
"es6.parameters",
22+
"es6.destructuring"
23+
]
24+
},
1125
"dependencies": {
1226
"events": "^1.0.2",
1327
"flux": "^2.1.1",
@@ -22,6 +36,18 @@
2236
"superagent": "^1.4.0"
2337
},
2438
"devDependencies": {
25-
"ios-deploy": "^1.7.0"
39+
"appium": "1.4.13",
40+
"babel": "^5.6.0",
41+
"chai": "^3.2.0",
42+
"chai-as-promised": "^5.1.0",
43+
"colors": "^1.0.3",
44+
"ios-deploy": "^1.7.0",
45+
"koa": "^0.21.0",
46+
"koa-bodyparser": "^2.0.1",
47+
"mocha": "^2.2.5",
48+
"promise": "^7.0.4",
49+
"underscore": "^1.8.3",
50+
"wd": "^0.3",
51+
"yiewd": "^0.6.0"
2652
}
2753
}

0 commit comments

Comments
 (0)