Skip to content

Commit ef29001

Browse files
committed
Merge pull request #9 from taskrabbit/tests
Integration Tests
2 parents 272a5c7 + 45dd157 commit ef29001

29 files changed

+1397
-12
lines changed

.travis.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Hepburn
2+
language: objective-c
3+
osx_image: xcode7
4+
xcode_sdk: iphonesimulator9.0
5+
6+
cache:
7+
directories:
8+
- node_modules
9+
- ios/Pods
10+
- ~/.nvm
11+
12+
before_install:
13+
- which nvm || curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.29.0/install.sh | bash
14+
- export NVM_DIR=~/.nvm
15+
- source ~/.nvm/nvm.sh --install
16+
- nvm install 4.0
17+
- brew update
18+
- brew reinstall xctool
19+
- brew reinstall watchman
20+
- npm install
21+
- gem install xcpretty
22+
- gem install cocoapods
23+
- pod install --project-directory=ios
24+
25+
before_script:
26+
- npm run compile:test
27+
28+
script:
29+
- npm test

App/Components/SegmentedControl.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ var SegmentedControl = React.createClass({
1616
var out = [];
1717
for(var i = 0; i < this.props.items.length; i++) {
1818
var item = this.props.items[i];
19+
var testID = null;
20+
if (!item.testID && this.props.appendTestId) {
21+
testID = 'seg' + item.title + '_' + this.props.appendTestId;
22+
}
1923
out.push(
20-
<Segment {...item} key={"item" + i} currentRoute={this.props.currentRoute} />
24+
<Segment {...item} key={"item" + i} testID={testID} currentRoute={this.props.currentRoute} />
2125
);
2226
};
2327
return out;
@@ -58,6 +62,7 @@ var Segment = React.createClass({
5862
<TouchableHighlight
5963
style={styles.flex}
6064
underlayColor='#FFFFFF'
65+
testID={this.props.testID}
6166
onPress={this.onSelection}
6267
>
6368
<View style={[styles.button, styles.linkButton]}>

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/Mixins/ListHelper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ var ListHelper = {
9494
renderHeader: function() {
9595
if (!this.props.segment) return null;
9696
return (
97-
<SegmentedControl currentRoute={this.props.currentRoute} {...this.props.segment} />
97+
<SegmentedControl currentRoute={this.props.currentRoute} appendTestId={this.getUsername()} {...this.props.segment} />
9898
);
9999
},
100100

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;

App/Screens/FollowList.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ var FollowList = React.createClass({
5151
FollowActions.fetchList(this.getUsername(), function(error) {
5252
// TODO: handle error
5353
if (error) {
54-
alert(error.message);
54+
console.log(error.message);
5555
}
5656
});
5757
}

App/Screens/PostList.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ var PostList = React.createClass({
5050
PostActions.fetchList(this.getUsername(), function(error) {
5151
// TODO: handle error
5252
if (error) {
53-
alert(error.message);
53+
console.log(error.message);
5454
}
5555
});
5656
}

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ In the `server` directory
2626

2727
It has sample data in the `models.js` file. For example, there is a user bleonard (password: "sample") that you can log in as.
2828

29+
### Tests
30+
31+
The integration tests are run using [Appium](http://appium.io/).
32+
33+
To run tests:
34+
35+
* Make sure you have the 9.0 simulators installed in XCode
36+
* Compile app for the test environment: `npm run compile:test`
37+
* Launch simulator and tests: `npm test`
38+
2939
### Compiling
3040

3141
You can compile and put it on the phone with: `npm run install:staging`
@@ -36,7 +46,6 @@ Not that there's a staging server at this point, but it's an example of how to c
3646

3747
We'll get there, but we're still working on the iOS version.
3848

39-
4049
# Current Concepts
4150

4251
### Navigation
@@ -85,10 +94,6 @@ We are currently sharing code through mixins. Some of them might be generally us
8594

8695
Let us know if you think one of these would be helpful
8796

88-
#### Testing
89-
90-
We're running our integration tests through Appium and it's pretty neat.
91-
9297
#### Extensions
9398

9499
We've been trying out ways to not use mixins.

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
};

0 commit comments

Comments
 (0)