thoov / mock-socket

Javascript mocking library for WebSockets and Socket.IO
MIT License
799 stars 118 forks source link

Mock ws inside web worker #356

Open alexandrzavalii opened 2 years ago

alexandrzavalii commented 2 years ago

Currently mocking web sockets within web worker is not possible.

Window and global object within web worker is not shared

Atrue commented 2 years ago

What library do you use for testing workers? I can suggest creating another module for WebSocket (and import it from the worker) and trying to mock this module using jest or sinon if it works for you

alexandrzavalii commented 2 years ago

I am running the component in storybook and I do not mock workers. The only thing I need to be mocked is the websocket.

Window.WebSocket = WebSocket; will not mock the websocket inside worker.

So I wonder if there is a way around this problem.

I tried injecting WebSocket implementation from Window object to Worker but had no luck

Atrue commented 2 years ago

Anyway, it's not enough to mock the WebSocket, you need all the code inside the worker scope. So you need to have also a Server instance from this module and describe how it should work. So you can't manage it outside this worker. Just to note: when you create a Server instance the WebSocket is mocked automatically and is restored when you call server.stop()

You can try to inject some script using importScript that creates a Server instance from mock-socket (but you need to use importScript again for mock-socket). Or try worker modules to use es imports https://web.dev/module-workers/#enter-module-workers. But do not forget about the production, this code should not get there.

But I'd suggest changing the code a little bit to have the WebSocket in the main thread, so instead of sending and receiving the data to WebSocket it would send and receive events to the main thread. It'd have all the logic inside the worker, and it'd be possible to manage the WebSocket in the main thread, so you can mock it the same way:

/** 
 * main.js
 */
const worker = new Worker('...');
let websocket;

worker.onmessage = ({ data }) => {
    switch (data.type) {
        case 'connect': {
            websocket = new WebSocket(data.url);
            websocket.onmessage = msg => worker.postMessage({ type: 'message', data: msg.data });
            break;
        }
        case 'message': {
            websocket?.send(data.data);
            break;
        }
        case 'close': {
            websocket?.close();
            break;
        }
    }
}

/**
 * worker.js
 */
self.onmessage = ({ data }) => {
    if (data.type === 'message') {
        // handle websocket message;
    }
}
// create connection
self.postMessage({ type: 'connect', url: 'some-url '});

/**
 * test.js
 */
const mockServer = new Server('some-url');

mockServer.on('connection', socket => {
  socket.send('message');
});