describe("Zotero.Attachments", function() { var HiddenBrowser; var browser; before(function () { HiddenBrowser = ChromeUtils.import("chrome://zotero/content/HiddenBrowser.jsm").HiddenBrowser; }); afterEach(function () { if (browser) { HiddenBrowser.destroy(browser); browser = null; } }); describe("#importFromFile()", function () { it("should create a child attachment from a text file", function* () { // Create test file var contents = "Test"; var tmpFile = Zotero.getTempDirectory(); tmpFile.append('test.txt'); yield Zotero.File.putContentsAsync(tmpFile, contents); // Create parent item var item = new Zotero.Item('book'); var parentItemID = yield item.saveTx(); // Create attachment and compare content var item = yield Zotero.Attachments.importFromFile({ file: tmpFile, parentItemID: parentItemID }); var storedFile = item.getFile(); assert.equal((yield Zotero.File.getContentsAsync(storedFile)), contents); // Clean up yield Zotero.Items.erase(item.id); }); it("should create a top-level attachment from a PNG file", function* () { var file = getTestDataDirectory(); file.append('test.png'); var contents = yield Zotero.File.getBinaryContentsAsync(file); // Create attachment and compare content var item = yield Zotero.Attachments.importFromFile({ file: file }); var storedFile = item.getFile(); assert.equal((yield Zotero.File.getBinaryContentsAsync(storedFile)), contents); // Clean up yield Zotero.Items.erase(item.id); }); it("should create a top-level attachment from a PNG file in a collection", function* () { var file = getTestDataDirectory(); file.append('test.png'); var contents = yield Zotero.File.getBinaryContentsAsync(file); var collection = yield createDataObject('collection'); // Create attachment and compare content var item = yield Zotero.Attachments.importFromFile({ file: file, collections: [collection.id] }); var storedFile = item.getFile(); assert.equal((yield Zotero.File.getBinaryContentsAsync(storedFile)), contents); // Clean up yield Zotero.Items.erase(item.id); }); it("should create a child attachment from a PNG file", function* () { var file = getTestDataDirectory(); file.append('test.png'); var contents = yield Zotero.File.getBinaryContentsAsync(file); // Create parent item var item = new Zotero.Item('book'); var parentItemID = yield item.saveTx(); // Create attachment and compare content var item = yield Zotero.Attachments.importFromFile({ file: file, parentItemID: parentItemID }); var storedFile = item.getFile(); assert.equal((yield Zotero.File.getBinaryContentsAsync(storedFile)), contents); // Clean up yield Zotero.Items.erase(item.id); }); }) describe("#linkFromFile()", function () { it("should link to a file in My Library", function* () { var item = yield createDataObject('item'); var file = getTestDataDirectory(); file.append('test.png'); var attachment = yield Zotero.Attachments.linkFromFile({ file: file, parentItemID: item.id }); assert.equal(attachment.getFilePath(), file.path); }) it.skip("should throw an error for a non-user library", function* () { // Should create a group library for use by all tests }) }) describe("#linkFromFileWithRelativePath()", function () { afterEach(function () { Zotero.Prefs.clear('baseAttachmentPath'); }); it("should link to a file using a relative path with no base directory set", async function () { Zotero.Prefs.clear('baseAttachmentPath'); var item = await createDataObject('item'); var spy = sinon.spy(Zotero.Fulltext, 'indexPDF'); var relPath = 'a/b/test.pdf'; var attachment = await Zotero.Attachments.linkFromFileWithRelativePath({ path: relPath, title: 'test.pdf', parentItemID: item.id, contentType: 'application/pdf' }); assert.ok(spy.notCalled); spy.restore(); assert.equal( attachment.attachmentPath, Zotero.Attachments.BASE_PATH_PLACEHOLDER + relPath ); }); it("should link to a file using a relative path within the base directory", async function () { var baseDir = await getTempDirectory(); Zotero.Prefs.set('baseAttachmentPath', baseDir); Zotero.Prefs.set('saveRelativeAttachmentPath', true); var subDir = OS.Path.join(baseDir, 'foo'); await OS.File.makeDir(subDir); var file = OS.Path.join(subDir, 'test.pdf'); await OS.File.copy(OS.Path.join(getTestDataDirectory().path, 'test.pdf'), file); var item = await createDataObject('item'); var spy = sinon.spy(Zotero.Fulltext, 'indexPDF'); var relPath = 'foo/test.pdf'; var attachment = await Zotero.Attachments.linkFromFileWithRelativePath({ path: relPath, title: 'test.pdf', parentItemID: item.id, contentType: 'application/pdf' }); assert.ok(spy.called); spy.restore(); assert.equal( attachment.attachmentPath, Zotero.Attachments.BASE_PATH_PLACEHOLDER + relPath ); assert.ok(await attachment.fileExists()); }); it("should link to a nonexistent file using a relative path within the base directory", async function () { var baseDir = await getTempDirectory(); Zotero.Prefs.set('baseAttachmentPath', baseDir); Zotero.Prefs.set('saveRelativeAttachmentPath', true); var subDir = OS.Path.join(baseDir, 'foo'); await OS.File.makeDir(subDir); var item = await createDataObject('item'); var spy = sinon.spy(Zotero.Fulltext, 'indexPDF'); var relPath = 'foo/test.pdf'; var attachment = await Zotero.Attachments.linkFromFileWithRelativePath({ path: relPath, title: 'test.pdf', parentItemID: item.id, contentType: 'application/pdf' }); assert.ok(spy.notCalled); spy.restore(); assert.equal( attachment.attachmentPath, Zotero.Attachments.BASE_PATH_PLACEHOLDER + relPath ); assert.isFalse(await attachment.fileExists()); }); it("should reject absolute paths", async function () { try { await Zotero.Attachments.linkFromFileWithRelativePath({ path: '/a/b/test.pdf', title: 'test.pdf', contentType: 'application/pdf' }); } catch (e) { return; } assert.fail(); }); }); describe("#importSnapshotFromFile()", function () { it("should import an HTML file", function* () { var item = yield createDataObject('item'); var file = getTestDataDirectory(); file.append('test.html'); var attachment = yield Zotero.Attachments.importSnapshotFromFile({ title: 'Snapshot', url: 'http://example.com', file, parentItemID: item.id, contentType: 'text/html', charset: 'utf-8' }); var matches = yield Zotero.Fulltext.findTextInItems([attachment.id], 'test'); assert.lengthOf(matches, 1); assert.propertyVal(matches[0], 'id', attachment.id); }); it("should detect charset for an HTML file", function* () { var item = yield createDataObject('item'); var file = getTestDataDirectory(); file.append('test.html'); var attachment = yield Zotero.Attachments.importSnapshotFromFile({ title: 'Snapshot', url: 'http://example.com', file, parentItemID: item.id, contentType: 'text/html' }); assert.equal(attachment.attachmentCharset, 'utf-8'); var matches = yield Zotero.Fulltext.findTextInItems([attachment.id], 'test'); assert.lengthOf(matches, 1); assert.propertyVal(matches[0], 'id', attachment.id); }); it("should index JavaScript-created text in an HTML file", async function () { var item = await createDataObject('item'); var file = getTestDataDirectory(); file.append('test-js.html'); var attachment = await Zotero.Attachments.importSnapshotFromFile({ title: 'Snapshot', url: 'http://example.com', file, parentItemID: item.id, contentType: 'text/html' }); assert.equal(attachment.attachmentCharset, 'utf-8'); var matches = await Zotero.Fulltext.findTextInItems([attachment.id], 'test'); assert.lengthOf(matches, 1); assert.propertyVal(matches[0], 'id', attachment.id); }); }); describe("#importFromURL()", function () { it("should download a PDF from a JS redirect page", async function () { this.timeout(65e3); var item = await Zotero.Attachments.importFromURL({ libraryID: Zotero.Libraries.userLibraryID, url: 'https://zotero-static.s3.amazonaws.com/test-pdf-redirect.html', contentType: 'application/pdf' }); assert.isTrue(item.isPDFAttachment()); var sample = await Zotero.File.getContentsAsync(item.getFilePath(), null, 1000); assert.equal(Zotero.MIME.sniffForMIMEType(sample), 'application/pdf'); // Clean up await Zotero.Items.erase(item.id); }); }); describe("#linkFromDocument", function () { it("should add a link attachment for the current webpage", function* () { var item = yield createDataObject('item'); var uri = OS.Path.join(getTestDataDirectory().path, "snapshot", "index.html"); browser = yield HiddenBrowser.create(uri); var file = getTestDataDirectory(); file.append('test.png'); var attachment = yield Zotero.Attachments.linkFromDocument({ document: yield HiddenBrowser.getDocument(browser), parentItemID: item.id }); assert.equal(attachment.getField('url'), "file://" + uri); // Check indexing var matches = yield Zotero.Fulltext.findTextInItems([attachment.id], 'share your research'); assert.lengthOf(matches, 1); assert.propertyVal(matches[0], 'id', attachment.id); }) }) describe("#importFromDocument()", function () { Components.utils.import("resource://gre/modules/FileUtils.jsm"); Components.utils.import("resource://zotero-unit/httpd.js"); var testServerPath, httpd, prefix; var testServerPortMin = 16213; var testServerPortMax = testServerPort + 20; var testServerPort = testServerPortMin; before(async function () { this.timeout(20000); Zotero.Prefs.set("httpServer.enabled", true); }); beforeEach(function () { // Cycle through ports to prevent NS_ERROR_SOCKET_ADDRESS_IN_USE errors from server // not always fully stopping in time if (testServerPort < testServerPortMax) { testServerPort++; } else { testServerPort = testServerPortMin; } // Use random prefix because httpd does not actually stop between tests prefix = Zotero.Utilities.randomString(); testServerPath = 'http://127.0.0.1:' + testServerPort + '/' + prefix; httpd = new HttpServer(); httpd.start(testServerPort); }); afterEach(async function () { var defer = new Zotero.Promise.defer(); httpd.stop(() => defer.resolve()); await defer.promise; }); it("should save a document with embedded files", async function () { var item = await createDataObject('item'); var uri = OS.Path.join(getTestDataDirectory().path, "snapshot"); httpd.registerDirectory("/" + prefix + "/", new FileUtils.File(uri)); browser = await HiddenBrowser.create(testServerPath + "/index.html"); Zotero.FullText.indexNextInTest(); var attachment = await Zotero.Attachments.importFromDocument({ browser, parentItemID: item.id }); assert.equal(attachment.getField('url'), testServerPath + "/index.html"); // Check indexing var matches = await Zotero.Fulltext.findTextInItems([attachment.id], 'share your research'); assert.lengthOf(matches, 1); assert.propertyVal(matches[0], 'id', attachment.id); var storageDir = Zotero.Attachments.getStorageDirectory(attachment).path; var file = await attachment.getFilePathAsync(); assert.equal(OS.Path.basename(file), 'index.html'); // Check attachment html file contents let path = OS.Path.join(storageDir, 'index.html'); assert.isTrue(await OS.File.exists(path)); let contents = await Zotero.File.getContentsAsync(path); assert.include(contents, ">