/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests for the FxA push service.
/* eslint-disable mozilla/use-chromeutils-generateqi */
const {
FXA_PUSH_SCOPE_ACCOUNT_UPDATE,
ONLOGOUT_NOTIFICATION,
ON_ACCOUNT_DESTROYED_NOTIFICATION,
ON_DEVICE_CONNECTED_NOTIFICATION,
ON_DEVICE_DISCONNECTED_NOTIFICATION,
ON_PASSWORD_CHANGED_NOTIFICATION,
ON_PASSWORD_RESET_NOTIFICATION,
ON_PROFILE_CHANGE_NOTIFICATION,
ON_PROFILE_UPDATED_NOTIFICATION,
ON_VERIFY_LOGIN_NOTIFICATION,
log,
} = ChromeUtils.importESModule(
"resource://gre/modules/FxAccountsCommon.sys.mjs"
);
const { FxAccountsPushService } = ChromeUtils.importESModule(
"resource://gre/modules/FxAccountsPush.sys.mjs"
);
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: PushService.pushTopic,
subscriptionChangeTopic: PushService.subscriptionChangeTopic,
subscribe(scope, principal, cb) {
cb(Cr.NS_OK, {
endpoint: MOCK_ENDPOINT,
});
},
unsubscribe(scope, principal, cb) {
cb(Cr.NS_OK,
true);
},
};
let mockFxAccounts = {
checkVerificationStatus() {},
updateDeviceRegistration() {},
};
let mockLog = {
trace() {},
debug() {},
warn() {},
error() {},
};
add_task(async
function initialize() {
let pushService =
new FxAccountsPushService();
equal(pushService.initialize(),
false);
});
add_task(async
function registerPushEndpointSuccess() {
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
fxai: mockFxAccounts,
});
let subscription = await pushService.registerPushEndpoint();
equal(subscription.endpoint, MOCK_ENDPOINT);
});
add_task(async
function registerPushEndpointFailure() {
let failPushService = Object.assign(mockPushService, {
subscribe(scope, principal, cb) {
cb(Cr.NS_ERROR_ABORT);
},
});
let pushService =
new FxAccountsPushService({
pushService: failPushService,
fxai: mockFxAccounts,
});
let subscription = await pushService.registerPushEndpoint();
equal(subscription,
null);
});
add_task(async
function unsubscribeSuccess() {
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
fxai: mockFxAccounts,
});
let result = await pushService.unsubscribe();
equal(result,
true);
});
add_task(async
function unsubscribeFailure() {
let failPushService = Object.assign(mockPushService, {
unsubscribe(scope, principal, cb) {
cb(Cr.NS_ERROR_ABORT);
},
});
let pushService =
new FxAccountsPushService({
pushService: failPushService,
fxai: mockFxAccounts,
});
let result = await pushService.unsubscribe();
equal(result,
null);
});
add_test(
function observeLogout() {
let customLog = Object.assign(mockLog, {
trace(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 observePushTopicVerify() {
let emptyMsg = {
QueryInterface() {
return this;
},
};
let customAccounts = Object.assign(mockFxAccounts, {
checkVerificationStatus() {
// checking verification status on push messages without data
run_next_test();
},
});
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
fxai: customAccounts,
});
pushService.observe(
emptyMsg,
mockPushService.pushTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
});
add_test(
function observePushTopicDeviceConnected() {
let msg = {
data: {
json: () => ({
command: ON_DEVICE_CONNECTED_NOTIFICATION,
data: {
deviceName:
"My phone",
},
}),
},
QueryInterface() {
return this;
},
};
let obs = (subject, topic) => {
Services.obs.removeObserver(obs, topic);
run_next_test();
};
Services.obs.addObserver(obs, ON_DEVICE_CONNECTED_NOTIFICATION);
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
fxai: mockFxAccounts,
});
pushService.observe(
msg,
mockPushService.pushTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
});
add_task(async
function observePushTopicDeviceDisconnected_current_device() {
const deviceId =
"bogusid";
let msg = {
data: {
json: () => ({
command: ON_DEVICE_DISCONNECTED_NOTIFICATION,
data: {
id: deviceId,
},
}),
},
QueryInterface() {
return this;
},
};
let signoutCalled =
false;
let { FxAccounts } = ChromeUtils.importESModule(
"resource://gre/modules/FxAccounts.sys.mjs"
);
const fxAccountsMock =
new FxAccounts({
newAccountState() {
return {
async getUserAccountData() {
return { device: { id: deviceId } };
},
};
},
signOut() {
signoutCalled =
true;
},
})._internal;
const deviceDisconnectedNotificationObserved =
new Promise(resolve => {
Services.obs.addObserver(
function obs(subject, topic, data) {
Services.obs.removeObserver(obs, topic);
equal(data, JSON.stringify({ isLocalDevice:
true }));
resolve();
}, ON_DEVICE_DISCONNECTED_NOTIFICATION);
});
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
fxai: fxAccountsMock,
});
pushService.observe(
msg,
mockPushService.pushTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
await deviceDisconnectedNotificationObserved;
ok(signoutCalled);
});
add_task(async
function observePushTopicDeviceDisconnected_another_device() {
const deviceId =
"bogusid";
let msg = {
data: {
json: () => ({
command: ON_DEVICE_DISCONNECTED_NOTIFICATION,
data: {
id: deviceId,
},
}),
},
QueryInterface() {
return this;
},
};
let signoutCalled =
false;
let { FxAccounts } = ChromeUtils.importESModule(
"resource://gre/modules/FxAccounts.sys.mjs"
);
const fxAccountsMock =
new FxAccounts({
newAccountState() {
return {
async getUserAccountData() {
return { device: { id:
"thelocaldevice" } };
},
};
},
signOut() {
signoutCalled =
true;
},
})._internal;
const deviceDisconnectedNotificationObserved =
new Promise(resolve => {
Services.obs.addObserver(
function obs(subject, topic, data) {
Services.obs.removeObserver(obs, topic);
equal(data, JSON.stringify({ isLocalDevice:
false }));
resolve();
}, ON_DEVICE_DISCONNECTED_NOTIFICATION);
});
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
fxai: fxAccountsMock,
});
pushService.observe(
msg,
mockPushService.pushTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
await deviceDisconnectedNotificationObserved;
ok(!signoutCalled);
});
add_test(
function observePushTopicAccountDestroyed() {
const uid =
"bogusuid";
let msg = {
data: {
json: () => ({
command: ON_ACCOUNT_DESTROYED_NOTIFICATION,
data: {
uid,
},
}),
},
QueryInterface() {
return this;
},
};
let customAccounts = Object.assign(mockFxAccounts, {
_handleAccountDestroyed() {
// checking verification status on push messages without data
run_next_test();
},
});
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
fxai: customAccounts,
});
pushService.observe(
msg,
mockPushService.pushTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
});
add_test(
function observePushTopicVerifyLogin() {
let url =
"http://localhost/newLogin";
let title =
"bogustitle";
let body =
"bogusbody";
let msg = {
data: {
json: () => ({
command: ON_VERIFY_LOGIN_NOTIFICATION,
data: {
body,
title,
url,
},
}),
},
QueryInterface() {
return this;
},
};
let obs = (subject, topic, data) => {
Services.obs.removeObserver(obs, topic);
Assert.equal(data, JSON.stringify(msg.data.json().data));
run_next_test();
};
Services.obs.addObserver(obs, ON_VERIFY_LOGIN_NOTIFICATION);
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
fxai: mockFxAccounts,
});
pushService.observe(
msg,
mockPushService.pushTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
});
add_test(
function observePushTopicProfileUpdated() {
let msg = {
data: {
json: () => ({
command: ON_PROFILE_UPDATED_NOTIFICATION,
}),
},
QueryInterface() {
return this;
},
};
let obs = (subject, topic) => {
Services.obs.removeObserver(obs, topic);
run_next_test();
};
Services.obs.addObserver(obs, ON_PROFILE_CHANGE_NOTIFICATION);
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
fxai: mockFxAccounts,
});
pushService.observe(
msg,
mockPushService.pushTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
});
add_test(
function observePushTopicPasswordChanged() {
let msg = {
data: {
json: () => ({
command: ON_PASSWORD_CHANGED_NOTIFICATION,
}),
},
QueryInterface() {
return this;
},
};
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
});
pushService._onPasswordChanged =
function () {
run_next_test();
};
pushService.observe(
msg,
mockPushService.pushTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
});
add_test(
function observePushTopicPasswordReset() {
let msg = {
data: {
json: () => ({
command: ON_PASSWORD_RESET_NOTIFICATION,
}),
},
QueryInterface() {
return this;
},
};
let pushService =
new FxAccountsPushService({
pushService: mockPushService,
});
pushService._onPasswordChanged =
function () {
run_next_test();
};
pushService.observe(
msg,
mockPushService.pushTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
});
add_task(async
function commandReceived() {
let msg = {
data: {
json: () => ({
command:
"fxaccounts:command_received",
data: {
url:
"https://api.accounts.firefox.com/auth/v1/account/device/commands?index=42&limit=1",pan>
},
}),
},
QueryInterface() {
return this;
},
};
let fxAccountsMock = {};
const promiseConsumeRemoteMessagesCalled = new Promise(res => {
fxAccountsMock.commands = {
pollDeviceCommands() {
res();
},
};
});
let pushService = new FxAccountsPushService({
pushService: mockPushService,
fxai: fxAccountsMock,
});
pushService.observe(
msg,
mockPushService.pushTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
await promiseConsumeRemoteMessagesCalled;
});
add_test(function observeSubscriptionChangeTopic() {
let customAccounts = Object.assign(mockFxAccounts, {
updateDeviceRegistration() {
// subscription change means updating the device registration
run_next_test();
},
});
let pushService = new FxAccountsPushService({
pushService: mockPushService,
fxai: customAccounts,
});
pushService.observe(
null,
mockPushService.subscriptionChangeTopic,
FXA_PUSH_SCOPE_ACCOUNT_UPDATE
);
});