Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Merge pull request #23078 from EverythingMe/1033641-create-offline
Browse files Browse the repository at this point in the history
Bug 1033641 - [Vertical Homescreen] [Collections] [SCR] Ability to create collection groups offline
  • Loading branch information
amirnissim committed Aug 27, 2014
2 parents 52c3473 + 30afdf9 commit 0059d16
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 86 deletions.
89 changes: 43 additions & 46 deletions apps/collection/js/create_collection.js
Expand Up @@ -10,7 +10,6 @@

(function(exports) {

var _ = navigator.mozL10n.get;
var eme = exports.eme;

function HandleCreate(activity) {
Expand Down Expand Up @@ -112,54 +111,58 @@
.then(generateIcons)
// XXX: We currently need to save before we populate info.
.then(saveAll)
.then(populateNativeInfo)
.then(generateIcons)
.then(saveAll)
.then(collections => {
// recovery scheme: population will fail if offline
return populateNativeInfo(collections)
.then(generateIcons)
.then(saveAll)
.catch(() => {
eme.error('NativeInfo task failed');
return collections;
});
})
.then(postResultIds)
.catch((ex) => {
eme.error('caught exception', ex);
postResultIds();
});
}

function onOffline() {
alert(navigator.mozL10n.get('network-error-message'));
activity.postResult(false);
}

function onOnline() {
CollectionsDatabase.getAllCategories()
.then(function doRequest(installed) {
request = eme.api.Categories.list().then(
function success(response) {
loading.style.display = 'none';

var data = response.response;
var categories = data.categories.filter(function filter(category) {
return installed.indexOf(category.categoryId) === -1;
});

Suggestions.load(categories)
.then(selected => {
createCollections(selected, data);
}, reason => {
eme.log('rejected with', reason);
activity.postResult(false);
});

}, function error(reason) {
eme.log('create-collection: error', reason);
activity.postError(reason === 'network error' ?
_('network-error-message') : undefined);
}).catch(function fail(ex) {
eme.log('create-collection: failed', ex);
activity.postError();
});
CollectionsDatabase.getAllCategories()
.then(function doRequest(installed) {
request = eme.api.Categories.list().then(
function success(response) {
loading.style.display = 'none';

var data = response.response;
var categories = data.categories.filter(function filter(category) {
return installed.indexOf(category.categoryId) === -1;
});

Suggestions.load(categories)
.then(selected => {
createCollections(selected, data);
}, reason => {
eme.log('rejected with', reason);
activity.postResult(false);
});

}, function error(reason) {
eme.log('create-collection: error', reason);

if (reason === 'network error') {
alert(navigator.mozL10n.get('network-error-message'));
}

activity.postResult(false);

}).catch(function fail(ex) {
eme.log('create-collection: failed', ex);
activity.postResult(false);
});

}, activity.postError);
}
}, activity.postResult.bind(null, false));

window.addEventListener('offline', onOffline);
cancel.addEventListener('click', function() {
// TODO request should always have an 'abort' method
// but sometimes it doesn't. find out why!
Expand All @@ -170,12 +173,6 @@
});

document.body.dataset.testReady = true;

if (navigator.onLine) {
onOnline();
} else {
onOffline();
}
}

navigator.mozSetMessageHandler('activity', function onActivity(activity) {
Expand Down
85 changes: 85 additions & 0 deletions apps/collection/test/unit/create_collection_test.js
@@ -0,0 +1,85 @@
'use strict';

/* global Promise */

require('/js/common.js');
require('/js/objects.js');
require('/js/collection_icon.js');
require('/shared/test/unit/mocks/mock_collections_database.js');
require('/shared/test/unit/mocks/mock_navigator_moz_set_message_handler.js');

var mocksForCollection = new MocksHelper(['CollectionsDatabase']).init();

suite('create', function() {
mocksForCollection.attachTestHelpers();

var realSetMessageHandler;
var realL10n;

var activity = {
source: {
name: 'create-collection',
data: {
maxIconSize: 60
}
},
postResult: () => {}
};

var mockL10n = {
get: key => key
};

var mockEme = {
init: () => Promise.resolve(),
log: console.log.bind(console),
api: {
Categories: {
list: function() {}
}
}
};

var mockSuggestions = {
load: (categories) => Promise.resolve([categories[0].categoryId])
};

suiteSetup(function() {
realL10n = navigator.mozL10n;
navigator.mozL10n = mockL10n;

realSetMessageHandler = navigator.mozSetMessageHandler;
navigator.mozSetMessageHandler = window.MockNavigatormozSetMessageHandler;
navigator.mozSetMessageHandler.mSetup();
});

suiteTeardown(function() {
navigator.mozL10n = realL10n;
navigator.mozSetMessageHandler = realSetMessageHandler;
});

setup(function(done) {
loadBodyHTML('/create.html');

window.eme = mockEme;
window.Suggestions = mockSuggestions;

require('/js/create_collection.js', function() {
done();
});
});

test('alerts a network error message when offline', function(done) {
var alertStub = this.sinon.stub(window, 'alert');
this.sinon.stub(mockEme.api.Categories, 'list')
.returns(Promise.reject('network error'));

navigator.mozSetMessageHandler.mTrigger('activity', activity);

setTimeout(() => {
assert.isTrue(alertStub.calledOnce);
done();
}, 10);

});
});
42 changes: 28 additions & 14 deletions apps/verticalhome/test/marionette/collection_offline_test.js
Expand Up @@ -36,30 +36,21 @@ marionette('Vertical - Collection', function() {
collection.setServerURL(server);
});

test('create collection shows message when offline', function() {
test('create collection shows offline message if no server response',
function() {

server.failAll();
collection.enterCreateScreen();

client.switchToFrame();
client.apps.switchToApp(Collection.URL);
server.unfailAll();

var expectedMsg = home.l10n(
'/locales-obj/en-US.json',
'network-error-message'
);

// Wait for listeners to be added
collection.waitForCreateScreenReady();

// This is not quite the same path the user sees during a collection create
// but it should still let us test quite a bit. Instead of following the
// navigator.isOnline path, we fire an offline event which will also show
// the same alert.
client.executeScript(function() {
setTimeout(function() {
window.dispatchEvent(new CustomEvent('offline'));
});
});

// Wait for the system alert to be populated with the expected message.
// Convert the alert to a RegExp.
expectedMsg = new RegExp('.*' + expectedMsg + '.*');
Expand All @@ -72,4 +63,27 @@ marionette('Vertical - Collection', function() {
return expectedMsg.test(msg);
});
});

test('create collection offline succeeds if response is cached',
function() {
var name1 = 'Around Me';
var name2 = 'Astrology';

// create a collection. expect it will cache the response
collection.enterCreateScreen();
collection.selectNew(name1);
client.apps.switchToApp(Home2.URL);
collection.getCollectionByName(name1);

// go offline, expect list to be retrieved from cache
server.failAll();

collection.enterCreateScreen();
collection.selectNew(name2);
client.apps.switchToApp(Home2.URL);
collection.getCollectionByName(name2);

server.unfailAll();
});

});
46 changes: 20 additions & 26 deletions shared/js/everythingme/api.js
Expand Up @@ -110,31 +110,6 @@
return promise;
}

/** Make a Request and
* 1. cache the response
* 2. if offline, get response from cache
*/
function CacheableRequest(service, method, options) {
return new Promise(function done(resolve, reject) {
if (navigator.onLine) {
Request(service, method, options).then(
function success(response) {
eme.Cache.addRequest(service, method, options, response);
resolve(response);
}, reject)
.catch();
}
else {
eme.Cache.getRequest(service, method, options)
.then(function success(cachedResponse) {
eme.log('using cached response:', service + '/' + method);
resolve(cachedResponse);
}, reject.bind(null, NETWORK_ERROR))
.catch(reject);
}
});
}

function SanitizeAppSearch(result) {
return new Promise(function(resolve, reject) {
// Sanitize app URLs returned from e.me
Expand Down Expand Up @@ -191,9 +166,28 @@
}
};

/** Make a Request and
* 1. on success: cache the response
* 2. on error: get response from cache
* 2.1 on error: reject with NETWORK_ERROR
*/
this.Categories = {
list: function list(options) {
return CacheableRequest('Categories', 'list', options);
var request = Request('Categories', 'list', options);

return request.then(
response => {
eme.Cache.addRequest('Categories', 'list', options, response);
return response;
},
() => {
return eme.Cache.getRequest('Categories', 'list', options)
.then(response => {
eme.log('using cached response (Categories/list)');
return response;
})
.catch(() => Promise.reject(NETWORK_ERROR));
});
}
};
}
Expand Down

0 comments on commit 0059d16

Please sign in to comment.