Update packages version
Add first test Add design pattern builder + Youtube builder
This commit is contained in:
parent
bf55adf8da
commit
f1b1e23792
778
package-lock.json
generated
778
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -16,14 +16,15 @@
|
||||||
"discord.js": "^12.0.1",
|
"discord.js": "^12.0.1",
|
||||||
"fs-extra": "^9.0.0",
|
"fs-extra": "^9.0.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.20",
|
||||||
"pm2": "^4.2.3",
|
"pm2": "^4.5.1",
|
||||||
"rss-parser": "^3.7.5"
|
"rss-parser": "^3.7.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"mocha": "^8.2.1",
|
"mocha": "^8.2.1",
|
||||||
"proxyquire": "^2.1.3",
|
"proxyquire": "^2.1.3",
|
||||||
"sinon": "^9.2.2"
|
"sinon": "^9.2.2",
|
||||||
|
"sinon-chai": "^3.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
47
src/Models/Builders/AbstractListenerRSSBuilder.js
Normal file
47
src/Models/Builders/AbstractListenerRSSBuilder.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
const ListenerRSS = require('../listener-rss');
|
||||||
|
|
||||||
|
class AbstractListnerRSSBuilder {
|
||||||
|
_listenerRSS = undefined;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
if(this.constructor === AbstractListnerRSSBuilder)
|
||||||
|
throw new Error('The Abstract class "AbstractListnerRSSBuilder" cannot be instantiated');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief first function to call after constructor. It's building the listener
|
||||||
|
* @param infos Must to be an object ListenerRSSInfos
|
||||||
|
*/
|
||||||
|
constructListener(infos) {
|
||||||
|
this._listenerRSS = new ListenerRSS();
|
||||||
|
|
||||||
|
this.setInfos(infos);
|
||||||
|
this.setSpecificInfos();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief give the listener just build
|
||||||
|
* @return ListenerRSS with all the setups datas
|
||||||
|
* @exception if the listener isn't construct
|
||||||
|
*/
|
||||||
|
get listenerRSS() {
|
||||||
|
if(this._listenerRSS === undefined)
|
||||||
|
throw new Error('the listener is not yet build');
|
||||||
|
return this._listenerRSS;
|
||||||
|
}
|
||||||
|
|
||||||
|
setInfos(infos) { // Nominal Infos (like name, addresse, and other)
|
||||||
|
this._listenerRSS.name = infos._name;
|
||||||
|
this._listenerRSS.address = infos._address;
|
||||||
|
this._listenerRSS.timeloop = infos._timeloop;
|
||||||
|
this._listenerRSS.name = infos._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSpecificInfos() { // More generic infos who's depend of platforms
|
||||||
|
throw new Error('This function is not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AbstractListnerRSSBuilder
|
16
src/Models/Builders/YoutubeListenerRSSBuilder.js
Normal file
16
src/Models/Builders/YoutubeListenerRSSBuilder.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
const AbstractListenerRSSBuilder = require('AbstractListenerRSSBuilder')
|
||||||
|
|
||||||
|
class YoutubeListenerRSSBuilder extends AbstractListenerRSSBuilder {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
setSpecificInfos() {
|
||||||
|
this.listenerRSS._customfields = [
|
||||||
|
['description', ['media:group', 'media:description']],
|
||||||
|
['icon', ['media:group', 'media:thumbnail']]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = YoutubeListenerRSSBuilder
|
42
src/Models/listenerRSSInfos.js
Normal file
42
src/Models/listenerRSSInfos.js
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
class ListenerRSSInfos {
|
||||||
|
_name = undefined; // name of the listener
|
||||||
|
_address = undefined; // feed's address
|
||||||
|
_timeloop = 1 * 60; // update time RSS feed
|
||||||
|
_customfields = [] // rss fields custom
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
set name(value) {
|
||||||
|
this._name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set address(value) {
|
||||||
|
this._address = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set timeloop(value) {
|
||||||
|
this._timeloop = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set customfields(value) {
|
||||||
|
this._customfields = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
get address() {
|
||||||
|
return this._address;
|
||||||
|
}
|
||||||
|
|
||||||
|
get timeloop() {
|
||||||
|
return this._timeloop;
|
||||||
|
}
|
||||||
|
|
||||||
|
get customfields() {
|
||||||
|
return this._customfields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ListenerRSSInfos
|
22
src/listener-director.js
Normal file
22
src/listener-director.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
const YoutubeListenerRSSBuilder = require('./Models/YoutubeListenerRSSBuilder');
|
||||||
|
|
||||||
|
class ListenerBuildDirector {
|
||||||
|
_builder = undefined;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(builder) {
|
||||||
|
this._builder = builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
getListener() {
|
||||||
|
return this._builder.listenerRSS();
|
||||||
|
}
|
||||||
|
|
||||||
|
build(infos) {
|
||||||
|
if(infos === undefined)
|
||||||
|
throw new Error('infos must be initialized');
|
||||||
|
this._builder.constructListener(infos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ListenerBuildDirector
|
61
src/listener-rss.js
Normal file
61
src/listener-rss.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
const jsonFile = require('jsonfile');
|
||||||
|
const Parser = require('rss-parser');
|
||||||
|
|
||||||
|
|
||||||
|
class ListenerRss {
|
||||||
|
name = undefined;
|
||||||
|
address = undefined;
|
||||||
|
timeloop = 5 * 60; // time in seconds
|
||||||
|
customfields = [];
|
||||||
|
|
||||||
|
// private fields
|
||||||
|
parser = null;
|
||||||
|
obj = null;
|
||||||
|
loopRunning = false;
|
||||||
|
|
||||||
|
constructor(info) {
|
||||||
|
// set parser
|
||||||
|
this.parser = new Parser({
|
||||||
|
customFields : {
|
||||||
|
item: info._customfields.map((elt) => {
|
||||||
|
return Array.isArray(elt[1]) ? elt[1][0] : elt[1];
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set data
|
||||||
|
this.name = info._name === undefined ? info._address : info._name; // if name is undefined let's take the address
|
||||||
|
this.address = info._address;
|
||||||
|
this.timeloop = info._timeloop;
|
||||||
|
this.customfields = info._customfields;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchRSS() {
|
||||||
|
return this.parser.parseURL(this.address)
|
||||||
|
.catch((err) => { throw new Error('bad address or no access : ' + err);});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief call the callback function each looptime
|
||||||
|
* @param callback function who's going to be called with the latest get
|
||||||
|
*/
|
||||||
|
start(callback) {
|
||||||
|
this.loopRunning = true;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
while(this.loopRunning === true) {
|
||||||
|
callback(await this.fetchRSS());
|
||||||
|
await new Promise(res => setTimeout(res, this.timeloop * 1000));
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief stop the async function
|
||||||
|
*/
|
||||||
|
stop() {
|
||||||
|
this.loopRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ListenerRss
|
|
@ -1,59 +1,100 @@
|
||||||
const chai = require("chai");
|
const chai = require("chai");
|
||||||
const sinon = require("sinon");
|
const sinon = require("sinon");
|
||||||
const proxyquire = require("proxyquire");
|
const sinon_chai = require("sinon-chai");
|
||||||
|
chai.use(sinon_chai);
|
||||||
const Parser = require("rss-parser");
|
const Parser = require("rss-parser");
|
||||||
|
|
||||||
|
const Listeners = require('../src/listener-rss')
|
||||||
|
const ListenerRRSInfo = require('../src/Models/listenerRSSInfos')
|
||||||
|
const YtbBuilder = require('../src/Models/YoutubeListenerRSSBuilder')
|
||||||
|
const YtbDirector = require('../src/listener-director')
|
||||||
|
|
||||||
|
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
describe("RSS Youtube", function () {
|
describe("test class RSS: jsonfile", function () {
|
||||||
let rssYoutubeService;
|
let myListener = undefined;
|
||||||
let timecode;
|
|
||||||
let routage;
|
let infosListener = new ListenerRRSInfo();
|
||||||
let parseUrlStub;
|
infosListener.name = 'my-test-service';
|
||||||
let mockedInput = {
|
infosListener.address = 'fake.rss.service';
|
||||||
|
infosListener.timeloop = 15;
|
||||||
|
|
||||||
|
// parseURL tests
|
||||||
|
let stubParser;
|
||||||
|
|
||||||
|
const mockedRSSOutput = {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: "::account",
|
'title': 'my title 00',
|
||||||
url:
|
'media:group': {
|
||||||
isoDate: Date.now().toISOString(),
|
'media:description': 'my description 00',
|
||||||
"media:group": {
|
'media:thumbnail': [{$: {height: 360, width: 420, url: 'my_image00.jpg'}}],
|
||||||
"media:description": [
|
},
|
||||||
"my description"
|
'link': 'my_url_00.com',
|
||||||
]
|
'pubDate': 'myDate00'
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
'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'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'title': 'my title 02',
|
||||||
|
'media:group' : {
|
||||||
|
'media:description': 'my description 02',
|
||||||
|
'media:thumbnail': [{$: { height: 360, width: 420, url: 'my_image02.jpg'}}],
|
||||||
|
},
|
||||||
|
'link': 'my_url_02.com',
|
||||||
|
'pubDate': 'myDate02'
|
||||||
|
},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
timecode = {
|
// stubs
|
||||||
"lastCheck": "2020-11-12T17:44:11.713Z",
|
stubParser = sinon.stub(Parser.prototype, 'parseURL')
|
||||||
"MyUser": "2020-11-12T17:44:11.713Z"
|
.withArgs(listenerInfo.address)
|
||||||
};
|
.resolves(mockedRSSOutput);
|
||||||
routage = {
|
|
||||||
log: sinon.stub(),
|
|
||||||
send: sinon.stub()
|
|
||||||
};
|
|
||||||
|
|
||||||
parseUrlStub = sinon.stub(Parser.prototype, "parseURL")
|
// constructor
|
||||||
.callsFake((_, callback) => callback(null, "data"));
|
myListener = new Listeners();
|
||||||
|
})
|
||||||
rssYoutubeService = proxyquire("../services/rss-youtube", {
|
|
||||||
"../modules/routage": () => routage,
|
|
||||||
"../db/rss-youtube.json": {
|
|
||||||
config: {
|
|
||||||
timeLoop: 5,
|
|
||||||
urlFeeds: "https://www.youtube.com/feeds/videos.xml?channel_id=",
|
|
||||||
sliceDescription: 142
|
|
||||||
},
|
|
||||||
feeds: {
|
|
||||||
MyUser: "Some channel"
|
|
||||||
},
|
|
||||||
timecode
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
Parser.prototype.parseURL.reset();
|
// restore stubs
|
||||||
|
Parser.prototype.parseURL.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Building Ytb listener", function () {
|
||||||
|
it("The build without problems", function () {
|
||||||
|
let builder = new YtbBuilder();
|
||||||
|
let director = new YtbDirector(builder);
|
||||||
|
director.build(infosListener);
|
||||||
|
myListener = director.getListener();
|
||||||
|
|
||||||
|
// assertions
|
||||||
|
expect(stubParser).to.have.been.calledOnce;
|
||||||
|
expect(stubParser).to.have.been.calledWith(infosListener._address);
|
||||||
|
expect(myListener.customFields).to.eql([
|
||||||
|
['description', ['media:group', 'media:description']],
|
||||||
|
['icon', ['media:group', 'media:thumbnail']]
|
||||||
|
]);
|
||||||
|
//TODO test les infos dans "myListener"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//Todo
|
||||||
|
describe.skip("start", function () {
|
||||||
|
it("Let's start the timer", function () {
|
||||||
|
myListener.setDatas(listenerInfo);
|
||||||
|
myListener.start();
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user