itemsTest.js (18249B)
1 describe("Zotero.Items", function () { 2 var win, collectionsView, zp; 3 4 before(function* () { 5 this.timeout(10000); 6 win = yield loadZoteroPane(); 7 collectionsView = win.ZoteroPane.collectionsView; 8 zp = win.ZoteroPane; 9 }) 10 beforeEach(function () { 11 return selectLibrary(win); 12 }) 13 after(function () { 14 win.close(); 15 }) 16 17 18 describe("#addToPublications", function () { 19 it("should add an item to My Publications", function* () { 20 var item = yield createDataObject('item'); 21 yield Zotero.Items.addToPublications([item]); 22 assert.isTrue(item.inPublications); 23 assert.equal( 24 (yield Zotero.DB.valueQueryAsync( 25 "SELECT COUNT(*) FROM publicationsItems WHERE itemID=?", item.id)), 26 1 27 ); 28 }); 29 30 describe("#license", function () { 31 it("should set a license if specified", function* () { 32 var item = createUnsavedDataObject('item'); 33 item.setField('rights', 'Test'); 34 yield item.saveTx(); 35 yield Zotero.Items.addToPublications( 36 [item], 37 { 38 license: 'reserved', 39 licenseName: 'All Rights Reserved', 40 keepRights: false 41 } 42 ); 43 assert.equal(item.getField('rights'), 'All Rights Reserved'); 44 }); 45 46 it("should keep existing Rights field if .keepRights is true", function* () { 47 var item1 = createUnsavedDataObject('item'); 48 item1.setField('rights', 'Test'); 49 yield item1.saveTx(); 50 var item2 = yield createDataObject('item'); 51 yield Zotero.Items.addToPublications( 52 [item1, item2], 53 { 54 license: 'reserved', 55 licenseName: 'All Rights Reserved', 56 keepRights: true 57 } 58 ); 59 assert.equal(item1.getField('rights'), 'Test'); 60 assert.equal(item2.getField('rights'), 'All Rights Reserved'); 61 }); 62 63 it("shouldn't set a license if not specified", function* () { 64 var item = createUnsavedDataObject('item'); 65 item.setField('rights', 'Test'); 66 yield item.saveTx(); 67 yield Zotero.Items.addToPublications([item]); 68 assert.equal(item.getField('rights'), 'Test'); 69 }); 70 }); 71 72 it("should add child notes if .childNotes is true", function* () { 73 var item = yield createDataObject('item'); 74 var note = yield createDataObject('item', { itemType: 'note', parentID: item.id }); 75 var attachment = yield Zotero.Attachments.linkFromURL({ 76 url: "http://example.com", 77 parentItemID: item.id, 78 title: "Example" 79 }); 80 81 yield Zotero.Items.addToPublications([item], { childNotes: true }); 82 assert.isTrue(note.inPublications); 83 assert.equal( 84 (yield Zotero.DB.valueQueryAsync( 85 "SELECT COUNT(*) FROM publicationsItems WHERE itemID=?", note.id)), 86 1 87 ); 88 assert.isFalse(attachment.inPublications); 89 }); 90 91 it("should add child link attachments if .childLinks is true", function* () { 92 var item = yield createDataObject('item'); 93 var attachment1 = yield Zotero.Attachments.linkFromURL({ 94 url: "http://example.com", 95 parentItemID: item.id, 96 title: "Example" 97 }); 98 var attachment2 = yield importFileAttachment('test.png', { parentItemID: item.id }); 99 var note = yield createDataObject('item', { itemType: 'note', parentID: item.id }); 100 101 yield Zotero.Items.addToPublications([item], { childLinks: true }); 102 assert.isTrue(attachment1.inPublications); 103 assert.equal( 104 (yield Zotero.DB.valueQueryAsync( 105 "SELECT COUNT(*) FROM publicationsItems WHERE itemID=?", attachment1.id)), 106 1 107 ); 108 assert.isFalse(attachment2.inPublications); 109 assert.isFalse(note.inPublications); 110 }); 111 112 it("should add child file attachments if .childFileAttachments is true", function* () { 113 var item = yield createDataObject('item'); 114 var attachment1 = yield importFileAttachment('test.png', { parentItemID: item.id }); 115 var attachment2 = yield Zotero.Attachments.linkFromURL({ 116 url: "http://example.com", 117 parentItemID: item.id, 118 title: "Example" 119 }); 120 var note = yield createDataObject('item', { itemType: 'note', parentID: item.id }); 121 122 yield Zotero.Items.addToPublications([item], { childFileAttachments: true }); 123 assert.isTrue(attachment1.inPublications); 124 assert.equal( 125 (yield Zotero.DB.valueQueryAsync( 126 "SELECT COUNT(*) FROM publicationsItems WHERE itemID=?", attachment1.id)), 127 1 128 ); 129 assert.isFalse(attachment2.inPublications); 130 assert.isFalse(note.inPublications); 131 }); 132 }); 133 134 135 describe("#removeFromPublications", function () { 136 it("should remove an item from My Publications", function* () { 137 var item = yield createDataObject('item'); 138 item.inPublications = true; 139 yield item.saveTx(); 140 assert.equal( 141 (yield Zotero.DB.valueQueryAsync( 142 "SELECT COUNT(*) FROM publicationsItems WHERE itemID=?", item.id)), 143 1 144 ); 145 yield Zotero.Items.removeFromPublications([item]); 146 assert.isFalse(item.inPublications); 147 assert.equal( 148 (yield Zotero.DB.valueQueryAsync( 149 "SELECT COUNT(*) FROM publicationsItems WHERE itemID=?", item.id)), 150 0 151 ); 152 }); 153 }); 154 155 156 describe("#merge()", function () { 157 it("should merge two items", function* () { 158 var item1 = yield createDataObject('item'); 159 var item2 = yield createDataObject('item'); 160 var item2URI = Zotero.URI.getItemURI(item2); 161 162 yield Zotero.Items.merge(item1, [item2]); 163 164 assert.isFalse(item1.deleted); 165 assert.isTrue(item2.deleted); 166 167 // Check for merge-tracking relation 168 assert.isFalse(item1.hasChanged()); 169 var rels = item1.getRelationsByPredicate(Zotero.Relations.replacedItemPredicate); 170 assert.lengthOf(rels, 1); 171 assert.equal(rels[0], item2URI); 172 }) 173 174 it("should merge three items", async function () { 175 var item1 = await createDataObject('item'); 176 var item2 = await createDataObject('item'); 177 var item3 = await createDataObject('item'); 178 var item2URI = Zotero.URI.getItemURI(item2); 179 var item3URI = Zotero.URI.getItemURI(item3); 180 181 await Zotero.Items.merge(item1, [item2, item3]); 182 183 assert.isFalse(item1.deleted); 184 assert.isTrue(item2.deleted); 185 assert.isTrue(item3.deleted); 186 187 // Check for merge-tracking relation 188 assert.isFalse(item1.hasChanged()); 189 var rels = item1.getRelationsByPredicate(Zotero.Relations.replacedItemPredicate); 190 assert.lengthOf(rels, 2); 191 assert.sameMembers(rels, [item2URI, item3URI]); 192 }) 193 194 it("should merge two items when servant is linked to an item absent from cache", function* () { 195 // two group libraries 196 var groupOneInfo = yield createGroup({ 197 id: 25026, 198 name: "Group One" 199 }); 200 var libraryOneID = Zotero.Groups.getLibraryIDFromGroupID(groupOneInfo.id); 201 202 var groupTwoInfo = yield createGroup({ 203 id: 11592, 204 name: "Group Two" 205 }); 206 var libraryTwoID = Zotero.Groups.getLibraryIDFromGroupID(groupTwoInfo.id); 207 208 assert.notEqual(libraryOneID, libraryTwoID); 209 210 // two items in the first library 211 var item1 = yield createDataObject('item', {libraryID: libraryOneID}); 212 var item2 = yield createDataObject('item', {libraryID: libraryOneID}); 213 var item2URI = Zotero.URI.getItemURI(item2); 214 215 // one item in the second library, linked to item2 as if it dragged and dropped from it 216 var itemX = yield createDataObject('item', {libraryID: libraryTwoID}); 217 yield itemX.addLinkedItem(item2); 218 219 // check that the owl:sameAs relation has been registered okay 220 var rels = itemX.getRelationsByPredicate(Zotero.Relations.linkedObjectPredicate); 221 assert.lengthOf(rels, 1); 222 assert.equal(rels[0], item2URI); 223 224 // the freshly minted item is in objectCache, but it might be absent in production, 225 // so we clobber it in this test 226 assert(!!Zotero.Items._objectCache[itemX.id], "itemX is in object cache") 227 delete Zotero.Items._objectCache[itemX.id]; 228 229 // merge the two items in the first library 230 yield Zotero.Items.merge(item1, [item2]); 231 232 // check that the merge completed okay 233 assert.isFalse(item1.deleted); 234 assert.isTrue(item2.deleted); 235 236 // Check for merge-tracking relation 237 assert.isFalse(item1.hasChanged()); 238 var rels = item1.getRelationsByPredicate(Zotero.Relations.replacedItemPredicate); 239 assert.lengthOf(rels, 1); 240 assert.equal(rels[0], item2URI); 241 }) 242 243 it("should move merge-tracking relation from replaced item to master", function* () { 244 var item1 = yield createDataObject('item'); 245 var item2 = yield createDataObject('item'); 246 var item2URI = Zotero.URI.getItemURI(item2); 247 var item3 = yield createDataObject('item'); 248 var item3URI = Zotero.URI.getItemURI(item3); 249 250 yield Zotero.Items.merge(item2, [item3]); 251 yield Zotero.Items.merge(item1, [item2]); 252 253 // Check for merge-tracking relation from 1 to 3 254 var rels = item1.getRelationsByPredicate(Zotero.Relations.replacedItemPredicate); 255 assert.lengthOf(rels, 2); 256 assert.sameMembers(rels, [item2URI, item3URI]); 257 }) 258 259 it("should update relations pointing to replaced item to point to master", function* () { 260 var item1 = yield createDataObject('item'); 261 var item1URI = Zotero.URI.getItemURI(item1); 262 var item2 = yield createDataObject('item'); 263 var item2URI = Zotero.URI.getItemURI(item2); 264 var item3 = createUnsavedDataObject('item'); 265 var predicate = Zotero.Relations.relatedItemPredicate; 266 item3.addRelation(predicate, item2URI); 267 yield item3.saveTx(); 268 269 yield Zotero.Items.merge(item1, [item2]); 270 271 // Check for related-item relation from 3 to 1 272 var rels = item3.getRelationsByPredicate(predicate); 273 assert.deepEqual(rels, [item1URI]); 274 }) 275 276 it("should not update relations pointing to replaced item in other libraries", function* () { 277 var group1 = yield createGroup(); 278 var group2 = yield createGroup(); 279 280 var item1 = yield createDataObject('item', { libraryID: group1.libraryID }); 281 var item1URI = Zotero.URI.getItemURI(item1); 282 var item2 = yield createDataObject('item', { libraryID: group1.libraryID }); 283 var item2URI = Zotero.URI.getItemURI(item2); 284 var item3 = createUnsavedDataObject('item', { libraryID: group2.libraryID }); 285 var predicate = Zotero.Relations.linkedObjectPredicate; 286 item3.addRelation(predicate, item2URI); 287 yield item3.saveTx(); 288 289 yield Zotero.Items.merge(item1, [item2]); 290 291 // Check for related-item relation from 3 to 2 292 var rels = item3.getRelationsByPredicate(predicate); 293 assert.deepEqual(rels, [item2URI]); 294 }) 295 }) 296 297 298 describe("#trash()", function () { 299 it("should send items to the trash", function* () { 300 var items = []; 301 items.push( 302 (yield createDataObject('item', { synced: true })), 303 (yield createDataObject('item', { synced: true })), 304 (yield createDataObject('item', { synced: true })) 305 ); 306 items.forEach(item => { 307 // Sanity-checked as true in itemTest#deleted 308 assert.isUndefined(item._changed.deleted); 309 }); 310 var ids = items.map(item => item.id); 311 yield Zotero.Items.trashTx(ids); 312 items.forEach(item => { 313 assert.isTrue(item.deleted); 314 // Item should be saved (can't use hasChanged() because that includes .synced) 315 assert.isUndefined(item._changed.deleted); 316 assert.isFalse(item.synced); 317 }); 318 assert.equal((yield Zotero.DB.valueQueryAsync( 319 `SELECT COUNT(*) FROM deletedItems WHERE itemID IN (${ids})` 320 )), 3); 321 for (let item of items) { 322 assert.equal((yield Zotero.DB.valueQueryAsync( 323 `SELECT synced FROM items WHERE itemID=${item.id}` 324 )), 0); 325 } 326 }); 327 328 it("should update parent item when trashing child item", function* () { 329 var item = yield createDataObject('item'); 330 var note = yield createDataObject('item', { itemType: 'note', parentID: item.id }); 331 assert.lengthOf(item.getNotes(), 1); 332 yield Zotero.Items.trashTx([note.id]); 333 assert.lengthOf(item.getNotes(), 0); 334 }); 335 }); 336 337 338 describe("#emptyTrash()", function () { 339 it("should delete items in the trash", function* () { 340 var item1 = createUnsavedDataObject('item'); 341 item1.setField('title', 'a'); 342 item1.deleted = true; 343 var id1 = yield item1.saveTx(); 344 345 var item2 = createUnsavedDataObject('item'); 346 item2.setField('title', 'b'); 347 item2.deleted = true; 348 var id2 = yield item2.saveTx(); 349 350 var item3 = createUnsavedDataObject('item', { itemType: 'attachment', parentID: id2 }); 351 item3.attachmentLinkMode = Zotero.Attachments.LINK_MODE_IMPORTED_URL; 352 item3.deleted = true; 353 var id3 = yield item3.saveTx(); 354 355 yield collectionsView.selectTrash(Zotero.Libraries.userLibraryID); 356 357 yield Zotero.Items.emptyTrash(Zotero.Libraries.userLibraryID); 358 359 assert.isFalse(yield Zotero.Items.getAsync(id1)); 360 assert.isFalse(yield Zotero.Items.getAsync(id2)); 361 assert.isFalse(yield Zotero.Items.getAsync(id3)); 362 363 // TEMP: This is failing on Travis due to a race condition 364 //assert.equal(zp.itemsView.rowCount, 0) 365 }) 366 }) 367 368 describe("#getFirstCreatorFromData()", function () { 369 it("should handle single eligible creator", function* () { 370 for (let creatorType of ['author', 'editor', 'contributor']) { 371 assert.equal( 372 Zotero.Items.getFirstCreatorFromData( 373 Zotero.ItemTypes.getID('book'), 374 [ 375 { 376 fieldMode: 0, 377 firstName: 'A', 378 lastName: 'B', 379 creatorTypeID: Zotero.CreatorTypes.getID(creatorType) 380 } 381 ] 382 ), 383 'B', 384 creatorType 385 ); 386 } 387 }); 388 389 it("should ignore single ineligible creator", function* () { 390 assert.strictEqual( 391 Zotero.Items.getFirstCreatorFromData( 392 Zotero.ItemTypes.getID('book'), 393 [ 394 { 395 fieldMode: 0, 396 firstName: 'A', 397 lastName: 'B', 398 creatorTypeID: Zotero.CreatorTypes.getID('translator') 399 } 400 ] 401 ), 402 '' 403 ); 404 }); 405 406 it("should handle single eligible creator after ineligible creator", function* () { 407 for (let creatorType of ['author', 'editor', 'contributor']) { 408 assert.equal( 409 Zotero.Items.getFirstCreatorFromData( 410 Zotero.ItemTypes.getID('book'), 411 [ 412 { 413 fieldMode: 0, 414 firstName: 'A', 415 lastName: 'B', 416 creatorTypeID: Zotero.CreatorTypes.getID('translator') 417 }, 418 { 419 fieldMode: 0, 420 firstName: 'C', 421 lastName: 'D', 422 creatorTypeID: Zotero.CreatorTypes.getID(creatorType) 423 } 424 ] 425 ), 426 'D', 427 creatorType 428 ); 429 } 430 }); 431 432 it("should handle two eligible creators", function* () { 433 for (let creatorType of ['author', 'editor', 'contributor']) { 434 assert.equal( 435 Zotero.Items.getFirstCreatorFromData( 436 Zotero.ItemTypes.getID('book'), 437 [ 438 { 439 fieldMode: 0, 440 firstName: 'A', 441 lastName: 'B', 442 creatorTypeID: Zotero.CreatorTypes.getID(creatorType) 443 }, 444 { 445 fieldMode: 0, 446 firstName: 'C', 447 lastName: 'D', 448 creatorTypeID: Zotero.CreatorTypes.getID(creatorType) 449 } 450 ] 451 ), 452 'B ' + Zotero.getString('general.and') + ' D', 453 creatorType 454 ); 455 } 456 }); 457 458 it("should handle three eligible creators", function* () { 459 for (let creatorType of ['author', 'editor', 'contributor']) { 460 assert.equal( 461 Zotero.Items.getFirstCreatorFromData( 462 Zotero.ItemTypes.getID('book'), 463 [ 464 { 465 fieldMode: 0, 466 firstName: 'A', 467 lastName: 'B', 468 creatorTypeID: Zotero.CreatorTypes.getID(creatorType) 469 }, 470 { 471 fieldMode: 0, 472 firstName: 'C', 473 lastName: 'D', 474 creatorTypeID: Zotero.CreatorTypes.getID(creatorType) 475 }, 476 { 477 fieldMode: 0, 478 firstName: 'E', 479 lastName: 'F', 480 creatorTypeID: Zotero.CreatorTypes.getID(creatorType) 481 } 482 ] 483 ), 484 'B ' + Zotero.getString('general.etAl'), 485 creatorType 486 ); 487 } 488 }); 489 490 it("should handle two eligible creators with intervening creators", function* () { 491 for (let creatorType of ['author', 'editor', 'contributor']) { 492 assert.equal( 493 Zotero.Items.getFirstCreatorFromData( 494 Zotero.ItemTypes.getID('book'), 495 [ 496 { 497 fieldMode: 0, 498 firstName: 'A', 499 lastName: 'B', 500 creatorTypeID: Zotero.CreatorTypes.getID('translator') 501 }, 502 { 503 fieldMode: 0, 504 firstName: 'C', 505 lastName: 'D', 506 creatorTypeID: Zotero.CreatorTypes.getID(creatorType) 507 }, 508 { 509 fieldMode: 0, 510 firstName: 'E', 511 lastName: 'F', 512 creatorTypeID: Zotero.CreatorTypes.getID('translator') 513 }, 514 { 515 fieldMode: 0, 516 firstName: 'G', 517 lastName: 'H', 518 creatorTypeID: Zotero.CreatorTypes.getID(creatorType) 519 } 520 ] 521 ), 522 'D ' + Zotero.getString('general.and') + ' H', 523 creatorType 524 ); 525 } 526 }); 527 }); 528 529 describe("#getAsync()", function() { 530 it("should return Zotero.Item for item ID", function* () { 531 let item = new Zotero.Item('journalArticle'); 532 let id = yield item.saveTx(); 533 item = yield Zotero.Items.getAsync(id); 534 assert.notOk(item.isFeedItem); 535 assert.instanceOf(item, Zotero.Item); 536 assert.notInstanceOf(item, Zotero.FeedItem); 537 }); 538 it("should return Zotero.FeedItem for feed item ID", function* () { 539 let feed = new Zotero.Feed({ name: 'foo', url: 'http://www.' + Zotero.randomString() + '.com' }); 540 yield feed.saveTx(); 541 542 let feedItem = new Zotero.FeedItem('journalArticle', { guid: Zotero.randomString() }); 543 feedItem.libraryID = feed.libraryID; 544 let id = yield feedItem.saveTx(); 545 546 feedItem = yield Zotero.Items.getAsync(id); 547 548 assert.isTrue(feedItem.isFeedItem); 549 assert.instanceOf(feedItem, Zotero.FeedItem); 550 }); 551 }); 552 553 describe("#keepParents()", function () { 554 it("should remove child items of passed items", async function () { 555 var item1 = await createDataObject('item'); 556 var item2 = await createDataObject('item', { itemType: 'note', parentItemID: item1.id }); 557 var item3 = await createDataObject('item', { itemType: 'note', parentItemID: item1.id }); 558 var item4 = await createDataObject('item'); 559 var item5 = await createDataObject('item', { itemType: 'note', parentItemID: item4.id }); 560 var otherItem = await createDataObject('item'); 561 var item6 = await createDataObject('item', { itemType: 'note', parentItemID: otherItem.id }); 562 563 var items = Zotero.Items.keepParents([item1, item2, item3, item4, item5, item6]); 564 assert.sameMembers( 565 // Convert to ids for clearer output 566 items.map(item => item.id), 567 [item1, item4, item6].map(item => item.id) 568 ); 569 }); 570 }); 571 });