Fix attachments & annotation box refresh bugs and add tests (#4031)
Fixes #3993 Fixes #3995 Closes #4082
This commit is contained in:
parent
07e8f0a01d
commit
91c0c28b5d
10 changed files with 806 additions and 49 deletions
|
@ -46,53 +46,95 @@
|
|||
}
|
||||
|
||||
set item(item) {
|
||||
super.item = item;
|
||||
super.item = (item instanceof Zotero.Item && item.isFileAttachment()) ? item : null;
|
||||
this._updateHidden();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.initCollapsibleSection();
|
||||
|
||||
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'attachmentAnnotationsBox');
|
||||
|
||||
this._body = this.querySelector('.body');
|
||||
|
||||
this._annotationItems = [];
|
||||
}
|
||||
|
||||
destroy() {}
|
||||
destroy() {
|
||||
Zotero.Notifier.unregisterObserver(this._notifierID);
|
||||
}
|
||||
|
||||
notify(action, type, ids) {
|
||||
if (action == 'modify' && this.item && ids.includes(this.item.id)) {
|
||||
this._forceRenderAll();
|
||||
notify(event, _type, ids, _extraData) {
|
||||
if (!this.item) return;
|
||||
|
||||
this._annotationItems = this.item.getAnnotations();
|
||||
let annotations = this._annotationItems.filter(
|
||||
annotation => ids.includes(annotation.id));
|
||||
|
||||
if (["add", "modify"].includes(event)) {
|
||||
for (let annotation of annotations) {
|
||||
let row = this.querySelector(`annotation-row[annotation-id="${annotation.id}"]`);
|
||||
row?.remove();
|
||||
this.addRow(annotation);
|
||||
}
|
||||
}
|
||||
else if (event == 'delete') {
|
||||
for (let id of ids) {
|
||||
let row = this.querySelector(`annotation-row[annotation-id="${id}"]`);
|
||||
row?.remove();
|
||||
}
|
||||
}
|
||||
this.updateCount();
|
||||
}
|
||||
|
||||
render() {
|
||||
this._annotationItems = this.item.getAnnotations();
|
||||
this.updateCount();
|
||||
}
|
||||
|
||||
async asyncRender() {
|
||||
if (!this.initialized || !this.item?.isFileAttachment()) return;
|
||||
if (this._isAlreadyRendered()) return;
|
||||
|
||||
let annotations = this.item.getAnnotations();
|
||||
this._section.setCount(annotations.length);
|
||||
await Zotero.PDFWorker.renderAttachmentAnnotations(this.item.id);
|
||||
|
||||
this._body.replaceChildren();
|
||||
|
||||
if (!this._section.open) {
|
||||
return;
|
||||
}
|
||||
|
||||
let count = annotations.length;
|
||||
if (count === 0) {
|
||||
this.hidden = true;
|
||||
if (!this._section.open || this._annotationItems.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hidden = false;
|
||||
for (let annotation of annotations) {
|
||||
let row = document.createXULElement('annotation-row');
|
||||
row.annotation = annotation;
|
||||
this._body.append(row);
|
||||
for (let annotation of this._annotationItems) {
|
||||
this.addRow(annotation);
|
||||
}
|
||||
}
|
||||
|
||||
addRow(annotation) {
|
||||
let row = document.createXULElement('annotation-row');
|
||||
row.annotation = annotation;
|
||||
|
||||
let index = this._annotationItems.findIndex(item => item.id == annotation.id);
|
||||
if (index < 0 || index >= this._body.children.length) {
|
||||
this._body.append(row);
|
||||
}
|
||||
else {
|
||||
this._body.insertBefore(row, this._body.children[index]);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
updateCount() {
|
||||
let count = this._annotationItems.length;
|
||||
this._section.setCount(count);
|
||||
if (count === 0) {
|
||||
this.hidden = true;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
_updateHidden() {
|
||||
this.hidden = !this.item?.isFileAttachment() || this.tabType == "reader";
|
||||
this.hidden = !this.item || this.tabType == "reader";
|
||||
}
|
||||
}
|
||||
customElements.define("attachment-annotations-box", AttachmentAnnotationsBox);
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
this._section = null;
|
||||
this._preview = null;
|
||||
|
||||
this._isRendering = false;
|
||||
this._asyncRendering = false;
|
||||
|
||||
this._isEditingFilename = false;
|
||||
}
|
||||
|
@ -304,12 +304,12 @@
|
|||
|
||||
async asyncRender() {
|
||||
if (!this.item) return;
|
||||
if (this._isRendering) return;
|
||||
if (this._asyncRendering) return;
|
||||
if (!this._section.open) return;
|
||||
if (this._isAlreadyRendered("async")) return;
|
||||
|
||||
Zotero.debug('Refreshing attachment box');
|
||||
this._isRendering = true;
|
||||
this._asyncRendering = true;
|
||||
// Cancel editing filename when refreshing
|
||||
this._isEditingFilename = false;
|
||||
|
||||
|
@ -457,7 +457,7 @@
|
|||
else {
|
||||
selectButton.hidden = true;
|
||||
}
|
||||
this._isRendering = false;
|
||||
this._asyncRendering = false;
|
||||
}
|
||||
|
||||
onViewClick(event) {
|
||||
|
|
|
@ -165,6 +165,7 @@
|
|||
this.addEventListener("focusin", this._handleFocusIn);
|
||||
this.addEventListener("keypress", this._handleKeypress);
|
||||
this.setAttribute("data-preview-type", "unknown");
|
||||
this._notifierID = Zotero.Notifier.registerObserver(this, ["item"], "attachmentPreview");
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@ -177,6 +178,38 @@
|
|||
this.removeEventListener("click", this._handleFocusIn);
|
||||
this.removeEventListener("focusin", this._handleFocusIn);
|
||||
this.removeEventListener("keypress", this._handleKeypress);
|
||||
Zotero.Notifier.unregisterObserver(this._notifierID);
|
||||
}
|
||||
|
||||
notify(event, type, ids, extraData) {
|
||||
if (!this.item) return;
|
||||
if (this.isReaderType && this._reader) {
|
||||
// Following chrome/content/zotero/xpcom/reader.js
|
||||
if (event === "delete") {
|
||||
let disappearedIDs = this._reader.annotationItemIDs.filter(x => ids.includes(x));
|
||||
if (disappearedIDs.length) {
|
||||
let keys = disappearedIDs.map(id => extraData[id].key);
|
||||
this._reader.unsetAnnotations(keys);
|
||||
}
|
||||
}
|
||||
else if (["add", "modify"].includes(event)) {
|
||||
let annotationItems = this.item.getAnnotations();
|
||||
this._reader.annotationItemIDs = annotationItems.map(x => x.id);
|
||||
let affectedAnnotations = annotationItems.filter(({ id }) => (
|
||||
ids.includes(id)
|
||||
&& !(extraData && extraData[id] && extraData[id].instanceID === this._reader._instanceID)
|
||||
));
|
||||
if (affectedAnnotations.length) {
|
||||
this._reader.setAnnotations(affectedAnnotations);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.isMediaType) {
|
||||
if (["refresh", "modify"].includes(event) && ids.includes(this.item.id)) {
|
||||
this.discard().then(() => this.render());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async render() {
|
||||
|
@ -184,6 +217,14 @@
|
|||
if (!this.initialized && itemID === this._renderingItemID) {
|
||||
return;
|
||||
}
|
||||
// For tests
|
||||
let resolve;
|
||||
if (Zotero.test) {
|
||||
this._renderPromise = new Promise(r => resolve = r);
|
||||
// Expose `resolve` for `this.discard`
|
||||
this._renderPromise.resolve = resolve;
|
||||
}
|
||||
|
||||
this._renderingItemID = itemID;
|
||||
let success = false;
|
||||
if (this.isValidType && await this._item.fileExists()) {
|
||||
|
@ -203,6 +244,9 @@
|
|||
if (this._renderingItemID === itemID) {
|
||||
this._renderingItemID = null;
|
||||
}
|
||||
if (Zotero.test) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
async discard(force = false) {
|
||||
|
@ -213,7 +257,7 @@
|
|||
if (this._isDiscarding) {
|
||||
return;
|
||||
}
|
||||
if (!force && this.isVisible) {
|
||||
if (!force && (this.isVisible || !this._reader)) {
|
||||
return;
|
||||
}
|
||||
this._isDiscarding = true;
|
||||
|
@ -237,6 +281,7 @@
|
|||
this._id("preview")?.after(this.nextPreview);
|
||||
this.setPreviewStatus("loading");
|
||||
this._isDiscarding = false;
|
||||
this._renderPromise?.resolve();
|
||||
}
|
||||
|
||||
async openAttachment(event) {
|
||||
|
|
|
@ -70,7 +70,9 @@
|
|||
|
||||
set usePreview(val) {
|
||||
this.toggleAttribute('data-use-preview', val);
|
||||
this.updatePreview();
|
||||
if (this.item) {
|
||||
this.updatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
|
@ -83,11 +85,17 @@
|
|||
this._addPopup.id = '';
|
||||
this.querySelector('popupset').append(this._addPopup);
|
||||
|
||||
this.usePreview = Zotero.Prefs.get('showAttachmentPreview');
|
||||
this._preview = this.querySelector('attachment-preview');
|
||||
|
||||
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'attachmentsBox');
|
||||
|
||||
this._section._contextMenu.addEventListener('popupshowing', this._handleContextMenu, { once: true });
|
||||
|
||||
// For tests
|
||||
this._asyncRendering = false;
|
||||
// Indicate if the preview should update, can be none | initial | final
|
||||
this._renderStage = "none";
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@ -96,16 +104,13 @@
|
|||
}
|
||||
|
||||
notify(action, type, ids) {
|
||||
if (ids.includes(this._item?.id)) {
|
||||
this._resetRenderedFlags();
|
||||
}
|
||||
if (!this._item?.isRegularItem()) return;
|
||||
|
||||
this._updateAttachmentIDs().then(() => {
|
||||
this.updatePreview();
|
||||
|
||||
let attachments = Zotero.Items.get((this._attachmentIDs).filter(id => ids.includes(id)));
|
||||
if (attachments.length === 0) {
|
||||
if (attachments.length === 0 && action !== "delete") {
|
||||
return;
|
||||
}
|
||||
if (action == 'add') {
|
||||
|
@ -113,20 +118,20 @@
|
|||
this.addRow(attachment);
|
||||
}
|
||||
}
|
||||
else if (action == 'modify') {
|
||||
// When annotation added to attachment, action=modify
|
||||
// When annotation deleted from attachment, action=refresh
|
||||
else if (action == 'modify' || action == 'refresh') {
|
||||
for (let attachment of attachments) {
|
||||
let row = this.querySelector(`attachment-row[attachment-id="${attachment.id}"]`);
|
||||
let open = false;
|
||||
if (row) {
|
||||
open = row.open;
|
||||
row.remove();
|
||||
}
|
||||
this.addRow(attachment).open = open;
|
||||
this.addRow(attachment);
|
||||
}
|
||||
}
|
||||
else if (action == 'delete') {
|
||||
for (let attachment of attachments) {
|
||||
let row = this.querySelector(`attachment-row[attachment-id="${attachment.id}"]`);
|
||||
for (let id of ids) {
|
||||
let row = this.querySelector(`attachment-row[attachment-id="${id}"]`);
|
||||
if (row) {
|
||||
row.remove();
|
||||
}
|
||||
|
@ -137,11 +142,9 @@
|
|||
});
|
||||
}
|
||||
|
||||
addRow(attachment, open = false) {
|
||||
addRow(attachment) {
|
||||
let row = document.createXULElement('attachment-row');
|
||||
this._updateRowAttributes(row, attachment);
|
||||
// Set open state before adding to dom to prevent animation
|
||||
row.toggleAttribute("open", open);
|
||||
|
||||
let index = this._attachmentIDs.indexOf(attachment.id);
|
||||
if (index < 0 || index >= this._attachments.children.length) {
|
||||
|
@ -156,12 +159,15 @@
|
|||
render() {
|
||||
if (!this._item) return;
|
||||
if (this._isAlreadyRendered()) return;
|
||||
this._renderStage = "initial";
|
||||
this.updateCount();
|
||||
}
|
||||
|
||||
async asyncRender() {
|
||||
if (!this._item) return;
|
||||
if (this._isAlreadyRendered("async")) return;
|
||||
this._renderStage = "final";
|
||||
this._asyncRendering = true;
|
||||
|
||||
await this._updateAttachmentIDs();
|
||||
|
||||
|
@ -171,15 +177,29 @@
|
|||
for (let attachment of itemAttachments) {
|
||||
this.addRow(attachment);
|
||||
}
|
||||
this.usePreview = Zotero.Prefs.get('showAttachmentPreview');
|
||||
await this.updatePreview();
|
||||
this._asyncRendering = false;
|
||||
}
|
||||
|
||||
updateCount() {
|
||||
if (!this._item?.isRegularItem()) {
|
||||
return;
|
||||
}
|
||||
let count = this._item.numAttachments(this.inTrash);
|
||||
this._section.setCount(count);
|
||||
}
|
||||
|
||||
async updatePreview() {
|
||||
// Skip if asyncRender is not finished/executed, which means the box is invisible
|
||||
// The box will be rendered when it becomes visible
|
||||
if (this._renderStage !== "final") {
|
||||
return;
|
||||
}
|
||||
let attachment = await this._getPreviewAttachment();
|
||||
this.toggleAttribute('data-use-preview', !!attachment && Zotero.Prefs.get('showAttachmentPreview'));
|
||||
if (!attachment) {
|
||||
return;
|
||||
}
|
||||
if (!this.usePreview
|
||||
// Skip only when the section is manually collapsed (when there's attachment),
|
||||
// This is necessary to ensure the rendering of the first added attachment
|
||||
|
@ -187,11 +207,6 @@
|
|||
|| (this._attachmentIDs.length && !this._section.open)) {
|
||||
return;
|
||||
}
|
||||
let attachment = await this._getPreviewAttachment();
|
||||
if (!attachment) {
|
||||
this.toggleAttribute('data-use-preview', false);
|
||||
return;
|
||||
}
|
||||
this._preview.item = attachment;
|
||||
await this._preview.render();
|
||||
}
|
||||
|
@ -199,7 +214,7 @@
|
|||
async _getPreviewAttachment() {
|
||||
let attachment = await this._item.getBestAttachment();
|
||||
if (this.tabType === "reader"
|
||||
&& Zotero_Tabs._getTab(Zotero_Tabs.selectedID)?.tab?.data?.itemID == attachment.id) {
|
||||
&& Zotero_Tabs._getTab(this.tabID)?.tab?.data?.itemID == attachment.id) {
|
||||
// In the reader, only show the preview when viewing a secondary attachment
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -295,6 +295,7 @@
|
|||
this._itemPaneDeck.appendChild(itemDetails);
|
||||
|
||||
itemDetails.editable = editable;
|
||||
itemDetails.tabID = tabID;
|
||||
itemDetails.tabType = "reader";
|
||||
itemDetails.item = targetItem;
|
||||
// Manually cache parentID
|
||||
|
|
|
@ -100,6 +100,14 @@
|
|||
this.toggleAttribute('readonly', !editable);
|
||||
}
|
||||
|
||||
get tabID() {
|
||||
return this._tabID;
|
||||
}
|
||||
|
||||
set tabID(tabID) {
|
||||
this._tabID = tabID;
|
||||
}
|
||||
|
||||
get tabType() {
|
||||
return this._tabType;
|
||||
}
|
||||
|
@ -215,12 +223,18 @@
|
|||
let item = this.item;
|
||||
Zotero.debug('Viewing item');
|
||||
this._isRendering = true;
|
||||
// For tests
|
||||
let resolve;
|
||||
if (Zotero.test) {
|
||||
this._renderPromise = new Promise(r => resolve = r);
|
||||
}
|
||||
|
||||
this.renderCustomSections();
|
||||
|
||||
let panes = this.getPanes();
|
||||
for (let box of [this._header, ...panes]) {
|
||||
box.editable = this.editable;
|
||||
box.tabID = this.tabID;
|
||||
box.tabType = this.tabType;
|
||||
box.item = item;
|
||||
// Execute sync render immediately
|
||||
|
@ -260,6 +274,9 @@
|
|||
if (this.item.id == item.id) {
|
||||
this._isRendering = false;
|
||||
}
|
||||
if (Zotero.test) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
renderCustomSections() {
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
this.mode = "item";
|
||||
|
||||
this._itemDetails.editable = this.editable;
|
||||
this._itemDetails.tabID = "zotero-pane";
|
||||
this._itemDetails.tabType = "library";
|
||||
this._itemDetails.item = item;
|
||||
|
||||
|
|
|
@ -43,6 +43,14 @@ class ItemPaneSectionElementBase extends XULElementBase {
|
|||
this.toggleAttribute('readonly', !editable);
|
||||
}
|
||||
|
||||
get tabID() {
|
||||
return this._tabID;
|
||||
}
|
||||
|
||||
set tabID(tabID) {
|
||||
this._tabID = tabID;
|
||||
}
|
||||
|
||||
get tabType() {
|
||||
return this._tabType;
|
||||
}
|
||||
|
|
|
@ -384,6 +384,31 @@ async function delay(ms) {
|
|||
return Zotero.Promise.delay(ms);
|
||||
}
|
||||
|
||||
async function waitForFrame() {
|
||||
return waitNoLongerThan(new Promise((resolve) => {
|
||||
requestAnimationFrame(resolve);
|
||||
}), 30);
|
||||
}
|
||||
|
||||
async function waitForFrames(n) {
|
||||
for (let i = 0; i < n; i++) {
|
||||
await waitForFrame();
|
||||
}
|
||||
}
|
||||
|
||||
async function waitNoLongerThan(promise, ms = 1000) {
|
||||
return Promise.race([
|
||||
promise,
|
||||
Zotero.Promise.delay(ms)
|
||||
]);
|
||||
}
|
||||
|
||||
async function waitForScrollToPane(itemDetails, paneID) {
|
||||
await itemDetails._renderPromise;
|
||||
itemDetails.scrollToPane(paneID, "instant");
|
||||
// Wait for some frames or up to 150ms to ensure the pane is visible
|
||||
await waitForFrames(5);
|
||||
}
|
||||
|
||||
function clickOnItemsRow(win, itemsView, row) {
|
||||
itemsView._treebox.scrollToRow(row);
|
||||
|
@ -1115,3 +1140,4 @@ async function startHTTPServer(port = null) {
|
|||
var baseURL = `http://localhost:${port}/`
|
||||
return { httpd, port, baseURL };
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,38 @@
|
|||
describe("Item pane", function () {
|
||||
var win, doc, itemsView;
|
||||
var win, doc, ZoteroPane, Zotero_Tabs, ZoteroContextPane, itemsView;
|
||||
|
||||
async function waitForPreviewBoxRender(box) {
|
||||
let success = await waitForCallback(
|
||||
() => box._asyncRenderItemID && !box._asyncRendering);
|
||||
if (!success) {
|
||||
throw new Error("Wait for box render time out");
|
||||
}
|
||||
await box._preview._renderPromise;
|
||||
return success;
|
||||
}
|
||||
|
||||
async function waitForPreviewBoxReader(box, itemID) {
|
||||
await waitForPreviewBoxRender(box);
|
||||
let success = await waitForCallback(
|
||||
() => box._preview._reader?.itemID == itemID, 100, 3000);
|
||||
if (!success) {
|
||||
throw new Error("Wait for box preview reader time out");
|
||||
}
|
||||
await box._preview._reader._initPromise;
|
||||
return success;
|
||||
}
|
||||
|
||||
function isPreviewDisplayed(box) {
|
||||
return !!(box._preview.hasPreview
|
||||
&& win.getComputedStyle(box._preview).display !== "none");
|
||||
}
|
||||
|
||||
before(function* () {
|
||||
win = yield loadZoteroPane();
|
||||
doc = win.document;
|
||||
ZoteroPane = win.ZoteroPane;
|
||||
Zotero_Tabs = win.Zotero_Tabs;
|
||||
ZoteroContextPane = win.ZoteroContextPane;
|
||||
itemsView = win.ZoteroPane.itemsView;
|
||||
});
|
||||
after(function () {
|
||||
|
@ -168,7 +197,7 @@ describe("Item pane", function () {
|
|||
assert.equal(label.value, 'Test');
|
||||
|
||||
yield Zotero.Items.erase(id);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
it("should swap creator names", async function () {
|
||||
|
@ -344,7 +373,479 @@ describe("Item pane", function () {
|
|||
'1'
|
||||
);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe("Attachments pane", function () {
|
||||
let paneID = "attachments";
|
||||
|
||||
beforeEach(function () {
|
||||
Zotero.Prefs.set("panes.attachments.open", true);
|
||||
Zotero.Prefs.set("showAttachmentPreview", true);
|
||||
Zotero_Tabs.select("zotero-pane");
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Zotero_Tabs.select("zotero-pane");
|
||||
});
|
||||
|
||||
it("should show attachments pane in library for regular item", async function () {
|
||||
// Regular item: show
|
||||
let attachmentsBox = ZoteroPane.itemPane._itemDetails.getPane(paneID);
|
||||
let item = new Zotero.Item('book');
|
||||
await item.saveTx();
|
||||
await ZoteroPane.selectItem(item.id);
|
||||
assert.isFalse(attachmentsBox.hidden);
|
||||
|
||||
// Child attachment: hide
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
let attachment = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
await ZoteroPane.selectItem(attachment.id);
|
||||
assert.isTrue(attachmentsBox.hidden);
|
||||
|
||||
// Standalone attachment: hide
|
||||
let attachment1 = await importFileAttachment('test.pdf');
|
||||
await ZoteroPane.selectItem(attachment1.id);
|
||||
assert.isTrue(attachmentsBox.hidden);
|
||||
});
|
||||
|
||||
it("should not show attachments pane preview in reader best-matched attachment item", async function () {
|
||||
let item = new Zotero.Item('book');
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
await item.saveTx();
|
||||
let attachment = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
await ZoteroPane.viewItems([attachment]);
|
||||
let tabID = Zotero_Tabs.selectedID;
|
||||
let itemDetails = ZoteroContextPane.context._getItemContext(tabID);
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
assert.isFalse(attachmentsBox.hidden);
|
||||
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
|
||||
assert.isFalse(isPreviewDisplayed(attachmentsBox));
|
||||
});
|
||||
|
||||
it("should not show attachments pane in reader standalone attachment item", async function () {
|
||||
let attachment = await importFileAttachment('test.pdf');
|
||||
await ZoteroPane.viewItems([attachment]);
|
||||
let tabID = Zotero_Tabs.selectedID;
|
||||
let itemDetails = ZoteroContextPane.context._getItemContext(tabID);
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
assert.isTrue(attachmentsBox.hidden);
|
||||
});
|
||||
|
||||
it("should show attachments pane preview in reader non-best-matched attachment item", async function () {
|
||||
let item = new Zotero.Item('book');
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
await item.saveTx();
|
||||
await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
|
||||
let bestAttachments = await item.getBestAttachments();
|
||||
await ZoteroPane.viewItems([bestAttachments[1]]);
|
||||
// Ensure context pane is open
|
||||
ZoteroContextPane.splitter.setAttribute("state", "open");
|
||||
await waitForFrame();
|
||||
let tabID = Zotero_Tabs.selectedID;
|
||||
let itemDetails = ZoteroContextPane.context._getItemContext(tabID);
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
assert.isFalse(attachmentsBox.hidden);
|
||||
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
await waitForPreviewBoxRender(attachmentsBox);
|
||||
assert.isTrue(isPreviewDisplayed(attachmentsBox));
|
||||
});
|
||||
|
||||
it("should not render attachments pane preview when show preview is disabled", async function () {
|
||||
Zotero.Prefs.set("showAttachmentPreview", false);
|
||||
|
||||
let itemDetails = ZoteroPane.itemPane._itemDetails;
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
let item = new Zotero.Item('book');
|
||||
await item.saveTx();
|
||||
await ZoteroPane.selectItem(item.id);
|
||||
assert.isFalse(attachmentsBox.hidden);
|
||||
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
|
||||
assert.isFalse(isPreviewDisplayed(attachmentsBox));
|
||||
});
|
||||
|
||||
it("should only render after attachments pane becomes visible", async function () {
|
||||
// Resize to very small height to ensure the attachment box is not in view
|
||||
let height = doc.documentElement.clientHeight;
|
||||
win.resizeTo(null, 100);
|
||||
|
||||
let itemDetails = ZoteroPane.itemPane._itemDetails;
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
let preview = attachmentsBox._preview;
|
||||
// Force discard previous preview
|
||||
await preview.discard(true);
|
||||
|
||||
let item = new Zotero.Item('book');
|
||||
await item.saveTx();
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
|
||||
await ZoteroPane.selectItem(item.id);
|
||||
assert.isFalse(itemDetails.isPaneVisible(paneID));
|
||||
// Do not use _isAlreadyRendered, since that changes the render flag state
|
||||
assert.equal(attachmentsBox._syncRenderItemID, item.id);
|
||||
assert.notEqual(attachmentsBox._asyncRenderItemID, item.id);
|
||||
assert.isFalse(isPreviewDisplayed(attachmentsBox));
|
||||
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
await waitForPreviewBoxRender(attachmentsBox);
|
||||
assert.isTrue(itemDetails.isPaneVisible(paneID));
|
||||
assert.equal(attachmentsBox._syncRenderItemID, item.id);
|
||||
assert.equal(attachmentsBox._asyncRenderItemID, item.id);
|
||||
|
||||
assert.isTrue(isPreviewDisplayed(attachmentsBox));
|
||||
assert.isTrue(preview.hasPreview);
|
||||
win.resizeTo(null, height);
|
||||
});
|
||||
|
||||
it("should update attachments pane when attachments changed", async function () {
|
||||
// https://forums.zotero.org/discussion/113632/zotero-7-beta-pdf-attachment-preview-and-annotations-not-refreshed-after-adding-annotations
|
||||
|
||||
let itemDetails = ZoteroPane.itemPane._itemDetails;
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
let preview = attachmentsBox._preview;
|
||||
// Force discard previous preview
|
||||
await preview.discard(true);
|
||||
|
||||
// Pin the pane to ensure it's rendered
|
||||
itemDetails.pinnedPane = paneID;
|
||||
|
||||
let item = new Zotero.Item('book');
|
||||
await item.saveTx();
|
||||
|
||||
await ZoteroPane.selectItem(item.id);
|
||||
assert.isTrue(await waitForPreviewBoxRender(attachmentsBox));
|
||||
// No preview
|
||||
assert.isFalse(isPreviewDisplayed(attachmentsBox));
|
||||
// No row
|
||||
assert.equal(attachmentsBox.querySelectorAll("attachment-row").length, 0);
|
||||
|
||||
// Add an attachment
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.png');
|
||||
let _attachment1 = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
await ZoteroPane.selectItem(item.id);
|
||||
await itemDetails._renderPromise;
|
||||
await waitForPreviewBoxRender(attachmentsBox);
|
||||
// Image preview for item with image attachment
|
||||
assert.isTrue(isPreviewDisplayed(attachmentsBox));
|
||||
assert.equal(preview.previewType, "image");
|
||||
// 1 row
|
||||
assert.equal(attachmentsBox.querySelectorAll("attachment-row").length, 1);
|
||||
|
||||
// Add an PDF attachment, which will be best match and update the preview
|
||||
file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
let attachment2 = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
await waitForPreviewBoxReader(attachmentsBox, attachment2.id);
|
||||
await Zotero.Promise.delay(100);
|
||||
// PDF preview
|
||||
assert.isTrue(isPreviewDisplayed(attachmentsBox));
|
||||
assert.equal(preview.previewType, "pdf");
|
||||
// 2 rows
|
||||
assert.equal(attachmentsBox.querySelectorAll("attachment-row").length, 2);
|
||||
|
||||
// Created annotations should be update in preview and attachment row
|
||||
let annotation = await createAnnotation('highlight', attachment2);
|
||||
await Zotero.Promise.delay(100);
|
||||
// Annotation updated in preview reader
|
||||
let readerAnnotation
|
||||
= preview._reader._internalReader._annotationManager._annotations.find(
|
||||
a => a.libraryID === annotation.libraryID && a.id === annotation.key
|
||||
);
|
||||
assert.exists(readerAnnotation);
|
||||
|
||||
assert.equal(attachmentsBox.querySelectorAll("attachment-row").length, 2);
|
||||
let attachmentRow = attachmentsBox.querySelector(`attachment-row[attachment-id="${attachment2.id}"]`);
|
||||
assert.isFalse(attachmentRow._annotationButton.hidden);
|
||||
// 1 annotation
|
||||
assert.equal(attachmentRow._annotationButton.querySelector('.label').textContent, "1");
|
||||
|
||||
// Deleted annotations should be removed from preview and attachment row
|
||||
await annotation.eraseTx();
|
||||
await Zotero.Promise.delay(100);
|
||||
// Annotation removed from preview reader
|
||||
readerAnnotation
|
||||
= preview._reader._internalReader._annotationManager._annotations.find(
|
||||
a => a.libraryID === annotation.libraryID && a.id === annotation.key
|
||||
);
|
||||
assert.notExists(readerAnnotation);
|
||||
// Row might be recreated
|
||||
attachmentRow = attachmentsBox.querySelector(`attachment-row[attachment-id="${attachment2.id}"]`);
|
||||
assert.isTrue(attachmentRow._annotationButton.hidden);
|
||||
// 0 annotation
|
||||
assert.equal(attachmentRow._annotationButton.querySelector('.label').textContent, "0");
|
||||
|
||||
// Delete attachment
|
||||
await attachment2.eraseTx();
|
||||
await Zotero.Promise.delay(100);
|
||||
// Image preview for item with image attachment
|
||||
assert.isTrue(isPreviewDisplayed(attachmentsBox));
|
||||
assert.equal(preview.previewType, "image");
|
||||
// 1 row
|
||||
assert.equal(attachmentsBox.querySelectorAll("attachment-row").length, 1);
|
||||
// The corresponding row should be removed
|
||||
attachmentRow = attachmentsBox.querySelector(`attachment-row[attachment-id="${attachment2.id}"]`);
|
||||
assert.notExists(attachmentRow);
|
||||
|
||||
// Unpin
|
||||
itemDetails.pinnedPane = "";
|
||||
});
|
||||
|
||||
it("should keep attachments pane preview status after switching tab", async function () {
|
||||
// https://forums.zotero.org/discussion/113658/zotero-7-beta-preview-appearing-in-the-item-pane-of-the-pdf-tab
|
||||
|
||||
let item = new Zotero.Item('book');
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
await item.saveTx();
|
||||
let attachment = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
|
||||
// Open reader
|
||||
await ZoteroPane.viewItems([attachment]);
|
||||
let tabID = Zotero_Tabs.selectedID;
|
||||
await Zotero.Reader.getByTabID(tabID)._waitForReader();
|
||||
// Ensure context pane is open
|
||||
ZoteroContextPane.splitter.setAttribute("state", "open");
|
||||
await waitForFrame();
|
||||
|
||||
let itemDetails = ZoteroContextPane.context._getItemContext(tabID);
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
assert.isFalse(attachmentsBox.hidden);
|
||||
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
assert.isFalse(isPreviewDisplayed(attachmentsBox));
|
||||
|
||||
// Select library tab
|
||||
Zotero_Tabs.select("zotero-pane");
|
||||
let libraryItemDetails = ZoteroPane.itemPane._itemDetails;
|
||||
let libraryAttachmentsBox = libraryItemDetails.getPane(paneID);
|
||||
await ZoteroPane.selectItem(item.id);
|
||||
await waitForScrollToPane(libraryItemDetails, paneID);
|
||||
// Collapse section
|
||||
libraryAttachmentsBox.querySelector('collapsible-section > .head').click();
|
||||
await Zotero.Promise.delay(50);
|
||||
// Open section
|
||||
libraryAttachmentsBox.querySelector('collapsible-section > .head').click();
|
||||
await Zotero.Promise.delay(50);
|
||||
|
||||
// Select reader tab
|
||||
Zotero_Tabs.select(tabID);
|
||||
|
||||
// Make sure the preview status is not changed in reader
|
||||
assert.isFalse(isPreviewDisplayed(attachmentsBox));
|
||||
});
|
||||
|
||||
/**
|
||||
* This test is essential to ensure the proper functioning of the sync/async rendering,
|
||||
* scrolling handler, and pinning mechanism of ItemDetails.
|
||||
* AttachmentsBox serves as a good example since it involves both sync and async rendering.
|
||||
* If this test fails, it is not recommended to add timeouts as a quick fix.
|
||||
*/
|
||||
it("should keep attachments pane status after changing selection", async function () {
|
||||
let itemDetails = ZoteroPane.itemPane._itemDetails;
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
let preview = attachmentsBox._preview;
|
||||
|
||||
// Pin the pane to avoid always scrolling to the section
|
||||
itemDetails.pinnedPane = paneID;
|
||||
|
||||
// item with attachment (1 annotation)
|
||||
let item1 = new Zotero.Item('book');
|
||||
await item1.saveTx();
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
let attachment1 = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item1.id
|
||||
});
|
||||
let annotation = await createAnnotation('highlight', attachment1);
|
||||
|
||||
await itemDetails._renderPromise;
|
||||
await waitForPreviewBoxReader(attachmentsBox, attachment1.id);
|
||||
|
||||
assert.isFalse(attachmentsBox.hidden);
|
||||
let readerAnnotation
|
||||
= preview._reader._internalReader._annotationManager._annotations.find(
|
||||
a => a.libraryID === annotation.libraryID && a.id === annotation.key
|
||||
);
|
||||
assert.exists(readerAnnotation);
|
||||
assert.equal(attachmentsBox.querySelectorAll("attachment-row").length, 1);
|
||||
let attachmentRow = attachmentsBox.querySelector(`attachment-row[attachment-id="${attachment1.id}"]`);
|
||||
assert.isFalse(attachmentRow._annotationButton.hidden);
|
||||
// 1 annotation
|
||||
assert.equal(attachmentRow._annotationButton.querySelector('.label').textContent, "1");
|
||||
|
||||
// item with attachment (no annotation)
|
||||
let item2 = new Zotero.Item('book');
|
||||
await item2.saveTx();
|
||||
file = getTestDataDirectory();
|
||||
file.append('wonderland_short.pdf');
|
||||
let attachment2 = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item2.id
|
||||
});
|
||||
|
||||
// Select item with attachment (no annotation)
|
||||
await itemDetails._renderPromise;
|
||||
await waitForPreviewBoxReader(attachmentsBox, attachment2.id);
|
||||
|
||||
assert.isFalse(attachmentsBox.hidden);
|
||||
readerAnnotation
|
||||
= preview._reader._internalReader._annotationManager._annotations.find(
|
||||
a => a.libraryID === annotation.libraryID && a.id === annotation.key
|
||||
);
|
||||
assert.notExists(readerAnnotation);
|
||||
assert.equal(attachmentsBox.querySelectorAll("attachment-row").length, 1);
|
||||
attachmentRow = attachmentsBox.querySelector(`attachment-row[attachment-id="${attachment2.id}"]`);
|
||||
assert.isTrue(attachmentRow._annotationButton.hidden);
|
||||
// 0 annotation
|
||||
assert.equal(attachmentRow._annotationButton.querySelector('.label').textContent, "0");
|
||||
|
||||
let item3 = new Zotero.Item('book');
|
||||
await item3.saveTx();
|
||||
|
||||
// Select item without attachment
|
||||
await itemDetails._renderPromise;
|
||||
|
||||
assert.isFalse(attachmentsBox.hidden);
|
||||
assert.equal(attachmentsBox.querySelectorAll("attachment-row").length, 0);
|
||||
|
||||
// Again, select item with attachment (1 annotation)
|
||||
await ZoteroPane.selectItem(item1.id);
|
||||
await itemDetails._renderPromise;
|
||||
await waitForPreviewBoxReader(attachmentsBox, attachment1.id);
|
||||
|
||||
assert.isFalse(attachmentsBox.hidden);
|
||||
readerAnnotation
|
||||
= preview._reader._internalReader._annotationManager._annotations.find(
|
||||
a => a.libraryID === annotation.libraryID && a.id === annotation.key
|
||||
);
|
||||
assert.exists(readerAnnotation);
|
||||
assert.equal(attachmentsBox.querySelectorAll("attachment-row").length, 1);
|
||||
attachmentRow = attachmentsBox.querySelector(`attachment-row[attachment-id="${attachment1.id}"]`);
|
||||
assert.isFalse(attachmentRow._annotationButton.hidden);
|
||||
// 1 annotation
|
||||
assert.equal(attachmentRow._annotationButton.querySelector('.label').textContent, "1");
|
||||
|
||||
// Unpin
|
||||
itemDetails.pinnedPane = "";
|
||||
});
|
||||
|
||||
it("should open attachment on clicking attachment row", async function () {
|
||||
let itemDetails = ZoteroPane.itemPane._itemDetails;
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
|
||||
let item = new Zotero.Item('book');
|
||||
await item.saveTx();
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
let attachment = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
|
||||
await ZoteroPane.selectItem(item.id);
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
await waitForPreviewBoxRender(attachmentsBox);
|
||||
|
||||
let attachmentRow = attachmentsBox.querySelector(`attachment-row[attachment-id="${attachment.id}"]`);
|
||||
attachmentRow._attachmentButton.click();
|
||||
await Zotero.Promise.delay(100);
|
||||
let reader = await Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
|
||||
// Should open attachment
|
||||
assert.equal(reader.itemID, attachment.id);
|
||||
});
|
||||
|
||||
it("should select attachment on clicking annotation button of attachment row", async function () {
|
||||
let itemDetails = ZoteroPane.itemPane._itemDetails;
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
|
||||
let item = new Zotero.Item('book');
|
||||
await item.saveTx();
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
let attachment = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
let _annotation = await createAnnotation('highlight', attachment);
|
||||
|
||||
await ZoteroPane.selectItem(item.id);
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
await waitForPreviewBoxRender(attachmentsBox);
|
||||
|
||||
let attachmentRow = attachmentsBox.querySelector(`attachment-row[attachment-id="${attachment.id}"]`);
|
||||
attachmentRow._annotationButton.click();
|
||||
await Zotero.Promise.delay(100);
|
||||
// Should select attachment
|
||||
assert.equal(ZoteroPane.getSelectedItems(true)[0], attachment.id);
|
||||
});
|
||||
|
||||
it("should open attachment on double-clicking attachments pane preview", async function () {
|
||||
let itemDetails = ZoteroPane.itemPane._itemDetails;
|
||||
let attachmentsBox = itemDetails.getPane(paneID);
|
||||
let preview = attachmentsBox._preview;
|
||||
|
||||
let item = new Zotero.Item('book');
|
||||
await item.saveTx();
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
let attachment = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
|
||||
await ZoteroPane.selectItem(item.id);
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
await waitForPreviewBoxRender(attachmentsBox);
|
||||
|
||||
let event = new MouseEvent('dblclick', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
view: window
|
||||
});
|
||||
preview.dispatchEvent(event);
|
||||
await Zotero.Promise.delay(100);
|
||||
let reader = await Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
|
||||
// Should open attachment
|
||||
assert.equal(reader.itemID, attachment.id);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("Notes pane", function () {
|
||||
|
@ -461,6 +962,18 @@ describe("Item pane", function () {
|
|||
|
||||
|
||||
describe("Attachment pane", function () {
|
||||
let paneID = "attachment-info";
|
||||
|
||||
beforeEach(function () {
|
||||
Zotero.Prefs.set("panes.attachment-info.open", true);
|
||||
Zotero.Prefs.set("showAttachmentPreview", true);
|
||||
Zotero_Tabs.select("zotero-pane");
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Zotero_Tabs.select("zotero-pane");
|
||||
});
|
||||
|
||||
it("should refresh on file rename", async function () {
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.png');
|
||||
|
@ -499,6 +1012,95 @@ describe("Item pane", function () {
|
|||
await promise;
|
||||
assert.equal(label.value, newTitle);
|
||||
});
|
||||
|
||||
it("should show attachment pane in library for attachment item", async function () {
|
||||
// Regular item: hide
|
||||
let itemDetails = ZoteroPane.itemPane._itemDetails;
|
||||
let box = itemDetails.getPane(paneID);
|
||||
let item = new Zotero.Item('book');
|
||||
await item.saveTx();
|
||||
await ZoteroPane.selectItem(item.id);
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
assert.isTrue(box.hidden);
|
||||
|
||||
// Child attachment: show
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
let attachment = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
await ZoteroPane.selectItem(attachment.id);
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
await waitForPreviewBoxReader(box, attachment.id);
|
||||
assert.isFalse(box.hidden);
|
||||
await Zotero.Promise.delay(100);
|
||||
assert.isTrue(isPreviewDisplayed(box));
|
||||
|
||||
// Standalone attachment: show
|
||||
let attachment1 = await importFileAttachment('test.pdf');
|
||||
await ZoteroPane.selectItem(attachment1.id);
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
await waitForPreviewBoxReader(box, attachment1.id);
|
||||
assert.isFalse(box.hidden);
|
||||
await Zotero.Promise.delay(100);
|
||||
assert.isTrue(isPreviewDisplayed(box));
|
||||
});
|
||||
|
||||
it("should show attachment pane without preview in reader for standalone attachment item", async function () {
|
||||
// Attachment item with parent item: hide
|
||||
let item = new Zotero.Item('book');
|
||||
let file = getTestDataDirectory();
|
||||
file.append('test.pdf');
|
||||
await item.saveTx();
|
||||
let attachment = await Zotero.Attachments.importFromFile({
|
||||
file,
|
||||
parentItemID: item.id
|
||||
});
|
||||
await ZoteroPane.viewItems([attachment]);
|
||||
let tabID = Zotero_Tabs.selectedID;
|
||||
let itemDetails = ZoteroContextPane.context._getItemContext(tabID);
|
||||
let box = itemDetails.getPane(paneID);
|
||||
assert.isTrue(box.hidden);
|
||||
|
||||
// Standalone attachment item: show
|
||||
attachment = await importFileAttachment('test.pdf');
|
||||
await ZoteroPane.viewItems([attachment]);
|
||||
tabID = Zotero_Tabs.selectedID;
|
||||
itemDetails = ZoteroContextPane.context._getItemContext(tabID);
|
||||
box = itemDetails.getPane(paneID);
|
||||
assert.isFalse(box.hidden);
|
||||
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
// No preview
|
||||
assert.isFalse(isPreviewDisplayed(box));
|
||||
});
|
||||
|
||||
it("should only show attachment note container when exists", async function () {
|
||||
let itemDetails = ZoteroPane.itemPane._itemDetails;
|
||||
let box = itemDetails.getPane(paneID);
|
||||
let noteContainer = box._id("note-container");
|
||||
let noteEditor = box._id('attachment-note-editor');
|
||||
|
||||
// Hide note container by default
|
||||
let attachment = await importFileAttachment('test.pdf');
|
||||
await ZoteroPane.selectItem(attachment.id);
|
||||
await itemDetails._renderPromise;
|
||||
await waitForScrollToPane(itemDetails, paneID);
|
||||
await waitForPreviewBoxRender(box);
|
||||
assert.isTrue(noteContainer.hidden);
|
||||
|
||||
// Add attachment note
|
||||
let itemModifyPromise = waitForItemEvent("modify");
|
||||
attachment.setNote("<h1>TEST</h1>");
|
||||
await attachment.saveTx();
|
||||
await itemModifyPromise;
|
||||
await waitForPreviewBoxRender(box);
|
||||
// Should show note container
|
||||
assert.isFalse(noteContainer.hidden);
|
||||
// Should be readonly
|
||||
assert.equal(noteEditor.mode, "view");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
@ -602,4 +1204,4 @@ describe("Item pane", function () {
|
|||
);
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue