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

mozilla / fx-private-relay / c7fda62d-7c27-442c-82d6-219fe9650b4c

20 Mar 2025 02:00PM UTC coverage: 85.133% (-0.004%) from 85.137%
c7fda62d-7c27-442c-82d6-219fe9650b4c

Pull #5456

circleci

groovecoder
fix MPP-4020: update getPlan.get__SubscribeLink functions to use SP3 url when available
Pull Request #5456: for MPP-4020: add sp3_plans to backend and API

2447 of 3584 branches covered (68.28%)

Branch coverage included in aggregate %.

156 of 163 new or added lines in 9 files covered. (95.71%)

2 existing lines in 1 file now uncovered.

17171 of 19460 relevant lines covered (88.24%)

9.83 hits per line

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

82.0
/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";
10✔
4

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

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

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

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

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

88
/**
89
 * Helper type that is used to create helper types like
90
 * {@see RuntimeDataWithBundleAvailable}, which in turn can help make sure
91
 * functions like {@see isBundleAvailableInCountry} have been called.
92
 */
93
type RuntimeDataWithPlanAvailable<Plan extends keyof RuntimeData> =
94
  RuntimeData &
95
    Record<Plan, RuntimeData[Plan] & { available_in_country: true }>;
96
export type RuntimeDataWithPeriodicalPremiumAvailable =
97
  RuntimeDataWithPlanAvailable<"PERIODICAL_PREMIUM_PLANS">;
98
export type RuntimeDataWithPhonesAvailable =
99
  RuntimeDataWithPlanAvailable<"PHONE_PLANS">;
100
export type RuntimeDataWithBundleAvailable =
101
  RuntimeDataWithPlanAvailable<"BUNDLE_PLANS">;
102

103
export function isPeriodicalPremiumAvailableInCountry(
418✔
104
  runtimeData: RuntimeData | undefined,
105
): runtimeData is RuntimeDataWithPeriodicalPremiumAvailable {
106
  return runtimeData?.PERIODICAL_PREMIUM_PLANS?.available_in_country === true;
418✔
107
}
108

109
export function isPhonesAvailableInCountry(
551✔
110
  runtimeData: RuntimeData | undefined,
111
): runtimeData is RuntimeDataWithPhonesAvailable {
112
  return runtimeData?.PHONE_PLANS?.available_in_country === true;
551✔
113
}
114

115
export function isBundleAvailableInCountry(
316✔
116
  runtimeData: RuntimeData | undefined,
117
): runtimeData is RuntimeDataWithBundleAvailable {
118
  return runtimeData?.BUNDLE_PLANS?.available_in_country === true;
316✔
119
}
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