Some tests of the application load process
This commit is contained in:
parent
b06c9f037e
commit
afc889ff4d
|
@ -45,7 +45,7 @@ var UpdateChecker = require("./updater");
|
||||||
var q = require('q');
|
var q = require('q');
|
||||||
var request = require('browser-request');
|
var request = require('browser-request');
|
||||||
|
|
||||||
var qs = require("querystring");
|
import {parseQs, parseQsFromFragment} from './url_utils';
|
||||||
|
|
||||||
var lastLocationHashSet = null;
|
var lastLocationHashSet = null;
|
||||||
|
|
||||||
|
@ -81,41 +81,12 @@ var validBrowser = checkBrowserFeatures([
|
||||||
"objectfit"
|
"objectfit"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// We want to support some name / value pairs in the fragment
|
|
||||||
// so we're re-using query string like format
|
|
||||||
//
|
|
||||||
// returns {location, params}
|
|
||||||
function parseQsFromFragment(location) {
|
|
||||||
// if we have a fragment, it will start with '#', which we need to drop.
|
|
||||||
// (if we don't, this will return '').
|
|
||||||
var fragment = location.hash.substring(1);
|
|
||||||
|
|
||||||
// our fragment may contain a query-param-like section. we need to fish
|
|
||||||
// this out *before* URI-decoding because the params may contain ? and &
|
|
||||||
// characters which are only URI-encoded once.
|
|
||||||
var hashparts = fragment.split('?');
|
|
||||||
|
|
||||||
var result = {
|
|
||||||
location: decodeURIComponent(hashparts[0]),
|
|
||||||
params: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (hashparts.length > 1) {
|
|
||||||
result.params = qs.parse(hashparts[1]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseQs(location) {
|
|
||||||
return qs.parse(location.search.substring(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here, we do some crude URL analysis to allow
|
// Here, we do some crude URL analysis to allow
|
||||||
// deep-linking.
|
// deep-linking.
|
||||||
function routeUrl(location) {
|
function routeUrl(location) {
|
||||||
if (!window.matrixChat) return;
|
if (!window.matrixChat) return;
|
||||||
|
|
||||||
console.log("Routing URL "+window.location);
|
console.log("Routing URL "+location);
|
||||||
var params = parseQs(location);
|
var params = parseQs(location);
|
||||||
var loginToken = params.loginToken;
|
var loginToken = params.loginToken;
|
||||||
if (loginToken) {
|
if (loginToken) {
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import qs from 'querystring';
|
||||||
|
|
||||||
|
// We want to support some name / value pairs in the fragment
|
||||||
|
// so we're re-using query string like format
|
||||||
|
//
|
||||||
|
// returns {location, params}
|
||||||
|
export function parseQsFromFragment(location) {
|
||||||
|
// if we have a fragment, it will start with '#', which we need to drop.
|
||||||
|
// (if we don't, this will return '').
|
||||||
|
var fragment = location.hash.substring(1);
|
||||||
|
|
||||||
|
// our fragment may contain a query-param-like section. we need to fish
|
||||||
|
// this out *before* URI-decoding because the params may contain ? and &
|
||||||
|
// characters which are only URI-encoded once.
|
||||||
|
var hashparts = fragment.split('?');
|
||||||
|
|
||||||
|
var result = {
|
||||||
|
location: decodeURIComponent(hashparts[0]),
|
||||||
|
params: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hashparts.length > 1) {
|
||||||
|
result.params = qs.parse(hashparts[1]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseQs(location) {
|
||||||
|
return qs.parse(location.search.substring(1));
|
||||||
|
}
|
|
@ -0,0 +1,249 @@
|
||||||
|
/*
|
||||||
|
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 */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import ReactTestUtils from 'react-addons-test-utils';
|
||||||
|
import expect from 'expect';
|
||||||
|
import q from 'q';
|
||||||
|
|
||||||
|
import jssdk from 'matrix-js-sdk';
|
||||||
|
|
||||||
|
import sdk from 'matrix-react-sdk';
|
||||||
|
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||||
|
|
||||||
|
import test_utils from '../test-utils';
|
||||||
|
import MockHttpBackend from '../mock-request';
|
||||||
|
import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils';
|
||||||
|
|
||||||
|
|
||||||
|
describe('loading:', function () {
|
||||||
|
let parentDiv;
|
||||||
|
let httpBackend;
|
||||||
|
|
||||||
|
// an Object simulating the window.location
|
||||||
|
let windowLocation;
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
if (parentDiv) {
|
||||||
|
ReactDOM.unmountComponentAtNode(parentDiv);
|
||||||
|
parentDiv.remove();
|
||||||
|
parentDiv = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
function loadApp(uriFragment) {
|
||||||
|
windowLocation = {
|
||||||
|
search: "",
|
||||||
|
hash: uriFragment,
|
||||||
|
toString: function() { return this.search + this.hash; },
|
||||||
|
};
|
||||||
|
|
||||||
|
let lastLoadedScreen = null;
|
||||||
|
let appLoaded = false;
|
||||||
|
function onNewScreen(screen) {
|
||||||
|
console.log("newscreen "+screen);
|
||||||
|
if (!appLoaded) {
|
||||||
|
lastLoadedScreen = screen;
|
||||||
|
} else {
|
||||||
|
var hash = '#/' + screen;
|
||||||
|
windowLocation.hash = hash;
|
||||||
|
console.log("browser URI now "+ windowLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MatrixChat = sdk.getComponent('structures.MatrixChat');
|
||||||
|
const fragParts = parseQsFromFragment(windowLocation);
|
||||||
|
const matrixChat = ReactDOM.render(
|
||||||
|
<MatrixChat
|
||||||
|
onNewScreen={onNewScreen}
|
||||||
|
config={{}}
|
||||||
|
startingQueryParams={fragParts.params}
|
||||||
|
enableGuest={true}
|
||||||
|
/>, parentDiv
|
||||||
|
);
|
||||||
|
|
||||||
|
function routeUrl(location, matrixChat) {
|
||||||
|
console.log("Routing URL "+location);
|
||||||
|
var params = parseQs(location);
|
||||||
|
var loginToken = params.loginToken;
|
||||||
|
if (loginToken) {
|
||||||
|
matrixChat.showScreen('token_login', params);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fragparts = parseQsFromFragment(location);
|
||||||
|
matrixChat.showScreen(fragparts.location.substring(1),
|
||||||
|
fragparts.params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pause for a cycle, then simulate the window.onload handler
|
||||||
|
q.delay(0).then(() => {
|
||||||
|
console.log("simulating window.onload");
|
||||||
|
routeUrl(windowLocation, matrixChat);
|
||||||
|
appLoaded = true;
|
||||||
|
if (lastLoadedScreen) {
|
||||||
|
onNewScreen(lastLoadedScreen);
|
||||||
|
lastLoadedScreen = null;
|
||||||
|
}
|
||||||
|
}).done();
|
||||||
|
|
||||||
|
return matrixChat;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Clean load with no stored credentials:", function() {
|
||||||
|
it('gives a login panel by default', function (done) {
|
||||||
|
let matrixChat = loadApp("");
|
||||||
|
|
||||||
|
q.delay(1).then(() => {
|
||||||
|
// at this point, we're trying to do a guest registration;
|
||||||
|
// we expect a spinner
|
||||||
|
ReactTestUtils.findRenderedComponentWithType(
|
||||||
|
matrixChat, sdk.getComponent('elements.Spinner'));
|
||||||
|
|
||||||
|
httpBackend.when('POST', '/register').check(function(req) {
|
||||||
|
expect(req.queryParams.kind).toEqual('guest');
|
||||||
|
}).respond(403, "Guest access is disabled");
|
||||||
|
|
||||||
|
return httpBackend.flush();
|
||||||
|
}).then(() => {
|
||||||
|
// we expect a single <Login> component
|
||||||
|
ReactTestUtils.findRenderedComponentWithType(
|
||||||
|
matrixChat, sdk.getComponent('structures.login.Login'));
|
||||||
|
expect(windowLocation.hash).toEqual("");
|
||||||
|
}).done(done, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should follow the original link after successful login', function(done) {
|
||||||
|
let matrixChat = loadApp("#/room/!room:id");
|
||||||
|
|
||||||
|
q.delay(1).then(() => {
|
||||||
|
// at this point, we're trying to do a guest registration;
|
||||||
|
// we expect a spinner
|
||||||
|
ReactTestUtils.findRenderedComponentWithType(
|
||||||
|
matrixChat, sdk.getComponent('elements.Spinner'));
|
||||||
|
|
||||||
|
httpBackend.when('POST', '/register').check(function(req) {
|
||||||
|
expect(req.queryParams.kind).toEqual('guest');
|
||||||
|
}).respond(403, "Guest access is disabled");
|
||||||
|
|
||||||
|
return httpBackend.flush();
|
||||||
|
}).then(() => {
|
||||||
|
// we expect a single <Login> component
|
||||||
|
let 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.user).toEqual('user');
|
||||||
|
expect(req.data.password).toEqual('pass');
|
||||||
|
}).respond(200, { user_id: 'user_id' });
|
||||||
|
login.onPasswordLogin("user", "pass")
|
||||||
|
return httpBackend.flush();
|
||||||
|
}).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' });
|
||||||
|
httpBackend.when('GET', '/sync').respond(200, {});
|
||||||
|
return httpBackend.flush();
|
||||||
|
}).then(() => {
|
||||||
|
// once the sync completes, we should have a room view
|
||||||
|
httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
ReactTestUtils.findRenderedComponentWithType(
|
||||||
|
matrixChat, sdk.getComponent('structures.RoomView'));
|
||||||
|
expect(windowLocation.hash).toEqual("#/room/!room:id");
|
||||||
|
}).done(done, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("MatrixClient rehydrated from stored credentials:", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
// start with a logged-in client
|
||||||
|
MatrixClientPeg.replaceUsingCreds({
|
||||||
|
homeserverUrl: 'http://localhost',
|
||||||
|
identityServerUrl: 'http://localhost',
|
||||||
|
userId: '@me:localhost',
|
||||||
|
accessToken: 'access_token',
|
||||||
|
guest: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows a directory by default if we have no joined rooms', function(done) {
|
||||||
|
httpBackend.when('GET', '/pushrules').respond(200, {});
|
||||||
|
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
|
||||||
|
httpBackend.when('GET', '/sync').respond(200, {});
|
||||||
|
|
||||||
|
let matrixChat = loadApp("");
|
||||||
|
|
||||||
|
q.delay(1).then(() => {
|
||||||
|
// we expect a spinner
|
||||||
|
ReactTestUtils.findRenderedComponentWithType(
|
||||||
|
matrixChat, sdk.getComponent('elements.Spinner'));
|
||||||
|
return httpBackend.flush();
|
||||||
|
}).then(() => {
|
||||||
|
// once the sync completes, we should have a directory
|
||||||
|
httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
ReactTestUtils.findRenderedComponentWithType(
|
||||||
|
matrixChat, sdk.getComponent('structures.RoomDirectory'));
|
||||||
|
expect(windowLocation.hash).toEqual("#/directory");
|
||||||
|
}).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' });
|
||||||
|
httpBackend.when('GET', '/sync').respond(200, {});
|
||||||
|
|
||||||
|
let matrixChat = loadApp("#/room/!room:id");
|
||||||
|
|
||||||
|
q.delay(1).then(() => {
|
||||||
|
// we expect a spinner
|
||||||
|
ReactTestUtils.findRenderedComponentWithType(
|
||||||
|
matrixChat, sdk.getComponent('elements.Spinner'));
|
||||||
|
return httpBackend.flush();
|
||||||
|
}).then(() => {
|
||||||
|
// once the sync completes, we should have a room view
|
||||||
|
httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
ReactTestUtils.findRenderedComponentWithType(
|
||||||
|
matrixChat, sdk.getComponent('structures.RoomView'));
|
||||||
|
expect(windowLocation.hash).toEqual("#/room/!room:id");
|
||||||
|
}).done(done, done);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue