2016-05-13 14:59:46 -04:00
|
|
|
"use strict";
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
let httpRequest = (method, url, options) => {
|
|
|
|
if (!options) {
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
if (!('errorDelayMax' in options)) {
|
|
|
|
options.errorDelayMax = 0;
|
|
|
|
}
|
|
|
|
return Zotero.HTTP.request(method, url, options);
|
|
|
|
}
|
|
|
|
|
2016-05-13 14:59:46 -04:00
|
|
|
describe("Connector Server", function () {
|
2025-06-19 08:01:52 +03:00
|
|
|
var { HttpServer } = ChromeUtils.import("chrome://remote/content/server/HTTPD.jsm");
|
2016-05-13 14:59:46 -04:00
|
|
|
var win, connectorServerPath, testServerPath, httpd;
|
|
|
|
var testServerPort = 16213;
|
2023-04-28 02:18:19 -04:00
|
|
|
var snapshotHTML = "<html><head><title>Title</title><body>Body</body></html>";
|
2016-05-13 14:59:46 -04:00
|
|
|
|
|
|
|
before(function* () {
|
2016-11-30 13:53:58 +02:00
|
|
|
this.timeout(20000);
|
2016-05-13 14:59:46 -04:00
|
|
|
Zotero.Prefs.set("httpServer.enabled", true);
|
|
|
|
yield resetDB({
|
|
|
|
thisArg: this,
|
|
|
|
skipBundledFiles: true
|
|
|
|
});
|
2016-11-30 13:53:58 +02:00
|
|
|
yield Zotero.Translators.init();
|
2016-05-13 14:59:46 -04:00
|
|
|
|
|
|
|
win = yield loadZoteroPane();
|
|
|
|
connectorServerPath = 'http://127.0.0.1:' + Zotero.Prefs.get('httpServer.port');
|
|
|
|
});
|
|
|
|
|
|
|
|
beforeEach(function () {
|
2016-06-23 16:15:48 +03:00
|
|
|
// Alternate ports to prevent exceptions not catchable in JS
|
|
|
|
testServerPort += (testServerPort & 1) ? 1 : -1;
|
|
|
|
testServerPath = 'http://127.0.0.1:' + testServerPort;
|
2016-05-13 14:59:46 -04:00
|
|
|
httpd = new HttpServer();
|
|
|
|
httpd.start(testServerPort);
|
2023-04-28 02:18:19 -04:00
|
|
|
|
|
|
|
httpd.registerPathHandler(
|
|
|
|
"/snapshot",
|
|
|
|
{
|
|
|
|
handle: function (request, response) {
|
|
|
|
response.setStatusLine(null, 200, "OK");
|
|
|
|
response.write(snapshotHTML);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
2016-05-13 14:59:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(function* () {
|
|
|
|
var defer = new Zotero.Promise.defer();
|
|
|
|
httpd.stop(() => defer.resolve());
|
|
|
|
yield defer.promise;
|
|
|
|
});
|
|
|
|
|
2016-05-23 01:19:44 -04:00
|
|
|
after(function () {
|
|
|
|
win.close();
|
|
|
|
});
|
2016-09-18 12:24:55 +03:00
|
|
|
|
|
|
|
|
|
|
|
describe('/connector/getTranslatorCode', function() {
|
|
|
|
it('should respond with translator code', function* () {
|
|
|
|
var code = 'function detectWeb() {}\nfunction doImport() {}';
|
|
|
|
var translator = buildDummyTranslator(4, code);
|
|
|
|
sinon.stub(Zotero.Translators, 'get').returns(translator);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
var response = yield httpRequest(
|
2016-09-18 12:24:55 +03:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/getTranslatorCode",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
translatorID: "dummy-translator",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.isTrue(Zotero.Translators.get.calledWith('dummy-translator'));
|
2021-07-26 13:14:56 +03:00
|
|
|
let translatorCode = yield Zotero.Translators.getCodeForTranslator(translator);
|
2016-12-12 14:29:59 +02:00
|
|
|
assert.equal(response.response, translatorCode);
|
2016-09-18 12:24:55 +03:00
|
|
|
|
|
|
|
Zotero.Translators.get.restore();
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
2016-05-23 01:19:44 -04:00
|
|
|
|
2016-12-12 14:29:59 +02:00
|
|
|
describe("/connector/detect", function() {
|
|
|
|
it("should return relevant translators with proxies", function* () {
|
|
|
|
var code = 'function detectWeb() {return "newspaperArticle";}\nfunction doWeb() {}';
|
|
|
|
var translator = buildDummyTranslator("web", code, {target: "https://www.example.com/.*"});
|
|
|
|
sinon.stub(Zotero.Translators, 'getAllForType').resolves([translator]);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
var response = yield httpRequest(
|
2016-12-12 14:29:59 +02:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/detect",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
uri: "https://www-example-com.proxy.example.com/article",
|
|
|
|
html: "<head><title>Owl</title></head><body><p>🦉</p></body>"
|
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(JSON.parse(response.response)[0].proxy.scheme, 'https://%h.proxy.example.com/%p');
|
|
|
|
|
|
|
|
Zotero.Translators.getAllForType.restore();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2016-05-13 14:59:46 -04:00
|
|
|
describe("/connector/saveItems", function () {
|
2016-05-20 15:51:54 -04:00
|
|
|
it("should save a translated item to the current selected collection", function* () {
|
2016-05-13 14:59:46 -04:00
|
|
|
var collection = yield createDataObject('collection');
|
2024-06-24 02:04:52 -04:00
|
|
|
yield select(win, collection);
|
2016-05-13 14:59:46 -04:00
|
|
|
|
|
|
|
var body = {
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
itemType: "newspaperArticle",
|
|
|
|
title: "Title",
|
|
|
|
creators: [
|
|
|
|
{
|
|
|
|
firstName: "First",
|
|
|
|
lastName: "Last",
|
|
|
|
creatorType: "author"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
}
|
|
|
|
],
|
|
|
|
uri: "http://example.com"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
var promise = waitForItemEvent('add');
|
2025-06-19 08:01:52 +03:00
|
|
|
var reqPromise = httpRequest(
|
2016-05-13 14:59:46 -04:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveItems",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify(body)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Check parent item
|
|
|
|
var ids = yield promise;
|
|
|
|
assert.lengthOf(ids, 1);
|
|
|
|
var item = Zotero.Items.get(ids[0]);
|
|
|
|
assert.equal(Zotero.ItemTypes.getName(item.itemTypeID), 'newspaperArticle');
|
|
|
|
assert.isTrue(collection.hasItem(item.id));
|
|
|
|
|
2017-02-09 02:17:09 -05:00
|
|
|
var req = yield reqPromise;
|
|
|
|
assert.equal(req.status, 201);
|
2016-05-13 14:59:46 -04:00
|
|
|
});
|
2016-06-09 02:44:47 -04:00
|
|
|
|
|
|
|
|
2018-04-27 18:27:06 -04:00
|
|
|
it("should switch to My Library if read-only library is selected", function* () {
|
2016-06-09 02:44:47 -04:00
|
|
|
var group = yield createGroup({
|
|
|
|
editable: false
|
|
|
|
});
|
2024-06-24 02:04:52 -04:00
|
|
|
yield select(win, group);
|
2016-06-09 02:44:47 -04:00
|
|
|
|
|
|
|
var body = {
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
itemType: "newspaperArticle",
|
|
|
|
title: "Title",
|
|
|
|
creators: [
|
|
|
|
{
|
|
|
|
firstName: "First",
|
|
|
|
lastName: "Last",
|
|
|
|
creatorType: "author"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
attachments: []
|
|
|
|
}
|
|
|
|
],
|
|
|
|
uri: "http://example.com"
|
|
|
|
};
|
|
|
|
|
2018-04-27 18:27:06 -04:00
|
|
|
var promise = waitForItemEvent('add');
|
2025-06-19 08:01:52 +03:00
|
|
|
var reqPromise = httpRequest(
|
2016-06-09 02:44:47 -04:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveItems",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
2017-07-19 11:55:41 +03:00
|
|
|
body: JSON.stringify(body),
|
|
|
|
successCodes: false
|
2016-06-09 02:44:47 -04:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2018-04-27 18:27:06 -04:00
|
|
|
// My Library be selected, and the item should be in it
|
|
|
|
var ids = yield promise;
|
2016-06-09 02:44:47 -04:00
|
|
|
assert.equal(
|
2018-04-27 18:27:06 -04:00
|
|
|
win.ZoteroPane.collectionsView.getSelectedLibraryID(),
|
|
|
|
Zotero.Libraries.userLibraryID
|
2016-06-09 02:44:47 -04:00
|
|
|
);
|
2018-04-27 18:27:06 -04:00
|
|
|
assert.lengthOf(ids, 1);
|
|
|
|
var item = Zotero.Items.get(ids[0]);
|
|
|
|
assert.equal(item.libraryID, Zotero.Libraries.userLibraryID);
|
|
|
|
assert.equal(Zotero.ItemTypes.getName(item.itemTypeID), 'newspaperArticle');
|
|
|
|
|
|
|
|
var req = yield reqPromise;
|
|
|
|
assert.equal(req.status, 201);
|
2016-06-09 02:44:47 -04:00
|
|
|
});
|
2016-12-12 14:29:59 +02:00
|
|
|
|
|
|
|
it("should use the provided proxy to deproxify item url", function* () {
|
|
|
|
yield selectLibrary(win, Zotero.Libraries.userLibraryID);
|
|
|
|
yield waitForItemsLoad(win);
|
|
|
|
|
|
|
|
var body = {
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
itemType: "newspaperArticle",
|
|
|
|
title: "Title",
|
|
|
|
creators: [
|
|
|
|
{
|
|
|
|
firstName: "First",
|
|
|
|
lastName: "Last",
|
|
|
|
creatorType: "author"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
attachments: [],
|
|
|
|
url: "https://www-example-com.proxy.example.com/path"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
uri: "https://www-example-com.proxy.example.com/path",
|
2022-11-10 12:46:14 +02:00
|
|
|
proxy: {scheme: 'https://%h.proxy.example.com/%p'}
|
2016-12-12 14:29:59 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
var promise = waitForItemEvent('add');
|
2025-06-19 08:01:52 +03:00
|
|
|
var req = yield httpRequest(
|
2016-12-12 14:29:59 +02:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveItems",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify(body)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Check item
|
|
|
|
var ids = yield promise;
|
|
|
|
assert.lengthOf(ids, 1);
|
|
|
|
var item = Zotero.Items.get(ids[0]);
|
|
|
|
assert.equal(item.getField('url'), 'https://www.example.com/path');
|
|
|
|
});
|
2016-05-13 14:59:46 -04:00
|
|
|
});
|
2020-07-17 15:14:10 -07:00
|
|
|
|
|
|
|
describe("/connector/saveSingleFile", function () {
|
|
|
|
it("should save a webpage item with /saveSnapshot", async function () {
|
|
|
|
var collection = await createDataObject('collection');
|
2024-06-24 02:04:52 -04:00
|
|
|
await select(win, collection);
|
2020-07-17 15:14:10 -07:00
|
|
|
|
|
|
|
// Promise for item save
|
|
|
|
let promise = waitForItemEvent('add');
|
|
|
|
|
|
|
|
let testDataDirectory = getTestDataDirectory().path;
|
|
|
|
let indexPath = OS.Path.join(testDataDirectory, 'snapshot', 'index.html');
|
|
|
|
|
|
|
|
let title = Zotero.Utilities.randomString();
|
|
|
|
let sessionID = Zotero.Utilities.randomString();
|
|
|
|
let payload = {
|
|
|
|
sessionID,
|
|
|
|
url: "http://example.com/test",
|
|
|
|
title,
|
|
|
|
};
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
await httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveSnapshot",
|
|
|
|
{
|
|
|
|
headers: {
|
2020-10-23 17:39:07 -06:00
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify(payload)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Await item save
|
|
|
|
let parentIDs = await promise;
|
|
|
|
|
|
|
|
// Check parent item
|
|
|
|
assert.lengthOf(parentIDs, 1);
|
|
|
|
var item = Zotero.Items.get(parentIDs[0]);
|
|
|
|
assert.equal(Zotero.ItemTypes.getName(item.itemTypeID), 'webpage');
|
|
|
|
assert.isTrue(collection.hasItem(item.id));
|
|
|
|
assert.equal(item.getField('title'), title);
|
|
|
|
|
|
|
|
// Promise for attachment save
|
|
|
|
promise = waitForItemEvent('add');
|
|
|
|
|
|
|
|
let body = JSON.stringify(Object.assign(payload, {
|
|
|
|
snapshotContent: await Zotero.File.getContentsAsync(indexPath)
|
|
|
|
}));
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
await httpRequest(
|
2020-10-23 17:39:07 -06:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveSingleFile",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Await attachment save
|
|
|
|
let attachmentIDs = await promise;
|
|
|
|
|
|
|
|
// Check attachment
|
|
|
|
assert.lengthOf(attachmentIDs, 1);
|
|
|
|
item = Zotero.Items.get(attachmentIDs[0]);
|
|
|
|
assert.isTrue(item.isImportedAttachment());
|
|
|
|
assert.equal(item.getField('title'), title);
|
|
|
|
|
|
|
|
// Check attachment html file
|
|
|
|
let attachmentDirectory = Zotero.Attachments.getStorageDirectory(item).path;
|
2025-06-19 08:01:52 +03:00
|
|
|
let path = OS.Path.join(attachmentDirectory, item.attachmentFilename);
|
2020-10-23 17:39:07 -06:00
|
|
|
assert.isTrue(await OS.File.exists(path));
|
|
|
|
let contents = await Zotero.File.getContentsAsync(path);
|
|
|
|
let expectedContents = await Zotero.File.getContentsAsync(indexPath);
|
|
|
|
assert.equal(contents, expectedContents);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("should save a webpage item with /saveItems", async function () {
|
|
|
|
let collection = await createDataObject('collection');
|
2024-06-24 02:04:52 -04:00
|
|
|
await select(win, collection);
|
2020-10-23 17:39:07 -06:00
|
|
|
|
|
|
|
let title = Zotero.Utilities.randomString();
|
|
|
|
let sessionID = Zotero.Utilities.randomString();
|
|
|
|
let payload = {
|
|
|
|
sessionID: sessionID,
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
itemType: "newspaperArticle",
|
|
|
|
title: title,
|
|
|
|
creators: [
|
|
|
|
{
|
|
|
|
firstName: "First",
|
|
|
|
lastName: "Last",
|
|
|
|
creatorType: "author"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
uri: "http://example.com"
|
|
|
|
};
|
|
|
|
|
|
|
|
let promise = waitForItemEvent('add');
|
2025-06-19 08:01:52 +03:00
|
|
|
let req = await httpRequest(
|
2020-10-23 17:39:07 -06:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveItems",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify(payload)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(req.status, 201);
|
|
|
|
|
|
|
|
// Check parent item
|
|
|
|
let itemIDs = await promise;
|
|
|
|
assert.lengthOf(itemIDs, 1);
|
|
|
|
let item = Zotero.Items.get(itemIDs[0]);
|
|
|
|
assert.equal(Zotero.ItemTypes.getName(item.itemTypeID), 'newspaperArticle');
|
|
|
|
assert.isTrue(collection.hasItem(item.id));
|
|
|
|
|
|
|
|
// Promise for attachment save
|
|
|
|
promise = waitForItemEvent('add');
|
|
|
|
|
|
|
|
let testDataDirectory = getTestDataDirectory().path;
|
|
|
|
let indexPath = OS.Path.join(testDataDirectory, 'snapshot', 'index.html');
|
|
|
|
|
|
|
|
let body = JSON.stringify(Object.assign(payload, {
|
2025-06-19 08:01:52 +03:00
|
|
|
url: `${testServerPath}/attachment`,
|
2020-10-23 17:39:07 -06:00
|
|
|
snapshotContent: await Zotero.File.getContentsAsync(indexPath)
|
|
|
|
}));
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
req = await httpRequest(
|
2020-10-23 17:39:07 -06:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveSingleFile",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(req.status, 201);
|
|
|
|
|
|
|
|
// Await attachment save
|
|
|
|
let attachmentIDs = await promise;
|
|
|
|
|
|
|
|
// Check attachment
|
|
|
|
assert.lengthOf(attachmentIDs, 1);
|
|
|
|
item = Zotero.Items.get(attachmentIDs[0]);
|
|
|
|
assert.isTrue(item.isImportedAttachment());
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(item.getField('title'), 'Test');
|
2020-10-23 17:39:07 -06:00
|
|
|
|
|
|
|
// Check attachment html file
|
|
|
|
let attachmentDirectory = Zotero.Attachments.getStorageDirectory(item).path;
|
2025-06-19 08:01:52 +03:00
|
|
|
let path = OS.Path.join(attachmentDirectory, item.attachmentFilename);
|
2020-10-23 17:39:07 -06:00
|
|
|
assert.isTrue(await OS.File.exists(path));
|
|
|
|
let contents = await Zotero.File.getContentsAsync(path);
|
|
|
|
let expectedContents = await Zotero.File.getContentsAsync(indexPath);
|
|
|
|
assert.equal(contents, expectedContents);
|
|
|
|
});
|
2025-06-19 08:01:52 +03:00
|
|
|
});
|
2020-10-23 17:39:07 -06:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
describe("/connector/saveSnapshot", function () {
|
|
|
|
it("should save a webpage item to the current selected collection", function* () {
|
|
|
|
var collection = yield createDataObject('collection');
|
|
|
|
yield select(win, collection);
|
2020-10-23 17:39:07 -06:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
// saveSnapshot saves parent and child before returning
|
|
|
|
var ids;
|
|
|
|
var promise = waitForItemEvent('add').then(function (_ids) {
|
|
|
|
ids = _ids;
|
|
|
|
});
|
2020-10-23 17:39:07 -06:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
var file = getTestDataDirectory();
|
|
|
|
file.append('snapshot');
|
|
|
|
file.append('index.html');
|
|
|
|
httpd.registerFile("/test", file);
|
2020-10-23 17:39:07 -06:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
yield httpRequest(
|
2020-10-23 17:39:07 -06:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveSnapshot",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
2020-07-17 15:14:10 -07:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: JSON.stringify({
|
|
|
|
url: `${testServerPath}/test`,
|
|
|
|
title: "Title"
|
|
|
|
})
|
2020-07-17 15:14:10 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.isTrue(promise.isFulfilled());
|
2020-07-17 15:14:10 -07:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
// Check item
|
|
|
|
assert.lengthOf(ids, 1);
|
|
|
|
var item = Zotero.Items.get(ids[0]);
|
2020-07-17 15:14:10 -07:00
|
|
|
assert.equal(Zotero.ItemTypes.getName(item.itemTypeID), 'webpage');
|
|
|
|
assert.isTrue(collection.hasItem(item.id));
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(item.getField('title'), 'Title');
|
|
|
|
});
|
2020-07-17 15:14:10 -07:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
it("should switch to My Library if a read-only library is selected", function* () {
|
|
|
|
var group = yield createGroup({
|
|
|
|
editable: false
|
|
|
|
});
|
|
|
|
yield select(win, group);
|
|
|
|
|
|
|
|
var promise = waitForItemEvent('add');
|
|
|
|
var reqPromise = httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
connectorServerPath + "/connector/saveSnapshot",
|
2020-07-17 15:14:10 -07:00
|
|
|
{
|
|
|
|
headers: {
|
2025-06-19 08:01:52 +03:00
|
|
|
"Content-Type": "application/json"
|
2020-07-17 15:14:10 -07:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: JSON.stringify({
|
|
|
|
url: testServerPath + '/snapshot',
|
|
|
|
html: snapshotHTML
|
|
|
|
}),
|
|
|
|
successCodes: false
|
2020-07-17 15:14:10 -07:00
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
// My Library be selected, and the item should be in it
|
|
|
|
var ids = yield promise;
|
|
|
|
assert.equal(
|
|
|
|
win.ZoteroPane.collectionsView.getSelectedLibraryID(),
|
|
|
|
Zotero.Libraries.userLibraryID
|
2020-07-17 15:14:10 -07:00
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.lengthOf(ids, 1);
|
|
|
|
var item = Zotero.Items.get(ids[0]);
|
|
|
|
assert.equal(item.libraryID, Zotero.Libraries.userLibraryID);
|
|
|
|
|
|
|
|
var req = yield reqPromise;
|
2020-07-17 15:14:10 -07:00
|
|
|
assert.equal(req.status, 201);
|
2025-06-19 08:01:52 +03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("/connector/saveAttachment", function () {
|
|
|
|
const pdfPath = OS.Path.join(getTestDataDirectory().path, 'test.pdf');
|
|
|
|
let pdfSample, pdfArrayBuffer;
|
|
|
|
before(async function () {
|
|
|
|
await selectLibrary(win, Zotero.Libraries.userLibraryID);
|
|
|
|
pdfSample = await Zotero.File.getSample(pdfPath);
|
|
|
|
pdfArrayBuffer = (await OS.File.read(pdfPath)).buffer;
|
2020-07-17 15:14:10 -07:00
|
|
|
});
|
2020-12-27 01:31:30 -07:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
it("should save a child item attachment to the specified parent item", async function () {
|
|
|
|
// First, save multiple items
|
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
|
|
|
const bookItemID = Zotero.Utilities.randomString();
|
|
|
|
const articleItemID = Zotero.Utilities.randomString();
|
|
|
|
const body = {
|
2020-12-27 01:31:30 -07:00
|
|
|
sessionID,
|
|
|
|
items: [
|
|
|
|
{
|
2025-06-19 08:01:52 +03:00
|
|
|
id: bookItemID,
|
|
|
|
itemType: "book",
|
|
|
|
title: "Book Title",
|
2020-12-27 01:31:30 -07:00
|
|
|
},
|
|
|
|
{
|
2025-06-19 08:01:52 +03:00
|
|
|
id: articleItemID,
|
|
|
|
itemType: "journalArticle",
|
|
|
|
title: "Article Title",
|
2020-12-27 01:31:30 -07:00
|
|
|
}
|
2025-06-19 08:01:52 +03:00
|
|
|
]
|
|
|
|
};
|
2016-06-09 02:44:47 -04:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
let itemAddPromise = waitForItemEvent('add');
|
|
|
|
let saveItemsReq = await httpRequest(
|
2016-06-09 02:44:47 -04:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
connectorServerPath + "/connector/saveItems",
|
2016-06-09 02:44:47 -04:00
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: JSON.stringify(body)
|
|
|
|
}
|
2016-06-09 02:44:47 -04:00
|
|
|
);
|
2018-04-27 18:27:06 -04:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(saveItemsReq.status, 201);
|
|
|
|
let itemIDs = await itemAddPromise;
|
|
|
|
let bookItem = Zotero.Items.get(itemIDs[0]);
|
|
|
|
let articleItem = Zotero.Items.get(itemIDs[1]);
|
|
|
|
assert.equal(bookItem.numAttachments(), 0);
|
|
|
|
assert.equal(articleItem.numAttachments(), 0);
|
|
|
|
|
|
|
|
// Now save an attachment to the first parent item (book)
|
|
|
|
let attachmentAddPromise = waitForItemEvent('add');
|
|
|
|
let attachmentReq = await httpRequest(
|
2017-02-21 14:26:14 +02:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
connectorServerPath + "/connector/saveAttachment",
|
2017-02-21 14:26:14 +02:00
|
|
|
{
|
|
|
|
headers: {
|
2025-06-19 08:01:52 +03:00
|
|
|
"Content-Type": "application/pdf",
|
|
|
|
"X-Metadata": JSON.stringify({
|
|
|
|
sessionID,
|
|
|
|
title: "Book Attachment",
|
|
|
|
parentItemID: bookItemID,
|
|
|
|
url: `${testServerPath}/attachment1.pdf`,
|
|
|
|
})
|
2017-02-21 14:26:14 +02:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: pdfArrayBuffer
|
2017-02-21 14:26:14 +02:00
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
assert.equal(attachmentReq.status, 201);
|
|
|
|
let attachmentIds = await attachmentAddPromise;
|
|
|
|
assert.lengthOf(attachmentIds, 1);
|
|
|
|
let attachment1 = Zotero.Items.get(attachmentIds[0]);
|
|
|
|
assert.equal(bookItem.numAttachments(), 1);
|
|
|
|
assert.equal(articleItem.numAttachments(), 0);
|
|
|
|
|
|
|
|
// Verify attachment was saved correctly
|
|
|
|
assert.equal(attachment1.parentItemID, bookItem.id);
|
|
|
|
assert.equal(attachment1.getField('title'), "Book Attachment");
|
|
|
|
assert.isTrue(attachment1.isPDFAttachment());
|
|
|
|
|
|
|
|
|
|
|
|
// Save a second attachment to the second parent item (article)
|
|
|
|
attachmentAddPromise = waitForItemEvent('add');
|
|
|
|
attachmentReq = await httpRequest(
|
2017-02-21 14:26:14 +02:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
connectorServerPath + "/connector/saveAttachment",
|
2017-02-21 14:26:14 +02:00
|
|
|
{
|
|
|
|
headers: {
|
2025-06-19 08:01:52 +03:00
|
|
|
"Content-Type": "application/pdf",
|
|
|
|
"X-Metadata": JSON.stringify({
|
|
|
|
sessionID,
|
|
|
|
title: "Article Attachment",
|
|
|
|
parentItemID: articleItemID,
|
|
|
|
url: `${testServerPath}/attachment2.pdf`,
|
|
|
|
})
|
2017-02-21 14:26:14 +02:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: pdfArrayBuffer
|
2017-02-21 14:26:14 +02:00
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
assert.equal(attachmentReq.status, 201);
|
|
|
|
attachmentIds = await attachmentAddPromise;
|
|
|
|
assert.lengthOf(attachmentIds, 1);
|
|
|
|
var attachment2 = Zotero.Items.get(attachmentIds[0]);
|
|
|
|
|
|
|
|
// Verify second attachment was saved correctly
|
|
|
|
assert.equal(attachment2.parentItemID, articleItem.id);
|
|
|
|
assert.equal(attachment2.getField('title'), "Article Attachment");
|
|
|
|
assert.isTrue(attachment2.isPDFAttachment());
|
|
|
|
|
|
|
|
assert.equal(bookItem.numAttachments(), 1);
|
|
|
|
assert.equal(articleItem.numAttachments(), 1);
|
2017-02-21 14:26:14 +02:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
// Verify attachment content
|
|
|
|
let attachmentDirectory = Zotero.Attachments.getStorageDirectory(attachment1).path;
|
|
|
|
let path = OS.Path.join(attachmentDirectory, attachment1.attachmentFilename);
|
|
|
|
assert.isTrue(await OS.File.exists(path));
|
|
|
|
let contents = await Zotero.File.getSample(path);
|
|
|
|
assert.equal(contents, pdfSample);
|
2017-02-21 14:26:14 +02:00
|
|
|
});
|
|
|
|
});
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
describe("/connector/hasAttachmentResolvers", function () {
|
|
|
|
it("should respond with 'true' if the item has OA attachments", async function () {
|
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
|
|
|
const itemID = Zotero.Utilities.randomString();
|
|
|
|
const body = {
|
2018-02-06 01:40:38 -05:00
|
|
|
sessionID,
|
|
|
|
items: [
|
|
|
|
{
|
2025-06-19 08:01:52 +03:00
|
|
|
id: itemID,
|
|
|
|
itemType: "journalArticle",
|
|
|
|
title: "Test Article with DOI",
|
|
|
|
DOI: "10.1234/example.doi",
|
2018-02-06 01:40:38 -05:00
|
|
|
}
|
2025-06-19 08:01:52 +03:00
|
|
|
]
|
2018-02-06 01:40:38 -05:00
|
|
|
};
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
let response = await httpRequest(
|
|
|
|
"POST",
|
|
|
|
connectorServerPath + "/connector/saveItems",
|
2018-02-06 01:40:38 -05:00
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify(body)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(response.status, 201);
|
2018-02-06 01:40:38 -05:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
response = await httpRequest(
|
|
|
|
"POST",
|
|
|
|
connectorServerPath + "/connector/hasAttachmentResolvers",
|
2018-02-06 01:40:38 -05:00
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
2025-06-19 08:01:52 +03:00
|
|
|
itemID
|
|
|
|
}),
|
2018-02-06 01:40:38 -05:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(response.status, 200);
|
|
|
|
assert.isTrue(JSON.parse(response.responseText));
|
2018-02-06 01:40:38 -05:00
|
|
|
});
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
it("should respond with 'false' if the item has no OA attachments", async function () {
|
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
|
|
|
const itemID = Zotero.Utilities.randomString();
|
|
|
|
const body = {
|
|
|
|
sessionID,
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
id: itemID,
|
|
|
|
itemType: "journalArticle",
|
|
|
|
title: "Test Article",
|
|
|
|
}
|
|
|
|
]
|
|
|
|
};
|
2018-02-06 01:40:38 -05:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
let response = await httpRequest(
|
|
|
|
"POST",
|
|
|
|
connectorServerPath + "/connector/saveItems",
|
2018-02-06 01:40:38 -05:00
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: JSON.stringify(body)
|
2018-02-06 01:40:38 -05:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(response.status, 201);
|
2018-02-06 01:40:38 -05:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
response = await httpRequest(
|
|
|
|
"POST",
|
|
|
|
connectorServerPath + "/connector/hasAttachmentResolvers",
|
2018-02-06 01:40:38 -05:00
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
2025-06-19 08:01:52 +03:00
|
|
|
itemID
|
|
|
|
}),
|
2018-02-06 01:40:38 -05:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(response.status, 200);
|
|
|
|
assert.isFalse(JSON.parse(response.responseText));
|
2018-02-06 01:40:38 -05:00
|
|
|
});
|
2025-06-19 08:01:52 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
describe("/connector/saveAttachmentFromResolver", function () {
|
|
|
|
it("should save an OA attachment for the specified item and return 201 if OA attachment is available", async function () {
|
|
|
|
let stub = sinon.stub(Zotero.Attachments, 'addFileFromURLs').returns({
|
|
|
|
id: Zotero.Utilities.randomString(),
|
|
|
|
getDisplayTitle: () => "OA Attachment"
|
2018-02-06 01:40:38 -05:00
|
|
|
});
|
2025-06-19 08:01:52 +03:00
|
|
|
try {
|
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
|
|
|
const itemID = Zotero.Utilities.randomString();
|
|
|
|
const body = {
|
|
|
|
sessionID,
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
id: itemID,
|
|
|
|
itemType: "journalArticle",
|
|
|
|
title: "Test Article with DOI",
|
|
|
|
DOI: "10.1234/example.doi",
|
|
|
|
}
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
|
|
|
let response = await httpRequest(
|
|
|
|
"POST",
|
|
|
|
connectorServerPath + "/connector/saveItems",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify(body)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(response.status, 201);
|
|
|
|
|
|
|
|
response = await httpRequest(
|
|
|
|
"POST",
|
|
|
|
connectorServerPath + "/connector/saveAttachmentFromResolver",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
|
|
|
itemID
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(response.status, 201);
|
|
|
|
assert.equal(response.responseText, "OA Attachment");
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
stub.restore();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it("should return 500 if OA attachment is not available", async function () {
|
|
|
|
let stub = sinon.stub(Zotero.Attachments, 'addFileFromURLs').returns(null);
|
|
|
|
try {
|
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
|
|
|
const itemID = Zotero.Utilities.randomString();
|
|
|
|
const body = {
|
|
|
|
sessionID,
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
id: itemID,
|
|
|
|
itemType: "journalArticle",
|
|
|
|
title: "Test Article with DOI",
|
|
|
|
DOI: "10.1234/example.doi",
|
|
|
|
}
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
|
|
|
let response = await httpRequest(
|
|
|
|
"POST",
|
|
|
|
connectorServerPath + "/connector/saveItems",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify(body)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(response.status, 201);
|
|
|
|
|
|
|
|
response = await httpRequest(
|
|
|
|
"POST",
|
|
|
|
connectorServerPath + "/connector/saveAttachmentFromResolver",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
successCodes: false,
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
|
|
|
itemID
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(response.status, 500);
|
|
|
|
assert.equal(response.responseText, "Failed to save an attachment");
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
stub.restore();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe("/connector/saveStandaloneAttachment", function () {
|
|
|
|
before(async function () {
|
|
|
|
await selectLibrary(win, Zotero.Libraries.userLibraryID);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("should save a standalone PDF attachment", async function () {
|
|
|
|
const pdfPath = OS.Path.join(getTestDataDirectory().path, 'test.pdf');
|
|
|
|
const pdfSample = await Zotero.File.getSample(pdfPath);
|
|
|
|
const pdfArrayBuffer = (await OS.File.read(pdfPath)).buffer;
|
|
|
|
const attachmentInfo = {
|
|
|
|
url: `${testServerPath}/test1.pdf`,
|
|
|
|
title: "Test PDF1",
|
|
|
|
contentType: "application/pdf",
|
|
|
|
sessionID: Zotero.Utilities.randomString()
|
|
|
|
};
|
|
|
|
let itemIDsPromise = waitForItemEvent('add');
|
|
|
|
let xhr = await httpRequest(
|
2018-02-06 01:40:38 -05:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
connectorServerPath + "/connector/saveStandaloneAttachment",
|
2018-02-06 01:40:38 -05:00
|
|
|
{
|
|
|
|
headers: {
|
2025-06-19 08:01:52 +03:00
|
|
|
"Content-Type": attachmentInfo.contentType,
|
|
|
|
"X-Metadata": JSON.stringify(attachmentInfo)
|
2018-02-06 01:40:38 -05:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: pdfArrayBuffer
|
2018-02-06 01:40:38 -05:00
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
assert.equal(xhr.status, 201);
|
|
|
|
assert.isTrue(JSON.parse(xhr.responseText).canRecognize);
|
|
|
|
let itemIDs = await itemIDsPromise;
|
|
|
|
let item = Zotero.Items.get(itemIDs[0]);
|
2018-02-06 01:40:38 -05:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(item.itemType, "attachment");
|
|
|
|
assert.equal(item.attachmentContentType, attachmentInfo.contentType);
|
|
|
|
assert.equal(item.getField("title"), attachmentInfo.title);
|
|
|
|
assert.equal(item.getField("url"), attachmentInfo.url);
|
|
|
|
// Check content
|
|
|
|
let attachmentDirectory = Zotero.Attachments.getStorageDirectory(item).path;
|
|
|
|
let path = OS.Path.join(attachmentDirectory, item.attachmentFilename);
|
|
|
|
assert.isTrue(await OS.File.exists(path));
|
|
|
|
let contents = await Zotero.File.getSample(path);
|
|
|
|
assert.equal(contents, pdfSample);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("should save a standalone image attachment", async function () {
|
|
|
|
const imagePath = OS.Path.join(getTestDataDirectory().path, 'test.png');
|
|
|
|
const imageSample = await Zotero.File.getSample(imagePath);
|
|
|
|
const imageArrayBuffer = (await OS.File.read(imagePath)).buffer;
|
|
|
|
const attachmentInfo = {
|
|
|
|
url: `${testServerPath}/test.png`,
|
|
|
|
title: "Test PNG",
|
|
|
|
contentType: "image/png",
|
|
|
|
sessionID: Zotero.Utilities.randomString()
|
|
|
|
};
|
2018-02-06 01:40:38 -05:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
let itemIDsPromise = waitForItemEvent('add');
|
|
|
|
let xhr = await httpRequest(
|
2018-02-06 01:40:38 -05:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
connectorServerPath + "/connector/saveStandaloneAttachment",
|
2018-02-06 01:40:38 -05:00
|
|
|
{
|
|
|
|
headers: {
|
2025-06-19 08:01:52 +03:00
|
|
|
"Content-Type": attachmentInfo.contentType,
|
|
|
|
"X-Metadata": JSON.stringify(attachmentInfo)
|
2018-02-06 01:40:38 -05:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: imageArrayBuffer
|
2018-02-06 01:40:38 -05:00
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
assert.equal(xhr.status, 201);
|
|
|
|
assert.isFalse(JSON.parse(xhr.responseText).canRecognize);
|
|
|
|
let itemIDs = await itemIDsPromise;
|
|
|
|
let item = Zotero.Items.get(itemIDs[0]);
|
2018-02-06 01:40:38 -05:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(item.itemType, "attachment");
|
|
|
|
assert.equal(item.attachmentContentType, attachmentInfo.contentType);
|
|
|
|
assert.equal(item.getField("title"), attachmentInfo.title);
|
|
|
|
assert.equal(item.getField("url"), attachmentInfo.url);
|
|
|
|
// Check content
|
|
|
|
let attachmentDirectory = Zotero.Attachments.getStorageDirectory(item).path;
|
|
|
|
let path = OS.Path.join(attachmentDirectory, item.attachmentFilename);
|
|
|
|
assert.isTrue(await OS.File.exists(path));
|
|
|
|
let contents = await Zotero.File.getSample(path);
|
|
|
|
assert.equal(contents, imageSample);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe("/connector/getRecognizedItem", function () {
|
|
|
|
it("should return the recognized parent item", async function () {
|
|
|
|
const stub = sinon.stub(Zotero.RecognizeDocument, '_recognize').callsFake(async () => {
|
|
|
|
return await createDataObject('item', {
|
|
|
|
title: "Recognized Item",
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
try {
|
|
|
|
const pdfPath = OS.Path.join(getTestDataDirectory().path, 'test.pdf');
|
|
|
|
const pdfArrayBuffer = (await OS.File.read(pdfPath)).buffer;
|
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
|
|
|
const attachmentInfo = {
|
|
|
|
url: `${testServerPath}/test2.pdf`,
|
|
|
|
title: "Test PDF2",
|
|
|
|
contentType: "application/pdf",
|
|
|
|
sessionID
|
|
|
|
};
|
|
|
|
let itemIDsPromise = waitForItemEvent('add');
|
|
|
|
let xhr = await httpRequest(
|
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveStandaloneAttachment",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": attachmentInfo.contentType,
|
|
|
|
"X-Metadata": JSON.stringify(attachmentInfo)
|
|
|
|
},
|
|
|
|
body: pdfArrayBuffer
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(xhr.status, 201);
|
|
|
|
assert.isTrue(JSON.parse(xhr.responseText).canRecognize);
|
|
|
|
let itemIDs = await itemIDsPromise;
|
|
|
|
let standaloneAttachment = Zotero.Items.get(itemIDs[0]);
|
|
|
|
|
|
|
|
assert.isFalse(standaloneAttachment.parentID);
|
|
|
|
|
|
|
|
let recognizedItemIDsPromise = waitForItemEvent('add');
|
|
|
|
xhr = await httpRequest(
|
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/getRecognizedItem",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID
|
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.isTrue(stub.called);
|
|
|
|
assert.equal(xhr.status, 200);
|
|
|
|
assert.equal(JSON.parse(xhr.responseText).title, "Recognized Item");
|
|
|
|
let recognizedItemIDs = await recognizedItemIDsPromise;
|
|
|
|
let recognizedItem = Zotero.Items.get(recognizedItemIDs[0]);
|
|
|
|
assert.equal(standaloneAttachment.parentID, recognizedItem.id);
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
stub.restore();
|
|
|
|
}
|
2018-02-06 01:40:38 -05:00
|
|
|
});
|
2025-06-19 08:01:52 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe("/connector/updateSession", function () {
|
|
|
|
it("should update collections and tags of item saved via /saveItems", async function () {
|
|
|
|
var collection1 = await createDataObject('collection');
|
|
|
|
var collection2 = await createDataObject('collection');
|
|
|
|
await select(win, collection2);
|
2019-01-07 04:34:14 -05:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
const id = Zotero.Utilities.randomString();
|
2019-01-07 04:34:14 -05:00
|
|
|
var sessionID = Zotero.Utilities.randomString();
|
|
|
|
var body = {
|
|
|
|
sessionID,
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
itemType: "newspaperArticle",
|
|
|
|
title: "Title",
|
2025-06-19 08:01:52 +03:00
|
|
|
id,
|
|
|
|
creators: [
|
2019-01-07 04:34:14 -05:00
|
|
|
{
|
2025-06-19 08:01:52 +03:00
|
|
|
firstName: "First",
|
|
|
|
lastName: "Last",
|
|
|
|
creatorType: "author"
|
2019-01-07 04:34:14 -05:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
uri: "http://example.com"
|
|
|
|
};
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
var reqPromise = httpRequest(
|
2019-01-07 04:34:14 -05:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveItems",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify(body)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
var ids = await waitForItemEvent('add');
|
|
|
|
var item = Zotero.Items.get(ids[0]);
|
|
|
|
assert.isTrue(collection2.hasItem(item.id));
|
2019-01-07 04:34:14 -05:00
|
|
|
var req = await reqPromise;
|
|
|
|
assert.equal(req.status, 201);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
reqPromise = httpRequest(
|
2019-01-07 04:34:14 -05:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
connectorServerPath + "/connector/saveAttachment",
|
2019-01-07 04:34:14 -05:00
|
|
|
{
|
|
|
|
headers: {
|
2025-06-19 08:01:52 +03:00
|
|
|
"Content-Type": "text/html",
|
|
|
|
"X-Metadata": JSON.stringify({
|
|
|
|
sessionID,
|
|
|
|
title: "Attachment",
|
|
|
|
parentItemID: id,
|
|
|
|
url: `${testServerPath}/attachment`,
|
|
|
|
})
|
2019-01-07 04:34:14 -05:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: "<html><head><title>Title</title><body>Body</body></html>"
|
2019-01-07 04:34:14 -05:00
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
let childIDs = await waitForItemEvent('add');
|
2019-01-07 04:34:14 -05:00
|
|
|
req = await reqPromise;
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(req.status, 201);
|
|
|
|
var childItem = Zotero.Items.get(childIDs[0]);
|
|
|
|
assert.equal(childItem.getField('title'), "Attachment");
|
|
|
|
assert.equal(childItem.parentID, item.id);
|
|
|
|
|
2019-01-07 04:34:14 -05:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
// Update saved item
|
|
|
|
var req = await httpRequest(
|
2019-01-07 04:34:14 -05:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/updateSession",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
2025-06-19 08:01:52 +03:00
|
|
|
target: collection1.treeViewID,
|
|
|
|
tags: "A, B"
|
2019-01-07 04:34:14 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(req.status, 200);
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.isTrue(collection1.hasItem(item.id));
|
|
|
|
assert.isTrue(item.hasTag("A"));
|
|
|
|
assert.isTrue(item.hasTag("B"));
|
2019-01-07 04:34:14 -05:00
|
|
|
});
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
it("should update collections and tags of a PDF saved via /saveStandaloneAttachment", async function () {
|
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
2019-01-07 04:34:14 -05:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
let collection1 = await createDataObject('collection');
|
|
|
|
let collection2 = await createDataObject('collection');
|
|
|
|
await select(win, collection2);
|
2020-07-17 15:14:10 -07:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
const pdfPath = OS.Path.join(getTestDataDirectory().path, 'test.pdf');
|
|
|
|
const pdfArrayBuffer = (await OS.File.read(pdfPath)).buffer;
|
|
|
|
const attachmentInfo = {
|
|
|
|
url: `${testServerPath}/test1.pdf`,
|
|
|
|
title: "Test PDF1",
|
|
|
|
contentType: "application/pdf",
|
|
|
|
sessionID
|
2020-07-17 15:14:10 -07:00
|
|
|
};
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
let ids;
|
|
|
|
let promise = waitForItemEvent('add');
|
|
|
|
let req = await httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
connectorServerPath + "/connector/saveStandaloneAttachment",
|
2020-07-17 15:14:10 -07:00
|
|
|
{
|
|
|
|
headers: {
|
2025-06-19 08:01:52 +03:00
|
|
|
"Content-Type": attachmentInfo.contentType,
|
|
|
|
"X-Metadata": JSON.stringify(attachmentInfo)
|
2020-07-17 15:14:10 -07:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: pdfArrayBuffer
|
2020-07-17 15:14:10 -07:00
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
ids = await promise;
|
|
|
|
let item = Zotero.Items.get(ids[0]);
|
|
|
|
assert.isTrue(collection2.hasItem(item.id));
|
2020-07-17 15:14:10 -07:00
|
|
|
assert.equal(req.status, 201);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
// Update saved item
|
|
|
|
req = await httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/updateSession",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
2025-06-19 08:01:52 +03:00
|
|
|
target: collection1.treeViewID,
|
|
|
|
tags: "A, B"
|
2020-07-17 15:14:10 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
2020-07-17 15:14:10 -07:00
|
|
|
assert.equal(req.status, 200);
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.isTrue(collection1.hasItem(item.id));
|
|
|
|
assert.isTrue(item.hasTag("A"));
|
|
|
|
assert.isTrue(item.hasTag("B"));
|
2020-07-17 15:14:10 -07:00
|
|
|
});
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
it("should update collections and tags of webpage saved via /saveSnapshot", async function () {
|
|
|
|
var sessionID = Zotero.Utilities.randomString();
|
|
|
|
|
|
|
|
var collection1 = await createDataObject('collection');
|
|
|
|
var collection2 = await createDataObject('collection');
|
|
|
|
await select(win, collection2);
|
|
|
|
|
|
|
|
// saveSnapshot saves parent and child before returning
|
|
|
|
var ids1, ids2;
|
|
|
|
var promise = waitForItemEvent('add').then(function (ids) {
|
|
|
|
ids1 = ids;
|
|
|
|
return waitForItemEvent('add').then(function (ids) {
|
|
|
|
ids2 = ids;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
await httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
connectorServerPath + "/connector/saveSnapshot",
|
2020-07-17 15:14:10 -07:00
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
2025-06-19 08:01:52 +03:00
|
|
|
url: testServerPath + '/snapshot',
|
|
|
|
title: "Title"
|
2020-07-17 15:14:10 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
await httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveSingleFile",
|
|
|
|
{
|
|
|
|
headers: {
|
2020-10-23 17:39:07 -06:00
|
|
|
"Content-Type": "application/json"
|
2020-07-17 15:14:10 -07:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
|
|
|
title: "Snapshot",
|
|
|
|
url: `${testServerPath}/snapshot`,
|
|
|
|
snapshotContent: "<html><head><title>Title</title><body>Body</body></html>"
|
|
|
|
})
|
2020-07-17 15:14:10 -07:00
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
assert.isTrue(promise.isFulfilled());
|
|
|
|
|
|
|
|
var item = Zotero.Items.get(ids1[0]);
|
|
|
|
|
|
|
|
// Update saved item
|
|
|
|
var req = await httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/updateSession",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
2025-06-19 08:01:52 +03:00
|
|
|
target: collection1.treeViewID,
|
|
|
|
tags: "A, B"
|
2020-07-17 15:14:10 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
2020-07-17 15:14:10 -07:00
|
|
|
assert.equal(req.status, 200);
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.isTrue(collection1.hasItem(item.id));
|
|
|
|
assert.isTrue(item.hasTag("A"));
|
|
|
|
assert.isTrue(item.hasTag("B"));
|
2020-07-17 15:14:10 -07:00
|
|
|
});
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
it("should move item saved via /saveItems to another library", async function () {
|
|
|
|
var group = await createGroup({ editable: true, filesEditable: false });
|
|
|
|
await select(win, group);
|
2020-07-17 15:14:10 -07:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
const id = Zotero.Utilities.randomString();
|
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
2020-07-17 15:14:10 -07:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
let saveAttachment = () => {
|
|
|
|
return httpRequest(
|
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveAttachment",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "text/html",
|
|
|
|
"X-Metadata": JSON.stringify({
|
|
|
|
sessionID,
|
|
|
|
title: "Attachment",
|
|
|
|
parentItemID: id,
|
|
|
|
url: `${testServerPath}/attachment`,
|
|
|
|
})
|
|
|
|
},
|
|
|
|
body: "<html><head><title>Title</title><body>Body</body></html>"
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
var body = {
|
|
|
|
sessionID,
|
2020-07-17 15:14:10 -07:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
itemType: "newspaperArticle",
|
2025-06-19 08:01:52 +03:00
|
|
|
title: "Title",
|
|
|
|
id,
|
2020-07-17 15:14:10 -07:00
|
|
|
}
|
|
|
|
],
|
|
|
|
uri: "http://example.com"
|
|
|
|
};
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
var reqPromise = httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveItems",
|
|
|
|
{
|
|
|
|
headers: {
|
2020-10-23 17:39:07 -06:00
|
|
|
"Content-Type": "application/json"
|
2020-07-17 15:14:10 -07:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: JSON.stringify(body)
|
2020-07-17 15:14:10 -07:00
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
var ids1 = await waitForItemEvent('add');
|
|
|
|
var item1 = Zotero.Items.get(ids1[0]);
|
|
|
|
var req = await reqPromise;
|
2020-07-17 15:14:10 -07:00
|
|
|
assert.equal(req.status, 201);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
req = await saveAttachment();
|
|
|
|
// Attachment save returns with 200 since library files are not editable
|
|
|
|
assert.equal(req.status, 200);
|
|
|
|
assert.equal(req.responseText, "Library files are not editable.");
|
|
|
|
|
|
|
|
// Move item to user library where we can save files
|
|
|
|
reqPromise = httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/updateSession",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
2025-06-19 08:01:52 +03:00
|
|
|
target: Zotero.Libraries.userLibrary.treeViewID
|
2020-07-17 15:14:10 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
var ids2 = await waitForItemEvent('add');
|
|
|
|
req = await reqPromise;
|
2020-07-17 15:14:10 -07:00
|
|
|
assert.equal(req.status, 200);
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
reqPromise = saveAttachment();
|
|
|
|
await waitForItemEvent('add');
|
|
|
|
req = await reqPromise;
|
|
|
|
// Attachment is saved in user library
|
|
|
|
assert.equal(req.status, 201);
|
|
|
|
|
|
|
|
var item2 = Zotero.Items.get(ids2[0]);
|
|
|
|
assert.isFalse(Zotero.Items.exists(item1.id));
|
|
|
|
assert.equal(item2.libraryID, Zotero.Libraries.userLibraryID);
|
|
|
|
assert.equal(item2.numAttachments(), 1);
|
|
|
|
|
|
|
|
// Move back to the file-editing restricted group
|
|
|
|
reqPromise = httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/updateSession",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
2025-06-19 08:01:52 +03:00
|
|
|
target: group.treeViewID
|
2020-07-17 15:14:10 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
var ids3 = await waitForItemEvent('add');
|
|
|
|
var item3 = Zotero.Items.get(ids3[0]);
|
|
|
|
|
|
|
|
req = await reqPromise;
|
2020-07-17 15:14:10 -07:00
|
|
|
assert.equal(req.status, 200);
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.isFalse(Zotero.Items.exists(item2.id));
|
|
|
|
assert.equal(item3.libraryID, group.libraryID);
|
|
|
|
assert.equal(item3.numAttachments(), 0);
|
2020-07-17 15:14:10 -07:00
|
|
|
});
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
it("should move item saved via /saveSnapshot to another library", async function () {
|
|
|
|
var group = await createGroup({ editable: true, filesEditable: false });
|
|
|
|
await select(win, group);
|
2020-07-17 15:14:10 -07:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
|
|
|
let saveSingleFile = () => {
|
|
|
|
return httpRequest(
|
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/saveSingleFile",
|
2020-07-17 15:14:10 -07:00
|
|
|
{
|
2025-06-19 08:01:52 +03:00
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
|
|
|
title: "Snapshot",
|
|
|
|
url: `${testServerPath}/snapshot`,
|
|
|
|
snapshotContent: "<html><head><title>Title</title><body>Body</body></html>"
|
|
|
|
})
|
2020-07-17 15:14:10 -07:00
|
|
|
}
|
2025-06-19 08:01:52 +03:00
|
|
|
);
|
2020-07-17 15:14:10 -07:00
|
|
|
};
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
var reqPromise = httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
connectorServerPath + "/connector/saveSnapshot",
|
2020-07-17 15:14:10 -07:00
|
|
|
{
|
|
|
|
headers: {
|
2020-10-23 17:39:07 -06:00
|
|
|
"Content-Type": "application/json"
|
2020-07-17 15:14:10 -07:00
|
|
|
},
|
2025-06-19 08:01:52 +03:00
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
|
|
|
url: testServerPath + '/snapshot',
|
|
|
|
})
|
2020-07-17 15:14:10 -07:00
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
var ids1 = await waitForItemEvent('add');
|
|
|
|
var req = await reqPromise;
|
|
|
|
assert.equal(req.status, 201);
|
|
|
|
var item1 = Zotero.Items.get(ids1[0]);
|
|
|
|
req = await saveSingleFile();
|
|
|
|
assert.equal(req.status, 200);
|
|
|
|
assert.equal(req.responseText, "Library files are not editable.");
|
|
|
|
|
|
|
|
// Move item to user library with file attachments
|
|
|
|
var reqPromise = httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/updateSession",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
2025-06-19 08:01:52 +03:00
|
|
|
target: Zotero.Libraries.userLibrary.treeViewID
|
2020-07-17 15:14:10 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
var ids2 = await waitForItemEvent('add');
|
|
|
|
var item2 = Zotero.Items.get(ids2[0]);
|
|
|
|
|
|
|
|
var req = await reqPromise;
|
2020-07-17 15:14:10 -07:00
|
|
|
assert.equal(req.status, 200);
|
|
|
|
assert.isFalse(Zotero.Items.exists(item1.id));
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(item2.libraryID, Zotero.Libraries.userLibraryID);
|
2020-07-17 15:14:10 -07:00
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
req = await saveSingleFile();
|
|
|
|
assert.equal(req.status, 201);
|
|
|
|
assert.equal(item2.numAttachments(), 1);
|
|
|
|
|
|
|
|
// Move back to the file-editing restricted group
|
|
|
|
reqPromise = httpRequest(
|
2020-07-17 15:14:10 -07:00
|
|
|
'POST',
|
|
|
|
connectorServerPath + "/connector/updateSession",
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
sessionID,
|
2025-06-19 08:01:52 +03:00
|
|
|
target: group.treeViewID
|
2020-07-17 15:14:10 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
2025-06-19 08:01:52 +03:00
|
|
|
|
|
|
|
var ids3 = await waitForItemEvent('add');
|
|
|
|
var item3 = Zotero.Items.get(ids3[0]);
|
|
|
|
|
|
|
|
req = await reqPromise;
|
2020-07-17 15:14:10 -07:00
|
|
|
assert.equal(req.status, 200);
|
|
|
|
assert.isFalse(Zotero.Items.exists(item2.id));
|
2025-06-19 08:01:52 +03:00
|
|
|
assert.equal(item3.libraryID, group.libraryID);
|
|
|
|
assert.equal(item3.numAttachments(), 0);
|
2020-07-17 15:14:10 -07:00
|
|
|
});
|
2018-02-06 01:40:38 -05:00
|
|
|
});
|
|
|
|
|
2016-11-30 13:53:58 +02:00
|
|
|
describe('/connector/installStyle', function() {
|
2016-11-29 21:59:58 +02:00
|
|
|
var endpoint;
|
2019-07-15 07:03:32 -04:00
|
|
|
var style;
|
2016-11-29 21:59:58 +02:00
|
|
|
|
|
|
|
before(function() {
|
2016-11-30 13:53:58 +02:00
|
|
|
endpoint = connectorServerPath + "/connector/installStyle";
|
2019-07-15 07:03:32 -04:00
|
|
|
style = `<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
<style xmlns="http://purl.org/net/xbiblio/csl" version="1.0" default-locale="de-DE">
|
|
|
|
<info>
|
|
|
|
<title>Test1</title>
|
|
|
|
<id>http://www.example.com/test2</id>
|
|
|
|
<link href="http://www.zotero.org/styles/cell" rel="independent-parent"/>
|
|
|
|
</info>
|
|
|
|
</style>
|
|
|
|
`;
|
2016-11-29 21:59:58 +02:00
|
|
|
});
|
|
|
|
|
2016-11-30 13:53:58 +02:00
|
|
|
it('should reject styles with invalid text', function* () {
|
2025-06-19 08:01:52 +03:00
|
|
|
var error = yield getPromiseError(httpRequest(
|
2016-11-30 13:53:58 +02:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
body: '{}'
|
|
|
|
}
|
|
|
|
));
|
|
|
|
assert.instanceOf(error, Zotero.HTTP.UnexpectedStatusException);
|
|
|
|
assert.equal(error.xmlhttp.status, 400);
|
2016-11-30 22:40:54 -05:00
|
|
|
assert.equal(error.xmlhttp.responseText, Zotero.getString("styles.installError", "(null)"));
|
2016-11-29 21:59:58 +02:00
|
|
|
});
|
|
|
|
|
2016-11-30 13:53:58 +02:00
|
|
|
it('should import a style with application/vnd.citationstyles.style+xml content-type', function* () {
|
2017-05-31 16:44:35 +01:00
|
|
|
sinon.stub(Zotero.Styles, 'install').callsFake(function(style) {
|
2020-07-05 17:20:31 -04:00
|
|
|
var parser = new DOMParser(),
|
2016-11-29 21:59:58 +02:00
|
|
|
doc = parser.parseFromString(style, "application/xml");
|
|
|
|
|
2018-08-13 18:16:43 -04:00
|
|
|
return Zotero.Promise.resolve({
|
|
|
|
styleTitle: Zotero.Utilities.xpathText(
|
|
|
|
doc, '/csl:style/csl:info[1]/csl:title[1]', Zotero.Styles.ns
|
|
|
|
),
|
|
|
|
styleID: Zotero.Utilities.xpathText(
|
|
|
|
doc, '/csl:style/csl:info[1]/csl:id[1]', Zotero.Styles.ns
|
|
|
|
)
|
|
|
|
});
|
2016-11-29 21:59:58 +02:00
|
|
|
});
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
var response = yield httpRequest(
|
2016-11-29 21:59:58 +02:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
2016-11-30 13:53:58 +02:00
|
|
|
headers: { "Content-Type": "application/vnd.citationstyles.style+xml" },
|
2016-11-29 21:59:58 +02:00
|
|
|
body: style
|
|
|
|
}
|
2019-07-15 07:03:32 -04:00
|
|
|
);
|
2016-11-29 21:59:58 +02:00
|
|
|
assert.equal(response.status, 201);
|
|
|
|
assert.equal(response.response, JSON.stringify({name: 'Test1'}));
|
|
|
|
Zotero.Styles.install.restore();
|
|
|
|
});
|
2019-07-15 07:03:32 -04:00
|
|
|
|
|
|
|
it('should accept text/plain request with X-Zotero-Connector-API-Version or Zotero-Allowed-Request', async function () {
|
|
|
|
sinon.stub(Zotero.Styles, 'install').callsFake(function(style) {
|
2020-07-05 17:20:31 -04:00
|
|
|
var parser = new DOMParser(),
|
2019-07-15 07:03:32 -04:00
|
|
|
doc = parser.parseFromString(style, "application/xml");
|
|
|
|
|
|
|
|
return Zotero.Promise.resolve({
|
|
|
|
styleTitle: Zotero.Utilities.xpathText(
|
|
|
|
doc, '/csl:style/csl:info[1]/csl:title[1]', Zotero.Styles.ns
|
|
|
|
),
|
|
|
|
styleID: Zotero.Utilities.xpathText(
|
|
|
|
doc, '/csl:style/csl:info[1]/csl:id[1]', Zotero.Styles.ns
|
|
|
|
)
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// X-Zotero-Connector-API-Version
|
2025-06-19 08:01:52 +03:00
|
|
|
var response = await httpRequest(
|
2019-07-15 07:03:32 -04:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "text/plain",
|
|
|
|
"X-Zotero-Connector-API-Version": "2"
|
|
|
|
},
|
|
|
|
body: style
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(response.status, 201);
|
|
|
|
|
|
|
|
// Zotero-Allowed-Request
|
2025-06-19 08:01:52 +03:00
|
|
|
response = await httpRequest(
|
2019-07-15 07:03:32 -04:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "text/plain",
|
|
|
|
"Zotero-Allowed-Request": "1"
|
|
|
|
},
|
|
|
|
body: style
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(response.status, 201);
|
|
|
|
|
|
|
|
Zotero.Styles.install.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should reject text/plain request without X-Zotero-Connector-API-Version', async function () {
|
2025-06-19 08:01:52 +03:00
|
|
|
var req = await httpRequest(
|
2019-07-15 07:03:32 -04:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "text/plain"
|
|
|
|
},
|
|
|
|
body: style,
|
|
|
|
successCodes: [403]
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(req.status, 403);
|
|
|
|
});
|
2016-11-29 21:59:58 +02:00
|
|
|
});
|
2016-11-30 13:53:58 +02:00
|
|
|
|
|
|
|
describe('/connector/import', function() {
|
|
|
|
var endpoint;
|
|
|
|
|
|
|
|
before(function() {
|
|
|
|
endpoint = connectorServerPath + "/connector/import";
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should reject resources that do not contain import data', function* () {
|
2025-06-19 08:01:52 +03:00
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
|
|
|
var error = yield getPromiseError(httpRequest(
|
2016-11-30 13:53:58 +02:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
endpoint + `?session=${sessionID}`,
|
2016-11-30 13:53:58 +02:00
|
|
|
{
|
2019-07-12 02:08:55 -04:00
|
|
|
headers: {
|
|
|
|
"Content-Type": "text/plain",
|
|
|
|
"X-Zotero-Connector-API-Version": "2"
|
|
|
|
},
|
2016-11-30 13:53:58 +02:00
|
|
|
body: 'Owl'
|
|
|
|
}
|
|
|
|
));
|
|
|
|
assert.instanceOf(error, Zotero.HTTP.UnexpectedStatusException);
|
|
|
|
assert.equal(error.xmlhttp.status, 400);
|
|
|
|
});
|
|
|
|
|
2019-07-15 07:03:32 -04:00
|
|
|
it('should reject requests without X-Zotero-Connector-API-Version', async function () {
|
2025-06-19 08:01:52 +03:00
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
|
|
|
var req = await httpRequest(
|
2019-07-15 07:03:32 -04:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
endpoint + `?session=${sessionID}`,
|
2019-07-15 07:03:32 -04:00
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "text/plain"
|
|
|
|
},
|
|
|
|
successCodes: [403]
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(req.status, 403);
|
|
|
|
});
|
|
|
|
|
2024-06-24 02:04:52 -04:00
|
|
|
it('should import resources (BibTeX) into selected collection', async function () {
|
2025-06-19 08:01:52 +03:00
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
2024-06-24 02:04:52 -04:00
|
|
|
var collection = await createDataObject('collection');
|
|
|
|
await select(win, collection);
|
2017-08-15 11:14:50 +03:00
|
|
|
|
2016-11-30 13:53:58 +02:00
|
|
|
var resource = `@book{test1,
|
|
|
|
title={Test1},
|
|
|
|
author={Owl},
|
|
|
|
year={1000},
|
2018-05-23 20:39:32 -04:00
|
|
|
publisher={Curly Braces Publishing},
|
|
|
|
keywords={A, B}
|
2016-11-30 13:53:58 +02:00
|
|
|
}`;
|
2018-04-27 18:27:06 -04:00
|
|
|
|
|
|
|
var addedItemIDsPromise = waitForItemEvent('add');
|
2025-06-19 08:01:52 +03:00
|
|
|
var req = await httpRequest(
|
2016-11-30 13:53:58 +02:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
endpoint + `?session=${sessionID}`,
|
2016-11-30 13:53:58 +02:00
|
|
|
{
|
2019-07-12 02:08:55 -04:00
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/x-bibtex",
|
|
|
|
"X-Zotero-Connector-API-Version": "2"
|
|
|
|
},
|
2016-11-30 13:53:58 +02:00
|
|
|
body: resource
|
|
|
|
}
|
2019-07-15 07:03:32 -04:00
|
|
|
);
|
2018-04-27 18:27:06 -04:00
|
|
|
assert.equal(req.status, 201);
|
|
|
|
assert.equal(JSON.parse(req.responseText)[0].title, 'Test1');
|
2017-08-15 11:14:50 +03:00
|
|
|
|
2024-06-24 02:04:52 -04:00
|
|
|
let itemIDs = await addedItemIDsPromise;
|
2018-04-27 18:27:06 -04:00
|
|
|
assert.isTrue(collection.hasItem(itemIDs[0]));
|
2018-05-23 20:39:32 -04:00
|
|
|
var item = Zotero.Items.get(itemIDs[0]);
|
|
|
|
assert.sameDeepMembers(item.getTags(), [{ tag: 'A', type: 1 }, { tag: 'B', type: 1 }]);
|
2016-11-30 13:53:58 +02:00
|
|
|
});
|
2017-09-27 17:31:17 -04:00
|
|
|
|
|
|
|
|
2024-06-24 02:04:52 -04:00
|
|
|
it('should switch to My Library if read-only library is selected', async function () {
|
2025-06-19 08:01:52 +03:00
|
|
|
const sessionID = Zotero.Utilities.randomString();
|
2024-06-24 02:04:52 -04:00
|
|
|
var group = await createGroup({
|
2017-09-27 17:31:17 -04:00
|
|
|
editable: false
|
|
|
|
});
|
2024-06-24 02:04:52 -04:00
|
|
|
await select(win, group);
|
2017-09-27 17:31:17 -04:00
|
|
|
|
|
|
|
var resource = `@book{test1,
|
|
|
|
title={Test1},
|
|
|
|
author={Owl},
|
|
|
|
year={1000},
|
|
|
|
publisher={Curly Braces Publishing}
|
|
|
|
}`;
|
2018-04-27 18:27:06 -04:00
|
|
|
|
|
|
|
var addedItemIDsPromise = waitForItemEvent('add');
|
2025-06-19 08:01:52 +03:00
|
|
|
var req = await httpRequest(
|
2017-09-27 17:31:17 -04:00
|
|
|
'POST',
|
2025-06-19 08:01:52 +03:00
|
|
|
endpoint + `?session=${sessionID}`,
|
2017-09-27 17:31:17 -04:00
|
|
|
{
|
2019-07-12 02:08:55 -04:00
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/x-bibtex",
|
|
|
|
"X-Zotero-Connector-API-Version": "2"
|
|
|
|
},
|
2017-09-27 17:31:17 -04:00
|
|
|
body: resource,
|
|
|
|
successCodes: false
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2018-04-27 18:27:06 -04:00
|
|
|
assert.equal(req.status, 201);
|
2017-09-27 17:31:17 -04:00
|
|
|
assert.equal(
|
2018-04-27 18:27:06 -04:00
|
|
|
win.ZoteroPane.collectionsView.getSelectedLibraryID(),
|
|
|
|
Zotero.Libraries.userLibraryID
|
2017-09-27 17:31:17 -04:00
|
|
|
);
|
2018-04-27 18:27:06 -04:00
|
|
|
|
2024-06-24 02:04:52 -04:00
|
|
|
let itemIDs = await addedItemIDsPromise;
|
2018-04-27 18:27:06 -04:00
|
|
|
var item = Zotero.Items.get(itemIDs[0]);
|
|
|
|
assert.equal(item.libraryID, Zotero.Libraries.userLibraryID);
|
2017-09-27 17:31:17 -04:00
|
|
|
});
|
2016-11-30 13:53:58 +02:00
|
|
|
});
|
2022-12-22 13:12:51 -05:00
|
|
|
|
|
|
|
describe('/connector/request', function () {
|
|
|
|
let endpoint;
|
|
|
|
|
|
|
|
before(function () {
|
|
|
|
endpoint = connectorServerPath + '/connector/request';
|
|
|
|
});
|
|
|
|
|
|
|
|
beforeEach(function () {
|
|
|
|
Zotero.Server.Connector.Request.enableValidation = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
after(function () {
|
|
|
|
Zotero.Server.Connector.Request.enableValidation = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should reject GET requests', async function () {
|
2025-06-19 08:01:52 +03:00
|
|
|
let req = await httpRequest(
|
2022-12-22 13:12:51 -05:00
|
|
|
'GET',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: { 'content-type': 'application/json' },
|
|
|
|
body: JSON.stringify({
|
|
|
|
method: 'GET',
|
|
|
|
url: 'https://www.example.com/'
|
|
|
|
}),
|
|
|
|
successCodes: false
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(req.status, 400);
|
|
|
|
assert.include(req.responseText, 'Endpoint does not support method');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should not make requests to arbitrary hosts', async function () {
|
2025-06-19 08:01:52 +03:00
|
|
|
let req = await httpRequest(
|
2022-12-22 13:12:51 -05:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: { 'content-type': 'application/json' },
|
|
|
|
body: JSON.stringify({
|
|
|
|
method: 'GET',
|
|
|
|
url: `http://localhost:${Zotero.Prefs.get('httpServer.port')}/`
|
|
|
|
}),
|
|
|
|
successCodes: false
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(req.status, 400);
|
|
|
|
assert.include(req.responseText, 'Unsupported URL');
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
req = await httpRequest(
|
2022-12-22 13:12:51 -05:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: { 'content-type': 'application/json' },
|
|
|
|
body: JSON.stringify({
|
|
|
|
method: 'GET',
|
|
|
|
url: `http://www.example.com/`
|
|
|
|
}),
|
|
|
|
successCodes: false
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(req.status, 400);
|
|
|
|
assert.include(req.responseText, 'Unsupported URL');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should reject requests with non-Mozilla/ user agents', async function () {
|
2025-06-19 08:01:52 +03:00
|
|
|
let req = await httpRequest(
|
2022-12-22 13:12:51 -05:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
'content-type': 'application/json',
|
|
|
|
'user-agent': 'BadBrowser/1.0'
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
method: 'GET',
|
|
|
|
url: `https://www.worldcat.org/api/nonexistent`
|
|
|
|
}),
|
|
|
|
successCodes: false
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(req.status, 400);
|
|
|
|
assert.include(req.responseText, 'Unsupported User-Agent');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should allow a request to an allowed host', async function () {
|
|
|
|
let stub = sinon.stub(Zotero.HTTP, 'request');
|
|
|
|
// First call: call original
|
|
|
|
stub.callThrough();
|
|
|
|
// Second call (call from within /connector/request handler): return the following
|
|
|
|
stub.onSecondCall().returns({
|
|
|
|
status: 200,
|
|
|
|
getAllResponseHeaders: () => '',
|
|
|
|
response: 'it went through'
|
|
|
|
});
|
|
|
|
|
2025-06-19 08:01:52 +03:00
|
|
|
let req = await httpRequest(
|
2022-12-22 13:12:51 -05:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: { 'content-type': 'application/json' },
|
|
|
|
body: JSON.stringify({
|
|
|
|
method: 'GET',
|
|
|
|
url: `https://www.worldcat.org/api/nonexistent`
|
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert.equal(req.status, 200);
|
|
|
|
assert.equal(JSON.parse(req.responseText).body, 'it went through');
|
|
|
|
|
|
|
|
stub.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return response in translator request() format with lowercase headers', async function () {
|
|
|
|
let testEndpointPath = '/test/header';
|
|
|
|
|
|
|
|
httpd.registerPathHandler(
|
|
|
|
testEndpointPath,
|
|
|
|
{
|
|
|
|
handle: function (request, response) {
|
|
|
|
response.setStatusLine(null, 200, 'OK');
|
|
|
|
response.setHeader('X-Some-Header', 'Header value');
|
|
|
|
response.write('body');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
Zotero.Server.Connector.Request.enableValidation = false;
|
2025-06-19 08:01:52 +03:00
|
|
|
let req = await httpRequest(
|
2022-12-22 13:12:51 -05:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: { 'content-type': 'application/json' },
|
|
|
|
body: JSON.stringify({
|
|
|
|
method: 'GET',
|
|
|
|
url: testServerPath + testEndpointPath
|
|
|
|
}),
|
|
|
|
responseType: 'json'
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(req.response.status, 200);
|
|
|
|
assert.equal(req.response.headers['x-some-header'], 'Header value');
|
|
|
|
assert.equal(req.response.body, 'body');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should set Referer', async function () {
|
|
|
|
let testEndpointPath = '/test/referer';
|
|
|
|
let referer = 'https://www.example.com/';
|
|
|
|
|
|
|
|
httpd.registerPathHandler(
|
|
|
|
testEndpointPath,
|
|
|
|
{
|
|
|
|
handle: function (request, response) {
|
|
|
|
assert.equal(request.getHeader('Referer'), referer);
|
|
|
|
response.setStatusLine(null, 200, 'OK');
|
|
|
|
response.write('');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
Zotero.Server.Connector.Request.enableValidation = false;
|
2025-06-19 08:01:52 +03:00
|
|
|
let req = await httpRequest(
|
2022-12-22 13:12:51 -05:00
|
|
|
'POST',
|
|
|
|
endpoint,
|
|
|
|
{
|
|
|
|
headers: { 'content-type': 'application/json' },
|
|
|
|
body: JSON.stringify({
|
|
|
|
method: 'GET',
|
|
|
|
url: testServerPath + testEndpointPath,
|
|
|
|
options: {
|
|
|
|
headers: {
|
|
|
|
Referer: referer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(JSON.parse(req.response).status, 200);
|
|
|
|
});
|
|
|
|
});
|
2016-05-13 14:59:46 -04:00
|
|
|
});
|