summaryrefslogtreecommitdiffstats
path: root/toolkit/components/search/tests/xpcshell/test_appProvided_icons_updates.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/search/tests/xpcshell/test_appProvided_icons_updates.js')
-rw-r--r--toolkit/components/search/tests/xpcshell/test_appProvided_icons_updates.js324
1 files changed, 324 insertions, 0 deletions
diff --git a/toolkit/components/search/tests/xpcshell/test_appProvided_icons_updates.js b/toolkit/components/search/tests/xpcshell/test_appProvided_icons_updates.js
new file mode 100644
index 0000000000..4159b9f0fb
--- /dev/null
+++ b/toolkit/components/search/tests/xpcshell/test_appProvided_icons_updates.js
@@ -0,0 +1,324 @@
+/* Any copyright is dedicated to the Public Domain.
+https://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests to ensure that icons for application provided engines are correctly
+ * updated from remote settings.
+ */
+
+"use strict";
+
+// A skeleton configuration that gets filled in from TESTS during `add_setup`.
+let CONFIG = [
+ {
+ identifier: "engine_no_initial_icon",
+ recordType: "engine",
+ base: {
+ name: "engine_no_initial_icon name",
+ urls: {
+ search: {
+ base: "https://example.com/1",
+ searchTermParamName: "q",
+ },
+ },
+ },
+ variants: [{ environment: { allRegionsAndLocales: true } }],
+ },
+ {
+ identifier: "engine_icon_updates",
+ recordType: "engine",
+ base: {
+ name: "engine_icon_updates name",
+ urls: {
+ search: {
+ base: "https://example.com/2",
+ searchTermParamName: "q",
+ },
+ },
+ },
+ variants: [{ environment: { allRegionsAndLocales: true } }],
+ },
+ {
+ identifier: "engine_icon_not_local",
+ recordType: "engine",
+ base: {
+ name: "engine_icon_not_local name",
+ urls: {
+ search: {
+ base: "https://example.com/3",
+ searchTermParamName: "q",
+ },
+ },
+ },
+ variants: [{ environment: { allRegionsAndLocales: true } }],
+ },
+ {
+ identifier: "engine_icon_out_of_date",
+ recordType: "engine",
+ base: {
+ name: "engine_icon_out_of_date name",
+ urls: {
+ search: {
+ base: "https://example.com/4",
+ searchTermParamName: "q",
+ },
+ },
+ },
+ variants: [{ environment: { allRegionsAndLocales: true } }],
+ },
+ {
+ recordType: "defaultEngines",
+ globalDefault: "engine_no_initial_icon",
+ specificDefaults: [],
+ },
+ {
+ recordType: "engineOrders",
+ orders: [],
+ },
+];
+
+async function assertIconMatches(actualIconData, expectedIcon) {
+ let expectedBuffer = new Uint8Array(await getFileDataBuffer(expectedIcon));
+
+ Assert.equal(
+ actualIconData.length,
+ expectedBuffer.length,
+ "Should have received matching buffer lengths for the expected icon"
+ );
+ Assert.ok(
+ actualIconData.every((value, index) => value === expectedBuffer[index]),
+ "Should have received matching data for the expected icon"
+ );
+}
+
+async function assertEngineIcon(engineName, expectedIcon) {
+ let engine = Services.search.getEngineByName(engineName);
+ let engineIconURL = await engine.getIconURL(16);
+
+ if (expectedIcon) {
+ Assert.notEqual(
+ engineIconURL,
+ null,
+ "Should have an icon URL for the engine."
+ );
+
+ let response = await fetch(engineIconURL);
+ let buffer = new Uint8Array(await response.arrayBuffer());
+
+ await assertIconMatches(buffer, expectedIcon);
+ } else {
+ Assert.equal(
+ engineIconURL,
+ null,
+ "Should not have an icon URL for the engine."
+ );
+ }
+}
+
+let originalIconId = Services.uuid.generateUUID().toString();
+let client;
+
+add_setup(async function setup() {
+ useHttpServer();
+ SearchTestUtils.useMockIdleService();
+
+ client = RemoteSettings("search-config-icons");
+ await client.db.clear();
+
+ sinon.stub(client.attachments, "_baseAttachmentsURL").returns(gDataUrl);
+
+ // Add some initial records and attachments into the remote settings collection.
+ await insertRecordIntoCollection(client, {
+ id: originalIconId,
+ filename: "remoteIcon.ico",
+ // This uses a wildcard match to test the icon is still applied correctly.
+ engineIdentifiers: ["engine_icon_upd*"],
+ imageSize: 16,
+ });
+ // This attachment is not cached, so we don't have it locally.
+ await insertRecordIntoCollection(
+ client,
+ {
+ id: Services.uuid.generateUUID().toString(),
+ filename: "bigIcon.ico",
+ engineIdentifiers: [
+ // This also tests multiple engine idenifiers works.
+ "enterprise",
+ "next_generation",
+ "engine_icon_not_local",
+ ],
+ imageSize: 16,
+ },
+ false
+ );
+
+ // Add a record that is out of date, and update it with a newer one, but don't
+ // cache the attachment for the new one.
+ let outOfDateRecordId = Services.uuid.generateUUID().toString();
+ await insertRecordIntoCollection(
+ client,
+ {
+ id: outOfDateRecordId,
+ filename: "remoteIcon.ico",
+ engineIdentifiers: ["engine_icon_out_of_date"],
+ imageSize: 16,
+ // 10 minutes ago.
+ lastModified: Date.now() - 600000,
+ },
+ true
+ );
+ let { record } = await mockRecordWithAttachment({
+ id: outOfDateRecordId,
+ filename: "bigIcon.ico",
+ engineIdentifiers: ["engine_icon_out_of_date"],
+ imageSize: 16,
+ });
+ await client.db.update(record);
+ await client.db.importChanges({}, record.lastModified);
+
+ await SearchTestUtils.useTestEngines("simple-engines", null, CONFIG);
+ await Services.search.init();
+
+ // Testing that an icon is not local generates a `Could not find {id}...`
+ // message.
+ consoleAllowList.push("Could not find");
+});
+
+add_task(async function test_icon_added_unknown_engine() {
+ // If the engine is unknown, and this is a new icon, we should still download
+ // the icon, in case the engine is added to the configuration later.
+ let newIconId = Services.uuid.generateUUID().toString();
+
+ let mock = await mockRecordWithAttachment({
+ id: newIconId,
+ filename: "bigIcon.ico",
+ engineIdentifiers: ["engine_unknown"],
+ imageSize: 16,
+ });
+ await client.db.update(mock.record, Date.now());
+
+ await client.emit("sync", {
+ data: {
+ current: [mock.record],
+ created: [mock.record],
+ updated: [],
+ deleted: [],
+ },
+ });
+
+ SearchTestUtils.idleService._fireObservers("idle");
+
+ let icon;
+ await TestUtils.waitForCondition(async () => {
+ try {
+ icon = await client.attachments.get(mock.record);
+ } catch (ex) {
+ // Do nothing.
+ }
+ return !!icon;
+ }, "Should have loaded the icon into the attachments store.");
+
+ await assertIconMatches(new Uint8Array(icon.buffer), "bigIcon.ico");
+});
+
+add_task(async function test_icon_added_existing_engine() {
+ // If the engine is unknown, and this is a new icon, we should still download
+ // it, in case the engine is added to the configuration later.
+ let newIconId = Services.uuid.generateUUID().toString();
+
+ let mock = await mockRecordWithAttachment({
+ id: newIconId,
+ filename: "bigIcon.ico",
+ engineIdentifiers: ["engine_no_initial_icon"],
+ imageSize: 16,
+ });
+ await client.db.update(mock.record, Date.now());
+
+ let promiseEngineUpdated = SearchTestUtils.promiseSearchNotification(
+ SearchUtils.MODIFIED_TYPE.CHANGED,
+ SearchUtils.TOPIC_ENGINE_MODIFIED
+ );
+
+ await client.emit("sync", {
+ data: {
+ current: [mock.record],
+ created: [mock.record],
+ updated: [],
+ deleted: [],
+ },
+ });
+
+ SearchTestUtils.idleService._fireObservers("idle");
+
+ await promiseEngineUpdated;
+ await assertEngineIcon("engine_no_initial_icon name", "bigIcon.ico");
+});
+
+add_task(async function test_icon_updated() {
+ // Test that when an update for an engine icon is received, the engine is
+ // correctly updated.
+
+ // Check the engine has the expected icon to start with.
+ await assertEngineIcon("engine_icon_updates name", "remoteIcon.ico");
+
+ // Update the icon for the engine.
+ let mock = await mockRecordWithAttachment({
+ id: originalIconId,
+ filename: "bigIcon.ico",
+ engineIdentifiers: ["engine_icon_upd*"],
+ imageSize: 16,
+ });
+ await client.db.update(mock.record, Date.now());
+
+ let promiseEngineUpdated = SearchTestUtils.promiseSearchNotification(
+ SearchUtils.MODIFIED_TYPE.CHANGED,
+ SearchUtils.TOPIC_ENGINE_MODIFIED
+ );
+
+ await client.emit("sync", {
+ data: {
+ current: [mock.record],
+ created: [],
+ updated: [{ new: mock.record }],
+ deleted: [],
+ },
+ });
+ SearchTestUtils.idleService._fireObservers("idle");
+
+ await promiseEngineUpdated;
+ await assertEngineIcon("engine_icon_updates name", "bigIcon.ico");
+});
+
+add_task(async function test_icon_not_local() {
+ // Tests that a download is queued and triggered when the icon for an engine
+ // is not in either the local dump nor the cache.
+
+ await assertEngineIcon("engine_icon_not_local name", null);
+
+ // A download should have been queued, so fire idle to trigger it.
+ let promiseEngineUpdated = SearchTestUtils.promiseSearchNotification(
+ SearchUtils.MODIFIED_TYPE.CHANGED,
+ SearchUtils.TOPIC_ENGINE_MODIFIED
+ );
+ SearchTestUtils.idleService._fireObservers("idle");
+ await promiseEngineUpdated;
+
+ await assertEngineIcon("engine_icon_not_local name", "bigIcon.ico");
+});
+
+add_task(async function test_icon_out_of_date() {
+ // Tests that a download is queued and triggered when the icon for an engine
+ // is not in either the local dump nor the cache.
+
+ await assertEngineIcon("engine_icon_out_of_date name", "remoteIcon.ico");
+
+ // A download should have been queued, so fire idle to trigger it.
+ let promiseEngineUpdated = SearchTestUtils.promiseSearchNotification(
+ SearchUtils.MODIFIED_TYPE.CHANGED,
+ SearchUtils.TOPIC_ENGINE_MODIFIED
+ );
+ SearchTestUtils.idleService._fireObservers("idle");
+ await promiseEngineUpdated;
+
+ await assertEngineIcon("engine_icon_out_of_date name", "bigIcon.ico");
+});