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:
vladikoff 2016-03-07 00:00:34 -05:00
Родитель 37e22e44ca
Коммит 5ceebef356
15 изменённых файлов: 553 добавлений и 24 удалений

Просмотреть файл

@ -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]