diff --git a/CHANGELOG.md b/CHANGELOG.md index 0019b44c..e9bea3ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## Wednesday, April 16th, 2025 + +### Added + +- Added new feature IDs to the `MESSAGING_EXPERIMENTS_DEFAULT_FEATURES` list for android messaging experiments +- Added support for android experiments with `messaging` featureIds in `getAndroidBranchInfo` + ## Tueday, April 15th, 2025 ### Updated diff --git a/__tests__/lib/messageUtils.test.ts b/__tests__/lib/messageUtils.test.ts index 054724a7..be1465cf 100644 --- a/__tests__/lib/messageUtils.test.ts +++ b/__tests__/lib/messageUtils.test.ts @@ -4,6 +4,7 @@ import { toBinary, getDashboardIdForTemplate, messageHasMicrosurvey, + getAndroidDashboard, } from "@/lib/messageUtils"; describe("isAboutWelcomeTemplate", () => { @@ -221,6 +222,39 @@ describe("getDashboard", () => { expect(params.get("Experiment")).toBe(""); expect(params.get("Branch")).toBe(""); }); + + it("returns a correct dashboard link for Android messaging experiments", () => { + const template = "survey"; + const msgIdPrefix = "a:bc-en-us"; // weird chars to test URI encoding + const experiment = "experiment:test"; + const branchSlug = "treatment:a"; + const startDate = "2025-03-08"; + const endDate = "2025-05-08"; + const dashboardId = "2191"; + const submissionDate = "2025-03-08 to today"; + + const result = getAndroidDashboard( + template, + msgIdPrefix, + undefined, + experiment, + branchSlug, + startDate, + endDate, + ) as string; + const url = new URL(result); + const params = url.searchParams; + + expect(url.pathname.endsWith(dashboardId)).toBe(true); + expect(params.get("Submission Date")).toBe(submissionDate); + expect(params.get("Normalized Channel")).toBe(""); + expect(params.get("Normalized OS")).toBe(""); + expect(params.get("Client Info App Display Version")).toBe(""); + expect(params.get("Normalized Country Code")).toBe(""); + expect(params.get("Experiment Slug")).toBe(experiment); + expect(params.get("Experiment Branch")).toBe(branchSlug); + expect(params.get("Value")).toBe("a:bc-%"); + }); }); describe("messageHasMicrosurvey", () => { diff --git a/__tests__/lib/nimbusRecipe.test.ts b/__tests__/lib/nimbusRecipe.test.ts index 779bb960..6e7e1670 100644 --- a/__tests__/lib/nimbusRecipe.test.ts +++ b/__tests__/lib/nimbusRecipe.test.ts @@ -1,7 +1,7 @@ import { NimbusRecipe } from "@/lib/nimbusRecipe"; import { ExperimentFakes } from "@/__tests__/ExperimentFakes.mjs"; import { BranchInfo } from "@/app/columns.jsx"; -import { getDashboard, getDashboardIdForTemplate } from "@/lib/messageUtils"; +import { getAndroidDashboard, getDashboard } from "@/lib/messageUtils"; import { getExperimentLookerDashboardDate } from "@/lib/lookerUtils"; import { formatDate } from "@/lib/experimentUtils"; @@ -71,6 +71,52 @@ const AW_RECIPE = { ], }; +const ANDROID_RECIPE = { + id: "android-msg-test-recipe", + slug: "android-msg-test-recipe", + schemaVersion: "1.12.0", + appId: "org.mozilla.firefox", + appName: "fenix", + application: "org.mozilla.firefox", + channel: "nightly", + isEnrollmentPaused: false, + probeSets: [], + startDate: null, + endDate: null, + proposedEnrollment: 7, + referenceBranch: "control", + userFacingName: "Android Messaging Recipe", + userFacingDescription: "Android messaging surface recipe", + bucketConfig: { + namespace: "nimbus-test-utils", + randomizationUnit: "nimbus_id", + start: 0, + count: 100, + total: 1000, + }, + branches: [ + { + features: [ + { + enabled: true, + featureId: "messaging", + value: { + messages: { + "android-msg-test-recipe-en-us": { + surface: "survey", + }, + }, + }, + }, + ], + ratio: 1, + slug: "control", + screenshots: ["screenshotURI"], + description: "control description", + }, + ], +}; + describe("NimbusRecipe", () => { it("creates a NimbusRecipe from a raw JS recipe object", () => { const rawRecipe = ExperimentFakes.recipe("test-recipe"); @@ -109,124 +155,193 @@ describe("NimbusRecipe", () => { }); describe("getBranchInfo", () => { - it("returns a BranchInfo object", () => { - const rawRecipe = ExperimentFakes.recipe("test-recipe"); - const nimbusRecipe = new NimbusRecipe(rawRecipe); - // XXX should add a method to NimbusRecipe and call the getter instead of - // violating encapsulation like this. Or, alternately, should retrieve - // branch info by slug. - const branch = rawRecipe.branches[1]; - - const branchInfo = nimbusRecipe.getBranchInfo(branch); - - expect(branchInfo).toEqual({ - product: "Desktop", - id: branch.slug, - isBranch: true, - nimbusExperiment: rawRecipe, - slug: branch.slug, - surface: "testTemplate", - template: "testTemplate", - screenshots: ["screenshotURI"], - description: "test description", - hasMicrosurvey: false, + describe("getDesktopBranchInfo", () => { + it("returns a BranchInfo object", () => { + const rawRecipe = ExperimentFakes.recipe("test-recipe"); + const nimbusRecipe = new NimbusRecipe(rawRecipe); + // XXX should add a method to NimbusRecipe and call the getter instead of + // violating encapsulation like this. Or, alternately, should retrieve + // branch info by slug. + const branch = rawRecipe.branches[1]; + + const branchInfo = nimbusRecipe.getBranchInfo(branch); + + expect(branchInfo).toEqual({ + product: "Desktop", + id: branch.slug, + isBranch: true, + nimbusExperiment: rawRecipe, + slug: branch.slug, + surface: "testTemplate", + template: "testTemplate", + screenshots: ["screenshotURI"], + description: "test description", + hasMicrosurvey: false, + }); }); - }); - - it("returns a specialized BranchInfo object if the recipe is from about:welcome and has screens", () => { - const nimbusRecipe = new NimbusRecipe(AW_RECIPE); - const branch = AW_RECIPE.branches[1]; - - const branchInfo = nimbusRecipe.getBranchInfo(branch); - const proposedEndDate = getExperimentLookerDashboardDate( - branchInfo.nimbusExperiment.startDate, - branchInfo.nimbusExperiment.proposedDuration, - ); - const formattedEndDate = formatDate( - branchInfo.nimbusExperiment.endDate as string, - 1, - ); - const dashboardLink = getDashboard( - branchInfo.template as string, - branchInfo.id, - undefined, - branchInfo.nimbusExperiment.slug, - branch.slug, - branchInfo.nimbusExperiment.startDate, - branchInfo.nimbusExperiment.endDate - ? formattedEndDate - : proposedEndDate, - false, // default for isCompleted in constructor - ); + it("returns a specialized BranchInfo object if the recipe is from about:welcome and has screens", () => { + const nimbusRecipe = new NimbusRecipe(AW_RECIPE); + const branch = AW_RECIPE.branches[1]; + + const branchInfo = nimbusRecipe.getBranchInfo(branch); + const proposedEndDate = getExperimentLookerDashboardDate( + branchInfo.nimbusExperiment.startDate, + branchInfo.nimbusExperiment.proposedDuration, + ); + const formattedEndDate = formatDate( + branchInfo.nimbusExperiment.endDate as string, + 1, + ); + + const dashboardLink = getDashboard( + branchInfo.template as string, + branchInfo.id, + undefined, + branchInfo.nimbusExperiment.slug, + branch.slug, + branchInfo.nimbusExperiment.startDate, + branchInfo.nimbusExperiment.endDate + ? formattedEndDate + : proposedEndDate, + false, // default for isCompleted in constructor + ); + + // XXX getBranchInfo is actually going to return a previewLink, which + // makes this test kind of brittle. We could refactor this to no longer + // use deepEqual and check for the existence of object properties instead. + expect(branchInfo).toEqual({ + product: "Desktop", + ctrDashboardLink: dashboardLink, + id: "feature_value_id:treatment-a", + isBranch: true, + nimbusExperiment: AW_RECIPE, + slug: branch.slug, + surface: "About:Welcome Page (1st screen)", + template: "aboutwelcome", + previewLink: + "about:messagepreview?json=ewAiAGkAZAAiADoAIgBhAGIAbwB1AHQAdwBlAGwAYwBvAG0AZQAtAHQAZQBzAHQALQByAGUAYwBpAHAAZQAiACwAIgB0AGUAbQBwAGwAYQB0AGUAIgA6ACIAcwBwAG8AdABsAGkAZwBoAHQAIgAsACIAdABhAHIAZwBlAHQAaQBuAGcAIgA6AHQAcgB1AGUALAAiAGMAbwBuAHQAZQBuAHQAIgA6AHsAIgBiAGEAYwBrAGQAcgBvAHAAIgA6ACIAdABlAHMAdAAtAGIAYQBjAGsAZAByAG8AcAAiACwAIgBpAGQAIgA6ACIAZgBlAGEAdAB1AHIAZQBfAHYAYQBsAHUAZQBfAGkAZAA6AHQAcgBlAGEAdABtAGUAbgB0AC0AYQAiACwAIgBzAGMAcgBlAGUAbgBzACIAOgBbAHsAIgBpAGQAIgA6ACIAVABFAFMAVABfAFMAQwBSAEUARQBOAF8ASQBEAF8AQQBfADAAIgB9AF0ALAAiAG0AbwBkAGEAbAAiADoAIgB0AGEAYgAiAH0AfQA%3D", + screenshots: ["screenshotURI"], + description: "treatment-a description", + hasMicrosurvey: false, + }); + }); - // XXX getBranchInfo is actually going to return a previewLink, which - // makes this test kind of brittle. We could refactor this to no longer - // use deepEqual and check for the existence of object properties instead. - expect(branchInfo).toEqual({ - product: "Desktop", - ctrDashboardLink: dashboardLink, - id: "feature_value_id:treatment-a", - isBranch: true, - nimbusExperiment: AW_RECIPE, - slug: branch.slug, - surface: "About:Welcome Page (1st screen)", - template: "aboutwelcome", - previewLink: - "about:messagepreview?json=ewAiAGkAZAAiADoAIgBhAGIAbwB1AHQAdwBlAGwAYwBvAG0AZQAtAHQAZQBzAHQALQByAGUAYwBpAHAAZQAiACwAIgB0AGUAbQBwAGwAYQB0AGUAIgA6ACIAcwBwAG8AdABsAGkAZwBoAHQAIgAsACIAdABhAHIAZwBlAHQAaQBuAGcAIgA6AHQAcgB1AGUALAAiAGMAbwBuAHQAZQBuAHQAIgA6AHsAIgBiAGEAYwBrAGQAcgBvAHAAIgA6ACIAdABlAHMAdAAtAGIAYQBjAGsAZAByAG8AcAAiACwAIgBpAGQAIgA6ACIAZgBlAGEAdAB1AHIAZQBfAHYAYQBsAHUAZQBfAGkAZAA6AHQAcgBlAGEAdABtAGUAbgB0AC0AYQAiACwAIgBzAGMAcgBlAGUAbgBzACIAOgBbAHsAIgBpAGQAIgA6ACIAVABFAFMAVABfAFMAQwBSAEUARQBOAF8ASQBEAF8AQQBfADAAIgB9AF0ALAAiAG0AbwBkAGEAbAAiADoAIgB0AGEAYgAiAH0AfQA%3D", - screenshots: ["screenshotURI"], - description: "treatment-a description", - hasMicrosurvey: false, + it("returns a BranchInfo that uses the message id if no screens exist", () => { + // https://github.com/jsdom/jsdom/issues/3363 is why we're using + // a JSON hack rather than structuredClone + const AW_RECIPE_NO_SCREENS = JSON.parse(JSON.stringify(AW_RECIPE)); + AW_RECIPE_NO_SCREENS.branches[1].features[0].value = { + id: "feature_value_id:treatment-a", + backdrop: "XXX-no-msg-test-hack-deleteme-see-getBranchInfo", + }; + + const nimbusRecipe = new NimbusRecipe(AW_RECIPE_NO_SCREENS); + const branch = AW_RECIPE_NO_SCREENS.branches[1]; + + const branchInfo = nimbusRecipe.getBranchInfo(branch); + const proposedEndDate = getExperimentLookerDashboardDate( + branchInfo.nimbusExperiment.startDate, + branchInfo.nimbusExperiment.proposedDuration, + ); + const formattedEndDate = formatDate( + branchInfo.nimbusExperiment.endDate as string, + 1, + ); + + const dashboardLink = getDashboard( + branchInfo.template as string, + branchInfo.id, + undefined, + branchInfo.nimbusExperiment.slug, + branch.slug, + branchInfo.nimbusExperiment.startDate, + branchInfo.nimbusExperiment.endDate + ? formattedEndDate + : proposedEndDate, + false, // default for isCompleted in constructor + ); + + expect(branchInfo).toEqual({ + product: "Desktop", + ctrDashboardLink: dashboardLink, + id: "feature_value_id:treatment-a", + isBranch: true, + nimbusExperiment: AW_RECIPE_NO_SCREENS, + slug: branch.slug, + surface: "About:Welcome Page (1st screen)", + template: "aboutwelcome", + screenshots: ["screenshotURI"], + description: "treatment-a description", + hasMicrosurvey: false, + }); }); }); - it("returns a BranchInfo that uses the message id if no screens exist", () => { - // https://github.com/jsdom/jsdom/issues/3363 is why we're using - // a JSON hack rather than structuredClone - const AW_RECIPE_NO_SCREENS = JSON.parse(JSON.stringify(AW_RECIPE)); - AW_RECIPE_NO_SCREENS.branches[1].features[0].value = { - id: "feature_value_id:treatment-a", - backdrop: "XXX-no-msg-test-hack-deleteme-see-getBranchInfo", - }; - - const nimbusRecipe = new NimbusRecipe(AW_RECIPE_NO_SCREENS); - const branch = AW_RECIPE_NO_SCREENS.branches[1]; - - const branchInfo = nimbusRecipe.getBranchInfo(branch); - const proposedEndDate = getExperimentLookerDashboardDate( - branchInfo.nimbusExperiment.startDate, - branchInfo.nimbusExperiment.proposedDuration, - ); - const formattedEndDate = formatDate( - branchInfo.nimbusExperiment.endDate as string, - 1, - ); - - const dashboardLink = getDashboard( - branchInfo.template as string, - branchInfo.id, - undefined, - branchInfo.nimbusExperiment.slug, - branch.slug, - branchInfo.nimbusExperiment.startDate, - branchInfo.nimbusExperiment.endDate - ? formattedEndDate - : proposedEndDate, - false, // default for isCompleted in constructor - ); + describe("getAndroidBranchInfo", () => { + it("returns a BranchInfo object for a `messaging` featureId", () => { + const nimbusRecipe = new NimbusRecipe(ANDROID_RECIPE); + const branch = ANDROID_RECIPE.branches[0]; + + const branchInfo = nimbusRecipe.getBranchInfo(branch); + const branchInfoId = "android-msg-test-recipe-en-us"; + const proposedEndDate = getExperimentLookerDashboardDate( + branchInfo.nimbusExperiment.startDate, + branchInfo.nimbusExperiment.proposedDuration, + ); + const formattedEndDate = formatDate( + branchInfo.nimbusExperiment.endDate as string, + 1, + ); + const dashboardLink = getAndroidDashboard( + branchInfo.template as string, + branchInfoId, + undefined, + branchInfo.nimbusExperiment.slug, + branch.slug, + branchInfo.nimbusExperiment.startDate, + branchInfo.nimbusExperiment.endDate + ? formattedEndDate + : proposedEndDate, + false, + ); + + expect(branchInfo).toEqual({ + product: "Android", + id: branchInfoId, + isBranch: true, + nimbusExperiment: ANDROID_RECIPE, + slug: branch.slug, + surface: "Survey", + template: "survey", + screenshots: ["screenshotURI"], + description: "control description", + ctrDashboardLink: dashboardLink, + }); + }); - expect(branchInfo).toEqual({ - product: "Desktop", - ctrDashboardLink: dashboardLink, - id: "feature_value_id:treatment-a", - isBranch: true, - nimbusExperiment: AW_RECIPE_NO_SCREENS, - slug: branch.slug, - surface: "About:Welcome Page (1st screen)", - template: "aboutwelcome", - screenshots: ["screenshotURI"], - description: "treatment-a description", - hasMicrosurvey: false, + it("returns a default BranchInfo object for `juno-onboarding` featureIds", () => { + // https://github.com/jsdom/jsdom/issues/3363 is why we're using + // a JSON hack rather than structuredClone + const ANDROID_RECIPE_ONBOARDING = JSON.parse( + JSON.stringify(ANDROID_RECIPE), + ); + ANDROID_RECIPE_ONBOARDING.branches[0].features[0].featureId = + "juno-onboarding"; + const nimbusRecipe = new NimbusRecipe(ANDROID_RECIPE_ONBOARDING); + const branch = ANDROID_RECIPE_ONBOARDING.branches[0]; + + const branchInfo = nimbusRecipe.getBranchInfo(branch); + + expect(branchInfo).toEqual({ + product: "Android", + id: branch.slug, + isBranch: true, + nimbusExperiment: ANDROID_RECIPE_ONBOARDING, + slug: branch.slug, + screenshots: ["screenshotURI"], + description: "control description", + }); }); }); }); diff --git a/app/columns.tsx b/app/columns.tsx index 744ce4a5..52a5c368 100644 --- a/app/columns.tsx +++ b/app/columns.tsx @@ -82,8 +82,8 @@ type NimbusExperiment = typeof nimbusExperimentV7Schema.properties; export type RecipeInfo = { product: "Desktop" | "Android"; id: string; - template?: string; - surface?: string; + template?: string; // XXX template JSON name + surface?: string; // XXX template display name segment?: string; ctrPercent?: number; ctrPercentChange?: number; diff --git a/lib/experimentUtils.ts b/lib/experimentUtils.ts index 9bb2fa71..2f9f7dcf 100644 --- a/lib/experimentUtils.ts +++ b/lib/experimentUtils.ts @@ -1,8 +1,9 @@ /** - * These are the Nimbus feature IDs that correspond to messaging experiments. - * Other Nimbus features contain specific variables whose keys are enumerated in - * FeatureManifest.yaml. Conversely, messaging experiment features contain - * actual messages, with the usual message keys like `template` and `targeting`. + * This is a cross-platform list of Nimbus feature IDs that correspond to + * messaging experiments in both desktop and android. Other Nimbus features + * contain specific variables whose keys are enumerated in FeatureManifest.yaml. + * Conversely, messaging experiment features contain actual messages, with the + * usual message keys like `template` and `targeting`. * @see FeatureManifest.yaml * * Copied from @see https://searchfox.org/mozilla-central/source/browser/components/newtab/lib/MessagingExperimentConstants.sys.mjs @@ -10,6 +11,7 @@ * Should be manually update when that file changes. */ export const MESSAGING_EXPERIMENTS_DEFAULT_FEATURES: string[] = [ + // Desktop features "aboutwelcome", "backgroundTaskMessage", // XXX need to backport this to tree "cfr", @@ -31,6 +33,13 @@ export const MESSAGING_EXPERIMENTS_DEFAULT_FEATURES: string[] = [ "spotlight", "testFeature", "whatsNewPage", + + // Android features + "cfr", + "encourage-search-cfr", + "messaging", + "juno-onboarding", + "set-to-default-prompt", ]; /** diff --git a/lib/messageUtils.ts b/lib/messageUtils.ts index 11d31eaf..4fe49802 100644 --- a/lib/messageUtils.ts +++ b/lib/messageUtils.ts @@ -73,6 +73,10 @@ export function getSurfaceDataForTemplate(template: string): SurfaceData { tagColor: "bg-pink-400", docs: "https://experimenter.info/messaging/desktop-messaging-surfaces/#multistage-spotlight", }, + survey: { + surface: "Survey", + tagColor: "bg-cyan-400", + }, update_action: { surface: "Moments Page", tagColor: "bg-rose-400", @@ -113,6 +117,59 @@ export function _isAboutWelcomeTemplate(template: string): boolean { return aboutWelcomeSurfaces.includes(template); } +export function getAndroidDashboard( + template: string, + msgIdPrefix: string, + channel?: string, + experiment?: string, + branchSlug?: string, + startDate?: string | null, + endDate?: string | null, + isCompleted?: boolean, +): string | undefined { + // The isCompleted value can be useful for messages that used to be in remote + // settings or old versions of Firefox. + const submissionDate = getLookerSubmissionTimestampDateFilter( + startDate, + endDate, + isCompleted, + ); + + // XXX consider using a similar function like getDashboardIdForTemplate for + // android dashboards to get dashboardId + const dashboardId = 2191; // messages/push notification + let baseUrl = `https://mozilla.cloud.looker.com/dashboards/${dashboardId}`; + let paramObj; + + paramObj = { + "Submission Date": submissionDate, + "Normalized Channel": channel ? channel : "", + "Normalized OS": "", + "Client Info App Display Version": "", + "Normalized Country Code": "", + "Experiment Slug": experiment ? experiment : "", // XXX + "Experiment Branch": branchSlug ? branchSlug : "", + // XXX assumes last part of message id is something like + // "-en-us" and chops that off, since we want to know about + // all the messages in the experiment. Will break + // (in "no results" way) on experiment with messages not configured + // like that. + Value: msgIdPrefix.slice(0, -5) + "%", // XXX + }; + + // XXX we really handle all messaging surfaces, at least in theory + if (template !== "survey") return undefined; + + if (paramObj) { + const params = new URLSearchParams(Object.entries(paramObj)); + let url = new URL(baseUrl); + url.search = params.toString(); + return url.toString(); + } + + return undefined; +} + export function getDashboard( template: string, msgId: string, diff --git a/lib/nimbusRecipe.ts b/lib/nimbusRecipe.ts index 0d0b34c1..57764493 100644 --- a/lib/nimbusRecipe.ts +++ b/lib/nimbusRecipe.ts @@ -1,5 +1,6 @@ import { BranchInfo, RecipeInfo, RecipeOrBranchInfo } from "../app/columns.jsx"; import { + getAndroidDashboard, getDashboard, getSurfaceDataForTemplate, getPreviewLink, @@ -78,10 +79,109 @@ export class NimbusRecipe implements NimbusRecipeType { this._isCompleted = isCompleted; } + getAndroidBranchInfo(branch: any): BranchInfo { + let branchInfo: BranchInfo = { + product: "Android", + id: branch.slug, + isBranch: true, + // The raw experiment data can be automatically serialized to + // the client by NextJS (but classes can't), and any + // needed NimbusRecipe class rewrapping can be done there. + nimbusExperiment: this._rawRecipe, + slug: branch.slug, + screenshots: branch.screenshots, + description: branch.description, + }; + + // XXX need to handle multi branches + const feature = branch.features[0]; + + switch (feature.featureId) { + case "messaging": + // console.log("in messaging feature, feature = ", feature); + + // console.log("feature.value = ", feature.value); + if (Object.keys(feature.value).length === 0) { + console.warn( + "empty feature value, returning error, branch.slug = ", + branch.slug, + ); + return branchInfo; + } + + const message0: any = Object.values(feature.value.messages)[0]; + const message0Id: string = Object.keys(feature.value.messages)[0]; + branchInfo.id = message0Id; + + // console.log("message0 = ", message0); + + const surface = message0.surface; + // XXX need to rename template & surface somehow + branchInfo.template = surface; + branchInfo.surface = getSurfaceDataForTemplate(surface).surface; + + switch (surface) { + case "messages": + // XXX I don' think this a real case + console.log("in messages surface case"); + break; + + case "survey": + break; + + default: + console.warn("unhandled message surface: ", branchInfo.surface); + } + break; + + case "juno-onboarding": + console.warn(`we don't fully support juno-onboarding messages yet`); + break; + + default: + console.warn("default hit"); + console.warn("branch.slug = ", branch.slug); + console.warn("We don't support feature = ", feature); + } + + const proposedEndDate = getExperimentLookerDashboardDate( + branchInfo.nimbusExperiment.startDate, + branchInfo.nimbusExperiment.proposedDuration, + ); + let formattedEndDate; + if (branchInfo.nimbusExperiment.endDate) { + formattedEndDate = formatDate(branchInfo.nimbusExperiment.endDate, 1); + } + + branchInfo.ctrDashboardLink = getAndroidDashboard( + branchInfo.template as string, + branchInfo.id, + undefined, + branchInfo.nimbusExperiment.slug, + branch.slug, + branchInfo.nimbusExperiment.startDate, + branchInfo.nimbusExperiment.endDate ? formattedEndDate : proposedEndDate, + this._isCompleted, + ); + + console.log("Android Dashboard: ", branchInfo.ctrDashboardLink); + + return branchInfo; + } + /** * @returns an array of BranchInfo objects, one per branch in this recipe */ getBranchInfo(branch: any): BranchInfo { + switch (this._rawRecipe.appName) { + case "fenix": + return this.getAndroidBranchInfo(branch); + default: + return this.getDesktopBranchInfo(branch); + } + } + + getDesktopBranchInfo(branch: any): BranchInfo { let branchInfo: BranchInfo = { product: "Desktop", id: branch.slug, @@ -105,7 +205,7 @@ export class NimbusRecipe implements NimbusRecipeType { // a surface to it. let template; if (feature.featureId === "aboutwelcome" && branch.slug != "control") { - // XXXdmose nasty hack to prevent what I'm calling + // XXX dmose nasty hack to prevent what I'm calling // "non-messaging-aboutwelcome" features from breaking // Skylight completely. Need to talk to Jason and Meg to // understand more details and figure out what to do here... @@ -246,6 +346,7 @@ export class NimbusRecipe implements NimbusRecipeType { return branchInfo; default: + // console.log("Hit default case, template = ", template); if (!feature.value?.messages) { // console.log("v.messages is null"); // console.log(", feature.value = ", feature.value);