From dd4bf59e416a72490ebcfd3a3622b988e0f81408 Mon Sep 17 00:00:00 2001 From: Florent Date: Sat, 6 Mar 2021 17:32:59 +0100 Subject: [PATCH] Improve tests a little bit --- src/listener-rss.ts | 6 +- tests/index-spec.ts | 230 ++++++++++++++++++++++++-------------------- 2 files changed, 129 insertions(+), 107 deletions(-) diff --git a/src/listener-rss.ts b/src/listener-rss.ts index 0e3b6c7..23f1c07 100644 --- a/src/listener-rss.ts +++ b/src/listener-rss.ts @@ -54,9 +54,7 @@ export class ListenerRss extends EventEmitter { * @return return a promise with the received data */ fetchRSS(): Promise> { - return this.parser.parseURL(this.address).catch((err) => { - throw new Error("bad address or no access : " + err); - }); + return this.parser.parseURL(this.address); } /** @@ -69,7 +67,7 @@ export class ListenerRss extends EventEmitter { const fun: () => void = () => { this.fetchRSS() .then((obj: { [key: string]: any }) => this.emit("update", obj)) - .catch((err) => this.emit("update_err", err)); + .catch((err) => this.emit("error", err)); }; (async () => { diff --git a/tests/index-spec.ts b/tests/index-spec.ts index bf2a11b..c2b1dd4 100644 --- a/tests/index-spec.ts +++ b/tests/index-spec.ts @@ -8,12 +8,12 @@ import { } from "./../src/index"; // Unit test +import assert from "assert"; import * as chai from "chai"; -import * as sinon from "ts-sinon"; +import sinon from "ts-sinon"; +import sinonChai from "sinon-chai"; import { ImportMock, InPlaceMockManager } from "ts-mock-imports"; -const sinonChai = require("sinon-chai"); - chai.use(sinonChai); const expect = chai.expect; @@ -29,9 +29,7 @@ describe("test class RSS: jsonfile", function () { }, }; - const mockedRSSOutput: Parser.Output<{ - "media:group": { [key: string]: string | [any] }; - }> = { + const mockedRSSOutput: Parser.Output = { items: [ { title: "my title 00", @@ -70,8 +68,8 @@ describe("test class RSS: jsonfile", function () { }; describe("Building Ytb listener", function () { - it("The build without issues (infosListener parameters)", function () { - let myListener = new Listeners(infosListener); + it("builds without issues (infosListener parameters)", function () { + const myListener = new Listeners(infosListener); // assertions // myListener data @@ -90,8 +88,9 @@ describe("test class RSS: jsonfile", function () { item: ["media:group", "media:group"], }); }); - it("The build without issues (raw infos : 4 params)", function () { - let myListener = new Listeners({ + + it("builds without issues (raw infos : 4 params)", function () { + const myListener = new Listeners({ name: "my-test-service", address: "fake.rss.service", timeloop: 15, @@ -118,8 +117,9 @@ describe("test class RSS: jsonfile", function () { item: ["media:group", "media:group"], }); }); - it("The build without issues (raw infos : just 2 params)", function () { - let myListener = new Listeners({ + + it("builds without issues (raw infos : just 2 params)", function () { + const myListener = new Listeners({ name: "my-test-service", address: "fake.rss.service", }); @@ -139,8 +139,9 @@ describe("test class RSS: jsonfile", function () { }); }); }); - it("The build without issues (raw infos : just 3 params (no custom fields))", function () { - let myListener = new Listeners({ + + it("builds without issues (raw infos: just 3 params (no custom fields))", function () { + const myListener = new Listeners({ name: "my-test-service", address: "fake.rss.service", timeloop: 15, @@ -160,8 +161,9 @@ describe("test class RSS: jsonfile", function () { item: [], }); }); + it("The build without issues (raw infos : just 3 params (no timeloop))", function () { - let myListener = new Listeners({ + const myListener = new Listeners({ name: "my-test-service", address: "fake.rss.service", customfields: { @@ -188,44 +190,36 @@ describe("test class RSS: jsonfile", function () { }); }); - describe("fetch some data", function () { - it("fetch without issues", function () { + describe("data fetching", function () { + it("fetches without issues", async function () { + // given const mockManager: InPlaceMockManager = ImportMock.mockClassInPlace( Parser ); - let stubTest = mockManager.mock("parseURL"); - stubTest.withArgs(infosListener.address).resolves(mockedRSSOutput); - stubTest - .withArgs("bad.rss.service") - .rejects(new Error("connect ECONNREFUSED 127.0.0.1:80")); + const stubParser = mockManager.mock("parseURL"); + stubParser.resolves(mockedRSSOutput); const myListener = new Listeners(infosListener); - // fetch - let res = myListener.fetchRSS(); + // when + const res = await myListener.fetchRSS(); - //assertion - // calls - return res - .then((obj: any) => { - expect(obj).to.be.eql(mockedRSSOutput); - }) - .catch((err) => { - expect(err).to.be.undefined; - }); + // then + expect(stubParser).to.have.been.calledOnce; + expect(stubParser).to.have.been.calledWith(infosListener.address); + expect(res).to.be.eql(mockedRSSOutput); }); - it("fetch with bad address", function () { + it("rejects when fetching fails", async function () { + // given const mockManager: InPlaceMockManager = ImportMock.mockClassInPlace( Parser ); - let stubParser = mockManager.mock("parseURL"); - stubParser.withArgs(infosListener.address).resolves(mockedRSSOutput); - stubParser - .withArgs("bad.rss.service") - .rejects(new Error("connect ECONNREFUSED 127.0.0.1:80")); + const stubParser = mockManager.mock("parseURL"); + const err = new Error("connect ECONNREFUSED 127.0.0.1:80"); + stubParser.rejects(err); - let myListener = new Listeners({ + const myListener = new Listeners({ name: "my-test-service", address: "bad.rss.service", customfields: { @@ -233,98 +227,120 @@ describe("test class RSS: jsonfile", function () { icon: ["media:group", "media:thumbnail"], }, }); - // fetch - let res = myListener.fetchRSS(); - - //assertion - // calls - expect(stubParser).to.have.been.calledOnce; - expect(stubParser).to.have.been.calledWith("bad.rss.service"); - // Promise - res - .then((obj: any) => { - expect(obj).to.be.undefined; - }) - .catch((err) => { - expect(err).to.be.eql(new Error("connect ECONNREFUSED 127.0.0.1:80")); - }); + // when + await assert.rejects(() => myListener.fetchRSS(), err); }); }); describe("start", function () { - it("Let's start the timer", async function () { - let clock: sinon.default.SinonFakeTimers = sinon.default.useFakeTimers(); + it("fetches immediately the RSS information", async function () { + // given + const clock = sinon.useFakeTimers(); const mockManager: InPlaceMockManager = ImportMock.mockClassInPlace( Parser ); - let stubParser = mockManager.mock("parseURL"); - stubParser.withArgs(infosListener.address).resolves(mockedRSSOutput); - stubParser - .withArgs("bad.rss.service") - .rejects(new Error("connect ECONNREFUSED 127.0.0.1:80")); + const stubParser = mockManager.mock("parseURL"); + stubParser.resolves(mockedRSSOutput); // classic build - let myListener = new Listeners({ - name: "my-test-service", - address: "fake.rss.service", - timeloop: 60, - customfields: { - description: ["media:group", "media:description"], - icon: ["media:group", "media:thumbnail"], - }, + const myListener = new Listeners(infosListener); + + //spy + const updateListenerSpy = sinon.spy(); + + // start timer + myListener.on("update", updateListenerSpy); + + myListener.start(); + + // when + await clock.tickAsync(1); + + // then + expect(updateListenerSpy).to.have.been.calledOnce; + expect(updateListenerSpy).to.have.been.calledWith(mockedRSSOutput); + expect(stubParser).to.have.calledWith(myListener.address); + myListener.stop(); + }); + + it("has fetched multiple times after a while", async function () { + // given + const clock = sinon.useFakeTimers(); + + const mockManager: InPlaceMockManager = ImportMock.mockClassInPlace( + Parser + ); + const stubParser = mockManager.mock("parseURL"); + stubParser.resolves(mockedRSSOutput); + + // classic build + const myListener = new Listeners({ + ...infosListener, + timeloop: 15, }); //spy - const fun_spy: sinon.default.SinonSpy = sinon.default.spy((obj) => { - expect(obj).to.be.eql(mockedRSSOutput); - }); + const updateListenerSpy: sinon.SinonSpy = sinon.spy(); + const newRSSOutput = { + ...mockedRSSOutput, + items: mockedRSSOutput.items.concat({ + title: "my title 03", + "media:group": { + "media:description": "my description 03", + "media:thumbnail": [ + { $: { height: 360, width: 420, url: "my_image03.jpg" } }, + ], + }, + link: "my_url_03.com", + pubDate: "myDate03", + }), + }; // start timer - myListener.on("update", (obj) => fun_spy(obj)); + myListener.on("update", updateListenerSpy); + myListener.start(); - // wait and assertion - // After 1ms + // when await clock.tickAsync(1); - expect(fun_spy).to.have.been.calledOnce; + stubParser.resolves(newRSSOutput); + await clock.tickAsync(60000); - // After 60s - await clock.tickAsync(59999); - expect(fun_spy).to.have.been.calledTwice; + // then + expect(updateListenerSpy).to.have.been.callCount(5); + expect(updateListenerSpy.firstCall).to.have.been.calledWith( + mockedRSSOutput + ); + expect(updateListenerSpy.secondCall).to.have.been.calledWith( + newRSSOutput + ); myListener.stop(); }); - it("Let's start the timer (with a bad address)", async function () { - let clock: sinon.default.SinonFakeTimers = sinon.default.useFakeTimers(); + it("notifies with a 'error' when fetching fails", async function () { + const clock = sinon.useFakeTimers(); const mockManager: InPlaceMockManager = ImportMock.mockClassInPlace( Parser ); - let stubParser = mockManager.mock("parseURL"); - stubParser.withArgs(infosListener.address).resolves(mockedRSSOutput); - stubParser - .withArgs("bad.rss.service") - .rejects(new Error("connect ECONNREFUSED 127.0.0.1:80")); + const stubParser = mockManager.mock("parseURL"); + const expectedErr = new Error("connect ECONNREFUSED 127.0.0.1:80"); + stubParser.rejects(expectedErr); // classic build - let myListener = new Listeners({ - name: "my-test-service", - address: "bad.rss.service", + const myListener = new Listeners({ + ...infosListener, timeloop: 60, - customfields: { - description: ["media:group", "media:description"], - icon: ["media:group", "media:thumbnail"], - }, }); //spy - let fun_spy_err: sinon.default.SinonSpy = sinon.default.spy((err) => { - expect(err).to.not.be.eql(undefined); - }); + const updateListenerSpy = sinon.spy(); + const updateErrorListenerSpy = sinon.spy(); - myListener.on("update_err", fun_spy_err); + myListener.on("error", updateErrorListenerSpy); + myListener.on("update", updateListenerSpy); // start timer myListener.start(); @@ -332,13 +348,21 @@ describe("test class RSS: jsonfile", function () { // wait and assertion // After 1ms await clock.tickAsync(1); - // expect(stubListener.fetchRSS).to.have.been.calledOnce; - expect(fun_spy_err).to.have.been.calledOnce; + expect(updateErrorListenerSpy).to.have.been.calledOnce; + expect(updateListenerSpy).to.not.have.been.called; + expect(updateErrorListenerSpy).to.have.been.calledWith(expectedErr); // After 60s - await clock.tickAsync(59999); - // expect(stubListener.fetchRSS).to.have.been.calledTwice; - expect(fun_spy_err).to.have.been.calledTwice; + await clock.tickAsync(60000); + expect(updateErrorListenerSpy).to.have.been.calledTwice; + expect(updateListenerSpy).to.not.have.been.called; + + // When the service is back + stubParser.resolves(mockedRSSOutput); + await clock.tickAsync(60000); + + expect(updateListenerSpy).to.have.been.calledOnce; + expect(updateListenerSpy).to.have.been.calledWith(mockedRSSOutput); myListener.stop(); });