Compare commits

...

29 Commits

Author SHA1 Message Date
Diego Mello fba9151bc8 waitForToast 2020-05-20 13:32:54 -03:00
Diego Mello 3fbced85bb navigateToRoom 2020-05-20 11:58:41 -03:00
Diego Mello 470145e75c Onboarding tests passing 2020-05-20 11:34:31 -03:00
Diego Mello 0152f16053 Initial Android setup 2020-05-20 10:56:49 -03:00
Diego Mello 4062b9470b Update readme 2020-05-19 16:49:27 -03:00
Diego Mello 8c5d6c06d7 detox-test command 2020-05-19 16:25:11 -03:00
Diego Mello 557590b54c Merge branch 'develop' into chore.update-detox 2020-05-19 16:15:00 -03:00
Diego Mello 09e52102d8 mac-env executor 2020-05-19 16:14:36 -03:00
Diego Mello 09d2a8e6a7
Merge branch 'develop' into chore.update-detox 2020-05-18 14:10:54 -03:00
Diego Mello 3f624ad15b Save brew cache 2020-05-18 13:06:14 -03:00
Diego Mello 0501c20bba Add all tests 2020-05-18 12:56:41 -03:00
Diego Mello a59a1bfa99 npx detox 2020-05-18 11:14:32 -03:00
Diego Mello 93f433fb19 Onboarding tests 2020-05-18 10:11:37 -03:00
Diego Mello 7721bc3489 CI refactor 2020-05-18 09:55:12 -03:00
Diego Mello 100824ebd0 lint 2020-05-15 18:03:19 -03:00
Diego Mello 6695fccbd8 CI 2020-05-15 18:00:21 -03:00
Diego Mello 519574f945 Revert "config.yml refactor"
This reverts commit 0e984d3029.
2020-05-15 17:36:41 -03:00
Diego Mello 0e984d3029 config.yml refactor 2020-05-15 17:35:44 -03:00
Diego Mello cf46ca3e29 Tests separated 2020-05-15 15:19:54 -03:00
Diego Mello 13612474fe room and onboarding 2020-05-15 14:06:14 -03:00
Diego Mello 125b7e61dc working 2020-05-15 11:27:18 -03:00
Diego Mello 5ffd8749d0 forgot password 2020-05-14 15:52:28 -03:00
Diego Mello 62bb88429f custom status 2020-05-14 15:45:25 -03:00
Diego Mello 0b402489d1 profile 2020-05-14 15:25:51 -03:00
Diego Mello 2db9caf40f broadcast 2020-05-14 15:08:35 -03:00
Diego Mello 79b4879526 change server 2020-05-14 14:13:27 -03:00
Diego Mello 9c54e16ed7 roominfo 2020-05-14 14:07:01 -03:00
Diego Mello 379112e6b7 create room 2020-05-14 13:01:13 -03:00
Diego Mello 889de2aefe Tests passing until roomslist 2020-05-14 12:55:03 -03:00
47 changed files with 842 additions and 826 deletions

View File

@ -1,7 +1,127 @@
defaults: &defaults defaults: &defaults
working_directory: ~/repo working_directory: ~/repo
version: 2 macos: &macos
macos:
xcode: "11.2.1"
bash-env: &bash-env
BASH_ENV: "~/.nvm/nvm.sh"
install-npm-modules: &install-npm-modules
name: Install NPM modules
command: yarn
restore-npm-cache-linux: &restore-npm-cache-linux
name: Restore NPM cache
key: node-modules-{{ checksum "yarn.lock" }}
save-npm-cache-linux: &save-npm-cache-linux
key: node-modules-{{ checksum "yarn.lock" }}
name: Save NPM cache
paths:
- ./node_modules
restore-npm-cache-mac: &restore-npm-cache-mac
name: Restore NPM cache
key: node-v1-mac-{{ checksum "yarn.lock" }}
save-npm-cache-mac: &save-npm-cache-mac
key: node-v1-mac-{{ checksum "yarn.lock" }}
name: Save NPM cache
paths:
- ./node_modules
install-node: &install-node
name: Install Node 10
command: |
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash
source ~/.nvm/nvm.sh
# https://github.com/creationix/nvm/issues/1394
set +e
nvm install 10
echo 'export PATH="/home/circleci/.nvm/versions/node/v10.20.1/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
restore-gems-cache: &restore-gems-cache
name: Restore gems cache
key: bundle-v1-{{ checksum "ios/Gemfile.lock" }}
save-gems-cache: &save-gems-cache
name: Save gems cache
key: bundle-v1-{{ checksum "ios/Gemfile.lock" }}
paths:
- vendor/bundle
update-fastlane: &update-fastlane
name: Update Fastlane
command: |
echo "ruby-2.6.4" > ~/.ruby-version
bundle install
working_directory: ios
restore-brew-cache: &restore-brew-cache
name: Restore Brew cache
key: brew-{{ checksum "yarn.lock" }}-{{ checksum ".circleci/config.yml" }}
save-brew-cache: &save-brew-cache
name: Save brew cache
key: brew-{{ checksum "yarn.lock" }}-{{ checksum ".circleci/config.yml" }}
paths:
- /usr/local/Homebrew
install-apple-sim-utils: &install-apple-sim-utils
name: Install appleSimUtils
command: |
brew update
brew tap wix/brew
brew install wix/brew/applesimutils
rebuild-detox: &rebuild-detox
name: Rebuild Detox framework cache
command: |
npx detox clean-framework-cache
npx detox build-framework-cache
version: 2.1
# EXECUTORS
executors:
mac-env:
<<: *macos
environment:
<<: *bash-env
# COMMANDS
commands:
detox-test:
parameters:
folder:
type: string
steps:
- checkout
- attach_workspace:
at: .
- restore_cache: *restore-npm-cache-mac
- restore_cache: *restore-brew-cache
- run: *install-node
- run: *install-apple-sim-utils
- run: *install-npm-modules
- run: *rebuild-detox
- run:
name: Test
command: |
npx detox test << parameters.folder >> --configuration ios.sim.release --cleanup
# JOBS
jobs: jobs:
lint-testunit: lint-testunit:
<<: *defaults <<: *defaults
@ -14,14 +134,9 @@ jobs:
steps: steps:
- checkout - checkout
- restore_cache: - restore_cache: *restore-npm-cache-linux
name: Restore NPM cache
key: node-modules-{{ checksum "yarn.lock" }}
- run: - run: *install-npm-modules
name: Install NPM modules
command: |
yarn
- run: - run:
name: Lint name: Lint
@ -38,162 +153,79 @@ jobs:
command: | command: |
yarn codecov yarn codecov
- save_cache: - save_cache: *save-npm-cache-linux
key: node-modules-{{ checksum "yarn.lock" }}
name: Save NPM cache
paths:
- ./node_modules
# E2E
e2e-build: e2e-build:
macos: executor: mac-env
xcode: "11.2.1"
environment:
BASH_ENV: "~/.nvm/nvm.sh"
steps: steps:
- checkout - checkout
- restore_cache: - restore_cache: *restore-npm-cache-mac
name: Restore NPM cache
key: node-v1-mac-{{ checksum "yarn.lock" }}
- run: - restore_cache: *restore-brew-cache
name: Install Node 10
command: |
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash
source ~/.nvm/nvm.sh
# https://github.com/creationix/nvm/issues/1394
set +e
nvm install 10
- run: - run: *install-node
name: Install appleSimUtils
command: |
brew update
brew tap wix/brew
brew install wix/brew/applesimutils
- run: - run: *install-apple-sim-utils
name: Install NPM modules
command: |
yarn global add detox-cli
yarn
- run: - run: *install-npm-modules
name: Rebuild Detox framework cache
command: | - run: *rebuild-detox
detox clean-framework-cache
detox build-framework-cache
- run: - run:
name: Build name: Build
command: | command: |
detox build --configuration ios.sim.release npx detox build --configuration ios.sim.release
- persist_to_workspace: - persist_to_workspace:
root: . root: .
paths: paths:
- ios/build/Build/Products/Release-iphonesimulator/RocketChatRN.app - ios/build/Build/Products/Release-iphonesimulator/RocketChatRN.app
- save_cache: - save_cache: *save-npm-cache-mac
name: Save NPM cache
key: node-v1-mac-{{ checksum "yarn.lock" }}
paths:
- node_modules
e2e-test: - save_cache: *save-brew-cache
macos:
xcode: "11.2.1"
environment:
BASH_ENV: "~/.nvm/nvm.sh"
e2e-test-onboarding:
executor: mac-env
steps: steps:
- checkout - detox-test:
folder: "./e2e/tests/onboarding"
- attach_workspace: e2e-test-room:
at: . executor: mac-env
steps:
- detox-test:
folder: "./e2e/tests/room"
- restore_cache: e2e-test-assorted:
name: Restore NPM cache executor: mac-env
key: node-v1-mac-{{ checksum "yarn.lock" }} steps:
- detox-test:
- run: folder: "./e2e/tests/assorted"
name: Install Node 10
command: |
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash
source ~/.nvm/nvm.sh
# https://github.com/creationix/nvm/issues/1394
set +e
nvm install 10
- run:
name: Install appleSimUtils
command: |
brew update
brew tap wix/brew
brew install wix/brew/applesimutils
- run:
name: Install NPM modules
command: |
yarn global add detox-cli
yarn
- run:
name: Rebuild Detox framework cache
command: |
detox clean-framework-cache
detox build-framework-cache
- run:
name: Test
command: |
detox test --configuration ios.sim.release --cleanup
- save_cache:
name: Save NPM cache
key: node-v1-mac-{{ checksum "yarn.lock" }}
paths:
- node_modules
# Android builds
android-build: android-build:
<<: *defaults <<: *defaults
docker: docker:
- image: circleci/android:api-28-node - image: circleci/android:api-28-node
environment: environment:
# GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError"
# GRADLE_OPTS: -Xmx2048m -Dorg.gradle.daemon=false
# JVM_OPTS: -Xmx4096m
JAVA_OPTS: '-Xms512m -Xmx2g' JAVA_OPTS: '-Xms512m -Xmx2g'
GRADLE_OPTS: '-Xmx3g -Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx2g -XX:+HeapDumpOnOutOfMemoryError"' GRADLE_OPTS: '-Xmx3g -Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-Xmx2g -XX:+HeapDumpOnOutOfMemoryError"'
TERM: dumb TERM: dumb
BASH_ENV: "~/.nvm/nvm.sh" <<: *bash-env
steps: steps:
- checkout - checkout
- run: - run: *install-node
name: Install Node 10
command: |
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash
source ~/.nvm/nvm.sh
# https://github.com/creationix/nvm/issues/1394
set +e
nvm install 10
echo 'export PATH="/home/circleci/.nvm/versions/node/v10.20.1/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
- restore_cache: - restore_cache: *restore-npm-cache-linux
name: Restore NPM cache
key: node-modules-{{ checksum "yarn.lock" }}
- run: - run: *install-npm-modules
name: Install NPM modules
command: |
yarn
- restore_cache: - restore_cache:
name: Restore gradle cache name: Restore gradle cache
@ -261,11 +293,7 @@ jobs:
- store_artifacts: - store_artifacts:
path: /tmp/build/outputs path: /tmp/build/outputs
- save_cache: - save_cache: *save-npm-cache-linux
name: Save NPM cache
key: node-modules-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
- save_cache: - save_cache:
name: Save gradle cache name: Save gradle cache
@ -273,44 +301,22 @@ jobs:
paths: paths:
- ~/.gradle - ~/.gradle
# iOS builds
ios-build: ios-build:
macos: executor: mac-env
xcode: "11.2.1"
environment:
BASH_ENV: "~/.nvm/nvm.sh"
steps: steps:
- checkout - checkout
- restore_cache: - restore_cache: *restore-gems-cache
name: Restore gems cache
key: bundle-v1-{{ checksum "ios/Gemfile.lock" }}
- restore_cache: - restore_cache: *restore-npm-cache-mac
name: Restore NPM cache
key: node-v1-mac-{{ checksum "yarn.lock" }}
- run: - run: *install-node
name: Install Node 10
command: |
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash
source ~/.nvm/nvm.sh
# https://github.com/creationix/nvm/issues/1394
set +e
nvm install 10
- run: - run: *install-npm-modules
name: Install NPM modules
command: |
yarn
- run: - run: *update-fastlane
name: Update Fastlane
command: |
echo "ruby-2.6.4" > ~/.ruby-version
bundle install
working_directory: ios
- run: - run:
name: Set Google Services name: Set Google Services
@ -348,17 +354,9 @@ jobs:
fi fi
working_directory: ios working_directory: ios
- save_cache: - save_cache: *save-npm-cache-mac
name: Save NPM cache
key: node-v1-mac-{{ checksum "yarn.lock" }}
paths:
- node_modules
- save_cache: - save_cache: *save-gems-cache
name: Save gems cache
key: bundle-v1-{{ checksum "ios/Gemfile.lock" }}
paths:
- vendor/bundle
- store_artifacts: - store_artifacts:
path: ios/RocketChatRN.ipa path: ios/RocketChatRN.ipa
@ -370,8 +368,7 @@ jobs:
- ios/fastlane/report.xml - ios/fastlane/report.xml
ios-testflight: ios-testflight:
macos: executor: mac-env
xcode: "11.2.1"
steps: steps:
- checkout - checkout
@ -379,16 +376,9 @@ jobs:
- attach_workspace: - attach_workspace:
at: ios at: ios
- restore_cache: - restore_cache: *restore-gems-cache
name: Restore gems cache
key: bundle-v1-{{ checksum "ios/Gemfile.lock" }}
- run: - run: *update-fastlane
name: Update Fastlane
command: |
echo "ruby-2.4" > ~/.ruby-version
bundle install
working_directory: ios
- run: - run:
name: Fastlane Tesflight Upload name: Fastlane Tesflight Upload
@ -396,14 +386,9 @@ jobs:
bundle exec fastlane ios beta bundle exec fastlane ios beta
working_directory: ios working_directory: ios
- save_cache: - save_cache: *save-gems-cache
name: Save gems cache
key: bundle-v1-{{ checksum "ios/Gemfile.lock" }}
paths:
- vendor/bundle
workflows: workflows:
version: 2
build-and-test: build-and-test:
jobs: jobs:
- lint-testunit - lint-testunit
@ -415,7 +400,13 @@ workflows:
- e2e-build: - e2e-build:
requires: requires:
- e2e-hold - e2e-hold
- e2e-test: - e2e-test-onboarding:
requires:
- e2e-build
- e2e-test-room:
requires:
- e2e-build
- e2e-test-assorted:
requires: requires:
- e2e-build - e2e-build

View File

@ -208,13 +208,15 @@ Readme will guide you on how to config.
- Build your app - Build your app
```bash ```bash
$ detox build --configuration ios.sim.release $ npx detox build --configuration ios.sim.release
``` ```
- Run tests - Run tests
```bash ```bash
$ detox test --configuration ios.sim.release $ npx detox test ./e2e/tests/onboarding --configuration ios.sim.release
$ npx detox test ./e2e/tests/room --configuration ios.sim.release
$ npx detox test ./e2e/tests/assorted --configuration ios.sim.release
``` ```
## Storybook ## Storybook

View File

@ -143,6 +143,8 @@ android {
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below! missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below!
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
} }
signingConfigs { signingConfigs {
@ -242,6 +244,7 @@ dependencies {
implementation "com.google.code.gson:gson:2.8.5" implementation "com.google.code.gson:gson:2.8.5"
implementation "com.github.bumptech.glide:glide:4.9.0" implementation "com.github.bumptech.glide:glide:4.9.0"
annotationProcessor "com.github.bumptech.glide:compiler:4.9.0" annotationProcessor "com.github.bumptech.glide:compiler:4.9.0"
androidTestImplementation('com.wix:detox:+')
} }
// Run this once to be able to run the application with BUCK // Run this once to be able to run the application with BUCK

View File

@ -0,0 +1,26 @@
package chat.rocket.reactnative;
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
// Replace 'MainActivity' with the value of android:name entry in
// <activity> in AndroidManifest.xml
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
@Test
public void runDetoxTests() {
Detox.runTests(mActivityRule);
}
}

View File

@ -6,4 +6,7 @@
<certificates src="user" /> <certificates src="user" />
</trust-anchors> </trust-anchors>
</base-config> </base-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">10.0.2.2</domain>
</domain-config>
</network-security-config> </network-security-config>

View File

@ -45,6 +45,8 @@ allprojects {
url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases" url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases"
} }
maven { url "$rootDir/../node_modules/detox/Detox-android" }
google() google()
jcenter() jcenter()

View File

@ -23,7 +23,7 @@ export const FormContainerInner = ({ children }) => (
</View> </View>
); );
const FormContainer = ({ children, theme }) => ( const FormContainer = ({ children, theme, testID }) => (
<KeyboardView <KeyboardView
style={{ backgroundColor: themes[theme].backgroundColor }} style={{ backgroundColor: themes[theme].backgroundColor }}
contentContainerStyle={sharedStyles.container} contentContainerStyle={sharedStyles.container}
@ -31,7 +31,7 @@ const FormContainer = ({ children, theme }) => (
> >
<StatusBar theme={theme} /> <StatusBar theme={theme} />
<ScrollView {...scrollPersistTaps} style={sharedStyles.container} contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}> <ScrollView {...scrollPersistTaps} style={sharedStyles.container} contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}>
<SafeAreaView style={sharedStyles.container} forceInset={{ top: 'never' }}> <SafeAreaView style={sharedStyles.container} forceInset={{ top: 'never' }} testID={testID}>
{children} {children}
<AppVersion theme={theme} /> <AppVersion theme={theme} />
</SafeAreaView> </SafeAreaView>
@ -41,6 +41,7 @@ const FormContainer = ({ children, theme }) => (
FormContainer.propTypes = { FormContainer.propTypes = {
theme: PropTypes.string, theme: PropTypes.string,
testID: PropTypes.string,
children: PropTypes.element children: PropTypes.element
}; };

View File

@ -92,7 +92,7 @@ const TwoFactor = React.memo(({ theme, split }) => {
isVisible={visible} isVisible={visible}
hideModalContentWhileAnimating hideModalContentWhileAnimating
> >
<View style={styles.container}> <View style={styles.container} testID='two-factor'>
<View style={[styles.content, split && [sharedStyles.modal, sharedStyles.modalFormSheet], { backgroundColor: themes[theme].backgroundColor }]}> <View style={[styles.content, split && [sharedStyles.modal, sharedStyles.modalFormSheet], { backgroundColor: themes[theme].backgroundColor }]}>
<Text style={[styles.title, { color }]}>{I18n.t(method?.title || 'Two_Factor_Authentication')}</Text> <Text style={[styles.title, { color }]}>{I18n.t(method?.title || 'Two_Factor_Authentication')}</Text>
{method?.text ? <Text style={[styles.subtitle, { color }]}>{I18n.t(method.text)}</Text> : null} {method?.text ? <Text style={[styles.subtitle, { color }]}>{I18n.t(method.text)}</Text> : null}
@ -106,6 +106,7 @@ const TwoFactor = React.memo(({ theme, split }) => {
keyboardType={method?.keyboardType} keyboardType={method?.keyboardType}
secureTextEntry={method?.secureTextEntry} secureTextEntry={method?.secureTextEntry}
error={data.invalid && { error: 'totp-invalid', reason: I18n.t('Code_or_password_invalid') }} error={data.invalid && { error: 'totp-invalid', reason: I18n.t('Code_or_password_invalid') }}
testID='two-factor-input'
/> />
{isEmail && <Text style={[styles.sendEmail, { color }]} onPress={sendEmail}>{I18n.t('Send_me_the_code_again')}</Text>} {isEmail && <Text style={[styles.sendEmail, { color }]} onPress={sendEmail}>{I18n.t('Send_me_the_code_again')}</Text>}
<View style={styles.buttonContainer}> <View style={styles.buttonContainer}>
@ -123,6 +124,7 @@ const TwoFactor = React.memo(({ theme, split }) => {
style={styles.button} style={styles.button}
onPress={onSubmit} onPress={onSubmit}
theme={theme} theme={theme}
testID='two-factor-send'
/> />
</View> </View>
</View> </View>

View File

@ -27,11 +27,13 @@ const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
rid: subscriptions[0].rid, name: username, fname: name, message rid: subscriptions[0].rid, name: username, fname: name, message
}); });
} else { } else {
const room = yield RocketChat.createDirectMessage(username); const result = yield RocketChat.createDirectMessage(username);
if (result?.success) {
yield goRoom({ yield goRoom({
rid: room.rid, name: username, fname: name, message rid: result?.room.rid, t: 'd', name: username, fname: name, message
}); });
} }
}
} catch (e) { } catch (e) {
log(e); log(e);
} }

View File

@ -85,7 +85,7 @@ class ForgotPasswordView extends React.Component {
const { theme } = this.props; const { theme } = this.props;
return ( return (
<FormContainer theme={theme}> <FormContainer theme={theme} testID='forgot-password-view'>
<FormContainerInner> <FormContainerInner>
<Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Forgot_password')}</Text> <Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Forgot_password')}</Text>
<TextInput <TextInput

View File

@ -201,7 +201,7 @@ class LoginView extends React.Component {
render() { render() {
const { Accounts_ShowFormLogin, theme } = this.props; const { Accounts_ShowFormLogin, theme } = this.props;
return ( return (
<FormContainer theme={theme}> <FormContainer theme={theme} testID='login-view'>
<FormContainerInner> <FormContainerInner>
<LoginServices separator={Accounts_ShowFormLogin} /> <LoginServices separator={Accounts_ShowFormLogin} />
{this.renderUserForm()} {this.renderUserForm()}

View File

@ -69,7 +69,7 @@ class NewServerView extends React.Component {
const previousServer = navigation.getParam('previousServer', null); const previousServer = navigation.getParam('previousServer', null);
const close = navigation.getParam('close', () => {}); const close = navigation.getParam('close', () => {});
return { return {
headerLeft: previousServer ? <CloseModalButton navigation={navigation} onPress={close} /> : undefined, headerLeft: previousServer ? <CloseModalButton navigation={navigation} onPress={close} testID='new-server-view-close' /> : undefined,
title: I18n.t('Workspaces'), title: I18n.t('Workspaces'),
...themedHeader(screenProps.theme) ...themedHeader(screenProps.theme)
}; };
@ -297,7 +297,7 @@ class NewServerView extends React.Component {
const { connecting, theme } = this.props; const { connecting, theme } = this.props;
const { text, connectingOpen } = this.state; const { text, connectingOpen } = this.state;
return ( return (
<FormContainer theme={theme}> <FormContainer theme={theme} testID='new-server-view'>
<FormContainerInner> <FormContainerInner>
<Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Join_your_workspace')}</Text> <Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Join_your_workspace')}</Text>
<TextInput <TextInput
@ -321,8 +321,8 @@ class NewServerView extends React.Component {
disabled={!text || connecting} disabled={!text || connecting}
loading={!connectingOpen && connecting} loading={!connectingOpen && connecting}
style={styles.connectButton} style={styles.connectButton}
testID='new-server-view-button'
theme={theme} theme={theme}
testID='new-server-view-button'
/> />
<OrSeparator theme={theme} /> <OrSeparator theme={theme} />
<Text style={[styles.description, { color: themes[theme].auxiliaryText }]}>{I18n.t('Onboarding_join_open_description')}</Text> <Text style={[styles.description, { color: themes[theme].auxiliaryText }]}>{I18n.t('Onboarding_join_open_description')}</Text>
@ -334,6 +334,7 @@ class NewServerView extends React.Component {
disabled={connecting} disabled={connecting}
loading={connectingOpen && connecting} loading={connectingOpen && connecting}
theme={theme} theme={theme}
testID='new-server-view-open'
/> />
</FormContainerInner> </FormContainerInner>
{ isIOS ? this.renderCertificatePicker() : null } { isIOS ? this.renderCertificatePicker() : null }

View File

@ -68,7 +68,7 @@ class OnboardingView extends React.Component {
render() { render() {
const { theme } = this.props; const { theme } = this.props;
return ( return (
<FormContainer theme={theme}> <FormContainer theme={theme} testID='onboarding-view'>
<FormContainerInner> <FormContainerInner>
<Image style={styles.onboarding} source={{ uri: 'logo' }} fadeDuration={0} /> <Image style={styles.onboarding} source={{ uri: 'logo' }} fadeDuration={0} />
<Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Onboarding_title')}</Text> <Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Onboarding_title')}</Text>
@ -80,6 +80,7 @@ class OnboardingView extends React.Component {
type='primary' type='primary'
onPress={this.connectServer} onPress={this.connectServer}
theme={theme} theme={theme}
testID='join-workspace'
/> />
<Button <Button
title={I18n.t('Create_a_new_workspace')} title={I18n.t('Create_a_new_workspace')}
@ -87,6 +88,7 @@ class OnboardingView extends React.Component {
backgroundColor={themes[theme].chatComponentBackground} backgroundColor={themes[theme].chatComponentBackground}
onPress={this.createWorkspace} onPress={this.createWorkspace}
theme={theme} theme={theme}
testID='create-workspace-button'
/> />
</View> </View>
</FormContainerInner> </FormContainerInner>

View File

@ -58,7 +58,7 @@ class RegisterView extends React.Component {
return { return {
...themedHeader(screenProps.theme), ...themedHeader(screenProps.theme),
title, title,
headerRight: <LegalButton navigation={navigation} /> headerRight: <LegalButton navigation={navigation} testID='register-view-more' />
}; };
} }
@ -230,7 +230,7 @@ class RegisterView extends React.Component {
const { saving } = this.state; const { saving } = this.state;
const { theme, showLoginButton } = this.props; const { theme, showLoginButton } = this.props;
return ( return (
<FormContainer theme={theme}> <FormContainer theme={theme} testID='register-view'>
<FormContainerInner> <FormContainerInner>
<LoginServices /> <LoginServices />
<Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Sign_Up')}</Text> <Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Sign_Up')}</Text>

View File

@ -12,21 +12,25 @@ const Channel = ({ room, theme }) => {
label={I18n.t('Description')} label={I18n.t('Description')}
content={description || `__${ I18n.t('No_label_provided', { label: 'description' }) }__`} content={description || `__${ I18n.t('No_label_provided', { label: 'description' }) }__`}
theme={theme} theme={theme}
testID='room-info-view-description'
/> />
<Item <Item
label={I18n.t('Topic')} label={I18n.t('Topic')}
content={topic || `__${ I18n.t('No_label_provided', { label: 'topic' }) }__`} content={topic || `__${ I18n.t('No_label_provided', { label: 'topic' }) }__`}
theme={theme} theme={theme}
testID='room-info-view-topic'
/> />
<Item <Item
label={I18n.t('Announcement')} label={I18n.t('Announcement')}
content={announcement || `__${ I18n.t('No_label_provided', { label: 'announcement' }) }__`} content={announcement || `__${ I18n.t('No_label_provided', { label: 'announcement' }) }__`}
theme={theme} theme={theme}
testID='room-info-view-announcement'
/> />
<Item <Item
label={I18n.t('Broadcast_Channel')} label={I18n.t('Broadcast_Channel')}
content={room.broadcast && I18n.t('Broadcast_channel_Description')} content={room.broadcast && I18n.t('Broadcast_channel_Description')}
theme={theme} theme={theme}
testID='room-info-view-broadcast'
/> />
</> </>
); );

View File

@ -6,9 +6,11 @@ import styles from './styles';
import Markdown from '../../containers/markdown'; import Markdown from '../../containers/markdown';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
const Item = ({ label, content, theme }) => ( const Item = ({
label, content, theme, testID
}) => (
content ? ( content ? (
<View style={styles.item}> <View style={styles.item} testID={testID}>
<Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>{label}</Text> <Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>{label}</Text>
<Markdown <Markdown
style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} style={[styles.itemContent, { color: themes[theme].auxiliaryText }]}
@ -21,7 +23,8 @@ const Item = ({ label, content, theme }) => (
Item.propTypes = { Item.propTypes = {
label: PropTypes.string, label: PropTypes.string,
content: PropTypes.string, content: PropTypes.string,
theme: PropTypes.string theme: PropTypes.string,
testID: PropTypes.string
}; };
export default Item; export default Item;

View File

@ -55,6 +55,7 @@ const Header = React.memo(({
placeholder='Search' placeholder='Search'
onChangeText={onSearchChangeText} onChangeText={onSearchChangeText}
theme={theme} theme={theme}
testID='rooms-list-view-search-input'
/> />
</View> </View>
); );

View File

@ -12,7 +12,7 @@ const SearchBar = React.memo(({
return ( return (
<SearchBox <SearchBox
onChangeText={onChangeSearchText} onChangeText={onChangeSearchText}
testID='rooms-list-view-search' testID='rooms-list-view-search-input'
inputRef={inputRef} inputRef={inputRef}
theme={theme} theme={theme}
hasCancel={searching} hasCancel={searching}

View File

@ -132,6 +132,7 @@ class RoomsListView extends React.Component {
title='search' title='search'
iconName='magnifier' iconName='magnifier'
onPress={initSearching} onPress={initSearching}
testID='rooms-list-view-search-button'
/> />
) : null} ) : null}
<Item <Item

View File

@ -224,7 +224,12 @@ class Sidebar extends Component {
<View style={styles.headerUsername}> <View style={styles.headerUsername}>
<Text numberOfLines={1} style={[styles.username, { color: themes[theme].titleText }]}>{useRealName ? user.name : user.username}</Text> <Text numberOfLines={1} style={[styles.username, { color: themes[theme].titleText }]}>{useRealName ? user.name : user.username}</Text>
</View> </View>
<Text style={[styles.currentServerText, { color: themes[theme].titleText }]} numberOfLines={1}>{Site_Name}</Text> <Text
style={[styles.currentServerText, { color: themes[theme].titleText }]}
numberOfLines={1}
accessibilityLabel={`Connected to ${ baseUrl }`}
>{Site_Name}
</Text>
</View> </View>
</View> </View>

View File

@ -46,7 +46,7 @@ class WorkspaceView extends React.Component {
theme, Site_Name, Site_Url, Assets_favicon_512, server, registrationEnabled, registrationText, showLoginButton theme, Site_Name, Site_Url, Assets_favicon_512, server, registrationEnabled, registrationText, showLoginButton
} = this.props; } = this.props;
return ( return (
<FormContainer theme={theme}> <FormContainer theme={theme} testID='workspace-view'>
<FormContainerInner> <FormContainerInner>
<View style={styles.alignItemsCenter}> <View style={styles.alignItemsCenter}>
<ServerAvatar theme={theme} url={server} image={Assets_favicon_512 && Assets_favicon_512.defaultUrl} /> <ServerAvatar theme={theme} url={server} image={Assets_favicon_512 && Assets_favicon_512.defaultUrl} />
@ -60,6 +60,7 @@ class WorkspaceView extends React.Component {
type='primary' type='primary'
onPress={this.login} onPress={this.login}
theme={theme} theme={theme}
testID='workspace-view-login'
/> />
) : null} ) : null}
{ {
@ -70,6 +71,7 @@ class WorkspaceView extends React.Component {
backgroundColor={themes[theme].chatComponentBackground} backgroundColor={themes[theme].chatComponentBackground}
onPress={this.register} onPress={this.register}
theme={theme} theme={theme}
testID='workspace-view-register'
/> />
) : ( ) : (
<Text style={[styles.registrationText, { color: themes[theme].auxiliaryText }]}>{registrationText}</Text> <Text style={[styles.registrationText, { color: themes[theme].auxiliaryText }]}>{registrationText}</Text>

View File

@ -1,77 +0,0 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('./data');
describe('Onboarding', () => {
before(async() => {
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
});
describe('Render', async() => {
it('should have onboarding screen', async() => {
await expect(element(by.id('onboarding-view'))).toBeVisible();
});
it('should have "Connect to a server"', async() => {
await expect(element(by.id('connect-server-button'))).toBeVisible();
});
it('should have "Join the community"', async() => {
await expect(element(by.id('join-community-button'))).toBeVisible();
});
it('should have "Create a new workspace"', async() => {
await expect(element(by.id('create-workspace-button'))).toBeVisible();
});
});
describe('Usage', async() => {
it('should navigate to create new workspace', async() => {
// webviews are not supported by detox: https://github.com/wix/detox/issues/136#issuecomment-306591554
});
it('should navigate to join community', async() => {
await element(by.id('join-community-button')).tap();
await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('welcome-view'))).toBeVisible();
// await waitFor(element(by.text('Rocket.Chat'))).toBeVisible().withTimeout(60000);
// await expect(element(by.text('Rocket.Chat'))).toBeVisible();
});
it('should navigate to new server', async() => {
await device.launchApp({ newInstance: true });
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
await element(by.id('connect-server-button')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('new-server-view'))).toBeVisible();
});
it('should enter an invalid server and get error', async() => {
await element(by.id('new-server-view-input')).replaceText('invalidtest');
await element(by.id('new-server-view-button')).tap();
const errorText = 'Oops!';
await waitFor(element(by.text(errorText))).toBeVisible().withTimeout(60000);
await expect(element(by.text(errorText))).toBeVisible();
});
it('should enter a valid server with login services and navigate to welcome', async() => {
await element(by.text('OK')).tap();
await element(by.id('new-server-view-input')).replaceText('open');
await element(by.id('new-server-view-button')).tap();
await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('welcome-view'))).toBeVisible();
});
it('should enter a valid server without login services and navigate to login', async() => {
await device.launchApp({ newInstance: true });
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
await element(by.id('connect-server-button')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await element(by.id('new-server-view-input')).replaceText(data.server);
await element(by.id('new-server-view-button')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('login-view'))).toBeVisible();
});
});
});

View File

@ -1,50 +0,0 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const { tapBack } = require('./helpers/app');
describe('Welcome screen', () => {
before(async() => {
await device.launchApp({ newInstance: true });
await element(by.id('join-community-button')).tap();
await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000);
})
describe('Render', async() => {
it('should have welcome screen', async() => {
await expect(element(by.id('welcome-view'))).toBeVisible();
});
it('should have register button', async() => {
await expect(element(by.id('welcome-view-register'))).toBeVisible();
});
it('should have login button', async() => {
await expect(element(by.id('welcome-view-login'))).toBeVisible();
});
// TODO: oauth
});
describe('Usage', async() => {
it('should navigate to login', async() => {
await element(by.id('welcome-view-login')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('login-view'))).toBeVisible();
});
it('should navigate to register', async() => {
await tapBack();
await element(by.id('welcome-view-register')).tap();
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('register-view'))).toBeVisible();
});
it('should navigate to legal', async() => {
await tapBack();
await element(by.id('welcome-view-more')).tap();
await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('legal-view'))).toBeVisible();
});
});
});

View File

@ -1,47 +0,0 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const { tapBack } = require('./helpers/app');
describe('Legal screen', () => {
before(async() => {
await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('legal-view'))).toBeVisible();
})
describe('Render', async() => {
it('should have legal screen', async() => {
await expect(element(by.id('legal-view'))).toBeVisible();
});
it('should have terms of service button', async() => {
await expect(element(by.id('legal-terms-button'))).toBeVisible();
});
it('should have privacy policy button', async() => {
await expect(element(by.id('legal-privacy-button'))).toBeVisible();
});
});
describe('Usage', async() => {
// We can't simulate how webview behaves, so I had to disable :(
// it('should navigate to terms', async() => {
// await element(by.id('legal-terms-button')).tap();
// await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000);
// await expect(element(by.id('terms-view'))).toBeVisible();
// });
// it('should navigate to privacy', async() => {
// await tapBack();
// await element(by.id('legal-privacy-button')).tap();
// await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000);
// await expect(element(by.id('privacy-view'))).toBeVisible();
// });
it('should navigate to welcome', async() => {
await tapBack();
await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('welcome-view'))).toBeVisible();
});
});
});

View File

@ -1,81 +0,0 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const { login, logout, navigateToLogin, tapBack, sleep } = require('./helpers/app');
const data = require('./data');
describe('Rooms list screen', () => {
describe('Render', async() => {
it('should have rooms list screen', async() => {
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
// it('should have rooms list', async() => {
// await expect(element(by.id('rooms-list-view-list'))).toBeVisible();
// });
it('should have room item', async() => {
await expect(element(by.id('rooms-list-view-item-general')).atIndex(0)).toExist();
});
// Render - Header
describe('Header', async() => {
it('should have create channel button', async() => {
await expect(element(by.id('rooms-list-view-create-channel'))).toBeVisible();
});
it('should have sidebar button', async() => {
await expect(element(by.id('rooms-list-view-sidebar'))).toBeVisible();
// await expect(element(by.id('rooms-list-view-sidebar'))).toHaveLabel(`Connected to ${ data.server }. Tap to view servers list.`);
});
});
});
describe('Usage', async() => {
it('should search room and navigate', async() => {
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
await waitFor(element(by.id('rooms-list-view-search'))).toExist().withTimeout(2000);
await element(by.id('rooms-list-view-search')).typeText('rocket.cat');
await sleep(2000);
await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible();
await element(by.id('rooms-list-view-item-rocket.cat')).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(10000);
await expect(element(by.id('room-view'))).toBeVisible();
await waitFor(element(by.id('room-view-title-rocket.cat'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('room-view-title-rocket.cat'))).toBeVisible();
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
// await element(by.id('rooms-list-view-search')).typeText('');
await sleep(2000);
await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toExist().withTimeout(60000);
await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toExist();
});
// Usage - Sidebar
describe('SidebarView', async() => {
it('should navigate to add server', async() => {
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible();
await expect(element(by.id('rooms-list-header-server-add'))).toBeVisible();
await element(by.id('rooms-list-header-server-add')).tap();
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('onboarding-view'))).toBeVisible();
await element(by.id('onboarding-close')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
it('should logout', async() => {
await logout();
});
});
after(async() => {
await navigateToLogin();
await login();
});
});
});

View File

@ -3,27 +3,28 @@ const {
} = require('detox'); } = require('detox');
const data = require('../data'); const data = require('../data');
async function addServer() { async function navigateToWorkspace() {
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
await element(by.id('connect-server-button')).tap(); await element(by.id('join-workspace')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('new-server-view'))).toBeVisible();
await element(by.id('new-server-view-input')).replaceText(data.server); await element(by.id('new-server-view-input')).replaceText(data.server);
await element(by.id('new-server-view-button')).tap(); await element(by.id('new-server-view-button')).tap();
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('workspace-view'))).toBeVisible();
} }
async function navigateToLogin() { async function navigateToLogin() {
await addServer(); await navigateToWorkspace();
try { await element(by.id('workspace-view-login')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('login-view'))).toBeVisible(); await expect(element(by.id('login-view'))).toBeVisible();
} catch (error) { }
await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('welcome-view'))).toBeVisible(); async function navigateToRegister() {
await element(by.id('welcome-view-login')).tap(); await navigateToWorkspace();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); await element(by.id('workspace-view-register')).tap();
await expect(element(by.id('login-view'))).toBeVisible(); await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
} await expect(element(by.id('register-view'))).toBeVisible();
} }
async function login() { async function login() {
@ -41,7 +42,11 @@ async function logout() {
await waitFor(element(by.id('sidebar-settings'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('sidebar-settings'))).toBeVisible().withTimeout(2000);
await element(by.id('sidebar-settings')).tap(); await element(by.id('sidebar-settings')).tap();
await waitFor(element(by.id('settings-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('settings-view'))).toBeVisible().withTimeout(2000);
if (device.getPlatform() === 'ios') {
await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom'); await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom');
} else {
await element(by.id('settings-view-list')).scrollTo('bottom');
}
await element(by.id('settings-logout')).tap(); await element(by.id('settings-logout')).tap();
const logoutAlertMessage = 'You will be logged out of this application.'; const logoutAlertMessage = 'You will be logged out of this application.';
await waitFor(element(by.text(logoutAlertMessage)).atIndex(0)).toExist().withTimeout(10000); await waitFor(element(by.text(logoutAlertMessage)).atIndex(0)).toExist().withTimeout(10000);
@ -51,6 +56,18 @@ async function logout() {
await expect(element(by.id('onboarding-view'))).toBeVisible(); await expect(element(by.id('onboarding-view'))).toBeVisible();
} }
async function createUser() {
await navigateToRegister();
await element(by.id('register-view-name')).replaceText(data.user);
await element(by.id('register-view-username')).replaceText(data.user);
await element(by.id('register-view-email')).replaceText(data.email);
await element(by.id('register-view-password')).replaceText(data.password);
await sleep(300);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
}
async function tapBack() { async function tapBack() {
await element(by.id('header-back')).atIndex(0).tap(); await element(by.id('header-back')).atIndex(0).tap();
} }
@ -59,11 +76,42 @@ async function sleep(ms) {
return new Promise(res => setTimeout(res, ms)); return new Promise(res => setTimeout(res, ms));
} }
async function searchRoom(room) {
if (device.getPlatform() === 'ios') {
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
} else {
await waitFor(element(by.id('rooms-list-view-search-button'))).toExist().withTimeout(2000);
await element(by.id('rooms-list-view-search-button')).tap();
}
await waitFor(element(by.id('rooms-list-view-search-input'))).toExist().withTimeout(2000);
await element(by.id('rooms-list-view-search-input')).typeText(room);
await sleep(2000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeVisible().withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeVisible();
}
async function navigateToRoom(room) {
await searchRoom(room);
await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await waitFor(element(by.id(`room-view-title-${ room }`))).toBeVisible().withTimeout(5000);
await expect(element(by.id(`room-view-title-${ room }`))).toBeVisible();
}
// TODO: it should be only temp... we need to figure a better way
async function waitForToast() {
await sleep(5000);
}
module.exports = { module.exports = {
addServer, navigateToWorkspace,
navigateToLogin, navigateToLogin,
navigateToRegister,
login, login,
logout, logout,
createUser,
tapBack, tapBack,
sleep sleep,
searchRoom,
navigateToRoom,
waitForToast
}; };

View File

@ -1,15 +1,28 @@
const { const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const data = require('./data'); const data = require('../../data');
const { sleep, logout } = require('./helpers/app'); const { sleep, createUser } = require('../../helpers/app');
const checkServer = async(server) => {
const label = `Connected to ${ server }`;
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.label(label))).toBeVisible().withTimeout(60000);
await expect(element(by.label(label))).toBeVisible();
await element(by.type('UIScrollView')).atIndex(1).swipe('left'); // close sidebar
}
describe('Change server', () => { describe('Change server', () => {
before(async() => { before(async() => {
await device.launchApp({ newInstance: true }); await createUser();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
}); });
it('should be logged in main server', async() => {
await checkServer(data.server);
})
it('should add server and create new user', async() => { it('should add server and create new user', async() => {
await sleep(5000); await sleep(5000);
await element(by.id('rooms-list-header-server-dropdown-button')).tap(); await element(by.id('rooms-list-header-server-dropdown-button')).tap();
@ -17,29 +30,14 @@ describe('Change server', () => {
await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist(); await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist();
await sleep(1000); await sleep(1000);
await element(by.id('rooms-list-header-server-add')).tap(); await element(by.id('rooms-list-header-server-add')).tap();
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(60000);
await sleep(1000); // TODO: refactor
await element(by.id('connect-server-button')).tap();
// Add server
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await element(by.id('new-server-view-input')).replaceText(data.alternateServer); await element(by.id('new-server-view-input')).replaceText(data.alternateServer);
await sleep(1000);
await element(by.id('new-server-view-button')).tap(); await element(by.id('new-server-view-button')).tap();
// Navigate to register await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
// await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000); await expect(element(by.id('workspace-view'))).toBeVisible();
// await element(by.id('welcome-view-register')).tap(); await element(by.id('workspace-view-register')).tap();
// await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
try {
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('login-view'))).toBeVisible();
await sleep(1000);
await element(by.id('login-view-register')).tap();
} catch (error) {
await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('welcome-view'))).toBeVisible();
await sleep(1000);
await element(by.id('welcome-view-register')).tap();
}
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('register-view'))).toBeVisible(); await expect(element(by.id('register-view'))).toBeVisible();
// Register new user // Register new user
@ -51,13 +49,15 @@ describe('Change server', () => {
await element(by.id('register-view-submit')).tap(); await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('rooms-list-view'))).toBeVisible(); await expect(element(by.id('rooms-list-view'))).toBeVisible();
// For a sanity test, to make sure roomslist is showing correct rooms // For a sanity test, to make sure roomslist is showing correct rooms
// app CANNOT show public room created on previous tests // app CANNOT show public room created on previous tests
await waitFor(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeNotVisible().withTimeout(60000); // await waitFor(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeNotVisible().withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeNotVisible(); // await expect(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeNotVisible();
await checkServer(data.alternateServer);
}); });
it('should change server', async() => { it('should change back', async() => {
await sleep(5000); await sleep(5000);
await element(by.id('rooms-list-header-server-dropdown-button')).tap(); await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000); await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000);
@ -65,9 +65,6 @@ describe('Change server', () => {
await sleep(1000); await sleep(1000);
await element(by.id(`rooms-list-header-server-${ data.server }`)).tap(); await element(by.id(`rooms-list-header-server-${ data.server }`)).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
// For a sanity test, to make sure roomslist is showing correct rooms await checkServer(data.server);
// app MUST show public room created on previous tests
await waitFor(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeVisible().withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeVisible();
}); });
}); });

View File

@ -3,39 +3,14 @@ const {
} = require('detox'); } = require('detox');
const OTP = require('otp.js'); const OTP = require('otp.js');
const GA = OTP.googleAuthenticator; const GA = OTP.googleAuthenticator;
const { navigateToLogin, login, tapBack, sleep } = require('./helpers/app'); const { navigateToLogin, login, tapBack, sleep, navigateToRoom } = require('../../helpers/app');
const data = require('./data'); const data = require('../../data');
const logout = async() => {
// previous tests added alternate server to the device
// so logout will only remove this server data and select alternate server
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('sidebar-settings'))).toBeVisible().withTimeout(2000);
await element(by.id('sidebar-settings')).tap();
await waitFor(element(by.id('settings-view'))).toBeVisible().withTimeout(2000);
await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom');
await element(by.id('settings-logout')).tap();
const logoutAlertMessage = 'You will be logged out of this application.';
await waitFor(element(by.text(logoutAlertMessage)).atIndex(0)).toExist().withTimeout(10000);
await expect(element(by.text(logoutAlertMessage)).atIndex(0)).toExist();
await element(by.text('Logout')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await sleep(5000);
}
const localNavigateToLogin = async() => {
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000);
await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist();
await sleep(1000);
await element(by.id('rooms-list-header-server-add')).tap();
await navigateToLogin();
}
describe('Broadcast room', () => { describe('Broadcast room', () => {
before(async() => { before(async() => {
await device.launchApp({ newInstance: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login();
}); });
it('should create broadcast room', async() => { it('should create broadcast room', async() => {
@ -43,6 +18,9 @@ describe('Broadcast room', () => {
await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000);
await element(by.id('new-message-view-create-channel')).tap(); await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000);
await element(by.id('select-users-view-search')).replaceText(data.alternateUser);
await waitFor(element(by.id(`select-users-view-item-${ data.alternateUser }`))).toBeVisible().withTimeout(60000);
await expect(element(by.id(`select-users-view-item-${ data.alternateUser }`))).toBeVisible();
await element(by.id(`select-users-view-item-${ data.alternateUser }`)).tap(); await element(by.id(`select-users-view-item-${ data.alternateUser }`)).tap();
await waitFor(element(by.id(`selected-user-${ data.alternateUser }`))).toBeVisible().withTimeout(5000); await waitFor(element(by.id(`selected-user-${ data.alternateUser }`))).toBeVisible().withTimeout(5000);
await sleep(1000); await sleep(1000);
@ -62,54 +40,38 @@ describe('Broadcast room', () => {
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000); await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000);
await element(by.id('room-actions-info')).tap(); await element(by.id('room-actions-info')).tap();
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.label('Broadcast Channel'))).toBeVisible(); await expect(element(by.label('Broadcast Channel').withAncestor(by.id('room-info-view-broadcast')))).toBeVisible();
await tapBack(); await tapBack();
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000);
await tapBack(); await tapBack();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(2000);
// await tapBack();
// await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
// await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist().withTimeout(60000);
// await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist();
}); });
it('should send message', async() => { it('should send message', async() => {
// await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText(`${ data.random }message`); await element(by.id('messagebox-input')).typeText(`${ data.random }message`);
await element(by.id('messagebox-send-message')).tap(); await element(by.id('messagebox-send-message')).tap();
// await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible().withTimeout(60000); await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).toExist().withTimeout(60000);
// await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible(); await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible();
await sleep(5000);
await tapBack(); await tapBack();
}); });
it('should login as user without write message authorization and enter room', async() => { it('should login as user without write message authorization and enter room', async() => {
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await expect(element(by.id('rooms-list-view'))).toBeVisible(); await navigateToLogin();
await logout();
await localNavigateToLogin();
// 2FA login in stable:detox
await element(by.id('login-view-email')).replaceText(data.alternateUser); await element(by.id('login-view-email')).replaceText(data.alternateUser);
await element(by.id('login-view-password')).replaceText(data.alternateUserPassword); await element(by.id('login-view-password')).replaceText(data.alternateUserPassword);
await sleep(2000); await sleep(1000);
await element(by.id('login-view-submit')).tap(); await element(by.id('login-view-submit')).tap();
await waitFor(element(by.id('two-factor'))).toBeVisible().withTimeout(5000);
await expect(element(by.id('two-factor'))).toBeVisible();
const code = GA.gen(data.alternateUserTOTPSecret); const code = GA.gen(data.alternateUserTOTPSecret);
await element(by.id('login-view-totp')).replaceText(code); await element(by.id('two-factor-input')).replaceText(code);
await sleep(2000); await sleep(1000);
await element(by.id('login-view-submit')).tap(); await element(by.id('two-factor-send')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top'); await navigateToRoom(`broadcast${ data.random }`);
await element(by.id('rooms-list-view-search')).typeText(`broadcast${ data.random }`);
await sleep(2000);
await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist().withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist();
await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible().withTimeout(60000);
await expect(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible();
await sleep(1000); await sleep(1000);
}); });
@ -140,19 +102,8 @@ describe('Broadcast room', () => {
it('should reply broadcasted message', async() => { it('should reply broadcasted message', async() => {
await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText(`${ data.random }broadcastreply`); await element(by.id('messagebox-input')).typeText(`${ data.random }broadcastreply`);
await sleep(1000);
await element(by.id('messagebox-send-message')).tap(); await element(by.id('messagebox-send-message')).tap();
await sleep(1000);
await waitFor(element(by.label(`${ data.random }broadcastreply`)).atIndex(0)).toBeVisible().withTimeout(60000); await waitFor(element(by.label(`${ data.random }broadcastreply`)).atIndex(0)).toBeVisible().withTimeout(60000);
await expect(element(by.label(`${ data.random }broadcastreply`)).atIndex(0)).toBeVisible(); await expect(element(by.label(`${ data.random }broadcastreply`)).atIndex(0)).toBeVisible();
}); });
after(async() => {
// log back as main test user and left screen on RoomsListView
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await logout();
await localNavigateToLogin();
await login();
})
}); });

View File

@ -1,28 +1,22 @@
const { const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const { logout, navigateToLogin, login, sleep } = require('./helpers/app'); const { logout, navigateToLogin, login, sleep, waitForToast } = require('../../helpers/app');
const data = require('./data'); const data = require('../../data');
const scrollDown = 200; const scrollDown = 200;
async function waitForToast() {
// await waitFor(element(by.id('toast'))).toBeVisible().withTimeout(10000);
// await expect(element(by.id('toast'))).toBeVisible();
// await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000);
// await expect(element(by.id('toast'))).toBeNotVisible();
await sleep(5000);
}
describe('Profile screen', () => { describe('Profile screen', () => {
before(async() => { before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login();
await element(by.id('rooms-list-view-sidebar')).tap(); await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('sidebar-profile'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('sidebar-profile'))).toBeVisible().withTimeout(2000);
// await expect(element(by.id('sidebar-profile'))).toBeVisible(); await expect(element(by.id('sidebar-profile'))).toBeVisible();
await element(by.id('sidebar-profile')).tap(); await element(by.id('sidebar-profile')).tap();
await waitFor(element(by.id('profile-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('profile-view'))).toBeVisible().withTimeout(2000);
}); });
describe('Render', async() => { describe('Render', async() => {
@ -34,10 +28,6 @@ describe('Profile screen', () => {
await expect(element(by.id('profile-view-avatar')).atIndex(0)).toExist(); await expect(element(by.id('profile-view-avatar')).atIndex(0)).toExist();
}); });
it('should have custom status', async() => {
await expect(element(by.id('profile-view-custom-status'))).toExist();
});
it('should have name', async() => { it('should have name', async() => {
await expect(element(by.id('profile-view-name'))).toExist(); await expect(element(by.id('profile-view-name'))).toExist();
}); });
@ -80,16 +70,6 @@ describe('Profile screen', () => {
}); });
describe('Usage', async() => { describe('Usage', async() => {
it('should change custom status', async() => {
await element(by.type('UIScrollView')).atIndex(1).swipe('down');
await element(by.id('profile-view-custom-status')).replaceText(`${ data.user }new`);
await sleep(1000);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await sleep(1000);
await element(by.id('profile-view-submit')).tap();
await waitForToast();
});
it('should change name and username', async() => { it('should change name and username', async() => {
await element(by.type('UIScrollView')).atIndex(1).swipe('down'); await element(by.type('UIScrollView')).atIndex(1).swipe('down');
await element(by.id('profile-view-name')).replaceText(`${ data.user }new`); await element(by.id('profile-view-name')).replaceText(`${ data.user }new`);

View File

@ -1,7 +1,7 @@
const { const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const { logout, navigateToLogin, login } = require('./helpers/app'); const { logout, navigateToLogin, login } = require('../../helpers/app');
describe('Settings screen', () => { describe('Settings screen', () => {
before(async() => { before(async() => {

View File

@ -1,8 +1,8 @@
const { const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const data = require('./data'); const data = require('../../data');
const { tapBack, sleep } = require('./helpers/app'); const { tapBack, sleep, navigateToRoom } = require('../../helpers/app');
const room = 'detox-public'; const room = 'detox-public';
@ -14,16 +14,6 @@ async function mockMessage(message) {
await sleep(1000); await sleep(1000);
}; };
async function navigateToRoom() {
await sleep(2000);
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
await element(by.id('rooms-list-view-search')).typeText(room);
await sleep(2000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toBeVisible().withTimeout(60000);
await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
}
async function navigateToRoomActions() { async function navigateToRoomActions() {
await sleep(2000); await sleep(2000);
await element(by.id('room-view-header-actions')).tap(); await element(by.id('room-view-header-actions')).tap();
@ -34,7 +24,7 @@ async function navigateToRoomActions() {
describe('Join public room', () => { describe('Join public room', () => {
before(async() => { before(async() => {
await device.launchApp({ newInstance: true }); await device.launchApp({ newInstance: true });
await navigateToRoom(); await navigateToRoom(room);
}); });
describe('Render', async() => { describe('Render', async() => {
@ -178,7 +168,6 @@ describe('Join public room', () => {
await expect(element(by.text('Yes, leave it!'))).toBeVisible(); await expect(element(by.text('Yes, leave it!'))).toBeVisible();
await element(by.text('Yes, leave it!')).tap(); await element(by.text('Yes, leave it!')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
// await element(by.id('rooms-list-view-search')).typeText('');
await sleep(2000); await sleep(2000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000); await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible(); await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible();

View File

@ -1,11 +1,7 @@
const { const {
expect, element, by, waitFor expect, element, by, waitFor
} = require('detox'); } = require('detox');
const { sleep } = require('./helpers/app'); const { sleep, waitForToast } = require('../../helpers/app');
async function waitForToast() {
await sleep(5000);
}
describe('Status screen', () => { describe('Status screen', () => {
before(async() => { before(async() => {
@ -29,8 +25,9 @@ describe('Status screen', () => {
describe('Usage', async() => { describe('Usage', async() => {
it('should change status', async() => { it('should change status', async() => {
await sleep(1000);
await element(by.id('status-view-busy')).tap(); await element(by.id('status-view-busy')).tap();
sleep(1000); await sleep(1000);
await expect(element(by.id('status-view-current-busy'))).toExist(); await expect(element(by.id('status-view-current-busy'))).toExist();
}); });
@ -39,6 +36,7 @@ describe('Status screen', () => {
await sleep(1000); await sleep(1000);
await element(by.id('status-view-submit')).tap(); await element(by.id('status-view-submit')).tap();
await waitForToast(); await waitForToast();
await waitFor(element(by.label('status-text-new').withAncestor(by.id('sidebar-custom-status')))).toBeVisible().withTimeout(2000);
}); });
}); });
}); });

View File

@ -1,5 +1,5 @@
const detox = require('detox'); const detox = require('detox');
const config = require('../package.json').detox; const config = require('../../../package.json').detox;
before(async() => { before(async() => {
await detox.init(config, { launchApp: false }); await detox.init(config, { launchApp: false });

View File

@ -0,0 +1,62 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
describe('Onboarding', () => {
before(async() => {
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
});
describe('Render', () => {
it('should have onboarding screen', async() => {
await expect(element(by.id('onboarding-view'))).toBeVisible();
});
it('should have "Join a workspace"', async() => {
await expect(element(by.id('join-workspace'))).toBeVisible();
});
it('should have "Create a new workspace"', async() => {
await expect(element(by.id('create-workspace-button'))).toBeVisible();
});
});
describe('Usage', () => {
// it('should navigate to create new workspace', async() => {
// // webviews are not supported by detox: https://github.com/wix/detox/issues/136#issuecomment-306591554
// });
it('should navigate to join a workspace', async() => {
await element(by.id('join-workspace')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('new-server-view'))).toBeVisible();
});
it('should enter an invalid server and get error', async() => {
await element(by.id('new-server-view-input')).replaceText('invalidtest');
await element(by.id('new-server-view-button')).tap();
const errorText = 'Oops!';
await waitFor(element(by.text(errorText))).toBeVisible().withTimeout(60000);
await expect(element(by.text(errorText))).toBeVisible();
await element(by.text('OK')).tap();
});
it('should tap on "Join our open workspace" and navigate', async() => {
await element(by.id('new-server-view-open')).tap();
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('workspace-view'))).toBeVisible();
});
it('should enter a valid server without login services and navigate to login', async() => {
await device.launchApp({ newInstance: true });
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
await element(by.id('join-workspace')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await element(by.id('new-server-view-input')).replaceText(data.server);
await element(by.id('new-server-view-button')).tap();
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('workspace-view'))).toBeVisible();
});
});
});

View File

@ -0,0 +1,57 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const { navigateToRegister, navigateToLogin } = require('../../helpers/app');
describe('Legal screen', () => {
it('should have legal button on login', async() => {
await device.launchApp({ newInstance: true });
await navigateToLogin();
await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('login-view-more'))).toBeVisible();
});
it('should navigate to legal from login', async() => {
await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000);
await element(by.id('login-view-more')).tap();
});
it('should have legal button on register', async() => {
await device.launchApp({ newInstance: true });
await navigateToRegister();
await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('register-view-more'))).toBeVisible();
});
it('should navigate to legal from register', async() => {
await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000);
await element(by.id('register-view-more')).tap();
});
it('should have legal screen', async() => {
await expect(element(by.id('legal-view'))).toBeVisible();
});
it('should have terms of service button', async() => {
await expect(element(by.id('legal-terms-button'))).toBeVisible();
});
it('should have privacy policy button', async() => {
await expect(element(by.id('legal-privacy-button'))).toBeVisible();
});
// We can't simulate how webview behaves, so I had to disable :(
// it('should navigate to terms', async() => {
// await element(by.id('legal-terms-button')).tap();
// await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000);
// await expect(element(by.id('terms-view'))).toBeVisible();
// });
// it('should navigate to privacy', async() => {
// await tapBack();
// await element(by.id('legal-privacy-button')).tap();
// await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000);
// await expect(element(by.id('privacy-view'))).toBeVisible();
// });
});

View File

@ -1,19 +1,20 @@
const { const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const data = require('./data'); const data = require('../../data');
const { navigateToLogin } = require('../../helpers/app');
describe('Forgot password screen', () => { describe('Forgot password screen', () => {
before(async() => { before(async() => {
await element(by.id('welcome-view-login')).tap(); await device.launchApp({ newInstance: true });
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); await navigateToLogin();
await element(by.id('login-view-forgot-password')).tap(); await element(by.id('login-view-forgot-password')).tap();
await waitFor(element(by.id('forgot-password-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('forgot-password-view'))).toExist().withTimeout(2000);
}); });
describe('Render', async() => { describe('Render', async() => {
it('should have forgot password screen', async() => { it('should have forgot password screen', async() => {
await expect(element(by.id('forgot-password-view'))).toBeVisible(); await expect(element(by.id('forgot-password-view'))).toExist();
}); });
it('should have email input', async() => { it('should have email input', async() => {

View File

@ -1,20 +1,8 @@
const { const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const { logout, sleep } = require('./helpers/app'); const { navigateToRegister, sleep } = require('../../helpers/app');
const data = require('./data'); const data = require('../../data');
async function navigateToRegister() {
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(2000);
await element(by.id('connect-server-button')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await element(by.id('new-server-view-input')).replaceText(data.server);
await element(by.id('new-server-view-button')).tap();
// we're assuming the server don't have login services and the navigation will jump to login
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000);
await element(by.id('login-view-register')).tap();
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
}
describe('Create user screen', () => { describe('Create user screen', () => {
before(async() => { before(async() => {
@ -39,10 +27,6 @@ describe('Create user screen', () => {
await expect(element(by.id('register-view-password'))).toBeVisible(); await expect(element(by.id('register-view-password'))).toBeVisible();
}); });
it('should have show password icon', async() => {
await expect(element(by.id('register-view-password-icon-right'))).toBeVisible();
});
it('should have submit button', async() => { it('should have submit button', async() => {
await expect(element(by.id('register-view-submit'))).toBeVisible(); await expect(element(by.id('register-view-submit'))).toBeVisible();
}); });
@ -99,9 +83,5 @@ describe('Create user screen', () => {
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('rooms-list-view'))).toBeVisible(); await expect(element(by.id('rooms-list-view'))).toBeVisible();
}); });
after(async() => {
await logout();
});
}); });
}); });

View File

@ -1,11 +1,12 @@
const { const {
device, expect, element, by, waitFor expect, element, by, waitFor
} = require('detox'); } = require('detox');
const { navigateToLogin, tapBack, sleep } = require('./helpers/app'); const { navigateToLogin, tapBack, sleep } = require('../../helpers/app');
const data = require('./data'); const data = require('../../data');
describe('Login screen', () => { describe('Login screen', () => {
before(async() => { before(async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true });
await navigateToLogin(); await navigateToLogin();
}); });
@ -22,10 +23,6 @@ describe('Login screen', () => {
await expect(element(by.id('login-view-password'))).toBeVisible(); await expect(element(by.id('login-view-password'))).toBeVisible();
}); });
it('should have show password icon', async() => {
await expect(element(by.id('login-view-password-icon-right'))).toBeVisible();
});
it('should have submit button', async() => { it('should have submit button', async() => {
await expect(element(by.id('login-view-submit'))).toBeVisible(); await expect(element(by.id('login-view-submit'))).toBeVisible();
}); });
@ -53,8 +50,8 @@ describe('Login screen', () => {
it('should navigate to forgot password', async() => { it('should navigate to forgot password', async() => {
await element(by.id('login-view-forgot-password')).tap(); await element(by.id('login-view-forgot-password')).tap();
await waitFor(element(by.id('forgot-password-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('forgot-password-view'))).toExist().withTimeout(2000);
await expect(element(by.id('forgot-password-view'))).toBeVisible(); await expect(element(by.id('forgot-password-view'))).toExist();
await tapBack(); await tapBack();
}); });

View File

@ -0,0 +1,43 @@
const {
device, expect, element, by, waitFor
} = require('detox');
const { logout, tapBack, sleep, navigateToRoom } = require('../../helpers/app');
describe('Rooms list screen', () => {
describe('Render', () => {
it('should have rooms list screen', async() => {
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
it('should have room item', async() => {
await expect(element(by.id('rooms-list-view-item-general')).atIndex(0)).toExist();
});
// Render - Header
describe('Header', () => {
it('should have create channel button', async() => {
await expect(element(by.id('rooms-list-view-create-channel'))).toBeVisible();
});
it('should have sidebar button', async() => {
await expect(element(by.id('rooms-list-view-sidebar'))).toBeVisible();
});
});
});
describe('Usage', () => {
it('should search room and navigate', async() => {
await navigateToRoom('rocket.cat');
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
await sleep(2000);
await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toExist().withTimeout(60000);
await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toExist();
});
it('should logout', async() => {
await logout();
});
});
});

View File

@ -0,0 +1,11 @@
const detox = require('detox');
const config = require('../../../package.json').detox;
before(async() => {
await detox.init(config, { launchApp: false });
await device.launchApp({ permissions: { notifications: 'YES' } });
});
after(async() => {
await detox.cleanup();
});

View File

@ -1,14 +1,12 @@
const { const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const data = require('./data'); const data = require('../../data');
const { tapBack, sleep } = require('./helpers/app'); const { tapBack, sleep, createUser } = require('../../helpers/app');
describe('Create room screen', () => { describe('Create room screen', () => {
before(async() => { before(async() => {
await sleep(5000); await createUser();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await device.launchApp({ newInstance: true });
await element(by.id('rooms-list-view-create-channel')).tap(); await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000);
}); });

View File

@ -1,8 +1,8 @@
const { const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const data = require('./data'); const data = require('../../data');
const { tapBack, sleep } = require('./helpers/app'); const { tapBack, sleep, navigateToRoom } = require('../../helpers/app');
async function mockMessage(message) { async function mockMessage(message) {
await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).tap();
@ -12,20 +12,11 @@ async function mockMessage(message) {
await expect(element(by.label(`${ data.random }${ message }`)).atIndex(0)).toBeVisible(); await expect(element(by.label(`${ data.random }${ message }`)).atIndex(0)).toBeVisible();
}; };
async function navigateToRoom() {
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
await element(by.id('rooms-list-view-search')).typeText(`private${ data.random }`);
await sleep(2000);
await waitFor(element(by.id(`rooms-list-view-item-private${ data.random }`))).toBeVisible().withTimeout(60000);
await element(by.id(`rooms-list-view-item-private${ data.random }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
}
describe('Room screen', () => { describe('Room screen', () => {
const mainRoom = `private${ data.random }`; const mainRoom = `private${ data.random }`;
before(async() => { before(async() => {
await navigateToRoom(); await navigateToRoom(mainRoom);
}); });
describe('Render', async() => { describe('Render', async() => {

View File

@ -1,8 +1,8 @@
const { const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const data = require('./data'); const data = require('../../data');
const { tapBack, sleep } = require('./helpers/app'); const { tapBack, sleep, navigateToRoom } = require('../../helpers/app');
const scrollDown = 200; const scrollDown = 200;
@ -13,13 +13,7 @@ async function navigateToRoomActions(type) {
} else { } else {
room = `private${ data.random }`; room = `private${ data.random }`;
} }
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await navigateToRoom(room);
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
await element(by.id('rooms-list-view-search')).typeText(room);
await sleep(2000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(2000);
await sleep(1000); await sleep(1000);
await element(by.id('room-view-header-actions')).tap(); await element(by.id('room-view-header-actions')).tap();
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000); await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000);

View File

@ -1,8 +1,8 @@
const { const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const data = require('./data'); const data = require('../../data');
const { tapBack, sleep } = require('./helpers/app'); const { tapBack, sleep, navigateToRoom, waitForToast } = require('../../helpers/app');
async function navigateToRoomInfo(type) { async function navigateToRoomInfo(type) {
let room; let room;
@ -11,13 +11,7 @@ async function navigateToRoomInfo(type) {
} else { } else {
room = `private${ data.random }`; room = `private${ data.random }`;
} }
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await navigateToRoom(room);
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
await element(by.id('rooms-list-view-search')).typeText(room);
await sleep(2000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(2000);
await sleep(1000); await sleep(1000);
await element(by.id('room-view-header-actions')).tap(); await element(by.id('room-view-header-actions')).tap();
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000); await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000);
@ -26,14 +20,6 @@ async function navigateToRoomInfo(type) {
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
} }
async function waitForToast() {
// await waitFor(element(by.id('toast'))).toBeVisible().withTimeout(10000);
// await expect(element(by.id('toast'))).toBeVisible();
// await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000);
// await expect(element(by.id('toast'))).toBeNotVisible();
await sleep(5000);
}
describe('Room info screen', () => { describe('Room info screen', () => {
describe('Direct', async() => { describe('Direct', async() => {
before(async() => { before(async() => {
@ -84,7 +70,7 @@ describe('Room info screen', () => {
await sleep(1000); await sleep(1000);
await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000); await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap(); await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
}); });
it('should have room info edit view', async() => { it('should have room info edit view', async() => {
@ -169,7 +155,8 @@ describe('Room info screen', () => {
// change name to original // change name to original
await element(by.id('room-info-view-edit-button')).tap(); await element(by.id('room-info-view-edit-button')).tap();
await sleep(1000); await sleep(1000);
await waitFor(element(by.id('room-info-edit-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
await sleep(1000);
await element(by.id('room-info-edit-view-name')).replaceText(`${ room }`); await element(by.id('room-info-edit-view-name')).replaceText(`${ room }`);
await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await sleep(1000); await sleep(1000);
@ -186,8 +173,11 @@ describe('Room info screen', () => {
await element(by.id('room-info-edit-view-password')).replaceText('abc'); await element(by.id('room-info-edit-view-password')).replaceText('abc');
await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-t')).tap(); await element(by.id('room-info-edit-view-t')).tap();
await sleep(1000);
await element(by.id('room-info-edit-view-ro')).tap(); await element(by.id('room-info-edit-view-ro')).tap();
await sleep(1000);
await element(by.id('room-info-edit-view-react-when-ro')).tap(); await element(by.id('room-info-edit-view-react-when-ro')).tap();
await sleep(1000);
await element(by.id('room-info-edit-view-reset')).tap(); await element(by.id('room-info-edit-view-reset')).tap();
// after reset // after reset
await expect(element(by.id('room-info-edit-view-name'))).toHaveText(room); await expect(element(by.id('room-info-edit-view-name'))).toHaveText(room);
@ -210,14 +200,14 @@ describe('Room info screen', () => {
await tapBack(); await tapBack();
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
await sleep(1000); await sleep(1000);
// await expect(element(by.id('room-info-view-description'))).toHaveLabel('new description'); await expect(element(by.label('new description').withAncestor(by.id('room-info-view-description')))).toBeVisible();
await expect(element(by.label('new description'))).toBeVisible();
await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toBeVisible().withTimeout(2000);
}); });
it('should change room topic', async() => { it('should change room topic', async() => {
await sleep(1000);
await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
await sleep(1000); await sleep(1000);
await element(by.id('room-info-edit-view-topic')).replaceText('new topic'); await element(by.id('room-info-edit-view-topic')).replaceText('new topic');
await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.type('UIScrollView')).atIndex(1).swipe('up');
@ -226,14 +216,14 @@ describe('Room info screen', () => {
await tapBack(); await tapBack();
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
await sleep(1000); await sleep(1000);
// await expect(element(by.id('room-info-view-topic'))).toHaveLabel('new topic'); await expect(element(by.label('new topic').withAncestor(by.id('room-info-view-topic')))).toBeVisible();
await expect(element(by.label('new topic'))).toBeVisible();
await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toBeVisible().withTimeout(2000);
}); });
it('should change room announcement', async() => { it('should change room announcement', async() => {
await sleep(1000);
await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
await sleep(1000); await sleep(1000);
await element(by.id('room-info-edit-view-announcement')).replaceText('new announcement'); await element(by.id('room-info-edit-view-announcement')).replaceText('new announcement');
await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.type('UIScrollView')).atIndex(1).swipe('up');
@ -242,14 +232,14 @@ describe('Room info screen', () => {
await tapBack(); await tapBack();
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
await sleep(1000); await sleep(1000);
// await expect(element(by.id('room-info-view-announcement'))).toHaveLabel('new announcement'); await expect(element(by.label('new announcement').withAncestor(by.id('room-info-view-announcement')))).toBeVisible();
await expect(element(by.label('new announcement'))).toBeVisible();
await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toBeVisible().withTimeout(2000);
}); });
it('should change room password', async() => { it('should change room password', async() => {
await sleep(1000);
await waitFor(element(by.id('room-info-view-edit-button'))).toBeVisible().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
await sleep(1000); await sleep(1000);
await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-password')).replaceText('password'); await element(by.id('room-info-edit-view-password')).replaceText('password');
@ -307,7 +297,6 @@ describe('Room info screen', () => {
await expect(element(by.text('Yes, delete it!'))).toBeVisible(); await expect(element(by.text('Yes, delete it!'))).toBeVisible();
await element(by.text('Yes, delete it!')).tap(); await element(by.text('Yes, delete it!')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
// await element(by.id('rooms-list-view-search')).typeText('');
await sleep(2000); await sleep(2000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000); await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible(); await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible();

11
e2e/tests/room/init.js Normal file
View File

@ -0,0 +1,11 @@
const detox = require('detox');
const config = require('../../../package.json').detox;
before(async() => {
await detox.init(config, { launchApp: false });
await device.launchApp({ permissions: { notifications: 'YES' } });
});
after(async() => {
await detox.cleanup();
});

View File

@ -41,8 +41,8 @@
"expo-av": "^8.1.0", "expo-av": "^8.1.0",
"expo-file-system": "^8.1.0", "expo-file-system": "^8.1.0",
"expo-haptics": "^8.1.0", "expo-haptics": "^8.1.0",
"expo-local-authentication": "9.0.0",
"expo-keep-awake": "^8.1.0", "expo-keep-awake": "^8.1.0",
"expo-local-authentication": "9.0.0",
"expo-web-browser": "8.2.1", "expo-web-browser": "8.2.1",
"hoist-non-react-statics": "3.3.2", "hoist-non-react-statics": "3.3.2",
"i18n-js": "3.5.1", "i18n-js": "3.5.1",
@ -131,7 +131,7 @@
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"bugsnag-sourcemaps": "1.3.0", "bugsnag-sourcemaps": "1.3.0",
"codecov": "3.6.5", "codecov": "3.6.5",
"detox": "^15.2.2", "detox": "^16.5.0",
"emotion-theming": "10.0.27", "emotion-theming": "10.0.27",
"eslint": "6.8.0", "eslint": "6.8.0",
"eslint-plugin-import": "2.20.2", "eslint-plugin-import": "2.20.2",
@ -143,7 +143,7 @@
"jest": "^24.9.0", "jest": "^24.9.0",
"jest-cli": "^23.6.0", "jest-cli": "^23.6.0",
"metro-react-native-babel-preset": "^0.58.0", "metro-react-native-babel-preset": "^0.58.0",
"mocha": "^5.2.0", "mocha": "7.1.2",
"otp.js": "1.2.0", "otp.js": "1.2.0",
"patch-package": "6.2.2", "patch-package": "6.2.2",
"react-dom": "16.11.0", "react-dom": "16.11.0",
@ -178,13 +178,33 @@
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/RocketChatRN.app", "binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/RocketChatRN.app",
"build": "xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build", "build": "xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator", "type": "ios.simulator",
"name": "iPhone 11 Pro" "device": {
"type": "iPhone 11 Pro"
}
}, },
"ios.sim.release": { "ios.sim.release": {
"binaryPath": "ios/build/Build/Products/Release-iphonesimulator/RocketChatRN.app", "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/RocketChatRN.app",
"build": "xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Release -sdk iphonesimulator -derivedDataPath ios/build", "build": "xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Release -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator", "type": "ios.simulator",
"name": "iPhone 11 Pro" "device": {
"type": "iPhone 11 Pro"
}
},
"android.emu.debug": {
"binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
"build": "cd android && ./gradlew app:assembleDebug app:assembleAndroidTest -DtestBuildType=debug && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Pixel_3_API_29"
}
},
"android.emu.release": {
"binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
"build": "cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Pixel_3_API_29"
}
} }
} }
} }

281
yarn.lock
View File

@ -3660,6 +3660,11 @@ ansi-align@^3.0.0:
dependencies: dependencies:
string-width "^3.0.0" string-width "^3.0.0"
ansi-colors@3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813"
integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==
ansi-colors@^1.0.1: ansi-colors@^1.0.1:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9"
@ -3789,6 +3794,14 @@ anymatch@^2.0.0:
micromatch "^3.1.4" micromatch "^3.1.4"
normalize-path "^2.1.1" normalize-path "^2.1.1"
anymatch@~3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
app-root-dir@^1.0.2: app-root-dir@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118"
@ -4694,17 +4707,17 @@ binary-extensions@^1.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
binary-extensions@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
binstring@^0.2.1: binstring@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/binstring/-/binstring-0.2.1.tgz#8a174d301f6d54efda550dd98bb4cb524eacd75d" resolved "https://registry.yarnpkg.com/binstring/-/binstring-0.2.1.tgz#8a174d301f6d54efda550dd98bb4cb524eacd75d"
integrity sha1-ihdNMB9tVO/aVQ3Zi7TLUk6s110= integrity sha1-ihdNMB9tVO/aVQ3Zi7TLUk6s110=
bluebird@3.5.x: bluebird@^3.3.5, bluebird@^3.5.4, bluebird@^3.5.5:
version "3.5.5"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
bluebird@^3.3.5, bluebird@^3.5.5:
version "3.7.2" version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
@ -4811,7 +4824,7 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2" split-string "^3.0.2"
to-regex "^3.0.1" to-regex "^3.0.1"
braces@^3.0.1: braces@^3.0.1, braces@~3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
@ -5278,6 +5291,21 @@ child-process-promise@^2.2.0:
node-version "^1.0.0" node-version "^1.0.0"
promise-polyfill "^6.0.1" promise-polyfill "^6.0.1"
chokidar@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6"
integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==
dependencies:
anymatch "~3.1.1"
braces "~3.0.2"
glob-parent "~5.1.0"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.2.0"
optionalDependencies:
fsevents "~2.1.1"
chokidar@^2.0.4, chokidar@^2.1.8: chokidar@^2.0.4, chokidar@^2.1.8:
version "2.1.8" version "2.1.8"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
@ -5520,11 +5548,6 @@ command-exists@^1.2.8:
resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.8.tgz#715acefdd1223b9c9b37110a149c6392c2852291" resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.8.tgz#715acefdd1223b9c9b37110a149c6392c2852291"
integrity sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw== integrity sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw==
commander@2.15.1:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
commander@^2.11.0, commander@^2.19.0: commander@^2.11.0, commander@^2.19.0:
version "2.19.0" version "2.19.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
@ -6039,12 +6062,12 @@ debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@3.1.0: debug@3.2.6, debug@^3.0.0, debug@^3.1.0, debug@^3.2.5:
version "3.1.0" version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies: dependencies:
ms "2.0.0" ms "^2.1.1"
debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
version "4.1.1" version "4.1.1"
@ -6053,13 +6076,6 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
debug@^3.0.0, debug@^3.1.0, debug@^3.2.5:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "^2.1.1"
decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@ -6241,10 +6257,10 @@ detect-port@^1.3.0:
address "^1.0.1" address "^1.0.1"
debug "^2.6.0" debug "^2.6.0"
detox@^15.2.2: detox@^16.5.0:
version "15.2.2" version "16.5.0"
resolved "https://registry.yarnpkg.com/detox/-/detox-15.2.2.tgz#490a8c54944f658492490f8dc88c0946a57c9197" resolved "https://registry.yarnpkg.com/detox/-/detox-16.5.0.tgz#81cd560de917fca9e6ecd10cfd68c1831e896da4"
integrity sha512-lmtRTrl7gW5noBaCJZr3WCEv5hvTw6DK2p81ATGUL1pImxpjeHhJJhTYAq5taz0kl706g9DOxFao8i/NgBR4bg== integrity sha512-UQ4GuMyHv87+LOP0VQqoNIVRhren2xSGt2bXikVNjURlxBTT3GG+e+QmjCZtlUVntbsV0UHsbdNPzYveLS84mw==
dependencies: dependencies:
"@babel/core" "^7.4.5" "@babel/core" "^7.4.5"
bunyan "^1.8.12" bunyan "^1.8.12"
@ -6261,7 +6277,7 @@ detox@^15.2.2:
sanitize-filename "^1.6.1" sanitize-filename "^1.6.1"
shell-utils "^1.0.9" shell-utils "^1.0.9"
tail "^2.0.0" tail "^2.0.0"
telnet-client "0.15.3" telnet-client "1.2.8"
tempfile "^2.0.0" tempfile "^2.0.0"
which "^1.3.1" which "^1.3.1"
ws "^3.3.1" ws "^3.3.1"
@ -7589,6 +7605,13 @@ flat-cache@^2.0.1:
rimraf "2.6.3" rimraf "2.6.3"
write "1.0.3" write "1.0.3"
flat@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2"
integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==
dependencies:
is-buffer "~2.0.3"
flatted@^2.0.0: flatted@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
@ -7783,6 +7806,11 @@ fsevents@^1.2.7:
nan "^2.12.1" nan "^2.12.1"
node-pre-gyp "^0.12.0" node-pre-gyp "^0.12.0"
fsevents@~2.1.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
function-bind@^1.1.1: function-bind@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@ -7917,6 +7945,13 @@ glob-parent@^5.0.0:
dependencies: dependencies:
is-glob "^4.0.1" is-glob "^4.0.1"
glob-parent@~5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
dependencies:
is-glob "^4.0.1"
glob-to-regexp@^0.3.0: glob-to-regexp@^0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
@ -7934,10 +7969,10 @@ glob@7.0.6:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@7.1.2: glob@7.1.3, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3:
version "7.1.2" version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
dependencies: dependencies:
fs.realpath "^1.0.0" fs.realpath "^1.0.0"
inflight "^1.0.4" inflight "^1.0.4"
@ -7969,18 +8004,6 @@ glob@^7.0.0, glob@^7.1.4:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3:
version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
global-modules@2.0.0: global-modules@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
@ -8244,12 +8267,7 @@ hastscript@^5.0.0:
property-information "^5.0.1" property-information "^5.0.1"
space-separated-tokens "^1.0.0" space-separated-tokens "^1.0.0"
he@1.1.1: he@1.2.0, he@^1.2.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
he@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@ -8794,6 +8812,13 @@ is-binary-path@^1.0.0:
dependencies: dependencies:
binary-extensions "^1.0.0" binary-extensions "^1.0.0"
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-boolean-object@^1.0.0: is-boolean-object@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e"
@ -8804,6 +8829,11 @@ is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
is-buffer@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623"
integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==
is-builtin-module@^1.0.0: is-builtin-module@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
@ -8991,7 +9021,7 @@ is-glob@^4.0.0:
dependencies: dependencies:
is-extglob "^2.1.1" is-extglob "^2.1.1"
is-glob@^4.0.1: is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
@ -10687,6 +10717,13 @@ lodash@4.x.x, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, l
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
log-symbols@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==
dependencies:
chalk "^2.4.2"
log-symbols@^2.2.0: log-symbols@^2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
@ -11406,36 +11443,49 @@ mixin-deep@^1.2.0:
for-in "^1.0.2" for-in "^1.0.2"
is-extendable "^1.0.1" is-extendable "^1.0.1"
mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: mkdirp@0.5.5, mkdirp@^0.5.3:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
dependencies:
minimist "0.0.8"
mkdirp@^0.5.3:
version "0.5.5" version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies: dependencies:
minimist "^1.2.5" minimist "^1.2.5"
mocha@^5.2.0: mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1:
version "5.2.0" version "0.5.1"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
dependencies: dependencies:
minimist "0.0.8"
mocha@7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.1.2.tgz#8e40d198acf91a52ace122cd7599c9ab857b29e6"
integrity sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==
dependencies:
ansi-colors "3.2.3"
browser-stdout "1.3.1" browser-stdout "1.3.1"
commander "2.15.1" chokidar "3.3.0"
debug "3.1.0" debug "3.2.6"
diff "3.5.0" diff "3.5.0"
escape-string-regexp "1.0.5" escape-string-regexp "1.0.5"
glob "7.1.2" find-up "3.0.0"
glob "7.1.3"
growl "1.10.5" growl "1.10.5"
he "1.1.1" he "1.2.0"
js-yaml "3.13.1"
log-symbols "3.0.0"
minimatch "3.0.4" minimatch "3.0.4"
mkdirp "0.5.1" mkdirp "0.5.5"
supports-color "5.4.0" ms "2.1.1"
node-environment-flags "1.0.6"
object.assign "4.1.0"
strip-json-comments "2.0.1"
supports-color "6.0.0"
which "1.3.1"
wide-align "1.1.3"
yargs "13.3.2"
yargs-parser "13.1.2"
yargs-unparser "1.6.0"
moment@2.25.3: moment@2.25.3:
version "2.25.3" version "2.25.3"
@ -11567,6 +11617,14 @@ no-case@^3.0.3:
lower-case "^2.0.1" lower-case "^2.0.1"
tslib "^1.10.0" tslib "^1.10.0"
node-environment-flags@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088"
integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==
dependencies:
object.getownpropertydescriptors "^2.0.3"
semver "^5.7.0"
node-fetch@^1.0.1: node-fetch@^1.0.1:
version "1.7.3" version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
@ -11727,7 +11785,7 @@ normalize-path@^2.0.1, normalize-path@^2.1.1:
dependencies: dependencies:
remove-trailing-separator "^1.0.1" remove-trailing-separator "^1.0.1"
normalize-path@^3.0.0: normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
@ -11857,7 +11915,7 @@ object-visit@^1.0.0:
dependencies: dependencies:
isobject "^3.0.0" isobject "^3.0.0"
object.assign@^4.1.0: object.assign@4.1.0, object.assign@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
@ -12478,7 +12536,7 @@ phin@^2.9.1:
resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c" resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c"
integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA== integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==
picomatch@^2.0.5: picomatch@^2.0.4, picomatch@^2.0.5:
version "2.2.2" version "2.2.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
@ -13880,6 +13938,13 @@ readdirp@^2.2.1:
micromatch "^3.1.10" micromatch "^3.1.10"
readable-stream "^2.0.2" readable-stream "^2.0.2"
readdirp@~3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839"
integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==
dependencies:
picomatch "^2.0.4"
realpath-native@^1.0.0: realpath-native@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.2.tgz#cd51ce089b513b45cf9b1516c82989b51ccc6560" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.2.tgz#cd51ce089b513b45cf9b1516c82989b51ccc6560"
@ -14547,6 +14612,11 @@ semver@7.3.2:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
semver@^5.7.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
semver@^6.0.0: semver@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65" resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65"
@ -15329,16 +15399,16 @@ strip-indent@^1.0.1:
dependencies: dependencies:
get-stdin "^4.0.1" get-stdin "^4.0.1"
strip-json-comments@2.0.1, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
strip-json-comments@^3.0.1: strip-json-comments@^3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7"
integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==
strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
stubs@^3.0.0: stubs@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b"
@ -15357,10 +15427,10 @@ sudo-prompt@^9.0.0:
resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.1.1.tgz#73853d729770392caec029e2470db9c221754db0" resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.1.1.tgz#73853d729770392caec029e2470db9c221754db0"
integrity sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA== integrity sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA==
supports-color@5.4.0: supports-color@6.0.0:
version "5.4.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a"
integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==
dependencies: dependencies:
has-flag "^3.0.0" has-flag "^3.0.0"
@ -15478,12 +15548,12 @@ telejson@^3.2.0:
lodash "^4.17.15" lodash "^4.17.15"
memoizerific "^1.11.3" memoizerific "^1.11.3"
telnet-client@0.15.3: telnet-client@1.2.8:
version "0.15.3" version "1.2.8"
resolved "https://registry.yarnpkg.com/telnet-client/-/telnet-client-0.15.3.tgz#99ec754e4acf6fa51dc69898f574df3c2550712e" resolved "https://registry.yarnpkg.com/telnet-client/-/telnet-client-1.2.8.tgz#946c0dadc8daa3f19bb40a3e898cb870403a4ca4"
integrity sha512-GSfdzQV0BKIYsmeXq7bJFJ2wHeJud6icaIxCUf6QCGQUD6R0BBGbT1+yLDhq67JRdgRpwyPwUbV7JxFeRrZomQ== integrity sha512-W+w4k3QAmULVNhBVT2Fei369kGZCh/TH25M7caJAXW+hLxwoQRuw0di3cX4l0S9fgH3Mvq7u+IFMoBDpEw/eIg==
dependencies: dependencies:
bluebird "3.5.x" bluebird "^3.5.4"
temp-dir@^1.0.0: temp-dir@^1.0.0:
version "1.0.0" version "1.0.0"
@ -16459,14 +16529,14 @@ which-typed-array@^1.1.2:
has-symbols "^1.0.1" has-symbols "^1.0.1"
is-typed-array "^1.1.3" is-typed-array "^1.1.3"
which@^1.2.12, which@^1.2.9, which@^1.3.0, which@^1.3.1: which@1.3.1, which@^1.2.12, which@^1.2.9, which@^1.3.0, which@^1.3.1:
version "1.3.1" version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
dependencies: dependencies:
isexe "^2.0.0" isexe "^2.0.0"
wide-align@^1.1.0: wide-align@1.1.3, wide-align@^1.1.0:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
@ -16716,6 +16786,14 @@ yaml@^1.7.2:
dependencies: dependencies:
"@babel/runtime" "^7.9.2" "@babel/runtime" "^7.9.2"
yargs-parser@13.1.2, yargs-parser@^13.1.2:
version "13.1.2"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^11.1.1: yargs-parser@^11.1.1:
version "11.1.1" version "11.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"
@ -16755,6 +16833,31 @@ yargs-parser@^9.0.2:
dependencies: dependencies:
camelcase "^4.1.0" camelcase "^4.1.0"
yargs-unparser@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f"
integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==
dependencies:
flat "^4.1.0"
lodash "^4.17.15"
yargs "^13.3.0"
yargs@13.3.2:
version "13.3.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
dependencies:
cliui "^5.0.0"
find-up "^3.0.0"
get-caller-file "^2.0.1"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^3.0.0"
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^13.1.2"
yargs@^11.0.0: yargs@^11.0.0:
version "11.1.0" version "11.1.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77"