зеркало из https://github.com/mozilla/fxa-shared.git
feat(metrics): extract common code for emitting amplitude events
https://github.com/mozilla/fxa-shared/pull/20 r=shane-tomlinson
This commit is contained in:
Родитель
b377166887
Коммит
d8e9379e72
3
index.js
3
index.js
|
@ -3,6 +3,9 @@ module.exports = {
|
|||
email: {
|
||||
popularDomains: require('./email/popularDomains')
|
||||
},
|
||||
metrics: {
|
||||
amplitude: require('./metrics/amplitude')
|
||||
},
|
||||
l10n: {
|
||||
localizeTimestamp: require('./l10n/localizeTimestamp'),
|
||||
supportedLanguages: require('./l10n/supportedLanguages')
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
/* 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/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
let APP_VERSION;
|
||||
try {
|
||||
APP_VERSION = /^[0-9]+\.([0-9]+)\./.exec(require('../../../package.json').version)[1];
|
||||
} catch (err) {
|
||||
}
|
||||
|
||||
const DAY = 1000 * 60 * 60 * 24;
|
||||
const WEEK = DAY * 7;
|
||||
const FOUR_WEEKS = WEEK * 4;
|
||||
|
||||
const GROUPS = {
|
||||
activity: 'fxa_activity',
|
||||
connectDevice: 'fxa_connect_device',
|
||||
email: 'fxa_email',
|
||||
emailFirst: 'fxa_email_first',
|
||||
login: 'fxa_login',
|
||||
registration: 'fxa_reg',
|
||||
settings: 'fxa_pref',
|
||||
sms: 'fxa_sms'
|
||||
};
|
||||
|
||||
const CONNECT_DEVICE_FLOWS = {
|
||||
'app-store': 'store_buttons',
|
||||
install_from: 'store_buttons',
|
||||
signin_from: 'signin',
|
||||
sms: 'sms'
|
||||
};
|
||||
|
||||
const EMAIL_TYPES = {
|
||||
// Indexed by content server view name
|
||||
'complete-reset-password': 'reset_password',
|
||||
'complete-signin': 'login',
|
||||
'verify-email': 'registration',
|
||||
// Indexed by auth server template name
|
||||
newDeviceLoginEmail: 'login',
|
||||
passwordChangedEmail: 'change_password',
|
||||
passwordResetEmail: 'reset_password',
|
||||
passwordResetRequiredEmail: 'reset_password',
|
||||
postChangePrimaryEmail: 'change_email',
|
||||
postRemoveSecondaryEmail: 'secondary_email',
|
||||
postVerifyEmail: 'registration',
|
||||
postVerifySecondaryEmail: 'secondary_email',
|
||||
postConsumeRecoveryCodeEmail: '2fa',
|
||||
postNewRecoveryCodesEmail: '2fa',
|
||||
recoveryEmail: 'reset_password',
|
||||
unblockCode: 'unblock',
|
||||
verifyEmail: 'registration',
|
||||
verifyLoginEmail: 'login',
|
||||
verifyLoginCodeEmail: 'login',
|
||||
verifyPrimaryEmail: 'verify',
|
||||
verifySyncEmail: 'registration',
|
||||
verifySecondaryEmail: 'secondary_email'
|
||||
}
|
||||
|
||||
const NEWSLETTER_STATES = {
|
||||
optIn: 'subscribed',
|
||||
optOut: 'unsubscribed',
|
||||
};
|
||||
|
||||
const EVENT_PROPERTIES = {
|
||||
[GROUPS.activity]: NOP,
|
||||
[GROUPS.connectDevice]: mapConnectDeviceFlow,
|
||||
[GROUPS.email]: mapEmailType,
|
||||
[GROUPS.emailFirst]: NOP,
|
||||
[GROUPS.login]: NOP,
|
||||
[GROUPS.registration]: NOP,
|
||||
[GROUPS.settings]: mapDisconnectReason,
|
||||
[GROUPS.sms]: NOP
|
||||
}
|
||||
|
||||
function NOP () {}
|
||||
|
||||
function mapConnectDeviceFlow (eventType, eventCategory, eventTarget) {
|
||||
const connect_device_flow = CONNECT_DEVICE_FLOWS[eventCategory];
|
||||
|
||||
if (connect_device_flow) {
|
||||
const result = { connect_device_flow };
|
||||
|
||||
if (eventTarget) {
|
||||
result.connect_device_os = eventTarget;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function mapEmailType (eventType, eventCategory, eventTarget, data) {
|
||||
const email_type = EMAIL_TYPES[eventCategory];
|
||||
|
||||
if (email_type) {
|
||||
const result = { email_type, email_provider: data.emailDomain };
|
||||
|
||||
const { templateVersion } = data;
|
||||
if (templateVersion) {
|
||||
result.email_template = eventCategory;
|
||||
result.email_version = templateVersion;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function mapDisconnectReason (eventType, eventCategory) {
|
||||
if (eventType === 'disconnect_device' && eventCategory) {
|
||||
return { reason: eventCategory };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
GROUPS,
|
||||
|
||||
EMAIL_TYPES,
|
||||
|
||||
/**
|
||||
* Initialize an amplitude event mapper. You can read more about the amplitude
|
||||
* event structure here:
|
||||
*
|
||||
* https://amplitude.zendesk.com/hc/en-us/articles/204771828-HTTP-API
|
||||
*
|
||||
* And you can see our event taxonomy here:
|
||||
*
|
||||
* https://docs.google.com/spreadsheets/d/1G_8OJGOxeWXdGJ1Ugmykk33Zsl-qAQL05CONSeD4Uz4
|
||||
*
|
||||
* @param {Object} services An object of client-id:service-name mappings.
|
||||
*
|
||||
* @param {Object} events An object of name:definition event mappings, where
|
||||
* each defintion value is itself an object with `group`
|
||||
* and `event` string properties.
|
||||
*
|
||||
* @param {Map} fuzzyEvents A map of regex:definition event mappings. Each regex
|
||||
* key may include up to two capturing groups. The first
|
||||
* group is used as the `eventCategory` and the second is
|
||||
* used as the `eventTarget`. Again each definition value
|
||||
* is an object containing `group` and `event` properties
|
||||
* but here `group` can be a string or a function. If it's
|
||||
* a function, it will be passed the matched `eventCategory`
|
||||
* as its argument and should return the group string.
|
||||
*
|
||||
* @returns {Function} The mapper function.
|
||||
*/
|
||||
initialize (services, events, fuzzyEvents) {
|
||||
/**
|
||||
* Map from a source event and it's associated data to an amplitude event.
|
||||
*
|
||||
* @param {Object} event The source event to map from.
|
||||
*
|
||||
* @param {String} event.type The type of the event.
|
||||
*
|
||||
* @param {Number} event.time The time of the event in epoch-milliseconds.
|
||||
*
|
||||
* @param {Object} data All of the data associated with the event. This
|
||||
* parameter supports many properties that are too
|
||||
* numerous to list here, but may be discerned with
|
||||
* ease by perusing the code.
|
||||
*/
|
||||
return (event, data) => {
|
||||
if (! event || ! data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let eventType = event.type;
|
||||
let mapping = events[eventType];
|
||||
let eventCategory, eventTarget;
|
||||
|
||||
if (! mapping) {
|
||||
for (const [ key, value ] of fuzzyEvents.entries()) {
|
||||
const match = key.exec(eventType);
|
||||
if (match) {
|
||||
mapping = value;
|
||||
|
||||
if (match.length >= 2) {
|
||||
eventCategory = match[1];
|
||||
if (match.length === 3) {
|
||||
eventTarget = match[2];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mapping) {
|
||||
eventType = mapping.event;
|
||||
let eventGroup = mapping.group;
|
||||
if (typeof eventGroup === 'function') {
|
||||
eventGroup = eventGroup(eventCategory);
|
||||
if (! eventGroup) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return pruneUnsetValues({
|
||||
op: 'amplitudeEvent',
|
||||
event_type: `${eventGroup} - ${eventType}`,
|
||||
time: event.time,
|
||||
user_id: data.uid,
|
||||
device_id: data.deviceId,
|
||||
session_id: data.flowBeginTime,
|
||||
app_version: APP_VERSION,
|
||||
language: data.lang,
|
||||
country: data.country,
|
||||
region: data.region,
|
||||
os_name: data.os,
|
||||
os_version: data.osVersion,
|
||||
device_model: data.formFactor,
|
||||
event_properties: mapEventProperties(eventType, eventGroup, eventCategory, eventTarget, data),
|
||||
user_properties: mapUserProperties(eventGroup, eventCategory, data)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mapEventProperties (eventType, eventGroup, eventCategory, eventTarget, data) {
|
||||
const { serviceName, clientId } = getServiceNameAndClientId(data);
|
||||
|
||||
return Object.assign(pruneUnsetValues({
|
||||
service: serviceName,
|
||||
oauth_client_id: clientId
|
||||
}), EVENT_PROPERTIES[eventGroup](eventType, eventCategory, eventTarget, data))
|
||||
}
|
||||
|
||||
function getServiceNameAndClientId (data) {
|
||||
let serviceName, clientId;
|
||||
|
||||
const { service } = data;
|
||||
if (service && service !== 'content-server') {
|
||||
if (service === 'sync') {
|
||||
serviceName = service
|
||||
} else {
|
||||
serviceName = services[service] || 'undefined_oauth'
|
||||
clientId = service
|
||||
}
|
||||
}
|
||||
|
||||
return { serviceName, clientId }
|
||||
}
|
||||
|
||||
function mapUserProperties (eventGroup, eventCategory, data) {
|
||||
return Object.assign(
|
||||
pruneUnsetValues({
|
||||
entrypoint: data.entrypoint,
|
||||
flow_id: data.flowId,
|
||||
ua_browser: data.browser,
|
||||
ua_version: data.browserVersion,
|
||||
utm_campaign: data.utm_campaign,
|
||||
utm_content: data.utm_content,
|
||||
utm_medium: data.utm_medium,
|
||||
utm_source: data.utm_source,
|
||||
utm_term: data.utm_term
|
||||
}),
|
||||
mapAppendProperties(data),
|
||||
mapSyncDevices(data),
|
||||
mapNewsletterState(eventCategory, data)
|
||||
);
|
||||
}
|
||||
|
||||
function mapAppendProperties (data) {
|
||||
const servicesUsed = mapServicesUsed(data);
|
||||
const experiments = mapExperiments(data);
|
||||
|
||||
if (servicesUsed || experiments) {
|
||||
return {
|
||||
'$append': Object.assign({}, servicesUsed, experiments)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function mapServicesUsed (data) {
|
||||
const { serviceName } = getServiceNameAndClientId(data);
|
||||
|
||||
if (serviceName) {
|
||||
return {
|
||||
fxa_services_used: serviceName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function pruneUnsetValues (data) {
|
||||
const result = {};
|
||||
|
||||
Object.keys(data).forEach(key => {
|
||||
const value = data[key];
|
||||
|
||||
if (value || value === false) {
|
||||
result[key] = value;
|
||||
}
|
||||
})
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function mapExperiments (data) {
|
||||
const { experiments } = data;
|
||||
|
||||
if (Array.isArray(experiments) && experiments.length > 0) {
|
||||
return {
|
||||
experiments: experiments.map(e => `${toSnakeCase(e.choice)}_${toSnakeCase(e.group)}`)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function toSnakeCase (string) {
|
||||
return string.replace(/([a-z])([A-Z])/g, (s, c1, c2) => `${c1}_${c2.toLowerCase()}`)
|
||||
.replace(/([A-Z])/g, c => c.toLowerCase())
|
||||
.replace(/\./g, '_')
|
||||
.replace(/-/g, '_');
|
||||
}
|
||||
|
||||
function mapSyncDevices (data) {
|
||||
const { devices } = data;
|
||||
|
||||
if (Array.isArray(devices)) {
|
||||
return {
|
||||
sync_device_count: devices.length,
|
||||
sync_active_devices_day: countDevices(devices, DAY),
|
||||
sync_active_devices_week: countDevices(devices, WEEK),
|
||||
sync_active_devices_month: countDevices(devices, FOUR_WEEKS)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function countDevices (devices, period) {
|
||||
return devices.filter(device => device.lastAccessTime >= Date.now() - period).length
|
||||
}
|
||||
|
||||
function mapNewsletterState (eventCategory, data) {
|
||||
let newsletter_state = NEWSLETTER_STATES[eventCategory];
|
||||
|
||||
if (! newsletter_state) {
|
||||
const { marketingOptIn } = data;
|
||||
|
||||
if (marketingOptIn === true || marketingOptIn === false) {
|
||||
newsletter_state = marketingOptIn ? 'subscribed' : 'unsubscribed';
|
||||
}
|
||||
}
|
||||
|
||||
if (newsletter_state) {
|
||||
return { newsletter_state };
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@ describe('index:', () => {
|
|||
});
|
||||
|
||||
it('exports the correct interface', () => {
|
||||
assert.isArray(index.email.popularDomains);
|
||||
assert.isObject(index.metrics.amplitude);
|
||||
assert.isArray(index.l10n.supportedLanguages);
|
||||
assert.isFunction(index.l10n.localizeTimestamp);
|
||||
})
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
/* 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/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const { assert } = require('chai');
|
||||
|
||||
const DAY = 1000 * 60 * 60 * 24;
|
||||
const WEEK = DAY * 7;
|
||||
const FOUR_WEEKS = WEEK * 4;
|
||||
|
||||
describe('metrics/amplitude:', () => {
|
||||
let amplitude;
|
||||
|
||||
before(() => {
|
||||
amplitude = require('../../metrics/amplitude');
|
||||
});
|
||||
|
||||
it('exports the event groups', () => {
|
||||
assert.isObject(amplitude.GROUPS);
|
||||
assert.isString(amplitude.GROUPS.activity);
|
||||
assert.isString(amplitude.GROUPS.connectDevice);
|
||||
assert.isString(amplitude.GROUPS.email);
|
||||
assert.isString(amplitude.GROUPS.emailFirst);
|
||||
assert.isString(amplitude.GROUPS.login);
|
||||
assert.isString(amplitude.GROUPS.registration);
|
||||
assert.isString(amplitude.GROUPS.settings);
|
||||
assert.isString(amplitude.GROUPS.sms);
|
||||
});
|
||||
|
||||
it('exports the email types', () => {
|
||||
assert.isObject(amplitude.EMAIL_TYPES);
|
||||
assert.isString(amplitude.EMAIL_TYPES['complete-reset-password']);
|
||||
assert.isString(amplitude.EMAIL_TYPES['complete-signin']);
|
||||
assert.isString(amplitude.EMAIL_TYPES['verify-email']);
|
||||
assert.isString(amplitude.EMAIL_TYPES.newDeviceLoginEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.passwordChangedEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.passwordResetEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.passwordResetRequiredEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.postChangePrimaryEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.postRemoveSecondaryEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.postVerifyEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.postVerifySecondaryEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.postConsumeRecoveryCodeEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.postNewRecoveryCodesEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.recoveryEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.unblockCode);
|
||||
assert.isString(amplitude.EMAIL_TYPES.verifyEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.verifyLoginEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.verifyLoginCodeEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.verifyPrimaryEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.verifySyncEmail);
|
||||
assert.isString(amplitude.EMAIL_TYPES.verifySecondaryEmail);
|
||||
});
|
||||
|
||||
it('exports an initialize method', () => {
|
||||
assert.isFunction(amplitude.initialize);
|
||||
assert.lengthOf(amplitude.initialize, 3);
|
||||
});
|
||||
|
||||
describe('initialize:', () => {
|
||||
let transform;
|
||||
|
||||
before(() => {
|
||||
transform = amplitude.initialize({
|
||||
foo: 'bar',
|
||||
baz: 'qux'
|
||||
}, {
|
||||
sourceEvent1: {
|
||||
group: amplitude.GROUPS.activity,
|
||||
event: 'wibble'
|
||||
},
|
||||
sourceEvent2: {
|
||||
group: amplitude.GROUPS.sms,
|
||||
event: 'blee'
|
||||
}
|
||||
}, new Map([
|
||||
[ /3/, {
|
||||
group: amplitude.GROUPS.login,
|
||||
event: 'targetEvent3'
|
||||
} ],
|
||||
[ /^(wibble)\.(blee)/, {
|
||||
group: eventCategory => eventCategory === 'wibble' ? amplitude.GROUPS.registration : null,
|
||||
event: 'targetEvent4'
|
||||
} ],
|
||||
[ /(sms)\.(\w+)/, {
|
||||
group: amplitude.GROUPS.connectDevice,
|
||||
event: 'cadEvent'
|
||||
} ],
|
||||
[ /(verifySecondaryEmail)\.(\w+)/, {
|
||||
group: amplitude.GROUPS.email,
|
||||
event: 'emailEvent'
|
||||
} ],
|
||||
[ /disconnect\.(\w+)\.(\w+)/, {
|
||||
group: amplitude.GROUPS.settings,
|
||||
event: 'disconnect_device'
|
||||
} ],
|
||||
[ /newsletter\.(\w+)\.(\w+)/, {
|
||||
group: amplitude.GROUPS.settings,
|
||||
event: 'newsletterEvent'
|
||||
} ]
|
||||
]));
|
||||
});
|
||||
|
||||
describe('transform a simple event:', () => {
|
||||
let now, result;
|
||||
|
||||
before(() => {
|
||||
now = Date.now();
|
||||
result = transform({
|
||||
type: 'sourceEvent2',
|
||||
time: 42
|
||||
}, {
|
||||
browser: 'a',
|
||||
browserVersion: 'b',
|
||||
country: 'c',
|
||||
deviceId: 'd',
|
||||
devices: [
|
||||
{ lastAccessTime: now - DAY + 1000 },
|
||||
{ lastAccessTime: now - DAY - 1 },
|
||||
{ lastAccessTime: now - WEEK + 1000 },
|
||||
{ lastAccessTime: now - WEEK - 1 },
|
||||
{ lastAccessTime: now - FOUR_WEEKS + 1000 },
|
||||
{ lastAccessTime: now - FOUR_WEEKS - 1 }
|
||||
],
|
||||
emailDomain: 'e',
|
||||
entrypoint: 'f',
|
||||
experiments: [
|
||||
{ choice: 'g', group: 'h' },
|
||||
{ choice: 'iI', group: 'jJ-J' }
|
||||
],
|
||||
flowBeginTime: 'k',
|
||||
flowId: 'l',
|
||||
formFactor: 'm',
|
||||
lang: 'n',
|
||||
marketingOptIn: 'o',
|
||||
os: 'p',
|
||||
osVersion: 'q',
|
||||
region: 'r',
|
||||
service: 'baz',
|
||||
templateVersion: 's',
|
||||
uid: 't',
|
||||
utm_campaign: 'u',
|
||||
utm_content: 'v',
|
||||
utm_medium: 'w',
|
||||
utm_source: 'x',
|
||||
utm_term: 'y'
|
||||
});
|
||||
})
|
||||
|
||||
it('returned the correct result', () => {
|
||||
assert.deepEqual(result, {
|
||||
country: 'c',
|
||||
device_id: 'd',
|
||||
device_model: 'm',
|
||||
event_properties: {
|
||||
oauth_client_id: 'baz',
|
||||
service: 'qux'
|
||||
},
|
||||
event_type: 'fxa_sms - blee',
|
||||
language: 'n',
|
||||
op: 'amplitudeEvent',
|
||||
os_name: 'p',
|
||||
os_version: 'q',
|
||||
region: 'r',
|
||||
session_id: 'k',
|
||||
time: 42,
|
||||
user_id: 't',
|
||||
user_properties: {
|
||||
'$append': {
|
||||
experiments: [ 'g_h', 'i_i_j_j_j' ],
|
||||
fxa_services_used: 'qux'
|
||||
},
|
||||
entrypoint: 'f',
|
||||
flow_id: 'l',
|
||||
sync_active_devices_day: 1,
|
||||
sync_active_devices_month: 5,
|
||||
sync_active_devices_week: 3,
|
||||
sync_device_count: 6,
|
||||
ua_browser: 'a',
|
||||
ua_version: 'b',
|
||||
utm_campaign: 'u',
|
||||
utm_content: 'v',
|
||||
utm_medium: 'w',
|
||||
utm_source: 'x',
|
||||
utm_term: 'y'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform a fuzzy event:', () => {
|
||||
let result;
|
||||
|
||||
before(() => {
|
||||
result = transform({
|
||||
type: 'sourceEvent3',
|
||||
time: 1
|
||||
}, {
|
||||
deviceId: 'a',
|
||||
flowBeginTime: 'b',
|
||||
flowId: 'c',
|
||||
uid: 'd'
|
||||
});
|
||||
})
|
||||
|
||||
it('returned the correct result', () => {
|
||||
assert.deepEqual(result, {
|
||||
device_id: 'a',
|
||||
event_properties: {},
|
||||
event_type: 'fxa_login - targetEvent3',
|
||||
op: 'amplitudeEvent',
|
||||
session_id: 'b',
|
||||
time: 1,
|
||||
user_id: 'd',
|
||||
user_properties: {
|
||||
flow_id: 'c'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform a fuzzy event with a group function:', () => {
|
||||
let result;
|
||||
|
||||
before(() => {
|
||||
result = transform({ type: 'wibble.blee' }, {});
|
||||
})
|
||||
|
||||
it('returned the correct event type', () => {
|
||||
assert.equal(result.event_type, 'fxa_reg - targetEvent4');
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform an event with connect-another-device properties:', () => {
|
||||
let result;
|
||||
|
||||
before(() => {
|
||||
result = transform({ type: 'sms.ios' }, {});
|
||||
})
|
||||
|
||||
it('returned the correct event data', () => {
|
||||
assert.equal(result.event_type, 'fxa_connect_device - cadEvent');
|
||||
assert.deepEqual(result.event_properties, {
|
||||
connect_device_flow: 'sms',
|
||||
connect_device_os: 'ios'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform an event with email properties:', () => {
|
||||
let result;
|
||||
|
||||
before(() => {
|
||||
result = transform({ type: 'verifySecondaryEmail.wibble' }, {
|
||||
emailDomain: 'foo',
|
||||
templateVersion: 'bar'
|
||||
});
|
||||
})
|
||||
|
||||
it('returned the correct event data', () => {
|
||||
assert.equal(result.event_type, 'fxa_email - emailEvent');
|
||||
assert.deepEqual(result.event_properties, {
|
||||
email_provider: 'foo',
|
||||
email_template: 'verifySecondaryEmail',
|
||||
email_type: 'secondary_email',
|
||||
email_version: 'bar'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform an event with disconnect properties:', () => {
|
||||
let result;
|
||||
|
||||
before(() => {
|
||||
result = transform({ type: 'disconnect.wibble.blee' }, {});
|
||||
})
|
||||
|
||||
it('returned the correct event data', () => {
|
||||
assert.equal(result.event_type, 'fxa_pref - disconnect_device');
|
||||
assert.deepEqual(result.event_properties, { reason: 'wibble' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform an event with newsletter properties:', () => {
|
||||
let result;
|
||||
|
||||
before(() => {
|
||||
result = transform({ type: 'newsletter.optIn.wibble' }, {});
|
||||
})
|
||||
|
||||
it('returned the correct event data', () => {
|
||||
assert.equal(result.event_type, 'fxa_pref - newsletterEvent');
|
||||
assert.deepEqual(result.user_properties, { newsletter_state: 'subscribed' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform an event with undefined service:', () => {
|
||||
let result;
|
||||
|
||||
before(() => {
|
||||
result = transform({ type: 'wibble.blee' }, { service: 'gribble' });
|
||||
})
|
||||
|
||||
it('returned the correct event data', () => {
|
||||
assert.deepEqual(result.event_properties, {
|
||||
oauth_client_id: 'gribble',
|
||||
service: 'undefined_oauth'
|
||||
});
|
||||
assert.deepEqual(result.user_properties, { '$append': { fxa_services_used: 'undefined_oauth' } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform an event with service=sync:', () => {
|
||||
let result;
|
||||
|
||||
before(() => {
|
||||
result = transform({ type: 'wibble.blee' }, { service: 'sync' });
|
||||
})
|
||||
|
||||
it('returned the correct event data', () => {
|
||||
assert.deepEqual(result.event_properties, { service: 'sync' });
|
||||
assert.deepEqual(result.user_properties, { '$append': { fxa_services_used: 'sync' } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform an event with service=content-server:', () => {
|
||||
let result;
|
||||
|
||||
before(() => {
|
||||
result = transform({ type: 'wibble.blee' }, { service: 'content-server' });
|
||||
})
|
||||
|
||||
it('returned the correct event data', () => {
|
||||
assert.deepEqual(result.event_properties, {});
|
||||
assert.deepEqual(result.user_properties, {});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Загрузка…
Ссылка в новой задаче