refactor(email-opt-in): Extract the email-opt-in frontend logic for re-use. (#5275) r=@vbudhram
Extract the email-opt-in login to a mixin for use in the email-first flow.
This commit is contained in:
Родитель
59f014ed01
Коммит
8fafad7f41
|
@ -0,0 +1,43 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mixin that provides email opt-in functionality.
|
||||||
|
*
|
||||||
|
* Sets `isEmailOptInVisible` in the context.
|
||||||
|
* Provides `hasOptedInToEmail` to query whether the user has
|
||||||
|
* opted-in.
|
||||||
|
*/
|
||||||
|
define(function (require, exports, module) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
initialize (options = {}) {
|
||||||
|
this._experimentGroupingRules = options.experimentGroupingRules;
|
||||||
|
},
|
||||||
|
|
||||||
|
setInitialContext (context) {
|
||||||
|
context.set('isEmailOptInVisible', this._isEmailOptInEnabled());
|
||||||
|
},
|
||||||
|
|
||||||
|
afterRender () {
|
||||||
|
this.logViewEvent(`email-optin.visible.${String(this._isEmailOptInEnabled())}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
_isEmailOptInEnabled () {
|
||||||
|
return !! this._experimentGroupingRules.choose('communicationPrefsVisible', {
|
||||||
|
lang: this.navigator.language
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query whether user has opted-in to marketing email.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
hasOptedInToMarketingEmail () {
|
||||||
|
return !! this.$('.marketing-email-optin').is(':checked');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
|
@ -10,6 +10,7 @@ define(function (require, exports, module) {
|
||||||
const CheckboxMixin = require('views/mixins/checkbox-mixin');
|
const CheckboxMixin = require('views/mixins/checkbox-mixin');
|
||||||
const Cocktail = require('cocktail');
|
const Cocktail = require('cocktail');
|
||||||
const CoppaAgeInput = require('views/coppa/coppa-age-input');
|
const CoppaAgeInput = require('views/coppa/coppa-age-input');
|
||||||
|
const EmailOptInMixin = require('views/mixins/email-opt-in-mixin');
|
||||||
const ExperimentMixin = require('views/mixins/experiment-mixin');
|
const ExperimentMixin = require('views/mixins/experiment-mixin');
|
||||||
const FlowBeginMixin = require('views/mixins/flow-begin-mixin');
|
const FlowBeginMixin = require('views/mixins/flow-begin-mixin');
|
||||||
const FormPrefillMixin = require('views/mixins/form-prefill-mixin');
|
const FormPrefillMixin = require('views/mixins/form-prefill-mixin');
|
||||||
|
@ -91,9 +92,6 @@ define(function (require, exports, module) {
|
||||||
},
|
},
|
||||||
|
|
||||||
afterRender () {
|
afterRender () {
|
||||||
this.logViewEvent('email-optin.visible.' +
|
|
||||||
String(this._isEmailOptInEnabled()));
|
|
||||||
|
|
||||||
return this._createCoppaView()
|
return this._createCoppaView()
|
||||||
.then(() => FormView.prototype.afterRender.call(this));
|
.then(() => FormView.prototype.afterRender.call(this));
|
||||||
},
|
},
|
||||||
|
@ -150,7 +148,6 @@ define(function (require, exports, module) {
|
||||||
forceEmail: forceEmail,
|
forceEmail: forceEmail,
|
||||||
isAmoMigration: this.isAmoMigration(),
|
isAmoMigration: this.isAmoMigration(),
|
||||||
isCustomizeSyncChecked: relier.isCustomizeSyncChecked(),
|
isCustomizeSyncChecked: relier.isCustomizeSyncChecked(),
|
||||||
isEmailOptInVisible: this._isEmailOptInEnabled(),
|
|
||||||
isSignInEnabled: ! forceEmail,
|
isSignInEnabled: ! forceEmail,
|
||||||
isSync: isSync,
|
isSync: isSync,
|
||||||
isSyncMigration: this.isSyncMigration(),
|
isSyncMigration: this.isSyncMigration(),
|
||||||
|
@ -334,7 +331,7 @@ define(function (require, exports, module) {
|
||||||
var account = this.user.initAccount({
|
var account = this.user.initAccount({
|
||||||
customizeSync: this.$('.customize-sync').is(':checked'),
|
customizeSync: this.$('.customize-sync').is(':checked'),
|
||||||
email: this.getElementValue('.email'),
|
email: this.getElementValue('.email'),
|
||||||
needsOptedInToMarketingEmail: this.$('.marketing-email-optin').is(':checked')
|
needsOptedInToMarketingEmail: this.hasOptedInToMarketingEmail()
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.relier.isSync()) {
|
if (this.relier.isSync()) {
|
||||||
|
@ -348,12 +345,6 @@ define(function (require, exports, module) {
|
||||||
_suggestSignIn (err) {
|
_suggestSignIn (err) {
|
||||||
err.forceMessage = t('Account already exists. <a href="/signin">Sign in</a>');
|
err.forceMessage = t('Account already exists. <a href="/signin">Sign in</a>');
|
||||||
return this.unsafeDisplayError(err);
|
return this.unsafeDisplayError(err);
|
||||||
},
|
|
||||||
|
|
||||||
_isEmailOptInEnabled () {
|
|
||||||
return !! this._experimentGroupingRules.choose('communicationPrefsVisible', {
|
|
||||||
lang: this.navigator.language
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
ENTRYPOINT: 'fxa:signup'
|
ENTRYPOINT: 'fxa:signup'
|
||||||
|
@ -363,6 +354,7 @@ define(function (require, exports, module) {
|
||||||
View,
|
View,
|
||||||
AccountResetMixin,
|
AccountResetMixin,
|
||||||
CheckboxMixin,
|
CheckboxMixin,
|
||||||
|
EmailOptInMixin,
|
||||||
ExperimentMixin,
|
ExperimentMixin,
|
||||||
FlowBeginMixin,
|
FlowBeginMixin,
|
||||||
FormPrefillMixin,
|
FormPrefillMixin,
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
define(function (require, exports, module) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { assert } = require('chai');
|
||||||
|
const Cocktail = require('cocktail');
|
||||||
|
const EmailOptInMixin = require('views/mixins/email-opt-in-mixin');
|
||||||
|
const sinon = require('sinon');
|
||||||
|
const BaseView = require('views/base');
|
||||||
|
|
||||||
|
const View = BaseView.extend({
|
||||||
|
template: () => `
|
||||||
|
<input type="checkbox" class="marketing-email-optin" checked>Opt-in to email</input>
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
Cocktail.mixin(
|
||||||
|
View,
|
||||||
|
EmailOptInMixin
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('views/mixins/email-opt-in-mixin', function () {
|
||||||
|
let experimentGroupingRules;
|
||||||
|
let view;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
experimentGroupingRules = {
|
||||||
|
choose: () => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
view = new View({
|
||||||
|
experimentGroupingRules
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exports correct interface', function () {
|
||||||
|
assert.isObject(EmailOptInMixin);
|
||||||
|
assert.lengthOf(Object.keys(EmailOptInMixin), 5);
|
||||||
|
assert.isFunction(EmailOptInMixin.hasOptedInToMarketingEmail);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('render', () => {
|
||||||
|
let communicationPrefsVisible;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
sinon.stub(experimentGroupingRules, 'choose', () => communicationPrefsVisible);
|
||||||
|
sinon.spy(view, 'logViewEvent');
|
||||||
|
sinon.spy(view, 'template');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('enabled for user', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
communicationPrefsVisible = true;
|
||||||
|
return view.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets `isEmailOptInVisible=true, logs correctly`', () => {
|
||||||
|
assert.isTrue(view.template.calledOnce);
|
||||||
|
const templateArgs = view.template.args[0][0];
|
||||||
|
assert.isTrue(templateArgs.isEmailOptInVisible);
|
||||||
|
|
||||||
|
assert.isTrue(view.logViewEvent.calledOnce);
|
||||||
|
assert.isTrue(view.logViewEvent.calledWith('email-optin.visible.true'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('disabled for user', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
communicationPrefsVisible = false;
|
||||||
|
return view.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets `isEmailOptInVisible=false, logs correctly`', () => {
|
||||||
|
assert.isTrue(view.template.calledOnce);
|
||||||
|
const templateArgs = view.template.args[0][0];
|
||||||
|
assert.isFalse(templateArgs.isEmailOptInVisible);
|
||||||
|
|
||||||
|
assert.isTrue(view.logViewEvent.calledOnce);
|
||||||
|
assert.isTrue(view.logViewEvent.calledWith('email-optin.visible.false'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasOptedInToMarketingEmail', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return view.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns `true` if the checkbox is checked', () => {
|
||||||
|
view.$('.marketing-email-optin').attr('checked', 'checked');
|
||||||
|
assert.isTrue(view.hasOptedInToMarketingEmail());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns `false` if the checkbox is unchecked', () => {
|
||||||
|
view.$('.marketing-email-optin').removeAttr('checked');
|
||||||
|
assert.isFalse(view.hasOptedInToMarketingEmail());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -850,26 +850,25 @@ define(function (require, exports, module) {
|
||||||
|
|
||||||
describe('signup succeeds', function () {
|
describe('signup succeeds', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
sinon.stub(view, 'signUp', function () {
|
sinon.stub(view, 'signUp', () => p());
|
||||||
return p();
|
sinon.stub(view, 'hasOptedInToMarketingEmail', () => true);
|
||||||
});
|
|
||||||
|
|
||||||
return view.submit();
|
return view.submit();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not call view.signIn', function () {
|
it('calls view.signUp correctly, does not display any errors', function () {
|
||||||
assert.isFalse(view.signIn.called);
|
assert.isFalse(view.signIn.called);
|
||||||
});
|
|
||||||
|
|
||||||
it('calls view.signUp correctly', function () {
|
|
||||||
assert.equal(view.signUp.callCount, 1);
|
assert.equal(view.signUp.callCount, 1);
|
||||||
|
|
||||||
var args = view.signUp.args[0];
|
var args = view.signUp.args[0];
|
||||||
assert.instanceOf(args[0], Account);
|
const account = args[0];
|
||||||
assert.equal(args[1], 'password');
|
assert.instanceOf(account, Account);
|
||||||
});
|
assert.equal(account.get('email'), email);
|
||||||
|
assert.isTrue(account.get('needsOptedInToMarketingEmail'));
|
||||||
|
assert.isFalse(account.get('customizeSync'));
|
||||||
|
|
||||||
|
assert.equal(args[1], 'password');
|
||||||
|
|
||||||
it('does not display any errors', function () {
|
|
||||||
assert.isFalse(view.displayError.called);
|
assert.isFalse(view.displayError.called);
|
||||||
assert.isFalse(view.unsafeDisplayError.called);
|
assert.isFalse(view.unsafeDisplayError.called);
|
||||||
});
|
});
|
||||||
|
|
|
@ -145,6 +145,7 @@ function (Translator, Session) {
|
||||||
'../tests/spec/views/mixins/back-mixin',
|
'../tests/spec/views/mixins/back-mixin',
|
||||||
'../tests/spec/views/mixins/checkbox-mixin',
|
'../tests/spec/views/mixins/checkbox-mixin',
|
||||||
'../tests/spec/views/mixins/connect-another-device-mixin',
|
'../tests/spec/views/mixins/connect-another-device-mixin',
|
||||||
|
'../tests/spec/views/mixins/email-opt-in-mixin',
|
||||||
'../tests/spec/views/mixins/experiment-mixin',
|
'../tests/spec/views/mixins/experiment-mixin',
|
||||||
'../tests/spec/views/mixins/external-links-mixin',
|
'../tests/spec/views/mixins/external-links-mixin',
|
||||||
'../tests/spec/views/mixins/floating-placeholder-mixin',
|
'../tests/spec/views/mixins/floating-placeholder-mixin',
|
||||||
|
|
Загрузка…
Ссылка в новой задаче