...
 
Commits (2)
......@@ -144,6 +144,7 @@ exports[`Activity component renders correctly 1`] = `
},
}
}
testID=""
/>
<RemindAction
entity={
......
......@@ -14,6 +14,7 @@ exports[`Activity component renders correctly 1`] = `
},
]
}
testID="ActivityView"
>
<Pinned
entity={
......@@ -217,6 +218,7 @@ exports[`Activity component renders correctly 1`] = `
}
}
onTranslate={[Function]}
testID=""
toggleEdit={[Function]}
/>
</View>
......
......@@ -13,12 +13,9 @@ exports[`Remind owner component renders correctly 1`] = `
}
}
>
<Icon
<Themed.Icon
color="rgb(70, 144, 214)"
name="repeat"
raised={false}
reverse={false}
reverseColor="white"
size={16}
style={
Object {
......@@ -26,7 +23,6 @@ exports[`Remind owner component renders correctly 1`] = `
"marginRight": 8,
}
}
underlayColor="white"
/>
<TouchableOpacity
activeOpacity={0.2}
......
import 'react-native';
import React from 'react';
import { shallow } from 'enzyme';
import { Button } from 'react-native-elements';
import ForgotPassword from '../../src/auth/ForgotPassword';
import authService from '../../src/auth/AuthService';
jest.mock('../../src/auth/AuthService');
// Note: test renderer must be required after react-native.
......@@ -37,7 +37,7 @@ describe('ForgotPassword component', () => {
});
// press send
await render.find('Button').at(1).simulate('press');
await render.find(Button).at(1).simulate('press');
// expect auth service login to be called once
expect(authService.forgot).toBeCalled();
......@@ -57,7 +57,7 @@ describe('ForgotPassword component', () => {
const render = wrapper.dive();
// press go back
await render.find('Button').at(0).simulate('press');
await render.find(Button).at(0).simulate('press');
// expect onLogin to be called once
expect(mockFn).toBeCalled();
......
......@@ -2,6 +2,7 @@ import 'react-native';
import React from 'react';
import { Text, TouchableOpacity } from "react-native";
import { shallow } from 'enzyme';
import { Button } from 'react-native-elements';
import LoginForm from '../../src/auth/LoginForm';
import authService from '../../src/auth/AuthService';
......@@ -41,7 +42,7 @@ describe('LoginForm component', () => {
});
// press login
await render.find('Button').at(1).simulate('press');
await render.find(Button).at(1).simulate('press');
// check state
expect(wrapper.state().password).toEqual('data');
......
......@@ -2,6 +2,7 @@ import 'react-native';
import React from 'react';
import { Alert } from "react-native";
import { shallow } from 'enzyme';
import { Button, CheckBox } from 'react-native-elements';
import RegisterForm from '../../src/auth/RegisterForm';
import authService from '../../src/auth/AuthService';
......@@ -45,7 +46,7 @@ describe('RegisterForm component', () => {
let inputs = render.find('TextInput');
// should have 4 inputs
expect(inputs.length).toBe(3);
expect(inputs.length).toBe(4);
// simulate user input
inputs.at(0).simulate('changeText', 'myFancyUsername');
......@@ -53,7 +54,7 @@ describe('RegisterForm component', () => {
inputs.at(2).simulate('changeText', 'somepassword');
// simulate press checkbox
await render.find('CheckBox').at(1).simulate('press');
await render.find(CheckBox).at(1).simulate('press');
// update component (password confirmation is shown after the password field is set)
await wrapper.update();
......@@ -65,7 +66,7 @@ describe('RegisterForm component', () => {
inputs.at(3).simulate('changeText', 'somepassword');
// simulate press register
await render.find('Button').at(1).simulate('press');
await render.find(Button).at(1).simulate('press');
// expect auth service register to be called once
expect(authService.register).toBeCalled();
......@@ -83,8 +84,7 @@ describe('RegisterForm component', () => {
// find the text inputs
let inputs = render.find('TextInput');
// should have 4 inputs
expect(inputs.length).toBe(3);
expect(inputs.length).toBe(4);
// simulate user input
inputs.at(0).simulate('changeText', 'myFancyUsername');
......@@ -100,7 +100,7 @@ describe('RegisterForm component', () => {
inputs.at(3).simulate('changeText', 'ohNoItIsDifferent');
// simulate press register
await render.find('Button').at(1).simulate('press');
await render.find(Button).at(1).simulate('press');
// should call alert
expect(Alert.alert).toBeCalled();
......@@ -122,7 +122,7 @@ describe('RegisterForm component', () => {
let inputs = render.find('TextInput');
// should have 4 inputs
expect(inputs.length).toBe(3);
expect(inputs.length).toBe(4);
// simulate user input
inputs.at(0).simulate('changeText', 'myFancyUsername');
......@@ -138,7 +138,7 @@ describe('RegisterForm component', () => {
inputs.at(3).simulate('changeText', 'somepassword');
// simulate press register
await render.find('Button').at(1).simulate('press');
await render.find(Button).at(1).simulate('press');
// should call alert
expect(Alert.alert).toBeCalled();
......
......@@ -66,23 +66,14 @@ exports[`ForgotPassword component should renders correctly 1`] = `
>
<View
style={
Array [
Object {
"marginLeft": 15,
"marginRight": 15,
},
undefined,
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
},
Object {
"borderRadius": 4,
},
]
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderRadius": 30,
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
}
}
>
<View
......@@ -96,75 +87,37 @@ exports[`ForgotPassword component should renders correctly 1`] = `
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={null}
style={
Object {
"opacity": 1,
}
}
>
<View
pointerEvents="box-only"
style={
Array [
Object {
"alignItems": "center",
"backgroundColor": "#9E9E9E",
"flexDirection": "row",
"justifyContent": "center",
"padding": 19,
},
undefined,
undefined,
undefined,
undefined,
undefined,
Object {
"backgroundColor": "rgba(0,0,0, 0.5)",
},
Object {
"borderRadius": 4,
},
Object {
"padding": 12,
},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
]
Object {
"alignItems": "center",
"backgroundColor": "transparent",
"borderColor": "#2089dc",
"borderRadius": 3,
"borderWidth": 0,
"flexDirection": "row",
"justifyContent": "center",
"padding": 8,
}
}
>
<Text
style={
Array [
Object {},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
Array [
Object {
"color": "white",
"fontSize": 20,
},
undefined,
Object {
"fontSize": 17.5,
},
undefined,
Object {
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
},
undefined,
undefined,
undefined,
],
]
Object {
"color": "white",
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
"paddingBottom": 1,
"paddingTop": 2,
"textAlign": "center",
}
}
>
GO BACK
......@@ -174,23 +127,14 @@ exports[`ForgotPassword component should renders correctly 1`] = `
</View>
<View
style={
Array [
Object {
"marginLeft": 15,
"marginRight": 15,
},
undefined,
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
},
Object {
"borderRadius": 4,
},
]
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderRadius": 30,
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
}
}
>
<View
......@@ -204,75 +148,37 @@ exports[`ForgotPassword component should renders correctly 1`] = `
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={null}
style={
Object {
"opacity": 1,
}
}
>
<View
pointerEvents="box-only"
style={
Array [
Object {
"alignItems": "center",
"backgroundColor": "#9E9E9E",
"flexDirection": "row",
"justifyContent": "center",
"padding": 19,
},
undefined,
undefined,
undefined,
undefined,
undefined,
Object {
"backgroundColor": "rgba(0,0,0, 0.5)",
},
Object {
"borderRadius": 4,
},
Object {
"padding": 12,
},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
]
Object {
"alignItems": "center",
"backgroundColor": "transparent",
"borderColor": "#2089dc",
"borderRadius": 3,
"borderWidth": 0,
"flexDirection": "row",
"justifyContent": "center",
"padding": 8,
}
}
>
<Text
style={
Array [
Object {},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
Array [
Object {
"color": "white",
"fontSize": 20,
},
undefined,
Object {
"fontSize": 17.5,
},
undefined,
Object {
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
},
undefined,
undefined,
undefined,
],
]
Object {
"color": "white",
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
"paddingBottom": 1,
"paddingTop": 2,
"textAlign": "center",
}
}
>
CONTINUE
......
......@@ -134,23 +134,14 @@ exports[`ForgotScreen component should renders correctly 1`] = `
>
<View
style={
Array [
Object {
"marginLeft": 15,
"marginRight": 15,
},
undefined,
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
},
Object {
"borderRadius": 4,
},
]
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderRadius": 30,
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
}
}
>
<View
......@@ -164,75 +155,37 @@ exports[`ForgotScreen component should renders correctly 1`] = `
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={null}
style={
Object {
"opacity": 1,
}
}
>
<View
pointerEvents="box-only"
style={
Array [
Object {
"alignItems": "center",
"backgroundColor": "#9E9E9E",
"flexDirection": "row",
"justifyContent": "center",
"padding": 19,
},
undefined,
undefined,
undefined,
undefined,
undefined,
Object {
"backgroundColor": "rgba(0,0,0, 0.5)",
},
Object {
"borderRadius": 4,
},
Object {
"padding": 12,
},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
]
Object {
"alignItems": "center",
"backgroundColor": "transparent",
"borderColor": "#2089dc",
"borderRadius": 3,
"borderWidth": 0,
"flexDirection": "row",
"justifyContent": "center",
"padding": 8,
}
}
>
<Text
style={
Array [
Object {},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
Array [
Object {
"color": "white",
"fontSize": 20,
},
undefined,
Object {
"fontSize": 17.5,
},
undefined,
Object {
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
},
undefined,
undefined,
undefined,
],
]
Object {
"color": "white",
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
"paddingBottom": 1,
"paddingTop": 2,
"textAlign": "center",
}
}
>
GO BACK
......@@ -242,23 +195,14 @@ exports[`ForgotScreen component should renders correctly 1`] = `
</View>
<View
style={
Array [
Object {
"marginLeft": 15,
"marginRight": 15,
},
undefined,
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
},
Object {
"borderRadius": 4,
},
]
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderRadius": 30,
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
}
}
>
<View
......@@ -272,75 +216,37 @@ exports[`ForgotScreen component should renders correctly 1`] = `
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={null}
style={
Object {
"opacity": 1,
}
}
>
<View
pointerEvents="box-only"
style={
Array [
Object {
"alignItems": "center",
"backgroundColor": "#9E9E9E",
"flexDirection": "row",
"justifyContent": "center",
"padding": 19,
},
undefined,
undefined,
undefined,
undefined,
undefined,
Object {
"backgroundColor": "rgba(0,0,0, 0.5)",
},
Object {
"borderRadius": 4,
},
Object {
"padding": 12,
},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
]
Object {
"alignItems": "center",
"backgroundColor": "transparent",
"borderColor": "#2089dc",
"borderRadius": 3,
"borderWidth": 0,
"flexDirection": "row",
"justifyContent": "center",
"padding": 8,
}
}
>
<Text
style={
Array [
Object {},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
Array [
Object {
"color": "white",
"fontSize": 20,
},
undefined,
Object {
"fontSize": 17.5,
},
undefined,
Object {
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
},
undefined,
undefined,
undefined,
],
]
Object {
"color": "white",
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
"paddingBottom": 1,
"paddingTop": 2,
"textAlign": "center",
}
}
>
CONTINUE
......
......@@ -112,23 +112,14 @@ exports[`LoginForm component should renders correctly 1`] = `
>
<View
style={
Array [
Object {
"marginLeft": 15,
"marginRight": 15,
},
undefined,
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
},
Object {
"borderRadius": 30,
},
]
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderRadius": 30,
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
}
}
>
<View
......@@ -142,75 +133,38 @@ exports[`LoginForm component should renders correctly 1`] = `
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={null}
style={
Object {
"opacity": 1,
}
}
testID="registerButton"
>
<View
pointerEvents="box-only"
style={
Array [
Object {
"alignItems": "center",
"backgroundColor": "#9E9E9E",
"flexDirection": "row",
"justifyContent": "center",
"padding": 19,
},
undefined,
undefined,
undefined,
undefined,
undefined,
Object {
"backgroundColor": "transparent",
},
Object {
"borderRadius": 30,
},
Object {
"padding": 12,
},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
]
Object {
"alignItems": "center",
"backgroundColor": "transparent",
"borderColor": "#2089dc",
"borderRadius": 3,
"borderWidth": 0,
"flexDirection": "row",
"justifyContent": "center",
"padding": 8,
}
}
>
<Text
style={
Array [
Object {},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
Array [
Object {
"color": "white",
"fontSize": 20,
},
undefined,
Object {
"fontSize": 17.5,
},
undefined,
Object {
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
},
undefined,
undefined,
undefined,
],
]
Object {
"color": "white",
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
"paddingBottom": 1,
"paddingTop": 2,
"textAlign": "center",
}
}
>
CREATE A CHANNEL
......@@ -220,23 +174,14 @@ exports[`LoginForm component should renders correctly 1`] = `
</View>
<View
style={
Array [
Object {
"marginLeft": 15,
"marginRight": 15,
},
undefined,
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
},
Object {
"borderRadius": 30,
},
]
Object {
"backgroundColor": "transparent",
"borderColor": "#FFF",
"borderRadius": 30,
"borderWidth": 1,
"marginLeft": 10,
"marginRight": 0,
}
}
>
<View
......@@ -250,76 +195,38 @@ exports[`LoginForm component should renders correctly 1`] = `
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={null}
style={
Object {
"opacity": 1,
}
}
testID="loginButton"
>
<View
pointerEvents="box-only"
style={
Array [
Object {
"alignItems": "center",
"backgroundColor": "#9E9E9E",
"flexDirection": "row",
"justifyContent": "center",
"padding": 19,
},
undefined,
undefined,
undefined,
undefined,
undefined,
Object {
"backgroundColor": "transparent",
},
Object {
"borderRadius": 30,
},
Object {
"padding": 12,
},
undefined,
undefined,
undefined,
undefined,
false,
false,
]
Object {
"alignItems": "center",
"backgroundColor": "transparent",
"borderColor": "#2089dc",
"borderRadius": 3,
"borderWidth": 0,
"flexDirection": "row",
"justifyContent": "center",
"padding": 8,
}
}
>
<Text
style={
Array [
Object {},
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
Array [
Object {
"color": "white",
"fontSize": 20,
},
undefined,
Object {
"fontSize": 17.5,
},
undefined,
Object {
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
},
undefined,
undefined,
false,
],
]
Object {
"color": "white",
"fontSize": 16,
"fontWeight": "600",
"letterSpacing": 1.25,
"paddingBottom": 1,
"paddingTop": 2,
"textAlign": "center",
}
}
>
LOGIN
......
......@@ -115,45 +115,159 @@ exports[`blog card component should renders correctly 1`] = `
<View
height={24}
style={
Array [
Object {
"backgroundColor": "transparent",
"height": 24,
"paddingBottom": 10,
"paddingRight": 10,
"paddingTop": 10,
"width": 24,
},
Object {
"borderRadius": 12,
Object {
"backgroundColor": "transparent",
"borderRadius": 17,
"height": 34,
"width": 34,
}
}
theme={
Object {
"colors": Object {
"disabled": "hsl(208, 8%, 90%)",
"divider": "#bcbbc1",
"error": "#ff190c",
"grey0": "#393e42",
"grey1": "#43484d",
"grey2": "#5e6977",
"grey3": "#86939e",
"grey4": "#bdc6cf",
"grey5": "#e1e8ee",
"greyOutline": "#bbb",
"platform": Object {
"android": Object {
"error": "#f44336",
"primary": "#2196f3",
"secondary": "#9C27B0",
"success": "#4caf50",
"warning": "#ffeb3b",
},
"ios": Object {
"error": "#ff3b30",
"primary": "#007aff",
"secondary": "#5856d6",
"success": "#4cd964",
"warning": "#ffcc00",
},
},
"primary": "#2089dc",
"searchBg": "#303337",
"secondary": "#8F0CE8",
"success": "#52c41a",
"warning": "#faad14",
},
undefined,
]
}
}
width={24}
>
<View
accessibilityIgnoresInvertColors={true}
style={
Array [
Object {
"backgroundColor": "#bdbdbd",
"borderRadius": 17,
"flex": 1,
"overflow": "hidden",
"position": "relative",
}
}
>
<Image
onLoad={[Function]}
style={
Array [
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
},
Object {
"height": null,
"width": null,
},
]
}
testID="RNE__Image"
theme={
Object {
"colors": Object {
"disabled": "hsl(208, 8%, 90%)",
"divider": "#bcbbc1",
"error": "#ff190c",
"grey0": "#393e42",
"grey1": "#43484d",
"grey2": "#5e6977",
"grey3": "#86939e",
"grey4": "#bdc6cf",
"grey5": "#e1e8ee",
"greyOutline": "#bbb",
"platform": Object {
"android": Object {
"error": "#f44336",
"primary": "#2196f3",
"secondary": "#9C27B0",
"success": "#4caf50",
"warning": "#ffeb3b",
},
"ios": Object {
"error": "#ff3b30",
"primary": "#007aff",
"secondary": "#5856d6",
"success": "#4cd964",
"warning": "#ffcc00",
},
},
"primary": "#2089dc",
"searchBg": "#303337",
"secondary": "#8F0CE8",
"success": "#52c41a",
"warning": "#faad14",
},
}
}
/>
<View
accessibilityElementsHidden={false}
importantForAccessibility="yes"
pointerEvents="auto"
style={
Object {
"alignItems": "center",
"alignSelf": "stretch",
"backgroundColor": "rgba(0,0,0,0.2)",
"bottom": 0,
"flex": 1,
"justifyContent": "center",
"left": 0,
"opacity": 1,
"position": "absolute",
"right": 0,
"top": 0,
},
}
}
>
<View
style={
Object {
"alignItems": "center",
"backgroundColor": "transparent",
"flex": 1,
"height": null,
"justifyContent": "center",
"width": null,
}
}
testID="RNE__Image__placeholder"
/>
</View>
<View
style={
Object {
"borderRadius": 12,
},
undefined,
]
}
/>
"flex": 1,
"height": null,
"width": null,
}
}
/>
</View>
</View>
<Text
numberOfLines={1}
......
......@@ -851,14 +851,10 @@ exports[`blog view screen component should renders correctly 1`] = `
}
}
>
<Icon
<Themed.Icon
color="#b0bec5"
name="public"
raised={false}
reverse={false}
reverseColor="white"
size={18}
underlayColor="white"
/>
<Text
style={
......@@ -880,15 +876,11 @@ exports[`blog view screen component should renders correctly 1`] = `
>
Attribution-NonCommerical CC BY-NC
</Text>
<Icon
<Themed.Icon
color="#4690D6"
name="share"
onPress={[Function]}
raised={false}
reverse={false}
reverseColor="white"
size={20}
underlayColor="white"
/>
</View>
<ForwardRef(_SafeAreaView)
......@@ -900,15 +892,12 @@ exports[`blog view screen component should renders correctly 1`] = `
}
}
>
<Icon
<Themed.Icon
color="#4690D6"
name="arrow-back"
onPress={[Function]}
raised={true}
reverse={false}
reverseColor="white"
size={22}
underlayColor="white"
/>
</ForwardRef(_SafeAreaView)>
</View>
......
......@@ -25,12 +25,9 @@ exports[`Boost action bar component renders correctly 1`] = `
}
}
>
<Icon
<Themed.Icon
color="rgb(96, 125, 139)"
name="bank"
raised={false}
reverse={false}
reverseColor="white"
size={20}
style={
Object {
......@@ -38,7 +35,6 @@ exports[`Boost action bar component renders correctly 1`] = `
}
}
type="material-community"
underlayColor="white"
/>
<Text
style={
......
......@@ -7,6 +7,7 @@ exports[`cature poster component should renders correctly 1`] = `
"flex": 1,
}
}
testID="capturePosterView"
>
<View
style={
......
......@@ -936,6 +936,7 @@ exports[`cature poster flags component should renders correctly for props false
"width": "100%",
}
}
testID="tagInput"
underlineColorAndroid="transparent"
value=""
/>
......@@ -2104,6 +2105,7 @@ exports[`cature poster flags component should renders correctly for props true 1
"width": "100%",
}
}
testID="tagInput"
underlineColorAndroid="transparent"
value=""
/>
......
......@@ -40,6 +40,7 @@ exports[`channel actions component should renders correctly 1`] = `
"padding": 4,
}
}
testID="MoreButton"
>
<Text
style={
......
......@@ -7,6 +7,7 @@ exports[`Channel screen component should renders correctly 1`] = `
"flex": 1,
}
}
testID="ChannelScreen"
>
<FeedList
emptyMessage={null}
......@@ -471,15 +472,12 @@ exports[`Channel screen component should renders correctly 1`] = `
}
}
>
<Icon
<Themed.Icon
color="#4690D6"
name="arrow-back"
onPress={[Function]}
raised={true}
reverse={false}
reverseColor="white"
size={22}
underlayColor="white"
/>
</SafeAreaView>
<CaptureFab
......@@ -525,6 +523,7 @@ exports[`Channel screen component should renders correctly 1`] = `
},
}
}
testID="captureFab"
/>
</View>
`;
......@@ -536,6 +535,7 @@ exports[`Channel screen component should show closed channel message 1`] = `
"flex": 1,
}
}
testID="ChannelScreen"
>
<View>
<ChannelHeader
......@@ -968,15 +968,12 @@ exports[`Channel screen component should show closed channel message 1`] = `
}
}
>
<Icon
<Themed.Icon
color="#4690D6"
name="arrow-back"
onPress={[Function]}
raised={true}
reverse={false}
reverseColor="white"
size={22}
underlayColor="white"
/>
</SafeAreaView>
<CaptureFab
......@@ -1022,6 +1019,7 @@ exports[`Channel screen component should show closed channel message 1`] = `
},
}
}
testID="captureFab"
/>
</View>
`;
......@@ -152,6 +152,7 @@ exports[`channel header component owner should render correctly 1`] = `
>
<View
style={null}
testID="SubscribersView"
>
<Text>
SUBSCRIBERS
......@@ -161,7 +162,9 @@ exports[`channel header component owner should render correctly 1`] = `
</Text>
</View>
</View>
<View>
<View
testID="ViewsView"
>
<Text>
VIEWS
</Text>
......@@ -179,6 +182,7 @@ exports[`channel header component owner should render correctly 1`] = `
"flexDirection": "row",
}
}
testID="ChannelNameView"
>
<Text
ellipsizeMode="tail"
......@@ -234,6 +238,7 @@ exports[`channel header component owner should render correctly 1`] = `
"padding": 4,
}
}
testID="EditButton"
>
<Text
style={
......
......@@ -127,6 +127,7 @@ exports[`Messenger setup component should render correctly for unlock 1`] = `
"padding": 16,
}
}
testID="MessengerSetupText"
underlineColorAndroid="transparent"
/>
</View>
......
......@@ -137,6 +137,8 @@ android {
versionCode Integer.parseInt(project.versionCode)
versionName project.versionName
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60"
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
signingConfigs {
debug {
......@@ -245,6 +247,9 @@ dependencies {
implementation("com.github.bumptech.glide:annotations:${glideVersion}") {
exclude group: "com.android.support", module: "annotations"
}
androidTestImplementation('com.wix:detox:+') { transitive = true }
androidTestImplementation 'junit:junit:4.12'
}
// Run this once to be able to run the application with BUCK
......
package com.minds.mobile;
import com.wix.detox.Detox;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class DetoxTest {
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
@Test
public void runDetoxTests() {
Detox.runTests(mActivityRule);
}
}
\ No newline at end of file
......@@ -11,6 +11,7 @@ buildscript {
supportLibVersion = "28.0.0"
androidXCore = "1.0.2"
glideVersion = "4.7.1"
kotlinVersion = '1.3.10'
}
repositories {
google()
......@@ -19,6 +20,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.4.2'
classpath 'com.google.gms:google-services:4.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
......@@ -27,6 +29,10 @@ buildscript {
allprojects {
repositories {
mavenLocal()
maven {
// All of Detox' artifacts are provided via the npm module
url "$rootDir/../node_modules/detox/Detox-android"
}
maven {
url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases"
}
......@@ -56,6 +62,10 @@ subprojects {
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.supportLibVersion
defaultConfig {
minSdkVersion 21
}
}
}
}
......
import login from "./login";
import { waitForElement, tapElement, waitForAndTap } from "../helpers/waitFor";
export const capturePoster = async () => {
await waitForElement(by.id('usernameInput'));
await login(process.env.loginUser, process.env.loginPass);
await waitForElement(by.id('NewsfeedScreen'));
await tapElement(by.id('captureFab'));
}
export const deletePost = async () => {
await waitForAndTap(by.id('ActivityMoreButton'));
await waitForAndTap(by.id('deleteOption'));
await waitForAndTap(by.text('Ok'));
await waitForAndTap(by.text('Ok'));
}
import { waitForElement, waitForAndType, tapElement, waitForAndTap } from './helpers/waitFor';
import { deletePost } from './actions/capturePoster';
import login from './actions/login';
/**
* Uses process.env.userDisplayName:
* export userDisplayName="Something Something"
*/
describe('Channel Flow', () => {
beforeEach(async () => {
await device.launchApp({
newInstance: true,
permissions: {
notifications: 'YES',
camera: 'YES',
medialibrary: 'YES',
photos: 'YES',
},
});
await waitForElement(by.id('usernameInput'));
await login(process.env.loginUser, process.env.loginPass);
await waitForAndTap(by.id('AvatarButton'));
});
it('channel header should be render correctly', async () => {
const beVisible = true;
await waitForElement(by.id('SubscribersView'));
await waitForElement(by.id('ViewsView'));
await waitForElement(by.id('ChannelNameView'));
await waitForElement(by.id('SubscribeButton'), !beVisible);
await waitForElement(by.id('SendMessageButton'), !beVisible);
await waitForElement(by.id('EditButton'));
await waitForElement(by.id('WireButton'), !beVisible);
await waitForElement(by.id('MoreButton'), !beVisible);
});
it('should be able to change display name', async () => {
const displayNameTest = "New Display Name";
const replaceText = true;
// new display name
await waitForAndTap(by.id('EditButton'));
await waitForAndType(by.id('ChannelNameTextInput'), displayNameTest, replaceText);
await tapElement(by.id('EditButton'));
await waitForElement(by.id('ChannelNameView'));
// display name back to normal
await waitForAndTap(by.id('EditButton'));
await waitForAndType(by.id('ChannelNameTextInput'), process.env.userDisplayName, replaceText);
await tapElement(by.id('EditButton'));
await waitForElement(by.id('ChannelNameView'));
});
it('should be able to create a post', async () => {
const text = 'e2eTest';
await waitForAndTap(by.id('captureFab'));
// create post
await waitForAndType(by.id('PostInput'), text);
await tapElement(by.id('CapturePostButton'));
// wait for channelscreen
await waitForElement(by.id('ChannelScreen'));
await deletePost();
});
it('should be move between tabs', async () => {
// wait for channelscreen
await waitForElement(by.id('ChannelScreen'));
await tapElement(by.id('ImagesButton'));
await tapElement(by.id('VideosButton'));
await tapElement(by.id('BlogsButton'));
await tapElement(by.id('FeedButton'));
});
});
import sleep from '../src/common/helpers/sleep';
import { waitForElement, waitForAndType, tapElement, waitForAndTap } from './helpers/waitFor';
import { capturePoster, deletePost } from './actions/capturePoster';
describe('Comment Flow', () => {
beforeEach(async () => {
await device.launchApp({
newInstance: true,
permissions: {
notifications: 'YES',
camera: 'YES',
medialibrary: 'YES',
photos: 'YES',
},
});
await capturePoster();
});
it('should be able to create post and comment', async () => {
const text = 'e2eTest';
const commentText = 'commentE2ETest';
const replyText = 'replyE2ETest';
// create post
await waitForAndType(by.id('PostInput'), text);
await tapElement(by.id('CapturePostButton'));
// wait for newsfeed
await waitForElement(by.id('NewsfeedScreen'));
// add comment
await waitForAndTap(by.id('ActivityCommentButton'));
await waitForAndType(by.id('CommentText'), commentText);
await tapElement(by.id('PostCommentButton'));
// add reply
await waitForAndTap(by.id('ReplyCommentButton'));
await waitForAndType(by.id('CommentText').withAncestor(by.id('CommentParentView')), replyText);
await tapElement(by.id('PostCommentButton').withAncestor(by.id('CommentParentView')));
// check reply
await waitForElement(by.label(`@${process.env.loginUser} ${replyText}`));
// finish
await deletePost();
});
});
/**
* @author Ben Hayward
*/
import fetch from 'node-fetch';
export default async (username, password, email) => {
const params = {
username: username,
password: password,
password2: password,
email: email,
Homepage121118: null,
captcha: "",
exclusive_promotions: false,
tos: true
};
await fetch('https://www.minds.com/api/v1/register', {
method: 'POST',
body: JSON.stringify(params)
})
.then(result => result.json())
.then(result => {
console.log(
`new user - ${process.env.newUsername} created: ${JSON.stringify(
result.status,
)}`,
);
});
};
/**
* @author Ben Hayward
*/
import fetch from 'node-fetch';
export default async (username, password) => {
let params = {
grant_type: 'password',
client_id: 'mobile',
username,
password,
};
try {
const result = await fetch('https://www.minds.com/api/v2/oauth/token', {
method: 'POST',
body: JSON.stringify(params),
}).then(res => res.json()); //get the token.
const result1 = fetch('https://www.minds.com/api/v2/settings/delete', {
method: 'POST',
body: JSON.stringify({password}),
headers: {Authorization: 'Bearer ' + result.access_token},
}).then(res => res.json()); //make the deletion request.
console.log('deleting user returned:\n', await result1);
} catch (e) {
console.log('error deleting user:\n', e);
}
};
export const TIME = 10000;
export const waitForElement = async (e, visible = true) => {
if (visible) {
await waitFor(element(e)).toBeVisible().withTimeout(TIME);
} else {
await waitFor(element(e)).toBeNotVisible().withTimeout(TIME);
}
}
export const tapElement = async (e) => {
await element(e).tap();
}
export const typeText = async (e, text, replaceText = false) => {
if (!replaceText) {
await element(e).typeText(text);
} else {
await element(e).replaceText(text);
}
}
export const waitForAndTap = async (e) => {
await waitForElement(e);
await tapElement(e);
}
export const waitForAndType = async (e, text, replaceText = false) => {
await waitForElement(e);
await typeText(e, text, replaceText);
}
\ No newline at end of file
import login from "./actions/login";
import sleep from '../src/common/helpers/sleep';
describe('Login Flow', () => {
beforeEach(async () => {
......@@ -12,13 +11,18 @@ describe('Login Flow', () => {
});
it('should show error', async () => {
// should login successfully
await expect(element(by.id('usernameInput'))).toBeVisible();
// login should be visible
await waitFor(element(by.id('usernameInput')))
.toBeVisible()
.withTimeout(10000);
// we moved the login logic to an action to avoid code duplication
await login('bad', 'credentials');
await sleep(1000);
// wait for the message
await waitFor(element(by.id('loginMsg')))
.toBeVisible()
.withTimeout(2000);
// it should show the error message
// according to the detox docs it should be toHaveText but it only works with toHaveLabel
......@@ -26,8 +30,10 @@ describe('Login Flow', () => {
});
it('should login successfully', async () => {
// should login successfully
await expect(element(by.id('usernameInput'))).toBeVisible();
// login should be visible
await waitFor(element(by.id('usernameInput')))
.toBeVisible()
.withTimeout(10000);
// we moved the login logic to an action to avoid code duplication
await login(process.env.loginUser, process.env.loginPass);
......
import sleep from '../src/common/helpers/sleep';
import { waitForElement, waitForAndType, tapElement, waitForAndTap } from './helpers/waitFor';
import login from './actions/login';
describe('Messenger Flow', () => {
beforeEach(async () => {
await device.launchApp({
newInstance: true,
permissions: {
notifications: 'YES',
camera: 'YES',
medialibrary: 'YES',
photos: 'YES',
},
});
await waitFor(element(by.id('usernameInput'))).toBeVisible().withTimeout(5000);
await login(process.env.loginUser, process.env.loginPass);
await expect(element(by.id('NewsfeedScreen'))).toBeVisible();
});
it('should be able to open messenger, unblock and send message', async () => {
const userName = 'JUANMSOLARO_TEST5';
const messageText = 'This is an auto generated message for testing purpose';
// wait for newsfeed
await waitForElement(by.id('NewsfeedScreen'));
await tapElement(by.id('MessengerTabButton'));
await waitForAndType(by.id('MessengerContactText'), userName);
await waitForAndTap(by.id(userName));
await tapElement(by.id(userName));
await waitForAndType(by.id('MessengerSetupText'), process.env.messengerpass);
await tapElement(by.id('NavNextButton'));
await waitForAndType(by.id('ConversationTextInput'), messageText);
await tapElement(by.id('ConversationSendButton'));
await waitForElement(by.label(messageText));
});
});
import sleep from '../src/common/helpers/sleep';
import { waitForElement, waitForAndType, tapElement, waitForAndTap } from './helpers/waitFor';
import { deletePost, capturePoster } from './actions/capturePoster';
describe('Post Flow', () => {
beforeEach(async () => {
await device.launchApp({
newInstance: true,
permissions: {
notifications: 'YES',
camera: 'YES',
medialibrary: 'YES',
photos: 'YES',
},
});
await capturePoster();
});
it('should be able to create a text only post', async () => {
const text = 'e2eTest';
// create post
await waitForAndType(by.id('PostInput'), text);
await tapElement(by.id('CapturePostButton'));
// wait for newsfeed
await waitForElement(by.id('NewsfeedScreen'));
await deletePost();
});
it('should be able to create a nsfw post with text', async () => {
const text = 'e2eTest';
// create post
await waitForAndType(by.id('PostInput'), text);
await tapElement(by.id('NsfwToggle'));
await tapElement(by.id('NsfwToggle'));
await waitForAndTap(by.id('NsfwReasonNudity'));
await tapElement(by.id('NsfwToggle'));
await tapElement(by.id('CapturePostButton'));
// wait for newsfeed
await waitForElement(by.id('NewsfeedScreen'));
await deletePost();
});
it('should be able to create a text and image post', async () => {
const text = 'e2eTest';
// create post
await waitForAndType(by.id('PostInput'), text);
await tapElement(by.id('GalleryImage0'));
await tapElement(by.id('GalleryImage0'));
await waitFor(element(by.id('CapturePostButton'))).toBeVisible().withTimeout(120000);
await tapElement(by.id('CapturePostButton'));
// wait for newsfeed
await waitForElement(by.id('NewsfeedScreen'));
await deletePost();
});
it('should be able to cancel image upload and then post', async () => {
const text = 'e2eTest';
// create post
await waitForAndType(by.id('PostInput'), text);
await tapElement(by.id('GalleryImage0'));
await tapElement(by.id('GalleryImage0'));
await waitForAndTap(by.id('AttachmentDeleteButton'));
await tapElement(by.id('CapturePostButton'));
// wait for newsfeed
await waitForElement(by.id('NewsfeedScreen'));
await deletePost();
});
});
import deleteUser from './helpers/deleteUser';
import sleep from '../src/common/helpers/sleep';
describe('Register Flow', () => {
const username = 'e2euser' + ((Math.random() * 0xffffff) << 0).toString(16);
const password = process.env.loginPass;
beforeEach(async () => {
await device.launchApp({
newInstance: true,
permissions: {
notifications: 'YES',
photos: 'YES',
},
});
});
afterAll(async () => {
await deleteUser(username, password);
});
it('should register correctly', async () => {
console.log('registering', username);
// login shoulf be visible
await waitFor(element(by.id('usernameInput')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('registerButton')).tap();
// email filed should be visible
await waitFor(element(by.id('registerEmailInput')))
.toBeVisible()
.withTimeout(2000);
// disable sync to prevent long waits for animations
await device.disableSynchronization();
// accept terms
await element(by.id('checkbox')).atIndex(0).tapAtPoint({x:30, y:30});
// fill the form
await element(by.id('registerUsernameInput')).typeText(username);
await element(by.id('registerEmailInput')).typeText('mye2e@minds.com');
await element(by.id('registerPasswordInput')).typeText(password);
await element(by.id('registerPasswordConfirmInput')).typeText(password);
// press register
const registerButton = await element(by.id('registerCreateButton'));
await registerButton.tap();
await registerButton.tap();
await device.enableSynchronization();
// is the onboarding visible?
await waitFor(element(by.id('artTestID')))
.toBeVisible()
.withTimeout(5000);
// select art hashtag
await element(by.id('artTestID')).tap();
// move to next step
await element(by.id('wizardNext')).tap();
// wait for the suggested users list
await waitFor(element(by.id('suggestedUser0SubscriptionButton')))
.toBeVisible()
.withTimeout(10000);
// subscribe to the first user of the list
await element(by.id('suggestedUser0SubscriptionButton')).tap();
// move to next step
await element(by.id('wizardNext')).tap();
// wait for the channel setup
await waitFor(element(by.id('selectAvatar')))
.toBeVisible()
.withTimeout(10000);
// tap the select avatar button
await element(by.id('selectAvatar')).tap();
await sleep(3000);
// move to next step
await element(by.id('wizardNext')).tap();
// wait for the channel setup
await waitFor(element(by.id('RewardsOnboarding')))
.toBeVisible()
.withTimeout(10000);
// move to next step
await element(by.id('wizardNext')).tap();
// newsfeed should be visible
await waitFor(element(by.id('NewsfeedScreen')))
.toBeVisible()
.withTimeout(10000);
});
});
......@@ -360,7 +360,7 @@ DEPENDENCIES:
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
trunk:
- boost-for-react-native
- JitsiMeetSDK
- libwebp
......@@ -539,4 +539,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 1f47b505eab73a09e6446d8196944df88cfdecc2
COCOAPODS: 1.7.5
COCOAPODS: 1.8.4
......@@ -5,6 +5,7 @@
* @format
*/
const blacklist = require('metro-config/src/defaults/blacklist');
const defaultSourceExts = require('metro-config/src/defaults/defaults').sourceExts;
module.exports = {
resolver: {
......@@ -12,6 +13,9 @@ module.exports = {
blacklistRE: blacklist([
/ios\/Pods\/JitsiMeetSDK\/Frameworks\/JitsiMeet.framework\/assets\/node_modules\/react-native\/.*/,
]),
sourceExts: process.env.RN_SRC_EXT
? process.env.RN_SRC_EXT.split(',').concat(defaultSourceExts)
: defaultSourceExts,
},
transformer: {
getTransformOptions: async () => ({
......
......@@ -23,6 +23,7 @@
"@react-native-community/netinfo": "^4.4.0",
"@sentry/react-native": "^1.0.9",
"crypto-js": "^3.1.9-1",
"detox": "^14.8.1",
"entities": "^2.0.0",
"ethjs-signer": "^0.1.1",
"i18n-js": "^3.2.2",
......@@ -42,7 +43,7 @@
"react-native-convert-ph-asset": "^1.0.3",
"react-native-device-info": "^4.0.1",
"react-native-device-log": "Minds/react-native-device-log#74f06b09c6656aa228a9a3a474c714d82abf509e",
"react-native-elements": "^0.19.1",
"react-native-elements": "^1.2.7",
"react-native-exception-handler": "^2.10.8",
"react-native-exit-app": "wumke/react-native-exit-app",
"react-native-fast-image": "^7.0.2",
......@@ -109,7 +110,7 @@
"configurations": {
"ios.sim.debug": {
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/Minds.app",
"build": "xcodebuild -workspace ios/Minds.xcworkspace -scheme Minds -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"build": "RN_SRC_EXT=e2e.js xcodebuild -workspace ios/Minds.xcworkspace -scheme Minds -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"device": {
"type": "iPhone 11"
......@@ -117,11 +118,27 @@
},
"ios.sim.release": {
"binaryPath": "ios/build/Build/Products/Release-iphonesimulator/Minds.app",
"build": "xcodebuild -workspace ios/Minds.xcworkspace -scheme Minds -configuration Release -sdk iphonesimulator -derivedDataPath ios/build",
"build": "RN_SRC_EXT=e2e.js xcodebuild -workspace ios/Minds.xcworkspace -scheme Minds -configuration Release -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"device": {
"type": "iPhone 11"
}
},
"android.emu.debug": {
"binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
"build": "cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
"type": "android.attached",
"device": {
"name": "ZY2234PLHJ"
}
},
"android.emu.release": {
"binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
"build": "cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
"type": "android.attached",
"device": {
"name": "ZY2234PLHJ"
}
}
},
"test-runner": "jest"
......
......@@ -57,22 +57,21 @@ export default class ForgotPassword extends PureComponent {
<Button
onPress={() => this.onPressBack()}
title={i18n.t('goback')}
backgroundColor="rgba(0,0,0, 0.5)"
borderRadius={4}
containerViewStyle={ComponentsStyle.loginButton}
textStyle={ComponentsStyle.loginButtonText}
type="clear"
containerStyle={ComponentsStyle.loginButton}
titleStyle={ComponentsStyle.loginButtonText}
/>
{!this.state.sent && <Button
onPress={() => this.onContinuePress()}
title={i18n.t('continue').toUpperCase()}
backgroundColor="rgba(0,0,0, 0.5)"
hidde={this.state.sent}
borderRadius={4}
loading={this.state.sending}
loadingRight={true}
disable={this.state.sending}
containerViewStyle={ComponentsStyle.loginButton}
textStyle={ComponentsStyle.loginButtonText}
type="clear"
containerStyle={ComponentsStyle.loginButton}
titleStyle={ComponentsStyle.loginButtonText}
/>}
</View>
</KeyboardAvoidingView>
......
......@@ -121,10 +121,9 @@ export default class LoginForm extends Component {
<Button
onPress={() => this.onLoginPress()}
title={i18n.t('auth.login')}
borderRadius={30}
backgroundColor="transparent"
containerViewStyle={ComponentsStyle.loginButton}
textStyle={ComponentsStyle.loginButtonText}
type="clear"
containerStyle={ComponentsStyle.loginButton}
titleStyle={ComponentsStyle.loginButtonText}
key={1}
loading={this.state.inProgress}
loadingRight={true}
......@@ -139,11 +138,11 @@ export default class LoginForm extends Component {
<Button
onPress={() => this.props.onRegister()}
title={i18n.t('auth.create')}
borderRadius={30}
backgroundColor="transparent"
containerViewStyle={ComponentsStyle.loginButton}
textStyle={ComponentsStyle.loginButtonText}
type="clear"
containerStyle={ComponentsStyle.loginButton}
titleStyle={ComponentsStyle.loginButtonText}
key={2}
testID="registerButton"
/>
);
}
......
......@@ -27,6 +27,7 @@ import i18n from '../common/services/i18n.service';
import sessionService from '../common/services/session.service';
import delay from '../common/helpers/delay';
import apiService from '../common/services/api.service';
import { DISABLE_PASSWORD_INPUTS } from '../config/Config';
/**
* Register Form
......@@ -83,6 +84,7 @@ export default class RegisterForm extends Component {
onChangeText={(value) => this.setState({ username: value })}
value={this.state.username}
editable={!this.state.inProgress}
testID="registerUsernameInput"
/>
<TextInput
style={[ComponentsStyle.loginInput, CommonStyle.marginTop2x]}
......@@ -94,11 +96,12 @@ export default class RegisterForm extends Component {
onChangeText={(value) => this.setState({ email: value })}
value={this.state.email}
editable={!this.state.inProgress}
testID="registerEmailInput"
/>
<TextInput
style={[ComponentsStyle.loginInput, CommonStyle.marginTop2x]}
placeholder={i18n.t('auth.password')}
secureTextEntry={true}
secureTextEntry={!DISABLE_PASSWORD_INPUTS} // e2e workaround
autoCapitalize={'none'}
returnKeyType={'done'}
placeholderTextColor="#444"
......@@ -106,20 +109,21 @@ export default class RegisterForm extends Component {
onChangeText={(value) => this.setState({ password: value })}
value={this.state.password}
editable={!this.state.inProgress}
testID="registerPasswordInput"
/>
<TextInput
style={[ComponentsStyle.loginInput, CommonStyle.marginTop2x]}
placeholder={i18n.t('auth.confirmpassword')}
secureTextEntry={!DISABLE_PASSWORD_INPUTS} // e2e workaround
autoCapitalize={'none'}
returnKeyType={'done'}
placeholderTextColor="#444"
underlineColorAndroid='transparent'
onChangeText={(value) => this.setState({ confirmPassword: value })}
value={this.state.confirmPassword}
editable={!this.state.inProgress}
testID="registerPasswordConfirmInput"
/>
{ this.state.password ?
<TextInput
style={[ComponentsStyle.loginInput, CommonStyle.marginTop2x]}
placeholder={i18n.t('auth.confirmpassword')}
secureTextEntry={true}
autoCapitalize={'none'}
returnKeyType={'done'}
placeholderTextColor="#444"
underlineColorAndroid='transparent'
onChangeText={(value) => this.setState({ confirmPassword: value })}
value={this.state.confirmPassword}
editable={!this.state.inProgress}
/> : null }
<CheckBox
right
iconRight
......@@ -144,20 +148,19 @@ export default class RegisterForm extends Component {
<Button
onPress={() => this.onPressBack()}
title={i18n.t('goback')}
borderRadius={30}
backgroundColor="transparent"
containerViewStyle={ComponentsStyle.loginButton}
textStyle={ComponentsStyle.loginButtonText}
type="clear"
containerStyle={ComponentsStyle.loginButton}
titleStyle={ComponentsStyle.loginButtonText}
disabled={this.state.inProgress}
disabledStyle={CommonStyle.backgroundTransparent}
/>
<Button
onPress={() => this.onPressRegister()}
title={i18n.t('auth.create')}
backgroundColor="transparent"
borderRadius={30}
containerViewStyle={ComponentsStyle.loginButton}
textStyle={ComponentsStyle.loginButtonText}
testID="registerCreateButton"
type="clear"
containerStyle={ComponentsStyle.loginButton}
titleStyle={ComponentsStyle.loginButtonText}
loading={this.state.inProgress}
loadingRight={true}
disabled={this.state.inProgress}
......
......@@ -14,7 +14,7 @@ import authService from './AuthService';
import { CommonStyle as CS } from '../styles/Common';
import { ComponentsStyle } from '../styles/Components';
import { Button } from 'react-native-elements'
import { Button } from 'react-native-elements';
import i18n from '../common/services/i18n.service';
import navigation from '../navigation/NavigationService';
......@@ -73,23 +73,23 @@ export default class ResetPassword extends PureComponent {
<Button
onPress={this.onPressBack}
title={i18n.t('goback')}
backgroundColor="rgba(0,0,0, 0.5)"
borderRadius={30}
containerViewStyle={ComponentsStyle.loginButton}
textStyle={ComponentsStyle.loginButtonText}
type="clear"
containerStyle={ComponentsStyle.loginButton}
titleStyle={ComponentsStyle.loginButtonText}
/>
{!this.state.sent && <Button
onPress={() => this.onContinuePress()}
title={i18n.t('continue')}
backgroundColor="rgba(0,0,0, 0.5)"
hidde={this.state.sent}
borderRadius={30}
loading={this.state.sending}
loadingRight={true}
disable={this.state.sending || !this.state.password || !this.state.confirmation}
containerViewStyle={ComponentsStyle.loginButton}
textStyle={ComponentsStyle.loginButtonText}
/>}
{!this.state.sent &&
<Button
onPress={() => this.onContinuePress()}
title={i18n.t('continue')}
type="clear"
containerStyle={ComponentsStyle.loginButton}
titleStyle={ComponentsStyle.loginButtonText}
hidde={this.state.sent}
loading={this.state.sending}
loadingRight={true}
disable={this.state.sending || !this.state.password || !this.state.confirmation}
/>
}
</View>
</KeyboardAvoidingView>
);
......
......@@ -29,13 +29,14 @@ export default class CaptureFab extends Component {
return (
<Icon
raised
reverse
name="md-create"
type='ionicon'
color='#fff'
size={32}
color='#4690DF'
size={28}
containerStyle={ settingsStore.leftHanded ? styles.leftSide : styles.rightSide }
onPress={() => this.navToCapture()}
{...testID('CaptureButton')}
testID={this.props.testID}
/>
);
......@@ -45,18 +46,14 @@ export default class CaptureFab extends Component {
const styles = StyleSheet.create({
rightSide: {
position:'absolute',
backgroundColor:'#4690DF',
width:55,
height:55,
// backgroundColor:'#4690DF',
bottom:8,
zIndex:1000,
right:8
},
leftSide: {
position:'absolute',
backgroundColor:'#4690DF',
width:55,
height:55,
// backgroundColor:'#4690DF',
bottom:8,
zIndex:1000,
left:8
......
......@@ -159,7 +159,7 @@ export default class CaptureGallery extends PureComponent {
});
}
}
{...testID(`Gallery ${node.type}`)}
testID={`GalleryImage${item.index}`}
>
<Image
source={{ uri : node.image.uri }}
......
......@@ -46,7 +46,7 @@ class CapturePostButton extends Component {
: CS.borderGreyed,
CS.border,
]}
{...testID('Capture Post Button')}
testID={this.props.testID}
>
<Text style={[styles.buttonText, connectivityService.isConnected ? CS.colorPrimary : CS.colorGreyed]}>{text}</Text>
</TouchableOpacity>
......
......@@ -73,6 +73,7 @@ export default class CapturePoster extends Component {
headerRight: <CapturePostButton
onPress={() => !params.isRemind ? this.submit() : this.remind()}
text={params.isRemind ? i18n.t('capture.remind').toUpperCase() : i18n.t('capture.post').toUpperCase()}
testID="CapturePostButton"
/>
});
}
......@@ -186,7 +187,7 @@ export default class CapturePoster extends Component {
multiline={true}
selectTextOnFocus={false}
onSelectionChange={this.onSelectionChanges}
{...testID('PostInput')}
testID="PostInput"
/>
</View>
{showAttachmentFeatures && this.getAttachFeature()}
......@@ -212,7 +213,7 @@ export default class CapturePoster extends Component {
const params = navigation.state.params || {};
return (
<View style={CS.flexContainer}>
<View style={CS.flexContainer} testID="capturePosterView">
<CaptureGallery
onSelected={this.onAttachedMedia}
header={this.getHeader(true)}
......@@ -292,7 +293,7 @@ export default class CapturePoster extends Component {
uri={attachment.uri}
type={attachment.type}
/>
<Icon raised name="md-close" type="ionicon" color='#fff' size={22} containerStyle={styles.deleteAttachment} onPress={() => this.deleteAttachment()} {...testID('Attachment Delete Button')} />
<Icon raised reverse name="md-close" type="ionicon" color='#4690DF' size={18} containerStyle={styles.deleteAttachment} onPress={() => this.deleteAttachment()} testID="AttachmentDeleteButton" />
</View>}
<CaptureTabs onSelectedMedia={this.onAttachedMedia} />
</React.Fragment>
......@@ -371,7 +372,7 @@ export default class CapturePoster extends Component {
Alert.alert(i18n.t('capture.pleaseTryAgain'));
return false;
}
if (
!attachment.hasAttachment &&
!text &&
......@@ -542,9 +543,6 @@ const styles = StyleSheet.create({
},
deleteAttachment: {
position: 'absolute',
backgroundColor:'#4690DF',
width:28,
height:28,
right: 8,
top: 0,
}
......
......@@ -361,7 +361,7 @@ export default class CapturePosterFlags extends Component {
}
renderNsfw() {
if (GOOGLE_PLAY_STORE || Platform.OS === 'ios') return null;
//if (GOOGLE_PLAY_STORE || Platform.OS === 'ios') return null;
return (
<NsfwToggle
containerStyle={styles.cell}
......
......@@ -157,6 +157,7 @@ class ChannelActions extends Component {
containerStyle={[CS.rowJustifyCenter, CS.marginLeft0x]}
accessibilityLabel={i18n.t('channel.subscribeMessage')}
text={i18n.t('channel.subscribe')}
testID="SubscribeButton"
/>
}
{ showMessage &&
......@@ -165,6 +166,7 @@ class ChannelActions extends Component {
containerStyle={[CS.rowJustifyCenter, CS.marginLeft0x]}
accessibilityLabel={i18n.t('channel.sendMessage')}
text={i18n.t('channel.message')}
testID="SendMessageButton"
/>
}
{ showEdit &&
......@@ -174,6 +176,7 @@ class ChannelActions extends Component {
accessibilityLabel={this.props.editing ? i18n.t('channel.saveChanges') : i18n.t('channel.editChannel')}
text={this.props.editing ? i18n.t('save') : i18n.t('edit')}
loading={this.props.saving}
testID="EditButton"
/>
}
{ showMode &&
......@@ -187,6 +190,7 @@ class ChannelActions extends Component {
textStyle={[CS.marginLeft, CS.marginRight]}
icon="ios-flash"
text="Wire"
testID="WireButton"
>
<Icon name='ios-flash' size={18} style={[CS.marginLeft, CS.colorPrimary]} />
</ButtonCustom>
......@@ -199,6 +203,7 @@ class ChannelActions extends Component {
textStyle={[CS.marginLeft, CS.marginRight]}
icon="ios-flash"
text={i18n.t('more')}
testID="MoreButton"
/>
}
<ActionSheet
......
......@@ -15,7 +15,7 @@ import {
inject
} from 'mobx-react/native'
import { Icon } from 'react-native-elements'
import { Icon } from 'react-native-elements';
import ChannelHeader from './header/ChannelHeader';
import Toolbar from './toolbar/Toolbar';
......@@ -329,12 +329,12 @@ class ChannelScreen extends Component {
/>
return (
<View style={CommonStyle.flexContainer}>
<View style={CommonStyle.flexContainer} testID="ChannelScreen">
{ (!channel.blocked && !isClosed) ? body : header }
<SafeAreaView style={styles.gobackicon}>
<Icon raised color={colors.primary} size={22} name='arrow-back' onPress={this.goBack}/>
</SafeAreaView>
<CaptureFab navigation={this.props.navigation} />
<CaptureFab navigation={this.props.navigation} testID="captureFab"/>
</View>
);
}
......
......@@ -217,12 +217,12 @@ export default class ChannelHeader extends Component {
<View style={styles.headertextcontainer}>
<View style={styles.countercontainer}>
<TouchableHighlightCustom underlayColor="transparent" style={[styles.counter]} onPress={() => { this._navToSubscribers() }}>
<View style={styles.counter}>
<View style={styles.counter} testID="SubscribersView">
<Text style={styles.countertitle}>{i18n.t('subscribers').toUpperCase()}</Text>
<Text style={styles.countervalue}>{abbrev(channel.subscribers_count, 0)}</Text>
</View>
</TouchableHighlightCustom>
<View style={styles.counter}>
<View style={styles.counter} testID="ViewsView">
<Text style={styles.countertitle}>{i18n.t('views').toUpperCase()}</Text>
<Text style={styles.countervalue}>{abbrev(channel.impressions, 0)}</Text>
</View>
......@@ -235,9 +235,10 @@ export default class ChannelHeader extends Component {
style={styles.nameTextInput}
value={this.state.name}
onChangeText={this.setName}
testID="ChannelNameTextInput"
/>}
{!isEditable &&
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View style={{ flexDirection: 'row', alignItems: 'center' }} testID="ChannelNameView">
<Text
style={styles.name}
ellipsizeMode='tail'
......
......@@ -77,19 +77,19 @@ class Toolbar extends Component {
return (
<View style={styles.container}>
<View style={styles.topbar}>
<TouchableOpacity style={styles.button} onPress={this.filterFeed}>
<TouchableOpacity style={styles.button} onPress={this.filterFeed} testID="FeedButton">
<Icon name="list" size={ICON_SIZE} color={filter == 'feed' ? colors.primary : color} style={styles.icon} />
<Text style={[styles.buttontext, filter == 'feed' ? styles.buttontextSelected : null]}>{i18n.t('feed').toUpperCase()}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={this.filterImages} >
<TouchableOpacity style={styles.button} onPress={this.filterImages} testID="ImagesButton">
<IonIcon name="md-image" size={ICON_SIZE} color={filter == 'images' ? colors.primary : color} style={styles.icon} />
<Text style={[styles.buttontext, filter == 'images' ? styles.buttontextSelected : null]}>{i18n.t('images').toUpperCase()}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={this.filterVideos} >
<TouchableOpacity style={styles.button} onPress={this.filterVideos} testID="VideosButton">
<IonIcon name="md-videocam" size={ICON_SIZE} color={filter == 'videos' ? colors.primary : color} style={styles.icon} />
<Text style={[styles.buttontext, filter == 'videos' ? styles.buttontextSelected : null]}>{i18n.t('videos').toUpperCase()}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={this.filterBlogs}>
<TouchableOpacity style={styles.button} onPress={this.filterBlogs} testID="BlogsButton">
<Icon name="subject" size={ICON_SIZE} color={filter == 'blogs' ? colors.primary : color} style={styles.icon} />
<Text style={[styles.buttontext, filter == 'blogs' ? styles.buttontextSelected : null]}>{i18n.t('blogs.blogs').toUpperCase()}</Text>
</TouchableOpacity>
......
......@@ -337,7 +337,7 @@ class CommentList extends React.Component<PropsType, StateType> {
return (
<View>
<View style={[CS.rowJustifyCenter, CS.margin, CS.padding, CS.backgroundWhite, CS.borderRadius12x, CS.borderGreyed, CS.borderHair]}>
<View style={[CS.rowJustifyCenter, CS.margin, CS.padding, CS.backgroundWhite, CS.borderRadius12x, CS.borderGreyed, CS.borderHair]} testID={this.props.parent ? 'CommentParentView' : ''}>
<Image source={avatarImg} style={CmpStyle.posterAvatar} />
<TextInput
style={[CS.flexContainer, CS.marginLeft, inputStyle, {paddingVertical: 2}]}
......@@ -352,6 +352,7 @@ class CommentList extends React.Component<PropsType, StateType> {
maxHeight={110}
value={comments.text}
onSelectionChange={this.onSelectionChanges}
testID='CommentText'
/>
{ attachment.uploading ?
<Progress.Pie progress={attachment.progress} size={36} /> :
......@@ -359,7 +360,7 @@ class CommentList extends React.Component<PropsType, StateType> {
<ActivityIndicator size={'large'} /> :
<View style={[CS.rowJustifyEnd, CS.centered]}>
<TouchableOpacity onPress={this.showAttachment} style={CS.paddingRight2x}><Icon name="md-attach" size={24} style={CS.paddingRight2x} /></TouchableOpacity>
<TouchableOpacity onPress={this.postComment} style={CS.paddingRight2x}><Icon name="md-send" size={24} /></TouchableOpacity>
<TouchableOpacity onPress={this.postComment} style={CS.paddingRight2x} testID='PostCommentButton'><Icon name="md-send" size={24} /></TouchableOpacity>
</View>
}
</View>
......
......@@ -49,7 +49,7 @@ export default class ReplyAction extends Component {
const textStyle = {color};
return (
<TouchableOpacityCustom style={[CommonStyle.flexContainer, CommonStyle.centered, CommonStyle.paddingRight2x, this.props.orientation == 'column' ? CommonStyle.columnAlignCenter : CommonStyle.rowJustifyCenter ]} onPress={this.toggleExpand}>
<TouchableOpacityCustom style={[CommonStyle.flexContainer, CommonStyle.centered, CommonStyle.paddingRight2x, this.props.orientation == 'column' ? CommonStyle.columnAlignCenter : CommonStyle.rowJustifyCenter ]} onPress={this.toggleExpand} testID='ReplyCommentButton'>
<Icon color={color} name={this.iconName} size={this.props.size} />
<Text style={textStyle}>{i18n.t('reply')}</Text>
<Counter size={this.props.size * 0.75} count={entity.replies_count} orientation={this.props.orientation}/>
......
......@@ -149,7 +149,7 @@ export default class FeedList extends Component {
if (this.props.feedStore.loading && !this.props.feedStore.refreshing){
return (
<View style={[CS.centered, CS.padding3x]}>
<View style={[CS.centered, CS.padding3x]} testID="ActivityIndicatorView">
<ActivityIndicator size={'large'} />
</View>
);
......
......@@ -6,7 +6,6 @@ import {
Platform
} from 'react-native';
import { Icon } from 'react-native-elements'
import Modal from 'react-native-modal';
import { LICENSES, getLicenseText} from '../services/list-options.service';
import { CommonStyle } from '../../styles/Common';
......@@ -38,7 +37,7 @@ export default class LicensePicker extends PureComponent {
*/
licenseSelected = (value) => {
this.toggle(value);
this.props.onLicenseSelected(value)
this.props.onLicenseSelected(value);
}
/**
......
......@@ -42,6 +42,7 @@ export default class NavNextButton extends Component {
style.button,
this.props.style,
]}
testID="NavNextButton"
>
<View style={style.row}>
{submitContent}
......
......@@ -50,6 +50,7 @@ export default class SearchView extends PureComponent {
'transparent'
}
style={styles.input}
testID="MessengerContactText"
/>
{rIcon}
</View>
......
......@@ -100,6 +100,7 @@ export default class TagInput extends Component {
keyboardType="default"
onSubmitEditing={this.addTag}
onEndEditing={this.addTag}
testID="tagInput"
/>
</ViewCmp>
......
......@@ -75,6 +75,7 @@ export default class TagSelect extends Component {
key={i}
onPress={() => this.toogle(tag)}
onLongPress={() => this.toogleOne(tag)}
testID={tag.value + 'TestID'}
>
<Text style={[styles.tagText, textStyle, tag.selected ? [CS.colorPrimary, textSelectedStyle] : null]}>#{tag.value}</Text>
</TouchableOpacity>)}
......
import React, { Component } from 'react';
import {View, StyleSheet} from 'react-native';
/**
* Mock video component for e2e (it takes too much cpu power in the simulator)
*/
export default class VideoBackground extends Component {
render() {
return <View style={[styles.container, {backgroundColor: 'black'}]}/>;
}
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
}
});
\ No newline at end of file
......@@ -59,13 +59,13 @@ export default class Wizard extends PureComponent<Props, State> {
<View style={[CS.rowJustifyCenter, CS.backgroundWhite, CS.padding2x, CS.marginTop4x, CS.marginBottom4x]}>
{first ?
<View style={{width:50}}/> :
<TouchableOpacity style={[{width:50}, CS.centered]} onPress={this.previous}>
<TouchableOpacity style={[{width:50}, CS.centered]} onPress={this.previous} testID="wizardPrevious">
<Icon size={34} name="keyboard-arrow-left" color={colors.primary}/>
</TouchableOpacity>}
<View style={[CS.flexContainer, CS.centered]}>
<Image source={require('./../../assets/logos/bulb.png')} style={{width:35, height:60}}/>
</View>
<TouchableOpacity style={[{width:50}, CS.centered]} onPress={this.next} disabled={!ready || this.state.waitingNext}>
<TouchableOpacity style={[{width:50}, CS.centered]} onPress={this.next} disabled={!ready || this.state.waitingNext} testID="wizardNext">
{
this.state.waitingNext ? <ActivityIndicator size="small"/> :
<Icon size={34} name="keyboard-arrow-right" color={ready ? colors.primary : colors.greyed}/>
......
......@@ -58,7 +58,7 @@ export default class NsfwToggle extends Component {
render() {
const isActive = Boolean(this.props.value && this.props.value.length);
const button = (
<Touchable style={this.props.containerStyle} onPress={this.showDropdown} {...testID('NSFW button')}>
<Touchable style={this.props.containerStyle} onPress={this.showDropdown} testID="NsfwToggle">
<MdIcon
name="explicit"
color={isActive ? Colors.explicit : Colors.darkGreyed}
......@@ -80,7 +80,7 @@ export default class NsfwToggle extends Component {
key={i}
onPress={() => this.toggleDropdownOption(reason)}
textStyle={[styles.menuItemText, this.isReasonActive(reason) && styles.menuItemTextActive]}
{...testID(`NSFW ${reason.label}`)}
testID={`NsfwReason${reason.label}`}
>{this.isReasonActive(reason) && <MdIcon name="check" />} {reason.label}</MenuItem>
))}
</Menu>
......
import CameraRoll from '@react-native-community/cameraroll';
/**
* Image picker service e2e mock
*/
class ImagePickerService {
/**
* Show image picker selector
*
* We mock the image picker selector returning the first image of the cameraroll
* We can't controll the ios image selector because it runs in a separate process
*
* @param {string} title
* @param {string} type photo or video
*/
async show(title, type='photo') {
const params = {
first: 30,
assetType: 'All',
};
const result = await CameraRoll.getPhotos(params);
return {
uri: result.edges[0].node.image.uri,
type: result.edges[0].node.type,
fileName: result.edges[0].node.image.filename,
duration: result.edges[0].node.image.playableDuration,
width: result.edges[0].node.image.width,
height: result.edges[0].node.image.height,
}
}
}
export default new ImagePickerService();
\ No newline at end of file
import {
Platform
} from 'react-native';
import DeviceInfo from 'react-native-device-info';
// export const MINDS_URI = 'https://www.minds.com/';
// export const MINDS_URI = 'http://dev.minds.io/';
// remember to update deeplink uri on AndroidManifest.xml !!!
// export const MINDS_URI = 'http://172.16.2.61:8080/';
export const MINDS_URI = 'https://www.minds.com/';
export const MINDS_API_URI = 'https://www.minds.com/';
export const NETWORK_TIMEOUT = 5000;
export const CONECTIVITY_CHECK_URI = 'https://www.minds.com/';
export const CONECTIVITY_CHECK_INTERVAL = 10000;
export const MINDS_URI_SETTINGS = {
//basicAuth: 'crypto:ohms',
};
export const MINDS_MAX_VIDEO_LENGTH = 5; // in minutes
export const SOCKET_URI = 'wss://ha-socket-io-us-east-1.minds.com:3030'
export const MINDS_CDN_URI = 'https://cdn.minds.com/';
export const MINDS_ASSETS_CDN_URI = 'https://cdn-assets.minds.com/';
// export const MINDS_CDN_URI = 'http://dev.minds.io/';
export const BLOCKCHAIN_URI = 'https://www.minds.com/api/v2/blockchain/proxy/';
// export const BLOCKCHAIN_URI = 'http://localhost:9545';
export const MINDS_LINK_URI = 'https://www.minds.com/';
export const CODE_PUSH_TOKEN = '';
/**
* Plataform dependant or fixed features
*/
export const MINDS_FEATURES = {
crypto: Platform.OS === 'ios' ? false : true,
};
/**
* Deeplink to screen/params maping
*/
export const MINDS_DEEPLINK = [
['groups/profile/:guid/feed', 'GroupView'],
['groups/profile/:guid', 'GroupView'],
['notifications', 'Notifications'],
['groups/:filter', 'GroupsList'],
['newsfeed/:guid', 'Activity'],
['media/:guid', 'Activity'],
['channels/:username', 'Channel'],
['blog/:filter', 'BlogList'],
['blog/view/:guid', 'BlogView'],
[':user/blog/:slug', 'BlogView'],
[':username', 'Channel'],
['wallet/tokens/:section', 'Wallet'],
];
export const DISABLE_PASSWORD_INPUTS = true;
// IF TRUE COMMENT THE SMS PERMISSIONS IN ANDROID MANIFEST TOO!!!
export const GOOGLE_PLAY_STORE = DeviceInfo.getBuildNumber() < 1050000000 && Platform.OS == 'android';
......@@ -59,5 +59,7 @@ export const MINDS_DEEPLINK = [
['wallet/tokens/:section', 'Wallet'],
];
export const DISABLE_PASSWORD_INPUTS = false;
// IF TRUE COMMENT THE SMS PERMISSIONS IN ANDROID MANIFEST TOO!!!
export const GOOGLE_PLAY_STORE = DeviceInfo.getBuildNumber() < 1050000000 && Platform.OS == 'android';
......@@ -26,6 +26,29 @@ import SubscriptionButton from '../channel/subscription/SubscriptionButton';
export default
@observer
class DiscoveryUser extends Component {
/**
* State
*/
state = {
guid: null,
};
/**
* Derive state from props
* @param {object} nextProps
* @param {object} prevState
*/
static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.guid !== nextProps.row.item.guid) {
return {
guid: nextProps.row.item.guid,
source: { uri: MINDS_CDN_URI + 'icon/' + nextProps.row.item.guid + '/medium' }
}
}
return null;
}
/**
* Navigate To channel
......@@ -40,35 +63,38 @@ class DiscoveryUser extends Component {
}
}
/**
* Render right button
*/
renderRightButton() {
const channel = this.props.row.item;
if (channel.isOwner() || this.props.hideButtons || (channel.isOpen() && !channel.can(FLAG_SUBSCRIBE) )) {
return;
}
const testID = (this.props.testID) ? `${this.props.testID}SubscriptionButton` : 'subscriptionButton';
return (
<SubscriptionButton
channel={channel}
testID={testID}
/>
)
}
getChannel() {
return this.props.row.item;
}
/**
* Render
*/
render() {
const item = this.getChannel();
const avatarImg = { uri: MINDS_CDN_URI + 'icon/' + item.guid + '/medium' };
const {row, ...otherProps} = this.props;
return (
<TouchableOpacity style={styles.row} onPress={this._navToChannel}>
<Image source={avatarImg} style={styles.avatar} />
<TouchableOpacity style={styles.row} onPress={this._navToChannel} {...otherProps}>
<Image source={this.state.source} style={styles.avatar} />
<View style={[CommonStyle.flexContainerCenter]}>
<Text style={[styles.body, CommonStyle.fontXL]}>{item.name}</Text>
<Text style={[styles.body, CommonStyle.fontS, CommonStyle.colorMedium]}>@{item.username}</Text>
<Text style={[styles.body, CommonStyle.fontXL]}>{row.item.name}</Text>
<Text style={[styles.body, CommonStyle.fontS, CommonStyle.colorMedium]}>@{row.item.username}</Text>
</View>
{this.renderRightButton()}
</TouchableOpacity>
......
......@@ -8,12 +8,11 @@ import {
} from 'mobx-react/native'
import {
FlatList, View, TouchableOpacity, StyleSheet, ActivityIndicator
FlatList, View, TouchableOpacity, StyleSheet, ActivityIndicator, Text
} from 'react-native'
import {CommonStyle as CS} from '../styles/Common';
import GrousBarItem from './GroupsBarItem';
import { Text } from 'react-native-elements';
import i18n from '../common/services/i18n.service';
@inject('groupsBar')
......
......@@ -210,8 +210,9 @@ export default class ConversationScreen extends Component {
autogrow={true}
maxHeight={110}
value={this.state.text}
testID='ConversationTextInput'
/>
<TouchableOpacity onPress={this.send} style={styles.sendicon}><Icon name="md-send" size={24} style={{ color: '#444' }}/></TouchableOpacity>
<TouchableOpacity onPress={this.send} style={styles.sendicon} testID='ConversationSendButton'><Icon name="md-send" size={24} style={{ color: '#444' }}/></TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
......
......@@ -158,6 +158,7 @@ export default class MessengerScreen extends Component {
refreshing={messengerList.refreshing}
style={styles.listView}
ListEmptyComponent={empty}
testID="MessengerList"
/>
</View>
);
......@@ -210,6 +211,7 @@ export default class MessengerScreen extends Component {
item={row.item}
styles={styles}
navigation={this.props.navigation}
testID={row.item.username.toUpperCase()}
/>
);
}
......
......@@ -112,6 +112,7 @@ export default class MessengerSetup extends Component {
placeholder={i18n.t('passwordPlaceholder')}
secureTextEntry={true}
onChangeText={(password) => this.password = password}
testID="MessengerSetupText"
/>
</View>
......
......@@ -39,7 +39,7 @@ export default class ConversationView extends Component {
let online = item.online ? <Icon style={styles.icons} name='md-radio-button-on' color='#2196f3' size={19} /> : null;
return (
<TouchableOpacity style={styles.row} onPress={this._navToConversation}>
<TouchableOpacity style={styles.row} onPress={this._navToConversation} testID={this.props.testID}>
<Image source={avatarImg} style={styles.avatar} />
<Text style={styles.body}>{item.username.toUpperCase()}</Text>
{unread}
......
......@@ -115,7 +115,7 @@ export default class NewsfeedScreen extends Component {
header={header}
navigation={this.props.navigation}
/>
<CaptureFab navigation={this.props.navigation}/>
<CaptureFab navigation={this.props.navigation} testID="captureFab"/>
</View>
);
}
......@@ -127,7 +127,7 @@ export default class NewsfeedScreen extends Component {
header={header}
navigation={this.props.navigation}
/>
<CaptureFab navigation={this.props.navigation}/>
<CaptureFab navigation={this.props.navigation} testID="captureFab"/>
</View>
);
}
......
......@@ -37,7 +37,7 @@ export default class Actions extends PureComponent {
<ThumbUpAction entity={entity} me={this.props.user.me}/>
<ThumbDownAction entity={entity} me={this.props.user.me}/>
{!isOwner && hasCrypto && <WireAction owner={entity.ownerObj} navigation={this.props.navigation}/>}
<CommentsAction entity={entity} navigation={this.props.navigation}/>
<CommentsAction entity={entity} navigation={this.props.navigation} testID={this.props.entity.text==='e2eTest' ? 'ActivityCommentButton' : ''}/>
<RemindAction entity={entity} navigation={this.props.navigation}/>
{isOwner && hasCrypto && !isScheduled && <BoostAction entity={entity} navigation={this.props.navigation}/>}
</View> }
......
......@@ -74,7 +74,7 @@ export default class Activity extends Component {
if (this.props.entity.listRef) {
const offsetToScrollTo = this.props.entity._list.scrollOffset + e.nativeEvent.layout.height;
setTimeout(() => {
this.props.entity.listRef.scrollToOffset({
offset: offsetToScrollTo,
......@@ -116,7 +116,7 @@ export default class Activity extends Component {
return (
<View style={[styles.container, this.props.isReminded ? null : CommonStyle.hairLineBottom]} onLayout={this.onLayout}>
<View style={[styles.container, this.props.isReminded ? null : CommonStyle.hairLineBottom]} onLayout={this.onLayout} testID="ActivityView">
<Pinned entity={this.props.entity}/>
{ this.showOwner() }
{ lock }
......@@ -134,15 +134,15 @@ export default class Activity extends Component {
{ overlay }
</View>
{ this.showActions() }
{ this.props.entity.isScheduled() &&
{ this.props.entity.isScheduled() &&
<View style={[{backgroundColor: '#ffecb3'}, CommonStyle.padding]}>
<Text style={[styles.scheduledText, CommonStyle.paddingLeft]}>
<Text style={[styles.scheduledText, CommonStyle.paddingLeft]}>
{`${i18n.t('activity.scheduled')} ${formatDate(this.props.entity.time_created)}.`}
</Text>
</View> }
{ this.props.isLast ? <View style={styles.activitySpacer}></View> : null}
{ !this.props.hideTabs &&
!this.props.entity.isScheduled() &&
{ !this.props.hideTabs &&
!this.props.entity.isScheduled() &&
<ActivityMetrics entity={this.props.entity}/> }
</View>
);
......@@ -197,6 +197,7 @@ export default class Activity extends Component {
entity={this.props.entity}
navigation={this.props.navigation}
onTranslate={this.showTranslate}
testID={this.props.entity.text==='e2eTest' ? 'ActivityMoreButton' : ''}
/>
</View>
)
......@@ -237,6 +238,7 @@ export default class Activity extends Component {
entity={this.props.entity}
navigation={this.props.navigation}
onTranslate={this.showTranslate}
testID={this.props.entity.text==='e2eTest' ? 'ActivityMoreButton' : ''}
/>}
</View>
</View>
......
......@@ -38,7 +38,7 @@ export default class ActivityActionSheet extends Component {
*/
constructor(props) {
super(props);
this.deleteOption = <Text style={[CS.colorDanger, CS.fontXL]}>{i18n.t('delete')}</Text>
this.deleteOption = <Text testID='deleteOption' style={[CS.colorDanger, CS.fontXL]}>{i18n.t('delete')}</Text>
}
/**
......@@ -310,7 +310,7 @@ export default class ActivityActionSheet extends Component {
onPress={() => this.showActionSheet()}
size={26}
style={CS.colorDarkGreyed}
{...testID('Activity Menu button')}
testID={this.props.testID}
/>
<ActionSheet
ref={o => this.ActionSheet = o}
......
......@@ -39,7 +39,7 @@ class CommentsAction extends Component {
const color = canComment ? (this.props.entity['comments:count'] > 0 ? CS.colorPrimary : CS.colorAction) : CS.colorLightGreyed;
return (
<TouchableOpacityCustom style={[CS.flexContainer, CS.centered, CS.rowJustifyCenter]} onPress={this.openComments}>
<TouchableOpacityCustom style={[CS.flexContainer, CS.centered, CS.rowJustifyCenter]} onPress={this.openComments} testID={this.props.testID}>
<Icon style={[color, CS.marginRight]} name={icon} size={this.props.size} />
<Counter size={this.props.size * 0.70} count={this.props.entity['comments:count']} />
</TouchableOpacityCustom>
......
......@@ -127,7 +127,12 @@ export default class ChannelSetupStep extends Component {
<View style={[CS.padding4x, CS.flexContainer, CS.rowJustifyStart, CS.alignCenter]}>
<Text style={[CS.fontXXL, CS.colorDark, CS.fontMedium]}>{i18n.t('onboarding.chooseAvatar')}</Text>
<View style={[CS.rowJustifyEnd, CS.flexContainer]}>
<TouchableCustom onPress={this.changeAvatarAction} style={[styles.avatar, CS.marginLeft3x, CS.border, CS.borderGreyed ]} disabled={this.saving}>
<TouchableCustom
onPress={this.changeAvatarAction}
style={[styles.avatar, CS.marginLeft3x, CS.border, CS.borderGreyed ]}
disabled={this.saving}
testID="selectAvatar"
>
{hasAvatar && <Image source={avatar} style={styles.wrappedAvatar} />}
<View style={[styles.tapOverlayView, hasAvatar ? null : CS.backgroundTransparent]}/>
......
......@@ -124,7 +124,7 @@ export default class RewardsStep extends Component {
return (
<View>
<View style={[style.cols, style.form]}>
<View style={[style.cols, style.form]} testID="RewardsOnboarding">
<PhoneInput
disabled={this.state.inProgress}
style={{ ...stylesheet.col, ...stylesheet.colFirst, ...stylesheet.phoneInput }}
......
......@@ -17,27 +17,37 @@ import i18n from '../../common/services/i18n.service';
@observer
export default class SuggestedChannelsStep extends Component {
/**
* Component did mount
*/
componentDidMount() {
this.props.onboarding.suggestedUsers.list.clearList();
this.props.onboarding.getSuggestedUsers();
}
renderUser = (user) => {
/**
* Render user
*/
renderUser = (user, index) => {
return <DiscoveryUser
row={{item: user}}
key={user.guid}
testID={`suggestedUser${index}`}
/>
}
/**
* Render
*/
render() {
return (
<View>
<View style={[CS.padding4x]}>
<View style={[CS.padding4x]} testID="suggestedChannelWizard">
<Text style={[CS.fontXXL, CS.colorDark, CS.fontMedium]}>{i18n.t('onboarding.suggestedChannels')}</Text>
<Text style={[CS.fontL, CS.colorDarkGreyed, CS.marginBottom3x]}>{i18n.t('onboarding.suggestedChannelsDescription')}</Text>
</View>
{!this.props.onboarding.suggestedUsers.list.loaded && <ActivityIndicator/>}
{this.props.onboarding.suggestedUsers.list.entities.map(user => this.renderUser(user))}
{this.props.onboarding.suggestedUsers.list.entities.map((user, i) => this.renderUser(user, i))}
</View>
);
}
......
......@@ -18,10 +18,8 @@ import {
import reportService from './ReportService';
import ModalTopbar from '../topbar/ModalTopbar';
import colors from '../styles/Colors';
import { CommonStyle as CS } from '../styles/Common';
import { ComponentsStyle } from '../styles/Components';
import i18n from '../common/services/i18n.service';
import mindsService from '../common/services/minds.service';
......
......@@ -20,7 +20,7 @@ import Icon from 'react-native-vector-icons/MaterialIcons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import authService from './../auth/AuthService';
import { List, ListItem } from 'react-native-elements';
import { ListItem } from 'react-native-elements';
import { FormLabel, FormInput, Button } from 'react-native-elements';
import settingsService from './SettingsService';
import settingsStore from './SettingsStore';
......@@ -202,7 +202,7 @@ export default class SettingsScreen extends Component {
items={languages}
/>
<View style={styles.scrollViewContainer}>
<List containerStyle={styles.container}>
<View style={styles.container}>
{
list.map((l, i) => (
<ListItem
......@@ -221,7 +221,7 @@ export default class SettingsScreen extends Component {
/>
))
}
</List>
</View>
</View>
</ScrollView>
);
......
......@@ -12,8 +12,7 @@ import {
} from 'react-native';
import { List, ListItem } from 'react-native-elements';
import { FormLabel, FormInput, Button } from 'react-native-elements';
import settingsService from '../SettingsService';
import { Button } from 'react-native-elements';
import i18n from '../../common/services/i18n.service';
export default class BillingScreen extends Component {
......
......@@ -9,8 +9,7 @@ import {
Alert,
} from 'react-native';
import { List, ListItem } from 'react-native-elements';
import { FormLabel, FormInput, FormValidationMessage } from 'react-native-elements';
import { Input } from 'react-native-elements';
import settingsService from '../SettingsService';
import i18n from '../../common/services/i18n.service';
import validator from '../../common/services/validator.service';
......@@ -87,13 +86,16 @@ export default class EmailScreen extends Component {
// validate
const error = validator.emailMessage(email);
const message = error ? <FormValidationMessage>{error}</FormValidationMessage> : null;
return (
<View style={[CommonStyle.flexContainer, CommonStyle.backgroundWhite]}>
<FormLabel labelStyle={CommonStyle.fieldLabel}>{i18n.t('settings.currentEmail')}</FormLabel>
<FormInput onChangeText={this.setEmail} value={email} inputStyle={CommonStyle.fieldTextInput}/>
{message}
<View style={[CommonStyle.flexContainer, CommonStyle.backgroundWhite, CommonStyle.marginTop2x]}>
<Input
label={i18n.t('settings.currentEmail')}
onChangeText={this.setEmail}
value={email}
errorMessage={error}
leftIcon={{ type: 'font-awesome', name: 'envelope-o' }}
/>
<Button
text={i18n.t('save').toUpperCase()}
loading={this.state.saving}
......
......@@ -47,13 +47,15 @@ export const ComponentsStyle = StyleSheet.create({
marginLeft: 10,
backgroundColor: 'transparent',
borderColor: '#FFF',
borderWidth: 1
borderWidth: 1,
borderRadius: 30,
},
loginButtonText: {
// fontFamily: 'Roboto',
fontSize: 16,
fontWeight: '600',
letterSpacing: 1.25
letterSpacing: 1.25,
color: 'white',
},
registerCheckboxText: {
color: 'white'
......
......@@ -3,38 +3,38 @@ import React, {
} from 'react';
import {
View,
StyleSheet,
Text,
ScrollView,
BackHandler,
Linking,
ImageBackground,
Alert,
Platform,
ToastAndroid,
TouchableOpacity,
View,
StyleSheet,
Text,
ScrollView,
BackHandler,
Linking,
ImageBackground,
Alert,
Platform,
ToastAndroid,
TouchableOpacity,
} from 'react-native';
import {
inject
inject
} from 'mobx-react/native'
import RNExitApp from 'react-native-exit-app';
import {
MINDS_URI,
CODE_PUSH_TOKEN
MINDS_URI,
CODE_PUSH_TOKEN
} from '../config/Config';
import {
StackActions,
NavigationActions
StackActions,
NavigationActions
} from 'react-navigation';
import Icon from 'react-native-vector-icons/MaterialIcons';
import authService from './../auth/AuthService';
import { List, ListItem } from 'react-native-elements'
import { ListItem } from 'react-native-elements'
import FastImage from 'react-native-fast-image';
import { ComponentsStyle } from '../styles/Components';
......@@ -198,7 +198,7 @@ export default class MoreScreen extends Component {
getList(list) {
return (
<List containerStyle={styles.container}>
<View style={styles.container}>
{
list.map((l, i) => (
<ListItem
......@@ -215,7 +215,7 @@ export default class MoreScreen extends Component {
/>
))
}
</List>
</View>
)
}
}
......
......@@ -43,7 +43,7 @@ let screens = {
Messenger: {
screen: withErrorBoundaryScreen(MessengerScreen),
navigationOptions: {
tabBarTestID:'Messenger tab button',
tabBarTestID:'MessengerTabButton',
tabBarAccessibilityLabel: 'Messenger tab button',
},
},
......
......@@ -10,9 +10,6 @@ import {
import { observer, inject } from 'mobx-react/native'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { Avatar } from 'react-native-elements';
import { MINDS_CDN_URI } from '../config/Config';
export default class ModalTopbar extends Component {
......
......@@ -54,7 +54,7 @@ export default class Topbar extends Component {
width={38}
height={38}
onPress={() => this.props.navigation.push('Channel', { guid: this.props.user.me.guid })}
{...testID('topbar avatar button')}
testID="AvatarButton"
/> }
</View>
......
......@@ -5,6 +5,7 @@ import React, {
import {
StyleSheet,
FlatList,
Text,
View
} from 'react-native';
......@@ -17,7 +18,6 @@ import Icon from 'react-native-vector-icons/MaterialIcons';
import IonIcon from 'react-native-vector-icons/Ionicons';
import CenteredLoading from '../common/components/CenteredLoading';
import { Text } from 'react-native-elements';
import formatDate from '../common/helpers/date';
/**
* Notification Screen
......
This diff is collapsed.