mirror of
https://github.com/zotero/zotero.git
synced 2026-10-03 02:21:49 +00:00
The login session creates a key on the server before we try to store it, so each attempt that failed to save the key left an active key behind.
221 lines
7.5 KiB
JavaScript
221 lines
7.5 KiB
JavaScript
describe("Account Preferences", function () {
|
|
var win, doc;
|
|
before(function* () {
|
|
// Load prefs with sync pane
|
|
win = yield loadWindow("chrome://zotero/content/preferences/preferences.xhtml", {
|
|
pane: 'zotero-prefpane-account'
|
|
});
|
|
doc = win.document;
|
|
yield win.Zotero_Preferences.waitForFirstPaneLoad();
|
|
});
|
|
|
|
after(function () {
|
|
win.close();
|
|
});
|
|
|
|
describe("Settings", function () {
|
|
describe("Data Syncing", function () {
|
|
var createLoginSessionStub, checkLoginSessionStub, deleteAPIKey, launchURLStub,
|
|
streamerSubscribeStub, streamerUnsubscribeStub, indicatorElem, apiKey;
|
|
|
|
var performLogin = async function (username) {
|
|
apiKey = Zotero.Utilities.randomString(24);
|
|
|
|
createLoginSessionStub.resolves({
|
|
sessionToken: 'test-session-token',
|
|
loginURL: 'https://www.zotero.org/authorize?token=test-session-token'
|
|
});
|
|
checkLoginSessionStub.resolves({
|
|
status: 'completed',
|
|
apiKey,
|
|
userID: 1,
|
|
username
|
|
});
|
|
|
|
await win.Zotero_Preferences.Sync.linkAccount();
|
|
};
|
|
|
|
before(function* () {
|
|
createLoginSessionStub = sinon.stub(
|
|
Zotero.Sync.APIClient.prototype, 'createLoginSession');
|
|
checkLoginSessionStub = sinon.stub(
|
|
Zotero.Sync.APIClient.prototype, 'checkLoginSession');
|
|
deleteAPIKey = sinon.stub(Zotero.Sync.APIClient.prototype, 'deleteAPIKey').resolves();
|
|
launchURLStub = sinon.stub(Zotero, 'launchURL');
|
|
streamerSubscribeStub = sinon.stub(Zotero.Streamer, 'subscribe').returns(false);
|
|
streamerUnsubscribeStub = sinon.stub(Zotero.Streamer, 'unsubscribe');
|
|
indicatorElem = doc.querySelector('.account-login-status-indicator');
|
|
sinon.stub(Zotero, 'alert');
|
|
// Speed up polling for tests
|
|
win.Zotero_Preferences.Sync._pollInterval = 10;
|
|
});
|
|
|
|
beforeEach(function* () {
|
|
yield win.Zotero_Preferences.Sync.unlinkAccount(false);
|
|
deleteAPIKey.resetHistory();
|
|
createLoginSessionStub.resetHistory();
|
|
checkLoginSessionStub.resetHistory();
|
|
launchURLStub.resetHistory();
|
|
streamerSubscribeStub.resetHistory();
|
|
streamerUnsubscribeStub.resetHistory();
|
|
Zotero.alert.reset();
|
|
});
|
|
|
|
after(function () {
|
|
Zotero.HTTP.mock = null;
|
|
Zotero.alert.restore();
|
|
createLoginSessionStub.restore();
|
|
checkLoginSessionStub.restore();
|
|
deleteAPIKey.restore();
|
|
launchURLStub.restore();
|
|
streamerSubscribeStub.restore();
|
|
streamerUnsubscribeStub.restore();
|
|
win.Zotero_Preferences.Sync._pollInterval = 3000;
|
|
});
|
|
|
|
it("should set API key and display full controls after successful login", async function () {
|
|
await performLogin("Username");
|
|
|
|
assert.equal(await Zotero.Sync.Data.Local.getAPIKey(), apiKey);
|
|
assert.isTrue(doc.getElementById('sync-unauthorized').hidden);
|
|
assert.isTrue(launchURLStub.calledOnce);
|
|
});
|
|
|
|
|
|
it("should show error when login session expires", async function () {
|
|
createLoginSessionStub.resolves({
|
|
sessionToken: 'test-session-token',
|
|
loginURL: 'https://www.zotero.org/authorize?token=test-session-token'
|
|
});
|
|
let expiredError = new Error("Login session expired");
|
|
expiredError.expired = true;
|
|
checkLoginSessionStub.rejects(expiredError);
|
|
|
|
await win.Zotero_Preferences.Sync.linkAccount();
|
|
|
|
assert.isTrue(Zotero.alert.called);
|
|
assert.equal(await Zotero.Sync.Data.Local.getAPIKey(), "");
|
|
assert.equal(doc.getElementById('sync-settings-section').hidden, true);
|
|
});
|
|
|
|
|
|
it("should reset UI when login session is cancelled on server", async function () {
|
|
createLoginSessionStub.resolves({
|
|
sessionToken: 'test-session-token',
|
|
loginURL: 'https://www.zotero.org/authorize?token=test-session-token'
|
|
});
|
|
checkLoginSessionStub.resolves({
|
|
status: 'cancelled'
|
|
});
|
|
|
|
await win.Zotero_Preferences.Sync.linkAccount();
|
|
|
|
assert.equal(await Zotero.Sync.Data.Local.getAPIKey(), "");
|
|
assert.equal(doc.querySelector('.account-login-default').hidden, false);
|
|
assert.equal(doc.querySelector('.account-login-pending').hidden, true);
|
|
});
|
|
|
|
|
|
it("should revoke the API key when it can't be stored locally", async function () {
|
|
var setAPIKeyStub = sinon.stub(Zotero.Sync.Data.Local, 'setAPIKey')
|
|
.rejects(new Error("User canceled OS unlock entry"));
|
|
try {
|
|
let e = await getPromiseError(performLogin("Username"));
|
|
assert.ok(e);
|
|
assert.isTrue(deleteAPIKey.calledOnce);
|
|
assert.equal(deleteAPIKey.firstCall.thisValue.apiKey, apiKey);
|
|
assert.equal(doc.querySelector('.account-login-default').hidden, false);
|
|
assert.equal(doc.querySelector('.account-login-pending').hidden, true);
|
|
}
|
|
finally {
|
|
setAPIKeyStub.restore();
|
|
}
|
|
});
|
|
|
|
|
|
it("should delete API key and display auth form when 'Unlink Account' clicked", async function () {
|
|
await performLogin("Username");
|
|
assert.equal(await Zotero.Sync.Data.Local.getAPIKey(), apiKey);
|
|
|
|
await win.Zotero_Preferences.Sync.unlinkAccount(false);
|
|
|
|
assert.isTrue(deleteAPIKey.called);
|
|
assert.equal(await Zotero.Sync.Data.Local.getAPIKey(), "");
|
|
assert.equal(doc.getElementById('sync-settings-section').hidden, true);
|
|
});
|
|
|
|
it("should reset the storage controller when unlinking", async function () {
|
|
await performLogin("Username");
|
|
assert.equal(await Zotero.Sync.Data.Local.getAPIKey(), apiKey);
|
|
|
|
let options = {
|
|
apiClient: Zotero.Sync.Runner.getAPIClient({ apiKey })
|
|
};
|
|
let controller = Zotero.Sync.Runner.getStorageController('zfs', options);
|
|
let apiKey1 = controller.apiClient.apiKey;
|
|
|
|
await win.Zotero_Preferences.Sync.unlinkAccount(false);
|
|
await performLogin("Username");
|
|
|
|
options = {
|
|
apiClient: Zotero.Sync.Runner.getAPIClient({ apiKey })
|
|
};
|
|
controller = Zotero.Sync.Runner.getStorageController('zfs', options);
|
|
assert.notEqual(controller.apiClient.apiKey, apiKey1);
|
|
});
|
|
|
|
it("should not unlink on pressing cancel", async function () {
|
|
await performLogin("Username");
|
|
|
|
waitForDialog(null, 'cancel');
|
|
|
|
await win.Zotero_Preferences.Sync.unlinkAccount();
|
|
assert.equal(await Zotero.Sync.Data.Local.getAPIKey(), apiKey);
|
|
assert.isTrue(doc.getElementById('sync-unauthorized').hidden);
|
|
});
|
|
|
|
it("should clear sync errors from the toolbar after logging in", async function () {
|
|
let win = await loadZoteroPane();
|
|
|
|
let syncError = win.document.getElementById('zotero-tb-sync-error');
|
|
|
|
Zotero.Sync.Runner.updateIcons(new Error("a sync error"));
|
|
assert.isFalse(syncError.hidden);
|
|
|
|
await performLogin("Username");
|
|
assert.isTrue(syncError.hidden);
|
|
|
|
win.close();
|
|
});
|
|
|
|
it("should cancel login and reset UI when cancelLogin is called", async function () {
|
|
let cancelLoginSessionStub = sinon.stub(
|
|
Zotero.Sync.APIClient.prototype, 'cancelLoginSession').resolves();
|
|
|
|
createLoginSessionStub.resolves({
|
|
sessionToken: 'test-session-token',
|
|
loginURL: 'https://www.zotero.org/authorize?token=test-session-token'
|
|
});
|
|
// Return "pending" so the poll loop keeps iterating
|
|
checkLoginSessionStub.resolves({ status: 'pending' });
|
|
|
|
// Start login but don't await -- it will keep polling
|
|
let loginPromise = win.Zotero_Preferences.Sync.linkAccount();
|
|
|
|
// Wait for the poll loop to start
|
|
await Zotero.Promise.delay(50);
|
|
|
|
win.Zotero_Preferences.Sync.cancelLogin();
|
|
|
|
// Wait for the login promise to resolve after cancellation
|
|
await loginPromise;
|
|
|
|
assert.equal(doc.querySelector('.account-login-default').hidden, false);
|
|
assert.equal(doc.querySelector('.account-login-pending').hidden, true);
|
|
assert.isTrue(cancelLoginSessionStub.calledWith('test-session-token'));
|
|
|
|
cancelLoginSessionStub.restore();
|
|
});
|
|
});
|
|
});
|
|
});
|