DB isolation changes and item selection tweaks

- Add an 'exclusive' option to transactions that causes them to block other
  transactions and wait for other transactions to finish before starting,
  instead of nesting
- Resolve Zotero.DB.waitForTransaction() promise before returning from
  executeTransaction()
- A side effect of the above: wait for a newly created item to be selected in
  the middle pane and rendered in the right-hand pane before returning from
  executeTransaction()
- Don't save items multiple times when adding/removing a non-final creator in
  the Info pane
- Use a simpler, non-recursive method for focusing the next field in the Info
  pane; this prevents "too much recursion" errors if something causes the
  right-hand pane not to be rendered when expected
This commit is contained in:
Dan Stillman 2015-05-03 03:17:53 -04:00
parent 4a0018ec63
commit bdd44e9a44
7 changed files with 182 additions and 146 deletions

View File

@ -1680,20 +1680,9 @@
<method name="hideEditor"> <method name="hideEditor">
<parameter name="textbox"/> <parameter name="textbox"/>
<body> <body><![CDATA[
<![CDATA[ return Zotero.spawn(function* () {
try {
Zotero.debug('Hiding editor'); Zotero.debug('Hiding editor');
/*
var textbox = Zotero.getAncestorByTagName(t, 'textbox');
if (!textbox){
Zotero.debug('Textbox not found in hideEditor');
return;
}
*/
// TODO: get rid of this?
var saveChanges = this.saveOnEdit;
// Prevent autocomplete breakage in Firefox 3 // Prevent autocomplete breakage in Firefox 3
if (textbox.mController) { if (textbox.mController) {
@ -1738,7 +1727,7 @@
if (creatorsToShift > 0) { if (creatorsToShift > 0) {
//Add extra creators //Add extra creators
for (var i=0;i<nameArray.length;i++) { for (var i=0;i<nameArray.length;i++) {
this.modifyCreator(i+initNumCreators,otherFields); yield this.modifyCreator(i + initNumCreators, otherFields, true);
} }
//Shift existing creators //Shift existing creators
@ -1760,7 +1749,7 @@
otherFields.lastName=tempName; otherFields.lastName=tempName;
otherFields.firstName=''; otherFields.firstName='';
} }
this.modifyCreator(creatorIndex,otherFields); yield this.modifyCreator(creatorIndex, otherFields, true);
creatorIndex++; creatorIndex++;
} }
this._tabDirection = tabDirectionBuffer; this._tabDirection = tabDirectionBuffer;
@ -1773,7 +1762,11 @@
} }
} }
else { else {
this.modifyCreator(creatorIndex, otherFields); yield this.modifyCreator(creatorIndex, otherFields);
}
if (this.saveOnEdit) {
yield this.item.save();
} }
var val = this.item.getCreator(creatorIndex); var val = this.item.getCreator(creatorIndex);
@ -1885,15 +1878,8 @@
var focusBox = this._dynamicFields; var focusBox = this._dynamicFields;
this._focusNextField(focusBox, this._lastTabIndex, this._tabDirection == -1); this._focusNextField(focusBox, this._lastTabIndex, this._tabDirection == -1);
} }
} }, this);
// Thrown errors don't seem to show up within XBL without explicit logging ]]></body>
catch (e) {
Zotero.debug(e);
Components.utils.reportError(e);
throw (e);
}
]]>
</body>
</method> </method>
@ -2030,7 +2016,7 @@
<method name="modifyCreator"> <method name="modifyCreator">
<parameter name="index"/> <parameter name="index"/>
<parameter name="fields"/> <parameter name="fields"/>
<parameter name="changeGlobally"/> <parameter name="skipSave"/>
<body><![CDATA[ <body><![CDATA[
return Zotero.Promise.try(function () { return Zotero.Promise.try(function () {
var libraryID = this.item.libraryID; var libraryID = this.item.libraryID;
@ -2047,14 +2033,14 @@
return; return;
} }
this.item.removeCreator(index); this.item.removeCreator(index);
if (this.saveOnEdit) { if (this.saveOnEdit && !skipSave) {
return this.item.save(); return this.item.save();
} }
return; return;
} }
var changed = this.item.setCreator(index, fields); var changed = this.item.setCreator(index, fields);
if (changed && this.saveOnEdit) { if (changed && this.saveOnEdit && !skipSave) {
return this.item.save(); return this.item.save();
} }
}.bind(this)); }.bind(this));
@ -2157,75 +2143,52 @@
<![CDATA[ <![CDATA[
tabindex = parseInt(tabindex); tabindex = parseInt(tabindex);
if (back) // Get all fields with ztabindex attributes
{ var tabbableFields = box.querySelectorAll('label[ztabindex]');
switch (tabindex)
{ if (!tabbableFields.length) {
case 1: Zotero.debug("No tabbable fields found");
//Zotero.debug('At beginning');
document.getElementById('item-type-menu').focus();
return false; return false;
}
case this._tabIndexMinCreators: var next;
var nextIndex = 1; // Title field if (back) {
Zotero.debug('Looking for previous tabindex before ' + tabindex, 4);
for (let i = tabbableFields.length - 1; i >= 0; i--) {
if (parseInt(tabbableFields[i].getAttribute('ztabindex')) < tabindex) {
next = tabbableFields[i];
break; break;
}
case this._tabIndexMinFields: }
// No creators
if (this._tabIndexMaxCreators == 0) {
var nextIndex = 1; // Title field
} }
else { else {
var nextIndex = this._tabIndexMaxCreators; Zotero.debug('Looking for next tabindex after ' + tabindex, 4);
} for (var pos = 0; pos < tabbableFields.length; pos++) {
if (parseInt(tabbableFields[pos].getAttribute('ztabindex')) > tabindex) {
next = tabbableFields[pos];
break; break;
default:
var nextIndex = tabindex - 1;
} }
} }
else }
{
switch (tabindex)
{
case 1:
var nextIndex = this._tabIndexMinCreators;
break;
case this._tabIndexMaxCreators: if (!next) {
var nextIndex = this._tabIndexMinFields; Zotero.debug("Next field not found");
break;
case this._tabIndexMaxFields:
//Zotero.debug('At end');
return false; return false;
default:
var nextIndex = tabindex + 1;
}
} }
Zotero.debug('Looking for tabindex ' + nextIndex, 4); next.click();
var next = box.getElementsByAttribute('ztabindex', nextIndex); // 1) next.parentNode is always null for some reason
if (!next[0]) // 2) For some reason it's necessary to scroll to the next element when
{ // moving forward for the target element to be fully in view
//Zotero.debug("Next field not found"); if (!back && tabbableFields[pos + 1]) {
return this._focusNextField(box, nextIndex, back); Zotero.debug("Scrolling to next field");
} var visElem = tabbableFields[pos + 1];
next[0].click();
// DEBUG: next[0] is always equal to the target element,
// but for some reason it's necessary to scroll to the next
// element when moving forward for the target element to
// be fully in view
if (!back && next[0].parentNode.nextSibling) {
var visElem = next[0].parentNode.nextSibling;
} }
else { else {
var visElem = next[0]; var visElem = next;
} }
// DEBUG: This doesn't seem to work anymore
this.ensureElementIsVisible(visElem); this.ensureElementIsVisible(visElem);
return true; return true;

View File

@ -441,6 +441,9 @@ Zotero.DBConnection.prototype.getNextName = Zotero.Promise.coroutine(function* (
* @return {Promise} - Promise for result of generator function * @return {Promise} - Promise for result of generator function
*/ */
Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(function* (func, options) { Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(function* (func, options) {
options = options || {};
var resolve;
// Set temporary options for this transaction that will be reset at the end // Set temporary options for this transaction that will be reset at the end
var origOptions = {}; var origOptions = {};
if (options) { if (options) {
@ -450,19 +453,21 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
} }
} }
if ((options.exclusive && this._inTransaction) || this._inExclusiveTransaction) {
yield Zotero.DB.waitForTransaction();
}
try { try {
if (this._inTransaction) { if (this._inTransaction) {
Zotero.debug("Async DB transaction in progress -- increasing level to " Zotero.debug("Async DB transaction in progress -- increasing level to "
+ ++this._asyncTransactionNestingLevel, 5); + ++this._asyncTransactionNestingLevel, 5);
if (options) {
if (options.onCommit) { if (options.onCommit) {
this._callbacks.current.commit.push(options.onCommit); this._callbacks.current.commit.push(options.onCommit);
} }
if (options.onRollback) { if (options.onRollback) {
this._callbacks.current.rollback.push(options.onRollback); this._callbacks.current.rollback.push(options.onRollback);
} }
}
try { try {
var result = yield Zotero.Promise.coroutine(func)(); var result = yield Zotero.Promise.coroutine(func)();
@ -481,12 +486,10 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
Zotero.debug("Beginning async DB transaction", 5); Zotero.debug("Beginning async DB transaction", 5);
this._inTransaction = true; this._inTransaction = true;
this._inExclusiveTransaction = options.exclusive;
var resolve;
var reject;
this._transactionPromise = new Zotero.Promise(function () { this._transactionPromise = new Zotero.Promise(function () {
resolve = arguments[0]; resolve = arguments[0];
reject = arguments[1];
}); });
// Set a timestamp for this transaction // Set a timestamp for this transaction
@ -535,8 +538,6 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
} }
} }
setTimeout(resolve, 0);
return result; return result;
} }
} }
@ -544,6 +545,7 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
Zotero.debug("Rolled back async DB transaction", 5); Zotero.debug("Rolled back async DB transaction", 5);
Zotero.debug(e, 1); Zotero.debug(e, 1);
this._inTransaction = false; this._inTransaction = false;
this._inExclusiveTransaction = false;
if (options) { if (options) {
// Function to run once transaction has been committed but before any // Function to run once transaction has been committed but before any
@ -566,12 +568,6 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
} }
} }
if (reject) {
setTimeout(function () {
reject(e);
}, 0);
}
throw e; throw e;
} }
finally { finally {
@ -581,18 +577,21 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
this[option] = origOptions[option]; this[option] = origOptions[option];
} }
} }
// Process all resolvers
if (resolve) {
resolve.call();
}
} }
}); });
Zotero.DBConnection.prototype.waitForTransaction = function () { Zotero.DBConnection.prototype.waitForTransaction = function () {
if (!this._transactionPromise) { if (!this._inTransaction) {
return Zotero.Promise.resolve(); return Zotero.Promise.resolve();
} }
Zotero.debug("Waiting for transaction to finish"); Zotero.debug("Waiting for transaction to finish");
return this._transactionPromise.then(function () { return this._transactionPromise;
Zotero.debug("Done waiting for transaction");
});
}; };

View File

@ -932,7 +932,9 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
} }
//this._treebox.endUpdateBatch(); //this._treebox.endUpdateBatch();
var promise = this._getItemSelectedPromise();
this.selection.selectEventsSuppressed = false; this.selection.selectEventsSuppressed = false;
yield promise;
}); });
/* /*
@ -1588,9 +1590,6 @@ Zotero.ItemTreeView.prototype.sort = Zotero.Promise.coroutine(function* (itemID)
* Select an item * Select an item
*/ */
Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (id, expand, noRecurse) { Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (id, expand, noRecurse) {
var selected = this.getSelectedItems(true);
var alreadySelected = selected.length == 1 && selected[0] == id;
// Don't change selection if UI updates are disabled (e.g., during sync) // Don't change selection if UI updates are disabled (e.g., during sync)
if (Zotero.suppressUIUpdates) { if (Zotero.suppressUIUpdates) {
Zotero.debug("Sync is running; not selecting item"); Zotero.debug("Sync is running; not selecting item");
@ -1655,20 +1654,14 @@ Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (i
row = this._itemRowMap[id]; row = this._itemRowMap[id];
} }
// This function calls nsITreeSelection.select(), which triggers the <tree>'s 'onselect' // This function calls nsITreeSelection.select(), which triggers the <tree>'s 'onselect'
// attribute, which calls ZoteroPane.itemSelected(), which calls ZoteroItemPane.viewItem(), // attribute, which calls ZoteroPane.itemSelected(), which calls ZoteroItemPane.viewItem(),
// which refreshes the itembox. But since the 'onselect' doesn't handle promises, // which refreshes the itembox. But since the 'onselect' doesn't handle promises,
// itemSelected() isn't waited for and 'yield selectItem(itemID)' continues before the // itemSelected() isn't waited for and 'yield selectItem(itemID)' continues before the
// itembox has been refreshed. To get around this, we make a promise resolver that's // itembox has been refreshed. To get around this, we make a promise resolver that's
// triggered by itemSelected() when it's done. // triggered by itemSelected() when it's done.
if (!alreadySelected && !this.selection.selectEventsSuppressed) { if (!this.selection.selectEventsSuppressed) {
var itemSelectedPromise = new Zotero.Promise(function () { var itemSelectedPromise = this._getItemSelectedPromise(id);
this._itemSelectedPromiseResolver = {
resolve: arguments[0],
reject: arguments[1]
};
}.bind(this));
} }
this.selection.select(row); this.selection.select(row);
@ -1679,7 +1672,7 @@ Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (i
} }
this.selection.select(row); this.selection.select(row);
if (!alreadySelected && !this.selection.selectEventsSuppressed) { if (!this.selection.selectEventsSuppressed) {
yield itemSelectedPromise; yield itemSelectedPromise;
} }
@ -1705,6 +1698,23 @@ Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (i
}); });
Zotero.ItemTreeView.prototype._getItemSelectedPromise = function (itemID) {
if (itemID) {
var selected = this.getSelectedItems(true);
var alreadySelected = selected.length == 1 && selected[0] == itemID;
if (alreadySelected) {
return Zotero.Promise.resolve();
}
}
return new Zotero.Promise(function () {
this._itemSelectedPromiseResolver = {
resolve: arguments[0],
reject: arguments[1]
};
}.bind(this));
}
/** /**
* Select multiple top-level items * Select multiple top-level items
* *

View File

@ -972,7 +972,7 @@ Zotero.Search.idsToTempTable = function (ids) {
yield Zotero.DB.queryAsync(sql); yield Zotero.DB.queryAsync(sql);
return tmpTable; return tmpTable;
}); }, { exclusive: true });
} }

View File

@ -781,12 +781,11 @@ var ZoteroPane = new function()
// Update most-recently-used list for New Item menu // Update most-recently-used list for New Item menu
this.addItemTypeToNewItemTypeMRU(typeID); this.addItemTypeToNewItemTypeMRU(typeID);
yield this.selectItem(itemID);
// Focus the title field // Focus the title field
document.getElementById('zotero-editpane-item-box').focusFirstField(); document.getElementById('zotero-editpane-item-box').focusFirstField();
} }
return Zotero.Items.get(itemID); return Zotero.Items.getAsync(itemID);
}); });

View File

@ -1,7 +1,15 @@
describe("Zotero.DB", function() { describe("Zotero.DB", function() {
var tmpTable = "tmpDBTest";
beforeEach(function* () {
Zotero.DB.queryAsync("DROP TABLE IF EXISTS " + tmpTable);
});
after(function* () {
Zotero.DB.queryAsync("DROP TABLE IF EXISTS " + tmpTable);
});
describe("#executeTransaction()", function () { describe("#executeTransaction()", function () {
it("should nest concurrent transactions", Zotero.Promise.coroutine(function* () { it("should nest concurrent transactions", Zotero.Promise.coroutine(function* () {
var tmpTable = "tmpWaitForTransactions";
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)"); yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
var resolve1, resolve2, reject1, reject2; var resolve1, resolve2, reject1, reject2;
@ -18,8 +26,7 @@ describe("Zotero.DB", function() {
yield Zotero.Promise.delay(100); yield Zotero.Promise.delay(100);
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0); assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)"); yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
// Make sure we're still in a transaction Zotero.DB.transactionDate; // Make sure we're still in a transaction
Zotero.DB.transactionDate;
}) })
.then(resolve1) .then(resolve1)
.catch(reject1); .catch(reject1);
@ -27,8 +34,7 @@ describe("Zotero.DB", function() {
Zotero.DB.executeTransaction(function* () { Zotero.DB.executeTransaction(function* () {
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 1); assert.equal(Zotero.DB._asyncTransactionNestingLevel, 1);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)"); yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
// Make sure we're still in a transaction Zotero.DB.transactionDate; // Make sure we're still in a transaction
Zotero.DB.transactionDate;
}) })
.then(resolve2) .then(resolve2)
.catch(reject2); .catch(reject2);
@ -36,8 +42,77 @@ describe("Zotero.DB", function() {
yield Zotero.Promise.all([promise1, promise2]); yield Zotero.Promise.all([promise1, promise2]);
})); }));
it("shouldn't nest transactions if an exclusive transaction is open", Zotero.Promise.coroutine(function* () {
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
var resolve1, resolve2, reject1, reject2;
var promise1 = new Promise(function (resolve, reject) {
resolve1 = resolve;
reject1 = reject;
});
var promise2 = new Promise(function (resolve, reject) {
resolve2 = resolve;
reject2 = reject;
});
Zotero.DB.executeTransaction(function* () {
yield Zotero.Promise.delay(100);
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
Zotero.DB.transactionDate; // Make sure we're still in a transaction
}, { exclusive: true })
.then(resolve1)
.catch(reject1);
Zotero.DB.executeTransaction(function* () {
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
assert.equal(num, 1);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
Zotero.DB.transactionDate; // Make sure we're still in a transaction
})
.then(resolve2)
.catch(reject2);
yield Zotero.Promise.all([promise1, promise2]);
}));
it("shouldn't nest an exclusive transaction if another transaction is open", Zotero.Promise.coroutine(function* () {
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
var resolve1, resolve2, reject1, reject2;
var promise1 = new Promise(function (resolve, reject) {
resolve1 = resolve;
reject1 = reject;
});
var promise2 = new Promise(function (resolve, reject) {
resolve2 = resolve;
reject2 = reject;
});
Zotero.DB.executeTransaction(function* () {
yield Zotero.Promise.delay(100);
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
Zotero.DB.transactionDate; // Make sure we're still in a transaction
})
.then(resolve1)
.catch(reject1);
Zotero.DB.executeTransaction(function* () {
assert.equal(Zotero.DB._asyncTransactionNestingLevel, 0);
var num = yield Zotero.DB.valueQueryAsync("SELECT COUNT(*) FROM " + tmpTable);
assert.equal(num, 1);
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (2)");
Zotero.DB.transactionDate; // Make sure we're still in a transaction
}, { exclusive: true })
.then(resolve2)
.catch(reject2);
yield Zotero.Promise.all([promise1, promise2]);
}));
it("should roll back on error", function* () { it("should roll back on error", function* () {
var tmpTable = "tmpRollbackOnError";
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)"); yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)"); yield Zotero.DB.queryAsync("INSERT INTO " + tmpTable + " VALUES (1)");
try { try {
@ -59,7 +134,6 @@ describe("Zotero.DB", function() {
}); });
it("should run onRollback callbacks", function* () { it("should run onRollback callbacks", function* () {
var tmpTable = "tmpOnRollback";
var callbackRan = false; var callbackRan = false;
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)"); yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
try { try {
@ -84,7 +158,6 @@ describe("Zotero.DB", function() {
}); });
it("should run onRollback callbacks for nested transactions", function* () { it("should run onRollback callbacks for nested transactions", function* () {
var tmpTable = "tmpOnNestedRollback";
var callback1Ran = false; var callback1Ran = false;
var callback2Ran = false; var callback2Ran = false;
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)"); yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
@ -120,7 +193,6 @@ describe("Zotero.DB", function() {
}); });
it("should not commit nested transactions", function* () { it("should not commit nested transactions", function* () {
var tmpTable = "tmpNoCommitNested";
yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)"); yield Zotero.DB.queryAsync("CREATE TABLE " + tmpTable + " (foo INT)");
try { try {
yield Zotero.DB.executeTransaction(function* () { yield Zotero.DB.executeTransaction(function* () {

View File

@ -18,7 +18,6 @@ describe("Zotero.ItemTreeView", function() {
var item = new Zotero.Item('book'); var item = new Zotero.Item('book');
existingItemID = yield item.save(); existingItemID = yield item.save();
yield Zotero.Promise.delay(100);
}); });
after(function () { after(function () {
if (win) { if (win) {
@ -53,7 +52,6 @@ describe("Zotero.ItemTreeView", function() {
var id = yield item.save(); var id = yield item.save();
// New item should be selected // New item should be selected
yield Zotero.Promise.delay(100);
var selected = itemsView.getSelectedItems(); var selected = itemsView.getSelectedItems();
assert.lengthOf(selected, 1); assert.lengthOf(selected, 1);
assert.equal(selected[0].id, id); assert.equal(selected[0].id, id);
@ -92,7 +90,6 @@ describe("Zotero.ItemTreeView", function() {
}); });
// Existing item should still be selected // Existing item should still be selected
yield Zotero.Promise.delay(100);
selected = itemsView.getSelectedItems(true); selected = itemsView.getSelectedItems(true);
assert.lengthOf(selected, 1); assert.lengthOf(selected, 1);
assert.equal(selected[0], existingItemID); assert.equal(selected[0], existingItemID);
@ -103,7 +100,6 @@ describe("Zotero.ItemTreeView", function() {
var item = new Zotero.Item('book'); var item = new Zotero.Item('book');
var id = yield item.save(); var id = yield item.save();
item = yield Zotero.Items.getAsync(id); item = yield Zotero.Items.getAsync(id);
yield Zotero.Promise.delay(100);
itemsView.selection.clearSelection(); itemsView.selection.clearSelection();
assert.lengthOf(itemsView.getSelectedItems(), 0); assert.lengthOf(itemsView.getSelectedItems(), 0);
@ -112,7 +108,6 @@ describe("Zotero.ItemTreeView", function() {
yield item.save(); yield item.save();
// Modified item should not be selected // Modified item should not be selected
yield Zotero.Promise.delay(100);
assert.lengthOf(itemsView.getSelectedItems(), 0); assert.lengthOf(itemsView.getSelectedItems(), 0);
}); });
@ -121,7 +116,6 @@ describe("Zotero.ItemTreeView", function() {
var item = new Zotero.Item('book'); var item = new Zotero.Item('book');
var id = yield item.save(); var id = yield item.save();
item = yield Zotero.Items.getAsync(id); item = yield Zotero.Items.getAsync(id);
yield Zotero.Promise.delay(100);
yield itemsView.selectItem(id); yield itemsView.selectItem(id);
var selected = itemsView.getSelectedItems(true); var selected = itemsView.getSelectedItems(true);
@ -132,7 +126,6 @@ describe("Zotero.ItemTreeView", function() {
yield item.save(); yield item.save();
// Modified item should still be selected // Modified item should still be selected
yield Zotero.Promise.delay(100);
selected = itemsView.getSelectedItems(true); selected = itemsView.getSelectedItems(true);
assert.lengthOf(selected, 1); assert.lengthOf(selected, 1);
assert.equal(selected[0], id); assert.equal(selected[0], id);