describe("Zotero.Library", function () { describe("#constructor()", function () { it("should allow no arguments", function () { assert.doesNotThrow(() => new Zotero.Library()); }); }); describe("#libraryID", function () { it("should not allow setting a library ID", function () { let library = new Zotero.Library(); assert.throws(() => library.libraryID = 1); }); it("should return a library ID for a saved library", function () { let library = Zotero.Libraries.get(Zotero.Libraries.userLibraryID); assert.isAbove(library.libraryID, 0); }) }); describe("#libraryType", function () { it("should not allow creating a non-basic library", function () { let library = new Zotero.Library(); assert.throws(() => library.libraryType = 'group', /^Invalid library type /); }); it("should not allow setting a library type for a saved library", async function () { let library = await createGroup(); assert.throws(() => library.libraryType = 'feed'); }); it("should not allow creating new unique libraries", async function () { for (let i=0; i library.libraryVersion = -2); assert.throws(() => library.libraryVersion = "a"); assert.throws(() => library.libraryVersion = 1.1); assert.doesNotThrow(() => library.libraryVersion = 0); assert.doesNotThrow(() => library.libraryVersion = 5); }); it("should not be possible to decrement", function () { let library = new Zotero.Library(); library.libraryVersion = 5; assert.throws(() => library.libraryVersion = 0); }); it("should be possible to set to -1", function () { let library = new Zotero.Library(); library.libraryVersion = 5; assert.doesNotThrow(() => library.libraryVersion = -1); }); }); describe("#clientVersion", function () { it("should be settable to increasing values", function () { let library = new Zotero.Library(); assert.throws(() => library.clientVersion = -2); assert.throws(() => library.clientVersion = "a"); assert.throws(() => library.clientVersion = 1.1); assert.doesNotThrow(() => library.clientVersion = 0); assert.doesNotThrow(() => library.clientVersion = 5); }); it("should not be possible to decrement", function () { let library = new Zotero.Library(); library.clientVersion = 5; assert.throws(() => library.clientVersion = 0); }); it("should not be possible to set to -1", function () { let library = new Zotero.Library(); library.clientVersion = 5; assert.throws(() => library.clientVersion = -1); }); }); describe("#editable", function () { it("should return editable status", function () { let library = Zotero.Libraries.get(Zotero.Libraries.userLibraryID); assert.isTrue(library.editable, 'user library is editable'); }); it("should allow setting editable status", async function () { let library = await createGroup({ editable: true }); assert.isTrue(library.editable); assert.isTrue(Zotero.Libraries.isEditable(library.libraryID), "sets editable in cache to true"); assert.equal(((await Zotero.DB.valueQueryAsync("SELECT editable FROM libraries WHERE libraryID=?", library.libraryID))), 1) library.editable = false; await library.saveTx(); assert.isFalse(library.editable); assert.isFalse(Zotero.Libraries.isEditable(library.libraryID), "sets editable in cache to false"); assert.equal(((await Zotero.DB.valueQueryAsync("SELECT editable FROM libraries WHERE libraryID=?", library.libraryID))), 0) }); it("should also set filesEditable to false", async function () { let library = await createGroup({ editable: true, filesEditable: true }); assert.isTrue(library.filesEditable); library.editable = false; await library.saveTx(); assert.isFalse(library.filesEditable); assert.equal(((await Zotero.DB.valueQueryAsync("SELECT filesEditable FROM libraries WHERE libraryID=?", library.libraryID))), 0) }); it("should not be settable for user libraries", async function () { let library = Zotero.Libraries.get(Zotero.Libraries.userLibraryID); assert.throws(function () {library.editable = false}, /^Cannot change _libraryEditable for user library$/, "does not allow setting user library as not editable"); }); }); describe("#filesEditable", function () { it("should always return true for user library", function () { assert.isTrue(Zotero.Libraries.userLibrary.filesEditable); }); it("should return files editable status", function () { let library = Zotero.Libraries.get(Zotero.Libraries.userLibraryID); assert.isTrue(library.filesEditable, 'user library is files editable'); }); it("should allow setting files editable status", async function () { let library = await createGroup({ filesEditable: true }); assert.isTrue(library.filesEditable); assert.isTrue(Zotero.Libraries.isFilesEditable(library.libraryID), "sets files editable in cache to true"); library.filesEditable = false; await library.saveTx(); assert.isFalse(library.filesEditable); assert.isFalse(Zotero.Libraries.isFilesEditable(library.libraryID), "sets files editable in cache to false"); }); it("should not be settable for user libraries", async function () { let library = Zotero.Libraries.get(Zotero.Libraries.userLibraryID); assert.throws(function () {library.filesEditable = false}, /^Cannot change _libraryFilesEditable for user library$/, "does not allow setting user library as not files editable"); }); }); describe("#allowsLinkedFiles", function () { it("should return true for personal library", function () { assert.isTrue(Zotero.Libraries.userLibrary.allowsLinkedFiles); }); it("should return false for group libraries", async function () { var group = await getGroup(); assert.isFalse(group.allowsLinkedFiles); }); }); describe("#archived", function () { it("should return archived status", function () { let library = Zotero.Libraries.get(Zotero.Libraries.userLibraryID); assert.isFalse(library.archived, 'user library is not archived'); }); it("should allow setting archived status", async function () { let library = await createGroup({ editable: false, archived: true }); assert.isTrue(library.archived); assert.equal(((await Zotero.DB.valueQueryAsync("SELECT archived FROM libraries WHERE libraryID=?", library.libraryID))), 1) library.archived = false; await library.saveTx(); assert.isFalse(library.archived); assert.equal(((await Zotero.DB.valueQueryAsync("SELECT archived FROM libraries WHERE libraryID=?", library.libraryID))), 0) }); it("should not be settable for user libraries", async function () { let library = Zotero.Libraries.get(Zotero.Libraries.userLibraryID); assert.throws(() => library.archived = true, /^Cannot change _libraryArchived for user library$/, "does not allow setting user library as archived"); }); it("should only be settable on read-only library", async function () { let library = await createGroup(); assert.throws(() => library.archived = true, /^Cannot set editable library as archived$/); }); }); describe("#save()", function () { it("should require mandatory parameters to be set", async function () { let library = new Zotero.Library({ editable: true, filesEditable: true }); assert.equal((await getPromiseError(library.saveTx())).message, 'libraryType must be set before saving'); // Required group params let groupID = Zotero.Utilities.rand(1000, 10000); let name = 'foo'; let description = ''; let version = Zotero.Utilities.rand(1000, 10000); library = new Zotero.Group({ filesEditable: true, groupID, name, description, version }); assert.equal((await getPromiseError(library.saveTx())).message, 'editable must be set before saving'); library = new Zotero.Group({ editable: true, groupID, name, description, version }); assert.equal((await getPromiseError(library.saveTx())).message, 'filesEditable must be set before saving'); library = new Zotero.Group({ editable: true, filesEditable: true, groupID, name, description, version }); await library.saveTx(); }); it("should save new library to DB", async function () { let library = await createGroup({}); assert.isAbove(library.libraryID, 0, "sets a libraryID"); assert.isTrue(Zotero.Libraries.exists(library.libraryID)); assert.equal(library.libraryType, 'group'); let inDB = await Zotero.DB.valueQueryAsync('SELECT COUNT(*) FROM libraries WHERE libraryID=?', library.libraryID); assert.ok(inDB, 'added to DB'); }); it("should save library changes to DB", async function () { let library = await createGroup({ editable: true }); library.editable = false; await library.saveTx(); assert.isFalse(Zotero.Libraries.isEditable(library.libraryID)); }); it("should initialize library after creation", async function () { let library = await createGroup({}); Zotero.SyncedSettings.get(library.libraryID, "tagColors"); }); }); describe("#erase()", function () { it("should erase a group library", async function () { let library = await createGroup(); let libraryID = library.libraryID; await library.eraseTx(); assert.isFalse(Zotero.Libraries.exists(libraryID), "library no longer exists in cache");assert.isFalse(Zotero.Libraries.exists(libraryID)); let inDB = await Zotero.DB.valueQueryAsync('SELECT COUNT(*) FROM libraries WHERE libraryID=?', libraryID); assert.notOk(inDB, 'removed from DB'); }); it("should erase a read-only library", async function () { let library = await createGroup({ editable:false, filesEditable:false }); await library.eraseTx(); }); it("should not allow erasing permanent libraries", async function () { let library = Zotero.Libraries.get(Zotero.Libraries.userLibraryID); assert.equal((await getPromiseError(library.eraseTx())).message, "Cannot erase library of type 'user'"); }); it("should not allow erasing unsaved libraries", async function () { let library = new Zotero.Library(); assert.ok(await getPromiseError(library.eraseTx())); }); it("should throw when accessing erased library methods, except for #libraryID and #name", async function () { let library = await createGroup(); await library.eraseTx(); assert.doesNotThrow(() => library.libraryID); assert.throws(() => library.editable = false, /^Group \(\d+\) has been disabled$/); }); it("should clear child items from caches and DB", async function () { let group = await createGroup(); let libraryID = group.libraryID; let collection = await createDataObject('collection', { libraryID }); assert.ok(await Zotero.Collections.getAsync(collection.id)); let item = await createDataObject('item', { libraryID }); assert.ok(await Zotero.Items.getAsync(item.id)); let search = await createDataObject('search', { libraryID }); assert.ok(await Zotero.Searches.getAsync(search.id)); await group.eraseTx(); assert.notOk(((await Zotero.Searches.getAsync(search.id))), 'search was unloaded'); assert.notOk(((await Zotero.Collections.getAsync(collection.id))), 'collection was unloaded'); assert.notOk(((await Zotero.Items.getAsync(item.id))), 'item was unloaded'); }); it("should delete attachment files", async function () { // My Library var item1 = await createDataObject('item'); var attachment1 = await importFileAttachment('test.png', { parentID: item1.id }); var path1 = attachment1.getFilePath(); // Group var group = await createGroup(); var libraryID = group.libraryID; var item2 = await createDataObject('item', { libraryID }); var attachment2 = await importFileAttachment('test.png', { libraryID, parentID: item2.id }); var path2 = attachment2.getFilePath(); assert.isTrue(await OS.File.exists(path1)); assert.isTrue(await OS.File.exists(path2)); await group.eraseTx(); // My Library file should still exist, but group file should be deleted assert.isTrue(await OS.File.exists(path1)); assert.isFalse(await OS.File.exists(path2)); }); }); describe("#hasCollections()", function () { it("should throw if called before saving a library", function () { let library = new Zotero.Library(); assert.throws(() => library.hasCollections()); }); it("should stay up to date as collections are added and removed", async function () { let library = await createGroup({ editable: true }); let libraryID = library.libraryID; assert.isFalse(library.hasCollections()); let c1 = await createDataObject('collection', { libraryID }); assert.isTrue(library.hasCollections()); let c2 = await createDataObject('collection', { libraryID }); assert.isTrue(library.hasCollections()); await c1.eraseTx(); assert.isTrue(library.hasCollections()); await c2.eraseTx(); assert.isFalse(library.hasCollections()); }) }); describe("#hasSearches()", function () { it("should throw if called before saving a library", function () { let library = new Zotero.Library(); assert.throws(() => library.hasSearches()); }); it("should stay up to date as searches are added and removed", async function () { let library = await createGroup({ editable: true }); let libraryID = library.libraryID; assert.isFalse(library.hasSearches()); let s1 = await createDataObject('search', { libraryID }); assert.isTrue(library.hasSearches()); let s2 = await createDataObject('search', { libraryID }); assert.isTrue(library.hasSearches()); await s1.eraseTx(); assert.isTrue(library.hasSearches()); await s2.eraseTx(); assert.isFalse(library.hasSearches()); }) }); describe("#hasItems()", function () { it("should throw if called before saving a library", async function () { let library = new Zotero.Library(); try { await library.hasItems(); assert.isFalse(true, "Library#hasItems did not throw an error"); } catch (e) { assert.ok(e); } }); it("should stay up to date as items are added and removed", async function () { let library = await createGroup({ editable: true }); let libraryID = library.libraryID; var hasItems = await library.hasItems(); assert.isFalse(hasItems); let i1 = await createDataObject('item', { libraryID }); hasItems = await library.hasItems(); assert.isTrue(hasItems); let i2 = await createDataObject('item', { libraryID }); hasItems = await library.hasItems(); assert.isTrue(hasItems); await i1.eraseTx(); hasItems = await library.hasItems(); assert.isTrue(hasItems); await i2.eraseTx(); hasItems = await library.hasItems(); assert.isFalse(hasItems); }); }); describe("#updateLastSyncTime()", function () { it("should set sync time to current time", async function () { let group = await createGroup(); assert.isFalse(group.lastSync); group.updateLastSyncTime(); assert.ok(group.lastSync); assert.closeTo(Date.now(), group.lastSync.getTime(), 1000); await group.saveTx(); let dbTime = await Zotero.DB.valueQueryAsync('SELECT lastSync FROM libraries WHERE libraryID=?', group.libraryID); assert.equal(dbTime*1000, group.lastSync.getTime()); }) }); })