unit tests, and some cleaning up

This commit is contained in:
Josh Burman
2019-03-14 10:14:42 -04:00
parent 501a14d713
commit 21f77fa4c3
134 changed files with 16787 additions and 359 deletions

57
node_modules/mock-socket/src/algorithms/close.js generated vendored Normal file
View File

@ -0,0 +1,57 @@
import WebSocket from '../websocket';
import delay from '../helpers/delay';
import networkBridge from '../network-bridge';
import { createCloseEvent, createEvent } from '../event/factory';
export function closeWebSocketConnection(context, code, reason) {
context.readyState = WebSocket.CLOSING;
const server = networkBridge.serverLookup(context.url);
const closeEvent = createCloseEvent({
type: 'close',
target: context,
code,
reason
});
delay(() => {
networkBridge.removeWebSocket(context, context.url);
context.readyState = WebSocket.CLOSED;
context.dispatchEvent(closeEvent);
if (server) {
server.dispatchEvent(closeEvent, server);
}
}, context);
}
export function failWebSocketConnection(context, code, reason) {
context.readyState = WebSocket.CLOSING;
const server = networkBridge.serverLookup(context.url);
const closeEvent = createCloseEvent({
type: 'close',
target: context,
code,
reason,
wasClean: false
});
const errorEvent = createEvent({
type: 'error',
target: context
});
delay(() => {
networkBridge.removeWebSocket(context, context.url);
context.readyState = WebSocket.CLOSED;
context.dispatchEvent(errorEvent);
context.dispatchEvent(closeEvent);
if (server) {
server.dispatchEvent(closeEvent, server);
}
}, context);
}

29
node_modules/mock-socket/src/constants.js generated vendored Normal file
View File

@ -0,0 +1,29 @@
/*
* https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
*/
export const CLOSE_CODES = {
CLOSE_NORMAL: 1000,
CLOSE_GOING_AWAY: 1001,
CLOSE_PROTOCOL_ERROR: 1002,
CLOSE_UNSUPPORTED: 1003,
CLOSE_NO_STATUS: 1005,
CLOSE_ABNORMAL: 1006,
UNSUPPORTED_DATA: 1007,
POLICY_VIOLATION: 1008,
CLOSE_TOO_LARGE: 1009,
MISSING_EXTENSION: 1010,
INTERNAL_ERROR: 1011,
SERVICE_RESTART: 1012,
TRY_AGAIN_LATER: 1013,
TLS_HANDSHAKE: 1015
};
export const ERROR_PREFIX = {
CONSTRUCTOR_ERROR: "Failed to construct 'WebSocket':",
CLOSE_ERROR: "Failed to execute 'close' on 'WebSocket':",
EVENT: {
CONSTRUCT: "Failed to construct 'Event':",
MESSAGE: "Failed to construct 'MessageEvent':",
CLOSE: "Failed to construct 'CloseEvent':"
}
};

34
node_modules/mock-socket/src/event/close.js generated vendored Normal file
View File

@ -0,0 +1,34 @@
import EventPrototype from './prototype';
import { ERROR_PREFIX } from '../constants';
export default class CloseEvent extends EventPrototype {
constructor(type, eventInitConfig = {}) {
super();
if (!type) {
throw new TypeError(`${ERROR_PREFIX.EVENT.CLOSE} 1 argument required, but only 0 present.`);
}
if (typeof eventInitConfig !== 'object') {
throw new TypeError(`${ERROR_PREFIX.EVENT.CLOSE} parameter 2 ('eventInitDict') is not an object`);
}
const { bubbles, cancelable, code, reason, wasClean } = eventInitConfig;
this.type = `${type}`;
this.timeStamp = Date.now();
this.target = null;
this.srcElement = null;
this.returnValue = true;
this.isTrusted = false;
this.eventPhase = 0;
this.defaultPrevented = false;
this.currentTarget = null;
this.cancelable = cancelable ? Boolean(cancelable) : false;
this.cancelBubble = false;
this.bubbles = bubbles ? Boolean(bubbles) : false;
this.code = typeof code === 'number' ? parseInt(code, 10) : 0;
this.reason = `${reason || ''}`;
this.wasClean = wasClean ? Boolean(wasClean) : false;
}
}

31
node_modules/mock-socket/src/event/event.js generated vendored Normal file
View File

@ -0,0 +1,31 @@
import EventPrototype from './prototype';
import { ERROR_PREFIX } from '../constants';
export default class Event extends EventPrototype {
constructor(type, eventInitConfig = {}) {
super();
if (!type) {
throw new TypeError(`${ERROR_PREFIX.EVENT_ERROR} 1 argument required, but only 0 present.`);
}
if (typeof eventInitConfig !== 'object') {
throw new TypeError(`${ERROR_PREFIX.EVENT_ERROR} parameter 2 ('eventInitDict') is not an object.`);
}
const { bubbles, cancelable } = eventInitConfig;
this.type = `${type}`;
this.timeStamp = Date.now();
this.target = null;
this.srcElement = null;
this.returnValue = true;
this.isTrusted = false;
this.eventPhase = 0;
this.defaultPrevented = false;
this.currentTarget = null;
this.cancelable = cancelable ? Boolean(cancelable) : false;
this.canncelBubble = false;
this.bubbles = bubbles ? Boolean(bubbles) : false;
}
}

75
node_modules/mock-socket/src/event/factory.js generated vendored Normal file
View File

@ -0,0 +1,75 @@
import Event from './event';
import MessageEvent from './message';
import CloseEvent from './close';
/*
* Creates an Event object and extends it to allow full modification of
* its properties.
*
* @param {object} config - within config you will need to pass type and optionally target
*/
function createEvent(config) {
const { type, target } = config;
const eventObject = new Event(type);
if (target) {
eventObject.target = target;
eventObject.srcElement = target;
eventObject.currentTarget = target;
}
return eventObject;
}
/*
* Creates a MessageEvent object and extends it to allow full modification of
* its properties.
*
* @param {object} config - within config: type, origin, data and optionally target
*/
function createMessageEvent(config) {
const { type, origin, data, target } = config;
const messageEvent = new MessageEvent(type, {
data,
origin
});
if (target) {
messageEvent.target = target;
messageEvent.srcElement = target;
messageEvent.currentTarget = target;
}
return messageEvent;
}
/*
* Creates a CloseEvent object and extends it to allow full modification of
* its properties.
*
* @param {object} config - within config: type and optionally target, code, and reason
*/
function createCloseEvent(config) {
const { code, reason, type, target } = config;
let { wasClean } = config;
if (!wasClean) {
wasClean = code === 1000;
}
const closeEvent = new CloseEvent(type, {
code,
reason,
wasClean
});
if (target) {
closeEvent.target = target;
closeEvent.srcElement = target;
closeEvent.currentTarget = target;
}
return closeEvent;
}
export { createEvent, createMessageEvent, createCloseEvent };

35
node_modules/mock-socket/src/event/message.js generated vendored Normal file
View File

@ -0,0 +1,35 @@
import EventPrototype from './prototype';
import { ERROR_PREFIX } from '../constants';
export default class MessageEvent extends EventPrototype {
constructor(type, eventInitConfig = {}) {
super();
if (!type) {
throw new TypeError(`${ERROR_PREFIX.EVENT.MESSAGE} 1 argument required, but only 0 present.`);
}
if (typeof eventInitConfig !== 'object') {
throw new TypeError(`${ERROR_PREFIX.EVENT.MESSAGE} parameter 2 ('eventInitDict') is not an object`);
}
const { bubbles, cancelable, data, origin, lastEventId, ports } = eventInitConfig;
this.type = `${type}`;
this.timeStamp = Date.now();
this.target = null;
this.srcElement = null;
this.returnValue = true;
this.isTrusted = false;
this.eventPhase = 0;
this.defaultPrevented = false;
this.currentTarget = null;
this.cancelable = cancelable ? Boolean(cancelable) : false;
this.canncelBubble = false;
this.bubbles = bubbles ? Boolean(bubbles) : false;
this.origin = `${origin}`;
this.ports = typeof ports === 'undefined' ? null : ports;
this.data = typeof data === 'undefined' ? null : data;
this.lastEventId = `${lastEventId || ''}`;
}
}

13
node_modules/mock-socket/src/event/prototype.js generated vendored Normal file
View File

@ -0,0 +1,13 @@
export default class EventPrototype {
// Noops
stopPropagation() {}
stopImmediatePropagation() {}
// if no arguments are passed then the type is set to "undefined" on
// chrome and safari.
initEvent(type = 'undefined', bubbles = false, cancelable = false) {
this.type = `${type}`;
this.bubbles = Boolean(bubbles);
this.cancelable = Boolean(cancelable);
}
}

73
node_modules/mock-socket/src/event/target.js generated vendored Normal file
View File

@ -0,0 +1,73 @@
import { reject, filter } from '../helpers/array-helpers';
/*
* EventTarget is an interface implemented by objects that can
* receive events and may have listeners for them.
*
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
*/
class EventTarget {
constructor() {
this.listeners = {};
}
/*
* Ties a listener function to an event type which can later be invoked via the
* dispatchEvent method.
*
* @param {string} type - the type of event (ie: 'open', 'message', etc.)
* @param {function} listener - the callback function to invoke whenever an event is dispatched matching the given type
* @param {boolean} useCapture - N/A TODO: implement useCapture functionality
*/
addEventListener(type, listener /* , useCapture */) {
if (typeof listener === 'function') {
if (!Array.isArray(this.listeners[type])) {
this.listeners[type] = [];
}
// Only add the same function once
if (filter(this.listeners[type], item => item === listener).length === 0) {
this.listeners[type].push(listener);
}
}
}
/*
* Removes the listener so it will no longer be invoked via the dispatchEvent method.
*
* @param {string} type - the type of event (ie: 'open', 'message', etc.)
* @param {function} listener - the callback function to invoke whenever an event is dispatched matching the given type
* @param {boolean} useCapture - N/A TODO: implement useCapture functionality
*/
removeEventListener(type, removingListener /* , useCapture */) {
const arrayOfListeners = this.listeners[type];
this.listeners[type] = reject(arrayOfListeners, listener => listener === removingListener);
}
/*
* Invokes all listener functions that are listening to the given event.type property. Each
* listener will be passed the event as the first argument.
*
* @param {object} event - event object which will be passed to all listeners of the event.type property
*/
dispatchEvent(event, ...customArguments) {
const eventName = event.type;
const listeners = this.listeners[eventName];
if (!Array.isArray(listeners)) {
return false;
}
listeners.forEach(listener => {
if (customArguments.length > 0) {
listener.apply(this, customArguments);
} else {
listener.call(this, event);
}
});
return true;
}
}
export default EventTarget;

21
node_modules/mock-socket/src/helpers/array-helpers.js generated vendored Normal file
View File

@ -0,0 +1,21 @@
export function reject(array, callback) {
const results = [];
array.forEach(itemInArray => {
if (!callback(itemInArray)) {
results.push(itemInArray);
}
});
return results;
}
export function filter(array, callback) {
const results = [];
array.forEach(itemInArray => {
if (callback(itemInArray)) {
results.push(itemInArray);
}
});
return results;
}

5
node_modules/mock-socket/src/helpers/byte-length.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
export default function lengthInUtf8Bytes(str) {
// Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence.
const m = encodeURIComponent(str).match(/%[89ABab]/g);
return str.length + (m ? m.length : 0);
}

5
node_modules/mock-socket/src/helpers/dedupe.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
export default arr =>
arr.reduce((deduped, b) => {
if (deduped.indexOf(b) > -1) return deduped;
return deduped.concat(b);
}, []);

11
node_modules/mock-socket/src/helpers/delay.js generated vendored Normal file
View File

@ -0,0 +1,11 @@
/*
* This delay allows the thread to finish assigning its on* methods
* before invoking the delay callback. This is purely a timing hack.
* http://geekabyte.blogspot.com/2014/01/javascript-effect-of-setting-settimeout.html
*
* @param {callback: function} the callback which will be invoked after the timeout
* @parma {context: object} the context in which to invoke the function
*/
export default function delay(callback, context) {
setTimeout(timeoutContext => callback.call(timeoutContext), 4, context);
}

View File

@ -0,0 +1,7 @@
export default function retrieveGlobalObject() {
if (typeof window !== 'undefined') {
return window;
}
return typeof process === 'object' && typeof require === 'function' && typeof global === 'object' ? global : this;
}

7
node_modules/mock-socket/src/helpers/logger.js generated vendored Normal file
View File

@ -0,0 +1,7 @@
export default function log(method, message) {
/* eslint-disable no-console */
if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'test') {
console[method].call(null, message);
}
/* eslint-enable no-console */
}

View File

@ -0,0 +1,7 @@
export default function normalizeSendData(data) {
if (Object.prototype.toString.call(data) !== '[object Blob]' && !(data instanceof ArrayBuffer)) {
data = String(data);
}
return data;
}

View File

@ -0,0 +1,24 @@
import { ERROR_PREFIX } from '../constants';
export default function protocolVerification(protocols = []) {
if (!Array.isArray(protocols) && typeof protocols !== 'string') {
throw new SyntaxError(`${ERROR_PREFIX.CONSTRUCTOR_ERROR} The subprotocol '${protocols.toString()}' is invalid.`);
}
if (typeof protocols === 'string') {
protocols = [protocols];
}
const uniq = protocols.map(p => ({ count: 1, protocol: p })).reduce((a, b) => {
a[b.protocol] = (a[b.protocol] || 0) + b.count;
return a;
}, {});
const duplicates = Object.keys(uniq).filter(a => uniq[a] > 1);
if (duplicates.length > 0) {
throw new SyntaxError(`${ERROR_PREFIX.CONSTRUCTOR_ERROR} The subprotocol '${duplicates[0]}' is duplicated.`);
}
return protocols;
}

45
node_modules/mock-socket/src/helpers/proxy-factory.js generated vendored Normal file
View File

@ -0,0 +1,45 @@
import { CLOSE_CODES } from '../constants';
import { closeWebSocketConnection } from '../algorithms/close';
import normalizeSendData from './normalize-send';
import { createMessageEvent } from '../event/factory';
export default function proxyFactory(target) {
const handler = {
get(obj, prop) {
if (prop === 'close') {
return function close(options = {}) {
const code = options.code || CLOSE_CODES.CLOSE_NORMAL;
const reason = options.reason || '';
closeWebSocketConnection(target, code, reason);
};
}
if (prop === 'send') {
return function send(data) {
data = normalizeSendData(data);
target.dispatchEvent(
createMessageEvent({
type: 'message',
data,
origin: this.url,
target
})
);
};
}
if (prop === 'on') {
return function onWrapper(type, cb) {
target.addEventListener(`server::${type}`, cb);
};
}
return obj[prop];
}
};
const proxy = new Proxy(target, handler);
return proxy;
}

View File

@ -0,0 +1,37 @@
import URL from 'url-parse';
import { ERROR_PREFIX } from '../constants';
export default function urlVerification(url) {
const urlRecord = new URL(url);
const { pathname, protocol, hash } = urlRecord;
if (!url) {
throw new TypeError(`${ERROR_PREFIX.CONSTRUCTOR_ERROR} 1 argument required, but only 0 present.`);
}
if (!pathname) {
urlRecord.pathname = '/';
}
if (protocol === '') {
throw new SyntaxError(`${ERROR_PREFIX.CONSTRUCTOR_ERROR} The URL '${urlRecord.toString()}' is invalid.`);
}
if (protocol !== 'ws:' && protocol !== 'wss:') {
throw new SyntaxError(
`${ERROR_PREFIX.CONSTRUCTOR_ERROR} The URL's scheme must be either 'ws' or 'wss'. '${protocol}' is not allowed.`
);
}
if (hash !== '') {
/* eslint-disable max-len */
throw new SyntaxError(
`${
ERROR_PREFIX.CONSTRUCTOR_ERROR
} The URL contains a fragment identifier ('${hash}'). Fragment identifiers are not allowed in WebSocket URLs.`
);
/* eslint-enable max-len */
}
return urlRecord.toString();
}

7
node_modules/mock-socket/src/index.js generated vendored Normal file
View File

@ -0,0 +1,7 @@
import MockServer from './server';
import MockSocketIO from './socket-io';
import MockWebSocket from './websocket';
export const Server = MockServer;
export const WebSocket = MockWebSocket;
export const SocketIO = MockSocketIO;

135
node_modules/mock-socket/src/network-bridge.js generated vendored Normal file
View File

@ -0,0 +1,135 @@
import { reject } from './helpers/array-helpers';
/*
* The network bridge is a way for the mock websocket object to 'communicate' with
* all available servers. This is a singleton object so it is important that you
* clean up urlMap whenever you are finished.
*/
class NetworkBridge {
constructor() {
this.urlMap = {};
}
/*
* Attaches a websocket object to the urlMap hash so that it can find the server
* it is connected to and the server in turn can find it.
*
* @param {object} websocket - websocket object to add to the urlMap hash
* @param {string} url
*/
attachWebSocket(websocket, url) {
const connectionLookup = this.urlMap[url];
if (connectionLookup && connectionLookup.server && connectionLookup.websockets.indexOf(websocket) === -1) {
connectionLookup.websockets.push(websocket);
return connectionLookup.server;
}
}
/*
* Attaches a websocket to a room
*/
addMembershipToRoom(websocket, room) {
const connectionLookup = this.urlMap[websocket.url];
if (connectionLookup && connectionLookup.server && connectionLookup.websockets.indexOf(websocket) !== -1) {
if (!connectionLookup.roomMemberships[room]) {
connectionLookup.roomMemberships[room] = [];
}
connectionLookup.roomMemberships[room].push(websocket);
}
}
/*
* Attaches a server object to the urlMap hash so that it can find a websockets
* which are connected to it and so that websockets can in turn can find it.
*
* @param {object} server - server object to add to the urlMap hash
* @param {string} url
*/
attachServer(server, url) {
const connectionLookup = this.urlMap[url];
if (!connectionLookup) {
this.urlMap[url] = {
server,
websockets: [],
roomMemberships: {}
};
return server;
}
}
/*
* Finds the server which is 'running' on the given url.
*
* @param {string} url - the url to use to find which server is running on it
*/
serverLookup(url) {
const connectionLookup = this.urlMap[url];
if (connectionLookup) {
return connectionLookup.server;
}
}
/*
* Finds all websockets which is 'listening' on the given url.
*
* @param {string} url - the url to use to find all websockets which are associated with it
* @param {string} room - if a room is provided, will only return sockets in this room
* @param {class} broadcaster - socket that is broadcasting and is to be excluded from the lookup
*/
websocketsLookup(url, room, broadcaster) {
let websockets;
const connectionLookup = this.urlMap[url];
websockets = connectionLookup ? connectionLookup.websockets : [];
if (room) {
const members = connectionLookup.roomMemberships[room];
websockets = members || [];
}
return broadcaster ? websockets.filter(websocket => websocket !== broadcaster) : websockets;
}
/*
* Removes the entry associated with the url.
*
* @param {string} url
*/
removeServer(url) {
delete this.urlMap[url];
}
/*
* Removes the individual websocket from the map of associated websockets.
*
* @param {object} websocket - websocket object to remove from the url map
* @param {string} url
*/
removeWebSocket(websocket, url) {
const connectionLookup = this.urlMap[url];
if (connectionLookup) {
connectionLookup.websockets = reject(connectionLookup.websockets, socket => socket === websocket);
}
}
/*
* Removes a websocket from a room
*/
removeMembershipFromRoom(websocket, room) {
const connectionLookup = this.urlMap[websocket.url];
const memberships = connectionLookup.roomMemberships[room];
if (connectionLookup && memberships !== null) {
connectionLookup.roomMemberships[room] = reject(memberships, socket => socket === websocket);
}
}
}
export default new NetworkBridge(); // Note: this is a singleton

217
node_modules/mock-socket/src/server.js generated vendored Normal file
View File

@ -0,0 +1,217 @@
import URL from 'url-parse';
import WebSocket from './websocket';
import dedupe from './helpers/dedupe';
import EventTarget from './event/target';
import { CLOSE_CODES } from './constants';
import networkBridge from './network-bridge';
import globalObject from './helpers/global-object';
import normalizeSendData from './helpers/normalize-send';
import { createEvent, createMessageEvent, createCloseEvent } from './event/factory';
class Server extends EventTarget {
constructor(url, options = {}) {
super();
const urlRecord = new URL(url);
if (!urlRecord.pathname) {
urlRecord.pathname = '/';
}
this.url = urlRecord.toString();
this.originalWebSocket = null;
const server = networkBridge.attachServer(this, this.url);
if (!server) {
this.dispatchEvent(createEvent({ type: 'error' }));
throw new Error('A mock server is already listening on this url');
}
if (typeof options.verifyClient === 'undefined') {
options.verifyClient = null;
}
if (typeof options.selectProtocol === 'undefined') {
options.selectProtocol = null;
}
this.options = options;
this.start();
}
/*
* Attaches the mock websocket object to the global object
*/
start() {
const globalObj = globalObject();
if (globalObj.WebSocket) {
this.originalWebSocket = globalObj.WebSocket;
}
globalObj.WebSocket = WebSocket;
}
/*
* Removes the mock websocket object from the global object
*/
stop(callback = () => {}) {
const globalObj = globalObject();
if (this.originalWebSocket) {
globalObj.WebSocket = this.originalWebSocket;
} else {
delete globalObj.WebSocket;
}
this.originalWebSocket = null;
networkBridge.removeServer(this.url);
if (typeof callback === 'function') {
callback();
}
}
/*
* This is the main function for the mock server to subscribe to the on events.
*
* ie: mockServer.on('connection', function() { console.log('a mock client connected'); });
*
* @param {string} type - The event key to subscribe to. Valid keys are: connection, message, and close.
* @param {function} callback - The callback which should be called when a certain event is fired.
*/
on(type, callback) {
this.addEventListener(type, callback);
}
/*
* Closes the connection and triggers the onclose method of all listening
* websockets. After that it removes itself from the urlMap so another server
* could add itself to the url.
*
* @param {object} options
*/
close(options = {}) {
const { code, reason, wasClean } = options;
const listeners = networkBridge.websocketsLookup(this.url);
// Remove server before notifications to prevent immediate reconnects from
// socket onclose handlers
networkBridge.removeServer(this.url);
listeners.forEach(socket => {
socket.readyState = WebSocket.CLOSE;
socket.dispatchEvent(
createCloseEvent({
type: 'close',
target: socket,
code: code || CLOSE_CODES.CLOSE_NORMAL,
reason: reason || '',
wasClean
})
);
});
this.dispatchEvent(createCloseEvent({ type: 'close' }), this);
}
/*
* Sends a generic message event to all mock clients.
*/
emit(event, data, options = {}) {
let { websockets } = options;
if (!websockets) {
websockets = networkBridge.websocketsLookup(this.url);
}
if (typeof options !== 'object' || arguments.length > 3) {
data = Array.prototype.slice.call(arguments, 1, arguments.length);
data = data.map(item => normalizeSendData(item));
} else {
data = normalizeSendData(data);
}
websockets.forEach(socket => {
if (Array.isArray(data)) {
socket.dispatchEvent(
createMessageEvent({
type: event,
data,
origin: this.url,
target: socket
}),
...data
);
} else {
socket.dispatchEvent(
createMessageEvent({
type: event,
data,
origin: this.url,
target: socket
})
);
}
});
}
/*
* Returns an array of websockets which are listening to this server
* TOOD: this should return a set and not be a method
*/
clients() {
return networkBridge.websocketsLookup(this.url);
}
/*
* Prepares a method to submit an event to members of the room
*
* e.g. server.to('my-room').emit('hi!');
*/
to(room, broadcaster, broadcastList = []) {
const self = this;
const websockets = dedupe(broadcastList.concat(networkBridge.websocketsLookup(this.url, room, broadcaster)));
return {
to: (chainedRoom, chainedBroadcaster) => this.to.call(this, chainedRoom, chainedBroadcaster, websockets),
emit(event, data) {
self.emit(event, data, { websockets });
}
};
}
/*
* Alias for Server.to
*/
in(...args) {
return this.to.apply(null, args);
}
/*
* Simulate an event from the server to the clients. Useful for
* simulating errors.
*/
simulate(event) {
const listeners = networkBridge.websocketsLookup(this.url);
if (event === 'error') {
listeners.forEach(socket => {
socket.readyState = WebSocket.CLOSE;
socket.dispatchEvent(createEvent({ type: 'error' }));
});
}
}
}
/*
* Alternative constructor to support namespaces in socket.io
*
* http://socket.io/docs/rooms-and-namespaces/#custom-namespaces
*/
Server.of = function of(url) {
return new Server(url);
};
export default Server;

279
node_modules/mock-socket/src/socket-io.js generated vendored Normal file
View File

@ -0,0 +1,279 @@
import URL from 'url-parse';
import delay from './helpers/delay';
import EventTarget from './event/target';
import networkBridge from './network-bridge';
import { CLOSE_CODES } from './constants';
import logger from './helpers/logger';
import { createEvent, createMessageEvent, createCloseEvent } from './event/factory';
/*
* The socket-io class is designed to mimick the real API as closely as possible.
*
* http://socket.io/docs/
*/
class SocketIO extends EventTarget {
/*
* @param {string} url
*/
constructor(url = 'socket.io', protocol = '') {
super();
this.binaryType = 'blob';
const urlRecord = new URL(url);
if (!urlRecord.pathname) {
urlRecord.pathname = '/';
}
this.url = urlRecord.toString();
this.readyState = SocketIO.CONNECTING;
this.protocol = '';
if (typeof protocol === 'string' || (typeof protocol === 'object' && protocol !== null)) {
this.protocol = protocol;
} else if (Array.isArray(protocol) && protocol.length > 0) {
this.protocol = protocol[0];
}
const server = networkBridge.attachWebSocket(this, this.url);
/*
* Delay triggering the connection events so they can be defined in time.
*/
delay(function delayCallback() {
if (server) {
this.readyState = SocketIO.OPEN;
server.dispatchEvent(createEvent({ type: 'connection' }), server, this);
server.dispatchEvent(createEvent({ type: 'connect' }), server, this); // alias
this.dispatchEvent(createEvent({ type: 'connect', target: this }));
} else {
this.readyState = SocketIO.CLOSED;
this.dispatchEvent(createEvent({ type: 'error', target: this }));
this.dispatchEvent(
createCloseEvent({
type: 'close',
target: this,
code: CLOSE_CODES.CLOSE_NORMAL
})
);
logger('error', `Socket.io connection to '${this.url}' failed`);
}
}, this);
/**
Add an aliased event listener for close / disconnect
*/
this.addEventListener('close', event => {
this.dispatchEvent(
createCloseEvent({
type: 'disconnect',
target: event.target,
code: event.code
})
);
});
}
/*
* Closes the SocketIO connection or connection attempt, if any.
* If the connection is already CLOSED, this method does nothing.
*/
close() {
if (this.readyState !== SocketIO.OPEN) {
return undefined;
}
const server = networkBridge.serverLookup(this.url);
networkBridge.removeWebSocket(this, this.url);
this.readyState = SocketIO.CLOSED;
this.dispatchEvent(
createCloseEvent({
type: 'close',
target: this,
code: CLOSE_CODES.CLOSE_NORMAL
})
);
if (server) {
server.dispatchEvent(
createCloseEvent({
type: 'disconnect',
target: this,
code: CLOSE_CODES.CLOSE_NORMAL
}),
server
);
}
return this;
}
/*
* Alias for Socket#close
*
* https://github.com/socketio/socket.io-client/blob/master/lib/socket.js#L383
*/
disconnect() {
return this.close();
}
/*
* Submits an event to the server with a payload
*/
emit(event, ...data) {
if (this.readyState !== SocketIO.OPEN) {
throw new Error('SocketIO is already in CLOSING or CLOSED state');
}
const messageEvent = createMessageEvent({
type: event,
origin: this.url,
data
});
const server = networkBridge.serverLookup(this.url);
if (server) {
server.dispatchEvent(messageEvent, ...data);
}
return this;
}
/*
* Submits a 'message' event to the server.
*
* Should behave exactly like WebSocket#send
*
* https://github.com/socketio/socket.io-client/blob/master/lib/socket.js#L113
*/
send(data) {
this.emit('message', data);
return this;
}
/*
* For broadcasting events to other connected sockets.
*
* e.g. socket.broadcast.emit('hi!');
* e.g. socket.broadcast.to('my-room').emit('hi!');
*/
get broadcast() {
if (this.readyState !== SocketIO.OPEN) {
throw new Error('SocketIO is already in CLOSING or CLOSED state');
}
const self = this;
const server = networkBridge.serverLookup(this.url);
if (!server) {
throw new Error(`SocketIO can not find a server at the specified URL (${this.url})`);
}
return {
emit(event, data) {
server.emit(event, data, { websockets: networkBridge.websocketsLookup(self.url, null, self) });
return self;
},
to(room) {
return server.to(room, self);
},
in(room) {
return server.in(room, self);
}
};
}
/*
* For registering events to be received from the server
*/
on(type, callback) {
this.addEventListener(type, callback);
return this;
}
/*
* Remove event listener
*
* https://socket.io/docs/client-api/#socket-on-eventname-callback
*/
off(type) {
this.removeEventListener(type);
}
/*
* Join a room on a server
*
* http://socket.io/docs/rooms-and-namespaces/#joining-and-leaving
*/
join(room) {
networkBridge.addMembershipToRoom(this, room);
}
/*
* Get the websocket to leave the room
*
* http://socket.io/docs/rooms-and-namespaces/#joining-and-leaving
*/
leave(room) {
networkBridge.removeMembershipFromRoom(this, room);
}
to(room) {
return this.broadcast.to(room);
}
in() {
return this.to.apply(null, arguments);
}
/*
* Invokes all listener functions that are listening to the given event.type property. Each
* listener will be passed the event as the first argument.
*
* @param {object} event - event object which will be passed to all listeners of the event.type property
*/
dispatchEvent(event, ...customArguments) {
const eventName = event.type;
const listeners = this.listeners[eventName];
if (!Array.isArray(listeners)) {
return false;
}
listeners.forEach(listener => {
if (customArguments.length > 0) {
listener.apply(this, customArguments);
} else {
// Regular WebSockets expect a MessageEvent but Socketio.io just wants raw data
// payload instanceof MessageEvent works, but you can't isntance of NodeEvent
// for now we detect if the output has data defined on it
listener.call(this, event.data ? event.data : event);
}
});
}
}
SocketIO.CONNECTING = 0;
SocketIO.OPEN = 1;
SocketIO.CLOSING = 2;
SocketIO.CLOSED = 3;
/*
* Static constructor methods for the IO Socket
*/
const IO = function ioConstructor(url, protocol) {
return new SocketIO(url, protocol);
};
/*
* Alias the raw IO() constructor
*/
IO.connect = function ioConnect(url, protocol) {
/* eslint-disable new-cap */
return IO(url, protocol);
/* eslint-enable new-cap */
};
export default IO;

191
node_modules/mock-socket/src/websocket.js generated vendored Normal file
View File

@ -0,0 +1,191 @@
import delay from './helpers/delay';
import logger from './helpers/logger';
import EventTarget from './event/target';
import networkBridge from './network-bridge';
import proxyFactory from './helpers/proxy-factory';
import lengthInUtf8Bytes from './helpers/byte-length';
import { CLOSE_CODES, ERROR_PREFIX } from './constants';
import urlVerification from './helpers/url-verification';
import normalizeSendData from './helpers/normalize-send';
import protocolVerification from './helpers/protocol-verification';
import { createEvent, createMessageEvent, createCloseEvent } from './event/factory';
import { closeWebSocketConnection, failWebSocketConnection } from './algorithms/close';
/*
* The main websocket class which is designed to mimick the native WebSocket class as close
* as possible.
*
* https://html.spec.whatwg.org/multipage/web-sockets.html
*/
class WebSocket extends EventTarget {
constructor(url, protocols) {
super();
this.url = urlVerification(url);
protocols = protocolVerification(protocols);
this.protocol = protocols[0] || '';
this.binaryType = 'blob';
this.readyState = WebSocket.CONNECTING;
const server = networkBridge.attachWebSocket(this, this.url);
/*
* This delay is needed so that we dont trigger an event before the callbacks have been
* setup. For example:
*
* var socket = new WebSocket('ws://localhost');
*
* If we dont have the delay then the event would be triggered right here and this is
* before the onopen had a chance to register itself.
*
* socket.onopen = () => { // this would never be called };
*
* and with the delay the event gets triggered here after all of the callbacks have been
* registered :-)
*/
delay(function delayCallback() {
if (server) {
if (
server.options.verifyClient &&
typeof server.options.verifyClient === 'function' &&
!server.options.verifyClient()
) {
this.readyState = WebSocket.CLOSED;
logger(
'error',
`WebSocket connection to '${this.url}' failed: HTTP Authentication failed; no valid credentials available`
);
networkBridge.removeWebSocket(this, this.url);
this.dispatchEvent(createEvent({ type: 'error', target: this }));
this.dispatchEvent(createCloseEvent({ type: 'close', target: this, code: CLOSE_CODES.CLOSE_NORMAL }));
} else {
if (server.options.selectProtocol && typeof server.options.selectProtocol === 'function') {
const selectedProtocol = server.options.selectProtocol(protocols);
const isFilled = selectedProtocol !== '';
const isRequested = protocols.indexOf(selectedProtocol) !== -1;
if (isFilled && !isRequested) {
this.readyState = WebSocket.CLOSED;
logger('error', `WebSocket connection to '${this.url}' failed: Invalid Sub-Protocol`);
networkBridge.removeWebSocket(this, this.url);
this.dispatchEvent(createEvent({ type: 'error', target: this }));
this.dispatchEvent(createCloseEvent({ type: 'close', target: this, code: CLOSE_CODES.CLOSE_NORMAL }));
return;
}
this.protocol = selectedProtocol;
}
this.readyState = WebSocket.OPEN;
this.dispatchEvent(createEvent({ type: 'open', target: this }));
server.dispatchEvent(createEvent({ type: 'connection' }), proxyFactory(this));
}
} else {
this.readyState = WebSocket.CLOSED;
this.dispatchEvent(createEvent({ type: 'error', target: this }));
this.dispatchEvent(createCloseEvent({ type: 'close', target: this, code: CLOSE_CODES.CLOSE_NORMAL }));
logger('error', `WebSocket connection to '${this.url}' failed`);
}
}, this);
}
get onopen() {
return this.listeners.open;
}
get onmessage() {
return this.listeners.message;
}
get onclose() {
return this.listeners.close;
}
get onerror() {
return this.listeners.error;
}
set onopen(listener) {
delete this.listeners.open;
this.addEventListener('open', listener);
}
set onmessage(listener) {
delete this.listeners.message;
this.addEventListener('message', listener);
}
set onclose(listener) {
delete this.listeners.close;
this.addEventListener('close', listener);
}
set onerror(listener) {
delete this.listeners.error;
this.addEventListener('error', listener);
}
send(data) {
if (this.readyState === WebSocket.CLOSING || this.readyState === WebSocket.CLOSED) {
throw new Error('WebSocket is already in CLOSING or CLOSED state');
}
// TODO: handle bufferedAmount
const messageEvent = createMessageEvent({
type: 'server::message',
origin: this.url,
data: normalizeSendData(data)
});
const server = networkBridge.serverLookup(this.url);
if (server) {
delay(() => {
this.dispatchEvent(messageEvent, data);
}, server);
}
}
close(code, reason) {
if (code !== undefined) {
if (typeof code !== 'number' || (code !== 1000 && (code < 3000 || code > 4999))) {
throw new TypeError(
`${ERROR_PREFIX.CLOSE_ERROR} The code must be either 1000, or between 3000 and 4999. ${code} is neither.`
);
}
}
if (reason !== undefined) {
const length = lengthInUtf8Bytes(reason);
if (length > 123) {
throw new SyntaxError(`${ERROR_PREFIX.CLOSE_ERROR} The message must not be greater than 123 bytes.`);
}
}
if (this.readyState === WebSocket.CLOSING || this.readyState === WebSocket.CLOSED) {
return;
}
if (this.readyState === WebSocket.CONNECTING) {
failWebSocketConnection(this, code, reason);
} else {
closeWebSocketConnection(this, code, reason);
}
}
}
WebSocket.CONNECTING = 0;
WebSocket.prototype.CONNECTING = WebSocket.CONNECTING;
WebSocket.OPEN = 1;
WebSocket.prototype.OPEN = WebSocket.OPEN;
WebSocket.CLOSING = 2;
WebSocket.prototype.CLOSING = WebSocket.CLOSING;
WebSocket.CLOSED = 3;
WebSocket.prototype.CLOSED = WebSocket.CLOSED;
export default WebSocket;