Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #6869 from lightsofapollo/calendar-better-login

Bug 810153 - Better login system for calendar
  • Loading branch information...
commit 8ca3aa6ce4353d2bccb33381d867374e6fa5e0dd 2 parents 6e6fa07 + b664b4c
@lightsofapollo lightsofapollo authored
View
19 apps/calendar/index.html
@@ -20,6 +20,7 @@
<link rel="stylesheet" type="text/css" href="/shared/style/confirm.css" />
<link rel="stylesheet" type="text/css" href="/shared/style/switches.css" />
<link rel="stylesheet" type="text/css" href="/shared/style/status.css" />
+ <link rel="stylesheet" type="text/css" href="/shared/style_unstable/progress_activity.css" />
<script src="/js/mozalarm_shim.js" type="text/javascript" charset="utf-8"></script>
@@ -350,6 +351,12 @@ <h1 data-l10n-id="add-event-header">
<h1 data-l10n-id="account-header">Account</h1>
</header>
+
+ <section class="during-progress">
+ <p data-l10n-id="modify-account-setting-up">SeTtiNg uP ACcOunt</p>
+ <progress class="small"></progress>
+ </section>
+
<form>
<fieldset>
<ol>
@@ -369,13 +376,11 @@ <h1 data-l10n-id="account-header">Account</h1>
</ol>
</fieldset>
</form>
-
- <section role="status">
- <p class="errors"></p>
- </section>
-
- <div class="during-progress">
- </div>
+
+ <section role="status">
+ <p class="errors"></p>
+ </section>
+
<a data-l10n-id="remove-account-data" role="button" href="#remove-account-dialog" class="danger delete-record">
Remove Local Data
View
5 apps/calendar/js/app.js
@@ -24,6 +24,10 @@ Calendar.App = (function(window) {
CreateAccount: [
{type: 'Templates', name: 'Account'}
],
+ ModifyAccount: [
+ {type: 'Utils', name: 'AccountCreation'},
+ {type: 'Style', name: 'ModifyAccountView'}
+ ],
Day: [
{type: 'Views', name: 'DayChild'},
{type: 'Views', name: 'TimeParent'}
@@ -165,7 +169,6 @@ Calendar.App = (function(window) {
this.dateFormat = navigator.mozL10n.DateTimeFormat();
- this.syncController.observe();
this.timeController.observe();
this.alarmController.observe();
View
140 apps/calendar/js/controllers/sync.js
@@ -10,116 +10,102 @@ Calendar.ns('Controllers').Sync = (function() {
*/
function Sync(app) {
this.app = app;
+ this.pending = 0;
+
Calendar.Responder.call(this);
}
Sync.prototype = {
__proto__: Calendar.Responder.prototype,
- observe: function() {
- var self = this;
- var account = this.app.store('Account');
+ _incrementPending: function() {
+ if (!this.pending)
+ this.emit('syncStart');
- // used instead of bind for testing reasons.
- account.on('add', function(id, data) {
- self.emit('sync start');
- self._syncAccount(data, function() {
- self.emit('sync complete');
- });
- });
+ this.pending++;
+ },
+
+ _resolvePending: function() {
+ if (!(--this.pending)) {
+ this.emit('syncComplete');
+ }
},
/**
* Sync all accounts, calendars, events.
+ * There is no callback for all intentionally.
+ *
+ * Use:
+ *
+ * controller.once('syncComplete', cb);
+ *
*/
- sync: function(callback) {
- var key;
- var self = this;
+ all: function() {
var account = this.app.store('Account');
- var pending = 0;
- var errorList = [];
-
- function next(err) {
- if (err) {
- errorList.push(err);
- }
-
- if (!(--pending)) {
- if (callback) {
- if (errorList.length) {
- callback(errorList);
- } else {
- callback(null);
- }
- }
- self.emit('sync complete');
- }
- }
-
- this.emit('sync start');
-
- for (key in account.cached) {
- pending++;
- this._syncAccount(
- account.cached[key], next
- );
- }
- if (!pending) {
- callback();
- this.emit('sync complete');
+ for (var key in account.cached) {
+ this.account(account.cached[key]);
}
},
- _syncAccount: function(model, callback) {
+ /**
+ * Initiates a sync for a single calendar.
+ *
+ * @param {Object} account parent of calendar.
+ * @param {Object} calendar specific calendar to sync.
+ * @param {Function} [callback] optional callback.
+ */
+ calendar: function(account, calendar, callback) {
+ var store = this.app.store('Calendar');
var self = this;
- var account = this.app.store('Account');
- var calendar = this.app.store('Calendar');
- account.sync(model, function(err) {
- if (err) {
- //TODO: Implement error handling to show
- // UI to change user/password, etc..
+ this._incrementPending();
+ store.sync(account, calendar, function(err) {
+ self._resolvePending();
+ if (callback)
callback(err);
- return;
- }
+ });
+ },
- var pending = 0;
- var cals = calendar.remotesByAccount(
- model._id
- );
+ /**
+ * Initiates a sync of a single account and all
+ * associated calendars (calendars that exist after
+ * the full sync of the account itself).
+ *
+ * @param {Object} account sync target.
+ * @param {Function} [callback] optional callback.
+ */
+ account: function(account, callback) {
+ var accountStore = this.app.store('Account');
+ var calendarStore = this.app.store('Calendar');
- var key;
- var errorList = [];
+ var self = this;
- function next(err) {
- if (err) {
- errorList.push(err);
- }
+ this._incrementPending();
+ accountStore.sync(account, function(err) {
+ // find all calendars
+ var calendars = calendarStore.remotesByAccount(
+ account._id
+ );
+
+ var pending = 0;
+ function next() {
if (!(--pending)) {
- if (errorList.length) {
- callback(errorList);
- } else {
- callback(null);
- }
+ self._resolvePending();
+
+ if (callback)
+ callback();
}
}
- for (key in cals) {
+ for (var key in calendars) {
pending++;
- calendar.sync(
- model,
- cals[key],
- next
- );
+ self.calendar(account, calendars[key], next);
}
});
}
-
};
return Sync;
-
}());
-
View
8 apps/calendar/js/controllers/time.js
@@ -149,8 +149,8 @@ Calendar.ns('Controllers').Time = (function() {
// handle cache pause/resume
var sync = this.app.syncController;
- sync.on('sync start', this);
- sync.on('sync complete', this);
+ sync.on('syncStart', this);
+ sync.on('syncComplete', this);
// XXX: case that the event name is so generic
// we handle it here directly.
@@ -454,10 +454,10 @@ Calendar.ns('Controllers').Time = (function() {
var type;
switch (event.type) {
- case 'sync start':
+ case 'syncStart':
this.cacheLocked = true;
break;
- case 'sync complete':
+ case 'syncComplete':
this.cacheLocked = false;
this.purgeCache();
break;
View
2  apps/calendar/js/db.js
@@ -411,7 +411,7 @@
this.once('loaded', function() {
if ('syncController' in Calendar.App && navigator.onLine) {
- Calendar.App.syncController.sync(function() {
+ Calendar.App.syncController.all(function() {
debug('begin resync after reset');
});
} else {
View
5 apps/calendar/js/service/caldav.js
@@ -624,6 +624,11 @@ Calendar.ns('Service').Caldav = (function() {
}
function handleResponse(url, data) {
+ if (!data) {
+ // throw some error;
+ console.log('Could not sync: ', url);
+ return;
+ }
var etag = data.getetag.value;
if (url in cache) {
// don't need to track this for missing events.
View
88 apps/calendar/js/utils/account_creation.js
@@ -0,0 +1,88 @@
+Calendar.ns('Utils').AccountCreation = (function() {
+
+ /**
+ * Helper class to create accounts.
+ * Emits events during the process of
+ * creation to allow views to hook into
+ * the full cycle while further separating
+ * this logic from their own.
+ *
+ *
+ * Events:
+ *
+ * - authorize
+ * - calendar sync
+ *
+ *
+ * @param {Calendar.App} app instance of app.
+ */
+ function AccountCreation(app) {
+ this.app = app || Calendar.App;
+
+ Calendar.Responder.call(this);
+ }
+
+ AccountCreation.prototype = {
+ __proto__: Calendar.Responder.prototype,
+
+ /**
+ * Sends a request to create an account.
+ *
+ * @param {Calendar.Models.Account} model account details.
+ * @param {Function} callback fired when entire transaction is complete.
+ */
+ send: function(model, callback) {
+ var self = this;
+ var accountStore = this.app.store('Account');
+ var calendarStore = this.app.store('Calendar');
+
+ // begin by persisting the account
+ accountStore.verifyAndPersist(model, function(accErr, id, result) {
+
+ if (accErr) {
+ // we bail when we cannot create the account
+ // but also give custom error events.
+ self.emit('authorizeError', accErr);
+ callback(accErr);
+ return;
+ }
+
+
+ self.emit('authorize', result);
+
+ // finally sync the account so when
+ // we exit the request the user actually
+ // has some calendars. This should not take
+ // too long (compared to event sync).
+ accountStore.sync(result, function(syncErr) {
+ if (syncErr) {
+ self.emit('calendarSyncError', syncErr);
+ callback(syncErr);
+ return;
+ }
+
+ self.emit('calendarSync');
+
+ // begin sync of calendars
+ var calendars = calendarStore.remotesByAccount(
+ result._id
+ );
+
+ // note we don't wait for any of this to complete
+ // we just begin the sync and let the event handlers
+ // on the sync controller do the work.
+ for (var key in calendars) {
+ self.app.syncController.calendar(
+ result,
+ calendars[key]
+ );
+ }
+
+ callback(null, result);
+ });
+ });
+ }
+ };
+
+ return AccountCreation;
+}());
View
47 apps/calendar/js/views/modify_account.js
@@ -6,6 +6,12 @@
this.save = this.save.bind(this);
this.deleteRecord = this.deleteRecord.bind(this);
this.cancel = this.cancel.bind(this);
+
+ this.accountHandler = new Calendar.Utils.AccountCreation(
+ this.app
+ );
+
+ this.accountHandler.on('authorizeError', this);
}
ModifyAccount.prototype = {
@@ -64,12 +70,16 @@
return this._fields;
},
- _clearErrors: function() {
- this.errors.textContent = '';
- },
+ handleEvent: function(event) {
+ var type = event.type;
+ var data = event.data;
- _displayError: function(err) {
- this.showErrors(err);
+ switch (type) {
+ case 'authorizeError':
+ // we only expect one argument an error object.
+ this.showErrors(data[0]);
+ break;
+ }
},
updateForm: function() {
@@ -122,7 +132,10 @@
list.add(this.progressClass);
- this._persistForm(function(err) {
+ this.errors.textContent = '';
+ this.updateModel();
+
+ this.accountHandler.send(this.model, function(err) {
list.remove(self.progressClass);
if (!err) {
self.app.go(self.completeUrl);
@@ -131,28 +144,6 @@
},
/**
- * Persist the form
- *
- * @param {Function} callback node style.
- */
- _persistForm: function(callback) {
- var self = this;
- var store = self.app.store('Account');
-
- this._clearErrors();
- this.updateModel();
-
- store.verifyAndPersist(this.model, function(err, id, model) {
- if (err) {
- self._displayError(err);
- callback(err);
- return;
- }
- callback(null, model);
- });
- },
-
- /**
* @param {String} preset name of value in Calendar.Presets.
*/
_createModel: function(preset, callback) {
View
12 apps/calendar/js/views/settings.js
@@ -67,12 +67,12 @@
break;
// hide sync button
- case 'sync start':
+ case 'syncStart':
this.syncProgressTarget.classList.add(this.syncClass);
break;
// show sync button
- case 'sync complete':
+ case 'syncComplete':
this.syncProgressTarget.classList.remove(this.syncClass);
break;
}
@@ -88,8 +88,8 @@
// sync controller events
var controller = this.app.syncController;
- controller.on('sync start', this);
- controller.on('sync complete', this);
+ controller.on('syncStart', this);
+ controller.on('syncComplete', this);
// dom events
this.syncButton.addEventListener('click', this._onSyncClick.bind(this));
@@ -123,9 +123,9 @@
},
_onSyncClick: function() {
- // trigger the sync the sync start/complete events
+ // trigger the sync the syncStart/complete events
// will hide/show the button.
- this.app.syncController.sync();
+ this.app.syncController.all();
},
_update: function(id, model) {
View
1  apps/calendar/locales/calendar.en-US.properties
@@ -35,6 +35,7 @@ advanced-settings-personalization=Personalization
field-user.placeholder=Username
field-password.placeholder=Password
field-full-url.placeholder=URL
+modify-account-setting-up=Please wait while your account is set up
# presets
View
29 apps/calendar/style/modify_account_view.css
@@ -0,0 +1,29 @@
+#modify-account-view.preset-google .full-url,
+#modify-account-view.preset-yahoo .full-url {
+ display: none;
+}
+
+#modify-account-view.create .delete-record {
+ display: none;
+}
+
+:target#remove-account-dialog {
+ display: block;
+}
+
+#modify-account-view .during-progress {
+ display: none;
+}
+
+#modify-account-view.in-progress .during-progress {
+ display: block;
+ height: 100%;
+ width: 100%;
+ padding: 10% 0%;
+ text-align: center;
+}
+
+#modify-account-view.in-progress button,
+#modify-account-view.in-progress form {
+ display: none;
+}
View
15 apps/calendar/style/ui.css
@@ -172,19 +172,6 @@ ol.link-list a {
/* modify account view */
-#modify-account-view.preset-google .full-url,
-#modify-account-view.preset-yahoo .full-url {
- display: none;
-}
-
-#modify-account-view.create .delete-record {
- display: none;
-}
-
-:target#remove-account-dialog {
- display: block;
-}
-
#remove-account-dialog {
display: none;
}
@@ -570,4 +557,4 @@ section[role="status"].active {
/* Hide on initial render to prevent flashing */
#settings {
visibility: hidden;
-}
+}
View
281 apps/calendar/test/unit/controllers/sync_test.js
@@ -20,15 +20,18 @@ suite('controllers/sync', function() {
this.timeout(10000);
app = testSupport.calendar.app();
+ db = app.db;
subject = new Calendar.Controllers.Sync(app);
calendar = app.store('Calendar');
account = app.store('Account');
event = app.store('Event');
- accModel = Factory('account');
+ accModel = Factory('account', {
+ _id: 'one'
+ });
- event.db.open(function(err) {
+ db.open(function(err) {
if (err) {
done(err);
return;
@@ -39,169 +42,213 @@ suite('controllers/sync', function() {
teardown(function(done) {
testSupport.calendar.clearStore(
- event.db,
+ db,
['accounts', 'calendars', 'events', 'busytimes'],
done
);
});
teardown(function() {
- event.db.close();
+ db.close();
});
- test('#observe', function(done) {
- var model = Factory('account');
- var calledWith;
- var syncStart;
- var syncEnd;
-
- function complete() {
- done(function() {
- assert.equal(calledWith[0], model);
- assert.ok(syncStart, 'start sync');
- assert.ok(syncEnd, 'end sync');
- });
- };
-
- subject.on('sync start', function() {
- syncStart = true;
- });
-
- subject.on('sync complete', function() {
- syncEnd = true;
- });
-
- subject.observe();
-
- subject._syncAccount = function(data) {
- calledWith = arguments;
- setTimeout(complete, 0);
- var cb = arguments[1];
- cb();
- }
-
- account.persist(model);
+ test('initialization', function() {
+ assert.equal(subject.app, app);
+ assert.equal(subject.pending, 0);
});
- suite('#sync', function() {
+ suite('#all', function() {
var list = [];
- setup(function() {
+ setup(function(done) {
+ var trans = db.transaction('accounts', 'readwrite');
+
list.push(Factory('account'));
list.push(Factory('account'));
- });
- setup(function(done) {
- account.persist(list[0], done);
- });
+ account.persist(list[0], trans);
+ account.persist(list[1], trans);
- setup(function(done) {
- account.persist(list[1], done);
+ trans.oncomplete = function() {
+ done();
+ };
});
- test('sync account', function(done) {
+ test('sync account', function() {
var calledModels = [];
- subject._syncAccount = function(model, cb) {
+ subject.account = function(model) {
calledModels.push(model);
- setTimeout(function() {
- cb(null);
- }, 0);
}
- subject.sync(function() {
- done(function() {
- assert.deepEqual(
- calledModels,
- list
- );
- });
- });
+ subject.all();
+
+ assert.deepEqual(
+ calledModels,
+ list,
+ 'requests a sync of all accounts'
+ );
});
});
- suite('#_syncAccount', function() {
- var list;
+ suite('individual sync operations', function() {
+ var calendars;
+ var accountSyncCall;
+ var calendarSyncCalls;
+ var events;
+
+ function assertEmit(event) {
+ assert.isTrue(
+ (event in events),
+ 'has emitted ' + event
+ );
+ }
+
+ function assertDoesNotEmit(event) {
+ assert.isFalse(
+ (event in events),
+ 'emitted ' + event
+ );
+ }
+
+ var handler = {
+ handleEvent: function(event) {
+ events[event.type] = event.data;
+ }
+ };
setup(function(done) {
- list = [];
- account.persist(accModel, done);
- });
+ var trans = db.transaction(
+ ['accounts', 'calendars'],
+ 'readwrite'
+ );
+
+ trans.oncomplete = function() {
+ done();
+ };
+
+ // setup events
+ events = {};
+ subject.on('syncStart', handler);
+ subject.on('syncComplete', handler);
+
+ // setup mocks
+ calendarSyncCalls = [];
+ account.sync = function() {
+ accountSyncCall = Array.slice(arguments);
+ }
- function addCalendar() {
- setup(function(done) {
+ calendar.sync = function() {
+ calendarSyncCalls.push(Array.slice(arguments));
+ };
+
+ // setup db
+ calendars = [];
+ account.persist(accModel, trans);
+
+ var numberOfCalendars = 2;
+ while (numberOfCalendars--) {
var item = Factory('calendar', {
accountId: accModel._id
});
- list.push(item);
- calendar.persist(item, done);
- });
- }
+ calendars.push(item);
+ calendar.persist(item, trans);
+ }
+ });
- addCalendar();
- addCalendar();
+ suite('#account', function() {
+ test('success', function() {
+ var firedCallback = false;
+ subject.account(accModel, function() {
+ firedCallback = true;
+ });
- var syncedCals;
+ assertEmit('syncStart');
+ assert.equal(accountSyncCall[0], accModel);
- setup(function() {
- syncedCals = [];
- calendar.sync = function(acc, cal, cb) {
- setTimeout(function() {
- syncedCals.push([acc, cal]);
- cb(null);
- }, 0);
- };
+ // successful sync
+ accountSyncCall[1]();
+
+ // not done until calendars have synced
+ assertDoesNotEmit('syncComplete');
+ assert.length(calendarSyncCalls, 2);
+
+ // complete all calls
+ calendarSyncCalls.forEach(function(item) {
+ var cb = item.pop();
+ assert.ok(
+ !firedCallback,
+ 'does not fire callback before all are complete'
+ );
+ cb();
+ });
+
+ assert.ok(firedCallback, 'fires after all sync operations');
+ assertEmit('syncComplete');
+ });
});
- test('sync /w calendars', function(done) {
- var calledAcc;
+ suite('#calendar', function() {
- account.sync = function(model, cb) {
- setTimeout(function() {
- calledAcc = model;
- cb(null);
- }, 0);
- }
+ test('multiple in progress', function() {
+ var complete = 0;
- subject._syncAccount(accModel, function(err) {
- if (err) {
- done(err);
- return;
- }
-
- done(function() {
- assert.deepEqual(
- syncedCals,
- [
- [accModel, list[0]],
- [accModel, list[1]]
- ]
- );
+ subject.calendar(accModel, calendars[0]);
+ assertEmit('syncStart');
+ delete events['syncStart'];
- assert.equal(
- calledAcc,
- accModel
- );
+ subject.calendar(accModel, calendars[1]);
+ assertDoesNotEmit('syncStart');
+
+ var firstSync = calendarSyncCalls.shift();
+ firstSync[firstSync.length - 1]();
+
+ // because there are two pending...
+ assertDoesNotEmit('syncComplete');
+
+ var secondSync = calendarSyncCalls.shift();
+ secondSync[secondSync.length - 1]();
+
+ // now both are fully completed.
+ assertEmit('syncComplete');
+ });
+
+ test('success', function(done) {
+ subject.calendar(accModel, calendars[0], function() {
+ assertEmit('syncComplete');
+ done();
});
+ assertEmit('syncStart');
+
+ assert.length(calendarSyncCalls, 1, 'emits syncComplete');
+
+ var sync = calendarSyncCalls[0];
+
+ assert.deepEqual(
+ sync.slice(0, 2),
+ [accModel, calendars[0]]
+ );
+
+ assertDoesNotEmit('syncComplete');
+ sync[sync.length - 1]();
});
- });
- test('sync - account fail', function(done) {
- account.sync = function(model, cb) {
- setTimeout(function() {
- cb(new Error('err'));
- }, 0);
- };
+ test('failure', function(done) {
+ var sentErr = new Error();
- subject._syncAccount(accModel, function(err) {
- done(function() {
- assert.equal(syncedCals.length, 0);
- assert.instanceOf(err, Error);
+ subject.calendar(accModel, calendars[0], function(err) {
+ assert.equal(err, sentErr);
+ assertEmit('syncComplete');
+ done();
});
+
+ var sync = calendarSyncCalls.shift();
+ assert.ok(sync);
+
+ sync[sync.length - 1](sentErr);
});
});
-
});
});
View
6 apps/calendar/test/unit/controllers/time_test.js
@@ -60,10 +60,10 @@ suite('controllers/time', function() {
suite('sync cache lock', function() {
setup(function() {
subject.observe();
- app.syncController.emit('sync start');
+ app.syncController.emit('syncStart');
});
- test('locks after sync start', function() {
+ test('locks after syncStart', function() {
assert.isTrue(subject.cacheLocked);
});
@@ -74,7 +74,7 @@ suite('controllers/time', function() {
calledWith = true;
}
- app.syncController.emit('sync complete');
+ app.syncController.emit('syncComplete');
assert.isFalse(subject.cacheLocked);
assert.isTrue(calledWith, 'purged');
});
View
2  apps/calendar/test/unit/db_test.js
@@ -517,7 +517,7 @@ suite('db', function() {
this.timeout(12000);
Calendar.App.syncController = {
- sync: function() {
+ all: function() {
syncCalled = true;
}
};
View
174 apps/calendar/test/unit/utils/account_creation_test.js
@@ -0,0 +1,174 @@
+requireApp('calendar/test/unit/helper.js', function() {
+ requireLib('utils/account_creation.js');
+ requireLib('models/account.js');
+});
+
+suite('utils/account_creation', function() {
+ var subject;
+ var accountStore;
+ var calendarStore;
+ var app;
+
+ setup(function() {
+ app = testSupport.calendar.app();
+ accountStore = app.store('Account');
+ calendarStore = app.store('Calendar');
+
+ subject = new Calendar.Utils.AccountCreation(
+ app
+ );
+ });
+
+ test('initialization', function() {
+ assert.equal(subject.app, app);
+ assert.instanceOf(subject, Calendar.Responder);
+ });
+
+ suite('#send', function() {
+ var verifyCall;
+ var syncCall;
+ var calendarSyncCalls = [];
+ var events;
+ var model;
+
+ var calendars = {
+ one: {},
+ two: {}
+ };
+
+ var handler = {
+ handleEvent: function(event) {
+ var type = event.type;
+ var data = event.data;
+
+ if (type in events) {
+ throw new Error('event ' + type + ' fired twice');
+ }
+
+ events[type] = data;
+ }
+ };
+
+ setup(function() {
+ events = {};
+ syncCall = null;
+ verifyCall = null;
+ calendarSyncCalls.length = 0;
+
+ model = new Calendar.Models.Account(
+ Factory('account')
+ );
+
+
+ subject.on('authorizeError', handler);
+ subject.on('authorize', handler);
+ subject.on('calendarSyncError', handler);
+ subject.on('calendarSync', handler);
+
+ accountStore.verifyAndPersist = function() {
+ verifyCall = arguments;
+ }
+
+ accountStore.sync = function() {
+ syncCall = arguments;
+ }
+
+ calendarStore.remotesByAccount = function() {
+ return calendars;
+ };
+
+ calendarStore.sync = function() {
+ calendarSyncCalls.push(Array.slice(arguments));
+ };
+ });
+
+ test('success', function(done) {
+ var savedAccount;
+
+ subject.send(model, function(err, result) {
+ assert.ok(!err, 'is a success');
+
+ assert.ok(verifyCall);
+ assert.ok(syncCall);
+ assert.equal(result, savedAccount);
+
+ done();
+ });
+
+ assert.equal(verifyCall[0], model, 'calls verify');
+ assert.ok(!syncCall, 'has not synced yet');
+
+ var savedAccount = model.toJSON();
+ savedAccount._id = 1;
+ verifyCall[1](null, savedAccount._id, savedAccount);
+
+ assert.deepEqual(
+ events.authorize,
+ [savedAccount]
+ );
+
+ assert.equal(syncCall[0], savedAccount, 'syncs with persisted model');
+
+ // TODO: we don't pass calendars should we?
+ syncCall[1]();
+
+ // verify each calendar was synced..
+ assert.length(calendarSyncCalls, 2);
+
+ assert.deepEqual(
+ calendarSyncCalls[0].slice(0, 2),
+ [savedAccount, calendars.one]
+ );
+
+ assert.deepEqual(
+ calendarSyncCalls[1].slice(0, 2),
+ [savedAccount, calendars.two]
+ );
+
+ assert.deepEqual(
+ events.calendarSync,
+ []
+ );
+ });
+
+ test('account failure', function(done) {
+ var accountErr = new Error();
+
+ subject.send(model, function(err) {
+ assert.equal(err, accountErr);
+ done();
+ });
+
+ assert.ok(verifyCall, 'calls verify');
+ verifyCall[1](accountErr);
+
+ assert.deepEqual(
+ events.authorizeError,
+ [accountErr]
+ );
+ });
+
+ test('calendar failure', function(done) {
+ var calendarErr = new Error();
+
+ subject.send(model, function(err) {
+ assert.deepEqual(
+ events.calendarSyncError,
+ [calendarErr]
+ );
+
+ assert.equal(err, calendarErr);
+ done();
+ });
+
+ // send account
+ var account = model.toJSON();
+ account._id = 1;
+ verifyCall[1](null, account);
+
+ // on sync we send the error
+ syncCall[1](calendarErr);
+ });
+
+ });
+});
View
163 apps/calendar/test/unit/views/modify_account_test.js
@@ -1,9 +1,10 @@
requireApp('calendar/test/unit/helper.js', function() {
- requireApp('calendar/js/templates/account.js');
- requireApp('calendar/js/presets.js');
- requireApp('calendar/js/provider/local.js');
- requireApp('calendar/js/models/account.js');
- requireApp('calendar/js/views/modify_account.js');
+ requireLib('templates/account.js');
+ requireLib('presets.js');
+ requireLib('provider/local.js');
+ requireLib('models/account.js');
+ requireLib('utils/account_creation.js');
+ requireLib('views/modify_account.js');
});
suite('views/modify_account', function() {
@@ -74,6 +75,11 @@ suite('views/modify_account', function() {
model: account,
type: 'new'
});
+
+ assert.instanceOf(
+ subject.accountHandler,
+ Calendar.Utils.AccountCreation
+ );
});
});
@@ -94,6 +100,28 @@ suite('views/modify_account', function() {
assert.ok(subject.form);
});
+ suite('#handleEvent', function() {
+ var handler;
+ var showErrorCall;
+
+ setup(function() {
+ handler = subject.accountHandler;
+ subject.showErrors = function() {
+ showErrorCall = arguments;
+ };
+ });
+
+ test('authorizeError', function() {
+ var sentErr = new Error();
+ handler.emit('authorizeError', sentErr);
+
+ assert.deepEqual(
+ showErrorCall,
+ [sentErr]
+ );
+ });
+ });
+
test('#fields', function() {
var result = subject.fields;
@@ -107,48 +135,6 @@ suite('views/modify_account', function() {
hasName('fullUrl');
});
- suite('#_persistForm', function() {
- var calledSetup;
- var calledPersist;
-
- var store = {
- // mock out persist
- // we are not trying to
- // test db functionality here.
- verifyAndPersist: function(obj, callback) {
- calledPersist = arguments;
- setTimeout(function() {
- callback(null, obj);
- }, 0);
- }
- };
-
- setup(function() {
- calledPersist = null;
- // mock out account store
- app.db._stores.Account = store;
- });
-
- suite('success', function() {
-
- test('result', function(done) {
- getField('user').value = 'user';
- getField('password').value = 'pass';
-
- subject._persistForm(function() {
- done(function() {
- assert.equal(calledPersist[0], subject.model);
-
- var model = subject.model;
-
- assert.equal(model.user, 'user');
- assert.equal(model.password, 'pass');
- });
- });
- });
- });
- });
-
suite('#deleteRecord', function() {
var calledShow;
var calledRemove;
@@ -200,57 +186,64 @@ suite('views/modify_account', function() {
setup(function() {
calledWith = null;
subject.completeUrl = '/settings';
- });
+ Calendar.Test.FakePage.shown = null;
- test('on success', function(done) {
- subject._persistForm = function(callback) {
- setTimeout(function() {
- callback(null, true);
- done(function() {
- assert.equal(Calendar.Test.FakePage.shown, subject.completeUrl);
- assert.isFalse(hasClass(subject.progressClass));
- });
- }, 0);
- }
+ subject.accountHandler.send = function() {
+ calledWith = arguments;
+ };
+ });
+ test('clears errors', function() {
+ subject.errors.textContent = 'foo';
subject.save();
- assert.isTrue(hasClass(subject.progressClass));
+ assert.ok(!subject.errors.textContent, 'clears text');
});
- test('on failure', function(done) {
- subject._persistForm = function(callback) {
- setTimeout(function() {
- callback(new Error('ouch'));
- done(function() {
- assert.ok(!calledWith);
- assert.isFalse(hasClass(subject.progressClass));
- });
- }, 0);
- }
+ test('updates form', function() {
+ subject.fields['user'].value = 'iupdatedu';
+ subject.save();
+ assert.equal(subject.model.user, 'iupdatedu');
+ });
+ test('on success', function() {
subject.save();
assert.isTrue(hasClass(subject.progressClass));
+
+ assert.equal(calledWith[0], subject.model);
+ calledWith[1]();
+
+ assert.equal(
+ Calendar.Test.FakePage.shown,
+ subject.completeUrl,
+ 'redirects to complete url'
+ );
+
+ assert.isFalse(
+ hasClass(subject.progressClass),
+ 'disabled progress class'
+ );
});
- });
+ test('on failure', function() {
+ subject.save();
+ assert.ok(calledWith, 'sends request');
+ assert.equal(calledWith[0], subject.model);
- test('#_clearErrors', function() {
- subject.errors.textContent = 'foo';
+ assert.isTrue(hasClass(subject.progressClass));
+ calledWith[1](new Error());
- subject._clearErrors();
+ assert.ok(
+ !hasClass(subject.progressClass),
+ 'hides progress'
+ );
- assert.equal(subject.errors.textContent, '');
- });
+ assert.notEqual(
+ Calendar.Test.FakePage.shown,
+ subject.completeUrl,
+ 'does not redirect on complete'
+ );
+ });
- test('#_displayError', function() {
- subject.errors.textContent = '';
- var error = new Error();
- error.name = 'default';
- subject._displayError(error);
- // mozL10n library has it's own tests,
- // we only check if the field has the error
- // identifier added
- assert.equal(subject.errors.textContent, 'default');
});
test('#_createModel', function() {
@@ -401,7 +394,7 @@ suite('views/modify_account', function() {
test('save button', function() {
var called;
- subject._persistForm = function() {
+ subject.accountHandler.send = function() {
called = true;
}
View
6 apps/calendar/test/unit/views/settings_test.js
@@ -154,7 +154,7 @@ suite('views/settings', function() {
'not active initially'
);
- app.syncController.emit('sync start');
+ app.syncController.emit('syncStart');
});
teardown(function() {
@@ -166,7 +166,7 @@ suite('views/settings', function() {
});
test('complete', function() {
- app.syncController.emit('sync complete');
+ app.syncController.emit('syncComplete');
assert.ok(!classList.contains(subject.syncClass), 'remove active');
});
});
@@ -178,7 +178,7 @@ suite('views/settings', function() {
var calledWith;
var el = subject.syncButton;
- controller.sync = function() {
+ controller.all = function() {
calledWith = arguments;
}
Please sign in to comment.
Something went wrong with that request. Please try again.