342 lines
9.1 KiB
JavaScript
342 lines
9.1 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
|
* vim:set ts=2 sw=2 sts=2 et:
|
|
*/
|
|
"use strict";
|
|
|
|
const { Weave } = ChromeUtils.importESModule(
|
|
"resource://services-sync/main.sys.mjs"
|
|
);
|
|
const { SyncedTabs } = ChromeUtils.importESModule(
|
|
"resource://services-sync/SyncedTabs.sys.mjs"
|
|
);
|
|
|
|
Log.repository.getLogger("Sync.RemoteTabs").addAppender(new Log.DumpAppender());
|
|
|
|
// A mock "Tabs" engine which the SyncedTabs module will use instead of the real
|
|
// engine. We pass a constructor that Sync creates.
|
|
function MockTabsEngine() {
|
|
this.clients = {}; // We'll set this dynamically
|
|
// Mock fxAccounts + recentDeviceList as if we hit the FxA server
|
|
this.fxAccounts = {
|
|
device: {
|
|
recentDeviceList: [
|
|
{
|
|
id: 1,
|
|
name: "updated desktop name",
|
|
availableCommands: {
|
|
"https://identity.mozilla.com/cmd/open-uri": "baz",
|
|
},
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "updated mobile name",
|
|
availableCommands: {
|
|
"https://identity.mozilla.com/cmd/open-uri": "boo",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
MockTabsEngine.prototype = {
|
|
name: "tabs",
|
|
enabled: true,
|
|
|
|
getAllClients() {
|
|
return Object.values(this.clients);
|
|
},
|
|
|
|
getOpenURLs() {
|
|
return new Set();
|
|
},
|
|
};
|
|
|
|
let tabsEngine;
|
|
|
|
// A clients engine that doesn't need to be a constructor.
|
|
let MockClientsEngine = {
|
|
clientSettings: null, // Set in `configureClients`.
|
|
|
|
isMobile(guid) {
|
|
if (!guid.endsWith("desktop") && !guid.endsWith("mobile")) {
|
|
throw new Error(
|
|
"this module expected guids to end with 'desktop' or 'mobile'"
|
|
);
|
|
}
|
|
return guid.endsWith("mobile");
|
|
},
|
|
remoteClientExists(id) {
|
|
return this.clientSettings[id] !== false;
|
|
},
|
|
getClientName(id) {
|
|
if (this.clientSettings[id]) {
|
|
return this.clientSettings[id];
|
|
}
|
|
let client = tabsEngine.clients[id];
|
|
let fxaDevice = tabsEngine.fxAccounts.device.recentDeviceList.find(
|
|
device => device.id === client.fxaDeviceId
|
|
);
|
|
return fxaDevice ? fxaDevice.name : client.clientName;
|
|
},
|
|
|
|
getClientFxaDeviceId(id) {
|
|
if (this.clientSettings[id]) {
|
|
return this.clientSettings[id];
|
|
}
|
|
return tabsEngine.clients[id].fxaDeviceId;
|
|
},
|
|
|
|
getClientType() {
|
|
return "desktop";
|
|
},
|
|
};
|
|
|
|
function configureClients(clients, clientSettings = {}) {
|
|
// each client record is expected to have an id.
|
|
for (let [guid, client] of Object.entries(clients)) {
|
|
client.id = guid;
|
|
}
|
|
tabsEngine.clients = clients;
|
|
// Apply clients collection overrides.
|
|
MockClientsEngine.clientSettings = clientSettings;
|
|
// Send an observer that pretends the engine just finished a sync.
|
|
Services.obs.notifyObservers(null, "weave:engine:sync:finish", "tabs");
|
|
}
|
|
|
|
add_task(async function setup() {
|
|
await Weave.Service.promiseInitialized;
|
|
// Configure Sync with our mock tabs engine and force it to become initialized.
|
|
await Weave.Service.engineManager.unregister("tabs");
|
|
await Weave.Service.engineManager.register(MockTabsEngine);
|
|
Weave.Service.clientsEngine = MockClientsEngine;
|
|
tabsEngine = Weave.Service.engineManager.get("tabs");
|
|
|
|
// Tell the Sync XPCOM service it is initialized.
|
|
let weaveXPCService = Cc["@mozilla.org/weave/service;1"].getService(
|
|
Ci.nsISupports
|
|
).wrappedJSObject;
|
|
weaveXPCService.ready = true;
|
|
});
|
|
|
|
// The tests.
|
|
add_task(async function test_noClients() {
|
|
// no clients, can't be tabs.
|
|
await configureClients({});
|
|
|
|
let tabs = await SyncedTabs.getTabClients();
|
|
equal(Object.keys(tabs).length, 0);
|
|
});
|
|
|
|
add_task(async function test_clientWithTabs() {
|
|
await configureClients({
|
|
guid_desktop: {
|
|
clientName: "My Desktop",
|
|
tabs: [
|
|
{
|
|
urlHistory: ["http://foo.com/"],
|
|
icon: "http://foo.com/favicon",
|
|
lastUsed: 1655745700, // Mon, 20 Jun 2022 17:21:40 GMT
|
|
},
|
|
],
|
|
},
|
|
guid_mobile: {
|
|
clientName: "My Phone",
|
|
tabs: [],
|
|
},
|
|
});
|
|
|
|
let clients = await SyncedTabs.getTabClients();
|
|
equal(clients.length, 2);
|
|
clients.sort((a, b) => {
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
equal(clients[0].tabs.length, 1);
|
|
equal(clients[0].tabs[0].url, "http://foo.com/");
|
|
equal(clients[0].tabs[0].icon, "http://foo.com/favicon");
|
|
equal(clients[0].tabs[0].lastUsed, 1655745700);
|
|
// second client has no tabs.
|
|
equal(clients[1].tabs.length, 0);
|
|
});
|
|
|
|
add_task(async function test_staleClientWithTabs() {
|
|
await configureClients(
|
|
{
|
|
guid_desktop: {
|
|
clientName: "My Desktop",
|
|
tabs: [
|
|
{
|
|
urlHistory: ["http://foo.com/"],
|
|
icon: "http://foo.com/favicon",
|
|
lastUsed: 1655745750,
|
|
},
|
|
],
|
|
},
|
|
guid_mobile: {
|
|
clientName: "My Phone",
|
|
tabs: [],
|
|
},
|
|
guid_stale_mobile: {
|
|
clientName: "My Deleted Phone",
|
|
tabs: [],
|
|
},
|
|
guid_stale_desktop: {
|
|
clientName: "My Deleted Laptop",
|
|
tabs: [
|
|
{
|
|
urlHistory: ["https://bar.com/"],
|
|
icon: "https://bar.com/favicon",
|
|
lastUsed: 1655745700,
|
|
},
|
|
],
|
|
},
|
|
guid_stale_name_desktop: {
|
|
clientName: "My Generic Device",
|
|
tabs: [
|
|
{
|
|
urlHistory: ["https://example.edu/"],
|
|
icon: "https://example.edu/favicon",
|
|
lastUsed: 1655745800,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
guid_stale_mobile: false,
|
|
guid_stale_desktop: false,
|
|
// We should always use the device name from the clients collection, instead
|
|
// of the possibly stale tabs collection.
|
|
guid_stale_name_desktop: "My Laptop",
|
|
}
|
|
);
|
|
let clients = await SyncedTabs.getTabClients();
|
|
clients.sort((a, b) => {
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
equal(clients.length, 3);
|
|
equal(clients[0].name, "My Desktop");
|
|
equal(clients[0].tabs.length, 1);
|
|
equal(clients[0].tabs[0].url, "http://foo.com/");
|
|
equal(clients[0].tabs[0].lastUsed, 1655745750);
|
|
equal(clients[1].name, "My Laptop");
|
|
equal(clients[1].tabs.length, 1);
|
|
equal(clients[1].tabs[0].url, "https://example.edu/");
|
|
equal(clients[1].tabs[0].lastUsed, 1655745800);
|
|
equal(clients[2].name, "My Phone");
|
|
equal(clients[2].tabs.length, 0);
|
|
});
|
|
|
|
add_task(async function test_clientWithTabsIconsDisabled() {
|
|
Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteIcons", false);
|
|
await configureClients({
|
|
guid_desktop: {
|
|
clientName: "My Desktop",
|
|
tabs: [
|
|
{
|
|
urlHistory: ["http://foo.com/"],
|
|
icon: "http://foo.com/favicon",
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
let clients = await SyncedTabs.getTabClients();
|
|
equal(clients.length, 1);
|
|
clients.sort((a, b) => {
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
equal(clients[0].tabs.length, 1);
|
|
equal(clients[0].tabs[0].url, "http://foo.com/");
|
|
// Expect the default favicon due to the pref being false.
|
|
equal(clients[0].tabs[0].icon, "page-icon:http://foo.com/");
|
|
Services.prefs.clearUserPref("services.sync.syncedTabs.showRemoteIcons");
|
|
});
|
|
|
|
add_task(async function test_filter() {
|
|
// Nothing matches.
|
|
await configureClients({
|
|
guid_desktop: {
|
|
clientName: "My Desktop",
|
|
tabs: [
|
|
{
|
|
urlHistory: ["http://foo.com/"],
|
|
title: "A test page.",
|
|
},
|
|
{
|
|
urlHistory: ["http://bar.com/"],
|
|
title: "Another page.",
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
let clients = await SyncedTabs.getTabClients("foo");
|
|
equal(clients.length, 1);
|
|
equal(clients[0].tabs.length, 1);
|
|
equal(clients[0].tabs[0].url, "http://foo.com/");
|
|
// check it matches the title.
|
|
clients = await SyncedTabs.getTabClients("test");
|
|
equal(clients.length, 1);
|
|
equal(clients[0].tabs.length, 1);
|
|
equal(clients[0].tabs[0].url, "http://foo.com/");
|
|
});
|
|
|
|
add_task(async function test_duplicatesTabsAcrossClients() {
|
|
await configureClients({
|
|
guid_desktop: {
|
|
clientName: "My Desktop",
|
|
tabs: [
|
|
{
|
|
urlHistory: ["http://foo.com/"],
|
|
title: "A test page.",
|
|
},
|
|
],
|
|
},
|
|
guid_mobile: {
|
|
clientName: "My Phone",
|
|
tabs: [
|
|
{
|
|
urlHistory: ["http://foo.com/"],
|
|
title: "A test page.",
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
let clients = await SyncedTabs.getTabClients();
|
|
equal(clients.length, 2);
|
|
equal(clients[0].tabs.length, 1);
|
|
equal(clients[1].tabs.length, 1);
|
|
equal(clients[0].tabs[0].url, "http://foo.com/");
|
|
equal(clients[1].tabs[0].url, "http://foo.com/");
|
|
});
|
|
|
|
add_task(async function test_clientsTabUpdatedName() {
|
|
// See the "fxAccounts" object in the MockEngine above for the device list
|
|
await configureClients({
|
|
guid_desktop: {
|
|
clientName: "My Desktop",
|
|
tabs: [
|
|
{
|
|
urlHistory: ["http://foo.com/"],
|
|
icon: "http://foo.com/favicon",
|
|
},
|
|
],
|
|
fxaDeviceId: 1,
|
|
},
|
|
guid_mobile: {
|
|
clientName: "My Phone",
|
|
tabs: [
|
|
{
|
|
urlHistory: ["http://bar.com/"],
|
|
icon: "http://bar.com/favicon",
|
|
},
|
|
],
|
|
fxaDeviceId: 2,
|
|
},
|
|
});
|
|
let clients = await SyncedTabs.getTabClients();
|
|
equal(clients.length, 2);
|
|
equal(clients[0].name, "updated desktop name");
|
|
equal(clients[1].name, "updated mobile name");
|
|
});
|