feedTest.js (15944B)
1 describe("Zotero.Feed", function() { 2 // Clean up after after tests 3 after(function* () { 4 yield clearFeeds(); 5 }); 6 7 it("should be an instance of Zotero.Library", function() { 8 let feed = new Zotero.Feed(); 9 assert.instanceOf(feed, Zotero.Library); 10 }); 11 12 describe("#constructor()", function() { 13 it("should accept required fields as arguments", function* () { 14 let feed = new Zotero.Feed(); 15 yield assert.isRejected(feed.saveTx(), /^Feed name not set$/); 16 17 feed = new Zotero.Feed({ 18 name: 'Test ' + Zotero.randomString(), 19 url: 'http://www.' + Zotero.randomString() + '.com' 20 }); 21 yield assert.isFulfilled(feed.saveTx()); 22 }); 23 }); 24 25 describe("#isFeed", function() { 26 it("should be true", function() { 27 let feed = new Zotero.Feed(); 28 assert.isTrue(feed.isFeed); 29 }); 30 it("should be falsy for regular Library", function() { 31 let library = new Zotero.Library(); 32 assert.notOk(library.isFeed); 33 }); 34 }); 35 36 describe("#editable", function() { 37 it("should always be not editable", function* () { 38 let feed = yield createFeed(); 39 assert.isFalse(feed.editable); 40 feed.editable = true; 41 assert.isFalse(feed.editable); 42 yield feed.saveTx(); 43 assert.isFalse(feed.editable); 44 }); 45 it("should allow adding items without editCheck override", function* () { 46 let feed = yield createFeed(); 47 let feedItem = new Zotero.FeedItem('book', { guid: Zotero.randomString() }); 48 feedItem.libraryID = feed.libraryID; 49 yield assert.isFulfilled(feedItem.saveTx()); 50 }); 51 }); 52 53 describe("#libraryTypeID", function () { 54 it("should be undefind", function* () { 55 let feed = yield createFeed(); 56 assert.isUndefined(feed.libraryTypeID); 57 }); 58 }); 59 60 describe("#url", function() { 61 it("should throw if trying to set an invalid URL", function *() { 62 let feed = new Zotero.Feed({ name: 'Test ' + Zotero.randomString() }); 63 64 assert.throws(function() {feed.url = 'foo'}, /^Invalid feed URL /); 65 assert.throws(function() {feed.url = 'ftp://example.com'}, /^Invalid feed URL /); 66 }); 67 }); 68 69 describe("#save()", function() { 70 it("should save a new feed to the feed library", function* () { 71 let props = { 72 name: 'Test ' + Zotero.randomString(), 73 url: 'http://' + Zotero.randomString() + '.com/' 74 }; 75 let feed = yield createFeed(props); 76 77 assert.equal(feed.name, props.name, "name is correct"); 78 assert.equal(feed.url.toLowerCase(), props.url.toLowerCase(), "url is correct"); 79 }); 80 it("should save a feed with all fields set", function* () { 81 let props = { 82 name: 'Test ' + Zotero.randomString(), 83 url: 'http://' + Zotero.randomString() + '.com/', 84 refreshInterval: 30, 85 cleanupReadAfter: 1, 86 cleanupUnreadAfter: 30 87 }; 88 89 let feed = yield createFeed(props); 90 91 assert.equal(feed.name, props.name, "name is correct"); 92 assert.equal(feed.url.toLowerCase(), props.url.toLowerCase(), "url is correct"); 93 assert.equal(feed.refreshInterval, props.refreshInterval, "refreshInterval is correct"); 94 assert.equal(feed.cleanupReadAfter, props.cleanupReadAfter, "cleanupReadAfter is correct"); 95 assert.equal(feed.cleanupUnreadAfter, props.cleanupUnreadAfter, "cleanupUnreadAfter is correct"); 96 97 assert.isNull(feed.lastCheck, "lastCheck is null"); 98 assert.isNull(feed.lastUpdate, "lastUpdate is null"); 99 assert.isNull(feed.lastCheckError, "lastCheckError is null"); 100 }); 101 it("should throw if name or url are missing", function *() { 102 let feed = new Zotero.Feed(); 103 yield assert.isRejected(feed.saveTx(), /^Feed name not set$/); 104 105 feed.name = 'Test ' + Zotero.randomString(); 106 yield assert.isRejected(feed.saveTx(), /^Feed URL not set$/); 107 108 feed = new Zotero.Feed(); 109 feed.url = 'http://' + Zotero.randomString() + '.com'; 110 yield assert.isRejected(feed.saveTx(), /^Feed name not set$/); 111 }); 112 it("should not allow saving a feed with the same url", function *() { 113 let url = 'http://' + Zotero.randomString() + '.com'; 114 let feed1 = yield createFeed({ url }); 115 116 let feed2 = new Zotero.Feed({ name: 'Test ' + Zotero.randomString(), url }); 117 yield assert.isRejected(feed2.saveTx(), /^Feed for URL already exists: /); 118 119 // Perform check with normalized URL 120 feed2.url = url + '/'; 121 yield assert.isRejected(feed2.saveTx(), /^Feed for URL already exists: /); 122 123 feed2.url = url.toUpperCase(); 124 yield assert.isRejected(feed2.saveTx(), /^Feed for URL already exists: /); 125 }); 126 it("should allow saving a feed with the same name", function *() { 127 let name = 'Test ' + Zotero.randomString(); 128 let feed1 = yield createFeed({ name }); 129 130 let feed2 = new Zotero.Feed({ name , url: 'http://' + Zotero.randomString() + '.com' }); 131 132 yield assert.isFulfilled(feed2.saveTx(), "allow saving feed with an existing name"); 133 134 assert.equal(feed1.name, feed2.name, "feed names remain the same"); 135 }); 136 it("should save field to DB after editing", function* () { 137 let feed = yield createFeed(); 138 139 feed.name = 'bar'; 140 yield feed.saveTx(); 141 142 let dbVal = yield Zotero.DB.valueQueryAsync('SELECT name FROM feeds WHERE libraryID=?', feed.libraryID); 143 assert.equal(feed.name, 'bar'); 144 assert.equal(dbVal, feed.name); 145 }); 146 it("should add a new synced setting after creation", function* () { 147 let url = 'http://' + Zotero.Utilities.randomString(10, 'abcde') + '.com/feed.rss'; 148 149 let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds'); 150 assert.notOk(syncedFeeds[url]); 151 152 yield createFeed({url}); 153 154 syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds'); 155 assert.ok(syncedFeeds[url]); 156 }); 157 it("should remove previous feed and add a new one if url changed", function* () { 158 let feed = yield createFeed(); 159 160 let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds'); 161 assert.ok(syncedFeeds[feed.url]); 162 163 let oldUrl = feed.url; 164 feed.url = 'http://' + Zotero.Utilities.randomString(10, 'abcde') + '.com/feed.rss'; 165 yield feed.saveTx(); 166 167 syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds'); 168 assert.notOk(syncedFeeds[oldUrl]); 169 assert.ok(syncedFeeds[feed.url]); 170 }); 171 it('should update syncedSettings if `name`, `url`, `refreshInterval` or `cleanupUnreadAfter` was modified', function* () { 172 let feed = yield createFeed(); 173 let syncedSetting = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds'); 174 yield Zotero.SyncedSettings.set(Zotero.Libraries.userLibraryID, 'feeds', syncedSetting, 0, true); 175 176 feed.name = "New name"; 177 yield feed.saveTx(); 178 assert.isFalse(Zotero.SyncedSettings.getMetadata(Zotero.Libraries.userLibraryID, 'feeds').synced) 179 }); 180 it('should not update syncedSettings if `name`, `url`, `refreshInterval` or `cleanupUnreadAfter` were not modified', function* () { 181 let feed = yield createFeed(); 182 let syncedSetting = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds'); 183 yield Zotero.SyncedSettings.set(Zotero.Libraries.userLibraryID, 'feeds', syncedSetting, 0, true); 184 185 feed._set('_feedLastCheck', Zotero.Date.dateToSQL(new Date(), true)); 186 yield feed.saveTx(); 187 assert.isTrue(Zotero.SyncedSettings.getMetadata(Zotero.Libraries.userLibraryID, 'feeds').synced) 188 }); 189 }); 190 describe("#erase()", function() { 191 it("should erase a saved feed", function* () { 192 let feed = yield createFeed(); 193 let id = feed.libraryID; 194 let url = feed.url; 195 196 yield feed.eraseTx(); 197 198 assert.isFalse(Zotero.Libraries.exists(id)); 199 assert.isFalse(Zotero.Feeds.existsByURL(url)); 200 201 let dbValue = yield Zotero.DB.valueQueryAsync('SELECT COUNT(*) FROM feeds WHERE libraryID=?', id); 202 assert.equal(dbValue, '0'); 203 }); 204 it("should clear feedItems from cache", function* () { 205 let feed = yield createFeed(); 206 207 let feedItem = yield createDataObject('feedItem', { libraryID: feed.libraryID }); 208 assert.ok(yield Zotero.FeedItems.getAsync(feedItem.id)); 209 210 yield feed.eraseTx(); 211 212 assert.notOk(yield Zotero.FeedItems.getAsync(feedItem.id)); 213 }); 214 it("should remove synced settings", function* () { 215 let url = 'http://' + Zotero.Utilities.randomString(10, 'abcde') + '.com/feed.rss'; 216 let feed = yield createFeed({url}); 217 218 let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds'); 219 assert.ok(syncedFeeds[feed.url]); 220 221 yield feed.eraseTx(); 222 223 syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds'); 224 assert.notOk(syncedFeeds[url]); 225 226 }); 227 }); 228 229 describe("#storeSyncedSettings", function() { 230 it("should store settings for feed in compact format", function* () { 231 let url = 'http://' + Zotero.Utilities.randomString().toLowerCase() + '.com/feed.rss'; 232 let settings = [Zotero.Utilities.randomString(), 1, 30, 1]; 233 let feed = yield createFeed({ 234 url, 235 name: settings[0], 236 cleanupReadAfter: settings[1], 237 cleanupUnreadAfter: settings[2], 238 refreshInterval: settings[3] 239 }); 240 241 let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds'); 242 assert.deepEqual(syncedFeeds[url], settings); 243 }); 244 }); 245 246 describe("#clearExpiredItems()", function() { 247 var feed, readExpiredFI, unreadExpiredFI, readFeedItem, feedItem, readStillInFeed, feedItemIDs; 248 249 before(function* (){ 250 feed = yield createFeed({cleanupReadAfter: 1, cleanupUnreadAfter: 3}); 251 252 readExpiredFI = yield createDataObject('feedItem', { libraryID: feed.libraryID }); 253 // Read 2 days ago 254 readExpiredFI.isRead = true; 255 readExpiredFI._feedItemReadTime = Zotero.Date.dateToSQL( 256 new Date(Date.now() - 2 * 24*60*60*1000), true); 257 yield readExpiredFI.saveTx(); 258 259 // Added 5 days ago 260 unreadExpiredFI = yield createDataObject('feedItem', { 261 libraryID: feed.libraryID, 262 dateAdded: Zotero.Date.dateToSQL(new Date(Date.now() - 5 * 24*60*60*1000), true), 263 dateModified: Zotero.Date.dateToSQL(new Date(Date.now() - 5 * 24*60*60*1000), true) 264 }); 265 yield unreadExpiredFI.saveTx(); 266 267 readStillInFeed = yield createDataObject('feedItem', { libraryID: feed.libraryID }); 268 // Read 2 days ago 269 readStillInFeed.isRead = true; 270 readStillInFeed._feedItemReadTime = Zotero.Date.dateToSQL( 271 new Date(Date.now() - 2 * 24*60*60*1000), true); 272 yield readStillInFeed.saveTx(); 273 274 readFeedItem = yield createDataObject('feedItem', { libraryID: feed.libraryID }); 275 readFeedItem.isRead = true; 276 yield readFeedItem.saveTx(); 277 278 feedItem = yield createDataObject('feedItem', { libraryID: feed.libraryID }); 279 280 feedItemIDs = yield Zotero.FeedItems.getAll(feed.libraryID).map((row) => row.id); 281 282 assert.include(feedItemIDs, feedItem.id, "feed contains unread feed item"); 283 assert.include(feedItemIDs, readFeedItem.id, "feed contains read feed item"); 284 assert.include(feedItemIDs, readExpiredFI.id, "feed contains expired feed item"); 285 assert.include(feedItemIDs, readStillInFeed.id, "feed contains expired but still in rss feed item"); 286 287 yield feed.clearExpiredItems(new Set([readStillInFeed.id])); 288 289 feedItemIDs = yield Zotero.FeedItems.getAll(feed.libraryID).map((row) => row.id); 290 }); 291 292 it('should clear expired items', function() { 293 assert.notInclude(feedItemIDs, readExpiredFI.id, "feed no longer contains expired read feed item"); 294 assert.notInclude(feedItemIDs, unreadExpiredFI.id, "feed no longer contains expired feed item"); 295 }); 296 297 it('should not clear read items that have not expired yet', function() { 298 assert.include(feedItemIDs, readFeedItem.id, "feed still contains new feed item"); 299 }); 300 301 it('should not clear read items that are still in rss', function() { 302 assert.include(feedItemIDs, readStillInFeed.id, "feed still contains read still in rss feed item"); 303 }); 304 305 it('should not clear unread items', function() { 306 assert.include(feedItemIDs, feedItem.id, "feed still contains new feed item"); 307 }); 308 }); 309 310 describe('#updateFeed()', function() { 311 var feed, scheduleNextFeedCheck; 312 var feedUrl = getTestDataUrl("feed.rss"); 313 var modifiedFeedUrl = getTestDataUrl("feedModified.rss"); 314 315 before(function() { 316 scheduleNextFeedCheck = sinon.stub(Zotero.Feeds, 'scheduleNextFeedCheck').resolves(); 317 }); 318 319 beforeEach(function* (){ 320 scheduleNextFeedCheck.resetHistory(); 321 feed = yield createFeed(); 322 feed._feedUrl = feedUrl; 323 yield feed.updateFeed(); 324 }); 325 326 afterEach(function* () { 327 yield clearFeeds(); 328 }); 329 330 after(function() { 331 scheduleNextFeedCheck.restore(); 332 }); 333 334 it('should schedule next feed check', function* () { 335 let feed = yield createFeed(); 336 feed._feedUrl = feedUrl; 337 yield feed.updateFeed(); 338 assert.equal(scheduleNextFeedCheck.called, true); 339 }); 340 341 it('should add new feed items', function* () { 342 let feedItems = yield Zotero.FeedItems.getAll(feed.id, true); 343 assert.equal(feedItems.length, 3); 344 }); 345 346 it('should set lastCheck and lastUpdated values', function* () { 347 yield clearFeeds(); 348 let feed = yield createFeed(); 349 feed._feedUrl = feedUrl; 350 351 assert.notOk(feed.lastCheck); 352 assert.notOk(feed.lastUpdate); 353 354 yield feed.updateFeed(); 355 356 assert.isTrue(feed.lastCheck > Zotero.Date.dateToSQL(new Date(Date.now() - 1000*60), true), 'feed.lastCheck updated'); 357 assert.isTrue(feed.lastUpdate > Zotero.Date.dateToSQL(new Date(Date.now() - 1000*60), true), 'feed.lastUpdate updated'); 358 }); 359 it('should update modified items and set unread', function* () { 360 let feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573"); 361 feedItem.isRead = true; 362 yield feedItem.saveTx(); 363 feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573"); 364 assert.isTrue(feedItem.isRead); 365 366 let oldDateModified = feedItem.getField('date'); 367 368 feed._feedUrl = modifiedFeedUrl; 369 yield feed.updateFeed(); 370 371 feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573"); 372 373 assert.notEqual(oldDateModified, feedItem.getField('date')); 374 assert.isFalse(feedItem.isRead) 375 }); 376 it('should skip items that are not modified', function* () { 377 let save = sinon.spy(Zotero.FeedItem.prototype, 'save'); 378 379 feed._feedUrl = modifiedFeedUrl; 380 yield feed.updateFeed(); 381 382 assert.equal(save.thisValues[0].guid, "http://liftoff.msfc.nasa.gov/2003/06/03.html#item573"); 383 save.restore(); 384 }); 385 it('should update unread count', function* () { 386 assert.equal(feed.unreadCount, 3); 387 388 let feedItems = yield Zotero.FeedItems.getAll(feed.id); 389 for (let feedItem of feedItems) { 390 feedItem.isRead = true; 391 yield feedItem.saveTx(); 392 } 393 394 feed._feedUrl = modifiedFeedUrl; 395 yield feed.updateFeed(); 396 397 assert.equal(feed.unreadCount, 2); 398 }); 399 it('should add a link to enclosed pdfs from <enclosure/> elements', function* () { 400 let feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573"); 401 let pdf = yield Zotero.Items.getAsync(feedItem.getAttachments()[0]); 402 403 assert.equal(pdf.getField('url'), "http://www.example.com/example.pdf"); 404 }); 405 }); 406 407 describe("Adding items", function() { 408 let feed; 409 before(function* () { 410 feed = yield createFeed(); 411 }) 412 it("should not allow adding collections", function* () { 413 let collection = new Zotero.Collection({ name: 'test', libraryID: feed.libraryID }); 414 yield assert.isRejected(collection.saveTx({ skipEditCheck: true }), /^Cannot add /); 415 }); 416 it("should not allow adding saved search", function* () { 417 let search = new Zotero.Search({ name: 'test', libraryID: feed.libraryID }); 418 yield assert.isRejected(search.saveTx({ skipEditCheck: true }), /^Cannot add /); 419 }); 420 it("should allow adding feed item", function* () { 421 let feedItem = new Zotero.FeedItem('book', { guid: Zotero.randomString() }); 422 feedItem.libraryID = feed.libraryID; 423 yield assert.isFulfilled(feedItem.saveTx()); 424 }); 425 }); 426 })