listener-rss/tests/index-spec.ts

513 lines
15 KiB
TypeScript
Raw Permalink Normal View History

// external lib
2021-02-28 18:56:33 +01:00
import * as Parser from "rss-parser";
2021-02-07 17:50:54 +01:00
// tested class
import { ListenerRss } from "../";
// Unit test
2021-03-06 17:32:59 +01:00
import assert from "assert";
2021-02-14 15:00:33 +01:00
import * as chai from "chai";
2021-03-06 17:32:59 +01:00
import sinon from "ts-sinon";
import sinonChai from "sinon-chai";
2021-02-28 18:56:33 +01:00
import { ImportMock, InPlaceMockManager } from "ts-mock-imports";
2021-02-14 15:00:33 +01:00
chai.use(sinonChai);
const expect = chai.expect;
describe("test class RSS: jsonfile", function () {
2021-06-29 17:49:01 +02:00
const infosListener: ListenerRss.Config = {
address: "fake.rss.service",
timeloop: 15,
customfields: {
description: ["media:group", "media:description"],
icon: ["media:group", "media:thumbnail"],
},
lastEntriesLinks: ["my_url_02.com"],
};
2021-03-06 17:32:59 +01:00
const mockedRSSOutput: Parser.Output<any> = {
items: [
{
2021-03-08 09:27:32 +01:00
title: "my title 02",
"media:group": {
2021-03-08 09:27:32 +01:00
"media:description": "my description 02",
"media:thumbnail": [
2021-03-08 09:27:32 +01:00
{ $: { height: 360, width: 420, url: "my_image02.jpg" } },
],
},
2021-03-08 09:27:32 +01:00
link: "my_url_02.com",
pubDate: "myDate02",
},
{
title: "my title 01",
"media:group": {
"media:description": "my description 01",
"media:thumbnail": [
{ $: { height: 360, width: 420, url: "my_image01.jpg" } },
],
},
link: "my_url_01.com",
pubDate: "myDate01",
},
{
2021-03-08 09:27:32 +01:00
title: "my title 00",
"media:group": {
2021-03-08 09:27:32 +01:00
"media:description": "my description 00",
"media:thumbnail": [
2021-03-08 09:27:32 +01:00
{ $: { height: 360, width: 420, url: "my_image00.jpg" } },
],
},
2021-03-08 09:27:32 +01:00
link: "my_url_00.com",
pubDate: "myDate00",
},
],
};
describe("Building Ytb listener", function () {
it("builds with 4 params", function () {
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss({
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
expect(myListener.timeloop).to.eql(15);
expect(myListener.address).to.eql("fake.rss.service");
expect(myListener.customfields).to.eql({
description: ["media:group", "media:description"],
icon: ["media:group", "media:thumbnail"],
});
2021-02-25 13:14:34 +01:00
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"]);
});
2021-03-06 17:32:59 +01:00
it("builds with 3 params (no custom fields)", function () {
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss({
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 () {
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss({
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"]);
});
2021-03-06 17:32:59 +01:00
it("build with 3 params (no lastEntries)", function () {
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss({
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"],
});
expect(myListener.lastEntriesLinks).to.be.eql([]);
});
it("builds with 1 params (only address)", function () {
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss({
address: "fake.rss.service",
});
// assertions
// myListener data
expect(myListener.timeloop).to.eql(5 * 60);
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([]);
});
});
describe("export property", function () {
it("should export properties into a ListenerRSSInfos", function () {
// given
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss(infosListener);
// assertions
expect(myListener.getProperty()).to.be.eql(infosListener);
});
});
2021-03-06 17:32:59 +01:00
describe("data fetching", function () {
it("fetches without issues", async function () {
// given
2021-02-28 18:56:33 +01:00
const mockManager: InPlaceMockManager<Parser> = ImportMock.mockClassInPlace<Parser>(
Parser
);
2021-03-06 17:32:59 +01:00
const stubParser = mockManager.mock("parseURL");
stubParser.resolves(mockedRSSOutput);
2021-02-28 18:56:33 +01:00
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss(infosListener);
2021-02-28 18:56:33 +01:00
2021-03-06 17:32:59 +01:00
// when
const res = await myListener.fetchRSS();
// then
expect(stubParser).to.have.been.calledOnce;
expect(stubParser).to.have.been.calledWith(infosListener.address);
expect(res).to.be.eql(mockedRSSOutput);
});
2021-02-25 13:14:34 +01:00
2021-03-06 17:32:59 +01:00
it("rejects when fetching fails", async function () {
// given
2021-02-28 18:56:33 +01:00
const mockManager: InPlaceMockManager<Parser> = ImportMock.mockClassInPlace<Parser>(
Parser
);
2021-03-06 17:32:59 +01:00
const stubParser = mockManager.mock("parseURL");
const err = new Error("connect ECONNREFUSED 127.0.0.1:80");
stubParser.rejects(err);
2021-02-28 18:56:33 +01:00
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss({
address: "bad.rss.service",
customfields: {
description: ["media:group", "media:description"],
icon: ["media:group", "media:thumbnail"],
},
});
2021-03-06 17:32:59 +01:00
// when
await assert.rejects(() => myListener.fetchRSS(), err);
});
});
2021-02-28 18:56:33 +01:00
describe("start", function () {
2021-03-06 17:32:59 +01:00
it("fetches immediately the RSS information", async function () {
// given
const clock = sinon.useFakeTimers();
2021-02-28 18:56:33 +01:00
const mockManager: InPlaceMockManager<Parser> = ImportMock.mockClassInPlace<Parser>(
Parser
);
2021-03-06 17:32:59 +01:00
const stubParser = mockManager.mock("parseURL");
stubParser.resolves(mockedRSSOutput);
2021-02-28 18:56:33 +01:00
// classic build
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss(infosListener);
//spy
2021-03-06 17:32:59 +01:00
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<Parser> = ImportMock.mockClassInPlace<Parser>(
Parser
);
const stubParser = mockManager.mock("parseURL");
stubParser.resolves(mockedRSSOutput);
// classic build
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss({
2021-03-06 17:32:59 +01:00
...infosListener,
timeloop: 15,
});
2021-03-06 17:32:59 +01:00
//spy
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
2021-03-06 17:32:59 +01:00
myListener.on("update", updateListenerSpy);
2021-02-28 18:56:33 +01:00
myListener.start();
2021-03-06 17:32:59 +01:00
// when
2021-02-25 13:14:34 +01:00
await clock.tickAsync(1);
2021-03-06 17:32:59 +01:00
stubParser.resolves(newRSSOutput);
await clock.tickAsync(60000);
2021-03-06 17:32:59 +01:00
// then
expect(updateListenerSpy).to.have.been.callCount(5);
expect(updateListenerSpy.firstCall).to.have.been.calledWith(
mockedRSSOutput
);
expect(updateListenerSpy.secondCall).to.have.been.calledWith(
newRSSOutput
);
2021-02-28 18:56:33 +01:00
myListener.stop();
});
2021-03-06 17:32:59 +01:00
it("notifies with a 'error' when fetching fails", async function () {
const clock = sinon.useFakeTimers();
2021-02-28 18:56:33 +01:00
const mockManager: InPlaceMockManager<Parser> = ImportMock.mockClassInPlace<Parser>(
Parser
);
2021-03-06 17:32:59 +01:00
const stubParser = mockManager.mock("parseURL");
const expectedErr = new Error("connect ECONNREFUSED 127.0.0.1:80");
stubParser.rejects(expectedErr);
2021-02-28 18:56:33 +01:00
// classic build
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss({
2021-03-06 17:32:59 +01:00
...infosListener,
2021-02-25 13:14:34 +01:00
timeloop: 60,
});
//spy
2021-03-06 17:32:59 +01:00
const updateListenerSpy = sinon.spy();
const updateErrorListenerSpy = sinon.spy();
2021-03-06 17:32:59 +01:00
myListener.on("error", updateErrorListenerSpy);
myListener.on("update", updateListenerSpy);
2021-02-28 18:56:33 +01:00
// start timer
2021-02-28 18:56:33 +01:00
myListener.start();
2021-02-25 13:14:34 +01:00
// wait and assertion
// After 1ms
await clock.tickAsync(1);
2021-03-06 17:32:59 +01:00
expect(updateErrorListenerSpy).to.have.been.calledOnce;
expect(updateListenerSpy).to.not.have.been.called;
expect(updateErrorListenerSpy).to.have.been.calledWith(expectedErr);
2021-02-25 13:14:34 +01:00
// After 60s
2021-03-06 17:32:59 +01:00
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);
2021-02-25 13:14:34 +01:00
2021-02-28 18:56:33 +01:00
myListener.stop();
2021-02-07 17:50:54 +01:00
});
2021-03-08 09:27:32 +01:00
it("notifies with 'newEntries' when a new entry is detected", async function () {
// given
const clock = sinon.useFakeTimers();
const mockManager = ImportMock.mockClassInPlace<Parser>(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
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss({
2021-03-08 09:27:32 +01:00
...infosListener,
timeloop: 60,
});
//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;
2021-07-06 14:52:50 +02:00
expect(newEntriesListenerSpy).to.not.have.been.called;
2021-03-08 09:27:32 +01:00
// given
stubParser.resolves(newRSSOutput);
// then
await clock.tickAsync(60000);
expect(updateListenerSpy).to.have.been.calledTwice;
2021-07-06 14:52:50 +02:00
expect(newEntriesListenerSpy).to.have.been.calledOnce;
2021-03-08 09:27:32 +01:00
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();
});
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>(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
2021-06-29 17:49:01 +02:00
const myListener = new ListenerRss({
...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();
});
2021-02-07 17:50:54 +01:00
});
});