From 967f58f31770848413a092a3a6b1f62a14145ac5 Mon Sep 17 00:00:00 2001 From: sharkykh Date: Wed, 12 Sep 2018 19:13:09 +0300 Subject: [PATCH] Fix custom mutations feature + test --- README.md | 68 +++++++++++++++++++++++--------- dist/build.js | 2 +- src/Observer.js | 5 +-- test/unit/specs/Observer.spec.js | 34 +++++++++++++++- 4 files changed, 85 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index aa64cbd..05c7bac 100755 --- a/README.md +++ b/README.md @@ -175,9 +175,34 @@ export default new Vuex.Store({ ##### With custom mutation names ``` js +// mutation-types.js +const SOCKET_ONOPEN = '✅ Socket connected!' +const SOCKET_ONCLOSE = '❌ Socket disconnected!' +const SOCKET_ONERROR = '❌ Socket Error!!!' +const SOCKET_ONMESSAGE = 'Websocket message received' +const SOCKET_RECONNECT = 'Websocket reconnected' +const SOCKET_RECONNECT_ERROR = 'Websocket is having issues reconnecting..' + +export { + SOCKET_ONOPEN, + SOCKET_ONCLOSE, + SOCKET_ONERROR, + SOCKET_ONMESSAGE, + SOCKET_RECONNECT, + SOCKET_RECONNECT_ERROR +} + // store.js import Vue from 'vue' import Vuex from 'vuex' +import { + SOCKET_ONOPEN, + SOCKET_ONCLOSE, + SOCKET_ONERROR, + SOCKET_ONMESSAGE, + SOCKET_RECONNECT, + SOCKET_RECONNECT_ERROR +} from './mutation-types' Vue.use(Vuex); @@ -189,46 +214,53 @@ export default new Vuex.Store({ reconnectError: false, } }, - mutations:{ - SOCKET_ONOPEN (state, event) { + mutations: { + [SOCKET_ONOPEN](state, event) { state.socket.isConnected = true }, - SOCKET_ONCLOSE (state, event) { + [SOCKET_ONCLOSE](state, event) { state.socket.isConnected = false }, - SOCKET_ONERROR (state, event) { + [SOCKET_ONERROR](state, event) { console.error(state, event) }, // default handler called for all methods - SOCKET_ONMESSAGE (state, message) { + [SOCKET_ONMESSAGE](state, message) { state.socket.message = message }, // mutations for reconnect methods - SOCKET_RECONNECT(state, count) { + [SOCKET_RECONNECT](state, count) { console.info(state, count) }, - SOCKET_RECONNECT_ERROR(state) { + [SOCKET_RECONNECT_ERROR](state) { state.socket.reconnectError = true; - }, + } } }) // index.js import store from './store' +import { + SOCKET_ONOPEN, + SOCKET_ONCLOSE, + SOCKET_ONERROR, + SOCKET_ONMESSAGE, + SOCKET_RECONNECT, + SOCKET_RECONNECT_ERROR +} from './mutation-types' const mutations = { - SOCKET_ONOPEN: '✅ Socket connected!', - SOCKET_ONCLOSE: '❌ Socket disconnected!', - SOCKET_ONERROR: '❌ Socket Error!!!', - SOCKET_ONMESSAGE: 'Websocket message received', - SOCKET_RECONNECT: 'Websocket reconnected', - SOCKET_RECONNECT_ERROR: 'Websocket is having issues reconnecting..' -}; + SOCKET_ONOPEN, + SOCKET_ONCLOSE, + SOCKET_ONERROR, + SOCKET_ONMESSAGE, + SOCKET_RECONNECT, + SOCKET_RECONNECT_ERROR +} Vue.use(VueNativeSock, 'ws://localhost:9090', { - store: store, - format: 'json', - mutations: mutations + store: store, + mutations: mutations }) ``` diff --git a/dist/build.js b/dist/build.js index b096664..cb622c5 100755 --- a/dist/build.js +++ b/dist/build.js @@ -1 +1 @@ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.VueNativeSock=e():t.VueNativeSock=e()}("undefined"!=typeof self?self:this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=1)}([function(t,e,n){"use strict";function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){for(var n=0;n-1)&&(o.splice(r,1),this.listeners.set(t,o),!0)}},{key:"emit",value:function(t){for(var e=arguments.length,n=Array(e>1?e-1:0),o=1;o2&&void 0!==arguments[2]?arguments[2]:{};if(!e)throw new Error("[vue-native-socket] cannot locate connection");var o=null;n.$setInstance=function(e){t.prototype.$socket=e},n.connectManually?(t.prototype.$connect=function(){o=new i.default(e,n),t.prototype.$socket=o.WebSocket},t.prototype.$disconnect=function(){o&&o.reconnection&&(o.reconnection=!1),t.prototype.$socket&&(t.prototype.$socket.close(),delete t.prototype.$socket)}):(o=new i.default(e,n),t.prototype.$socket=o.WebSocket);var r="undefined"!=typeof Proxy&&"function"==typeof Proxy&&/native code/.test(Proxy.toString());t.mixin({created:function(){var t=this,e=this,n=this.$options.sockets;r?(this.$options.sockets=new Proxy({},{set:function(t,n,o){return c.default.addListener(n,o,e),t[n]=o,!0},deleteProperty:function(t,n){return c.default.removeListener(n,e.$options.sockets[n],e),delete t.key,!0}}),n&&Object.keys(n).forEach(function(e){t.$options.sockets[e]=n[e]})):Object.seal(this.$options.sockets)},beforeDestroy:function(){var t=this;if(r){var e=this.$options.sockets;e&&Object.keys(e).forEach(function(e){delete t.$options.sockets[e]})}}})}}},function(t,e,n){"use strict";function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{};if(o(this,t),this.format=n.format&&n.format.toLowerCase(),e.startsWith("//")){e=("https:"===window.location.protocol?"wss":"ws")+"://"+e}this.connectionUrl=e,this.opts=n,this.reconnection=this.opts.reconnection||!1,this.reconnectionAttempts=this.opts.reconnectionAttempts||1/0,this.reconnectionDelay=this.opts.reconnectionDelay||1e3,this.reconnectTimeoutId=0,this.reconnectionCount=0,this.passToStoreHandler=this.opts.passToStoreHandler||!1,this.connect(e,n),n.store&&(this.store=n.store),n.mutations&&(this.mutations=n.mutations),this.onEvent()}return r(t,[{key:"connect",value:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=n.protocol||"";return this.WebSocket=n.WebSocket||(""===o?new WebSocket(t):new WebSocket(t,o)),"json"===this.format&&("sendObj"in this.WebSocket||(this.WebSocket.sendObj=function(t){return e.WebSocket.send(JSON.stringify(t))})),this.WebSocket}},{key:"reconnect",value:function(){var t=this;this.reconnectionCount<=this.reconnectionAttempts?(this.reconnectionCount++,clearTimeout(this.reconnectTimeoutId),this.reconnectTimeoutId=setTimeout(function(){t.store&&t.passToStore("SOCKET_RECONNECT",t.reconnectionCount),t.connect(t.connectionUrl,t.opts),t.onEvent()},this.reconnectionDelay)):this.store&&this.passToStore("SOCKET_RECONNECT_ERROR",!0)}},{key:"onEvent",value:function(){var t=this;["onmessage","onclose","onerror","onopen"].forEach(function(e){t.WebSocket[e]=function(n){s.default.emit(e,n),t.store&&t.passToStore("SOCKET_"+e,n),t.reconnection&&"onopen"===e&&(t.opts.$setInstance(n.currentTarget),t.reconnectionCount=0),t.reconnection&&"onclose"===e&&t.reconnect()}})}},{key:"passToStore",value:function(t,e){this.passToStoreHandler?this.passToStoreHandler(t,e,this.defaultPassToStore.bind(this)):this.defaultPassToStore(t,e)}},{key:"defaultPassToStore",value:function(t,e){if(t.startsWith("SOCKET_")){var n="commit",o=t.toUpperCase(),r=e;"json"===this.format&&e.data&&(r=JSON.parse(e.data),r.mutation?o=[r.namespace||"",r.mutation].filter(function(t){return!!t}).join("/"):r.action&&(n="dispatch",o=[r.namespace||"",r.action].filter(function(t){return!!t}).join("/"))),this.mutations?this.store[this.mutations[n]||n](o,r):this.store[n](o,r)}}}]),t}();e.default=c}])}); \ No newline at end of file +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.VueNativeSock=e():t.VueNativeSock=e()}("undefined"!=typeof self?self:this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=1)}([function(t,e,n){"use strict";function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){for(var n=0;n-1)&&(o.splice(r,1),this.listeners.set(t,o),!0)}},{key:"emit",value:function(t){for(var e=arguments.length,n=Array(e>1?e-1:0),o=1;o2&&void 0!==arguments[2]?arguments[2]:{};if(!e)throw new Error("[vue-native-socket] cannot locate connection");var o=null;n.$setInstance=function(e){t.prototype.$socket=e},n.connectManually?(t.prototype.$connect=function(){o=new i.default(e,n),t.prototype.$socket=o.WebSocket},t.prototype.$disconnect=function(){o&&o.reconnection&&(o.reconnection=!1),t.prototype.$socket&&(t.prototype.$socket.close(),delete t.prototype.$socket)}):(o=new i.default(e,n),t.prototype.$socket=o.WebSocket);var r="undefined"!=typeof Proxy&&"function"==typeof Proxy&&/native code/.test(Proxy.toString());t.mixin({created:function(){var t=this,e=this,n=this.$options.sockets;r?(this.$options.sockets=new Proxy({},{set:function(t,n,o){return c.default.addListener(n,o,e),t[n]=o,!0},deleteProperty:function(t,n){return c.default.removeListener(n,e.$options.sockets[n],e),delete t.key,!0}}),n&&Object.keys(n).forEach(function(e){t.$options.sockets[e]=n[e]})):Object.seal(this.$options.sockets)},beforeDestroy:function(){var t=this;if(r){var e=this.$options.sockets;e&&Object.keys(e).forEach(function(e){delete t.$options.sockets[e]})}}})}}},function(t,e,n){"use strict";function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{};if(o(this,t),this.format=n.format&&n.format.toLowerCase(),e.startsWith("//")){e=("https:"===window.location.protocol?"wss":"ws")+"://"+e}this.connectionUrl=e,this.opts=n,this.reconnection=this.opts.reconnection||!1,this.reconnectionAttempts=this.opts.reconnectionAttempts||1/0,this.reconnectionDelay=this.opts.reconnectionDelay||1e3,this.reconnectTimeoutId=0,this.reconnectionCount=0,this.passToStoreHandler=this.opts.passToStoreHandler||!1,this.connect(e,n),n.store&&(this.store=n.store),n.mutations&&(this.mutations=n.mutations),this.onEvent()}return r(t,[{key:"connect",value:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=n.protocol||"";return this.WebSocket=n.WebSocket||(""===o?new WebSocket(t):new WebSocket(t,o)),"json"===this.format&&("sendObj"in this.WebSocket||(this.WebSocket.sendObj=function(t){return e.WebSocket.send(JSON.stringify(t))})),this.WebSocket}},{key:"reconnect",value:function(){var t=this;this.reconnectionCount<=this.reconnectionAttempts?(this.reconnectionCount++,clearTimeout(this.reconnectTimeoutId),this.reconnectTimeoutId=setTimeout(function(){t.store&&t.passToStore("SOCKET_RECONNECT",t.reconnectionCount),t.connect(t.connectionUrl,t.opts),t.onEvent()},this.reconnectionDelay)):this.store&&this.passToStore("SOCKET_RECONNECT_ERROR",!0)}},{key:"onEvent",value:function(){var t=this;["onmessage","onclose","onerror","onopen"].forEach(function(e){t.WebSocket[e]=function(n){s.default.emit(e,n),t.store&&t.passToStore("SOCKET_"+e,n),t.reconnection&&"onopen"===e&&(t.opts.$setInstance(n.currentTarget),t.reconnectionCount=0),t.reconnection&&"onclose"===e&&t.reconnect()}})}},{key:"passToStore",value:function(t,e){this.passToStoreHandler?this.passToStoreHandler(t,e,this.defaultPassToStore.bind(this)):this.defaultPassToStore(t,e)}},{key:"defaultPassToStore",value:function(t,e){if(t.startsWith("SOCKET_")){var n="commit",o=t.toUpperCase(),r=e;"json"===this.format&&e.data&&(r=JSON.parse(e.data),r.mutation?o=[r.namespace||"",r.mutation].filter(function(t){return!!t}).join("/"):r.action&&(n="dispatch",o=[r.namespace||"",r.action].filter(function(t){return!!t}).join("/"))),this.mutations&&(o=this.mutations[o]||o),this.store[n](o,r)}}}]),t}();e.default=c}])}); \ No newline at end of file diff --git a/src/Observer.js b/src/Observer.js index ecb0ab5..48949a5 100755 --- a/src/Observer.js +++ b/src/Observer.js @@ -95,9 +95,8 @@ export default class { } } if (this.mutations) { - this.store[this.mutations[method] || method](target, msg) - } else { - this.store[method](target, msg) + target = this.mutations[target] || target } + this.store[method](target, msg) } } diff --git a/test/unit/specs/Observer.spec.js b/test/unit/specs/Observer.spec.js index afc8284..66e89ba 100644 --- a/test/unit/specs/Observer.spec.js +++ b/test/unit/specs/Observer.spec.js @@ -63,7 +63,7 @@ describe('Observer.js', () => { }, 100) }) - // TODO: DRY + // TODO: DRY it('passes a json action to the provided vuex store', (done) => { let expectedMsg = { action: 'setName', value: 'steve' } let mockStore = sinon.mock({ @@ -117,7 +117,7 @@ describe('Observer.js', () => { }, 100) }) - // TODO: DRY + // TODO: DRY it('passes a namespaced json action to the provided vuex store', (done) => { let expectedMsg = { namespace: 'users', action: 'setName', value: 'steve' } let mockStore = sinon.mock({ @@ -145,6 +145,36 @@ describe('Observer.js', () => { }, 100) }) + // TODO: DRY + it('passes a custom commit name to the provided vuex store', (done) => { + let expectedMsg = 'hello world' + let mutations = { + SOCKET_ONOPEN: '✅ Socket connected', + SOCKET_ONMESSAGE: 'Websocket message received' + } + let mockStore = sinon.mock({ commit: () => {} }) + mockStore.expects('commit').withArgs(mutations.SOCKET_ONOPEN) + mockStore.expects('commit').withArgs(mutations.SOCKET_ONMESSAGE) + + mockServer = new Server(wsUrl) + mockServer.on('connection', ws => { + ws.send(expectedMsg) + }) + + Vue.use(VueNativeSock, wsUrl) + let vm = new Vue() + observer = new Observer(wsUrl, { + store: mockStore.object, + mutations, + websocket: new WebSocket(wsUrl) + }) + + setTimeout(() => { + mockStore.verify() + mockServer.stop(done) + }, 100) + }) + describe('reconnection feature', () => { let observer, mockServer, vm, mockStore let wsUrl = 'ws://localhost:8080'