UNPKG

@reactivex/rxjs

Version:

Reactive Extensions for modern JavaScript

529 lines 22.1 kB
"use strict"; var chai_1 = require('chai'); var sinon = require('sinon'); var Rx = require('../../../dist/cjs/Rx'); var Observable = Rx.Observable; /** @test {webSocket} */ describe('Observable.webSocket', function () { var __ws; function setupMockWebSocket() { MockWebSocket.clearSockets(); __ws = __root__.WebSocket; __root__.WebSocket = MockWebSocket; } function teardownMockWebSocket() { __root__.WebSocket = __ws; MockWebSocket.clearSockets(); } beforeEach(function () { setupMockWebSocket(); }); afterEach(function () { teardownMockWebSocket(); }); it('should send and receive messages', function () { var messageReceived = false; var subject = Observable.webSocket('ws://mysocket'); subject.next('ping'); subject.subscribe(function (x) { chai_1.expect(x).to.equal('pong'); messageReceived = true; }); var socket = MockWebSocket.lastSocket; chai_1.expect(socket.url).to.equal('ws://mysocket'); socket.open(); chai_1.expect(socket.lastMessageSent).to.equal('ping'); socket.triggerMessage(JSON.stringify('pong')); chai_1.expect(messageReceived).to.be.true; subject.unsubscribe(); }); it('should allow the user to chain operators', function () { var messageReceived = false; var subject = Observable.webSocket('ws://mysocket'); subject .map(function (x) { return x + '?'; }) .map(function (x) { return x + '!'; }) .map(function (x) { return x + '!'; }) .subscribe(function (x) { chai_1.expect(x).to.equal('pong?!!'); messageReceived = true; }); var socket = MockWebSocket.lastSocket; socket.open(); socket.triggerMessage(JSON.stringify('pong')); chai_1.expect(messageReceived).to.be.true; subject.unsubscribe(); }); it('receive multiple messages', function () { var expected = ['what', 'do', 'you', 'do', 'with', 'a', 'drunken', 'sailor?']; var results = []; var subject = Observable.webSocket('ws://mysocket'); subject.subscribe(function (x) { results.push(x); }); var socket = MockWebSocket.lastSocket; socket.open(); expected.forEach(function (x) { socket.triggerMessage(JSON.stringify(x)); }); chai_1.expect(results).to.deep.equal(expected); subject.unsubscribe(); }); it('should queue messages prior to subscription', function () { var expected = ['make', 'him', 'walk', 'the', 'plank']; var subject = Observable.webSocket('ws://mysocket'); expected.forEach(function (x) { subject.next(x); }); var socket = MockWebSocket.lastSocket; chai_1.expect(socket).not.exist; subject.subscribe(); socket = MockWebSocket.lastSocket; chai_1.expect(socket.sent.length).to.equal(0); socket.open(); chai_1.expect(socket.sent.length).to.equal(expected.length); subject.unsubscribe(); }); it('should send messages immediately if already open', function () { var subject = Observable.webSocket('ws://mysocket'); subject.subscribe(); var socket = MockWebSocket.lastSocket; socket.open(); subject.next('avast!'); chai_1.expect(socket.lastMessageSent).to.equal('avast!'); subject.next('ye swab!'); chai_1.expect(socket.lastMessageSent).to.equal('ye swab!'); subject.unsubscribe(); }); it('should close the socket when completed', function () { var subject = Observable.webSocket('ws://mysocket'); subject.subscribe(); var socket = MockWebSocket.lastSocket; socket.open(); chai_1.expect(socket.readyState).to.equal(1); // open sinon.spy(socket, 'close'); chai_1.expect(socket.close).not.have.been.called; subject.complete(); chai_1.expect(socket.close).have.been.called; chai_1.expect(socket.readyState).to.equal(3); // closed subject.unsubscribe(); socket.close.restore(); }); it('should close the socket with a code and a reason when errored', function () { var subject = Observable.webSocket('ws://mysocket'); subject.subscribe(); var socket = MockWebSocket.lastSocket; socket.open(); sinon.spy(socket, 'close'); chai_1.expect(socket.close).not.have.been.called; subject.error({ code: 1337, reason: 'Too bad, so sad :(' }); chai_1.expect(socket.close).have.been.calledWith(1337, 'Too bad, so sad :('); subject.unsubscribe(); socket.close.restore(); }); it('should allow resubscription after closure via complete', function () { var subject = Observable.webSocket('ws://mysocket'); subject.subscribe(); var socket1 = MockWebSocket.lastSocket; socket1.open(); subject.complete(); subject.next('a mariner yer not. yarrr.'); subject.subscribe(); var socket2 = MockWebSocket.lastSocket; socket2.open(); chai_1.expect(socket2).not.to.equal(socket1); chai_1.expect(socket2.lastMessageSent).to.equal('a mariner yer not. yarrr.'); subject.unsubscribe(); }); it('should allow resubscription after closure via error', function () { var subject = Observable.webSocket('ws://mysocket'); subject.subscribe(); var socket1 = MockWebSocket.lastSocket; socket1.open(); subject.error({ code: 1337 }); subject.next('yo-ho! yo-ho!'); subject.subscribe(); var socket2 = MockWebSocket.lastSocket; socket2.open(); chai_1.expect(socket2).not.to.equal(socket1); chai_1.expect(socket2.lastMessageSent).to.equal('yo-ho! yo-ho!'); subject.unsubscribe(); }); it('should have a default resultSelector that parses message data as JSON', function () { var result; var expected = { mork: 'shazbot!' }; var subject = Observable.webSocket('ws://mysocket'); subject.subscribe(function (x) { result = x; }); var socket = MockWebSocket.lastSocket; socket.open(); socket.triggerMessage(JSON.stringify(expected)); chai_1.expect(result).to.deep.equal(expected); subject.unsubscribe(); }); describe('with a config object', function () { it('should send and receive messages', function () { var messageReceived = false; var subject = Observable.webSocket({ url: 'ws://mysocket' }); subject.next('ping'); subject.subscribe(function (x) { chai_1.expect(x).to.equal('pong'); messageReceived = true; }); var socket = MockWebSocket.lastSocket; chai_1.expect(socket.url).to.equal('ws://mysocket'); socket.open(); chai_1.expect(socket.lastMessageSent).to.equal('ping'); socket.triggerMessage(JSON.stringify('pong')); chai_1.expect(messageReceived).to.be.true; subject.unsubscribe(); }); it('should take a protocol and set it properly on the web socket', function () { var subject = Observable.webSocket({ url: 'ws://mysocket', protocol: 'someprotocol' }); subject.subscribe(); var socket = MockWebSocket.lastSocket; chai_1.expect(socket.protocol).to.equal('someprotocol'); subject.unsubscribe(); }); it('should take a resultSelector', function () { var results = []; var subject = Observable.webSocket({ url: 'ws://mysocket', resultSelector: function (e) { return e.data + '!'; } }); subject.subscribe(function (x) { results.push(x); }); var socket = MockWebSocket.lastSocket; socket.open(); ['ahoy', 'yarr', 'shove off'].forEach(function (x) { socket.triggerMessage(x); }); chai_1.expect(results).to.deep.equal(['ahoy!', 'yarr!', 'shove off!']); subject.unsubscribe(); }); it('if the resultSelector fails it should go down the error path', function () { var subject = Observable.webSocket({ url: 'ws://mysocket', resultSelector: function (e) { throw new Error('I am a bad error'); } }); subject.subscribe(function (x) { chai_1.expect(x).to.equal('this should not happen'); }, function (err) { chai_1.expect(err).to.be.an('error', 'I am a bad error'); }); var socket = MockWebSocket.lastSocket; socket.open(); socket.triggerMessage('weee!'); subject.unsubscribe(); }); it('should accept a closingObserver', function () { var calls = 0; var subject = Observable.webSocket({ url: 'ws://mysocket', closingObserver: { next: function (x) { calls++; chai_1.expect(x).to.be.an('undefined'); } } }); subject.subscribe(); var socket = MockWebSocket.lastSocket; socket.open(); chai_1.expect(calls).to.equal(0); subject.complete(); chai_1.expect(calls).to.equal(1); subject.subscribe(); socket = MockWebSocket.lastSocket; socket.open(); subject.error({ code: 1337 }); chai_1.expect(calls).to.equal(2); subject.unsubscribe(); }); it('should accept a closeObserver', function () { var expected = [{ wasClean: true }, { wasClean: false }]; var closes = []; var subject = Observable.webSocket({ url: 'ws://mysocket', closeObserver: { next: function (e) { closes.push(e); } } }); subject.subscribe(); var socket = MockWebSocket.lastSocket; socket.open(); chai_1.expect(closes.length).to.equal(0); socket.triggerClose(expected[0]); chai_1.expect(closes.length).to.equal(1); subject.subscribe(null, function (err) { chai_1.expect(err).to.equal(expected[1]); }); socket = MockWebSocket.lastSocket; socket.open(); socket.triggerClose(expected[1]); chai_1.expect(closes.length).to.equal(2); chai_1.expect(closes[0]).to.equal(expected[0]); chai_1.expect(closes[1]).to.equal(expected[1]); subject.unsubscribe(); }); it('should handle constructor errors', function () { var subject = Observable.webSocket({ url: 'bad_url', WebSocketCtor: function (url, protocol) { throw new Error("connection refused"); } }); subject.subscribe(function (x) { chai_1.expect(x).to.equal('this should not happen'); }, function (err) { chai_1.expect(err).to.be.an('error', 'connection refused'); }); subject.unsubscribe(); }); }); describe('multiplex', function () { it('should be retryable', function () { var results = []; var subject = Observable.webSocket('ws://websocket'); var source = subject.multiplex(function () { return { sub: 'foo' }; }, function () { return { unsub: 'foo' }; }, function (value) { return value.name === 'foo'; }); source .retry(1) .map(function (x) { return x.value; }) .take(2) .subscribe(function (x) { results.push(x); }); var socket = MockWebSocket.lastSocket; socket.open(); chai_1.expect(socket.lastMessageSent).to.deep.equal({ sub: 'foo' }); socket.triggerClose({ wasClean: false }); // Bad connection var socket2 = MockWebSocket.lastSocket; chai_1.expect(socket2).not.to.equal(socket); socket2.open(); chai_1.expect(socket2.lastMessageSent).to.deep.equal({ sub: 'foo' }); socket2.triggerMessage(JSON.stringify({ name: 'foo', value: 'test' })); socket2.triggerMessage(JSON.stringify({ name: 'foo', value: 'this' })); chai_1.expect(results).to.deep.equal(['test', 'this']); }); it('should be repeatable', function () { var results = []; var subject = Observable.webSocket('ws://websocket'); var source = subject.multiplex(function () { return { sub: 'foo' }; }, function () { return { unsub: 'foo' }; }, function (value) { return value.name === 'foo'; }); source .repeat(2) .map(function (x) { return x.value; }) .subscribe(function (x) { results.push(x); }); var socket = MockWebSocket.lastSocket; socket.open(); chai_1.expect(socket.lastMessageSent).to.deep.equal({ sub: 'foo' }, 'first multiplexed sub'); socket.triggerMessage(JSON.stringify({ name: 'foo', value: 'test' })); socket.triggerMessage(JSON.stringify({ name: 'foo', value: 'this' })); socket.triggerClose({ wasClean: true }); var socket2 = MockWebSocket.lastSocket; chai_1.expect(socket2).not.to.equal(socket, 'a new socket was not created'); socket2.open(); chai_1.expect(socket2.lastMessageSent).to.deep.equal({ sub: 'foo' }, 'second multiplexed sub'); socket2.triggerMessage(JSON.stringify({ name: 'foo', value: 'test' })); socket2.triggerMessage(JSON.stringify({ name: 'foo', value: 'this' })); socket2.triggerClose({ wasClean: true }); chai_1.expect(results).to.deep.equal(['test', 'this', 'test', 'this'], 'results were not equal'); }); it('should multiplex over the websocket', function () { var results = []; var subject = Observable.webSocket('ws://websocket'); var source = subject.multiplex(function () { return { sub: 'foo' }; }, function () { return { unsub: 'foo' }; }, function (value) { return value.name === 'foo'; }); var sub = source.subscribe(function (x) { results.push(x.value); }); var socket = MockWebSocket.lastSocket; socket.open(); chai_1.expect(socket.lastMessageSent).to.deep.equal({ sub: 'foo' }); [1, 2, 3, 4, 5].map(function (x) { return { name: x % 3 === 0 ? 'bar' : 'foo', value: x }; }).forEach(function (x) { socket.triggerMessage(JSON.stringify(x)); }); chai_1.expect(results).to.deep.equal([1, 2, 4, 5]); sinon.spy(socket, 'close'); sub.unsubscribe(); chai_1.expect(socket.lastMessageSent).to.deep.equal({ unsub: 'foo' }); chai_1.expect(socket.close).have.been.called; socket.close.restore(); }); it('should keep the same socket for multiple multiplex subscriptions', function () { var socketSubject = Rx.Observable.webSocket({ url: 'ws://mysocket' }); var results = []; var socketMessages = [ { id: 'A' }, { id: 'B' }, { id: 'A' }, { id: 'B' }, { id: 'B' }, ]; var sub1 = socketSubject.multiplex(function () { return 'no-op'; }, function () { return results.push('A unsub'); }, function (req) { return req.id === 'A'; }) .takeWhile(function (req) { return !req.complete; }) .subscribe(function () { return results.push('A next'); }, function (e) { return results.push('A error ' + e); }, function () { return results.push('A complete'); }); socketSubject.multiplex(function () { return 'no-op'; }, function () { return results.push('B unsub'); }, function (req) { return req.id === 'B'; }) .subscribe(function () { return results.push('B next'); }, function (e) { return results.push('B error ' + e); }, function () { return results.push('B complete'); }); // Setup socket and send messages var socket = MockWebSocket.lastSocket; socket.open(); socketMessages.forEach(function (msg, i) { if (i === 1) { sub1.unsubscribe(); chai_1.expect(socketSubject.socket).to.equal(socket); } socket.triggerMessage(JSON.stringify(msg)); }); socket.triggerClose({ wasClean: true }); chai_1.expect(results).to.deep.equal([ 'A next', 'A unsub', 'B next', 'B next', 'B next', 'B complete', 'B unsub', ]); }); it('should not close the socket until all subscriptions complete', function () { var socketSubject = Rx.Observable.webSocket({ url: 'ws://mysocket' }); var results = []; var socketMessages = [ { id: 'A' }, { id: 'B' }, { id: 'A', complete: true }, { id: 'B' }, { id: 'B', complete: true }, ]; socketSubject.multiplex(function () { return 'no-op'; }, function () { return results.push('A unsub'); }, function (req) { return req.id === 'A'; }) .takeWhile(function (req) { return !req.complete; }) .subscribe(function () { return results.push('A next'); }, function (e) { return results.push('A error ' + e); }, function () { return results.push('A complete'); }); socketSubject.multiplex(function () { return 'no-op'; }, function () { return results.push('B unsub'); }, function (req) { return req.id === 'B'; }) .takeWhile(function (req) { return !req.complete; }) .subscribe(function () { return results.push('B next'); }, function (e) { return results.push('B error ' + e); }, function () { return results.push('B complete'); }); // Setup socket and send messages var socket = MockWebSocket.lastSocket; socket.open(); socketMessages.forEach(function (msg) { socket.triggerMessage(JSON.stringify(msg)); }); chai_1.expect(results).to.deep.equal([ 'A next', 'B next', 'A complete', 'A unsub', 'B next', 'B complete', 'B unsub', ]); }); }); }); var MockWebSocket = (function () { function MockWebSocket(url, protocol) { this.url = url; this.protocol = protocol; this.sent = []; this.handlers = {}; this.readyState = 0; MockWebSocket.sockets.push(this); } Object.defineProperty(MockWebSocket, "lastSocket", { get: function () { var socket = MockWebSocket.sockets; var length = socket.length; return length > 0 ? socket[length - 1] : undefined; }, enumerable: true, configurable: true }); MockWebSocket.clearSockets = function () { MockWebSocket.sockets.length = 0; }; MockWebSocket.prototype.send = function (data) { this.sent.push(data); }; Object.defineProperty(MockWebSocket.prototype, "lastMessageSent", { get: function () { var sent = this.sent; var length = sent.length; return length > 0 ? sent[length - 1] : undefined; }, enumerable: true, configurable: true }); MockWebSocket.prototype.triggerClose = function (e) { this.readyState = 3; this.trigger('close', e); }; MockWebSocket.prototype.triggerMessage = function (data) { var messageEvent = { data: data, origin: 'mockorigin', ports: undefined, source: __root__, }; this.trigger('message', messageEvent); }; MockWebSocket.prototype.open = function () { this.readyState = 1; this.trigger('open', {}); }; MockWebSocket.prototype.close = function (code, reason) { if (this.readyState < 2) { this.readyState = 2; this.closeCode = code; this.closeReason = reason; this.triggerClose({ wasClean: true }); } }; MockWebSocket.prototype.trigger = function (name, e) { if (this['on' + name]) { this['on' + name](e); } var lookup = this.handlers[name]; if (lookup) { for (var i = 0; i < lookup.length; i++) { lookup[i](e); } } }; MockWebSocket.sockets = []; return MockWebSocket; }()); //# sourceMappingURL=webSocket-spec.js.map