diff --git a/package.json b/package.json index f51290061d..87b004ce1e 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "karma-mocha": "^0.2.2", "karma-phantomjs-launcher": "^1.0.0", "karma-webpack": "^1.7.0", + "matrix-mock-request": "^0.1.3", "minimist": "^1.2.0", "mkdirp": "^0.5.1", "mocha": "^2.4.5", diff --git a/test/app-tests/joining.js b/test/app-tests/joining.js index d967012567..11fd3d4810 100644 --- a/test/app-tests/joining.js +++ b/test/app-tests/joining.js @@ -36,7 +36,7 @@ var expect = require('expect'); var q = require('q'); var test_utils = require('../test-utils'); -var MockHttpBackend = require('../mock-request'); +var MockHttpBackend = require('matrix-mock-request'); var HS_URL='http://localhost'; var IS_URL='http://localhost'; diff --git a/test/app-tests/loading.js b/test/app-tests/loading.js index 54e98760e9..d01836a36a 100644 --- a/test/app-tests/loading.js +++ b/test/app-tests/loading.js @@ -33,7 +33,7 @@ import {VIEWS} from 'matrix-react-sdk/lib/components/structures/MatrixChat'; import dis from 'matrix-react-sdk/lib/dispatcher'; import * as test_utils from '../test-utils'; -import MockHttpBackend from '../mock-request'; +import MockHttpBackend from 'matrix-mock-request'; import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils'; var DEFAULT_HS_URL='http://my_server'; diff --git a/test/mock-request.js b/test/mock-request.js deleted file mode 100644 index 64ac6c06d5..0000000000 --- a/test/mock-request.js +++ /dev/null @@ -1,336 +0,0 @@ -"use strict"; -const q = require("q"); -import expect from 'expect'; - -/** - * Construct a mock HTTP backend, heavily inspired by Angular.js. - * @constructor - */ -function HttpBackend() { - this.requests = []; - this.expectedRequests = []; - const self = this; - // the request function dependency that the SDK needs. - this.requestFn = function(opts, callback) { - const req = new Request(opts, callback); - console.log(`${Date.now()} HTTP backend received request: ${req}`); - self.requests.push(req); - - const abort = function() { - const idx = self.requests.indexOf(req); - if (idx >= 0) { - console.log("Aborting HTTP request: %s %s", opts.method, - opts.uri); - self.requests.splice(idx, 1); - req.callback("aborted"); - } - }; - - return { - abort: abort, - }; - }; - - // very simplistic mapping from the whatwg fetch interface onto the request - // interface, so we can use the same mock backend for both. - this.fetchFn = function(input, init) { - init = init || {}; - const requestOpts = { - uri: input, - method: init.method || 'GET', - body: init.body, - }; - - return new Promise((resolve, reject) => { - function callback(err, response, body) { - if (err) { - reject(err); - } - resolve({ - ok: response.statusCode >= 200 && response.statusCode < 300, - json: () => body, - }); - }; - - const req = new Request(requestOpts, callback); - console.log(`HTTP backend received request: ${req}`); - self.requests.push(req); - }); - }; -} -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. - * @param {integer=} waitTime The time (in ms) to wait for a request to happen. - * default: 100 - * - * @return {Promise} resolves when there is nothing left to flush, with the - * number of requests flushed - */ - flush: function(path, numToFlush, waitTime) { - const defer = q.defer(); - const self = this; - let flushed = 0; - if (waitTime === undefined) { - waitTime = 100; - } - - function log(msg) { - console.log(`${Date.now()} flush[${path || ''}]: ${msg}`); - } - - log("HTTP backend flushing... (path=" + path - + " numToFlush=" + numToFlush - + " waitTime=" + waitTime - + ")", - ); - const endTime = waitTime + Date.now(); - - const tryFlush = function() { - // if there's more real requests and more expected requests, flush 'em. - log(` trying to flush => reqs=[${self.requests}] ` + - `expected=[${self.expectedRequests}]`, - ); - if (self._takeFromQueue(path)) { - // try again on the next tick. - flushed += 1; - if (numToFlush && flushed === numToFlush) { - log(`Flushed assigned amount: ${numToFlush}`); - defer.resolve(flushed); - } else { - log(` flushed. Trying for more.`); - setTimeout(tryFlush, 0); - } - } else if (flushed === 0 && Date.now() < endTime) { - // we may not have made the request yet, wait a generous amount of - // time before giving up. - log(` nothing to flush yet; waiting for requests.`); - setTimeout(tryFlush, 5); - } else { - if (flushed === 0) { - log("nothing to flush; giving up"); - } else { - log(`no more flushes after flushing ${flushed} requests`); - } - defer.resolve(flushed); - } - }; - - 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) { - let req = null; - let i; - let j; - let matchingReq = null; - let expectedReq = null; - let 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(`${Date.now()} responding to ${matchingReq.path}`); - let 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() { - const 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() { - const firstOutstandingExpectation = this.expectedRequests[0] || {}; - expect(this.expectedRequests.length).toEqual(0, - "Expected to see HTTP request for " + 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) { - const pendingReq = new ExpectedRequest(method, path, data); - this.expectedRequests.push(pendingReq); - return pendingReq; - }, -}; - -/** - * Represents the expectation of a request. - * - *
Includes the conditions to be matched against, the checks to be made, - * and the response to be returned. - * - * @constructor - * @param {string} method - * @param {string} path - * @param {object?} data - */ -function ExpectedRequest(method, path, data) { - this.method = method; - this.path = path; - this.data = data; - this.response = null; - this.checks = []; -} - -ExpectedRequest.prototype = { - toString: function() { - return this.method + " " + this.path - }, - - /** - * 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, - }; - }, -}; - -/** - * Represents a request made by the app. - * - * @constructor - * @param {object} opts opts passed to request() - * @param {function} callback - */ -function Request(opts, callback) { - this.opts = opts; - this.callback = callback; - - Object.defineProperty(this, 'method', { - get: function() { - return opts.method; - }, - }); - - Object.defineProperty(this, 'path', { - get: function() { - return opts.uri; - }, - }); - - Object.defineProperty(this, 'data', { - get: function() { - return opts.body; - }, - }); - - Object.defineProperty(this, 'queryParams', { - get: function() { - return opts.qs; - }, - }); - - Object.defineProperty(this, 'headers', { - get: function() { - return opts.headers || {}; - }, - }); -} - -Request.prototype = { - toString: function() { - return this.method + " " + this.path; - }, -}; - -/** - * The HttpBackend class. - */ -module.exports = HttpBackend;