describe("Zotero.Items", function () { var win, collectionsView, zp; before(function* () { this.timeout(10000); win = yield loadZoteroPane(); collectionsView = win.ZoteroPane.collectionsView; zp = win.ZoteroPane; }) beforeEach(function () { return selectLibrary(win); }) after(function () { win.close(); }) describe("#merge()", function () { it("should merge two items", function* () { var item1 = yield createDataObject('item'); var item2 = yield createDataObject('item'); var item2URI = Zotero.URI.getItemURI(item2); yield Zotero.Items.merge(item1, [item2]); assert.isFalse(item1.deleted); assert.isTrue(item2.deleted); // Check for merge-tracking relation assert.isFalse(item1.hasChanged()); var rels = item1.getRelationsByPredicate(Zotero.Relations.replacedItemPredicate); assert.lengthOf(rels, 1); assert.equal(rels[0], item2URI); }) it("should move merge-tracking relation from replaced item to master", function* () { var item1 = yield createDataObject('item'); var item2 = yield createDataObject('item'); var item2URI = Zotero.URI.getItemURI(item2); var item3 = yield createDataObject('item'); var item3URI = Zotero.URI.getItemURI(item3); yield Zotero.Items.merge(item2, [item3]); yield Zotero.Items.merge(item1, [item2]); // Check for merge-tracking relation from 1 to 3 var rels = item1.getRelationsByPredicate(Zotero.Relations.replacedItemPredicate); assert.lengthOf(rels, 2); assert.sameMembers(rels, [item2URI, item3URI]); }) it("should update relations pointing to replaced item to point to master", function* () { var item1 = yield createDataObject('item'); var item1URI = Zotero.URI.getItemURI(item1); var item2 = yield createDataObject('item'); var item2URI = Zotero.URI.getItemURI(item2); var item3 = createUnsavedDataObject('item'); var predicate = Zotero.Relations.relatedItemPredicate; item3.addRelation(predicate, item2URI); yield item3.saveTx(); yield Zotero.Items.merge(item1, [item2]); // Check for related-item relation from 3 to 1 var rels = item3.getRelationsByPredicate(predicate); assert.deepEqual(rels, [item1URI]); }) it("should not update relations pointing to replaced item in other libraries", function* () { var group1 = yield createGroup(); var group2 = yield createGroup(); var item1 = yield createDataObject('item', { libraryID: group1.libraryID }); var item1URI = Zotero.URI.getItemURI(item1); var item2 = yield createDataObject('item', { libraryID: group1.libraryID }); var item2URI = Zotero.URI.getItemURI(item2); var item3 = createUnsavedDataObject('item', { libraryID: group2.libraryID }); var predicate = Zotero.Relations.linkedObjectPredicate; item3.addRelation(predicate, item2URI); yield item3.saveTx(); yield Zotero.Items.merge(item1, [item2]); // Check for related-item relation from 3 to 2 var rels = item3.getRelationsByPredicate(predicate); assert.deepEqual(rels, [item2URI]); }) }) describe("#trash()", function () { it("should send items to the trash", function* () { var items = []; items.push( (yield createDataObject('item', { synced: true })), (yield createDataObject('item', { synced: true })), (yield createDataObject('item', { synced: true })) ); items.forEach(item => { // Sanity-checked as true in itemTest#deleted assert.isUndefined(item._changed.deleted); }); var ids = items.map(item => item.id); yield Zotero.Items.trashTx(ids); items.forEach(item => { assert.isTrue(item.deleted); // Item should be saved (can't use hasChanged() because that includes .synced) assert.isUndefined(item._changed.deleted); assert.isFalse(item.synced); }); assert.equal((yield Zotero.DB.valueQueryAsync( `SELECT COUNT(*) FROM deletedItems WHERE itemID IN (${ids})` )), 3); for (let item of items) { assert.equal((yield Zotero.DB.valueQueryAsync( `SELECT synced FROM items WHERE itemID=${item.id}` )), 0); } }); it("should update parent item when trashing child item", function* () { var item = yield createDataObject('item'); var note = yield createDataObject('item', { itemType: 'note', parentID: item.id }); assert.lengthOf(item.getNotes(), 1); yield Zotero.Items.trashTx([note.id]); assert.lengthOf(item.getNotes(), 0); }); }); describe("#emptyTrash()", function () { it("should delete items in the trash", function* () { var item1 = createUnsavedDataObject('item'); item1.setField('title', 'a'); item1.deleted = true; var id1 = yield item1.saveTx(); var item2 = createUnsavedDataObject('item'); item2.setField('title', 'b'); item2.deleted = true; var id2 = yield item2.saveTx(); var item3 = createUnsavedDataObject('item', { itemType: 'attachment', parentID: id2 }); item3.attachmentLinkMode = Zotero.Attachments.LINK_MODE_IMPORTED_URL; item3.deleted = true; var id3 = yield item3.saveTx(); yield collectionsView.selectTrash(Zotero.Libraries.userLibraryID); yield Zotero.Items.emptyTrash(Zotero.Libraries.userLibraryID); assert.isFalse(yield Zotero.Items.getAsync(id1)); assert.isFalse(yield Zotero.Items.getAsync(id2)); assert.isFalse(yield Zotero.Items.getAsync(id3)); // TEMP: This is failing on Travis due to a race condition //assert.equal(zp.itemsView.rowCount, 0) }) }) describe("#getFirstCreatorFromData()", function () { it("should handle single eligible creator", function* () { for (let creatorType of ['author', 'editor', 'contributor']) { assert.equal( Zotero.Items.getFirstCreatorFromData( Zotero.ItemTypes.getID('book'), [ { fieldMode: 0, firstName: 'A', lastName: 'B', creatorTypeID: Zotero.CreatorTypes.getID(creatorType) } ] ), 'B', creatorType ); } }); it("should ignore single ineligible creator", function* () { assert.strictEqual( Zotero.Items.getFirstCreatorFromData( Zotero.ItemTypes.getID('book'), [ { fieldMode: 0, firstName: 'A', lastName: 'B', creatorTypeID: Zotero.CreatorTypes.getID('translator') } ] ), '' ); }); it("should handle single eligible creator after ineligible creator", function* () { for (let creatorType of ['author', 'editor', 'contributor']) { assert.equal( Zotero.Items.getFirstCreatorFromData( Zotero.ItemTypes.getID('book'), [ { fieldMode: 0, firstName: 'A', lastName: 'B', creatorTypeID: Zotero.CreatorTypes.getID('translator') }, { fieldMode: 0, firstName: 'C', lastName: 'D', creatorTypeID: Zotero.CreatorTypes.getID(creatorType) } ] ), 'D', creatorType ); } }); it("should handle two eligible creators", function* () { for (let creatorType of ['author', 'editor', 'contributor']) { assert.equal( Zotero.Items.getFirstCreatorFromData( Zotero.ItemTypes.getID('book'), [ { fieldMode: 0, firstName: 'A', lastName: 'B', creatorTypeID: Zotero.CreatorTypes.getID(creatorType) }, { fieldMode: 0, firstName: 'C', lastName: 'D', creatorTypeID: Zotero.CreatorTypes.getID(creatorType) } ] ), 'B ' + Zotero.getString('general.and') + ' D', creatorType ); } }); it("should handle three eligible creators", function* () { for (let creatorType of ['author', 'editor', 'contributor']) { assert.equal( Zotero.Items.getFirstCreatorFromData( Zotero.ItemTypes.getID('book'), [ { fieldMode: 0, firstName: 'A', lastName: 'B', creatorTypeID: Zotero.CreatorTypes.getID(creatorType) }, { fieldMode: 0, firstName: 'C', lastName: 'D', creatorTypeID: Zotero.CreatorTypes.getID(creatorType) }, { fieldMode: 0, firstName: 'E', lastName: 'F', creatorTypeID: Zotero.CreatorTypes.getID(creatorType) } ] ), 'B ' + Zotero.getString('general.etAl'), creatorType ); } }); it("should handle two eligible creators with intervening creators", function* () { for (let creatorType of ['author', 'editor', 'contributor']) { assert.equal( Zotero.Items.getFirstCreatorFromData( Zotero.ItemTypes.getID('book'), [ { fieldMode: 0, firstName: 'A', lastName: 'B', creatorTypeID: Zotero.CreatorTypes.getID('translator') }, { fieldMode: 0, firstName: 'C', lastName: 'D', creatorTypeID: Zotero.CreatorTypes.getID(creatorType) }, { fieldMode: 0, firstName: 'E', lastName: 'F', creatorTypeID: Zotero.CreatorTypes.getID('translator') }, { fieldMode: 0, firstName: 'G', lastName: 'H', creatorTypeID: Zotero.CreatorTypes.getID(creatorType) } ] ), 'D ' + Zotero.getString('general.and') + ' H', creatorType ); } }); }); describe("#getAsync()", function() { it("should return Zotero.Item for item ID", function* () { let item = new Zotero.Item('journalArticle'); let id = yield item.saveTx(); item = yield Zotero.Items.getAsync(id); assert.notOk(item.isFeedItem); assert.instanceOf(item, Zotero.Item); assert.notInstanceOf(item, Zotero.FeedItem); }); it("should return Zotero.FeedItem for feed item ID", function* () { let feed = new Zotero.Feed({ name: 'foo', url: 'http://www.' + Zotero.randomString() + '.com' }); yield feed.saveTx(); let feedItem = new Zotero.FeedItem('journalArticle', { guid: Zotero.randomString() }); feedItem.libraryID = feed.libraryID; let id = yield feedItem.saveTx(); feedItem = yield Zotero.Items.getAsync(id); assert.isTrue(feedItem.isFeedItem); assert.instanceOf(feedItem, Zotero.FeedItem); }); }); });