• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / fx-private-relay / 74d01625-9661-4180-904e-0bd85eb6c11f

29 May 2025 12:03PM CUT coverage: 85.583% (+0.2%) from 85.353%
74d01625-9661-4180-904e-0bd85eb6c11f

push

circleci

web-flow
Merge pull request #5573 from mozilla/MPP-4155-new-megabundle-banner

MPP-4155 New Megabundle Banner Landing Page

2490 of 3629 branches covered (68.61%)

Branch coverage included in aggregate %.

60 of 62 new or added lines in 8 files covered. (96.77%)

19 existing lines in 6 files now uncovered.

17598 of 19843 relevant lines covered (88.69%)

9.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.3
/frontend/src/functions/getPlan.ts
1
import { ReactLocalization } from "@fluent/react";
2
import { PlanData, ProductData, RuntimeData } from "../hooks/api/runtimeData";
3
import { getLocale } from "./getLocale";
12✔
4

5
const getPlan = <P extends Partial<PlanData>>(
12✔
6
  productData: ProductData<P>,
7
  billingPeriod: keyof P,
8
) => {
9
  const languageCode = navigator.language.split("-")[0].toLowerCase();
391✔
10
  const countryPlans =
11
    productData.plan_country_lang_mapping[productData.country_code];
391✔
12
  const plan =
13
    countryPlans[languageCode] ?? countryPlans[Object.keys(countryPlans)[0]];
391✔
14

15
  return plan[billingPeriod];
391✔
16
};
17

18
export const getPeriodicalPremiumPrice = (
53✔
19
  runtimeData: RuntimeDataWithPeriodicalPremiumAvailable,
20
  billingPeriod: keyof PlanData,
21
  l10n: ReactLocalization,
22
) => {
23
  const plan = getPlan(runtimeData.PERIODICAL_PREMIUM_PLANS, billingPeriod);
53✔
24
  const formatter = new Intl.NumberFormat(getLocale(l10n), {
53✔
25
    style: "currency",
26
    currency: plan.currency,
27
  });
28
  return formatter.format(plan.price);
53✔
29
};
30
/**
31
 * Given {@link RuntimeDataWithPeriodicalPremiumAvailable}, returns the URL the user should visit to purchase Premium.
32
 */
33
export const getPeriodicalPremiumSubscribeLink = (
152✔
34
  runtimeData: RuntimeDataWithPeriodicalPremiumAvailable,
35
  billingPeriod: keyof PlanData,
36
) => {
37
  const plan = getPlan(runtimeData.PERIODICAL_PREMIUM_PLANS, billingPeriod);
152✔
38
  if (plan.id) {
152✔
39
    return `${runtimeData.FXA_ORIGIN}/subscriptions/products/${runtimeData.PERIODICAL_PREMIUM_PRODUCT_ID}?plan=${plan.id}`;
150✔
40
  }
41
  return plan.url ?? "";
2!
42
};
43

44
export const getPhonesPrice = (
41✔
45
  runtimeData: RuntimeDataWithPhonesAvailable,
46
  billingPeriod: keyof PlanData,
47
  l10n: ReactLocalization,
48
) => {
49
  const plan = getPlan(runtimeData.PHONE_PLANS, billingPeriod);
41✔
50
  const formatter = new Intl.NumberFormat(getLocale(l10n), {
41✔
51
    style: "currency",
52
    currency: plan.currency,
53
  });
54
  return formatter.format(plan.price);
41✔
55
};
56
export const getPhoneSubscribeLink = (
44✔
57
  runtimeData: RuntimeDataWithPhonesAvailable,
58
  billingPeriod: keyof PlanData,
59
) => {
60
  const plan = getPlan(runtimeData.PHONE_PLANS, billingPeriod);
44✔
61
  if (plan.id) {
44✔
62
    return `${runtimeData.FXA_ORIGIN}/subscriptions/products/${runtimeData.PHONE_PRODUCT_ID}?plan=${plan.id}`;
42✔
63
  }
64
  return plan.url ?? "";
2!
65
};
66

67
export const getBundlePrice = (
25✔
68
  runtimeData: RuntimeDataWithBundleAvailable,
69
  l10n: ReactLocalization,
70
) => {
71
  const plan = getPlan(runtimeData.BUNDLE_PLANS, "yearly");
25✔
72
  const formatter = new Intl.NumberFormat(getLocale(l10n), {
25✔
73
    style: "currency",
74
    currency: plan.currency,
75
  });
76
  return formatter.format(plan.price);
25✔
77
};
78
export const getBundleSubscribeLink = (
18✔
79
  runtimeData: RuntimeDataWithBundleAvailable,
80
) => {
81
  const plan = getPlan(runtimeData.BUNDLE_PLANS, "yearly");
18✔
82
  if (plan.id) {
18✔
83
    return `${runtimeData.FXA_ORIGIN}/subscriptions/products/${runtimeData.BUNDLE_PRODUCT_ID}?plan=${plan.id}`;
17✔
84
  }
85
  return plan.url ?? "";
1!
86
};
87

88
export const getMegabundlePrice = (
19✔
89
  runtimeData: RuntimeDataWithBundleAvailable,
90
  l10n: ReactLocalization,
91
) => {
92
  const plan = getPlan(runtimeData.MEGABUNDLE_PLANS, "yearly");
19✔
93
  const formatter = new Intl.NumberFormat(getLocale(l10n), {
19✔
94
    style: "currency",
95
    currency: plan.currency,
96
  });
97
  return formatter.format(plan.price);
19✔
98
};
99

100
export const getMegabundleYearlyPrice = (
19✔
101
  runtimeData: RuntimeDataWithBundleAvailable,
102
  l10n: ReactLocalization,
103
) => {
104
  const plan = getPlan(runtimeData.MEGABUNDLE_PLANS, "yearly");
19✔
105
  const total = plan.price * 12;
19✔
106
  const formatter = new Intl.NumberFormat(getLocale(l10n), {
19✔
107
    style: "currency",
108
    currency: plan.currency,
109
  });
110
  return formatter.format(total);
19✔
111
};
112

113
export const getMegabundleSubscribeLink = (
20✔
114
  runtimeData: RuntimeDataWithBundleAvailable,
115
) => {
116
  const plan = getPlan(runtimeData.MEGABUNDLE_PLANS, "yearly");
20✔
117
  if (plan.id) {
20✔
118
    return `${runtimeData.FXA_ORIGIN}/subscriptions/products/${runtimeData.MEGABUNDLE_PRODUCT_ID}?plan=${plan.id}`;
20✔
119
  }
NEW
120
  return plan.url ?? "";
×
121
};
122

123
/**
124
 * Helper type that is used to create helper types like
125
 * {@see RuntimeDataWithBundleAvailable}, which in turn can help make sure
126
 * functions like {@see isBundleAvailableInCountry} have been called.
127
 */
128
type RuntimeDataWithPlanAvailable<Plan extends keyof RuntimeData> =
129
  RuntimeData &
130
    Record<Plan, RuntimeData[Plan] & { available_in_country: true }>;
131
export type RuntimeDataWithPeriodicalPremiumAvailable =
132
  RuntimeDataWithPlanAvailable<"PERIODICAL_PREMIUM_PLANS">;
133
export type RuntimeDataWithPhonesAvailable =
134
  RuntimeDataWithPlanAvailable<"PHONE_PLANS">;
135
export type RuntimeDataWithBundleAvailable =
136
  RuntimeDataWithPlanAvailable<"BUNDLE_PLANS">;
137
export type RuntimeDataWithMegabundleAvailable =
138
  RuntimeDataWithPlanAvailable<"MEGABUNDLE_PLANS">;
139

140
export function isPeriodicalPremiumAvailableInCountry(
438✔
141
  runtimeData: RuntimeData | undefined,
142
): runtimeData is RuntimeDataWithPeriodicalPremiumAvailable {
143
  return runtimeData?.PERIODICAL_PREMIUM_PLANS?.available_in_country === true;
438✔
144
}
145

146
export function isPhonesAvailableInCountry(
518✔
147
  runtimeData: RuntimeData | undefined,
148
): runtimeData is RuntimeDataWithPhonesAvailable {
149
  return runtimeData?.PHONE_PLANS?.available_in_country === true;
518✔
150
}
151

152
export function isBundleAvailableInCountry(
285✔
153
  runtimeData: RuntimeData | undefined,
154
): runtimeData is RuntimeDataWithBundleAvailable {
155
  return runtimeData?.BUNDLE_PLANS?.available_in_country === true;
285✔
156
}
157

158
export function isMegabundleAvailableInCountry(
55✔
159
  runtimeData: RuntimeData | undefined,
160
): runtimeData is RuntimeDataWithBundleAvailable {
161
  return runtimeData?.MEGABUNDLE_PLANS?.available_in_country === true;
55✔
162
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc