Giter Club home page Giter Club logo

jest-websocket-mock's Introduction

Jest websocket mock

npm version Build Status Coverage report code style: prettier

A set of utilities and Jest matchers to help testing complex websocket interactions.

Examples: Several examples are provided in the examples folder. In particular:

Install

npm install --save-dev jest-websocket-mock

Mock a websocket server

The WS constructor

jest-websocket-mock exposes a WS class that can instantiate mock websocket servers that keep track of the messages they receive, and in turn can send messages to connected clients.

import WS from "jest-websocket-mock";

// create a WS instance, listening on port 1234 on localhost
const server = new WS("ws://localhost:1234");

// real clients can connect
const client = new WebSocket("ws://localhost:1234");
await server.connected; // wait for the server to have established the connection

// the mock websocket server will record all the messages it receives
client.send("hello");

// the mock websocket server can also send messages to all connected clients
server.send("hello everyone");

// ...simulate an error and close the connection
server.error();

// ...or gracefully close the connection
server.close();

// The WS class also has a static "clean" method to gracefully close all open connections,
// particularly useful to reset the environment between test runs.
WS.clean();

The WS constructor also accepts an optional options object as second argument:

  • jsonProtocol: true can be used to automatically serialize and deserialize JSON messages:
const server = new WS("ws://localhost:1234", { jsonProtocol: true });
server.send({ type: "GREETING", payload: "hello" });
  • The mock-server options verifyClient and selectProtocol are directly passed-through to the mock-server's constructor.

Attributes of a WS instance

A WS instance has the following attributes:

  • connected: a Promise that resolves every time the WS instance receives a new connection. The resolved value is the WebSocket instance that initiated the connection.
  • closed: a Promise that resolves every time a connection to a WS instance is closed.
  • nextMessage: a Promise that resolves every time a WS instance receives a new message. The resolved value is the received message (deserialized as a JavaScript Object if the WS was instantiated with the { jsonProtocol: true } option).

Methods on a WS instance

  • send: send a message to all connected clients. (The message will be serialized from a JavaScript Object to a JSON string if the WS was instantiated with the { jsonProtocol: true } option).
  • close: gracefully closes all opened connections.
  • error: sends an error message to all connected clients and closes all opened connections.
  • on: attach event listeners to handle new connection, message and close events. The callback receives the socket as its only argument.

Run assertions on received messages

jest-websocket-mock registers custom jest matchers to make assertions on received messages easier:

  • .toReceiveMessage: async matcher that waits for the next message received by the the mock websocket server, and asserts its content. It will time out with a helpful message after 1000ms.
  • .toHaveReceivedMessages: synchronous matcher that checks that all the expected messages have been received by the mock websocket server.

Run assertions on messages as they are received by the mock server

test("the server keeps track of received messages, and yields them as they come in", async () => {
  const server = new WS("ws://localhost:1234");
  const client = new WebSocket("ws://localhost:1234");

  await server.connected;
  client.send("hello");
  await expect(server).toReceiveMessage("hello");
  expect(server).toHaveReceivedMessages(["hello"]);
});

Send messages to the connected clients

test("the mock server sends messages to connected clients", async () => {
  const server = new WS("ws://localhost:1234");
  const client1 = new WebSocket("ws://localhost:1234");
  await server.connected;
  const client2 = new WebSocket("ws://localhost:1234");
  await server.connected;

  const messages = { client1: [], client2: [] };
  client1.onmessage = (e) => {
    messages.client1.push(e.data);
  };
  client2.onmessage = (e) => {
    messages.client2.push(e.data);
  };

  server.send("hello everyone");
  expect(messages).toEqual({
    client1: ["hello everyone"],
    client2: ["hello everyone"],
  });
});

JSON protocols support

jest-websocket-mock can also automatically serialize and deserialize JSON messages:

test("the mock server seamlessly handles JSON protocols", async () => {
  const server = new WS("ws://localhost:1234", { jsonProtocol: true });
  const client = new WebSocket("ws://localhost:1234");

  await server.connected;
  client.send(`{ "type": "GREETING", "payload": "hello" }`);
  await expect(server).toReceiveMessage({ type: "GREETING", payload: "hello" });
  expect(server).toHaveReceivedMessages([
    { type: "GREETING", payload: "hello" },
  ]);

  let message = null;
  client.onmessage = (e) => {
    message = e.data;
  };

  server.send({ type: "CHITCHAT", payload: "Nice weather today" });
  expect(message).toEqual(`{"type":"CHITCHAT","payload":"Nice weather today"}`);
});

verifyClient server option

A verifyClient function can be given in the options for the jest-websocket-mock constructor. This can be used to test behaviour for a client that connects to a WebSocket server it's blacklisted from for example.

Note : Currently mock-socket's implementation does not send any parameters to this function (unlike the real ws implementation).

test("rejects connections that fail the verifyClient option", async () => {
  new WS("ws://localhost:1234", { verifyClient: () => false });
  const errorCallback = jest.fn();

  await expect(
    new Promise((resolve, reject) => {
      errorCallback.mockImplementation(reject);
      const client = new WebSocket("ws://localhost:1234");
      client.onerror = errorCallback;
      client.onopen = resolve;
    }),
    // WebSocket onerror event gets called with an event of type error and not an error
  ).rejects.toEqual(expect.objectContaining({ type: "error" }));
});

selectProtocol server option

A selectProtocol function can be given in the options for the jest-websocket-mock constructor. This can be used to test behaviour for a client that connects to a WebSocket server using the wrong protocol.

test("rejects connections that fail the selectProtocol option", async () => {
  const selectProtocol = () => null;
  new WS("ws://localhost:1234", { selectProtocol });
  const errorCallback = jest.fn();

  await expect(
    new Promise((resolve, reject) => {
      errorCallback.mockImplementationOnce(reject);
      const client = new WebSocket("ws://localhost:1234", "foo");
      client.onerror = errorCallback;
      client.onopen = resolve;
    }),
  ).rejects.toEqual(
    // WebSocket onerror event gets called with an event of type error and not an error
    expect.objectContaining({
      type: "error",
      currentTarget: expect.objectContaining({ protocol: "foo" }),
    }),
  );
});

Sending errors

test("the mock server sends errors to connected clients", async () => {
  const server = new WS("ws://localhost:1234");
  const client = new WebSocket("ws://localhost:1234");
  await server.connected;

  let disconnected = false;
  let error = null;
  client.onclose = () => {
    disconnected = true;
  };
  client.onerror = (e) => {
    error = e;
  };

  server.send("hello everyone");
  server.error();
  expect(disconnected).toBe(true);
  expect(error.origin).toBe("ws://localhost:1234/");
  expect(error.type).toBe("error");
});

Add custom event listeners

For instance, to refuse connections:

it("the server can refuse connections", async () => {
  const server = new WS("ws://localhost:1234");
  server.on("connection", (socket) => {
    socket.close({ wasClean: false, code: 1003, reason: "NOPE" });
  });

  const client = new WebSocket("ws://localhost:1234");
  client.onclose = (event: CloseEvent) => {
    expect(event.code).toBe(1003);
    expect(event.wasClean).toBe(false);
    expect(event.reason).toBe("NOPE");
  };

  expect(client.readyState).toBe(WebSocket.CONNECTING);

  await server.connected;
  expect(client.readyState).toBe(WebSocket.CLOSING);

  await server.closed;
  expect(client.readyState).toBe(WebSocket.CLOSED);
});

Environment set up and tear down between tests

You can set up a mock server and a client, and reset them between tests:

beforeEach(async () => {
  server = new WS("ws://localhost:1234");
  client = new WebSocket("ws://localhost:1234");
  await server.connected;
});

afterEach(() => {
  WS.clean();
});

Known issues

mock-socket has a strong usage of delays (setTimeout to be more specific). This means using jest.useFakeTimers(); will cause issues such as the client appearing to never connect to the server.

While running the websocket server from tests within the jest-dom environment (as opposed to node) you may see errors of the nature:

 ReferenceError: setImmediate is not defined

You can work around this by installing the setImmediate shim from https://github.com/YuzuJS/setImmediate and adding require('setimmediate'); to your setupTests.js.

Testing React applications

When testing React applications, jest-websocket-mock will look for @testing-library/react's implementation of act. If it is available, it will wrap all the necessary calls in act, so you don't have to.

If @testing-library/react is not available, we will assume that you're not testing a React application, and you might need to call act manually.

Using jest-websocket-mock to interact with a non-global WebSocket object

jest-websocket-mock uses Mock Socket under the hood to mock out WebSocket clients. Out of the box, Mock Socket will only mock out the global WebSocket object. If you are using a third-party WebSocket client library (eg. a Node.js implementation, like ws), you'll need to set up a manual mock:

  • Create a __mocks__ folder in your project root
  • Add a new file in the __mocks__ folder named after the library you want to mock out. For instance, for the ws library: __mocks__/ws.js.
  • Export Mock Socket's implementation in-lieu of the normal export from the library you want to mock out. For instance, for the ws library:
// __mocks__/ws.js

export { WebSocket as default } from "mock-socket";

NOTE The ws library is not 100% compatible with the browser API, and the mock-socket library that jest-websocket-mock uses under the hood only implements the browser API. As a result, jest-websocket-mock will only work with the ws library if you restrict yourself to the browser APIs!

Examples

For a real life example, see the examples directory, and in particular the saga tests.

Contributing

See the contributing guide.

jest-websocket-mock's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar g-rath avatar jwoodrow avatar kubilaysalih avatar michaelgt04 avatar mikejw avatar polemius avatar romgain avatar steveluscher avatar valainisgt avatar zouxuoz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

jest-websocket-mock's Issues

Jenkins build failure after upgrade to v2.3.0

Hello!

I have upgraded jest-websocket-mock to v2.3.0 from v2.2.1 and my Jenkins CI build started failing because of a test that's making use of jest-websocket-mock.

This is the error I'm getting:
image

Worth noting that this issue is only happening on Jenkins, not locally.

What's strange is that the changelog does not appear to mention any breaking changes.

Have I missed any migration guides or anything?

If you need more details, please let me know.

Any help is appreciated, thank you!

Consider supporting connection delays

It would be nice to have an option that delays connections so we can mock connection timeouts and catch cases where the readyState is CONNECTING.

const server = new WS("ws://localhost:1234", {delay: 10000});

expect(client.readyState).toBe(WebSocket.CONNECTING);

// 10 seconds later...
await server.connected;

Running multiple jest tests after each other fails

Hi folks,

I've following tests:

Jest Test Code
import WS from "jest-websocket-mock";
import { act, cleanup, renderHook } from "@testing-library/react";

import { z } from "zod";
import useLiveDbHook from "./websockets";
const BASE_URL = "http://127.0.0.1:8080";

jest.mock("react-oidc-context", () => ({
  useAuth: () => {
    return {
      isLoading: false,
      isAuthenticated: true,
      user: { access_token: "test-token" },
    };
  },
}));

describe("useLiveDbHook", () => {
  let server: WS;

  beforeEach(() => {
    server = new WS(`${BASE_URL.replace(/^http/, "ws")}/endpoint`);
  });
  afterEach(() => {
    server.close();
    WS.clean();
  });

  test.only("elements will be updated if update was received", async () => {
    const { result } = renderHook(() =>
      useLiveDbHook(
        "/endpoint",
        z.object({ _id: z.string(), name: z.string(), age: z.number() }),
      ),
    );

    await server.connected;

    server.send(
      JSON.stringify({
        operation_type: "insert",
        object_id: "some-id",
        data: { _id: "some-id", name: "Peter", age: 23 },
      }),
    );

    expect(server).toReceiveMessage(JSON.stringify(["some-id"]));

    server.send(
      JSON.stringify({
        operation_type: "insert",
        object_id: "some-id2",
        data: { _id: "some-id2", name: "Gรผnther", age: 24 },
      }),
    );

    expect(server).toReceiveMessage(JSON.stringify(["some-id", "some-id2"]));

    server.send(
      JSON.stringify({
        operation_type: "update",
        object_id: "some-id",
        data: { _id: "some-id", name: "Hans", age: 30 },
      }),
    );

    expect(result.current.elements.length).toBe(2);
    expect(result.current.elements).toContainEqual({
      _id: "some-id",
      name: "Hans",
      age: 30,
    });
    expect(result.current.elements).toContainEqual({
      _id: "some-id2",
      name: "Gรผnther",
      age: 24,
    });

    expect(server).toReceiveMessage(JSON.stringify(["some-id2", "some-id"])); // (elements.map(elem => elem._id)
    await cleanup();
  });

  test.only("elements will not be effected if a update with not existing id was received", async () => {
    const { result } = renderHook(() =>
      useLiveDbHook(
        "/endpoint",
        z.object({ _id: z.string(), name: z.string(), age: z.number() }),
      ),
    );

    await server.connected;
    act(() => {
      server.send(
        JSON.stringify({
          operation_type: "insert",
          object_id: "some-id",
          data: { _id: "some-id", name: "Peter", age: 23 },
        }),
      );
    });
    expect(server).toReceiveMessage(JSON.stringify(["some-id"]));
    act(() => {
      server.send(
        JSON.stringify({
          operation_type: "insert",
          object_id: "some-id2",
          data: { _id: "some-id2", name: "Gรผnther", age: 24 },
        }),
      );
    });
    expect(server).toReceiveMessage(JSON.stringify(["some-id", "some-id2"]));
    act(() => {
      server.send(
        JSON.stringify({
          operation_type: "insert",
          object_id: "some-id3",
          data: { _id: "some-id3", name: "Hans", age: 30 },
        }),
      );
    });
    expect(server).toReceiveMessage(
      JSON.stringify(["some-id", "some-id2", "some-id3"]),
    );

    expect(result.current.elements.length).toBe(3);
    act(() => {
      server.send(
        JSON.stringify({
          operation_type: "update",
          object_id: "NOT_EXISTING",
          data: null,
        }),
      );
    });

    expect(result.current.elements.length).toBe(3);
    expect(result.current.elements).toContainEqual({
      _id: "some-id",
      name: "Peter",
      age: 23,
    });
    expect(result.current.elements).toContainEqual({
      _id: "some-id2",
      name: "Gรผnther",
      age: 24,
    });
    expect(result.current.elements).toContainEqual({
      _id: "some-id3",
      name: "Hans",
      age: 30,
    });
    expect(server).toReceiveMessage(
      JSON.stringify(["some-id", "some-id2", "some-id3"]),
    );
    await cleanup();
  });

Remove one .only and the remaing test will pass. If both are executed with .only one test will fail (see following logs):
Logs:

Logs of the different test executions

Single run 1:

 PASS  src/hooks/db/websocket.unit.ts
  useLiveDbHook
    โœ“ elements will be updated if update was received (33 ms)
    โ—‹ skipped connects to WebSocket
    โ—‹ skipped reconnect with WebSocket if connection was lost
    โ—‹ skipped elements contains received data
    โ—‹ skipped elements will not be effected if a update with not existing id was received
    โ—‹ skipped elements will be deleted if delete was received
    โ—‹ skipped elements will not be effected if a delete with not existing id was received
    โ—‹ skipped setElements causes a subscription request containing ALL current ids

Single run 2:

PASS  src/hooks/db/websocket.unit.ts
  useLiveDbHook
    โœ“ elements will not be effected if a update with not existing id was received (32 ms)
    โ—‹ skipped connects to WebSocket
    โ—‹ skipped reconnect with WebSocket if connection was lost
    โ—‹ skipped elements contains received data
    โ—‹ skipped elements will be updated if update was received
    โ—‹ skipped elements will be deleted if delete was received
    โ—‹ skipped elements will not be effected if a delete with not existing id was received
    โ—‹ skipped setElements causes a subscription request containing ALL current ids

Together

FAIL  src/hooks/db/websocket.unit.ts
  useLiveDbHook
    โœ“ elements will be updated if update was received (31 ms)
    โœ• elements will not be effected if a update with not existing id was received (11 ms)
    โ—‹ skipped connects to WebSocket
    โ—‹ skipped reconnect with WebSocket if connection was lost
    โ—‹ skipped elements contains received data
    โ—‹ skipped elements will be deleted if delete was received
    โ—‹ skipped elements will not be effected if a delete with not existing id was received
    โ—‹ skipped setElements causes a subscription request containing ALL current ids

  โ— useLiveDbHook โ€บ elements will not be effected if a update with not existing id was received

    expect(WS).toReceiveMessage(expected)

    Expected the next received message to equal:
      "[\"some-id\",\"some-id2\"]"
    Received:
      undefined

    Difference:

      Comparing two different types of values. Expected string but received undefined.

      111 |     );
      112 |
    > 113 |     expect(server).toReceiveMessage(JSON.stringify(["some-id", "some-id2"]));
          |                    ^
      114 |
      115 |     server.send(
      116 |       JSON.stringify({

      at src/hooks/db/websocket.unit.ts:113:20
      at fulfilled (src/hooks/db/websocket.unit.ts:5:58)

  โ— useLiveDbHook โ€บ elements will not be effected if a update with not existing id was received

    expect(WS).toReceiveMessage(expected)

    Expected the next received message to equal:
      "[\"some-id2\",\"some-id\"]"
    Received:
      undefined

    Difference:

      Comparing two different types of values. Expected string but received undefined.

      133 |     });
      134 |
    > 135 |     expect(server).toReceiveMessage(JSON.stringify(["some-id2", "some-id"])); // (elements.map(elem => elem._id)
          |                    ^
      136 |     await cleanup();
      137 |   });
      138 |

      at src/hooks/db/websocket.unit.ts:135:20
      at fulfilled (src/hooks/db/websocket.unit.ts:5:58)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 6 skipped, 1 passed, 8 total
Snapshots:   0 total
Time:        1.683 s
Ran all test suites.

Both tests are testing a react hook I wrote which connects to a websocket server. You can pass the endpoint name to the hook. The hook itself will use useAuth to determine the access token and add this to the websocket url : ws://<ip>/<endpoint>?token=<access_token>.

Further the hook provides a react state ( elements) and a setter (setElements).
Everytime the websocket receives a message from the server, the elements can change (depending on the message).
Everytime the elements change (covered by useEffect(() => ..., [elements]); inside of useLiveDbHook) the client sends a list of the _ids of the current elements to the server.

So if the server sends a element, then the elements change and the client answers with the old id list including the new element (if INSERTED).

I tested the logic manually and I'm sure it works.
But the tests I build just fails if I execute them after each other but are passed if I run them separatly.
I have no idea why...

Is there a way to simulate a lost connection?

I can see that the library supports close and error, one of which gracefully shuts down the connection, and the other which sends an error message; but I want to simulate the connection abruptly and ungracefully being lost (running out of data, losing wifi, ethernet unplugged, etc). How would I go about doing that?

require error

When using the library, that is something along;

Example:

const WS = require('jest-websocket-mock');

Uncaught ReferenceError: expect is not defined

Rename `close` method to `tearDown`

The close method would be better suited as as tearDown.

This is b/c it calls server.stop, which removes the WebSocket reference.

This means the proper way to test close functionality is like so:

it('emits a "Close" event', () => {
  clientSocket.addListener(BSClientSocketEvent.Close, listener);

  ws.server.close({
    code: 3001,
    reason: 'no reason',
    wasClean: true
  });

  const expected: BSClientSocket.CloseEvent = {
    disconnectCode: 3001
  };

  expect(listener).toHaveBeenCalledTimes(1);
  expect(listener).toHaveBeenCalledWith(expected);
});

Meanwhile, WS#close should be used to clean up everything without having to worry about the details, such as in an afterEach block.

The emit should probably be removed too, since server.close already emits the proper close event.

WebSocket onclose callback is invoked multiple time with wrong readyState code

I am executing this test case

test("WebSocket Close", async () => {
    const server = new WS("wss://localhost:1234");
    let ws = new WebSocket("wss://localhost:1234");
    ws.addEventListener("open", () => console.log("WS OPEN"));
    ws.addEventListener("error", () => console.log("WS ERROR"));
    ws.addEventListener("close", () => {
        console.log("WS CLOSE : " + ws.readyState)
    });
    await server.connected; // wait for the server to have established the connection
    server.close(); // Gracefully close the connection
    await server.closed;
})

Console Outputs were as follows

WebSocket Close (33ms)

console.log test/web-socket.spec.js:277
  WS OPEN

console.log test/web-socket.spec.js:280
  WS CLOSE : 1

console.log test/web-socket.spec.js:280
  WS CLOSE : undefined

close callback should have been invoked only once and with readyState 3 (as stated here)

Same behaviour is observed even when I replace addEventListener with callback assignment

test("WebSocket Close", async () => {
    const server = new WS("wss://localhost:1234");
    let ws = new WebSocket("wss://localhost:1234");
    ws.onopen = () => console.log("WS OPEN");
    ws.onerror = () => console.log("WS ERROR");
    ws.onclose = () => console.log("WS CLOSE : " + ws.readyState);
    await server.connected; // wait for the server to have established the connection
    server.close(); // Gracefully close the connection
    await server.closed;
})

Jest - Async Callback timeout with Example Tests

Issue:

When following the example of initializing the server in 'beforeEach' and calling WS.clean() in 'afterEach', the provided example tests fail with an Async Callback timeout:

Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

I'm attempting to use 'jest-websocket-mock' in a project I'm working on. Before using the library, I'm trying to run the example tests provided. In the example below, I use the suggested setup and teardown logic from the documentation, and apply it to one of the example tests. The test fails with an Async Callback Timeout.

Is there any guidance on how I should modify the test?

import WS from "jest-websocket-mock";

describe("Testing Jest Websocket Mock", () => {
  let  server : WS;
  beforeEach(async () => {
    server = new WS("ws://localhost:1234");
    await server.connected;
  });

  afterEach(() => {
    WS.clean();
  });



  test("the server keeps track of received messages, and yields them as they come in", async () => {
    const client = new WebSocket("ws://localhost:1234");
       client.send("hello");
    await expect(server).toReceiveMessage("hello");
    expect(server).toHaveReceivedMessages(["hello"]);
  });

});

However, when I remove the 'beforeEach' and 'afterEach', and move the server logic into the test itself, the test passes.

server.error() removes the server from the urlMap, and it cannot be reconnected to

Hi,
I have a unit test that is something along the line of

it('retries to connect to the websocket connection after 5 seconds on error'){
  await server.connected

   server.error();
   await server.closed;
   jest.useFakeTimers()
   jest.advancedTimerByTime(5000);
   jest.useRealTimers()
   
   await server.connected;
   server.send({message: 'whatever'})
   expect(clentMessages).toBe([{message: 'whatever'}])
}

Bit pseudo-cody to prevent drowning you in implementation details. But the problem is that on the second instantiation of WebSocket(), this.urlMap[serverURL] is undefined, and the websocket is then never attached.

I have to recreate the websocket server in the middle of my test to make it work, which is not ideal.

Timeout error running simple test in jest

I am running the following test in NodeJS.

import WS from 'jest-websocket-mock';
import WebSocket from 'ws';

test('example test with jest-websocket-mock', async () => {
    const server = new WS('ws://localhost:1234');
    const client = new WebSocket('ws://localhost:1234');

    await server.connected;
    client.send('hello');
    await expect(server).toReceiveMessage('hello');
    expect(server).toHaveReceivedMessages(['hello']);
}, 25000);

And I get the following error

image

Please could you suggest how can I resolve this.

Add ability to flush pending message queue

I am currently utilizing this library to mock communication between my frontend app and an IRC gateway called webircgateway for my unit tests, and I wasn't able to find a function that can clear out the pending message queue within a test. I am testing whether my app can send out a message, and I currently have to copy and paste await server.nextMessage so that I can discard the IRC initial communication messages (PASS, NICK, and USER) which I don't care about so that I can then call toReceiveMessage and toHaveReceivedMessages on the PRIVMSG message, which I do care about.

I attempted to make my own makeshift flush function:

async function flushWebSocketMessages(server: WS) {
    while (1) {
        const result = await promiseWithTimeout(server.nextMessage);
        if (result === null) {
            break;
        }
    }
}

function promiseWithTimeout<T>(promise: Promise<T>) {
    return new Promise((resolve, reject) => {
        promise
            .then((val) => resolve(val))
            .catch((err) => reject(err));

        setTimeout(() => {
            resolve(null);
        }, 2000);
    });
}

However, it does not seem to work because once I clear out the queue (which happens when we cannot get any more messages and the 2s timeout returns null) any message I send through the WebSocket is reported by the mock server as undefined:

 expect(WS).toReceiveMessage(expected)

    Expected the next received message to equal:
      "PRIVMSG #mychannel :Hello there"
    Received:
      undefined

    Difference:

      Comparing two different types of values. Expected string but received undefined.

I have decided to work around this for the time being by copy and pasting await server.nextMessage like I mentioned earlier in order to clear out the queue, but I would like to know why this is happening if possible. Also, let me know if flushing the pending message queue is something that can benefit this project, thanks.

Using QueryParams in ws url doesn't work

import WS from "jest-websocket-mock";

afterEach(() => {
  WS.clean();
});

describe("Websocket url tests", () => {
  it("should work", async () => {
    const ws = new WS(`wss://localhost:1234?Auth=JWTAuthenticationToken`);
    const client = new WebSocket(`wss://localhost:1234?Auth=JWTAuthenticationToken`);
    await ws.connected; // doesn't work
  });

  it("Works", async () => {
    const ws = new WS("wss://localhost:1234");
    const client = new WebSocket("wss://localhost:1234");
    await ws.connected; // works
  });
});

Add the possibility to pass mock-socket server options

the mock-socket Server constructor accepts a variety of options, such as verifyClient which can be used to emulate a rejection in the handshake part of the connection (if I understand correctly).

I don't think this would be too much of a change since it's basically changing the constructor to add an verifyClient that needs to be a function that takes no argument (unlike the real ws mock-socket doesn't pass any info along with the connection) and pass this on to the mockSocket.Server constructor.

I'll try and do a PR if you want later today.

Usage with NodeJS and ws package

The example from the readme works fine:

test("the server keeps track of received messages, and yields them as they come in", async () => {
    const server = new WS("ws://localhost:1234");
    const client = new WebSocket("ws://localhost:1234");

    await server.connected;
    client.send("hello");
    await expect(server).toReceiveMessage("hello");
    expect(server).toHaveReceivedMessages(["hello"]);
});

But since im working with NodeJS I need to import a specific WebSocket implementation:

import WebSocket from "ws";

test("the server keeps track of received messages, and yields them as they come in", async () => {
    const server = new WS("ws://localhost:1234");
    const client = new WebSocket("ws://localhost:1234");

    await server.connected;
    client.send("hello");
    await expect(server).toReceiveMessage("hello");
    expect(server).toHaveReceivedMessages(["hello"]);
});

That gives connect ECONNREFUSED 127.0.0.1:1234 ๐Ÿ˜Ÿ

jest.config.js

module.exports = {
    transform: {
        "^.+\\.tsx?$": "ts-jest",
    },
    testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
    moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
};

Package.json

{
  "dependencies": {
    "ws": "^8.9.0"
  },
  "devDependencies": {
    "jest": "^28.1.0",
    "jest-websocket-mock": "^2.4.0",
    "ts-jest": "^28.0.2",
    "ts-node": "^10.9.1",
    "typescript": "^4.7.2"
  },
}

How can I make this work with the ws package?

Using JWM with partysocket causes Jest error: `TypeError: ports is not iterable`

Hi, thanks for this useful library. I don't know whether this is an issue with JWM or partysocket, but I wanted to let you know about it.

I'm trying to use jest-websocket-mock in conjunction with partysocket, which seems like the most updated browser WebSocket library right now: https://docs.partykit.io/reference/partysocket-api/.

When I run my tests and I use jest-websocket-mock to send a message, I get this error: this error: TypeError: ports is not iterable.

Here's what I do in my tests:

server.send({ type: 'system' })

Then, JWM creates the message from this function:

function createMessageEvent(config) {
  var type = config.type;
  var origin = config.origin;
  var data = config.data;
  var target = config.target;
  var messageEvent = new MessageEvent(type, {
    data: data,
    origin: origin
  });

  if (target) {
    messageEvent.target = target;
    messageEvent.srcElement = target;
    messageEvent.currentTarget = target;
  }

  return messageEvent;
}

Then partysocket clones the event with this code (it actually uses cloneEventNode because Jest is running in the node environment).

function cloneEventBrowser(e) {
  return new e.constructor(e.type, e);
}

function cloneEventNode(e) {
  if ("data" in e) {
    const evt2 = new MessageEvent(e.type, e);
    return evt2;
  }
  if ("code" in e || "reason" in e) {
    const evt2 = new CloseEvent(
      // @ts-expect-error we need to fix event/listener types
      e.code || 1999,
      // @ts-expect-error we need to fix event/listener types
      e.reason || "unknown reason",
      e
    );
    return evt2;
  }
  if ("error" in e) {
    const evt2 = new ErrorEvent(e.error, e);
    return evt2;
  }
  const evt = new Event(e.type, e);
  return evt;
}

The above code fails on this line:

const evt2 = new MessageEvent(e.type, e);

I looked at the e parameter and e.ports, is null which explains the error. This may be because of a different environment, but do you have any suggestions on how to fix this?

Note: I also tried to force the environment to Node (instead of the browser), but got this cryptic error:

TypeError: The "event" argument must be an instance of Event. Received an instance of Event

  at WebSocket._handleOpen (node_modules/partysocket/dist/index.js:444:10)
  at node_modules/mock-socket/dist/mock-socket.js:859:16
      at Array.forEach (<anonymous>)
  at WebSocket.dispatchEvent (node_modules/mock-socket/dist/mock-socket.js:855:13)
  at WebSocket.delayCallback (node_modules/mock-socket/dist/mock-socket.js:1522:16)
  at Timeout._onTimeout (node_modules/mock-socket/dist/mock-socket.js:757:58)

Versions:

 "partysocket": "0.0.25",
 "jest-websocket-mock": "2.4.0",

error!!!

nuwa.bundle.js:2 GET http://neo.nuwarobotics.com:35729/livereload.js?snipver=1 net::ERR_CONNECTION_REFUSED
(anonymous) @ nuwa.bundle.js:2
(anonymous) @ nuwa.bundle.js:2
jest-websocket-mock.es.js?7b91:135 Uncaught ReferenceError: expect is not defined
at eval (jest-websocket-mock.es.js?7b91:135)
at Module../node_modules/_jest-websocket-mock@1.5.0@jest-websocket-mock/lib/jest-websocket-mock.es.js (index.js:2287)
at webpack_require (index.js:724)
at fn (index.js:101)
at eval (mock.js?abc5:1)
at Module../src/service/webSocket/mock.js (index.js:5028)
at webpack_require (index.js:724)
at fn (index.js:101)
at eval (main.js?3aad:1)
at Module../src/code-lab/main.js (index.js:4959)

`jest-websocket-mock` only support a single client connection

At

this._isConnected = new Promise((done) => (connectionResolver = done));
this._isClosed = new Promise((done) => (closedResolver = done));
are defined _isConnected to be resolved when a new connection is established, and _isClossed when the WebSockets server gets clossed. I think there should be a mechanism to be notified each time a new connection happens, so it's possible to have multiple clients at the same time.

readyState is open after closing connection

The WebSocket.readyState is still open after closing the connection:

const server = new WS('ws://localhost:8080');
const connection = await server.connected;

server.close();

await server.closed;

// outputs true
console.log(connection.readyState === WebSocket.OPEN); 

Jest encountered an unexpected token: jest-websocket-mock.cjs.js

I have installed this package and tried to import the module (as per the examples) in my test file, however, when trying to run the tests, I get the following error:

  โ— Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     โ€ข If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     โ€ข If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     โ€ข To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     โ€ข If you need a custom transformation specify a "transform" option in your config.
     โ€ข If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /<project-path>/node_modules/jest-websocket-mock/lib/jest-websocket-mock.cjs.js:206
        const waitDelay = options?.timeout ?? WAIT_DELAY;
                                  ^

    SyntaxError: Unexpected token '.'

      1 | import {useWebSocket} from "../webSocket";
    > 2 | import WS from "jest-websocket-mock";
        | ^
      3 |

I do not use TypeScript in my project and my jest.config.js file looks like this:

module.exports = {
    // Your normal jest config settings
    testPathIgnorePatterns: ["frontend/src/cypress/"],
    setupFiles: ["./frontend/src/setupTests.js"],
    moduleNameMapper: {
        "\\.(css|less|scss)$": "<rootDir>/frontend/src/empty-module.js",
    },
};

It is worth noting that I use the ?. operator in other parts of my project and it works flawlessly.

I tried adding TypeScript support to both my babel and jest configs but this didn't make a difference.

Any ideas what is causing this issue?

Undefined WebSocket.bufferedAmount

The WebSocket.bufferedAmount should not be undefined but zero:

const server = new WS('ws://localhost:8080');
new WebSocket('ws://localhost:8080');
const connection = await server.connected;

// Outputs undefined
console.log(connection.bufferedAmount);

ArrayBuffer & Binary messages?

I'm trying to test an XTerm web shell that makes use of channel.k8s.io subprotocol for the WebSocket, basically the messages are ArrayBuffers, and I have issues using this library to mock the server since I don't see an option to send ArrayBuffer or Binary data.

Is there an undocumented way or is just not supported?

Open handles

When using the library, I run the tests and the suite completes fine. However, I get a warning from a jest that there might be open handles.

When I run npm test -- --detectOpenHandles jest always complains about using the toReceiveMessage matcher.

Example:

  โ—  Timeout

      146 |
      147 |       const promisedResponse = client.createWithdrawTicket(params);
    > 148 |       await expect(server).toReceiveMessage(JSON.stringify({ m: 0, i: 0, n: 'CreateWithdrawTicket', o: JSON.stringify({
          |                            ^
      149 |         TfaType: 'Google',
      150 |         TfaCode: TfaCode,
      151 |         Payload: JSON.stringify({

      at ../node_modules/jest-websocket-mock/lib/jest-websocket-mock.cjs.js:206:89
      at Object.toReceiveMessage (../node_modules/jest-websocket-mock/lib/jest-websocket-mock.cjs.js:206:66)
      at __EXTERNAL_MATCHER_TRAP__ (../node_modules/expect/build/index.js:386:30)
      at Object.throwingMatcher [as toReceiveMessage] (../node_modules/expect/build/index.js:387:15)
      at lib/apex/apexClient.spec.ts:148:28
      at lib/apex/apexClient.spec.ts:8:71
      at Object.<anonymous>.__awaiter (lib/apex/apexClient.spec.ts:4:12)
      at Object.<anonymous> (lib/apex/apexClient.spec.ts:131:74)
      at TestScheduler.scheduleTests (../node_modules/@jest/core/build/TestScheduler.js:333:13)
      at runJest (../node_modules/@jest/core/build/runJest.js:387:19)
      at _run10000 (../node_modules/@jest/core/build/cli/index.js:408:7)
      at runCLI (../node_modules/@jest/core/build/cli/index.js:261:3)

This is because the code internally does:

Promise.race([yourPromise, setTimeout(...)])

Is there a way to configure it to tear down the timeouts it sets?

Use optional generics in expect matchers

This is change I've suggested for the actual jest typings as well.

Basically, if you defined your matchers with generics, it'll let us explicitly denote the expected type, and benefit from both autocompletion & strong typing:

toReceiveMessage<TMessage = object>(message: DeserializedMessage<TMessage>): R;
toHaveReceivedMessages<TMessage = object>(messages: Array<DeserializedMessage<TMessage>>): R;

This would result in DeserializedMessage becoming generic as well:

export type DeserializedMessage<TMessage = object> = string | TMessage;

Use case:

ss+(2019-04-06+at+11 14 41)

it('sends the message', () => {
  const requestType: BotSocket.RequestType = 'handshake';
  const requestData: BotSocket.ClientHandshakeData = {
    userId: 'client-1'
  };

  clientSocket.sendMessageToServer(requestType, requestData);

  expect(ws).toHaveReceivedMessages<BotSocket.Message>([
    {
      request: 'handshake',
      data: {
        userId: 'hello world'
      }
    }
  ]);
});

I'm happy to make a PR if you're happy to merge it ๐Ÿ˜„

Refuse connections

We have a use case where we have to test that a client automatically reconnects (within a max number of attempts) to the server after the server has closed the connection.

Is there any way to refuse connections currently?

Websocket server doesn't seem to be connecting

I've got a project where a node micro service is connected to a rails actioncable websocket.

I'm trying to speck the websocket connection part (regardless of the fact it's an actioncable websocket behind the scenes) by using jest and jest-websocket-mock and mock-socket.

My ActionCable class connection method looks like this

// lib/classes/action_cable.js
const WebSocket = require('ws');
class ActionCable {
  constructor(url, opts) {
    this.cableURL = url;
    this.origin = opts.origin || 'http://localhost:3000';
    this.headers = opts.headers || {};
    this.connection = null;
    this.connect = null;
  }
  _connect() {
    if (!this.connect) {
      this.connect = new Promise((resolve, reject) => {
        this.connection = new WebSocket(this.cableURL, undefined, { origin: this.origin, headers: this.headers });

        this.connection.onerror = (err) => {
          reject(err);
        };
        this.connection.onclose = () => {
          reject(new Error('Connection closed'));
        };
        this.connection.onopen = () => {
          resolve(this.connection)
        };
      });
    }
    return this.connect;
  }
}

and in my jest test I've got this

// tests/classes/action_cable.test.js
import ActionCable from '../../lib/classes/action_cable.js';
import WS from "jest-websocket-mock";
import { Server } from 'mock-socket';

let actioncableInstance = false;

beforeEach(() => {
  actioncableInstance = new ActionCable('ws://localhost:1234', { origin: 'http://localhost:4321' });
});

describe('_connect', () => {
  let WSServer = false;

  beforeEach(() => {
    WSServer = new WS('ws://localhost:1234');
  });
  afterEach(() => {
    WS.clean();
  });

  describe('when WebSocket server refuses the connection', () => {

    beforeEach(() => {
      WSServer.on('connection', (socket) => {
        console.log('received stuff');
        socket.close({ wasClean: false, code: 1003, reason: "NOPE" });
      });
    });

    test('rejects with error and calls _disconnected', async () => {
      expect.assertions(1);
      await expect(actioncableInstance._connect()).rejects;
    });
  });
});

I've got a mocks file that seems to no cause errors when I have it like this

// tests/__mocks__/ws.js
const WebSocket = require('mock-socket').WebSocket;

module.exports = WebSocket;

my project tree looks like this

lib
  | classes
    | action_cable.js
tests
  | __mocks__
    | ws.js
  | classes
    | action_cable.test.js

I'm guessing I've got something configured wrong but I fail to see what after half a day of trying to get my test to work for this simple case (I haven't even begun the other cases but I think once I get the connection refused case going I'll be able to move on to the rest). But in case there's something else I'm getting a timeout error from my test
Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Timeout

For a little context this is part of a bigger library of functions I've got for general NodeJS usage so I'm not running anything part of a NodeJS framework.

Any help or insight would be appreciated !

Reconnection logic / Empty `closedResolver`

Hi!

I try to test some reconnection logic. So I'm basically doing something like this:

server.close({ code: 1001, reason: "Something went wrong!", wasClean: true });
await server.closed;
await server.connected;  // problematic...

But it seems that the connection is not "cleaned up" after a close. The closedResolver is doing nothing: closedResolver!: () => void;. So, the last line in my code seems to be always true (I think from the first "connection"), because the open event is not fired again. Is it possible to alter the closedResolver somehow? Or did I oversee something?

Great library, btw!

Best regards,
Kersten

Does not work with React <= 16.8.6

I'm trying to get this example from the README to work:

import WS from 'jest-websocket-mock';

it('tests websocket', async () => {
  const server = new WS("ws://localhost:1234");
  const client = new WebSocket("ws://localhost:1234");

  await server.connected;
  client.send("hello");
  await expect(server).toReceiveMessage("hello");
  expect(server).toHaveReceivedMessages(["hello"]);
});

When I run that, I get this error:

console.error
    Warning: The callback passed to ReactTestUtils.act(...) function must not return anything.
    
    It looks like you wrote ReactTestUtils.act(async () => ...), or returned a Promise from the callback passed to it. Putting asynchronous logic inside ReactTestUtils.act(...) is not supported.
    

       5 |   const client = new WebSocket("ws://localhost:1234");
       6 |
    >  7 |   await server.connected;
         |                ^
       8 |   client.send("hello");
       9 |   await expect(server).toReceiveMessage("hello");
      10 |   expect(server).toHaveReceivedMessages(["hello"]);

      at warningWithoutStack (node_modules/react-dom/cjs/react-dom-test-utils.development.js:100:32)
      at act (node_modules/react-dom/cjs/react-dom-test-utils.development.js:1172:9)
      at waitForConnected (node_modules/jest-websocket-mock/lib/jest-websocket-mock.cjs.js:128:13)
      at WS.connected (node_modules/jest-websocket-mock/lib/jest-websocket-mock.cjs.js:134:5)

And the test fails because it exceeded the 5 second timeout for a test. How can I get this to work? I'm using version 2.3.0 of jest-websocket-mock and Node 14.17.0.

Add support for mocking WebSocket.bufferedAmount

First of all, thank you for sharing such a useful library!

For showing the progress of large payloads, clients usually rely on checking the property bufferedAmount periodically until it becomes zero. It would be useful to have a mechanism that allows mocking that behavior.

Since the property is read-only, you cannot do it currently:

const connection = await server.connected;

// TS2540: Cannot assign to 'bufferedAmount' because it is a read-only property.
connection.bufferedAmount = 100;

await expect(promise).resolves.toBeUndefined();

connection.bufferedAmount = 0;

//...

Use of https://github.com/YuzuJS/setImmediate

I've noticed that without using the above shim for setImmediate running the web socket server from my tests produces the error ReferenceError: setImmediate is not defined, presumably because the test environment is jest-dom as opposed to node. Adding this shim with require("setimmediate"); to setupTests.js resolves the issue.

Message Event on Server not working

Hi @romgain ,
It seems to me, that the on message event is not working. I created the code below based on your examples in the readme.
Because of console.log in server.on('message'), I would expect to see an output, when the client sends a message, but that does not happen.
To ensure events are working, I added the server.on('connection'), which does it's job perfectly.
Also the server.nextMessage promise works like described. Also the client message is obviously received by the server, as the test passes.

Is this a user error or a bug?

Best regards

test("the server keeps track of received messages, and yields them as they come in", async () => {
	const server = new WS("ws://localhost:1234");
	server.on('connection', socket => {
		console.log(`server got event connection`);
	});
	server.on('message', msg => {
		console.log(msg);
	});

	const client = new WebSocket("ws://localhost:1234");
	client.onmessage = msg => {
		console.log(msg.data);
	};

	await server.connected;

	server.send('server says hi');
	client.send('client says hi');

	await expect(server).toReceiveMessage('client says hi');
	expect(server).toHaveReceivedMessages(['client says hi']);
});

Output:

  โœ“ the server keeps track of received messages, and yields them as they come in (31ms)

  console.log test/100_ws.mod.test.js:63
    server got event connection

  console.log test/100_ws.mod.test.js:71
    server says hi

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.79s, estimated 1s

Include `socket` in `connected`

When mock-socket emits connection, it includes the socket as the first parameter.

The connectionResolver could include this value, to give developers access to the client-side socket.

Use case:

describe('when there is an active connection', () => {
  let socket: WebSocket;
  beforeEach(() => ws = new WS(url, { jsonProtocol: true }));
  beforeEach(async () => {
    clientSocket.connect();

    socket = await ws.connected;

    mockedLogger.mockClear();
  });

  afterEach(() => WS.clean());

  //#region it closes the connection with the given disconnect code
  it('closes the connection with the given disconnect code', async () => {
    const mockClose = jest.fn();

    socket.close = mockClose;
    const code = 3001;

    clientSocket.close(code);

    expect(mockClose).toHaveBeenCalledWith(code);
  });
  //#endregion
});

(I tried using spyOn, but that for some reason didn't work - jest would error with "object[methodName].mockImplementation is not a function")

Cannot assign to read only property 'readyState' of object '#<WebSocket>

I was following the example but it gives me the following error. Can you please help?

  console.error
    Error: Uncaught [TypeError: Cannot assign to read only property 'readyState' of object '#<WebSocket>']
        at reportException (/Users/alanwang/*/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:62:24)
        at Timeout.task [as _onTimeout] (/Users/alanwang/*/node_modules/jsdom/lib/jsdom/browser/Window.js:396:9)
        at listOnTimeout (internal/timers.js:554:17)
        at processTimers (internal/timers.js:497:7) TypeError: Cannot assign to read only property 'readyState' of object '#<WebSocket>'
        at WebSocket.delayCallback (/Users/alanwang/*/node_modules/mock-socket/src/websocket.js:81:26)
        at /Users/alanwang/*/node_modules/mock-socket/src/helpers/delay.js:10:41
        at Timeout.task [as _onTimeout] (/Users/alanwang*/node_modules/jsdom/lib/jsdom/browser/Window.js:391:19)
        at listOnTimeout (internal/timers.js:554:17)
        at processTimers (internal/timers.js:497:7)

      at VirtualConsole.<anonymous> (node_modules/jsdom/lib/jsdom/virtual-console.js:29:45)
      at reportException (node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:28)
      at Timeout.task [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:396:9)

server.connected and server.closed are usable only for the first connection

server.connected is resolved on first connection and when connection is closed and re-opened again, it doesn't work properly.
The same goes for server.closed.

Example:

const server = new WS("ws://localhost:1234");

let totalConnections = 0;

server.on("connection", () => {
  totalConnections++;
});

const clientOne = new WebSocket("ws://localhost:1234");

await server.connected;

expect(totalConnections).toBe(1); // OK

clientOne.close();

await server.closed;

const clientTwo = new WebSocket("ws://localhost:1234");

await server.connected;

expect(totalConnections).toBe(2); // ERROR

Example from Docs doesn't work

@romgain huge thank you for such a great idea of a package for convinient testing websockets ๐Ÿ‘ I've just fell in love with it while reading the docs ๐Ÿ˜

But unfortunately it seems that an example from the doc doesn't work for me:

import WebSocket from 'ws'
import JestWebsocketMock from 'jest-websocket-mock'

test("the server keeps track of received messages, and yields them as they come in", async () => {
  const server = new JestWebsocketMock("ws://localhost:1234");
  const client = new WebSocket("ws://localhost:1234");

  await server.connected;
  client.send("hello");
  await expect(server).toReceiveMessage("hello");
  expect(server).toHaveReceivedMessages(["hello"]);
});

Jest fails with this message:
connect ECONNREFUSED 127.0.0.1:1234

I suppose my client instanse can't manage to connect to server mock.

Please, could you help me to solve this issue?

Thanks again for a good project

Reconnection always fails after an error

Trying to reconnect after an error always fails. It's particularly useful for testing auto-reconnect mechanisms.

const server = new WS('ws://localhost:8080');

new WebSocket('ws://localhost:8080');
await server.connected;
server.close();
await server.closed;

// Fails
new WebSocket('ws://localhost:8080');
await server.connected;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.