From 8c2a61dafdbdd14a16c9c3df78602b91eba56e7b Mon Sep 17 00:00:00 2001 From: Harry Twyford Date: Wed, 2 Jun 2021 22:43:29 +0000 Subject: [PATCH] Bug 1677126 - Part 4 - Port remote tab tests and update them to expect result flex. r=adw Differential Revision: https://phabricator.services.mozilla.com/D114895 --- .../test/xpcshell/test_ext_urlbar.js | 2 + .../urlbar/UrlbarProviderRemoteTabs.jsm | 2 +- .../urlbar/tests/browser/browser_remotetab.js | 2 +- .../browser_searchMode_excludeResults.js | 4 +- .../browser_urlbar_telemetry_remotetab.js | 2 +- .../browser_view_resultTypes_display.js | 2 +- browser/components/urlbar/tests/unit/head.js | 85 +++ .../urlbar/tests/unit/test_remote_tabs.js | 568 ++++++++++++++++++ .../components/urlbar/tests/unit/xpcshell.ini | 2 + .../test_remote_tab_matches.js | 408 ------------- .../places/tests/unifiedcomplete/xpcshell.ini | 2 - 11 files changed, 663 insertions(+), 416 deletions(-) create mode 100644 browser/components/urlbar/tests/unit/test_remote_tabs.js delete mode 100644 toolkit/components/places/tests/unifiedcomplete/test_remote_tab_matches.js diff --git a/browser/components/extensions/test/xpcshell/test_ext_urlbar.js b/browser/components/extensions/test/xpcshell/test_ext_urlbar.js index 8ede83926906..2a966437cbd6 100644 --- a/browser/components/extensions/test/xpcshell/test_ext_urlbar.js +++ b/browser/components/extensions/test/xpcshell/test_ext_urlbar.js @@ -230,6 +230,7 @@ add_task(async function test_onProviderResultsRequested() { title: "Test remote_tab-tabs result", url: "https://example.com/remote_tab-tabs", device: "device", + lastUsed: 1621366890, }, }, { @@ -327,6 +328,7 @@ add_task(async function test_onProviderResultsRequested() { url: "https://example.com/remote_tab-tabs", displayUrl: "example.com/remote_tab-tabs", device: "device", + lastUsed: 1621366890, }, }, { diff --git a/browser/components/urlbar/UrlbarProviderRemoteTabs.jsm b/browser/components/urlbar/UrlbarProviderRemoteTabs.jsm index ea485d3d59e0..1c2d0f15af75 100644 --- a/browser/components/urlbar/UrlbarProviderRemoteTabs.jsm +++ b/browser/components/urlbar/UrlbarProviderRemoteTabs.jsm @@ -166,7 +166,7 @@ class ProviderRemoteTabs extends UrlbarProvider { title: [tab.title, UrlbarUtils.HIGHLIGHT.TYPED], device: client.name, icon: showRemoteIconsPref ? tab.icon : "", - lastUsed: tab.lastUsed * 1000, + lastUsed: (tab.lastUsed || 0) * 1000, }) ) ); diff --git a/browser/components/urlbar/tests/browser/browser_remotetab.js b/browser/components/urlbar/tests/browser/browser_remotetab.js index 51bb79eac2bf..e985e274e1f2 100644 --- a/browser/components/urlbar/tests/browser/browser_remotetab.js +++ b/browser/components/urlbar/tests/browser/browser_remotetab.js @@ -27,7 +27,7 @@ const REMOTE_TAB = { url: TEST_URL, icon: UrlbarUtils.ICON.DEFAULT, client: "7cqCr77ptzX3", - lastUsed: 1452124677, + lastUsed: Math.floor(Date.now() / 1000), }, ], }; diff --git a/browser/components/urlbar/tests/browser/browser_searchMode_excludeResults.js b/browser/components/urlbar/tests/browser/browser_searchMode_excludeResults.js index 9a6ce1951fcf..429ff1bc5e56 100644 --- a/browser/components/urlbar/tests/browser/browser_searchMode_excludeResults.js +++ b/browser/components/urlbar/tests/browser/browser_searchMode_excludeResults.js @@ -50,7 +50,7 @@ add_task(async function setup() { url: "https://example.com", icon: UrlbarUtils.ICON.DEFAULT, client: "7cqCr77ptzX3", - lastUsed: 1452124677, + lastUsed: Math.floor(Date.now() / 1000), }, { type: "tab", @@ -58,7 +58,7 @@ add_task(async function setup() { url: "https://example-2.com", icon: UrlbarUtils.ICON.DEFAULT, client: "7cqCr77ptzX3", - lastUsed: 1452124677, + lastUsed: Math.floor(Date.now() / 1000), }, ], }; diff --git a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_remotetab.js b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_remotetab.js index 320a98b10fb6..38585316c1c9 100644 --- a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_remotetab.js +++ b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_remotetab.js @@ -136,7 +136,7 @@ add_task(async function setup() { url: "http://example.com", icon: UrlbarUtils.ICON.DEFAULT, client: "7cqCr77ptzX3", - lastUsed: 1452124677, + lastUsed: Math.floor(Date.now() / 1000), }, ], }; diff --git a/browser/components/urlbar/tests/browser/browser_view_resultTypes_display.js b/browser/components/urlbar/tests/browser/browser_view_resultTypes_display.js index 91bd453f44c0..05d5de2408d5 100644 --- a/browser/components/urlbar/tests/browser/browser_view_resultTypes_display.js +++ b/browser/components/urlbar/tests/browser/browser_view_resultTypes_display.js @@ -261,7 +261,7 @@ add_task(async function test_remote_tab_result() { url: "http://example.com", icon: UrlbarUtils.ICON.DEFAULT, client: "7cqCr77ptzX3", - lastUsed: parseInt(Date.now() / 1000), + lastUsed: Math.floor(Date.now() / 1000), }, ], }; diff --git a/browser/components/urlbar/tests/unit/head.js b/browser/components/urlbar/tests/unit/head.js index aa497ca3c92f..0aa50e496694 100644 --- a/browser/components/urlbar/tests/unit/head.js +++ b/browser/components/urlbar/tests/unit/head.js @@ -295,6 +295,18 @@ async function addTestTailSuggestionsEngine(suggestionsFn = null) { return engine; } +async function addOpenPages(uri, count = 1, userContextId = 0) { + for (let i = 0; i < count; i++) { + await UrlbarProviderOpenTabs.registerOpenTab(uri.spec, userContextId); + } +} + +async function removeOpenPages(aUri, aCount = 1, aUserContextId = 0) { + for (let i = 0; i < aCount; i++) { + await UrlbarProviderOpenTabs.unregisterOpenTab(aUri.spec, aUserContextId); + } +} + /** * Helper for tests that generate search results but aren't interested in * suggestions, such as autofill tests. Installs a test engine and disables @@ -457,6 +469,31 @@ function makeOmniboxResult( return result; } +/** + * Creates a UrlbarResult for an switch-to-tab result. + * @param {UrlbarQueryContext} queryContext + * The context that this result will be displayed in. + * @param {string} options.uri + * The page URI. + * @param {string} [options.title] + * The page title. + * @param {string} [options.iconUri] + * A URI for the page icon. + * @returns {UrlbarResult} + */ +function makeTabSwitchResult(queryContext, { uri, title, iconUri }) { + return new UrlbarResult( + UrlbarUtils.RESULT_TYPE.TAB_SWITCH, + UrlbarUtils.RESULT_SOURCE.TABS, + ...UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, { + url: [uri, UrlbarUtils.HIGHLIGHT.TYPED], + title: [title, UrlbarUtils.HIGHLIGHT.TYPED], + // Check against undefined so consumers can pass in the empty string. + icon: typeof iconUri != "undefined" ? iconUri : `page-icon:${uri}`, + }) + ); +} + /** * Creates a UrlbarResult for a keyword search result. * @param {UrlbarQueryContext} queryContext @@ -530,6 +567,54 @@ function makePrioritySearchResult( return result; } +/** + * Creates a UrlbarResult for a remote tab result. + * @param {UrlbarQueryContext} queryContext + * The context that this result will be displayed in. + * @param {string} options.uri + * The page URI. + * @param {string} options.device + * The name of the device that the remote tab comes from. + * @param {string} [options.title] + * The page title. + * @param {number} [options.lastUsed] + * The last time the remote tab was visited, in epoch seconds. Defaults + * to 0. + * @param {string} [options.iconUri] + * A URI for the page's icon. + * @returns {UrlbarResult} + */ +function makeRemoteTabResult( + queryContext, + { uri, device, title, iconUri, lastUsed = 0 } +) { + let payload = { + url: [uri, UrlbarUtils.HIGHLIGHT.TYPED], + device: [device, UrlbarUtils.HIGHLIGHT.TYPED], + // Check against undefined so consumers can pass in the empty string. + icon: + typeof iconUri != "undefined" + ? iconUri + : `moz-anno:favicon:page-icon:${uri}`, + lastUsed: lastUsed * 1000, + }; + + // Check against undefined so consumers can pass in the empty string. + if (typeof title != "undefined") { + payload.title = [title, UrlbarUtils.HIGHLIGHT.TYPED]; + } else { + payload.title = [uri, UrlbarUtils.HIGHLIGHT.TYPED]; + } + + let result = new UrlbarResult( + UrlbarUtils.RESULT_TYPE.REMOTE_TAB, + UrlbarUtils.RESULT_SOURCE.TABS, + ...UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, payload) + ); + + return result; +} + /** * Creates a UrlbarResult for a search result. * @param {UrlbarQueryContext} queryContext diff --git a/browser/components/urlbar/tests/unit/test_remote_tabs.js b/browser/components/urlbar/tests/unit/test_remote_tabs.js new file mode 100644 index 000000000000..3a3c6ad82d7a --- /dev/null +++ b/browser/components/urlbar/tests/unit/test_remote_tabs.js @@ -0,0 +1,568 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim:set ts=2 sw=2 sts=2 et: + */ +"use strict"; + +const { Weave } = ChromeUtils.import("resource://services-sync/main.js"); + +// A mock "Tabs" engine which autocomplete will use instead of the real +// engine. We pass a constructor that Sync creates. +function MockTabsEngine() { + this.clients = null; // We'll set this dynamically +} + +MockTabsEngine.prototype = { + name: "tabs", + + startTracking() {}, + getAllClients() { + return this.clients; + }, +}; + +// A clients engine that doesn't need to be a constructor. +let MockClientsEngine = { + getClientType(guid) { + Assert.ok(guid.endsWith("desktop") || guid.endsWith("mobile")); + return guid.endsWith("mobile") ? "phone" : "desktop"; + }, + remoteClientExists(id) { + return true; + }, + getClientName(id) { + return id.endsWith("mobile") ? "My Phone" : "My Desktop"; + }, +}; + +// Configure the singleton engine for a test. +function configureEngine(clients) { + // Configure the instance Sync created. + let engine = Weave.Service.engineManager.get("tabs"); + engine.clients = clients; + Weave.Service.clientsEngine = MockClientsEngine; + // 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() { + // Tell Sync about the mocks. + Weave.Service.engineManager.register(MockTabsEngine); + + // Tell the Sync XPCOM service it is initialized. + let weaveXPCService = Cc["@mozilla.org/weave/service;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + weaveXPCService.ready = true; + + // Install a test engine. + let engine = await addTestSuggestionsEngine(); + let oldDefaultEngine = await Services.search.getDefault(); + + registerCleanupFunction(async () => { + Services.search.setDefault(oldDefaultEngine); + Services.prefs.clearUserPref("services.sync.username"); + Services.prefs.clearUserPref("services.sync.registerEngines"); + Services.prefs.clearUserPref("browser.urlbar.suggest.searches"); + await cleanupPlaces(); + }); + + Services.search.setDefault(engine); + Services.prefs.setCharPref("services.sync.username", "someone@somewhere.com"); + Services.prefs.setCharPref("services.sync.registerEngines", ""); + // Avoid hitting the network. + Services.prefs.setBoolPref("browser.urlbar.suggest.searches", false); +}); + +add_task(async function test_minimal() { + // The minimal client and tabs info we can get away with. + configureEngine({ + guid_desktop: { + id: "desktop", + tabs: [ + { + urlHistory: ["http://example.com/"], + }, + ], + }, + }); + + let query = "ex"; + let context = createContext(query, { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + makeRemoteTabResult(context, { + uri: "http://example.com/", + device: "My Desktop", + }), + ], + }); +}); + +add_task(async function test_maximal() { + // Every field that could possibly exist on a remote record. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: ["http://example.com/"], + title: "An Example", + icon: "http://favicon", + }, + ], + }, + }); + + let query = "ex"; + let context = createContext(query, { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + makeRemoteTabResult(context, { + uri: "http://example.com/", + device: "My Phone", + title: "An Example", + iconUri: "moz-anno:favicon:http://favicon/", + }), + ], + }); +}); + +add_task(async function test_noShowIcons() { + Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteIcons", false); + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: ["http://example.com/"], + title: "An Example", + icon: "http://favicon", + }, + ], + }, + }); + + let query = "ex"; + let context = createContext(query, { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + makeRemoteTabResult(context, { + uri: "http://example.com/", + device: "My Phone", + title: "An Example", + // expecting the default favicon due to that pref. + iconUri: "", + }), + ], + }); + Services.prefs.clearUserPref("services.sync.syncedTabs.showRemoteIcons"); +}); + +add_task(async function test_dontMatchSyncedTabs() { + Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteTabs", false); + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: ["http://example.com/"], + title: "An Example", + icon: "http://favicon", + }, + ], + }, + }); + + let context = createContext("ex", { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + ], + }); + + Services.prefs.clearUserPref("services.sync.syncedTabs.showRemoteTabs"); +}); + +add_task(async function test_matches_title() { + // URL doesn't match search expression, should still match the title. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: ["http://foo.com/"], + title: "An Example", + }, + ], + }, + }); + + let query = "ex"; + let context = createContext(query, { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + makeRemoteTabResult(context, { + uri: "http://foo.com/", + device: "My Phone", + title: "An Example", + }), + ], + }); +}); + +add_task(async function test_localtab_matches_override() { + // We have an open tab to the same page on a remote device, only "switch to + // tab" should appear as duplicate detection removed the remote one. + + // First set up Sync to have the page as a remote tab. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: ["http://foo.com/"], + title: "An Example", + }, + ], + }, + }); + + // Set up Places to think the tab is open locally. + let uri = Services.io.newURI("http://foo.com/"); + await PlacesTestUtils.addVisits([{ uri, title: "An Example" }]); + await addOpenPages(uri, 1); + + let query = "ex"; + let context = createContext(query, { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + makeTabSwitchResult(context, { + uri: "http://foo.com/", + title: "An Example", + }), + ], + }); + + await removeOpenPages(uri, 1); + await cleanupPlaces(); +}); + +add_task(async function test_remotetab_matches_override() { + // If we have an history result to the same page, we should only get the + // remote tab match. + let url = "http://foo.remote.com/"; + // First set up Sync to have the page as a remote tab. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs: [ + { + urlHistory: [url], + title: "An Example", + }, + ], + }, + }); + + // Set up Places to think the tab is in history. + await PlacesTestUtils.addVisits(url); + + let query = "ex"; + let context = createContext(query, { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/", + device: "My Phone", + title: "An Example", + }), + ], + }); + + await cleanupPlaces(); +}); + +add_task(async function test_mixed_result_types() { + // In case we have many results, non-remote results should flex to the bottom. + let url = "http://foo.remote.com/"; + let tabs = Array(5) + .fill(0) + .map((e, i) => ({ + urlHistory: [`${url}${i}`], + title: "A title", + lastUsed: Math.floor(Date.now() / 1000) - i * 86400, // i days ago. + })); + // First set up Sync to have the page as a remote tab. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs, + }, + }); + + // Register the page as an open tab. + let openTabUrl = url + "openpage/"; + let uri = Services.io.newURI(openTabUrl); + await PlacesTestUtils.addVisits([{ uri, title: "An Example" }]); + await addOpenPages(uri, 1); + + // Also add a local history result. + let historyUrl = url + "history/"; + await PlacesTestUtils.addVisits(historyUrl); + + let query = "rem"; + let context = createContext(query, { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/0", + device: "My Phone", + title: "A title", + lastUsed: tabs[0].lastUsed, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/1", + device: "My Phone", + title: "A title", + lastUsed: tabs[1].lastUsed, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/2", + device: "My Phone", + title: "A title", + lastUsed: tabs[2].lastUsed, + }), + // We expect to not see /3 and /4 because they are older than the 72 hour + // cutoff. + makeVisitResult(context, { + uri: historyUrl, + title: "test visit for " + historyUrl, + }), + makeTabSwitchResult(context, { + uri: openTabUrl, + title: "An Example", + }), + ], + }); + await removeOpenPages(uri, 1); + await cleanupPlaces(); +}); + +add_task(async function test_many_remotetab_results() { + let url = "http://foo.remote.com/"; + let tabs = Array(6) + .fill(0) + .map((e, i) => ({ + urlHistory: [`${url}${i}`], + title: "A title", + // All the results in this array are fresh (< 72 hours old.) + lastUsed: Math.floor(Date.now() / 1000), + })); + tabs.push({ + urlHistory: [`${url}5`], + title: "A title", + // This result is older than the 72 hour cutoff. + lastUsed: Math.floor(Date.now() / 1000) - 4 * 86400, + }); + + // First set up Sync to have the page as a remote tab. + configureEngine({ + guid_mobile: { + id: "mobile", + tabs, + }, + }); + + let query = "rem"; + let context = createContext(query, { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/0", + device: "My Phone", + title: "A title", + lastUsed: tabs[0].lastUsed, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/1", + device: "My Phone", + title: "A title", + lastUsed: tabs[1].lastUsed, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/2", + device: "My Phone", + title: "A title", + lastUsed: tabs[2].lastUsed, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/3", + device: "My Phone", + title: "A title", + lastUsed: tabs[3].lastUsed, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/4", + device: "My Phone", + title: "A title", + lastUsed: tabs[4].lastUsed, + }), + // We don't expect http://foo.remote.com/5, because it is over the limit + // of maxResults / 2. + // We don't expect http://foo.remote.com/6, because it is both over the + // limit of maxResults / 2 and it is stale. + ], + }); +}); + +add_task(async function test_restrictionCharacter() { + let url = "http://foo.remote.com/"; + let tabs = Array(5) + .fill(0) + .map((e, i) => ({ + urlHistory: [`${url}${i}`], + title: "A title", + lastUsed: Math.floor(Date.now() / 1000) - i, + })); + configureEngine({ + guid_mobile: { + id: "mobile", + tabs, + }, + }); + + // Also add an open page. + let openTabUrl = url + "openpage/"; + let uri = Services.io.newURI(openTabUrl); + await PlacesTestUtils.addVisits([{ uri, title: "An Example" }]); + await addOpenPages(uri, 1); + + // We expect the open tab to flex to the bottom. + let query = UrlbarTokenizer.RESTRICT.OPENPAGE; + let context = createContext(query, { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/0", + device: "My Phone", + title: "A title", + lastUsed: tabs[0].lastUsed, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/1", + device: "My Phone", + title: "A title", + lastUsed: tabs[1].lastUsed, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/2", + device: "My Phone", + title: "A title", + lastUsed: tabs[2].lastUsed, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/3", + device: "My Phone", + title: "A title", + lastUsed: tabs[3].lastUsed, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/4", + device: "My Phone", + title: "A title", + lastUsed: tabs[4].lastUsed, + }), + makeTabSwitchResult(context, { + uri: openTabUrl, + title: "An Example", + }), + ], + }); + await removeOpenPages(uri, 1); + await cleanupPlaces(); +}); + +add_task(async function test_duplicate_remote_tabs() { + let url = "http://foo.remote.com/"; + let tabs = Array(3) + .fill(0) + .map((e, i) => ({ + urlHistory: [url], + title: "A title", + lastUsed: Math.floor(Date.now() / 1000), + })); + configureEngine({ + guid_mobile: { + id: "mobile", + tabs, + }, + }); + + // We expect the duplicate tabs to be deduped. + let query = UrlbarTokenizer.RESTRICT.OPENPAGE; + let context = createContext(query, { isPrivate: false }); + await check_results({ + context, + matches: [ + makeSearchResult(context, { + engineName: SUGGESTIONS_ENGINE_NAME, + heuristic: true, + }), + makeRemoteTabResult(context, { + uri: "http://foo.remote.com/", + device: "My Phone", + title: "A title", + lastUsed: tabs[0].lastUsed, + }), + ], + }); +}); diff --git a/browser/components/urlbar/tests/unit/xpcshell.ini b/browser/components/urlbar/tests/unit/xpcshell.ini index 4a30cf94deeb..156312ca5a4f 100644 --- a/browser/components/urlbar/tests/unit/xpcshell.ini +++ b/browser/components/urlbar/tests/unit/xpcshell.ini @@ -36,6 +36,8 @@ skip-if = os == 'linux' # bug 1474616 [test_providerUnifiedComplete_duplicate_entries.js] [test_query_url.js] [test_queryScorer.js] +[test_remote_tabs.js] +skip-if = !sync [test_resultBuckets.js] [test_search_engine_host.js] [test_search_suggestions.js] diff --git a/toolkit/components/places/tests/unifiedcomplete/test_remote_tab_matches.js b/toolkit/components/places/tests/unifiedcomplete/test_remote_tab_matches.js deleted file mode 100644 index c1575065383b..000000000000 --- a/toolkit/components/places/tests/unifiedcomplete/test_remote_tab_matches.js +++ /dev/null @@ -1,408 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- - * vim:set ts=2 sw=2 sts=2 et: - */ -"use strict"; - -const { Weave } = ChromeUtils.import("resource://services-sync/main.js"); - -Services.prefs.setCharPref("services.sync.username", "someone@somewhere.com"); -Services.prefs.setCharPref("services.sync.registerEngines", ""); - -// A mock "Tabs" engine which autocomplete will use instead of the real -// engine. We pass a constructor that Sync creates. -function MockTabsEngine() { - this.clients = null; // We'll set this dynamically -} - -MockTabsEngine.prototype = { - name: "tabs", - - startTracking() {}, - getAllClients() { - return this.clients; - }, -}; - -// A clients engine that doesn't need to be a constructor. -let MockClientsEngine = { - getClientType(guid) { - Assert.ok(guid.endsWith("desktop") || guid.endsWith("mobile")); - return guid.endsWith("mobile") ? "phone" : "desktop"; - }, - remoteClientExists(id) { - return true; - }, - getClientName(id) { - return id.endsWith("mobile") ? "My Phone" : "My Desktop"; - }, -}; - -// Tell Sync about the mocks. -Weave.Service.engineManager.register(MockTabsEngine); - -// Tell the Sync XPCOM service it is initialized. -let weaveXPCService = Cc["@mozilla.org/weave/service;1"].getService( - Ci.nsISupports -).wrappedJSObject; -weaveXPCService.ready = true; - -// Configure the singleton engine for a test. -function configureEngine(clients) { - // Configure the instance Sync created. - let engine = Weave.Service.engineManager.get("tabs"); - engine.clients = clients; - Weave.Service.clientsEngine = MockClientsEngine; - // Send an observer that pretends the engine just finished a sync. - Services.obs.notifyObservers(null, "weave:engine:sync:finish", "tabs"); -} - -// Make a match object suitable for passing to check_autocomplete. -function makeRemoteTabMatch(url, deviceName, extra = {}) { - return { - uri: makeActionURI("remotetab", { url, deviceName }), - title: extra.title || url, - style: ["action", "remotetab"], - icon: extra.icon, - }; -} - -add_task(async function test_minimal() { - // The minimal client and tabs info we can get away with. - configureEngine({ - guid_desktop: { - id: "desktop", - tabs: [ - { - urlHistory: ["http://example.com/"], - }, - ], - }, - }); - - await check_autocomplete({ - search: "ex", - searchParam: "enable-actions", - matches: [makeRemoteTabMatch("http://example.com/", "My Desktop")], - }); -}); - -add_task(async function test_maximal() { - // Every field that could possibly exist on a remote record. - configureEngine({ - guid_mobile: { - id: "mobile", - tabs: [ - { - urlHistory: ["http://example.com/"], - title: "An Example", - icon: "http://favicon", - }, - ], - }, - }); - - await check_autocomplete({ - search: "ex", - searchParam: "enable-actions", - matches: [ - makeRemoteTabMatch("http://example.com/", "My Phone", { - title: "An Example", - icon: "moz-anno:favicon:http://favicon/", - }), - ], - }); -}); - -add_task(async function test_noShowIcons() { - Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteIcons", false); - configureEngine({ - guid_mobile: { - id: "mobile", - tabs: [ - { - urlHistory: ["http://example.com/"], - title: "An Example", - icon: "http://favicon", - }, - ], - }, - }); - - await check_autocomplete({ - search: "ex", - searchParam: "enable-actions", - matches: [ - makeRemoteTabMatch("http://example.com/", "My Phone", { - title: "An Example", - // expecting the default favicon due to that pref. - icon: "", - }), - ], - }); - Services.prefs.clearUserPref("services.sync.syncedTabs.showRemoteIcons"); -}); - -add_task(async function test_dontMatchSyncedTabs() { - Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteTabs", false); - configureEngine({ - guid_mobile: { - id: "mobile", - tabs: [ - { - urlHistory: ["http://example.com/"], - title: "An Example", - icon: "http://favicon", - }, - ], - }, - }); - - await check_autocomplete({ - search: "ex", - searchParam: "enable-actions", - matches: [], - }); - Services.prefs.clearUserPref("services.sync.syncedTabs.showRemoteTabs"); -}); - -add_task(async function test_matches_title() { - // URL doesn't match search expression, should still match the title. - configureEngine({ - guid_mobile: { - id: "mobile", - tabs: [ - { - urlHistory: ["http://foo.com/"], - title: "An Example", - }, - ], - }, - }); - - await check_autocomplete({ - search: "ex", - searchParam: "enable-actions", - matches: [ - makeRemoteTabMatch("http://foo.com/", "My Phone", { - title: "An Example", - }), - ], - }); -}); - -add_task(async function test_localtab_matches_override() { - // We have an open tab to the same page on a remote device, only "switch to - // tab" should appear as duplicate detection removed the remote one. - - // First setup Sync to have the page as a remote tab. - configureEngine({ - guid_mobile: { - id: "mobile", - tabs: [ - { - urlHistory: ["http://foo.com/"], - title: "An Example", - }, - ], - }, - }); - - // Setup Places to think the tab is open locally. - let uri = NetUtil.newURI("http://foo.com/"); - await PlacesTestUtils.addVisits([{ uri, title: "An Example" }]); - await addOpenPages(uri, 1); - - await check_autocomplete({ - search: "ex", - searchParam: "enable-actions", - matches: [makeSwitchToTabMatch("http://foo.com/", { title: "An Example" })], - }); - await removeOpenPages(uri, 1); -}); - -add_task(async function test_remotetab_matches_override() { - // If We have an history result to the same page, we should only get the - // remote tab match. - let url = "http://foo.remote.com/"; - // First setup Sync to have the page as a remote tab. - configureEngine({ - guid_mobile: { - id: "mobile", - tabs: [ - { - urlHistory: [url], - title: "An Example", - }, - ], - }, - }); - - // Setup Places to think the tab is open locally. - await PlacesTestUtils.addVisits(url); - - await check_autocomplete({ - search: "rem", - searchParam: "enable-actions", - matches: [ - makeRemoteTabMatch("http://foo.remote.com/", "My Phone", { - title: "An Example", - }), - ], - }); -}); - -add_task(async function test_many_remotetab_matches() { - await PlacesUtils.history.clear(); - - // In case we have many results, the most recent ones should be added on top, - // while others should be appended. - let url = "http://foo.remote.com/"; - // First setup Sync to have the page as a remote tab. - configureEngine({ - guid_mobile: { - id: "mobile", - tabs: Array(5) - .fill(0) - .map((e, i) => ({ - urlHistory: [`${url}${i}`], - title: "A title", - lastUsed: Date.now() / 1000 - i * 86400, // i days ago. - })), - }, - }); - - // Also add a local history result. - let historyUrl = url + "history/"; - await PlacesTestUtils.addVisits(historyUrl); - - await check_autocomplete({ - search: "rem", - searchParam: "enable-actions", - checkSorting: true, - matches: [ - makeRemoteTabMatch("http://foo.remote.com/0", "My Phone", { - title: "A title", - }), - makeRemoteTabMatch("http://foo.remote.com/1", "My Phone", { - title: "A title", - }), - makeRemoteTabMatch("http://foo.remote.com/2", "My Phone", { - title: "A title", - }), - { - uri: Services.io.newURI(historyUrl), - title: "test visit for " + historyUrl, - }, - makeRemoteTabMatch("http://foo.remote.com/3", "My Phone", { - title: "A title", - }), - makeRemoteTabMatch("http://foo.remote.com/4", "My Phone", { - title: "A title", - }), - ], - }); -}); - -add_task(async function test_maxResults() { - await PlacesUtils.history.clear(); - - let url = "http://foo.remote.com/"; - - // Add 10 mock remote tabs. Important: We set `lastUsed` so that these tabs - // are newer than RECENT_REMOTE_TAB_THRESHOLD_MS in UnifiedComplete. That way - // they are added immediately in _matchRemoteTabs rather than being pushed - // onto _extraRemoteTabRows and then added later in execute(). The former - // does not check maxResults, but the latter does, and in this test, we want - // to verify that PlacesRemoteTabsAutocompleteProvider.getMatches returns at - // most ceil(maxResults / 2) tabs. - configureEngine({ - guid_mobile: { - id: "mobile", - tabs: Array(10) - .fill(0) - .map((e, i) => ({ - urlHistory: [`${url}${i}`], - title: "A title", - lastUsed: Date.now() - i, - })), - }, - }); - - // Set maxResults to 4 in our search. - await check_autocomplete({ - search: "rem", - searchParam: "enable-actions max-results:4", - checkSorting: true, - matches: [ - makeRemoteTabMatch("http://foo.remote.com/0", "My Phone", { - title: "A title", - }), - makeRemoteTabMatch("http://foo.remote.com/1", "My Phone", { - title: "A title", - }), - makeRemoteTabMatch("http://foo.remote.com/2", "My Phone", { - title: "A title", - }), - makeRemoteTabMatch("http://foo.remote.com/3", "My Phone", { - title: "A title", - }), - ], - }); -}); - -add_task(async function test_restrictionCharacter() { - await PlacesUtils.history.clear(); - - let url = "http://foo.remote.com/"; - - configureEngine({ - guid_mobile: { - id: "mobile", - tabs: Array(10) - .fill(0) - .map((e, i) => ({ - urlHistory: [`${url}${i}`], - title: "A title", - lastUsed: Date.now() - i, - })), - }, - }); - - // Also add an open page. - let uri = Services.io.newURI(`${url}openpage/`); - await PlacesTestUtils.addVisits([{ uri, title: "An Example" }]); - await addOpenPages(uri, 1); - - // Set maxResults to 7 in our search. 7 results should be returned: - // ceil(maxResults / 2) remote tabs, then the open tab, then 2 more remote tab - // results to get to 7 total. - await check_autocomplete({ - search: UrlbarTokenizer.RESTRICT.OPENPAGE, - searchParam: "enable-actions max-results:7", - checkSorting: true, - matches: [ - makeRemoteTabMatch("http://foo.remote.com/0", "My Phone", { - title: "A title", - }), - makeRemoteTabMatch("http://foo.remote.com/1", "My Phone", { - title: "A title", - }), - makeRemoteTabMatch("http://foo.remote.com/2", "My Phone", { - title: "A title", - }), - makeRemoteTabMatch("http://foo.remote.com/3", "My Phone", { - title: "A title", - }), - makeSwitchToTabMatch("http://foo.remote.com/openpage/", { - title: "An Example", - }), - makeRemoteTabMatch("http://foo.remote.com/4", "My Phone", { - title: "A title", - }), - makeRemoteTabMatch("http://foo.remote.com/5", "My Phone", { - title: "A title", - }), - ], - }); - await removeOpenPages(uri, 1); -}); diff --git a/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini b/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini index cf800156825e..c315e1c77293 100644 --- a/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini +++ b/toolkit/components/places/tests/unifiedcomplete/xpcshell.ini @@ -23,8 +23,6 @@ support-files = [test_keyword_search_actions.js] [test_multi_word_search.js] [test_preloaded_sites.js] -[test_remote_tab_matches.js] -skip-if = !sync [test_search_engine_alias.js] [test_search_engine_restyle.js] [test_special_search.js]