Skip to content

Commit

Permalink
Merge pull request #17154 from mozilla/FXA-9600-9601-9602-9785
Browse files Browse the repository at this point in the history
feat(glean): Connect another device glean front-end metrics
  • Loading branch information
vpomerleau authored Jun 21, 2024
2 parents d560088 + ad10eb2 commit 157de95
Show file tree
Hide file tree
Showing 10 changed files with 283 additions and 46 deletions.
57 changes: 57 additions & 0 deletions packages/fxa-content-server/app/scripts/lib/glean/cad.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// AUTOGENERATED BY glean_parser v14.1.2. DO NOT EDIT. DO NOT COMMIT.

import EventMetricType from '@mozilla/glean/private/metrics/event';

/**
* User clicks "Start browsing" on "Connect another device page"
*
* Generated from `cad.startbrowsing_submit`.
*/
export const startbrowsingSubmit = new EventMetricType(
{
category: 'cad',
name: 'startbrowsing_submit',
sendInPings: ['events'],
lifetime: 'ping',
disabled: false,
},
[]
);

/**
* User clicks "connect another device" to proceed to "connect Firefox on another
* device" screen with QR code
*
* Generated from `cad.submit`.
*/
export const submit = new EventMetricType(
{
category: 'cad',
name: 'submit',
sendInPings: ['events'],
lifetime: 'ping',
disabled: false,
},
[]
);

/**
* User views the Connect another device page after EITHER registering or signing
* into Sync - this is only specific to the Sync flow
*
* Generated from `cad.view`.
*/
export const view = new EventMetricType(
{
category: 'cad',
name: 'view',
sendInPings: ['events'],
lifetime: 'ping',
disabled: false,
},
[]
);
16 changes: 16 additions & 0 deletions packages/fxa-content-server/app/scripts/lib/glean/cadFirefox.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ export const choiceEngage = new EventMetricType(
['reason']
);

/**
* User clicked "Not now"
*
* Generated from `cad_firefox.choice_notnow_submit`.
*/
export const choiceNotnowSubmit = new EventMetricType(
{
category: 'cad_firefox',
name: 'choice_notnow_submit',
sendInPings: ['events'],
lifetime: 'ping',
disabled: false,
},
[]
);

/**
* User submitted on the "Connect another device" screen with choice options,
* submitting either of "I already have FF for mobile" or "I don't have FF for
Expand Down
38 changes: 29 additions & 9 deletions packages/fxa-content-server/app/scripts/lib/glean/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@

import Glean from '@mozilla/glean/web';

import { accountsEvents } from './pings';
import * as event from './event';
import * as email from './email';
import * as reg from './reg';
import * as login from './login';
import { userIdSha256 } from './account';
import * as cachedLogin from './cachedLogin';
import * as passwordReset from './passwordReset';
import * as cadFirefox from './cadFirefox';
import * as cadApproveDevice from './cadApproveDevice';
import * as cadFirefox from './cadFirefox';
import * as cadMobilePair from './cadMobilePair';
import * as setPasswordThirdPartyAuth from './setPasswordThirdPartyAuth';
import { userIdSha256 } from './account';
import * as cad from './cad';
import * as email from './email';
import * as event from './event';
import * as login from './login';
import * as passwordReset from './passwordReset';
import { accountsEvents } from './pings';
import * as reg from './reg';
import { oauthClientId, service } from './relyingParty';
import { deviceType, entrypoint, flowId } from './session';
import * as setPasswordThirdPartyAuth from './setPasswordThirdPartyAuth';
import * as utm from './utm';

export type GleanMetricsConfig = {
Expand Down Expand Up @@ -244,6 +245,9 @@ const recordEventMetric = (eventName: string, properties: EventProperties) => {
reason: properties['reason'] || '',
});
break;
case 'cad_firefox_choice_notnow_submit':
cadFirefox.choiceNotnowSubmit.record();
break;
case 'cad_firefox_sync_device_submit':
cadFirefox.syncDeviceSubmit.record();
break;
Expand All @@ -253,6 +257,15 @@ const recordEventMetric = (eventName: string, properties: EventProperties) => {
case 'cad_mobile_pair_view':
cadMobilePair.view.record();
break;
case 'cad_view':
cad.view.record();
break;
case 'cad_submit':
cad.submit.record();
break;
case 'cad_startbrowsing_submit':
cad.startbrowsingSubmit.record();
break;
case 'third_party_auth_set_password_view':
setPasswordThirdPartyAuth.view.record();
break;
Expand Down Expand Up @@ -364,11 +377,18 @@ export const GleanMetrics = {
success: createEventFn('login_totp_code_success_view'),
},

cad: {
view: createEventFn('cad_view'),
submit: createEventFn('cad_submit'),
startbrowsingSubmit: createEventFn('cad_startbrowsing_submit'),
},

cadFirefox: {
view: createEventFn('cad_firefox_view'),
choiceView: createEventFn('cad_firefox_choice_view'),
choiceEngage: createEventFn('cad_firefox_choice_engage'),
choiceSubmit: createEventFn('cad_firefox_choice_submit'),
choiceNotnowSubmit: createEventFn('cad_firefox_choice_notnow_submit'),
syncDeviceSubmit: createEventFn('cad_firefox_sync_device_submit'),
},
cadMobilePair: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
</div>
</form>

<p class="mt-5 text-sm text-center"><a id="pair-not-now" class="link-blue" href="/settings">{{#t}}Not now{{/t}}</a></p>
<p class="mt-5 text-sm text-center"><a id="choice-pair-not-now" class="link-blue" href="/settings">{{#t}}Not now{{/t}}</a></p>
{{/needsMobileConfirmed}}

{{#needsMobileConfirmed}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@
* browser, they are nudged to install Firefox for Android or iOS.
*/
import Cocktail from 'cocktail';
import Template from 'templates/connect_another_device.mustache';
import Constants from '../lib/constants';
import GleanMetrics from '../lib/glean';
import UserAgentMixin from '../lib/user-agent-mixin';
import FormView from './form';
import MarketingSnippet from './marketing_snippet';
import ConnectAnotherDeviceMixin from './mixins/connect-another-device-mixin';
import FlowEventsMixin from './mixins/flow-events-mixin';
import FormView from './form';
import HasModalChildViewMixin from './mixins/has-modal-child-view-mixin';
import PairingGraphicsMixin from './mixins/pairing-graphics-mixin';
import Constants from '../lib/constants';
import MarketingMixin from './mixins/marketing-mixin';
import MarketingSnippet from './marketing_snippet';
import PairingGraphicsMixin from './mixins/pairing-graphics-mixin';
import SyncAuthMixin from './mixins/sync-auth-mixin';
import Template from 'templates/connect_another_device.mustache';
import UserAgentMixin from '../lib/user-agent-mixin';
import VerificationReasonMixin from './mixins/verification-reason-mixin';

const entrypoints = Object.keys(Constants)
Expand All @@ -36,6 +37,7 @@ const ConnectAnotherDeviceView = FormView.extend({

events: {
'click #cad-not-now': 'notNowLinkHandler',
'click #sync-firefox-devices': 'connectAnotherDeviceLinkHandler',
},

beforeRender() {
Expand All @@ -49,8 +51,8 @@ const ConnectAnotherDeviceView = FormView.extend({
}

// Some RPs specify a `redirect_to` query param, check to see if this should
// be automatically navigated via the `redirect_immediately` param. Note that
// the user is redirected to `settings` page because it performs extra validation
// be automatically navigated via the `redirect_immediately` param. Note that
// the user is redirected to `settings` page because it performs extra validation
// on whether the url is allowed to be redirected to.
if (this.getSearchParam('redirect_immediately') === 'true') {
this.navigate('/settings');
Expand Down Expand Up @@ -94,6 +96,7 @@ const ConnectAnotherDeviceView = FormView.extend({
* @private
*/
_logViewMetrics() {
GleanMetrics.cad.view();
const isSignedIn = this._isSignedIn();
this.logFlowEvent(`signedin.${isSignedIn}`);

Expand Down Expand Up @@ -262,6 +265,12 @@ const ConnectAnotherDeviceView = FormView.extend({

notNowLinkHandler() {
this.logEvent('cad.notnow.engage');
GleanMetrics.cad.startbrowsingSubmit();
return true;
},

connectAnotherDeviceLinkHandler() {
GleanMetrics.cad.submit();
return true;
},
});
Expand Down
24 changes: 16 additions & 8 deletions packages/fxa-content-server/app/scripts/views/pair/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import FormView from '../form';
import Cocktail from 'cocktail';
import FlowEventsMixin from '../mixins/flow-events-mixin';
import Template from '../../templates/pair/index.mustache';
import { MARKETING_ID_AUTUMN_2016, SYNC_SERVICE } from '../../lib/constants';
import GleanMetrics from '../../lib/glean';
import UserAgentMixin from '../../lib/user-agent-mixin';
import FormNeedsMobile from '../../models/pairing/form-needs-mobile';
import Template from '../../templates/pair/index.mustache';
import FormView from '../form';
import FlowEventsMixin from '../mixins/flow-events-mixin';
import MarketingMixin from '../mixins/marketing-mixin';
import PairingGraphicsMixin from '../mixins/pairing-graphics-mixin';
import PairingTotpMixin from './pairing-totp-mixin';
import { MARKETING_ID_AUTUMN_2016, SYNC_SERVICE } from '../../lib/constants';
import SyncAuthMixin from '../mixins/sync-auth-mixin';
import MarketingMixin from '../mixins/marketing-mixin';
import FormNeedsMobile from '../../models/pairing/form-needs-mobile';
import GleanMetrics from '../../lib/glean';
import PairingTotpMixin from './pairing-totp-mixin';

const GLEAN_EVENT_REASON_HAS_MOBILE = 'has mobile';
const GLEAN_EVENT_REASON_NO_MOBILE = 'does not have mobile';
Expand All @@ -25,6 +25,7 @@ class PairIndexView extends FormView {
...FormView.prototype.events,
'click #get-fx-mobile': 'downloadLinkEngagement',
'click #pair-not-now': 'pairNotNowHandler',
'click #choice-pair-not-now': 'choicePairNotNowHandler',
'click #set-needs-mobile': 'setNeedsMobile',
'click #back-btn': 'handleBackButton',
'click .input-radio': 'handleRadioEngage',
Expand Down Expand Up @@ -130,6 +131,13 @@ class PairIndexView extends FormView {
this.metrics.logEvent('screen.pair.notnow.engage');
return true;
}

// When user is offered a choice to select if they do or do not have Firefox for mobile
// to start connecting another device but click the "Not Now" button and cancel out of the flow
choicePairNotNowHandler() {
GleanMetrics.cadFirefox.choiceNotnowSubmit();
return true;
}
}

Cocktail.mixin(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import Backbone from 'backbone';
import { assert } from 'chai';
import $ from 'jquery';
import Notifier from 'lib/channels/notifier';
import Account from 'models/account';
import { assert } from 'chai';
import AuthBroker from 'models/auth_brokers/base';
import Backbone from 'backbone';
import Notifier from 'lib/channels/notifier';
import Relier from 'models/reliers/relier';
import sinon from 'sinon';
import User from 'models/user';
import sinon from 'sinon';
import View from 'views/connect_another_device';
import GleanMetrics from '../../../scripts/lib/glean';
import WindowMock from '../../mocks/window';

describe('views/connect_another_device', () => {
Expand Down Expand Up @@ -63,8 +64,10 @@ describe('views/connect_another_device', () => {
}

describe('render/afterVisible', () => {
let viewEventStub;
describe('with a Fx desktop user that is signed in', () => {
beforeEach(() => {
viewEventStub = sinon.stub(GleanMetrics.cad, 'view');
sinon.stub(view, '_isSignedIn').callsFake(() => true);

windowMock.navigator.userAgent =
Expand All @@ -75,6 +78,14 @@ describe('views/connect_another_device', () => {
});
});

afterEach(() => {
viewEventStub.restore();
});

it('logs the view event', () => {
sinon.assert.calledOnce(viewEventStub);
});

it('shows the pairing link, logs appropriately', () => {
assert.isTrue(view._isSignedIn.called);
assert.lengthOf(view.$('#sync-firefox-devices'), 1);
Expand Down Expand Up @@ -111,13 +122,22 @@ describe('views/connect_another_device', () => {
].forEach((entrypoint) => {
describe(`with a Fx desktop user that can pair from ${entrypoint}`, () => {
beforeEach(() => {
viewEventStub = sinon.stub(GleanMetrics.cad, 'view');
sinon.stub(view, '_isSignedIn').callsFake(() => true);
relier.set('entrypoint', entrypoint);
sinon.spy(view, 'navigate');
windowMock.navigator.userAgent =
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:55.0) Gecko/20100101 Firefox/55.0';
});

afterEach(() => {
viewEventStub.restore();
});

it('does not log a view event', () => {
sinon.assert.notCalled(viewEventStub);
});

describe('with fx_desktop_v3 context', () => {
beforeEach(() => {
relier.set('context', 'fx_desktop_v3');
Expand Down Expand Up @@ -156,6 +176,7 @@ describe('views/connect_another_device', () => {
describe(`with known entrypoint ${entrypoint} and action=email'
}`, () => {
beforeEach(() => {
viewEventStub = sinon.stub(GleanMetrics.cad, 'view');
sinon.stub(view, '_isSignedIn').callsFake(() => true);
relier.set('entrypoint', entrypoint);
relier.set('context', 'fx_desktop_v3');
Expand All @@ -172,6 +193,14 @@ describe('views/connect_another_device', () => {
});
});

afterEach(() => {
viewEventStub.restore();
});

it('logs the view event', () => {
sinon.assert.calledOnce(viewEventStub);
});

it('shows the success message and does not redirect', () => {
assert.lengthOf(view.$(FXA_CONNECTED_SELECTOR), 1);
assert.isTrue(view.navigate.notCalled);
Expand All @@ -190,6 +219,7 @@ describe('views/connect_another_device', () => {

describe('with an invalid entrypoint', () => {
beforeEach(() => {
viewEventStub = sinon.stub(GleanMetrics.cad, 'view');
sinon.stub(view, '_isSignedIn').callsFake(() => true);
sinon.spy(view, 'navigate');
windowMock.location.search = '?entrypoint=t3st0';
Expand All @@ -201,6 +231,14 @@ describe('views/connect_another_device', () => {
});
});

afterEach(() => {
viewEventStub.restore();
});

it('logs the view event', () => {
sinon.assert.calledOnce(viewEventStub);
});

it('shows the success message and does not redirect', () => {
assert.lengthOf(view.$(FXA_CONNECTED_SELECTOR), 1);
assert.isTrue(view.navigate.notCalled);
Expand Down
Loading

0 comments on commit 157de95

Please sign in to comment.