[REGRESSION] Auth via deep linking not working (#3015)

* Update rocketchat and add e2e test for deep linking

* Update rocketchat and add e2e test for deep linking

* Update deeplinking e2e

* fix deep linking auth

* Test deep linking auth

* Fix deeplink to rid and add tests

* Small refactor

* Add non existing server test

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Gerzon Z 2021-03-31 17:01:20 -04:00 committed by GitHub
parent 25b71155e6
commit c5bf13c22d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 167 additions and 33 deletions

View File

@ -15,6 +15,7 @@ import {
} from '../actions/app'; } from '../actions/app';
import { localAuthenticate } from '../utils/localAuthentication'; import { localAuthenticate } from '../utils/localAuthentication';
import { goRoom } from '../utils/goRoom'; import { goRoom } from '../utils/goRoom';
import { loginRequest } from '../actions/login';
const roomTypes = { const roomTypes = {
channel: 'c', direct: 'd', group: 'p', channels: 'l' channel: 'c', direct: 'd', group: 'p', channels: 'l'
@ -41,9 +42,13 @@ const popToRoot = function popToRoot({ isMasterDetail }) {
const navigate = function* navigate({ params }) { const navigate = function* navigate({ params }) {
yield put(appStart({ root: ROOT_INSIDE })); yield put(appStart({ root: ROOT_INSIDE }));
if (params.path || params.rid) {
let type;
let name;
if (params.path) { if (params.path) {
const [type, name] = params.path.split('/'); [type, name] = params.path.split('/');
if (type !== 'invite') { }
if (type !== 'invite' || params.rid) {
const room = yield RocketChat.canOpenRoom(params); const room = yield RocketChat.canOpenRoom(params);
if (room) { if (room) {
const item = { const item = {
@ -110,7 +115,11 @@ const handleOpen = function* handleOpen({ params }) {
// If there's host, continue // If there's host, continue
if (!/^(http|https)/.test(host)) { if (!/^(http|https)/.test(host)) {
if (/^localhost(:\d+)?/.test(host)) {
host = `http://${ host }`;
} else {
host = `https://${ host }`; host = `https://${ host }`;
}
} else { } else {
// Notification should always come from https // Notification should always come from https
host = host.replace('http://', 'https://'); host = host.replace('http://', 'https://');
@ -163,7 +172,7 @@ const handleOpen = function* handleOpen({ params }) {
if (params.token) { if (params.token) {
yield take(types.SERVER.SELECT_SUCCESS); yield take(types.SERVER.SELECT_SUCCESS);
yield RocketChat.connect({ server: host, user: { token: params.token } }); yield put(loginRequest({ resume: params.token }, true));
yield take(types.LOGIN.SUCCESS); yield take(types.LOGIN.SUCCESS);
yield navigate({ params }); yield navigate({ params });
} else { } else {

View File

@ -1,7 +1,7 @@
const random = require('./helpers/random'); const random = require('./helpers/random');
const value = random(20); const value = random(20);
const data = { const data = {
server: 'http://127.0.0.1:3000', server: 'http://localhost:3000',
adminUser: 'admin', adminUser: 'admin',
adminPassword: 'password', adminPassword: 'password',
alternateServer: 'https://stable.rocket.chat', alternateServer: 'https://stable.rocket.chat',

View File

@ -3,30 +3,29 @@ const {
} = require('detox'); } = require('detox');
const data = require('../data'); const data = require('../data');
async function navigateToWorkspace() { async function navigateToWorkspace(server = data.server) {
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(10000); await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(10000);
await element(by.id('join-workspace')).tap(); await element(by.id('join-workspace')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
await element(by.id('new-server-view-input')).replaceText(data.server); await element(by.id('new-server-view-input')).replaceText(server);
await element(by.id('new-server-view-button')).tap(); await element(by.id('new-server-view-button')).tap();
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('workspace-view'))).toBeVisible(); await expect(element(by.id('workspace-view'))).toBeVisible();
} }
async function navigateToLogin() { async function navigateToLogin(server) {
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(20000); await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(20000);
await navigateToWorkspace(); await navigateToWorkspace(server);
await element(by.id('workspace-view-login')).tap(); await element(by.id('workspace-view-login')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('login-view'))).toBeVisible(); await expect(element(by.id('login-view'))).toBeVisible();
} }
async function navigateToRegister() { async function navigateToRegister(server) {
await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(20000); await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(20000);
await navigateToWorkspace(); await navigateToWorkspace(server);
await element(by.id('workspace-view-register')).tap(); await element(by.id('workspace-view-register')).tap();
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('register-view'))).toBeVisible();
} }
async function login(username, password) { async function login(username, password) {
@ -120,6 +119,14 @@ async function tryTapping(theElement, timeout, longtap = false){
} }
} }
const checkServer = async(server) => {
const label = `Connected to ${ server }`;
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.label(label))).toBeVisible().withTimeout(10000);
await element(by.id('sidebar-close-drawer')).tap();
}
module.exports = { module.exports = {
navigateToWorkspace, navigateToWorkspace,
navigateToLogin, navigateToLogin,
@ -133,5 +140,6 @@ module.exports = {
tapBack, tapBack,
sleep, sleep,
searchRoom, searchRoom,
tryTapping tryTapping,
checkServer
}; };

View File

@ -135,6 +135,16 @@ const setup = async () => {
return return
} }
module.exports = { const get = (endpoint) => {
setup, sendMessage console.log(`GET /${ endpoint }`)
return rocketchat.get(endpoint);
}
const post = (endpoint, body) => {
console.log(`POST /${ endpoint } ${ JSON.stringify(body) }`)
return rocketchat.post(endpoint, body);
}
module.exports = {
setup, sendMessage, get, post
} }

View File

@ -2,15 +2,7 @@ const {
device, expect, element, by, waitFor device, expect, element, by, waitFor
} = require('detox'); } = require('detox');
const data = require('../../data'); const data = require('../../data');
const { sleep, navigateToLogin, login } = require('../../helpers/app'); const { navigateToLogin, login, checkServer } = require('../../helpers/app');
const checkServer = async(server) => {
const label = `Connected to ${ server }`;
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.label(label))).toBeVisible().withTimeout(60000);
await element(by.id('sidebar-close-drawer')).tap();
}
const reopenAndCheckServer = async(server) => { const reopenAndCheckServer = async(server) => {
await device.launchApp({ permissions: { notifications: 'YES' } }); await device.launchApp({ permissions: { notifications: 'YES' } });

View File

@ -2,15 +2,7 @@ const {
device, element, by, waitFor device, element, by, waitFor
} = require('detox'); } = require('detox');
const data = require('../../data'); const data = require('../../data');
const { sleep, navigateToLogin, login } = require('../../helpers/app'); const { sleep, navigateToLogin, login, checkServer } = require('../../helpers/app');
const checkServer = async(server) => {
const label = `Connected to ${ server }`;
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.label(label))).toBeVisible().withTimeout(10000);
await element(by.id('sidebar-close-drawer')).tap();
}
describe('Delete server', () => { describe('Delete server', () => {
before(async() => { before(async() => {

View File

@ -0,0 +1,123 @@
const {
device, element, by, waitFor
} = require('detox');
const data = require('../../data');
const { tapBack, checkServer, navigateToRegister, login } = require('../../helpers/app');
const { post, get } = require('../../helpers/data_setup');
const DEEPLINK_METHODS = { AUTH: 'auth', ROOM: 'room' };
const getDeepLink = (method, server, params) => {
const deeplink = `rocketchat://${ method }?host=${ server.replace(/^(http:\/\/|https:\/\/)/, '') }&${params}`;
console.log(`Deeplinking to: ${ deeplink }`);
return deeplink;
};
describe('Deep linking', () => {
let userId;
let token;
before(async() => {
const loginResult = await post('login', {
user: data.users.regular.username,
password: data.users.regular.password
})
userId = loginResult.data.data.userId
token = loginResult.data.data.authToken
});
describe('Authentication', () => {
it('should run a deep link to an invalid account and raise error', async() => {
await device.launchApp({
permissions: { notifications: 'YES' },
newInstance: true,
url: getDeepLink(DEEPLINK_METHODS.AUTH, data.server, 'userId=123&token=abc'),
sourceApp: 'com.apple.mobilesafari'
});
await waitFor(element(by.text('You\'ve been logged out by the server. Please log in again.'))).toExist().withTimeout(5000); // 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 }&token=${ token }&path=group/${ data.groups.private.name }`),
sourceApp: 'com.apple.mobilesafari'
});
await waitFor(element(by.id(`room-view-title-${ data.groups.private.name }`))).toExist().withTimeout(10000);
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 }`))).toBeVisible().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.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(10000);
await authAndNavigate();
});
});
describe('Room', () => {
describe('While logged in', async() => {
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 }`),
sourceApp: 'com.apple.mobilesafari'
});
await waitFor(element(by.id(`room-view-title-${ data.groups.private.name }`))).toExist().withTimeout(10000);
});
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 }`),
sourceApp: 'com.apple.mobilesafari'
});
await waitFor(element(by.id(`room-view-title-${ data.groups.private.name }`))).toExist().withTimeout(10000);
await tapBack();
});
});
describe('Others', async() => {
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 }`),
sourceApp: 'com.apple.mobilesafari'
});
await waitFor(element(by.id(`room-view-title-${ data.groups.private.name }`))).toExist().withTimeout(10000);
});
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'),
sourceApp: 'com.apple.mobilesafari'
});
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await checkServer(data.server);
});
});
});
});