From b607834e3198a9b11d91611e22ed032b24f014dd Mon Sep 17 00:00:00 2001 From: Vijay Sharma Date: Sun, 8 May 2016 10:39:09 -0400 Subject: [PATCH] Expanded Example application --- example/android/app/app.iml | 14 +- example/index.android.js | 61 +-------- example/index.ios.js | 62 +-------- example/js/actions.js | 53 ++++++++ example/js/app.js | 22 ++++ example/js/list.js | 118 +++++++++++++++++ example/js/main.js | 243 +++++++++++++++++++++++++++++++++++ example/js/projects.js | 74 +++++++++++ example/js/reducers/index.js | 47 +++++++ example/js/session.js | 79 ++++++++++++ example/js/signin.js | 118 +++++++++++++++++ example/package.json | 7 +- 12 files changed, 771 insertions(+), 127 deletions(-) create mode 100644 example/js/actions.js create mode 100644 example/js/app.js create mode 100644 example/js/list.js create mode 100644 example/js/main.js create mode 100644 example/js/projects.js create mode 100644 example/js/reducers/index.js create mode 100644 example/js/session.js create mode 100644 example/js/signin.js diff --git a/example/android/app/app.iml b/example/android/app/app.iml index b334aeb..25b0e05 100644 --- a/example/android/app/app.iml +++ b/example/android/app/app.iml @@ -61,13 +61,6 @@ - - - - - - - @@ -75,6 +68,13 @@ + + + + + + + diff --git a/example/index.android.js b/example/index.android.js index f79ca79..dedced3 100644 --- a/example/index.android.js +++ b/example/index.android.js @@ -1,61 +1,4 @@ -/** - * Sample React Native App - * https://github.com/facebook/react-native - */ - -import React, { - AppRegistry, - Component, - StyleSheet, - Text, - View -} from 'react-native'; - -const TestFairyBridge = require('react-native-testfairy'); - -class TestFairy extends Component { - componentWillMount() { - TestFairyBridge.begin('5b3af35e59a1e074e2d50675b1b629306cf0cfbd'); - } - - componentDidMount() { - TestFairyBridge.hideView(this.refs.instructions); - } - - render() { - return ( - - - Welcome to React Native! - - - To get started, edit index.android.js - - - Shake or press menu button for dev menu - - - ); - } -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#F5FCFF', - }, - welcome: { - fontSize: 20, - textAlign: 'center', - margin: 10, - }, - instructions: { - textAlign: 'center', - color: '#333333', - marginBottom: 5, - }, -}); +const {AppRegistry} = require('react-native'); +const TestFairy = require('./js/app'); AppRegistry.registerComponent('TestFairy', () => TestFairy); diff --git a/example/index.ios.js b/example/index.ios.js index 6dee44a..dedced3 100644 --- a/example/index.ios.js +++ b/example/index.ios.js @@ -1,62 +1,4 @@ -/** - * Sample React Native App - * https://github.com/facebook/react-native - */ - -import React, { - AppRegistry, - Component, - StyleSheet, - Text, - View -} from 'react-native'; - -const TestFairyBridge = require('react-native-testfairy'); - -class TestFairy extends Component { - componentWillMount() { - TestFairyBridge.begin('5b3af35e59a1e074e2d50675b1b629306cf0cfbd'); - } - - componentDidMount() { - TestFairyBridge.hideView(this.refs.instructions); - } - - render() { - return ( - - - Welcome to React Native! - - - To get started, edit index.ios.js - - - Press Cmd+R to reload,{'\n'} - Cmd+D or shake for dev menu - - - ); - } -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#F5FCFF', - }, - welcome: { - fontSize: 20, - textAlign: 'center', - margin: 10, - }, - instructions: { - textAlign: 'center', - color: '#333333', - marginBottom: 5, - }, -}); +const {AppRegistry} = require('react-native'); +const TestFairy = require('./js/app'); AppRegistry.registerComponent('TestFairy', () => TestFairy); diff --git a/example/js/actions.js b/example/js/actions.js new file mode 100644 index 0000000..2245857 --- /dev/null +++ b/example/js/actions.js @@ -0,0 +1,53 @@ +const base64 = require('base-64'); +const endpoint = "https://app.testfairy.com/api/1"; + +module.exports = { + projects: function(login) { + var authentication = base64.encode(`${login.email}:${login.token}`); + var url = endpoint + "/projects" + return fetch(url, { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Basic ${authentication}` + } + }) + .then(res => res.json()); + }, + builds: function(project, login) { + var authentication = base64.encode(`${login.email}:${login.token}`); + var url = endpoint + `/projects/${project.id}/builds` + return fetch(url, { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Basic ${authentication}` + } + }) + .then(res => res.json()); + }, + sessions: function(project, build, login) { + var authentication = base64.encode(`${login.email}:${login.token}`); + var url = endpoint + `/projects/${project.id}/builds/${build.id}/sessions` + return fetch(url, { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Basic ${authentication}` + } + }) + .then(res => res.json()); + }, + session: function(project, build, session, login) { + var authentication = base64.encode(`${login.email}:${login.token}`); + var url = endpoint + `/projects/${project.id}/builds/${build.id}/sessions/${session.id}` + return fetch(url, { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Basic ${authentication}` + } + }) + .then(res => res.json()); + } +} \ No newline at end of file diff --git a/example/js/app.js b/example/js/app.js new file mode 100644 index 0000000..50ab162 --- /dev/null +++ b/example/js/app.js @@ -0,0 +1,22 @@ +'use strict'; + +import React from 'react-native' +import { createStore, applyMiddleware } from 'redux' +import { Provider } from 'react-redux' +import thunk from 'redux-thunk' + +import reducers from './reducers' +import Main from './main' + +const createStoreWithMW = applyMiddleware(thunk)(createStore) +const store = createStoreWithMW(reducers) + +module.exports = React.createClass({ + render: function () { + return ( + +
+ + ); + } +}); diff --git a/example/js/list.js b/example/js/list.js new file mode 100644 index 0000000..be7a968 --- /dev/null +++ b/example/js/list.js @@ -0,0 +1,118 @@ +'use strict'; + +var React = require('react-native'); +var { + View, + Text, + StyleSheet, + ListView, + RefreshControl +} = React; + +import NavBar, { NavButton, NavButtonText, NavTitle } from 'react-native-nav' + +module.exports = React.createClass({ + getInitialState: function() { + return { + refreshing: false, + dataSource: new ListView.DataSource({ + rowHasChanged: (row1, row2) => row1 !== row2 + }) + } + }, + + componentDidMount: function() { + this.onRefresh(); + }, + + componentWillReceiveProps: function(props) { + const {dataSource} = this.state; + this.setState({ + dataSource: dataSource.cloneWithRows(props.items) + }); + }, + + render: function () { + const {dataSource} = this.state; + var navigation; + if (this.props.hasBack) { + navigation = ( + + this.props.navigator.pop()}>{"Back"} + {this.props.title} + this.props.onLogout()}>{'Logout'} + + ); + } else { + navigation = ( + + {' '} + {this.props.title} + this.props.onLogout()}>{'Logout'} + + ); + } + + if (dataSource.getRowCount() == 0 && this.state.refreshing == false) { + return ( + + {navigation} + + No {this.props.title} found! + + + ); + } + + return ( + + {navigation} + + + + + ); + }, + + renderRow: function(item) { + return this.props.renderRow(item, this.props); + }, + + onRefresh: function() { + const {fetch} = this.props; + this.setState({refreshing: true}); + fetch(this.props) + .then(() => { + this.setState({refreshing: false}); + }); + } +}); + +var styles = StyleSheet.create({ + emptyView: { + flex: 1, + justifyContent: 'center', + alignItems: 'center' + }, + container: { + flex: 1 + }, + statusBar: { + backgroundColor: '#000', + }, + navBar: { + backgroundColor: '#212121', + }, + title: { + color: '#fff', + flex: 1 + }, + listView: { + + } +}); \ No newline at end of file diff --git a/example/js/main.js b/example/js/main.js new file mode 100644 index 0000000..fee1b91 --- /dev/null +++ b/example/js/main.js @@ -0,0 +1,243 @@ +'use strict'; + +const React = require('react-native'); +const {Platform, StyleSheet, Navigator} = React; +const {View, Text, TouchableHighlight} = React; +const {connect} = require('react-redux'); +const TestFairyBridge = require('react-native-testfairy'); + +const SignIn = require('./signin'); +const Projects = require('./projects'); +const List = require('./list'); +const Session = require('./session'); +const actions = require('./actions'); + +var Main = React.createClass({ + componentWillMount() { + TestFairyBridge.begin('5b3af35e59a1e074e2d50675b1b629306cf0cfbd'); + }, + + render: function () { + return ( + { + if (Platform.OS === 'android') { + return Navigator.SceneConfigs.FloatFromBottomAndroid; + } else { + return Navigator.SceneConfigs.FloatFromRight; + } + }} + renderScene={this.renderScene} + /> + ); + }, + + isLoggedIn: function() { + var {login} = this.props; + return login.email && login.token; + }, + + renderScene: function(route, navigator) { + var { + login, + fetchProjects, projects, + fetchBuilds, builds, + fetchSessions, sessions, + fetchSession, session + } = this.props; + + if (route.session) { + return () + } + + if (route.sessions) { + return ( + ); + } + + if (route.builds) { + return ( + ); + } + + // if (route.projects) { + if (this.isLoggedIn()) { + return ( + + ); + } + + return ( + + ); + }, + + renderProjectItem: function(project, props) { + return ( + { + this.refs.navigator.push({builds: true, project: project}) + }} underlayColor='#CCC'> + + {project.name} + {project.packageName} + + + ); + }, + + renderBuildItem: function(build, props) { + return ( + { + this.refs.navigator.push({sessions: true, build: build, project: props.project}) + }} underlayColor='#CCC'> + + {build.appName} + {build.version} + + + ); + }, + + renderSessionItem: function(session, props) { + return ( + { + this.refs.navigator.push({session: true, build: props.build, project: props.project, session: session}) + }} underlayColor='#CCC'> + + {session.testerEmail} + Session #{session.id} - {session.duration} + + + ); + } +}); + +module.exports = connect(state => { + return {...state} +}, (dispatch) => { + return { + doLogin: function(email, token) { + dispatch({ + type: 'LOGIN', + item: {email, token} + }); + }, + doLogout: function() { + dispatch({ + type: 'LOGOUT' + }); + }, + fetchProjects: function(props) { + var login = props.login; + return actions.projects(login) + .then(res => { + dispatch({ + type: 'NEW_PROJECTS', + items: res.projects + }) + }, err => { + console.log(err); + }) + }, + fetchBuilds: function(props) { + var {project, login} = props; + return actions.builds(project, login) + .then(res => { + dispatch({ + type: 'NEW_BUILDS', + items: res.builds + }); + }, err => { + console.log(err); + }) + }, + fetchSessions: function(props) { + var {project, build, login} = props; + return actions.sessions(project, build, login) + .then(res => { + dispatch({ + type: 'NEW_SESSIONS', + items: res.sessions + }); + }, err => { + console.log(err); + }) + }, + fetchSession: function(props) { + var {project, build, login, session} = props; + return actions.session(project, build, session, login) + .then(res => { + console.log(res); + return res; + }) + .then(res => { + dispatch({ + type: 'SET_SELECTED_SESSION', + item: res.session + }); + }, err => { + console.log(err); + }) + } + } +})(Main); + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + projectRow: { + padding: 16 + }, + projectRowTitle: { + fontSize: 18 + }, + projectRowSubTitle: { + color: '#666' + } +}); diff --git a/example/js/projects.js b/example/js/projects.js new file mode 100644 index 0000000..49fe8e7 --- /dev/null +++ b/example/js/projects.js @@ -0,0 +1,74 @@ +'use strict'; + +var React = require('react-native'); +var { + View, + Text, + StyleSheet, + ListView +} = React; + +import NavBar, { NavButton, NavButtonText, NavTitle } from 'react-native-nav' + +module.exports = React.createClass({ + getInitialState: function() { + return { + dataSource: new ListView.DataSource({ + rowHasChanged: (row1, row2) => row1 !== row2 + }) + } + }, + + componentDidMount: function() { + const {login, fetchProjects} = this.props; + fetchProjects(login); + }, + + componentWillReceiveProps: function(props) { + const {dataSource} = this.state; + this.setState({ + dataSource: dataSource.cloneWithRows(props.projects) + }); + }, + + render: function () { + const {dataSource} = this.state; + return ( + + + TestFairy + + + + ); + }, + + renderRow: function(project) { + return ( + {project.name} + ); + } +}); + +var styles = StyleSheet.create({ + container: { + flex: 1 + }, + statusBar: { + backgroundColor: '#000', + }, + navBar: { + backgroundColor: '#212121', + }, + title: { + color: '#fff', + flex: 1 + }, + listView: { + + } +}); \ No newline at end of file diff --git a/example/js/reducers/index.js b/example/js/reducers/index.js new file mode 100644 index 0000000..f46d0af --- /dev/null +++ b/example/js/reducers/index.js @@ -0,0 +1,47 @@ +'use strict'; + +var { combineReducers } = require('redux'); + +var initial = { + email: undefined, + token: undefined +}; + +module.exports = combineReducers({ + login: function(state = initial, action) { + if (action.type == 'LOGIN') { + return action.item; + } else if (action.type == 'LOGOUT') { + return {}; + } + + return state; + }, + projects: function(state = [], action) { + if (action.type === 'NEW_PROJECTS') { + return action.items; + } + return state; + }, + builds: function(state = [], action) { + if (action.type === 'NEW_BUILDS') { + return action.items; + } + return state; + }, + sessions: function(state = [], action) { + if (action.type === 'NEW_SESSIONS') { + return action.items; + } + return state; + }, + session: function(state = {}, action) { + if (action.type === 'SET_SELECTED_SESSION') { + return action.item; + } else if (action.type === 'CLEAR_SELECTED_SESSION') { + return {}; + } + + return state; + } +}); \ No newline at end of file diff --git a/example/js/session.js b/example/js/session.js new file mode 100644 index 0000000..1b2e48d --- /dev/null +++ b/example/js/session.js @@ -0,0 +1,79 @@ +'use strict'; + +var React = require('react-native'); +var { + View, + Text, + StyleSheet, + ScrollView +} = React; + +import NavBar, { NavButton, NavButtonText, NavTitle } from 'react-native-nav' + +module.exports = React.createClass({ + getInitialState: function() { + return { + session: this.props.session + }; + }, + componentWillReceiveProps: function(props) { + var session = Object.assign({}, this.state.session, props.session); + this.setState({ + session: session + }); + }, + componentDidMount: function() { + this.onRefresh(); + }, + onRefresh: function() { + const {fetch} = this.props; + fetch(this.props); + }, + render: function () { + return ( + + + this.props.navigator.pop()}>{"Back"} + {this.props.project.name} (#{this.props.session.id}) + this.props.onLogout()}>{'Logout'} + + + + {this.sessionItem('Package: ', this.props.project.packageName)} + {this.sessionItem('Version: ', this.props.build.version)} + {this.sessionItem('Tester: ', this.props.session.testerEmail)} + {this.sessionItem('Duration: ', this.props.session.duration)} + {this.sessionItem('Device: ', this.props.session.device)} + + + + ); + }, + sessionItem: function(key, value) { + return + {key}{value} + + } +}); + +var styles = StyleSheet.create({ + container: { + flex: 1 + }, + statusBar: { + backgroundColor: '#000', + }, + navBar: { + backgroundColor: '#212121', + }, + title: { + color: '#fff' + }, + key: { + color: '#CCC', + fontSize: 20 + }, + value: { + fontSize: 20 + } +}); \ No newline at end of file diff --git a/example/js/signin.js b/example/js/signin.js new file mode 100644 index 0000000..34afb71 --- /dev/null +++ b/example/js/signin.js @@ -0,0 +1,118 @@ +'use strict'; + +var React = require('react-native'); +var { + View, + Text, + StatusBar, + StyleSheet, + TextInput, + TouchableHighlight +} = React; + +const TestFairyBridge = require('react-native-testfairy'); + +module.exports = React.createClass({ + getInitialState: function() { + return { + email: this.props.login.email, + token: this.props.login.token + }; + }, + + componentDidMount: function() { + TestFairyBridge.hideView(this.refs.emailInput); + TestFairyBridge.hideView(this.refs.apiTokenInput); + }, + + render: function () { + return ( + + + + TestFairy + + this.setState({email: text})} + value={this.state.email} + placeholder={'Email'} + autoCapitalize={'none'} + keyboardType={'email-address'} + autoCorrect={false} + /> + + + this.setState({token: text})} + value={this.state.token} + placeholder={'API Token'} + /> + + + Sign In + + + + + + + ); + }, + + doLogin: function() { + this.props.onLogin(this.state.email, this.state.token); + } +}); + +var styles = StyleSheet.create({ + container: { + flex: 1 + }, + top: { + flex: 5, + marginTop: 100, + }, + bottom: { + flex: 4 + }, + header: { + fontSize: 25, + padding: 4, + textAlign: 'center', + marginBottom: 12 + }, + inputWrapper: { + + }, + input: { + padding: 4, + height: 40, + width: 350, + borderColor: 'gray', + borderWidth: 1, + borderRadius: 5, + margin: 5, + alignSelf: 'center', + flex: 1, + textAlign: 'center' + }, + buttonWrapper: { + margin: 12, + alignItems: 'center' + }, + button: { + padding: 8, + textAlign: 'center', + fontSize: 20, + backgroundColor: '#000000', + color: '#FFFFFF', + borderRadius: 5, + width: 350 + } +}); \ No newline at end of file diff --git a/example/package.json b/example/package.json index 41e3188..b94db6a 100644 --- a/example/package.json +++ b/example/package.json @@ -6,8 +6,13 @@ "start": "node node_modules/react-native/local-cli/cli.js start" }, "dependencies": { + "base-64": "^0.1.0", "react": "^0.14.8", "react-native": "^0.23.1", - "react-native-testfairy": "file:../" + "react-native-nav": "^1.1.2", + "react-native-testfairy": "file:../", + "react-redux": "~4.3.0", + "redux": "~3.2.1", + "redux-thunk": "~1.0.3" } }