Skip to content

Commit 83e352f

Browse files
authored
Merge pull request #20403 from mozilla/FXA-13414
feat(pair): Send Tab variant and origin-driven messaging on /pair, Backbone & React
2 parents 3d5c15b + 46b9a19 commit 83e352f

23 files changed

Lines changed: 651 additions & 155 deletions

File tree

packages/functional-tests/tests/react-conversion/oauthSignup.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ test.describe('severity-1 #smoke', () => {
130130
await confirmSignupCode.fillOutCodeForm(code);
131131

132132
await expect(page).toHaveURL(/pair/);
133-
await expect(page).toHaveURL(/signupSuccess=true/);
134-
await expect(page).toHaveURL(/showSuccessMessage=true/);
133+
await expect(
134+
page.getByText('Account created. You’re now syncing.')
135+
).toBeVisible();
135136
await signup.checkWebChannelMessage(FirefoxCommand.OAuthLogin);
136137
});
137138
});

packages/fxa-content-server/app/scripts/lib/constants.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,18 @@ module.exports = {
174174
FIREFOX_TABS_SIDEBAR_ENTRYPOINT: 'tabs-sidebar',
175175
FIREFOX_FX_VIEW_ENTRYPOINT: 'fx-view',
176176

177+
// Keep in sync with packages/fxa-settings/src/constants/index.tsx SEND_TAB_ENTRYPOINTS
178+
// We're removing all this code soon enough ;)
179+
SEND_TAB_ENTRYPOINTS: [
180+
'send-tab-tab-context-menu',
181+
'send-tab-account-menu',
182+
'send-tab-app-menu',
183+
'send-tab-firefox-view-three-dots',
184+
'send-tab-link-context-menu',
185+
'send-tab-page-context-menu',
186+
'send-tab-toolbar-icon',
187+
],
188+
177189
// This is compared against all secondary email
178190
// records, both verified and unverified
179191
MAX_SECONDARY_EMAILS: 3,
Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,50 @@
11
<div class="card">
2-
<header class="mb-9">
3-
<h1 id="pair-auth-complete-header" class="card-header">{{#t}}Device connected{{/t}}</h1>
4-
</header>
5-
6-
<section>
7-
<div class="error"></div>
8-
9-
<div class="{{ graphicId }}" role="img" aria-label="{{#t}}Successfully connected{{/t}}"></div>
10-
11-
<h2 id="device-os" class="text-base">
12-
{{#unsafeTranslate}}
13-
You are now syncing with: <span>%(deviceFamily)s on %(deviceOS)s</span>
14-
{{/unsafeTranslate}}
15-
</h2>
16-
17-
<p class="my-5">{{#t}}Now you can access your open tabs, passwords, and bookmarks on all your devices.{{/t}}</p>
18-
19-
<div class="flex">
20-
{{#hasFirefoxViewSupport}}
21-
<button id="open-firefox-view" class="cta-primary cta-xl">{{#t}}See tabs from synced devices{{/t}}</button>
22-
{{/hasFirefoxViewSupport}}
23-
{{^hasFirefoxViewSupport}}
24-
<button id="open-connected-services" class="cta-primary cta-xl">{{#t}}Manage devices{{/t}}</button>
25-
{{/hasFirefoxViewSupport}}
26-
</div>
27-
</section>
2+
{{#isSendTab}}
3+
<header class="mb-5">
4+
<h1 id="pair-auth-complete-header" class="card-header">{{#t}}You’re ready to send some tabs{{/t}}</h1>
5+
</header>
6+
7+
<section>
8+
<div class="error"></div>
9+
10+
<div class="{{ graphicId }}" role="img" aria-label="{{#t}}Successfully connected{{/t}}"></div>
11+
12+
<p class="text-sm mt-4">
13+
{{#unsafeTranslate}}
14+
<span>%(deviceFamily)s for %(deviceOS)s</span> is connected.
15+
{{/unsafeTranslate}}
16+
</p>
17+
18+
<p class="text-sm mt-2">{{#t}}You’re free to instantly send open tabs, passwords, and bookmarks between devices.{{/t}}</p>
19+
</section>
20+
{{/isSendTab}}
21+
22+
{{^isSendTab}}
23+
<header class="mb-9">
24+
<h1 id="pair-auth-complete-header" class="card-header">{{#t}}Device connected{{/t}}</h1>
25+
</header>
26+
27+
<section>
28+
<div class="error"></div>
29+
30+
<div class="{{ graphicId }}" role="img" aria-label="{{#t}}Successfully connected{{/t}}"></div>
31+
32+
<h2 id="device-os" class="text-base">
33+
{{#unsafeTranslate}}
34+
You are now syncing with: <span>%(deviceFamily)s on %(deviceOS)s</span>
35+
{{/unsafeTranslate}}
36+
</h2>
37+
38+
<p class="my-5">{{#t}}Now you can access your open tabs, passwords, and bookmarks on all your devices.{{/t}}</p>
39+
40+
<div class="flex">
41+
{{#hasFirefoxViewSupport}}
42+
<button id="open-firefox-view" class="cta-primary cta-xl">{{#t}}See tabs from synced devices{{/t}}</button>
43+
{{/hasFirefoxViewSupport}}
44+
{{^hasFirefoxViewSupport}}
45+
<button id="open-connected-services" class="cta-primary cta-xl">{{#t}}Manage devices{{/t}}</button>
46+
{{/hasFirefoxViewSupport}}
47+
</div>
48+
</section>
49+
{{/isSendTab}}
2850
</div>

packages/fxa-content-server/app/scripts/templates/pair/index.mustache

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,13 @@
2828
{{/needsMobileConfirmed}}
2929

3030
{{^needsMobileConfirmed}}
31-
<h1 class="mb-5 text-grey-400 text-base" id="cad-header">{{#t}}Connect another device{{/t}}</h1>
32-
<h2 id="pair-header" class="card-header focus:outline-none" tabindex="-1">{{#t}}Sync your Firefox experience{{/t}}</h2>
31+
{{#isSendTab}}
32+
<h1 id="pair-header" class="card-header focus:outline-none" tabindex="-1">{{#t}}Download or open Firefox on the device where you want to send tabs{{/t}}</h1>
33+
{{/isSendTab}}
34+
{{^isSendTab}}
35+
<h1 class="mb-5 text-grey-400 text-base" id="cad-header">{{#t}}Connect another device{{/t}}</h1>
36+
<h2 id="pair-header" class="card-header focus:outline-none" tabindex="-1">{{#t}}Sync your Firefox experience{{/t}}</h2>
37+
{{/isSendTab}}
3338
{{/needsMobileConfirmed}}
3439

3540
{{#needsMobileConfirmed}}
@@ -41,7 +46,9 @@
4146
<div class="error"></div>
4247

4348
{{^needsMobileConfirmed}}
44-
<p class="my-3 text-base">{{#t}}View your saved passwords, tabs, browsing history and more — across all your devices.{{/t}}</p>
49+
{{^isSendTab}}
50+
<p class="my-3 text-base">{{#t}}View your saved passwords, tabs, browsing history and more — across all your devices.{{/t}}</p>
51+
{{/isSendTab}}
4552

4653
<form novalidate id="form-ask-mobile-status">
4754
<fieldset>

packages/fxa-content-server/app/scripts/views/pair/auth_complete.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,18 @@ class PairAuthCompleteView extends FormView {
4343
setInitialContext(context) {
4444
const deviceContext = assign({}, this.broker.get('remoteMetaData'));
4545
const graphicId = this.getGraphicsId();
46+
const entrypoint =
47+
(this.relier && this.relier.get && this.relier.get('entrypoint')) ||
48+
this.getSearchParam('entrypoint');
49+
const isSendTab =
50+
!!entrypoint && Constants.SEND_TAB_ENTRYPOINTS.indexOf(entrypoint) !== -1;
4651

4752
context.set({
4853
graphicId,
4954
deviceFamily: deviceContext.family,
5055
deviceOS: deviceContext.OS,
5156
hasFirefoxViewSupport: this._hasFirefoxViewSupport(),
57+
isSendTab,
5258
});
5359
}
5460

packages/fxa-content-server/app/scripts/views/pair/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
import Cocktail from 'cocktail';
6+
import Constants from '../../lib/constants';
67
import { MARKETING_ID_AUTUMN_2016, SYNC_SERVICE } from '../../lib/constants';
78
import GleanMetrics from '../../lib/glean';
89
import UserAgentMixin from '../../lib/user-agent-mixin';
@@ -187,6 +188,10 @@ class PairIndexView extends FormView {
187188
}
188189
}
189190

191+
const entrypoint = this.getSearchParam('entrypoint');
192+
const isSendTab =
193+
!!entrypoint && Constants.SEND_TAB_ENTRYPOINTS.indexOf(entrypoint) !== -1;
194+
190195
context.set({
191196
graphicId,
192197
needsMobileConfirmed,
@@ -195,6 +200,7 @@ class PairIndexView extends FormView {
195200
showPasswordCreatedMessage: this.showPasswordCreatedMessage(),
196201
buttonTextShadowClass,
197202
tabletBackArrowColor,
203+
isSendTab,
198204
});
199205
}
200206

packages/fxa-content-server/app/tests/spec/views/pair/auth_complete.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ describe('views/pair/auth_complete', () => {
6262
view = new View({
6363
broker,
6464
notifier,
65+
relier,
6566
user,
6667
viewName: 'pairAuthComplete',
6768
window: windowMock,
@@ -93,5 +94,32 @@ describe('views/pair/auth_complete', () => {
9394
assert.ok(view.$el.find('.bg-image-triple-device-hearts').length);
9495
});
9596
});
97+
98+
it('renders the Send Tab variant when the entrypoint is a send-tab value', () => {
99+
relier.set('entrypoint', 'send-tab-toolbar-icon');
100+
return view.render().then(() => {
101+
assert.equal(
102+
view.$el.find('#pair-auth-complete-header').text(),
103+
'You’re ready to send some tabs'
104+
);
105+
assert.include(view.$el.text(), 'Firefox for Windows');
106+
assert.include(view.$el.text(), 'is connected.');
107+
// No CTA buttons in the Send Tab variant
108+
assert.lengthOf(view.$el.find('#open-firefox-view'), 0);
109+
assert.lengthOf(view.$el.find('#open-connected-services'), 0);
110+
// The "syncing with" heading is suppressed
111+
assert.lengthOf(view.$el.find('#device-os'), 0);
112+
});
113+
});
114+
115+
it('renders the default variant when entrypoint is not a send-tab value', () => {
116+
relier.set('entrypoint', 'preferences');
117+
return view.render().then(() => {
118+
assert.equal(
119+
view.$el.find('#pair-auth-complete-header').text(),
120+
'Device connected'
121+
);
122+
});
123+
});
96124
});
97125
});

packages/fxa-content-server/app/tests/spec/views/pair/index.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ describe('views/pair/index', () => {
187187
const subheading = view.$('#pair-header');
188188
assert.strictEqual(subheading.text(), 'Sync your Firefox experience');
189189
assert.equal(view.$('#pair-header-mobile').length, 0);
190+
// Description paragraph should be present in the non-Send-Tab flow.
191+
assert.include(view.$el.text(), 'View your saved passwords');
190192

191193
assert.strictEqual(view.$('#form-ask-mobile-status').length, 1);
192194

@@ -235,6 +237,30 @@ describe('views/pair/index', () => {
235237
sinon.assert.calledOnce(viewChoiceEventStub);
236238
});
237239

240+
describe('Send Tab variant', () => {
241+
beforeEach(() => {
242+
view.render.restore && view.render.restore();
243+
const origGetSearchParam = view.getSearchParam.bind(view);
244+
sinon.stub(view, 'getSearchParam').callsFake((name) => {
245+
if (name === 'entrypoint') {
246+
return 'send-tab-toolbar-icon';
247+
}
248+
return origGetSearchParam(name);
249+
});
250+
return view.render();
251+
});
252+
253+
it('renders the send-tab heading without the grey "Connect another device" text', () => {
254+
assert.strictEqual(view.$('#cad-header').length, 0);
255+
assert.strictEqual(
256+
view.$('#pair-header').text(),
257+
'Download or open Firefox on the device where you want to send tabs'
258+
);
259+
// Description paragraph is suppressed in Send-Tab flow.
260+
assert.notInclude(view.$el.text(), 'View your saved passwords');
261+
});
262+
});
263+
238264
describe('handleRadioEngage', () => {
239265
let engageChoiceEventStub;
240266
beforeEach(() => {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ const AuthAndAccountSetupRoutes = ({
804804

805805
{/* Pairing */}
806806
<ConnectAnotherDevice path="/connect_another_device/*" />
807-
<PairIndex path="/pair/*" />
807+
<PairIndex path="/pair/*" integration={integration} />
808808
<PairSupp path="/pair/supp/*" integration={integration} />
809809
<PairSuppAllow path="/pair/supp/allow/*" integration={integration} />
810810
<PairSuppWaitForAuth

packages/fxa-settings/src/lib/utilities.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ import {
99
isBase32Crockford,
1010
isMobileDevice,
1111
isValidCmsUrl,
12+
isSendTabEntrypoint,
1213
navigateWithQueryHelper,
1314
once,
1415
resetOnce,
1516
searchParam,
1617
searchParams,
1718
toGenericOSName,
1819
} from './utilities';
20+
import { SEND_TAB_ENTRYPOINTS } from '../constants';
1921

2022
describe('deepMerge', () => {
2123
it('recursively merges multiple objects', () => {
@@ -349,3 +351,22 @@ describe('isValidCmsUrl', () => {
349351
expect(isValidCmsUrl(value as string | null | undefined)).toBe(false);
350352
});
351353
});
354+
355+
describe('isSendTabEntrypoint', () => {
356+
it('returns true for all send-tab entrypoints', () => {
357+
for (const entrypoint of SEND_TAB_ENTRYPOINTS) {
358+
expect(isSendTabEntrypoint(entrypoint)).toBe(true);
359+
}
360+
});
361+
362+
it('returns false for other entrypoints', () => {
363+
expect(isSendTabEntrypoint('preferences')).toBe(false);
364+
expect(isSendTabEntrypoint('fxa_app_menu')).toBe(false);
365+
});
366+
367+
it('returns false for nullish values', () => {
368+
expect(isSendTabEntrypoint(undefined)).toBe(false);
369+
expect(isSendTabEntrypoint(null)).toBe(false);
370+
expect(isSendTabEntrypoint('')).toBe(false);
371+
});
372+
});

0 commit comments

Comments
 (0)