diff --git a/src/Models/ListenerRSSInfos.ts b/src/Models/ListenerRSSInfos.ts index dfbe635..0da2390 100644 --- a/src/Models/ListenerRSSInfos.ts +++ b/src/Models/ListenerRSSInfos.ts @@ -2,5 +2,5 @@ export interface ListenerRSSInfos { readonly address: string; // feed's address readonly timeloop?: number; // update time RSS feed readonly customfields?: { [key: string]: string | string[] }; // rss fields custom - readonly lastEntriesLinks?: [string]; // links from lastentries + readonly lastEntriesLinks?: string[]; // links from lastentries } diff --git a/src/listener-rss.ts b/src/listener-rss.ts index 1d48aae..f72011c 100644 --- a/src/listener-rss.ts +++ b/src/listener-rss.ts @@ -66,25 +66,27 @@ export class ListenerRss extends EventEmitter { start(): void { this.loopRunning = true; - const fun: () => void = () => { - this.fetchRSS() - .then((obj: { [key: string]: any }) => { - this.emit("update", obj); - const updatedEntriesLinks = obj.items.map( - (item: { link: string }) => item.link - ); + const fun: () => void = async () => { + await Promise.resolve( + this.fetchRSS() + .then((obj: { [key: string]: any }) => { + this.emit("update", obj); + const updatedEntriesLinks = obj.items.map( + (item: { link: string }) => item.link + ); - const newEntries = obj.items.filter( - (item: { link: string }) => - !this.lastEntriesLinks.includes(item.link) - ); + const newEntries = obj.items.filter( + (item: { link: string }) => + !this.lastEntriesLinks.includes(item.link) + ); - if (newEntries.length !== 0) { - this.emit("newEntries", newEntries); - } - this.lastEntriesLinks = updatedEntriesLinks; - }) - .catch((err) => this.emit("error", err)); + if (newEntries.length !== 0) { + this.emit("newEntries", newEntries); + } + this.lastEntriesLinks = updatedEntriesLinks; + }) + .catch((err) => this.emit("error", err)) + ); }; (async () => { diff --git a/tests/index-spec.ts b/tests/index-spec.ts index ee07fc0..279d156 100644 --- a/tests/index-spec.ts +++ b/tests/index-spec.ts @@ -26,6 +26,7 @@ describe("test class RSS: jsonfile", function () { description: ["media:group", "media:description"], icon: ["media:group", "media:thumbnail"], }, + lastEntriesLinks: ["my_url_02.com"], }; const mockedRSSOutput: Parser.Output = { @@ -67,8 +68,16 @@ describe("test class RSS: jsonfile", function () { }; describe("Building Ytb listener", function () { - it("builds without issues (infosListener parameters)", function () { - const myListener = new Listeners(infosListener); + it("builds with 4 params", function () { + const myListener = new Listeners({ + address: "fake.rss.service", + timeloop: 15, + customfields: { + description: ["media:group", "media:description"], + icon: ["media:group", "media:thumbnail"], + }, + lastEntriesLinks: ["my_url_02.com"], + }); // assertions // myListener data @@ -85,12 +94,62 @@ describe("test class RSS: jsonfile", function () { feed: [], item: ["media:group", "media:group"], }); + expect(myListener.lastEntriesLinks).to.be.eql(["my_url_02.com"]); }); - it("builds without issues (raw infos : 4 params)", function () { + it("builds with 3 params (no custom fields)", function () { const myListener = new Listeners({ address: "fake.rss.service", timeloop: 15, + lastEntriesLinks: ["my_url_02.com"], + }); + + // assertions + // myListener data + expect(myListener.timeloop).to.eql(15); + expect(myListener.address).to.eql("fake.rss.service"); + expect(myListener.customfields).to.eql(undefined); + expect(myListener.parser) + .to.have.property("options") + .to.have.property("customFields") + .to.be.eql({ + feed: [], + item: [], + }); + expect(myListener.lastEntriesLinks).to.be.eql(["my_url_02.com"]); + }); + + it("build with 3 params (no timeloop)", function () { + const myListener = new Listeners({ + address: "fake.rss.service", + customfields: { + description: ["media:group", "media:description"], + icon: ["media:group", "media:thumbnail"], + }, + lastEntriesLinks: ["my_url_02.com"], + }); + + // assertions + // myListener data + expect(myListener.timeloop).to.eql(5 * 60); + expect(myListener.address).to.eql("fake.rss.service"); + expect(myListener.customfields).to.eql({ + description: ["media:group", "media:description"], + icon: ["media:group", "media:thumbnail"], + }); + expect(myListener.parser) + .to.have.property("options") + .to.have.property("customFields") + .to.be.eql({ + feed: [], + item: ["media:group", "media:group"], + }); + expect(myListener.lastEntriesLinks).to.be.eql(["my_url_02.com"]); + }); + + it("build with 3 params (no lastEntries)", function () { + const myListener = new Listeners({ + address: "fake.rss.service", customfields: { description: ["media:group", "media:description"], icon: ["media:group", "media:thumbnail"], @@ -99,7 +158,7 @@ describe("test class RSS: jsonfile", function () { // assertions // myListener data - expect(myListener.timeloop).to.eql(15); + expect(myListener.timeloop).to.eql(5 * 60); expect(myListener.address).to.eql("fake.rss.service"); expect(myListener.customfields).to.eql({ description: ["media:group", "media:description"], @@ -112,9 +171,10 @@ describe("test class RSS: jsonfile", function () { feed: [], item: ["media:group", "media:group"], }); + expect(myListener.lastEntriesLinks).to.be.eql([]); }); - it("builds without issues (raw infos : just 2 params)", function () { + it("builds with 1 params (only address)", function () { const myListener = new Listeners({ address: "fake.rss.service", }); @@ -131,55 +191,10 @@ describe("test class RSS: jsonfile", function () { feed: [], item: [], }); + expect(myListener.lastEntriesLinks).to.be.eql([]); }); }); - it("builds without issues (raw infos: just 3 params (no custom fields))", function () { - const myListener = new Listeners({ - address: "fake.rss.service", - timeloop: 15, - }); - - // assertions - // myListener data - expect(myListener.timeloop).to.eql(15); - expect(myListener.address).to.eql("fake.rss.service"); - expect(myListener.customfields).to.eql(undefined); - expect(myListener.parser) - .to.have.property("options") - .to.have.property("customFields") - .to.be.eql({ - feed: [], - item: [], - }); - }); - - it("The build without issues (raw infos : just 3 params (no timeloop))", function () { - const myListener = new Listeners({ - address: "fake.rss.service", - customfields: { - description: ["media:group", "media:description"], - icon: ["media:group", "media:thumbnail"], - }, - }); - - // assertions - // myListener data - expect(myListener.timeloop).to.eql(5 * 60); - expect(myListener.address).to.eql("fake.rss.service"); - expect(myListener.customfields).to.eql({ - description: ["media:group", "media:description"], - icon: ["media:group", "media:thumbnail"], - }); - expect(myListener.parser) - .to.have.property("options") - .to.have.property("customFields") - .to.be.eql({ - feed: [], - item: ["media:group", "media:group"], - }); - }); - describe("data fetching", function () { it("fetches without issues", async function () { // given @@ -420,5 +435,71 @@ describe("test class RSS: jsonfile", function () { myListener.stop(); }); + + it("not notifies with 'newEntries' when a new entry is detected but she's already in the history", async function () { + // given + const clock = sinon.useFakeTimers(); + + const mockManager = ImportMock.mockClassInPlace(Parser); + const stubParser = mockManager.mock("parseURL"); + stubParser.resolves(mockedRSSOutput); + const newEntry = { + 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", + }; + const newRSSOutput = { + ...mockedRSSOutput, + items: [newEntry, ...mockedRSSOutput.items], + }; + + // classic build + const myListener = new Listeners({ + ...infosListener, + timeloop: 60, + lastEntriesLinks: ["my_url_02.com", "my_url_01.com", "my_url_00.com"], + }); + + //spy + const updateListenerSpy = sinon.spy(); + const newEntriesListenerSpy = sinon.spy(); + + myListener.on("update", updateListenerSpy); + myListener.on("newEntries", newEntriesListenerSpy); + + // when + myListener.start(); + + // then + await clock.tickAsync(1); + expect(updateListenerSpy).to.have.been.calledOnce; + expect(newEntriesListenerSpy).to.not.have.been.calledOnce; + + // given + stubParser.resolves(newRSSOutput); + + // then + await clock.tickAsync(60000); + expect(updateListenerSpy).to.have.been.calledTwice; + expect(newEntriesListenerSpy).to.have.been.calledOnce; + expect(newEntriesListenerSpy).to.have.been.calledWith([newEntry]); + + // given + newEntriesListenerSpy.resetHistory(); + + // then + await clock.tickAsync(60000); + expect(updateListenerSpy).to.have.been.calledThrice; + expect(updateListenerSpy).to.have.been.calledWith(mockedRSSOutput); + expect(newEntriesListenerSpy).to.not.have.been.called; + + myListener.stop(); + }); }); });