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

mozilla / fx-private-relay / 8aed337d-6ae1-4a90-8596-746db75f1871

19 Mar 2025 03:25PM CUT coverage: 85.024% (-0.1%) from 85.137%
8aed337d-6ae1-4a90-8596-746db75f1871

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

2442 of 3580 branches covered (68.21%)

Branch coverage included in aggregate %.

115 of 145 new or added lines in 7 files covered. (79.31%)

13 existing lines in 2 files now uncovered.

17133 of 19443 relevant lines covered (88.12%)

9.84 hits per line

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

73.21
/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✔
NEW
40
  } else if (plan.url) {
×
NEW
41
    return plan.url;
×
42
  }
NEW
UNCOV
43
  return "";
×
44
};
45

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

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

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

109
export function isPeriodicalPremiumAvailableInCountry(
418✔
110
  runtimeData: RuntimeData | undefined,
111
): runtimeData is RuntimeDataWithPeriodicalPremiumAvailable {
112
  return runtimeData?.PERIODICAL_PREMIUM_PLANS?.available_in_country === true;
418✔
113
}
114

115
export function isPhonesAvailableInCountry(
551✔
116
  runtimeData: RuntimeData | undefined,
117
): runtimeData is RuntimeDataWithPhonesAvailable {
118
  return runtimeData?.PHONE_PLANS?.available_in_country === true;
551✔
119
}
120

121
export function isBundleAvailableInCountry(
316✔
122
  runtimeData: RuntimeData | undefined,
123
): runtimeData is RuntimeDataWithBundleAvailable {
124
  return runtimeData?.BUNDLE_PLANS?.available_in_country === true;
316✔
125
}
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