Home

Awesome

<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 🛠

For more detailed examples, check out examples directory inside the project!

Also, you can check out the Docs 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.

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.

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.

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.

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:

import Pusher from "pusher-js";

You will need to mock Pusher in a specific way.

I suggest you use Jest to test your code. To do this in Jest, you'll need something like this:

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:

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

It's best for you to use PusherFactoryMock.

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.

// 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;
// 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

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:

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:

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

Code of Conduct

Contributing

Credits

Photo by Octavian Rosca on Unsplash