Compare commits

..

1 Commits

Author SHA1 Message Date
David Baker
569353f5d4 See if pre installing phantomjs fixes boom error 2017-07-03 12:53:57 +01:00
10 changed files with 369 additions and 77 deletions

View File

@@ -1,10 +1,3 @@
# we need trusty for the chrome addon
dist: trusty
# we don't need sudo, so can run in a container, which makes startup much
# quicker.
sudo: false
language: node_js
node_js:
# make sure we work with a range of node versions.
@@ -23,9 +16,7 @@ node_js:
- 6.3
- 6
- 7
addons:
chrome: stable
install:
# clone the deps with depth 1: we know we will only ever need that one
# commit.
- scripts/fetch-develop.deps.sh --depth 1 && npm install
- scripts/fetch-develop.deps.sh --depth 1 && npm i phantomjs-prebuilt && npm install

View File

@@ -81,7 +81,7 @@ to build.
npm run build
```
However, we recommend setting up a proper development environment (see "Setting
up a dev environment" below) if you want to run your own copy of the
up a development environment" below) if you want to run your own copy of the
`develop` branch, as it makes it much easier to keep these dependencies
up-to-date. Or just use https://riot.im/develop - the continuous integration
release of the develop branch.
@@ -253,6 +253,7 @@ Finally, build and start Riot itself:
1. `rm -r node_modules/matrix-react-sdk; ln -s ../../matrix-react-sdk node_modules/`
1. `npm start`
1. Wait a few seconds for the initial build to finish; you should see something like:
```
Hash: b0af76309dd56d7275c8
Version: webpack 1.12.14
@@ -281,34 +282,19 @@ If any of these steps error with, `file table overflow`, you are probably on a m
which has a very low limit on max open files. Run `ulimit -Sn 1024` and try again.
You'll need to do this in each new terminal you open before building Riot.
Running the tests
-----------------
There are a number of application-level tests in the `tests` directory; these
are designed to run in a browser instance under the control of
[karma](https://karma-runner.github.io). To run them:
* Make sure you have Chrome installed (a recent version, like 59)
* Make sure you have `matrix-js-sdk` and `matrix-react-sdk` installed and
built, as above
* `npm run test`
The above will run the tests under Chrome in a `headless` mode.
You can also tell karma to run the tests in a loop (every time the source
changes), in an instance of Chrome on your desktop, with `npm run
test-multi`. This also gives you the option of running the tests in 'debug'
mode, which is useful for stepping through the tests in the developer tools.
Translations
============
To add a new translation, head to the [translating doc](docs/translating.md).
For a developer guide, see the [translating dev doc](docs/translating-dev.md).
How to add a new translation?
=============================
[<img src="https://translate.riot.im/widgets/riot-web/-/multi-auto.svg" alt="translationsstatus" width="340">](https://translate.riot.im/engage/riot-web/?utm_source=widget)
Head to the [translating doc](docs/translating.md)
Adding Strings to the translations (Developer Guide)
====================================================
Head to the [translating dev doc](docs/translating-dev.md)
Triaging issues
===============

View File

@@ -113,23 +113,8 @@ module.exports = function (config) {
browsers: [
'Chrome',
//'PhantomJS',
//'ChromeHeadless'
],
customLaunchers: {
'ChromeHeadless': {
base: 'Chrome',
flags: [
// '--no-sandbox',
// See https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md
'--headless',
'--disable-gpu',
// Without a remote debugging port, Google Chrome exits immediately.
'--remote-debugging-port=9222',
],
}
},
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
// singleRun: false,

View File

@@ -48,7 +48,7 @@
"lintall": "eslint src/ test/",
"clean": "rimraf lib webapp electron_app/dist",
"prepublish": "npm run build:compile",
"test": "karma start --single-run=true --autoWatch=false --browsers ChromeHeadless --colors=false",
"test": "karma start --single-run=true --autoWatch=false --browsers PhantomJS --colors=false",
"test-multi": "karma start"
},
"dependencies": {
@@ -119,12 +119,13 @@
"karma-cli": "^0.1.2",
"karma-junit-reporter": "^0.4.1",
"karma-mocha": "^0.2.2",
"karma-phantomjs-launcher": "^1.0.0",
"karma-webpack": "^1.7.0",
"matrix-mock-request": "^1.0.0",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"mocha": "^2.4.5",
"parallelshell": "^1.2.0",
"phantomjs-prebuilt": "^2.1.7",
"postcss-extend": "^1.0.5",
"postcss-import": "^9.0.0",
"postcss-loader": "^1.2.2",

View File

@@ -70,11 +70,9 @@ module.exports = React.createClass({
this.setState({protocolsLoading: true});
MatrixClientPeg.get().getThirdpartyProtocols().done((response) => {
console.log('got 3p networks: '+response);
this.protocols = response;
this.setState({protocolsLoading: false});
}, (err) => {
console.warn(`error loading thirdparty protocols: ${err}`);
this.setState({protocolsLoading: false});
if (MatrixClientPeg.get().isGuest()) {
// Guests currently aren't allowed to use this API, so
@@ -499,9 +497,6 @@ module.exports = React.createClass({
);
}
console.log('renderiong with 3p networks: '+ this.protocols);
let content;
if (this.state.loading) {
content = <div className="mx_RoomDirectory">

View File

@@ -67,7 +67,7 @@ module.exports = React.createClass({
onResendClick: function() {
Resend.resend(this.props.mxEvent);
this.closeMenu();
if (this.props.onFinished) this.props.onFinished();
},
onViewSourceClick: function() {
@@ -75,7 +75,7 @@ module.exports = React.createClass({
Modal.createDialog(ViewSource, {
content: this.props.mxEvent.event,
}, 'mx_Dialog_viewsource');
this.closeMenu();
if (this.props.onFinished) this.props.onFinished();
},
onViewClearSourceClick: function() {
@@ -84,7 +84,7 @@ module.exports = React.createClass({
// FIXME: _clearEvent is private
content: this.props.mxEvent._clearEvent,
}, 'mx_Dialog_viewsource');
this.closeMenu();
if (this.props.onFinished) this.props.onFinished();
},
onRedactClick: function() {
@@ -106,12 +106,12 @@ module.exports = React.createClass({
}).done();
},
}, 'mx_Dialog_confirmredact');
this.closeMenu();
if (this.props.onFinished) this.props.onFinished();
},
onCancelSendClick: function() {
Resend.removeFromQueue(this.props.mxEvent);
this.closeMenu();
if (this.props.onFinished) this.props.onFinished();
},
onForwardClick: function() {
@@ -130,7 +130,7 @@ module.exports = React.createClass({
if (this.props.eventTileOps) {
this.props.eventTileOps.unhideWidget();
}
this.closeMenu();
if (this.props.onFinished) this.props.onFinished();
},
onQuoteClick: function() {
@@ -139,7 +139,6 @@ module.exports = React.createClass({
action: 'quote',
event: this.props.mxEvent,
});
this.closeMenu();
},
render: function() {

View File

@@ -50,16 +50,15 @@
margin: 0 3px;
}
.mx_Autocomplete_Completion_container_truncate {
.mx_Autocomplete_Completion_title,
.mx_Autocomplete_Completion_subtitle,
.mx_Autocomplete_Completion_description {
/* Ellipsis for long names/subtitles/descriptions*/
max-width: 150px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mx_Autocomplete_provider_name,
.mx_Autocomplete_Completion_title,
.mx_Autocomplete_Completion_subtitle,
.mx_Autocomplete_Completion_description {
/* Ellipsis for long names/subtitles/descriptions*/
max-width: 150px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* container for pill-style completions */

View File

@@ -36,7 +36,7 @@ var expect = require('expect');
var q = require('q');
var test_utils = require('../test-utils');
var MockHttpBackend = require('matrix-mock-request');
var MockHttpBackend = require('../mock-request');
var HS_URL='http://localhost';
var IS_URL='http://localhost';

View File

@@ -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 'matrix-mock-request';
import MockHttpBackend from '../mock-request';
import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils';
var DEFAULT_HS_URL='http://my_server';

336
test/mock-request.js Normal file
View File

@@ -0,0 +1,336 @@
"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.
*
* <p>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;