Run some tests under karma
Including a regression test for https://github.com/vector-im/vector-web/issues/1314
This commit is contained in:
parent
46572ae793
commit
429d110212
9 changed files with 595 additions and 11 deletions
7
test/all-tests.js
Normal file
7
test/all-tests.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
// all-tests.js
|
||||
//
|
||||
// Our master test file: uses the webpack require API to find our test files
|
||||
// and run them
|
||||
|
||||
var context = require.context('./app-tests', true, /\.jsx?$/);
|
||||
context.keys().forEach(context);
|
165
test/app-tests/joining.js
Normal file
165
test/app-tests/joining.js
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/* joining.js: tests for the various paths when joining a room */
|
||||
|
||||
require('skin-sdk');
|
||||
|
||||
var jssdk = require('matrix-js-sdk');
|
||||
|
||||
var sdk = require('matrix-react-sdk');
|
||||
var peg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var MatrixChat = sdk.getComponent('structures.MatrixChat');
|
||||
var RoomDirectory = sdk.getComponent('structures.RoomDirectory');
|
||||
var RoomPreviewBar = sdk.getComponent('rooms.RoomPreviewBar');
|
||||
var RoomView = sdk.getComponent('structures.RoomView');
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var ReactTestUtils = require('react-addons-test-utils');
|
||||
var expect = require('expect');
|
||||
var q = require('q');
|
||||
|
||||
var test_utils = require('../test-utils');
|
||||
var MockHttpBackend = require('../mock-request');
|
||||
|
||||
var HS_URL='http://localhost';
|
||||
var IS_URL='http://localhost';
|
||||
var USER_ID='@me:localhost';
|
||||
var ACCESS_TOKEN='access_token';
|
||||
|
||||
describe('joining a room', function () {
|
||||
describe('over federation', function () {
|
||||
var parentDiv;
|
||||
var httpBackend;
|
||||
var matrixChat;
|
||||
|
||||
beforeEach(function() {
|
||||
test_utils.beforeEach(this);
|
||||
httpBackend = new MockHttpBackend();
|
||||
jssdk.request(httpBackend.requestFn);
|
||||
parentDiv = document.createElement('div');
|
||||
document.body.appendChild(parentDiv);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
if (parentDiv) {
|
||||
ReactDOM.unmountComponentAtNode(parentDiv);
|
||||
document.body.removeChild(parentDiv);
|
||||
parentDiv = null;
|
||||
}
|
||||
httpBackend.verifyNoOutstandingRequests();
|
||||
});
|
||||
|
||||
it('should not get stuck at a spinner', function(done) {
|
||||
var ROOM_ALIAS = '#alias:localhost';
|
||||
var ROOM_ID = '!id:localhost';
|
||||
|
||||
httpBackend.when('PUT', '/presence/'+encodeURIComponent(USER_ID)+'/status')
|
||||
.respond(200, {});
|
||||
if (test_utils.browserSupportsWebRTC()) {
|
||||
httpBackend.when('GET', '/voip/turnServer').respond(200, {});
|
||||
}
|
||||
httpBackend.when('GET', '/pushrules').respond(200, {});
|
||||
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
|
||||
httpBackend.when('GET', '/sync').respond(200, {});
|
||||
httpBackend.when('GET', '/publicRooms').respond(200, {chunk: []});
|
||||
|
||||
// start with a logged-in client
|
||||
peg.replaceUsingAccessToken(HS_URL, IS_URL, USER_ID, ACCESS_TOKEN);
|
||||
|
||||
var mc = <MatrixChat config={{}}/>;
|
||||
matrixChat = ReactDOM.render(mc, parentDiv);
|
||||
|
||||
// switch to the Directory
|
||||
dis.dispatch({
|
||||
action: 'view_room_directory',
|
||||
});
|
||||
|
||||
var roomView;
|
||||
httpBackend.flush().then(() => {
|
||||
var roomDir = ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, RoomDirectory);
|
||||
|
||||
// enter an alias in the input, and simulate enter
|
||||
var input = ReactTestUtils.findRenderedDOMComponentWithTag(
|
||||
roomDir, 'input');
|
||||
input.value = ROOM_ALIAS;
|
||||
ReactTestUtils.Simulate.keyUp(input, {key: 'Enter'});
|
||||
|
||||
// that should create a roomview which will start a peek; wait
|
||||
// for the peek.
|
||||
httpBackend.when('GET', '/rooms/'+encodeURIComponent(ROOM_ALIAS)+"/initialSync")
|
||||
.respond(401, {errcode: 'M_GUEST_ACCESS_FORBIDDEN'});
|
||||
return httpBackend.flush();
|
||||
}).then(() => {
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
|
||||
// we should now have a roomview, with a preview bar
|
||||
roomView = ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, RoomView);
|
||||
|
||||
var previewBar = ReactTestUtils.findRenderedComponentWithType(
|
||||
roomView, RoomPreviewBar);
|
||||
|
||||
var joinLink = ReactTestUtils.findRenderedDOMComponentWithTag(
|
||||
previewBar, 'a');
|
||||
|
||||
ReactTestUtils.Simulate.click(joinLink);
|
||||
|
||||
// that will fire off a request to check our displayname, followed by a
|
||||
// join request
|
||||
httpBackend.when('GET', '/profile/'+encodeURIComponent(USER_ID))
|
||||
.respond(200, {displayname: 'boris'});
|
||||
httpBackend.when('POST', '/join/'+encodeURIComponent(ROOM_ALIAS))
|
||||
.respond(200, {room_id: ROOM_ID});
|
||||
return httpBackend.flush();
|
||||
}).then(() => {
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
|
||||
// the roomview should now be loading
|
||||
expect(roomView.state.room).toBe(null);
|
||||
expect(roomView.state.joining).toBe(true);
|
||||
|
||||
// there should be a spinner
|
||||
ReactTestUtils.findRenderedDOMComponentWithClass(
|
||||
roomView, "mx_Spinner");
|
||||
|
||||
// now send the room down the /sync pipe
|
||||
httpBackend.when('GET', '/sync').
|
||||
respond(200, {
|
||||
rooms: {
|
||||
join: {
|
||||
[ROOM_ID]: {
|
||||
state: {},
|
||||
timeline: {
|
||||
events: [],
|
||||
limited: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return httpBackend.flush();
|
||||
}).then(() => {
|
||||
// now the room should have loaded
|
||||
expect(roomView.state.room).toExist();
|
||||
expect(roomView.state.joining).toBe(false);
|
||||
}).done(done, done);
|
||||
});
|
||||
});
|
||||
});
|
228
test/mock-request.js
Normal file
228
test/mock-request.js
Normal file
|
@ -0,0 +1,228 @@
|
|||
"use strict";
|
||||
var q = require("q");
|
||||
var expect = require('expect');
|
||||
|
||||
/**
|
||||
* Construct a mock HTTP backend, heavily inspired by Angular.js.
|
||||
* @constructor
|
||||
*/
|
||||
function HttpBackend() {
|
||||
this.requests = [];
|
||||
this.expectedRequests = [];
|
||||
var self = this;
|
||||
// the request function dependency that the SDK needs.
|
||||
this.requestFn = function(opts, callback) {
|
||||
var realReq = new Request(opts.method, opts.uri, opts.body, opts.qs);
|
||||
realReq.callback = callback;
|
||||
console.log("HTTP backend received request: %s %s", opts.method, opts.uri);
|
||||
self.requests.push(realReq);
|
||||
|
||||
var abort = function() {
|
||||
var idx = self.requests.indexOf(realReq);
|
||||
if (idx >= 0) {
|
||||
console.log("Aborting HTTP request: %s %s", opts.method, opts.uri);
|
||||
self.requests.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
return {
|
||||
abort: abort
|
||||
};
|
||||
};
|
||||
}
|
||||
HttpBackend.prototype = {
|
||||
/**
|
||||
* Respond to all of the requests (flush the queue).
|
||||
* @param {string} path The path to flush (optional) default: all.
|
||||
* @param {integer} numToFlush The number of things to flush (optional), default: all.
|
||||
* @return {Promise} resolved when there is nothing left to flush.
|
||||
*/
|
||||
flush: function(path, numToFlush) {
|
||||
var defer = q.defer();
|
||||
var self = this;
|
||||
var flushed = 0;
|
||||
var triedWaiting = false;
|
||||
console.log(
|
||||
"HTTP backend flushing... (path=%s numToFlush=%s)", path, numToFlush
|
||||
);
|
||||
var tryFlush = function() {
|
||||
// if there's more real requests and more expected requests, flush 'em.
|
||||
console.log(
|
||||
" trying to flush queue => reqs=%s expected=%s [%s]",
|
||||
self.requests.length, self.expectedRequests.length, path
|
||||
);
|
||||
if (self._takeFromQueue(path)) {
|
||||
// try again on the next tick.
|
||||
console.log(" flushed. Trying for more. [%s]", path);
|
||||
flushed += 1;
|
||||
if (numToFlush && flushed === numToFlush) {
|
||||
console.log(" [%s] Flushed assigned amount: %s", path, numToFlush);
|
||||
defer.resolve();
|
||||
}
|
||||
else {
|
||||
setTimeout(tryFlush, 0);
|
||||
}
|
||||
}
|
||||
else if (flushed === 0 && !triedWaiting) {
|
||||
// we may not have made the request yet, wait a generous amount of
|
||||
// time before giving up.
|
||||
setTimeout(tryFlush, 5);
|
||||
triedWaiting = true;
|
||||
}
|
||||
else {
|
||||
console.log(" no more flushes. [%s]", path);
|
||||
defer.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(tryFlush, 0);
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Attempts to resolve requests/expected requests.
|
||||
* @param {string} path The path to flush (optional) default: all.
|
||||
* @return {boolean} true if something was resolved.
|
||||
*/
|
||||
_takeFromQueue: function(path) {
|
||||
var req = null;
|
||||
var i, j;
|
||||
var matchingReq, expectedReq, testResponse = null;
|
||||
for (i = 0; i < this.requests.length; i++) {
|
||||
req = this.requests[i];
|
||||
for (j = 0; j < this.expectedRequests.length; j++) {
|
||||
expectedReq = this.expectedRequests[j];
|
||||
if (path && path !== expectedReq.path) { continue; }
|
||||
if (expectedReq.method === req.method &&
|
||||
req.path.indexOf(expectedReq.path) !== -1) {
|
||||
if (!expectedReq.data || (JSON.stringify(expectedReq.data) ===
|
||||
JSON.stringify(req.data))) {
|
||||
matchingReq = expectedReq;
|
||||
this.expectedRequests.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingReq) {
|
||||
// remove from request queue
|
||||
this.requests.splice(i, 1);
|
||||
i--;
|
||||
|
||||
for (j = 0; j < matchingReq.checks.length; j++) {
|
||||
matchingReq.checks[j](req);
|
||||
}
|
||||
testResponse = matchingReq.response;
|
||||
console.log(" responding to %s", matchingReq.path);
|
||||
var body = testResponse.body;
|
||||
if (Object.prototype.toString.call(body) == "[object Function]") {
|
||||
body = body(req.path, req.data);
|
||||
}
|
||||
req.callback(
|
||||
testResponse.err, testResponse.response, body
|
||||
);
|
||||
matchingReq = null;
|
||||
}
|
||||
}
|
||||
if (testResponse) { // flushed something
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes sure that the SDK hasn't sent any more requests to the backend.
|
||||
*/
|
||||
verifyNoOutstandingRequests: function() {
|
||||
var firstOutstandingReq = this.requests[0] || {};
|
||||
expect(this.requests.length).toEqual(0,
|
||||
"Expected no more HTTP requests but received request to " +
|
||||
firstOutstandingReq.path
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes sure that the test doesn't have any unresolved requests.
|
||||
*/
|
||||
verifyNoOutstandingExpectation: function() {
|
||||
var firstOutstandingExpectation = this.expectedRequests[0] || {};
|
||||
expect(this.expectedRequests.length).toEqual(
|
||||
0,
|
||||
"Expected to see HTTP request for "
|
||||
+ firstOutstandingExpectation.method
|
||||
+ " " + firstOutstandingExpectation.path
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create an expected request.
|
||||
* @param {string} method The HTTP method
|
||||
* @param {string} path The path (which can be partial)
|
||||
* @param {Object} data The expected data.
|
||||
* @return {Request} An expected request.
|
||||
*/
|
||||
when: function(method, path, data) {
|
||||
var pendingReq = new Request(method, path, data);
|
||||
this.expectedRequests.push(pendingReq);
|
||||
return pendingReq;
|
||||
}
|
||||
};
|
||||
|
||||
function Request(method, path, data, queryParams) {
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
this.data = data;
|
||||
this.queryParams = queryParams;
|
||||
this.callback = null;
|
||||
this.response = null;
|
||||
this.checks = [];
|
||||
}
|
||||
Request.prototype = {
|
||||
/**
|
||||
* Execute a check when this request has been satisfied.
|
||||
* @param {Function} fn The function to execute.
|
||||
* @return {Request} for chaining calls.
|
||||
*/
|
||||
check: function(fn) {
|
||||
this.checks.push(fn);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Respond with the given data when this request is satisfied.
|
||||
* @param {Number} code The HTTP status code.
|
||||
* @param {Object|Function} data The HTTP JSON body. If this is a function,
|
||||
* it will be invoked when the JSON body is required (which should be returned).
|
||||
*/
|
||||
respond: function(code, data) {
|
||||
this.response = {
|
||||
response: {
|
||||
statusCode: code,
|
||||
headers: {}
|
||||
},
|
||||
body: data,
|
||||
err: null
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Fail with an Error when this request is satisfied.
|
||||
* @param {Number} code The HTTP status code.
|
||||
* @param {Error} err The error to throw (e.g. Network Error)
|
||||
*/
|
||||
fail: function(code, err) {
|
||||
this.response = {
|
||||
response: {
|
||||
statusCode: code,
|
||||
headers: {}
|
||||
},
|
||||
body: null,
|
||||
err: err
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* The HttpBackend class.
|
||||
*/
|
||||
module.exports = HttpBackend;
|
8
test/skin-sdk.js
Normal file
8
test/skin-sdk.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* skin-sdk.js
|
||||
*
|
||||
* Skins the react-sdk with the vector components
|
||||
*/
|
||||
|
||||
var sdk = require('matrix-react-sdk');
|
||||
sdk.loadSkin(require('component-index'));
|
24
test/test-utils.js
Normal file
24
test/test-utils.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
"use strict";
|
||||
|
||||
var q = require('q');
|
||||
|
||||
/**
|
||||
* Perform common actions before each test case, e.g. printing the test case
|
||||
* name to stdout.
|
||||
* @param {Mocha.Context} context The test context
|
||||
*/
|
||||
module.exports.beforeEach = function(context) {
|
||||
var desc = context.currentTest.fullTitle();
|
||||
console.log();
|
||||
console.log(desc);
|
||||
console.log(new Array(1 + desc.length).join("="));
|
||||
};
|
||||
|
||||
/**
|
||||
* returns true if the current environment supports webrtc
|
||||
*/
|
||||
module.exports.browserSupportsWebRTC = function() {
|
||||
var n = global.window.navigator;
|
||||
return n.getUserMedia || n.webkitGetUserMedia ||
|
||||
n.mozGetUserMedia;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue