diff --git a/test/tests/fileInterfaceTest.js b/test/tests/fileInterfaceTest.js
index a571930791..e18c2a716e 100644
--- a/test/tests/fileInterfaceTest.js
+++ b/test/tests/fileInterfaceTest.js
@@ -234,6 +234,15 @@ describe("Zotero_File_Interface", function () {
assert.include(str, 'A');
assert.include(str, 'B');
});
+
+ it("should write text and html flavors directly to the clipboard", function () {
+ win.Zotero_File_Interface.writeToClipboard({
+ text: 'plain text',
+ html: '
html text
'
+ });
+ assert.equal(getDataForFlavor('text/plain'), 'plain text');
+ assert.equal(getDataForFlavor('text/html'), 'html text
');
+ });
});
describe('Citavi annotations', () => {
@@ -306,184 +315,4 @@ describe("Zotero_File_Interface", function () {
});
});
- describe("Copy citations to clipboard", function () {
- var noteItem, itemOne, itemTwo;
- var styleID = 'http://www.zotero.org/styles/chicago-notes-bibliography';
-
- before(async function () {
- await Zotero.Styles.init();
- await Zotero.Translators.init();
-
- itemOne = createUnsavedDataObject('item', {
- itemType: 'book',
- title: 'Item One',
- creators: [
- { firstName: 'firstOne', lastName: 'lastOne', creatorType: 'author' },
- { firstName: 'firstTwo', lastName: 'lastTwo', creatorType: 'author' }
- ]
- });
- await itemOne.saveTx();
-
- itemTwo = createUnsavedDataObject('item', {
- itemType: 'book',
- title: 'Item Two',
- creators: [
- { firstName: 'firstTwo', lastName: 'lastTwo', creatorType: 'author' },
- { firstName: 'firstThree', lastName: 'lastThree', creatorType: 'author' }
- ]
- });
- await itemTwo.saveTx();
-
- var uriOne = Zotero.URI.getItemURI(itemOne);
- var uriTwo = Zotero.URI.getItemURI(itemTwo);
- var citationOne = {
- citationItems: [{ uris: [uriOne] }],
- properties: {}
- };
- var citationTwo = {
- citationItems: [{ uris: [uriTwo] }],
- properties: {}
- };
- var noteHTML = ''
- + '
Some text
'
- + '
(lastOne and lastTwo)
'
- + '
(lastTwo and lastThree)
'
- + '
Done
'
- + '
';
-
- noteItem = createUnsavedDataObject('item', {
- itemType: 'note',
- note: noteHTML
- });
- await noteItem.saveTx();
-
- // Preload note translators
- let markdownTranslator = Zotero.Translators.get(Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN);
- markdownTranslator.cacheCode = true;
- await Zotero.Translators.getCodeForTranslator(markdownTranslator);
- let htmlTranslator = Zotero.Translators.get(Zotero.Translators.TRANSLATOR_ID_NOTE_HTML);
- htmlTranslator.cacheCode = true;
- await Zotero.Translators.getCodeForTranslator(htmlTranslator);
- });
-
- afterEach(function () {
- Zotero.Prefs.clear('export.quickCopy.bibliographySetting');
- Zotero.Prefs.clear('export.noteQuickCopy.setting');
- });
-
- after(async function () {
- await Zotero.Items.erase([noteItem.id, itemOne.id, itemTwo.id]);
- });
-
- function getClipboardText() {
- let transferable = Components.classes['@mozilla.org/widget/transferable;1']
- .createInstance(Components.interfaces.nsITransferable);
- let clipboardService = Components.classes['@mozilla.org/widget/clipboard;1']
- .getService(Components.interfaces.nsIClipboard);
- transferable.addDataFlavor('text/plain');
- clipboardService.getData(transferable, Components.interfaces.nsIClipboard.kGlobalClipboard);
- let str = {};
- transferable.getTransferData('text/plain', str);
- return str.value.QueryInterface(Components.interfaces.nsISupportsString).data;
- }
-
- it(`should copy CSL-formatted citations to clipboard`, async function () {
- Zotero.Prefs.set('export.quickCopy.bibliographySetting', JSON.stringify({
- mode: 'bibliography', id: styleID, contentType: '', locale: ''
- }));
- Zotero.Prefs.set('export.noteQuickCopy.setting', JSON.stringify({
- mode: 'export',
- id: Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT,
- markdownOptions: { includeAppLinks: false },
- htmlOptions: { includeAppLinks: false }
- }));
-
- await win.ZoteroPane.selectItem(noteItem.id);
- win.ZoteroPane.copySelectedItemsToClipboard(false, 'bibliography');
- await Zotero.Promise.delay();
-
- var text = getClipboardText();
- var expected = 'Some text\n\n'
- + '(firstOne lastOne and firstTwo lastTwo, Item One (n.d.).)\n\n'
- + '(firstTwo lastTwo and firstThree lastThree, Item Two (n.d.).)\n\n'
- + 'Done';
- assert.equal(text, expected);
- });
-
- it("should use embedded item data for deleted items when copying to clipboard", async function () {
- var deletedItem = createUnsavedDataObject('item', {
- itemType: 'book',
- title: 'Deleted Item',
- creators: [
- { firstName: 'firstDeleted', lastName: 'lastDeleted', creatorType: 'author' }
- ]
- });
- await deletedItem.saveTx();
-
- var uriOne = Zotero.URI.getItemURI(itemOne);
- var uriDeleted = Zotero.URI.getItemURI(deletedItem);
- var itemDataOne = Zotero.Utilities.Item.itemToCSLJSON(itemOne);
- var itemDataDeleted = Zotero.Utilities.Item.itemToCSLJSON(deletedItem);
-
- var citationOne = {
- citationItems: [{ uris: [uriOne] }],
- properties: {}
- };
- var citationDeleted = {
- citationItems: [{ uris: [uriDeleted] }],
- properties: {}
- };
-
- var storedCitationItems = [
- { uris: [uriOne], itemData: itemDataOne },
- { uris: [uriDeleted], itemData: itemDataDeleted }
- ];
- var deletedNoteHTML = ''
- + '
Before
'
- + '
(lastOne and lastTwo)
'
- + '
(lastDeleted)
'
- + '
After
'
- + '
';
-
- var deletedNoteItem = createUnsavedDataObject('item', {
- itemType: 'note',
- note: deletedNoteHTML
- });
- await deletedNoteItem.saveTx();
-
- await deletedItem.eraseTx();
-
- Zotero.Prefs.set('export.quickCopy.bibliographySetting', JSON.stringify({ mode: 'bibliography', id: styleID, contentType: '', locale: '' }));
-
- let items = Zotero.QuickCopy.reformatNoteCitations([deletedNoteItem]);
- let format = {
- mode: 'export',
- id: Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT,
- markdownOptions: { includeAppLinks: false },
- htmlOptions: { includeAppLinks: false }
- };
- let content = Zotero.QuickCopy.getContentFromItems(items, format);
- win.Zotero_File_Interface.writeToClipboard(content);
- await Zotero.Promise.delay();
-
- var text = getClipboardText();
- var expected = 'Before\n\n'
- + '(firstOne lastOne and firstTwo lastTwo, Item One (n.d.).)\n\n'
- + '(firstDeleted lastDeleted, Deleted Item (n.d.).)\n\n'
- + 'After';
- assert.equal(text, expected);
-
- await deletedNoteItem.eraseTx();
- });
- });
});
diff --git a/test/tests/quickCopyTest.js b/test/tests/quickCopyTest.js
index 1435b66da5..b411f80e69 100644
--- a/test/tests/quickCopyTest.js
+++ b/test/tests/quickCopyTest.js
@@ -1,5 +1,6 @@
describe("Zotero.QuickCopy", function () {
let domain = "test.org";
+ var win, zp, doc;
const DEFAULT_BIB = {
mode: 'bibliography',
@@ -16,7 +17,25 @@ describe("Zotero.QuickCopy", function () {
const BIBTEX_TRANSLATOR_ID = '9cb70025-a888-4a29-a210-93ec52da40d4'; // BibTeX
before(async function () {
+ win = await loadZoteroPane();
+ zp = win.ZoteroPane;
+ doc = win.document;
+ await Zotero.Styles.init();
+ await Zotero.Translators.init();
await Zotero.QuickCopy.loadSiteSettings();
+
+ let translatorMarkdown = Zotero.Translators.get(Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN);
+ translatorMarkdown.cacheCode = true;
+ await Zotero.Translators.getCodeForTranslator(translatorMarkdown);
+
+ let translatorHTML = Zotero.Translators.get(Zotero.Translators.TRANSLATOR_ID_NOTE_HTML);
+ translatorHTML.cacheCode = true;
+ await Zotero.Translators.getCodeForTranslator(translatorHTML);
+ });
+
+ after(function () {
+ win.Zotero_Tabs.closeAll();
+ win.close();
});
beforeEach(function () {
@@ -288,8 +307,6 @@ describe("Zotero.QuickCopy", function () {
describe("#getContentFromItems()", function () {
it("should generate BibTeX", async function () {
var item = await createDataObject('item');
-
- await Zotero.Translators.init();
var translatorID = '9cb70025-a888-4a29-a210-93ec52da40d4'; // BibTeX
var format = 'export=' + translatorID;
@@ -305,7 +322,138 @@ describe("Zotero.QuickCopy", function () {
assert.isTrue(content.text.trim().startsWith('@'));
});
});
-
+
+ describe("#reformatNoteCitations()", function () {
+ var noteItem, itemOne, itemTwo;
+ var styleID = 'http://www.zotero.org/styles/chicago-notes-bibliography';
+
+ before(async function () {
+ itemOne = createUnsavedDataObject('item', {
+ itemType: 'book',
+ title: 'Item One',
+ creators: [
+ { firstName: 'firstOne', lastName: 'lastOne', creatorType: 'author' },
+ { firstName: 'firstTwo', lastName: 'lastTwo', creatorType: 'author' }
+ ]
+ });
+ await itemOne.saveTx();
+
+ itemTwo = createUnsavedDataObject('item', {
+ itemType: 'book',
+ title: 'Item Two',
+ creators: [
+ { firstName: 'firstTwo', lastName: 'lastTwo', creatorType: 'author' },
+ { firstName: 'firstThree', lastName: 'lastThree', creatorType: 'author' }
+ ]
+ });
+ await itemTwo.saveTx();
+
+ var uriOne = Zotero.URI.getItemURI(itemOne);
+ var uriTwo = Zotero.URI.getItemURI(itemTwo);
+ var citationOne = { citationItems: [{ uris: [uriOne] }], properties: {} };
+ var citationTwo = { citationItems: [{ uris: [uriTwo] }], properties: {} };
+ var noteHTML = ''
+ + '
Some text
'
+ + '
(lastOne and lastTwo)
'
+ + '
(lastTwo and lastThree)
'
+ + '
Done
'
+ + '
';
+
+ noteItem = createUnsavedDataObject('item', { itemType: 'note', note: noteHTML });
+ await noteItem.saveTx();
+ });
+
+ after(async function () {
+ await Zotero.Items.erase([noteItem.id, itemOne.id, itemTwo.id]);
+ });
+
+ // getContentFromItems() calls reformatNoteCitations() internally when
+ // items are notes, so we can read the reformatted output off content.text.
+ function getNoteFormat() {
+ return {
+ mode: 'export',
+ id: Zotero.Translators.TRANSLATOR_ID_MARKDOWN_AND_RICH_TEXT,
+ markdownOptions: { includeAppLinks: false },
+ htmlOptions: { includeAppLinks: false }
+ };
+ }
+
+ it("should reformat note citations in the current CSL style", function () {
+ Zotero.Prefs.set('export.quickCopy.bibliographySetting', JSON.stringify({
+ mode: 'bibliography', id: styleID, contentType: '', locale: ''
+ }));
+
+ let content = Zotero.QuickCopy.getContentFromItems([noteItem], getNoteFormat());
+ var expected = 'Some text\n\n'
+ + '(firstOne lastOne and firstTwo lastTwo, Item One (n.d.).)\n\n'
+ + '(firstTwo lastTwo and firstThree lastThree, Item Two (n.d.).)\n\n'
+ + 'Done';
+ assert.equal(content.text, expected);
+ });
+
+ it("should use embedded item data for deleted items", async function () {
+ var deletedItem = createUnsavedDataObject('item', {
+ itemType: 'book',
+ title: 'Deleted Item',
+ creators: [
+ { firstName: 'firstDeleted', lastName: 'lastDeleted', creatorType: 'author' }
+ ]
+ });
+ await deletedItem.saveTx();
+
+ var uriOne = Zotero.URI.getItemURI(itemOne);
+ var uriDeleted = Zotero.URI.getItemURI(deletedItem);
+ var itemDataOne = Zotero.Utilities.Item.itemToCSLJSON(itemOne);
+ var itemDataDeleted = Zotero.Utilities.Item.itemToCSLJSON(deletedItem);
+
+ var citationOne = { citationItems: [{ uris: [uriOne] }], properties: {} };
+ var citationDeleted = { citationItems: [{ uris: [uriDeleted] }], properties: {} };
+
+ var storedCitationItems = [
+ { uris: [uriOne], itemData: itemDataOne },
+ { uris: [uriDeleted], itemData: itemDataDeleted }
+ ];
+ var deletedNoteHTML = ''
+ + '
Before
'
+ + '
(lastOne and lastTwo)
'
+ + '
(lastDeleted)
'
+ + '
After
'
+ + '
';
+
+ var deletedNoteItem = createUnsavedDataObject('item', {
+ itemType: 'note',
+ note: deletedNoteHTML
+ });
+ await deletedNoteItem.saveTx();
+ // Delete the item so its URI no longer resolves -- forces the
+ // fallback path through embedded data-citation-items JSON.
+ await deletedItem.eraseTx();
+
+ Zotero.Prefs.set('export.quickCopy.bibliographySetting', JSON.stringify({
+ mode: 'bibliography', id: styleID, contentType: '', locale: ''
+ }));
+
+ let content = Zotero.QuickCopy.getContentFromItems([deletedNoteItem], getNoteFormat());
+ var expected = 'Before\n\n'
+ + '(firstOne lastOne and firstTwo lastTwo, Item One (n.d.).)\n\n'
+ + '(firstDeleted lastDeleted, Deleted Item (n.d.).)\n\n'
+ + 'After';
+ assert.equal(content.text, expected);
+
+ await deletedNoteItem.eraseTx();
+ });
+ });
+
it("should generate bibliography in default locale if Quick Copy locale not set", async function () {
var item = createUnsavedDataObject('item', { itemType: 'webpage', title: 'Foo' });
item.setField('date', '2020-03-11');
@@ -313,7 +461,6 @@ describe("Zotero.QuickCopy", function () {
// This shouldn't be used
Zotero.Prefs.set('export.lastLocale', 'fr-FR');
- await Zotero.Styles.init();
var format = 'bibliography=http://www.zotero.org/styles/apa';
Zotero.Prefs.set("export.quickCopy.bibliographySetting", format);
@@ -349,152 +496,128 @@ describe("Zotero.QuickCopy", function () {
let content = Zotero.QuickCopy.getContentFromItems([item], format, { asCitations: true });
assert.equal(content.text, '《新型数据财产的行为主义保护:基于财产权理论的分析》。');
});
-});
-
-describe("Smart copy", function () {
- var win, zp, doc;
- var regularItem, attachment, annotation, note;
- var clipboardService;
- const SMART_COPY_BIB = {
- mode: 'bibliography',
- id: 'http://www.zotero.org/styles/apa',
- contentType: '',
- locale: ''
- };
-
- before(async function () {
- win = await loadZoteroPane();
- zp = win.ZoteroPane;
- doc = win.document;
- clipboardService = Components.classes["@mozilla.org/widget/clipboard;1"]
- .getService(Components.interfaces.nsIClipboard);
-
- await Zotero.Styles.init();
- await Zotero.Translators.init();
-
- // Preload Note Markdown / Note HTML translators
- for (let id of [
- Zotero.Translators.TRANSLATOR_ID_NOTE_MARKDOWN,
- Zotero.Translators.TRANSLATOR_ID_NOTE_HTML,
- ]) {
- let translator = Zotero.Translators.get(id);
- translator.cacheCode = true;
- await Zotero.Translators.getCodeForTranslator(translator);
- }
-
- regularItem = createUnsavedDataObject('item', {
- itemType: 'journalArticle',
- title: 'My Smart Copy Paper'
- });
- regularItem.setField('date', '2020');
- regularItem.setCreators([
- { firstName: 'Jane', lastName: 'Smartcopy', creatorType: 'author' }
- ]);
- await regularItem.saveTx();
-
- attachment = await importFileAttachment('test.pdf', { parentItemID: regularItem.id });
- annotation = await createAnnotation('highlight', attachment);
-
- note = createUnsavedDataObject('item', { itemType: 'note' });
- note.setNote('Test note content
');
- await note.saveTx();
- });
-
- // The test runner has a root-level afterEach (test/content/runtests.js)
- // that clears any user-set pref after every test. Re-pin the bibliography
- // style here before each test.
- beforeEach(function () {
- Zotero.Prefs.set('export.quickCopy.bibliographySetting', JSON.stringify(SMART_COPY_BIB));
- });
-
- after(async function () {
- win.Zotero_Tabs.closeAll();
- win.close();
- });
-
- function getClipboardText() {
- let transferable = Components.classes["@mozilla.org/widget/transferable;1"]
- .createInstance(Components.interfaces.nsITransferable);
- transferable.init(null);
- transferable.addDataFlavor('text/plain');
-
- clipboardService.getData(transferable, Components.interfaces.nsIClipboard.kGlobalClipboard);
- let str = {};
- transferable.getTransferData('text/plain', str, {});
- return str.value.QueryInterface(Components.interfaces.nsISupportsString).data;
- }
-
- function clearClipboard() {
- clipboardService.emptyClipboard(Components.interfaces.nsIClipboard.kGlobalClipboard);
- }
-
- describe("Library tab", function () {
- beforeEach(async function () {
- await selectLibrary(win);
- doc.getElementById('item-tree-main').focus();
- });
-
- it("should copy citation for a regular item", async function () {
- await zp.itemsView.selectItem(regularItem.id);
- clearClipboard();
- doc.getElementById('key_smartCopy').doCommand();
- assert.equal(getClipboardText(), '(Smartcopy, 2020)');
- });
-
- it("should copy annotation content when an annotation is selected", async function () {
- await zp.itemsView.selectItem(annotation.id);
- clearClipboard();
- doc.getElementById('key_smartCopy').doCommand();
- assert.include(getClipboardText(), annotation.annotationText);
- });
-
- it("should copy note content when a note is selected", async function () {
- await zp.itemsView.selectItem(note.id);
- clearClipboard();
- doc.getElementById('key_smartCopy').doCommand();
- assert.include(getClipboardText(), 'Test note content');
- });
- });
-
- describe("Reader tab", function () {
- var reader;
- // Use a local sandbox: sinon's default sandbox tracks every stub
- // forever, so a later `sinon.restore()` elsewhere would
- // re-invoke our stub's restore against the already dead reader.
- var sandbox;
+ describe("Smart copy", function () {
+ var regularItem, attachment, annotation, note;
+ var clipboardService;
+ const SMART_COPY_BIB = {
+ mode: 'bibliography',
+ id: 'http://www.zotero.org/styles/apa',
+ contentType: '',
+ locale: ''
+ };
before(async function () {
- sandbox = sinon.createSandbox();
- reader = await Zotero.Reader.open(attachment.id);
- await reader._initPromise;
- await reader._internalReader._primaryView.initializedPromise;
- while (!reader._iframeWindow) {
- await Zotero.Promise.delay(50);
- }
- // Pretend that the reader is focused
- sandbox.stub(reader._iframeWindow.document, 'hasFocus').returns(true);
+ clipboardService = Components.classes["@mozilla.org/widget/clipboard;1"]
+ .getService(Components.interfaces.nsIClipboard);
+
+ regularItem = createUnsavedDataObject('item', {
+ itemType: 'journalArticle',
+ title: 'My Smart Copy Paper'
+ });
+ regularItem.setField('date', '2020');
+ regularItem.setCreators([
+ { firstName: 'Jane', lastName: 'Smartcopy', creatorType: 'author' }
+ ]);
+ await regularItem.saveTx();
+
+ attachment = await importFileAttachment('test.pdf', { parentItemID: regularItem.id });
+ annotation = await createAnnotation('highlight', attachment);
+
+ note = createUnsavedDataObject('item', { itemType: 'note' });
+ note.setNote('Test note content
');
+ await note.saveTx();
});
- beforeEach(async function () {
- await win.Zotero_Tabs.select(reader.tabID);
+ // The parent `beforeEach` re-pins prefs to the file's defaults, so
+ // override the bibliography setting here for Smart copy assertions.
+ beforeEach(function () {
+ Zotero.Prefs.set('export.quickCopy.bibliographySetting', JSON.stringify(SMART_COPY_BIB));
});
- after(function () {
- sandbox.restore();
+ function getClipboardText() {
+ let transferable = Components.classes["@mozilla.org/widget/transferable;1"]
+ .createInstance(Components.interfaces.nsITransferable);
+ transferable.init(null);
+ transferable.addDataFlavor('text/plain');
+
+ clipboardService.getData(transferable, Components.interfaces.nsIClipboard.kGlobalClipboard);
+ let str = {};
+ transferable.getTransferData('text/plain', str, {});
+ return str.value.QueryInterface(Components.interfaces.nsISupportsString).data;
+ }
+
+ function clearClipboard() {
+ clipboardService.emptyClipboard(Components.interfaces.nsIClipboard.kGlobalClipboard);
+ }
+
+ describe("Library tab", function () {
+ beforeEach(async function () {
+ await selectLibrary(win);
+ doc.getElementById('item-tree-main').focus();
+ });
+
+ it("should copy citation for a regular item", async function () {
+ await zp.itemsView.selectItem(regularItem.id);
+ clearClipboard();
+ doc.getElementById('key_smartCopy').doCommand();
+ assert.equal(getClipboardText(), '(Smartcopy, 2020)');
+ });
+
+ it("should copy annotation content when an annotation is selected", async function () {
+ await zp.itemsView.selectItem(annotation.id);
+ clearClipboard();
+ doc.getElementById('key_smartCopy').doCommand();
+ assert.include(getClipboardText(), annotation.annotationText);
+ });
+
+ it("should copy note content when a note is selected", async function () {
+ await zp.itemsView.selectItem(note.id);
+ clearClipboard();
+ doc.getElementById('key_smartCopy').doCommand();
+ assert.include(getClipboardText(), 'Test note content');
+ });
});
- it("should copy parent citation when nothing is selected in the reader", function () {
- clearClipboard();
- doc.getElementById('key_smartCopy').doCommand();
- assert.equal(getClipboardText(), '(Smartcopy, 2020)');
- });
+ describe("Reader tab", function () {
+ var reader;
+ // Use a local sandbox: sinon's default sandbox tracks every stub
+ // forever, so a later `sinon.restore()` elsewhere would
+ // re-invoke our stub's restore against the already dead reader.
+ var sandbox;
- it("should copy selected annotation", function () {
- sandbox.stub(reader, 'getSelectedAnnotationIDs').returns([annotation.key]);
- clearClipboard();
- doc.getElementById('key_smartCopy').doCommand();
- assert.include(getClipboardText(), annotation.annotationText);
+ before(async function () {
+ sandbox = sinon.createSandbox();
+ reader = await Zotero.Reader.open(attachment.id);
+ await reader._initPromise;
+ await reader._internalReader._primaryView.initializedPromise;
+ while (!reader._iframeWindow) {
+ await Zotero.Promise.delay(50);
+ }
+ // Pretend that the reader is focused
+ sandbox.stub(reader._iframeWindow.document, 'hasFocus').returns(true);
+ });
+
+ beforeEach(async function () {
+ await win.Zotero_Tabs.select(reader.tabID);
+ });
+
+ after(function () {
+ sandbox.restore();
+ });
+
+ it("should copy parent citation when nothing is selected in the reader", function () {
+ clearClipboard();
+ doc.getElementById('key_smartCopy').doCommand();
+ assert.equal(getClipboardText(), '(Smartcopy, 2020)');
+ });
+
+ it("should copy selected annotation", function () {
+ sandbox.stub(reader, 'getSelectedAnnotationIDs').returns([annotation.key]);
+ clearClipboard();
+ doc.getElementById('key_smartCopy').doCommand();
+ assert.include(getClipboardText(), annotation.annotationText);
+ });
});
});
});