/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { ASRouter, MessageLoaderUtils } = ChromeUtils.import( "resource://activity-stream/lib/ASRouter.jsm" ); const { PanelTestProvider } = ChromeUtils.importESModule( "resource://activity-stream/lib/PanelTestProvider.sys.mjs" ); const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); const { RemoteL10n } = ChromeUtils.importESModule( "resource://activity-stream/lib/RemoteL10n.sys.mjs" ); const { RemoteSettings } = ChromeUtils.importESModule( "resource://services-settings/remote-settings.sys.mjs" ); // This pref is used to override the Remote Settings server URL in tests. // See SERVER_URL in services/settings/Utils.jsm for more details. const RS_SERVER_PREF = "services.settings.server"; const FLUENT_CONTENT = "asrouter-test-string = Test Test Test\n"; async function serveRemoteSettings() { const server = new HttpServer(); server.start(-1); const baseURL = `http://localhost:${server.identity.primaryPort}/`; const attachmentUuid = crypto.randomUUID(); const attachment = new TextEncoder().encode(FLUENT_CONTENT); // Serve an index so RS knows where to fetch images from. server.registerPathHandler("/v1/", (request, response) => { response.write( JSON.stringify({ capabilities: { attachments: { base_url: `${baseURL}cdn`, }, }, }) ); }); // Serve the ms-language-packs record for cfr-v1-ja-JP-mac, pointing to an attachment. server.registerPathHandler( "/v1/buckets/main/collections/ms-language-packs/records/cfr-v1-ja-JP-mac", (request, response) => { response.setStatusLine(null, 200, "OK"); response.setHeader( "Content-type", "application/json; charset=utf-8", false ); response.write( JSON.stringify({ permissions: {}, data: { attachment: { hash: "f9aead2693c4ff95c2764df72b43fdf5b3490ed06414588843848f991136040b", size: attachment.buffer.byteLength, filename: "asrouter.ftl", location: `main-workspace/ms-language-packs/${attachmentUuid}`, }, id: "cfr-v1-ja-JP-mac", last_modified: Date.now(), }, }) ); } ); // Serve the attachment for ms-language-packs/cfr-va-ja-JP-mac. server.registerPathHandler( `/cdn/main-workspace/ms-language-packs/${attachmentUuid}`, (request, response) => { const stream = Cc[ "@mozilla.org/io/arraybuffer-input-stream;1" ].createInstance(Ci.nsIArrayBufferInputStream); stream.setData(attachment.buffer, 0, attachment.buffer.byteLength); response.setStatusLine(null, 200, "OK"); response.setHeader("Content-type", "application/octet-stream"); response.bodyOutputStream.writeFrom(stream, attachment.buffer.byteLength); } ); // Serve the list of changed collections. cfr must have changed, otherwise we // won't attempt to fetch the cfr records (and then won't fetch // ms-language-packs). server.registerPathHandler( "/v1/buckets/monitor/collections/changes/changeset", (request, response) => { const now = Date.now(); response.setStatusLine(null, 200, "OK"); response.setHeader( "Content-type", "application/json; charset=utf-8", false ); response.write( JSON.stringify({ timestamp: now, changes: [ { host: `localhost:${server.identity.primaryPort}`, last_modified: now, bucket: "main", collection: "cfr", }, ], metadata: {}, }) ); } ); const message = await PanelTestProvider.getMessages().then(msgs => msgs.find(msg => msg.id === "PERSONALIZED_CFR_MESSAGE") ); // Serve the "changed" cfr entries. If there are no changes, then ASRouter // won't re-fetch ms-language-packs. server.registerPathHandler( "/v1/buckets/main/collections/cfr/changeset", (request, response) => { const now = Date.now(); response.setStatusLine(null, 200, "OK"); response.setHeader( "Content-type", "application/json; charset=utf-8", false ); response.write( JSON.stringify({ timestamp: now, changes: [message], metadata: {}, }) ); } ); await SpecialPowers.pushPrefEnv({ set: [[RS_SERVER_PREF, `${baseURL}v1`]], }); return async () => { await new Promise(resolve => server.stop(() => resolve())); await SpecialPowers.popPrefEnv(); }; } add_task(async function test_asrouter() { const MS_LANGUAGE_PACKS_DIR = PathUtils.join( PathUtils.localProfileDir, "settings", "main", "ms-language-packs" ); const sandbox = sinon.createSandbox(); const stop = await serveRemoteSettings(); await SpecialPowers.pushPrefEnv({ set: [ [ "browser.newtabpage.activity-stream.asrouter.providers.cfr", JSON.stringify({ id: "cfr", enabled: true, type: "remote-settings", collection: "cfr", updateCyleInMs: 3600000, }), ], ], }); const localeService = Services.locale; RemoteSettings("cfr").verifySignature = false; registerCleanupFunction(async () => { RemoteSettings("cfr").verifySignature = true; Services.locale = localeService; await SpecialPowers.popPrefEnv(); await stop(); sandbox.restore(); await IOUtils.remove(MS_LANGUAGE_PACKS_DIR, { recursive: true }); RemoteL10n.reloadL10n(); }); // We can't stub Services.locale.appLocaleAsBCP47 directly because its an // XPCOM_Native object. const fakeLocaleService = new Proxy(localeService, { get(obj, prop) { if (prop === "appLocaleAsBCP47") { return "ja-JP-macos"; } return obj[prop]; }, }); const localeSpy = sandbox.spy(MessageLoaderUtils, "locale", ["get"]); Services.locale = fakeLocaleService; const cfrProvider = ASRouter.state.providers.find(p => p.id === "cfr"); await ASRouter.loadMessagesFromAllProviders([cfrProvider]); Assert.equal( Services.locale.appLocaleAsBCP47, "ja-JP-macos", "Locale service returns ja-JP-macos" ); Assert.ok(localeSpy.get.called, "MessageLoaderUtils.locale getter called"); Assert.ok( localeSpy.get.alwaysReturned("ja-JP-mac"), "MessageLoaderUtils.locale getter returned expected locale ja-JP-mac" ); const path = PathUtils.join( MS_LANGUAGE_PACKS_DIR, "browser", "newtab", "asrouter.ftl" ); Assert.ok(await IOUtils.exists(path), "asrouter.ftl was downloaded"); Assert.equal( await IOUtils.readUTF8(path), FLUENT_CONTENT, "asrouter.ftl content matches expected" ); });