E2E: Initial Detox setup for Mobile UI automation (#4535)
* initial Detox setup for mobile UI automation * fix iOS allow permission on opening the app, add npm script to root folder, fix test when using longer site URL and add detox dependency to root for android build dependency * remove detox proguardFile * update packages and emulator * change folder build on local and CI * add test to post a message, ability to have test in isolation, server API commands, server config, dependency updates and update folder structure * update snapshot * update detox and do clean up * update dependencies
This commit is contained in:
parent
619153ebe0
commit
f05faaa949
42 changed files with 9430 additions and 9 deletions
|
|
@ -53,6 +53,23 @@
|
|||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["detox/e2e/**"],
|
||||
"globals": {
|
||||
"by": true,
|
||||
"detox": true,
|
||||
"device": true,
|
||||
"element": true,
|
||||
"waitFor": true
|
||||
},
|
||||
"rules": {
|
||||
"func-names": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"max-nested-callbacks": 0,
|
||||
"no-process-env": 0,
|
||||
"no-unused-expressions": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,8 @@ android {
|
|||
ndk {
|
||||
abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
|
||||
}
|
||||
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
}
|
||||
signingConfigs {
|
||||
release {
|
||||
|
|
@ -260,6 +261,8 @@ dependencies {
|
|||
// For WebP support, including animated WebP
|
||||
implementation 'com.facebook.fresco:animated-webp:2.0.0'
|
||||
implementation 'com.facebook.fresco:webpsupport:2.0.0'
|
||||
|
||||
androidTestImplementation('com.wix:detox:+')
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package com.mattermost.rnbeta;
|
||||
|
||||
import com.wix.detox.Detox;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class DetoxTest {
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
|
||||
|
||||
@Test
|
||||
public void runDetoxTests() {
|
||||
Detox.DetoxIdlePolicyConfig idlePolicyConfig = new Detox.DetoxIdlePolicyConfig();
|
||||
idlePolicyConfig.masterTimeoutSec = 60;
|
||||
idlePolicyConfig.idleResourceTimeoutSec = 30;
|
||||
|
||||
Detox.runTests(mActivityRule, idlePolicyConfig);
|
||||
}
|
||||
}
|
||||
|
|
@ -66,5 +66,8 @@ allprojects {
|
|||
maven {
|
||||
url ("https://dl.bintray.com/rudderstack/rudderstack")
|
||||
}
|
||||
maven {
|
||||
url "$rootDir/../node_modules/detox/Detox-android"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ exports[`ErrorText should match snapshot 1`] = `
|
|||
},
|
||||
]
|
||||
}
|
||||
testID="error_text"
|
||||
>
|
||||
Username must begin with a letter and contain between 3 and 22 characters including numbers, lowercase letters, and the symbols
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export default class ErrorText extends PureComponent {
|
|||
if (intl) {
|
||||
return (
|
||||
<FormattedText
|
||||
testID='error_text'
|
||||
id={intl.id}
|
||||
defaultMessage={intl.defaultMessage}
|
||||
values={intl.values}
|
||||
|
|
@ -38,7 +39,10 @@ export default class ErrorText extends PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<Text style={[GlobalStyles.errorLabel, style.errorLabel, textStyle]}>
|
||||
<Text
|
||||
testID='error_text'
|
||||
style={[GlobalStyles.errorLabel, style.errorLabel, textStyle]}
|
||||
>
|
||||
{error.message || error}
|
||||
</Text>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export default class FormattedText extends React.PureComponent {
|
|||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string,
|
||||
values: PropTypes.object,
|
||||
testID: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ exports[`PasteableTextInput should render pasteable text input 1`] = `
|
|||
allowFontScaling={true}
|
||||
onPaste={[Function]}
|
||||
rejectResponderTermination={true}
|
||||
testID="post_input"
|
||||
underlineColorAndroid="transparent"
|
||||
>
|
||||
My Text
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export class PasteableTextInput extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<TextInput
|
||||
testID='post_input'
|
||||
{...props}
|
||||
onPaste={this.onPaste}
|
||||
ref={forwardRef}
|
||||
|
|
|
|||
|
|
@ -315,6 +315,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
|
|||
"paddingTop": 6,
|
||||
}
|
||||
}
|
||||
testID="post_input"
|
||||
underlineColorAndroid="transparent"
|
||||
/>
|
||||
<View
|
||||
|
|
@ -519,6 +520,7 @@ exports[`PostDraft Should render the DraftInput 1`] = `
|
|||
"paddingRight": 8,
|
||||
}
|
||||
}
|
||||
testID="disabled_send_button"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ exports[`SendAction should change theme backgroundColor to 0.3 opacity 1`] = `
|
|||
"paddingRight": 8,
|
||||
}
|
||||
}
|
||||
testID="disabled_send_button"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
|
|
@ -44,6 +45,7 @@ exports[`SendAction should match snapshot 1`] = `
|
|||
"paddingRight": 8,
|
||||
}
|
||||
}
|
||||
testID="send_button"
|
||||
type="opacity"
|
||||
>
|
||||
<View
|
||||
|
|
@ -76,6 +78,7 @@ exports[`SendAction should render theme backgroundColor 1`] = `
|
|||
"paddingRight": 8,
|
||||
}
|
||||
}
|
||||
testID="send_button"
|
||||
type="opacity"
|
||||
>
|
||||
<View
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ function SendButton(props) {
|
|||
|
||||
if (props.disabled) {
|
||||
return (
|
||||
<View style={style.sendButtonContainer}>
|
||||
<View
|
||||
testID='disabled_send_button'
|
||||
style={style.sendButtonContainer}
|
||||
>
|
||||
<View style={[style.sendButton, style.disableButton]}>
|
||||
<PaperPlane
|
||||
height={16}
|
||||
|
|
@ -34,6 +37,7 @@ function SendButton(props) {
|
|||
|
||||
return (
|
||||
<TouchableWithFeedback
|
||||
testID='send_button'
|
||||
onPress={props.handleSendMessage}
|
||||
style={style.sendButtonContainer}
|
||||
type={'opacity'}
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ export default class DrawerLayout extends Component {
|
|||
|
||||
return (
|
||||
<View
|
||||
testID={this.props.testID}
|
||||
style={containerStyles}
|
||||
{...panHandlers}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ export default class MainSidebarIOS extends MainSidebarBase {
|
|||
|
||||
return (
|
||||
<DrawerLayout
|
||||
testID={this.props.testID}
|
||||
ref={this.drawerRef}
|
||||
renderNavigationView={this.renderNavigationView}
|
||||
onDrawerClose={this.handleDrawerClose}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,10 @@ export default class ChannelAndroid extends ChannelBase {
|
|||
if (!component) {
|
||||
component = (
|
||||
<KeyboardLayout>
|
||||
<View style={style.flex}>
|
||||
<View
|
||||
testID='channel_screen'
|
||||
style={style.flex}
|
||||
>
|
||||
<ChannelPostList registerTypingAnimation={this.registerTypingAnimation}/>
|
||||
</View>
|
||||
<PostDraft
|
||||
|
|
|
|||
|
|
@ -106,7 +106,10 @@ export default class ChannelIOS extends ChannelBase {
|
|||
);
|
||||
|
||||
return (
|
||||
<MainSidebar ref={this.mainSidebarRef}>
|
||||
<MainSidebar
|
||||
testID='channel_screen'
|
||||
ref={this.mainSidebarRef}
|
||||
>
|
||||
<SettingsSidebar ref={this.settingsSidebarRef}>
|
||||
<View style={style.backdrop}>
|
||||
{drawerContent}
|
||||
|
|
|
|||
|
|
@ -339,6 +339,7 @@ export default class Login extends PureComponent {
|
|||
|
||||
proceed = (
|
||||
<Button
|
||||
testID='signin_button'
|
||||
onPress={this.preSignIn}
|
||||
containerStyle={[GlobalStyles.signupButton, additionalStyle]}
|
||||
>
|
||||
|
|
@ -384,7 +385,7 @@ export default class Login extends PureComponent {
|
|||
<Image
|
||||
source={require('@assets/images/logo.png')}
|
||||
/>
|
||||
<View>
|
||||
<View testID='login_screen'>
|
||||
<Text style={GlobalStyles.header}>
|
||||
{this.props.config.SiteName}
|
||||
</Text>
|
||||
|
|
@ -396,6 +397,7 @@ export default class Login extends PureComponent {
|
|||
</View>
|
||||
<ErrorText error={this.state.error}/>
|
||||
<TextInput
|
||||
testID='username_input'
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
blurOnSubmit={false}
|
||||
|
|
@ -411,6 +413,7 @@ export default class Login extends PureComponent {
|
|||
underlineColorAndroid='transparent'
|
||||
/>
|
||||
<TextInput
|
||||
testID='password_input'
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
disableFullscreenUI={true}
|
||||
|
|
|
|||
|
|
@ -485,6 +485,7 @@ export default class SelectServer extends PureComponent {
|
|||
|
||||
return (
|
||||
<SafeAreaView
|
||||
testID='select_server_screen'
|
||||
style={style.container}
|
||||
>
|
||||
<KeyboardAvoidingView
|
||||
|
|
@ -511,6 +512,7 @@ export default class SelectServer extends PureComponent {
|
|||
/>
|
||||
</View>
|
||||
<TextInput
|
||||
testID='server_url_input'
|
||||
ref={this.inputRef}
|
||||
value={url}
|
||||
editable={!inputDisabled}
|
||||
|
|
@ -530,6 +532,7 @@ export default class SelectServer extends PureComponent {
|
|||
disableFullscreenUI={true}
|
||||
/>
|
||||
<Button
|
||||
testID='connect_button'
|
||||
onPress={this.handleConnect}
|
||||
containerStyle={[GlobalStyles.signupButton, style.connectButton]}
|
||||
>
|
||||
|
|
|
|||
12
detox/.babelrc
Normal file
12
detox/.babelrc
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env"],
|
||||
"plugins": [
|
||||
"@babel/plugin-transform-runtime",
|
||||
["module-resolver", {
|
||||
"root": ["."],
|
||||
"alias": {
|
||||
"@support": "./e2e/support"
|
||||
}
|
||||
}]
|
||||
]
|
||||
}
|
||||
42
detox/.detoxrc.json
Normal file
42
detox/.detoxrc.json
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"testRunner": "jest",
|
||||
"runnerConfig": "e2e/config.json",
|
||||
"configurations": {
|
||||
"ios.sim.debug": {
|
||||
"binaryPath": "../ios/Build/Products/Debug-iphonesimulator/Mattermost.app",
|
||||
"type": "ios.simulator",
|
||||
"device": {
|
||||
"type": "iPhone 11"
|
||||
}
|
||||
},
|
||||
"ios.sim.release": {
|
||||
"type": "ios.simulator",
|
||||
"binaryPath": "../ios/Build/Products/Release-iphonesimulator/Mattermost.app",
|
||||
"build": "cd ../fastlane && NODE_ENV=production bundle exec fastlane ios simulator && cd ../detox",
|
||||
"device": {
|
||||
"type": "iPhone 11"
|
||||
}
|
||||
},
|
||||
"android.emu.debug": {
|
||||
"type": "android.emulator",
|
||||
"binaryPath": "../android/app/build/outputs/apk/debug/app-debug.apk",
|
||||
"build": "cd ../android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ../detox",
|
||||
"device": {
|
||||
"avdName": "detox_emu_api_30"
|
||||
}
|
||||
},
|
||||
"android.emu.release": {
|
||||
"type": "android.emulator",
|
||||
"binaryPath": "../android/app/build/outputs/apk/release/app-release.apk",
|
||||
"build": "cd ../android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ../detox",
|
||||
"device": {
|
||||
"avdName": "detox_emu_api_30"
|
||||
}
|
||||
}
|
||||
},
|
||||
"behavior": {
|
||||
"init": {
|
||||
"launchApp": false
|
||||
}
|
||||
}
|
||||
}
|
||||
9
detox/e2e/config.json
Normal file
9
detox/e2e/config.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"setupFilesAfterEnv" : ["./init.js"],
|
||||
"testEnvironment": "./environment",
|
||||
"testRunner": "jest-circus/runner",
|
||||
"testTimeout": 120000,
|
||||
"testRegex": "\\.e2e\\.js$",
|
||||
"reporters": ["detox/runners/jest/streamlineReporter"],
|
||||
"verbose": true
|
||||
}
|
||||
25
detox/e2e/environment.js
Normal file
25
detox/e2e/environment.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
const {
|
||||
DetoxCircusEnvironment,
|
||||
SpecReporter,
|
||||
WorkerAssignReporter,
|
||||
} = require('detox/runners/jest-circus');
|
||||
|
||||
class CustomDetoxEnvironment extends DetoxCircusEnvironment {
|
||||
constructor(config) {
|
||||
super(config);
|
||||
|
||||
this.initTimeout = 300000;
|
||||
|
||||
// This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
|
||||
// This is strictly optional.
|
||||
this.registerListeners({
|
||||
SpecReporter,
|
||||
WorkerAssignReporter,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CustomDetoxEnvironment;
|
||||
21
detox/e2e/init.js
Normal file
21
detox/e2e/init.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {System, User} from '@support/server_api';
|
||||
|
||||
beforeAll(async () => {
|
||||
// Login as sysadmin and reset server configuration
|
||||
await User.apiAdminLogin();
|
||||
await System.apiUpdateConfig();
|
||||
|
||||
await device.launchApp({
|
||||
newInstance: false,
|
||||
launchArgs: {detoxPrintBusyIdleResources: 'YES'},
|
||||
permissions: {
|
||||
notifications: 'YES',
|
||||
camera: 'YES',
|
||||
medialibrary: 'YES',
|
||||
photos: 'YES',
|
||||
},
|
||||
});
|
||||
});
|
||||
81
detox/e2e/support/server_api/channel.js
Normal file
81
detox/e2e/support/server_api/channel.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {capitalize, getRandomId} from '@support/utils';
|
||||
|
||||
import client from './client';
|
||||
import {getResponseFromError} from './common';
|
||||
|
||||
// ****************************************************************
|
||||
// Channels
|
||||
// See https://api.mattermost.com/#tag/channels
|
||||
//
|
||||
// Exported API function should have the following:
|
||||
// - documented using JSDoc
|
||||
// - meaningful description
|
||||
// - match the referenced API endpoints
|
||||
// - parameter/s defined by `@param`
|
||||
// - return value defined by `@return`
|
||||
// ****************************************************************
|
||||
|
||||
/**
|
||||
* Create a channel.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels/post
|
||||
* @param {string} option.teamId - The team ID of the team to create the channel on
|
||||
* @param {string} option.type - 'O' (default) for a public channel, 'P' for a private channel
|
||||
* @param {string} option.prefix - option to add prefix to name and display name
|
||||
* @param {Object} option.channel - fix channel object to be created
|
||||
* @return {Object} returns {channel} on success or {error, status} on error
|
||||
*/
|
||||
export const apiCreateChannel = async ({teamId = null, type = 'O', prefix = 'channel', channel = null} = {}) => {
|
||||
try {
|
||||
const response = await client.post(
|
||||
'/api/v4/channels',
|
||||
channel || generateRandomChannel(teamId, type, prefix),
|
||||
);
|
||||
|
||||
return {channel: response.data};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add user to channel.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels~1{channel_id}~1members/post
|
||||
* @param {string} userId - The ID of user to add into the channel
|
||||
* @param {string} channelId - The channel ID
|
||||
* @return {Object} returns {member} on success or {error, status} on error
|
||||
*/
|
||||
export const apiAddUserToChannel = async (userId, channelId) => {
|
||||
try {
|
||||
const response = await client.post(
|
||||
`/api/v4/channels/${channelId}/members`,
|
||||
{user_id: userId},
|
||||
);
|
||||
|
||||
return {member: response.data};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
};
|
||||
|
||||
function generateRandomChannel(teamId, type, prefix) {
|
||||
const randomId = getRandomId();
|
||||
|
||||
return {
|
||||
team_id: teamId,
|
||||
name: `${prefix}-${randomId}`,
|
||||
display_name: `${capitalize(prefix)} ${randomId}`,
|
||||
type,
|
||||
purpose: `Channel purpose: ${prefix} ${randomId}`,
|
||||
header: `Channel header: ${prefix} ${randomId}`,
|
||||
};
|
||||
}
|
||||
|
||||
export const Channel = {
|
||||
apiAddUserToChannel,
|
||||
apiCreateChannel,
|
||||
};
|
||||
|
||||
export default Channel;
|
||||
13
detox/e2e/support/server_api/client.js
Normal file
13
detox/e2e/support/server_api/client.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
import testConfig from '../test_config';
|
||||
|
||||
export const client = axios.create({
|
||||
baseURL: testConfig.siteUrl,
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
});
|
||||
|
||||
export default client;
|
||||
8
detox/e2e/support/server_api/common.js
Normal file
8
detox/e2e/support/server_api/common.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export const getResponseFromError = (err) => {
|
||||
const {response: {data, status}} = err;
|
||||
|
||||
return {error: data, status};
|
||||
};
|
||||
296
detox/e2e/support/server_api/default_config.json
Normal file
296
detox/e2e/support/server_api/default_config.json
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
{
|
||||
"ServiceSettings": {
|
||||
"SiteURL": "http://localhost:8065",
|
||||
"WebsocketURL": "",
|
||||
"LicenseFileLocation": "",
|
||||
"ListenAddress": ":8065",
|
||||
"ConnectionSecurity": "",
|
||||
"TLSCertFile": "",
|
||||
"TLSKeyFile": "",
|
||||
"TLSMinVer": "1.2",
|
||||
"TLSStrictTransport": false,
|
||||
"TLSStrictTransportMaxAge": 63072000,
|
||||
"TLSOverwriteCiphers": [],
|
||||
"UseLetsEncrypt": false,
|
||||
"Forward80To443": false,
|
||||
"TrustedProxyIPHeader": [],
|
||||
"ReadTimeout": 300,
|
||||
"WriteTimeout": 300,
|
||||
"IdleTimeout": 300,
|
||||
"MaximumLoginAttempts": 10,
|
||||
"GoroutineHealthThreshold": -1,
|
||||
"GoogleDeveloperKey": "",
|
||||
"EnableOAuthServiceProvider": false,
|
||||
"EnableIncomingWebhooks": true,
|
||||
"EnableOutgoingWebhooks": true,
|
||||
"EnableCommands": true,
|
||||
"EnableOnlyAdminIntegrations": true,
|
||||
"EnablePostUsernameOverride": false,
|
||||
"EnablePostIconOverride": false,
|
||||
"EnableLinkPreviews": false,
|
||||
"EnableTesting": false,
|
||||
"EnableDeveloper": false,
|
||||
"EnableOpenTracing": false,
|
||||
"EnableSecurityFixAlert": true,
|
||||
"EnableInsecureOutgoingConnections": false,
|
||||
"AllowedUntrustedInternalConnections": "localhost",
|
||||
"EnableMultifactorAuthentication": false,
|
||||
"EnforceMultifactorAuthentication": false,
|
||||
"EnableUserAccessTokens": false,
|
||||
"AllowCorsFrom": "",
|
||||
"CorsExposedHeaders": "",
|
||||
"CorsAllowCredentials": false,
|
||||
"CorsDebug": false,
|
||||
"AllowCookiesForSubdomains": false,
|
||||
"ExtendSessionLengthWithActivity": true,
|
||||
"SessionLengthWebInDays": 30,
|
||||
"SessionLengthMobileInDays": 30,
|
||||
"SessionLengthSSOInDays": 30,
|
||||
"SessionCacheInMinutes": 10,
|
||||
"SessionIdleTimeoutInMinutes": 43200,
|
||||
"WebsocketSecurePort": 443,
|
||||
"WebsocketPort": 80,
|
||||
"WebserverMode": "gzip",
|
||||
"EnableCustomEmoji": false,
|
||||
"EnableEmojiPicker": true,
|
||||
"EnableGifPicker": false,
|
||||
"GfycatApiKey": "2_KtH_W5",
|
||||
"GfycatApiSecret": "3wLVZPiswc3DnaiaFoLkDvB4X0IV6CpMkj4tf2inJRsBY6-FnkT08zGmppWFgeof",
|
||||
"RestrictCustomEmojiCreation": "all",
|
||||
"RestrictPostDelete": "all",
|
||||
"AllowEditPost": "always",
|
||||
"PostEditTimeLimit": -1,
|
||||
"TimeBetweenUserTypingUpdatesMilliseconds": 5000,
|
||||
"EnablePostSearch": true,
|
||||
"MinimumHashtagLength": 3,
|
||||
"EnableUserTypingMessages": true,
|
||||
"EnableChannelViewedMessages": true,
|
||||
"EnableUserStatuses": true,
|
||||
"ExperimentalEnableAuthenticationTransfer": true,
|
||||
"CloseUnusedDirectMessages": false,
|
||||
"EnablePreviewFeatures": true,
|
||||
"ExperimentalEnableDefaultChannelLeaveJoinMessages": true,
|
||||
"ExperimentalGroupUnreadChannels": "disabled",
|
||||
"ExperimentalChannelOrganization": false,
|
||||
"ExperimentalChannelSidebarOrganization": "disabled",
|
||||
"EnableAPITeamDeletion": true,
|
||||
"ExperimentalEnableHardenedMode": false,
|
||||
"DisableLegacyMFA": true,
|
||||
"ExperimentalStrictCSRFEnforcement": false,
|
||||
"EnableEmailInvitations": true,
|
||||
"DisableBotsWhenOwnerIsDeactivated": true,
|
||||
"EnableBotAccountCreation": true,
|
||||
"EnableSVGs": true,
|
||||
"EnableLatex": false
|
||||
},
|
||||
"TeamSettings": {
|
||||
"SiteName": "Mattermost",
|
||||
"MaxUsersPerTeam": 2000,
|
||||
"EnableTeamCreation": true,
|
||||
"EnableUserCreation": true,
|
||||
"EnableOpenServer": true,
|
||||
"EnableUserDeactivation": false,
|
||||
"RestrictCreationToDomains": "",
|
||||
"EnableCustomBrand": false,
|
||||
"CustomBrandText": "",
|
||||
"CustomDescriptionText": "",
|
||||
"RestrictDirectMessage": "any",
|
||||
"RestrictTeamInvite": "all",
|
||||
"RestrictPublicChannelManagement": "all",
|
||||
"RestrictPrivateChannelManagement": "all",
|
||||
"RestrictPublicChannelCreation": "all",
|
||||
"RestrictPrivateChannelCreation": "all",
|
||||
"RestrictPublicChannelDeletion": "all",
|
||||
"RestrictPrivateChannelDeletion": "all",
|
||||
"RestrictPrivateChannelManageMembers": "all",
|
||||
"EnableXToLeaveChannelsFromLHS": false,
|
||||
"UserStatusAwayTimeout": 300,
|
||||
"MaxChannelsPerTeam": 2000,
|
||||
"MaxNotificationsPerChannel": 1000,
|
||||
"EnableConfirmNotificationsToChannel": true,
|
||||
"TeammateNameDisplay": "username",
|
||||
"ExperimentalViewArchivedChannels": false,
|
||||
"ExperimentalEnableAutomaticReplies": false,
|
||||
"ExperimentalHideTownSquareinLHS": false,
|
||||
"ExperimentalTownSquareIsReadOnly": false,
|
||||
"LockTeammateNameDisplay": false,
|
||||
"ExperimentalPrimaryTeam": "",
|
||||
"ExperimentalDefaultChannels": []
|
||||
},
|
||||
"ClientRequirements": {
|
||||
"AndroidLatestVersion": "",
|
||||
"AndroidMinVersion": "",
|
||||
"DesktopLatestVersion": "",
|
||||
"DesktopMinVersion": "",
|
||||
"IosLatestVersion": "",
|
||||
"IosMinVersion": ""
|
||||
},
|
||||
"LogSettings": {
|
||||
"EnableConsole": true
|
||||
},
|
||||
"NotificationLogSettings": {
|
||||
"EnableConsole": true
|
||||
},
|
||||
"PasswordSettings": {
|
||||
"MinimumLength": 5,
|
||||
"Lowercase": false,
|
||||
"Number": false,
|
||||
"Uppercase": false,
|
||||
"Symbol": false,
|
||||
"Enable": false
|
||||
},
|
||||
"FileSettings": {
|
||||
"EnableFileAttachments": true,
|
||||
"EnableMobileUpload": true,
|
||||
"EnableMobileDownload": true,
|
||||
"MaxFileSize": 52428800,
|
||||
"DriverName": "local",
|
||||
"Directory": "./data/",
|
||||
"EnablePublicLink": false,
|
||||
"PublicLinkSalt": ""
|
||||
},
|
||||
"EmailSettings": {
|
||||
"EnableSignUpWithEmail": true,
|
||||
"EnableSignInWithEmail": true,
|
||||
"EnableSignInWithUsername": true,
|
||||
"SendEmailNotifications": true,
|
||||
"UseChannelInEmailNotifications": false,
|
||||
"RequireEmailVerification": false,
|
||||
"FeedbackName": "",
|
||||
"FeedbackEmail": "test@example.com",
|
||||
"ReplyToAddress": "test@example.com",
|
||||
"FeedbackOrganization": "",
|
||||
"EnableSMTPAuth": false,
|
||||
"SMTPUsername": "",
|
||||
"SMTPPassword": "",
|
||||
"SMTPServer": "localhost",
|
||||
"SMTPPort": "10025",
|
||||
"SMTPServerTimeout": 10,
|
||||
"ConnectionSecurity": "",
|
||||
"SendPushNotifications": true,
|
||||
"PushNotificationServer": "https://push-test.mattermost.com",
|
||||
"PushNotificationContents": "generic",
|
||||
"EnableEmailBatching": false,
|
||||
"EmailBatchingBufferSize": 256,
|
||||
"EmailBatchingInterval": 30,
|
||||
"EnablePreviewModeBanner": true,
|
||||
"SkipServerCertificateVerification": false,
|
||||
"EmailNotificationContentsType": "full",
|
||||
"LoginButtonColor": "#0000",
|
||||
"LoginButtonBorderColor": "#2389D7",
|
||||
"LoginButtonTextColor": "#2389D7"
|
||||
},
|
||||
"RateLimitSettings": {
|
||||
"Enable": false
|
||||
},
|
||||
"PrivacySettings": {
|
||||
"ShowEmailAddress": true,
|
||||
"ShowFullName": true
|
||||
},
|
||||
"SupportSettings": {
|
||||
"TermsOfServiceLink": "https://about.mattermost.com/default-terms/",
|
||||
"PrivacyPolicyLink": "https://about.mattermost.com/default-privacy-policy/",
|
||||
"AboutLink": "https://about.mattermost.com/default-about/",
|
||||
"HelpLink": "https://about.mattermost.com/default-help/",
|
||||
"ReportAProblemLink": "https://about.mattermost.com/default-report-a-problem/",
|
||||
"SupportEmail": "feedback@mattermost.com",
|
||||
"CustomTermsOfServiceEnabled": false,
|
||||
"CustomTermsOfServiceReAcceptancePeriod": 365,
|
||||
"EnableAskCommunityLink": true
|
||||
},
|
||||
"AnnouncementSettings": {
|
||||
"EnableBanner": false
|
||||
},
|
||||
"ThemeSettings": {
|
||||
"EnableThemeSelection": true,
|
||||
"DefaultTheme": "default",
|
||||
"AllowCustomThemes": true,
|
||||
"AllowedThemes": []
|
||||
},
|
||||
"GitLabSettings": {
|
||||
"Enable": false
|
||||
},
|
||||
"GoogleSettings": {
|
||||
"Enable": false
|
||||
},
|
||||
"Office365Settings": {
|
||||
"Enable": false
|
||||
},
|
||||
"LdapSettings": {
|
||||
"Enable": false
|
||||
},
|
||||
"ComplianceSettings": {
|
||||
"Enable": false
|
||||
},
|
||||
"LocalizationSettings": {
|
||||
"DefaultServerLocale": "en",
|
||||
"DefaultClientLocale": "en",
|
||||
"AvailableLocales": ""
|
||||
},
|
||||
"SamlSettings": {
|
||||
"Enable": false
|
||||
},
|
||||
"NativeAppSettings": {
|
||||
"AppDownloadLink": "https://mattermost.com/download/#mattermostApps",
|
||||
"AndroidAppDownloadLink": "https://about.mattermost.com/mattermost-android-app/",
|
||||
"IosAppDownloadLink": "https://about.mattermost.com/mattermost-ios-app/"
|
||||
},
|
||||
"ClusterSettings": {
|
||||
"Enable": false
|
||||
},
|
||||
"MetricsSettings": {
|
||||
"Enable": false
|
||||
},
|
||||
"ExperimentalSettings": {
|
||||
"ClientSideCertEnable": false,
|
||||
"ClientSideCertCheck": "secondary",
|
||||
"LinkMetadataTimeoutMilliseconds": 5000,
|
||||
"RestrictSystemAdmin": false,
|
||||
"UseNewSAMLLibrary": false
|
||||
},
|
||||
"AnalyticsSettings": {
|
||||
"MaxUsersForStatistics": 2500
|
||||
},
|
||||
"ElasticsearchSettings": {
|
||||
"ConnectionUrl": "http://localhost:9200",
|
||||
"Username": "elastic",
|
||||
"Password": "changeme",
|
||||
"EnableIndexing": false,
|
||||
"EnableSearching": false,
|
||||
"EnableAutocomplete": false,
|
||||
"Sniff": true
|
||||
},
|
||||
"DataRetentionSettings": {
|
||||
"EnableMessageDeletion": false,
|
||||
"EnableFileDeletion": false,
|
||||
"MessageRetentionDays": 365,
|
||||
"FileRetentionDays": 365,
|
||||
"DeletionJobStartTime": "02:00"
|
||||
},
|
||||
"MessageExportSettings": {
|
||||
"EnableExport": false
|
||||
},
|
||||
"JobSettings": {
|
||||
"RunJobs": true,
|
||||
"RunScheduler": true
|
||||
},
|
||||
"PluginSettings": {
|
||||
"Enable": false
|
||||
},
|
||||
"DisplaySettings": {
|
||||
"CustomUrlSchemes": [],
|
||||
"ExperimentalTimezone": false
|
||||
},
|
||||
"GuestAccountsSettings": {
|
||||
"Enable": true,
|
||||
"AllowEmailAccounts": true,
|
||||
"EnforceMultifactorAuthentication": false,
|
||||
"RestrictCreationToDomains": ""
|
||||
},
|
||||
"ImageProxySettings": {
|
||||
"Enable": true,
|
||||
"ImageProxyType": "local",
|
||||
"RemoteImageProxyURL": "",
|
||||
"RemoteImageProxyOptions": ""
|
||||
}
|
||||
}
|
||||
16
detox/e2e/support/server_api/index.js
Normal file
16
detox/e2e/support/server_api/index.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import Channel from './channel';
|
||||
import Setup from './setup';
|
||||
import System from './system';
|
||||
import Team from './team';
|
||||
import User from './user';
|
||||
|
||||
export {
|
||||
Channel,
|
||||
Setup,
|
||||
System,
|
||||
Team,
|
||||
User,
|
||||
};
|
||||
41
detox/e2e/support/server_api/setup.js
Normal file
41
detox/e2e/support/server_api/setup.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import Channel from './channel';
|
||||
import Team from './team';
|
||||
import User from './user';
|
||||
import {getResponseFromError} from './common';
|
||||
|
||||
/**
|
||||
* Creates new user, channel and team for test isolation.
|
||||
* @param {Object} options - may pass options to predefine channel, team and user creation
|
||||
* @return {Object} returns {channel, team, user} on success or {error, status} on error
|
||||
*/
|
||||
export const apiInit = async ({
|
||||
channelOptions = {type: 'O', prefix: 'channel'},
|
||||
teamOptions = {type: 'O', prefix: 'team'},
|
||||
userOptions = {prefix: 'user'},
|
||||
} = {}) => {
|
||||
try {
|
||||
const {team} = await Team.apiCreateTeam(teamOptions);
|
||||
const {channel} = await Channel.apiCreateChannel({...channelOptions, teamId: team.id});
|
||||
const {user} = await User.apiCreateUser(userOptions);
|
||||
|
||||
await Team.apiAddUserToTeam(user.id, team.id);
|
||||
await Channel.apiAddUserToChannel(user.id, channel.id);
|
||||
|
||||
return {
|
||||
channel,
|
||||
team,
|
||||
user,
|
||||
};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const Setup = {
|
||||
apiInit,
|
||||
};
|
||||
|
||||
export default Setup;
|
||||
72
detox/e2e/support/server_api/system.js
Normal file
72
detox/e2e/support/server_api/system.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import merge from 'merge-deep';
|
||||
|
||||
import testConfig from '@support/test_config';
|
||||
|
||||
import client from './client';
|
||||
import {getResponseFromError} from './common';
|
||||
import defaultServerConfig from './default_config.json';
|
||||
|
||||
// ****************************************************************
|
||||
// System
|
||||
// See https://api.mattermost.com/#tag/system
|
||||
//
|
||||
// Exported API function should have the following:
|
||||
// - documented using JSDoc
|
||||
// - meaningful description
|
||||
// - match the referenced API endpoints
|
||||
// - parameter/s defined by `@param`
|
||||
// - return value defined by `@return`
|
||||
// ****************************************************************
|
||||
|
||||
/**
|
||||
* Get configuration.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1config/get
|
||||
*/
|
||||
export const apiGetConfig = async () => {
|
||||
try {
|
||||
const response = await client.get('/api/v4/config');
|
||||
|
||||
return {config: response.data};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update configuration.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1config/put
|
||||
* @param {Object} newConfig - specific config to update
|
||||
*/
|
||||
export const apiUpdateConfig = async (newConfig = {}) => {
|
||||
try {
|
||||
const {config: currentConfig} = await apiGetConfig();
|
||||
const config = merge(currentConfig, getDefaultConfig(), newConfig);
|
||||
|
||||
const response = await client.put(
|
||||
'/api/v4/config',
|
||||
config,
|
||||
);
|
||||
|
||||
return {config: response.data};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
};
|
||||
|
||||
const getDefaultConfig = () => {
|
||||
const fromEnv = {
|
||||
ServiceSettings: {SiteURL: testConfig.siteUrl},
|
||||
};
|
||||
|
||||
return merge(defaultServerConfig, fromEnv);
|
||||
};
|
||||
|
||||
export const System = {
|
||||
apiGetConfig,
|
||||
apiUpdateConfig,
|
||||
};
|
||||
|
||||
export default System;
|
||||
77
detox/e2e/support/server_api/team.js
Normal file
77
detox/e2e/support/server_api/team.js
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {capitalize, getRandomId} from '@support/utils';
|
||||
|
||||
import client from './client';
|
||||
import {getResponseFromError} from './common';
|
||||
|
||||
// ****************************************************************
|
||||
// Teams
|
||||
// See https://api.mattermost.com/#tag/teams
|
||||
//
|
||||
// Exported API function should have the following:
|
||||
// - documented using JSDoc
|
||||
// - meaningful description
|
||||
// - match the referenced API endpoints
|
||||
// - parameter/s defined by `@param`
|
||||
// - return value defined by `@return`
|
||||
// ****************************************************************
|
||||
|
||||
/**
|
||||
* Create a team.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams/post
|
||||
* @param {string} option.type - 'O' (default) for open, 'I' for invite only
|
||||
* @param {string} option.prefix - option to add prefix to name and display name
|
||||
* @param {Object} team - fix team object to be created
|
||||
* @return {Object} returns {team} on success or {error, status} on error
|
||||
*/
|
||||
export const apiCreateTeam = async ({type = 'O', prefix = 'team', team = null} = {}) => {
|
||||
try {
|
||||
const response = await client.post(
|
||||
'/api/v4/teams',
|
||||
team || generateRandomTeam(type, prefix),
|
||||
);
|
||||
|
||||
return {team: response.data};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add user to team.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams~1{team_id}~1members/post
|
||||
* @param {string} userId - The ID of user to add into the team
|
||||
* @param {string} teamId - The team ID
|
||||
* @return {Object} returns {member} on success or {error, status} on error
|
||||
*/
|
||||
export const apiAddUserToTeam = async (userId, teamId) => {
|
||||
try {
|
||||
const response = await client.post(
|
||||
`/api/v4/teams/${teamId}/members`,
|
||||
{team_id: teamId, user_id: userId},
|
||||
);
|
||||
|
||||
return {member: response.data};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
};
|
||||
|
||||
function generateRandomTeam(type, prefix) {
|
||||
const randomId = getRandomId();
|
||||
|
||||
return {
|
||||
name: `${prefix}-${randomId}`,
|
||||
display_name: `${capitalize(prefix)} ${randomId}`,
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
export const Team = {
|
||||
apiAddUserToTeam,
|
||||
apiCreateTeam,
|
||||
};
|
||||
|
||||
export default Team;
|
||||
136
detox/e2e/support/server_api/user.js
Normal file
136
detox/e2e/support/server_api/user.js
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import testConfig from '@support/test_config';
|
||||
import {getRandomId} from '@support/utils';
|
||||
|
||||
import client from './client';
|
||||
import {getResponseFromError} from './common';
|
||||
|
||||
// ****************************************************************
|
||||
// Users
|
||||
// See https://api.mattermost.com/#tag/users
|
||||
//
|
||||
// Exported API function should have the following:
|
||||
// - documented using JSDoc
|
||||
// - meaningful description
|
||||
// - match the referenced API endpoints
|
||||
// - parameter/s defined by `@param`
|
||||
// - return value defined by `@return`
|
||||
// ****************************************************************
|
||||
|
||||
/**
|
||||
* Login to Mattermost server.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1login/post
|
||||
* @param {string} user.username - username of a user
|
||||
* @param {string} user.password - password of a user
|
||||
*/
|
||||
export const apiLogin = async (user) => {
|
||||
try {
|
||||
const response = await client.post(
|
||||
'/api/v4/users/login',
|
||||
{login_id: user.username, password: user.password},
|
||||
);
|
||||
|
||||
const {data, headers, status} = response;
|
||||
|
||||
// Get MMAUTHTOKEN cookie and attach to the client instance
|
||||
const [mmAuthToken] = headers['set-cookie'];
|
||||
client.defaults.headers.Cookie = mmAuthToken;
|
||||
|
||||
return {
|
||||
status,
|
||||
user: data,
|
||||
};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Login to Mattermost server as sysadmin.
|
||||
*/
|
||||
export const apiAdminLogin = async () => {
|
||||
return apiLogin({
|
||||
username: testConfig.adminUsername,
|
||||
password: testConfig.adminPassword,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Logout from the Mattermost server.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1logout/post
|
||||
*/
|
||||
export const apiLogout = async () => {
|
||||
const response = await client.post('/api/v4/users/logout');
|
||||
|
||||
client.defaults.headers.Cookie = '';
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a user.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users/post
|
||||
* @param {Object} user - user object to be created
|
||||
*/
|
||||
export const apiCreateUser = async ({prefix = 'user', user = null} = {}) => {
|
||||
try {
|
||||
const newUser = user || generateRandomUser(prefix);
|
||||
|
||||
const response = await client.post(
|
||||
'/api/v4/users',
|
||||
newUser,
|
||||
);
|
||||
|
||||
return {user: {...response.data, password: newUser.password}};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get user from a current session.
|
||||
*/
|
||||
export const apiGetMe = () => {
|
||||
return apiGetUserById('me');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a user by ID.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}/get
|
||||
* @param {string} userId
|
||||
*/
|
||||
export const apiGetUserById = async (userId) => {
|
||||
try {
|
||||
const response = await client.get(`/api/v4/users/${userId}`);
|
||||
|
||||
return {user: response.data};
|
||||
} catch (err) {
|
||||
return getResponseFromError(err);
|
||||
}
|
||||
};
|
||||
|
||||
function generateRandomUser(prefix) {
|
||||
const randomId = getRandomId();
|
||||
|
||||
return {
|
||||
email: `${prefix}${randomId}@sample.mattermost.com`,
|
||||
username: `${prefix}${randomId}`,
|
||||
password: 'passwd',
|
||||
first_name: `First${randomId}`,
|
||||
last_name: `Last${randomId}`,
|
||||
nickname: `Nickname${randomId}`,
|
||||
};
|
||||
}
|
||||
|
||||
export const User = {
|
||||
apiAdminLogin,
|
||||
apiLogin,
|
||||
apiLogout,
|
||||
apiCreateUser,
|
||||
apiGetMe,
|
||||
apiGetUserById,
|
||||
};
|
||||
|
||||
export default User;
|
||||
9
detox/e2e/support/test_config.js
Normal file
9
detox/e2e/support/test_config.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
module.exports = {
|
||||
serverUrl: process.env.SITE_URL || (process.env.IOS ? 'http://localhost:8065' : 'http://10.0.2.2:8065'),
|
||||
siteUrl: process.env.SITE_URL || 'http://localhost:8065',
|
||||
adminUsername: process.env.ADMIN_USERNAME || 'sysadmin',
|
||||
adminPassword: process.env.ADMIN_PASSWORD || 'Sys@dmin-sample1',
|
||||
};
|
||||
56
detox/e2e/support/ui/screen.js
Normal file
56
detox/e2e/support/ui/screen.js
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {serverUrl} from '../test_config';
|
||||
|
||||
// ****************************************************************
|
||||
// Select Server Screen
|
||||
// ****************************************************************
|
||||
|
||||
/**
|
||||
* fulfillSelectServerScreen enters server URL on Select Server screen and then tap connect button.
|
||||
* @param {String} url - server URL
|
||||
*/
|
||||
export async function fulfillSelectServerScreen(url) {
|
||||
await expect(element(by.id('select_server_screen'))).toBeVisible();
|
||||
|
||||
await element(by.id('server_url_input')).replaceText(url);
|
||||
|
||||
// # Tap anywhere to hide keyboard
|
||||
await element(by.text('Enter Server URL')).tap();
|
||||
|
||||
await element(by.id('connect_button')).tap();
|
||||
}
|
||||
|
||||
// ****************************************************************
|
||||
// Login Screen
|
||||
// ****************************************************************
|
||||
|
||||
/**
|
||||
* fulfillLoginScreen enters credential on Login screen and then tap "Sign in" button to log in.
|
||||
* @param {Object} user - user to login with username and password
|
||||
*/
|
||||
export async function fulfillLoginScreen(user = {}) {
|
||||
await expect(element(by.id('login_screen'))).toBeVisible();
|
||||
|
||||
await element(by.id('username_input')).replaceText(user.username);
|
||||
|
||||
// # Tap anywhere to hide keyboard
|
||||
await element(by.text('Mattermost')).tap();
|
||||
|
||||
await element(by.id('password_input')).replaceText(user.password);
|
||||
|
||||
// # Tap anywhere to hide keyboard
|
||||
await element(by.text('Mattermost')).tap();
|
||||
|
||||
await element(by.id('signin_button')).tap();
|
||||
}
|
||||
|
||||
// ****************************************************************
|
||||
// Channel Screen
|
||||
// ****************************************************************
|
||||
|
||||
export async function toChannelScreen(user) {
|
||||
await fulfillSelectServerScreen(serverUrl);
|
||||
await fulfillLoginScreen(user);
|
||||
}
|
||||
53
detox/e2e/support/utils.js
Normal file
53
detox/e2e/support/utils.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
/**
|
||||
* Explicit `wait` should not normally used but made available for special cases.
|
||||
* @param {number} ms - duration in millisecond
|
||||
*/
|
||||
export const wait = async (ms) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
export const isAndroid = () => {
|
||||
return !process.env.IOS;
|
||||
};
|
||||
|
||||
export const isIos = () => {
|
||||
return process.env.IOS;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} length - length on random string to return, e.g. 6 (default)
|
||||
* @return {string} random string
|
||||
*/
|
||||
export const getRandomId = (length = 6) => {
|
||||
const MAX_SUBSTRING_INDEX = 27;
|
||||
|
||||
return uuidv4().replace(/-/g, '').substring(MAX_SUBSTRING_INDEX - length, MAX_SUBSTRING_INDEX);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @return {string} capitalized text
|
||||
*/
|
||||
export const capitalize = (text) => {
|
||||
return text.charAt(0).toUpperCase() + text.slice(1);
|
||||
};
|
||||
|
||||
const SECOND = 1000;
|
||||
const MINUTE = 60 * 1000;
|
||||
|
||||
export const timeouts = {
|
||||
HALF_SEC: SECOND / 2,
|
||||
ONE_SEC: SECOND,
|
||||
TWO_SEC: SECOND * 2,
|
||||
FOUR_SEC: SECOND * 4,
|
||||
TEN_SEC: SECOND * 10,
|
||||
HALF_MIN: MINUTE / 2,
|
||||
ONE_MIN: MINUTE,
|
||||
TWO_MIN: MINUTE * 2,
|
||||
FOUR_MIN: MINUTE * 4,
|
||||
};
|
||||
29
detox/e2e/test/messaging/message_posting.e2e.js
Normal file
29
detox/e2e/test/messaging/message_posting.e2e.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {toChannelScreen} from '@support/ui/screen';
|
||||
|
||||
import {Setup} from '@support/server_api';
|
||||
|
||||
describe('Messaging', () => {
|
||||
beforeAll(async () => {
|
||||
const {user} = await Setup.apiInit();
|
||||
|
||||
await toChannelScreen(user);
|
||||
});
|
||||
|
||||
it('should post a message on tap to paper send button', async () => {
|
||||
await expect(element(by.id('channel_screen'))).toBeVisible();
|
||||
await expect(element(by.id('post_input'))).toExist();
|
||||
await expect(element(by.id('send_button'))).not.toExist();
|
||||
await element(by.id('post_input')).tap();
|
||||
|
||||
const text = Date.now().toString();
|
||||
await element(by.id('post_input')).typeText(text);
|
||||
|
||||
await expect(element(by.id('send_button'))).toBeVisible();
|
||||
await element(by.id('send_button')).tap();
|
||||
|
||||
await expect(element(by.text(text))).toExist();
|
||||
});
|
||||
});
|
||||
163
detox/e2e/test/on_boarding/login.e2e.js
Normal file
163
detox/e2e/test/on_boarding/login.e2e.js
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Setup} from '@support/server_api';
|
||||
import {serverUrl} from '@support/test_config';
|
||||
import {fulfillSelectServerScreen} from '@support/ui/screen';
|
||||
import {isAndroid, timeouts, wait} from '@support/utils';
|
||||
|
||||
describe('On boarding', () => {
|
||||
let user;
|
||||
|
||||
beforeAll(async () => {
|
||||
({user} = await Setup.apiInit());
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await device.reloadReactNative();
|
||||
});
|
||||
|
||||
it('should show Select server screen on initial load', async () => {
|
||||
// Verify basic elements on Select Server screen
|
||||
await expect(element(by.id('select_server_screen'))).toBeVisible();
|
||||
await expect(element(by.id('server_url_input'))).toBeVisible();
|
||||
await expect(element(by.id('connect_button'))).toBeVisible();
|
||||
});
|
||||
|
||||
it('should show error on invalid server URL', async () => {
|
||||
await expect(element(by.id('select_server_screen'))).toBeVisible();
|
||||
|
||||
// Enter invalid server URL
|
||||
await element(by.id('server_url_input')).typeText('http://invalid:8065');
|
||||
|
||||
// Tap anywhere to hide keyboard
|
||||
await element(by.text('Enter Server URL')).tap();
|
||||
|
||||
// Verify that the error message does not exist
|
||||
await waitFor(element(by.id('error_text'))).not.toExist().withTimeout(timeouts.HALF_SEC);
|
||||
|
||||
// Tap connect button
|
||||
await element(by.id('connect_button')).tap();
|
||||
|
||||
// Explicitly wait on Android before verifying error message
|
||||
if (isAndroid()) {
|
||||
await wait(timeouts.ONE_MIN);
|
||||
}
|
||||
|
||||
// Verify error message
|
||||
await waitFor(element(by.id('error_text'))).toBeVisible().withTimeout(timeouts.ONE_MIN);
|
||||
await expect(element(by.id('error_text'))).toHaveText('Cannot connect to the server. Please check your server URL and internet connection.');
|
||||
});
|
||||
|
||||
it('should move to Login screen on valid server URL', async () => {
|
||||
await expect(element(by.id('select_server_screen'))).toBeVisible();
|
||||
|
||||
// Enter valid server URL
|
||||
await element(by.id('server_url_input')).replaceText(serverUrl);
|
||||
|
||||
// Tap connect button
|
||||
await element(by.id('connect_button')).tap();
|
||||
|
||||
// Verify that it goes into Login screen
|
||||
await expect(element(by.id('login_screen'))).toBeVisible();
|
||||
});
|
||||
|
||||
it('should match elements on Login screen', async () => {
|
||||
await fulfillSelectServerScreen(serverUrl);
|
||||
|
||||
// Verify basic elements on Login screen
|
||||
await expect(element(by.id('login_screen'))).toBeVisible();
|
||||
|
||||
await expect(element(by.id('username_input'))).toBeVisible();
|
||||
await expect(element(by.id('password_input'))).toBeVisible();
|
||||
|
||||
await expect(element(by.id('signin_button'))).toBeVisible();
|
||||
});
|
||||
|
||||
it('should show error on missing any of the username or password', async () => {
|
||||
await fulfillSelectServerScreen(serverUrl);
|
||||
|
||||
await expect(element(by.id('login_screen'))).toBeVisible();
|
||||
|
||||
// On Login screen, enter invalid username
|
||||
await element(by.id('username_input')).typeText('any');
|
||||
|
||||
// Tap anywhere to hide keyboard
|
||||
await element(by.text('Mattermost')).tap();
|
||||
|
||||
// Tap "Sign in" button
|
||||
await element(by.id('signin_button')).tap();
|
||||
|
||||
// Verify that the error message is shown as expected
|
||||
await expect(element(by.id('error_text'))).toBeVisible();
|
||||
await expect(element(by.id('error_text'))).toHaveText('Please enter your password');
|
||||
|
||||
// Clear input to username and enter invalid password
|
||||
await element(by.id('username_input')).replaceText('');
|
||||
await element(by.id('password_input')).typeText('any');
|
||||
|
||||
// Tap anywhere to hide keyboard
|
||||
await element(by.text('Mattermost')).tap();
|
||||
|
||||
// Tap "Sign in" button
|
||||
await element(by.id('signin_button')).tap();
|
||||
|
||||
// Verify that the error message is shown as expected
|
||||
await expect(element(by.id('error_text'))).toBeVisible();
|
||||
await expect(element(by.id('error_text'))).toHaveText('Please enter your email or username');
|
||||
});
|
||||
|
||||
it('should show error on incorrect credential', async () => {
|
||||
await fulfillSelectServerScreen(serverUrl);
|
||||
|
||||
await expect(element(by.id('login_screen'))).toBeVisible();
|
||||
|
||||
// Enter invalid username
|
||||
await element(by.id('username_input')).replaceText('any');
|
||||
|
||||
// Tap anywhere to hide keyboard
|
||||
await element(by.text('Mattermost')).tap();
|
||||
|
||||
// Enter invalid password
|
||||
await element(by.id('password_input')).replaceText('any');
|
||||
|
||||
// Tap anywhere to hide keyboard
|
||||
await element(by.text('Mattermost')).tap();
|
||||
|
||||
// Tap "Sign in" button
|
||||
await element(by.id('signin_button')).tap();
|
||||
|
||||
// Verify that the error message is shown as expected
|
||||
await expect(element(by.id('error_text'))).toBeVisible();
|
||||
await expect(element(by.id('error_text'))).toHaveText('Enter a valid email or username and/or password.');
|
||||
});
|
||||
|
||||
it('should move to Channel screen on successful login', async () => {
|
||||
await fulfillSelectServerScreen(serverUrl);
|
||||
|
||||
await expect(element(by.id('login_screen'))).toBeVisible();
|
||||
|
||||
// Enter valid username
|
||||
await element(by.id('username_input')).replaceText(user.username);
|
||||
|
||||
// # Tap anywhere to hide keyboard
|
||||
await element(by.text('Mattermost')).tap();
|
||||
|
||||
// Enter valid password
|
||||
await element(by.id('password_input')).replaceText(user.password);
|
||||
|
||||
// # Tap anywhere to hide keyboard
|
||||
await element(by.text('Mattermost')).tap();
|
||||
|
||||
// Tap "Sign in" button
|
||||
await element(by.id('signin_button')).tap();
|
||||
|
||||
// Verify that it goes into Channel screen
|
||||
await expect(element(by.id('channel_screen'))).toBeVisible();
|
||||
});
|
||||
|
||||
it('should directly go into Channel screen on reload', async () => {
|
||||
// On reload and after successful login, verify that it goes straight into Channel screen
|
||||
await expect(element(by.id('channel_screen'))).toBeVisible();
|
||||
});
|
||||
});
|
||||
7681
detox/package-lock.json
generated
Normal file
7681
detox/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
35
detox/package.json
Normal file
35
detox/package.json
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"name": "mattermost-mobile-e2e",
|
||||
"description": "End-to-end testing for Mattermost Mobile",
|
||||
"repository": "git@github.com:mattermost/mattermost-mobile.git",
|
||||
"author": "Mattermost, Inc.",
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-modules-commonjs": "7.10.4",
|
||||
"@babel/plugin-transform-runtime": "7.11.0",
|
||||
"@babel/preset-env": "7.11.0",
|
||||
"axios": "0.20.0",
|
||||
"babel-jest": "26.3.0",
|
||||
"babel-plugin-module-resolver": "4.0.0",
|
||||
"detox": "17.4.4",
|
||||
"jest": "26.4.2",
|
||||
"jest-circus": "26.4.2",
|
||||
"jest-cli": "26.4.2",
|
||||
"merge-deep": "3.0.2",
|
||||
"uuid": "8.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"e2e:android-create-emulator": "avdmanager create avd -n detox_emu_api_30 -k 'system-images;android-30;google_apis;x86' -g google_apis -d 'pixel'",
|
||||
"e2e:android-build": "detox build -c android.emu.debug",
|
||||
"e2e:android-test": "detox test -c android.emu.debug",
|
||||
"e2e:android-build-release": "detox build -c android.emu.release",
|
||||
"e2e:android-test-release": "detox test -c android.emu.release",
|
||||
"e2e:ios-test": "IOS=true detox test -c ios.sim.debug",
|
||||
"e2e:ios-build-release": "detox build -c ios.sim.release",
|
||||
"e2e:ios-test-release": "IOS=true detox test -c ios.sim.release"
|
||||
},
|
||||
"jest": {
|
||||
"transform": {
|
||||
"^.+\\.js?$": "babel-jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -358,8 +358,15 @@ platform :ios do
|
|||
replace_assets
|
||||
|
||||
sh 'cd ../ios && xcodebuild -workspace Mattermost.xcworkspace -scheme Mattermost -arch x86_64 -sdk iphonesimulator -configuration Release -parallelizeTargets -derivedDataPath ../build-ios ENABLE_BITCODE=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ENABLE_BITCODE=NO'
|
||||
sh 'cd ../build-ios/Build/Products/Release-iphonesimulator && zip -r Mattermost-simulator-x86_64.app.zip Mattermost.app'
|
||||
sh 'mv ../build-ios/Build/Products/Release-iphonesimulator/Mattermost-simulator-x86_64.app.zip ../'
|
||||
|
||||
data_path = 'ios'
|
||||
if ENV['CIRCLECI'] == 'true'
|
||||
data_path = 'build-ios'
|
||||
end
|
||||
|
||||
sh "cd ../#{data_path}/Build/Products/Release-iphonesimulator && zip -r Mattermost-simulator-x86_64.app.zip Mattermost.app"
|
||||
sh "mv ../#{data_path}/Build/Products/Release-iphonesimulator/Mattermost-simulator-x86_64.app.zip ../"
|
||||
|
||||
upload_file_to_s3({
|
||||
:os_type => "iOS",
|
||||
:file => "Mattermost-simulator-x86_64.app.zip"
|
||||
|
|
|
|||
454
package-lock.json
generated
454
package-lock.json
generated
|
|
@ -6907,6 +6907,28 @@
|
|||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"bunyan": {
|
||||
"version": "1.8.14",
|
||||
"resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.14.tgz",
|
||||
"integrity": "sha512-LlahJUxXzZLuw/hetUQJmRgZ1LF6+cr5TPpRj6jf327AsiIq2jhYEH4oqUUkVKTor+9w2BT3oxVwhzE5lw9tcg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dtrace-provider": "~0.8",
|
||||
"moment": "^2.19.3",
|
||||
"mv": "~2",
|
||||
"safe-json-stringify": "~1"
|
||||
}
|
||||
},
|
||||
"bunyan-debug-stream": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bunyan-debug-stream/-/bunyan-debug-stream-1.1.1.tgz",
|
||||
"integrity": "sha512-jJbQ1gXUL6vMmZVdbaTFK1v1sGa7axLrSQQwkB6HU9HCPTzsw2HsKcPHm1vgXZlEck/4IvEuRwg/9+083YelCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colors": "^1.0.3",
|
||||
"exception-formatter": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
||||
|
|
@ -7096,6 +7118,29 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"child-process-promise": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/child-process-promise/-/child-process-promise-2.2.1.tgz",
|
||||
"integrity": "sha1-RzChHvYQ+tRQuPIjx50x172tgHQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^4.0.2",
|
||||
"node-version": "^1.0.0",
|
||||
"promise-polyfill": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-spawn": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
|
||||
"integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^4.0.1",
|
||||
"which": "^1.2.9"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "2.1.8",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
|
||||
|
|
@ -7274,6 +7319,12 @@
|
|||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz",
|
||||
"integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw=="
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"dev": true
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
|
|
@ -7756,6 +7807,232 @@
|
|||
"integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
|
||||
"dev": true
|
||||
},
|
||||
"detox": {
|
||||
"version": "17.4.4",
|
||||
"resolved": "https://registry.npmjs.org/detox/-/detox-17.4.4.tgz",
|
||||
"integrity": "sha512-yImcH38K6u3uX0uFCi8bXpHS1rdbFX8TyjbnlHmqAPq7ahC4iZLM5mn/QR8XorlckSdHQAfSt2rBSGvmrw5f+g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bunyan": "^1.8.12",
|
||||
"bunyan-debug-stream": "^1.1.0",
|
||||
"chalk": "^2.4.2",
|
||||
"child-process-promise": "^2.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"fs-extra": "^4.0.2",
|
||||
"funpermaproxy": "^1.0.1",
|
||||
"get-port": "^2.1.0",
|
||||
"ini": "^1.3.4",
|
||||
"lodash": "^4.17.5",
|
||||
"minimist": "^1.2.0",
|
||||
"proper-lockfile": "^3.0.2",
|
||||
"sanitize-filename": "^1.6.1",
|
||||
"shell-utils": "^1.0.9",
|
||||
"signal-exit": "^3.0.3",
|
||||
"tail": "^2.0.0",
|
||||
"telnet-client": "1.2.8",
|
||||
"tempfile": "^2.0.0",
|
||||
"which": "^1.3.1",
|
||||
"ws": "^3.3.1",
|
||||
"yargs": "^13.0.0",
|
||||
"yargs-parser": "^13.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cliui": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
||||
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^3.1.0",
|
||||
"strip-ansi": "^5.2.0",
|
||||
"wrap-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
|
||||
"dev": true
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz",
|
||||
"integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true
|
||||
},
|
||||
"proper-lockfile": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-3.2.0.tgz",
|
||||
"integrity": "sha512-iMghHHXv2bsxl6NchhEaFck8tvX3F9cknEEh1SUpguUOBjN7PAAW9BLzmbc1g/mCD1gY3EE2EABBHPJfFdHFmA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.11",
|
||||
"retry": "^0.12.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||
"dev": true
|
||||
},
|
||||
"retry": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
||||
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"ultron": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
|
||||
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
|
||||
"dev": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
|
||||
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
"string-width": "^3.0.0",
|
||||
"strip-ansi": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
|
||||
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0",
|
||||
"safe-buffer": "~5.1.0",
|
||||
"ultron": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"yargs": {
|
||||
"version": "13.3.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
|
||||
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"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"
|
||||
},
|
||||
"dependencies": {
|
||||
"find-up": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^3.0.0",
|
||||
"path-exists": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
|
||||
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
|
||||
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
|
||||
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"diff-sequences": {
|
||||
"version": "25.2.6",
|
||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz",
|
||||
|
|
@ -7838,6 +8115,16 @@
|
|||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"dtrace-provider": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
|
||||
"integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "^2.14.0"
|
||||
}
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
|
|
@ -8664,6 +8951,15 @@
|
|||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
|
||||
"integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="
|
||||
},
|
||||
"exception-formatter": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/exception-formatter/-/exception-formatter-1.0.7.tgz",
|
||||
"integrity": "sha512-zV45vEsjytJrwfGq6X9qd1Ll56cW4NC2mhCO6lqwMk4ZpA1fZ6C3UiaQM/X7if+7wZFmCgss3ahp9B/uVFuLRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colors": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"exec-sh": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz",
|
||||
|
|
@ -9502,6 +9798,12 @@
|
|||
"integrity": "sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA==",
|
||||
"dev": true
|
||||
},
|
||||
"funpermaproxy": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/funpermaproxy/-/funpermaproxy-1.0.1.tgz",
|
||||
"integrity": "sha512-9pEzs5vnNtR7ZGihly98w/mQ7blsvl68Wj30ZCDAXy7qDN4CWLLjdfjtH/P2m6whsnaJkw15hysCNHMXue+wdA==",
|
||||
"dev": true
|
||||
},
|
||||
"fuse.js": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.1.tgz",
|
||||
|
|
@ -9523,6 +9825,15 @@
|
|||
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"get-port": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-port/-/get-port-2.1.0.tgz",
|
||||
"integrity": "sha1-h4P53OvR7qSVozThpqJR54iHqxo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"get-stdin": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
|
||||
|
|
@ -16517,6 +16828,44 @@
|
|||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
|
||||
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
|
||||
},
|
||||
"mv": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
|
||||
"integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"mkdirp": "~0.5.1",
|
||||
"ncp": "~2.0.0",
|
||||
"rimraf": "~2.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
|
||||
"integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "2 || 3",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
|
||||
"integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"glob": "^6.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.14.1",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
|
||||
|
|
@ -16552,6 +16901,13 @@
|
|||
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
|
||||
"dev": true
|
||||
},
|
||||
"ncp": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
|
||||
"integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"nearley": {
|
||||
"version": "2.19.4",
|
||||
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.19.4.tgz",
|
||||
|
|
@ -16683,6 +17039,12 @@
|
|||
"resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.11.2.tgz",
|
||||
"integrity": "sha512-cowCX+OyzS3tN2i4BMMFxCr/pE6cQlEMTbVCugmos0TNEJQNtcG04tR41CY8lumO1I7F5GFiLaU4WavomJthaA=="
|
||||
},
|
||||
"node-version": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/node-version/-/node-version-1.2.0.tgz",
|
||||
"integrity": "sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||
|
|
@ -17430,6 +17792,21 @@
|
|||
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
||||
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="
|
||||
},
|
||||
"pinkie": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
|
||||
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
|
||||
"dev": true
|
||||
},
|
||||
"pinkie-promise": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
|
||||
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pinkie": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"pirates": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz",
|
||||
|
|
@ -17606,6 +17983,12 @@
|
|||
"asap": "~2.0.3"
|
||||
}
|
||||
},
|
||||
"promise-polyfill": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz",
|
||||
"integrity": "sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=",
|
||||
"dev": true
|
||||
},
|
||||
"prompts": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz",
|
||||
|
|
@ -19335,6 +19718,13 @@
|
|||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"safe-json-stringify": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz",
|
||||
"integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safe-regex": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
|
||||
|
|
@ -19364,6 +19754,15 @@
|
|||
"walker": "~1.0.5"
|
||||
}
|
||||
},
|
||||
"sanitize-filename": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
|
||||
"integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"truncate-utf8-bytes": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
|
|
@ -19552,6 +19951,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"shell-utils": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/shell-utils/-/shell-utils-1.0.10.tgz",
|
||||
"integrity": "sha512-p1xuqhj3jgcXiV8wGoF1eL/NOvapN9tyGDoObqKwvZTUZn7fIzK75swLTEHfGa7sObeN9vxFplHw/zgYUYRTsg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "4.x.x"
|
||||
}
|
||||
},
|
||||
"shellwords": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
|
||||
|
|
@ -20205,12 +20613,27 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"tail": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/tail/-/tail-2.0.4.tgz",
|
||||
"integrity": "sha512-xHkZdNWIzO++g+V/rHGqVoHd2LRxz+8t8bj6FGelfb8FHBjg5yjkX7Su/8sQSBo5alIspYkRp/fU0A2SM5h+5A==",
|
||||
"dev": true
|
||||
},
|
||||
"tapable": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
|
||||
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
|
||||
"dev": true
|
||||
},
|
||||
"telnet-client": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/telnet-client/-/telnet-client-1.2.8.tgz",
|
||||
"integrity": "sha512-W+w4k3QAmULVNhBVT2Fei369kGZCh/TH25M7caJAXW+hLxwoQRuw0di3cX4l0S9fgH3Mvq7u+IFMoBDpEw/eIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bluebird": "^3.5.4"
|
||||
}
|
||||
},
|
||||
"temp": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
|
||||
|
|
@ -20227,6 +20650,22 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"temp-dir": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz",
|
||||
"integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=",
|
||||
"dev": true
|
||||
},
|
||||
"tempfile": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz",
|
||||
"integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"temp-dir": "^1.0.0",
|
||||
"uuid": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"terminal-link": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
|
||||
|
|
@ -20381,6 +20820,15 @@
|
|||
"punycode": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"truncate-utf8-bytes": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||
"integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"utf8-byte-length": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "26.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.3.0.tgz",
|
||||
|
|
@ -20795,6 +21243,12 @@
|
|||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"utf8-byte-length": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
|
||||
"integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=",
|
||||
"dev": true
|
||||
},
|
||||
"util": {
|
||||
"version": "0.12.3",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz",
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@
|
|||
"babel-plugin-module-resolver": "4.0.0",
|
||||
"babel-plugin-transform-remove-console": "6.9.4",
|
||||
"deep-freeze": "0.0.1",
|
||||
"detox": "17.4.4",
|
||||
"enzyme": "3.11.0",
|
||||
"enzyme-adapter-react-16": "1.15.3",
|
||||
"enzyme-to-json": "3.5.0",
|
||||
|
|
@ -160,6 +161,8 @@
|
|||
"test:watch": "jest --watch --runInBand --detectOpenHandles",
|
||||
"test:coverage": "jest --coverage",
|
||||
"updatesnapshot": "jest --updateSnapshot",
|
||||
"mmjstool": "mmjstool"
|
||||
"mmjstool": "mmjstool",
|
||||
"e2e:android": "cd detox && npm run e2e:android-build && npm run e2e:android-test && cd ..",
|
||||
"e2e:ios": "cd detox && npm run e2e:ios-test && cd .."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue