Skip to content
Commits on Source (242)
......@@ -43,10 +43,10 @@ jobs:
- store_artifacts:
path: test-results
ios:
macos:
xcode: "10.1"
xcode: "11.1.0"
working_directory: ~/mobile-native
# use a --login shell so our "set Ruby version" command gets picked up for later steps
......@@ -60,7 +60,7 @@ jobs:
command: brew install getsentry/tools/sentry-cli
- run:
name: set Ruby version
command: echo "ruby-2.4" > ~/.ruby-version
command: echo "ruby-2.6" > ~/.ruby-version
- restore_cache:
key: yarn-v1-{{ checksum "yarn.lock" }}-{{ arch }}
......@@ -68,9 +68,19 @@ jobs:
- restore_cache:
key: node-v1-{{ checksum "package.json" }}-{{ arch }}
# remove detox from CI until is fixed
# - run:
# name: Install detox
# command:
# |
# HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew
# HOMEBREW_NO_AUTO_UPDATE=1 brew install --HEAD applesimutils
# npm install -g detox-cli
# npm install -g detox
# not using a workspace here as Node and Yarn versions
# differ between our macOS executor image and the Docker containers above
- run: yarn install
- run: yarn install --frozen-lockfile
- save_cache:
key: yarn-v1-{{ checksum "yarn.lock" }}-{{ arch }}
......@@ -94,6 +104,25 @@ jobs:
paths:
- vendor/bundle
- restore_cache:
key: pods-v1-{{ checksum "ios/Podfile.lock" }}-{{ arch }}
- run:
name: Install CocoaPods
command: pod install --verbose
working_directory: ios
- save_cache:
key: pods-v1-{{ checksum "ios/Podfile.lock" }}-{{ arch }}
paths:
- ios/Pods
# remove detox from CI until is fixed
# Run e2e
# - run: detox build -c ios.sim.release
# - run: detox test -c ios.sim.release --cleanup
### TODO- get tests running with fastlane
#- run:
......@@ -114,7 +143,7 @@ jobs:
#- store_artifacts:
# path: ios/test-results
- run:
- run:
name: Build release .ipa
command: fastlane buildrelease
working_directory: ios
......@@ -124,8 +153,8 @@ jobs:
- /release-*/
- test/circle-ci
- run:
name: Upload to crashalytics
- run:
name: Upload to crashalytics
command: echo "TODO"
working_directory: ios
branches:
......@@ -162,8 +191,8 @@ jobs:
command: |
apk add git
- checkout
- run:
name: Tag sentry release
- run:
name: Tag sentry release
command: |
version=`cat /tmp/workspace/version`
echo Tagging release with ${version}
......
module.exports = {
"parser": "babel-eslint",
"plugins": [
"react",
"react-native",
"flowtype"
],
"extends": ["plugin:react-native/all", "plugin:react/recommended"],
"env": {
"react-native/react-native": true
},
"rules": {
"no-unused-vars": [1],
"react/jsx-uses-vars": [2],
"flowtype/boolean-style": [
2,
"boolean"
],
"flowtype/define-flow-type": 1,
"flowtype/delimiter-dangle": [
2,
"never"
],
"flowtype/generic-spacing": [
2,
"never"
],
"flowtype/no-mixed": 0,
"flowtype/no-primitive-constructor-types": 2,
"flowtype/no-types-missing-file-annotation": 2,
"flowtype/no-weak-types": 0,
"flowtype/object-type-delimiter": [
2,
"comma"
],
"flowtype/require-parameter-type": 2,
"flowtype/require-readonly-react-props": 0,
"flowtype/require-return-type": [
2,
"always",
{
"annotateUndefined": "never"
}
],
"flowtype/require-valid-file-annotation": 2,
"flowtype/semi": [
2,
"always"
],
"flowtype/space-after-type-colon": [
2,
"always"
],
"flowtype/space-before-generic-bracket": [
2,
"never"
],
"flowtype/space-before-type-colon": [
2,
"never"
],
"flowtype/type-id-match": [
2,
"^([A-Z][a-z0-9]+)+Type$"
],
"flowtype/union-intersection-spacing": [
2,
"always"
],
"flowtype/use-flow-type": 1,
"flowtype/valid-syntax": 1
},
"settings": {
"flowtype": {
"onlyFilesWithFlowAnnotation": true
}
}
}
\ No newline at end of file
root: true,
extends: '@react-native-community',
};
\ No newline at end of file
......@@ -5,67 +5,71 @@
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore polyfills
node_modules/react-native/Libraries/polyfills/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
; These should not be required directly
; require from fbjs/lib instead: require('fbjs/lib/warning')
node_modules/warning/.*
; Ignore polyfills
.*/Libraries/polyfills/.*
; Flow doesn't support platforms
.*/Libraries/Utilities/LoadingView.js
; Ignore metro
.*/node_modules/metro/.*
[untyped]
.*/node_modules/@react-native-community/cli/.*/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
node_modules/react-native/flow-github/
[options]
emoji=true
esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable
esproposal.decorators=ignore
module.system=haste
module.system.haste.use_name_reducers=true
# get basename
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
# strip .js or .js.flow suffix
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
# strip .ios suffix
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
module.system.haste.paths.blacklist=.*/__tests__/.*
module.system.haste.paths.blacklist=.*/__mocks__/.*
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.json
module.file_ext=.native.js
module.file_ext=.ios.js
munge_underscores=true
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/react-native/react-native-implementation'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[lints]
sketchy-null-number=warn
sketchy-null-mixed=warn
sketchy-number=warn
untyped-type-import=warn
nonstrict-import=warn
deprecated-type=warn
unsafe-getters-setters=warn
inexact-spread=warn
unnecessary-invariant=warn
signature-verification-failure=warn
deprecated-utility=error
[strict]
deprecated-type
nonstrict-import
sketchy-null
unclear-type
unsafe-getters-setters
untyped-import
untyped-type-import
[version]
^0.92.0
^0.107.0
......@@ -5,7 +5,6 @@
# Xcode
#
ios/Podfile.lock
build/
*.pbxuser
!default.pbxuser
......@@ -42,6 +41,7 @@ yarn-error.log
buck-out/
\.buckd/
*.keystore
!debug.keystore
# fastlane
#
......@@ -57,6 +57,9 @@ buck-out/
# Bundle artifact
*.jsbundle
# CocoaPods
/ios/Pods/
# Jest cache
.jest/
......
......@@ -6,7 +6,7 @@ stages:
- deploy
test:jest:
image: node:10.10.0
image: node:10.16.3
stage: test
cache:
key: ${CI_COMMIT_REF_SLUG}
......@@ -22,7 +22,7 @@ build:android:
stage: build
before_script:
- sudo sysctl fs.inotify.max_user_watches=524288
- sudo sysctl -p
- sudo sysctl -p
script:
- yarn install
- cd android
......@@ -36,29 +36,6 @@ build:android:
expire_in: 7 days
when: on_success
e2e:browserstacks:
image: node:10.10.0
stage: e2e
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
script:
- yarn install
- export bsAPP=`curl -u "${bsUSER}:${bsKEY}" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@./Minds-$CI_BUILD_REF_SLUG.apk"| grep -o 'bs\:\/\/.*"' | sed 's/.$//'`
- yarn e2e
tags:
- docker
dependencies:
- build:android
only:
refs:
- /^stable-*/
- /^release-*/
- /^feat-*/
- /^test-*/
allow_failure: true
deploy:s3:
image: minds/ci:latest
stage: deploy
......
module.exports = {
bracketSpacing: false,
jsxBracketSameLine: true,
singleQuote: true,
trailingComma: 'all',
};
//import 'abortcontroller-polyfill/dist/polyfill-patch-fetch'
import './global';
import './shim'
//import crypto from "crypto"; // DO NOT REMOVE!
import React, {
Component
} from 'react';
import {
createStackNavigator,
NavigationActions
} from 'react-navigation';
import {
BackHandler,
Platform,
AppState,
Linking,
Text,
Alert,
View,
} from 'react-native';
import NavigationStack from './src/navigation/NavigationStack';
import NavigationService from './src/navigation/NavigationService';
const Stack = createStackNavigator({});
/**
* App
*/
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
appState: AppState.currentState
};
}
/**
* Handle app state changes
*/
handleAppStateChange = (nextState) => {
// if the app turns active we check for shared
if (this.state.appState.match(/inactive|background/) && nextState === 'active') {
receiveShare.handle();
}
this.setState({appState: nextState})
}
/**
* On component will mount
*/
componentWillMount() {
if (!Text.defaultProps) Text.defaultProps = {};
Text.defaultProps.style = {
fontFamily: 'Roboto',
color: '#444',
};
}
/**
* Render
*/
render() {
const app = (
<View></View>
);
return [ app ];
}
}
......@@ -5,20 +5,17 @@
* @format
* @flow
*/
import './global';
import './shim'
import crypto from "crypto"; // DO NOT REMOVE!
import React, {
Component
Component,
} from 'react';
import {
Observer,
Provider,
} from 'mobx-react/native' // import from mobx-react/native instead of mobx-react fix test
} from 'mobx-react/native'; // import from mobx-react/native instead of mobx-react fix test
import NavigationService from './src/navigation/NavigationService';
import RNBootSplash from "react-native-bootsplash";
import {
BackHandler,
......@@ -28,10 +25,10 @@ import {
Text,
Alert,
Clipboard,
StatusBar,
} from 'react-native';
import FlashMessage from "react-native-flash-message";
import CookieManager from 'react-native-cookies';
import FlashMessage from 'react-native-flash-message';
import KeychainModalScreen from './src/keychain/KeychainModalScreen';
import BlockchainTransactionModalScreen from './src/blockchain/transaction-modal/BlockchainTransactionModalScreen';
......@@ -47,9 +44,9 @@ import sessionService from './src/common/services/session.service';
import deeplinkService from './src/common/services/deeplinks-router.service';
import badgeService from './src/common/services/badge.service';
import authService from './src/auth/AuthService';
import NotificationsService from "./src/notifications/NotificationsService";
import NotificationsService from './src/notifications/NotificationsService';
import getMaches from './src/common/helpers/getMatches';
import {CODE_PUSH_TOKEN, GOOGLE_PLAY_STORE} from './src/config/Config';
import { GOOGLE_PLAY_STORE } from './src/config/Config';
import updateService from './src/common/services/update.service';
import ErrorBoundary from './src/common/components/ErrorBoundary';
import { CommonStyle as CS } from './src/styles/Common';
......@@ -63,17 +60,20 @@ import connectivityService from './src/common/services/connectivity.service';
import sqliteStorageProviderService from './src/common/services/sqlite-storage-provider.service';
import commentStorageService from './src/comments/CommentStorageService';
import * as Sentry from '@sentry/react-native';
import apiService from './src/common/services/api.service';
import boostedContentService from './src/common/services/boosted-content.service';
let deepLinkUrl = '';
const statusBarStyle = Platform.OS === 'ios' ? 'dark-content' : 'default';
// init push service
pushService.init();
// fire sqlite init
sqliteStorageProviderService.get();
CookieManager.clearAll();
apiService.clearCookies();
// On app login (runs if the user login or if it is already logged in)
sessionService.onLogin(async () => {
......@@ -85,8 +85,9 @@ sessionService.onLogin(async () => {
});
logService.info('[App] Getting minds settings and onboarding progress');
// load minds settings and onboarding progresss on login
const results = await Promise.all([mindsService.getSettings(), stores.onboarding.getProgress(), boostedContentService.load()]);
// load minds settings and boosted content
await Promise.all([mindsService.getSettings(), boostedContentService.load()]);
logService.info('[App] updatting features');
// reload fatures on login
......@@ -96,15 +97,15 @@ sessionService.onLogin(async () => {
pushService.registerToken();
// get onboarding progress
const onboarding = results[1];
logService.info('[App] navigating to initial screen', sessionService.initialScreen);
if (onboarding && onboarding.show_onboarding) {
sessionService.setInitialScreen('OnboardingScreen');
}
// hide splash
RNBootSplash.hide({ duration: 250 });
logService.info('[App] navigating to initial screen', sessionService.initialScreen);
NavigationService.reset(sessionService.initialScreen);
NavigationService.navigate(sessionService.initialScreen);
// check onboarding progress and navigate if necessary
stores.onboarding.getProgress();
// check update
if (Platform.OS !== 'ios' && !GOOGLE_PLAY_STORE) {
......@@ -142,13 +143,10 @@ sessionService.onLogin(async () => {
//on app logout
sessionService.onLogout(() => {
// clear app badge
badgeService.setUnreadConversations(0);
badgeService.setUnreadNotifications(0);
// clear minds settings
mindsService.clear();
// clear offline cache
entitiesStorage.removeAll();
feedsStorage.removeAll();
......@@ -188,10 +186,14 @@ export default class App extends Component<Props, State> {
}
/**
* On component will mount
* contructor
*/
componentWillMount() {
if (!Text.defaultProps) Text.defaultProps = {};
constructor(props) {
super(props);
if (!Text.defaultProps) {
Text.defaultProps = {};
}
Text.defaultProps.style = {
fontFamily: 'Roboto',
color: '#444',
......@@ -203,8 +205,9 @@ export default class App extends Component<Props, State> {
*/
async componentDidMount() {
try {
// load app setting before start
const results = await Promise.all([settingsStore.init(), await Linking.getInitialURL()]),
const results = await Promise.all([settingsStore.init(), await Linking.getInitialURL()]);
deepLinkUrl = results[1];
......@@ -214,11 +217,13 @@ export default class App extends Component<Props, State> {
if (!this.handlePasswordResetDeepLink()) {
logService.info('[App] initializing session');
const token = await sessionService.init();
if (!token) {
logService.info('[App] there is no active session');
NavigationService.reset('Login');
RNBootSplash.hide({ duration: 250 });
NavigationService.navigate('Login');
} else {
logService.info('[App] session initialized');
}
......@@ -294,13 +299,13 @@ export default class App extends Component<Props, State> {
const app = (
<Provider key="app" {...stores}>
<ErrorBoundary message="An error occurred" containerStyle={CS.centered}>
<StatusBar barStyle={statusBarStyle} />
<NavigationStack
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
<FlashMessage renderCustomContent={this.renderNotification}
/>
<FlashMessage renderCustomContent={this.renderNotification} />
</ErrorBoundary>
</Provider>
);
......@@ -314,7 +319,7 @@ export default class App extends Component<Props, State> {
);
const tosModal = (
<TosModal user={stores.user}/>
<TosModal user={stores.user} key="tosModal"/>
)
return [ app, keychainModal, blockchainTransactionModal, tosModal];
......
......@@ -12,6 +12,7 @@ import logService from './src/common/services/log.service';
import * as Sentry from '@sentry/react-native';
import { isAbort, isNetworkFail } from './src/common/helpers/abortableFetch';
import { isApiError } from './src/common/services/api.service';
import { isUserError } from './src/common/UserError';
// Init Sentry (if not running test)
......@@ -33,6 +34,10 @@ if (process.env.JEST_WORKER_ID === undefined) {
if (isAbort(hint.originalException)) {
return null;
}
// ignore user errors
if (isUserError(hint.originalException)) {
return null;
}
// only log api 500 errors
if (isApiError(hint.originalException) && hint.originalException.status < 500) {
return null;
......
......@@ -27,11 +27,13 @@ import groupsBar from './src/groups/GroupsBarStore';
import sessionService from './src/common/services/session.service';
import logService from './src/common/services/log.service';
import SubscriptionRequestStore from './src/channel/subscription/SubscriptionRequestStore';
/**
* App stores
*/
const stores = {
subscriptionRequest: new SubscriptionRequestStore(),
newsfeed: new newsfeed(),
notifications: new notifications(),
notificationsSettings: new notificationsSettings(),
......
......@@ -28,14 +28,31 @@
- iOS
- Android
## Building
## Install dependencies
- `yarn install`
- `react-native run-ios` or `react-native run-android`
- `cd ios && pod install` (iOS only)
## Building
- `yarn android` or `yarn ios`
## Testing
- `yarn test`
## Testing e2e (macOS)
Install the detox cli
- `brew tap wix/brew`
- `brew install applesimutils`
- `yarn global add detox-cli`
Run the tests
- `detox build -c ios.sim.debug`
- `detox test -c ios.sim.debug`
You can use -c ios.sim.release for e2e test a production build
### _Copyright Minds 2018_
import wd from 'wd';
import sleep from '../../src/common/helpers/sleep';
export default async(driver) => {
// should ask for permissions
const permmision = await driver.waitForElementById('com.android.packageinstaller:id/permission_allow_button', wd.asserters.isDisplayed, 10000)
// we accept
permmision.click();
}
\ No newline at end of file
import wd from 'wd';
import sleep from '../../src/common/helpers/sleep';
export default async(driver) => {
// select first image
const firstImage = await driver.waitForElementByAccessibilityId('Gallery image/jpeg', wd.asserters.isDisplayed, 5000);
await firstImage.click();
await sleep(3000);
}
\ No newline at end of file
import wd from 'wd';
import sleep from '../../src/common/helpers/sleep';
export default async(driver, amount) => {
const lockButton = await driver.waitForElementByAccessibilityId('Post lock button', wd.asserters.isDisplayed, 5000);
await lockButton.click();
const postInput = await driver.waitForElementByAccessibilityId('Poster lock amount input', wd.asserters.isDisplayed, 5000);
await postInput.type(amount);
// we press post button
const postButton = await driver.elementByAccessibilityId('Poster lock done button');
await postButton.click();
}
\ No newline at end of file
export default async(driver) => {
const username = await driver.elementByAccessibilityId('username input');
const password = await driver.elementByAccessibilityId('password input');
const loginButton = await driver.elementByAccessibilityId('login button');
await username.type(process.env.loginUser);
await password.type(process.env.loginPass);
await loginButton.click();
}
\ No newline at end of file
import wd from 'wd';
import sleep from '../../src/common/helpers/sleep';
export default async(driver, text) => {
// post screen must be shown
const postInput = await driver.waitForElementByAccessibilityId('PostInput', wd.asserters.isDisplayed, 5000);
await postInput.type(text);
// we press post button
const postButton = await driver.elementByAccessibilityId('Capture Post Button');
await postButton.click();
}
\ No newline at end of file
import wd from 'wd';
import sleep from '../../src/common/helpers/sleep';
export default async(driver) => {
// tap the capture button
const button = await driver.waitForElementByAccessibilityId('CaptureButton', wd.asserters.isDisplayed, 10000);
button.click();
return button;
}
\ No newline at end of file
import wd from 'wd';
import sleep from '../../src/common/helpers/sleep';
export default async(driver, options) => {
// tap the toggle button
const button = await driver.waitForElementByAccessibilityId('NSFW button', wd.asserters.isDisplayed, 5000);
await button.click();
// wait until the menu is shown
await sleep(500);
for (let index = 0; index < options.length; index++) {
const name = options[index];
const element = await driver.elementByAccessibilityId(`NSFW ${name}`);
await element.click();
}
let action = new wd.TouchAction(driver);
action.tap({x:100, y:170});
await action.release().perform();
}
\ No newline at end of file
import wd from 'wd';
import reporterFactory from '../tests-helpers/browserstack-reporter.factory';
import post from './actions/post';
import login from './actions/login';
import { driver, capabilities} from './config';
import sleep from '../src/common/helpers/sleep';
import pressCapture from './actions/pressCapture';
import acceptPermissions from './actions/acceptPermissions';
import attachPostGalleryImage from './actions/attachPostGalleryImage';
import selectNsfw from './actions/selectNsfw';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;
const data = {sessiondID: null};
jasmine.getEnv().addReporter(reporterFactory(data));
//TODO: add support for ios to this test (xpath)
describe('Activity flow tests', () => {
beforeAll(async () => {
await driver.init(capabilities);
data.sessiondID = await driver.getSessionId();
console.log('BROWSERSTACK_SESSION: ' + data.sessiondID);
await driver.waitForElementByAccessibilityId('username input', wd.asserters.isDisplayed, 5000);
// we should login
await login(driver);
});
afterAll(async () => {
await driver.quit();
});
it('should post a text and see it in the newsfeed', async () => {
const str = 'My e2e activity';
// press capture button
await pressCapture(driver);
// accept gallery permissions
await acceptPermissions(driver);
// make the post
await post(driver, str);
// should post and return to the newsfeed
await driver.waitForElementByAccessibilityId('Newsfeed Screen', wd.asserters.isDisplayed, 10000);
// the first element of the list should be the post
const textElement = await driver.waitForElementByXPath('//android.view.ViewGroup[@content-desc="Newsfeed Screen"]/android.view.ViewGroup[1]/android.widget.ScrollView/android.view.ViewGroup/android.view.ViewGroup[2]/android.view.ViewGroup/android.widget.TextView[2]');
expect(await textElement.text()).toBe(str);
});
it('should like the post', async() => {
const likeButton = await driver.waitForElementByAccessibilityId('Thumb up activity button', 5000);
await likeButton.click();
const likeCount = await driver.waitForElementByAccessibilityId('Thumb up count', 5000);
expect(await likeCount.text()).toBe('1');
});
it('should unlike the post', async() => {
const likeButton = await driver.waitForElementByAccessibilityId('Thumb down activity button', 5000);
await likeButton.click();
const likeCount = await driver.waitForElementByAccessibilityId('Thumb down count', 5000);
expect(await likeCount.text()).toBe('1');
});
});
\ No newline at end of file
import factory from '../tests-helpers/e2e-driver.factory';
const customCapabilities = {
'device' : 'Samsung Galaxy S9',
'os_version' : '8.0'
};
let driver, capabilities;
if (process.env.e2elocal) {
[driver, capabilities] = factory('androidLocal', {});
} else {
[driver, capabilities] = factory('browserStack', customCapabilities);
}
export {driver, capabilities} ;