From c2cb9b1069be7345bf36c80af1fb6fd0e686090e Mon Sep 17 00:00:00 2001 From: Ardalan Amini Date: Sun, 6 Dec 2020 01:26:40 +0330 Subject: [PATCH] test: Improve test coverage --- .github/workflows/test.yml | 3 +-- CHANGELOG.md | 38 -------------------------- README.md | 38 ++++++++++++++++++++------ __tests__/emit.ts | 8 ++++++ __tests__/listeners.ts | 13 ++++++--- __tests__/once.ts | 46 +++++++++++++++++++++++++++----- __tests__/prependListener.ts | 20 ++++++++++++++ __tests__/prependOnceListener.ts | 25 +++++++++++++++++ __tests__/removeAllListeners.ts | 6 +++++ __tests__/removeListener.ts | 6 +++++ benchmarks/README.md | 2 +- package-lock.json | 8 +++--- package.json | 8 +++--- src/EventEmitter.ts | 1 - tslint.json | 30 --------------------- 15 files changed, 154 insertions(+), 98 deletions(-) delete mode 100644 CHANGELOG.md create mode 100644 __tests__/prependListener.ts create mode 100644 __tests__/prependOnceListener.ts delete mode 100644 tslint.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 304926a..2afec84 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,8 +17,7 @@ jobs: strategy: matrix: os: [ macos-latest, windows-latest, ubuntu-latest ] - node-version: [ 14.x ] -# node-version: [ 8.x, 10.x, 12.x, 14.x ] + node-version: [ 8.x, 10.x, 12.x, 14.x ] env: OS: ${{ matrix.os }} diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index fd5b052..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,38 +0,0 @@ -# Changelog - -## Emojis - -- New Features -> :zap: -- Enhancements -> :star2: -- Breaking Changes -> :boom: -- Dependency Changes -> :package: -- Bugs -> :beetle: -- Pull Requests -> :book: -- Documents -> :mortar_board: -- Tests -> :eyeglasses: - ---- - -## [v1.1.4](https://github.com/foxifyjs/events/releases/tag/v1.1.4) - _(2019-05-22)_ - -- :beetle: Fixed a bug related to extending `on` and `off` methods through `addListener` and `removeListener`. - -## [v1.1.3](https://github.com/foxifyjs/events/releases/tag/v1.1.3) - _(2019-04-13)_ - -- :beetle: Fixed a bug related to extended (with `&` instead of `extends`) strict events. - -## [v1.1.2](https://github.com/foxifyjs/events/releases/tag/v1.1.2) - _(2019-04-12)_ - -- :star2: Added Ability to define strict events. - -## [v1.1.1](https://github.com/foxifyjs/events/releases/tag/v1.1.1) - _(2019-04-10)_ - -- :star2: Minimize code for browsers. - -## [v1.1.0](https://github.com/foxifyjs/events/releases/tag/v1.1.0) - _(2019-04-04)_ - -- :zap: Now we `throw` an error when you emit an `error` event and nobody is listening. - -## [v1.0.0](https://github.com/foxifyjs/events/releases/tag/v1.0.0) - _(2019-03-24)_ - -- :tada: First Release diff --git a/README.md b/README.md index c3e7b2e..c7e511a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Events +# Events `@foxify/events` is a high performance EventEmitter alternative for Node.js and browser that has been optimized to be faster than the native version, (why not?!). @@ -35,6 +35,8 @@ This module is API compatible with the EventEmitter that ships by default with N - [Strict events](#strict-events) - [Contextual emits](#contextual-emits) - [Benchmarks](#benchmarks) +- [Tests](#tests) +- [Coverage](#coverage) - [Versioning](#versioning) - [Authors](#authors) - [License](#license) @@ -47,8 +49,16 @@ npm i @foxify/events ## Usage +JavaScript: + ```js -const { EventEmitter } = require("@foxify/events"); +const EventEmitter = require("@foxify/events").default; +``` + +TypeScript: + +```ts +import EventEmitter from "@foxify/events"; ``` For the API documentation, please follow the official Node.js [documentation](https://nodejs.org/api/events.html). @@ -71,14 +81,14 @@ class Emitter extends EventEmitter { const eventEmitter = new Emitter(); -// Throws an error, since this event requires the "bar" argument of type "string" -eventEmitter.emit("foo"); +// Works just fine. so don't worry about "ImplicitAny" config, since type of "bar" is defined as "string" +eventEmitter.on("foo", bar => 1); -// Works just fine +// Throws an error (TS compile time), since this event requires the "bar" argument of type "string" eventEmitter.emit("foo"); -// Works just fine. so don't worry about "ImplicitAny" config, since type of "bar" is defined as "string" -eventEmitter.on("foo", bar => 1); +// Works just fine +eventEmitter.emit("foo", "bar"); ``` @@ -107,10 +117,22 @@ eventEmitter.prependOnceListener("event:5", emitted, context); ## Benchmarks -```bash +```shell npm run benchmarks ``` +## Tests + +```shell +npm test +``` + +## Coverage + +```shell +npm run coverage +``` + ## Versioning We use [SemVer](http://semver.org) for versioning. For the versions available, see the [tags on this repository](https://github.com/foxifyjs/events/tags). diff --git a/__tests__/emit.ts b/__tests__/emit.ts index 281c13c..ff70053 100644 --- a/__tests__/emit.ts +++ b/__tests__/emit.ts @@ -172,3 +172,11 @@ it("emits to all event listeners", () => { expect(pattern.join(";")).toBe("foo1;foo2"); }); + +it("should throw the error", () => { + const e = new EventEmitter(); + + const error = new Error("test"); + + expect(() => e.emit("error", error)).toThrow(error); +}); diff --git a/__tests__/listeners.ts b/__tests__/listeners.ts index 646b2bf..66fb295 100644 --- a/__tests__/listeners.ts +++ b/__tests__/listeners.ts @@ -15,9 +15,16 @@ it("returns an array of function", () => { e.on("foo", foo); - expect(e.listeners("foo")).toBeInstanceOf(Array); - expect(e.listeners("foo").length).toBe(1); - expect(e.listeners("foo")).toEqual([foo]); + // eslint-disable-next-line @typescript-eslint/no-empty-function + function bar() {} + + e.on("foo", bar); + + const listeners = e.listeners("foo"); + + expect(listeners).toBeInstanceOf(Array); + expect(listeners.length).toBe(2); + expect(listeners).toEqual([foo, bar]); }); it("is not vulnerable to modifications", () => { diff --git a/__tests__/once.ts b/__tests__/once.ts index 6089270..ca9b5ec 100644 --- a/__tests__/once.ts +++ b/__tests__/once.ts @@ -1,12 +1,11 @@ import EventEmitter from "../src"; -it("only emits it once", () => { +it("should only emits it once (case 1)", () => { const e = new EventEmitter(); - let calls = 0; - e.once("foo", () => { - calls++; - }); + const listener = jest.fn(); + + e.once("foo", listener); e.emit("foo"); e.emit("foo"); @@ -15,7 +14,35 @@ it("only emits it once", () => { e.emit("foo"); expect(e.listeners("foo").length).toBe(0); - expect(calls).toBe(1); + expect(listener).toBeCalledTimes(1); +}); + +it("should only emits it once (case 2)", () => { + const e = new EventEmitter(); + + const listener = jest.fn(); + + e.once("foo", listener); + + e.emit("foo"); + e.emit("foo"); + + expect(e.listeners("foo").length).toBe(0); + expect(listener).toBeCalledTimes(1); +}); + +it("should only emits it once (case 3)", () => { + const e = new EventEmitter(); + + const listener = jest.fn(); + + e.once("foo", listener); + e.once("foo", listener); + + e.emit("foo"); + + expect(e.listeners("foo").length).toBe(0); + expect(listener).toBeCalledTimes(2); }); it("only emits once if emits are nested inside the listener", () => { @@ -63,6 +90,8 @@ it("only emits once for multiple events", () => { }); it("only emits once with context", (done) => { + expect.assertions(4); + const context = { foo: "bar" }; const e = new EventEmitter(); @@ -75,5 +104,8 @@ it("only emits once with context", (done) => { done(); }, context, - ).emit("foo", "bar"); + ); + + expect(e.emit("foo", "bar")).toBe(true); + expect(e.listenerCount("foo")).toBe(0); }); diff --git a/__tests__/prependListener.ts b/__tests__/prependListener.ts new file mode 100644 index 0000000..c48c064 --- /dev/null +++ b/__tests__/prependListener.ts @@ -0,0 +1,20 @@ +import EventEmitter from "../src"; + +it("should prepend the listener", () => { + const e = new EventEmitter(); + const calls: number[] = []; + + e.on("foo", () => { + calls.push(1); + }); + + e.prependListener("foo", () => { + calls.push(2); + }); + + e.emit("foo"); + + expect(e.listeners("foo").length).toBe(2); + expect(e.listenerCount("foo")).toBe(2); + expect(calls).toEqual([2, 1]); +}); diff --git a/__tests__/prependOnceListener.ts b/__tests__/prependOnceListener.ts new file mode 100644 index 0000000..e273664 --- /dev/null +++ b/__tests__/prependOnceListener.ts @@ -0,0 +1,25 @@ +import EventEmitter from "../src"; + +it("should prepend the listener and remove it after first event", () => { + const e = new EventEmitter(); + const calls: number[] = []; + + e.on("foo", () => { + calls.push(1); + }); + + e.on("foo", () => { + calls.push(2); + }); + + e.prependOnceListener("foo", () => { + calls.push(3); + }); + + e.emit("foo"); + e.emit("foo"); + + expect(e.listeners("foo").length).toBe(2); + expect(e.listenerCount("foo")).toBe(2); + expect(calls).toEqual([3, 1, 2, 1, 2]); +}); diff --git a/__tests__/removeAllListeners.ts b/__tests__/removeAllListeners.ts index 4cd6755..274a5ff 100644 --- a/__tests__/removeAllListeners.ts +++ b/__tests__/removeAllListeners.ts @@ -54,3 +54,9 @@ it("removes all events, literally!", () => { expect(e.emit("bar")).toBe(false); expect(e.emit("aaa")).toBe(false); }); + +it("should not fail if the events don't exist", () => { + const e = new EventEmitter(); + + expect(e.removeAllListeners("not-found")).toEqual(e); +}); diff --git a/__tests__/removeListener.ts b/__tests__/removeListener.ts index 50c3dda..f690d14 100644 --- a/__tests__/removeListener.ts +++ b/__tests__/removeListener.ts @@ -38,3 +38,9 @@ it("removes only the first listener matching the specified listener", () => { expect(e.listeners("bar")).toEqual([bar]); expect(e.listeners("foo")).toEqual([]); }); + +it("should not fail if the events don't exist", () => { + const e = new EventEmitter(); + + expect(e.removeListener("not-found", () => 1)).toEqual(e); +}); diff --git a/benchmarks/README.md b/benchmarks/README.md index 71dd812..48566f8 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -1,6 +1,6 @@ # Benchmarks -```bash +```shell Starting benchmark add-remove.js ╔══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╗ diff --git a/package-lock.json b/package-lock.json index fbec73e..268717d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,11 +12,11 @@ "benchmarks" ], "devDependencies": { - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.16", "@types/node": "^14.14.10", - "@typescript-eslint/eslint-plugin": "^4.8.2", - "@typescript-eslint/parser": "^4.8.2", - "eslint": "^7.14.0", + "@typescript-eslint/eslint-plugin": "^4.9.0", + "@typescript-eslint/parser": "^4.9.0", + "eslint": "^7.15.0", "eslint-plugin-jest": "^24.1.3", "jest": "^26.6.3", "prettier": "^2.2.1", diff --git a/package.json b/package.json index 8358544..642ad31 100644 --- a/package.json +++ b/package.json @@ -44,11 +44,11 @@ "benchmarks": "find benchmarks -maxdepth 1 -name '*.js' -exec benchmarks/start.sh {} \\;" }, "devDependencies": { - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.16", "@types/node": "^14.14.10", - "@typescript-eslint/eslint-plugin": "^4.8.2", - "@typescript-eslint/parser": "^4.8.2", - "eslint": "^7.14.0", + "@typescript-eslint/eslint-plugin": "^4.9.0", + "@typescript-eslint/parser": "^4.9.0", + "eslint": "^7.15.0", "eslint-plugin-jest": "^24.1.3", "jest": "^26.6.3", "prettier": "^2.2.1", diff --git a/src/EventEmitter.ts b/src/EventEmitter.ts index 1eda7e5..118d7ff 100644 --- a/src/EventEmitter.ts +++ b/src/EventEmitter.ts @@ -220,6 +220,5 @@ export default EventEmitter; // eslint-disable-next-line @typescript-eslint/no-explicit-any function isArray(value: any): value is any[] { - // return typeof value.length === "number"; return value.length !== undefined; } diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 2144abc..0000000 --- a/tslint.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": ["tslint:recommended", "tslint-config-airbnb"], - "jsRules": {}, - "rules": { - "no-namespace": false, - "no-increment-decrement": false, - "no-parameter-reassignment": false, - "interface-name": false, - "prefer-array-literal": false, - "quotemark": [true, "double"], - "variable-name": [ - true, - "ban-keywords", - "check-format", - "allow-leading-underscore" - ], - "function-name": [ - true, - { - "method-regex": "^\\*?\\[?[a-zA-Z][\\w\\d\\.]*\\]?$", - "private-method-regex": "^\\*?\\[?_?[a-zA-Z][\\w\\d\\.]*\\]?$", - "protected-method-regex": "^\\*?\\[?_?[a-zA-Z][\\w\\d\\.]*\\]?$", - "static-method-regex": "^\\*?\\[?[a-zA-Z][\\w\\d\\.]*\\]?$", - "function-regex": "^\\*?\\[?[a-zA-Z][\\w\\d\\.]*\\]?$" - } - ] - }, - "rulesDirectory": ["src"] -}