translateTest.js (67354B)
1 new function() { 2 Components.utils.import("resource://gre/modules/osfile.jsm"); 3 4 /** 5 * Create a new translator that saves the specified items 6 * @param {String} translatorType - "import" or "web" 7 * @param {Object} items - items as translator JSON 8 */ 9 function saveItemsThroughTranslator(translatorType, items) { 10 let tyname; 11 if (translatorType == "web") { 12 tyname = "Web"; 13 } else if (translatorType == "import") { 14 tyname = "Import"; 15 } else { 16 throw new Error("invalid translator type "+translatorType); 17 } 18 19 let translate = new Zotero.Translate[tyname](); 20 let browser; 21 if (translatorType == "web") { 22 browser = Zotero.Browser.createHiddenBrowser(); 23 translate.setDocument(browser.contentDocument); 24 } else if (translatorType == "import") { 25 translate.setString(""); 26 } 27 translate.setTranslator(buildDummyTranslator( 28 translatorType, 29 "function detectWeb() {}\n"+ 30 "function do"+tyname+"() {\n"+ 31 " var json = JSON.parse('"+JSON.stringify(items).replace(/['\\]/g, "\\$&")+"');\n"+ 32 " for (var i=0; i<json.length; i++) {"+ 33 " var item = new Zotero.Item;\n"+ 34 " for (var field in json[i]) { item[field] = json[i][field]; }\n"+ 35 " item.complete();\n"+ 36 " }\n"+ 37 "}")); 38 return translate.translate().then(function(items) { 39 if (browser) Zotero.Browser.deleteHiddenBrowser(browser); 40 return items; 41 }); 42 } 43 44 /** 45 * Convert an array of items to an object in which they are indexed by 46 * their display titles 47 */ 48 function itemsArrayToObject(items) { 49 var obj = {}; 50 for (let item of items) { 51 obj[item.getDisplayTitle()] = item; 52 } 53 return obj; 54 } 55 56 const TEST_TAGS = [ 57 "manual tag as string", 58 {"tag":"manual tag as object"}, 59 {"tag":"manual tag as object with type", "type":0}, 60 {"tag":"automatic tag as object", "type":1}, 61 {"name":"tag in name property"} 62 ]; 63 64 /** 65 * Check that tags match expected values, if TEST_TAGS is passed as test array 66 */ 67 function checkTestTags(newItem, web) { 68 assert.equal(newItem.getTagType("manual tag as string"), web ? 1 : 0); 69 assert.equal(newItem.getTagType("manual tag as object"), web ? 1 : 0); 70 assert.equal(newItem.getTagType("manual tag as object with type"), web ? 1 : 0); 71 assert.equal(newItem.getTagType("automatic tag as object"), 1); 72 assert.equal(newItem.getTagType("tag in name property"), web ? 1 : 0); 73 } 74 75 /** 76 * Get included test snapshot file 77 * @returns {nsIFile} 78 */ 79 function getTestSnapshot() { 80 let snapshot = getTestDataDirectory(); 81 snapshot.append("snapshot"); 82 snapshot.append("index.html"); 83 return snapshot; 84 } 85 86 /** 87 * Get included test snapshot file 88 * @returns {nsIFile} 89 */ 90 function getTestPDF() { 91 let testPDF = getTestDataDirectory(); 92 testPDF.append("empty.pdf"); 93 return testPDF; 94 } 95 96 /** 97 * Set up endpoints for testing attachment saving 98 * This must happen immediately before the test, since Zotero might get 99 * restarted by resetDB(), which would erase our registered endpoints. 100 */ 101 function setupAttachmentEndpoints() { 102 var SnapshotTest = function() {}; 103 Zotero.Server.Endpoints["/test/translate/test.html"] = SnapshotTest; 104 SnapshotTest.prototype = { 105 "supportedMethods":["GET"], 106 "init":function(data, sendResponseCallback) { 107 Zotero.File.getBinaryContentsAsync(getTestSnapshot()).then(function (data) { 108 sendResponseCallback(200, "text/html", data); 109 }); 110 } 111 } 112 var PDFTest = function() {}; 113 Zotero.Server.Endpoints["/test/translate/test.pdf"] = PDFTest; 114 PDFTest.prototype = { 115 "supportedMethods":["GET"], 116 "init":function(data, sendResponseCallback) { 117 Zotero.File.getBinaryContentsAsync(getTestPDF()).then(function (data) { 118 sendResponseCallback(200, "application/pdf", data); 119 }); 120 } 121 } 122 var NonExistentTest = function() {}; 123 Zotero.Server.Endpoints["/test/translate/does_not_exist.html"] = NonExistentTest; 124 NonExistentTest.prototype = { 125 "supportedMethods":["GET"], 126 "init":function(data, sendResponseCallback) { 127 sendResponseCallback(404, "text/html", "File does not exist"); 128 } 129 } 130 } 131 132 describe("Zotero.Translate", function() { 133 let win; 134 before(function* () { 135 // TEMP: Fix for slow translator initialization on Linux/Travis 136 this.timeout(20000); 137 yield Zotero.Translators.init(); 138 139 setupAttachmentEndpoints(); 140 win = yield loadBrowserWindow(); 141 }); 142 after(function () { 143 win.close(); 144 }); 145 146 describe("Zotero.Item", function() { 147 it('should save ordinary fields and creators', function* () { 148 this.timeout(10000); 149 let data = loadSampleData('allTypesAndFields'); 150 let trueItems = loadSampleData('itemJSON'); 151 let saveItems = []; 152 for (let itemType in data) { 153 saveItems.push(data[itemType]); 154 let trueItem = trueItems[itemType]; 155 delete trueItem.dateAdded; 156 delete trueItem.dateModified; 157 delete trueItem.key; 158 } 159 160 let newItems = yield saveItemsThroughTranslator("import", saveItems); 161 let savedItems = {}; 162 for (let i=0; i<newItems.length; i++) { 163 let savedItem = newItems[i].toJSON(); 164 savedItems[Zotero.ItemTypes.getName(newItems[i].itemTypeID)] = savedItem; 165 delete savedItem.dateAdded; 166 delete savedItem.dateModified; 167 delete savedItem.key; 168 } 169 assert.deepEqual(savedItems, trueItems, "saved items match inputs"); 170 }); 171 172 it('should accept deprecated SQL accessDates', function* () { 173 let myItem = { 174 "itemType":"webpage", 175 "title":"Test Item", 176 "accessDate":"2015-01-02 03:04:05" 177 } 178 let newItems = yield saveItemsThroughTranslator("import", [myItem]); 179 assert.equal(newItems[0].getField("accessDate"), "2015-01-02 03:04:05"); 180 }); 181 182 it('should save tags', function* () { 183 let myItem = { 184 "itemType":"book", 185 "title":"Test Item", 186 "tags":TEST_TAGS 187 }; 188 checkTestTags((yield saveItemsThroughTranslator("import", [myItem]))[0]); 189 }); 190 191 it('should save notes', function* () { 192 let myItems = [ 193 { 194 "itemType":"book", 195 "title":"Test Item", 196 "notes":[ 197 "1 note as string", 198 { 199 "note":"2 note as object", 200 "tags":TEST_TAGS 201 } 202 ] 203 }, 204 { 205 "itemType":"note", 206 "note":"standalone note", 207 "tags":TEST_TAGS 208 } 209 ]; 210 211 let newItems = itemsArrayToObject(yield saveItemsThroughTranslator("import", myItems)); 212 let noteIDs = newItems["Test Item"].getNotes(); 213 let note1 = yield Zotero.Items.getAsync(noteIDs[0]); 214 assert.equal(Zotero.ItemTypes.getName(note1.itemTypeID), "note"); 215 assert.equal(note1.getNote(), "1 note as string"); 216 let note2 = yield Zotero.Items.getAsync(noteIDs[1]); 217 assert.equal(Zotero.ItemTypes.getName(note2.itemTypeID), "note"); 218 assert.equal(note2.getNote(), "2 note as object"); 219 checkTestTags(note2); 220 let note3 = newItems["standalone note"]; 221 assert.equal(note3.getNote(), "standalone note"); 222 checkTestTags(note3); 223 }); 224 225 it('should save relations', async function () { 226 var item = await createDataObject('item'); 227 var itemURI = Zotero.URI.getItemURI(item); 228 let myItem = { 229 itemType: "book", 230 title: "Test Item", 231 relations: { 232 "dc:relation": [itemURI] 233 } 234 }; 235 let newItems = await saveItemsThroughTranslator("import", [myItem]); 236 var relations = newItems[0].getRelations(); 237 assert.lengthOf(Object.keys(relations), 1); 238 assert.lengthOf(relations["dc:relation"], 1); 239 assert.equal(relations["dc:relation"][0], itemURI); 240 }); 241 242 it('should save collections', function* () { 243 let translate = new Zotero.Translate.Import(); 244 translate.setString(""); 245 translate.setTranslator(buildDummyTranslator(4, 246 'function detectWeb() {}\n'+ 247 'function doImport() {\n'+ 248 ' var item1 = new Zotero.Item("book");\n'+ 249 ' item1.title = "Not in Collection";\n'+ 250 ' item1.complete();\n'+ 251 ' var item2 = new Zotero.Item("book");\n'+ 252 ' item2.id = 1;\n'+ 253 ' item2.title = "In Parent Collection";\n'+ 254 ' item2.complete();\n'+ 255 ' var item3 = new Zotero.Item("book");\n'+ 256 ' item3.id = 2;\n'+ 257 ' item3.title = "In Child Collection";\n'+ 258 ' item3.complete();\n'+ 259 ' var collection = new Zotero.Collection();\n'+ 260 ' collection.name = "Parent Collection";\n'+ 261 ' collection.children = [{"id":1}, {"type":"collection", "name":"Child Collection", "children":[{"id":2}]}];\n'+ 262 ' collection.complete();\n'+ 263 '}')); 264 let newItems = yield translate.translate(); 265 assert.equal(newItems.length, 3); 266 newItems = itemsArrayToObject(newItems); 267 assert.equal(newItems["Not in Collection"].getCollections().length, 0); 268 269 let parentCollection = newItems["In Parent Collection"].getCollections(); 270 assert.equal(parentCollection.length, 1); 271 parentCollection = (yield Zotero.Collections.getAsync(parentCollection))[0]; 272 assert.equal(parentCollection.name, "Parent Collection"); 273 assert.isTrue(parentCollection.hasChildCollections()); 274 275 let childCollection = newItems["In Child Collection"].getCollections(); 276 assert.equal(childCollection.length, 1); 277 childCollection = (yield Zotero.Collections.getAsync(childCollection[0])); 278 assert.equal(childCollection.name, "Child Collection"); 279 let parentChildren = parentCollection.getChildCollections(); 280 assert.equal(parentChildren.length, 1); 281 assert.equal(parentChildren[0], childCollection); 282 }); 283 284 it('import translators should save attachments', function* () { 285 let emptyPDF = getTestPDF().path; 286 let snapshot = getTestSnapshot().path; 287 let myItems = [ 288 { 289 "itemType":"attachment", 290 "path":emptyPDF, 291 "title":"Empty PDF", 292 "note":"attachment note", 293 "tags":TEST_TAGS 294 }, 295 { 296 "itemType":"attachment", 297 "url":"http://www.zotero.org/", 298 "title":"Link to zotero.org", 299 "note":"attachment 2 note", 300 "tags":TEST_TAGS 301 } 302 ]; 303 let childAttachments = myItems.slice(); 304 childAttachments.push({ 305 "itemType":"attachment", 306 "path":snapshot, 307 "url":"http://www.example.com/", 308 "title":"Snapshot", 309 "note":"attachment 3 note", 310 "tags":TEST_TAGS 311 }); 312 myItems.push({ 313 "itemType":"book", 314 "title":"Container Item", 315 "attachments":childAttachments 316 }); 317 318 let newItems = itemsArrayToObject(yield saveItemsThroughTranslator("import", myItems)); 319 let containedAttachments = yield Zotero.Items.getAsync(newItems["Container Item"].getAttachments()); 320 assert.equal(containedAttachments.length, 3); 321 322 for (let savedAttachments of [[newItems["Empty PDF"], newItems["Link to zotero.org"]], 323 [containedAttachments[0], containedAttachments[1]]]) { 324 assert.equal(savedAttachments[0].getField("title"), "Empty PDF"); 325 assert.equal(savedAttachments[0].getNote(), "attachment note"); 326 assert.equal(savedAttachments[0].attachmentLinkMode, Zotero.Attachments.LINK_MODE_IMPORTED_FILE); 327 checkTestTags(savedAttachments[0]); 328 329 assert.equal(savedAttachments[1].getField("title"), "Link to zotero.org"); 330 assert.equal(savedAttachments[1].getField("url"), "http://www.zotero.org/"); 331 assert.equal(savedAttachments[1].getNote(), "attachment 2 note"); 332 assert.equal(savedAttachments[1].attachmentLinkMode, Zotero.Attachments.LINK_MODE_LINKED_URL); 333 checkTestTags(savedAttachments[1]); 334 } 335 336 assert.equal(containedAttachments[2].getField("title"), "Snapshot"); 337 assert.equal(containedAttachments[2].getField("url"), "http://www.example.com/"); 338 assert.equal(containedAttachments[2].getNote(), "attachment 3 note"); 339 assert.equal(containedAttachments[2].attachmentLinkMode, Zotero.Attachments.LINK_MODE_IMPORTED_URL); 340 checkTestTags(containedAttachments[2]); 341 }); 342 343 it('import translators should save missing snapshots as links', function* () { 344 let missingFile = getTestDataDirectory(); 345 missingFile.append("missing"); 346 assert.isFalse(missingFile.exists()); 347 missingFile = missingFile.path; 348 let myItems = [ 349 { 350 "itemType":"book", 351 "title":"Container Item", 352 "attachments":[ 353 { 354 "itemType":"attachment", 355 "path":missingFile, 356 "url":"http://www.example.com/", 357 "title":"Snapshot with missing file", 358 "note":"attachment note", 359 "tags":TEST_TAGS 360 } 361 ] 362 } 363 ]; 364 365 let newItems = yield saveItemsThroughTranslator("import", myItems); 366 assert.equal(newItems.length, 1); 367 assert.equal(newItems[0].getField("title"), "Container Item"); 368 let containedAttachments = yield Zotero.Items.getAsync(newItems[0].getAttachments()); 369 assert.equal(containedAttachments.length, 1); 370 371 assert.equal(containedAttachments[0].getField("title"), "Snapshot with missing file"); 372 assert.equal(containedAttachments[0].getField("url"), "http://www.example.com/"); 373 assert.equal(containedAttachments[0].getNote(), "attachment note"); 374 assert.equal(containedAttachments[0].attachmentLinkMode, Zotero.Attachments.LINK_MODE_LINKED_URL); 375 checkTestTags(containedAttachments[0]); 376 }); 377 378 it('import translators should ignore missing file attachments', function* () { 379 let missingFile = getTestDataDirectory(); 380 missingFile.append("missing"); 381 assert.isFalse(missingFile.exists()); 382 missingFile = missingFile.path; 383 let myItems = [ 384 { 385 "itemType":"attachment", 386 "path":missingFile, 387 "title":"Missing file" 388 }, 389 { 390 "itemType":"book", 391 "title":"Container Item", 392 "attachments":[ 393 { 394 "itemType":"attachment", 395 "path":missingFile, 396 "title":"Missing file" 397 } 398 ] 399 } 400 ]; 401 402 let newItems = yield saveItemsThroughTranslator("import", myItems); 403 assert.equal(newItems.length, 1); 404 assert.equal(newItems[0].getField("title"), "Container Item"); 405 assert.equal(newItems[0].getAttachments().length, 0); 406 }); 407 408 it('web translators should set accessDate to current date', function* () { 409 let myItem = { 410 "itemType":"webpage", 411 "title":"Test Item", 412 "url":"http://www.zotero.org/" 413 }; 414 let newItems = yield saveItemsThroughTranslator("web", [myItem]); 415 let currentDate = new Date(); 416 let delta = currentDate - Zotero.Date.sqlToDate(newItems[0].getField("accessDate"), true); 417 assert.isAbove(delta, -500); 418 assert.isBelow(delta, 5000); 419 }); 420 421 it('web translators should set accessDate to current date for CURRENT_TIMESTAMP', function* () { 422 let myItem = { 423 itemType: "webpage", 424 title: "Test Item", 425 url: "https://www.zotero.org/", 426 accessDate: 'CURRENT_TIMESTAMP' 427 }; 428 let newItems = yield saveItemsThroughTranslator("web", [myItem]); 429 let currentDate = new Date(); 430 let delta = currentDate - Zotero.Date.sqlToDate(newItems[0].getField("accessDate"), true); 431 assert.isAbove(delta, -500); 432 assert.isBelow(delta, 5000); 433 }); 434 435 it('web translators should save attachments', function* () { 436 let myItems = [ 437 { 438 "itemType":"book", 439 "title":"Container Item", 440 "attachments":[ 441 { 442 "url":"http://www.zotero.org/", 443 "title":"Link to zotero.org", 444 "note":"attachment note", 445 "tags":TEST_TAGS, 446 "snapshot":false 447 }, 448 { 449 "url":"http://127.0.0.1:23119/test/translate/test.html", 450 "title":"Test Snapshot", 451 "note":"attachment 2 note", 452 "tags":TEST_TAGS 453 }, 454 { 455 "url":"http://127.0.0.1:23119/test/translate/test.pdf", 456 "title":"Test PDF", 457 "note":"attachment 3 note", 458 "tags":TEST_TAGS 459 } 460 ] 461 } 462 ]; 463 464 let newItems = yield saveItemsThroughTranslator("web", myItems); 465 assert.equal(newItems.length, 1); 466 let containedAttachments = itemsArrayToObject(yield Zotero.Items.getAsync(newItems[0].getAttachments())); 467 468 let link = containedAttachments["Link to zotero.org"]; 469 assert.equal(link.getField("url"), "http://www.zotero.org/"); 470 assert.equal(link.getNote(), "attachment note"); 471 assert.equal(link.attachmentLinkMode, Zotero.Attachments.LINK_MODE_LINKED_URL); 472 checkTestTags(link, true); 473 474 let snapshot = containedAttachments["Test Snapshot"]; 475 assert.equal(snapshot.getField("url"), "http://127.0.0.1:23119/test/translate/test.html"); 476 assert.equal(snapshot.getNote(), "attachment 2 note"); 477 assert.equal(snapshot.attachmentLinkMode, Zotero.Attachments.LINK_MODE_IMPORTED_URL); 478 assert.equal(snapshot.attachmentContentType, "text/html"); 479 checkTestTags(snapshot, true); 480 481 let pdf = containedAttachments["Test PDF"]; 482 assert.equal(pdf.getField("url"), "http://127.0.0.1:23119/test/translate/test.pdf"); 483 assert.equal(pdf.getNote(), "attachment 3 note"); 484 assert.equal(pdf.attachmentLinkMode, Zotero.Attachments.LINK_MODE_IMPORTED_URL); 485 assert.equal(pdf.attachmentContentType, "application/pdf"); 486 checkTestTags(pdf, true); 487 }); 488 489 it('web translators should save attachment from browser document', function* () { 490 let deferred = Zotero.Promise.defer(); 491 let browser = Zotero.HTTP.loadDocuments( 492 "http://127.0.0.1:23119/test/translate/test.html", 493 doc => deferred.resolve(doc), 494 undefined, 495 undefined, 496 true 497 ); 498 let doc = yield deferred.promise; 499 500 let translate = new Zotero.Translate.Web(); 501 translate.setDocument(doc); 502 translate.setTranslator(buildDummyTranslator(4, 503 'function detectWeb() {}\n'+ 504 'function doWeb(doc) {\n'+ 505 ' var item = new Zotero.Item("book");\n'+ 506 ' item.title = "Container Item";\n'+ 507 ' item.attachments = [{\n'+ 508 ' "document":doc,\n'+ 509 ' "title":"Snapshot from Document",\n'+ 510 ' "note":"attachment note",\n'+ 511 ' "tags":'+JSON.stringify(TEST_TAGS)+'\n'+ 512 ' }];\n'+ 513 ' item.complete();\n'+ 514 '}')); 515 let newItems = yield translate.translate(); 516 assert.equal(newItems.length, 1); 517 let containedAttachments = Zotero.Items.get(newItems[0].getAttachments()); 518 assert.equal(containedAttachments.length, 1); 519 520 let snapshot = containedAttachments[0]; 521 assert.equal(snapshot.getField("url"), "http://127.0.0.1:23119/test/translate/test.html"); 522 assert.equal(snapshot.getNote(), "attachment note"); 523 assert.equal(snapshot.attachmentLinkMode, Zotero.Attachments.LINK_MODE_IMPORTED_URL); 524 assert.equal(snapshot.attachmentContentType, "text/html"); 525 checkTestTags(snapshot, true); 526 527 Zotero.Browser.deleteHiddenBrowser(browser); 528 }); 529 530 it('web translators should save attachment from non-browser document', function* () { 531 return Zotero.HTTP.processDocuments( 532 "http://127.0.0.1:23119/test/translate/test.html", 533 async function (doc) { 534 let translate = new Zotero.Translate.Web(); 535 translate.setDocument(doc); 536 translate.setTranslator(buildDummyTranslator(4, 537 'function detectWeb() {}\n'+ 538 'function doWeb(doc) {\n'+ 539 ' var item = new Zotero.Item("book");\n'+ 540 ' item.title = "Container Item";\n'+ 541 ' item.attachments = [{\n'+ 542 ' "document":doc,\n'+ 543 ' "title":"Snapshot from Document",\n'+ 544 ' "note":"attachment note",\n'+ 545 ' "tags":'+JSON.stringify(TEST_TAGS)+'\n'+ 546 ' }];\n'+ 547 ' item.complete();\n'+ 548 '}')); 549 let newItems = await translate.translate(); 550 assert.equal(newItems.length, 1); 551 let containedAttachments = Zotero.Items.get(newItems[0].getAttachments()); 552 assert.equal(containedAttachments.length, 1); 553 554 let snapshot = containedAttachments[0]; 555 assert.equal(snapshot.getField("url"), "http://127.0.0.1:23119/test/translate/test.html"); 556 assert.equal(snapshot.getNote(), "attachment note"); 557 assert.equal(snapshot.attachmentLinkMode, Zotero.Attachments.LINK_MODE_IMPORTED_URL); 558 assert.equal(snapshot.attachmentContentType, "text/html"); 559 checkTestTags(snapshot, true); 560 } 561 ); 562 }); 563 564 it('web translators should ignore attachments that return error codes', function* () { 565 this.timeout(60000); 566 let myItems = [ 567 { 568 "itemType":"book", 569 "title":"Container Item", 570 "attachments":[ 571 { 572 "url":"http://127.0.0.1:23119/test/translate/does_not_exist.html", 573 "title":"Non-Existent HTML" 574 }, 575 { 576 "url":"http://127.0.0.1:23119/test/translate/does_not_exist.pdf", 577 "title":"Non-Existent PDF" 578 } 579 ] 580 } 581 ]; 582 583 let newItems = yield saveItemsThroughTranslator("web", myItems); 584 assert.equal(newItems.length, 1); 585 let containedAttachments = yield Zotero.Items.getAsync(newItems[0].getAttachments()); 586 assert.equal(containedAttachments.length, 0); 587 }); 588 589 it('web translators should save PDFs only if the content type matches', function* () { 590 this.timeout(60000); 591 let myItems = [ 592 { 593 "itemType":"book", 594 "title":"Container Item", 595 "attachments":[ 596 { 597 "url":"http://127.0.0.1:23119/test/translate/test.html", 598 "mimeType":"application/pdf", 599 "title":"Test PDF with wrong mime type" 600 }, 601 { 602 "url":"http://127.0.0.1:23119/test/translate/test.pdf", 603 "mimeType":"application/pdf", 604 "title":"Test PDF", 605 "note":"attachment note", 606 "tags":TEST_TAGS 607 } 608 ] 609 } 610 ]; 611 612 let newItems = yield saveItemsThroughTranslator("web", myItems); 613 assert.equal(newItems.length, 1); 614 let containedAttachments = yield Zotero.Items.getAsync(newItems[0].getAttachments()); 615 assert.equal(containedAttachments.length, 1); 616 617 let pdf = containedAttachments[0]; 618 assert.equal(pdf.getField("title"), "Test PDF"); 619 assert.equal(pdf.getField("url"), "http://127.0.0.1:23119/test/translate/test.pdf"); 620 assert.equal(pdf.getNote(), "attachment note"); 621 assert.equal(pdf.attachmentLinkMode, Zotero.Attachments.LINK_MODE_IMPORTED_URL); 622 checkTestTags(pdf, true); 623 }); 624 625 it('should not convert tags to canonical form in child translators', function* () { 626 var childTranslator = buildDummyTranslator(1, 627 `function detectWeb() {} 628 function doImport() { 629 var item = new Zotero.Item; 630 item.itemType = "book"; 631 item.title = "The Definitive Guide of Owls"; 632 item.tags = ['owl', 'tag']; 633 item.complete(); 634 }`, {translatorID: 'child-dummy-translator'} 635 ); 636 sinon.stub(Zotero.Translators, 'get').withArgs('child-dummy-translator').returns(childTranslator); 637 638 var parentTranslator = buildDummyTranslator(1, 639 `function detectWeb() {} 640 function doImport() { 641 var translator = Zotero.loadTranslator("import"); 642 translator.setTranslator('child-dummy-translator'); 643 translator.setHandler('itemDone', Zotero.childItemDone); 644 translator.translate(); 645 }` 646 ); 647 648 function childItemDone(obj, item) { 649 // Non-canonical tags after child translator is done 650 assert.deepEqual(['owl', 'tag'], item.tags); 651 item.complete(); 652 } 653 654 var translate = new Zotero.Translate.Import(); 655 translate.setTranslator(parentTranslator); 656 translate.setString(""); 657 yield translate._loadTranslator(parentTranslator); 658 translate._sandboxManager.importObject({childItemDone}); 659 660 var items = yield translate.translate(); 661 662 // Canonicalized tags after parent translator 663 assert.deepEqual([{tag: 'owl'}, {tag: 'tag'}], items[0].getTags()); 664 665 Zotero.Translators.get.restore(); 666 }); 667 }); 668 669 670 describe("#processDocuments()", function () { 671 var url = "http://127.0.0.1:23119/test/translate/test.html"; 672 var doc; 673 674 beforeEach(function* () { 675 // This is the main processDocuments, not the translation sandbox one being tested 676 doc = (yield Zotero.HTTP.processDocuments(url, doc => doc))[0]; 677 }); 678 679 it("should provide document object", async function () { 680 var translate = new Zotero.Translate.Web(); 681 translate.setDocument(doc); 682 translate.setTranslator( 683 buildDummyTranslator( 684 4, 685 `function detectWeb() {} 686 function doWeb(doc) { 687 ZU.processDocuments( 688 doc.location.href + '?t', 689 function (doc) { 690 var item = new Zotero.Item("book"); 691 item.title = "Container Item"; 692 // document.location 693 item.url = doc.location.href; 694 // document.evaluate() 695 item.extra = doc 696 .evaluate('//p', doc, null, XPathResult.ANY_TYPE, null) 697 .iterateNext() 698 .textContent; 699 item.attachments = [{ 700 document: doc, 701 title: "Snapshot from Document", 702 note: "attachment note", 703 tags: ${JSON.stringify(TEST_TAGS)} 704 }]; 705 item.complete(); 706 } 707 ); 708 }` 709 ) 710 ); 711 var newItems = await translate.translate(); 712 assert.equal(newItems.length, 1); 713 714 var item = newItems[0]; 715 assert.equal(item.getField('url'), url + '?t'); 716 assert.include(item.getField('extra'), 'your research sources'); 717 718 var containedAttachments = Zotero.Items.get(newItems[0].getAttachments()); 719 assert.equal(containedAttachments.length, 1); 720 721 var snapshot = containedAttachments[0]; 722 assert.equal(snapshot.getField("url"), url + '?t'); 723 assert.equal(snapshot.getNote(), "attachment note"); 724 assert.equal(snapshot.attachmentLinkMode, Zotero.Attachments.LINK_MODE_IMPORTED_URL); 725 assert.equal(snapshot.attachmentContentType, "text/html"); 726 checkTestTags(snapshot, true); 727 }); 728 729 it("should use loaded document instead of reloading if possible", function* () { 730 var translate = new Zotero.Translate.Web(); 731 translate.setDocument(doc); 732 translate.setTranslator( 733 buildDummyTranslator( 734 4, 735 `function detectWeb() {} 736 function doWeb(doc) { 737 ZU.processDocuments( 738 doc.location.href, 739 function (doc) { 740 var item = new Zotero.Item("book"); 741 item.title = "Container Item"; 742 // document.location 743 item.url = doc.location.href; 744 // document.evaluate() 745 item.extra = doc 746 .evaluate('//p', doc, null, XPathResult.ANY_TYPE, null) 747 .iterateNext() 748 .textContent; 749 item.attachments = [{ 750 document: doc, 751 title: "Snapshot from Document", 752 note: "attachment note", 753 tags: ${JSON.stringify(TEST_TAGS)} 754 }]; 755 item.complete(); 756 } 757 ); 758 }` 759 ) 760 ); 761 var newItems = yield translate.translate(); 762 assert.equal(newItems.length, 1); 763 764 var item = newItems[0]; 765 assert.equal(item.getField('url'), url); 766 assert.include(item.getField('extra'), 'your research sources'); 767 768 var containedAttachments = Zotero.Items.get(newItems[0].getAttachments()); 769 assert.equal(containedAttachments.length, 1); 770 771 var snapshot = containedAttachments[0]; 772 assert.equal(snapshot.getField("url"), url); 773 assert.equal(snapshot.getNote(), "attachment note"); 774 assert.equal(snapshot.attachmentLinkMode, Zotero.Attachments.LINK_MODE_IMPORTED_URL); 775 assert.equal(snapshot.attachmentContentType, "text/html"); 776 checkTestTags(snapshot, true); 777 }); 778 }); 779 780 781 describe("Translators", function () { 782 it("should round-trip child attachment via BibTeX", function* () { 783 var item = yield createDataObject('item'); 784 yield importFileAttachment('test.png', { parentItemID: item.id }); 785 786 var translation = new Zotero.Translate.Export(); 787 var tmpDir = yield getTempDirectory(); 788 var exportDir = OS.Path.join(tmpDir, 'export'); 789 translation.setLocation(Zotero.File.pathToFile(exportDir)); 790 translation.setItems([item]); 791 translation.setTranslator("9cb70025-a888-4a29-a210-93ec52da40d4"); 792 translation.setDisplayOptions({ 793 exportFileData: true 794 }); 795 yield translation.translate(); 796 797 var exportFile = OS.Path.join(exportDir, 'export.bib'); 798 assert.isTrue(yield OS.File.exists(exportFile)); 799 800 var translation = new Zotero.Translate.Import(); 801 translation.setLocation(Zotero.File.pathToFile(exportFile)); 802 var translators = yield translation.getTranslators(); 803 translation.setTranslator(translators[0]); 804 var importCollection = yield createDataObject('collection'); 805 var items = yield translation.translate({ 806 libraryID: Zotero.Libraries.userLibraryID, 807 collections: [importCollection.id] 808 }); 809 810 assert.lengthOf(items, 1); 811 var attachments = items[0].getAttachments(); 812 assert.lengthOf(attachments, 1); 813 var attachment = Zotero.Items.get(attachments[0]); 814 assert.isTrue(yield attachment.fileExists()); 815 }); 816 }); 817 818 819 describe("ItemSaver", function () { 820 describe("#saveCollections()", function () { 821 it("should add top-level collections to specified collection", function* () { 822 var collection = yield createDataObject('collection'); 823 var collections = [ 824 { 825 name: "Collection", 826 type: "collection", 827 children: [] 828 } 829 ]; 830 var items = [ 831 { 832 itemType: "book", 833 title: "Test" 834 } 835 ]; 836 837 var translation = new Zotero.Translate.Import(); 838 translation.setString(""); 839 translation.setTranslator(buildDummyTranslator( 840 "import", 841 "function detectImport() {}\n" 842 + "function doImport() {\n" 843 + " var json = JSON.parse('" + JSON.stringify(collections).replace(/['\\]/g, "\\$&") + "');\n" 844 + " for (let o of json) {" 845 + " var collection = new Zotero.Collection;\n" 846 + " for (let field in o) { collection[field] = o[field]; }\n" 847 + " collection.complete();\n" 848 + " }\n" 849 + " json = JSON.parse('" + JSON.stringify(items).replace(/['\\]/g, "\\$&") + "');\n" 850 + " for (let o of json) {" 851 + " var item = new Zotero.Item;\n" 852 + " for (let field in o) { item[field] = o[field]; }\n" 853 + " item.complete();\n" 854 + " }\n" 855 + "}" 856 )); 857 yield translation.translate({ 858 collections: [collection.id] 859 }); 860 assert.lengthOf(translation.newCollections, 1); 861 assert.isNumber(translation.newCollections[0].id); 862 assert.lengthOf(translation.newItems, 1); 863 assert.isNumber(translation.newItems[0].id); 864 var childCollections = Array.from(collection.getChildCollections(true)); 865 assert.sameMembers(childCollections, translation.newCollections.map(c => c.id)); 866 }); 867 }); 868 869 describe("#_saveAttachment()", function () { 870 it("should save standalone attachment to collection", function* () { 871 var collection = yield createDataObject('collection'); 872 var items = [ 873 { 874 itemType: "attachment", 875 title: "Test", 876 mimeType: "text/html", 877 url: "http://example.com" 878 } 879 ]; 880 881 var translation = new Zotero.Translate.Import(); 882 translation.setString(""); 883 translation.setTranslator(buildDummyTranslator( 884 "import", 885 "function detectImport() {}\n" 886 + "function doImport() {\n" 887 + " var json = JSON.parse('" + JSON.stringify(items).replace(/['\\]/g, "\\$&") + "');\n" 888 + " for (var i=0; i<json.length; i++) {" 889 + " var item = new Zotero.Item;\n" 890 + " for (var field in json[i]) { item[field] = json[i][field]; }\n" 891 + " item.complete();\n" 892 + " }\n" 893 + "}" 894 )); 895 yield translation.translate({ 896 collections: [collection.id] 897 }); 898 assert.lengthOf(translation.newItems, 1); 899 assert.isNumber(translation.newItems[0].id); 900 assert.ok(collection.hasItem(translation.newItems[0].id)); 901 }); 902 903 }); 904 describe('#saveItems', function() { 905 it("should deproxify item and attachment urls when proxy provided", function* (){ 906 var itemID; 907 var item = loadSampleData('journalArticle'); 908 item = item.journalArticle; 909 item.url = 'https://www-example-com.proxy.example.com/'; 910 item.attachments = [{ 911 url: 'https://www-example-com.proxy.example.com/pdf.pdf', 912 mimeType: 'application/pdf', 913 title: 'Example PDF'}]; 914 var itemSaver = new Zotero.Translate.ItemSaver({ 915 libraryID: Zotero.Libraries.userLibraryID, 916 attachmentMode: Zotero.Translate.ItemSaver.ATTACHMENT_MODE_FILE, 917 proxy: new Zotero.Proxy({scheme: 'https://%h.proxy.example.com/%p', dotsToHyphens: true}) 918 }); 919 var itemDeferred = Zotero.Promise.defer(); 920 var attachmentDeferred = Zotero.Promise.defer(); 921 itemSaver.saveItems([item], Zotero.Promise.coroutine(function* (attachment, progressPercentage) { 922 // ItemSaver returns immediately without waiting for attachments, so we use the callback 923 // to test attachments 924 if (progressPercentage != 100) return; 925 try { 926 yield itemDeferred.promise; 927 let item = Zotero.Items.get(itemID); 928 attachment = Zotero.Items.get(item.getAttachments()[0]); 929 assert.equal(attachment.getField('url'), 'https://www.example.com/pdf.pdf'); 930 attachmentDeferred.resolve(); 931 } catch (e) { 932 attachmentDeferred.reject(e); 933 } 934 })).then(function(items) { 935 try { 936 assert.equal(items[0].getField('url'), 'https://www.example.com/'); 937 itemID = items[0].id; 938 itemDeferred.resolve(); 939 } catch (e) { 940 itemDeferred.reject(e); 941 } 942 }); 943 yield Zotero.Promise.all([itemDeferred.promise, attachmentDeferred.promise]); 944 }); 945 }); 946 }); 947 948 949 describe("Error Handling", function () { 950 it("should propagate saveItems() errors from synchronous doImport()", function* () { 951 var items = [ 952 { 953 // Invalid object 954 }, 955 { 956 itemType: "book", 957 title: "B" 958 } 959 ]; 960 961 var added = 0; 962 var notifierID = Zotero.Notifier.registerObserver({ 963 notify: function (event, type, ids, extraData) { 964 added++; 965 } 966 }, ['item']); 967 968 var translation = new Zotero.Translate.Import(); 969 translation.setString(""); 970 translation.setTranslator(buildDummyTranslator( 971 "import", 972 "function detectImport() {}" 973 + "function doImport() {" 974 + " var json = JSON.parse('" + JSON.stringify(items).replace(/['\\]/g, "\\$&") + "');" 975 + " for (let o of json) {" 976 + " let item = new Zotero.Item;" 977 + " for (let field in o) { item[field] = o[field]; }" 978 + " item.complete();" 979 + " }" 980 + "}" 981 )); 982 var e = yield getPromiseError(translation.translate()); 983 Zotero.Notifier.unregisterObserver(notifierID); 984 assert.ok(e); 985 986 // Saving should be stopped without any saved items 987 assert.equal(added, 0); 988 assert.equal(translation._savingItems, 0); 989 assert.equal(translation._runningAsyncProcesses, 0); 990 assert.isNull(translation._currentState); 991 }); 992 993 it("should propagate saveItems() errors from asynchronous doImport()", function* () { 994 var items = [ 995 { 996 // Invalid object 997 }, 998 { 999 itemType: "book", 1000 title: "B" 1001 } 1002 ]; 1003 1004 var added = 0; 1005 var notifierID = Zotero.Notifier.registerObserver({ 1006 notify: function (event, type, ids, extraData) { 1007 added++; 1008 } 1009 }, ['item']); 1010 1011 var translation = new Zotero.Translate.Import(); 1012 translation.setString(""); 1013 translation.setTranslator(buildDummyTranslator( 1014 "import", 1015 "function detectImport() {}" 1016 + "function doImport() {" 1017 + " var json = JSON.parse('" + JSON.stringify(items).replace(/['\\]/g, "\\$&") + "');" 1018 + " return new Promise(function (resolve, reject) {" 1019 + " function next() {" 1020 + " var data = json.shift();" 1021 + " if (!data) {" 1022 + " resolve();" 1023 + " return;" 1024 + " }" 1025 + " var item = new Zotero.Item;" 1026 + " for (let field in data) { item[field] = data[field]; }" 1027 + " item.complete().then(next).catch(reject);" 1028 + " }" 1029 + " next();" 1030 + " });" 1031 + "}", 1032 { 1033 configOptions: { 1034 async: true 1035 } 1036 } 1037 )); 1038 var e = yield getPromiseError(translation.translate()); 1039 Zotero.Notifier.unregisterObserver(notifierID); 1040 assert.ok(e); 1041 1042 // Saving should be stopped without any saved items 1043 assert.equal(added, 0); 1044 assert.equal(translation._savingItems, 0); 1045 assert.equal(translation._runningAsyncProcesses, 0); 1046 assert.isNull(translation._currentState); 1047 }); 1048 1049 it("should propagate errors from saveItems with synchronous doSearch()", function* () { 1050 var stub = sinon.stub(Zotero.Translate.ItemSaver.prototype, "saveItems"); 1051 stub.returns(Zotero.Promise.reject(new Error("Save error"))); 1052 1053 var translation = new Zotero.Translate.Search(); 1054 translation.setTranslator(buildDummyTranslator( 1055 "search", 1056 "function detectSearch() {}" 1057 + "function doSearch() {" 1058 + " var item = new Zotero.Item('journalArticle');" 1059 + " item.itemType = 'book';" 1060 + " item.title = 'A';" 1061 + " item.complete();" 1062 + "}" 1063 )); 1064 translation.setSearch({ itemType: "journalArticle", DOI: "10.111/Test"}); 1065 var e = yield getPromiseError(translation.translate({ 1066 libraryID: Zotero.Libraries.userLibraryID, 1067 saveAttachments: false 1068 })); 1069 assert.ok(e); 1070 1071 stub.restore(); 1072 }); 1073 1074 it("should propagate errors from saveItems() with asynchronous doSearch()", function* () { 1075 var stub = sinon.stub(Zotero.Translate.ItemSaver.prototype, "saveItems"); 1076 stub.returns(Zotero.Promise.reject(new Error("Save error"))); 1077 1078 var translation = new Zotero.Translate.Search(); 1079 translation.setTranslator(buildDummyTranslator( 1080 "search", 1081 "function detectSearch() {}" 1082 + "function doSearch() {" 1083 + " var item = new Zotero.Item('journalArticle');" 1084 + " item.itemType = 'book';" 1085 + " item.title = 'A';" 1086 + " return new Promise(function (resolve, reject) {" 1087 + " item.complete().then(next).catch(reject);" 1088 + " });" 1089 + "}", 1090 { 1091 configOptions: { 1092 async: true 1093 } 1094 } 1095 )); 1096 translation.setSearch({ itemType: "journalArticle", DOI: "10.111/Test"}); 1097 var e = yield getPromiseError(translation.translate({ 1098 libraryID: Zotero.Libraries.userLibraryID, 1099 saveAttachments: false 1100 })); 1101 assert.ok(e); 1102 1103 stub.restore(); 1104 }); 1105 }); 1106 }); 1107 1108 describe("Zotero.Translate.ItemGetter", function() { 1109 describe("nextItem", function() { 1110 it('should return false for an empty database', Zotero.Promise.coroutine(function* () { 1111 let getter = new Zotero.Translate.ItemGetter(); 1112 assert.isFalse(getter.nextItem()); 1113 })); 1114 it('should return items in order they are supplied', Zotero.Promise.coroutine(function* () { 1115 let getter = new Zotero.Translate.ItemGetter(); 1116 let items, itemIDs, itemURIs; 1117 1118 yield Zotero.DB.executeTransaction(function* () { 1119 items = [ 1120 yield new Zotero.Item('journalArticle'), 1121 yield new Zotero.Item('book') 1122 ]; 1123 1124 itemIDs = [ yield items[0].save(), yield items[1].save() ]; 1125 itemURIs = items.map(i => Zotero.URI.getItemURI(i)); 1126 }); 1127 1128 getter._itemsLeft = items; 1129 1130 assert.equal((getter.nextItem()).uri, itemURIs[0], 'first item comes out first'); 1131 assert.equal((getter.nextItem()).uri, itemURIs[1], 'second item comes out second'); 1132 assert.isFalse((getter.nextItem()), 'end of item queue'); 1133 })); 1134 it('should return items with tags in expected format', Zotero.Promise.coroutine(function* () { 1135 let getter = new Zotero.Translate.ItemGetter(); 1136 let itemWithAutomaticTag, itemWithManualTag, itemWithMultipleTags 1137 1138 yield Zotero.DB.executeTransaction(function* () { 1139 itemWithAutomaticTag = new Zotero.Item('journalArticle'); 1140 itemWithAutomaticTag.addTag('automatic tag', 0); 1141 yield itemWithAutomaticTag.save(); 1142 1143 itemWithManualTag = new Zotero.Item('journalArticle'); 1144 itemWithManualTag.addTag('manual tag', 1); 1145 yield itemWithManualTag.save(); 1146 1147 itemWithMultipleTags = new Zotero.Item('journalArticle'); 1148 itemWithMultipleTags.addTag('tag1', 0); 1149 itemWithMultipleTags.addTag('tag2', 1); 1150 yield itemWithMultipleTags.save(); 1151 }); 1152 1153 let legacyMode = [false, true]; 1154 for (let i=0; i<legacyMode.length; i++) { 1155 getter._itemsLeft = [itemWithAutomaticTag, itemWithManualTag, itemWithMultipleTags]; 1156 getter.legacy = legacyMode[i]; 1157 let suffix = legacyMode[i] ? ' in legacy mode' : ''; 1158 1159 // itemWithAutomaticTag 1160 let translatorItem = getter.nextItem(); 1161 assert.isArray(translatorItem.tags, 'item contains automatic tags in an array' + suffix); 1162 assert.isObject(translatorItem.tags[0], 'automatic tag is an object' + suffix); 1163 assert.equal(translatorItem.tags[0].tag, 'automatic tag', 'automatic tag name provided as "tag" property' + suffix); 1164 if (legacyMode[i]) { 1165 assert.equal(translatorItem.tags[0].type, 0, 'automatic tag "type" is 0' + suffix); 1166 } else { 1167 assert.isUndefined(translatorItem.tags[0].type, '"type" is undefined for automatic tag' + suffix); 1168 } 1169 1170 // itemWithManualTag 1171 translatorItem = getter.nextItem(); 1172 assert.isArray(translatorItem.tags, 'item contains manual tags in an array' + suffix); 1173 assert.isObject(translatorItem.tags[0], 'manual tag is an object' + suffix); 1174 assert.equal(translatorItem.tags[0].tag, 'manual tag', 'manual tag name provided as "tag" property' + suffix); 1175 assert.equal(translatorItem.tags[0].type, 1, 'manual tag "type" is 1' + suffix); 1176 1177 // itemWithMultipleTags 1178 translatorItem = getter.nextItem(); 1179 assert.isArray(translatorItem.tags, 'item contains multiple tags in an array' + suffix); 1180 assert.lengthOf(translatorItem.tags, 2, 'expected number of tags returned' + suffix); 1181 } 1182 })); 1183 it('should return item collections in expected format', Zotero.Promise.coroutine(function* () { 1184 let getter = new Zotero.Translate.ItemGetter(); 1185 let items, collections; 1186 1187 yield Zotero.DB.executeTransaction(function* () { 1188 items = getter._itemsLeft = [ 1189 new Zotero.Item('journalArticle'), // Not in collection 1190 new Zotero.Item('journalArticle'), // In a single collection 1191 new Zotero.Item('journalArticle'), //In two collections 1192 new Zotero.Item('journalArticle') // In a nested collection 1193 ]; 1194 yield Zotero.Promise.all(items.map(item => item.save())); 1195 1196 collections = [ 1197 new Zotero.Collection, 1198 new Zotero.Collection, 1199 new Zotero.Collection, 1200 new Zotero.Collection 1201 ]; 1202 collections[0].name = "test1"; 1203 collections[1].name = "test2"; 1204 collections[2].name = "subTest1"; 1205 collections[3].name = "subTest2"; 1206 yield collections[0].save(); 1207 yield collections[1].save(); 1208 collections[2].parentID = collections[0].id; 1209 collections[3].parentID = collections[1].id; 1210 yield collections[2].save(); 1211 yield collections[3].save(); 1212 1213 yield collections[0].addItems([items[1].id, items[2].id]); 1214 yield collections[1].addItem(items[2].id); 1215 yield collections[2].addItem(items[3].id); 1216 }); 1217 1218 let translatorItem = getter.nextItem(); 1219 assert.isArray(translatorItem.collections, 'item in library root has a collections array'); 1220 assert.equal(translatorItem.collections.length, 0, 'item in library root does not list any collections'); 1221 1222 translatorItem = getter.nextItem(); 1223 assert.isArray(translatorItem.collections, 'item in a single collection has a collections array'); 1224 assert.equal(translatorItem.collections.length, 1, 'item in a single collection lists one collection'); 1225 assert.equal(translatorItem.collections[0], collections[0].key, 'item in a single collection identifies correct collection'); 1226 1227 translatorItem = getter.nextItem(); 1228 assert.isArray(translatorItem.collections, 'item in two collections has a collections array'); 1229 assert.equal(translatorItem.collections.length, 2, 'item in two collections lists two collections'); 1230 assert.deepEqual( 1231 translatorItem.collections.sort(), 1232 [collections[0].key, collections[1].key].sort(), 1233 'item in two collections identifies correct collections' 1234 ); 1235 1236 translatorItem = getter.nextItem(); 1237 assert.isArray(translatorItem.collections, 'item in a nested collection has a collections array'); 1238 assert.equal(translatorItem.collections.length, 1, 'item in a single nested collection lists one collection'); 1239 assert.equal(translatorItem.collections[0], collections[2].key, 'item in a single collection identifies correct collection'); 1240 })); 1241 1242 it('should return item relations in expected format', Zotero.Promise.coroutine(function* () { 1243 let getter = new Zotero.Translate.ItemGetter(); 1244 let items; 1245 1246 yield Zotero.DB.executeTransaction(function* () { 1247 items = [ 1248 new Zotero.Item('journalArticle'), // Item with no relations 1249 1250 new Zotero.Item('journalArticle'), // Bidirectional relations 1251 new Zotero.Item('journalArticle'), // between these items 1252 1253 new Zotero.Item('journalArticle'), // This item is related to two items below 1254 new Zotero.Item('journalArticle'), // But this item is not related to the item below 1255 new Zotero.Item('journalArticle') 1256 ]; 1257 yield Zotero.Promise.all(items.map(item => item.save())); 1258 1259 yield items[1].addRelatedItem(items[2]); 1260 yield items[2].addRelatedItem(items[1]); 1261 1262 yield items[3].addRelatedItem(items[4]); 1263 yield items[4].addRelatedItem(items[3]); 1264 yield items[3].addRelatedItem(items[5]); 1265 yield items[5].addRelatedItem(items[3]); 1266 }); 1267 1268 getter._itemsLeft = items.slice(); 1269 1270 let translatorItem = getter.nextItem(); 1271 assert.isObject(translatorItem.relations, 'item with no relations has a relations object'); 1272 assert.equal(Object.keys(translatorItem.relations).length, 0, 'item with no relations does not list any relations'); 1273 1274 translatorItem = getter.nextItem(); 1275 1276 assert.isObject(translatorItem.relations, 'item that is the subject of a single relation has a relations object'); 1277 assert.equal(Object.keys(translatorItem.relations).length, 1, 'item that is the subject of a single relation lists one relations predicate'); 1278 assert.lengthOf(translatorItem.relations['dc:relation'], 1, 'item that is the subject of a single relation lists one "dc:relation" object'); 1279 assert.equal(translatorItem.relations['dc:relation'][0], Zotero.URI.getItemURI(items[2]), 'item that is the subject of a single relation identifies correct object URI'); 1280 1281 // We currently assign these bidirectionally above, so this is a bit redundant 1282 translatorItem = getter.nextItem(); 1283 assert.isObject(translatorItem.relations, 'item that is the object of a single relation has a relations object'); 1284 assert.equal(Object.keys(translatorItem.relations).length, 1, 'item that is the object of a single relation list one relations predicate'); 1285 assert.lengthOf(translatorItem.relations['dc:relation'], 1, 'item that is the object of a single relation lists one "dc:relation" object'); 1286 assert.equal(translatorItem.relations['dc:relation'][0], Zotero.URI.getItemURI(items[1]), 'item that is the object of a single relation identifies correct subject URI'); 1287 1288 translatorItem = getter.nextItem(); 1289 assert.isObject(translatorItem.relations, 'item that is the subject of two relations has a relations object'); 1290 assert.equal(Object.keys(translatorItem.relations).length, 1, 'item that is the subject of two relations list one relations predicate'); 1291 assert.isDefined(translatorItem.relations['dc:relation'], 'item that is the subject of two relations uses "dc:relation" as the predicate'); 1292 assert.isArray(translatorItem.relations['dc:relation'], 'item that is the subject of two relations lists "dc:relation" object as an array'); 1293 assert.equal(translatorItem.relations['dc:relation'].length, 2, 'item that is the subject of two relations lists two relations in the "dc:relation" array'); 1294 assert.deepEqual( 1295 translatorItem.relations['dc:relation'].sort(), 1296 [Zotero.URI.getItemURI(items[4]), Zotero.URI.getItemURI(items[5])].sort(), 1297 'item that is the subject of two relations identifies correct object URIs' 1298 ); 1299 1300 translatorItem = getter.nextItem(); 1301 assert.isObject(translatorItem.relations, 'item that is the object of one relation from item with two relations has a relations object'); 1302 assert.equal(Object.keys(translatorItem.relations).length, 1, 'item that is the object of one relation from item with two relations list one relations predicate'); 1303 assert.isDefined(translatorItem.relations['dc:relation'], 'item that is the object of one relation from item with two relations uses "dc:relation" as the predicate'); 1304 assert.lengthOf(translatorItem.relations['dc:relation'], 1, 'item that is the object of one relation from item with two relations lists one "dc:relation" object'); 1305 assert.equal(translatorItem.relations['dc:relation'][0], Zotero.URI.getItemURI(items[3]), 'item that is the object of one relation from item with two relations identifies correct subject URI'); 1306 })); 1307 1308 it('should return standalone note in expected format', Zotero.Promise.coroutine(function* () { 1309 let relatedItem, note, collection; 1310 1311 yield Zotero.DB.executeTransaction(function* () { 1312 relatedItem = new Zotero.Item('journalArticle'); 1313 yield relatedItem.save(); 1314 1315 note = new Zotero.Item('note'); 1316 note.setNote('Note'); 1317 note.addTag('automaticTag', 0); 1318 note.addTag('manualTag', 1); 1319 note.addRelatedItem(relatedItem); 1320 yield note.save(); 1321 1322 relatedItem.addRelatedItem(note); 1323 yield relatedItem.save(); 1324 1325 collection = new Zotero.Collection; 1326 collection.name = 'test'; 1327 yield collection.save(); 1328 yield collection.addItem(note.id); 1329 }); 1330 1331 let legacyMode = [false, true]; 1332 for (let i=0; i<legacyMode.length; i++) { 1333 let getter = new Zotero.Translate.ItemGetter(); 1334 getter._itemsLeft = [note]; 1335 let legacy = getter.legacy = legacyMode[i]; 1336 let suffix = legacy ? ' in legacy mode' : ''; 1337 1338 let translatorNote = getter.nextItem(); 1339 assert.isDefined(translatorNote, 'returns standalone note' + suffix); 1340 assert.equal(translatorNote.itemType, 'note', 'itemType is correct' + suffix); 1341 assert.equal(translatorNote.note, 'Note', 'note is correct' + suffix); 1342 1343 assert.isString(translatorNote.dateAdded, 'dateAdded is string' + suffix); 1344 assert.isString(translatorNote.dateModified, 'dateModified is string' + suffix); 1345 1346 if (legacy) { 1347 assert.isTrue(sqlDateTimeRe.test(translatorNote.dateAdded), 'dateAdded is in correct format' + suffix); 1348 assert.isTrue(sqlDateTimeRe.test(translatorNote.dateModified), 'dateModified is in correct format' + suffix); 1349 1350 assert.isNumber(translatorNote.itemID, 'itemID is set' + suffix); 1351 assert.isString(translatorNote.key, 'key is set' + suffix); 1352 } else { 1353 assert.isTrue(isoDateTimeRe.test(translatorNote.dateAdded), 'dateAdded is in correct format' + suffix); 1354 assert.isTrue(isoDateTimeRe.test(translatorNote.dateModified), 'dateModified is in correct format' + suffix); 1355 } 1356 1357 // Tags 1358 assert.isArray(translatorNote.tags, 'contains tags as array' + suffix); 1359 assert.equal(translatorNote.tags.length, 2, 'contains correct number of tags' + suffix); 1360 let possibleTags = [ 1361 { tag: 'automaticTag', type: 0 }, 1362 { tag: 'manualTag', type: 1 } 1363 ]; 1364 for (let i=0; i<possibleTags.length; i++) { 1365 let match = false; 1366 for (let j=0; j<translatorNote.tags.length; j++) { 1367 if (possibleTags[i].tag == translatorNote.tags[j].tag) { 1368 let type = possibleTags[i].type; 1369 if (!legacy && type == 0) type = undefined; 1370 1371 assert.equal(translatorNote.tags[j].type, type, possibleTags[i].tag + ' tag is correct' + suffix); 1372 match = true; 1373 break; 1374 } 1375 } 1376 assert.isTrue(match, 'has ' + possibleTags[i].tag + ' tag ' + suffix); 1377 } 1378 1379 // Relations 1380 assert.isObject(translatorNote.relations, 'has relations as object' + suffix); 1381 assert.lengthOf(translatorNote.relations['dc:relation'], 1, 'has one relation' + suffix); 1382 assert.equal(translatorNote.relations['dc:relation'][0], Zotero.URI.getItemURI(relatedItem), 'relation is correct' + suffix); 1383 1384 if (!legacy) { 1385 // Collections 1386 assert.isArray(translatorNote.collections, 'has a collections array' + suffix); 1387 assert.equal(translatorNote.collections.length, 1, 'lists one collection' + suffix); 1388 assert.equal(translatorNote.collections[0], collection.key, 'identifies correct collection' + suffix); 1389 } 1390 } 1391 })); 1392 it('should return attached note in expected format', Zotero.Promise.coroutine(function* () { 1393 let relatedItem, items, collection, note; 1394 yield Zotero.DB.executeTransaction(function* () { 1395 relatedItem = new Zotero.Item('journalArticle'); 1396 yield relatedItem.save(); 1397 1398 items = [ 1399 new Zotero.Item('journalArticle'), 1400 new Zotero.Item('journalArticle') 1401 ]; 1402 yield Zotero.Promise.all(items.map(item => item.save())); 1403 1404 collection = new Zotero.Collection; 1405 collection.name = 'test'; 1406 yield collection.save(); 1407 yield collection.addItem(items[0].id); 1408 yield collection.addItem(items[1].id); 1409 1410 note = new Zotero.Item('note'); 1411 note.setNote('Note'); 1412 note.addTag('automaticTag', 0); 1413 note.addTag('manualTag', 1); 1414 yield note.save(); 1415 1416 note.addRelatedItem(relatedItem); 1417 relatedItem.addRelatedItem(note); 1418 yield note.save(); 1419 yield relatedItem.save(); 1420 }); 1421 1422 let legacyMode = [false, true]; 1423 for (let i=0; i<legacyMode.length; i++) { 1424 let item = items[i]; 1425 1426 let getter = new Zotero.Translate.ItemGetter(); 1427 getter._itemsLeft = [item]; 1428 let legacy = getter.legacy = legacyMode[i]; 1429 let suffix = legacy ? ' in legacy mode' : ''; 1430 1431 let translatorItem = getter.nextItem(); 1432 assert.isArray(translatorItem.notes, 'item with no notes contains notes array' + suffix); 1433 assert.equal(translatorItem.notes.length, 0, 'item with no notes contains empty notes array' + suffix); 1434 1435 note.parentID = item.id; 1436 yield note.saveTx(); 1437 1438 getter = new Zotero.Translate.ItemGetter(); 1439 getter._itemsLeft = [item]; 1440 getter.legacy = legacy; 1441 1442 translatorItem = getter.nextItem(); 1443 assert.isArray(translatorItem.notes, 'item with no notes contains notes array' + suffix); 1444 assert.equal(translatorItem.notes.length, 1, 'item with one note contains array with one note' + suffix); 1445 1446 let translatorNote = translatorItem.notes[0]; 1447 assert.equal(translatorNote.itemType, 'note', 'itemType is correct' + suffix); 1448 assert.equal(translatorNote.note, 'Note', 'note is correct' + suffix); 1449 1450 assert.isString(translatorNote.dateAdded, 'dateAdded is string' + suffix); 1451 assert.isString(translatorNote.dateModified, 'dateModified is string' + suffix); 1452 1453 if (legacy) { 1454 assert.isTrue(sqlDateTimeRe.test(translatorNote.dateAdded), 'dateAdded is in correct format' + suffix); 1455 assert.isTrue(sqlDateTimeRe.test(translatorNote.dateModified), 'dateModified is in correct format' + suffix); 1456 1457 assert.isNumber(translatorNote.itemID, 'itemID is set' + suffix); 1458 assert.isString(translatorNote.key, 'key is set' + suffix); 1459 } else { 1460 assert.isTrue(isoDateTimeRe.test(translatorNote.dateAdded), 'dateAdded is in correct format' + suffix); 1461 assert.isTrue(isoDateTimeRe.test(translatorNote.dateModified), 'dateModified is in correct format' + suffix); 1462 } 1463 1464 // Tags 1465 assert.isArray(translatorNote.tags, 'contains tags as array' + suffix); 1466 assert.equal(translatorNote.tags.length, 2, 'contains correct number of tags' + suffix); 1467 let possibleTags = [ 1468 { tag: 'automaticTag', type: 0 }, 1469 { tag: 'manualTag', type: 1 } 1470 ]; 1471 for (let i=0; i<possibleTags.length; i++) { 1472 let match = false; 1473 for (let j=0; j<translatorNote.tags.length; j++) { 1474 if (possibleTags[i].tag == translatorNote.tags[j].tag) { 1475 let type = possibleTags[i].type; 1476 if (!legacy && type == 0) type = undefined; 1477 1478 assert.equal(translatorNote.tags[j].type, type, possibleTags[i].tag + ' tag is correct' + suffix); 1479 match = true; 1480 break; 1481 } 1482 } 1483 assert.isTrue(match, 'has ' + possibleTags[i].tag + ' tag ' + suffix); 1484 } 1485 1486 // Relations 1487 assert.isObject(translatorNote.relations, 'has relations as object' + suffix); 1488 assert.lengthOf(translatorNote.relations['dc:relation'], 1, 'has one relation' + suffix); 1489 assert.equal(translatorNote.relations['dc:relation'][0], Zotero.URI.getItemURI(relatedItem), 'relation is correct' + suffix); 1490 1491 if (!legacy) { 1492 // Collections 1493 assert.isUndefined(translatorNote.collections, 'has no collections array' + suffix); 1494 } 1495 } 1496 })); 1497 1498 it('should return stored/linked file and URI attachments in expected format', Zotero.Promise.coroutine(function* () { 1499 this.timeout(60000); 1500 let file = getTestPDF(); 1501 let item, relatedItem; 1502 1503 yield Zotero.DB.executeTransaction(function* () { 1504 item = new Zotero.Item('journalArticle'); 1505 yield item.save(); 1506 relatedItem = new Zotero.Item('journalArticle'); 1507 yield relatedItem.save(); 1508 }); 1509 1510 // Attachment items 1511 let attachments = [ 1512 yield Zotero.Attachments.importFromFile({"file":file}), // Standalone stored file 1513 yield Zotero.Attachments.linkFromFile({"file":file}), // Standalone link to file 1514 yield Zotero.Attachments.importFromFile({"file":file, "parentItemID":item.id}), // Attached stored file 1515 yield Zotero.Attachments.linkFromFile({"file":file, "parentItemID":item.id}), // Attached link to file 1516 yield Zotero.Attachments.linkFromURL({"url":'http://example.com', "parentItemID":item.id, "contentType":'application/pdf', "title":'empty.pdf'}) // Attached link to URL 1517 ]; 1518 1519 yield Zotero.DB.executeTransaction(function* () { 1520 // Make sure all fields are populated 1521 for (let i=0; i<attachments.length; i++) { 1522 let attachment = attachments[i]; 1523 attachment.setField('accessDate', '2001-02-03 12:13:14'); 1524 attachment.attachmentCharset = 'utf-8'; 1525 attachment.setField('url', 'http://example.com'); 1526 attachment.setNote('note'); 1527 1528 attachment.addTag('automaticTag', 0); 1529 attachment.addTag('manualTag', 1); 1530 1531 attachment.addRelatedItem(relatedItem); 1532 1533 yield attachment.save(); 1534 1535 relatedItem.addRelatedItem(attachment); 1536 } 1537 1538 yield relatedItem.save(); 1539 }); 1540 1541 let items = [ attachments[0], attachments[1], item ]; // Standalone attachments and item with child attachments 1542 1543 // Run tests 1544 let legacyMode = [false, true]; 1545 for (let i=0; i<legacyMode.length; i++) { 1546 let getter = new Zotero.Translate.ItemGetter(); 1547 getter._itemsLeft = items.slice(); 1548 1549 let exportDir = yield getTempDirectory(); 1550 getter._exportFileDirectory = Components.classes["@mozilla.org/file/local;1"] 1551 .createInstance(Components.interfaces.nsILocalFile); 1552 getter._exportFileDirectory.initWithPath(exportDir); 1553 1554 let legacy = getter.legacy = legacyMode[i]; 1555 let suffix = legacy ? ' in legacy mode' : ''; 1556 1557 // Gather all standalone and child attachments into a single array, 1558 // since tests are mostly the same 1559 let translatorAttachments = [], translatorItem; 1560 let itemsLeft = items.length, attachmentsLeft = attachments.length; 1561 while (translatorItem = getter.nextItem()) { 1562 assert.isString(translatorItem.itemType, 'itemType is set' + suffix); 1563 1564 // Standalone attachments 1565 if (translatorItem.itemType == 'attachment') { 1566 translatorAttachments.push({ 1567 child: false, 1568 attachment: translatorItem 1569 }); 1570 attachmentsLeft--; 1571 1572 // Child attachments 1573 } else if (translatorItem.itemType == 'journalArticle') { 1574 assert.isArray(translatorItem.attachments, 'item contains attachment array' + suffix); 1575 assert.equal(translatorItem.attachments.length, 3, 'attachment array contains all items' + suffix); 1576 1577 for (let i=0; i<translatorItem.attachments.length; i++) { 1578 let attachment = translatorItem.attachments[i]; 1579 assert.equal(attachment.itemType, 'attachment', 'item attachment is of itemType "attachment"' + suffix); 1580 1581 translatorAttachments.push({ 1582 child: true, 1583 attachment: attachment 1584 }); 1585 1586 attachmentsLeft--; 1587 } 1588 1589 // Unexpected 1590 } else { 1591 assert.fail(translatorItem.itemType, 'attachment or journalArticle', 'expected itemType returned'); 1592 } 1593 1594 itemsLeft--; 1595 } 1596 1597 assert.equal(itemsLeft, 0, 'all items returned by getter'); 1598 assert.equal(attachmentsLeft, 0, 'all attachments returned by getter'); 1599 1600 // Since we make no guarantees on the order of child attachments, 1601 // we have to rely on URI as the identifier 1602 let uriMap = {}; 1603 for (let i=0; i<attachments.length; i++) { 1604 uriMap[Zotero.URI.getItemURI(attachments[i])] = attachments[i]; 1605 } 1606 1607 for (let j=0; j<translatorAttachments.length; j++) { 1608 let childAttachment = translatorAttachments[j].child; 1609 let attachment = translatorAttachments[j].attachment; 1610 assert.isString(attachment.uri, 'uri is set' + suffix); 1611 1612 let zoteroItem = uriMap[attachment.uri]; 1613 assert.isDefined(zoteroItem, 'uri is correct' + suffix); 1614 delete uriMap[attachment.uri]; 1615 1616 let storedFile = zoteroItem.attachmentLinkMode == Zotero.Attachments.LINK_MODE_IMPORTED_FILE 1617 || zoteroItem.attachmentLinkMode == Zotero.Attachments.LINK_MODE_IMPORTED_URL; 1618 let linkToURL = zoteroItem.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL; 1619 1620 let prefix = (childAttachment ? 'attached ' : '') 1621 + (storedFile ? 'stored ' : 'link to ') 1622 + (linkToURL ? 'URL ' : 'file '); 1623 1624 // Set fields 1625 assert.equal(attachment.itemType, 'attachment', prefix + 'itemType is correct' + suffix); 1626 assert.equal(attachment.title, 'empty.pdf', prefix + 'title is correct' + suffix); 1627 assert.equal(attachment.url, 'http://example.com', prefix + 'url is correct' + suffix); 1628 assert.equal(attachment.note, 'note', prefix + 'note is correct' + suffix); 1629 1630 // Automatically set fields 1631 assert.isString(attachment.dateAdded, prefix + 'dateAdded is set' + suffix); 1632 assert.isString(attachment.dateModified, prefix + 'dateModified is set' + suffix); 1633 1634 // Legacy mode fields 1635 if (legacy) { 1636 assert.isNumber(attachment.itemID, prefix + 'itemID is set' + suffix); 1637 assert.isString(attachment.key, prefix + 'key is set' + suffix); 1638 assert.equal(attachment.mimeType, 'application/pdf', prefix + 'mimeType is correct' + suffix); 1639 1640 assert.equal(attachment.accessDate, '2001-02-03 12:13:14', prefix + 'accessDate is correct' + suffix); 1641 1642 assert.isTrue(sqlDateTimeRe.test(attachment.dateAdded), prefix + 'dateAdded matches SQL format' + suffix); 1643 assert.isTrue(sqlDateTimeRe.test(attachment.dateModified), prefix + 'dateModified matches SQL format' + suffix); 1644 } else { 1645 assert.equal(attachment.contentType, 'application/pdf', prefix + 'contentType is correct' + suffix); 1646 1647 assert.equal(attachment.accessDate, '2001-02-03T12:13:14Z', prefix + 'accessDate is correct' + suffix); 1648 1649 assert.isTrue(isoDateTimeRe.test(attachment.dateAdded), prefix + 'dateAdded matches ISO-8601 format' + suffix); 1650 assert.isTrue(isoDateTimeRe.test(attachment.dateModified), prefix + 'dateModified matches ISO-8601 format' + suffix); 1651 } 1652 1653 if (!linkToURL) { 1654 // localPath 1655 assert.isString(attachment.localPath, prefix + 'localPath is set' + suffix); 1656 let attachmentFile = Components.classes["@mozilla.org/file/local;1"] 1657 .createInstance(Components.interfaces.nsILocalFile); 1658 attachmentFile.initWithPath(attachment.localPath); 1659 assert.isTrue(attachmentFile.exists(), prefix + 'localPath points to a file' + suffix); 1660 assert.isTrue(attachmentFile.equals(attachments[j].getFile()), prefix + 'localPath points to the correct file' + suffix); 1661 1662 assert.equal(attachment.filename, 'empty.pdf', prefix + 'filename is correct' + suffix); 1663 assert.equal(attachment.defaultPath, 'files/' + attachments[j].id + '/' + attachment.filename, prefix + 'defaultPath is correct' + suffix); 1664 1665 // saveFile function 1666 assert.isFunction(attachment.saveFile, prefix + 'has saveFile function' + suffix); 1667 attachment.saveFile(attachment.defaultPath); 1668 assert.equal(attachment.path, OS.Path.join(exportDir, OS.Path.normalize(attachment.defaultPath)), prefix + 'path is set correctly after saveFile call' + suffix); 1669 1670 let fileExists = yield OS.File.exists(attachment.path); 1671 assert.isTrue(fileExists, prefix + 'file was copied to the correct path by saveFile function' + suffix); 1672 fileExists = yield OS.File.exists(attachment.localPath); 1673 assert.isTrue(fileExists, prefix + 'file was not removed from original location' + suffix); 1674 1675 assert.throws(attachment.saveFile.bind(attachment, attachment.defaultPath), /^ERROR_FILE_EXISTS /, prefix + 'saveFile does not overwrite existing file by default' + suffix); 1676 assert.throws(attachment.saveFile.bind(attachment, 'file/../../'), /./, prefix + 'saveFile does not allow exporting outside export directory' + suffix); 1677 /** TODO: check if overwriting existing file works **/ 1678 } 1679 1680 // Tags 1681 assert.isArray(attachment.tags, prefix + 'contains tags as array' + suffix); 1682 assert.equal(attachment.tags.length, 2, prefix + 'contains correct number of tags' + suffix); 1683 let possibleTags = [ 1684 { tag: 'automaticTag', type: 0 }, 1685 { tag: 'manualTag', type: 1 } 1686 ]; 1687 for (let i=0; i<possibleTags.length; i++) { 1688 let match = false; 1689 for (let j=0; j<attachment.tags.length; j++) { 1690 if (possibleTags[i].tag == attachment.tags[j].tag) { 1691 let type = possibleTags[i].type; 1692 if (!legacy && type == 0) type = undefined; 1693 1694 assert.equal(attachment.tags[j].type, type, prefix + possibleTags[i].tag + ' tag is correct' + suffix); 1695 match = true; 1696 break; 1697 } 1698 } 1699 assert.isTrue(match, prefix + ' has ' + possibleTags[i].tag + ' tag ' + suffix); 1700 } 1701 1702 // Relations 1703 assert.isObject(attachment.relations, prefix + 'has relations as object' + suffix); 1704 assert.lengthOf(attachment.relations['dc:relation'], 1, prefix + 'has one relation' + suffix); 1705 assert.equal(attachment.relations['dc:relation'][0], Zotero.URI.getItemURI(relatedItem), prefix + 'relation is correct' + suffix); 1706 /** TODO: test other relations and multiple relations per predicate (should be an array) **/ 1707 } 1708 } 1709 })); 1710 }); 1711 1712 describe("#setCollection()", function () { 1713 it("should add collection items", function* () { 1714 var col = yield createDataObject('collection'); 1715 var item1 = yield createDataObject('item', { collections: [col.id] }); 1716 var item2 = yield createDataObject('item', { collections: [col.id] }); 1717 var item3 = yield createDataObject('item'); 1718 1719 let getter = new Zotero.Translate.ItemGetter(); 1720 getter.setCollection(col); 1721 1722 assert.equal(getter.numItems, 2); 1723 }); 1724 }); 1725 1726 describe("#_attachmentToArray()", function () { 1727 it("should handle missing attachment files", function* () { 1728 var item = yield importFileAttachment('test.png'); 1729 var path = item.getFilePath(); 1730 // Delete attachment file 1731 yield OS.File.remove(path); 1732 1733 var translation = new Zotero.Translate.Export(); 1734 var tmpDir = yield getTempDirectory(); 1735 var exportDir = OS.Path.join(tmpDir, 'export'); 1736 translation.setLocation(Zotero.File.pathToFile(exportDir)); 1737 translation.setItems([item]); 1738 translation.setTranslator('14763d24-8ba0-45df-8f52-b8d1108e7ac9'); // Zotero RDF 1739 translation.setDisplayOptions({ 1740 exportFileData: true 1741 }); 1742 yield translation.translate(); 1743 1744 var exportFile = OS.Path.join(exportDir, 'export.rdf'); 1745 assert.isAbove((yield OS.File.stat(exportFile)).size, 0); 1746 }); 1747 1748 it("should handle empty attachment path", function* () { 1749 var item = yield importFileAttachment('test.png'); 1750 item._attachmentPath = ''; 1751 assert.equal(item.attachmentPath, ''); 1752 1753 var translation = new Zotero.Translate.Export(); 1754 var tmpDir = yield getTempDirectory(); 1755 var exportDir = OS.Path.join(tmpDir, 'export'); 1756 translation.setLocation(Zotero.File.pathToFile(exportDir)); 1757 translation.setItems([item]); 1758 translation.setTranslator('14763d24-8ba0-45df-8f52-b8d1108e7ac9'); // Zotero RDF 1759 translation.setDisplayOptions({ 1760 exportFileData: true 1761 }); 1762 yield translation.translate(); 1763 1764 var exportFile = OS.Path.join(exportDir, 'export.rdf'); 1765 assert.isAbove((yield OS.File.stat(exportFile)).size, 0); 1766 }); 1767 }); 1768 }); 1769 }