зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1247786 - Add push subscription to device registration r=kitcambridge,markh
MozReview-Commit-ID: 40Tt8sJPYrr --HG-- extra : rebase_source : e80d3230a7a38e2b28c736cb0188c60be1d6a871
This commit is contained in:
Родитель
37e22e44ca
Коммит
5ceebef356
|
@ -0,0 +1,30 @@
|
|||
// An XPCOM service that's registered with the category manager for handling
|
||||
// push notifications with scope "chrome://test-scope"
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let pushService = Cc["@mozilla.org/push/Service;1"].getService(Ci.nsIPushService);
|
||||
|
||||
function PushServiceHandler() {
|
||||
// So JS code can reach into us.
|
||||
this.wrappedJSObject = this;
|
||||
// Register a push observer.
|
||||
this.observed = [];
|
||||
Services.obs.addObserver(this, pushService.pushTopic, false);
|
||||
Services.obs.addObserver(this, pushService.subscriptionChangeTopic, false);
|
||||
}
|
||||
|
||||
PushServiceHandler.prototype = {
|
||||
classID: Components.ID("{bb7c5199-c0f7-4976-9f6d-1306e32c5591}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([]),
|
||||
|
||||
observe(subject, topic, data) {
|
||||
this.observed.push({ subject, topic, data });
|
||||
},
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PushServiceHandler]);
|
|
@ -0,0 +1,4 @@
|
|||
component {bb7c5199-c0f7-4976-9f6d-1306e32c5591} PushServiceHandler.js
|
||||
contract @mozilla.org/dom/push/test/PushServiceHandler;1 {bb7c5199-c0f7-4976-9f6d-1306e32c5591}
|
||||
|
||||
category push chrome://test-scope @mozilla.org/dom/push/test/PushServiceHandler;1
|
|
@ -0,0 +1,36 @@
|
|||
"use strict";
|
||||
|
||||
// Here we test that if an xpcom component is registered with the category
|
||||
// manager for push notifications against a specific scope, that service is
|
||||
// instantiated before the message is delivered.
|
||||
|
||||
// This component is registered for "chrome://test-scope"
|
||||
const kServiceContractID = "@mozilla.org/dom/push/test/PushServiceHandler;1";
|
||||
|
||||
let pushService = Cc["@mozilla.org/push/Service;1"].getService(Ci.nsIPushService);
|
||||
|
||||
add_test(function test_service_instantiation() {
|
||||
do_load_manifest("PushServiceHandler.manifest");
|
||||
|
||||
let scope = "chrome://test-scope";
|
||||
let pushNotifier = Cc["@mozilla.org/push/Notifier;1"].getService(Ci.nsIPushNotifier);
|
||||
let principal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
pushNotifier.notifyPush(scope, principal);
|
||||
|
||||
// Now get a handle to our service and check it received the notification.
|
||||
let handlerService = Cc[kServiceContractID]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
|
||||
equal(handlerService.observed.length, 1);
|
||||
equal(handlerService.observed[0].topic, pushService.pushTopic);
|
||||
equal(handlerService.observed[0].data, scope);
|
||||
|
||||
// and a subscription change.
|
||||
pushNotifier.notifySubscriptionChange(scope, principal);
|
||||
equal(handlerService.observed.length, 2);
|
||||
equal(handlerService.observed[1].topic, pushService.subscriptionChangeTopic);
|
||||
equal(handlerService.observed[1].data, scope);
|
||||
|
||||
run_next_test();
|
||||
});
|
|
@ -36,6 +36,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Utils",
|
|||
// All properties exposed by the public FxAccounts API.
|
||||
var publicProperties = [
|
||||
"accountStatus",
|
||||
"checkVerificationStatus",
|
||||
"getAccountsClient",
|
||||
"getAccountsSignInURI",
|
||||
"getAccountsSignUpURI",
|
||||
|
@ -135,7 +136,7 @@ AccountState.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
// Get user account data. Optionally specify explcit field names to fetch
|
||||
// Get user account data. Optionally specify explicit field names to fetch
|
||||
// (and note that if you require an in-memory field you *must* specify the
|
||||
// field name(s).)
|
||||
getUserAccountData(fieldNames = null) {
|
||||
|
@ -315,6 +316,16 @@ this.FxAccounts = function (mockInternal) {
|
|||
external.internal = internal;
|
||||
}
|
||||
|
||||
if (!internal.fxaPushService) {
|
||||
// internal.fxaPushService option is used in testing.
|
||||
// Otherwise we load the service lazily.
|
||||
XPCOMUtils.defineLazyGetter(internal, "fxaPushService", function () {
|
||||
return Components.classes["@mozilla.org/fxaccounts/push;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
});
|
||||
}
|
||||
|
||||
// wait until after the mocks are setup before initializing.
|
||||
internal.initialize();
|
||||
|
||||
|
@ -337,9 +348,9 @@ function FxAccountsInternal() {
|
|||
*/
|
||||
FxAccountsInternal.prototype = {
|
||||
// The timeout (in ms) we use to poll for a verified mail for the first 2 mins.
|
||||
VERIFICATION_POLL_TIMEOUT_INITIAL: 5000, // 5 seconds
|
||||
VERIFICATION_POLL_TIMEOUT_INITIAL: 15000, // 15 seconds
|
||||
// And how often we poll after the first 2 mins.
|
||||
VERIFICATION_POLL_TIMEOUT_SUBSEQUENT: 15000, // 15 seconds.
|
||||
VERIFICATION_POLL_TIMEOUT_SUBSEQUENT: 30000, // 30 seconds.
|
||||
|
||||
_fxAccountsClient: null,
|
||||
|
||||
|
@ -495,14 +506,16 @@ FxAccountsInternal.prototype = {
|
|||
// We're telling the caller that this is durable now (although is that
|
||||
// really something we should commit to? Why not let the write happen in
|
||||
// the background? Already does for updateAccountData ;)
|
||||
return currentAccountState.promiseInitialized.then(() =>
|
||||
this.updateDeviceRegistration()
|
||||
).then(() => {
|
||||
Services.telemetry.getHistogramById("FXA_CONFIGURED").add(1);
|
||||
this.notifyObservers(ONLOGIN_NOTIFICATION);
|
||||
return currentAccountState.promiseInitialized.then(() => {
|
||||
// Starting point for polling if new user
|
||||
if (!this.isUserEmailVerified(credentials)) {
|
||||
this.startVerifiedCheck(credentials);
|
||||
}
|
||||
|
||||
return this.updateDeviceRegistration();
|
||||
}).then(() => {
|
||||
Services.telemetry.getHistogramById("FXA_CONFIGURED").add(1);
|
||||
this.notifyObservers(ONLOGIN_NOTIFICATION);
|
||||
}).then(() => {
|
||||
return currentAccountState.resolve();
|
||||
});
|
||||
|
@ -611,6 +624,22 @@ FxAccountsInternal.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
checkVerificationStatus: function() {
|
||||
log.trace('checkVerificationStatus');
|
||||
let currentState = this.currentAccountState;
|
||||
return currentState.getUserAccountData().then(data => {
|
||||
if (!data) {
|
||||
log.trace("checkVerificationStatus - no user data");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this.isUserEmailVerified(data)) {
|
||||
log.trace("checkVerificationStatus - forcing verification status check");
|
||||
this.pollEmailStatus(currentState, data.sessionToken, "start");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_destroyOAuthToken: function(tokenData) {
|
||||
let client = new FxAccountsOAuthGrantClient({
|
||||
serverURL: tokenData.server,
|
||||
|
@ -1066,7 +1095,7 @@ FxAccountsInternal.prototype = {
|
|||
let ageMs = Date.now() - this.pollStartDate;
|
||||
if (ageMs >= this.POLL_SESSION) {
|
||||
if (currentState.whenVerifiedDeferred) {
|
||||
let error = new Error("User email verification timed out.")
|
||||
let error = new Error("User email verification timed out.");
|
||||
currentState.whenVerifiedDeferred.reject(error);
|
||||
delete currentState.whenVerifiedDeferred;
|
||||
}
|
||||
|
@ -1400,18 +1429,24 @@ FxAccountsInternal.prototype = {
|
|||
}
|
||||
} catch(ignore) {}
|
||||
|
||||
return Promise.resolve().then(() => {
|
||||
return this.fxaPushService.registerPushEndpoint().then(subscription => {
|
||||
const deviceName = this._getDeviceName();
|
||||
let deviceOptions = {};
|
||||
|
||||
// if we were able to obtain a subscription
|
||||
if (subscription && subscription.endpoint) {
|
||||
deviceOptions.pushCallback = subscription.endpoint;
|
||||
}
|
||||
|
||||
if (signedInUser.deviceId) {
|
||||
log.debug("updating existing device details");
|
||||
return this.fxAccountsClient.updateDevice(
|
||||
signedInUser.sessionToken, signedInUser.deviceId, deviceName);
|
||||
signedInUser.sessionToken, signedInUser.deviceId, deviceName, deviceOptions);
|
||||
}
|
||||
|
||||
log.debug("registering new device details");
|
||||
return this.fxAccountsClient.registerDevice(
|
||||
signedInUser.sessionToken, deviceName, this._getDeviceType());
|
||||
signedInUser.sessionToken, deviceName, this._getDeviceType(), deviceOptions);
|
||||
}).then(device =>
|
||||
this.currentAccountState.updateUserAccountData({
|
||||
deviceId: device.id,
|
||||
|
@ -1468,7 +1503,7 @@ FxAccountsInternal.prototype = {
|
|||
const matchingDevices = devices.filter(device => device.isCurrentDevice);
|
||||
const length = matchingDevices.length;
|
||||
if (length === 1) {
|
||||
const deviceId = matchingDevices[0].id
|
||||
const deviceId = matchingDevices[0].id;
|
||||
return this.currentAccountState.updateUserAccountData({
|
||||
deviceId,
|
||||
isDeviceStale: true
|
||||
|
|
|
@ -358,6 +358,10 @@ this.FxAccountsClient.prototype = {
|
|||
* Device name
|
||||
* @param type
|
||||
* Device type (mobile|desktop)
|
||||
* @param [options]
|
||||
* Extra device options
|
||||
* @param [options.pushCallback]
|
||||
* `pushCallback` push endpoint callback
|
||||
* @return Promise
|
||||
* Resolves to an object:
|
||||
* {
|
||||
|
@ -367,12 +371,16 @@ this.FxAccountsClient.prototype = {
|
|||
* type: Type of device (mobile|desktop)
|
||||
* }
|
||||
*/
|
||||
registerDevice(sessionTokenHex, name, type) {
|
||||
registerDevice(sessionTokenHex, name, type, options = {}) {
|
||||
let path = "/account/device";
|
||||
|
||||
let creds = deriveHawkCredentials(sessionTokenHex, "sessionToken");
|
||||
let body = { name, type };
|
||||
|
||||
if (options.pushCallback) {
|
||||
body.pushCallback = options.pushCallback;
|
||||
}
|
||||
|
||||
return this._request(path, "POST", creds, body);
|
||||
},
|
||||
|
||||
|
@ -386,6 +394,10 @@ this.FxAccountsClient.prototype = {
|
|||
* Device identifier
|
||||
* @param name
|
||||
* Device name
|
||||
* @param [options]
|
||||
* Extra device options
|
||||
* @param [options.pushCallback]
|
||||
* `pushCallback` push endpoint callback
|
||||
* @return Promise
|
||||
* Resolves to an object:
|
||||
* {
|
||||
|
@ -393,11 +405,14 @@ this.FxAccountsClient.prototype = {
|
|||
* name: Device name
|
||||
* }
|
||||
*/
|
||||
updateDevice(sessionTokenHex, id, name) {
|
||||
updateDevice(sessionTokenHex, id, name, options = {}) {
|
||||
let path = "/account/device";
|
||||
|
||||
let creds = deriveHawkCredentials(sessionTokenHex, "sessionToken");
|
||||
let body = { id, name };
|
||||
if (options.pushCallback) {
|
||||
body.pushCallback = options.pushCallback;
|
||||
}
|
||||
|
||||
return this._request(path, "POST", creds, body);
|
||||
},
|
||||
|
@ -419,7 +434,7 @@ this.FxAccountsClient.prototype = {
|
|||
* Resolves to an empty object:
|
||||
* {}
|
||||
*/
|
||||
signOutAndDestroyDevice(sessionTokenHex, id, options={}) {
|
||||
signOutAndDestroyDevice(sessionTokenHex, id, options = {}) {
|
||||
let path = "/account/device/destroy";
|
||||
|
||||
if (options.service) {
|
||||
|
|
|
@ -90,6 +90,8 @@ exports.ONLOGOUT_NOTIFICATION = "fxaccounts:onlogout";
|
|||
// Internal to services/fxaccounts only
|
||||
exports.ON_FXA_UPDATE_NOTIFICATION = "fxaccounts:update";
|
||||
|
||||
exports.FXA_PUSH_SCOPE_ACCOUNT_UPDATE = "chrome://fxa-device-update";
|
||||
|
||||
exports.ON_PROFILE_CHANGE_NOTIFICATION = "fxaccounts:profilechange";
|
||||
|
||||
// UI Requests.
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# FxAccountsPush.js
|
||||
component {1b7db999-2ecd-4abf-bb95-a726896798ca} FxAccountsPush.js
|
||||
contract @mozilla.org/fxaccounts/push;1 {1b7db999-2ecd-4abf-bb95-a726896798ca}
|
||||
category push chrome://fxa-device-update @mozilla.org/fxaccounts/push;1
|
|
@ -0,0 +1,195 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
|
||||
/**
|
||||
* FxAccountsPushService manages Push notifications for Firefox Accounts in the browser
|
||||
*
|
||||
* @param [options]
|
||||
* Object, custom options that used for testing
|
||||
* @constructor
|
||||
*/
|
||||
function FxAccountsPushService(options = {}) {
|
||||
this.log = log;
|
||||
|
||||
if (options.log) {
|
||||
// allow custom log for testing purposes
|
||||
this.log = options.log;
|
||||
}
|
||||
|
||||
this.log.debug("FxAccountsPush loading service");
|
||||
this.wrappedJSObject = this;
|
||||
this.initialize(options);
|
||||
}
|
||||
|
||||
FxAccountsPushService.prototype = {
|
||||
/**
|
||||
* Helps only initialize observers once.
|
||||
*/
|
||||
_initialized: false,
|
||||
/**
|
||||
* Instance of the nsIPushService or a mocked object.
|
||||
*/
|
||||
pushService: null,
|
||||
/**
|
||||
* Instance of FxAccounts or a mocked object.
|
||||
*/
|
||||
fxAccounts: null,
|
||||
/**
|
||||
* Component ID of this service, helps register this component.
|
||||
*/
|
||||
classID: Components.ID("{1b7db999-2ecd-4abf-bb95-a726896798ca}"),
|
||||
/**
|
||||
* Register used interfaces in this service
|
||||
*/
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
/**
|
||||
* Initialize the service and register all the required observers.
|
||||
*
|
||||
* @param [options]
|
||||
*/
|
||||
initialize(options) {
|
||||
if (this._initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._initialized = true;
|
||||
|
||||
if (options.pushService) {
|
||||
this.pushService = options.pushService;
|
||||
} else {
|
||||
this.pushService = Cc["@mozilla.org/push/Service;1"].getService(Ci.nsIPushService);
|
||||
}
|
||||
|
||||
if (options.fxAccounts) {
|
||||
this.fxAccounts = options.fxAccounts;
|
||||
} else {
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
|
||||
"resource://gre/modules/FxAccounts.jsm");
|
||||
}
|
||||
|
||||
// listen to new push messages, push changes and logout events
|
||||
Services.obs.addObserver(this, this.pushService.pushTopic, false);
|
||||
Services.obs.addObserver(this, this.pushService.subscriptionChangeTopic, false);
|
||||
Services.obs.addObserver(this, ONLOGOUT_NOTIFICATION, false);
|
||||
|
||||
this.log.debug("FxAccountsPush initialized");
|
||||
},
|
||||
/**
|
||||
* Registers a new endpoint with the Push Server
|
||||
*
|
||||
* @returns {Promise}
|
||||
* Promise always resolves with a subscription or a null if failed to subscribe.
|
||||
*/
|
||||
registerPushEndpoint() {
|
||||
this.log.trace("FxAccountsPush registerPushEndpoint");
|
||||
|
||||
return new Promise((resolve) => {
|
||||
this.pushService.subscribe(FXA_PUSH_SCOPE_ACCOUNT_UPDATE,
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, subscription) => {
|
||||
if (Components.isSuccessCode(result)) {
|
||||
this.log.debug("FxAccountsPush got subscription");
|
||||
resolve(subscription);
|
||||
} else {
|
||||
this.log.warn("FxAccountsPush failed to subscribe", result);
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Standard observer interface to listen to push messages, changes and logout.
|
||||
*
|
||||
* @param subject
|
||||
* @param topic
|
||||
* @param data
|
||||
*/
|
||||
observe(subject, topic, data) {
|
||||
this.log.trace(`observed topic=${topic}, data=${data}, subject=${subject}`);
|
||||
switch (topic) {
|
||||
case this.pushService.pushTopic:
|
||||
if (data === FXA_PUSH_SCOPE_ACCOUNT_UPDATE) {
|
||||
this._onPushMessage();
|
||||
}
|
||||
break;
|
||||
case this.pushService.subscriptionChangeTopic:
|
||||
if (data === FXA_PUSH_SCOPE_ACCOUNT_UPDATE) {
|
||||
this._onPushSubscriptionChange();
|
||||
}
|
||||
break;
|
||||
case ONLOGOUT_NOTIFICATION:
|
||||
// user signed out, we need to stop polling the Push Server
|
||||
this.unsubscribe().catch(err => {
|
||||
this.log.error("Error during unsubscribe", err);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Fired when the Push server sends a notification.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onPushMessage() {
|
||||
this.log.trace("FxAccountsPushService _onPushMessage");
|
||||
// Use this signal to check the verification state of the account right away
|
||||
this.fxAccounts.checkVerificationStatus();
|
||||
},
|
||||
/**
|
||||
* Fired when the Push server drops a subscription, or the subscription identifier changes.
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPushService#Receiving_Push_Messages
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onPushSubscriptionChange() {
|
||||
this.log.trace("FxAccountsPushService _onPushSubscriptionChange");
|
||||
this.fxAccounts.updateDeviceRegistration();
|
||||
},
|
||||
/**
|
||||
* Unsubscribe from the Push server
|
||||
*
|
||||
* Ref: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPushService#unsubscribe()
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @private
|
||||
*/
|
||||
unsubscribe() {
|
||||
this.log.trace("FxAccountsPushService unsubscribe");
|
||||
return new Promise((resolve) => {
|
||||
this.pushService.unsubscribe(FXA_PUSH_SCOPE_ACCOUNT_UPDATE,
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, ok) => {
|
||||
if (Components.isSuccessCode(result)) {
|
||||
if (ok === true) {
|
||||
this.log.debug("FxAccountsPushService unsubscribed");
|
||||
} else {
|
||||
this.log.debug("FxAccountsPushService had no subscription to unsubscribe");
|
||||
}
|
||||
} else {
|
||||
this.log.warn("FxAccountsPushService failed to unsubscribe", result);
|
||||
}
|
||||
return resolve(ok);
|
||||
})
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
// Service registration below registers with FxAccountsComponents.manifest
|
||||
const components = [FxAccountsPushService];
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
|
||||
|
||||
// The following registration below helps with testing this service.
|
||||
this.EXPORTED_SYMBOLS=["FxAccountsPushService"];
|
|
@ -10,6 +10,11 @@ MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
|
|||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'FxAccountsComponents.manifest',
|
||||
'FxAccountsPush.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'Credentials.jsm',
|
||||
'FxAccounts.jsm',
|
||||
|
@ -19,6 +24,7 @@ EXTRA_JS_MODULES += [
|
|||
'FxAccountsOAuthGrantClient.jsm',
|
||||
'FxAccountsProfile.jsm',
|
||||
'FxAccountsProfileClient.jsm',
|
||||
'FxAccountsPush.js',
|
||||
'FxAccountsStorage.jsm',
|
||||
'FxAccountsWebChannel.jsm',
|
||||
]
|
||||
|
|
|
@ -99,7 +99,16 @@ function MockFxAccounts(device = {}) {
|
|||
_getDeviceName() {
|
||||
return device.name || "mock device name";
|
||||
},
|
||||
fxAccountsClient: new MockFxAccountsClient(device)
|
||||
fxAccountsClient: new MockFxAccountsClient(device),
|
||||
fxaPushService: {
|
||||
registerPushEndpoint() {
|
||||
return new Promise((resolve) => {
|
||||
resolve({
|
||||
endpoint: "http://mochi.test:8888"
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -145,10 +154,11 @@ add_task(function* test_updateDeviceRegistration_with_new_device() {
|
|||
do_check_eq(spy.updateDevice.count, 0);
|
||||
do_check_eq(spy.getDeviceList.count, 0);
|
||||
do_check_eq(spy.registerDevice.count, 1);
|
||||
do_check_eq(spy.registerDevice.args[0].length, 3);
|
||||
do_check_eq(spy.registerDevice.args[0].length, 4);
|
||||
do_check_eq(spy.registerDevice.args[0][0], credentials.sessionToken);
|
||||
do_check_eq(spy.registerDevice.args[0][1], deviceName);
|
||||
do_check_eq(spy.registerDevice.args[0][2], "desktop");
|
||||
do_check_eq(spy.registerDevice.args[0][3].pushCallback, "http://mochi.test:8888");
|
||||
|
||||
const state = fxa.internal.currentAccountState;
|
||||
const data = yield state.getUserAccountData();
|
||||
|
@ -195,10 +205,11 @@ add_task(function* test_updateDeviceRegistration_with_existing_device() {
|
|||
do_check_eq(spy.registerDevice.count, 0);
|
||||
do_check_eq(spy.getDeviceList.count, 0);
|
||||
do_check_eq(spy.updateDevice.count, 1);
|
||||
do_check_eq(spy.updateDevice.args[0].length, 3);
|
||||
do_check_eq(spy.updateDevice.args[0].length, 4);
|
||||
do_check_eq(spy.updateDevice.args[0][0], credentials.sessionToken);
|
||||
do_check_eq(spy.updateDevice.args[0][1], credentials.deviceId);
|
||||
do_check_eq(spy.updateDevice.args[0][2], deviceName);
|
||||
do_check_eq(spy.updateDevice.args[0][3].pushCallback, "http://mochi.test:8888");
|
||||
|
||||
const state = fxa.internal.currentAccountState;
|
||||
const data = yield state.getUserAccountData();
|
||||
|
@ -251,10 +262,12 @@ add_task(function* test_updateDeviceRegistration_with_unknown_device_error() {
|
|||
do_check_eq(spy.getDeviceList.count, 0);
|
||||
do_check_eq(spy.registerDevice.count, 0);
|
||||
do_check_eq(spy.updateDevice.count, 1);
|
||||
do_check_eq(spy.updateDevice.args[0].length, 3);
|
||||
do_check_eq(spy.updateDevice.args[0].length, 4);
|
||||
do_check_eq(spy.updateDevice.args[0][0], credentials.sessionToken);
|
||||
do_check_eq(spy.updateDevice.args[0][1], credentials.deviceId);
|
||||
do_check_eq(spy.updateDevice.args[0][2], deviceName);
|
||||
do_check_eq(spy.updateDevice.args[0][3].pushCallback, "http://mochi.test:8888");
|
||||
|
||||
|
||||
const state = fxa.internal.currentAccountState;
|
||||
const data = yield state.getUserAccountData();
|
||||
|
@ -312,10 +325,11 @@ add_task(function* test_updateDeviceRegistration_with_device_session_conflict_er
|
|||
do_check_eq(result, credentials.deviceId);
|
||||
do_check_eq(spy.registerDevice.count, 0);
|
||||
do_check_eq(spy.updateDevice.count, 1);
|
||||
do_check_eq(spy.updateDevice.args[0].length, 3);
|
||||
do_check_eq(spy.updateDevice.args[0].length, 4);
|
||||
do_check_eq(spy.updateDevice.args[0][0], credentials.sessionToken);
|
||||
do_check_eq(spy.updateDevice.args[0][1], credentials.deviceId);
|
||||
do_check_eq(spy.updateDevice.args[0][2], deviceName);
|
||||
do_check_eq(spy.updateDevice.args[0][3].pushCallback, "http://mochi.test:8888");
|
||||
do_check_eq(spy.getDeviceList.count, 1);
|
||||
do_check_eq(spy.getDeviceList.args[0].length, 1);
|
||||
do_check_eq(spy.getDeviceList.args[0][0], credentials.sessionToken);
|
||||
|
@ -368,7 +382,7 @@ add_task(function* test_updateDeviceRegistration_with_unrecoverable_error() {
|
|||
do_check_eq(spy.getDeviceList.count, 0);
|
||||
do_check_eq(spy.updateDevice.count, 0);
|
||||
do_check_eq(spy.registerDevice.count, 1);
|
||||
do_check_eq(spy.registerDevice.args[0].length, 3);
|
||||
do_check_eq(spy.registerDevice.args[0].length, 4);
|
||||
|
||||
const state = fxa.internal.currentAccountState;
|
||||
const data = yield state.getUserAccountData();
|
||||
|
|
|
@ -48,6 +48,15 @@ function createFxAccounts() {
|
|||
return new FxAccounts({
|
||||
_getDeviceName() {
|
||||
return "mock device name";
|
||||
},
|
||||
fxaPushService: {
|
||||
registerPushEndpoint() {
|
||||
return new Promise((resolve) => {
|
||||
resolve({
|
||||
endpoint: "http://mochi.test:8888"
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -93,7 +93,16 @@ function MockFxAccounts(device={}) {
|
|||
},
|
||||
_getDeviceName() {
|
||||
return "mock device name";
|
||||
}
|
||||
},
|
||||
fxaPushService: {
|
||||
registerPushEndpoint() {
|
||||
return new Promise((resolve) => {
|
||||
resolve({
|
||||
endpoint: "http://mochi.test:8888"
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,16 @@ function MockFxAccounts(mockGrantClient) {
|
|||
},
|
||||
_getDeviceName() {
|
||||
return "mock device name";
|
||||
}
|
||||
},
|
||||
fxaPushService: {
|
||||
registerPushEndpoint() {
|
||||
return new Promise((resolve) => {
|
||||
resolve({
|
||||
endpoint: "http://mochi.test:8888"
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests for the FxA push service.
|
||||
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
Cu.import("resource://gre/modules/FxAccountsPush.js");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "pushService",
|
||||
"@mozilla.org/push/Service;1", "nsIPushService");
|
||||
|
||||
initTestLogging("Trace");
|
||||
log.level = Log.Level.Trace;
|
||||
|
||||
const MOCK_ENDPOINT = "http://mochi.test:8888";
|
||||
|
||||
// tests do not allow external connections, mock the PushService
|
||||
let mockPushService = {
|
||||
pushTopic: this.pushService.pushTopic,
|
||||
subscriptionChangeTopic: this.pushService.subscriptionChangeTopic,
|
||||
subscribe(scope, principal, cb) {
|
||||
cb(Components.results.NS_OK, {
|
||||
endpoint: MOCK_ENDPOINT
|
||||
});
|
||||
},
|
||||
unsubscribe(scope, principal, cb) {
|
||||
cb(Components.results.NS_OK, true);
|
||||
}
|
||||
};
|
||||
|
||||
let mockFxAccounts = {
|
||||
checkVerificationStatus() {},
|
||||
updateDeviceRegistration() {}
|
||||
};
|
||||
|
||||
let mockLog = {
|
||||
trace() {},
|
||||
debug() {},
|
||||
warn() {},
|
||||
error() {}
|
||||
};
|
||||
|
||||
|
||||
add_task(function* initialize() {
|
||||
let pushService = new FxAccountsPushService();
|
||||
do_check_eq(pushService.initialize(), false);
|
||||
});
|
||||
|
||||
add_task(function* registerPushEndpointSuccess() {
|
||||
let pushService = new FxAccountsPushService({
|
||||
pushService: mockPushService,
|
||||
fxAccounts: mockFxAccounts,
|
||||
});
|
||||
|
||||
let subscription = yield pushService.registerPushEndpoint();
|
||||
do_check_eq(subscription.endpoint, MOCK_ENDPOINT);
|
||||
});
|
||||
|
||||
add_task(function* registerPushEndpointFailure() {
|
||||
let failPushService = Object.assign(mockPushService, {
|
||||
subscribe(scope, principal, cb) {
|
||||
cb(Components.results.NS_ERROR_ABORT);
|
||||
}
|
||||
});
|
||||
|
||||
let pushService = new FxAccountsPushService({
|
||||
pushService: failPushService,
|
||||
fxAccounts: mockFxAccounts,
|
||||
});
|
||||
|
||||
let subscription = yield pushService.registerPushEndpoint();
|
||||
do_check_eq(subscription, null);
|
||||
});
|
||||
|
||||
add_task(function* unsubscribeSuccess() {
|
||||
let pushService = new FxAccountsPushService({
|
||||
pushService: mockPushService,
|
||||
fxAccounts: mockFxAccounts,
|
||||
});
|
||||
|
||||
let result = yield pushService.unsubscribe();
|
||||
do_check_eq(result, true);
|
||||
});
|
||||
|
||||
add_task(function* unsubscribeFailure() {
|
||||
let failPushService = Object.assign(mockPushService, {
|
||||
unsubscribe(scope, principal, cb) {
|
||||
cb(Components.results.NS_ERROR_ABORT);
|
||||
}
|
||||
});
|
||||
|
||||
let pushService = new FxAccountsPushService({
|
||||
pushService: failPushService,
|
||||
fxAccounts: mockFxAccounts,
|
||||
});
|
||||
|
||||
let result = yield pushService.unsubscribe();
|
||||
do_check_eq(result, null);
|
||||
});
|
||||
|
||||
add_test(function observeLogout() {
|
||||
let customLog = Object.assign(mockLog, {
|
||||
trace: function (msg) {
|
||||
if (msg === "FxAccountsPushService unsubscribe") {
|
||||
// logout means we unsubscribe
|
||||
run_next_test();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let pushService = new FxAccountsPushService({
|
||||
pushService: mockPushService,
|
||||
log: customLog
|
||||
});
|
||||
|
||||
pushService.observe(null, ONLOGOUT_NOTIFICATION);
|
||||
});
|
||||
|
||||
add_test(function observePushTopic() {
|
||||
let customAccounts = Object.assign(mockFxAccounts, {
|
||||
checkVerificationStatus: function () {
|
||||
// checking verification status on push messages
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
|
||||
let pushService = new FxAccountsPushService({
|
||||
pushService: mockPushService,
|
||||
fxAccounts: customAccounts,
|
||||
});
|
||||
|
||||
pushService.observe(null, mockPushService.pushTopic, FXA_PUSH_SCOPE_ACCOUNT_UPDATE);
|
||||
});
|
||||
|
||||
add_test(function observeSubscriptionChangeTopic() {
|
||||
let customAccounts = Object.assign(mockFxAccounts, {
|
||||
updateDeviceRegistration: function () {
|
||||
// subscription change means updating the device registration
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
|
||||
let pushService = new FxAccountsPushService({
|
||||
pushService: mockPushService,
|
||||
fxAccounts: customAccounts,
|
||||
});
|
||||
|
||||
pushService.observe(null, mockPushService.subscriptionChangeTopic, FXA_PUSH_SCOPE_ACCOUNT_UPDATE);
|
||||
});
|
||||
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
|
@ -20,6 +20,7 @@ reason = FxAccountsManager is only available for B2G for now
|
|||
[test_oauth_tokens.js]
|
||||
[test_oauth_token_storage.js]
|
||||
[test_profile_client.js]
|
||||
[test_push_service.js]
|
||||
[test_web_channel.js]
|
||||
skip-if = (appname == 'b2g' || appname == 'thunderbird') # fxa web channels only used on desktop
|
||||
[test_profile.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче