diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..8727aab --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,89 @@ +version: 2 + +aliases: + # ------------------------- + # ALIASES: Caches + # ------------------------- + - &restore-deps-cache + key: deps-cache-{{ checksum "package-lock.json" }} + + - &save-deps-cache + key: deps-cache-{{ checksum "package-lock.json" }} + paths: + - ~/client-js/node_modules + + # ------------------------- + # ALIASES: Branch Filters + # ------------------------- + - &filter-only-master + branches: + only: master + - &filter-only-semantic-pr + branches: + only: /^(pull|dependabot|fix|feat)\/.*$/ + +defaults: &defaults + working_directory: ~/client-js + +jobs: + test: + <<: *defaults + docker: + - image: circleci/node:10 + steps: + - checkout + - restore_cache: *restore-deps-cache + - run: npm install + - run: npm install codecov + - run: npm test + - run: ./node_modules/.bin/codecov + - save_cache: *save-deps-cache + + build: + <<: *defaults + docker: + - image: circleci/node:10 + steps: + - checkout + - restore_cache: *restore-deps-cache + - run: npm install + - run: npm run build + - save_cache: *save-deps-cache + + release: + <<: *defaults + docker: + - image: circleci/node:10 + steps: + - checkout + - restore_cache: *restore-deps-cache + - run: npm install + - run: npm run build + - run: npx semantic-release + - save_cache: *save-deps-cache + +workflows: + version: 2 + analysis: + jobs: + - test: + filters: *filter-only-semantic-pr + - build: + filters: *filter-only-semantic-pr + + release: + jobs: + - test: + filters: *filter-only-master + - build: + filters: *filter-only-master + - hold: + filters: *filter-only-master + type: approval + requires: + - test + - build + - release: + filters: *filter-only-master + requires: + - hold diff --git a/src/RequestManager.test.ts b/src/RequestManager.test.ts index a33eef2..d666f52 100644 --- a/src/RequestManager.test.ts +++ b/src/RequestManager.test.ts @@ -21,6 +21,12 @@ describe("client-js", () => { return c.connect(); }); + it("can close", () => { + const transport = new EventEmitterTransport("foo://unique-uri"); + const c = new RequestManager([transport]); + c.close(); + }); + it("can send a request", (done) => { const transport = new EventEmitterTransport("foo://unique-uri"); const c = new RequestManager([transport]); @@ -33,4 +39,12 @@ describe("client-js", () => { c.request("foo", []); }); + it("can handle onData", () => { + const transport = new EventEmitterTransport("foo://unique-uri"); + const c = new RequestManager([transport]); + const data = "{\"result\": \"bar\", \"id\": 1}"; + c.request("foo", []); + c.onData(data); + }); + }); diff --git a/src/RequestManager.ts b/src/RequestManager.ts index 7df1644..9f7c69e 100644 --- a/src/RequestManager.ts +++ b/src/RequestManager.ts @@ -33,8 +33,10 @@ class RequestManager { return; } // call request callback for id - this.requests[parsedData.id](parsedData); - delete this.requests[parsedData.id]; + if (this.requests[parsedData.id]) { + this.requests[parsedData.id](parsedData); + delete this.requests[parsedData.id]; + } } public async request(method: string, params: any): Promise { await this.connectPromise; diff --git a/src/__mocks__/isomorphic-fetch.ts b/src/__mocks__/isomorphic-fetch.ts new file mode 100644 index 0000000..d24f65a --- /dev/null +++ b/src/__mocks__/isomorphic-fetch.ts @@ -0,0 +1,10 @@ +const Fetch = (url: string, options: any): Promise => { + const resultPromise = { + text: () => { + return Promise.resolve(options.body); + }, + }; + return Promise.resolve(resultPromise); +}; + +export default Fetch; diff --git a/src/__mocks__/isomorphic-ws.ts b/src/__mocks__/isomorphic-ws.ts new file mode 100644 index 0000000..a9763dc --- /dev/null +++ b/src/__mocks__/isomorphic-ws.ts @@ -0,0 +1,28 @@ +class WebSocket { + private callbacks: any; + constructor(uri: string, props: any) { + this.callbacks = {}; + } + public addEventListener(eventName: string, callback: any) { + this.callbacks[eventName] = callback; + if (eventName === "open") { + setTimeout(() => { + callback(); + }, 10); + } + } + public removeEventListener(eventName: string, callback: any) { + delete this.callbacks[eventName]; + } + public send(data: any) { + Object.entries(this.callbacks).forEach(([eventName, callback]: [string, any]) => { + if (eventName === "message") { + callback({data}); + } + }); + } + public close() { + this.callbacks = {}; + } +} +export default WebSocket; diff --git a/src/transports/HTTPTransport.test.ts b/src/transports/HTTPTransport.test.ts new file mode 100644 index 0000000..7c57d4e --- /dev/null +++ b/src/transports/HTTPTransport.test.ts @@ -0,0 +1,21 @@ +import HTTPTransport from "./HTTPTransport"; + +describe("HTTPTransport", () => { + it("can connect", () => { + const wst = new HTTPTransport("http://localhost:8545"); + return wst.connect(); + }); + it("can close", () => { + const wst = new HTTPTransport("http://localhost:8545"); + wst.close(); + }); + it("can send and receive data", (done) => { + const wst = new HTTPTransport("http://localhost:8545"); + wst.onData((data: any) => { + const d = JSON.parse(data); + expect(d.foo).toEqual("bar"); + done(); + }); + wst.sendData(JSON.stringify({foo: "bar"})); + }); +}); diff --git a/src/transports/WebSocketTransport.test.ts b/src/transports/WebSocketTransport.test.ts new file mode 100644 index 0000000..79845b8 --- /dev/null +++ b/src/transports/WebSocketTransport.test.ts @@ -0,0 +1,21 @@ +import WebSocketTransport from "./WebSocketTransport"; + +describe("WebSocketTransport", () => { + it("can connect", () => { + const wst = new WebSocketTransport("http://localhost:8545"); + return wst.connect(); + }); + it("can close", () => { + const wst = new WebSocketTransport("http://localhost:8545"); + wst.close(); + }); + it("can send and receive data", (done) => { + const wst = new WebSocketTransport("http://localhost:8545"); + wst.onData((data: any) => { + const d = JSON.parse(data); + expect(d.foo).toEqual("bar"); + done(); + }); + wst.sendData(JSON.stringify({foo: "bar"})); + }); +}); diff --git a/src/transports/WebSocketTransport.ts b/src/transports/WebSocketTransport.ts index 5215258..dbcdd47 100644 --- a/src/transports/WebSocketTransport.ts +++ b/src/transports/WebSocketTransport.ts @@ -1,10 +1,10 @@ -import WebSocket from "isomorphic-ws"; +import WS from "isomorphic-ws"; import ITransport from "./Transport"; class WebSocketTransport implements ITransport { - public connection: WebSocket; + public connection: WS; constructor(uri: string) { - this.connection = new WebSocket(uri); + this.connection = new WS(uri); } public connect(): Promise { return new Promise((resolve, reject) => {