Chore: Migrate E2E tests from JS to TS (#4475)

* Migrate E2E tests from JS to TS

* fixed type mocha into tsconfig

* migration data.cloud and data.docker

* migrate team folder and fix Detox namescpace

* add type TTextMatcher and start the room folder migration

* room / 02 and 03

* room's folder

* onboarding's folder

* assorted/ 01

* assorted/ 02, 03, 04

* assorted/ 05, 06, 07

* folder assorted

* fix lint

* fix device
This commit is contained in:
Reinaldo Neto 2022-09-12 11:51:33 -03:00 committed by Diego Mello
parent 1da2417834
commit fbcee3a976
47 changed files with 508 additions and 256 deletions

View File

@ -156,22 +156,6 @@ module.exports = {
__DEV__: true __DEV__: true
}, },
overrides: [ overrides: [
{
files: ['e2e/**'],
globals: {
by: true,
detox: true,
device: true,
element: true,
expect: true,
waitFor: true
},
rules: {
'import/no-extraneous-dependencies': 0,
'no-await-in-loop': 0,
'no-restricted-syntax': 0
}
},
{ {
files: ['**/*.ts', '**/*.tsx'], files: ['**/*.ts', '**/*.tsx'],
extends: [ extends: [
@ -253,6 +237,23 @@ module.exports = {
} }
} }
} }
},
{
files: ['e2e/**'],
globals: {
by: true,
detox: true,
device: true,
element: true,
waitFor: true
},
rules: {
'import/no-extraneous-dependencies': 0,
'no-await-in-loop': 0,
'no-restricted-syntax': 0,
// TODO: remove this rule when update Detox to 20 and test if the namespace Detox is available
'no-undef': 1
}
} }
] ]
}; };

1
.gitignore vendored
View File

@ -66,5 +66,6 @@ artifacts
e2e/docker/rc_test_env/docker-compose.yml e2e/docker/rc_test_env/docker-compose.yml
e2e/docker/data/db e2e/docker/data/db
e2e/e2e_account.js e2e/e2e_account.js
e2e/e2e_account.ts
*.p8 *.p8

View File

@ -2,5 +2,7 @@
"timeout": 300000, "timeout": 300000,
"recursive": true, "recursive": true,
"bail": true, "bail": true,
"file": "e2e/tests/init.js" "require": ["ts-node/register"],
"file": "e2e/tests/init.ts",
"extension": ["ts"]
} }

View File

@ -26,9 +26,9 @@ Or
### 2. Prepare test data ### 2. Prepare test data
* If you're running your own Rocket.Chat server, ensure it's started (e.g. `meteor npm start` in the server project directory). * If you're running your own Rocket.Chat server, ensure it's started (e.g. `meteor npm start` in the server project directory).
* Edit `e2e/data.js`: * Edit `e2e/data.ts`:
* Set the `server` to the address of the server under test * Set the `server` to the address of the server under test
* Create a file called `e2e_account.js`, in the same folder as `data.js`. Set the `adminUser` and `adminPassword` to an admin user on that environment (or a user with at least `create-user` and `create-c` permissions). The example of how to create this file is on `e2e/e2e_account.example.js` * Create a file called `e2e_account.ts`, in the same folder as `data.ts`. Set the `adminUser` and `adminPassword` to an admin user on that environment (or a user with at least `create-user` and `create-c` permissions). The example of how to create this file is on `e2e/e2e_account.example.ts`
* Working example configs exist in `./e2e/data/`. Setting `FORCE_DEFAULT_DOCKER_DATA` to `1` in the `runTestsInDocker.sh` script will use the example config automatically * Working example configs exist in `./e2e/data/`. Setting `FORCE_DEFAULT_DOCKER_DATA` to `1` in the `runTestsInDocker.sh` script will use the example config automatically
### 3. Running tests ### 3. Running tests

View File

@ -1,8 +1,22 @@
const random = require('./helpers/random'); /* eslint-disable import/extensions, import/no-unresolved */
// eslint-disable-next-line import/no-unresolved, import/extensions import random from './helpers/random';
const account = require('./e2e_account'); // @ts-ignore
import account from './e2e_account';
const value = random(20); export interface IUser {
username: string;
password: string;
email: string;
}
export type TData = typeof data;
export type TDataKeys = keyof TData;
export type TDataUsers = keyof typeof data.users;
export type TDataChannels = keyof typeof data.channels;
export type TDataGroups = keyof typeof data.groups;
export type TDataTeams = keyof typeof data.teams;
const value: string = random(20);
const data = { const data = {
server: 'https://mobile.rocket.chat', server: 'https://mobile.rocket.chat',
...account, ...account,
@ -77,4 +91,5 @@ const data = {
}, },
random: value random: value
}; };
module.exports = data;
export default data;

View File

@ -1,7 +1,21 @@
// eslint-disable-next-line import/no-unresolved, import/extensions /* eslint-disable import/extensions, import/no-unresolved */
const random = require('./helpers/random'); // @ts-ignore
// eslint-disable-next-line import/no-unresolved, import/extensions import random from './helpers/random';
const account = require('./e2e_account'); // @ts-ignore
import account from './e2e_account';
export interface IUser {
username: string;
password: string;
email: string;
}
export type TData = typeof data;
export type TDataKeys = keyof TData;
export type TDataUsers = keyof typeof data.users;
export type TDataChannels = keyof typeof data.channels;
export type TDataGroups = keyof typeof data.groups;
export type TDataTeams = keyof typeof data.teams;
const value = random(20); const value = random(20);
const data = { const data = {
@ -72,4 +86,5 @@ const data = {
}, },
random: value random: value
}; };
module.exports = data;
export default data;

View File

@ -1,5 +1,19 @@
// eslint-disable-next-line import/no-unresolved, import/extensions /* eslint-disable import/extensions, import/no-unresolved */
const random = require('./helpers/random'); // @ts-ignore
import random from './helpers/random';
export interface IUser {
username: string;
password: string;
email: string;
}
export type TData = typeof data;
export type TDataKeys = keyof TData;
export type TDataUsers = keyof typeof data.users;
export type TDataChannels = keyof typeof data.channels;
export type TDataGroups = keyof typeof data.groups;
export type TDataTeams = keyof typeof data.teams;
const value = random(20); const value = random(20);
const data = { const data = {
@ -77,4 +91,5 @@ const data = {
}, },
random: value random: value
}; };
module.exports = data;
export default data;

View File

@ -3,4 +3,4 @@ const account = {
adminPassword: 'Change_here' adminPassword: 'Change_here'
}; };
module.exports = account; export default account;

View File

@ -1,5 +1,10 @@
const { exec } = require('child_process'); import { exec } from 'child_process';
const data = require('../data');
import { by, expect, element } from 'detox';
import data from '../data';
export type TTextMatcher = keyof Pick<Detox.ByFacade, 'text' | 'label'>;
const platformTypes = { const platformTypes = {
android: { android: {
@ -7,18 +12,18 @@ const platformTypes = {
alertButtonType: 'android.widget.Button', alertButtonType: 'android.widget.Button',
scrollViewType: 'android.widget.ScrollView', scrollViewType: 'android.widget.ScrollView',
textInputType: 'android.widget.EditText', textInputType: 'android.widget.EditText',
textMatcher: 'text' textMatcher: 'text' as TTextMatcher
}, },
ios: { ios: {
// iOS types // iOS types
alertButtonType: '_UIAlertControllerActionView', alertButtonType: '_UIAlertControllerActionView',
scrollViewType: 'UIScrollView', scrollViewType: 'UIScrollView',
textInputType: '_UIAlertControllerTextField', textInputType: '_UIAlertControllerTextField',
textMatcher: 'label' textMatcher: 'label' as TTextMatcher
} }
}; };
function sleep(ms) { function sleep(ms: number) {
return new Promise(res => setTimeout(res, ms)); return new Promise(res => setTimeout(res, ms));
} }
@ -34,7 +39,7 @@ async function navigateToWorkspace(server = data.server) {
await expect(element(by.id('workspace-view'))).toBeVisible(); await expect(element(by.id('workspace-view'))).toBeVisible();
} }
async function navigateToLogin(server) { async function navigateToLogin(server?: string) {
await navigateToWorkspace(server); await navigateToWorkspace(server);
await element(by.id('workspace-view-login')).tap(); await element(by.id('workspace-view-login')).tap();
await waitFor(element(by.id('login-view'))) await waitFor(element(by.id('login-view')))
@ -42,7 +47,7 @@ async function navigateToLogin(server) {
.withTimeout(2000); .withTimeout(2000);
} }
async function navigateToRegister(server) { async function navigateToRegister(server?: string) {
await navigateToWorkspace(server); await navigateToWorkspace(server);
await element(by.id('workspace-view-register')).tap(); await element(by.id('workspace-view-register')).tap();
await waitFor(element(by.id('register-view'))) await waitFor(element(by.id('register-view')))
@ -50,7 +55,7 @@ async function navigateToRegister(server) {
.withTimeout(2000); .withTimeout(2000);
} }
async function login(username, password) { async function login(username: string, password: string) {
await waitFor(element(by.id('login-view'))) await waitFor(element(by.id('login-view')))
.toExist() .toExist()
.withTimeout(2000); .withTimeout(2000);
@ -90,23 +95,22 @@ async function logout() {
await expect(element(by.id('new-server-view'))).toBeVisible(); await expect(element(by.id('new-server-view'))).toBeVisible();
} }
async function mockMessage(message, isThread = false) { async function mockMessage(message: string, isThread = false) {
const deviceType = device.getPlatform(); const deviceType = device.getPlatform();
const { textMatcher } = platformTypes[deviceType]; const { textMatcher } = platformTypes[deviceType];
const input = isThread ? 'messagebox-input-thread' : 'messagebox-input'; const input = isThread ? 'messagebox-input-thread' : 'messagebox-input';
await element(by.id(input)).replaceText(`${data.random}${message}`); await element(by.id(input)).replaceText(`${data.random}${message}`);
await sleep(300); await sleep(300);
await element(by.id('messagebox-send-message')).tap(); await element(by.id('messagebox-send-message')).tap();
await sleep(500);
await waitFor(element(by[textMatcher](`${data.random}${message}`))) await waitFor(element(by[textMatcher](`${data.random}${message}`)))
.toExist() .toExist()
.withTimeout(10000); .withTimeout(60000);
await element(by[textMatcher](`${data.random}${message}`)) await element(by[textMatcher](`${data.random}${message}`))
.atIndex(0) .atIndex(0)
.tap(); .tap();
} }
async function starMessage(message) { async function starMessage(message: string) {
const deviceType = device.getPlatform(); const deviceType = device.getPlatform();
const { textMatcher } = platformTypes[deviceType]; const { textMatcher } = platformTypes[deviceType];
const messageLabel = `${data.random}${message}`; const messageLabel = `${data.random}${message}`;
@ -120,7 +124,7 @@ async function starMessage(message) {
.withTimeout(5000); .withTimeout(5000);
} }
async function pinMessage(message) { async function pinMessage(message: string) {
const deviceType = device.getPlatform(); const deviceType = device.getPlatform();
const { textMatcher } = platformTypes[deviceType]; const { textMatcher } = platformTypes[deviceType];
const messageLabel = `${data.random}${message}`; const messageLabel = `${data.random}${message}`;
@ -148,24 +152,25 @@ async function tapBack() {
await element(by.id('header-back')).atIndex(0).tap(); await element(by.id('header-back')).atIndex(0).tap();
} }
async function searchRoom(room) { async function searchRoom(room: string) {
await waitFor(element(by.id('rooms-list-view'))) await waitFor(element(by.id('rooms-list-view')))
.toBeVisible() .toBeVisible()
.withTimeout(30000); .withTimeout(30000);
await element(by.id('rooms-list-view-search')).tap(); await element(by.id('rooms-list-view-search')).tap();
await expect(element(by.id('rooms-list-view-search-input'))).toExist();
await waitFor(element(by.id('rooms-list-view-search-input'))) await waitFor(element(by.id('rooms-list-view-search-input')))
.toExist() .toExist()
.withTimeout(5000); .withTimeout(5000);
await expect(element(by.id('rooms-list-view-search-input'))).toExist();
await sleep(300); await sleep(300);
await element(by.id('rooms-list-view-search-input')).replaceText(room); await element(by.id('rooms-list-view-search-input')).typeText(room);
await sleep(300); await sleep(300);
await waitFor(element(by.id(`rooms-list-view-item-${room}`))) await waitFor(element(by.id(`rooms-list-view-item-${room}`)))
.toBeVisible() .toBeVisible()
.withTimeout(60000); .withTimeout(60000);
} }
async function tryTapping(theElement, timeout, longtap = false) { // eslint-disable-next-line no-undef
async function tryTapping(theElement: Detox.IndexableNativeElement, timeout: number, longtap = false) {
try { try {
if (longtap) { if (longtap) {
await theElement.longPress(); await theElement.longPress();
@ -182,7 +187,7 @@ async function tryTapping(theElement, timeout, longtap = false) {
} }
} }
const checkServer = async server => { const checkServer = async (server: string) => {
const label = `Connected to ${server}`; const label = `Connected to ${server}`;
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'))) await waitFor(element(by.id('sidebar-view')))
@ -200,9 +205,9 @@ const checkServer = async server => {
.withTimeout(10000); .withTimeout(10000);
}; };
function runCommand(command) { function runCommand(command: string) {
return new Promise((resolve, reject) => { return new Promise<void>((resolve, reject) => {
exec(command, (error, stdout, stderr) => { exec(command, (error, _stdout, stderr) => {
if (error) { if (error) {
reject(new Error(`exec error: ${stderr}`)); reject(new Error(`exec error: ${stderr}`));
return; return;
@ -223,7 +228,7 @@ async function prepareAndroid() {
await runCommand('adb shell settings put global animator_duration_scale 0.0'); await runCommand('adb shell settings put global animator_duration_scale 0.0');
} }
module.exports = { export {
navigateToWorkspace, navigateToWorkspace,
navigateToLogin, navigateToLogin,
navigateToRegister, navigateToRegister,

View File

@ -1,7 +1,7 @@
const axios = require('axios').default; import axios from 'axios';
const data = require('../data'); import data, { TDataChannels, TDataGroups, TDataTeams, TDataUsers } from '../data';
const random = require('./random'); import random from './random';
const TEAM_TYPE = { const TEAM_TYPE = {
PUBLIC: 0, PUBLIC: 0,
@ -17,7 +17,7 @@ const rocketchat = axios.create({
} }
}); });
const login = async (username, password) => { const login = async (username: string, password: string) => {
console.log(`Logging in as user ${username}`); console.log(`Logging in as user ${username}`);
const response = await rocketchat.post('login', { const response = await rocketchat.post('login', {
user: username, user: username,
@ -30,7 +30,7 @@ const login = async (username, password) => {
return { authToken, userId }; return { authToken, userId };
}; };
const createUser = async (username, password, name, email) => { const createUser = async (username: string, password: string, name: string, email: string) => {
console.log(`Creating user ${username}`); console.log(`Creating user ${username}`);
try { try {
await rocketchat.post('users.create', { await rocketchat.post('users.create', {
@ -45,7 +45,7 @@ const createUser = async (username, password, name, email) => {
} }
}; };
const createChannelIfNotExists = async channelname => { const createChannelIfNotExists = async (channelname: string) => {
console.log(`Creating public channel ${channelname}`); console.log(`Creating public channel ${channelname}`);
try { try {
const room = await rocketchat.post('channels.create', { const room = await rocketchat.post('channels.create', {
@ -65,7 +65,7 @@ const createChannelIfNotExists = async channelname => {
} }
}; };
const createTeamIfNotExists = async teamname => { const createTeamIfNotExists = async (teamname: string) => {
console.log(`Creating private team ${teamname}`); console.log(`Creating private team ${teamname}`);
try { try {
await rocketchat.post('teams.create', { await rocketchat.post('teams.create', {
@ -84,7 +84,7 @@ const createTeamIfNotExists = async teamname => {
} }
}; };
const createGroupIfNotExists = async groupname => { const createGroupIfNotExists = async (groupname: string) => {
console.log(`Creating private group ${groupname}`); console.log(`Creating private group ${groupname}`);
try { try {
await rocketchat.post('groups.create', { await rocketchat.post('groups.create', {
@ -102,7 +102,7 @@ const createGroupIfNotExists = async groupname => {
} }
}; };
const changeChannelJoinCode = async (roomId, joinCode) => { const changeChannelJoinCode = async (roomId: string, joinCode: string) => {
console.log(`Changing channel Join Code ${roomId}`); console.log(`Changing channel Join Code ${roomId}`);
try { try {
await rocketchat.post('method.call/saveRoomSettings', { await rocketchat.post('method.call/saveRoomSettings', {
@ -119,7 +119,7 @@ const changeChannelJoinCode = async (roomId, joinCode) => {
} }
}; };
const sendMessage = async (user, channel, msg, tmid) => { const sendMessage = async (user: { username: string; password: string }, channel: string, msg: string, tmid?: string) => {
console.log(`Sending message to ${channel}`); console.log(`Sending message to ${channel}`);
try { try {
await login(user.username, user.password); await login(user.username, user.password);
@ -136,21 +136,21 @@ const setup = async () => {
for (const userKey in data.users) { for (const userKey in data.users) {
if (Object.prototype.hasOwnProperty.call(data.users, userKey)) { if (Object.prototype.hasOwnProperty.call(data.users, userKey)) {
const user = data.users[userKey]; const user = data.users[userKey as TDataUsers];
await createUser(user.username, user.password, user.username, user.email); await createUser(user.username, user.password, user.username, user.email);
} }
} }
for (const channelKey in data.channels) { for (const channelKey in data.channels) {
if (Object.prototype.hasOwnProperty.call(data.channels, channelKey)) { if (Object.prototype.hasOwnProperty.call(data.channels, channelKey)) {
const channel = data.channels[channelKey]; const channel = data.channels[channelKey as TDataChannels];
const { const {
data: { data: {
channel: { _id } channel: { _id }
} }
} = await createChannelIfNotExists(channel.name); } = await createChannelIfNotExists(channel.name);
if (channel.joinCode) { if ('joinCode' in channel) {
await changeChannelJoinCode(_id, channel.joinCode); await changeChannelJoinCode(_id, channel.joinCode);
} }
} }
@ -160,33 +160,27 @@ const setup = async () => {
for (const groupKey in data.groups) { for (const groupKey in data.groups) {
if (Object.prototype.hasOwnProperty.call(data.groups, groupKey)) { if (Object.prototype.hasOwnProperty.call(data.groups, groupKey)) {
const group = data.groups[groupKey]; const group = data.groups[groupKey as TDataGroups];
await createGroupIfNotExists(group.name); await createGroupIfNotExists(group.name);
} }
} }
for (const teamKey in data.teams) { for (const teamKey in data.teams) {
if (Object.prototype.hasOwnProperty.call(data.teams, teamKey)) { if (Object.prototype.hasOwnProperty.call(data.teams, teamKey)) {
const team = data.teams[teamKey]; const team = data.teams[teamKey as TDataTeams];
await createTeamIfNotExists(team.name); await createTeamIfNotExists(team.name);
} }
} }
}; };
const get = endpoint => { const get = (endpoint: string) => {
console.log(`GET /${endpoint}`); console.log(`GET /${endpoint}`);
return rocketchat.get(endpoint); return rocketchat.get(endpoint);
}; };
const post = (endpoint, body) => { const post = (endpoint: string, body: any) => {
console.log(`POST /${endpoint} ${JSON.stringify(body)}`); console.log(`POST /${endpoint} ${JSON.stringify(body)}`);
return rocketchat.post(endpoint, body); return rocketchat.post(endpoint, body);
}; };
module.exports = { export { setup, sendMessage, get, post, login };
setup,
sendMessage,
get,
post,
login
};

View File

@ -1,4 +1,4 @@
function random(length) { function random(length: number) {
let text = ''; let text = '';
const possible = 'abcdefghijklmnopqrstuvwxyz'; const possible = 'abcdefghijklmnopqrstuvwxyz';
for (let i = 0; i < length; i += 1) { for (let i = 0; i < length; i += 1) {
@ -6,4 +6,5 @@ function random(length) {
} }
return text; return text;
} }
module.exports = random;
export default random;

View File

@ -1,11 +1,22 @@
const { navigateToLogin, login, sleep, tapBack, mockMessage, searchRoom, logout, platformTypes } = require('../../helpers/app'); import { expect } from 'detox';
const data = require('../../data'); import {
navigateToLogin,
login,
sleep,
tapBack,
mockMessage,
searchRoom,
logout,
platformTypes,
TTextMatcher
} from '../../helpers/app';
import data from '../../data';
const testuser = data.users.regular; const testuser = data.users.regular;
const otheruser = data.users.alternate; const otheruser = data.users.alternate;
const checkServer = async server => { const checkServer = async (server: string) => {
const label = `Connected to ${server}`; const label = `Connected to ${server}`;
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'))) await waitFor(element(by.id('sidebar-view')))
@ -24,7 +35,7 @@ const checkBanner = async () => {
.withTimeout(10000); .withTimeout(10000);
}; };
async function navigateToRoom(roomName) { async function navigateToRoom(roomName: string) {
await searchRoom(`${roomName}`); await searchRoom(`${roomName}`);
await element(by.id(`rooms-list-view-item-${roomName}`)).tap(); await element(by.id(`rooms-list-view-item-${roomName}`)).tap();
await waitFor(element(by.id('room-view'))) await waitFor(element(by.id('room-view')))
@ -60,13 +71,12 @@ async function navigateSecurityPrivacy() {
describe('E2E Encryption', () => { describe('E2E Encryption', () => {
const room = `encrypted${data.random}`; const room = `encrypted${data.random}`;
const newPassword = 'abc'; const newPassword = 'abc';
let alertButtonType; let alertButtonType: string;
let scrollViewType; let textMatcher: TTextMatcher;
let textMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, scrollViewType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin(); await navigateToLogin();
await login(testuser.username, testuser.password); await login(testuser.username, testuser.password);
}); });
@ -293,14 +303,16 @@ describe('E2E Encryption', () => {
await element(by[textMatcher]('Yes, reset it').and(by.type(alertButtonType))).tap(); await element(by[textMatcher]('Yes, reset it').and(by.type(alertButtonType))).tap();
await sleep(2000); await sleep(2000);
await waitFor(element(by[textMatcher]("You've been logged out by the server. Please log in again."))) // FIXME: The app isn't showing this alert anymore
.toExist() // await waitFor(element(by[textMatcher]("You've been logged out by the server. Please log in again.")))
.withTimeout(20000); // .toExist()
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); // .withTimeout(20000);
await waitFor(element(by.id('workspace-view'))) // await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
.toBeVisible() // await waitFor(element(by.id('workspace-view')))
.withTimeout(10000); // .toBeVisible()
await element(by.id('workspace-view-login')).tap(); // .withTimeout(10000);
// await element(by.id('workspace-view-login')).tap();
await navigateToLogin();
await waitFor(element(by.id('login-view'))) await waitFor(element(by.id('login-view')))
.toBeVisible() .toBeVisible()
.withTimeout(2000); .withTimeout(2000);
@ -308,7 +320,7 @@ describe('E2E Encryption', () => {
// TODO: assert 'Save Your Encryption Password' // TODO: assert 'Save Your Encryption Password'
await waitFor(element(by.id('listheader-encryption'))) await waitFor(element(by.id('listheader-encryption')))
.toBeVisible() .toBeVisible()
.withTimeout(2000); .withTimeout(5000);
}); });
}); });
}); });

View File

@ -1,14 +1,15 @@
// const OTP = require('otp.js'); // const OTP = require('otp.js');
// const GA = OTP.googleAuthenticator; // const GA = OTP.googleAuthenticator;
import { expect } from 'detox';
const { navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes } = require('../../helpers/app'); import { navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes, TTextMatcher, sleep } from '../../helpers/app';
const data = require('../../data'); import data from '../../data';
const testuser = data.users.regular; const testuser = data.users.regular;
const otheruser = data.users.alternate; const otheruser = data.users.alternate;
describe('Broadcast room', () => { describe('Broadcast room', () => {
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ textMatcher } = platformTypes[device.getPlatform()]); ({ textMatcher } = platformTypes[device.getPlatform()]);
@ -49,6 +50,7 @@ describe('Broadcast room', () => {
await waitFor(element(by.id(`room-view-title-broadcast${data.random}`))) await waitFor(element(by.id(`room-view-title-broadcast${data.random}`)))
.toBeVisible() .toBeVisible()
.withTimeout(60000); .withTimeout(60000);
await sleep(500);
await element(by.id('room-header')).tap(); await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view'))) await waitFor(element(by.id('room-actions-view')))
.toBeVisible() .toBeVisible()
@ -123,6 +125,17 @@ describe('Broadcast room', () => {
}); });
it('should reply broadcasted message', async () => { it('should reply broadcasted message', async () => {
await mockMessage('broadcastreply'); // Server is adding 2 spaces in front a reply message
await element(by.id('messagebox-input')).replaceText(`${data.random}broadcastreply`);
await sleep(300);
await element(by.id('messagebox-send-message')).tap();
await waitFor(element(by[textMatcher](`${data.random}message`)))
.toExist()
.withTimeout(10000);
await element(by[textMatcher](`${data.random}message`)).tap();
await sleep(600);
await waitFor(element(by.id(`room-view-title-broadcast${data.random}`)))
.toBeVisible()
.withTimeout(10000);
}); });
}); });

View File

@ -1,5 +1,7 @@
const { navigateToLogin, login, sleep, platformTypes } = require('../../helpers/app'); import { expect } from 'detox';
const data = require('../../data');
import { navigateToLogin, login, sleep, platformTypes, TTextMatcher } from '../../helpers/app';
import data from '../../data';
const profileChangeUser = data.users.profileChanges; const profileChangeUser = data.users.profileChanges;
@ -14,14 +16,12 @@ async function waitForToast() {
} }
describe('Profile screen', () => { describe('Profile screen', () => {
let textInputType; let scrollViewType: string;
let scrollViewType; let textMatcher: TTextMatcher;
let alertButtonType;
let textMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ textInputType, scrollViewType, alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ scrollViewType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin(); await navigateToLogin();
await login(profileChangeUser.username, profileChangeUser.password); await login(profileChangeUser.username, profileChangeUser.password);
await element(by.id('rooms-list-view-sidebar')).tap(); await element(by.id('rooms-list-view-sidebar')).tap();
@ -107,6 +107,7 @@ describe('Profile screen', () => {
it('should change email and password', async () => { it('should change email and password', async () => {
await element(by.id('profile-view-email')).replaceText(`mobile+profileChangesNew${data.random}@rocket.chat`); await element(by.id('profile-view-email')).replaceText(`mobile+profileChangesNew${data.random}@rocket.chat`);
await element(by.id('profile-view-new-password')).replaceText(`${profileChangeUser.password}new`); await element(by.id('profile-view-new-password')).replaceText(`${profileChangeUser.password}new`);
await sleep(300);
await element(by.id('profile-view-submit')).tap(); await element(by.id('profile-view-submit')).tap();
await waitFor(element(by.id('profile-view-enter-password-sheet'))) await waitFor(element(by.id('profile-view-enter-password-sheet')))
.toBeVisible() .toBeVisible()

View File

@ -1,12 +1,13 @@
const { navigateToLogin, login, platformTypes } = require('../../helpers/app'); import { expect } from 'detox';
const data = require('../../data'); import { navigateToLogin, login, platformTypes, TTextMatcher } from '../../helpers/app';
import data from '../../data';
const testuser = data.users.regular; const testuser = data.users.regular;
describe('Settings screen', () => { describe('Settings screen', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,5 +1,7 @@
const data = require('../../data'); import { expect } from 'detox';
const { navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes } = require('../../helpers/app');
import data from '../../data';
import { navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes, TTextMatcher } from '../../helpers/app';
const testuser = data.users.regular; const testuser = data.users.regular;
const room = data.channels.detoxpublic.name; const room = data.channels.detoxpublic.name;
@ -20,8 +22,8 @@ async function navigateToRoomActions() {
} }
describe('Join public room', () => { describe('Join public room', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,5 +1,7 @@
const { navigateToLogin, login, sleep } = require('../../helpers/app'); import { expect } from 'detox';
const data = require('../../data');
import { navigateToLogin, login, sleep } from '../../helpers/app';
import data from '../../data';
const testuser = data.users.regular; const testuser = data.users.regular;

View File

@ -1,7 +1,7 @@
const data = require('../../data'); import data from '../../data';
const { navigateToLogin, login, checkServer, platformTypes, sleep } = require('../../helpers/app'); import { navigateToLogin, login, checkServer, sleep } from '../../helpers/app';
const reopenAndCheckServer = async server => { const reopenAndCheckServer = async (server: string) => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true }); await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true });
await waitFor(element(by.id('rooms-list-view'))) await waitFor(element(by.id('rooms-list-view')))
.toBeVisible() .toBeVisible()

View File

@ -1,5 +1,7 @@
const data = require('../../data'); import { expect } from 'detox';
const { navigateToLogin, login, mockMessage, searchRoom, sleep } = require('../../helpers/app');
import data from '../../data';
import { navigateToLogin, login, mockMessage, searchRoom } from '../../helpers/app';
const testuser = data.users.regular; const testuser = data.users.regular;
const room = data.channels.detoxpublicprotected.name; const room = data.channels.detoxpublicprotected.name;

View File

@ -1,9 +1,9 @@
const data = require('../../data'); import data from '../../data';
const { navigateToLogin, login, tapBack, sleep } = require('../../helpers/app'); import { navigateToLogin, login, tapBack, sleep } from '../../helpers/app';
const testuser = data.users.regular; const testuser = data.users.regular;
async function navigateToRoom(search) { async function navigateToRoom(search: string) {
await element(by.id('directory-view-search')).replaceText(search); await element(by.id('directory-view-search')).replaceText(search);
await waitFor(element(by.id(`directory-view-item-${search}`))) await waitFor(element(by.id(`directory-view-item-${search}`)))
.toBeVisible() .toBeVisible()

View File

@ -1,9 +1,9 @@
const data = require('../../data'); import data from '../../data';
const { sleep, navigateToLogin, login, checkServer, platformTypes } = require('../../helpers/app'); import { sleep, navigateToLogin, login, checkServer, platformTypes, TTextMatcher } from '../../helpers/app';
describe('Delete server', () => { describe('Delete server', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,31 +1,30 @@
const data = require('../../data'); import data from '../../data';
const { tapBack, checkServer, navigateToRegister, platformTypes } = require('../../helpers/app'); import { tapBack, checkServer, navigateToRegister, platformTypes, TTextMatcher } from '../../helpers/app';
const { get, login, sendMessage } = require('../../helpers/data_setup'); import { get, login, sendMessage } from '../../helpers/data_setup';
const DEEPLINK_METHODS = { AUTH: 'auth', ROOM: 'room' }; const DEEPLINK_METHODS = { AUTH: 'auth', ROOM: 'room' };
let amp = '&'; let amp = '&';
const getDeepLink = (method, server, params) => { const getDeepLink = (method: string, server: string, params?: string) => {
const deeplink = `rocketchat://${method}?host=${server.replace(/^(http:\/\/|https:\/\/)/, '')}${amp}${params}`; const deeplink = `rocketchat://${method}?host=${server.replace(/^(http:\/\/|https:\/\/)/, '')}${amp}${params}`;
console.log(`Deeplinking to: ${deeplink}`); console.log(`Deeplinking to: ${deeplink}`);
return deeplink; return deeplink;
}; };
describe('Deep linking', () => { describe('Deep linking', () => {
let userId; let userId: string;
let authToken; let authToken: string;
let scrollViewType; let scrollViewType: string;
let threadId; let threadId: string;
let textMatcher; let textMatcher: TTextMatcher;
let alertButtonType;
const threadMessage = `to-thread-${data.random}`; const threadMessage = `to-thread-${data.random}`;
before(async () => { before(async () => {
const loginResult = await login(data.users.regular.username, data.users.regular.password); const loginResult = await login(data.users.regular.username, data.users.regular.password);
({ userId, authToken } = loginResult); ({ userId, authToken } = loginResult);
const deviceType = device.getPlatform(); const deviceType = device.getPlatform();
amp = deviceType === 'android' ? '\\&' : '&'; amp = deviceType === 'android' ? '\\&' : '&';
({ scrollViewType, textMatcher, alertButtonType } = platformTypes[deviceType]); ({ scrollViewType, textMatcher } = platformTypes[deviceType]);
// create a thread with api // create a thread with api
const result = await sendMessage(data.users.regular, data.groups.alternate2.name, threadMessage); const result = await sendMessage(data.users.regular, data.groups.alternate2.name, threadMessage);
threadId = result.message._id; threadId = result.message._id;

View File

@ -1,9 +1,11 @@
const { navigateToLogin, login, sleep } = require('../../helpers/app'); import { expect } from 'detox';
const { post } = require('../../helpers/data_setup');
const data = require('../../data'); import { navigateToLogin, login, sleep } from '../../helpers/app';
import { post } from '../../helpers/data_setup';
import data from '../../data';
const testuser = data.users.regular; const testuser = data.users.regular;
const defaultLaunchArgs = { permissions: { notifications: 'YES' } }; const defaultLaunchArgs = { permissions: { notifications: 'YES' } } as Detox.DeviceLaunchAppConfig;
const navToLanguage = async () => { const navToLanguage = async () => {
await waitFor(element(by.id('rooms-list-view'))) await waitFor(element(by.id('rooms-list-view')))

View File

@ -1,5 +1,7 @@
const { login, navigateToLogin, sleep } = require('../../helpers/app'); import { expect } from 'detox';
const data = require('../../data');
import { login, navigateToLogin } from '../../helpers/app';
import data from '../../data';
const goToDisplayPref = async () => { const goToDisplayPref = async () => {
await expect(element(by.id('rooms-list-view-sidebar'))).toBeVisible(); await expect(element(by.id('rooms-list-view-sidebar'))).toBeVisible();

View File

@ -1,11 +1,12 @@
const detox = require('detox'); import detox from 'detox';
const adapter = require('detox/runners/mocha/adapter'); import adapter from 'detox/runners/mocha/adapter';
const config = require('../../package.json').detox; import { detox as config } from '../../package.json';
const { setup } = require('../helpers/data_setup'); import { setup } from '../helpers/data_setup';
const { prepareAndroid } = require('../helpers/app'); import { prepareAndroid } from '../helpers/app';
before(async () => { before(async () => {
// @ts-ignore
await Promise.all([setup(), detox.init(config, { launchApp: false })]); await Promise.all([setup(), detox.init(config, { launchApp: false })]);
await prepareAndroid(); // Make Android less flaky await prepareAndroid(); // Make Android less flaky
// await dataSetup() // await dataSetup()
@ -14,10 +15,12 @@ before(async () => {
}); });
beforeEach(async function () { beforeEach(async function () {
// @ts-ignore
await adapter.beforeEach(this); await adapter.beforeEach(this);
}); });
afterEach(async function () { afterEach(async function () {
// @ts-ignore
await adapter.afterEach(this); await adapter.afterEach(this);
}); });

View File

@ -1,9 +1,11 @@
const data = require('../../data'); import { expect } from 'detox';
const { platformTypes } = require('../../helpers/app');
import { TTextMatcher, platformTypes } from '../../helpers/app';
import data from '../../data';
describe('Onboarding', () => { describe('Onboarding', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,4 +1,6 @@
const { navigateToRegister, navigateToLogin } = require('../../helpers/app'); import { expect } from 'detox';
import { navigateToRegister, navigateToLogin } from '../../helpers/app';
describe('Legal screen', () => { describe('Legal screen', () => {
describe('From Login', () => { describe('From Login', () => {

View File

@ -1,9 +1,11 @@
const data = require('../../data'); import { expect } from 'detox';
const { navigateToLogin, platformTypes } = require('../../helpers/app');
import data from '../../data';
import { navigateToLogin, platformTypes, TTextMatcher } from '../../helpers/app';
describe('Forgot password screen', () => { describe('Forgot password screen', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,9 +1,11 @@
const { navigateToRegister, platformTypes } = require('../../helpers/app'); import { expect } from 'detox';
const data = require('../../data');
import { navigateToRegister, platformTypes, TTextMatcher } from '../../helpers/app';
import data from '../../data';
describe('Create user screen', () => { describe('Create user screen', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
@ -50,17 +52,18 @@ describe('Create user screen', () => {
// await element(by.id('register-view-submit')).tap(); // await element(by.id('register-view-submit')).tap();
// }); // });
it('should submit email already taken and raise error', async () => { // TODO: When server handle two errors in sequence, the server return Too many requests and force to wait for some time.
await element(by.id('register-view-name')).replaceText(data.registeringUser.username); // it('should submit email already taken and raise error', async () => {
await element(by.id('register-view-username')).replaceText(data.registeringUser.username); // await element(by.id('register-view-name')).replaceText(data.registeringUser.username);
await element(by.id('register-view-email')).replaceText(data.users.existing.email); // await element(by.id('register-view-username')).replaceText(data.registeringUser.username);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password); // await element(by.id('register-view-email')).replaceText(data.users.existing.email);
await element(by.id('register-view-submit')).tap(); // await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
await waitFor(element(by[textMatcher]('Email already exists. [403]')).atIndex(0)) // await element(by.id('register-view-submit')).tap();
.toExist() // await waitFor(element(by[textMatcher]('Email already exists. [403]')).atIndex(0))
.withTimeout(10000); // .toExist()
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap(); // .withTimeout(10000);
}); // await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
// });
it('should submit username already taken and raise error', async () => { it('should submit username already taken and raise error', async () => {
await element(by.id('register-view-name')).replaceText(data.registeringUser.username); await element(by.id('register-view-name')).replaceText(data.registeringUser.username);

View File

@ -1,9 +1,11 @@
const { navigateToLogin, tapBack, platformTypes, navigateToWorkspace, login } = require('../../helpers/app'); import { expect } from 'detox';
const data = require('../../data');
import { navigateToLogin, tapBack, platformTypes, navigateToWorkspace, login, TTextMatcher } from '../../helpers/app';
import data from '../../data';
describe('Login screen', () => { describe('Login screen', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,5 +1,7 @@
const { login, navigateToLogin, logout, tapBack, searchRoom } = require('../../helpers/app'); import { expect } from 'detox';
const data = require('../../data');
import { login, navigateToLogin, logout, tapBack, searchRoom } from '../../helpers/app';
import data from '../../data';
describe('Rooms list screen', () => { describe('Rooms list screen', () => {
before(async () => { before(async () => {

View File

@ -1,5 +1,7 @@
const { login, navigateToLogin, logout, tapBack } = require('../../helpers/app'); import { expect } from 'detox';
const data = require('../../data');
import { login, navigateToLogin, logout, tapBack } from '../../helpers/app';
import data from '../../data';
describe('Server history', () => { describe('Server history', () => {
before(async () => { before(async () => {

View File

@ -1,9 +1,11 @@
const data = require('../../data'); import { expect } from 'detox';
const { tapBack, navigateToLogin, login, tryTapping, platformTypes } = require('../../helpers/app');
import data from '../../data';
import { tapBack, navigateToLogin, login, tryTapping, platformTypes, TTextMatcher } from '../../helpers/app';
describe('Create room screen', () => { describe('Create room screen', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,5 +1,7 @@
const data = require('../../data'); import { expect } from 'detox';
const {
import data from '../../data';
import {
navigateToLogin, navigateToLogin,
login, login,
mockMessage, mockMessage,
@ -10,10 +12,11 @@ const {
pinMessage, pinMessage,
dismissReviewNag, dismissReviewNag,
tryTapping, tryTapping,
platformTypes platformTypes,
} = require('../../helpers/app'); TTextMatcher
} from '../../helpers/app';
async function navigateToRoom(roomName) { async function navigateToRoom(roomName: string) {
await searchRoom(`${roomName}`); await searchRoom(`${roomName}`);
await element(by.id(`rooms-list-view-item-${roomName}`)).tap(); await element(by.id(`rooms-list-view-item-${roomName}`)).tap();
await waitFor(element(by.id('room-view'))) await waitFor(element(by.id('room-view')))
@ -23,8 +26,8 @@ async function navigateToRoom(roomName) {
describe('Room screen', () => { describe('Room screen', () => {
const mainRoom = data.groups.private.name; const mainRoom = data.groups.private.name;
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });

View File

@ -1,5 +1,7 @@
const data = require('../../data'); import { expect } from 'detox';
const {
import data from '../../data';
import {
navigateToLogin, navigateToLogin,
login, login,
tapBack, tapBack,
@ -8,11 +10,13 @@ const {
mockMessage, mockMessage,
starMessage, starMessage,
pinMessage, pinMessage,
platformTypes platformTypes,
} = require('../../helpers/app'); TTextMatcher
} from '../../helpers/app';
const { sendMessage } = require('../../helpers/data_setup'); const { sendMessage } = require('../../helpers/data_setup');
async function navigateToRoomActions(type) { async function navigateToRoomActions(type: string) {
let room; let room;
if (type === 'd') { if (type === 'd') {
room = 'rocket.cat'; room = 'rocket.cat';
@ -53,8 +57,8 @@ async function waitForToast() {
} }
describe('Room actions screen', () => { describe('Room actions screen', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin(); await navigateToLogin();
@ -398,7 +402,7 @@ describe('Room actions screen', () => {
.withTimeout(2000); .withTimeout(2000);
}); });
const openActionSheet = async username => { const openActionSheet = async (username: string) => {
await waitFor(element(by.id(`room-members-view-item-${username}`))) await waitFor(element(by.id(`room-members-view-item-${username}`)))
.toExist() .toExist()
.withTimeout(5000); .withTimeout(5000);
@ -461,7 +465,7 @@ describe('Room actions screen', () => {
.toBeNotVisible() .toBeNotVisible()
.withTimeout(60000); .withTimeout(60000);
await element(by.id('room-members-view-search')).tap(); await element(by.id('room-members-view-search')).tap();
await element(by.id('room-members-view-search')).clearText(''); await element(by.id('room-members-view-search')).clearText();
await waitFor(element(by.id(`room-members-view-item-${user.username}`))) await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist() .toExist()
.withTimeout(60000); .withTimeout(60000);
@ -484,7 +488,7 @@ describe('Room actions screen', () => {
it('should clear search', async () => { it('should clear search', async () => {
await element(by.id('room-members-view-search')).tap(); await element(by.id('room-members-view-search')).tap();
await element(by.id('room-members-view-search')).clearText(''); await element(by.id('room-members-view-search')).clearText();
await waitFor(element(by.id(`room-members-view-item-${user.username}`))) await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist() .toExist()
.withTimeout(60000); .withTimeout(60000);

View File

@ -1,5 +1,7 @@
const { navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes } = require('../../helpers/app'); import { expect } from 'detox';
const data = require('../../data');
import { TTextMatcher, navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes } from '../../helpers/app';
import data from '../../data';
const channel = data.groups.private.name; const channel = data.groups.private.name;
@ -12,7 +14,7 @@ const navigateToRoom = async () => {
}; };
describe('Discussion', () => { describe('Discussion', () => {
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true });
({ textMatcher } = platformTypes[device.getPlatform()]); ({ textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,5 +1,7 @@
const data = require('../../data'); import { expect } from 'detox';
const {
import data from '../../data';
import {
navigateToLogin, navigateToLogin,
login, login,
mockMessage, mockMessage,
@ -7,10 +9,11 @@ const {
sleep, sleep,
searchRoom, searchRoom,
platformTypes, platformTypes,
dismissReviewNag dismissReviewNag,
} = require('../../helpers/app'); TTextMatcher
} from '../../helpers/app';
async function navigateToRoom(roomName) { async function navigateToRoom(roomName: string) {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin(); await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password); await login(data.users.regular.username, data.users.regular.password);
@ -23,7 +26,7 @@ async function navigateToRoom(roomName) {
describe('Threads', () => { describe('Threads', () => {
const mainRoom = data.groups.private.name; const mainRoom = data.groups.private.name;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
({ textMatcher } = platformTypes[device.getPlatform()]); ({ textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,8 +1,8 @@
const data = require('../../data'); import data from '../../data';
const { navigateToLogin, login, platformTypes } = require('../../helpers/app'); import { navigateToLogin, login, platformTypes, TTextMatcher } from '../../helpers/app';
describe('Group DM', () => { describe('Group DM', () => {
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ textMatcher } = platformTypes[device.getPlatform()]); ({ textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,8 +1,10 @@
const data = require('../../data'); import { expect } from 'detox';
const { navigateToLogin, login, searchRoom, sleep, platformTypes } = require('../../helpers/app');
const { sendMessage } = require('../../helpers/data_setup');
async function navigateToRoom(user) { import data from '../../data';
import { navigateToLogin, login, searchRoom, sleep, platformTypes, TTextMatcher } from '../../helpers/app';
import { sendMessage } from '../../helpers/data_setup';
async function navigateToRoom(user: string) {
await searchRoom(`${user}`); await searchRoom(`${user}`);
await element(by.id(`rooms-list-view-item-${user}`)).tap(); await element(by.id(`rooms-list-view-item-${user}`)).tap();
await waitFor(element(by.id('room-view'))) await waitFor(element(by.id('room-view')))
@ -12,7 +14,7 @@ async function navigateToRoom(user) {
describe('Mark as unread', () => { describe('Mark as unread', () => {
const user = data.users.alternate.username; const user = data.users.alternate.username;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });

View File

@ -1,9 +1,11 @@
const data = require('../../data'); import { expect } from 'detox';
const { navigateToLogin, login, tapBack, sleep, searchRoom, platformTypes } = require('../../helpers/app');
import data from '../../data';
import { navigateToLogin, login, tapBack, sleep, searchRoom, platformTypes, TTextMatcher } from '../../helpers/app';
const privateRoomName = data.groups.private.name; const privateRoomName = data.groups.private.name;
async function navigateToRoomInfo(type) { async function navigateToRoomInfo(type: string) {
let room; let room;
if (type === 'd') { if (type === 'd') {
room = 'rocket.cat'; room = 'rocket.cat';
@ -25,7 +27,7 @@ async function navigateToRoomInfo(type) {
.withTimeout(2000); .withTimeout(2000);
} }
async function swipe(direction) { async function swipe(direction: Detox.Direction) {
await element(by.id('room-info-edit-view-list')).swipe(direction, 'fast', 0.8); await element(by.id('room-info-edit-view-list')).swipe(direction, 'fast', 0.8);
} }
@ -34,8 +36,8 @@ async function waitForToast() {
} }
describe('Room info screen', () => { describe('Room info screen', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
@ -174,6 +176,7 @@ describe('Room info screen', () => {
await waitFor(element(by.id('room-info-edit-view'))) await waitFor(element(by.id('room-info-edit-view')))
.toExist() .toExist()
.withTimeout(2000); .withTimeout(2000);
await sleep(2000);
await element(by.id('room-info-edit-view-name')).replaceText(`${privateRoomName}`); await element(by.id('room-info-edit-view-name')).replaceText(`${privateRoomName}`);
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-submit')).tap(); await element(by.id('room-info-edit-view-submit')).tap();
@ -182,6 +185,7 @@ describe('Room info screen', () => {
}); });
it('should reset form', async () => { it('should reset form', async () => {
await sleep(2000);
await element(by.id('room-info-edit-view-name')).replaceText('abc'); await element(by.id('room-info-edit-view-name')).replaceText('abc');
await element(by.id('room-info-edit-view-description')).replaceText('abc'); await element(by.id('room-info-edit-view-description')).replaceText('abc');
await element(by.id('room-info-edit-view-topic')).replaceText('abc'); await element(by.id('room-info-edit-view-topic')).replaceText('abc');
@ -194,6 +198,7 @@ describe('Room info screen', () => {
await swipe('up'); await swipe('up');
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 element(by.id('room-info-edit-view-list')).swipe('down', 'fast', 0.5);
await expect(element(by.id('room-info-edit-view-name'))).toHaveText(privateRoomName); await expect(element(by.id('room-info-edit-view-name'))).toHaveText(privateRoomName);
await expect(element(by.id('room-info-edit-view-description'))).toHaveText(''); await expect(element(by.id('room-info-edit-view-description'))).toHaveText('');
await expect(element(by.id('room-info-edit-view-topic'))).toHaveText(''); await expect(element(by.id('room-info-edit-view-topic'))).toHaveText('');
@ -226,6 +231,7 @@ describe('Room info screen', () => {
await waitFor(element(by.id('room-info-edit-view'))) await waitFor(element(by.id('room-info-edit-view')))
.toExist() .toExist()
.withTimeout(2000); .withTimeout(2000);
await sleep(2000);
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.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-submit')).tap(); await element(by.id('room-info-edit-view-submit')).tap();
@ -245,6 +251,7 @@ describe('Room info screen', () => {
await waitFor(element(by.id('room-info-edit-view'))) await waitFor(element(by.id('room-info-edit-view')))
.toExist() .toExist()
.withTimeout(2000); .withTimeout(2000);
await sleep(2000);
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.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-submit')).tap(); await element(by.id('room-info-edit-view-submit')).tap();
@ -264,6 +271,7 @@ describe('Room info screen', () => {
await waitFor(element(by.id('room-info-edit-view'))) await waitFor(element(by.id('room-info-edit-view')))
.toExist() .toExist()
.withTimeout(2000); .withTimeout(2000);
await sleep(2000);
await element(by.id('room-info-edit-view-password')).replaceText('password'); await element(by.id('room-info-edit-view-password')).replaceText('password');
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5); await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-submit')).tap(); await element(by.id('room-info-edit-view-submit')).tap();

View File

@ -1,7 +1,9 @@
const data = require('../../data'); import { expect } from 'detox';
const { navigateToLogin, tapBack, login, searchRoom, sleep, platformTypes } = require('../../helpers/app');
async function navigateToRoom(roomName) { import data from '../../data';
import { navigateToLogin, tapBack, login, searchRoom, sleep, platformTypes, TTextMatcher } from '../../helpers/app';
async function navigateToRoom(roomName: string) {
await searchRoom(`${roomName}`); await searchRoom(`${roomName}`);
await element(by.id(`rooms-list-view-item-${roomName}`)).tap(); await element(by.id(`rooms-list-view-item-${roomName}`)).tap();
await waitFor(element(by.id('room-view'))) await waitFor(element(by.id('room-view')))
@ -12,8 +14,8 @@ async function navigateToRoom(roomName) {
.withTimeout(5000); .withTimeout(5000);
} }
let textMatcher; let textMatcher: TTextMatcher;
let alertButtonType; let alertButtonType: string;
async function clearCache() { async function clearCache() {
await waitFor(element(by.id('room-view'))) await waitFor(element(by.id('room-view')))
@ -123,6 +125,7 @@ describe('Room', () => {
await waitFor(element(by[textMatcher]('30')).atIndex(1)) await waitFor(element(by[textMatcher]('30')).atIndex(1))
.toExist() .toExist()
.withTimeout(30000); .withTimeout(30000);
await sleep(1000);
await element(by[textMatcher]('30')).atIndex(1).tap(); await element(by[textMatcher]('30')).atIndex(1).tap();
await waitForLoading(); await waitForLoading();
await waitFor(element(by[textMatcher]('30')).atIndex(0)) await waitFor(element(by[textMatcher]('30')).atIndex(0))
@ -194,7 +197,7 @@ describe('Room', () => {
}); });
}); });
const expectThreadMessages = async message => { const expectThreadMessages = async (message: string) => {
await waitFor(element(by.id('room-view-title-thread 1'))) await waitFor(element(by.id('room-view-title-thread 1')))
.toExist() .toExist()
.withTimeout(5000); .withTimeout(5000);

View File

@ -1,11 +1,13 @@
const data = require('../../data'); import { expect } from 'detox';
const { navigateToLogin, login, platformTypes } = require('../../helpers/app');
import data from '../../data';
import { navigateToLogin, login, platformTypes, TTextMatcher } from '../../helpers/app';
const teamName = `team-${data.random}`; const teamName = `team-${data.random}`;
describe('Create team screen', () => { describe('Create team screen', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);

View File

@ -1,7 +1,9 @@
const data = require('../../data'); import { expect } from 'detox';
const { navigateToLogin, login, tapBack, sleep, searchRoom, platformTypes } = require('../../helpers/app');
async function navigateToRoom(roomName) { import data from '../../data';
import { navigateToLogin, login, tapBack, sleep, searchRoom, platformTypes, TTextMatcher } from '../../helpers/app';
async function navigateToRoom(roomName: string) {
await searchRoom(`${roomName}`); await searchRoom(`${roomName}`);
await element(by.id(`rooms-list-view-item-${roomName}`)).tap(); await element(by.id(`rooms-list-view-item-${roomName}`)).tap();
await waitFor(element(by.id('room-view'))) await waitFor(element(by.id('room-view')))
@ -9,7 +11,7 @@ async function navigateToRoom(roomName) {
.withTimeout(5000); .withTimeout(5000);
} }
async function openActionSheet(username) { async function openActionSheet(username: string) {
await waitFor(element(by.id(`room-members-view-item-${username}`))) await waitFor(element(by.id(`room-members-view-item-${username}`)))
.toExist() .toExist()
.withTimeout(5000); .withTimeout(5000);
@ -48,7 +50,13 @@ async function waitForToast() {
await sleep(1000); await sleep(1000);
} }
async function swipeTillVisible(container, find, direction = 'up', delta = 0.3, speed = 'slow') { async function swipeTillVisible(
container: Detox.NativeMatcher,
find: Detox.NativeMatcher,
direction: Detox.Direction = 'up',
delta = 0.3,
speed: Detox.Speed = 'slow'
) {
let found = false; let found = false;
while (!found) { while (!found) {
try { try {
@ -67,8 +75,8 @@ describe('Team', () => {
const user = data.users.alternate; const user = data.users.alternate;
const room = `private${data.random}-channel-team`; const room = `private${data.random}-channel-team`;
const existingRoom = data.groups.alternate.name; const existingRoom = data.groups.alternate.name;
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
@ -388,7 +396,7 @@ describe('Team', () => {
.toBeNotVisible() .toBeNotVisible()
.withTimeout(60000); .withTimeout(60000);
await element(by.id('room-members-view-search')).tap(); await element(by.id('room-members-view-search')).tap();
await element(by.id('room-members-view-search')).clearText(''); await element(by.id('room-members-view-search')).clearText();
await waitFor(element(by.id(`room-members-view-item-${user.username}`))) await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist() .toExist()
.withTimeout(60000); .withTimeout(60000);

View File

@ -1,10 +1,10 @@
const data = require('../../data'); import data from '../../data';
const { navigateToLogin, login, tapBack, searchRoom, sleep, platformTypes } = require('../../helpers/app'); import { navigateToLogin, login, tapBack, searchRoom, platformTypes, TTextMatcher } from '../../helpers/app';
const toBeConverted = `to-be-converted-${data.random}`; const toBeConverted = `to-be-converted-${data.random}`;
const toBeMoved = `to-be-moved-${data.random}`; const toBeMoved = `to-be-moved-${data.random}`;
const createChannel = async room => { const createChannel = async (room: string) => {
await waitFor(element(by.id('rooms-list-view-create-channel'))) await waitFor(element(by.id('rooms-list-view-create-channel')))
.toBeVisible() .toBeVisible()
.withTimeout(5000); .withTimeout(5000);
@ -40,7 +40,7 @@ const createChannel = async room => {
.withTimeout(60000); .withTimeout(60000);
}; };
async function navigateToRoom(room) { async function navigateToRoom(room: string) {
await searchRoom(`${room}`); await searchRoom(`${room}`);
await element(by.id(`rooms-list-view-item-${room}`)).tap(); await element(by.id(`rooms-list-view-item-${room}`)).tap();
await waitFor(element(by.id('room-view'))) await waitFor(element(by.id('room-view')))
@ -48,7 +48,7 @@ async function navigateToRoom(room) {
.withTimeout(5000); .withTimeout(5000);
} }
async function navigateToRoomActions(room) { async function navigateToRoomActions(room: string) {
await navigateToRoom(room); await navigateToRoom(room);
await element(by.id('room-header')).tap(); await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view'))) await waitFor(element(by.id('room-actions-view')))
@ -57,8 +57,8 @@ async function navigateToRoomActions(room) {
} }
describe('Move/Convert Team', () => { describe('Move/Convert Team', () => {
let alertButtonType; let alertButtonType: string;
let textMatcher; let textMatcher: TTextMatcher;
before(async () => { before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]); ({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);

23
e2e/tsconfig.json Normal file
View File

@ -0,0 +1,23 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"jsx": "react",
"target": "es2018",
"module": "commonjs",
"importHelpers": true,
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"types": ["node", "detox", "mocha"]
},
"include": ["./**/*.ts"],
"exclude": ["../node_modules"]
}

View File

@ -165,6 +165,7 @@
"@types/i18n-js": "^3.8.2", "@types/i18n-js": "^3.8.2",
"@types/jest": "^26.0.24", "@types/jest": "^26.0.24",
"@types/lodash": "^4.14.171", "@types/lodash": "^4.14.171",
"@types/mocha": "^9.1.1",
"@types/react": "^17.0.14", "@types/react": "^17.0.14",
"@types/react-native": "0.68.1", "@types/react-native": "0.68.1",
"@types/react-native-background-timer": "^2.0.0", "@types/react-native-background-timer": "^2.0.0",
@ -205,6 +206,7 @@
"react-test-renderer": "17.0.2", "react-test-renderer": "17.0.2",
"reactotron-redux": "3.1.3", "reactotron-redux": "3.1.3",
"reactotron-redux-saga": "4.2.3", "reactotron-redux-saga": "4.2.3",
"ts-node": "^10.9.1",
"typescript": "^4.3.5" "typescript": "^4.3.5"
}, },
"jest": { "jest": {
@ -244,6 +246,7 @@
} }
}, },
"detox": { "detox": {
"test-runner": "mocha",
"runner-config": "e2e/.mocharc.json", "runner-config": "e2e/.mocharc.json",
"specs": "e2e/tests", "specs": "e2e/tests",
"configurations": { "configurations": {

View File

@ -3407,6 +3407,13 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
"@discoveryjs/json-ext@^0.5.3": "@discoveryjs/json-ext@^0.5.3":
version "0.5.7" version "0.5.7"
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
@ -4604,7 +4611,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec"
integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==
"@jridgewell/trace-mapping@^0.3.0": "@jridgewell/trace-mapping@0.3.9", "@jridgewell/trace-mapping@^0.3.0":
version "0.3.9" version "0.3.9"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
@ -5841,6 +5848,26 @@
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
"@tsconfig/node10@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
"@tsconfig/node12@^1.0.7":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
"@tsconfig/node14@^1.0.0":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
"@tsconfig/node16@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
"@types/anymatch@*": "@types/anymatch@*":
version "1.3.1" version "1.3.1"
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a"
@ -6124,6 +6151,11 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/mocha@^9.1.1":
version "9.1.1"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4"
integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==
"@types/node-fetch@^2.5.7": "@types/node-fetch@^2.5.7":
version "2.6.2" version "2.6.2"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da"
@ -6762,6 +6794,11 @@ acorn-jsx@^5.3.1:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
acorn@^6.4.1: acorn@^6.4.1:
version "6.4.1" version "6.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
@ -6772,7 +6809,7 @@ acorn@^7.4.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.5.0: acorn@^8.4.1, acorn@^8.5.0:
version "8.8.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
@ -7049,6 +7086,11 @@ arg@4.1.0:
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0"
integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg== integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
argparse@^1.0.7: argparse@^1.0.7:
version "1.0.10" version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@ -9146,6 +9188,11 @@ create-react-context@0.3.0:
gud "^1.0.0" gud "^1.0.0"
warning "^4.0.3" warning "^4.0.3"
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-fetch@^3.0.4: cross-fetch@^3.0.4:
version "3.1.5" version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
@ -9626,6 +9673,11 @@ diff@5.0.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
diffie-hellman@^5.0.0: diffie-hellman@^5.0.0:
version "5.0.3" version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@ -14279,6 +14331,11 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
dependencies: dependencies:
semver "^6.0.0" semver "^6.0.0"
make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
makeerror@1.0.12: makeerror@1.0.12:
version "1.0.12" version "1.0.12"
resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
@ -19589,6 +19646,25 @@ ts-interface-checker@^0.1.9:
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
ts-node@^10.9.1:
version "10.9.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
dependencies:
"@cspotcode/source-map-support" "^0.8.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
"@tsconfig/node16" "^1.0.2"
acorn "^8.4.1"
acorn-walk "^8.1.1"
arg "^4.1.0"
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
ts-pnp@^1.1.6: ts-pnp@^1.1.6:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
@ -20212,6 +20288,11 @@ uuid@^8.0.0, uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
v8-compile-cache@^2.0.3: v8-compile-cache@^2.0.3:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
@ -20827,6 +20908,11 @@ yargs@^17.3.1:
y18n "^5.0.5" y18n "^5.0.5"
yargs-parser "^21.0.0" yargs-parser "^21.0.0"
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
yocto-queue@^0.1.0: yocto-queue@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"