From 849a4074335a76224dbc6508ef3f971c0f8df985 Mon Sep 17 00:00:00 2001 From: lilia Date: Thu, 30 Apr 2015 15:43:11 -0700 Subject: [PATCH] Add mock-socket for testing socket stuff --- Gruntfile.js | 1 + bower.json | 6 +- components/mock-socket/dist/mock-socket.js | 635 ++++++++++++++++++++ libtextsecure/test/test.js | 636 +++++++++++++++++++++ 4 files changed, 1277 insertions(+), 1 deletion(-) create mode 100644 components/mock-socket/dist/mock-socket.js diff --git a/Gruntfile.js b/Gruntfile.js index 6018471a30..2d501c7337 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -73,6 +73,7 @@ module.exports = function(grunt) { }, libtextsecuretest: { src: [ + 'components/mock-socket/dist/mock-socket.js', 'components/mocha/mocha.js', 'components/chai/chai.js', 'libtextsecure/test/_test.js' diff --git a/bower.json b/bower.json index 49b23391dd..d736212cfb 100644 --- a/bower.json +++ b/bower.json @@ -26,7 +26,8 @@ }, "devDependencies": { "mocha": "~2.0.1", - "chai": "~1.9.2" + "chai": "~1.9.2", + "mock-socket": "~0.3.2" }, "preen": { "jquery": [ @@ -107,6 +108,9 @@ ], "emojijs": [ "emoji.js" + ], + "mock-socket": [ + "dist/mock-socket.js" ] }, "concat": { diff --git a/components/mock-socket/dist/mock-socket.js b/components/mock-socket/dist/mock-socket.js new file mode 100644 index 0000000000..9ff39d924d --- /dev/null +++ b/components/mock-socket/dist/mock-socket.js @@ -0,0 +1,635 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 3 ? '/' : '') + url.slice(3, url.length).join('/').split('?')[0].split('#')[0]); + var _p = _l.pathname; + + if (_p.charAt(_p.length-1) === '/') { _p=_p.substring(0, _p.length-1); } + var _h = _l.hostname, _hs = _h.split('.'), _ps = _p.split('/'); + + if (arg === 'hostname') { return _h; } + else if (arg === 'domain') { + if (/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(_h)) { return _h; } + return _hs.slice(-2).join('.'); + } + //else if (arg === 'tld') { return _hs.slice(-1).join('.'); } + else if (arg === 'sub') { return _hs.slice(0, _hs.length - 2).join('.'); } + else if (arg === 'port') { return _l.port; } + else if (arg === 'protocol') { return _l.protocol.split(':')[0]; } + else if (arg === 'auth') { return _l.auth; } + else if (arg === 'user') { return _l.auth.split(':')[0]; } + else if (arg === 'pass') { return _l.auth.split(':')[1] || ''; } + else if (arg === 'path') { return _l.pathname; } + else if (arg.charAt(0) === '.') { + arg = arg.substring(1); + if(isNumeric(arg)) {arg = parseInt(arg, 10); return _hs[arg < 0 ? _hs.length + arg : arg-1] || ''; } + } + else if (isNumeric(arg)) { arg = parseInt(arg, 10); return _ps[arg < 0 ? _ps.length + arg : arg] || ''; } + else if (arg === 'file') { return _ps.slice(-1)[0]; } + else if (arg === 'filename') { return _ps.slice(-1)[0].split('.')[0]; } + else if (arg === 'fileext') { return _ps.slice(-1)[0].split('.')[1] || ''; } + else if (arg.charAt(0) === '?' || arg.charAt(0) === '#') { + var params = _ls, param = null; + + if(arg.charAt(0) === '?') { params = (params.split('?')[1] || '').split('#')[0]; } + else if(arg.charAt(0) === '#') { params = (params.split('#')[1] || ''); } + + if(!arg.charAt(1)) { return params; } + + arg = arg.substring(1); + params = params.split('&'); + + for(var i=0,ii=params.length; i= 0 && newReadyState <= 4) { + this.readyState = newReadyState; + } + } +}; + +module.exports = MockSocket; + +},{"./helpers/delay":2,"./helpers/global-context":3,"./helpers/message-event":4,"./helpers/url-transform":5,"./helpers/websocket-properties":6}],9:[function(require,module,exports){ +var socketMessageEvent = require('./helpers/message-event'); +var globalContext = require('./helpers/global-context'); + +function SocketService() { + this.list = {}; +} + +SocketService.prototype = { + server: null, + + /* + * This notifies the mock server that a client is connecting and also sets up + * the ready state observer. + * + * @param {client: object} the context of the client + * @param {readyStateFunction: function} the function that will be invoked on a ready state change + */ + clientIsConnecting: function(client, readyStateFunction) { + this.observe('updateReadyState', readyStateFunction, client); + + // if the server has not been set then we notify the onclose method of this client + if(!this.server) { + this.notify(client, 'updateReadyState', globalContext.MockSocket.CLOSED); + this.notifyOnlyFor(client, 'clientOnError'); + return false; + } + + this.notifyOnlyFor(client, 'updateReadyState', globalContext.MockSocket.OPEN); + this.notify('clientHasJoined', this.server); + this.notifyOnlyFor(client, 'clientOnOpen', socketMessageEvent('open', null, this.server.url)); + }, + + /* + * Closes a connection from the server's perspective. This should + * close all clients. + * + * @param {messageEvent: object} the mock message event. + */ + closeConnectionFromServer: function(messageEvent) { + this.notify('updateReadyState', globalContext.MockSocket.CLOSING); + this.notify('clientOnclose', messageEvent); + this.notify('updateReadyState', globalContext.MockSocket.CLOSED); + this.notify('clientHasLeft'); + }, + + /* + * Closes a connection from the clients perspective. This + * should only close the client who initiated the close and not + * all of the other clients. + * + * @param {messageEvent: object} the mock message event. + * @param {client: object} the context of the client + */ + closeConnectionFromClient: function(messageEvent, client) { + if(client.readyState === globalContext.MockSocket.OPEN) { + this.notifyOnlyFor(client, 'updateReadyState', globalContext.MockSocket.CLOSING); + this.notifyOnlyFor(client, 'clientOnclose', messageEvent); + this.notifyOnlyFor(client, 'updateReadyState', globalContext.MockSocket.CLOSED); + this.notify('clientHasLeft'); + } + }, + + + /* + * Notifies the mock server that a client has sent a message. + * + * @param {messageEvent: object} the mock message event. + */ + sendMessageToServer: function(messageEvent) { + this.notify('clientHasSentMessage', messageEvent.data, messageEvent); + }, + + /* + * Notifies all clients that the server has sent a message + * + * @param {messageEvent: object} the mock message event. + */ + sendMessageToClients: function(messageEvent) { + this.notify('clientOnMessage', messageEvent); + }, + + /* + * Setup the callback function observers for both the server and client. + * + * @param {observerKey: string} either: connection, message or close + * @param {callback: function} the callback to be invoked + * @param {server: object} the context of the server + */ + setCallbackObserver: function(observerKey, callback, server) { + this.observe(observerKey, callback, server); + }, + + /* + * Binds a callback to a namespace. If notify is called on a namespace all "observers" will be + * fired with the context that is passed in. + * + * @param {namespace: string} + * @param {callback: function} + * @param {context: object} + */ + observe: function(namespace, callback, context) { + + // Make sure the arguments are of the correct type + if( typeof namespace !== 'string' || typeof callback !== 'function' || (context && typeof context !== 'object')) { + return false; + } + + // If a namespace has not been created before then we need to "initialize" the namespace + if(!this.list[namespace]) { + this.list[namespace] = []; + } + + this.list[namespace].push({callback: callback, context: context}); + }, + + /* + * Remove all observers from a given namespace. + * + * @param {namespace: string} The namespace to clear. + */ + clearAll: function(namespace) { + + if(!this.verifyNamespaceArg(namespace)) { + return false; + } + + this.list[namespace] = []; + }, + + /* + * Notify all callbacks that have been bound to the given namespace. + * + * @param {namespace: string} The namespace to notify observers on. + * @param {namespace: url} The url to notify observers on. + */ + notify: function(namespace) { + + // This strips the namespace from the list of args as we dont want to pass that into the callback. + var argumentsForCallback = Array.prototype.slice.call(arguments, 1); + + if(!this.verifyNamespaceArg(namespace)) { + return false; + } + + // Loop over all of the observers and fire the callback function with the context. + for(var i = 0, len = this.list[namespace].length; i < len; i++) { + this.list[namespace][i].callback.apply(this.list[namespace][i].context, argumentsForCallback); + } + }, + + /* + * Notify only the callback of the given context and namespace. + * + * @param {context: object} the context to match against. + * @param {namespace: string} The namespace to notify observers on. + */ + notifyOnlyFor: function(context, namespace) { + + // This strips the namespace from the list of args as we dont want to pass that into the callback. + var argumentsForCallback = Array.prototype.slice.call(arguments, 2); + + if(!this.verifyNamespaceArg(namespace)) { + return false; + } + + // Loop over all of the observers and fire the callback function with the context. + for(var i = 0, len = this.list[namespace].length; i < len; i++) { + if(this.list[namespace][i].context === context) { + this.list[namespace][i].callback.apply(this.list[namespace][i].context, argumentsForCallback); + } + } + }, + + /* + * Verifies that the namespace is valid. + * + * @param {namespace: string} The namespace to verify. + */ + verifyNamespaceArg: function(namespace) { + if(typeof namespace !== 'string' || !this.list[namespace]) { + return false; + } + + return true; + } +}; + +module.exports = SocketService; + +},{"./helpers/global-context":3,"./helpers/message-event":4}]},{},[1]); diff --git a/libtextsecure/test/test.js b/libtextsecure/test/test.js index b9af69b4f9..423fefe280 100644 --- a/libtextsecure/test/test.js +++ b/libtextsecure/test/test.js @@ -1,3 +1,639 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 3 ? '/' : '') + url.slice(3, url.length).join('/').split('?')[0].split('#')[0]); + var _p = _l.pathname; + + if (_p.charAt(_p.length-1) === '/') { _p=_p.substring(0, _p.length-1); } + var _h = _l.hostname, _hs = _h.split('.'), _ps = _p.split('/'); + + if (arg === 'hostname') { return _h; } + else if (arg === 'domain') { + if (/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(_h)) { return _h; } + return _hs.slice(-2).join('.'); + } + //else if (arg === 'tld') { return _hs.slice(-1).join('.'); } + else if (arg === 'sub') { return _hs.slice(0, _hs.length - 2).join('.'); } + else if (arg === 'port') { return _l.port; } + else if (arg === 'protocol') { return _l.protocol.split(':')[0]; } + else if (arg === 'auth') { return _l.auth; } + else if (arg === 'user') { return _l.auth.split(':')[0]; } + else if (arg === 'pass') { return _l.auth.split(':')[1] || ''; } + else if (arg === 'path') { return _l.pathname; } + else if (arg.charAt(0) === '.') { + arg = arg.substring(1); + if(isNumeric(arg)) {arg = parseInt(arg, 10); return _hs[arg < 0 ? _hs.length + arg : arg-1] || ''; } + } + else if (isNumeric(arg)) { arg = parseInt(arg, 10); return _ps[arg < 0 ? _ps.length + arg : arg] || ''; } + else if (arg === 'file') { return _ps.slice(-1)[0]; } + else if (arg === 'filename') { return _ps.slice(-1)[0].split('.')[0]; } + else if (arg === 'fileext') { return _ps.slice(-1)[0].split('.')[1] || ''; } + else if (arg.charAt(0) === '?' || arg.charAt(0) === '#') { + var params = _ls, param = null; + + if(arg.charAt(0) === '?') { params = (params.split('?')[1] || '').split('#')[0]; } + else if(arg.charAt(0) === '#') { params = (params.split('#')[1] || ''); } + + if(!arg.charAt(1)) { return params; } + + arg = arg.substring(1); + params = params.split('&'); + + for(var i=0,ii=params.length; i= 0 && newReadyState <= 4) { + this.readyState = newReadyState; + } + } +}; + +module.exports = MockSocket; + +},{"./helpers/delay":2,"./helpers/global-context":3,"./helpers/message-event":4,"./helpers/url-transform":5,"./helpers/websocket-properties":6}],9:[function(require,module,exports){ +var socketMessageEvent = require('./helpers/message-event'); +var globalContext = require('./helpers/global-context'); + +function SocketService() { + this.list = {}; +} + +SocketService.prototype = { + server: null, + + /* + * This notifies the mock server that a client is connecting and also sets up + * the ready state observer. + * + * @param {client: object} the context of the client + * @param {readyStateFunction: function} the function that will be invoked on a ready state change + */ + clientIsConnecting: function(client, readyStateFunction) { + this.observe('updateReadyState', readyStateFunction, client); + + // if the server has not been set then we notify the onclose method of this client + if(!this.server) { + this.notify(client, 'updateReadyState', globalContext.MockSocket.CLOSED); + this.notifyOnlyFor(client, 'clientOnError'); + return false; + } + + this.notifyOnlyFor(client, 'updateReadyState', globalContext.MockSocket.OPEN); + this.notify('clientHasJoined', this.server); + this.notifyOnlyFor(client, 'clientOnOpen', socketMessageEvent('open', null, this.server.url)); + }, + + /* + * Closes a connection from the server's perspective. This should + * close all clients. + * + * @param {messageEvent: object} the mock message event. + */ + closeConnectionFromServer: function(messageEvent) { + this.notify('updateReadyState', globalContext.MockSocket.CLOSING); + this.notify('clientOnclose', messageEvent); + this.notify('updateReadyState', globalContext.MockSocket.CLOSED); + this.notify('clientHasLeft'); + }, + + /* + * Closes a connection from the clients perspective. This + * should only close the client who initiated the close and not + * all of the other clients. + * + * @param {messageEvent: object} the mock message event. + * @param {client: object} the context of the client + */ + closeConnectionFromClient: function(messageEvent, client) { + if(client.readyState === globalContext.MockSocket.OPEN) { + this.notifyOnlyFor(client, 'updateReadyState', globalContext.MockSocket.CLOSING); + this.notifyOnlyFor(client, 'clientOnclose', messageEvent); + this.notifyOnlyFor(client, 'updateReadyState', globalContext.MockSocket.CLOSED); + this.notify('clientHasLeft'); + } + }, + + + /* + * Notifies the mock server that a client has sent a message. + * + * @param {messageEvent: object} the mock message event. + */ + sendMessageToServer: function(messageEvent) { + this.notify('clientHasSentMessage', messageEvent.data, messageEvent); + }, + + /* + * Notifies all clients that the server has sent a message + * + * @param {messageEvent: object} the mock message event. + */ + sendMessageToClients: function(messageEvent) { + this.notify('clientOnMessage', messageEvent); + }, + + /* + * Setup the callback function observers for both the server and client. + * + * @param {observerKey: string} either: connection, message or close + * @param {callback: function} the callback to be invoked + * @param {server: object} the context of the server + */ + setCallbackObserver: function(observerKey, callback, server) { + this.observe(observerKey, callback, server); + }, + + /* + * Binds a callback to a namespace. If notify is called on a namespace all "observers" will be + * fired with the context that is passed in. + * + * @param {namespace: string} + * @param {callback: function} + * @param {context: object} + */ + observe: function(namespace, callback, context) { + + // Make sure the arguments are of the correct type + if( typeof namespace !== 'string' || typeof callback !== 'function' || (context && typeof context !== 'object')) { + return false; + } + + // If a namespace has not been created before then we need to "initialize" the namespace + if(!this.list[namespace]) { + this.list[namespace] = []; + } + + this.list[namespace].push({callback: callback, context: context}); + }, + + /* + * Remove all observers from a given namespace. + * + * @param {namespace: string} The namespace to clear. + */ + clearAll: function(namespace) { + + if(!this.verifyNamespaceArg(namespace)) { + return false; + } + + this.list[namespace] = []; + }, + + /* + * Notify all callbacks that have been bound to the given namespace. + * + * @param {namespace: string} The namespace to notify observers on. + * @param {namespace: url} The url to notify observers on. + */ + notify: function(namespace) { + + // This strips the namespace from the list of args as we dont want to pass that into the callback. + var argumentsForCallback = Array.prototype.slice.call(arguments, 1); + + if(!this.verifyNamespaceArg(namespace)) { + return false; + } + + // Loop over all of the observers and fire the callback function with the context. + for(var i = 0, len = this.list[namespace].length; i < len; i++) { + this.list[namespace][i].callback.apply(this.list[namespace][i].context, argumentsForCallback); + } + }, + + /* + * Notify only the callback of the given context and namespace. + * + * @param {context: object} the context to match against. + * @param {namespace: string} The namespace to notify observers on. + */ + notifyOnlyFor: function(context, namespace) { + + // This strips the namespace from the list of args as we dont want to pass that into the callback. + var argumentsForCallback = Array.prototype.slice.call(arguments, 2); + + if(!this.verifyNamespaceArg(namespace)) { + return false; + } + + // Loop over all of the observers and fire the callback function with the context. + for(var i = 0, len = this.list[namespace].length; i < len; i++) { + if(this.list[namespace][i].context === context) { + this.list[namespace][i].callback.apply(this.list[namespace][i].context, argumentsForCallback); + } + } + }, + + /* + * Verifies that the namespace is valid. + * + * @param {namespace: string} The namespace to verify. + */ + verifyNamespaceArg: function(namespace) { + if(typeof namespace !== 'string' || !this.list[namespace]) { + return false; + } + + return true; + } +}; + +module.exports = SocketService; + +},{"./helpers/global-context":3,"./helpers/message-event":4}]},{},[1]); + ;(function(){ // CommonJS require()