nikolalsvk/pusher-js-mock

View on GitHub
README.md

Summary

Maintainability
Test Coverage
<p align="center">
  <img src='https://raw.githubusercontent.com/nikolalsvk/pusher-js-mock/master/logo.jpg' alt="pusher-js-mock logo" />

  <h1 align="center">👋 Welcome to pusher-js-mock</h1>

  <p align="center">
    <a href="https://badge.fury.io/js/pusher-js-mock" target="_blank">
     <img src="https://badge.fury.io/js/pusher-js-mock.svg" alt="npm version" />
    </a>
    <a href="https://semaphoreci.com/nikolalsvk/pusher-js-mock" target="_blank">
     <img src="https://semaphoreci.com/api/v1/nikolalsvk/pusher-js-mock/branches/master/shields_badge.svg" alt="Build Status" />
    </a>
    <a href="https://codeclimate.com/github/nikolalsvk/pusher-js-mock" target="_blank">
     <img src="https://codeclimate.com/github/nikolalsvk/pusher-js-mock/badges/gpa.svg" alt="Code Climate" />
    </a>
    <a href="https://codeclimate.com/github/nikolalsvk/pusher-js-mock/coverage" target="_blank">
     <img src="https://codeclimate.com/github/nikolalsvk/pusher-js-mock/badges/coverage.svg" alt="Test Coverage" />
    </a>
    <a href="https://www.codetriage.com/nikolalsvk/pusher-js-mock" target="_blank">
     <img src="https://www.codetriage.com/nikolalsvk/pusher-js-mock/badges/users.svg" alt="Open Source Helpers" />
    </a>
  </p>
  <p align="center">
    Mock <a href="https://github.com/pusher/pusher-js">Pusher.js</a> in your JavaScript tests with ease.
  </p>
</p>

## Installing ⏬

Using yarn:

```
yarn add --dev pusher-js-mock
```

Or using npm:

```
npm install -D pusher-js-mock
```

## Usage 🛠

- [Emitting an event 📶](#emitting-an-event-)
- [Listening for an event 👂](#listening-for-an-event-)
- [Emitting an event from connection 📶](#emitting-an-event-from-connection-)
- [Listening for an event from connection 👂](#listening-for-an-event-from-connection-)
- [Stubbing Pusher when imported from pusher-js package 📦](#stubbing-pusher-when-imported-from-pusher-js-package-)
- [Stubbing Pusher when used as a global variable 🌍](#stubbing-pusher-when-used-as-a-global-variable-)
- [Mocking presence channels](#mocking-presence-channels)
  - [Using custom authorizer](#using-custom-authorizer)
  - [Pusher events emitted by presence channels](#pusher-events-emitted-by-presence-channels)
- [Using with Vitest](#using-with-vitest)

For more detailed examples, check out [`examples` directory](https://github.com/nikolalsvk/pusher-js-mock/tree/master/examples)
inside the project!

Also, you can check out the
[Docs](https://nikolalsvk.github.io/pusher-js-mock/) for even more information.

### Emitting an event 📶

If you need to mock a Pusher object in your tests that can
subscribe to channel, it's best to use PusherMock.

```javascript
import { PusherMock } from "pusher-js-mock";

// initializing PusherMock
const pusher = new PusherMock();

// subscribing to a Pusher channel
const channel = pusher.subscribe("my-channel");

// emitting an event
channel.emit("event-name");
```

### Listening for an event 👂

If you want to check whether your callback is getting called properly, you can
bind a callback to your channel, and then emit an event.

```javascript
import { PusherMock } from "pusher-js-mock";

describe("listening for an event", () => {
  // initializing PusherMock
  const pusher = new PusherMock();

  // subscribing to a Pusher channel
  const channel = pusher.subscribe("my-channel");

  // define and attach a listener
  const listener = jest.fn();
  channel.bind("event-name", listener);

  // emitting an event
  channel.emit("event-name");

  // Expect listener to have been called
  expect(listener).toHaveBeenCalled();
});
```

### Emitting an event from connection 📶

The connection within pusher is mocked and can be used much like a channel channel. There's no need to subscribe to subscription as it's subscribed by default on pusher.

```javascript
import { PusherMock } from "pusher-js-mock";

// initializing PusherMock
const pusher = new PusherMock();

// emitting connection event
pusher.connection.emit("event-name");
```

### Listening for an event from connection 👂

As with channels, you can also listen to connection for events.

```javascript
import { PusherMock } from "pusher-js-mock";

describe("listening for an event", () => {
  // initializing PusherMock
  const pusher = new PusherMock();

  // define and attach a listener
  const listener = jest.fn();
  pusher.connection.bind("event-name", listener);

  // emitting an event
  pusher.connection.emit("event-name");

  // Expect listener to have been called
  expect(listener).toHaveBeenCalled();
});
```

### Stubbing Pusher when imported from pusher-js package 📦

If you're using Pusher in your code in this or similar manner:

```javascript
import Pusher from "pusher-js";
```

You will need to mock Pusher in a specific way.

I suggest you use [Jest](https://jestjs.io/) to test your code.
To do this in Jest, you'll need something like this:

```javascript
jest.mock("pusher-js", () => {
  const Pusher = require("pusher-js-mock").PusherMock;
  return Pusher;
});
```

If you have tips on how to mock this using other testing frameworks, please
submit an issue or a pull request.

### Stubbing Pusher when used as a global variable 🌍

This shows how to stub a pusher if you're attaching it to window object in your
project. If you're attaching a PusherFactory to a `window` object like this in
your code:

```javascript
window.PusherFactory = {
  pusherClient: function(pusherKey) {
    return new Pusher(pusherKey);
  }
};
```

It's best for you to use PusherFactoryMock.

```javascript
import { PusherFactoryMock } from "pusher-js-mock";

// initialize instance of PusherFactoryMock
const pusherFactoryMock = new PusherFactoryMock();
// replace it with the object that is attached to a window
window.PusherFactory = pusherFactoryMock;

// get the Pusher client reference
pusher = pusherFactoryMock.pusherClient();
```

This way you'll just replace your PusherFactory with PusherFactoryMock.

### Mocking presence channels

This package also supports using presence channels for multiple clients. The
mock will automatically detect when `presence-` is in the channel name and
return a presence channel with `channel.members` filled out as expected. You
can pass in IDs and info via a custom authorizer, just as you would with the
real package.

#### Using custom authorizer

If you want, you can pass in a custom authorizer when creating a Pusher client.

```js
// create-client.js
import Pusher from "pusher-js";
import { getAuthSomehow } from "./getAuthSomehow";

export const createClient = ({ id, info }) =>
  new Pusher("APP_KEY", {
    cluster: "APP_CLUSTER",
    // see https://github.com/pusher/pusher-js#authorizer-function
    authorizer: ({ name }) => ({
      authorize: (socketId, callback) => {
        const auth = getAuthSomehow(id, info);
        callback(false, auth);
      }
    })
  });

export default createClient;
```

```js
// create-client.spec.js
import createClient from "../create-client";

// mock the authorize function and pusher
jest.mock("pusher-js", () => require("pusher-js-mock"));
jest.mock("../getAuthSomehow", () => ({
  getAuthSomehow: (id, info) => ({ id, info })
}));

it("should create a presence channel", async () => {
  // arrange: create pusher client
  const pusher = createClient({ id: "my-id", info: { role: "moderator" } });

  // act: required to ensure pusher events are called, i.e. pusher:member_added
  const presenceChannel = await pusher.subscribe("presence-channel");

  // assert: presenceChannel has the properties we expect it to.
  expect(presenceChannel.members.myID).toBe("my-id");
  expect(presenceChannel.members.me).toEqual({
    id: "my-id",
    info: { role: "moderator" }
  });
  expect(presenceChannel.members.members).toEqual({
    "my-id": { role: "moderator" }
  });
});
```

[Check out a code example of using presence channels](https://github.com/nikolalsvk/pusher-js-mock/tree/master/examples/presence-channels)

#### Pusher events emitted by presence channels

The mocked Pusher instance will also emit pusher internal events
`pusher:subscription_succeeded`, `pusher:member_added` and
`pusher:member_removed` to the relevant clients:

```js
it("should emit presence-channel events", async () => {
  const client = createClient({ id: "my-id" });
  const channel = client.subscribe("presence-channel");
  const listener = jest.fn();

  /**
   * On bind, pusher:subscription_succeded will trigger
   * for the client subscribing. Other clients will be
   * notified via pusher:member_added as below.
   */
  await channel.bind("pusher:subscription_succeeded", listener);
  expect(listener).toHaveBeenCalledTimes(1);

  /**
   * Create and subscribe a new client that will trigger the
   * pusher:member_added event. This only gets triggered for
   * clients are not the client subscribing
   */
  channel.bind("pusher:member_added", listener);
  const otherClient = createClient({ id: "your-id" });
  await otherClient.subscribe("presence-channel");
  expect(listener).toHaveBeenCalledTimes(2);

  /**
   * Unsubscribe the otherClient to trigger pusher:member_removed.
   * This only gets triggered for clients that are not the client
   * unsubscribing.
   */
  channel.bind("pusher:member_removed", listener);
  await otherClient.unsubscribe("presence-channel");
  expect(listener).toHaveBeenCalledTimes(3);
});
```

### Using with Vitest

You can mock Pusher.js in Vitest environment like this:

```js
vi.mock("pusher-js", () => {
  return {
    __esModule: true,
    default: require("pusher-js-mock").PusherMock,
  };
});
```

### [Code of Conduct](CODE_OF_CODUCT.md)

### [Contributing](CONTRIBUTING.md)

### Credits

Photo by [Octavian Rosca on Unsplash](https://unsplash.com/@tavi004)