summaryrefslogtreecommitdiffstats
path: root/services/fxaccounts/tests/xpcshell/test_profile.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/fxaccounts/tests/xpcshell/test_profile.js')
-rw-r--r--services/fxaccounts/tests/xpcshell/test_profile.js409
1 files changed, 409 insertions, 0 deletions
diff --git a/services/fxaccounts/tests/xpcshell/test_profile.js b/services/fxaccounts/tests/xpcshell/test_profile.js
new file mode 100644
index 000000000..13adf8cbb
--- /dev/null
+++ b/services/fxaccounts/tests/xpcshell/test_profile.js
@@ -0,0 +1,409 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/FxAccountsCommon.js");
+Cu.import("resource://gre/modules/FxAccountsProfileClient.jsm");
+Cu.import("resource://gre/modules/FxAccountsProfile.jsm");
+
+const URL_STRING = "https://example.com";
+Services.prefs.setCharPref("identity.fxaccounts.settings.uri", "https://example.com/settings");
+
+const STATUS_SUCCESS = 200;
+
+/**
+ * Mock request responder
+ * @param {String} response
+ * Mocked raw response from the server
+ * @returns {Function}
+ */
+var mockResponse = function (response) {
+ let Request = function (requestUri) {
+ // Store the request uri so tests can inspect it
+ Request._requestUri = requestUri;
+ return {
+ setHeader: function () {},
+ head: function () {
+ this.response = response;
+ this.onComplete();
+ }
+ };
+ };
+
+ return Request;
+};
+
+/**
+ * Mock request error responder
+ * @param {Error} error
+ * Error object
+ * @returns {Function}
+ */
+var mockResponseError = function (error) {
+ return function () {
+ return {
+ setHeader: function () {},
+ head: function () {
+ this.onComplete(error);
+ }
+ };
+ };
+};
+
+var mockClient = function (fxa) {
+ let options = {
+ serverURL: "http://127.0.0.1:1111/v1",
+ fxa: fxa,
+ }
+ return new FxAccountsProfileClient(options);
+};
+
+const ACCOUNT_DATA = {
+ uid: "abc123"
+};
+
+function FxaMock() {
+}
+FxaMock.prototype = {
+ currentAccountState: {
+ profile: null,
+ get isCurrent() {
+ return true;
+ }
+ },
+
+ getSignedInUser: function () {
+ return Promise.resolve(ACCOUNT_DATA);
+ }
+};
+
+var mockFxa = function() {
+ return new FxaMock();
+};
+
+function CreateFxAccountsProfile(fxa = null, client = null) {
+ if (!fxa) {
+ fxa = mockFxa();
+ }
+ let options = {
+ fxa: fxa,
+ profileServerUrl: "http://127.0.0.1:1111/v1"
+ }
+ if (client) {
+ options.profileClient = client;
+ }
+ return new FxAccountsProfile(options);
+}
+
+add_test(function getCachedProfile() {
+ let profile = CreateFxAccountsProfile();
+ // a little pointless until bug 1157529 is fixed...
+ profile._cachedProfile = { avatar: "myurl" };
+
+ return profile._getCachedProfile()
+ .then(function (cached) {
+ do_check_eq(cached.avatar, "myurl");
+ run_next_test();
+ });
+});
+
+add_test(function cacheProfile_change() {
+ let fxa = mockFxa();
+/* Saving profile data disabled - bug 1157529
+ let setUserAccountDataCalled = false;
+ fxa.setUserAccountData = function (data) {
+ setUserAccountDataCalled = true;
+ do_check_eq(data.profile.avatar, "myurl");
+ return Promise.resolve();
+ };
+*/
+ let profile = CreateFxAccountsProfile(fxa);
+
+ makeObserver(ON_PROFILE_CHANGE_NOTIFICATION, function (subject, topic, data) {
+ do_check_eq(data, ACCOUNT_DATA.uid);
+// do_check_true(setUserAccountDataCalled); - bug 1157529
+ run_next_test();
+ });
+
+ return profile._cacheProfile({ avatar: "myurl" });
+});
+
+add_test(function cacheProfile_no_change() {
+ let fxa = mockFxa();
+ let profile = CreateFxAccountsProfile(fxa)
+ profile._cachedProfile = { avatar: "myurl" };
+// XXX - saving is disabled (but we can leave that in for now as we are
+// just checking it is *not* called)
+ fxa.setSignedInUser = function (data) {
+ throw new Error("should not update account data");
+ };
+
+ return profile._cacheProfile({ avatar: "myurl" })
+ .then((result) => {
+ do_check_false(!!result);
+ run_next_test();
+ });
+});
+
+add_test(function fetchAndCacheProfile_ok() {
+ let client = mockClient(mockFxa());
+ client.fetchProfile = function () {
+ return Promise.resolve({ avatar: "myimg"});
+ };
+ let profile = CreateFxAccountsProfile(null, client);
+
+ profile._cacheProfile = function (toCache) {
+ do_check_eq(toCache.avatar, "myimg");
+ return Promise.resolve();
+ };
+
+ return profile._fetchAndCacheProfile()
+ .then(result => {
+ do_check_eq(result.avatar, "myimg");
+ run_next_test();
+ });
+});
+
+// Check that a second profile request when one is already in-flight reuses
+// the in-flight one.
+add_task(function* fetchAndCacheProfileOnce() {
+ // A promise that remains unresolved while we fire off 2 requests for
+ // a profile.
+ let resolveProfile;
+ let promiseProfile = new Promise(resolve => {
+ resolveProfile = resolve;
+ });
+ let numFetches = 0;
+ let client = mockClient(mockFxa());
+ client.fetchProfile = function () {
+ numFetches += 1;
+ return promiseProfile;
+ };
+ let profile = CreateFxAccountsProfile(null, client);
+
+ let request1 = profile._fetchAndCacheProfile();
+ let request2 = profile._fetchAndCacheProfile();
+
+ // should be one request made to fetch the profile (but the promise returned
+ // by it remains unresolved)
+ do_check_eq(numFetches, 1);
+
+ // resolve the promise.
+ resolveProfile({ avatar: "myimg"});
+
+ // both requests should complete with the same data.
+ let got1 = yield request1;
+ do_check_eq(got1.avatar, "myimg");
+ let got2 = yield request1;
+ do_check_eq(got2.avatar, "myimg");
+
+ // and still only 1 request was made.
+ do_check_eq(numFetches, 1);
+});
+
+// Check that sharing a single fetch promise works correctly when the promise
+// is rejected.
+add_task(function* fetchAndCacheProfileOnce() {
+ // A promise that remains unresolved while we fire off 2 requests for
+ // a profile.
+ let rejectProfile;
+ let promiseProfile = new Promise((resolve,reject) => {
+ rejectProfile = reject;
+ });
+ let numFetches = 0;
+ let client = mockClient(mockFxa());
+ client.fetchProfile = function () {
+ numFetches += 1;
+ return promiseProfile;
+ };
+ let profile = CreateFxAccountsProfile(null, client);
+
+ let request1 = profile._fetchAndCacheProfile();
+ let request2 = profile._fetchAndCacheProfile();
+
+ // should be one request made to fetch the profile (but the promise returned
+ // by it remains unresolved)
+ do_check_eq(numFetches, 1);
+
+ // reject the promise.
+ rejectProfile("oh noes");
+
+ // both requests should reject.
+ try {
+ yield request1;
+ throw new Error("should have rejected");
+ } catch (ex) {
+ if (ex != "oh noes") {
+ throw ex;
+ }
+ }
+ try {
+ yield request2;
+ throw new Error("should have rejected");
+ } catch (ex) {
+ if (ex != "oh noes") {
+ throw ex;
+ }
+ }
+
+ // but a new request should work.
+ client.fetchProfile = function () {
+ return Promise.resolve({ avatar: "myimg"});
+ };
+
+ let got = yield profile._fetchAndCacheProfile();
+ do_check_eq(got.avatar, "myimg");
+});
+
+// Check that a new profile request within PROFILE_FRESHNESS_THRESHOLD of the
+// last one doesn't kick off a new request to check the cached copy is fresh.
+add_task(function* fetchAndCacheProfileAfterThreshold() {
+ let numFetches = 0;
+ let client = mockClient(mockFxa());
+ client.fetchProfile = function () {
+ numFetches += 1;
+ return Promise.resolve({ avatar: "myimg"});
+ };
+ let profile = CreateFxAccountsProfile(null, client);
+ profile.PROFILE_FRESHNESS_THRESHOLD = 1000;
+
+ yield profile.getProfile();
+ do_check_eq(numFetches, 1);
+
+ yield profile.getProfile();
+ do_check_eq(numFetches, 1);
+
+ yield new Promise(resolve => {
+ do_timeout(1000, resolve);
+ });
+
+ yield profile.getProfile();
+ do_check_eq(numFetches, 2);
+});
+
+// Check that a new profile request within PROFILE_FRESHNESS_THRESHOLD of the
+// last one *does* kick off a new request if ON_PROFILE_CHANGE_NOTIFICATION
+// is sent.
+add_task(function* fetchAndCacheProfileBeforeThresholdOnNotification() {
+ let numFetches = 0;
+ let client = mockClient(mockFxa());
+ client.fetchProfile = function () {
+ numFetches += 1;
+ return Promise.resolve({ avatar: "myimg"});
+ };
+ let profile = CreateFxAccountsProfile(null, client);
+ profile.PROFILE_FRESHNESS_THRESHOLD = 1000;
+
+ yield profile.getProfile();
+ do_check_eq(numFetches, 1);
+
+ Services.obs.notifyObservers(null, ON_PROFILE_CHANGE_NOTIFICATION, null);
+
+ yield profile.getProfile();
+ do_check_eq(numFetches, 2);
+});
+
+add_test(function tearDown_ok() {
+ let profile = CreateFxAccountsProfile();
+
+ do_check_true(!!profile.client);
+ do_check_true(!!profile.fxa);
+
+ profile.tearDown();
+ do_check_null(profile.fxa);
+ do_check_null(profile.client);
+
+ run_next_test();
+});
+
+add_test(function getProfile_ok() {
+ let cachedUrl = "myurl";
+ let didFetch = false;
+
+ let profile = CreateFxAccountsProfile();
+ profile._getCachedProfile = function () {
+ return Promise.resolve({ avatar: cachedUrl });
+ };
+
+ profile._fetchAndCacheProfile = function () {
+ didFetch = true;
+ return Promise.resolve();
+ };
+
+ return profile.getProfile()
+ .then(result => {
+ do_check_eq(result.avatar, cachedUrl);
+ do_check_true(didFetch);
+ run_next_test();
+ });
+});
+
+add_test(function getProfile_no_cache() {
+ let fetchedUrl = "newUrl";
+ let profile = CreateFxAccountsProfile();
+ profile._getCachedProfile = function () {
+ return Promise.resolve();
+ };
+
+ profile._fetchAndCacheProfile = function () {
+ return Promise.resolve({ avatar: fetchedUrl });
+ };
+
+ return profile.getProfile()
+ .then(result => {
+ do_check_eq(result.avatar, fetchedUrl);
+ run_next_test();
+ });
+});
+
+add_test(function getProfile_has_cached_fetch_deleted() {
+ let cachedUrl = "myurl";
+
+ let fxa = mockFxa();
+ let client = mockClient(fxa);
+ client.fetchProfile = function () {
+ return Promise.resolve({ avatar: null });
+ };
+
+ let profile = CreateFxAccountsProfile(fxa, client);
+ profile._cachedProfile = { avatar: cachedUrl };
+
+// instead of checking this in a mocked "save" function, just check after the
+// observer
+ makeObserver(ON_PROFILE_CHANGE_NOTIFICATION, function (subject, topic, data) {
+ profile.getProfile()
+ .then(profileData => {
+ do_check_null(profileData.avatar);
+ run_next_test();
+ });
+ });
+
+ return profile.getProfile()
+ .then(result => {
+ do_check_eq(result.avatar, "myurl");
+ });
+});
+
+function run_test() {
+ run_next_test();
+}
+
+function makeObserver(aObserveTopic, aObserveFunc) {
+ let callback = function (aSubject, aTopic, aData) {
+ log.debug("observed " + aTopic + " " + aData);
+ if (aTopic == aObserveTopic) {
+ removeMe();
+ aObserveFunc(aSubject, aTopic, aData);
+ }
+ };
+
+ function removeMe() {
+ log.debug("removing observer for " + aObserveTopic);
+ Services.obs.removeObserver(callback, aObserveTopic);
+ }
+
+ Services.obs.addObserver(callback, aObserveTopic, false);
+ return removeMe;
+}