diff options
Diffstat (limited to 'services/fxaccounts/tests/xpcshell/test_profile.js')
-rw-r--r-- | services/fxaccounts/tests/xpcshell/test_profile.js | 409 |
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; +} |