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

WIP Features/20 fizz #105

Open
wants to merge 2 commits into
base: develop
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
8 changes: 4 additions & 4 deletions mock.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ wss.on('connection', (ws, request) => {
} else if (message.substr(4) === "ping") {
ws.send(message.replace("ping", "pong"));
}
try {
ws.send("hello");
} catch (e) {
}
// try {
// ws.send("hello");
// } catch (e) {
// }
});

}
Expand Down
2 changes: 1 addition & 1 deletion src/Shell.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ describe("Shell", () => {
setTimeout(done, 100);
});
it("should remove a non existing listener", () => {
expect(() => shell.removeEventListener("message", () => null)).to.not.throw();
expect(() => shell.removeEventListener("message", () => null)).to.not.throw();
});
it("should accept more than one listener", (done) => {
let i = 0;
Expand Down
112 changes: 56 additions & 56 deletions src/Shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,6 @@ import WebSocket from "./polyfill/WebSocket";

export abstract class Shell implements WebSocket {

/** The connection is not yet open. */
public readonly CONNECTING = WebSocket.CONNECTING;
/** The connection is open and ready to communicate. */
public readonly OPEN = WebSocket.OPEN;
/** The connection is in the process of closing. */
public readonly CLOSING = WebSocket.CLOSING;
/** The connection is closed or couldn't be opened. */
public readonly CLOSED = WebSocket.CLOSED;

/**
* A string indicating the type of binary data being transmitted by the connection.
* This should be either "blob" if DOM Blob objects are being used or "arraybuffer"
* if ArrayBuffer objects are being used.
*/
public binaryType: "blob" | "arraybuffer";

protected closing: boolean = false;
protected ws: WebSocket;
protected _readyState: number = WebSocket.CONNECTING;

private readonly forwardListener: (evt: Event) => boolean;
private _onerror: (ev: Event) => any;
private _onmessage: (ev: MessageEvent) => any;
private _onopen: (ev: Event) => any;
private _onclose: (ev: CloseEvent) => any;
private listeners: Dict<keyof IHydratedWebSocketEventMap,
Array<{
listener: (this: WebSocket,
ev: IHydratedWebSocketEventMap[keyof IHydratedWebSocketEventMap]) => any,
useCapture?: boolean,
}>> = new Dict();

constructor(ws?: WebSocket) {
if (!this.dispatchEvent) {
throw new TypeError("Failed to construct. Please use the 'new' operator");
}
this.forwardListener = this.dispatchEvent.bind(this);
if (ws) {
this.ws = ws;
this.forwardEvents();
}
}

/**
* An event listener to be called when the WebSocket connection's **readyState** changes to **CLOSED**.
* The listener receives a _CloseEvent_ named "close".
Expand Down Expand Up @@ -130,6 +87,49 @@ export abstract class Shell implements WebSocket {
return this.ws.protocol;
}

/** The connection is not yet open. */
public readonly CONNECTING = WebSocket.CONNECTING;
/** The connection is open and ready to communicate. */
public readonly OPEN = WebSocket.OPEN;
/** The connection is in the process of closing. */
public readonly CLOSING = WebSocket.CLOSING;
/** The connection is closed or couldn't be opened. */
public readonly CLOSED = WebSocket.CLOSED;

/**
* A string indicating the type of binary data being transmitted by the connection.
* This should be either "blob" if DOM Blob objects are being used or "arraybuffer"
* if ArrayBuffer objects are being used.
*/
public binaryType: "blob" | "arraybuffer";

protected closing: boolean = false;
protected ws: WebSocket;
protected _readyState: number = WebSocket.CONNECTING;
protected listenersDict: Dict<keyof IHydratedWebSocketEventMap,
Array<{
listener: (this: WebSocket,
ev: IHydratedWebSocketEventMap[keyof IHydratedWebSocketEventMap]) => any,
useCapture?: boolean,
}>> = new Dict();

private readonly forwardListener: (evt: Event) => boolean;
private _onerror: (ev: Event) => any;
private _onmessage: (ev: MessageEvent) => any;
private _onopen: (ev: Event) => any;
private _onclose: (ev: CloseEvent) => any;

constructor(ws?: WebSocket) {
if (!this.dispatchEvent) {
throw new TypeError("Failed to construct. Please use the 'new' operator");
}
this.forwardListener = this.dispatchEvent.bind(this);
if (ws) {
this.ws = ws;
this.forwardEvents();
}
}

/**
* Closes the WebSocket connection or connection attempt, if any.
* If the connection is already CLOSED, this method does nothing.
Expand All @@ -141,7 +141,7 @@ export abstract class Shell implements WebSocket {
* This string must be no longer than 123 bytes of UTF-8 text (not characters).
*/
public close(code: number = 1000, reason?: string) {
this.ws.close(code, reason);
this.ws.close(code, reason || "");
}

/**
Expand Down Expand Up @@ -189,14 +189,14 @@ export abstract class Shell implements WebSocket {
* If not specified, useCapture defaults to false.
*/
public addEventListener<K extends keyof IHydratedWebSocketEventMap>(type: K,
listener: (this: WebSocket,
ev: IHydratedWebSocketEventMap[K])
=> any,
listener:
(this: WebSocket,
ev: IHydratedWebSocketEventMap[K]) => any,
useCapture?: boolean): void {
let listeners = this.listeners.get(type);
let listeners = this.listenersDict.get(type);
if (!listeners) {
listeners = [];
this.listeners.set(type, listeners);
this.listenersDict.set(type, listeners);
}
listeners.push({ listener, useCapture });
}
Expand All @@ -210,13 +210,13 @@ export abstract class Shell implements WebSocket {
* listener or not. If this parameter is absent, a default value of false is assumed.
*/
public removeEventListener<K extends keyof IHydratedWebSocketEventMap>(type: K,
listener: (this: WebSocket,
ev: IHydratedWebSocketEventMap[K])
=> any,
listener: (
this: WebSocket,
ev: IHydratedWebSocketEventMap[K]) => any,
useCapture?: boolean): void {
const listeners = this.listeners.get(type);
const listeners = this.listenersDict.get(type);
if (listeners) {
this.listeners.set(
this.listenersDict.set(
type,
listeners.filter((l) => l.listener !== listener || l.useCapture !== useCapture),
);
Expand All @@ -234,8 +234,8 @@ export abstract class Shell implements WebSocket {
if (typeof method === "function") {
method.call(this, evt);
}
return (this.listeners.get(evt.type as keyof IHydratedWebSocketEventMap) || [])
.some(({ listener }) => listener.call(this, evt) === false) === void 0;
return (this.listenersDict.get(evt.type as keyof IHydratedWebSocketEventMap) || [])
.some(({listener}) => listener.call(this, evt) === false) === void 0;
}

protected forwardEvents<K extends keyof IHydratedWebSocketEventMap>(list?: K[]) {
Expand Down
117 changes: 117 additions & 0 deletions src/fizz/Fizz.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { expect } from "chai";
import WebSocket from "../polyfill/WebSocket";
import { expectEventually, rnd, sleep, supervisor, TIMEOUT_FACTOR } from "../wrench.spec";
import { Fizz } from "./Fizz";

let ws: WebSocket;
let testCase: string;

const testListener = (method: "addListener"|"on"|"once") => {

describe(`listener with ${method}`, () => {

it ("close", (done) => {
const fizz = new Fizz(ws);
fizz[method]("close", (...args: any[]) => {
expect(args).to.deep.equal([1000, ""]);
done();
});
fizz.close();
});

it.skip ("error", (done) => {
const fizz = new Fizz(ws);
fizz[method]("error", (...args: any[]) => {
expect(args).to.have.lengthOf(1);
expect(args[0]).to.be.instanceof(Error);
done();
});
});

it ("message", (done) => {
const fizz = new Fizz(ws);
fizz[method]("message", (...args: any[]) => {
expect(args).to.deep.equal(["pong"]);
done();
});
ws.send("ping");
});

it ("open", (done) => {
const fizz = new Fizz(new WebSocket(`ws://localtest.me:4752/${rnd()}`));
fizz[method]("open", (...args: any[]) => {
expect(args).to.deep.equal([]);
done();
});
});

});
};

describe("Fizz", () => {
before(async () => {
await expectEventually(() => supervisor.ws.readyState === WebSocket.OPEN,
"The supervisor failed to connect");
});

beforeEach((done) => {
testCase = rnd();
ws = new WebSocket(`ws://localtest.me:4752/${testCase}`);
ws.onopen = () => done();
});

afterEach(async () => {
if (ws) {
ws.close();
}
});

describe("addListener", () => {
testListener("addListener");
});

describe("on", () => {
testListener("on");
});

describe("once", () => {
testListener("once");
});

describe("prependListener", () => {

});

describe("prependOnceListener", () => {

});

describe("removeListener", () => {

});

describe("off", () => {

});

describe("removeAllListeners", () => {

});

describe("setMaxListeners", () => {

});

describe("getMaxListeners", () => {

});

describe("listeners", () => {

});

describe("rawListeners", () => {

});

});
87 changes: 87 additions & 0 deletions src/fizz/Fizz.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { EventEmitter } from "events";
import { Shell } from "../Shell";

export class Fizz extends Shell implements EventEmitter {

public addListener(event: string | symbol, listener: (...args: any[]) => void): this {
switch (event) {
case "close":
this.addEventListener("close", (evt: CloseEvent) => listener(evt.code, evt.reason));
break;
case "error":
this.addEventListener("error", (evt: ErrorEvent) => listener(evt.error));
break;
case "message":
this.addEventListener("message", (evt: MessageEvent) => listener(evt.data));
break;
case "open":
this.addEventListener("open", () => listener());
break;
default:
console.log("not implemented");
}
return this;
}

public on(event: string | symbol, listener: (...args: any[]) => void): this {
return this.addListener(event, listener);
}

public once(event: string | symbol, listener: (...args: any[]) => void): this {
return this.addListener(event, listener);
}

public prependListener(event: string | symbol, listener: (...args: any[]) => void): this {
return this.addListener(event, listener);
}

public prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this {
return this.addListener(event, listener);
}

public removeListener(event: string | symbol, listener: (...args: any[]) => void): this {
super.removeEventListener(event as any, listener);
return this;
}

public off(event: string | symbol, listener: (...args: any[]) => void): this {
return this.removeListener(event, listener);
}

public removeAllListeners(event?: string | symbol): this {
for (const key of this.listenersDict.keys()) {
this.listenersDict.get(key).length = 0;
}
return this;
}

public setMaxListeners(n: number): this {
return this;
}

public getMaxListeners(): number {
return 0;
}

public listeners(event: string | symbol): Array<() => void> {
return [];
}

public rawListeners(event: string | symbol): Array<() => void> {
return [];

}

public emit(event: string | symbol, ...args: any[]): boolean {
// return this.dispatchEvent(event, args);
return true;
}

public eventNames(): Array<string | symbol> {
return this.listenersDict.keys();
}

public listenerCount(type: string | symbol): number {
return this.listenersDict[type] ? this.listenersDict[type].length : 0;
}
}
Loading