describe("Zotero_File_Interface", function () {
let win;
before(function* () {
win = yield loadZoteroPane();
yield OS.File.copy(OS.Path.join(getTestDataDirectory().path, "Test Import Translator.js"),
OS.Path.join(Zotero.getTranslatorsDirectory().path, "Test Import Translator.js"));
yield Zotero.Translators.reinit();
});
after(function () {
win.close();
});
it('should import all types and fields into a new collection', async function () {
this.timeout(10000);
let testFile = getTestDataDirectory();
testFile.append("allTypesAndFields.js");
await win.Zotero_File_Interface.importFile({
file: testFile
});
let importedCollection = Zotero.Collections.getByLibrary(
Zotero.Libraries.userLibraryID
).filter(x => x.name == 'allTypesAndFields');
assert.equal(importedCollection.length, 1);
let childItems = importedCollection[0].getChildItems();
let savedItems = {};
for (let i=0; iChild
');
});
it('should import an item and snapshot from Zotero RDF', async function () {
var tmpDir = await getTempDirectory();
var rdfFile = OS.Path.join(tmpDir, 'test.rdf');
await OS.File.copy(OS.Path.join(getTestDataDirectory().path, 'book_and_snapshot.rdf'), rdfFile);
await OS.File.makeDir(OS.Path.join(tmpDir, 'files'));
await OS.File.makeDir(OS.Path.join(tmpDir, 'files', '2'));
await OS.File.copy(
OS.Path.join(getTestDataDirectory().path, 'test.html'),
OS.Path.join(tmpDir, 'files', '2', 'test.html')
);
var promise = waitForItemEvent('add');
await win.Zotero_File_Interface.importFile({
file: rdfFile
});
var ids = await promise;
// Notifications are batched
assert.lengthOf(ids, 2);
// Check book
var item = Zotero.Items.get(ids[0]);
assert.equal(item.itemTypeID, Zotero.ItemTypes.getID('book'));
// Check attachment
var ids = item.getAttachments();
assert.lengthOf(ids, 1);
var attachment = Zotero.Items.get(ids[0]);
assert.equal(attachment.attachmentCharset, 'utf-8');
// Check indexing
var matches = await Zotero.Fulltext.findTextInItems([attachment.id], 'test');
assert.lengthOf(matches, 1);
assert.propertyVal(matches[0], 'id', attachment.id);
});
it("should import a single top-level collection without creating another collection", async function () {
var collection = await createDataObject('collection', { name: 'Exported' });
var subcollection = await createDataObject(
'collection', { name: 'Sub', parentID: collection.id }
);
var item = await createDataObject(
'item', { title: 'Collection', collections: [collection.id] }
);
var sharedItem = await createDataObject(
'item', { title: 'Both', collections: [collection.id, subcollection.id] }
);
var file = OS.Path.join(await getTempDirectory(), 'exported.rdf');
var translation = new Zotero.Translate.Export();
translation.setCollection(collection);
translation.setLocation(Zotero.File.pathToFile(file));
translation.setTranslator('14763d24-8ba0-45df-8f52-b8d1108e7ac9');
await translation.translate();
await win.Zotero_File_Interface.importFile({ file });
// The imported collection is at the top level, rather than inside a collection named
// after the file
var collections = Zotero.Collections.getByLibrary(Zotero.Libraries.userLibraryID);
assert.isEmpty(collections.filter(c => c.name == 'exported'));
var imported = collections.filter(c => c.name == collection.name && c.id != collection.id);
assert.lengthOf(imported, 1);
var titles = c => c.getChildItems().map(i => i.getField('title'));
assert.sameMembers(
titles(imported[0]), [item.getField('title'), sharedItem.getField('title')]
);
var importedSubcollections = imported[0].getChildCollections();
assert.lengthOf(importedSubcollections, 1);
assert.equal(importedSubcollections[0].name, subcollection.name);
assert.sameMembers(titles(importedSubcollections[0]), [sharedItem.getField('title')]);
});
it("should keep items outside a single imported collection out of it", async function () {
var group = await createGroup({ name: 'Import' + Zotero.Utilities.randomString() });
var libraryID = group.libraryID;
var collection = await createDataObject(
'collection', { name: 'Collection', libraryID }
);
var collectionItem = await createDataObject(
'item', { title: 'InCollection', libraryID, collections: [collection.id] }
);
var unfiledItem = await createDataObject('item', { title: 'Unfiled', libraryID });
var file = OS.Path.join(await getTempDirectory(), 'library-export.rdf');
var translation = new Zotero.Translate.Export();
translation.setLibraryID(libraryID);
translation.setLocation(Zotero.File.pathToFile(file));
translation.setTranslator('14763d24-8ba0-45df-8f52-b8d1108e7ac9');
await translation.translate();
await win.Zotero_File_Interface.importFile({ file });
// The unfiled item doesn't belong to the imported collection, so the collection created
// for the import is kept to hold it
var importCollections = Zotero.Collections.getByLibrary(Zotero.Libraries.userLibraryID)
.filter(c => c.name == 'library-export');
assert.lengthOf(importCollections, 1);
var titles = c => c.getChildItems().map(i => i.getField('title'));
assert.sameMembers(titles(importCollections[0]), [unfiledItem.getField('title')]);
var imported = importCollections[0].getChildCollections();
assert.lengthOf(imported, 1);
assert.equal(imported[0].name, collection.name);
assert.sameMembers(titles(imported[0]), [collectionItem.getField('title')]);
});
it("should import multiple exported collections below the collection created for the import", async function () {
var collection1 = await createDataObject('collection', { name: 'First' });
var subcollection = await createDataObject(
'collection', { name: 'Sub', parentID: collection1.id }
);
var collection2 = await createDataObject('collection', { name: 'Second' });
var item1 = await createDataObject(
'item', { title: 'InFirst', collections: [collection1.id] }
);
var subItem = await createDataObject(
'item', { title: 'InSub', collections: [subcollection.id] }
);
var item2 = await createDataObject(
'item', { title: 'InSecond', collections: [collection2.id] }
);
var file = OS.Path.join(await getTempDirectory(), 'multiple.rdf');
var translation = new Zotero.Translate.Export();
translation.setCollections([collection1, collection2]);
translation.setLocation(Zotero.File.pathToFile(file));
translation.setTranslator('14763d24-8ba0-45df-8f52-b8d1108e7ac9');
await translation.translate();
await win.Zotero_File_Interface.importFile({ file });
// Neither collection can stand in for the collection created for the import, so both are
// imported below it
var importCollections = Zotero.Collections.getByLibrary(Zotero.Libraries.userLibraryID)
.filter(c => c.name == 'multiple');
assert.lengthOf(importCollections, 1);
assert.isEmpty(importCollections[0].getChildItems());
var titles = c => c.getChildItems().map(i => i.getField('title'));
var imported = importCollections[0].getChildCollections();
assert.sameMembers(imported.map(c => c.name), [collection1.name, collection2.name]);
var importedFirst = imported.find(c => c.name == collection1.name);
assert.sameMembers(titles(importedFirst), [item1.getField('title')]);
var importedSubcollections = importedFirst.getChildCollections();
assert.lengthOf(importedSubcollections, 1);
assert.equal(importedSubcollections[0].name, subcollection.name);
assert.sameMembers(titles(importedSubcollections[0]), [subItem.getField('title')]);
var importedSecond = imported.find(c => c.name == collection2.name);
assert.sameMembers(titles(importedSecond), [item2.getField('title')]);
});
it('should import a MODS file', async function () {
var modsFile = OS.Path.join(getTestDataDirectory().path, "mods.xml");
var promise = waitForItemEvent('add');
await win.Zotero_File_Interface.importFile({
file: modsFile
});
var ids = await promise;
assert.lengthOf(ids, 1);
var item = Zotero.Items.get(ids[0]);
assert.equal(item.itemTypeID, Zotero.ItemTypes.getID('journalArticle'));
assert.equal(item.getField('title'), "Test");
});
describe("#importFromClipboard()", function () {
it("should import BibTeX from the clipboard", async function () {
var str = "@article{last_test_nodate,\n title = {Test},\n author = {Last, First},\n}";
Zotero.Utilities.Internal.copyTextToClipboard(str);
var promise = waitForItemEvent('add');
await win.Zotero_File_Interface.importFromClipboard();
var ids = await promise;
assert.lengthOf(ids, 1);
var item = Zotero.Items.get(ids[0]);
assert.equal(item.itemTypeID, Zotero.ItemTypes.getID('journalArticle'));
assert.equal(item.getField('title'), "Test");
var creator = item.getCreators()[0];
assert.propertyVal(creator, 'firstName', "First")
assert.propertyVal(creator, 'lastName', "Last")
});
});
describe("#copyItemsToClipboard()", function () {
var clipboardService, item1, item2;
before(function* () {
yield Zotero.Styles.init();
clipboardService = Components.classes["@mozilla.org/widget/clipboard;1"]
.getService(Components.interfaces.nsIClipboard);
item1 = createUnsavedDataObject('item', { title: "A" });
item1.setField('date', '2016');
yield item1.saveTx();
item2 = createUnsavedDataObject('item', { title: "B" });
item2.setField('date', '2016');
yield item2.saveTx();
});
function getDataForFlavor(flavor) {
var transferable = Components.classes["@mozilla.org/widget/transferable;1"]
.createInstance(Components.interfaces.nsITransferable);
transferable.init(null);
transferable.addDataFlavor(flavor);
clipboardService.getData(transferable, Components.interfaces.nsIClipboard.kGlobalClipboard);
var str = {};
transferable.getTransferData(flavor, str, {})
return str.value.QueryInterface(Components.interfaces.nsISupportsString).data;
}
//
// Non-"Copy as HTML" mode
//
it("should copy HTML and text citations to the clipboard", async function () {
win.Zotero_File_Interface.copyItemsToClipboard(
[item1, item2],
'http://www.zotero.org/styles/apa',
'en-US',
false,
true
);
// HTML
var str = getDataForFlavor('text/html');
assert.equal(str, '(A, 2016; B, 2016)');
// Plain text
str = getDataForFlavor('text/plain');
assert.equal(str, '(A, 2016; B, 2016)');
});
it("should copy HTML and text bibliography to the clipboard", async function () {
win.Zotero_File_Interface.copyItemsToClipboard(
[item1, item2],
'http://www.zotero.org/styles/apa',
'en-US'
);
var str = getDataForFlavor('text/html');
assert.include(str, 'line-height');
assert.include(str, 'A');
assert.include(str, 'B');
// Plain text
str = getDataForFlavor('text/plain');
assert.equal(str, 'A. (2016).\nB. (2016).\n');
});
//
// "Copy as HTML" mode
//
it("should copy HTML and HTML source citations to the clipboard", async function () {
win.Zotero_File_Interface.copyItemsToClipboard(
[item1, item2],
'http://www.zotero.org/styles/apa',
'en-US',
true,
true
);
var str = getDataForFlavor('text/html');
assert.equal(str, '(A, 2016; B, 2016)');
// Plain text
str = getDataForFlavor('text/plain');
assert.equal(str, '(A, 2016; B, 2016)');
});
it("should copy HTML and HTML source bibliography to the clipboard", async function () {
win.Zotero_File_Interface.copyItemsToClipboard(
[item1, item2],
'http://www.zotero.org/styles/apa',
'en-US',
true
);
var str = getDataForFlavor('text/html');
assert.include(str, 'line-height');
assert.include(str, 'A');
assert.include(str, 'B');
// Plain text
str = getDataForFlavor('text/plain');
assert.include(str, 'line-height');
assert.include(str, 'A');
assert.include(str, 'B');
});
});
describe('Citavi annotations', () => {
it('should import Citavi', async () => {
var testFile = OS.Path.join(getTestDataDirectory().path, 'citavi-test-project.ctv6');
const promise = waitForItemEvent('add');
await win.Zotero_File_Interface.importFile({
file: testFile,
createNewCollection: false
});
const itemIDs = await promise;
const importedItem = await Zotero.Items.getAsync(itemIDs[0]);
assert.equal(importedItem.getField('title'), 'Bitcoin: A Peer-to-Peer Electronic Cash System');
const importedPDF = await Zotero.Items.getAsync(importedItem.getAttachments()[0]);
const annotations = importedPDF.getAnnotations();
assert.lengthOf(annotations, 5);
const annotation1 = annotations.find(a => a.annotationText === 'peer-to-peer');
const annotation2 = annotations.find(a => a.annotationText === 'CPU power is controlled by nodes that are not cooperating to attack the network, they\'ll generate the longest chain and outpace attackers.');
const annotation3 = annotations.find(a => a.annotationText === 'double-spending');
const annotation4 = annotations.find(a => a.annotationText === 'This is a comment');
const annotation5 = annotations.find(a => a.annotationText === 'This is a green highlight on page 3');
assert.deepEqual(
JSON.parse(annotation1.annotationPosition),
{ pageIndex: 0, rects: [[230.202, 578.879, 275.478, 585.817], [230.202, 578.879, 275.478, 585.817]] }
);
// Note: The third rect not only wraps the first two rects, but also includes extra text,
// which makes the current annotation text essentially incorrect
assert.deepEqual(
JSON.parse(annotation2.annotationPosition),
{ pageIndex: 0, rects: [[228.335, 475.341, 461.756, 482.179], [146.3, 463.841, 437.511, 470.679], [146.3, 463.841, 461.756, 482.179]] },
);
assert.deepEqual(
JSON.parse(annotation3.annotationPosition),
{ pageIndex: 0, rects: [[254.515, 532.841, 316.462, 539.679], [254.515, 532.841, 316.462, 539.679]] },
);
assert.deepEqual(
JSON.parse(annotation4.annotationPosition),
{ pageIndex: 0, rects: [[146.3, 429.341, 199.495, 436.179], [146.3, 429.341, 199.495, 436.179]] }
);
assert.deepEqual(
JSON.parse(annotation5.annotationPosition),
{ pageIndex: 2, rects: [[133.3, 330.924, 185.269, 340.294], [133.3, 330.924, 185.269, 340.294]] }
);
assert.equal(annotation1.annotationSortIndex, '00000|000103|00206');
assert.equal(annotation2.annotationSortIndex, '00000|000706|00309');
assert.equal(annotation3.annotationSortIndex, '00000|000390|00252');
assert.equal(annotation4.annotationSortIndex, '00000|000981|00355');
assert.equal(annotation5.annotationSortIndex, '00002|001638|00451');
assert.deepEqual(annotation1.getTags(), [{ tag: 'red' }]);
assert.deepEqual(annotation2.getTags(), [{ tag: 'blue' }]);
assert.deepEqual(annotation3.getTags(), []);
assert.deepEqual(annotation4.getTags(), [{ tag: 'comment' }]);
assert.deepEqual(annotation5.getTags(), []);
assert.equal(annotation1.annotationPageLabel, '1');
assert.equal(annotation2.annotationPageLabel, '1');
assert.equal(annotation3.annotationPageLabel, '1');
assert.equal(annotation4.annotationPageLabel, '1');
assert.equal(annotation5.annotationPageLabel, '3');
});
});
});