Re-add previous tests

This commit is contained in:
Diego Mello 2023-01-25 16:03:02 -03:00
parent 2e371131df
commit b332bfecd4
45 changed files with 6646 additions and 27 deletions

101
e2e/data.ts Normal file
View File

@ -0,0 +1,101 @@
/* eslint-disable import/extensions, import/no-unresolved */
import random from './helpers/random';
// @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 TUserRegularChannels = keyof typeof data.userRegularChannels;
export type TDataGroups = keyof typeof data.groups;
export type TDataTeams = keyof typeof data.teams;
const value: string = random(20);
const data = {
server: 'https://mobile.rocket.chat',
...account,
alternateServer: 'https://stable.rocket.chat',
users: {
regular: {
username: `userone${value}`,
password: '123',
email: `mobile+regular${value}@rocket.chat`
},
alternate: {
username: `usertwo${value}`,
password: '123',
email: `mobile+alternate${value}@rocket.chat`,
totpSecret: 'NA4GOMZGHBQSK6KEFRVT62DMGJJGSYZJFZIHO3ZOGVXWCYZ6MMZQ'
},
profileChanges: {
username: `userthree${value}`,
password: '123',
email: `mobile+profileChanges${value}@rocket.chat`
},
existing: {
username: `existinguser${value}`,
password: '123',
email: `mobile+existing${value}@rocket.chat`
}
},
channels: {
detoxpublic: {
name: 'detox-public'
},
detoxpublicprotected: {
name: 'detox-public-protected',
joinCode: '123'
}
},
userRegularChannels: {
detoxpublic: {
name: `detox-public-${value}`
}
},
groups: {
private: {
name: `detox-private-${value}`
},
alternate: {
name: `detox-alternate-${value}`
},
alternate2: {
name: `detox-alternate2-${value}`
}
},
teams: {
private: {
name: `detox-team-${value}`
}
},
registeringUser: {
username: `newuser${value}`,
password: `password${value}`,
email: `mobile+registering${value}@rocket.chat`
},
registeringUser2: {
username: `newusertwo${value}`,
password: `passwordtwo${value}`,
email: `mobile+registeringtwo${value}@rocket.chat`
},
registeringUser3: {
username: `newuserthree${value}`,
password: `passwordthree${value}`,
email: `mobile+registeringthree${value}@rocket.chat`
},
registeringUser4: {
username: `newuserfour${value}`,
password: `passwordfour${value}`,
email: `mobile+registeringfour${value}@rocket.chat`
},
random: value
};
export default data;

96
e2e/data/data.cloud.ts Normal file
View File

@ -0,0 +1,96 @@
/* eslint-disable import/extensions, import/no-unresolved */
// @ts-ignore
import random from './helpers/random';
// @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 TUserRegularChannels = keyof typeof data.userRegularChannels;
export type TDataGroups = keyof typeof data.groups;
export type TDataTeams = keyof typeof data.teams;
const value = random(20);
const data = {
server: 'https://mobile.rocket.chat',
...account,
alternateServer: 'https://stable.rocket.chat',
users: {
regular: {
username: `userone${value}`,
password: '123',
email: `mobile+regular${value}@rocket.chat`
},
alternate: {
username: `usertwo${value}`,
password: '123',
email: `mobile+alternate${value}@rocket.chat`,
totpSecret: 'NA4GOMZGHBQSK6KEFRVT62DMGJJGSYZJFZIHO3ZOGVXWCYZ6MMZQ'
},
profileChanges: {
username: `userthree${value}`,
password: '123',
email: `mobile+profileChanges${value}@rocket.chat`
},
existing: {
username: `existinguser${value}`,
password: '123',
email: `mobile+existing${value}@rocket.chat`
}
},
channels: {
detoxpublic: {
name: 'detox-public'
},
detoxpublicprotected: {
name: 'detox-public-protected',
joinCode: '123'
}
},
userRegularChannels: {
detoxpublic: {
name: `detox-public-${value}`
}
},
groups: {
private: {
name: `detox-private-${value}`
}
},
teams: {
private: {
name: `detox-team-${value}`
}
},
registeringUser: {
username: `newuser${value}`,
password: `password${value}`,
email: `mobile+registering${value}@rocket.chat`
},
registeringUser2: {
username: `newusertwo${value}`,
password: `passwordtwo${value}`,
email: `mobile+registeringtwo${value}@rocket.chat`
},
registeringUser3: {
username: `newuserthree${value}`,
password: `passwordthree${value}`,
email: `mobile+registeringthree${value}@rocket.chat`
},
registeringUser4: {
username: `newuserfour${value}`,
password: `passwordfour${value}`,
email: `mobile+registeringfour${value}@rocket.chat`
},
random: value
};
export default data;

101
e2e/data/data.docker.ts Normal file
View File

@ -0,0 +1,101 @@
/* eslint-disable import/extensions, import/no-unresolved */
// @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 TUserRegularChannels = keyof typeof data.userRegularChannels;
export type TDataGroups = keyof typeof data.groups;
export type TDataTeams = keyof typeof data.teams;
const value = random(20);
const data = {
server: 'http://localhost:3000',
adminUser: 'admin',
adminPassword: 'password',
alternateServer: 'https://stable.rocket.chat',
users: {
regular: {
username: `userone${value}`,
password: '123',
email: `mobile+regular${value}@rocket.chat`
},
alternate: {
username: `usertwo${value}`,
password: '123',
email: `mobile+alternate${value}@rocket.chat`,
totpSecret: 'NA4GOMZGHBQSK6KEFRVT62DMGJJGSYZJFZIHO3ZOGVXWCYZ6MMZQ'
},
profileChanges: {
username: `userthree${value}`,
password: '123',
email: `mobile+profileChanges${value}@rocket.chat`
},
existing: {
username: `existinguser${value}`,
password: '123',
email: `mobile+existing${value}@rocket.chat`
}
},
channels: {
detoxpublic: {
name: 'detox-public'
},
detoxpublicprotected: {
name: 'detox-public-protected',
joinCode: '123'
}
},
userRegularChannels: {
detoxpublic: {
name: `detox-public-${value}`
}
},
groups: {
private: {
name: `detox-private-${value}`
},
alternate: {
name: `detox-alternate-${value}`
},
alternate2: {
name: `detox-alternate2-${value}`
}
},
teams: {
private: {
name: `detox-team-${value}`
}
},
registeringUser: {
username: `newuser${value}`,
password: `password${value}`,
email: `mobile+registering${value}@rocket.chat`
},
registeringUser2: {
username: `newusertwo${value}`,
password: `passwordtwo${value}`,
email: `mobile+registeringtwo${value}@rocket.chat`
},
registeringUser3: {
username: `newuserthree${value}`,
password: `passwordthree${value}`,
email: `mobile+registeringthree${value}@rocket.chat`
},
registeringUser4: {
username: `newuserfour${value}`,
password: `passwordfour${value}`,
email: `mobile+registeringfour${value}@rocket.chat`
},
random: value
};
export default data;

View File

@ -0,0 +1,6 @@
const account = {
adminUser: 'Change_here',
adminPassword: 'Change_here'
};
export default account;

248
e2e/helpers/app.ts Normal file
View File

@ -0,0 +1,248 @@
import { exec } from 'child_process';
import { by, expect, element } from 'detox';
import data from '../data';
export type TTextMatcher = keyof Pick<Detox.ByFacade, 'text' | 'label'>;
const platformTypes = {
android: {
// Android types
alertButtonType: 'android.widget.Button',
scrollViewType: 'android.widget.ScrollView',
textInputType: 'android.widget.EditText',
textMatcher: 'text' as TTextMatcher
},
ios: {
// iOS types
alertButtonType: '_UIAlertControllerActionView',
scrollViewType: 'UIScrollView',
textInputType: '_UIAlertControllerTextField',
textMatcher: 'label' as TTextMatcher
}
};
function sleep(ms: number) {
return new Promise(res => setTimeout(res, ms));
}
async function navigateToWorkspace(server = data.server) {
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(60000);
await element(by.id('new-server-view-input')).replaceText(`${server}`);
await element(by.id('new-server-view-input')).tapReturnKey();
await waitFor(element(by.id('workspace-view')))
.toBeVisible()
.withTimeout(60000);
await expect(element(by.id('workspace-view'))).toBeVisible();
}
async function navigateToLogin(server?: string) {
await navigateToWorkspace(server);
await element(by.id('workspace-view-login')).tap();
await waitFor(element(by.id('login-view')))
.toExist()
.withTimeout(2000);
}
async function navigateToRegister(server?: string) {
await navigateToWorkspace(server);
await element(by.id('workspace-view-register')).tap();
await waitFor(element(by.id('register-view')))
.toExist()
.withTimeout(2000);
}
async function login(username: string, password: string) {
await waitFor(element(by.id('login-view')))
.toExist()
.withTimeout(2000);
await element(by.id('login-view-email')).replaceText(username);
await element(by.id('login-view-password')).replaceText(password);
await element(by.id('login-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(30000);
}
async function logout() {
const deviceType = device.getPlatform();
const { scrollViewType, textMatcher } = platformTypes[deviceType];
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('sidebar-settings')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('sidebar-settings')).tap();
await waitFor(element(by.id('settings-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.type(scrollViewType)).atIndex(1).scrollTo('bottom');
await element(by.id('settings-logout')).tap();
const logoutAlertMessage = 'You will be logged out of this application.';
await waitFor(element(by[textMatcher](logoutAlertMessage)).atIndex(0))
.toExist()
.withTimeout(10000);
await expect(element(by[textMatcher](logoutAlertMessage)).atIndex(0)).toExist();
await element(by[textMatcher]('Logout')).atIndex(0).tap();
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(10000);
await expect(element(by.id('new-server-view'))).toBeVisible();
}
async function mockMessage(message: string, isThread = false) {
const deviceType = device.getPlatform();
const { textMatcher } = platformTypes[deviceType];
const input = isThread ? 'messagebox-input-thread' : 'messagebox-input';
await element(by.id(input)).replaceText(`${data.random}${message}`);
await sleep(300);
await element(by.id('messagebox-send-message')).tap();
await waitFor(element(by[textMatcher](`${data.random}${message}`)))
.toExist()
.withTimeout(60000);
await element(by[textMatcher](`${data.random}${message}`))
.atIndex(0)
.tap();
}
async function starMessage(message: string) {
const deviceType = device.getPlatform();
const { textMatcher } = platformTypes[deviceType];
const messageLabel = `${data.random}${message}`;
await element(by[textMatcher](messageLabel)).atIndex(0).longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by[textMatcher]('Star')).atIndex(0).tap();
await waitFor(element(by.id('action-sheet')))
.not.toExist()
.withTimeout(5000);
}
async function pinMessage(message: string) {
const deviceType = device.getPlatform();
const { textMatcher } = platformTypes[deviceType];
const messageLabel = `${data.random}${message}`;
await waitFor(element(by[textMatcher](messageLabel)).atIndex(0)).toExist();
await element(by[textMatcher](messageLabel)).atIndex(0).longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by[textMatcher]('Pin')).atIndex(0).tap();
await waitFor(element(by.id('action-sheet')))
.not.toExist()
.withTimeout(5000);
}
async function dismissReviewNag() {
const deviceType = device.getPlatform();
const { textMatcher } = platformTypes[deviceType];
await waitFor(element(by[textMatcher]('Are you enjoying this app?')))
.toExist()
.withTimeout(60000);
await element(by[textMatcher]('No')).atIndex(0).tap(); // Tap `no` on ask for review alert
}
async function tapBack() {
await element(by.id('header-back')).atIndex(0).tap();
}
async function searchRoom(room: string) {
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(30000);
await element(by.id('rooms-list-view-search')).tap();
await waitFor(element(by.id('rooms-list-view-search-input')))
.toExist()
.withTimeout(5000);
await expect(element(by.id('rooms-list-view-search-input'))).toExist();
await sleep(300);
await element(by.id('rooms-list-view-search-input')).typeText(room);
await sleep(300);
await waitFor(element(by.id(`rooms-list-view-item-${room}`)))
.toBeVisible()
.withTimeout(60000);
}
// eslint-disable-next-line no-undef
async function tryTapping(theElement: Detox.IndexableNativeElement, timeout: number, longtap = false) {
try {
if (longtap) {
await theElement.longPress();
} else {
await theElement.tap();
}
} catch (e) {
if (timeout <= 0) {
// TODO: Maths. How closely has the timeout been honoured here?
throw e;
}
await sleep(100);
await tryTapping(theElement, timeout - 100);
}
}
const checkServer = async (server: string) => {
const label = `Connected to ${server}`;
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.label(label)))
.toBeVisible()
.withTimeout(10000);
await waitFor(element(by.id('sidebar-close-drawer')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('sidebar-close-drawer')).tap();
await waitFor(element(by.id('sidebar-close-drawer')))
.not.toBeVisible()
.withTimeout(10000);
};
function runCommand(command: string) {
return new Promise<void>((resolve, reject) => {
exec(command, (error, _stdout, stderr) => {
if (error) {
reject(new Error(`exec error: ${stderr}`));
return;
}
resolve();
});
});
}
async function prepareAndroid() {
if (device.getPlatform() !== 'android') {
return;
}
await runCommand('adb shell settings put secure spell_checker_enabled 0');
await runCommand('adb shell settings put secure autofill_service null');
await runCommand('adb shell settings put global window_animation_scale 0.0');
await runCommand('adb shell settings put global transition_animation_scale 0.0');
await runCommand('adb shell settings put global animator_duration_scale 0.0');
}
export {
navigateToWorkspace,
navigateToLogin,
navigateToRegister,
login,
logout,
mockMessage,
starMessage,
pinMessage,
dismissReviewNag,
tapBack,
sleep,
searchRoom,
tryTapping,
checkServer,
platformTypes,
prepareAndroid
};

193
e2e/helpers/data_setup.ts Normal file
View File

@ -0,0 +1,193 @@
import axios from 'axios';
import data, { TDataChannels, TDataGroups, TDataTeams, TDataUsers, TUserRegularChannels } from '../data';
import random from './random';
const TEAM_TYPE = {
PUBLIC: 0,
PRIVATE: 1
};
const { server } = data;
const rocketchat = axios.create({
baseURL: `${server}/api/v1/`,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
});
const login = async (username: string, password: string) => {
console.log(`Logging in as user ${username}`);
const response = await rocketchat.post('login', {
user: username,
password
});
const { userId } = response.data.data;
const { authToken } = response.data.data;
rocketchat.defaults.headers.common['X-User-Id'] = userId;
rocketchat.defaults.headers.common['X-Auth-Token'] = authToken;
return { authToken, userId };
};
const createUser = async (username: string, password: string, name: string, email: string) => {
console.log(`Creating user ${username}`);
try {
await rocketchat.post('users.create', {
username,
password,
name,
email
});
} catch (error) {
console.log(JSON.stringify(error));
throw new Error('Failed to create user');
}
};
const createChannelIfNotExists = async (channelname: string) => {
console.log(`Creating public channel ${channelname}`);
try {
const room = await rocketchat.post('channels.create', {
name: channelname
});
return room;
} catch (createError) {
try {
// Maybe it exists already?
const room = rocketchat.get(`channels.info?roomName=${channelname}`);
return room;
} catch (infoError) {
console.log(JSON.stringify(createError));
console.log(JSON.stringify(infoError));
throw new Error('Failed to find or create public channel');
}
}
};
const createTeamIfNotExists = async (teamname: string) => {
console.log(`Creating private team ${teamname}`);
try {
await rocketchat.post('teams.create', {
name: teamname,
type: TEAM_TYPE.PRIVATE
});
} catch (createError) {
try {
// Maybe it exists already?
await rocketchat.get(`teams.info?teamName=${teamname}`);
} catch (infoError) {
console.log(JSON.stringify(createError));
console.log(JSON.stringify(infoError));
throw new Error('Failed to find or create private team');
}
}
};
const createGroupIfNotExists = async (groupname: string) => {
console.log(`Creating private group ${groupname}`);
try {
await rocketchat.post('groups.create', {
name: groupname
});
} catch (createError) {
try {
// Maybe it exists already?
await rocketchat.get(`groups.info?roomName=${groupname}`);
} catch (infoError) {
console.log(JSON.stringify(createError));
console.log(JSON.stringify(infoError));
throw new Error('Failed to find or create private group');
}
}
};
const changeChannelJoinCode = async (roomId: string, joinCode: string) => {
console.log(`Changing channel Join Code ${roomId}`);
try {
await rocketchat.post('method.call/saveRoomSettings', {
message: JSON.stringify({
msg: 'method',
id: random(10),
method: 'saveRoomSettings',
params: [roomId, { joinCode }]
})
});
} catch (createError) {
console.log(JSON.stringify(createError));
throw new Error('Failed to create protected channel');
}
};
const sendMessage = async (user: { username: string; password: string }, channel: string, msg: string, tmid?: string) => {
console.log(`Sending message to ${channel}`);
try {
await login(user.username, user.password);
const response = await rocketchat.post('chat.postMessage', { channel, msg, tmid });
return response.data;
} catch (infoError) {
console.log(JSON.stringify(infoError));
throw new Error('Failed to find or create private group');
}
};
const setup = async () => {
await login(data.adminUser, data.adminPassword);
for (const userKey in data.users) {
if (Object.prototype.hasOwnProperty.call(data.users, userKey)) {
const user = data.users[userKey as TDataUsers];
await createUser(user.username, user.password, user.username, user.email);
}
}
for (const channelKey in data.channels) {
if (Object.prototype.hasOwnProperty.call(data.channels, channelKey)) {
const channel = data.channels[channelKey as TDataChannels];
const {
data: {
channel: { _id }
}
} = await createChannelIfNotExists(channel.name);
if ('joinCode' in channel) {
await changeChannelJoinCode(_id, channel.joinCode);
}
}
}
await login(data.users.regular.username, data.users.regular.password);
for (const channelKey in data.userRegularChannels) {
if (Object.prototype.hasOwnProperty.call(data.userRegularChannels, channelKey)) {
const channel = data.userRegularChannels[channelKey as TUserRegularChannels];
await createChannelIfNotExists(channel.name);
}
}
for (const groupKey in data.groups) {
if (Object.prototype.hasOwnProperty.call(data.groups, groupKey)) {
const group = data.groups[groupKey as TDataGroups];
await createGroupIfNotExists(group.name);
}
}
for (const teamKey in data.teams) {
if (Object.prototype.hasOwnProperty.call(data.teams, teamKey)) {
const team = data.teams[teamKey as TDataTeams];
await createTeamIfNotExists(team.name);
}
}
};
const get = (endpoint: string) => {
console.log(`GET /${endpoint}`);
return rocketchat.get(endpoint);
};
const post = (endpoint: string, body: any) => {
console.log(`POST /${endpoint} ${JSON.stringify(body)}`);
return rocketchat.post(endpoint, body);
};
export { setup, sendMessage, get, post, login };

10
e2e/helpers/random.ts Normal file
View File

@ -0,0 +1,10 @@
function random(length: number) {
let text = '';
const possible = 'abcdefghijklmnopqrstuvwxyz';
for (let i = 0; i < length; i += 1) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
export default random;

View File

@ -1,7 +1,8 @@
/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
rootDir: '..',
testMatch: ['<rootDir>/e2e/**/*.test.js'],
setupFilesAfterEnv: ['<rootDir>/e2e/tests/init.ts'],
testMatch: ['<rootDir>/e2e/tests/**/*.spec.ts'],
testTimeout: 120000,
maxWorkers: 1,
globalSetup: 'detox/runners/jest/globalSetup',

View File

@ -1,26 +0,0 @@
describe('Example', () => {
beforeAll(async () => {
await device.launchApp();
});
// beforeEach(async () => {
// await device.reloadReactNative();
// });
it('should have welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
});
it('should have onboarding screen', async () => {
await expect(element(by.id('new-server-view'))).toBeVisible();
});
// it('should show hello screen after tap', async () => {
// await element(by.id('hello_button')).tap();
// await expect(element(by.text('Hello!!!'))).toBeVisible();
// });
// it('should show world screen after tap', async () => {
// await element(by.id('world_button')).tap();
// await expect(element(by.text('World!!!'))).toBeVisible();
// });
});

View File

@ -0,0 +1,401 @@
import { expect } from 'detox';
import {
navigateToLogin,
login,
sleep,
tapBack,
mockMessage,
searchRoom,
logout,
platformTypes,
TTextMatcher
} from '../../helpers/app';
import data from '../../data';
const testuser = data.users.regular;
const otheruser = data.users.alternate;
const checkServer = async (server: string) => {
const label = `Connected to ${server}`;
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.label(label)))
.toBeVisible()
.withTimeout(60000);
await element(by.id('sidebar-close-drawer')).tap();
};
const checkBanner = async () => {
// TODO: Assert 'Save Your Encryption Password'
await waitFor(element(by.id('listheader-encryption')))
.toExist()
.withTimeout(10000);
};
async function navigateToRoom(roomName: string) {
await searchRoom(`${roomName}`);
await element(by.id(`rooms-list-view-item-${roomName}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
}
async function waitForToast() {
await sleep(300);
}
async function navigateSecurityPrivacy() {
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('sidebar-settings')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('sidebar-settings')).tap();
await waitFor(element(by.id('settings-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('settings-view-security-privacy')).tap();
await waitFor(element(by.id('security-privacy-view')))
.toBeVisible()
.withTimeout(2000);
}
describe('E2E Encryption', () => {
const room = `encrypted${data.random}`;
const newPassword = 'abc';
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(testuser.username, testuser.password);
});
describe('Banner', () => {
describe('Render', () => {
it('should have encryption badge', async () => {
await checkBanner();
});
});
describe('Usage', () => {
it('should tap encryption badge and open save password modal', async () => {
await element(by.id('listheader-encryption')).tap();
await waitFor(element(by.id('e2e-save-password-view')))
.toBeVisible()
.withTimeout(2000);
});
it('should tap "How it works" and navigate', async () => {
await element(by.id('e2e-save-password-view-how-it-works').and(by.label('How It Works'))).tap();
await waitFor(element(by.id('e2e-how-it-works-view')))
.toBeVisible()
.withTimeout(2000);
await tapBack();
});
it('should tap "Save my password" and close modal', async () => {
await element(by.id('e2e-save-password-view-saved-password').and(by.label('I Saved My E2E Password'))).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
});
it('should create encrypted room', async () => {
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('select-users-view-search')).replaceText(otheruser.username);
await waitFor(element(by.id(`select-users-view-item-${otheruser.username}`)))
.toBeVisible()
.withTimeout(60000);
await element(by.id(`select-users-view-item-${otheruser.username}`)).tap();
await waitFor(element(by.id(`selected-user-${otheruser.username}`)))
.toBeVisible()
.withTimeout(5000);
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view')))
.toExist()
.withTimeout(5000);
await element(by.id('create-channel-name')).replaceText(room);
await element(by.id('create-channel-encrypted')).longPress();
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(60000);
await waitFor(element(by.id(`room-view-title-${room}`)))
.toBeVisible()
.withTimeout(60000);
});
it('should send message and be able to read it', async () => {
await mockMessage('message');
await tapBack();
});
});
});
describe('Security and Privacy', () => {
it('should navigate to security privacy', async () => {
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('sidebar-settings')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('sidebar-settings')).tap();
await waitFor(element(by.id('settings-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('settings-view-security-privacy')).tap();
await waitFor(element(by.id('security-privacy-view')))
.toBeVisible()
.withTimeout(2000);
});
it('render', async () => {
await expect(element(by.id('security-privacy-view-e2e-encryption'))).toExist();
await expect(element(by.id('security-privacy-view-screen-lock'))).toExist();
await expect(element(by.id('security-privacy-view-analytics-events'))).toExist();
await expect(element(by.id('security-privacy-view-crash-report'))).toExist();
});
});
describe('E2E Encryption Security', () => {
it('should navigate to e2e encryption security', async () => {
await element(by.id('security-privacy-view-e2e-encryption')).tap();
await waitFor(element(by.id('e2e-encryption-security-view')))
.toBeVisible()
.withTimeout(2000);
});
describe('Render', () => {
it('should have items', async () => {
await waitFor(element(by.id('e2e-encryption-security-view')))
.toBeVisible()
.withTimeout(2000);
await expect(element(by.id('e2e-encryption-security-view-password'))).toExist();
await expect(element(by.id('e2e-encryption-security-view-change-password').and(by.label('Save Changes')))).toExist();
await expect(element(by.id('e2e-encryption-security-view-reset-key').and(by.label('Reset E2E Key')))).toExist();
});
});
describe('Change password', () => {
it('should change password', async () => {
await element(by.id('e2e-encryption-security-view-password')).typeText(newPassword);
await element(by.id('e2e-encryption-security-view-change-password')).tap();
await waitFor(element(by[textMatcher]('Are you sure?')))
.toExist()
.withTimeout(2000);
await expect(element(by[textMatcher]("Make sure you've saved it carefully somewhere else."))).toExist();
await element(by[textMatcher]('Yes, change it')).atIndex(0).tap();
await waitForToast();
});
it('should navigate to the room and messages should remain decrypted', async () => {
await waitFor(element(by.id('e2e-encryption-security-view')))
.toBeVisible()
.withTimeout(2000);
await tapBack();
await waitFor(element(by.id('security-privacy-view')))
.toBeVisible()
.withTimeout(2000);
await tapBack();
await waitFor(element(by.id('settings-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('settings-view-drawer')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('sidebar-chats')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
await navigateToRoom(room);
await waitFor(element(by[textMatcher](`${data.random}message`)).atIndex(0))
.toExist()
.withTimeout(2000);
});
it('should logout, login and messages should be encrypted', async () => {
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
await logout();
await navigateToLogin();
await login(testuser.username, testuser.password);
await navigateToRoom(room);
await waitFor(element(by[textMatcher](`${data.random}message`)).atIndex(0))
.not.toExist()
.withTimeout(2000);
await expect(element(by.label('Encrypted message')).atIndex(0)).toExist();
});
it('should enter new e2e password and messages should be decrypted', async () => {
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
// TODO: assert 'Enter Your E2E Password'
await waitFor(element(by.id('listheader-encryption')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('listheader-encryption')).tap();
await waitFor(element(by.id('e2e-enter-your-password-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('e2e-enter-your-password-view-password')).typeText(newPassword);
await element(by.id('e2e-enter-your-password-view-confirm')).tap();
await waitFor(element(by.id('listheader-encryption')))
.not.toExist()
.withTimeout(10000);
await navigateToRoom(room);
await waitFor(element(by[textMatcher](`${data.random}message`)).atIndex(0))
.toExist()
.withTimeout(2000);
});
});
describe('Reset E2E key', () => {
beforeAll(async () => {
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
});
it('should reset e2e key', async () => {
// FIXME: too flaky on Android for now... let's fix it later
// It's also flaky on iOS, but it works from time to time
if (device.getPlatform() === 'android') {
return;
}
await navigateSecurityPrivacy();
await element(by.id('security-privacy-view-e2e-encryption')).tap();
await waitFor(element(by.id('e2e-encryption-security-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('e2e-encryption-security-view-reset-key').and(by.label('Reset E2E Key'))).tap();
await waitFor(element(by[textMatcher]('Are you sure?')))
.toExist()
.withTimeout(2000);
await expect(element(by[textMatcher]("You're going to be logged out."))).toExist();
await element(by[textMatcher]('Yes, reset it').and(by.type(alertButtonType))).tap();
await sleep(2000);
// FIXME: The app isn't showing this alert anymore
// await waitFor(element(by[textMatcher]("You've been logged out by the server. Please log in again.")))
// .toExist()
// .withTimeout(20000);
// await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
// await waitFor(element(by.id('workspace-view')))
// .toBeVisible()
// .withTimeout(10000);
// await element(by.id('workspace-view-login')).tap();
await navigateToLogin();
await waitFor(element(by.id('login-view')))
.toBeVisible()
.withTimeout(2000);
await login(testuser.username, testuser.password);
// TODO: assert 'Save Your Encryption Password'
await waitFor(element(by.id('listheader-encryption')))
.toBeVisible()
.withTimeout(5000);
});
});
});
describe('Persist Banner', () => {
beforeAll(async () => {
// reinstall the app because of one flaky test above
if (device.getPlatform() === 'android') {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(testuser.username, testuser.password);
}
});
it('check save banner', async () => {
await checkServer(data.server);
await checkBanner();
});
it('should add server and create new user', async () => {
await sleep(5000);
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('rooms-list-header-server-add')).tap();
// TODO: refactor
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(60000);
await element(by.id('new-server-view-input')).typeText(`${data.alternateServer}`);
await element(by.id('new-server-view-input')).tapReturnKey();
await waitFor(element(by.id('workspace-view')))
.toBeVisible()
.withTimeout(60000);
await element(by.id('workspace-view-register')).tap();
await waitFor(element(by.id('register-view')))
.toBeVisible()
.withTimeout(2000);
// Register new user
await element(by.id('register-view-name')).replaceText(data.registeringUser.username);
await element(by.id('register-view-username')).replaceText(data.registeringUser.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(60000);
await checkServer(data.alternateServer);
});
it('should change back', async () => {
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown')))
.toBeVisible()
.withTimeout(5000);
await element(by.id(`rooms-list-header-server-${data.server}`)).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await checkServer(data.server);
await checkBanner();
});
it('should reopen the app and have banner', async () => {
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true
});
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await checkBanner();
});
});
});

View File

@ -0,0 +1,141 @@
// const OTP = require('otp.js');
// const GA = OTP.googleAuthenticator;
import { expect } from 'detox';
import { navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes, TTextMatcher, sleep } from '../../helpers/app';
import data from '../../data';
const testuser = data.users.regular;
const otheruser = data.users.alternate;
describe('Broadcast room', () => {
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(testuser.username, testuser.password);
});
it('should create broadcast room', async () => {
await waitFor(element(by.id('rooms-list-view-create-channel')))
.toExist()
.withTimeout(2000);
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('select-users-view-search')).replaceText(otheruser.username);
await waitFor(element(by.id(`select-users-view-item-${otheruser.username}`)))
.toBeVisible()
.withTimeout(60000);
await element(by.id(`select-users-view-item-${otheruser.username}`)).tap();
await waitFor(element(by.id(`selected-user-${otheruser.username}`)))
.toBeVisible()
.withTimeout(5000);
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view')))
.toExist()
.withTimeout(5000);
await element(by.id('create-channel-name')).replaceText(`broadcast${data.random}`);
await element(by.id('create-channel-broadcast')).longPress(); // https://github.com/facebook/react-native/issues/28032
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(60000);
await waitFor(element(by.id(`room-view-title-broadcast${data.random}`)))
.toBeVisible()
.withTimeout(60000);
await sleep(500);
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('room-actions-info')).tap();
await waitFor(element(by.id('room-info-view')))
.toBeVisible()
.withTimeout(2000);
await expect(element(by.label('Broadcast').withAncestor(by.id('room-info-view-broadcast')))).toBeVisible();
await tapBack();
await waitFor(element(by.id('room-actions-view')))
.toBeVisible()
.withTimeout(2000);
await tapBack();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(2000);
});
it('should send message', async () => {
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
await mockMessage('message');
await tapBack();
});
it('should login as user without write message authorization and enter room', async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(otheruser.username, otheruser.password);
// await waitFor(element(by.id('two-factor'))).toBeVisible().withTimeout(5000);
// await expect(element(by.id('two-factor'))).toBeVisible();
// const code = GA.gen(data.alternateUserTOTPSecret);
// await element(by.id('two-factor-input')).replaceText(code);
// await element(by.id('two-factor-send')).tap();
await searchRoom(`broadcast${data.random}`);
await element(by.id(`rooms-list-view-item-broadcast${data.random}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id(`room-view-title-broadcast${data.random}`)))
.toBeVisible()
.withTimeout(60000);
});
it('should not have messagebox', async () => {
await expect(element(by.id('messagebox'))).toBeNotVisible();
});
it('should be read only', async () => {
await expect(element(by.label('This room is read only'))).toExist();
});
it('should have the message created earlier', async () => {
await waitFor(element(by[textMatcher](`${data.random}message`)))
.toExist()
.withTimeout(60000);
});
it('should have reply button', async () => {
await expect(element(by.id('message-broadcast-reply'))).toBeVisible();
});
it('should tap on reply button and navigate to direct room', async () => {
await element(by.id('message-broadcast-reply')).tap();
await waitFor(element(by.id(`room-view-title-${testuser.username}`)))
.toBeVisible()
.withTimeout(5000);
});
it('should reply broadcasted message', async () => {
// 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

@ -0,0 +1,130 @@
import { expect } from 'detox';
import { navigateToLogin, login, sleep, platformTypes, TTextMatcher } from '../../helpers/app';
import data from '../../data';
const profileChangeUser = data.users.profileChanges;
const scrollDown = 200;
async function waitForToast() {
// await waitFor(element(by.id('toast'))).toBeVisible().withTimeout(1000);
// await expect(element(by.id('toast'))).toBeVisible();
// await waitFor(element(by.id('toast'))).not.toBeNotVisible().withTimeout(1000);
// await expect(element(by.id('toast'))).not.toBeVisible();
await sleep(600);
}
describe('Profile screen', () => {
let scrollViewType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ scrollViewType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(profileChangeUser.username, profileChangeUser.password);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('sidebar-profile')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('sidebar-profile')).tap();
await waitFor(element(by.id('profile-view')))
.toBeVisible()
.withTimeout(2000);
});
describe('Render', () => {
it('should have profile view', async () => {
await expect(element(by.id('profile-view'))).toBeVisible();
});
it('should have avatar', async () => {
await expect(element(by.id('profile-view-avatar')).atIndex(0)).toExist();
});
it('should have name', async () => {
await expect(element(by.id('profile-view-name'))).toExist();
});
it('should have username', async () => {
await expect(element(by.id('profile-view-username'))).toExist();
});
it('should have email', async () => {
await expect(element(by.id('profile-view-email'))).toExist();
});
it('should have new password', async () => {
await expect(element(by.id('profile-view-new-password'))).toExist();
});
it('should have avatar url', async () => {
await expect(element(by.id('profile-view-avatar-url'))).toExist();
});
it('should have reset avatar button', async () => {
await waitFor(element(by.id('profile-view-reset-avatar')))
.toExist()
.whileElement(by.id('profile-view-list'))
.scroll(scrollDown, 'down');
});
it('should have upload avatar button', async () => {
await waitFor(element(by.id('profile-view-upload-avatar')))
.toExist()
.whileElement(by.id('profile-view-list'))
.scroll(scrollDown, 'down');
});
it('should have avatar url button', async () => {
await waitFor(element(by.id('profile-view-avatar-url-button')))
.toExist()
.whileElement(by.id('profile-view-list'))
.scroll(scrollDown, 'down');
});
it('should have submit button', async () => {
await waitFor(element(by.id('profile-view-submit')))
.toExist()
.whileElement(by.id('profile-view-list'))
.scroll(scrollDown, 'down');
});
});
describe('Usage', () => {
it('should change name and username', async () => {
await element(by.id('profile-view-name')).replaceText(`${profileChangeUser.username}new`);
await element(by.id('profile-view-username')).replaceText(`${profileChangeUser.username}new`);
await element(by.type(scrollViewType)).atIndex(1).swipe('up');
await element(by.id('profile-view-submit')).tap();
await waitForToast();
});
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-new-password')).replaceText(`${profileChangeUser.password}new`);
await waitFor(element(by.id('profile-view-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('profile-view-submit')).tap();
await waitFor(element(by.id('profile-view-enter-password-sheet')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('profile-view-enter-password-sheet')).replaceText(`${profileChangeUser.password}`);
await element(by[textMatcher]('Save').withAncestor(by.id('action-sheet-content-with-input-and-submit')))
.atIndex(0)
.tap();
await waitForToast();
});
it('should reset avatar', async () => {
await element(by.type(scrollViewType)).atIndex(1).swipe('up');
await element(by.id('profile-view-reset-avatar')).tap();
await waitForToast();
});
});
});

View File

@ -0,0 +1,92 @@
import { expect } from 'detox';
import { navigateToLogin, login, platformTypes, TTextMatcher } from '../../helpers/app';
import data from '../../data';
const testuser = data.users.regular;
describe('Settings screen', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(testuser.username, testuser.password);
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('sidebar-settings')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('sidebar-settings')).tap();
await waitFor(element(by.id('settings-view')))
.toBeVisible()
.withTimeout(2000);
});
describe('Render', () => {
it('should have settings view', async () => {
await expect(element(by.id('settings-view'))).toBeVisible();
});
it('should have language', async () => {
await expect(element(by.id('settings-view-language'))).toExist();
});
it('should have review app', async () => {
await expect(element(by.id('settings-view-review-app'))).toExist();
});
it('should have share app', async () => {
await expect(element(by.id('settings-view-share-app'))).toExist();
});
it('should have default browser', async () => {
await expect(element(by.id('settings-view-default-browser'))).toExist();
});
it('should have theme', async () => {
await expect(element(by.id('settings-view-theme'))).toExist();
});
it('should have security and privacy', async () => {
await expect(element(by.id('settings-view-security-privacy'))).toExist();
});
it('should have licence', async () => {
await expect(element(by.id('settings-view-license'))).toExist();
});
it('should have version no', async () => {
await expect(element(by.id('settings-view-version'))).toExist();
});
it('should have server version', async () => {
await expect(element(by.id('settings-view-server-version'))).toExist();
});
});
describe('Usage', () => {
it('should tap clear cache and navigate to roomslistview', async () => {
await waitFor(element(by.id('settings-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('settings-view-clear-cache')).tap();
await waitFor(element(by[textMatcher]('This will clear all your offline data.')))
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Clear').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id(`rooms-list-view-item-${data.groups.private.name}`)))
.toExist()
.withTimeout(10000);
});
});
});

View File

@ -0,0 +1,170 @@
import { expect } from 'detox';
import data from '../../data';
import { navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes, TTextMatcher } from '../../helpers/app';
const testuser = data.users.regular;
const room = data.channels.detoxpublic.name;
async function navigateToRoom() {
await searchRoom(room);
await element(by.id(`rooms-list-view-item-${room}`)).tap();
await waitFor(element(by.id('room-view')).atIndex(0))
.toExist()
.withTimeout(5000);
}
async function navigateToRoomActions() {
await element(by.id(`room-view-title-${room}`)).tap();
await waitFor(element(by.id('room-actions-view')))
.toBeVisible()
.withTimeout(5000);
}
describe('Join public room', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(testuser.username, testuser.password);
await navigateToRoom();
});
describe('Render', () => {
it('should have room screen', async () => {
await expect(element(by.id('room-view'))).toBeVisible();
});
// Render - Header
describe('Header', () => {
it('should have actions button ', async () => {
await expect(element(by.id('room-header'))).toBeVisible();
});
});
// Render - Join
describe('Join', () => {
it('should have join', async () => {
await expect(element(by.id('room-view-join'))).toBeVisible();
});
it('should have join text', async () => {
await expect(element(by.label('You are in preview mode'))).toBeVisible();
});
it('should have join button', async () => {
await expect(element(by.id('room-view-join-button'))).toBeVisible();
});
it('should not have messagebox', async () => {
await expect(element(by.id('messagebox'))).toBeNotVisible();
});
});
describe('Room Actions', () => {
beforeAll(async () => {
await navigateToRoomActions();
});
it('should have room actions screen', async () => {
await expect(element(by.id('room-actions-view'))).toBeVisible();
});
it('should have info', async () => {
await expect(element(by.id('room-actions-info'))).toBeVisible();
});
it('should have members', async () => {
await waitFor(element(by.id('room-actions-members')))
.toBeVisible()
.withTimeout(2000);
});
it('should have files', async () => {
await expect(element(by.id('room-actions-files'))).toBeVisible();
});
it('should have mentions', async () => {
await expect(element(by.id('room-actions-mentioned'))).toBeVisible();
});
it('should have starred', async () => {
await expect(element(by.id('room-actions-starred'))).toBeVisible();
});
it('should have share', async () => {
await expect(element(by.id('room-actions-share'))).toBeVisible();
});
it('should have pinned', async () => {
await expect(element(by.id('room-actions-pinned'))).toBeVisible();
});
it('should not have notifications', async () => {
await expect(element(by.id('room-actions-notifications'))).toBeNotVisible();
});
it('should not have leave channel', async () => {
await expect(element(by.id('room-actions-leave-channel'))).toBeNotVisible();
});
afterAll(async () => {
await tapBack();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(2000);
});
});
});
describe('Usage', () => {
it('should join room', async () => {
await element(by.id('room-view-join-button')).tap();
await tapBack();
await element(by.id(`rooms-list-view-item-${room}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id('messagebox')))
.toBeVisible()
.withTimeout(60000);
await expect(element(by.id('messagebox'))).toBeVisible();
await expect(element(by.id('room-view-join'))).toBeNotVisible();
});
it('should send message', async () => {
await mockMessage('message');
});
it('should have notifications and leave channel', async () => {
await navigateToRoomActions();
await expect(element(by.id('room-actions-view'))).toBeVisible();
await expect(element(by.id('room-actions-info'))).toBeVisible();
await expect(element(by.id('room-actions-members'))).toBeVisible();
await expect(element(by.id('room-actions-files'))).toBeVisible();
await expect(element(by.id('room-actions-mentioned'))).toBeVisible();
await expect(element(by.id('room-actions-starred'))).toBeVisible();
await expect(element(by.id('room-actions-share'))).toBeVisible();
await expect(element(by.id('room-actions-pinned'))).toBeVisible();
await expect(element(by.id('room-actions-notifications'))).toBeVisible();
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await expect(element(by.id('room-actions-leave-channel'))).toBeVisible();
});
it('should leave room', async () => {
await element(by.id('room-actions-leave-channel')).tap();
await waitFor(element(by[textMatcher]('Yes, leave it!').and(by.type(alertButtonType))))
.toBeVisible()
.withTimeout(5000);
await element(by[textMatcher]('Yes, leave it!').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await waitFor(element(by.id(`rooms-list-view-item-${room}`)))
.toBeNotVisible()
.withTimeout(60000); // flaky on Android
});
});
});

View File

@ -0,0 +1,65 @@
import { expect } from 'detox';
import { navigateToLogin, login, sleep } from '../../helpers/app';
import data from '../../data';
const testuser = data.users.regular;
describe('Status screen', () => {
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(testuser.username, testuser.password);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('sidebar-custom-status-online')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('sidebar-custom-status-online')).tap();
await waitFor(element(by.id('status-view')))
.toBeVisible()
.withTimeout(2000);
});
describe('Render', () => {
it('should have status input', async () => {
await expect(element(by.id('status-view-input'))).toBeVisible();
await expect(element(by.id('status-view-online'))).toExist();
await expect(element(by.id('status-view-busy'))).toExist();
await expect(element(by.id('status-view-away'))).toExist();
await expect(element(by.id('status-view-offline'))).toExist();
});
});
describe('Usage', () => {
it('should change status', async () => {
await element(by.id('status-view-busy')).tap();
await element(by.id('status-view-submit')).tap();
await sleep(3000); // Wait until the loading hide
await waitFor(element(by.id('rooms-list-view-sidebar')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('sidebar-custom-status-busy')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('sidebar-custom-status-busy')).tap();
});
// TODO: flaky
it('should change status text', async () => {
await element(by.id('status-view-input')).replaceText('status-text-new');
await element(by.id('status-view-submit')).tap();
await sleep(3000); // Wait until the loading hide
await waitFor(element(by.label('status-text-new')))
.toExist()
.withTimeout(5000);
});
});
});

View File

@ -0,0 +1,101 @@
import data from '../../data';
import { navigateToLogin, login, checkServer, sleep } from '../../helpers/app';
const reopenAndCheckServer = async (server: string) => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true });
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await checkServer(server);
};
describe('Change server', () => {
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
});
it('should open the dropdown button, have the server add button and create workspace button', async () => {
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id('rooms-list-header-server-add')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id('rooms-list-header-create-workspace-button')))
.toBeVisible()
.withTimeout(5000);
});
it('should login to server, add new server, close the app, open the app and show previous logged server', async () => {
await element(by.id('rooms-list-header-server-add')).tap();
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(6000);
await element(by.id('new-server-view-input')).replaceText(`${data.alternateServer}`);
await element(by.id('new-server-view-input')).tapReturnKey();
await waitFor(element(by.id('workspace-view')))
.toBeVisible()
.withTimeout(10000);
await reopenAndCheckServer(data.server);
});
it('should add server and create new user', async () => {
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown')))
.toBeVisible()
.withTimeout(5000);
await element(by.id(`rooms-list-header-server-${data.alternateServer}`)).tap();
await waitFor(element(by.id('workspace-view')))
.toBeVisible()
.withTimeout(60000);
await element(by.id('workspace-view-register')).tap();
await waitFor(element(by.id('register-view')))
.toExist()
.withTimeout(2000);
// Register new user
await sleep(5000);
await element(by.id('register-view-name')).replaceText(data.registeringUser2.username);
await element(by.id('register-view-username')).replaceText(data.registeringUser2.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser2.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser2.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(60000);
await waitFor(element(by.id(`rooms-list-view-item-${data.groups.private.name}`)))
.toBeNotVisible()
.withTimeout(60000);
await checkServer(data.alternateServer);
});
it('should reopen the app and show alternate server', async () => {
await reopenAndCheckServer(data.alternateServer);
});
it('should change back to main server', async () => {
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown')))
.toBeVisible()
.withTimeout(5000);
await element(by.id(`rooms-list-header-server-${data.server}`)).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await waitFor(element(by.id(`rooms-list-view-item-${data.groups.private.name}`)))
.toBeVisible()
.withTimeout(60000);
await checkServer(data.server);
});
it('should reopen the app and show main server', async () => {
await reopenAndCheckServer(data.server);
});
});

View File

@ -0,0 +1,73 @@
import { expect } from 'detox';
import data from '../../data';
import { navigateToLogin, login, mockMessage, searchRoom } from '../../helpers/app';
const testuser = data.users.regular;
const room = data.channels.detoxpublicprotected.name;
const { joinCode } = data.channels.detoxpublicprotected;
async function navigateToRoom() {
await searchRoom(room);
await element(by.id(`rooms-list-view-item-${room}`)).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(5000);
}
async function openJoinCode() {
await waitFor(element(by.id('room-view-join-button')))
.toExist()
.withTimeout(2000);
let n = 0;
while (n < 3) {
try {
await element(by.id('room-view-join-button')).tap();
await waitFor(element(by.id('join-code')))
.toBeVisible()
.withTimeout(500);
} catch (error) {
n += 1;
}
}
}
describe('Join protected room', () => {
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(testuser.username, testuser.password);
await navigateToRoom();
});
describe('Usage', () => {
it('should tap join and ask for join code', async () => {
await openJoinCode();
});
it('should cancel join room', async () => {
await element(by.id('join-code-cancel')).tap();
await waitFor(element(by.id('join-code')))
.toBeNotVisible()
.withTimeout(5000);
});
it('should join room', async () => {
await openJoinCode();
await element(by.id('join-code-input')).replaceText(joinCode);
await element(by.id('join-code-submit')).tap();
await waitFor(element(by.id('join-code')))
.toBeNotVisible()
.withTimeout(5000);
await waitFor(element(by.id('messagebox')))
.toBeVisible()
.withTimeout(60000);
await expect(element(by.id('messagebox'))).toBeVisible();
await expect(element(by.id('room-view-join'))).toBeNotVisible();
});
it('should send message', async () => {
await mockMessage('message');
});
});
});

View File

@ -0,0 +1,86 @@
import data from '../../data';
import { navigateToLogin, login, tapBack, sleep } from '../../helpers/app';
import { sendMessage } from '../../helpers/data_setup';
const testuser = data.users.regular;
async function navigateToRoom(search: string) {
await element(by.id('directory-view-search')).replaceText(search);
await waitFor(element(by.id(`directory-view-item-${search}`)))
.toBeVisible()
.withTimeout(10000);
await sleep(300); // app takes some time to animate
await element(by.id(`directory-view-item-${search}`)).tap();
await waitFor(element(by.id('room-view')).atIndex(0))
.toExist()
.withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${search}`)))
.toExist()
.withTimeout(5000);
}
describe('Join room from directory', () => {
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(testuser.username, testuser.password);
});
describe('Usage', () => {
const threadMessage = `thread-${data.random}`;
beforeAll(async () => {
const result = await sendMessage(data.users.alternate, data.channels.detoxpublic.name, threadMessage);
const threadId = result.message._id;
await sendMessage(data.users.alternate, result.message.rid, data.random, threadId);
});
it('should tap directory', async () => {
await element(by.id('rooms-list-view-directory')).tap();
await waitFor(element(by.id('directory-view')))
.toExist()
.withTimeout(2000);
});
it('should search public channel and navigate', async () => {
await navigateToRoom(data.channels.detoxpublic.name);
});
it('should navigate to thread messages view and load messages', async () => {
await waitFor(element(by.id('room-view-header-threads')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('room-view-header-threads')).tap();
await waitFor(element(by.id(`thread-messages-view-${threadMessage}`)))
.toBeVisible()
.withTimeout(2000);
await tapBack();
await waitFor(element(by.id('room-view-header-threads')))
.toBeVisible()
.withTimeout(2000);
});
it('should search user and navigate', async () => {
await tapBack();
await element(by.id('rooms-list-view-directory')).tap();
await waitFor(element(by.id('directory-view')))
.toExist()
.withTimeout(2000);
await element(by.id('directory-view-dropdown')).tap();
await element(by.label('Users')).atIndex(0).tap();
await element(by.label('Search by')).atIndex(0).tap();
await navigateToRoom(data.users.alternate.username);
});
it('should search team and navigate', async () => {
await tapBack();
await element(by.id('rooms-list-view-directory')).tap();
await waitFor(element(by.id('directory-view')))
.toExist()
.withTimeout(2000);
await element(by.id('directory-view-dropdown')).tap();
await element(by.label('Teams')).atIndex(0).tap();
await element(by.label('Search by')).atIndex(0).tap();
await navigateToRoom(data.teams.private.name);
});
});
});

View File

@ -0,0 +1,67 @@
import data from '../../data';
import { sleep, navigateToLogin, login, checkServer, platformTypes, TTextMatcher } from '../../helpers/app';
describe('Delete server', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
it('should be logged in main server', async () => {
await checkServer(data.server);
});
it('should add server', async () => {
await sleep(5000);
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('rooms-list-header-server-add')).tap();
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('new-server-view-input')).replaceText(`${data.alternateServer}`);
await element(by.id('new-server-view-input')).tapReturnKey();
await waitFor(element(by.id('workspace-view')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('workspace-view-register')).tap();
await waitFor(element(by.id('register-view')))
.toBeVisible()
.withTimeout(2000);
// Register new user
await element(by.id('register-view-name')).replaceText(data.registeringUser3.username);
await element(by.id('register-view-username')).replaceText(data.registeringUser3.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser3.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser3.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(60000);
await checkServer(data.alternateServer);
});
it('should delete server', async () => {
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown')))
.toBeVisible()
.withTimeout(5000);
await element(by.id(`rooms-list-header-server-${data.server}`)).longPress(1500);
await element(by[textMatcher]('Delete').and(by.type(alertButtonType))).tap();
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id(`rooms-list-header-server-${data.server}`)))
.toBeNotVisible()
.withTimeout(10000);
});
});

View File

@ -0,0 +1,218 @@
import EJSON from 'ejson';
import data from '../../data';
import { tapBack, checkServer, navigateToRegister, platformTypes, TTextMatcher } from '../../helpers/app';
import { get, login, sendMessage } from '../../helpers/data_setup';
const DEEPLINK_METHODS = { AUTH: 'auth', ROOM: 'room' };
let amp = '&';
const getDeepLink = (method: string, server: string, params?: string) => {
const deeplink = `rocketchat://${method}?host=${server.replace(/^(http:\/\/|https:\/\/)/, '')}${amp}${params}`;
console.log(`Deeplinking to: ${deeplink}`);
return deeplink;
};
describe('Deep linking', () => {
let userId: string;
let authToken: string;
let scrollViewType: string;
let threadId: string;
let textMatcher: TTextMatcher;
const threadMessage = `to-thread-${data.random}`;
beforeAll(async () => {
const loginResult = await login(data.users.regular.username, data.users.regular.password);
({ userId, authToken } = loginResult);
const deviceType = device.getPlatform();
amp = deviceType === 'android' ? '\\&' : '&';
({ scrollViewType, textMatcher } = platformTypes[deviceType]);
// create a thread with api
const result = await sendMessage(data.users.regular, data.groups.alternate2.name, threadMessage);
threadId = result.message._id;
await sendMessage(data.users.regular, result.message.rid, data.random, threadId);
});
describe('Authentication', () => {
it('should run a deep link to an invalid account and raise error', async () => {
await device.launchApp({
permissions: { notifications: 'YES' },
delete: true,
url: getDeepLink(DEEPLINK_METHODS.AUTH, data.server, `userId=123${amp}token=abc`)
});
await waitFor(element(by[textMatcher]("You've been logged out by the server. Please log in again.")))
.toExist()
.withTimeout(30000); // TODO: we need to improve this message
});
const authAndNavigate = async () => {
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true,
url: getDeepLink(
DEEPLINK_METHODS.AUTH,
data.server,
`userId=${userId}${amp}token=${authToken}${amp}path=group/${data.groups.private.name}`
)
});
await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`)))
.toExist()
.withTimeout(30000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await checkServer(data.server);
await waitFor(element(by.id(`rooms-list-view-item-${data.groups.private.name}`)))
.toExist()
.withTimeout(2000);
};
it('should authenticate and navigate', async () => {
await authAndNavigate();
});
it('should authenticate while logged in another server', async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToRegister(data.alternateServer);
await element(by.id('register-view-name')).replaceText(data.registeringUser4.username);
await element(by.id('register-view-username')).replaceText(data.registeringUser4.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser4.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser4.password);
await element(by.type(scrollViewType)).atIndex(0).scrollTo('bottom');
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await authAndNavigate();
});
});
describe('Room', () => {
describe('While logged in', () => {
it('should navigate to the room using path', async () => {
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true,
url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.private.name}`)
});
await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`)))
.toExist()
.withTimeout(30000);
});
it('should navigate to the thread using path', async () => {
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true,
url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.alternate2.name}/thread/${threadId}`)
});
await waitFor(element(by.id(`room-view-title-${threadMessage}`)))
.toExist()
.withTimeout(30000);
});
it('should navigate to the room using rid', async () => {
const roomResult = await get(`groups.info?roomName=${data.groups.private.name}`);
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true,
url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `rid=${roomResult.data.group._id}`)
});
await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`)))
.toExist()
.withTimeout(30000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
});
it('should resume from background and navigate to the room', async () => {
await device.sendToHome();
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: false,
url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.private.name}`)
});
await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`)))
.toExist()
.withTimeout(30000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
});
it('should simulate a tap on a push notification and navigate to the room', async () => {
/**
* Ideally, we would repeat this test to simulate a resume from background,
* but for some reason it was not working as expected
* This was always turning to false right before running the logic https://github.com/RocketChat/Rocket.Chat.ReactNative/blob/18f359a8ef9691144970c0c1fad990f82096b024/app/lib/notifications/push.ts#L58
*/
// await device.sendToHome();
await device.launchApp({
newInstance: true,
userNotification: {
trigger: {
type: 'push'
},
title: 'From push',
body: 'Body',
badge: 1,
payload: {
ejson: EJSON.stringify({
rid: null,
host: data.server,
name: data.groups.private.name,
type: 'p'
})
}
}
});
await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`)))
.toExist()
.withTimeout(30000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
});
});
describe('Others', () => {
it('should change server', async () => {
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown')))
.toBeVisible()
.withTimeout(5000);
await element(by.id(`rooms-list-header-server-${data.alternateServer}`)).tap();
await checkServer(data.alternateServer);
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true,
url: getDeepLink(DEEPLINK_METHODS.ROOM, data.server, `path=group/${data.groups.private.name}`)
});
await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`)))
.toExist()
.withTimeout(30000);
});
it('should add a not existing server and fallback to the previous one', async () => {
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true,
url: getDeepLink(DEEPLINK_METHODS.ROOM, 'https://google.com')
});
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(30000);
await checkServer(data.server);
});
});
});
});

View File

@ -0,0 +1,140 @@
import { expect } from 'detox';
import { navigateToLogin, login, sleep } from '../../helpers/app';
import { post } from '../../helpers/data_setup';
import data from '../../data';
const testuser = data.users.regular;
const defaultLaunchArgs = { permissions: { notifications: 'YES' } } as Detox.DeviceLaunchAppConfig;
const navToLanguage = async () => {
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await waitFor(element(by.id('sidebar-settings')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('sidebar-settings')).tap();
await waitFor(element(by.id('settings-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('settings-view-language')).tap();
await waitFor(element(by.id('language-view')))
.toBeVisible()
.withTimeout(10000);
};
describe('i18n', () => {
describe('OS language', () => {
it("OS set to 'en' and proper translate to 'en'", async () => {
if (device.getPlatform() === 'android') {
return; // FIXME: Passing language with launch parameters doesn't work with Android
}
await device.launchApp({
...defaultLaunchArgs,
languageAndLocale: {
language: 'en',
locale: 'en'
},
delete: true
});
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(20000);
await expect(element(by.id('new-server-view-open').and(by.label('Join our open workspace')))).toBeVisible();
});
it("OS set to unavailable language and fallback to 'en'", async () => {
if (device.getPlatform() === 'android') {
return; // FIXME: Passing language with launch parameters doesn't work with Android
}
await device.launchApp({
...defaultLaunchArgs,
languageAndLocale: {
language: 'es-MX',
locale: 'es-MX'
}
});
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(20000);
await expect(element(by.id('new-server-view-open').and(by.label('Join our open workspace')))).toBeVisible();
});
/**
* This test might become outdated as soon as we support the language
* Although this seems to be a bad approach, that's the intention for having fallback enabled
*/
// it('OS set to available language and fallback to \'en\' on strings missing translation', async() => {
// await device.launchApp({
// ...defaultLaunchArgs,
// languageAndLocale: {
// language: "nl",
// locale: "nl"
// }
// });
// });
});
describe('Rocket.Chat language', () => {
beforeAll(async () => {
await device.launchApp({ ...defaultLaunchArgs, delete: true });
await navigateToLogin();
await login(testuser.username, testuser.password);
});
it("should select 'en'", async () => {
await navToLanguage();
await element(by.id('language-view-en')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await expect(element(by.id('sidebar-chats').withDescendant(by.label('Chats')))).toBeVisible();
await expect(element(by.id('sidebar-profile').withDescendant(by.label('Profile')))).toBeVisible();
await expect(element(by.id('sidebar-settings').withDescendant(by.label('Settings')))).toBeVisible();
await element(by.id('sidebar-close-drawer')).tap();
});
it("should select 'nl' and fallback to 'en'", async () => {
await navToLanguage();
await element(by.id('language-view-nl')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await expect(element(by.id('sidebar-chats').withDescendant(by.label('Chats')))).toBeVisible(); // fallback to en
await expect(element(by.id('sidebar-profile').withDescendant(by.label('Profiel')))).toBeVisible();
await expect(element(by.id('sidebar-settings').withDescendant(by.label('Instellingen')))).toBeVisible();
await element(by.id('sidebar-close-drawer')).tap();
});
it("should set unsupported language and fallback to 'en'", async () => {
await post('users.setPreferences', { data: { language: 'eo' } }); // Set language to Esperanto
await device.launchApp({ ...defaultLaunchArgs, newInstance: true });
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
// give the app some time to apply new language
await sleep(3000);
await expect(element(by.id('sidebar-chats').withDescendant(by.label('Chats')))).toBeVisible();
await expect(element(by.id('sidebar-profile').withDescendant(by.label('Profile')))).toBeVisible();
await expect(element(by.id('sidebar-settings').withDescendant(by.label('Settings')))).toBeVisible();
await post('users.setPreferences', { data: { language: 'en' } }); // Set back to english
});
});
});

View File

@ -0,0 +1,102 @@
import { expect } from 'detox';
import { login, navigateToLogin } from '../../helpers/app';
import data from '../../data';
const goToDisplayPref = async () => {
await expect(element(by.id('rooms-list-view-sidebar'))).toBeVisible();
await element(by.id('rooms-list-view-sidebar')).tap();
await expect(element(by.id('sidebar-display'))).toBeVisible();
await element(by.id('sidebar-display')).tap();
};
const goToRoomList = async () => {
await expect(element(by.id('display-view-drawer'))).toBeVisible();
await element(by.id('display-view-drawer')).tap();
await expect(element(by.id('sidebar-chats'))).toBeVisible();
await element(by.id('sidebar-chats')).tap();
};
describe('Display prefs', () => {
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true });
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
describe('Render', () => {
it('should have rooms list screen', async () => {
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
it('should have room item', async () => {
await waitFor(element(by.id('rooms-list-view-item-general')))
.toExist()
.withTimeout(2000);
});
// Render - Header
describe('Header', () => {
it('should have create channel button', async () => {
await expect(element(by.id('rooms-list-view-create-channel'))).toBeVisible();
});
it('should have sidebar button', async () => {
await expect(element(by.id('rooms-list-view-sidebar'))).toBeVisible();
});
});
describe('DisplayPrefView', () => {
it('should go to Display Preferences', async () => {
await goToDisplayPref();
});
it('should have Displays button, expanded, condensed, avatars', async () => {
await expect(element(by.id('display-pref-view-expanded'))).toBeVisible();
await expect(element(by.id('display-pref-view-condensed'))).toBeVisible();
await expect(element(by.id('display-pref-view-avatars'))).toBeVisible();
});
it('should have Sort By button', async () => {
await expect(element(by.id('display-pref-view-activity'))).toBeVisible();
await expect(element(by.id('display-pref-view-name'))).toBeVisible();
});
it('should have Group by button', async () => {
await expect(element(by.id('display-pref-view-unread'))).toBeVisible();
await expect(element(by.id('display-pref-view-favorites'))).toBeVisible();
await expect(element(by.id('display-pref-view-types'))).toBeVisible();
});
});
describe('Change display', () => {
it('should appear the last message in RoomList when is Expanded', async () => {
await element(by.id('display-pref-view-expanded')).tap();
await goToRoomList();
await expect(element(by.id('room-item-last-message')).atIndex(0)).toBeVisible();
});
it('should not appear the last message in RoomList when is Condensed', async () => {
await goToDisplayPref();
await element(by.id('display-pref-view-condensed')).tap();
await goToRoomList();
await expect(element(by.id('room-item-last-message'))).not.toBeVisible();
});
});
describe('Change the avatar visible', () => {
it('should have avatar as default in room list', async () => {
await expect(element(by.id('avatar')).atIndex(0)).toExist();
});
it('should hide the avatar', async () => {
await goToDisplayPref();
await expect(element(by.id('display-pref-view-avatar-switch'))).toBeVisible();
await element(by.id('display-pref-view-avatar-switch')).tap();
await goToRoomList();
await waitFor(element(by.id('avatar').withAncestor(by.id('rooms-list-view-item-general'))))
.not.toBeVisible()
.withTimeout(2000);
});
});
});
});

View File

@ -0,0 +1,72 @@
import { expect } from 'detox';
import data from '../../data';
import { navigateToLogin, login, sleep, tapBack } from '../../helpers/app';
import { sendMessage, post } from '../../helpers/data_setup';
describe('InApp Notification', () => {
let dmCreatedRid: string;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
const result = await post(`im.create`, { username: data.users.alternate.username });
dmCreatedRid = result.data.room.rid;
});
describe('receive in RoomsListView', () => {
const text = 'Message in DM';
it('should have rooms list screen', async () => {
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
it('should send direct message from user alternate to user regular', async () => {
await sleep(1000);
await sendMessage(data.users.alternate, dmCreatedRid, text);
});
it('should tap on InApp Notification', async () => {
await waitFor(element(by.id(`in-app-notification-${text}`)))
.toExist()
.withTimeout(2000);
await sleep(500);
await element(by.id(`in-app-notification-${text}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
await expect(element(by.id(`room-view-title-${data.users.alternate.username}`))).toExist();
});
});
describe('receive in another room', () => {
const text = 'Another msg';
it('should back to RoomsListView and open the channel Detox Public', async () => {
await tapBack();
await sleep(500);
await element(by.id(`rooms-list-view-item-${data.userRegularChannels.detoxpublic.name}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
await expect(element(by.id(`room-view-title-${data.userRegularChannels.detoxpublic.name}`))).toExist();
});
it('should receive and tap InAppNotification in another room', async () => {
await sendMessage(data.users.alternate, dmCreatedRid, text);
await waitFor(element(by.id(`in-app-notification-${text}`)))
.toExist()
.withTimeout(2000);
await sleep(500);
await element(by.id(`in-app-notification-${text}`)).tap();
await sleep(500);
await expect(element(by.id('room-header'))).toExist();
await expect(element(by.id(`room-view-title-${data.users.alternate.username}`))).toExist();
});
it('should back to RoomsListView', async () => {
await tapBack();
await sleep(500);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
});
});

29
e2e/tests/init.ts Normal file
View File

@ -0,0 +1,29 @@
// import detox from 'detox';
// import adapter from 'detox/runners/mocha/adapter';
// import { detox as config } from '../../package.json';
import { setup } from '../helpers/data_setup';
// import { prepareAndroid } from '../helpers/app';
beforeAll(async () => {
await setup();
// await Promise.all([setup(), detox.init(config, { launchApp: false })]);
// await prepareAndroid(); // Make Android less flaky
// await dataSetup()
// await detox.init(config, { launchApp: false });
// await device.launchApp({ permissions: { notifications: 'YES' } });
});
// beforeEach(async function () {
// // @ts-ignore
// await adapter.beforeEach(this);
// });
// afterEach(async function () {
// // @ts-ignore
// await adapter.afterEach(this);
// });
// afterAll(async () => {
// await detox.cleanup();
// });

View File

@ -0,0 +1,56 @@
import { expect } from 'detox';
import { TTextMatcher, platformTypes } from '../../helpers/app';
import data from '../../data';
describe('Onboarding', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(20000);
});
describe('Render', () => {
it('should have onboarding screen', async () => {
await expect(element(by.id('new-server-view'))).toBeVisible();
});
it('should have "Join our open workspace"', async () => {
await expect(element(by.id('new-server-view-open'))).toBeVisible();
});
});
describe('Usage', () => {
it('should enter an invalid server and get error', async () => {
await element(by.id('new-server-view-input')).replaceText('invalidtest');
await element(by.id('new-server-view-input')).tapReturnKey();
await waitFor(element(by[textMatcher]('Oops!')))
.toExist()
.withTimeout(10000);
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
});
it('should tap on "Join our open workspace" and navigate', async () => {
await element(by.id('new-server-view-open')).tap();
await waitFor(element(by.id('workspace-view')))
.toBeVisible()
.withTimeout(60000);
});
it('should enter a valid server without login services and navigate to login', async () => {
await device.launchApp({ newInstance: true });
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('new-server-view-input')).replaceText(data.server);
await element(by.id('new-server-view-input')).tapReturnKey();
await waitFor(element(by.id('workspace-view')))
.toBeVisible()
.withTimeout(60000);
});
});
});

View File

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

View File

@ -0,0 +1,46 @@
import { expect } from 'detox';
import data from '../../data';
import { navigateToLogin, platformTypes, TTextMatcher } from '../../helpers/app';
describe('Forgot password screen', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await element(by.id('login-view-forgot-password')).tap();
await waitFor(element(by.id('forgot-password-view')))
.toExist()
.withTimeout(2000);
});
describe('Render', () => {
it('should have forgot password screen', async () => {
await expect(element(by.id('forgot-password-view'))).toExist();
});
it('should have email input', async () => {
await expect(element(by.id('forgot-password-view-email'))).toBeVisible();
});
it('should have submit button', async () => {
await expect(element(by.id('forgot-password-view-submit'))).toBeVisible();
});
});
describe('Usage', () => {
it('should reset password and navigate to login', async () => {
await element(by.id('forgot-password-view-email')).replaceText(data.users.existing.email);
await element(by.id('forgot-password-view-submit')).tap();
await waitFor(element(by[textMatcher]('OK')))
.toExist()
.withTimeout(10000);
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('login-view')))
.toBeVisible()
.withTimeout(60000);
});
});
});

View File

@ -0,0 +1,91 @@
import { expect } from 'detox';
import { navigateToRegister, platformTypes, TTextMatcher } from '../../helpers/app';
import data from '../../data';
describe('Create user screen', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToRegister();
});
describe('Render', () => {
it('should have create user screen', async () => {
await waitFor(element(by.id('register-view')))
.toExist()
.withTimeout(2000);
});
it('should have name input', async () => {
await expect(element(by.id('register-view-name'))).toBeVisible();
});
it('should have email input', async () => {
await expect(element(by.id('register-view-email'))).toBeVisible();
});
it('should have password input', async () => {
await expect(element(by.id('register-view-password'))).toBeVisible();
});
it('should have submit button', async () => {
await element(by.id('register-view')).atIndex(0).swipe('up', 'fast', 0.5);
await expect(element(by.id('register-view-submit'))).toBeVisible();
});
it('should have legal button', async () => {
await expect(element(by.id('register-view-more'))).toBeVisible();
});
});
describe('Usage', () => {
// FIXME: Detox isn't able to check if it's tappable: https://github.com/wix/Detox/issues/246
// it('should submit invalid email and do nothing', async() => {
// const invalidEmail = 'invalidemail';
// await element(by.id('register-view-name')).replaceText(data.user);
// await element(by.id('register-view-username')).replaceText(data.user);
// await element(by.id('register-view-email')).replaceText(invalidEmail);
// await element(by.id('register-view-password')).replaceText(data.password);
// await element(by.id('register-view-submit')).tap();
// });
// TODO: When server handle two errors in sequence, the server return Too many requests and force to wait for some time.
// it('should submit email already taken and raise error', async () => {
// await element(by.id('register-view-name')).replaceText(data.registeringUser.username);
// await element(by.id('register-view-username')).replaceText(data.registeringUser.username);
// await element(by.id('register-view-email')).replaceText(data.users.existing.email);
// await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
// await element(by.id('register-view-submit')).tap();
// await waitFor(element(by[textMatcher]('Email already exists. [403]')).atIndex(0))
// .toExist()
// .withTimeout(10000);
// await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
// });
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-username')).replaceText(data.users.existing.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by[textMatcher]('Username is already in use')).atIndex(0))
.toExist()
.withTimeout(10000);
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
});
it('should register', async () => {
await element(by.id('register-view-name')).replaceText(data.registeringUser.username);
await element(by.id('register-view-username')).replaceText(data.registeringUser.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(60000);
});
});
});

View File

@ -0,0 +1,88 @@
import { expect } from 'detox';
import { navigateToLogin, tapBack, platformTypes, navigateToWorkspace, login, TTextMatcher } from '../../helpers/app';
import data from '../../data';
describe('Login screen', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
});
describe('Render', () => {
it('should have login screen', async () => {
await expect(element(by.id('login-view'))).toExist();
});
it('should have email input', async () => {
await expect(element(by.id('login-view-email'))).toBeVisible();
});
it('should have password input', async () => {
await expect(element(by.id('login-view-password'))).toBeVisible();
});
it('should have submit button', async () => {
await expect(element(by.id('login-view-submit'))).toBeVisible();
});
it('should have register button', async () => {
await expect(element(by.id('login-view-register'))).toBeVisible();
});
it('should have forgot password button', async () => {
await expect(element(by.id('login-view-forgot-password'))).toBeVisible();
});
it('should have legal button', async () => {
await expect(element(by.id('login-view-more'))).toBeVisible();
});
});
describe('Usage', () => {
it('should navigate to register', async () => {
await element(by.id('login-view-register')).tap();
await waitFor(element(by.id('register-view')))
.toExist()
.withTimeout(2000);
await tapBack();
});
it('should navigate to forgot password', async () => {
await element(by.id('login-view-forgot-password')).tap();
await waitFor(element(by.id('forgot-password-view')))
.toExist()
.withTimeout(2000);
await tapBack();
});
it('should insert wrong password and get error', async () => {
await element(by.id('login-view-email')).replaceText(data.users.regular.username);
await element(by.id('login-view-password')).replaceText('NotMyActualPassword');
await element(by.id('login-view-submit')).tap();
await waitFor(element(by[textMatcher]('Your credentials were rejected! Please try again.')))
.toBeVisible()
.withTimeout(10000);
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
});
it('should login with success', async () => {
await element(by.id('login-view-password')).replaceText(data.users.regular.password);
await element(by.id('login-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(60000);
});
it('should connect, go back, connect to the same server and login', async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true });
await navigateToWorkspace();
await tapBack();
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
});
});

View File

@ -0,0 +1,59 @@
import { expect } from 'detox';
import { login, navigateToLogin, logout, tapBack, searchRoom } from '../../helpers/app';
import data from '../../data';
describe('Rooms list screen', () => {
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true });
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
describe('Render', () => {
it('should have rooms list screen', async () => {
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
it('should have room item', async () => {
await waitFor(element(by.id('rooms-list-view-item-general')))
.toExist()
.withTimeout(10000);
});
// Render - Header
describe('Header', () => {
it('should have create channel button', async () => {
await expect(element(by.id('rooms-list-view-create-channel'))).toBeVisible();
});
it('should have sidebar button', async () => {
await expect(element(by.id('rooms-list-view-sidebar'))).toBeVisible();
});
});
});
describe('Usage', () => {
it('should search room and navigate', async () => {
await searchRoom('rocket.cat');
await element(by.id('rooms-list-view-item-rocket.cat')).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(10000);
await waitFor(element(by.id('room-view-title-rocket.cat')))
.toBeVisible()
.withTimeout(60000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(6000);
await waitFor(element(by.id('rooms-list-view-item-rocket.cat')))
.toExist()
.withTimeout(60000);
});
it('should logout', async () => {
await logout();
});
});
});

View File

@ -0,0 +1,56 @@
import { expect } from 'detox';
import { login, navigateToLogin, logout, tapBack } from '../../helpers/app';
import data from '../../data';
describe('Server history', () => {
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
});
describe('Usage', () => {
it('should login, save server as history and logout', async () => {
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
await logout();
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(60000);
});
it('should show servers history', async () => {
await element(by.id('new-server-view-input')).tap();
await waitFor(element(by.id(`server-history-${data.server}`)))
.toBeVisible()
.withTimeout(2000);
});
it('should tap on a server history and navigate to login', async () => {
await element(by.id(`server-history-${data.server}`)).tap();
await waitFor(element(by.id('login-view-email')))
.toBeVisible()
.withTimeout(5000);
await expect(element(by.label(data.users.regular.username).withAncestor(by.id('login-view-email'))));
});
it('should delete server from history', async () => {
await tapBack();
await waitFor(element(by.id('workspace-view')))
.toBeVisible()
.withTimeout(2000);
await tapBack();
await waitFor(element(by.id('new-server-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('new-server-view-input')).tap();
await waitFor(element(by.id(`server-history-${data.server}`)))
.toBeVisible()
.withTimeout(2000);
await element(by.id(`server-history-delete-${data.server}`)).tap();
await element(by.id('new-server-view-input')).tap();
await waitFor(element(by.id(`server-history-${data.server}`)))
.toBeNotVisible()
.withTimeout(2000);
});
});
});

View File

@ -0,0 +1,257 @@
import { expect } from 'detox';
import data from '../../data';
import { tapBack, navigateToLogin, login, tryTapping, platformTypes, TTextMatcher } from '../../helpers/app';
describe('Create room screen', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
describe('New Message', () => {
beforeAll(async () => {
await waitFor(element(by.id('rooms-list-view-create-channel')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('rooms-list-view-create-channel')).tap();
});
describe('Render', () => {
it('should have new message screen', async () => {
await waitFor(element(by.id('new-message-view')))
.toBeVisible()
.withTimeout(2000);
});
it('should have search input', async () => {
await waitFor(element(by.id('new-message-view-search')))
.toBeVisible()
.withTimeout(2000);
});
});
describe('Usage', () => {
it('should back to rooms list', async () => {
await waitFor(element(by.id('new-message-view-close')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('new-message-view-close')).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(5000);
await tryTapping(element(by.id('rooms-list-view-create-channel')), 3000);
// await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view')))
.toExist()
.withTimeout(5000);
});
it('should search user and navigate', async () => {
await element(by.id('new-message-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id('new-message-view-item-rocket.cat')))
.toExist()
.withTimeout(60000);
await element(by.id('new-message-view-item-rocket.cat')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(10000);
await waitFor(element(by.id('room-view-title-rocket.cat')))
.toExist()
.withTimeout(60000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(5000);
});
it('should navigate to select users', async () => {
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view')))
.toExist()
.withTimeout(5000);
await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view')))
.toExist()
.withTimeout(5000);
});
});
});
describe('Select Users', () => {
it('should search users', async () => {
await element(by.id('select-users-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id('select-users-view-item-rocket.cat')))
.toBeVisible()
.withTimeout(10000);
});
it('should select/unselect user', async () => {
// Spotlight issues
await element(by.id('select-users-view-item-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('selected-user-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat')))
.toBeNotVisible()
.withTimeout(10000);
// Spotlight issues
await element(by.id('select-users-view-item-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat')))
.toBeVisible()
.withTimeout(10000);
});
it('should navigate to create channel view', async () => {
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view')))
.toExist()
.withTimeout(10000);
});
});
describe('Create Channel', () => {
describe('Render', () => {
it('should render all fields', async () => {
await expect(element(by.id('create-channel-name'))).toBeVisible();
await expect(element(by.id('create-channel-type'))).toBeVisible();
await expect(element(by.id('create-channel-readonly'))).toBeVisible();
await expect(element(by.id('create-channel-broadcast'))).toBeVisible();
});
});
describe('Usage', () => {
it('should get invalid room', async () => {
await element(by.id('create-channel-name')).replaceText('general');
await waitFor(element(by.id('create-channel-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by[textMatcher]('A channel with name general exists')))
.toExist()
.withTimeout(60000);
await expect(element(by[textMatcher]('A channel with name general exists'))).toExist();
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
});
it('should create public room', async () => {
const room = `public${data.random}`;
await element(by.id('create-channel-name')).replaceText('');
await element(by.id('create-channel-name')).replaceText(room);
await element(by.id('create-channel-type')).tap();
await waitFor(element(by.id('create-channel-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(6000);
await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${room}`)))
.toExist()
.withTimeout(6000);
await expect(element(by.id(`room-view-title-${room}`))).toExist();
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(10000);
await waitFor(element(by.id(`rooms-list-view-item-${room}`)))
.toExist()
.withTimeout(6000);
await expect(element(by.id(`rooms-list-view-item-${room}`))).toExist();
});
it('should create private room', async () => {
const room = `private${data.random}`;
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(5000);
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view')))
.toExist()
.withTimeout(5000);
await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view')))
.toExist()
.withTimeout(5000);
await element(by.id('select-users-view-item-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat')))
.toExist()
.withTimeout(5000);
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view')))
.toExist()
.withTimeout(5000);
await element(by.id('create-channel-name')).replaceText(room);
await waitFor(element(by.id('create-channel-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(60000);
await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${room}`)))
.toExist()
.withTimeout(60000);
await expect(element(by.id(`room-view-title-${room}`))).toExist();
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(5000);
await waitFor(element(by.id(`rooms-list-view-item-${room}`)))
.toExist()
.withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-${room}`))).toExist();
});
it('should create empty room', async () => {
const room = `empty${data.random}`;
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(10000);
// await device.launchApp({ newInstance: true });
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view')))
.toExist()
.withTimeout(5000);
await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view')))
.toExist()
.withTimeout(5000);
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view')))
.toExist()
.withTimeout(10000);
await element(by.id('create-channel-name')).replaceText(room);
await waitFor(element(by.id('create-channel-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(60000);
await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${room}`)))
.toExist()
.withTimeout(60000);
await expect(element(by.id(`room-view-title-${room}`))).toExist();
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id(`rooms-list-view-item-${room}`)))
.toExist()
.withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-${room}`))).toExist();
});
});
});
});

View File

@ -0,0 +1,536 @@
import { expect } from 'detox';
import data from '../../data';
import {
navigateToLogin,
login,
mockMessage,
tapBack,
sleep,
searchRoom,
dismissReviewNag,
tryTapping,
platformTypes,
TTextMatcher
} from '../../helpers/app';
import { sendMessage } from '../../helpers/data_setup';
async function navigateToRoom(roomName: string) {
await searchRoom(`${roomName}`);
await element(by.id(`rooms-list-view-item-${roomName}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
}
describe('Room screen', () => {
const mainRoom = data.groups.private.name;
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
await navigateToRoom(mainRoom);
});
describe('Render', () => {
it('should have room screen', async () => {
await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${mainRoom}`)))
.toExist()
.withTimeout(5000);
});
// Render - Header
describe('Header', () => {
it('should have actions button ', async () => {
await expect(element(by.id('room-header'))).toExist();
});
it('should have threads button ', async () => {
await expect(element(by.id('room-view-header-threads'))).toExist();
});
});
// Render - Messagebox
describe('Messagebox', () => {
it('should have messagebox', async () => {
await expect(element(by.id('messagebox'))).toExist();
});
it('should have open emoji button', async () => {
await expect(element(by.id('messagebox-open-emoji'))).toExist();
});
it('should have message input', async () => {
await expect(element(by.id('messagebox-input'))).toExist();
});
it('should have audio button', async () => {
await expect(element(by.id('messagebox-send-audio'))).toExist();
});
it('should have actions button', async () => {
await expect(element(by.id('messagebox-actions'))).toExist();
});
});
});
describe('Usage', () => {
describe('Messagebox', () => {
it('should send message', async () => {
await mockMessage('message');
await expect(element(by[textMatcher](`${data.random}message`)).atIndex(0)).toExist();
});
describe('Emoji Keyboard', () => {
it('should open emoji keyboard, select an emoji and send it', async () => {
await element(by.id('messagebox-open-emoji')).tap();
await waitFor(element(by.id('messagebox-keyboard-emoji')))
.toExist()
.withTimeout(10000);
await waitFor(element(by.id('emoji-picker-tab-emoji')))
.toExist()
.withTimeout(10000);
await element(by.id('emoji-picker-tab-emoji')).tap();
await expect(element(by.id('emoji-blush'))).toExist();
await element(by.id('emoji-blush')).tap();
await expect(element(by.id('messagebox-input'))).toHaveText('😊');
await element(by.id('messagebox-send-message')).tap();
await waitFor(element(by[textMatcher]('😊')))
.toExist()
.withTimeout(60000);
await element(by[textMatcher]('😊')).atIndex(0).tap();
});
it('should open emoji keyboard, select an emoji and delete it using emoji keyboards backspace', async () => {
await element(by.id('messagebox-open-emoji')).tap();
await waitFor(element(by.id('messagebox-keyboard-emoji')))
.toExist()
.withTimeout(10000);
await expect(element(by.id('emoji-picker-tab-emoji'))).toExist();
await element(by.id('emoji-picker-tab-emoji')).tap();
await expect(element(by.id('emoji-upside_down'))).toExist();
await element(by.id('emoji-upside_down')).tap();
await expect(element(by.id('messagebox-input'))).toHaveText('🙃');
await waitFor(element(by.id('emoji-picker-backspace')))
.toExist()
.withTimeout(2000);
await element(by.id('emoji-picker-backspace')).tap();
await expect(element(by.id('messagebox-input'))).toHaveText('');
await element(by.id('messagebox-close-emoji')).tap();
await waitFor(element(by.id('messagebox-keyboard-emoji')))
.not.toBeVisible()
.withTimeout(10000);
});
it('should search emoji and send it', async () => {
await element(by.id('messagebox-open-emoji')).tap();
await waitFor(element(by.id('emoji-picker-search')))
.toExist()
.withTimeout(4000);
await element(by.id('emoji-picker-search')).tap();
await waitFor(element(by.id('emoji-searchbar-input')))
.toExist()
.withTimeout(2000);
await element(by.id('emoji-searchbar-input')).replaceText('no_mouth');
await waitFor(element(by.id('emoji-no_mouth')))
.toExist()
.withTimeout(2000);
await element(by.id('emoji-no_mouth')).tap();
await expect(element(by.id('messagebox-input'))).toHaveText('😶');
await element(by.id('messagebox-send-message')).tap();
await waitFor(element(by[textMatcher]('😶')))
.toExist()
.withTimeout(60000);
await element(by[textMatcher]('😶')).atIndex(0).tap();
});
it('should search emojis, go back to Emoji keyboard and then close the Emoji keyboard', async () => {
await element(by.id('messagebox-open-emoji')).tap();
await waitFor(element(by.id('emoji-picker-search')))
.toExist()
.withTimeout(4000);
await element(by.id('emoji-picker-search')).tap();
await waitFor(element(by.id('emoji-searchbar-input')))
.toExist()
.withTimeout(2000);
await element(by.id('openback-emoji-keyboard')).tap();
await waitFor(element(by.id('emoji-searchbar-input')))
.not.toBeVisible()
.withTimeout(2000);
await expect(element(by.id('messagebox-close-emoji'))).toExist();
await element(by.id('messagebox-close-emoji')).tap();
await waitFor(element(by.id('messagebox-keyboard-emoji')))
.not.toBeVisible()
.withTimeout(10000);
});
it('frequently used emojis should contain the recently used emojis', async () => {
await element(by.id('messagebox-open-emoji')).tap();
await waitFor(element(by.id('emoji-picker-tab-clock')));
await element(by.id('emoji-picker-tab-clock')).tap();
await waitFor(element(by.id('emoji-blush')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id('emoji-upside_down')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id('emoji-no_mouth')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('messagebox-close-emoji'))).toExist();
await element(by.id('messagebox-close-emoji')).tap();
await waitFor(element(by.id('messagebox-keyboard-emoji')))
.not.toBeVisible()
.withTimeout(10000);
});
});
it('should show/hide emoji autocomplete', async () => {
await element(by.id('messagebox-input')).clearText();
await element(by.id('messagebox-input')).typeText(':joy');
await sleep(300);
await waitFor(element(by.id('messagebox-container')))
.toExist()
.withTimeout(10000);
await element(by.id('messagebox-input')).clearText();
await waitFor(element(by.id('messagebox-container')))
.toBeNotVisible()
.withTimeout(10000);
});
it('should show and tap on emoji autocomplete', async () => {
await element(by.id('messagebox-input')).typeText(':joy');
await sleep(300);
await waitFor(element(by.id('messagebox-container')))
.toExist()
.withTimeout(10000);
await waitFor(element(by.id('mention-item-joy')))
.toExist()
.withTimeout(10000);
await element(by.id('mention-item-joy')).tap();
await expect(element(by.id('messagebox-input'))).toHaveText(':joy: ');
await element(by.id('messagebox-input')).clearText();
});
it('should not show emoji autocomplete on semicolon in middle of a string', async () => {
await element(by.id('messagebox-input')).typeText('name:is');
await sleep(300);
await waitFor(element(by.id('messagebox-container')))
.toNotExist()
.withTimeout(20000);
await element(by.id('messagebox-input')).clearText();
});
it('should show and tap on user autocomplete and send mention', async () => {
const { username } = data.users.regular;
const messageMention = `@${username}`;
const message = `${data.random}mention`;
const fullMessage = `${messageMention} ${message}`;
await element(by.id('messagebox-input')).typeText(`@${username}`);
await sleep(300);
await waitFor(element(by.id('messagebox-container')))
.toExist()
.withTimeout(4000);
await waitFor(element(by.id(`mention-item-${username}`)))
.toBeVisible()
.withTimeout(4000);
await tryTapping(element(by.id(`mention-item-${username}`)), 2000, true);
await expect(element(by.id('messagebox-input'))).toHaveText(`${messageMention} `);
await tryTapping(element(by.id('messagebox-input')), 2000);
if (device.getPlatform() === 'ios') {
await element(by.id('messagebox-input')).typeText(message);
await element(by.id('messagebox-send-message')).tap();
const fullMessageMatcher = fullMessage.substr(1); // removes `@`
await waitFor(element(by[textMatcher](fullMessageMatcher)))
.toExist()
.withTimeout(60000);
await expect(element(by[textMatcher](fullMessageMatcher))).toExist();
await element(by[textMatcher](fullMessageMatcher)).atIndex(0).tap();
} else {
await element(by.id('messagebox-input')).replaceText(fullMessage);
await element(by.id('messagebox-send-message')).tap();
}
});
it('should not show user autocomplete on @ in the middle of a string', async () => {
await element(by.id('messagebox-input')).typeText('email@gmail');
await waitFor(element(by.id('messagebox-container')))
.toNotExist()
.withTimeout(4000);
await element(by.id('messagebox-input')).clearText();
});
it('should show and tap on room autocomplete', async () => {
await element(by.id('messagebox-input')).typeText('#general');
await waitFor(element(by.id('mention-item-general')))
.toBeVisible()
.withTimeout(4000);
await tryTapping(element(by.id('mention-item-general')), 2000, true);
await expect(element(by.id('messagebox-input'))).toHaveText('#general ');
await element(by.id('messagebox-input')).clearText();
});
it('should not show room autocomplete on # in middle of a string', async () => {
await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText('te#gen');
await waitFor(element(by.id('messagebox-container')))
.toNotExist()
.withTimeout(4000);
await element(by.id('messagebox-input')).clearText();
});
it('should draft message', async () => {
await element(by.id('messagebox-input')).typeText(`${data.random}draft`);
await tapBack();
await navigateToRoom(mainRoom);
await expect(element(by.id('messagebox-input'))).toHaveText(`${data.random}draft`);
await element(by.id('messagebox-input')).clearText();
await tapBack();
await navigateToRoom(mainRoom);
await expect(element(by.id('messagebox-input'))).toHaveText('');
});
});
describe('Message', () => {
it('should copy link', async () => {
await element(by[textMatcher](`${data.random}message`))
.atIndex(0)
.longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by[textMatcher]('Get Link')).atIndex(0).tap();
// TODO: test clipboard
});
it('should copy message', async () => {
await element(by[textMatcher](`${data.random}message`))
.atIndex(0)
.longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by[textMatcher]('Copy')).atIndex(0).tap();
// TODO: test clipboard
});
it('should react to message', async () => {
await waitFor(element(by[textMatcher](`${data.random}message`)))
.toExist()
.withTimeout(60000);
await element(by[textMatcher](`${data.random}message`))
.atIndex(0)
.tap();
await element(by[textMatcher](`${data.random}message`))
.atIndex(0)
.longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by.id('add-reaction')).tap();
await waitFor(element(by.id('emoji-picker-tab-emoji')))
.toExist()
.withTimeout(2000);
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 1);
await element(by.id('emoji-picker-tab-emoji')).tap();
await waitFor(element(by.id('emoji-grinning')))
.toExist()
.withTimeout(10000);
await element(by.id('emoji-grinning')).tap();
await waitFor(element(by.id('message-reaction-:grinning:')))
.toExist()
.withTimeout(60000);
});
it('should ask for review', async () => {
await dismissReviewNag(); // TODO: Create a proper test for this elsewhere.
});
it('should search emojis in the reaction picker and react', async () => {
await element(by[textMatcher](`${data.random}message`))
.atIndex(0)
.longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by.id('add-reaction')).tap();
await waitFor(element(by.id('emoji-searchbar-input')))
.toExist()
.withTimeout(2000);
await element(by.id('emoji-searchbar-input')).typeText('laughing');
await waitFor(element(by.id('emoji-laughing')))
.toExist()
.withTimeout(4000);
await element(by.id('emoji-laughing')).tap();
await waitFor(element(by.id('message-reaction-:laughing:')))
.toExist()
.withTimeout(60000);
});
it('should react to message with frequently used emoji', async () => {
await element(by[textMatcher](`${data.random}message`))
.atIndex(0)
.longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await waitFor(element(by.id('message-actions-emoji-upside_down')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('message-actions-emoji-upside_down')).tap();
await waitFor(element(by.id('message-reaction-:upside_down:')))
.toBeVisible()
.withTimeout(60000);
});
it('should show reaction picker on add reaction button pressed and have frequently used emoji', async () => {
await element(by.id('message-add-reaction')).tap();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 1);
await waitFor(element(by.id('emoji-upside_down')))
.toExist()
.withTimeout(4000);
await waitFor(element(by.id('emoji-picker-tab-emoji')))
.toExist()
.withTimeout(2000);
await element(by.id('emoji-picker-tab-emoji')).tap();
await waitFor(element(by.id('emoji-wink')))
.toExist()
.withTimeout(10000);
await element(by.id('emoji-wink')).tap();
await waitFor(element(by.id('message-reaction-:wink:')))
.toExist()
.withTimeout(60000);
});
it('should open/close reactions list', async () => {
await element(by.id('message-reaction-:grinning:')).longPress();
await waitFor(element(by.id('reactionsList')))
.toExist()
.withTimeout(4000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.5);
});
it('should remove reaction', async () => {
await element(by.id('message-reaction-:grinning:')).tap();
await waitFor(element(by.id('message-reaction-:grinning:')))
.not.toExist()
.withTimeout(60000);
});
it('should edit message', async () => {
await mockMessage('edit');
await element(by[textMatcher](`${data.random}edit`))
.atIndex(0)
.longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by[textMatcher]('Edit')).atIndex(0).tap();
await element(by.id('messagebox-input')).replaceText(`${data.random}edited`);
await element(by.id('messagebox-send-message')).tap();
await waitFor(element(by[textMatcher](`${data.random}edited`)).atIndex(0))
.toExist()
.withTimeout(60000);
await waitFor(element(by.id(`${data.random}edited-edited`)))
.toExist()
.withTimeout(60000);
});
it('should quote message', async () => {
await mockMessage('quote');
await element(by[textMatcher](`${data.random}quote`))
.atIndex(0)
.longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by[textMatcher]('Quote')).atIndex(0).tap();
await element(by.id('messagebox-input')).replaceText(`${data.random}quoted`);
await waitFor(element(by.id('messagebox-send-message')))
.toExist()
.withTimeout(2000);
await element(by.id('messagebox-send-message')).tap();
// TODO: test if quote was sent
});
it('should delete message', async () => {
await mockMessage('delete');
await waitFor(element(by[textMatcher](`${data.random}delete`)).atIndex(0)).toBeVisible();
await element(by[textMatcher](`${data.random}delete`))
.atIndex(0)
.longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await waitFor(element(by[textMatcher]('Delete')))
.toExist()
.withTimeout(1000);
await element(by[textMatcher]('Delete')).atIndex(0).tap();
const deleteAlertMessage = 'You will not be able to recover this message!';
await waitFor(element(by[textMatcher](deleteAlertMessage)).atIndex(0))
.toExist()
.withTimeout(10000);
await element(by[textMatcher]('Delete').and(by.type(alertButtonType))).tap();
await waitFor(element(by[textMatcher](`${data.random}delete`)).atIndex(0))
.toNotExist()
.withTimeout(2000);
await tapBack();
});
it('should reply in DM to another user', async () => {
const channelName = data.userRegularChannels.detoxpublic.name;
const stringToReply = 'Message to reply in DM';
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
await navigateToRoom(channelName);
await sendMessage(data.users.alternate, channelName, stringToReply);
await waitFor(element(by[textMatcher](stringToReply)).atIndex(0))
.toBeVisible()
.withTimeout(3000);
await element(by[textMatcher](stringToReply)).atIndex(0).longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await waitFor(element(by[textMatcher]('Reply in Direct Message')).atIndex(0))
.toExist()
.withTimeout(6000);
await element(by[textMatcher]('Reply in Direct Message')).atIndex(0).tap();
await waitFor(element(by.id(`room-view-title-${data.users.alternate.username}`)))
.toExist()
.withTimeout(6000);
await element(by.id('messagebox-input')).replaceText(`${data.random} replied in dm`);
await waitFor(element(by.id('messagebox-send-message')))
.toExist()
.withTimeout(2000);
await element(by.id('messagebox-send-message')).tap();
});
});
});
});

View File

@ -0,0 +1,659 @@
import { expect } from 'detox';
import data from '../../data';
import {
navigateToLogin,
login,
tapBack,
sleep,
searchRoom,
mockMessage,
starMessage,
pinMessage,
platformTypes,
TTextMatcher
} from '../../helpers/app';
const { sendMessage } = require('../../helpers/data_setup');
async function navigateToRoomActions(type: string) {
let room;
if (type === 'd') {
room = 'rocket.cat';
} else {
room = data.groups.private.name;
}
await searchRoom(room);
await element(by.id(`rooms-list-view-item-${room}`)).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(2000);
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(5000);
}
async function backToActions() {
await tapBack();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(2000);
}
async function backToRoomsList() {
await tapBack();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(2000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(2000);
}
async function waitForToast() {
await sleep(1000);
}
describe('Room actions screen', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
});
describe('Render', () => {
describe('Direct', () => {
beforeAll(async () => {
await navigateToRoomActions('d');
});
it('should have room actions screen', async () => {
await expect(element(by.id('room-actions-view'))).toExist();
});
it('should have info', async () => {
await expect(element(by.id('room-actions-info'))).toExist();
});
// it('should have voice', async() => {
// await expect(element(by.id('room-actions-voice'))).toExist();
// });
// it('should have video', async() => {
// await expect(element(by.id('room-actions-video'))).toExist();
// });
it('should have files', async () => {
await expect(element(by.id('room-actions-files'))).toExist();
});
it('should have mentions', async () => {
await expect(element(by.id('room-actions-mentioned'))).toExist();
});
it('should have starred', async () => {
await expect(element(by.id('room-actions-starred'))).toExist();
});
it('should have share', async () => {
await waitFor(element(by.id('room-actions-share'))).toExist();
await expect(element(by.id('room-actions-share'))).toExist();
});
it('should have pinned', async () => {
await waitFor(element(by.id('room-actions-pinned'))).toExist();
await expect(element(by.id('room-actions-pinned'))).toExist();
});
it('should have notifications', async () => {
await waitFor(element(by.id('room-actions-notifications'))).toExist();
await expect(element(by.id('room-actions-notifications'))).toExist();
});
it('should have block user', async () => {
await waitFor(element(by.id('room-actions-block-user'))).toExist();
await expect(element(by.id('room-actions-block-user'))).toExist();
});
afterAll(async () => {
await backToRoomsList();
});
});
describe('Channel/Group', () => {
beforeAll(async () => {
await navigateToRoomActions('c');
});
it('should have room actions screen', async () => {
await expect(element(by.id('room-actions-view'))).toExist();
});
it('should have info', async () => {
await expect(element(by.id('room-actions-info'))).toExist();
});
// it('should have voice', async() => {
// await expect(element(by.id('room-actions-voice'))).toExist();
// });
// it('should have video', async() => {
// await expect(element(by.id('room-actions-video'))).toExist();
// });
it('should have members', async () => {
await expect(element(by.id('room-actions-members'))).toExist();
});
it('should have files', async () => {
await expect(element(by.id('room-actions-files'))).toExist();
});
it('should have mentions', async () => {
await expect(element(by.id('room-actions-mentioned'))).toExist();
});
it('should have starred', async () => {
await expect(element(by.id('room-actions-starred'))).toExist();
});
it('should have share', async () => {
await waitFor(element(by.id('room-actions-share'))).toExist();
await expect(element(by.id('room-actions-share'))).toExist();
});
it('should have pinned', async () => {
await waitFor(element(by.id('room-actions-pinned'))).toExist();
await expect(element(by.id('room-actions-pinned'))).toExist();
});
it('should have notifications', async () => {
await waitFor(element(by.id('room-actions-notifications'))).toExist();
await expect(element(by.id('room-actions-notifications'))).toExist();
});
it('should have leave channel', async () => {
await waitFor(element(by.id('room-actions-leave-channel'))).toExist();
await expect(element(by.id('room-actions-leave-channel'))).toExist();
});
});
});
describe('Usage', () => {
describe('Common', () => {
it('should show mentioned messages', async () => {
await element(by.id('room-actions-mentioned')).tap();
await waitFor(element(by.id('mentioned-messages-view')))
.toExist()
.withTimeout(2000);
await backToActions();
});
it('should show starred message and unstar it', async () => {
// Go back to room and send a message
await tapBack();
await mockMessage('messageToStar');
// Star the message
await starMessage('messageToStar');
// Back into Room Actions
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(5000);
// Go to starred messages
await element(by.id('room-actions-view')).swipe('up');
await waitFor(element(by.id('room-actions-starred'))).toExist();
await element(by.id('room-actions-starred')).tap();
await waitFor(element(by.id('starred-messages-view')))
.toExist()
.withTimeout(2000);
await waitFor(element(by[textMatcher](`${data.random}messageToStar`).withAncestor(by.id('starred-messages-view'))))
.toExist()
.withTimeout(60000);
// Unstar message
await element(by[textMatcher](`${data.random}messageToStar`))
.atIndex(0)
.longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by[textMatcher]('Unstar')).atIndex(0).tap();
await waitFor(element(by[textMatcher](`${data.random}messageToStar`).withAncestor(by.id('starred-messages-view'))))
.toBeNotVisible()
.withTimeout(60000);
await backToActions();
});
it('should show pinned message and unpin it', async () => {
// Go back to room and send a message
await tapBack();
await mockMessage('messageToPin');
// Pin the message
await pinMessage('messageToPin');
// Back into Room Actions
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(5000);
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-pinned'))).toExist();
await element(by.id('room-actions-pinned')).tap();
await waitFor(element(by.id('pinned-messages-view')))
.toExist()
.withTimeout(2000);
await waitFor(element(by[textMatcher](`${data.random}messageToPin`).withAncestor(by.id('pinned-messages-view'))))
.toExist()
.withTimeout(6000);
await element(by[textMatcher](`${data.random}messageToPin`).withAncestor(by.id('pinned-messages-view')))
.atIndex(0)
.longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by[textMatcher]('Unpin')).atIndex(0).tap();
await waitFor(element(by[textMatcher](`${data.random}messageToPin`).withAncestor(by.id('pinned-messages-view'))))
.not.toExist()
.withTimeout(6000);
await backToActions();
});
});
describe('Notification', () => {
it('should navigate to notification preference view', async () => {
await waitFor(element(by.id('room-actions-scrollview')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-notifications')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-notifications')).tap();
await waitFor(element(by.id('notification-preference-view')))
.toExist()
.withTimeout(2000);
});
it('should have receive notification option', async () => {
await expect(element(by.id('notification-preference-view-receive-notification'))).toExist();
});
it('should have show unread count option', async () => {
await expect(element(by.id('notification-preference-view-mark-as-unread'))).toExist();
});
it('should have notification alert option', async () => {
await expect(element(by.id('notification-preference-view-alert'))).toExist();
});
it('should have push notification option', async () => {
await waitFor(element(by.id('notification-preference-view-push-notification')))
.toExist()
.withTimeout(4000);
});
it('should have notification sound option', async () => {
await waitFor(element(by.id('notification-preference-view-sound')))
.toExist()
.withTimeout(4000);
});
it('should have email alert option', async () => {
await waitFor(element(by.id('notification-preference-view-email-alert')))
.toExist()
.withTimeout(4000);
});
afterAll(async () => {
await backToActions();
});
});
describe('Channel/Group', () => {
// Currently, there's no way to add more owners to the room
// So we test only for the 'You are the last owner...' message
const user = data.users.alternate;
it('should tap on leave channel and raise alert', async () => {
await waitFor(element(by.id('room-actions-scrollview')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-leave-channel')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-leave-channel')).tap();
await waitFor(element(by[textMatcher]('Yes, leave it!')))
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Yes, leave it!').and(by.type(alertButtonType))).tap();
await waitFor(element(by[textMatcher]('You are the last owner. Please set new owner before leaving the room.')))
.toExist()
.withTimeout(8000);
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(2000);
});
it('should add users to the room', async () => {
await waitFor(element(by.id('room-actions-members')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-members')).tap();
await waitFor(element(by.id('room-members-view')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id('room-actions-add-user')))
.toExist()
.withTimeout(4000);
await element(by.id('room-actions-add-user')).tap();
// add rocket.cat
const rocketCat = 'rocket.cat';
await waitFor(element(by.id(`select-users-view-item-${rocketCat}`)))
.toExist()
.withTimeout(10000);
await element(by.id(`select-users-view-item-${rocketCat}`)).tap();
await waitFor(element(by.id(`selected-user-${rocketCat}`)))
.toExist()
.withTimeout(5000);
await waitFor(element(by.id('select-users-view-search')))
.toExist()
.withTimeout(4000);
await element(by.id('select-users-view-search')).tap();
await element(by.id('select-users-view-search')).replaceText(user.username);
await sleep(300);
await waitFor(element(by.id(`select-users-view-item-${user.username}`)))
.toExist()
.withTimeout(10000);
await element(by.id(`select-users-view-item-${user.username}`)).tap();
await waitFor(element(by.id(`selected-user-${user.username}`)))
.toExist()
.withTimeout(5000);
await element(by.id('selected-users-view-submit')).tap();
await sleep(300);
await backToActions();
});
describe('Room Members', () => {
beforeAll(async () => {
await waitFor(element(by.id('room-actions-members')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-members')).tap();
await waitFor(element(by.id('room-members-view')))
.toExist()
.withTimeout(2000);
});
const openActionSheet = async (username: string) => {
await waitFor(element(by.id(`room-members-view-item-${username}`)))
.toExist()
.withTimeout(5000);
let n = 0;
while (n < 3) {
// Max tries three times, in case it does not register the click
try {
await element(by.id(`room-members-view-item-${username}`)).tap();
await sleep(300);
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(5000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up');
return;
} catch (e) {
n += 1;
}
}
};
const closeActionSheet = async () => {
await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.6);
await waitFor(element(by.id('action-sheet')))
.toBeNotVisible()
.withTimeout(1000);
await sleep(100);
};
it('should show all users', async () => {
await waitFor(element(by.id('room-members-view-filter')))
.toExist()
.withTimeout(10000);
await element(by.id('room-members-view-filter')).tap();
await waitFor(element(by.id('room-members-view-toggle-status-all')))
.toExist()
.withTimeout(2000);
await element(by.id('room-members-view-toggle-status-all')).tap();
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist()
.withTimeout(60000);
await tapBack();
});
it('should filter user', async () => {
await waitFor(element(by.id('room-actions-members')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-members')).tap();
await element(by.id('room-members-view-filter')).tap();
await waitFor(element(by.id('room-members-view-toggle-status-all')))
.toExist()
.withTimeout(2000);
await element(by.id('room-members-view-toggle-status-all')).tap();
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist()
.withTimeout(60000);
await element(by.id('room-members-view-search')).replaceText('rocket');
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toBeNotVisible()
.withTimeout(60000);
await element(by.id('room-members-view-search')).tap();
await element(by.id('room-members-view-search')).clearText();
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist()
.withTimeout(60000);
});
it('should remove user from room', async () => {
await openActionSheet('rocket.cat');
await waitFor(element(by[textMatcher]('Remove from room')))
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Remove from room')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('Are you sure?')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Yes, remove user!').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('room-members-view-item-rocket.cat')))
.toBeNotVisible()
.withTimeout(60000);
});
it('should clear search', async () => {
await element(by.id('room-members-view-search')).tap();
await element(by.id('room-members-view-search')).clearText();
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist()
.withTimeout(60000);
});
it('should set/remove as owner', async () => {
await openActionSheet(user.username);
await element(by.id('action-sheet-set-owner')).tap();
await waitForToast();
await openActionSheet(user.username);
await waitFor(element(by.id('action-sheet-set-owner-checked')))
.toBeVisible()
.withTimeout(6000);
await element(by.id('action-sheet-set-owner')).tap();
await waitForToast();
await openActionSheet(user.username);
await waitFor(element(by.id('action-sheet-set-owner-unchecked')))
.toBeVisible()
.withTimeout(60000);
await closeActionSheet();
});
it('should set/remove as leader', async () => {
await openActionSheet(user.username);
await element(by.id('action-sheet-set-leader')).tap();
await waitForToast();
await openActionSheet(user.username);
await waitFor(element(by.id('action-sheet-set-leader-checked')))
.toBeVisible()
.withTimeout(6000);
await element(by.id('action-sheet-set-leader')).tap();
await waitForToast();
await openActionSheet(user.username);
await waitFor(element(by.id('action-sheet-set-owner-unchecked')))
.toBeVisible()
.withTimeout(60000);
await closeActionSheet();
});
it('should set/remove as moderator', async () => {
await openActionSheet(user.username);
await element(by.id('action-sheet-set-moderator')).tap();
await waitForToast();
await openActionSheet(user.username);
await waitFor(element(by.id('action-sheet-set-moderator-checked')))
.toBeVisible()
.withTimeout(6000);
await element(by.id('action-sheet-set-moderator')).tap();
await waitForToast();
await openActionSheet(user.username);
await waitFor(element(by.id('action-sheet-set-moderator-unchecked')))
.toBeVisible()
.withTimeout(60000);
await closeActionSheet();
});
it('should set/remove as mute', async () => {
await openActionSheet(user.username);
await element(by[textMatcher]('Mute')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('Are you sure?')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Mute').and(by.type(alertButtonType))).tap();
await waitForToast();
await openActionSheet(user.username);
await element(by[textMatcher]('Unmute')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('Are you sure?')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Unmute').and(by.type(alertButtonType))).tap();
await waitForToast();
await openActionSheet(user.username);
// Tests if Remove as mute worked
await waitFor(element(by[textMatcher]('Mute')))
.toExist()
.withTimeout(5000);
await closeActionSheet();
});
it('should ignore user', async () => {
const message = `${data.random}ignoredmessagecontent`;
const channelName = `#${data.groups.private.name}`;
await sendMessage(user, channelName, message);
await openActionSheet(user.username);
await element(by[textMatcher]('Ignore')).atIndex(0).tap();
await waitForToast();
await backToActions();
await tapBack();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(60000);
await waitFor(element(by[textMatcher]('Message ignored. Tap to display it.')).atIndex(0))
.toExist()
.withTimeout(60000);
await element(by[textMatcher]('Message ignored. Tap to display it.')).atIndex(0).tap();
await waitFor(element(by[textMatcher](message)).atIndex(0))
.toExist()
.withTimeout(60000);
await element(by[textMatcher](message)).atIndex(0).tap();
});
it('should navigate to direct message', async () => {
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(5000);
await waitFor(element(by.id('room-actions-members')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-members')).tap();
await waitFor(element(by.id('room-members-view')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id('room-members-view-filter')))
.toExist()
.withTimeout(10000);
await element(by.id('room-members-view-filter')).tap();
await waitFor(element(by.id('room-members-view-toggle-status-all')))
.toExist()
.withTimeout(2000);
await element(by.id('room-members-view-toggle-status-all')).tap();
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist()
.withTimeout(60000);
await openActionSheet(user.username);
await element(by[textMatcher]('Direct message')).atIndex(0).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(60000);
await waitFor(element(by.id(`room-view-title-${user.username}`)))
.toExist()
.withTimeout(60000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(2000);
});
});
});
describe('Direct', () => {
beforeAll(async () => {
await navigateToRoomActions('d');
});
it('should block/unblock user', async () => {
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-block-user'))).toExist();
await element(by.id('room-actions-block-user')).tap();
await waitFor(element(by[textMatcher]('Unblock user')))
.toExist()
.withTimeout(60000);
await element(by.id('room-actions-block-user')).tap();
await waitFor(element(by[textMatcher]('Block user')))
.toExist()
.withTimeout(60000);
});
});
});
});

View File

@ -0,0 +1,225 @@
import { expect } from 'detox';
import { TTextMatcher, navigateToLogin, login, mockMessage, tapBack, searchRoom, platformTypes } from '../../helpers/app';
import data from '../../data';
const channel = data.groups.private.name;
const navigateToRoom = async () => {
await searchRoom(channel);
await element(by.id(`rooms-list-view-item-${channel}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
};
describe('Discussion', () => {
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true });
({ textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
it('should create discussion from NewMessageView', async () => {
const discussionName = `${data.random} Discussion NewMessageView`;
await waitFor(element(by.id('rooms-list-view-create-channel')))
.toExist()
.withTimeout(2000);
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view')))
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Discussion')).atIndex(0).tap();
await waitFor(element(by.id('create-discussion-view')))
.toExist()
.withTimeout(60000);
await expect(element(by.id('create-discussion-view'))).toExist();
await element(by[textMatcher]('Select a Channel...')).tap();
await element(by.id('multi-select-search')).replaceText(`${channel}`);
await waitFor(element(by.id(`multi-select-item-${channel}`)))
.toExist()
.withTimeout(10000);
await element(by.id(`multi-select-item-${channel}`)).tap();
await element(by.id('multi-select-discussion-name')).replaceText(discussionName);
await waitFor(element(by.id('create-discussion-submit')))
.toExist()
.withTimeout(10000);
await element(by.id('create-discussion-submit')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(10000);
await waitFor(element(by.id(`room-view-title-${discussionName}`)))
.toExist()
.withTimeout(5000);
await tapBack();
await waitFor(element(by.id(`rooms-list-view-item-${discussionName}`)))
.toExist()
.withTimeout(5000);
});
it('should create discussion from action button', async () => {
const discussionName = `${data.random} Discussion Action Button`;
await navigateToRoom();
await element(by.id('messagebox-actions')).tap();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Create Discussion')).atIndex(0).tap();
await waitFor(element(by.id('create-discussion-view')))
.toExist()
.withTimeout(2000);
await element(by.id('multi-select-discussion-name')).replaceText(discussionName);
await waitFor(element(by.id('create-discussion-submit')))
.toExist()
.withTimeout(10000);
await element(by.id('create-discussion-submit')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(10000);
await waitFor(element(by.id(`room-view-title-${discussionName}`)))
.toExist()
.withTimeout(5000);
});
describe('Create Discussion from action sheet', () => {
it('should send a message', async () => {
await waitFor(element(by.id('messagebox')))
.toBeVisible()
.withTimeout(60000);
await mockMessage('message');
});
it('should create discussion', async () => {
const discussionName = `${data.random}message`;
await element(by[textMatcher](discussionName)).atIndex(0).longPress();
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Start a Discussion')).atIndex(0).tap();
await waitFor(element(by.id('create-discussion-view')))
.toExist()
.withTimeout(2000);
await element(by.id('create-discussion-submit')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(10000);
await waitFor(element(by.id(`room-view-title-${discussionName}`)))
.toExist()
.withTimeout(5000);
});
});
describe('Check RoomActionsView render', () => {
it('should navigate to RoomActionsView', async () => {
await waitFor(element(by.id('room-header')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toBeVisible()
.withTimeout(5000);
});
it('should have room actions screen', async () => {
await expect(element(by.id('room-actions-view'))).toBeVisible();
});
it('should have info', async () => {
await expect(element(by.id('room-actions-info'))).toBeVisible();
});
it('should have members', async () => {
await expect(element(by.id('room-actions-members'))).toBeVisible();
});
it('should have files', async () => {
await expect(element(by.id('room-actions-files'))).toBeVisible();
});
it('should have mentions', async () => {
await expect(element(by.id('room-actions-mentioned'))).toBeVisible();
});
it('should have starred', async () => {
await element(by.id('room-actions-scrollview')).swipe('up', 'slow', 0.5);
await expect(element(by.id('room-actions-starred'))).toBeVisible();
});
it('should have share', async () => {
await element(by.id('room-actions-scrollview')).swipe('up');
await expect(element(by.id('room-actions-share'))).toBeVisible();
});
it('should have pinned', async () => {
await expect(element(by.id('room-actions-pinned'))).toBeVisible();
});
it('should not have notifications', async () => {
await expect(element(by.id('room-actions-notifications'))).toBeVisible();
});
it('should not have leave channel', async () => {
await expect(element(by.id('room-actions-leave-channel'))).toBeVisible();
});
it('should navigate to RoomActionView', async () => {
await element(by.id('room-actions-scrollview')).swipe('down');
await expect(element(by.id('room-actions-info'))).toBeVisible();
await element(by.id('room-actions-info')).tap();
await waitFor(element(by.id('room-info-view')))
.toExist()
.withTimeout(60000);
await expect(element(by.id('room-info-view'))).toExist();
});
it('should have edit button', async () => {
await expect(element(by.id('room-info-view-edit-button'))).toBeVisible();
});
});
describe('Open Discussion from DiscussionsView', () => {
const discussionName = `${data.random}message`;
it('should go back to main room', async () => {
await tapBack();
await waitFor(element(by.id('room-actions-view')))
.toBeVisible()
.withTimeout(5000);
await tapBack();
await waitFor(element(by.id(`room-view-title-${discussionName}`)))
.toExist()
.withTimeout(5000);
await tapBack();
await navigateToRoom();
});
it('should navigate to DiscussionsView', async () => {
await waitFor(element(by.id(`room-view-title-${channel}`)))
.toExist()
.withTimeout(5000);
await waitFor(element(by.id('room-header')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-discussions')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('room-actions-discussions')).tap();
await waitFor(element(by.id('discussions-view')))
.toBeVisible()
.withTimeout(5000);
});
it('should navigate to discussion', async () => {
const discussionName = `${data.random} Discussion NewMessageView`;
await waitFor(element(by.label(discussionName)).atIndex(0))
.toExist()
.withTimeout(5000);
await element(by.label(discussionName)).atIndex(0).tap();
await waitFor(element(by.id(`room-view-title-${discussionName}`)))
.toExist()
.withTimeout(5000);
});
});
});

View File

@ -0,0 +1,240 @@
import { expect } from 'detox';
import data from '../../data';
import {
navigateToLogin,
login,
mockMessage,
tapBack,
sleep,
searchRoom,
platformTypes,
dismissReviewNag,
TTextMatcher
} from '../../helpers/app';
async function navigateToRoom(roomName: string) {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
await searchRoom(`${roomName}`);
await element(by.id(`rooms-list-view-item-${roomName}`)).tap();
await waitFor(element(by.id(`room-view-title-${roomName}`)))
.toExist()
.withTimeout(5000);
}
describe('Threads', () => {
const mainRoom = data.groups.private.name;
let textMatcher: TTextMatcher;
beforeAll(async () => {
({ textMatcher } = platformTypes[device.getPlatform()]);
await navigateToRoom(mainRoom);
});
describe('Render', () => {
it('should have room screen', async () => {
await waitFor(element(by.id(`room-view-title-${mainRoom}`)))
.toExist()
.withTimeout(5000);
});
// Render - Header
describe('Header', () => {
it('should have actions button ', async () => {
await expect(element(by.id('room-header'))).toExist();
});
it('should have threads button ', async () => {
await expect(element(by.id('room-view-header-threads'))).toExist();
});
});
// Render - Messagebox
describe('Messagebox', () => {
it('should have messagebox', async () => {
await expect(element(by.id('messagebox'))).toExist();
});
it('should have open emoji button', async () => {
if (device.getPlatform() === 'android') {
await expect(element(by.id('messagebox-open-emoji'))).toExist();
}
});
it('should have message input', async () => {
await expect(element(by.id('messagebox-input'))).toExist();
});
it('should have audio button', async () => {
await expect(element(by.id('messagebox-send-audio'))).toExist();
});
it('should have actions button', async () => {
await expect(element(by.id('messagebox-actions'))).toExist();
});
});
});
describe('Usage', () => {
describe('Thread', () => {
const thread = `${data.random}thread`;
it('should create thread', async () => {
await mockMessage('thread');
await element(by[textMatcher](thread)).atIndex(0).longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by[textMatcher]('Reply in Thread')).atIndex(0).tap();
await element(by.id('messagebox-input')).replaceText('replied');
await waitFor(element(by.id('messagebox-send-message')))
.toExist()
.withTimeout(2000);
await element(by.id('messagebox-send-message')).tap();
await waitFor(element(by.id(`message-thread-button-${thread}`)))
.toExist()
.withTimeout(5000);
await expect(element(by.id(`message-thread-button-${thread}`))).toExist();
});
it('should navigate to thread from button', async () => {
await element(by.id(`message-thread-button-${thread}`)).tap();
await waitFor(element(by.id(`room-view-title-${thread}`)))
.toExist()
.withTimeout(5000);
await expect(element(by.id(`room-view-title-${thread}`))).toExist();
await tapBack();
});
it('should toggle follow thread', async () => {
await element(by.id(`message-thread-button-${thread}`)).tap();
await waitFor(element(by.id(`room-view-title-${thread}`)))
.toExist()
.withTimeout(5000);
await element(by.id('room-view-header-unfollow')).tap();
await waitFor(element(by.id('room-view-header-follow')))
.toExist()
.withTimeout(60000);
await expect(element(by.id('room-view-header-follow'))).toExist();
await element(by.id('room-view-header-follow')).tap();
await waitFor(element(by.id('room-view-header-unfollow')))
.toExist()
.withTimeout(60000);
await expect(element(by.id('room-view-header-unfollow'))).toExist();
});
it('should send message in thread only', async () => {
const messageText = 'threadonly';
await mockMessage(messageText, true);
await tapBack();
await waitFor(element(by.id(`room-view-title-${data.random}thread`)))
.not.toExist()
.withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${mainRoom}`)))
.toExist()
.withTimeout(5000);
await waitFor(element(by[textMatcher](`${data.random}${messageText}`)).atIndex(0))
.toNotExist()
.withTimeout(2000);
});
it('should mark send to channel and show on main channel', async () => {
const messageText = 'sendToChannel';
await element(by.id(`message-thread-button-${thread}`)).tap();
await waitFor(element(by.id('messagebox-input-thread')))
.toExist()
.withTimeout(5000);
await element(by.id('messagebox-input-thread')).replaceText(messageText);
await element(by.id('messagebox-send-to-channel')).tap();
await element(by.id('messagebox-send-message')).tap();
await tapBack();
await waitFor(element(by.id(`room-view-title-${data.random}thread`)))
.not.toExist()
.withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${mainRoom}`)))
.toExist()
.withTimeout(5000);
await waitFor(element(by[textMatcher](messageText)).atIndex(0))
.toExist()
.withTimeout(2000);
});
it('should navigate to thread from thread name', async () => {
const messageText = 'navthreadname';
await mockMessage('dummymessagebetweenthethread'); // TODO: Create a proper test for this elsewhere.
await dismissReviewNag();
await element(by.id(`message-thread-button-${thread}`)).tap();
await waitFor(element(by.id('messagebox-input-thread')))
.toExist()
.withTimeout(5000);
await element(by.id('messagebox-input-thread')).replaceText(messageText);
await element(by.id('messagebox-send-to-channel')).tap();
await element(by.id('messagebox-send-message')).tap();
await tapBack();
await waitFor(element(by.id(`room-view-title-${data.random}thread`)))
.not.toExist()
.withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${mainRoom}`)))
.toExist()
.withTimeout(5000);
await waitFor(element(by.id(`message-thread-replied-on-${thread}`)))
.toBeVisible()
.withTimeout(2000);
await element(by.id(`message-thread-replied-on-${thread}`)).tap();
await waitFor(element(by.id(`room-view-title-${thread}`)))
.toExist()
.withTimeout(5000);
await expect(element(by.id(`room-view-title-${thread}`))).toExist();
await sleep(2000);
await tapBack();
});
it('should navigate to thread from threads view', async () => {
await waitFor(element(by.id('room-view-header-threads')))
.toExist()
.withTimeout(1000);
await element(by.id('room-view-header-threads')).tap();
await waitFor(element(by.id('thread-messages-view')))
.toExist()
.withTimeout(5000);
await element(by.id(`thread-messages-view-${thread}`))
.atIndex(0)
.tap();
await waitFor(element(by.id(`room-view-title-${thread}`)))
.toExist()
.withTimeout(5000);
await expect(element(by.id(`room-view-title-${thread}`))).toExist();
await tapBack();
await waitFor(element(by.id('thread-messages-view')))
.toExist()
.withTimeout(5000);
await expect(element(by.id('thread-messages-view'))).toExist();
await tapBack();
});
it('should draft thread message', async () => {
await element(by.id(`message-thread-button-${thread}`)).tap();
await waitFor(element(by.id(`room-view-title-${thread}`)))
.toExist()
.withTimeout(5000);
await element(by.id('messagebox-input-thread')).replaceText(`${thread}draft`);
await tapBack();
await element(by.id(`message-thread-button-${thread}`)).tap();
await waitFor(element(by.id(`room-view-title-${thread}`)))
.toExist()
.withTimeout(5000);
await expect(element(by.id('messagebox-input-thread'))).toHaveText(`${thread}draft`);
await element(by.id('messagebox-input-thread')).clearText();
await tapBack();
await element(by.id(`message-thread-button-${thread}`)).tap();
await waitFor(element(by.id(`room-view-title-${thread}`)))
.toExist()
.withTimeout(5000);
await expect(element(by.id('messagebox-input-thread'))).toHaveText('');
});
});
});
});

View File

@ -0,0 +1,61 @@
import data from '../../data';
import { navigateToLogin, login, platformTypes, TTextMatcher } from '../../helpers/app';
describe('Group DM', () => {
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
describe('Create Group DM', () => {
beforeAll(async () => {
await waitFor(element(by.id('rooms-list-view-create-channel')))
.toExist()
.withTimeout(2000);
await element(by.id('rooms-list-view-create-channel')).tap();
});
describe('Render', () => {
it('should have new message screen', async () => {
await waitFor(element(by.id('new-message-view')))
.toBeVisible()
.withTimeout(2000);
});
it('should have search input', async () => {
await waitFor(element(by.id('new-message-view-search')))
.toBeVisible()
.withTimeout(2000);
});
});
describe('Usage', () => {
it('should navigate to create DM', async () => {
await element(by[textMatcher]('Direct message')).atIndex(0).tap();
});
it('should add users', async () => {
await element(by.id('select-users-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id('select-users-view-item-rocket.cat')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('select-users-view-item-rocket.cat')).tap();
await element(by.id('select-users-view-search')).replaceText(data.users.existing.username);
await waitFor(element(by.id(`select-users-view-item-${data.users.existing.username}`)))
.toBeVisible()
.withTimeout(10000);
await element(by.id(`select-users-view-item-${data.users.existing.username}`)).tap();
await element(by.id('selected-users-view-submit')).tap();
});
it('check Group DM exist', async () => {
await waitFor(element(by.id(`room-view-title-${data.users.existing.username}, rocket.cat`)))
.toExist()
.withTimeout(10000);
});
});
});
});

View File

@ -0,0 +1,50 @@
import { expect } from 'detox';
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 element(by.id(`rooms-list-view-item-${user}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
}
describe('Mark as unread', () => {
const user = data.users.alternate.username;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
await navigateToRoom(user);
});
describe('Usage', () => {
describe('Mark message as unread', () => {
it('should mark message as unread', async () => {
const message = `${data.random}message-mark-as-unread`;
const channelName = `@${data.users.regular.username}`;
await sendMessage(data.users.alternate, channelName, message);
await waitFor(element(by[textMatcher](message)).atIndex(0))
.toExist()
.withTimeout(30000);
await sleep(300);
await element(by[textMatcher](message)).atIndex(0).longPress();
await waitFor(element(by.id('action-sheet-handle')))
.toBeVisible()
.withTimeout(3000);
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by[textMatcher]('Mark Unread')).atIndex(0).tap();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(5000);
await expect(element(by.id(`rooms-list-view-item-${data.users.alternate.username}`))).toExist();
});
});
});
});

View File

@ -0,0 +1,324 @@
import { expect } from 'detox';
import data from '../../data';
import { navigateToLogin, login, tapBack, sleep, searchRoom, platformTypes, TTextMatcher } from '../../helpers/app';
const privateRoomName = data.groups.private.name;
async function navigateToRoomInfo(type: string) {
let room;
if (type === 'd') {
room = 'rocket.cat';
} else {
room = privateRoomName;
}
await searchRoom(room);
await element(by.id(`rooms-list-view-item-${room}`)).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(2000);
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(5000);
await element(by.id('room-actions-info')).tap();
await waitFor(element(by.id('room-info-view')))
.toExist()
.withTimeout(2000);
}
async function swipe(direction: Detox.Direction) {
await element(by.id('room-info-edit-view-list')).swipe(direction, 'fast', 0.8);
}
async function waitForToast() {
await sleep(300);
}
describe('Room info screen', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
describe('Direct', () => {
beforeAll(async () => {
await navigateToRoomInfo('d');
});
it('should navigate to room info', async () => {
await expect(element(by.id('room-info-view'))).toExist();
await expect(element(by.id('room-info-view-name'))).toExist();
});
afterAll(async () => {
await tapBack();
await tapBack();
await tapBack();
});
});
describe('Channel/Group', () => {
beforeAll(async () => {
await navigateToRoomInfo('c');
});
describe('Render', () => {
it('should have room info view', async () => {
await expect(element(by.id('room-info-view'))).toExist();
});
it('should have name', async () => {
await expect(element(by.id('room-info-view-name'))).toExist();
});
it('should have description', async () => {
await expect(element(by[textMatcher]('Description'))).toExist();
});
it('should have topic', async () => {
await expect(element(by[textMatcher]('Topic'))).toExist();
});
it('should have announcement', async () => {
await expect(element(by[textMatcher]('Announcement'))).toExist();
});
it('should have edit button', async () => {
await expect(element(by.id('room-info-view-edit-button'))).toExist();
});
});
describe('Render Edit', () => {
beforeAll(async () => {
await waitFor(element(by.id('room-info-view-edit-button')))
.toExist()
.withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view')))
.toExist()
.withTimeout(2000);
});
it('should have room info edit view', async () => {
await expect(element(by.id('room-info-edit-view'))).toExist();
});
it('should have name input', async () => {
await expect(element(by.id('room-info-edit-view-name'))).toExist();
});
it('should have description input', async () => {
await expect(element(by.id('room-info-edit-view-description'))).toExist();
});
it('should have topic input', async () => {
await expect(element(by.id('room-info-edit-view-topic'))).toExist();
});
it('should have announcement input', async () => {
await expect(element(by.id('room-info-edit-view-announcement'))).toExist();
});
it('should have password input', async () => {
await expect(element(by.id('room-info-edit-view-password'))).toExist();
});
it('should have type switch', async () => {
await swipe('up');
await expect(element(by.id('room-info-edit-view-t'))).toExist();
});
it('should have ready only switch', async () => {
await expect(element(by.id('room-info-edit-view-ro'))).toExist();
});
it('should have submit button', async () => {
await expect(element(by.id('room-info-edit-view-submit'))).toExist();
});
it('should have reset button', async () => {
await expect(element(by.id('room-info-edit-view-reset'))).toExist();
});
it('should have archive button', async () => {
await expect(element(by.id('room-info-edit-view-archive'))).toExist();
});
it('should have delete button', async () => {
await expect(element(by.id('room-info-edit-view-delete'))).toExist();
});
afterAll(async () => {
await swipe('down');
});
});
describe('Usage', () => {
it('should change room name', async () => {
await element(by.id('room-info-edit-view-name')).replaceText(`${privateRoomName}new`);
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await swipe('up');
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
await tapBack();
await waitFor(element(by.id('room-info-view')))
.toExist()
.withTimeout(2000);
const matcher = device.getPlatform() === 'android' ? 'toHaveText' : 'toHaveLabel';
await expect(element(by.id('room-info-view-name')))[matcher](`${privateRoomName}new`);
// change name to original
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view')))
.toExist()
.withTimeout(2000);
await sleep(2000);
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-submit')).tap();
await waitForToast();
await swipe('down');
});
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-description')).replaceText('abc');
await element(by.id('room-info-edit-view-topic')).replaceText('abc');
await element(by.id('room-info-edit-view-announcement')).replaceText('abc');
await element(by.id('room-info-edit-view-password')).replaceText('abc');
await element(by.id('room-info-edit-view-t')).tap();
await swipe('up');
await element(by.id('room-info-edit-view-ro')).longPress(); // https://github.com/facebook/react-native/issues/28032
await element(by.id('room-info-edit-view-react-when-ro')).tap();
await swipe('up');
await element(by.id('room-info-edit-view-reset')).tap();
// 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-description'))).toHaveText('');
await expect(element(by.id('room-info-edit-view-topic'))).toHaveText('');
await expect(element(by.id('room-info-edit-view-announcement'))).toHaveText('');
await expect(element(by.id('room-info-edit-view-password'))).toHaveText('');
// await swipe('down');
await expect(element(by.id('room-info-edit-view-t'))).toHaveToggleValue(true);
await expect(element(by.id('room-info-edit-view-ro'))).toHaveToggleValue(false);
await expect(element(by.id('room-info-edit-view-react-when-ro'))).toBeNotVisible();
await swipe('down');
});
it('should change room description', async () => {
await element(by.id('room-info-edit-view-description')).replaceText('new description');
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 waitForToast();
await tapBack();
await waitFor(element(by.id('room-info-view')))
.toExist()
.withTimeout(2000);
await expect(element(by[textMatcher]('new description').withAncestor(by.id('room-info-view-description')))).toExist();
});
it('should change room topic', async () => {
await waitFor(element(by.id('room-info-view-edit-button')))
.toExist()
.withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view')))
.toExist()
.withTimeout(2000);
await sleep(2000);
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-submit')).tap();
await waitForToast();
await tapBack();
await waitFor(element(by.id('room-info-view')))
.toExist()
.withTimeout(2000);
await expect(element(by[textMatcher]('new topic').withAncestor(by.id('room-info-view-topic')))).toExist();
});
it('should change room announcement', async () => {
await waitFor(element(by.id('room-info-view-edit-button')))
.toExist()
.withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view')))
.toExist()
.withTimeout(2000);
await sleep(2000);
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-submit')).tap();
await waitForToast();
await tapBack();
await waitFor(element(by.id('room-info-view')))
.toExist()
.withTimeout(2000);
await expect(element(by[textMatcher]('new announcement').withAncestor(by.id('room-info-view-announcement')))).toExist();
});
it('should change room password', async () => {
await waitFor(element(by.id('room-info-view-edit-button')))
.toExist()
.withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view')))
.toExist()
.withTimeout(2000);
await sleep(2000);
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-submit')).tap();
await waitForToast();
});
it('should change room type', async () => {
await swipe('down');
await element(by.id('room-info-edit-view-t')).tap();
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 waitForToast();
await swipe('down');
await element(by.id('room-info-edit-view-t')).tap();
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 waitForToast();
});
it('should archive room', async () => {
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-archive')).tap();
await waitFor(element(by[textMatcher]('Yes, archive it!')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Yes, archive it!').and(by.type(alertButtonType))).tap();
await waitForToast();
// await waitFor(element(by.id('room-info-edit-view-unarchive')))
// .toExist()
// .withTimeout(60000);
// await expect(element(by.id('room-info-edit-view-archive'))).toBeNotVisible();
});
it('should delete room', async () => {
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-delete')).tap();
await waitFor(element(by[textMatcher]('Yes, delete it!')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Yes, delete it!').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(10000);
await waitFor(element(by.id(`rooms-list-view-item-${privateRoomName}`)))
.toBeNotVisible()
.withTimeout(60000);
});
});
});
});

View File

@ -0,0 +1,266 @@
import { expect } from 'detox';
import data from '../../data';
import { navigateToLogin, tapBack, login, searchRoom, sleep, platformTypes, TTextMatcher } from '../../helpers/app';
async function navigateToRoom(roomName: string) {
await searchRoom(`${roomName}`);
await element(by.id(`rooms-list-view-item-${roomName}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id(`room-view-title-${roomName}`)))
.toExist()
.withTimeout(5000);
}
let textMatcher: TTextMatcher;
let alertButtonType: string;
async function clearCache() {
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('sidebar-settings')).tap();
await waitFor(element(by.id('settings-view')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('settings-view-clear-cache')).tap();
await waitFor(element(by[textMatcher]('This will clear all your offline data.')))
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Clear').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id('rooms-list-view-item-jumping')))
.toExist()
.withTimeout(10000);
}
async function waitForLoading() {
if (device.getPlatform() === 'android') {
await sleep(10000);
return; // FIXME: Loading indicator doesn't animate properly on android
}
await waitFor(element(by.id('loading-image')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id('loading-image')))
.toBeNotVisible()
.withTimeout(10000);
}
describe('Room', () => {
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.adminUser, data.adminPassword);
});
it('should jump to an old message and load its surroundings', async () => {
await navigateToRoom('jumping');
await waitFor(element(by[textMatcher]('295')))
.toExist()
.withTimeout(5000);
await sleep(2000);
await element(by[textMatcher]('1')).atIndex(0).tap();
await waitForLoading();
await waitFor(element(by[textMatcher]('1')).atIndex(0))
.toExist()
.withTimeout(10000);
await expect(element(by[textMatcher]('2'))).toExist();
});
it('should tap FAB and scroll to bottom', async () => {
await waitFor(element(by.id('nav-jump-to-bottom')))
.toExist()
.withTimeout(15000);
await element(by.id('nav-jump-to-bottom')).tap();
await waitFor(element(by[textMatcher]("Go to jumping-thread's thread")))
.toExist()
.withTimeout(15000);
await clearCache();
});
it('should load messages on scroll', async () => {
await navigateToRoom('jumping');
await waitFor(element(by.id('room-view-messages')))
.toExist()
.withTimeout(5000);
await waitFor(element(by[textMatcher]('300')))
.toExist()
.withTimeout(5000);
let found = false;
while (!found) {
try {
const direction = device.getPlatform() === 'android' ? 'down' : 'up';
await element(by.id('room-view-messages')).scroll(500, direction);
await expect(element(by[textMatcher]('249'))).toExist();
found = true;
} catch {
//
}
}
await clearCache();
});
it('should search for old message and load its surroundings', async () => {
await navigateToRoom('jumping');
await sleep(1000); // wait for proper load the room
await element(by.id('room-view-search')).tap();
await waitFor(element(by.id('search-messages-view')))
.toExist()
.withTimeout(5000);
await element(by.id('search-message-view-input')).replaceText('30');
await waitFor(element(by[textMatcher]('30')).atIndex(1))
.toExist()
.withTimeout(30000);
await sleep(1000);
await element(by[textMatcher]('30')).atIndex(1).tap();
await waitForLoading();
await waitFor(element(by[textMatcher]('30')).atIndex(0))
.toExist()
.withTimeout(30000);
await expect(element(by[textMatcher]('31'))).toExist();
await expect(element(by[textMatcher]('32'))).toExist();
});
it('should load newer and older messages', async () => {
// TODO: couldn't make it work on Android :(
if (device.getPlatform() === 'android') {
return;
}
let found = false;
while (!found) {
try {
// it doesn't recognize this list
await element(by.id('room-view-messages')).scroll(500, 'up');
await expect(element(by[textMatcher]('Load Older'))).toBeVisible();
await expect(element(by[textMatcher]('5'))).toExist();
found = true;
} catch {
//
}
}
await element(by[textMatcher]('Load Older')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('4')))
.toExist()
.withTimeout(5000);
await element(by.id('room-view-messages')).atIndex(0).swipe('down', 'fast', 0.5);
await waitFor(element(by[textMatcher]('1')))
.toExist()
.withTimeout(5000);
await element(by.id('room-view-messages')).atIndex(0).swipe('up', 'fast', 0.5);
await waitFor(element(by[textMatcher]('25')))
.toExist()
.withTimeout(5000);
await element(by.id('room-view-messages')).atIndex(0).swipe('up', 'fast', 0.5);
await waitFor(element(by[textMatcher]('50')))
.toExist()
.withTimeout(5000);
await element(by.id('room-view-messages')).atIndex(0).swipe('up', 'slow', 0.4);
await waitFor(element(by[textMatcher]('Load Newer')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Load Newer')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('104')))
.toExist()
.withTimeout(5000);
await waitFor(element(by[textMatcher]('Load Newer')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Load Newer')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('154')))
.toExist()
.withTimeout(5000);
await waitFor(element(by[textMatcher]('Load Newer')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Load Newer')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('Load Newer')))
.toNotExist()
.withTimeout(5000);
await expect(element(by[textMatcher]('Load More'))).toNotExist();
await expect(element(by[textMatcher]('201'))).toExist();
await expect(element(by[textMatcher]('202'))).toExist();
await tapBack();
});
});
const expectThreadMessages = async (message: string) => {
await waitFor(element(by.id('room-view-title-thread 1')))
.toExist()
.withTimeout(10000);
await waitFor(element(by[textMatcher](message)).atIndex(0))
.toExist()
.withTimeout(10000);
await element(by[textMatcher](message)).atIndex(0).tap();
};
describe('Threads', () => {
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true });
});
it('should navigate to a thread from another room', async () => {
await navigateToRoom('jumping');
await waitFor(element(by[textMatcher]("Go to jumping-thread's thread")).atIndex(0))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]("Go to jumping-thread's thread")).atIndex(0).tap();
await expectThreadMessages("Go to jumping-thread's thread");
await tapBack();
});
it('should tap on thread message from main room', async () => {
await waitFor(element(by.id('room-view-title-jumping-thread')))
.toExist()
.withTimeout(5000);
await waitFor(element(by[textMatcher]('thread message sent to main room')))
.toExist()
.withTimeout(10000);
await element(by[textMatcher]('thread message sent to main room')).atIndex(0).tap();
await expectThreadMessages('thread message sent to main room');
await tapBack();
});
it('should tap on quote', async () => {
await waitFor(element(by.id('room-view-title-jumping-thread')))
.toExist()
.withTimeout(5000);
await waitFor(element(by[textMatcher]('quoted')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('quoted')).atIndex(0).tap();
await expectThreadMessages('quoted');
await tapBack();
});
it('should jump from search message', async () => {
await waitFor(element(by.id('room-view-title-jumping-thread')))
.toExist()
.withTimeout(5000);
await element(by.id('room-view-search')).atIndex(0).tap();
await waitFor(element(by.id('search-messages-view')))
.toExist()
.withTimeout(5000);
await element(by.id('search-message-view-input')).replaceText('to be searched');
await waitFor(element(by[textMatcher]('to be searched')).atIndex(1))
.toExist()
.withTimeout(30000);
await element(by[textMatcher]('to be searched')).atIndex(1).tap();
await expectThreadMessages('to be searched');
});
// TODO: Threads pagination
});

View File

@ -0,0 +1,104 @@
import { expect } from 'detox';
import data from '../../data';
import { navigateToLogin, login, searchRoom, sleep, platformTypes, TTextMatcher, tapBack } from '../../helpers/app';
import { sendMessage } from '../../helpers/data_setup';
async function navigateToRoom(user: string) {
await searchRoom(`${user}`);
await element(by.id(`rooms-list-view-item-${user}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
}
async function navigateToInfoView() {
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(5000);
await element(by.id('room-actions-info')).tap();
await waitFor(element(by.id('room-info-view')))
.toExist()
.withTimeout(2000);
}
describe('Ignore/Block User', () => {
const user = data.users.alternate.username;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
describe('Usage', () => {
describe('Block user from DM', () => {
it('should go to user info view', async () => {
await navigateToRoom(user);
await navigateToInfoView();
});
it('should block user', async () => {
await waitFor(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Block user'))))
.toBeVisible()
.withTimeout(2000);
await element(by.id('room-info-view-ignore')).tap();
await waitFor(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Unblock user'))))
.toExist()
.withTimeout(2000);
await tapBack();
await waitFor(element(by.id('room-actions-view')))
.toBeVisible()
.withTimeout(5000);
await tapBack();
await expect(element(by[textMatcher]('This room is blocked'))).toExist();
});
it('should unblock user', async () => {
await navigateToInfoView();
await element(by.id('room-info-view-ignore')).tap();
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Block user')))).toExist();
await tapBack();
await waitFor(element(by.id('room-actions-view')))
.toBeVisible()
.withTimeout(5000);
await tapBack();
await expect(element(by.id('messagebox'))).toBeVisible();
await tapBack();
});
});
describe('Ignore user from Message', () => {
it('should ignore user from message', async () => {
const channelName = data.userRegularChannels.detoxpublic.name;
await navigateToRoom(channelName);
await sleep(300);
await sendMessage(data.users.alternate, channelName, 'message-01');
await sendMessage(data.users.alternate, channelName, 'message-02');
await waitFor(element(by[textMatcher](user)).atIndex(0))
.toExist()
.withTimeout(30000);
await sleep(300);
await element(by[textMatcher](user)).atIndex(0).tap();
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Ignore')))).toExist();
await element(by.id('room-info-view-ignore')).tap();
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Unignore')))).toExist();
await tapBack();
});
it('should tap to display message', async () => {
await expect(element(by[textMatcher]('Message ignored. Tap to display it.')).atIndex(0)).toExist();
await element(by[textMatcher]('Message ignored. Tap to display it.')).atIndex(0).tap();
await waitFor(element(by[textMatcher](user)))
.toBeVisible()
.withTimeout(1000);
await element(by[textMatcher](user)).atIndex(0).tap();
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Unignore')))).toExist();
await element(by.id('room-info-view-ignore')).tap();
await expect(element(by.id('room-info-view-ignore').withDescendant(by[textMatcher]('Ignore')))).toExist();
await tapBack();
await expect(element(by[textMatcher]('message-02')).atIndex(0)).toBeVisible();
});
});
});
});

View File

@ -0,0 +1,110 @@
import { expect } from 'detox';
import data from '../../data';
import { navigateToLogin, login, platformTypes, TTextMatcher } from '../../helpers/app';
const teamName = `team-${data.random}`;
describe('Create team screen', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
describe('New Message', () => {
beforeAll(async () => {
await waitFor(element(by.id('rooms-list-view-create-channel')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('rooms-list-view-create-channel')).tap();
});
it('should have team button', async () => {
await waitFor(element(by.id('new-message-view-create-team')))
.toBeVisible()
.withTimeout(2000);
});
it('should navigate to select users', async () => {
await element(by.id('new-message-view-create-team')).tap();
await waitFor(element(by.id('select-users-view')))
.toExist()
.withTimeout(5000);
});
});
describe('Select Users', () => {
it('should nav to create team', async () => {
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view')))
.toExist()
.withTimeout(10000);
});
});
describe('Create Team', () => {
describe('Usage', () => {
it('should get invalid team name', async () => {
await element(by.id('create-channel-name')).replaceText(`${data.teams.private.name}`);
await waitFor(element(by.id('create-channel-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by[textMatcher]('OK').and(by.type(alertButtonType))))
.toBeVisible()
.withTimeout(5000);
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
});
it('should create private team', async () => {
await element(by.id('create-channel-name')).replaceText('');
await element(by.id('create-channel-name')).replaceText(teamName);
await waitFor(element(by.id('create-channel-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(20000);
await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${teamName}`)))
.toExist()
.withTimeout(6000);
await expect(element(by.id(`room-view-title-${teamName}`))).toExist();
});
});
});
describe('Delete Team', () => {
it('should navigate to room info edit view', async () => {
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(5000);
await element(by.id('room-actions-info')).tap();
await waitFor(element(by.id('room-info-view')))
.toExist()
.withTimeout(2000);
});
it('should delete team', async () => {
await element(by.id('room-info-view-edit-button')).tap();
await element(by.id('room-info-edit-view-list')).swipe('up', 'fast', 0.5);
await element(by.id('room-info-edit-view-delete')).tap();
await waitFor(element(by[textMatcher]('Yes, delete it!')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Yes, delete it!').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(10000);
await waitFor(element(by.id(`rooms-list-view-item-${teamName}`)))
.toBeNotVisible()
.withTimeout(60000);
});
});
});

View File

@ -0,0 +1,491 @@
import { expect } from 'detox';
import data from '../../data';
import { navigateToLogin, login, tapBack, sleep, searchRoom, platformTypes, TTextMatcher } from '../../helpers/app';
async function navigateToRoom(roomName: string) {
await searchRoom(`${roomName}`);
await element(by.id(`rooms-list-view-item-${roomName}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
}
async function openActionSheet(username: string) {
await waitFor(element(by.id(`room-members-view-item-${username}`)))
.toExist()
.withTimeout(5000);
await element(by.id(`room-members-view-item-${username}`)).tap();
await sleep(300);
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up');
}
async function navigateToRoomActions() {
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(2000);
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(5000);
}
async function backToActions() {
await tapBack();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(2000);
}
async function closeActionSheet() {
await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.6);
await waitFor(element(by.id('action-sheet-handle')))
.toBeNotVisible()
.withTimeout(3000);
await sleep(200);
}
async function waitForToast() {
await sleep(1000);
}
async function swipeTillVisible(
container: Detox.NativeMatcher,
find: Detox.NativeMatcher,
direction: Detox.Direction = 'up',
delta = 0.3,
speed: Detox.Speed = 'slow'
) {
let found = false;
while (!found) {
try {
await element(container).swipe(direction, speed, delta);
await sleep(200);
await expect(element(find)).toBeVisible();
found = true;
} catch (e) {
//
}
}
}
describe('Team', () => {
const team = data.teams.private.name;
const user = data.users.alternate;
const room = `private${data.random}-channel-team`;
const existingRoom = data.groups.alternate.name;
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
await navigateToRoom(team);
});
describe('Team Room', () => {
describe('Team Header', () => {
it('should have actions button ', async () => {
await expect(element(by.id('room-header'))).toExist();
});
it('should have team channels button ', async () => {
await expect(element(by.id('room-view-header-team-channels'))).toExist();
});
it('should have threads button ', async () => {
await expect(element(by.id('room-view-header-threads'))).toExist();
});
it('should have threads button ', async () => {
await expect(element(by.id('room-view-search'))).toExist();
});
});
describe('Team Header Usage', () => {
it('should navigate to team channels view', async () => {
await element(by.id('room-view-header-team-channels')).tap();
await waitFor(element(by.id('team-channels-view')))
.toExist()
.withTimeout(5000);
});
});
describe('Team Channels Header', () => {
it('should have actions button ', async () => {
await expect(element(by.id('room-header')).atIndex(0)).toExist();
});
it('should have team channels button ', async () => {
await expect(element(by.id('team-channels-view-create'))).toExist();
});
it('should have threads button ', async () => {
await expect(element(by.id('team-channels-view-search'))).toExist();
});
});
describe('Team Channels Header Usage', () => {
it('should navigate to add team channels view', async () => {
await element(by.id('team-channels-view-create')).tap();
await waitFor(element(by.id('add-channel-team-view')))
.toExist()
.withTimeout(5000);
});
it('should have create new button', async () => {
await waitFor(element(by.id('add-channel-team-view-create-channel')))
.toExist()
.withTimeout(5000);
});
it('should add existing button', async () => {
await waitFor(element(by.id('add-channel-team-view-add-existing')))
.toExist()
.withTimeout(5000);
});
});
describe('Channels', () => {
it('should create new channel for team', async () => {
await element(by.id('add-channel-team-view-create-channel')).tap();
await element(by.id('select-users-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id('select-users-view-item-rocket.cat')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('select-users-view-item-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view')))
.toExist()
.withTimeout(10000);
await element(by.id('create-channel-name')).replaceText('');
await element(by.id('create-channel-name')).replaceText(room);
await waitFor(element(by.id('create-channel-submit')))
.toExist()
.withTimeout(10000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(20000);
await expect(element(by.id('room-view'))).toExist();
await expect(element(by.id('room-view-header-team-channels'))).toExist();
await element(by.id('room-view-header-team-channels')).tap();
await waitFor(element(by.id('team-channels-view')))
.toExist()
.withTimeout(5000);
await waitFor(element(by.id(`rooms-list-view-item-${room}`)))
.toExist()
.withTimeout(6000);
await expect(element(by.id(`rooms-list-view-item-${room}`))).toExist();
await element(by.id(`rooms-list-view-item-${room}`)).tap();
await waitFor(element(by.id(`room-view-title-${room}`)))
.toExist()
.withTimeout(60000);
await expect(element(by.id(`room-view-title-${room}`))).toExist();
await expect(element(by.id('room-view-header-team-channels')).atIndex(0)).toExist();
await expect(element(by.id('room-view-header-threads')).atIndex(0)).toExist();
await expect(element(by.id('room-view-search')).atIndex(0)).toExist();
await tapBack();
});
it('should add existing channel to team', async () => {
await navigateToRoom(team);
await waitFor(element(by.id('room-view-header-team-channels')))
.toExist()
.withTimeout(5000);
await element(by.id('room-view-header-team-channels')).tap();
await waitFor(element(by.id('team-channels-view')))
.toExist()
.withTimeout(5000);
await element(by.id('team-channels-view-create')).tap();
await waitFor(element(by.id('add-channel-team-view')))
.toExist()
.withTimeout(5000);
await element(by.id('add-channel-team-view-add-existing')).tap();
await waitFor(element(by.id('add-existing-channel-view')))
.toExist()
.withTimeout(60000);
await expect(element(by.id(`add-existing-channel-view-item-${existingRoom}`))).toExist();
await element(by.id(`add-existing-channel-view-item-${existingRoom}`)).tap();
await waitFor(element(by.id('add-existing-channel-view-submit')))
.toExist()
.withTimeout(6000);
await element(by.id('add-existing-channel-view-submit')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(20000);
await expect(element(by.id('room-view'))).toExist();
await expect(element(by.id('room-view-header-team-channels'))).toExist();
await element(by.id('room-view-header-team-channels')).tap();
await waitFor(element(by.id(`rooms-list-view-item-${existingRoom}`)).atIndex(0))
.toExist()
.withTimeout(10000);
});
it('should activate/deactivate auto-join to channel', async () => {
await element(by.id(`rooms-list-view-item-${existingRoom}`))
.atIndex(0)
.longPress();
await sleep(500);
await swipeTillVisible(by.id('action-sheet-remove-from-team'), by.id('action-sheet-delete'));
await waitFor(element(by.id('action-sheet-auto-join')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id('auto-join-unchecked')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id('action-sheet-remove-from-team')))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id('action-sheet-delete')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('auto-join-unchecked')).tap();
await waitFor(element(by.id('auto-join-tag')))
.toBeVisible()
.withTimeout(5000);
await element(by.id(`rooms-list-view-item-${existingRoom}`))
.atIndex(0)
.longPress();
await waitFor(element(by.id('auto-join-checked')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('auto-join-checked')).tap();
await waitFor(element(by.id('auto-join-tag')))
.toBeNotVisible()
.withTimeout(5000);
await waitFor(element(by.id(`rooms-list-view-item-${existingRoom}`)).atIndex(0))
.toExist()
.withTimeout(6000);
});
});
describe('Team actions', () => {
beforeAll(async () => {
await tapBack();
await navigateToRoomActions();
});
it('should add users to the team', async () => {
await element(by.id('room-actions-members')).tap();
await waitFor(element(by.id('room-members-view')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id('room-actions-add-user')))
.toExist()
.withTimeout(10000);
await element(by.id('room-actions-add-user')).tap();
const rocketCat = 'rocket.cat';
await element(by.id('select-users-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id(`select-users-view-item-${rocketCat}`)))
.toExist()
.withTimeout(10000);
await element(by.id(`select-users-view-item-${rocketCat}`)).tap();
await waitFor(element(by.id(`selected-user-${rocketCat}`)))
.toExist()
.withTimeout(5000);
await waitFor(element(by.id('select-users-view-search')))
.toExist()
.withTimeout(4000);
await element(by.id('select-users-view-search')).tap();
await element(by.id('select-users-view-search')).replaceText(user.username);
await waitFor(element(by.id(`select-users-view-item-${user.username}`)))
.toExist()
.withTimeout(10000);
await element(by.id(`select-users-view-item-${user.username}`)).tap();
await waitFor(element(by.id(`selected-user-${user.username}`)))
.toExist()
.withTimeout(5000);
await element(by.id('selected-users-view-submit')).tap();
await sleep(300);
await tapBack();
await sleep(300);
await waitFor(element(by.id('room-actions-members')))
.toExist()
.withTimeout(10000);
await element(by.id('room-actions-members')).tap();
await element(by.id('room-members-view-filter')).tap();
await waitFor(element(by.id('room-members-view-toggle-status-all')))
.toExist()
.withTimeout(2000);
await element(by.id('room-members-view-toggle-status-all')).tap();
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist()
.withTimeout(60000);
await backToActions();
});
it('should try to leave to leave team and raise alert', async () => {
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-leave-channel')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-leave-channel')).tap();
await waitFor(element(by.id('select-list-view')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${room}`)))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${existingRoom}`)))
.toExist()
.withTimeout(2000);
await element(by.id(`select-list-view-item-${room}`)).tap();
await waitFor(
element(
by[textMatcher](
'You are the last owner of this channel. Once you leave the team, the channel will be kept inside the team but you will be managing it from outside.'
)
)
)
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('select-list-view-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('select-list-view-submit')).tap();
await waitFor(element(by[textMatcher]('Last owner cannot be removed')))
.toExist()
.withTimeout(8000);
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
await tapBack();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(2000);
});
describe('Room Members', () => {
beforeAll(async () => {
await element(by.id('room-actions-members')).tap();
await waitFor(element(by.id('room-members-view')))
.toExist()
.withTimeout(2000);
});
it('should show all users', async () => {
await element(by.id('room-members-view-filter')).tap();
await waitFor(element(by.id('room-members-view-toggle-status-all')))
.toExist()
.withTimeout(2000);
await element(by.id('room-members-view-toggle-status-all')).tap();
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist()
.withTimeout(60000);
});
it('should filter user', async () => {
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist()
.withTimeout(60000);
await element(by.id('room-members-view-search')).replaceText('rocket');
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toBeNotVisible()
.withTimeout(60000);
await element(by.id('room-members-view-search')).tap();
await element(by.id('room-members-view-search')).clearText();
await waitFor(element(by.id(`room-members-view-item-${user.username}`)))
.toExist()
.withTimeout(60000);
});
it('should remove member from team', async () => {
await openActionSheet('rocket.cat');
await waitFor(element(by.id('action-sheet-remove-from-team')))
.toBeVisible()
.withTimeout(2000);
await element(by.id('action-sheet-remove-from-team')).tap();
await waitFor(element(by.id('select-list-view')))
.toExist()
.withTimeout(5000);
await waitFor(element(by.id(`select-list-view-item-${room}`)))
.toExist()
.withTimeout(5000);
await element(by.id(`select-list-view-item-${room}`)).tap();
await waitFor(element(by.id(`${room}-checked`)))
.toExist()
.withTimeout(5000);
await element(by.id(`select-list-view-item-${room}`)).tap();
await waitFor(element(by.id(`${room}-checked`)))
.toNotExist()
.withTimeout(5000);
await element(by.id('select-list-view-submit')).tap();
await waitFor(element(by.id('room-members-view-item-rocket.cat')))
.toBeNotVisible()
.withTimeout(60000);
});
it('should set member as owner', async () => {
await openActionSheet(user.username);
await element(by.id('action-sheet-set-owner')).tap();
await waitForToast();
await openActionSheet(user.username);
await waitFor(element(by.id('action-sheet-set-owner-checked')))
.toBeVisible()
.withTimeout(6000);
await closeActionSheet();
});
it('should leave team', async () => {
await tapBack();
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-leave-channel')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-leave-channel')).tap();
await waitFor(element(by.id('select-list-view')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${room}`)))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${existingRoom}`)))
.toExist()
.withTimeout(2000);
await element(by.id(`select-list-view-item-${room}`)).tap();
await waitFor(
element(
by[textMatcher](
'You are the last owner of this channel. Once you leave the team, the channel will be kept inside the team but you will be managing it from outside.'
)
)
)
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('select-list-view-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('select-list-view-submit')).tap();
await waitFor(element(by.id(`rooms-list-view-item-${team}`)))
.toBeNotVisible()
.withTimeout(60000);
});
});
});
});
});

View File

@ -0,0 +1,189 @@
import data from '../../data';
import { navigateToLogin, login, tapBack, searchRoom, platformTypes, TTextMatcher } from '../../helpers/app';
const toBeConverted = `to-be-converted-${data.random}`;
const toBeMoved = `to-be-moved-${data.random}`;
const createChannel = async (room: string) => {
await waitFor(element(by.id('rooms-list-view-create-channel')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view')))
.toExist()
.withTimeout(5000);
await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view')))
.toExist()
.withTimeout(5000);
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view')))
.toExist()
.withTimeout(10000);
await element(by.id('create-channel-name')).replaceText(room);
await waitFor(element(by.id('create-channel-submit')))
.toExist()
.withTimeout(10000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(60000);
await waitFor(element(by.id(`room-view-title-${room}`)))
.toExist()
.withTimeout(60000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id(`rooms-list-view-item-${room}`)))
.toExist()
.withTimeout(60000);
};
async function navigateToRoom(room: string) {
await searchRoom(`${room}`);
await element(by.id(`rooms-list-view-item-${room}`)).tap();
await waitFor(element(by.id('room-view')))
.toBeVisible()
.withTimeout(5000);
}
async function navigateToRoomActions(room: string) {
await navigateToRoom(room);
await element(by.id('room-header')).tap();
await waitFor(element(by.id('room-actions-view')))
.toExist()
.withTimeout(5000);
}
describe('Move/Convert Team', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
beforeAll(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
});
describe('Convert', () => {
beforeAll(async () => {
await createChannel(toBeConverted);
});
it('should convert channel to a team', async () => {
await navigateToRoomActions(toBeConverted);
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-convert-to-team')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-convert-to-team')).tap();
await waitFor(element(by[textMatcher]('You are converting this Channel to a Team. All Members will be kept.')))
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Convert').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(20000);
await waitFor(element(by.id(`room-view-title-${toBeConverted}`)))
.toExist()
.withTimeout(6000);
});
afterAll(async () => {
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(2000);
});
});
describe('Move', () => {
beforeAll(async () => {
await createChannel(toBeMoved);
});
it('should move channel to a team', async () => {
await navigateToRoomActions(toBeMoved);
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-move-to-team')))
.toExist()
.withTimeout(2000);
await element(by.id('room-actions-move-to-team')).tap();
await waitFor(element(by[textMatcher]('Move to Team')).atIndex(0))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id('select-list-view-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('select-list-view-submit')).tap();
await waitFor(element(by[textMatcher]('Select Team')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${toBeConverted}`)))
.toExist()
.withTimeout(2000);
await element(by.id(`select-list-view-item-${toBeConverted}`)).tap();
await element(by.id('select-list-view-submit')).atIndex(0).tap();
await waitFor(
element(
by[textMatcher](
'After reading the previous intructions about this behavior, do you still want to move this channel to the selected team?'
)
)
)
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Yes, move it!').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('room-view-header-team-channels')))
.toExist()
.withTimeout(10000);
});
afterAll(async () => {
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(2000);
});
});
describe('Convert Team to Channel and Delete toBeMoved channel within the Converted', () => {
it('should convert a team to a channel', async () => {
await navigateToRoomActions(toBeConverted);
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by[textMatcher]('Convert to Channel')))
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Convert to Channel')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('Converting Team to Channel')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id(`select-list-view-item-${toBeMoved}`)))
.toExist()
.withTimeout(2000);
await element(by.id(`select-list-view-item-${toBeMoved}`)).tap();
await waitFor(element(by.id('select-list-view-submit')))
.toExist()
.withTimeout(2000);
await element(by.id('select-list-view-submit')).tap();
await waitFor(element(by[textMatcher]('You are converting this Team to a Channel')))
.toExist()
.withTimeout(2000);
await element(by[textMatcher]('Convert').and(by.type(alertButtonType))).tap();
await waitFor(element(by.id('room-view')))
.toExist()
.withTimeout(20000);
await waitFor(element(by.id(`room-view-title-${toBeConverted}`)))
.toExist()
.withTimeout(6000);
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(2000);
await waitFor(element(by.id(`rooms-list-view-item-${toBeMoved}`)))
.toBeNotVisible()
.withTimeout(60000);
});
});
});

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"]
}