Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,9 @@ test.describe('severity-1 #smoke', () => {
await confirmSignupCode.fillOutCodeForm(code);

await expect(page).toHaveURL(/pair/);
await expect(page).toHaveURL(/signupSuccess=true/);
await expect(page).toHaveURL(/showSuccessMessage=true/);
await expect(
page.getByText('Account created. You’re now syncing.')
).toBeVisible();
await signup.checkWebChannelMessage(FirefoxCommand.OAuthLogin);
});
});
Expand Down
12 changes: 12 additions & 0 deletions packages/fxa-content-server/app/scripts/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,18 @@ module.exports = {
FIREFOX_TABS_SIDEBAR_ENTRYPOINT: 'tabs-sidebar',
FIREFOX_FX_VIEW_ENTRYPOINT: 'fx-view',

// Keep in sync with packages/fxa-settings/src/constants/index.tsx SEND_TAB_ENTRYPOINTS
// We're removing all this code soon enough ;)
SEND_TAB_ENTRYPOINTS: [
'send-tab-tab-context-menu',
'send-tab-account-menu',
'send-tab-app-menu',
'send-tab-firefox-view-three-dots',
'send-tab-link-context-menu',
'send-tab-page-context-menu',
'send-tab-toolbar-icon',
],

// This is compared against all secondary email
// records, both verified and unverified
MAX_SECONDARY_EMAILS: 3,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,50 @@
<div class="card">
<header class="mb-9">
<h1 id="pair-auth-complete-header" class="card-header">{{#t}}Device connected{{/t}}</h1>
</header>

<section>
<div class="error"></div>

<div class="{{ graphicId }}" role="img" aria-label="{{#t}}Successfully connected{{/t}}"></div>

<h2 id="device-os" class="text-base">
{{#unsafeTranslate}}
You are now syncing with: <span>%(deviceFamily)s on %(deviceOS)s</span>
{{/unsafeTranslate}}
</h2>

<p class="my-5">{{#t}}Now you can access your open tabs, passwords, and bookmarks on all your devices.{{/t}}</p>

<div class="flex">
{{#hasFirefoxViewSupport}}
<button id="open-firefox-view" class="cta-primary cta-xl">{{#t}}See tabs from synced devices{{/t}}</button>
{{/hasFirefoxViewSupport}}
{{^hasFirefoxViewSupport}}
<button id="open-connected-services" class="cta-primary cta-xl">{{#t}}Manage devices{{/t}}</button>
{{/hasFirefoxViewSupport}}
</div>
</section>
{{#isSendTab}}
<header class="mb-5">
<h1 id="pair-auth-complete-header" class="card-header">{{#t}}You’re ready to send some tabs{{/t}}</h1>
</header>

<section>
<div class="error"></div>

<div class="{{ graphicId }}" role="img" aria-label="{{#t}}Successfully connected{{/t}}"></div>

<p class="text-sm mt-4">
{{#unsafeTranslate}}
<span>%(deviceFamily)s for %(deviceOS)s</span> is connected.
{{/unsafeTranslate}}
</p>

<p class="text-sm mt-2">{{#t}}You’re free to instantly send open tabs, passwords, and bookmarks between devices.{{/t}}</p>
</section>
{{/isSendTab}}

{{^isSendTab}}
<header class="mb-9">
<h1 id="pair-auth-complete-header" class="card-header">{{#t}}Device connected{{/t}}</h1>
</header>

<section>
<div class="error"></div>

<div class="{{ graphicId }}" role="img" aria-label="{{#t}}Successfully connected{{/t}}"></div>

<h2 id="device-os" class="text-base">
{{#unsafeTranslate}}
You are now syncing with: <span>%(deviceFamily)s on %(deviceOS)s</span>
{{/unsafeTranslate}}
</h2>

<p class="my-5">{{#t}}Now you can access your open tabs, passwords, and bookmarks on all your devices.{{/t}}</p>

<div class="flex">
{{#hasFirefoxViewSupport}}
<button id="open-firefox-view" class="cta-primary cta-xl">{{#t}}See tabs from synced devices{{/t}}</button>
{{/hasFirefoxViewSupport}}
{{^hasFirefoxViewSupport}}
<button id="open-connected-services" class="cta-primary cta-xl">{{#t}}Manage devices{{/t}}</button>
{{/hasFirefoxViewSupport}}
</div>
</section>
{{/isSendTab}}
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@
{{/needsMobileConfirmed}}

{{^needsMobileConfirmed}}
<h1 class="mb-5 text-grey-400 text-base" id="cad-header">{{#t}}Connect another device{{/t}}</h1>
<h2 id="pair-header" class="card-header focus:outline-none" tabindex="-1">{{#t}}Sync your Firefox experience{{/t}}</h2>
{{#isSendTab}}
<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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be receive instead of send?

Suggested change
<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>
<h1 id="pair-header" class="card-header focus:outline-none" tabindex="-1">{{#t}}Download or open Firefox on the device where you want to receive tabs{{/t}}</h1>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran this by Ross and Laurel - sounds like for now at least we want to leave it since we always talk about "sending" tabs instead of "receiving" them. I suggested "where you want to send your tabs", but this sentence was approved by Content so I'm going to leave it. This is a great flag though!

{{/isSendTab}}
{{^isSendTab}}
<h1 class="mb-5 text-grey-400 text-base" id="cad-header">{{#t}}Connect another device{{/t}}</h1>
<h2 id="pair-header" class="card-header focus:outline-none" tabindex="-1">{{#t}}Sync your Firefox experience{{/t}}</h2>
{{/isSendTab}}
{{/needsMobileConfirmed}}

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

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

<form novalidate id="form-ask-mobile-status">
<fieldset>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,18 @@ class PairAuthCompleteView extends FormView {
setInitialContext(context) {
const deviceContext = assign({}, this.broker.get('remoteMetaData'));
const graphicId = this.getGraphicsId();
const entrypoint =
(this.relier && this.relier.get && this.relier.get('entrypoint')) ||
this.getSearchParam('entrypoint');
const isSendTab =
!!entrypoint && Constants.SEND_TAB_ENTRYPOINTS.indexOf(entrypoint) !== -1;

context.set({
graphicId,
deviceFamily: deviceContext.family,
deviceOS: deviceContext.OS,
hasFirefoxViewSupport: this._hasFirefoxViewSupport(),
isSendTab,
});
}

Expand Down
6 changes: 6 additions & 0 deletions packages/fxa-content-server/app/scripts/views/pair/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import Cocktail from 'cocktail';
import Constants from '../../lib/constants';
import { MARKETING_ID_AUTUMN_2016, SYNC_SERVICE } from '../../lib/constants';
import GleanMetrics from '../../lib/glean';
import UserAgentMixin from '../../lib/user-agent-mixin';
Expand Down Expand Up @@ -187,6 +188,10 @@ class PairIndexView extends FormView {
}
}

const entrypoint = this.getSearchParam('entrypoint');
const isSendTab =
!!entrypoint && Constants.SEND_TAB_ENTRYPOINTS.indexOf(entrypoint) !== -1;

context.set({
graphicId,
needsMobileConfirmed,
Expand All @@ -195,6 +200,7 @@ class PairIndexView extends FormView {
showPasswordCreatedMessage: this.showPasswordCreatedMessage(),
buttonTextShadowClass,
tabletBackArrowColor,
isSendTab,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ describe('views/pair/auth_complete', () => {
view = new View({
broker,
notifier,
relier,
user,
viewName: 'pairAuthComplete',
window: windowMock,
Expand Down Expand Up @@ -93,5 +94,32 @@ describe('views/pair/auth_complete', () => {
assert.ok(view.$el.find('.bg-image-triple-device-hearts').length);
});
});

it('renders the Send Tab variant when the entrypoint is a send-tab value', () => {
relier.set('entrypoint', 'send-tab-toolbar-icon');
return view.render().then(() => {
assert.equal(
view.$el.find('#pair-auth-complete-header').text(),
'You’re ready to send some tabs'
);
assert.include(view.$el.text(), 'Firefox for Windows');
assert.include(view.$el.text(), 'is connected.');
// No CTA buttons in the Send Tab variant
assert.lengthOf(view.$el.find('#open-firefox-view'), 0);
assert.lengthOf(view.$el.find('#open-connected-services'), 0);
// The "syncing with" heading is suppressed
assert.lengthOf(view.$el.find('#device-os'), 0);
});
});

it('renders the default variant when entrypoint is not a send-tab value', () => {
relier.set('entrypoint', 'preferences');
return view.render().then(() => {
assert.equal(
view.$el.find('#pair-auth-complete-header').text(),
'Device connected'
);
});
});
});
});
26 changes: 26 additions & 0 deletions packages/fxa-content-server/app/tests/spec/views/pair/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ describe('views/pair/index', () => {
const subheading = view.$('#pair-header');
assert.strictEqual(subheading.text(), 'Sync your Firefox experience');
assert.equal(view.$('#pair-header-mobile').length, 0);
// Description paragraph should be present in the non-Send-Tab flow.
assert.include(view.$el.text(), 'View your saved passwords');

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

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

describe('Send Tab variant', () => {
beforeEach(() => {
view.render.restore && view.render.restore();
const origGetSearchParam = view.getSearchParam.bind(view);
sinon.stub(view, 'getSearchParam').callsFake((name) => {
if (name === 'entrypoint') {
return 'send-tab-toolbar-icon';
}
return origGetSearchParam(name);
});
return view.render();
});

it('renders the send-tab heading without the grey "Connect another device" text', () => {
assert.strictEqual(view.$('#cad-header').length, 0);
assert.strictEqual(
view.$('#pair-header').text(),
'Download or open Firefox on the device where you want to send tabs'
);
// Description paragraph is suppressed in Send-Tab flow.
assert.notInclude(view.$el.text(), 'View your saved passwords');
});
});

describe('handleRadioEngage', () => {
let engageChoiceEventStub;
beforeEach(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/fxa-settings/src/components/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ const AuthAndAccountSetupRoutes = ({

{/* Pairing */}
<ConnectAnotherDevice path="/connect_another_device/*" />
<PairIndex path="/pair/*" />
<PairIndex path="/pair/*" integration={integration} />
<PairSupp path="/pair/supp/*" integration={integration} />
<PairSuppAllow path="/pair/supp/allow/*" integration={integration} />
<PairSuppWaitForAuth
Expand Down
21 changes: 21 additions & 0 deletions packages/fxa-settings/src/lib/utilities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {
isBase32Crockford,
isMobileDevice,
isValidCmsUrl,
isSendTabEntrypoint,
navigateWithQueryHelper,
once,
resetOnce,
searchParam,
searchParams,
toGenericOSName,
} from './utilities';
import { SEND_TAB_ENTRYPOINTS } from '../constants';

describe('deepMerge', () => {
it('recursively merges multiple objects', () => {
Expand Down Expand Up @@ -349,3 +351,22 @@ describe('isValidCmsUrl', () => {
expect(isValidCmsUrl(value as string | null | undefined)).toBe(false);
});
});

describe('isSendTabEntrypoint', () => {
it('returns true for all send-tab entrypoints', () => {
for (const entrypoint of SEND_TAB_ENTRYPOINTS) {
expect(isSendTabEntrypoint(entrypoint)).toBe(true);
}
});

it('returns false for other entrypoints', () => {
expect(isSendTabEntrypoint('preferences')).toBe(false);
expect(isSendTabEntrypoint('fxa_app_menu')).toBe(false);
});

it('returns false for nullish values', () => {
expect(isSendTabEntrypoint(undefined)).toBe(false);
expect(isSendTabEntrypoint(null)).toBe(false);
expect(isSendTabEntrypoint('')).toBe(false);
});
});
8 changes: 8 additions & 0 deletions packages/fxa-settings/src/lib/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import base32Encode from 'base32-encode';
import { AttachedClient } from '../models/Account';
import { navigate, NavigateFn, NavigateOptions } from '@reach/router';
import { SEND_TAB_ENTRYPOINTS } from '../constants';

// Various utilities that don't fit in a standalone lib

Expand Down Expand Up @@ -283,3 +284,10 @@ export const DEFAULT_PAIRING_ERROR = 'An error occurred during pairing';
export function getPairingErrorMessage(err: unknown): string {
return err instanceof Error ? err.message : DEFAULT_PAIRING_ERROR;
}

/** Whether the given entrypoint originated from a Firefox "Send Tab" UI. */
export function isSendTabEntrypoint(
entrypoint: string | null | undefined
): boolean {
return !!entrypoint && SEND_TAB_ENTRYPOINTS.has(entrypoint);
}
11 changes: 10 additions & 1 deletion packages/fxa-settings/src/models/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,17 @@ export const MOCK_ACCOUNT: AccountData =
export const MOCK_SESSION: Session =
DEFAULT_APP_CONTEXT.session as unknown as Session;

export function createHistoryWithQuery(path: string, queryParams?: string) {
export function createHistoryWithQuery(
path: string,
queryParams?: string,
state?: object
) {
const history = createHistory(createMemorySource(path));
// navigate first (it resets search), then apply queryParams last so both
// state and search co-exist on the final location.
if (state !== undefined) {
history.navigate(path, { state });
Comment thread
vpomerleau marked this conversation as resolved.
}
if (queryParams != null) {
history.location.search = queryParams;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/fxa-settings/src/pages/Pair/AuthComplete/en.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ pair-auth-complete-now-syncing-device-text = You are now syncing with: { $device
pair-auth-complete-sync-benefits-text = Now you can access your open tabs, passwords, and bookmarks on all your devices.
pair-auth-complete-see-tabs-button = See tabs from synced devices
pair-auth-complete-manage-devices-link = Manage devices

## Alternate "Send Tab" variant — shown when the pair was initiated from a Send Tab entrypoint (toolbar icon, app menu, etc.)

# Heading
pair-auth-complete-send-tab-heading = You’re ready to send some tabs
# Variable { $deviceFamily } is generally a browser name, for example "Firefox"
# Variable { $deviceOS } is an operating system short name, for example "iOS", "Android"
pair-auth-complete-send-tab-device-connected = { $deviceFamily } for { $deviceOS } is connected.
pair-auth-complete-send-tab-benefits = You’re free to instantly send open tabs, passwords, and bookmarks between devices.
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ import { Meta } from '@storybook/react';
import { MOCK_ERROR } from './mocks';
import { MOCK_METADATA_UNKNOWN_LOCATION } from '../../../components/DeviceInfoBlock/mocks';
import { withLocalization } from 'fxa-react/lib/storybooks';
import { Integration } from '../../../models/integrations/integration';

export default {
title: 'Pages/Pair/AuthComplete',
component: AuthComplete,
decorators: [withLocalization],
} as Meta;

// Minimal integration stub carrying an entrypoint — just enough to drive the
// isSendTab branch in AuthComplete. The real PairingAuthorityIntegration is
// exercised in tests.
const integrationWithEntrypoint = (entrypoint: string) =>
({ data: { entrypoint } }) as unknown as Integration;

// Any metadata mock from DeviceInfoBlock will do, location is not displayed on this page
export const Default = () => (
<AuthComplete suppDeviceInfo={MOCK_METADATA_UNKNOWN_LOCATION} />
Expand All @@ -27,6 +34,13 @@ export const SupportsFirefoxView = () => (
/>
);

export const SendTabVariant = () => (
<AuthComplete
suppDeviceInfo={MOCK_METADATA_UNKNOWN_LOCATION}
integration={integrationWithEntrypoint('send-tab-toolbar-icon')}
/>
);

export const WithErrorMessage = () => (
<AuthComplete
suppDeviceInfo={MOCK_METADATA_UNKNOWN_LOCATION}
Expand Down
Loading
Loading