= 'typeText',
+ roomTestID?: string
+) {
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(30000);
await tapAndWaitFor(element(by.id('rooms-list-view-search')), element(by.id('rooms-list-view-search-input')), 5000);
- await element(by.id('rooms-list-view-search-input')).typeText(room);
- await sleep(300);
- await waitFor(element(by.id(`rooms-list-view-item-${room}`)))
+ if (nativeElementAction === 'replaceText') {
+ // trigger the input's onChangeText
+ await element(by.id('rooms-list-view-search-input')).typeText(' ');
+ }
+ await element(by.id('rooms-list-view-search-input'))[nativeElementAction](room);
+ await sleep(500);
+ await sleep(500);
+ await waitFor(element(by.id(roomTestID || `rooms-list-view-item-${room}`)))
.toBeVisible()
.withTimeout(60000);
}
diff --git a/e2e/helpers/data_setup.ts b/e2e/helpers/data_setup.ts
index 9537115b8..3dafb7e92 100644
--- a/e2e/helpers/data_setup.ts
+++ b/e2e/helpers/data_setup.ts
@@ -137,7 +137,61 @@ export const post = async (endpoint: string, body: any, user: ITestUser) => {
return rocketchat.post(endpoint, body);
};
-export const getProfileInfo = async (userId: string) => {
- const result = await get(`users.info?userId=${userId}`);
+interface UserId {
+ userId: string;
+}
+
+interface UserName {
+ username: string;
+}
+
+export const getProfileInfo = async (param: UserId | UserName) => {
+ let query = '';
+ if ('userId' in param) {
+ query += `userId=${param.userId}`;
+ } else if ('username' in param) {
+ query += `username=${param.username}`;
+ }
+ const result = await get(`users.info?${query}`);
return result.data.user;
};
+
+export interface IDeleteCreateUser {
+ server: string;
+ username: string;
+}
+
+const deleteCreatedUser = async ({ server, username: usernameToDelete }: IDeleteCreateUser) => {
+ const serverConnection = axios.create({
+ baseURL: `${server}/api/v1/`,
+ headers: {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ }
+ });
+ console.log(`Logging in as admin in ${server}`);
+ const response = await serverConnection.post('login', {
+ user: data.adminUser,
+ password: data.adminPassword
+ });
+ const { authToken, userId } = response.data.data;
+ serverConnection.defaults.headers.common['X-User-Id'] = userId;
+ serverConnection.defaults.headers.common['X-Auth-Token'] = authToken;
+
+ console.log(`Get user info: users.info?username=${usernameToDelete}`);
+ const result = await serverConnection.get(`users.info?username=${usernameToDelete}`);
+ const userIdToDelete = result.data.user._id;
+
+ const body = { userId: userIdToDelete, confirmRelinquish: false };
+ console.log(`Delete user: users.delete ${JSON.stringify(body)}`);
+ const responsePost = await serverConnection.post('users.delete', body);
+ return responsePost.data;
+};
+
+// Delete created users to avoid use all the Seats Available on the server
+export const deleteCreatedUsers = async (deleteUsersAfterAll: IDeleteCreateUser[]) => {
+ if (deleteUsersAfterAll.length) {
+ for await (const deleteUser of deleteUsersAfterAll) {
+ await deleteCreatedUser(deleteUser);
+ }
+ }
+};
diff --git a/e2e/tests/assorted/01-e2eencryption.spec.ts b/e2e/tests/assorted/01-e2eencryption.spec.ts
index 3daf3b3c1..e63c3e636 100644
--- a/e2e/tests/assorted/01-e2eencryption.spec.ts
+++ b/e2e/tests/assorted/01-e2eencryption.spec.ts
@@ -15,7 +15,7 @@ import {
tryTapping
} from '../../helpers/app';
import data from '../../data';
-import { createRandomUser, ITestUser } from '../../helpers/data_setup';
+import { createRandomUser, deleteCreatedUsers, IDeleteCreateUser, ITestUser } from '../../helpers/data_setup';
import random from '../../helpers/random';
const checkServer = async (server: string) => {
@@ -83,6 +83,8 @@ describe('E2E Encryption', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
+ const deleteUsersAfterAll: IDeleteCreateUser[] = [];
+
beforeAll(async () => {
user = await createRandomUser();
otherUser = await createRandomUser();
@@ -92,6 +94,10 @@ describe('E2E Encryption', () => {
await login(user.username, user.password);
});
+ afterAll(async () => {
+ await deleteCreatedUsers(deleteUsersAfterAll);
+ });
+
describe('Banner', () => {
describe('Render', () => {
it('should have encryption badge', async () => {
@@ -396,6 +402,7 @@ describe('E2E Encryption', () => {
await element(by.id('register-view-password')).replaceText(randomUser.password);
await element(by.id('register-view-password')).tapReturnKey();
await expectValidRegisterOrRetry(device.getPlatform());
+ deleteUsersAfterAll.push({ server: data.alternateServer, username: randomUser.username });
await checkServer(data.alternateServer);
});
diff --git a/e2e/tests/assorted/07-changeserver.spec.ts b/e2e/tests/assorted/07-changeserver.spec.ts
index 4d0b32aa4..bef8fc836 100644
--- a/e2e/tests/assorted/07-changeserver.spec.ts
+++ b/e2e/tests/assorted/07-changeserver.spec.ts
@@ -2,7 +2,7 @@ import { device, waitFor, element, by } from 'detox';
import data from '../../data';
import { navigateToLogin, login, checkServer, expectValidRegisterOrRetry } from '../../helpers/app';
-import { createRandomRoom, createRandomUser, ITestUser } from '../../helpers/data_setup';
+import { createRandomRoom, createRandomUser, deleteCreatedUsers, IDeleteCreateUser, ITestUser } from '../../helpers/data_setup';
const reopenAndCheckServer = async (server: string) => {
await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true });
@@ -16,6 +16,8 @@ describe('Change server', () => {
let user: ITestUser;
let room: string;
+ const deleteUsersAfterAll: IDeleteCreateUser[] = [];
+
beforeAll(async () => {
user = await createRandomUser();
({ name: room } = await createRandomRoom(user));
@@ -24,6 +26,10 @@ describe('Change server', () => {
await login(user.username, user.password);
});
+ afterAll(async () => {
+ await deleteCreatedUsers(deleteUsersAfterAll);
+ });
+
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')))
@@ -72,6 +78,7 @@ describe('Change server', () => {
await element(by.id('register-view-password')).replaceText(randomUser.password);
await element(by.id('register-view-password')).tapReturnKey();
await expectValidRegisterOrRetry(device.getPlatform());
+ deleteUsersAfterAll.push({ server: data.alternateServer, username: randomUser.username });
await waitFor(element(by.id(`rooms-list-view-item-${room}`)))
.toBeNotVisible()
diff --git a/e2e/tests/assorted/10-deleteserver.spec.ts b/e2e/tests/assorted/10-deleteserver.spec.ts
index a0b3ef8cd..22d601f1c 100644
--- a/e2e/tests/assorted/10-deleteserver.spec.ts
+++ b/e2e/tests/assorted/10-deleteserver.spec.ts
@@ -10,12 +10,13 @@ import {
TTextMatcher,
expectValidRegisterOrRetry
} from '../../helpers/app';
-import { createRandomUser, ITestUser } from '../../helpers/data_setup';
+import { createRandomUser, deleteCreatedUsers, IDeleteCreateUser, ITestUser } from '../../helpers/data_setup';
describe('Delete server', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
let user: ITestUser;
+ const deleteUsersAfterAll: IDeleteCreateUser[] = [];
beforeAll(async () => {
user = await createRandomUser();
@@ -25,6 +26,10 @@ describe('Delete server', () => {
await login(user.username, user.password);
});
+ afterAll(async () => {
+ await deleteCreatedUsers(deleteUsersAfterAll);
+ });
+
it('should be logged in main server', async () => {
await checkServer(data.server);
});
@@ -58,6 +63,7 @@ describe('Delete server', () => {
await element(by.id('register-view-password')).replaceText(randomUser.password);
await element(by.id('register-view-password')).tapReturnKey();
await expectValidRegisterOrRetry(device.getPlatform());
+ deleteUsersAfterAll.push({ server: data.alternateServer, username: randomUser.username });
await checkServer(data.alternateServer);
});
diff --git a/e2e/tests/assorted/11-deeplinking.spec.ts b/e2e/tests/assorted/11-deeplinking.spec.ts
index f8a3ab31d..18c6a37f1 100644
--- a/e2e/tests/assorted/11-deeplinking.spec.ts
+++ b/e2e/tests/assorted/11-deeplinking.spec.ts
@@ -10,7 +10,14 @@ import {
TTextMatcher,
expectValidRegisterOrRetry
} from '../../helpers/app';
-import { createRandomRoom, createRandomUser, login, sendMessage } from '../../helpers/data_setup';
+import {
+ IDeleteCreateUser,
+ createRandomRoom,
+ createRandomUser,
+ deleteCreatedUsers,
+ login,
+ sendMessage
+} from '../../helpers/data_setup';
import random from '../../helpers/random';
const DEEPLINK_METHODS = { AUTH: 'auth', ROOM: 'room' };
@@ -32,6 +39,8 @@ describe('Deep linking', () => {
let room: string;
const threadMessage = `to-thread-${random()}`;
+ const deleteUsersAfterAll: IDeleteCreateUser[] = [];
+
beforeAll(async () => {
const user = await createRandomUser();
({ _id: rid, name: room } = await createRandomRoom(user, 'p'));
@@ -46,6 +55,10 @@ describe('Deep linking', () => {
await sendMessage(user, result.message.rid, random(), threadId);
});
+ afterAll(async () => {
+ await deleteCreatedUsers(deleteUsersAfterAll);
+ });
+
describe('Authentication', () => {
it('should run a deep link to an invalid account and raise error', async () => {
await device.launchApp({
@@ -53,7 +66,7 @@ describe('Deep linking', () => {
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.")))
+ await waitFor(element(by[textMatcher]("You've been logged out by the workspace. Please log in again.")))
.toExist()
.withTimeout(30000); // TODO: we need to improve this message
});
@@ -91,6 +104,8 @@ describe('Deep linking', () => {
await element(by.id('register-view-password')).replaceText(randomUser.password);
await element(by.id('register-view-password')).tapReturnKey();
await expectValidRegisterOrRetry(device.getPlatform());
+ deleteUsersAfterAll.push({ server: data.alternateServer, username: randomUser.username });
+
await authAndNavigate();
});
});
diff --git a/e2e/tests/assorted/13-display-pref.spec.ts b/e2e/tests/assorted/13-display-pref.spec.ts
index 76361d340..f206fe096 100644
--- a/e2e/tests/assorted/13-display-pref.spec.ts
+++ b/e2e/tests/assorted/13-display-pref.spec.ts
@@ -73,14 +73,14 @@ describe('Display prefs', () => {
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();
+ await expect(element(by.id('room-item-last-message-container')).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();
+ await expect(element(by.id('room-item-last-message-container'))).not.toBeVisible();
});
});
diff --git a/e2e/tests/assorted/15-change-avatar.spec.ts b/e2e/tests/assorted/15-change-avatar.spec.ts
index a3ca766a6..f83037e56 100644
--- a/e2e/tests/assorted/15-change-avatar.spec.ts
+++ b/e2e/tests/assorted/15-change-avatar.spec.ts
@@ -68,7 +68,7 @@ describe('Profile screen', () => {
it('should change the avatar through a base64 image mocked', async () => {
await element(by.type(scrollViewType)).atIndex(1).swipe('down');
await element(by.id('avatar-edit-button')).tap();
- const previousUserInfo = await getProfileInfo(userId);
+ const previousUserInfo = await getProfileInfo({ userId });
const previousAvatarEtag = previousUserInfo.avatarETag;
await sleep(500);
await waitFor(element(by.id('change-avatar-view-upload-image')))
@@ -83,7 +83,7 @@ describe('Profile screen', () => {
.toBeVisible()
.withTimeout(2000);
await sleep(300);
- const newUserInfo = await getProfileInfo(userId);
+ const newUserInfo = await getProfileInfo({ userId });
const newAvatarEtag = newUserInfo.avatarETag;
await sleep(500);
if (previousAvatarEtag === newAvatarEtag) {
diff --git a/e2e/tests/room/01-createroom.spec.ts b/e2e/tests/room/01-createroom.spec.ts
index eb61aed6d..98d98a93a 100644
--- a/e2e/tests/room/01-createroom.spec.ts
+++ b/e2e/tests/room/01-createroom.spec.ts
@@ -1,6 +1,6 @@
import { device, waitFor, element, by, expect } from 'detox';
-import { tapBack, navigateToLogin, login, platformTypes, TTextMatcher, tapAndWaitFor } from '../../helpers/app';
+import { tapBack, navigateToLogin, login, platformTypes, TTextMatcher, tapAndWaitFor, searchRoom } from '../../helpers/app';
import { createRandomUser } from '../../helpers/data_setup';
import random from '../../helpers/random';
@@ -238,6 +238,9 @@ describe('Create room screen', () => {
await waitFor(element(by.id('create-channel-view')))
.toExist()
.withTimeout(10000);
+ await waitFor(element(by.id('create-channel-name')))
+ .toBeVisible()
+ .withTimeout(2000);
await element(by.id('create-channel-name')).replaceText(room);
await element(by.id('create-channel-name')).tapReturnKey();
await waitFor(element(by.id('create-channel-submit')))
@@ -261,6 +264,61 @@ describe('Create room screen', () => {
.withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-${room}`))).toExist();
});
+
+ it('should create a room with non-latin alphabet and do a case insensitive search for it', async () => {
+ const randomValue = random();
+ const roomName = `ПРОВЕРКА${randomValue}`;
+ const roomNameLower = roomName.toLowerCase();
+
+ await waitFor(element(by.id('rooms-list-view')))
+ .toExist()
+ .withTimeout(10000);
+ await element(by.id('rooms-list-view-create-channel')).tap();
+ await waitFor(element(by.id('new-message-view')))
+ .toBeVisible()
+ .withTimeout(5000);
+ await waitFor(element(by.id('new-message-view-create-channel')))
+ .toBeVisible()
+ .withTimeout(2000);
+ 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 waitFor(element(by.id('create-channel-name')))
+ .toBeVisible()
+ .withTimeout(2000);
+ await element(by.id('create-channel-name')).replaceText(roomName);
+ await element(by.id('create-channel-name')).tapReturnKey();
+ 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-${roomName}`)))
+ .toExist()
+ .withTimeout(60000);
+ await expect(element(by.id(`room-view-title-${roomName}`))).toExist();
+ await tapBack();
+ await waitFor(element(by.id('rooms-list-view')))
+ .toExist()
+ .withTimeout(2000);
+ await waitFor(element(by.id(`rooms-list-view-item-${roomName}`)))
+ .toExist()
+ .withTimeout(60000);
+ await expect(element(by.id(`rooms-list-view-item-${roomName}`))).toExist();
+ await searchRoom(roomNameLower, 'replaceText', `rooms-list-view-item-${roomName}`);
+ await element(by.id(`rooms-list-view-item-${roomName}`)).tap();
+ await waitFor(element(by.id('room-view')))
+ .toBeVisible()
+ .withTimeout(5000);
+ });
});
});
});
diff --git a/e2e/tests/room/02-room.spec.ts b/e2e/tests/room/02-room.spec.ts
index 8b19a4916..346653e84 100644
--- a/e2e/tests/room/02-room.spec.ts
+++ b/e2e/tests/room/02-room.spec.ts
@@ -305,7 +305,7 @@ describe('Room screen', () => {
.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();
+ await element(by[textMatcher]('Get link')).atIndex(0).tap();
// TODO: test clipboard
});
it('should copy message', async () => {
@@ -438,9 +438,10 @@ describe('Room screen', () => {
.toExist()
.withTimeout(60000);
});
+ let quotedMessage = '';
it('should quote message', async () => {
const quoteMessage = await mockMessage('quote');
- const quotedMessage = `${quoteMessage}d`;
+ quotedMessage = `${quoteMessage}d`;
await tryTapping(element(by[textMatcher](quoteMessage)).atIndex(0), 2000, true);
await waitFor(element(by.id('action-sheet')))
.toExist()
@@ -460,6 +461,15 @@ describe('Room screen', () => {
.toBeVisible()
.withTimeout(3000);
});
+ it('should back to rooms list view and see the last message correctly and navigate again to room', async () => {
+ const expectedLastMessage = `You: ${quotedMessage}`;
+ await sleep(300);
+ await tapBack();
+ await waitFor(element(by.id(`markdown-preview-${expectedLastMessage}`)))
+ .toBeVisible()
+ .withTimeout(5000);
+ await element(by.id(`markdown-preview-${expectedLastMessage}`)).tap();
+ });
it('should delete message', async () => {
const deleteMessage = await mockMessage('delete');
@@ -490,11 +500,11 @@ describe('Room screen', () => {
const { name: replyRoom } = await createRandomRoom(replyUser, 'c');
const originalMessage = 'Message to reply in DM';
const replyMessage = 'replied in dm';
+ await sendMessage(replyUser, replyRoom, originalMessage);
await waitFor(element(by.id('rooms-list-view')))
.toBeVisible()
.withTimeout(2000);
await navigateToRoom(replyRoom);
- await sendMessage(replyUser, replyRoom, originalMessage);
await waitFor(element(by[textMatcher](originalMessage)).atIndex(0))
.toBeVisible()
.withTimeout(10000);
@@ -502,16 +512,27 @@ describe('Room screen', () => {
await waitFor(element(by.id('room-view-join-button')))
.not.toBeVisible()
.withTimeout(10000);
- await element(by[textMatcher](originalMessage)).atIndex(0).tap();
await element(by[textMatcher](originalMessage)).atIndex(0).longPress();
- await sleep(300); // wait for animation
+ await sleep(600); // wait for animation
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
- await waitFor(element(by[textMatcher]('Reply in Direct Message')).atIndex(0))
+ await sleep(600); // wait for animation
+ // Fix android flaky test. Close the action sheet, then re-open again
+ await element(by.id('action-sheet-handle')).swipe('down', 'fast', 0.5);
+ await sleep(1000); // wait for animation
+ await element(by[textMatcher](originalMessage)).atIndex(0).longPress();
+ await sleep(600); // wait for animation
+ await waitFor(element(by.id('action-sheet')))
.toExist()
+ .withTimeout(2000);
+ await sleep(600); // wait for animation
+ await waitFor(element(by[textMatcher]('Reply in direct message')).atIndex(0))
+ .toBeVisible()
.withTimeout(6000);
- await element(by[textMatcher]('Reply in Direct Message')).atIndex(0).tap();
+ await sleep(600); // wait for animation
+ await element(by[textMatcher]('Reply in direct message')).atIndex(0).tap();
+ await sleep(600); // wait for animation
await waitFor(element(by.id(`room-view-title-${replyUser.username}`)))
.toExist()
.withTimeout(6000);
diff --git a/e2e/tests/room/04-discussion.spec.ts b/e2e/tests/room/04-discussion.spec.ts
index af260822d..434316739 100644
--- a/e2e/tests/room/04-discussion.spec.ts
+++ b/e2e/tests/room/04-discussion.spec.ts
@@ -46,7 +46,7 @@ describe('Discussion', () => {
.toExist()
.withTimeout(60000);
await expect(element(by.id('create-discussion-view'))).toExist();
- await element(by[textMatcher]('Select a Channel...')).tap();
+ await element(by[textMatcher]('Select a channel...')).tap();
await element(by.id('multi-select-search')).replaceText(`${room}`);
await waitFor(element(by.id(`multi-select-item-${room}`)))
.toExist()
@@ -76,7 +76,7 @@ describe('Discussion', () => {
await waitFor(element(by.id('action-sheet')))
.toBeVisible()
.withTimeout(2000);
- await element(by[textMatcher]('Create Discussion')).atIndex(0).tap();
+ await element(by[textMatcher]('Create discussion')).atIndex(0).tap();
await waitFor(element(by.id('create-discussion-view')))
.toExist()
.withTimeout(2000);
@@ -108,7 +108,7 @@ describe('Discussion', () => {
await waitFor(element(by.id('action-sheet')))
.toExist()
.withTimeout(2000);
- await element(by[textMatcher]('Start a Discussion')).atIndex(0).tap();
+ await element(by[textMatcher]('Start a discussion')).atIndex(0).tap();
await sleep(1000); // wait for animation
await waitFor(element(by.id('create-discussion-view')))
.toBeVisible()
diff --git a/e2e/tests/room/05-threads.spec.ts b/e2e/tests/room/05-threads.spec.ts
index 177e42742..de8d8b848 100644
--- a/e2e/tests/room/05-threads.spec.ts
+++ b/e2e/tests/room/05-threads.spec.ts
@@ -79,7 +79,7 @@ describe('Threads', () => {
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[textMatcher]('Reply in thread')).atIndex(0).tap();
await element(by.id('messagebox-input')).replaceText('replied');
await waitFor(element(by.id('messagebox-send-message')))
.toExist()
diff --git a/e2e/tests/room/07-markasunread.spec.ts b/e2e/tests/room/07-markasunread.spec.ts
index 664d7ae1a..11b79e98b 100644
--- a/e2e/tests/room/07-markasunread.spec.ts
+++ b/e2e/tests/room/07-markasunread.spec.ts
@@ -32,7 +32,7 @@ describe('Mark as unread', () => {
.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 element(by[textMatcher]('Mark unread')).atIndex(0).tap();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
.withTimeout(5000);
diff --git a/e2e/tests/room/09-jumptomessage.spec.ts b/e2e/tests/room/09-jumptomessage.spec.ts
index 95372ff5d..4f344bda4 100644
--- a/e2e/tests/room/09-jumptomessage.spec.ts
+++ b/e2e/tests/room/09-jumptomessage.spec.ts
@@ -154,14 +154,14 @@ describe('Room', () => {
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]('Load older'))).toBeVisible();
await expect(element(by[textMatcher]('5'))).toExist();
found = true;
} catch {
//
}
}
- await element(by[textMatcher]('Load Older')).atIndex(0).tap();
+ await element(by[textMatcher]('Load older')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('4')))
.toExist()
.withTimeout(5000);
@@ -178,35 +178,35 @@ describe('Room', () => {
.toExist()
.withTimeout(5000);
await element(by.id('room-view-messages')).atIndex(0).swipe('up', 'slow', 0.3);
- await waitFor(element(by[textMatcher]('Load Newer')))
+ await waitFor(element(by[textMatcher]('Load newer')))
.toExist()
.withTimeout(5000);
- await element(by[textMatcher]('Load Newer')).atIndex(0).tap();
+ 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')))
+ await waitFor(element(by[textMatcher]('Load newer')))
.toExist()
.withTimeout(5000);
- await element(by[textMatcher]('Load Newer')).atIndex(0).tap();
+ 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')))
+ await waitFor(element(by[textMatcher]('Load newer')))
.toExist()
.withTimeout(5000);
- await element(by[textMatcher]('Load Newer')).atIndex(0).tap();
+ await element(by[textMatcher]('Load newer')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('202')))
.toExist()
.withTimeout(5000);
- await waitFor(element(by[textMatcher]('Load Newer')))
+ 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')))
+ 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]('Load more'))).toNotExist();
await expect(element(by[textMatcher]('252'))).toExist();
await expect(element(by[textMatcher]('253'))).toExist();
await tapBack();
diff --git a/e2e/tests/team/03-moveconvert.spec.ts b/e2e/tests/team/03-moveconvert.spec.ts
index b2721e6a1..ae8a63f5f 100644
--- a/e2e/tests/team/03-moveconvert.spec.ts
+++ b/e2e/tests/team/03-moveconvert.spec.ts
@@ -6,8 +6,9 @@ import random from '../../helpers/random';
const toBeConverted = `to-be-converted-${random()}`;
const toBeMoved = `to-be-moved-${random()}`;
+const publicChannelToBeConverted = `channel-public-to-be-converted-${random()}`;
-const createChannel = async (room: string) => {
+const createChannel = async (room: string, publicChannel?: boolean) => {
await waitFor(element(by.id('rooms-list-view-create-channel')))
.toBeVisible()
.withTimeout(5000);
@@ -28,6 +29,9 @@ const createChannel = async (room: string) => {
.withTimeout(10000);
await element(by.id('create-channel-name')).replaceText(room);
await element(by.id('create-channel-name')).tapReturnKey();
+ if (publicChannel) {
+ await element(by.id('create-channel-type')).tap();
+ }
await waitFor(element(by.id('create-channel-submit')))
.toExist()
.withTimeout(10000);
@@ -68,18 +72,35 @@ describe('Move/Convert Team', () => {
});
describe('Convert', () => {
- beforeAll(async () => {
- await createChannel(toBeConverted);
+ it('should convert public channel to a team', async () => {
+ await createChannel(publicChannelToBeConverted, true);
+ await navigateToRoomActions(publicChannelToBeConverted);
+ await element(by.id('room-actions-scrollview')).scrollTo('bottom');
+ await waitFor(element(by.id('room-actions-convert-to-team')))
+ .toBeVisible()
+ .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-${publicChannelToBeConverted}`)))
+ .toExist()
+ .withTimeout(6000);
});
- it('should convert channel to a team', async () => {
+ it('should convert private channel to a team', async () => {
+ await createChannel(toBeConverted);
await navigateToRoomActions(toBeConverted);
await element(by.id('room-actions-scrollview')).scrollTo('bottom');
await waitFor(element(by.id('room-actions-convert-to-team')))
.toBeVisible()
.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.')))
+ 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();
@@ -91,7 +112,7 @@ describe('Move/Convert Team', () => {
.withTimeout(6000);
});
- afterAll(async () => {
+ afterEach(async () => {
await tapBack();
await waitFor(element(by.id('rooms-list-view')))
.toExist()
@@ -151,11 +172,11 @@ describe('Move/Convert Team', () => {
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')))
+ 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')))
+ 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}`)))
@@ -167,7 +188,7 @@ describe('Move/Convert Team', () => {
.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')))
+ 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();
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index c638e11f2..397db9568 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -407,8 +407,8 @@ PODS:
- React
- react-native-slider (4.4.2):
- React-Core
- - react-native-webview (10.3.2):
- - React
+ - react-native-webview (11.26.1):
+ - React-Core
- React-perflogger (0.71.7)
- React-RCTActionSheet (0.71.7):
- React-Core/RCTActionSheetHeaders (= 0.71.7)
@@ -949,7 +949,7 @@ SPEC CHECKSUMS:
react-native-safe-area-context: f0906bf8bc9835ac9a9d3f97e8bde2a997d8da79
react-native-simple-crypto: a26121696064628b6cb92f52f653353114deb7f4
react-native-slider: 33b8d190b59d4f67a541061bb91775d53d617d9d
- react-native-webview: 679b6f400176e2ea8a785acf7ae16cf282e7d1eb
+ react-native-webview: 9f111dfbcfc826084d6c507f569e5e03342ee1c1
React-perflogger: 2d505bbe298e3b7bacdd9e542b15535be07220f6
React-RCTActionSheet: 0e96e4560bd733c9b37efbf68f5b1a47615892fb
React-RCTAnimation: fd138e26f120371c87e406745a27535e2c8a04ef
diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj
index ab98c3f81..4dc095e32 100644
--- a/ios/RocketChatRN.xcodeproj/project.pbxproj
+++ b/ios/RocketChatRN.xcodeproj/project.pbxproj
@@ -1752,7 +1752,7 @@
INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
- MARKETING_VERSION = 4.39.0;
+ MARKETING_VERSION = 4.40.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
@@ -1791,7 +1791,7 @@
INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
- MARKETING_VERSION = 4.39.0;
+ MARKETING_VERSION = 4.40.0;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist
index 7c6dd9259..563bd43e6 100644
--- a/ios/RocketChatRN/Info.plist
+++ b/ios/RocketChatRN/Info.plist
@@ -26,7 +26,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 4.39.0
+ 4.40.0
CFBundleSignature
????
CFBundleURLTypes
diff --git a/ios/ShareRocketChatRN/Info.plist b/ios/ShareRocketChatRN/Info.plist
index f197651d6..a54193a43 100644
--- a/ios/ShareRocketChatRN/Info.plist
+++ b/ios/ShareRocketChatRN/Info.plist
@@ -26,7 +26,7 @@
CFBundlePackageType
XPC!
CFBundleShortVersionString
- 4.39.0
+ 4.40.0
CFBundleVersion
1
KeychainGroup
diff --git a/package.json b/package.json
index 98b98b777..f67c2c336 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rocket-chat-reactnative",
- "version": "4.39.0",
+ "version": "4.40.0",
"private": true,
"scripts": {
"start": "react-native start",
@@ -33,7 +33,7 @@
"*.{js,ts,tsx}": [
"prettier --write",
"eslint",
- "jest --bail --findRelatedTests"
+ "jest --bail --findRelatedTests --passWithNoTests"
]
},
"dependencies": {
@@ -131,7 +131,7 @@
"react-native-svg": "^13.8.0",
"react-native-ui-lib": "RocketChat/react-native-ui-lib#7.2.0",
"react-native-vector-icons": "^9.2.0",
- "react-native-webview": "10.3.2",
+ "react-native-webview": "11.26.1",
"react-redux": "^8.0.5",
"reactotron-react-native": "^5.0.3",
"redux": "4.2.0",
@@ -143,6 +143,7 @@
"rn-fetch-blob": "^0.12.0",
"rn-root-view": "RocketChat/rn-root-view",
"semver": "^7.3.8",
+ "transliteration": "^2.3.5",
"ua-parser-js": "^1.0.32",
"uri-js": "^4.4.1",
"url-parse": "1.5.10",
diff --git a/patches/react-native-webview+10.3.2.patch b/patches/react-native-webview+11.26.1.patch
similarity index 70%
rename from patches/react-native-webview+10.3.2.patch
rename to patches/react-native-webview+11.26.1.patch
index 6c9db9d55..35b11542f 100644
--- a/patches/react-native-webview+10.3.2.patch
+++ b/patches/react-native-webview+11.26.1.patch
@@ -1,10 +1,10 @@
diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
-index ab869cf..08ce7ce 100644
+index 9cfe821..b7fe976 100644
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
-@@ -84,6 +84,12 @@ import java.util.Map;
-
- import javax.annotation.Nullable;
+@@ -102,6 +102,12 @@ import java.util.Locale;
+ import java.util.Map;
+ import java.util.concurrent.atomic.AtomicReference;
+import java.security.cert.X509Certificate;
+import java.security.PrivateKey;
@@ -15,16 +15,16 @@ index ab869cf..08ce7ce 100644
/**
* Manages instances of {@link WebView}
*
-@@ -140,6 +146,8 @@ public class RNCWebViewManager extends SimpleViewManager {
- protected @Nullable String mUserAgent = null;
- protected @Nullable String mUserAgentWithApplicationName = null;
+@@ -166,6 +172,8 @@ public class RNCWebViewManager extends SimpleViewManager {
+ protected @Nullable String mDownloadingMessage = null;
+ protected @Nullable String mLackPermissionToDownloadMessage = null;
+ private static String certificateAlias = null;
+
public RNCWebViewManager() {
mWebViewConfig = new WebViewConfig() {
public void configWebView(WebView webView) {
-@@ -151,6 +159,10 @@ public class RNCWebViewManager extends SimpleViewManager {
+@@ -177,6 +185,10 @@ public class RNCWebViewManager extends SimpleViewManager {
mWebViewConfig = webViewConfig;
}
@@ -32,10 +32,10 @@ index ab869cf..08ce7ce 100644
+ certificateAlias = alias;
+ }
+
- protected static void dispatchEvent(WebView webView, Event event) {
- ReactContext reactContext = (ReactContext) webView.getContext();
- EventDispatcher eventDispatcher =
-@@ -562,7 +574,7 @@ public class RNCWebViewManager extends SimpleViewManager {
+ @Override
+ public String getName() {
+ return REACT_CLASS;
+@@ -687,7 +699,7 @@ public class RNCWebViewManager extends SimpleViewManager {
@Override
protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
// Do not register default touch emitter and let WebView implementation handle touches
@@ -44,17 +44,12 @@ index ab869cf..08ce7ce 100644
}
@Override
-@@ -742,12 +754,56 @@ public class RNCWebViewManager extends SimpleViewManager {
-
- protected static class RNCWebViewClient extends WebViewClient {
-
-+ protected ReactContext reactContext;
- protected boolean mLastLoadFailed = false;
- protected @Nullable
- ReadableArray mUrlPrefixesForDefaultIntent;
+@@ -913,6 +925,50 @@ public class RNCWebViewManager extends SimpleViewManager {
protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
protected @Nullable String ignoreErrFailedForThisURL = null;
-
+ protected @Nullable BasicAuthCredential basicAuthCredential = null;
++ protected ReactContext reactContext;
++
+ public RNCWebViewClient(ReactContext reactContext) {
+ this.reactContext = reactContext;
+ }
@@ -97,12 +92,11 @@ index ab869cf..08ce7ce 100644
+ super.onReceivedClientCertRequest(view, request);
+ }
+ }
-+
+
public void setIgnoreErrFailedForThisURL(@Nullable String url) {
ignoreErrFailedForThisURL = url;
- }
diff --git a/node_modules/react-native-webview/apple/RNCWebView.m b/node_modules/react-native-webview/apple/RNCWebView.m
-index 02b4238..e0635ed 100644
+index 7570d8d..eaa0e5d 100644
--- a/node_modules/react-native-webview/apple/RNCWebView.m
+++ b/node_modules/react-native-webview/apple/RNCWebView.m
@@ -17,6 +17,9 @@
@@ -115,8 +109,8 @@ index 02b4238..e0635ed 100644
static NSTimer *keyboardTimer;
static NSString *const HistoryShimName = @"ReactNativeHistoryShim";
static NSString *const MessageHandlerName = @"ReactNativeWebView";
-@@ -737,6 +740,68 @@ static NSDictionary* customCertificatesForHost;
- customCertificatesForHost = certificates;
+@@ -963,6 +966,68 @@ + (void)setCustomCertificatesForHost:(nullable NSDictionary*)certificates {
+ customCertificatesForHost = certificates;
}
+-(NSURLCredential *)getUrlCredential:(NSURLAuthenticationChallenge *)challenge path:(NSString *)path password:(NSString *)password
@@ -184,37 +178,36 @@ index 02b4238..e0635ed 100644
- (void) webView:(WKWebView *)webView
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable))completionHandler
-@@ -746,7 +811,32 @@ static NSDictionary* customCertificatesForHost;
- host = webView.URL.host;
- }
- if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
-- completionHandler(NSURLSessionAuthChallengeUseCredential, clientAuthenticationCredential);
-+ NSString *host = challenge.protectionSpace.host;
+@@ -972,7 +1037,31 @@ - (void) webView:(WKWebView *)webView
+ host = webView.URL.host;
+ }
+ if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
+- completionHandler(NSURLSessionAuthChallengeUseCredential, clientAuthenticationCredential);
++ NSString *host = challenge.protectionSpace.host;
+
-+ // Read the clientSSL info from MMKV
-+ __block NSDictionary *clientSSL;
-+ SecureStorage *secureStorage = [[SecureStorage alloc] init];
-+
-+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
-+ NSString *key = [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"]];
-+ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
-+
-+ if (key == NULL) {
-+ return completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, credential);
-+ }
-+
-+ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding];
-+ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess];
-+ clientSSL = [mmkv getObjectOfClass:[NSDictionary class] forKey:host];
-+
-+
-+ if (clientSSL != (id)[NSNull null]) {
-+ NSString *path = [clientSSL objectForKey:@"path"];
-+ NSString *password = [clientSSL objectForKey:@"password"];
-+ credential = [self getUrlCredential:challenge path:path password:password];
-+ }
-+
-+ completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
- return;
- }
- if ([[challenge protectionSpace] serverTrust] != nil && customCertificatesForHost != nil && host != nil) {
++ // Read the clientSSL info from MMKV
++ __block NSDictionary *clientSSL;
++ SecureStorage *secureStorage = [[SecureStorage alloc] init];
++
++ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
++ NSString *key = [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"]];
++ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
++
++ if (key == NULL) {
++ return completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, credential);
++ }
++
++ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding];
++ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess];
++ clientSSL = [mmkv getObjectOfClass:[NSDictionary class] forKey:host];
++
++ if (clientSSL != (id)[NSNull null]) {
++ NSString *path = [clientSSL objectForKey:@"path"];
++ NSString *password = [clientSSL objectForKey:@"password"];
++ credential = [self getUrlCredential:challenge path:path password:password];
++ }
++
++ completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
+ return;
+ }
+ if ([[challenge protectionSpace] serverTrust] != nil && customCertificatesForHost != nil && host != nil) {
diff --git a/yarn.lock b/yarn.lock
index a61f6de87..79927ade6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -17242,10 +17242,10 @@ react-native-vector-icons@^9.2.0:
prop-types "^15.7.2"
yargs "^16.1.1"
-react-native-webview@10.3.2:
- version "10.3.2"
- resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-10.3.2.tgz#c634946152099c95d521a3abc71065d1d642e192"
- integrity sha512-4A8FKL/puonkqQ1FOKd+iPulqRXCG4inmIK4pQ60zv9Ua+YkBKLxxofQiCvRwIXSSgAXYT+AE3rOHr3bx4A/cw==
+react-native-webview@11.26.1:
+ version "11.26.1"
+ resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-11.26.1.tgz#658c09ed5162dc170b361e48c2dd26c9712879da"
+ integrity sha512-hC7BkxOpf+z0UKhxFSFTPAM4shQzYmZHoELa6/8a/MspcjEP7ukYKpuSUTLDywQditT8yI9idfcKvfZDKQExGw==
dependencies:
escape-string-regexp "2.0.0"
invariant "2.2.4"
@@ -19640,6 +19640,13 @@ transformation-matrix@^2.8.0:
resolved "https://registry.yarnpkg.com/transformation-matrix/-/transformation-matrix-2.12.0.tgz#cb826a23aa5d675d18940215ccb7613b8587830f"
integrity sha512-BbzXM7el7rNwIr1s87m8tcffH5qgY+HYROLn3BStRU9Y6vYTL37YZKadfNPEvGbP813iA1h8qflo4pa2TomkyQ==
+transliteration@^2.3.5:
+ version "2.3.5"
+ resolved "https://registry.yarnpkg.com/transliteration/-/transliteration-2.3.5.tgz#8f92309575f69e4a8a525dab4ff705ebcf961c45"
+ integrity sha512-HAGI4Lq4Q9dZ3Utu2phaWgtm3vB6PkLUFqWAScg/UW+1eZ/Tg6Exo4oC0/3VUol/w4BlefLhUUSVBr/9/ZGQOw==
+ dependencies:
+ yargs "^17.5.1"
+
traverse@~0.6.6:
version "0.6.6"
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"