2016-08-10 01:15:04 +02:00
|
|
|
/*
|
|
|
|
Copyright 2016 OpenMarket Ltd
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* loading.js: test the myriad paths we have for loading the application */
|
|
|
|
|
2017-11-19 21:46:40 +01:00
|
|
|
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
|
|
|
|
import Platform from '../../src/vector/platform';
|
|
|
|
|
2016-12-09 11:31:26 +01:00
|
|
|
import 'skin-sdk';
|
|
|
|
|
2016-08-10 01:15:04 +02:00
|
|
|
import React from 'react';
|
|
|
|
import ReactDOM from 'react-dom';
|
|
|
|
import ReactTestUtils from 'react-addons-test-utils';
|
|
|
|
import expect from 'expect';
|
2017-07-13 01:48:31 +02:00
|
|
|
import Promise from 'bluebird';
|
2017-07-13 01:18:49 +02:00
|
|
|
import MatrixReactTestUtils from 'matrix-react-test-utils';
|
2016-08-10 01:15:04 +02:00
|
|
|
|
|
|
|
import jssdk from 'matrix-js-sdk';
|
|
|
|
|
|
|
|
import sdk from 'matrix-react-sdk';
|
|
|
|
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
2017-05-26 16:29:58 +02:00
|
|
|
import * as languageHandler from 'matrix-react-sdk/lib/languageHandler';
|
2017-06-13 15:45:05 +02:00
|
|
|
import {VIEWS} from 'matrix-react-sdk/lib/components/structures/MatrixChat';
|
2017-06-14 21:27:02 +02:00
|
|
|
import dis from 'matrix-react-sdk/lib/dispatcher';
|
2016-08-10 01:15:04 +02:00
|
|
|
|
2017-06-14 18:08:49 +02:00
|
|
|
import * as test_utils from '../test-utils';
|
2017-07-04 16:25:09 +02:00
|
|
|
import MockHttpBackend from 'matrix-mock-request';
|
2016-08-10 01:15:04 +02:00
|
|
|
import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils';
|
|
|
|
|
2016-08-11 03:00:27 +02:00
|
|
|
var DEFAULT_HS_URL='http://my_server';
|
|
|
|
var DEFAULT_IS_URL='http://my_is';
|
2016-08-10 01:15:04 +02:00
|
|
|
|
|
|
|
describe('loading:', function () {
|
|
|
|
let parentDiv;
|
|
|
|
let httpBackend;
|
|
|
|
|
|
|
|
// an Object simulating the window.location
|
|
|
|
let windowLocation;
|
|
|
|
|
2016-08-11 03:00:27 +02:00
|
|
|
// the mounted MatrixChat
|
|
|
|
let matrixChat;
|
|
|
|
|
2017-06-16 16:05:14 +02:00
|
|
|
// a promise which resolves when the MatrixChat calls onTokenLoginCompleted
|
2017-06-19 10:04:24 +02:00
|
|
|
let tokenLoginCompletePromise;
|
2016-08-11 03:00:27 +02:00
|
|
|
|
2016-08-10 01:15:04 +02:00
|
|
|
beforeEach(function() {
|
|
|
|
test_utils.beforeEach(this);
|
|
|
|
httpBackend = new MockHttpBackend();
|
|
|
|
jssdk.request(httpBackend.requestFn);
|
|
|
|
parentDiv = document.createElement('div');
|
|
|
|
|
|
|
|
// uncomment this to actually add the div to the UI, to help with
|
|
|
|
// debugging (but slow things down)
|
|
|
|
// document.body.appendChild(parentDiv);
|
|
|
|
|
|
|
|
windowLocation = null;
|
2016-08-11 03:00:27 +02:00
|
|
|
matrixChat = null;
|
2017-05-26 16:29:58 +02:00
|
|
|
|
|
|
|
languageHandler.setMissingEntryGenerator(function(key) {
|
|
|
|
return key.split('|', 2)[1];
|
|
|
|
});
|
2016-08-10 01:15:04 +02:00
|
|
|
});
|
|
|
|
|
2017-06-20 16:35:01 +02:00
|
|
|
afterEach(async function () {
|
2017-06-20 18:41:21 +02:00
|
|
|
console.log(`${Date.now()}: loading: afterEach`);
|
2016-08-10 01:15:04 +02:00
|
|
|
if (parentDiv) {
|
|
|
|
ReactDOM.unmountComponentAtNode(parentDiv);
|
|
|
|
parentDiv.remove();
|
|
|
|
parentDiv = null;
|
|
|
|
}
|
2017-06-14 18:08:49 +02:00
|
|
|
|
|
|
|
// unmounting should have cleared the MatrixClientPeg
|
|
|
|
expect(MatrixClientPeg.get()).toBe(null);
|
|
|
|
|
2017-07-20 12:01:27 +02:00
|
|
|
// chrome seems to take *ages* to delete the indexeddbs.
|
|
|
|
this.timeout(10000);
|
|
|
|
|
2017-06-14 18:08:49 +02:00
|
|
|
// clear the indexeddbs so we can start from a clean slate next time.
|
2017-07-20 12:01:27 +02:00
|
|
|
await Promise.all([
|
|
|
|
test_utils.deleteIndexedDB('matrix-js-sdk:crypto'),
|
|
|
|
test_utils.deleteIndexedDB('matrix-js-sdk:riot-web-sync'),
|
|
|
|
]);
|
2017-06-20 18:41:21 +02:00
|
|
|
console.log(`${Date.now()}: loading: afterEach complete`);
|
2016-08-10 01:15:04 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
/* simulate the load process done by index.js
|
|
|
|
*
|
|
|
|
* TODO: it would be nice to factor some of this stuff out of index.js so
|
|
|
|
* that we can test it rather than our own implementation of it.
|
|
|
|
*/
|
2016-08-11 03:00:27 +02:00
|
|
|
function loadApp(opts) {
|
|
|
|
opts = opts || {};
|
|
|
|
const queryString = opts.queryString || "";
|
|
|
|
const uriFragment = opts.uriFragment || "";
|
|
|
|
|
2016-08-10 01:15:04 +02:00
|
|
|
windowLocation = {
|
2016-08-11 03:00:27 +02:00
|
|
|
search: queryString,
|
2016-08-10 01:15:04 +02:00
|
|
|
hash: uriFragment,
|
|
|
|
toString: function() { return this.search + this.hash; },
|
|
|
|
};
|
|
|
|
|
2017-07-13 01:33:28 +02:00
|
|
|
let tokenLoginCompleteDefer = Promise.defer();
|
2017-06-19 10:04:24 +02:00
|
|
|
tokenLoginCompletePromise = tokenLoginCompleteDefer.promise;
|
2016-08-11 03:00:27 +02:00
|
|
|
|
2016-08-10 01:15:04 +02:00
|
|
|
function onNewScreen(screen) {
|
2017-01-24 12:26:09 +01:00
|
|
|
console.log(Date.now() + " newscreen "+screen);
|
2017-06-14 19:00:02 +02:00
|
|
|
var hash = '#/' + screen;
|
|
|
|
windowLocation.hash = hash;
|
|
|
|
console.log(Date.now() + " browser URI now "+ windowLocation);
|
2016-08-10 01:15:04 +02:00
|
|
|
}
|
|
|
|
|
2017-03-29 17:07:11 +02:00
|
|
|
// Parse the given window.location and return parameters that can be used when calling
|
|
|
|
// MatrixChat.showScreen(screen, params)
|
|
|
|
function getScreenFromLocation(location) {
|
|
|
|
const fragparts = parseQsFromFragment(location);
|
|
|
|
return {
|
|
|
|
screen: fragparts.location.substring(1),
|
|
|
|
params: fragparts.params,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-10 01:15:04 +02:00
|
|
|
const MatrixChat = sdk.getComponent('structures.MatrixChat');
|
|
|
|
const fragParts = parseQsFromFragment(windowLocation);
|
2017-06-15 03:17:24 +02:00
|
|
|
|
|
|
|
const config = Object.assign({
|
|
|
|
default_hs_url: DEFAULT_HS_URL,
|
|
|
|
default_is_url: DEFAULT_IS_URL,
|
|
|
|
}, opts.config || {});
|
|
|
|
|
2017-11-19 21:46:40 +01:00
|
|
|
PlatformPeg.set(new Platform());
|
|
|
|
|
2016-08-11 03:00:27 +02:00
|
|
|
var params = parseQs(windowLocation);
|
|
|
|
matrixChat = ReactDOM.render(
|
2016-08-10 01:15:04 +02:00
|
|
|
<MatrixChat
|
|
|
|
onNewScreen={onNewScreen}
|
2017-06-15 03:17:24 +02:00
|
|
|
config={config}
|
2016-08-11 03:00:27 +02:00
|
|
|
realQueryParams={params}
|
|
|
|
startingFragmentQueryParams={fragParts.params}
|
2016-08-10 01:15:04 +02:00
|
|
|
enableGuest={true}
|
2017-07-13 01:56:13 +02:00
|
|
|
onTokenLoginCompleted={() => tokenLoginCompleteDefer.resolve()}
|
2017-03-29 17:07:11 +02:00
|
|
|
initialScreenAfterLogin={getScreenFromLocation(windowLocation)}
|
2017-05-19 14:33:50 +02:00
|
|
|
makeRegistrationUrl={() => {throw new Error('Not implemented');}}
|
2016-08-10 01:15:04 +02:00
|
|
|
/>, parentDiv
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-05-19 14:33:50 +02:00
|
|
|
// set an expectation that we will get a call to /sync, then flush
|
|
|
|
// http requests until we do.
|
|
|
|
//
|
|
|
|
// returns a promise resolving to the received request
|
|
|
|
async function expectAndAwaitSync(response) {
|
|
|
|
response = response || {};
|
|
|
|
let syncRequest = null;
|
|
|
|
httpBackend.when('GET', '/sync')
|
|
|
|
.check((r) => {syncRequest = r;})
|
|
|
|
.respond(200, response);
|
|
|
|
|
|
|
|
for (let attempts = 10; attempts > 0; attempts--) {
|
2017-06-13 15:45:05 +02:00
|
|
|
console.log(Date.now() + " waiting for /sync");
|
2017-05-19 14:33:50 +02:00
|
|
|
if (syncRequest) {
|
|
|
|
return syncRequest;
|
|
|
|
}
|
|
|
|
await httpBackend.flush();
|
|
|
|
}
|
|
|
|
throw new Error("Gave up waiting for /sync");
|
|
|
|
}
|
|
|
|
|
2016-08-10 01:15:04 +02:00
|
|
|
describe("Clean load with no stored credentials:", function() {
|
|
|
|
it('gives a login panel by default', function (done) {
|
2016-08-11 03:00:27 +02:00
|
|
|
loadApp();
|
2016-08-10 01:15:04 +02:00
|
|
|
|
2017-07-13 01:33:28 +02:00
|
|
|
Promise.delay(1).then(() => {
|
2016-08-10 01:15:04 +02:00
|
|
|
// at this point, we're trying to do a guest registration;
|
|
|
|
// we expect a spinner
|
2016-08-10 15:37:30 +02:00
|
|
|
assertAtLoadingSpinner(matrixChat);
|
2016-08-10 01:15:04 +02:00
|
|
|
|
|
|
|
httpBackend.when('POST', '/register').check(function(req) {
|
|
|
|
expect(req.queryParams.kind).toEqual('guest');
|
|
|
|
}).respond(403, "Guest access is disabled");
|
|
|
|
|
|
|
|
return httpBackend.flush();
|
2017-06-06 16:54:45 +02:00
|
|
|
}).then(() => {
|
|
|
|
// Wait for another trip around the event loop for the UI to update
|
2017-07-13 01:18:49 +02:00
|
|
|
return awaitLoginComponent(matrixChat);
|
2016-12-09 15:27:41 +01:00
|
|
|
}).then(() => {
|
2017-06-13 15:45:05 +02:00
|
|
|
expect(windowLocation.hash).toEqual("#/login");
|
2017-06-06 16:54:45 +02:00
|
|
|
}).done(done, done);
|
2016-08-10 01:15:04 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should follow the original link after successful login', function(done) {
|
2016-08-11 03:00:27 +02:00
|
|
|
loadApp({
|
|
|
|
uriFragment: "#/room/!room:id",
|
|
|
|
});
|
2016-08-10 01:15:04 +02:00
|
|
|
|
2017-07-13 01:33:28 +02:00
|
|
|
Promise.delay(1).then(() => {
|
2016-08-10 01:15:04 +02:00
|
|
|
// at this point, we're trying to do a guest registration;
|
|
|
|
// we expect a spinner
|
2016-08-10 15:37:30 +02:00
|
|
|
assertAtLoadingSpinner(matrixChat);
|
2016-08-10 01:15:04 +02:00
|
|
|
|
|
|
|
httpBackend.when('POST', '/register').check(function(req) {
|
|
|
|
expect(req.queryParams.kind).toEqual('guest');
|
|
|
|
}).respond(403, "Guest access is disabled");
|
|
|
|
|
|
|
|
return httpBackend.flush();
|
2016-12-09 15:27:41 +01:00
|
|
|
}).then(() => {
|
|
|
|
// Wait for another trip around the event loop for the UI to update
|
2017-07-13 01:33:28 +02:00
|
|
|
return Promise.delay(10);
|
2016-08-10 01:15:04 +02:00
|
|
|
}).then(() => {
|
2017-06-13 13:52:35 +02:00
|
|
|
return completeLogin(matrixChat);
|
2016-08-10 01:15:04 +02:00
|
|
|
}).then(() => {
|
|
|
|
// once the sync completes, we should have a room view
|
2017-01-24 14:35:41 +01:00
|
|
|
return awaitRoomView(matrixChat);
|
|
|
|
}).then(() => {
|
2016-08-10 01:15:04 +02:00
|
|
|
httpBackend.verifyNoOutstandingExpectation();
|
|
|
|
expect(windowLocation.hash).toEqual("#/room/!room:id");
|
2016-08-11 03:00:27 +02:00
|
|
|
|
|
|
|
// and the localstorage should have been updated
|
|
|
|
expect(localStorage.getItem('mx_user_id')).toEqual('@user:id');
|
|
|
|
expect(localStorage.getItem('mx_access_token')).toEqual('access_token');
|
|
|
|
expect(localStorage.getItem('mx_hs_url')).toEqual(DEFAULT_HS_URL);
|
|
|
|
expect(localStorage.getItem('mx_is_url')).toEqual(DEFAULT_IS_URL);
|
2016-08-10 01:15:04 +02:00
|
|
|
}).done(done, done);
|
|
|
|
});
|
2017-06-14 17:59:00 +02:00
|
|
|
|
|
|
|
it('should not register as a guest when using a #/login link', function() {
|
|
|
|
loadApp({
|
|
|
|
uriFragment: "#/login",
|
|
|
|
});
|
|
|
|
|
2017-07-13 01:18:49 +02:00
|
|
|
return awaitLoginComponent(matrixChat).then(() => {
|
2017-06-14 17:59:00 +02:00
|
|
|
// we expect a single <Login> component
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.login.Login'));
|
|
|
|
|
|
|
|
// the only outstanding request should be a GET /login
|
|
|
|
// (in particular there should be no /register request for
|
|
|
|
// guest registration).
|
|
|
|
for (const req of httpBackend.requests) {
|
|
|
|
if (req.method === 'GET' && req.path.endsWith('/_matrix/client/r0/login')) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error(`Unexpected HTTP request to ${req}`);
|
|
|
|
}
|
|
|
|
return completeLogin(matrixChat);
|
|
|
|
}).then(() => {
|
|
|
|
|
|
|
|
// once the sync completes, we should have a room view
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.HomePage'));
|
|
|
|
expect(windowLocation.hash).toEqual("#/home");
|
|
|
|
});
|
|
|
|
});
|
2016-08-10 01:15:04 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
describe("MatrixClient rehydrated from stored credentials:", function() {
|
|
|
|
beforeEach(function() {
|
2016-08-10 15:37:30 +02:00
|
|
|
localStorage.setItem("mx_hs_url", "http://localhost" );
|
|
|
|
localStorage.setItem("mx_is_url", "http://localhost" );
|
|
|
|
localStorage.setItem("mx_access_token", "access_token");
|
|
|
|
localStorage.setItem("mx_user_id", "@me:localhost");
|
2017-06-13 13:52:35 +02:00
|
|
|
localStorage.setItem("mx_last_room_id", "!last_room:id");
|
|
|
|
});
|
|
|
|
|
|
|
|
it('shows the last known room by default', function() {
|
|
|
|
httpBackend.when('GET', '/pushrules').respond(200, {});
|
|
|
|
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
|
|
|
|
|
|
|
|
loadApp();
|
|
|
|
|
|
|
|
return awaitSyncingSpinner(matrixChat).then(() => {
|
|
|
|
// we got a sync spinner - let the sync complete
|
|
|
|
return expectAndAwaitSync();
|
|
|
|
}).then(() => {
|
|
|
|
// once the sync completes, we should have a room view
|
|
|
|
return awaitRoomView(matrixChat);
|
|
|
|
}).then(() => {
|
|
|
|
httpBackend.verifyNoOutstandingExpectation();
|
|
|
|
expect(windowLocation.hash).toEqual("#/room/!last_room:id");
|
|
|
|
});
|
2016-08-10 01:15:04 +02:00
|
|
|
});
|
|
|
|
|
2017-05-30 13:55:50 +02:00
|
|
|
it('shows a home page by default if we have no joined rooms', function(done) {
|
2017-06-13 13:52:35 +02:00
|
|
|
localStorage.removeItem("mx_last_room_id");
|
|
|
|
|
2016-08-10 01:15:04 +02:00
|
|
|
httpBackend.when('GET', '/pushrules').respond(200, {});
|
|
|
|
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
|
|
|
|
|
2016-08-11 03:00:27 +02:00
|
|
|
loadApp();
|
2016-08-10 01:15:04 +02:00
|
|
|
|
2017-01-24 12:26:09 +01:00
|
|
|
return awaitSyncingSpinner(matrixChat).then(() => {
|
|
|
|
// we got a sync spinner - let the sync complete
|
2017-05-19 14:33:50 +02:00
|
|
|
return expectAndAwaitSync();
|
2016-08-10 01:15:04 +02:00
|
|
|
}).then(() => {
|
2017-05-30 13:55:50 +02:00
|
|
|
// once the sync completes, we should have a home page
|
2016-08-10 01:15:04 +02:00
|
|
|
httpBackend.verifyNoOutstandingExpectation();
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
2017-05-30 13:55:50 +02:00
|
|
|
matrixChat, sdk.getComponent('structures.HomePage'));
|
|
|
|
expect(windowLocation.hash).toEqual("#/home");
|
2016-08-10 01:15:04 +02:00
|
|
|
}).done(done, done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('shows a room view if we followed a room link', function(done) {
|
|
|
|
httpBackend.when('GET', '/pushrules').respond(200, {});
|
|
|
|
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
|
|
|
|
|
2016-08-11 03:00:27 +02:00
|
|
|
loadApp({
|
|
|
|
uriFragment: "#/room/!room:id",
|
|
|
|
});
|
2016-08-10 01:15:04 +02:00
|
|
|
|
2017-01-24 12:26:09 +01:00
|
|
|
return awaitSyncingSpinner(matrixChat).then(() => {
|
|
|
|
// we got a sync spinner - let the sync complete
|
2017-05-19 14:33:50 +02:00
|
|
|
return expectAndAwaitSync();
|
2016-08-10 01:15:04 +02:00
|
|
|
}).then(() => {
|
|
|
|
// once the sync completes, we should have a room view
|
2017-01-24 14:35:41 +01:00
|
|
|
return awaitRoomView(matrixChat);
|
|
|
|
}).then(() => {
|
2016-08-10 01:15:04 +02:00
|
|
|
httpBackend.verifyNoOutstandingExpectation();
|
|
|
|
expect(windowLocation.hash).toEqual("#/room/!room:id");
|
|
|
|
}).done(done, done);
|
2017-06-13 13:52:35 +02:00
|
|
|
});
|
2016-08-10 01:15:04 +02:00
|
|
|
|
2017-06-15 15:54:32 +02:00
|
|
|
it("logs in correctly with a Riot Team Server", function() {
|
2017-06-15 03:17:24 +02:00
|
|
|
sdk.setFetch(httpBackend.fetchFn); // XXX: ought to restore this!
|
|
|
|
|
|
|
|
httpBackend.when('GET', '/pushrules').respond(200, {});
|
|
|
|
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
|
|
|
|
|
|
|
|
loadApp({
|
|
|
|
config: {
|
|
|
|
teamServerConfig: {
|
|
|
|
teamServerURL: 'http://my_team_server',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2017-07-13 01:33:28 +02:00
|
|
|
return Promise.delay(1).then(() => {
|
2017-06-15 03:17:24 +02:00
|
|
|
// we expect a loading spinner while we log into the RTS
|
|
|
|
assertAtLoadingSpinner(matrixChat);
|
|
|
|
|
|
|
|
httpBackend.when('GET', 'my_team_server/login').respond(200, {
|
|
|
|
team_token: 'nom',
|
|
|
|
});
|
|
|
|
return httpBackend.flush();
|
|
|
|
}).then(() => {
|
|
|
|
return awaitSyncingSpinner(matrixChat)
|
|
|
|
}).then(() => {
|
|
|
|
// we got a sync spinner - let the sync complete
|
|
|
|
return expectAndAwaitSync();
|
|
|
|
}).then(() => {
|
|
|
|
// once the sync completes, we should have a home page
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.HomePage'));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-06-13 13:52:35 +02:00
|
|
|
describe('/#/login link:', function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
loadApp({
|
|
|
|
uriFragment: "#/login",
|
|
|
|
});
|
2017-06-16 16:05:14 +02:00
|
|
|
|
2017-06-13 15:45:05 +02:00
|
|
|
// give the UI a chance to display
|
2017-07-13 01:18:49 +02:00
|
|
|
return awaitLoginComponent(matrixChat);
|
2017-06-13 13:52:35 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it('shows a login view', function() {
|
|
|
|
// we expect a single <Login> component
|
2017-06-14 17:59:00 +02:00
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
2017-06-13 13:52:35 +02:00
|
|
|
matrixChat, sdk.getComponent('structures.login.Login')
|
|
|
|
);
|
2017-06-14 17:59:00 +02:00
|
|
|
|
|
|
|
// the only outstanding request should be a GET /login
|
|
|
|
// (in particular there should be no /register request for
|
|
|
|
// guest registration, nor /sync, etc).
|
|
|
|
for (const req of httpBackend.requests) {
|
|
|
|
if (req.method === 'GET' && req.path.endsWith('/_matrix/client/r0/login')) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error(`Unexpected HTTP request to ${req}`);
|
|
|
|
}
|
2017-06-13 13:52:35 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it('shows the homepage after login', function() {
|
|
|
|
return completeLogin(matrixChat).then(() => {
|
2017-06-14 17:59:00 +02:00
|
|
|
// we should see a home page, even though we previously had
|
|
|
|
// a stored mx_last_room_id
|
2017-06-13 13:52:35 +02:00
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.HomePage'));
|
|
|
|
expect(windowLocation.hash).toEqual("#/home");
|
|
|
|
});
|
|
|
|
});
|
2016-08-10 01:15:04 +02:00
|
|
|
});
|
|
|
|
});
|
2016-08-10 15:37:30 +02:00
|
|
|
|
|
|
|
describe('Guest auto-registration:', function() {
|
2017-05-30 13:55:50 +02:00
|
|
|
it('shows a home page by default', function (done) {
|
2016-08-11 03:00:27 +02:00
|
|
|
loadApp();
|
2016-08-10 15:37:30 +02:00
|
|
|
|
2017-07-13 01:33:28 +02:00
|
|
|
Promise.delay(1).then(() => {
|
2016-08-10 15:37:30 +02:00
|
|
|
// at this point, we're trying to do a guest registration;
|
|
|
|
// we expect a spinner
|
|
|
|
assertAtLoadingSpinner(matrixChat);
|
|
|
|
|
|
|
|
httpBackend.when('POST', '/register').check(function(req) {
|
|
|
|
expect(req.queryParams.kind).toEqual('guest');
|
|
|
|
}).respond(200, {
|
|
|
|
user_id: "@guest:localhost",
|
|
|
|
access_token: "secret_token",
|
|
|
|
});
|
|
|
|
|
|
|
|
return httpBackend.flush();
|
2016-12-09 15:27:41 +01:00
|
|
|
}).then(() => {
|
2017-01-24 12:26:09 +01:00
|
|
|
return awaitSyncingSpinner(matrixChat);
|
2016-08-10 15:37:30 +02:00
|
|
|
}).then(() => {
|
2017-01-24 12:26:09 +01:00
|
|
|
// we got a sync spinner - let the sync complete
|
2017-05-19 14:33:50 +02:00
|
|
|
return expectAndAwaitSync();
|
2016-08-10 15:37:30 +02:00
|
|
|
}).then(() => {
|
2017-05-30 13:55:50 +02:00
|
|
|
// once the sync completes, we should have a home page
|
2016-08-10 15:37:30 +02:00
|
|
|
httpBackend.verifyNoOutstandingExpectation();
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
2017-05-30 13:55:50 +02:00
|
|
|
matrixChat, sdk.getComponent('structures.HomePage'));
|
|
|
|
expect(windowLocation.hash).toEqual("#/home");
|
2016-08-10 15:37:30 +02:00
|
|
|
}).done(done, done);
|
|
|
|
});
|
|
|
|
|
2016-08-11 03:00:27 +02:00
|
|
|
it('uses the last known homeserver to register with', function (done) {
|
|
|
|
localStorage.setItem("mx_hs_url", "https://homeserver" );
|
|
|
|
localStorage.setItem("mx_is_url", "https://idserver" );
|
|
|
|
|
|
|
|
loadApp();
|
|
|
|
|
2017-07-13 01:33:28 +02:00
|
|
|
Promise.delay(1).then(() => {
|
2016-08-11 03:00:27 +02:00
|
|
|
// at this point, we're trying to do a guest registration;
|
|
|
|
// we expect a spinner
|
|
|
|
assertAtLoadingSpinner(matrixChat);
|
|
|
|
|
|
|
|
httpBackend.when('POST', '/register').check(function(req) {
|
|
|
|
expect(req.path).toMatch(new RegExp("^https://homeserver/"));
|
|
|
|
expect(req.queryParams.kind).toEqual('guest');
|
|
|
|
}).respond(200, {
|
|
|
|
user_id: "@guest:localhost",
|
|
|
|
access_token: "secret_token",
|
|
|
|
});
|
|
|
|
|
|
|
|
return httpBackend.flush();
|
2016-12-09 15:27:41 +01:00
|
|
|
}).then(() => {
|
2017-01-24 12:26:09 +01:00
|
|
|
return awaitSyncingSpinner(matrixChat);
|
2016-08-11 03:00:27 +02:00
|
|
|
}).then(() => {
|
2017-05-19 14:33:50 +02:00
|
|
|
return expectAndAwaitSync();
|
|
|
|
}).then((req) => {
|
|
|
|
expect(req.path).toMatch(new RegExp("^https://homeserver/"));
|
|
|
|
|
2017-05-30 13:55:50 +02:00
|
|
|
// once the sync completes, we should have a home page
|
2016-08-11 03:00:27 +02:00
|
|
|
httpBackend.verifyNoOutstandingExpectation();
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
2017-05-30 13:55:50 +02:00
|
|
|
matrixChat, sdk.getComponent('structures.HomePage'));
|
|
|
|
expect(windowLocation.hash).toEqual("#/home");
|
2016-08-11 03:00:27 +02:00
|
|
|
expect(MatrixClientPeg.get().baseUrl).toEqual("https://homeserver");
|
|
|
|
expect(MatrixClientPeg.get().idBaseUrl).toEqual("https://idserver");
|
|
|
|
}).done(done, done);
|
|
|
|
});
|
|
|
|
|
2016-08-10 15:37:30 +02:00
|
|
|
it('shows a room view if we followed a room link', function(done) {
|
2016-08-11 03:00:27 +02:00
|
|
|
loadApp({
|
|
|
|
uriFragment: "#/room/!room:id"
|
|
|
|
});
|
2017-07-13 01:33:28 +02:00
|
|
|
Promise.delay(1).then(() => {
|
2016-08-10 15:37:30 +02:00
|
|
|
// at this point, we're trying to do a guest registration;
|
|
|
|
// we expect a spinner
|
|
|
|
assertAtLoadingSpinner(matrixChat);
|
|
|
|
|
|
|
|
httpBackend.when('POST', '/register').check(function(req) {
|
|
|
|
expect(req.queryParams.kind).toEqual('guest');
|
|
|
|
}).respond(200, {
|
|
|
|
user_id: "@guest:localhost",
|
|
|
|
access_token: "secret_token",
|
|
|
|
});
|
|
|
|
|
|
|
|
return httpBackend.flush();
|
2016-12-09 15:27:41 +01:00
|
|
|
}).then(() => {
|
2017-01-24 12:26:09 +01:00
|
|
|
return awaitSyncingSpinner(matrixChat);
|
2016-08-10 15:37:30 +02:00
|
|
|
}).then(() => {
|
2017-05-19 14:33:50 +02:00
|
|
|
return expectAndAwaitSync();
|
2016-08-10 15:37:30 +02:00
|
|
|
}).then(() => {
|
|
|
|
// once the sync completes, we should have a room view
|
2017-01-24 14:35:41 +01:00
|
|
|
return awaitRoomView(matrixChat);
|
|
|
|
}).then(() => {
|
2016-08-10 15:37:30 +02:00
|
|
|
httpBackend.verifyNoOutstandingExpectation();
|
|
|
|
expect(windowLocation.hash).toEqual("#/room/!room:id");
|
|
|
|
}).done(done, done);
|
|
|
|
});
|
2017-06-14 21:27:02 +02:00
|
|
|
|
|
|
|
describe('Login as user', function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
// first we have to load the homepage
|
|
|
|
loadApp();
|
|
|
|
|
|
|
|
httpBackend.when('POST', '/register').check(function(req) {
|
|
|
|
expect(req.queryParams.kind).toEqual('guest');
|
|
|
|
}).respond(200, {
|
|
|
|
user_id: "@guest:localhost",
|
|
|
|
access_token: "secret_token",
|
|
|
|
});
|
|
|
|
|
|
|
|
return httpBackend.flush().then(() => {
|
|
|
|
return awaitSyncingSpinner(matrixChat);
|
|
|
|
}).then(() => {
|
|
|
|
// we got a sync spinner - let the sync complete
|
|
|
|
return expectAndAwaitSync();
|
|
|
|
}).then(() => {
|
|
|
|
// once the sync completes, we should have a home page
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.HomePage'));
|
|
|
|
|
|
|
|
// we simulate a click on the 'login' button by firing off
|
|
|
|
// the relevant dispatch.
|
|
|
|
//
|
|
|
|
// XXX: is it an anti-pattern to access the react-sdk's
|
|
|
|
// dispatcher in this way? Is it better to find the login
|
|
|
|
// button and simulate a click? (we might have to arrange
|
|
|
|
// for it to be shown - it's not always, due to the
|
|
|
|
// collapsing left panel
|
|
|
|
|
|
|
|
dis.dispatch({ action: 'start_login' });
|
|
|
|
|
2017-07-13 01:18:49 +02:00
|
|
|
return awaitLoginComponent(matrixChat);
|
2017-06-14 21:27:02 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should give us a login page', function() {
|
|
|
|
expect(windowLocation.hash).toEqual("#/login");
|
|
|
|
|
|
|
|
// we expect a single <Login> component
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.login.Login')
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2017-10-26 19:08:42 +02:00
|
|
|
/*
|
|
|
|
// ILAG renders this obsolete. I think.
|
2017-06-14 21:27:02 +02:00
|
|
|
it('should allow us to return to the app', function() {
|
|
|
|
const login = ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.login.Login')
|
|
|
|
);
|
|
|
|
|
|
|
|
const linkText = 'Return to app';
|
|
|
|
|
|
|
|
const returnToApp = ReactTestUtils.scryRenderedDOMComponentsWithTag(
|
|
|
|
login, 'a').find((e) => e.innerText === linkText);
|
|
|
|
|
|
|
|
if (!returnToApp) {
|
|
|
|
throw new Error(`Couldn't find '${linkText}' link`);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReactTestUtils.Simulate.click(returnToApp);
|
|
|
|
|
2017-07-13 01:33:28 +02:00
|
|
|
return Promise.delay(1).then(() => {
|
2017-06-14 21:27:02 +02:00
|
|
|
// we should be straight back into the home page
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.HomePage'));
|
|
|
|
});
|
|
|
|
});
|
2017-10-26 19:08:42 +02:00
|
|
|
*/
|
2017-06-14 21:27:02 +02:00
|
|
|
});
|
2016-08-10 15:37:30 +02:00
|
|
|
});
|
2016-08-11 03:00:27 +02:00
|
|
|
|
|
|
|
describe('Token login:', function() {
|
|
|
|
it('logs in successfully', function (done) {
|
|
|
|
loadApp({
|
|
|
|
queryString: "?loginToken=secretToken&homeserver=https%3A%2F%2Fhomeserver&identityServer=https%3A%2F%2Fidserver",
|
|
|
|
});
|
|
|
|
|
2017-07-13 01:33:28 +02:00
|
|
|
Promise.delay(1).then(() => {
|
2016-08-11 03:00:27 +02:00
|
|
|
// we expect a spinner while we're logging in
|
|
|
|
assertAtLoadingSpinner(matrixChat);
|
|
|
|
|
|
|
|
httpBackend.when('POST', '/login').check(function(req) {
|
|
|
|
expect(req.path).toMatch(new RegExp("^https://homeserver/"));
|
|
|
|
expect(req.data.type).toEqual("m.login.token");
|
|
|
|
expect(req.data.token).toEqual("secretToken");
|
|
|
|
}).respond(200, {
|
|
|
|
user_id: "@user:localhost",
|
2017-02-03 00:41:49 +01:00
|
|
|
device_id: 'DEVICE_ID',
|
2016-08-11 03:00:27 +02:00
|
|
|
access_token: "access_token",
|
|
|
|
});
|
|
|
|
|
|
|
|
return httpBackend.flush();
|
|
|
|
}).then(() => {
|
2017-06-16 16:05:14 +02:00
|
|
|
// at this point, MatrixChat should fire onTokenLoginCompleted, which
|
2016-08-11 03:00:27 +02:00
|
|
|
// makes index.js reload the app. We're not going to attempt to
|
|
|
|
// simulate the reload - just check that things are left in the
|
|
|
|
// right state for the reloaded app.
|
|
|
|
|
2017-06-19 10:04:24 +02:00
|
|
|
return tokenLoginCompletePromise;
|
2016-08-11 03:00:27 +02:00
|
|
|
}).then(() => {
|
|
|
|
// check that the localstorage has been set up in such a way that
|
|
|
|
// the reloaded app can pick up where we leave off.
|
|
|
|
expect(localStorage.getItem('mx_user_id')).toEqual('@user:localhost');
|
|
|
|
expect(localStorage.getItem('mx_access_token')).toEqual('access_token');
|
|
|
|
expect(localStorage.getItem('mx_hs_url')).toEqual('https://homeserver');
|
|
|
|
expect(localStorage.getItem('mx_is_url')).toEqual('https://idserver');
|
|
|
|
}).done(done, done);
|
|
|
|
});
|
|
|
|
});
|
2017-06-13 13:52:35 +02:00
|
|
|
|
|
|
|
// check that we have a Login component, send a 'user:pass' login,
|
|
|
|
// and await the HTTP requests.
|
|
|
|
function completeLogin(matrixChat) {
|
|
|
|
// we expect a single <Login> component
|
|
|
|
const login = ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.login.Login'));
|
|
|
|
|
|
|
|
httpBackend.when('POST', '/login').check(function(req) {
|
|
|
|
expect(req.data.type).toEqual('m.login.password');
|
|
|
|
expect(req.data.identifier.type).toEqual('m.id.user');
|
|
|
|
expect(req.data.identifier.user).toEqual('user');
|
|
|
|
expect(req.data.password).toEqual('pass');
|
|
|
|
}).respond(200, {
|
|
|
|
user_id: '@user:id',
|
|
|
|
device_id: 'DEVICE_ID',
|
|
|
|
access_token: 'access_token',
|
|
|
|
});
|
|
|
|
login.onPasswordLogin("user", undefined, undefined, "pass");
|
|
|
|
|
|
|
|
return httpBackend.flush().then(() => {
|
|
|
|
// Wait for another trip around the event loop for the UI to update
|
2017-07-13 01:33:28 +02:00
|
|
|
return Promise.delay(1);
|
2017-06-13 13:52:35 +02:00
|
|
|
}).then(() => {
|
|
|
|
// we expect a spinner
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('elements.Spinner'));
|
|
|
|
|
|
|
|
httpBackend.when('GET', '/pushrules').respond(200, {});
|
|
|
|
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
|
|
|
|
return expectAndAwaitSync().catch((e) => {
|
|
|
|
throw new Error("Never got /sync after login: did the client start?");
|
|
|
|
});
|
|
|
|
}).then(() => {
|
|
|
|
httpBackend.verifyNoOutstandingExpectation();
|
|
|
|
});
|
|
|
|
}
|
2016-08-10 01:15:04 +02:00
|
|
|
});
|
2016-08-10 15:37:30 +02:00
|
|
|
|
|
|
|
// assert that we are on the loading page
|
|
|
|
function assertAtLoadingSpinner(matrixChat) {
|
|
|
|
var domComponent = ReactDOM.findDOMNode(matrixChat);
|
|
|
|
expect(domComponent.className).toEqual("mx_MatrixChat_splash");
|
|
|
|
|
|
|
|
// just the spinner
|
|
|
|
expect(domComponent.children.length).toEqual(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// we've got login creds, and are waiting for the sync to finish.
|
|
|
|
// the page includes a logout link.
|
2017-01-24 12:26:09 +01:00
|
|
|
function awaitSyncingSpinner(matrixChat, retryLimit, retryCount) {
|
|
|
|
if (retryLimit === undefined) {
|
2017-06-13 13:52:35 +02:00
|
|
|
retryLimit = 10;
|
2017-01-24 12:26:09 +01:00
|
|
|
}
|
|
|
|
if (retryCount === undefined) {
|
|
|
|
retryCount = 0;
|
|
|
|
}
|
|
|
|
|
2017-06-13 15:45:05 +02:00
|
|
|
if (matrixChat.state.view === VIEWS.LOADING ||
|
|
|
|
matrixChat.state.view === VIEWS.LOGGING_IN) {
|
2017-01-24 12:26:09 +01:00
|
|
|
console.log(Date.now() + " Awaiting sync spinner: still loading.");
|
|
|
|
if (retryCount >= retryLimit) {
|
|
|
|
throw new Error("MatrixChat still not loaded after " +
|
|
|
|
retryCount + " tries");
|
|
|
|
}
|
2017-06-13 13:52:35 +02:00
|
|
|
// loading can take quite a long time, because we delete the
|
|
|
|
// indexedDB store.
|
2017-07-13 01:33:28 +02:00
|
|
|
return Promise.delay(5).then(() => {
|
2017-01-24 12:26:09 +01:00
|
|
|
return awaitSyncingSpinner(matrixChat, retryLimit, retryCount + 1);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(Date.now() + " Awaiting sync spinner: load complete.");
|
|
|
|
|
|
|
|
// state looks good, check the rendered output
|
|
|
|
assertAtSyncingSpinner(matrixChat);
|
2017-07-13 01:32:07 +02:00
|
|
|
return Promise.resolve();
|
2017-01-24 12:26:09 +01:00
|
|
|
}
|
|
|
|
|
2016-08-10 15:37:30 +02:00
|
|
|
function assertAtSyncingSpinner(matrixChat) {
|
|
|
|
var domComponent = ReactDOM.findDOMNode(matrixChat);
|
|
|
|
expect(domComponent.className).toEqual("mx_MatrixChat_splash");
|
|
|
|
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('elements.Spinner'));
|
|
|
|
var logoutLink = ReactTestUtils.findRenderedDOMComponentWithTag(
|
|
|
|
matrixChat, 'a');
|
|
|
|
expect(logoutLink.text).toEqual("Logout");
|
|
|
|
}
|
2017-01-24 14:35:41 +01:00
|
|
|
|
|
|
|
function awaitRoomView(matrixChat, retryLimit, retryCount) {
|
|
|
|
if (retryLimit === undefined) {
|
|
|
|
retryLimit = 5;
|
|
|
|
}
|
|
|
|
if (retryCount === undefined) {
|
|
|
|
retryCount = 0;
|
|
|
|
}
|
|
|
|
|
2017-06-13 15:45:05 +02:00
|
|
|
if (matrixChat.state.view !== VIEWS.LOGGED_IN || !matrixChat.state.ready) {
|
2017-01-24 14:35:41 +01:00
|
|
|
console.log(Date.now() + " Awaiting room view: not ready yet.");
|
|
|
|
if (retryCount >= retryLimit) {
|
|
|
|
throw new Error("MatrixChat still not ready after " +
|
|
|
|
retryCount + " tries");
|
|
|
|
}
|
2017-07-13 01:33:28 +02:00
|
|
|
return Promise.delay(0).then(() => {
|
2017-01-24 14:35:41 +01:00
|
|
|
return awaitRoomView(matrixChat, retryLimit, retryCount + 1);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(Date.now() + " Awaiting room view: now ready.");
|
|
|
|
|
|
|
|
// state looks good, check the rendered output
|
|
|
|
ReactTestUtils.findRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.RoomView'));
|
2017-07-13 01:32:07 +02:00
|
|
|
return Promise.resolve();
|
2017-01-24 14:35:41 +01:00
|
|
|
}
|
2017-07-13 01:18:49 +02:00
|
|
|
|
|
|
|
function awaitLoginComponent(matrixChat, attempts) {
|
|
|
|
return MatrixReactTestUtils.waitForRenderedComponentWithType(
|
|
|
|
matrixChat, sdk.getComponent('structures.login.Login'), attempts,
|
|
|
|
);
|
|
|
|
}
|