diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 21e0695f..184a4d96 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -15,6 +15,7 @@ import { } from '../actions/app'; import { localAuthenticate } from '../utils/localAuthentication'; import { goRoom } from '../utils/goRoom'; +import { loginRequest } from '../actions/login'; const roomTypes = { channel: 'c', direct: 'd', group: 'p', channels: 'l' @@ -41,9 +42,13 @@ const popToRoot = function popToRoot({ isMasterDetail }) { const navigate = function* navigate({ params }) { yield put(appStart({ root: ROOT_INSIDE })); - if (params.path) { - const [type, name] = params.path.split('/'); - if (type !== 'invite') { + if (params.path || params.rid) { + let type; + let name; + if (params.path) { + [type, name] = params.path.split('/'); + } + if (type !== 'invite' || params.rid) { const room = yield RocketChat.canOpenRoom(params); if (room) { const item = { @@ -110,7 +115,11 @@ const handleOpen = function* handleOpen({ params }) { // If there's host, continue if (!/^(http|https)/.test(host)) { - host = `https://${ host }`; + if (/^localhost(:\d+)?/.test(host)) { + host = `http://${ host }`; + } else { + host = `https://${ host }`; + } } else { // Notification should always come from https host = host.replace('http://', 'https://'); @@ -163,7 +172,7 @@ const handleOpen = function* handleOpen({ params }) { if (params.token) { 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 navigate({ params }); } else { diff --git a/e2e/data/data.docker.js b/e2e/data/data.docker.js index e0bb530e..a5d56af5 100644 --- a/e2e/data/data.docker.js +++ b/e2e/data/data.docker.js @@ -1,7 +1,7 @@ const random = require('./helpers/random'); const value = random(20); const data = { - server: 'http://127.0.0.1:3000', + server: 'http://localhost:3000', adminUser: 'admin', adminPassword: 'password', alternateServer: 'https://stable.rocket.chat', diff --git a/e2e/helpers/app.js b/e2e/helpers/app.js index 993f2eb9..8a07cb45 100644 --- a/e2e/helpers/app.js +++ b/e2e/helpers/app.js @@ -3,30 +3,29 @@ const { } = require('detox'); const data = require('../data'); -async function navigateToWorkspace() { +async function navigateToWorkspace(server = data.server) { await waitFor(element(by.id('onboarding-view'))).toBeVisible().withTimeout(10000); await element(by.id('join-workspace')).tap(); 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 waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); 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 navigateToWorkspace(); + await navigateToWorkspace(server); await element(by.id('workspace-view-login')).tap(); await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); 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 navigateToWorkspace(); + await navigateToWorkspace(server); await element(by.id('workspace-view-register')).tap(); await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('register-view'))).toBeVisible(); } 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 = { navigateToWorkspace, navigateToLogin, @@ -133,5 +140,6 @@ module.exports = { tapBack, sleep, searchRoom, - tryTapping + tryTapping, + checkServer }; \ No newline at end of file diff --git a/e2e/helpers/data_setup.js b/e2e/helpers/data_setup.js index 8f95f0b7..66c6ea08 100644 --- a/e2e/helpers/data_setup.js +++ b/e2e/helpers/data_setup.js @@ -135,6 +135,16 @@ const setup = async () => { return } +const get = (endpoint) => { + 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 + setup, sendMessage, get, post } \ No newline at end of file diff --git a/e2e/tests/assorted/07-changeserver.spec.js b/e2e/tests/assorted/07-changeserver.spec.js index e91acfbb..63506f89 100644 --- a/e2e/tests/assorted/07-changeserver.spec.js +++ b/e2e/tests/assorted/07-changeserver.spec.js @@ -2,15 +2,7 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { sleep, navigateToLogin, login } = 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 { navigateToLogin, login, checkServer } = require('../../helpers/app'); const reopenAndCheckServer = async(server) => { await device.launchApp({ permissions: { notifications: 'YES' } }); diff --git a/e2e/tests/assorted/10-deleteserver.spec.js b/e2e/tests/assorted/10-deleteserver.spec.js index 7d5d53e7..474ebd26 100644 --- a/e2e/tests/assorted/10-deleteserver.spec.js +++ b/e2e/tests/assorted/10-deleteserver.spec.js @@ -2,15 +2,7 @@ const { device, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { sleep, navigateToLogin, login } = 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(); -} +const { sleep, navigateToLogin, login, checkServer } = require('../../helpers/app'); describe('Delete server', () => { before(async() => { diff --git a/e2e/tests/assorted/11-deeplinking.spec.js b/e2e/tests/assorted/11-deeplinking.spec.js new file mode 100644 index 00000000..cdde0323 --- /dev/null +++ b/e2e/tests/assorted/11-deeplinking.spec.js @@ -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); + }); + }); + }); +});