Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
-
CI results over the last 21 days6,428Runs99.98%Pass1Failures0Timeouts0Crashes0Skips
Runs with issues per day (up to 0.3%) Oct 3Oct 23- Failures
- This test runs only with pattern: os != 'android'
- Manifest: browser/components/asrouter/tests/xpcshell/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
"use strict";
const { PinnableSitesProvider } = ChromeUtils.importESModule(
"resource:///modules/asrouter/PinnableSitesProvider.sys.mjs"
);
const { NewTabUtils } = ChromeUtils.importESModule(
"resource://gre/modules/NewTabUtils.sys.mjs"
);
const { TaskbarTabs } = ChromeUtils.importESModule(
"moz-src:///browser/components/taskbartabs/TaskbarTabs.sys.mjs"
);
const { generateName } = ChromeUtils.importESModule(
"moz-src:///browser/components/taskbartabs/TaskbarTabsRegistry.sys.mjs"
);
const { HomePage } = ChromeUtils.importESModule(
"resource:///modules/HomePage.sys.mjs"
);
const { AboutNewTab } = ChromeUtils.importESModule(
"resource:///modules/AboutNewTab.sys.mjs"
);
const { SearchService } = ChromeUtils.importESModule(
"moz-src:///toolkit/components/search/SearchService.sys.mjs"
);
const { FilterAdult } = ChromeUtils.importESModule(
"resource:///modules/FilterAdult.sys.mjs"
);
const { PlacesUtils } = ChromeUtils.importESModule(
"resource://gre/modules/PlacesUtils.sys.mjs"
);
const CURATED = [
{
id: "curated-gmail",
name: "Gmail",
description: "mail.google.com",
},
{
id: "curated-youtube",
name: "YouTube",
description: "youtube.com",
},
];
function contentWithTile(tile = {}) {
return {
screens: [
{
id: "SCREEN_1",
content: {
tiles: {
type: "pinnable_sites",
source: "topFrecentSites",
data: structuredClone(CURATED),
...tile,
},
},
},
],
};
}
const tileOf = content => content.screens[0].content.tiles;
/**
* @param {object[]} pages - {url, frecency} pairs to serve as history.
* @param {object} [options]
* @param {string} [options.homePage] - Value for HomePage.get().
* @param {string} [options.newTabURL] - Value for AboutNewTab.newTabURL.
* @param {string[]} [options.engineDomains] - Visible search engine domains.
* @param {string[]} [options.pinnedHosts] - Hosts with an existing Taskbar Tab.
* @param {string[]} [options.iconlessHosts] - Hosts with no stored favicon.
* @returns {object} Sinon sandbox
*/
function stubEnvironment(pages, options = {}) {
const {
homePage = "about:home",
newTabURL = "about:newtab",
engineDomains = [],
pinnedHosts = [],
iconlessHosts = [],
} = options;
const sandbox = sinon.createSandbox();
sandbox
.stub(NewTabUtils.activityStreamProvider, "getTopFrecentSites")
.resolves(pages);
sandbox
.stub(TaskbarTabs, "findTaskbarTab")
.callsFake(async uri =>
pinnedHosts.includes(uri.host) ? { id: "already-pinned" } : null
);
sandbox.stub(PlacesUtils, "favicons").value({
getFaviconForPage: async uri =>
iconlessHosts.includes(uri.host) ? null : { width: 32 },
});
sandbox.stub(HomePage, "get").returns(homePage);
sandbox.stub(AboutNewTab, "newTabURL").get(() => newTabURL);
sandbox
.stub(SearchService, "getVisibleEngines")
.resolves(engineDomains.map(searchUrlDomain => ({ searchUrlDomain })));
return sandbox;
}
add_task(async function test_ranksByStrongestPageAndPreservesSubdomains() {
const sandbox = stubEnvironment([
]);
try {
const content = await PinnableSitesProvider.populate(
contentWithTile({ backfill: false })
);
const { data } = tileOf(content);
Assert.deepEqual(
data.map(item => item.url),
[
],
"Origins are ranked by their strongest page, subdomains kept distinct"
);
Assert.equal(
data[0].name,
"Google Docs",
"The tile label is the same clean app name the taskbar shortcut gets"
);
Assert.equal(
data[0].name,
"The label comes from the shared Taskbar Tabs name helper"
);
Assert.equal(
data[0].iconUrl,
"Icons resolve through the Places favicon service"
);
Assert.deepEqual(
data.map(item => item.id),
["site_1", "site_2", "site_3"],
"Ids are positional so origin is never reported in telemetry"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_wwwVariantsAreOneAppButSubdomainsAreNot() {
const sandbox = stubEnvironment([
]);
try {
const content = await PinnableSitesProvider.populate(
contentWithTile({ backfill: false })
);
Assert.deepEqual(
tileOf(content).data.map(item => item.url),
[
// We pin the variant the user uses most.
],
"www and non-www collapse to one app and subdomains stay distinct"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_strongestPageWinsOverManyWeakerPages() {
const sandbox = stubEnvironment([
]);
try {
const content = await PinnableSitesProvider.populate(contentWithTile());
Assert.equal(
tileOf(content).data[0].url,
"An origin scores on its strongest single page, so a news site read across many one-off articles does not outrank a daily use app"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_exclusionsAppliedBeforeRanking() {
const sandbox = stubEnvironment(
[
],
{
engineDomains: ["www.searchy.example", "searchy2.example"],
pinnedHosts: ["pinned.example.com"],
}
);
try {
const content = await PinnableSitesProvider.populate(
contentWithTile({ backfill: false })
);
Assert.deepEqual(
tileOf(content).data.map(item => item.url),
"non-https, localhost, IP literals, unqualified hosts, search roots the homepage, the newtab page and already-pinned apps are all removed"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_backfillComposesWithPersonalization() {
const sandbox = stubEnvironment([
]);
try {
const content = await PinnableSitesProvider.populate(
contentWithTile({ slots: 3 })
);
const { data } = tileOf(content);
Assert.deepEqual(
data.map(item => item.id),
["site_1", "curated-gmail", "curated-youtube"],
"Unfilled slots come from the curated array, keeping their authored ids"
);
Assert.deepEqual(
data.map(item => !!item.personalized),
[true, false, false],
"Only rows drawn from history are flagged as personalized"
);
Assert.equal(data.length, 3, "The slot count is honored");
} finally {
sandbox.restore();
}
});
add_task(async function test_backfillSkipsDuplicateAndPinnedOrigins() {
const sandbox = stubEnvironment(
{ pinnedHosts: ["www.youtube.com"] }
);
try {
const content = await PinnableSitesProvider.populate(
contentWithTile({ slots: 5 })
);
Assert.deepEqual(
tileOf(content).data.map(item => item.id),
["site_1"],
"Backfill skips an origin already shown and one already pinned"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_pinnedMatchesEitherWwwVariant() {
const sandbox = stubEnvironment(
[
],
{ pinnedHosts: ["www.youtube.com", "figma.com"] }
);
try {
const content = await PinnableSitesProvider.populate(
contentWithTile({ backfill: false })
);
Assert.deepEqual(
tileOf(content).data.map(item => item.url),
"An app pinned under the other www variant is still excluded"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_curatedTilesAreLeftAlone() {
const sandbox = stubEnvironment([
]);
try {
const content = contentWithTile({ source: "curated" });
Assert.ok(
!PinnableSitesProvider.hasPersonalizedTile(content),
"A curated tile does not register as personalized"
);
await PinnableSitesProvider.populate(content);
Assert.deepEqual(
tileOf(content).data,
CURATED,
"The configured data is untouched"
);
Assert.ok(
NewTabUtils.activityStreamProvider.getTopFrecentSites.notCalled,
"History is not read for a curated tile"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_historyFailureFallsBackToCurated() {
const sandbox = stubEnvironment([]);
NewTabUtils.activityStreamProvider.getTopFrecentSites.rejects(
new Error("Places is unavailable")
);
try {
const content = await PinnableSitesProvider.populate(contentWithTile());
Assert.deepEqual(
tileOf(content).data,
CURATED,
"A failure leaves the configured data in place"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_adultSitesAreNeverOffered() {
const sandbox = stubEnvironment([
]);
sandbox
.stub(FilterAdult, "filter")
.callsFake(links =>
);
try {
const content = await PinnableSitesProvider.populate(
contentWithTile({ backfill: false })
);
Assert.deepEqual(
tileOf(content).data.map(item => item.url),
"A site the adult filter rejects is dropped however high it ranks"
);
Assert.ok(
FilterAdult.filter.calledOnce,
"Candidates go through the filter"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_floorWithholdsTheMessage() {
const sandbox = stubEnvironment([
]);
try {
Assert.equal(
await PinnableSitesProvider.populate(
contentWithTile({ slots: 5, minPersonalized: 3 })
),
null,
"Too few personalized sites withholds the whole message"
);
const content = await PinnableSitesProvider.populate(
contentWithTile({ slots: 5, minPersonalized: 2 })
);
Assert.deepEqual(
tileOf(content).data.map(item => item.id),
["site_1", "site_2", "curated-gmail", "curated-youtube"],
"Meeting the floor shows the message, still backfilling the spare slots"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_floorIgnoresBackfilledSlots() {
const sandbox = stubEnvironment([
]);
try {
Assert.equal(
await PinnableSitesProvider.populate(
contentWithTile({ slots: 3, minPersonalized: 2 })
),
null,
"Curated sites topping up the slots do not count toward the floor"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_floorDefaultsToNeverWithholding() {
const sandbox = stubEnvironment([]);
try {
const content = await PinnableSitesProvider.populate(contentWithTile());
Assert.deepEqual(
tileOf(content).data,
CURATED,
"With no floor set, a user with no qualifying history still sees the curated list rather than nothing"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_emptyTileWithholdsTheMessage() {
const sandbox = stubEnvironment([]);
try {
Assert.equal(
await PinnableSitesProvider.populate(
contentWithTile({ backfill: false })
),
null,
"No qualifying history and no backfill withholds the message rather than showing an empty list"
);
} finally {
sandbox.restore();
}
});
add_task(async function test_sitesWithoutIconsAreSkipped() {
const sandbox = stubEnvironment(
[
],
{ iconlessHosts: ["intranet.example.com"] }
);
try {
const content = await PinnableSitesProvider.populate(
contentWithTile({ slots: 3 })
);
Assert.deepEqual(
tileOf(content).data.map(item => item.url),
[
],
"A site with no icon is skipped and its slot is backfilled"
);
} finally {
sandbox.restore();
}
});