Skip to content

Commit 1b8b7ba

Browse files
authored
Merge pull request #19522 from mozilla/FXA-12459
cleanup(settings): Remove MonitorPlus promo
2 parents 4a5d267 + 3e7ccd9 commit 1b8b7ba

15 files changed

Lines changed: 62 additions & 272 deletions

File tree

packages/fxa-auth-server/config/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ const convictConf = convict({
185185
rules: {
186186
doc: 'Mapping of features to country codes that are allowed to see the feature',
187187
format: Object,
188-
default: {}, // ex. { "MONITORPLUSPROMO": ["US"] }
188+
default: {}, // ex. { "FEATURE_NAME": ["US"] }
189189
env: 'GEO_ELIGIBILITY_RULES',
190190
},
191191
},

packages/fxa-settings/src/components/App/index.test.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,6 @@ jest.mock('../../lib/glean', () => ({
9797
},
9898
}));
9999

100-
const mockUseGeoEligibilityCheck = jest
101-
.fn()
102-
.mockReturnValue({ eligible: false });
103-
jest.mock('../../lib/hooks/useGeoEligibilityCheck', () => ({
104-
useGeoEligibilityCheck: () => mockUseGeoEligibilityCheck(),
105-
}));
106-
107100
const mockMetricsQueryAccountAmplitude = {
108101
recoveryKey: true,
109102
totpActive: true,
@@ -548,7 +541,6 @@ describe('SettingsRoutes', () => {
548541
...mockAppContext({
549542
account: {
550543
...MOCK_ACCOUNT,
551-
getMonitorPlusPromoEligibility: () => Promise.resolve(false),
552544
} as unknown as Account,
553545
}),
554546
...createAppContext(),
@@ -583,7 +575,6 @@ describe('SettingsRoutes', () => {
583575
value={mockAppContext({
584576
account: {
585577
...MOCK_ACCOUNT,
586-
getMonitorPlusPromoEligibility: () => Promise.resolve(false),
587578
} as unknown as Account,
588579
})}
589580
>

packages/fxa-settings/src/components/Settings/PageSettings/index.stories.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import React from 'react';
66
import { Account } from '../../../models/Account';
7-
import AuthClient from 'fxa-auth-client/browser';
87

98
import { PageSettings } from '.';
109
import { Config } from '../../../lib/config';
@@ -29,10 +28,6 @@ export default {
2928
decorators: [withLocalization],
3029
} as Meta;
3130

32-
const mockAuthClient = {
33-
geoEligibilityCheck: async () => ({ eligible: true }),
34-
} as Partial<AuthClient> as AuthClient;
35-
3631
const storyWithContext = (
3732
account: Partial<Account>,
3833
storyName?: string,
@@ -43,12 +38,10 @@ const storyWithContext = (
4338
? {
4439
account: account as Account,
4540
config: config,
46-
authClient: mockAuthClient,
4741
session: session,
4842
}
4943
: {
5044
account: account as Account,
51-
authClient: mockAuthClient,
5245
session: session,
5346
};
5447

packages/fxa-settings/src/components/Settings/PageSettings/index.test.tsx

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,8 @@ jest.mock('../../../lib/glean', () => ({
5050
},
5151
}));
5252

53-
const mockUseGeoEligibilityCheck = jest
54-
.fn()
55-
.mockReturnValue({ eligible: false });
56-
jest.mock('../../../lib/hooks/useGeoEligibilityCheck', () => ({
57-
useGeoEligibilityCheck: () => mockUseGeoEligibilityCheck(),
58-
}));
59-
6053
const mockGetProductPromoData = jest.fn().mockReturnValue({
6154
hidePromo: false,
62-
showMonitorPlusPromo: false,
6355
gleanEvent: { event: { reason: 'default' } },
6456
});
6557
jest.mock('../ProductPromo', () => ({
@@ -162,37 +154,14 @@ describe('PageSettings', () => {
162154
});
163155

164156
describe('product promo event', () => {
165-
it('user does not have Monitor', async () => {
166-
renderWithRouter(
167-
<AppContext.Provider
168-
value={mockAppContext({ account: coldStartAccount })}
169-
>
170-
<PageSettings />
171-
</AppContext.Provider>
172-
);
173-
await waitFor(() =>
174-
expect(
175-
GleanMetrics.accountPref.promoMonitorView
176-
).toHaveBeenCalledTimes(1)
177-
);
178-
expect(GleanMetrics.accountPref.promoMonitorView).toHaveBeenCalledWith({
179-
event: { reason: 'default' },
180-
});
181-
});
182-
183-
it('user has Monitor and is eligible for special promo', async () => {
184-
mockUseGeoEligibilityCheck.mockReturnValue({
185-
eligible: true,
186-
loading: false,
187-
});
157+
it('user does not have Monitor, promo is shown and glean metric is called', async () => {
188158
mockGetProductPromoData.mockReturnValue({
189159
hidePromo: false,
190-
showMonitorPlusPromo: true,
191-
gleanEvent: { event: { reason: 'special' } },
160+
gleanEvent: { event: { reason: 'default' } },
192161
});
193162
renderWithRouter(
194163
<AppContext.Provider
195-
value={mockAppContext({ account: completelyFilledOutAccount })}
164+
value={mockAppContext({ account: coldStartAccount })}
196165
>
197166
<PageSettings />
198167
</AppContext.Provider>
@@ -203,15 +172,11 @@ describe('PageSettings', () => {
203172
).toHaveBeenCalledTimes(1)
204173
);
205174
expect(GleanMetrics.accountPref.promoMonitorView).toHaveBeenCalledWith({
206-
event: { reason: 'special' },
175+
event: { reason: 'default' },
207176
});
208177
});
209178

210-
it('user has all products and subscriptions', async () => {
211-
mockUseGeoEligibilityCheck.mockReturnValue({
212-
eligible: true,
213-
loading: false,
214-
});
179+
it('user has Monitor, promo is not shown and glean metric is not called', async () => {
215180
mockGetProductPromoData.mockReturnValue({
216181
hidePromo: true,
217182
});

packages/fxa-settings/src/components/Settings/PageSettings/index.tsx

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import {
3030
isRecoveryKeyPromoDismissed,
3131
isRecoveryPhonePromoDismissed,
3232
} from '../../../lib/promo-dismissal';
33-
import { useGeoEligibilityCheck } from '../../../lib/hooks/useGeoEligibilityCheck';
3433

3534
export const PageSettings = ({
3635
integration,
@@ -47,11 +46,6 @@ export const PageSettings = ({
4746
recoveryPhone,
4847
} = account;
4948

50-
const {
51-
eligible: monitorPlusPromoEligible,
52-
loading: monitorPlusEligibilityLoading,
53-
} = useGeoEligibilityCheck('monitorpluspromo');
54-
5549
const ftlMsgResolver = useFtlMsgResolver();
5650
const alertBar = useAlertBar();
5751

@@ -104,24 +98,15 @@ export const PageSettings = ({
10498

10599
useEffect(() => {
106100
(async () => {
107-
if (monitorPromo !== null || monitorPlusEligibilityLoading) {
101+
if (monitorPromo !== null) {
108102
return;
109103
}
110104

111-
const promoData = getProductPromoData(
112-
account.attachedClients,
113-
account.subscriptions,
114-
monitorPlusPromoEligible
115-
);
105+
const promoData = getProductPromoData(account.attachedClients);
116106

117107
setMonitorPromo(promoData);
118108
})();
119-
}, [
120-
account,
121-
monitorPlusPromoEligible,
122-
monitorPlusEligibilityLoading,
123-
monitorPromo,
124-
]);
109+
}, [account, monitorPromo]);
125110

126111
// -- Relying party promotion checks --
127112
useEffect(() => {

packages/fxa-settings/src/components/Settings/ProductPromo/en.ftl

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,5 @@
33
product-promo-monitor =
44
.alt = { -product-mozilla-monitor }
55
product-promo-monitor-description-v2 = Find where your private info is exposed and take control
6-
# this message will only be shown to users eligible for a special promotion, based on their location (initially USA only)
7-
# $price - formatted for user locale, in the target market's currency (for launch, always USD)
8-
# /mo is 'per month'
9-
product-promo-monitor-special-promo-description = For { $price }/mo, save on { -product-mozilla-vpn-short }, { -product-mozilla-monitor-short }’s data-broker protection, and { -product-firefox-relay-short }’s unlimited email masks.
106
# Links out to the Monitor site
117
product-promo-monitor-cta = Get free scan
12-
# Links out to the Monitor pricing site
13-
product-promo-monitor-special-promo-cta = Get year-round protection

packages/fxa-settings/src/components/Settings/ProductPromo/index.stories.tsx

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ export default {
1414
decorators: [withLocalization, withLocation('/settings')],
1515
} as Meta<typeof ProductPromo>;
1616

17-
/**
18-
* Helper to generate a Storybook story with a mocked AppContext.
19-
*/
2017
function storyWithProps(props: ProductPromoProps, storyName?: string) {
2118
return {
2219
name: storyName,
@@ -28,34 +25,17 @@ function storyWithProps(props: ProductPromoProps, storyName?: string) {
2825
};
2926
}
3027

31-
// --- Generic promo: user *without* Monitor or **with** Monitor‑free but not in market eligible for special promo ----------------------------------
32-
export const DefaultMobile = storyWithProps(
28+
export const MobilePromo = storyWithProps(
3329
{
3430
type: 'settings',
35-
monitorPromo: { hidePromo: false, showMonitorPlusPromo: false },
31+
monitorPromo: { hidePromo: false },
3632
},
37-
'Default Monitor promo - Banner - mobile'
33+
'Monitor promo - Banner - mobile only'
3834
);
39-
export const DefaultDesktop = storyWithProps(
35+
export const DesktopPromo = storyWithProps(
4036
{
4137
type: 'sidebar',
42-
monitorPromo: { hidePromo: false, showMonitorPlusPromo: false },
38+
monitorPromo: { hidePromo: false },
4339
},
44-
'Default Monitor promo - Sidebar - desktop'
45-
);
46-
47-
// --- Special promo: Monitor‑free + eligible (US) ----------------------------
48-
export const SpecialPromoMobile = storyWithProps(
49-
{
50-
type: 'settings',
51-
monitorPromo: { hidePromo: false, showMonitorPlusPromo: true },
52-
},
53-
'Special promo (US) - Banner - mobile'
54-
);
55-
export const SpecialPromoDesktop = storyWithProps(
56-
{
57-
type: 'sidebar',
58-
monitorPromo: { hidePromo: false, showMonitorPlusPromo: true },
59-
},
60-
'Special promo (US) - Sidebar - desktop'
40+
'Monitor promo - Sidebar - desktop'
6141
);

packages/fxa-settings/src/components/Settings/ProductPromo/index.test.tsx

Lines changed: 31 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44

55
import React from 'react';
66
import { screen, waitFor } from '@testing-library/react';
7-
import ProductPromo from '.';
8-
import { Account, AppContext } from '../../../models';
9-
import { MozServices } from '../../../lib/types';
7+
import ProductPromo, { getProductPromoData } from '.';
108
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
11-
import { mockAppContext } from '../../../models/mocks';
129
import GleanMetrics from '../../../lib/glean';
1310
import userEvent from '@testing-library/user-event';
11+
import { MozServices } from '../../../lib/types';
12+
import { AttachedClient } from '../../../models';
1413

1514
jest.mock('../../../lib/glean', () => ({
1615
__esModule: true,
@@ -38,12 +37,9 @@ describe('ProductPromo', () => {
3837
);
3938
});
4039

41-
it('can show generic promo', async () => {
40+
it('can show promo', async () => {
4241
renderWithLocalizationProvider(
43-
<ProductPromo
44-
type="settings"
45-
monitorPromo={{ hidePromo: false, showMonitorPlusPromo: false }}
46-
/>
42+
<ProductPromo type="settings" monitorPromo={{ hidePromo: false }} />
4743
);
4844

4945
await waitFor(() =>
@@ -61,35 +57,13 @@ describe('ProductPromo', () => {
6157
);
6258
});
6359

64-
it('can show special promo', async () => {
65-
renderWithLocalizationProvider(
66-
<ProductPromo
67-
type="settings"
68-
monitorPromo={{ hidePromo: false, showMonitorPlusPromo: true }}
69-
/>
70-
);
71-
72-
await waitFor(() =>
73-
expect(
74-
screen.getByText(/save on VPN, Monitors data-broker protection/i)
75-
).toBeVisible()
76-
);
77-
expect(
78-
screen.getByRole('link', { name: /Get year-round protection/i })
79-
).toHaveAttribute(
80-
'href',
81-
'https://monitor.mozilla.org/subscription-plans?utm_source=moz-account&utm_medium=referral&utm_term=settings&utm_content=get-year-round-protection-us&utm_campaign=settings-promo'
82-
);
83-
});
84-
85-
it('emits telemetry when CTA clicked (default)', async () => {
60+
it('emits telemetry when CTA clicked', async () => {
8661
const user = userEvent.setup();
8762
renderWithLocalizationProvider(
8863
<ProductPromo
8964
type="settings"
9065
monitorPromo={{
9166
hidePromo: false,
92-
showMonitorPlusPromo: false,
9367
gleanEvent: { event: { reason: 'default' } },
9468
}}
9569
/>
@@ -114,41 +88,33 @@ describe('ProductPromo', () => {
11488
});
11589
});
11690

117-
it('emits telemetry when CTA clicked (special)', async () => {
118-
const user = userEvent.setup();
119-
const account = {
120-
attachedClients: [{ name: MozServices.Monitor }],
121-
subscriptions: [],
122-
getMonitorPlusPromoEligibility: () => Promise.resolve({ eligible: true }),
123-
} as unknown as Account;
91+
describe('getProductPromoData', () => {
92+
it('hides promo when Monitor is present', () => {
93+
const result = getProductPromoData([
94+
{ name: MozServices.Monitor },
95+
] as AttachedClient[]);
96+
expect(result).toEqual({ hidePromo: true });
97+
});
12498

125-
renderWithLocalizationProvider(
126-
<AppContext.Provider value={mockAppContext({ account })}>
127-
<ProductPromo
128-
type="settings"
129-
monitorPromo={{
130-
hidePromo: false,
131-
showMonitorPlusPromo: true,
132-
gleanEvent: { event: { reason: 'special' } },
133-
}}
134-
/>
135-
</AppContext.Provider>
136-
);
99+
it('hides promo when Monitor Stage is present', () => {
100+
const result = getProductPromoData([
101+
{ name: MozServices.MonitorStage },
102+
] as AttachedClient[]);
103+
expect(result).toEqual({ hidePromo: true });
104+
});
137105

138-
await waitFor(() =>
139-
expect(
140-
screen.getByText(/save on VPN, Monitors data-broker protection/i)
141-
).toBeVisible()
142-
);
143-
await user.click(
144-
screen.getByRole('link', {
145-
name: /Get year-round protection/i,
146-
})
147-
);
148-
await waitFor(() => {
149-
expect(GleanMetrics.accountPref.promoMonitorSubmit).toHaveBeenCalledWith({
150-
event: { reason: 'special' },
151-
});
106+
it('shows promo and provides gleanEvent when Monitor not present', () => {
107+
const result = getProductPromoData([
108+
{ name: MozServices.Default },
109+
] as AttachedClient[]);
110+
expect(result.hidePromo).toBe(false);
111+
expect(result.gleanEvent).toEqual({ event: { reason: 'default' } });
112+
});
113+
114+
it('shows promo with gleanEvent when there are no attached clients', () => {
115+
const result = getProductPromoData([] as AttachedClient[]);
116+
expect(result.hidePromo).toBe(false);
117+
expect(result.gleanEvent).toEqual({ event: { reason: 'default' } });
152118
});
153119
});
154120
});

0 commit comments

Comments
 (0)