From cbf927255a0c758f2bc51eb2ec1adee9c3ac774a Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Fri, 3 Jul 2026 12:00:12 +0200 Subject: [PATCH] test(wsMixin): copri cleanup, riconnessione e guardie residue Aggiunge test per beforeUnmount con connessione OPEN e con ws.close che lancia, l'annullamento del reconnectTimeout allo smontaggio, la guardia isConnecting, la chiusura della WebSocket precedente prima di riconnettersi (incluso il caso in cui close lancia), gli errori di registrazione/parsing nei gestori onopen/onmessage e la guardia di scheduleReconnect contro timer doppi. --- tests/component/wsMixin.test.js | 97 +++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/tests/component/wsMixin.test.js b/tests/component/wsMixin.test.js index 69d4eef..52dbcd5 100644 --- a/tests/component/wsMixin.test.js +++ b/tests/component/wsMixin.test.js @@ -224,5 +224,102 @@ describe('createWsMixin (wsMixin.js)', () => { wrapper.unmount() expect(ws.close).toHaveBeenCalled() }) + + it('beforeUnmount con connessione OPEN chiude con codice 1000', () => { + const wrapper = mountWith() + const ws = ultimaWs() + ws.readyState = MockWebSocket.OPEN + wrapper.unmount() + expect(ws.close).toHaveBeenCalledWith(1000, 'Component unmounting') + }) + + it('beforeUnmount non lancia se ws.close lancia', () => { + const wrapper = mountWith() + const ws = ultimaWs() + ws.readyState = MockWebSocket.OPEN + ws.close.mockImplementation(() => { throw new Error('già chiusa') }) + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + expect(() => wrapper.unmount()).not.toThrow() + expect(consoleSpy).toHaveBeenCalled() + }) + + it('beforeUnmount annulla un reconnectTimeout pendente', () => { + vi.useFakeTimers() + try { + const wrapper = mountWith() + wrapper.vm.isConnecting = false + wrapper.vm.scheduleReconnect() + expect(wrapper.vm.reconnectTimeout).not.toBe(null) + wrapper.unmount() + // se il timer non fosse stato annullato, allo scoccare del delay + // verrebbe creata una nuova connessione anche a componente smontato + const primaDelTimeout = MockWebSocket.instances.length + vi.advanceTimersByTime(30000) + expect(MockWebSocket.instances.length).toBe(primaDelTimeout) + } finally { + vi.useRealTimers() + } + }) + }) + + describe('rami aggiuntivi', () => { + it('connectWebSocket non fa nulla se è già in corso un tentativo di connessione', () => { + const wrapper = mountWith() + const primaDelTimeout = MockWebSocket.instances.length + wrapper.vm.isConnecting = true + wrapper.vm.connectWebSocket() + expect(MockWebSocket.instances.length).toBe(primaDelTimeout) + }) + + it('riconnettendosi con la WebSocket precedente OPEN, la chiude con codice 1000', () => { + const wrapper = mountWith() + const prima = ultimaWs() + prima.readyState = MockWebSocket.OPEN + wrapper.vm.isConnecting = false + wrapper.vm.connectWebSocket() + expect(prima.close).toHaveBeenCalledWith(1000, 'Reconnecting') + }) + + it('se la chiusura della WebSocket precedente lancia, logga e continua a riconnettersi', () => { + const wrapper = mountWith() + const prima = ultimaWs() + prima.readyState = MockWebSocket.OPEN + prima.close.mockImplementation(() => { throw new Error('già chiusa') }) + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + wrapper.vm.isConnecting = false + + expect(() => wrapper.vm.connectWebSocket()).not.toThrow() + expect(consoleSpy).toHaveBeenCalled() + expect(ultimaWs()).not.toBe(prima) + }) + + it('onopen non lancia se il registro fallisce (send che lancia)', () => { + const wrapper = mountWith() + const ws = ultimaWs() + ws.readyState = MockWebSocket.OPEN + ws.send.mockImplementation(() => { throw new Error('boom') }) + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + + expect(() => ws.onopen()).not.toThrow() + expect(wrapper.vm.wsConnected).toBe(true) + expect(consoleSpy).toHaveBeenCalled() + }) + + it('onmessage non lancia su JSON non valido', () => { + mountWith() + const ws = ultimaWs() + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + + expect(() => ws.onmessage({ data: 'non è JSON {{{' })).not.toThrow() + expect(consoleSpy).toHaveBeenCalled() + }) + + it('scheduleReconnect non arma un secondo timer se uno è già pendente', () => { + const wrapper = mountWith() + const spy = vi.spyOn(globalThis, 'setTimeout') + wrapper.vm.reconnectTimeout = 123 // timer già pendente (valore fittizio) + wrapper.vm.scheduleReconnect() + expect(spy).not.toHaveBeenCalled() + }) }) })