Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ISSUE-77): Fix presence channel members property #78

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/__tests__/pusher-js-mock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ describe("PusherMock", () => {
expect(pusherMock.channel("my-channel")).toBeDefined();
});

it("is possible to access pusher client through the channel object", () => {
expect(pusherMock.channel("my-channel").pusher).toBeDefined();
});

it("adds new channel to channels object", () => {
pusherMock.channel("my-channel");
expect(pusherMock.channels).toMatchObject({ "my-channel": {} });
Expand Down
19 changes: 17 additions & 2 deletions src/__tests__/pusher-presence-channel-mock.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PusherMock, PusherPresenceChannelMock } from "../";
import { isPresenceChannel } from "../pusher-presence-channel-mock";

describe("PusherPresenceChannelMock", () => {
let channelMock: PusherPresenceChannelMock;
Expand Down Expand Up @@ -51,14 +52,28 @@ describe("Proxied PusherPresenceChannelMock", () => {
client = createClient("my-id", {});
otherClient = createClient("your-id", {});

proxiedChannelMock = client.subscribe(PRESENCE_CHANNEL);
otherProxiedChannelMock = otherClient.subscribe(PRESENCE_CHANNEL);
const channel = client.subscribe(PRESENCE_CHANNEL);
const otherChannel = otherClient.subscribe(PRESENCE_CHANNEL);

if (!isPresenceChannel(channel) || !isPresenceChannel(otherChannel)) return;

proxiedChannelMock = channel;
otherProxiedChannelMock = otherChannel;
});

it("doesn't proxy class members it doesn't care about", () => {
expect(proxiedChannelMock.subscribed).toBe(true);
});

it("me and myID values should be different when accessed from different channel instances", () => {
expect(proxiedChannelMock.members.myID).not.toBe(
otherProxiedChannelMock.members.myID
);
expect(proxiedChannelMock.members.me).not.toBe(
otherProxiedChannelMock.members.me
);
});

it("add new members to the channel", () => {
expect(proxiedChannelMock.members.count).toBe(2);
expect(proxiedChannelMock.members.get("my-id")).toEqual({
Expand Down
33 changes: 33 additions & 0 deletions src/proxy-channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { PusherChannelMock, PusherMock } from ".";

/**
* Create the proxied channel
* @param {PusherChannelMock} channel the channel to be proxied
* @param {PusherMock} client the client we'll use to proxy the channel
* @returns {Proxy<PusherChannelMock>} the proxied channel
*/
export const proxyChannel = (
channel: PusherChannelMock,
client: PusherMock
) => {
const handler = {
/**
* Proxies a channel and augments it with client specific information
* @param target The channel we're proxying
* @param key The attribute, property or method we're trapping
* @returns {mixed} the result of the trapped function
*/
get(target: PusherChannelMock, key: keyof PusherChannelMock) {
switch (key) {
case "IS_PROXY":
return true;
case "pusher":
return client;
default:
return target[key];
}
}
};

return new Proxy(channel, handler);
};
26 changes: 20 additions & 6 deletions src/proxy-presence-channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,24 @@ export interface IProxiedCallback {
* @returns {Members} The proxied members property on the channel
*/
const proxyMembers = (original: Members, client: PusherMock) => {
original.myID = client.id;
original.me = {
id: client.id,
info: client.info
};
return original;
return new Proxy(original, {
get(
target: PusherPresenceChannelMock["members"],
key: keyof PusherPresenceChannelMock["members"]
) {
switch (key) {
case "me":
return {
id: client.id,
info: client.info
};
case "myID":
return client.id;
default:
return target[key];
}
}
});
};

/**
Expand Down Expand Up @@ -94,6 +106,8 @@ export const proxyPresenceChannel = (
/** Attach this client's info the member specific calls */
case "members":
return proxyMembers(target.members, client);
case "pusher":
return client;
/** Attach the owner of the callback so we can ignore it in future */
case "bind":
return proxyBind(target, client);
Expand Down
4 changes: 4 additions & 0 deletions src/pusher-channel-mock.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import PusherMock from "./pusher-js-mock";

/** Interface for all the callbacks each Pusher event could potentially have */
interface ICallbacks {
[key: string]: Array<() => void>;
Expand All @@ -8,6 +10,8 @@ class PusherChannelMock {
public name: string;
public callbacks: ICallbacks;
public subscribed: boolean = true;
public IS_PROXY?: boolean;
public pusher?: PusherMock;

/** Initialize PusherChannelMock with callbacks object. */
constructor(name: string = "public-channel") {
Expand Down
5 changes: 3 additions & 2 deletions src/pusher-js-mock-instance.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { proxyChannel } from "./proxy-channel";
import { proxyPresenceChannel } from "./proxy-presence-channel";
import PusherChannelMock from "./pusher-channel-mock";
import PusherMock from "./pusher-js-mock";
Expand All @@ -24,7 +25,7 @@ class PusherMockInstance {
* @returns {PusherChannelMock} PusherChannelMock object that represents channel
*/
public channel(name: string, client: PusherMock = new PusherMock()) {
const presenceChannel = name.includes("presence-");
const presenceChannel = name.startsWith("presence-");
if (!this.channels[name]) {
this.channels[name] = presenceChannel
? new PusherPresenceChannelMock(name)
Expand All @@ -33,7 +34,7 @@ class PusherMockInstance {

return presenceChannel
? proxyPresenceChannel(this.channels[name], client)
: this.channels[name];
: proxyChannel(this.channels[name], client);
}

/**
Expand Down
10 changes: 7 additions & 3 deletions src/pusher-js-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { PusherChannelMock } from ".";
import { IProxiedCallback } from "./proxy-presence-channel";
import { emitConnectionEvents, emitDisconnectionEvents } from "./pusher-events";
import PusherMockInstance from "./pusher-js-mock-instance";
import { isPresenceChannel } from "./pusher-presence-channel-mock";

export interface IPusherMockOptions {
authorizer: (
Expand Down Expand Up @@ -49,9 +50,11 @@ class PusherMock {
public subscribe(name: string) {
const channel = PusherMockInstance.channel(name, this);

if (name.includes("presence-")) {
if (isPresenceChannel(channel)) {
this.config?.authorizer
? this.config.authorizer({} as any).authorize(channel, this.setAuthInfo)
? this.config
.authorizer({} as any)
.authorize(channel as any, this.setAuthInfo)
: this.setAuthInfo(false, {
id: Math.random()
.toString(36)
Expand All @@ -69,8 +72,9 @@ class PusherMock {
* @param {String} name - name of the channel.
*/
public unsubscribe(name: string) {
const channel = PusherMockInstance.channel(name, this);
if (name in PusherMockInstance.channels) {
if (name.includes("presence-")) {
if (isPresenceChannel(channel)) {
this.unsubscribePresence(name);
} else {
// public channel
Expand Down
6 changes: 6 additions & 0 deletions src/pusher-presence-channel-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,10 @@ class PusherPresenceChannelMock extends PusherChannelMock {
}
}

export function isPresenceChannel(
channel: PusherChannelMock
): channel is PusherPresenceChannelMock {
return channel.name.startsWith("presence-");
}

export default PusherPresenceChannelMock;