Skip to content

Integration Tests #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Nov 6, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Hepburn
language: objective-c
osx_image: xcode7
xcode_sdk: iphonesimulator9.0

cache:
directories:
- node_modules
- ios/Pods
- ~/.nvm

before_install:
- which nvm || curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.29.0/install.sh | bash
- export NVM_DIR=~/.nvm
- source ~/.nvm/nvm.sh --install
- nvm install 4.0
- brew update
- brew reinstall xctool
- brew reinstall watchman
- npm install
- gem install xcpretty
- gem install cocoapods
- pod install --project-directory=ios

before_script:
- npm run compile:test

script:
- npm test
7 changes: 6 additions & 1 deletion App/Components/SegmentedControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ var SegmentedControl = React.createClass({
var out = [];
for(var i = 0; i < this.props.items.length; i++) {
var item = this.props.items[i];
var testID = null;
if (!item.testID && this.props.appendTestId) {
testID = 'seg' + item.title + '_' + this.props.appendTestId;
}
out.push(
<Segment {...item} key={"item" + i} currentRoute={this.props.currentRoute} />
<Segment {...item} key={"item" + i} testID={testID} currentRoute={this.props.currentRoute} />
);
};
return out;
Expand Down Expand Up @@ -58,6 +62,7 @@ var Segment = React.createClass({
<TouchableHighlight
style={styles.flex}
underlayColor='#FFFFFF'
testID={this.props.testID}
onPress={this.onSelection}
>
<View style={[styles.button, styles.linkButton]}>
Expand Down
3 changes: 2 additions & 1 deletion App/Constants/AppConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ module.exports = keyMirror({
NAVBAR_UPDATE: null,
POST_LIST_UPDATED: null,
POST_ADDED: null,
FOLLOW_LIST_UPDATED: null
FOLLOW_LIST_UPDATED: null,
TEST_COMPONENT_ROUTE: null,
});
2 changes: 1 addition & 1 deletion App/Mixins/ListHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ var ListHelper = {
renderHeader: function() {
if (!this.props.segment) return null;
return (
<SegmentedControl currentRoute={this.props.currentRoute} {...this.props.segment} />
<SegmentedControl currentRoute={this.props.currentRoute} appendTestId={this.getUsername()} {...this.props.segment} />
);
},

Expand Down
4 changes: 4 additions & 0 deletions App/Platform/StatusBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ var {
var StatusBar = {
setNetworkActive: function(active) {
StatusBarIOS.setNetworkActivityIndicatorVisible(active);
},

setHidden: function(hidden) {
StatusBarIOS.setHidden(hidden);
}
};

Expand Down
11 changes: 11 additions & 0 deletions App/Root.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var Launch = require('./Root/Launch');
var LoggedOut = require('./Root/LoggedOut');
var LoggedIn = require('./Root/LoggedIn');
var Launcher = require('./Root/Launcher');
var TestRunner = require('./Root/TestRunner');

var AppActions = require('./Actions/AppActions');
var CurrentUserStore = require('./Stores/CurrentUserStore');
Expand Down Expand Up @@ -64,6 +65,8 @@ var Root = React.createClass({
},

renderContent: function() {
if (this.state.routeUnderTest) return null;

var routeStack = this.state.routeStack;
if(this.state.user.isLoggedIn()) {
return(<LoggedIn ref="current" routeStack={routeStack} />);
Expand All @@ -78,6 +81,14 @@ var Root = React.createClass({
if (!this.state.user || !this.state.environment) {
return(<Launch ref="current" />);
}
else if (this.state.environment.data.name === 'test') {
return (
<View style={{flex:1}}>
<TestRunner routeUnderTest={this.state.routeUnderTest}/>
{this.renderContent()}
</View>
);
}
else {
return this.renderContent();
}
Expand Down
5 changes: 5 additions & 0 deletions App/Root/Launcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ var Launcher = {
// root.setState({loading: false});
//});
break;
case AppConstants.TEST_COMPONENT_ROUTE:
var TestComponents = require("../Root/TestComponents");
action.routeUnderTest.component = TestComponents.find(action.routeUnderTest.component);
rootComponent.setState({routeUnderTest: action.routeUnderTest});
break;
default:
break;
}
Expand Down
11 changes: 11 additions & 0 deletions App/Root/TestComponents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
var components = {
'Components/SimpleListItem': require("../Components/SimpleListItem")
};

var TestComponents = {
find(name) {
return components[name];
}
};

module.exports = TestComponents;
134 changes: 134 additions & 0 deletions App/Root/TestRunner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
var React = require('react-native');

var {
Text,
View,
StyleSheet,
TouchableOpacity,
NativeModules,
} = React;

var client = require('../Api/HTTPClient');
var Dispatcher = require('../Dispatcher');

var TestRunnerManager = require('react-native').NativeModules.TestRunnerManager;
var DevMenu = require('react-native').NativeModules.DevMenu;

var StatusBar = require('../Platform/StatusBar');

var TestRunner = React.createClass({
componentDidMount: function() {
StatusBar.setHidden(true);
},

resetApp: function() {
TestRunnerManager.reset(function() {
console.log("reset")
});
},

showDevMenu: function() {
DevMenu.show();
},

hookConsole: function() {
global.console = {
log: function(...args) {
client.post("test/console.json", {level: 'log', arguments: args});
},
error: function(...args) {
client.post("test/console.json", {level: 'error', arguments: args});
},
warn: function(...args) {
client.post("test/console.json", {level: 'warn', arguments: args});
}
}
},

bootstrapApp: function() {
client.get("test/bootstrap.json", null,
(error, response) => {
if (error) {
console.log("BOOTSTRAP ERROR");
alert('"BOOTSTRAP ERROR"');
}
else {
this.hookConsole();
if (response.actions) {
for (var i=0; i<response.actions.length; i++) {
var options = response.actions[i].options || {};
options.actionType = response.actions[i].type;

switch(options.actionType) {
default:
Dispatcher.dispatch(options);
}
}
}
}
});
},

onLoadedTest: function(component) {
if (this.props.routeUnderTest && this.props.routeUnderTest.setState) {
component.setState(this.props.routeUnderTest.setState);
}
},

renderRouteUnderTest: function() {
if (!this.props.routeUnderTest) return null;

var Component = this.props.routeUnderTest.component;
var testProps = this.props.routeUnderTest.passProps || {};
return (
<Component ref={this.onLoadedTest} {...testProps} />
);
},

render: function() {
var containerStyle = (this.props.routeUnderTest ? styles.withComponent : styles.noComponent);
return (
<View style={containerStyle}>
{this.renderRouteUnderTest()}
<View style={styles.bar}>
<TouchableOpacity onPress={this.resetApp}>
<Text style={styles.action}>
ResetTest
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.bootstrapApp}>
<Text style={styles.action}>
Bootstrap
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showDevMenu}>
<Text style={styles.action}>
DevMenu
</Text>
</TouchableOpacity>
</View>
</View>
)
}
});

var styles = StyleSheet.create({
withComponent: {
flex: 1
},
noComponent: {
height: 10
},
bar: {
height: 10,
backgroundColor: 'red',
justifyContent: 'center',
flexDirection: 'row'
},
action: {
marginHorizontal: 4,
fontSize: 10
}
});

module.exports = TestRunner;
2 changes: 1 addition & 1 deletion App/Screens/FollowList.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ var FollowList = React.createClass({
FollowActions.fetchList(this.getUsername(), function(error) {
// TODO: handle error
if (error) {
alert(error.message);
console.log(error.message);
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion App/Screens/PostList.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ var PostList = React.createClass({
PostActions.fetchList(this.getUsername(), function(error) {
// TODO: handle error
if (error) {
alert(error.message);
console.log(error.message);
}
});
}
Expand Down
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ In the `server` directory

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

### Tests

The integration tests are run using [Appium](http://appium.io/).

To run tests:

* Make sure you have the 9.0 simulators installed in XCode
* Compile app for the test environment: `npm run compile:test`
* Launch simulator and tests: `npm test`

### Compiling

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

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


# Current Concepts

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

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

#### Testing

We're running our integration tests through Appium and it's pretty neat.

#### Extensions

We've been trying out ways to not use mixins.
Expand Down
4 changes: 4 additions & 0 deletions ios/Sample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
684860A3F12E466838065EED /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A565388F7CDBDB0548B5DC8D /* libPods.a */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
D339AEF21BAA809100CFA651 /* libRNKeyboardEvents.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D339AEF01BAA808800CFA651 /* libRNKeyboardEvents.a */; };
D36840121BEA726800A9B3F4 /* TestRunnerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D36840111BEA726800A9B3F4 /* TestRunnerManager.m */; };
D374ADC51BA929C500453203 /* libRNKeychain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D374ADC41BA929BA00453203 /* libRNKeychain.a */; };
D374ADD31BA9336E00453203 /* EnvironmentManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D374ADD21BA9336E00453203 /* EnvironmentManager.m */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -139,6 +140,7 @@
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
A565388F7CDBDB0548B5DC8D /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
D339AEE11BAA808800CFA651 /* RNKeyboardEvents.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNKeyboardEvents.xcodeproj; path = "../node_modules/react-native-keyboardevents/RNKeyboardEvents.xcodeproj"; sourceTree = "<group>"; };
D36840111BEA726800A9B3F4 /* TestRunnerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestRunnerManager.m; path = Sample/TestRunnerManager.m; sourceTree = "<group>"; };
D36DCD9F1BD4A92600B3EB2F /* Staging.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Staging.xcconfig; sourceTree = "<group>"; };
D36DCDA01BD4A92600B3EB2F /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
D374ADB51BA929B900453203 /* RNKeychain.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNKeychain.xcodeproj; path = "../node_modules/react-native-keychain/RNKeychain.xcodeproj"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -246,6 +248,7 @@
13B07FAE1A68108700A75B9A /* Sample */ = {
isa = PBXGroup;
children = (
D36840111BEA726800A9B3F4 /* TestRunnerManager.m */,
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
Expand Down Expand Up @@ -636,6 +639,7 @@
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
D374ADD31BA9336E00453203 /* EnvironmentManager.m in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
D36840121BEA726800A9B3F4 /* TestRunnerManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Loading