mirror of
https://github.com/zotero/zotero.git
synced 2026-08-28 05:25:31 +00:00
776 lines
23 KiB
JavaScript
776 lines
23 KiB
JavaScript
"use strict";
|
|
|
|
describe("Zotero.DataObject", function () {
|
|
var types = ['collection', 'item', 'search'];
|
|
|
|
describe("#library", function () {
|
|
it("should return a Zotero.Library", async function () {
|
|
var item = await createDataObject('item');
|
|
assert.equal(item.library, Zotero.Libraries.userLibrary);
|
|
});
|
|
});
|
|
|
|
describe("#libraryID", function () {
|
|
it("should return a libraryID", async function () {
|
|
var item = await createDataObject('item');
|
|
assert.isNumber(item.libraryID);
|
|
assert.equal(item.libraryID, Zotero.Libraries.userLibraryID);
|
|
});
|
|
});
|
|
|
|
describe("#key", function () {
|
|
it("shouldn't update .loaded on get if unset", async function () {
|
|
for (let type of types) {
|
|
let param;
|
|
if (type == 'item') {
|
|
param = 'book';
|
|
}
|
|
let obj = new (Zotero[Zotero.Utilities.capitalize(type)])(param);
|
|
obj.libraryID = Zotero.Libraries.userLibraryID;
|
|
assert.isNull(obj.key, 'key is null for ' + type);
|
|
assert.isFalse(obj._loaded.primaryData, 'primary data not loaded for ' + type);
|
|
obj.key = Zotero.DataObjectUtilities.generateKey();
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#version", function () {
|
|
it("should be set to 0 after creating object", async function () {
|
|
for (let type of types) {
|
|
let obj = await createDataObject(type);
|
|
assert.equal(obj.version, 0);
|
|
await obj.eraseTx();
|
|
}
|
|
})
|
|
|
|
it("should be set after creating object", async function () {
|
|
for (let type of types) {
|
|
let obj = await createDataObject(type, { version: 1234 });
|
|
assert.equal(obj.version, 1234, type + " version mismatch");
|
|
await obj.eraseTx();
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#clientVersion", function () {
|
|
it("should be set to library clientVersion after creating object", async function () {
|
|
for (let type of types) {
|
|
let obj = await createDataObject(type);
|
|
assert.equal(obj.clientVersion, obj.library.clientVersion);
|
|
await obj.eraseTx();
|
|
}
|
|
});
|
|
|
|
it("should load 0 as a number", async function () {
|
|
for (let type of types) {
|
|
let objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(type);
|
|
let obj = await createDataObject(type);
|
|
let id = obj.id;
|
|
await Zotero.DB.queryAsync(
|
|
`UPDATE ${objectsClass.table} SET clientVersion=0 WHERE ${objectsClass.idColumn}=?`,
|
|
id
|
|
);
|
|
objectsClass.unload(id);
|
|
obj = await objectsClass.getAsync(id);
|
|
assert.strictEqual(obj.clientVersion, 0, type + " clientVersion mismatch");
|
|
}
|
|
});
|
|
|
|
it("should increase after modifying object", async function () {
|
|
for (let type of types) {
|
|
let obj = await createDataObject(type);
|
|
let clientVersion = obj.clientVersion;
|
|
|
|
await modifyDataObject(obj);
|
|
|
|
assert.isAbove(obj.clientVersion, clientVersion);
|
|
assert.equal(obj.clientVersion, obj.library.clientVersion);
|
|
|
|
await obj.eraseTx();
|
|
}
|
|
});
|
|
|
|
it("should increase once per library per transaction", async function () {
|
|
for (let type of types) {
|
|
let obj1 = await createDataObject(type);
|
|
let obj2 = await createDataObject(type);
|
|
|
|
let group = await getGroup();
|
|
let obj3 = await createDataObject(type, { libraryID: group.libraryID });
|
|
|
|
assert.isBelow(obj1.clientVersion, obj2.clientVersion);
|
|
assert.equal(obj2.clientVersion, Zotero.Libraries.userLibrary.clientVersion);
|
|
|
|
await modifyDataObject(obj1);
|
|
assert.isAbove(obj1.clientVersion, obj2.clientVersion);
|
|
assert.equal(obj1.clientVersion, Zotero.Libraries.userLibrary.clientVersion);
|
|
|
|
let libraryVersionBefore = Zotero.Libraries.userLibrary.clientVersion;
|
|
let groupVersionBefore = group.clientVersion;
|
|
await Zotero.DB.executeTransaction(async () => {
|
|
await modifyDataObject(obj1, undefined, { tx: false });
|
|
await modifyDataObject(obj2, undefined, { tx: false });
|
|
await modifyDataObject(obj3, undefined, { tx: false });
|
|
});
|
|
assert.equal(obj1.clientVersion, Zotero.Libraries.userLibrary.clientVersion);
|
|
assert.equal(obj1.clientVersion, obj2.clientVersion);
|
|
assert.notEqual(obj1.clientVersion, libraryVersionBefore);
|
|
|
|
assert.equal(obj3.clientVersion, group.clientVersion);
|
|
assert.notEqual(obj3.clientVersion, groupVersionBefore);
|
|
}
|
|
});
|
|
|
|
it("should increment library clientVersion when object is deleted", async function () {
|
|
for (let type of types) {
|
|
let obj = await createDataObject(type);
|
|
let libraryVersion = obj.library.clientVersion;
|
|
await obj.eraseTx();
|
|
assert.equal(obj.library.clientVersion, libraryVersion + 1);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("#synced", function () {
|
|
it("should be set to false after creating object", async function () {
|
|
for (let type of types) {
|
|
var obj = createUnsavedDataObject(type);
|
|
var id = await obj.saveTx();
|
|
assert.isFalse(obj.synced);
|
|
await obj.eraseTx();
|
|
}
|
|
});
|
|
|
|
it("should be set to false after modifying object", async function () {
|
|
for (let type of types) {
|
|
var obj = createUnsavedDataObject(type);
|
|
var id = await obj.saveTx();
|
|
|
|
obj.synced = true;
|
|
await obj.saveTx();
|
|
|
|
if (type == 'item') {
|
|
obj.setField('title', Zotero.Utilities.randomString());
|
|
}
|
|
else {
|
|
obj.name = Zotero.Utilities.randomString();
|
|
}
|
|
await obj.saveTx();
|
|
assert.isFalse(obj.synced);
|
|
|
|
await obj.eraseTx();
|
|
}
|
|
});
|
|
|
|
it("should be changed to true explicitly with no other changes", async function () {
|
|
for (let type of types) {
|
|
var obj = createUnsavedDataObject(type);
|
|
var id = await obj.saveTx();
|
|
|
|
obj.synced = true;
|
|
await obj.saveTx();
|
|
assert.isTrue(obj.synced);
|
|
|
|
await obj.eraseTx();
|
|
}
|
|
});
|
|
|
|
it("should be changed to true explicitly with other field changes", async function () {
|
|
for (let type of types) {
|
|
var obj = createUnsavedDataObject(type);
|
|
var id = await obj.saveTx();
|
|
|
|
if (type == 'item') {
|
|
obj.setField('title', Zotero.Utilities.randomString());
|
|
}
|
|
else {
|
|
obj.name = Zotero.Utilities.randomString();
|
|
}
|
|
obj.synced = true;
|
|
await obj.saveTx();
|
|
assert.isTrue(obj.synced);
|
|
|
|
await obj.eraseTx();
|
|
}
|
|
});
|
|
|
|
it("should remain at true if set explicitly", async function () {
|
|
for (let type of types) {
|
|
var obj = createUnsavedDataObject(type);
|
|
obj.synced = true;
|
|
var id = await obj.saveTx();
|
|
assert.isTrue(obj.synced);
|
|
|
|
if (type == 'item') {
|
|
obj.setField('title', 'test');
|
|
}
|
|
else {
|
|
obj.name = Zotero.Utilities.randomString();
|
|
}
|
|
obj.synced = true;
|
|
await obj.saveTx();
|
|
assert.isTrue(obj.synced);
|
|
|
|
await obj.eraseTx();
|
|
}
|
|
});
|
|
|
|
it("should be unchanged if skipSyncedUpdate passed", async function () {
|
|
for (let type of types) {
|
|
var obj = createUnsavedDataObject(type);
|
|
var id = await obj.saveTx();
|
|
|
|
obj.synced = true;
|
|
await obj.saveTx();
|
|
|
|
if (type == 'item') {
|
|
obj.setField('title', Zotero.Utilities.randomString());
|
|
}
|
|
else {
|
|
obj.name = Zotero.Utilities.randomString();
|
|
}
|
|
await obj.saveTx({
|
|
skipSyncedUpdate: true
|
|
});
|
|
assert.ok(obj.synced);
|
|
|
|
await obj.eraseTx();
|
|
}
|
|
});
|
|
})
|
|
|
|
|
|
describe("#deleted", function () {
|
|
it("should set trash status", async function () {
|
|
for (let type of types) {
|
|
let plural = Zotero.DataObjectUtilities.getObjectTypePlural(type)
|
|
let pluralClass = Zotero[Zotero.Utilities.capitalize(plural)];
|
|
|
|
// Set to true
|
|
var obj = await createDataObject(type);
|
|
assert.isFalse(obj.deleted, type);
|
|
obj.deleted = true;
|
|
// Sanity check for itemsTest#trash()
|
|
if (type == 'item') {
|
|
assert.isTrue(obj._changedData.deleted, type);
|
|
}
|
|
await obj.saveTx();
|
|
var id = obj.id;
|
|
await pluralClass.reload(id, false, true);
|
|
assert.isTrue(obj.deleted, type);
|
|
|
|
// Set to false
|
|
obj.deleted = false;
|
|
await obj.saveTx();
|
|
await pluralClass.reload(id, false, true);
|
|
assert.isFalse(obj.deleted, type);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("#loadPrimaryData()", function () {
|
|
it("should load unloaded primary data if partially set", async function () {
|
|
var objs = {};
|
|
for (let type of types) {
|
|
let obj = createUnsavedDataObject(type);
|
|
await obj.save({
|
|
skipCache: true
|
|
});
|
|
objs[type] = {
|
|
key: obj.key,
|
|
version: obj.version
|
|
};
|
|
}
|
|
|
|
for (let type of types) {
|
|
let obj = new (Zotero[Zotero.Utilities.capitalize(type)]);
|
|
obj.libraryID = Zotero.Libraries.userLibraryID;
|
|
obj.key = objs[type].key;
|
|
await obj.loadPrimaryData();
|
|
assert.equal(obj.version, objs[type].version);
|
|
}
|
|
});
|
|
|
|
it("shouldn't overwrite item type set in constructor", async function () {
|
|
var item = new Zotero.Item('book');
|
|
item.libraryID = Zotero.Libraries.userLibraryID;
|
|
item.key = Zotero.DataObjectUtilities.generateKey();
|
|
await item.loadPrimaryData();
|
|
var saved = await item.saveTx();
|
|
assert.ok(saved);
|
|
});
|
|
})
|
|
|
|
describe("#loadAllData()", function () {
|
|
it("should load data on a regular item", async function () {
|
|
var item = new Zotero.Item('book');
|
|
var id = await item.saveTx();
|
|
await item.loadAllData();
|
|
assert.throws(item.getNote.bind(item), 'getNote() can only be called on notes and attachments');
|
|
})
|
|
|
|
it("should load data on an attachment item", async function () {
|
|
var item = new Zotero.Item('attachment');
|
|
var id = await item.saveTx();
|
|
await item.loadAllData();
|
|
assert.equal(item.note, '');
|
|
})
|
|
|
|
it("should load data on a note item", async function () {
|
|
var item = new Zotero.Item('note');
|
|
var id = await item.saveTx();
|
|
await item.loadAllData();
|
|
assert.equal(item.note, '');
|
|
})
|
|
})
|
|
|
|
|
|
describe("#hasChanged()", function () {
|
|
it("should return false if 'synced' was set but unchanged and nothing else changed", async function () {
|
|
for (let type of types) {
|
|
// True
|
|
var obj = createUnsavedDataObject(type);
|
|
obj.synced = true;
|
|
var id = await obj.saveTx();
|
|
assert.isTrue(obj.synced);
|
|
|
|
obj.synced = true;
|
|
assert.isFalse(obj.hasChanged(), type + " shouldn't be changed");
|
|
|
|
// False
|
|
var obj = createUnsavedDataObject(type);
|
|
obj.synced = false;
|
|
var id = await obj.saveTx();
|
|
assert.isFalse(obj.synced);
|
|
obj.synced = false;
|
|
assert.isFalse(obj.hasChanged(), type + " shouldn't be changed");
|
|
}
|
|
})
|
|
|
|
it("should return true if 'synced' was set but unchanged and another primary field changed", async function () {
|
|
for (let type of types) {
|
|
let obj = createUnsavedDataObject(type);
|
|
obj.synced = true;
|
|
await obj.saveTx();
|
|
|
|
obj.synced = true;
|
|
obj.version = 1234;
|
|
assert.isTrue(obj.hasChanged());
|
|
}
|
|
})
|
|
});
|
|
|
|
|
|
describe("#save()", function () {
|
|
it("should add new identifiers to cache", async function () {
|
|
for (let type of types) {
|
|
let objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(type);
|
|
let obj = createUnsavedDataObject(type);
|
|
let id = await obj.saveTx();
|
|
let { libraryID, key } = objectsClass.getLibraryAndKeyFromID(id);
|
|
assert.typeOf(key, 'string');
|
|
assert.equal(objectsClass.getIDFromLibraryAndKey(libraryID, key), id);
|
|
}
|
|
})
|
|
|
|
it("should reset changed state on objects", async function () {
|
|
for (let type of types) {
|
|
let objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(type);
|
|
let obj = createUnsavedDataObject(type);
|
|
await obj.saveTx();
|
|
assert.isFalse(obj.hasChanged());
|
|
}
|
|
})
|
|
|
|
it("should handle additional tag change in the middle of a save", async function () {
|
|
var item = await createDataObject('item');
|
|
item.setTags(['a']);
|
|
|
|
var deferred = Zotero.Promise.defer();
|
|
var origFunc = Zotero.Notifier.queue.bind(Zotero.Notifier);
|
|
sinon.stub(Zotero.Notifier, "queue").callsFake(function (event, type, ids, extraData) {
|
|
// Add a new tag after the first one has been added to the DB and before the save is
|
|
// finished. The changed state should've cleared before saving to the DB the first
|
|
// time, so the second setTags() should mark the item as changed and allow the new tag
|
|
// to be saved in the second saveTx().
|
|
if (event == 'add' && type == 'item-tag') {
|
|
item.setTags(['a', 'b']);
|
|
Zotero.Notifier.queue.restore();
|
|
deferred.resolve(item.saveTx());
|
|
}
|
|
origFunc(...arguments);
|
|
});
|
|
|
|
await Promise.all([item.saveTx(), deferred.promise]);
|
|
assert.sameMembers(item.getTags().map(o => o.tag), ['a', 'b']);
|
|
var tags = await Zotero.DB.columnQueryAsync(
|
|
"SELECT name FROM tags JOIN itemTags USING (tagID) WHERE itemID=?", item.id
|
|
);
|
|
assert.sameMembers(tags, ['a', 'b']);
|
|
});
|
|
|
|
describe("Edit Check", function () {
|
|
var group;
|
|
|
|
before(function* () {
|
|
group = yield createGroup({
|
|
editable: false
|
|
});
|
|
});
|
|
|
|
it("should disallow saving to read-only libraries", async function () {
|
|
let item = createUnsavedDataObject('item', { libraryID: group.libraryID });
|
|
var e = await getPromiseError(item.saveTx());
|
|
assert.ok(e);
|
|
assert.include(e.message, "Cannot edit item");
|
|
});
|
|
|
|
it("should allow saving if skipEditCheck is passed", async function () {
|
|
let item = createUnsavedDataObject('item', { libraryID: group.libraryID });
|
|
var e = await getPromiseError(item.saveTx({
|
|
skipEditCheck: true
|
|
}));
|
|
assert.isFalse(e);
|
|
});
|
|
|
|
it("should allow saving if skipAll is passed", async function () {
|
|
let item = createUnsavedDataObject('item', { libraryID: group.libraryID });
|
|
var e = await getPromiseError(item.saveTx({
|
|
skipAll: true
|
|
}));
|
|
assert.isFalse(e);
|
|
});
|
|
});
|
|
})
|
|
|
|
describe("#erase()", function () {
|
|
it("shouldn't trigger notifier if skipNotifier is passed", async function () {
|
|
let observerIDs = [];
|
|
let promises = [];
|
|
for (let type of types) {
|
|
let obj = await createDataObject(type);
|
|
// For items, test automatic child item deletion
|
|
if (type == 'item') {
|
|
await createDataObject(type, { itemType: 'note', parentID: obj.id });
|
|
}
|
|
|
|
let deferred = Zotero.Promise.defer();
|
|
promises.push(deferred.promise);
|
|
observerIDs.push(Zotero.Notifier.registerObserver(
|
|
{
|
|
notify: function (event, type, ids) {
|
|
if (event == 'delete' && ids.includes(obj.id)) {
|
|
deferred.reject("Notifier called for erase on " + type);
|
|
}
|
|
}
|
|
},
|
|
type,
|
|
'test'
|
|
));
|
|
await obj.eraseTx({
|
|
skipNotifier: true
|
|
});
|
|
}
|
|
await Promise.race([
|
|
Promise.all(promises),
|
|
// Give notifier time to trigger
|
|
Zotero.Promise.delay(100),
|
|
]);
|
|
|
|
for (let id of observerIDs) {
|
|
Zotero.Notifier.unregisterObserver(id);
|
|
}
|
|
})
|
|
|
|
it("should delete object versions from sync cache", async function () {
|
|
for (let type of types) {
|
|
let obj = await createDataObject(type);
|
|
let libraryID = obj.libraryID;
|
|
let key = obj.key;
|
|
let json = obj.toJSON();
|
|
await Zotero.Sync.Data.Local.saveCacheObjects(type, libraryID, [json]);
|
|
await obj.eraseTx();
|
|
let versions = await Zotero.Sync.Data.Local.getCacheObjectVersions(
|
|
type, libraryID, key
|
|
);
|
|
assert.lengthOf(versions, 0);
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#updateVersion()", function () {
|
|
it("should update the object version", async function () {
|
|
for (let type of types) {
|
|
let obj = await createDataObject(type);
|
|
assert.equal(obj.version, 0);
|
|
|
|
await obj.updateVersion(1234);
|
|
assert.equal(obj.version, 1234);
|
|
assert.isFalse(obj.hasChanged());
|
|
|
|
obj.synced = true;
|
|
assert.ok(obj.hasChanged());
|
|
await obj.updateVersion(1235);
|
|
assert.equal(obj.version, 1235);
|
|
assert.ok(obj.hasChanged());
|
|
|
|
await obj.eraseTx();
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#updateSynced()", function () {
|
|
it("should update the object sync status", async function () {
|
|
for (let type of types) {
|
|
let obj = await createDataObject(type);
|
|
assert.isFalse(obj.synced);
|
|
|
|
await obj.updateSynced(false);
|
|
assert.isFalse(obj.synced);
|
|
assert.isFalse(obj.hasChanged());
|
|
|
|
await obj.updateSynced(true);
|
|
assert.ok(obj.synced);
|
|
assert.isFalse(obj.hasChanged());
|
|
|
|
obj.version = 1234;
|
|
assert.ok(obj.hasChanged());
|
|
await obj.updateSynced(false);
|
|
assert.isFalse(obj.synced);
|
|
assert.ok(obj.hasChanged());
|
|
|
|
await obj.eraseTx();
|
|
}
|
|
})
|
|
|
|
it("should clear changed status", async function () {
|
|
var item = createUnsavedDataObject('item');
|
|
item.synced = true;
|
|
await item.saveTx();
|
|
|
|
// Only synced changed
|
|
item.synced = false;
|
|
assert.isTrue(item.hasChanged());
|
|
assert.isTrue(item._changed.primaryData.synced);
|
|
await item.updateSynced(true);
|
|
assert.isFalse(item.hasChanged());
|
|
// Should clear primary data change object
|
|
assert.isUndefined(item._changed.primaryData);
|
|
|
|
// Another primary field also changed
|
|
item.setField('dateModified', '2017-02-27 12:34:56');
|
|
item.synced = false;
|
|
assert.isTrue(item.hasChanged());
|
|
assert.isTrue(item._changed.primaryData.synced);
|
|
await item.updateSynced(true);
|
|
assert.isTrue(item.hasChanged());
|
|
// Should clear only 'synced' change status
|
|
assert.isUndefined(item._changed.primaryData.synced);
|
|
});
|
|
})
|
|
|
|
describe("Relations", function () {
|
|
var types = ['collection', 'item'];
|
|
|
|
function makeObjectURI(objectType) {
|
|
var objectTypePlural = Zotero.DataObjectUtilities.getObjectTypePlural(objectType);
|
|
return 'http://zotero.org/groups/1/' + objectTypePlural + '/'
|
|
+ Zotero.Utilities.generateObjectKey();
|
|
}
|
|
|
|
describe("#addRelation()", function () {
|
|
it("should add a relation to an object", async function () {
|
|
for (let type of types) {
|
|
let predicate = 'owl:sameAs';
|
|
let object = makeObjectURI(type);
|
|
let obj = createUnsavedDataObject(type);
|
|
obj.addRelation(predicate, object);
|
|
await obj.saveTx();
|
|
var relations = obj.getRelations();
|
|
assert.property(relations, predicate);
|
|
assert.include(relations[predicate], object);
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#removeRelation()", function () {
|
|
it("should remove a relation from an object", async function () {
|
|
for (let type of types) {
|
|
let predicate = 'owl:sameAs';
|
|
let object = makeObjectURI(type);
|
|
let obj = createUnsavedDataObject(type);
|
|
obj.addRelation(predicate, object);
|
|
await obj.saveTx();
|
|
|
|
obj.removeRelation(predicate, object);
|
|
await obj.saveTx();
|
|
|
|
assert.lengthOf(Object.keys(obj.getRelations()), 0);
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#hasRelation()", function () {
|
|
it("should return true if an object has a given relation", async function () {
|
|
for (let type of types) {
|
|
let predicate = 'owl:sameAs';
|
|
let object = makeObjectURI(type);
|
|
let obj = createUnsavedDataObject(type);
|
|
obj.addRelation(predicate, object);
|
|
await obj.saveTx();
|
|
assert.ok(obj.hasRelation(predicate, object));
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("#setRelations()", function () {
|
|
it("shouldn't allow invalid 'relations' predicates", async function () {
|
|
var item = new Zotero.Item("book");
|
|
assert.throws(() => {
|
|
item.setRelations({
|
|
"0": ["http://example.com/foo"]
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#_getLinkedObject()", function () {
|
|
it("should return a linked object in another library", async function () {
|
|
var group = await getGroup();
|
|
var item1 = await createDataObject('item');
|
|
var item2 = await createDataObject('item', { libraryID: group.libraryID });
|
|
var item2URI = Zotero.URI.getItemURI(item2);
|
|
|
|
await item2.addLinkedItem(item1);
|
|
var linkedItem = await item1.getLinkedItem(item2.libraryID);
|
|
assert.equal(linkedItem.id, item2.id);
|
|
})
|
|
|
|
it("shouldn't return a linked item in the trash in another library", async function () {
|
|
var group = await getGroup();
|
|
var item1 = await createDataObject('item');
|
|
var item2 = await createDataObject('item', { libraryID: group.libraryID });
|
|
var item2URI = Zotero.URI.getItemURI(item2);
|
|
|
|
await item2.addLinkedItem(item1);
|
|
item2.deleted = true;
|
|
await item2.saveTx();
|
|
var linkedItem = await item1.getLinkedItem(item2.libraryID);
|
|
assert.isFalse(linkedItem);
|
|
})
|
|
|
|
it("shouldn't return reverse linked objects by default", async function () {
|
|
var group = await getGroup();
|
|
var item1 = await createDataObject('item');
|
|
var item1URI = Zotero.URI.getItemURI(item1);
|
|
var item2 = await createDataObject('item', { libraryID: group.libraryID });
|
|
|
|
await item2.addLinkedItem(item1);
|
|
var linkedItem = await item2.getLinkedItem(item1.libraryID);
|
|
assert.isFalse(linkedItem);
|
|
})
|
|
|
|
it("should return reverse linked objects with bidirectional flag", async function () {
|
|
var group = await getGroup();
|
|
var item1 = await createDataObject('item');
|
|
var item1URI = Zotero.URI.getItemURI(item1);
|
|
var item2 = await createDataObject('item', { libraryID: group.libraryID });
|
|
|
|
await item2.addLinkedItem(item1);
|
|
var linkedItem = await item2.getLinkedItem(item1.libraryID, true);
|
|
assert.equal(linkedItem.id, item1.id);
|
|
})
|
|
})
|
|
|
|
describe("#_addLinkedObject()", function () {
|
|
it("should add an owl:sameAs relation", async function () {
|
|
var group = await getGroup();
|
|
var item1 = await createDataObject('item');
|
|
var dateModified = item1.getField('dateModified');
|
|
var item2 = await createDataObject('item', { libraryID: group.libraryID });
|
|
var item2URI = Zotero.URI.getItemURI(item2);
|
|
|
|
await item2.addLinkedItem(item1);
|
|
var preds = item1.getRelationsByPredicate(Zotero.Relations.linkedObjectPredicate);
|
|
assert.include(preds, item2URI);
|
|
|
|
// Make sure Date Modified hasn't changed
|
|
assert.equal(item1.getField('dateModified'), dateModified);
|
|
})
|
|
})
|
|
});
|
|
|
|
describe("#fromJSON()", function () {
|
|
it("should remove object from trash if 'deleted' property not provided", async function () {
|
|
for (let type of types) {
|
|
let obj = await createDataObject(type, { deleted: true });
|
|
|
|
assert.isTrue(obj.deleted, type);
|
|
|
|
let json = obj.toJSON();
|
|
delete json.deleted;
|
|
|
|
obj.fromJSON(json);
|
|
await obj.saveTx();
|
|
|
|
assert.isFalse(obj.deleted, type);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("#toJSON()", function () {
|
|
it("should output 'deleted' as true", function () {
|
|
for (let type of types) {
|
|
let obj = createUnsavedDataObject(type);
|
|
obj.deleted = true;
|
|
let json = obj.toJSON();
|
|
assert.isTrue(json.deleted, type);
|
|
}
|
|
});
|
|
|
|
it("shouldn't include 'deleted' if not set in default mode", function () {
|
|
for (let type of types) {
|
|
let obj = createUnsavedDataObject(type);
|
|
let json = obj.toJSON();
|
|
assert.notProperty(json, 'deleted', type);
|
|
}
|
|
});
|
|
|
|
describe("'patch' mode", function () {
|
|
it("should include changed 'deleted' field", async function () {
|
|
for (let type of types) {
|
|
let plural = Zotero.DataObjectUtilities.getObjectTypePlural(type)
|
|
let pluralClass = Zotero[Zotero.Utilities.capitalize(plural)];
|
|
|
|
// True to false
|
|
let obj = createUnsavedDataObject(type)
|
|
obj.deleted = true;
|
|
let id = await obj.saveTx();
|
|
obj = await pluralClass.getAsync(id);
|
|
let patchBase = obj.toJSON();
|
|
|
|
obj.deleted = false;
|
|
let json = obj.toJSON({
|
|
patchBase: patchBase
|
|
})
|
|
assert.isUndefined(json.title, type);
|
|
assert.isFalse(json.deleted, type);
|
|
|
|
// False to true
|
|
obj = createUnsavedDataObject(type);
|
|
obj.deleted = false;
|
|
id = await obj.saveTx();
|
|
obj = await pluralClass.getAsync(id);
|
|
patchBase = obj.toJSON();
|
|
|
|
obj.deleted = true;
|
|
json = obj.toJSON({
|
|
patchBase: patchBase
|
|
})
|
|
assert.isUndefined(json.title, type);
|
|
assert.isTrue(json.deleted, type);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
})
|