diff --git a/packages/fxa-content-server/app/scripts/lib/experiments/grouping-rules/TEMPLATE.js b/packages/fxa-content-server/app/scripts/lib/experiments/grouping-rules/TEMPLATE.js deleted file mode 100644 index 1d7b28fd5df..00000000000 --- a/packages/fxa-content-server/app/scripts/lib/experiments/grouping-rules/TEMPLATE.js +++ /dev/null @@ -1,36 +0,0 @@ -/* 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/. */ - -/** - * Template file for new grouping rules. To use: - * - * 1. Copy TEMPLATE.js to a new grouping rule file. - * 2. Change `ChangeMeGroupingRule` class name to another name. - * 3. Change `this.name` from `CHANGE_ME` in the constructor. - * 4. Fill in the `choose` function. - * 5. Include the new rule file in index.js. - * 6. Access in views via `this.experiments.choose('name from 3')` - * or `this.isInExperimentGroup('name from 3', 'group name')`. - */ -'use strict'; - -const BaseGroupingRule = require('./base'); - -module.exports = class ChangeMeGroupingRule extends BaseGroupingRule { - constructor() { - super(); - this.name = 'CHANGE_ME'; - } - - /** - * Use `subject` data to make a choice. - * - * @param {Object} subject data used to decide - * @returns {Any} - */ - choose(subject) { - // Fill in decision logic here. - return true; - } -}; diff --git a/packages/fxa-content-server/app/scripts/lib/key-stretch-experiment-mixin.js b/packages/fxa-content-server/app/scripts/lib/key-stretch-experiment-mixin.js deleted file mode 100644 index 63fb089853b..00000000000 --- a/packages/fxa-content-server/app/scripts/lib/key-stretch-experiment-mixin.js +++ /dev/null @@ -1,14 +0,0 @@ -/* 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/. */ - - import ExperimentMixin from '../views/mixins/experiment-mixin'; - - export default { - dependsOn: [ExperimentMixin], - - isInKeyStretchExperiment() { - const experimentGroup = this.getAndReportExperimentGroup('keyStretchV2'); - return experimentGroup === 'v2'; - }, - }; diff --git a/packages/fxa-content-server/app/scripts/models/attached-clients.js b/packages/fxa-content-server/app/scripts/models/attached-clients.js deleted file mode 100644 index 4dc4a2bb51b..00000000000 --- a/packages/fxa-content-server/app/scripts/models/attached-clients.js +++ /dev/null @@ -1,77 +0,0 @@ -/* 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/. */ - -/** - * Attached clients are OAuth apps and devices - * - * It sorts items in order that is defined in FxA-89 feature description. - */ -import Backbone from 'backbone'; -import Constants from '../lib/constants'; -import AttachedClient from './attached-client'; - -var AttachedClients = Backbone.Collection.extend({ - model: AttachedClient, - - fetchClients(user) { - const account = user.getSignedInAccount(); - return user.fetchAccountAttachedClients(account).then((clients) => { - this.reset(clients); - }); - }, - - comparator(a, b) { - // 1. the current session (and any associated device record) is first. - // 2. devices of the same type are grouped together as defined in clientOrder. - // 3. those with lastAccessTime are sorted in descending order - // 4. the rest sorted in alphabetical order. - if (a.get('isCurrentSession')) { - return -1; - } - if (b.get('isCurrentSession')) { - return 1; - } - - var clientOrder = { - [Constants.CLIENT_TYPE_DEVICE]: 0, - [Constants.CLIENT_TYPE_OAUTH_APP]: 1, - [Constants.CLIENT_TYPE_WEB_SESSION]: 2, - }; - - var aClientType = a.get('clientType'); - var bClientType = b.get('clientType'); - if (aClientType !== bClientType) { - return clientOrder[aClientType] - clientOrder[bClientType]; - } - - // check lastAccessTime. If one has an access time and the other does - // not, the one with the access time is automatically higher in the - // list. If both have access times, sort in descending order, unless - // access times are the same, then sort alphabetically. - var aLastAccessTime = a.get('lastAccessTime'); - var bLastAccessTime = b.get('lastAccessTime'); - - if (aLastAccessTime !== bLastAccessTime) { - return bLastAccessTime - aLastAccessTime; - } else if (aLastAccessTime && !bLastAccessTime) { - return -1; - } else if (!aLastAccessTime && bLastAccessTime) { - return 1; - } - - // if access time is the same, sort alphabetically - var aName = (a.get('name') || '').trim().toLowerCase(); - var bName = (b.get('name') || '').trim().toLowerCase(); - - if (aName < bName) { - return -1; - } else if (aName > bName) { - return 1; - } - - return 0; - }, -}); - -export default AttachedClients; diff --git a/packages/fxa-content-server/app/scripts/models/cropper-image.js b/packages/fxa-content-server/app/scripts/models/cropper-image.js deleted file mode 100644 index 5788780d4e4..00000000000 --- a/packages/fxa-content-server/app/scripts/models/cropper-image.js +++ /dev/null @@ -1,40 +0,0 @@ -/* 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/. */ - -// This model abstracts images used by the cropper view - -import AuthErrors from '../lib/auth-errors'; -import Backbone from 'backbone'; -import Constants from '../lib/constants'; - -// a 1x1 jpeg -var jpegSrc = - 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsM' + - 'DAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQE' + - 'AAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISU' + - 'pTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAA' + - 'wEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5' + - 'OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn' + - '6/9oADAMBAAIRAxEAPwD9/KKKKAP/2Q=='; - -var CropperImage = Backbone.Model.extend({ - defaults: { - height: 1, - src: jpegSrc, - type: 'image/jpeg', - width: 1, - }, - - initialize(options) { - if ( - options && - (options.width < Constants.PROFILE_IMAGE_MIN_HEIGHT || - options.height < Constants.PROFILE_IMAGE_MIN_WIDTH) - ) { - throw AuthErrors.toError('INVALID_IMAGE_SIZE'); - } - }, -}); - -export default CropperImage; diff --git a/packages/fxa-content-server/app/scripts/models/email.js b/packages/fxa-content-server/app/scripts/models/email.js deleted file mode 100644 index 6d87356e31b..00000000000 --- a/packages/fxa-content-server/app/scripts/models/email.js +++ /dev/null @@ -1,19 +0,0 @@ -/* 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/. */ - -/** - * Email information - */ - -import Backbone from 'backbone'; - -var Email = Backbone.Model.extend({ - defaults: { - email: null, - isPrimary: false, - verified: false, - }, -}); - -export default Email; diff --git a/packages/fxa-content-server/app/scripts/models/security-events.js b/packages/fxa-content-server/app/scripts/models/security-events.js deleted file mode 100644 index 928475cbfec..00000000000 --- a/packages/fxa-content-server/app/scripts/models/security-events.js +++ /dev/null @@ -1,19 +0,0 @@ -/* 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/. */ - -/** - * Security events information - */ - -import Backbone from 'backbone'; - -const SecurityEvent = Backbone.Model.extend({ - defaults: { - name: null, - verified: false, - createdAt: null, - }, -}); - -export default SecurityEvent; diff --git a/packages/fxa-content-server/app/scripts/templates/partial/email-autocomplete-domains.mustache b/packages/fxa-content-server/app/scripts/templates/partial/email-autocomplete-domains.mustache deleted file mode 100644 index 32ca3e18e59..00000000000 --- a/packages/fxa-content-server/app/scripts/templates/partial/email-autocomplete-domains.mustache +++ /dev/null @@ -1,7 +0,0 @@ - - - {{#options}} - - diff --git a/packages/fxa-content-server/app/scripts/templates/partial/settings-header.mustache b/packages/fxa-content-server/app/scripts/templates/partial/settings-header.mustache deleted file mode 100644 index 32dd1fe7655..00000000000 --- a/packages/fxa-content-server/app/scripts/templates/partial/settings-header.mustache +++ /dev/null @@ -1,8 +0,0 @@ -{{#displayName}} -

{{displayName}}

-

{{email}}

-{{/displayName}} -{{^displayName}} -

{{email}}

-

-{{/displayName}} diff --git a/packages/fxa-content-server/app/scripts/templates/pp.mustache b/packages/fxa-content-server/app/scripts/templates/pp.mustache deleted file mode 100644 index 56217088c09..00000000000 --- a/packages/fxa-content-server/app/scripts/templates/pp.mustache +++ /dev/null @@ -1,18 +0,0 @@ -
- - - -
diff --git a/packages/fxa-content-server/app/scripts/templates/security_events.mustache b/packages/fxa-content-server/app/scripts/templates/security_events.mustache deleted file mode 100644 index 6285d6a89db..00000000000 --- a/packages/fxa-content-server/app/scripts/templates/security_events.mustache +++ /dev/null @@ -1,26 +0,0 @@ -

{{#t}}Recent Activity{{/t}}

- -

{{#t}}Security Events{{/t}}

- - - - - - - - - - {{#securityEvents}} - - - - - - {{/securityEvents}} - -
{{#t}}Event Name{{/t}}{{#t}}Confirmed{{/t}}{{#t}}Created At{{/t}}
{{name}}{{verified}}{{createdAt}}
- -
- - - diff --git a/packages/fxa-content-server/app/scripts/templates/settings/account_recovery/confirm_password.mustache b/packages/fxa-content-server/app/scripts/templates/settings/account_recovery/confirm_password.mustache deleted file mode 100644 index c044ab54189..00000000000 --- a/packages/fxa-content-server/app/scripts/templates/settings/account_recovery/confirm_password.mustache +++ /dev/null @@ -1,24 +0,0 @@ -
- -
diff --git a/packages/fxa-content-server/app/scripts/templates/settings/account_recovery/recovery_key_print_template.mustache b/packages/fxa-content-server/app/scripts/templates/settings/account_recovery/recovery_key_print_template.mustache deleted file mode 100644 index 78510938540..00000000000 --- a/packages/fxa-content-server/app/scripts/templates/settings/account_recovery/recovery_key_print_template.mustache +++ /dev/null @@ -1,7 +0,0 @@ - - - - -

{{recoveryKey}}

- - diff --git a/packages/fxa-content-server/app/scripts/templates/sub_panels.mustache b/packages/fxa-content-server/app/scripts/templates/sub_panels.mustache deleted file mode 100644 index 74bea0188d4..00000000000 --- a/packages/fxa-content-server/app/scripts/templates/sub_panels.mustache +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/packages/fxa-content-server/app/scripts/views/elements/recovery-key-input.js b/packages/fxa-content-server/app/scripts/views/elements/recovery-key-input.js deleted file mode 100644 index 892be6b06d6..00000000000 --- a/packages/fxa-content-server/app/scripts/views/elements/recovery-key-input.js +++ /dev/null @@ -1,35 +0,0 @@ -/* 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/. */ - -import AuthErrors from '../../lib/auth-errors'; -import textInput from './text-input'; -import Vat from '../../lib/vat'; - -const element = Object.create(textInput); - -element.match = function ($el) { - return $el.attr('type') === 'text' && $el.hasClass('recovery-key'); -}; - -element.val = function (val) { - if (arguments.length === 1) { - return this.__val(val); - } - - // Account recovery key is displayed to the user with spaces every 4 - // characters, replace them with empty spaces. - return this.__val().replace(/ /g, '').toUpperCase(); -}; - -element.validate = function () { - const value = this.val(); - - if (!value.length) { - throw AuthErrors.toError('RECOVERY_KEY_REQUIRED'); - } else if (Vat.base32().validate(value).error) { - throw AuthErrors.toError('INVALID_RECOVERY_KEY'); - } -}; - -export default element; diff --git a/packages/fxa-content-server/app/scripts/views/mixins/account-by-uid-mixin.js b/packages/fxa-content-server/app/scripts/views/mixins/account-by-uid-mixin.js deleted file mode 100644 index 264bd8df87d..00000000000 --- a/packages/fxa-content-server/app/scripts/views/mixins/account-by-uid-mixin.js +++ /dev/null @@ -1,35 +0,0 @@ -/* 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/. */ - -import Session from '../../lib/session'; - -export default { - /** - * Set the user's signed in account with the relier's uid. - * Used in settings and support views. - */ - getUidAndSetSignedInAccount() { - const uid = this.relier.get('uid'); - const metricsEnabled = this.relier.get('metricsEnabled'); - this.notifier.trigger('set-uid', uid, metricsEnabled); - - // A uid param is set by RPs linking directly to the settings - // page for a particular account. - // - // We set the current account to the one with `uid` if - // it exists in our list of cached accounts. If the account is - // not in the list of cached accounts, clear the current account. - // - if (!this.user.getAccountByUid(uid).isDefault()) { - // The account with uid exists; set it to our current account. - this.user.setSignedInAccountByUid(uid); - } else if (uid) { - // session is expired or user does not exist. Force the user - // to sign in. - Session.clear(); - this.user.clearSignedInAccountUid(); - this.logViewEvent('signout.forced'); - } - }, -}; diff --git a/packages/fxa-content-server/app/scripts/views/mixins/email-autocomplete-domains-mixin.js b/packages/fxa-content-server/app/scripts/views/mixins/email-autocomplete-domains-mixin.js deleted file mode 100644 index a903dfcf6ac..00000000000 --- a/packages/fxa-content-server/app/scripts/views/mixins/email-autocomplete-domains-mixin.js +++ /dev/null @@ -1,119 +0,0 @@ -/* 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/. */ - -/** - * A mixin to autocomplete common domains from `datalist option` `value`s. - */ -import Template from 'templates/partial/email-autocomplete-domains.mustache'; -import UserAgentMixin from '../../lib/user-agent-mixin'; - -export const EMAIL_SELECTOR = 'input[type=email]'; -export const DATALIST_OPTIONS_SELECTOR = '#autocomplete-domain option'; -export const FOCUS_HACK_SELECTOR = '#focus-hack'; - -export const DOMAINS = [ - 'gmail.com', - 'outlook.com', - 'hotmail.com', - 'yahoo.com', - 'qq.com', - 'web.de', - 'aol.com', - 'mail.ru', - 'icloud.com', - 'gmx.de', - 't-online.de', - 'orange.fr', - 'yandex.ru', - 'yahoo.fr', - 'live.com', - '163.com', - 'msn.com', - 'comcast.net', - 'hotmail.co.uk', - 'hotmail.fr', - 'protonmail.com', - 'pm.me', -]; - -export default { - events: { - [`keyup ${EMAIL_SELECTOR}`]: '_toggleDomainAutocomplete', - }, - dependsOn: [UserAgentMixin], - - setInitialContext(context) { - const unsafeEmailAutocompleteDomainsHTML = this.renderTemplate(Template, { - options: DOMAINS, - }); - context.set({ - unsafeEmailAutocompleteDomainsHTML, - }); - }, - - afterRender() { - this.isAndroid = this.getUserAgent().isAndroid(); - this.isChrome = this.getUserAgent().isChrome(); - this.isEdge = this.getUserAgent().isEdge(); - this.input = this.$(EMAIL_SELECTOR); - this.datalistOptions = this.$(DATALIST_OPTIONS_SELECTOR); - this.focusHack = this.$(FOCUS_HACK_SELECTOR); - this.previousUsername = null; - }, - - /** - * A catch-most-browsers hack when datalist options are modified. - * Chrome otherwise shows all options when '@' is typed, - * Safari immediately goes to the dropdown preventing users - * from typing further, and FF will not update the dropdown on `keyup` - * and has a known bug with dynamic datalists - * https://bugzilla.mozilla.org/show_bug.cgi?id=1474137 - */ - _focusHack() { - this.focusHack.focus(); - this.input.focus(); - }, - - _toggleDomainAutocomplete() { - const inputValue = this.input.val(); - const [username] = inputValue.split('@'); - - if ( - inputValue.length >= 2 && - inputValue.includes('@') && - (username !== this.previousUsername || !this.input.attr('list')) - ) { - // Chrome, Android browsers,a nd Edge show all options when '@' is typed. - // Non-Android Chrome is covered in `_focusHack` but if the device is - // Android we must wait until there is one additional character. - if ((!this.isAndroid && !this.isEdge) || !inputValue.endsWith('@')) { - // Trigger _before_ datalist option mods here because Chrome - // looks janky if text entered pulls up the autocomplete list, but - // NOT in Chrome Android because it _causes_ jankiness - if (!this.isAndroid && this.isChrome) { - this._focusHack(); - } - // Dynamically add/remove `list` attr because it makes conditionals - // easier and Chrome otherwise shows all options on focus - this.input.attr('list', 'autocomplete-domain'); - this.datalistOptions.each((index, option) => { - option.setAttribute('value', `${username}@${DOMAINS[index]}`); - }); - - // Causes jankiness in Chrome Android, needed in other cases - if (!this.isAndroid || (this.isAndroid && !this.isChrome)) { - this._focusHack(); - } - } - } else if (!inputValue.includes('@') && this.input.attr('list')) { - this.input.removeAttr('list'); - this.datalistOptions.each((_, option) => { - option.removeAttribute('value'); - }); - this._focusHack(); - } - - this.previousUsername = username; - }, -}; diff --git a/packages/fxa-content-server/app/scripts/views/mixins/last-checked-time-mixin.js b/packages/fxa-content-server/app/scripts/views/mixins/last-checked-time-mixin.js deleted file mode 100644 index 2d4485a963d..00000000000 --- a/packages/fxa-content-server/app/scripts/views/mixins/last-checked-time-mixin.js +++ /dev/null @@ -1,41 +0,0 @@ -/* 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/. */ - -const t = (msg) => msg; - -/** - * Mixin to set the time a panel was refreshed or checked. - * - * @mixin LastCheckedTimeMixin - */ - -const Mixin = { - setInitialContext(context) { - context.set({ - lastCheckedTime: this.getLastCheckedTimeString(), - }); - }, - - setLastCheckedTime(date) { - if (!date) { - date = new Date(); - } - this.lastCheckedTime = date.toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit', - }); - }, - - getLastCheckedTimeString() { - let time = t('none'); - if (this.lastCheckedTime) { - time = this.lastCheckedTime; - } - return this.translate(t('Last checked: %(lastCheckTime)s'), { - lastCheckTime: time, - }); - }, -}; - -export default Mixin; diff --git a/packages/fxa-content-server/app/scripts/views/mixins/modal-panel-mixin.js b/packages/fxa-content-server/app/scripts/views/mixins/modal-panel-mixin.js deleted file mode 100644 index ca062a50715..00000000000 --- a/packages/fxa-content-server/app/scripts/views/mixins/modal-panel-mixin.js +++ /dev/null @@ -1,125 +0,0 @@ -/* 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/. */ - -/** - * Convert a view into a modal panel. A `modal-cancel` event - * will be triggered if the user clicks on the background area. - * - * Any navigation will cause the panel to close. - */ - -import $ from 'jquery'; -import 'modal'; -import KeyCodes from '../../lib/key-codes'; - -export default { - isModal: true, - notifications: { - navigate: 'closePanel', - 'navigate-back': 'closePanel', - }, - events: { - keydown: 'keyDownListener', - }, - - /** - * Open the panel. - */ - openPanel() { - this.$el.modal({ - clickClose: false, // we take care of closing on the background ourselves. - escapeClose: false, // we take care of closing on the escape key ourselves. - opacity: 0.75, - showClose: false, - zIndex: 999, - }); - - this.$el.on($.modal.AFTER_CLOSE, () => this.onAfterClose()); - - this._previousActiveElement = document.activeElement; - - /** - * Trap keyboard focus when modal opens. - */ - const modal = this.$el[0]; - modal.setAttribute('tabindex', '1'); - modal.focus(); - - const focusableElementsNodeList = modal.querySelectorAll( - 'a[href], button, input[type="radio"]' - ); - const focusableElements = Array.prototype.slice.call( - focusableElementsNodeList - ); - - this._keyDownListener = this.keyDownListener.bind(null, focusableElements); - modal.addEventListener('keydown', this._keyDownListener); - }, - - keyDownListener(focusableElements, event) { - function getNextSelectable(element = focusableElements[0]) { - const nextIndex = focusableElements.indexOf(element) + 1; - // return first element if the next element doesn't exist - return focusableElements[nextIndex] - ? focusableElements[nextIndex] - : focusableElements[0]; - } - - function getPreviousSelectable( - element = focusableElements[focusableElements.length - 1] - ) { - const previousIndex = focusableElements.indexOf(element) - 1; - // return last element if the previous element doesn't exist - return focusableElements[previousIndex] - ? focusableElements[previousIndex] - : focusableElements[focusableElements.length - 1]; - } - - //avoid IME composition keydown events - //ref: https://developer.mozilla.org/docs/Web/Events/keydown#Notes - if (event.isComposing || event.keyCode === KeyCodes.IME) { - return; - } - if (event.keyCode === KeyCodes.TAB) { - event.preventDefault(); - if (event.shiftKey) { - getPreviousSelectable(document.activeElement).focus(); - } else { - getNextSelectable(document.activeElement).focus(); - } - // the form submits on 'enter' keypress by default, but if focus is on an - // unselected radio button, we want to select it instead - } else if ( - event.keyCode === KeyCodes.ENTER && - document.activeElement.getAttribute('type') === 'radio' && - document.activeElement.checked === false - ) { - event.preventDefault(); - document.activeElement.checked = true; - } - }, - - /** - * Close the panel. - */ - closePanel() { - if ($.modal.isActive()) { - $.modal.close(); - } - }, - - onAfterClose() { - this.destroy(true); - this.trigger('modal-cancel'); - this._previousActiveElement.focus(); - }, - - /** - * Wrap the destroy function to close the panel, if it has not - * already been done. - */ - destroy() { - this.closePanel(); - }, -}; diff --git a/packages/fxa-content-server/app/scripts/views/mixins/recovery-key-mixin.js b/packages/fxa-content-server/app/scripts/views/mixins/recovery-key-mixin.js deleted file mode 100644 index 0df63526d55..00000000000 --- a/packages/fxa-content-server/app/scripts/views/mixins/recovery-key-mixin.js +++ /dev/null @@ -1,32 +0,0 @@ -/* 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 to support common methods on an account recovery key. - * - * @mixin RecoveryKeyMixin - */ -const t = (msg) => msg; - -const Mixin = { - formatRecoveryKey(key) { - if (key) { - // Insert spaces every 4 characters and remove trailing space - return key.replace(/(\w{4})/g, '$1 ').replace(/(^\s+|\s+$)/, ''); - } - }, - - getFormatedRecoveryKeyFilename() { - const account = this.getSignedInAccount(); - let formattedFilename = - account.get('email') + ' ' + t('Firefox Account Recovery Key'); - if (formattedFilename.length > 200) { - // 200 bytes (close to filesystem max) - 4 for '.txt' extension - formattedFilename = formattedFilename.substring(0, 196); - } - return `${formattedFilename}.txt`; - }, -}; - -export default Mixin; diff --git a/packages/fxa-content-server/app/scripts/views/mixins/session-verified-notification-mixin.js b/packages/fxa-content-server/app/scripts/views/mixins/session-verified-notification-mixin.js deleted file mode 100644 index c57c3141b40..00000000000 --- a/packages/fxa-content-server/app/scripts/views/mixins/session-verified-notification-mixin.js +++ /dev/null @@ -1,17 +0,0 @@ -/* 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/. */ - -import Notifier from '../../lib/channels/notifier'; - -const Mixin = { - notifications: { - [Notifier.SESSION_VERIFIED]: '_render', - }, - - _render() { - this.render(); - }, -}; - -export default Mixin; diff --git a/packages/fxa-content-server/app/scripts/views/mixins/settings-panel-mixin.js b/packages/fxa-content-server/app/scripts/views/mixins/settings-panel-mixin.js deleted file mode 100644 index b1e9028d711..00000000000 --- a/packages/fxa-content-server/app/scripts/views/mixins/settings-panel-mixin.js +++ /dev/null @@ -1,80 +0,0 @@ -/* 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/. */ - -// This is a mixin used by views that are childViews of Settings -// Modal childViews of Settings use modal-settings-panel-mixin instead. - -import $ from 'jquery'; -import preventDefaultThen from '../decorators/prevent_default_then'; - -export default { - initialize(options) { - this.parentView = options.parentView; - }, - - events: { - 'click .cancel': preventDefaultThen('_closePanelReturnToSettings'), - 'click .settings-unit-toggle': preventDefaultThen('_triggerPanel'), - }, - - afterRender() { - // Disable autofocus as specified by the templates because the panels - // are hidden until opened and focus cannot be set. Instead, set - // the focus when the panel is opened. - this.$('[autofocus]') - .attr('data-autofocus-on-panel-open', true) - .removeAttr('autofocus'); - }, - - _triggerPanel(event) { - var href = event && $(event.currentTarget).data('href'); - if (href) { - this.navigate(href); - } - }, - - openPanel() { - this.closeAllPanels(); - - this.$('.settings-unit').addClass('open'); - this.$el.closest('#fxa-settings-content').addClass('animate-shadow'); - this.focus(this.$('[data-autofocus-on-panel-open]')); - this.model.set('isPanelOpen', true); - }, - - hidePanel() { - this._closePanelReturnToSettings(); - }, - - isPanelOpen() { - return !!this.model.get('isPanelOpen'); - }, - - _closePanelReturnToSettings() { - this.clearInput(); - this.navigate('settings'); - this.closePanel(); - }, - - closePanel() { - this.$el.closest('#fxa-settings-content').removeClass('animate-shadow'); - this.$('.settings-unit').removeClass('open'); - this.model.set('isPanelOpen', false); - }, - - closeAllPanels() { - $('.settings-unit').removeClass('open'); - }, - - displaySuccess(msg, options = { closePanel: true }) { - if (!this.parentView) { - return; - } - this.parentView.displaySuccess(msg); - - if (options.closePanel) { - this.closePanel(); - } - }, -}; diff --git a/packages/fxa-content-server/app/scripts/views/mixins/signed-out-notification-mixin.js b/packages/fxa-content-server/app/scripts/views/mixins/signed-out-notification-mixin.js deleted file mode 100644 index 8ff0ed745a6..00000000000 --- a/packages/fxa-content-server/app/scripts/views/mixins/signed-out-notification-mixin.js +++ /dev/null @@ -1,47 +0,0 @@ -/* 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/. */ - -// Handle signed-out notifications. - -import Notifier from '../../lib/channels/notifier'; -import Session from '../../lib/session'; -import Url from '../../lib/url'; - -var Mixin = { - notifications: { - // populated below using event name aliases - }, - - clearSessionAndNavigateToSignIn() { - this.user.clearSignedInAccountUid(); - Session.clear(); - this.navigateToSignIn(); - }, - - navigateToSignIn() { - const queryString = Url.objToSearchString({ - context: this.relier.get('context'), - entrypoint: this.relier.get('entrypoint'), - /* eslint-disable camelcase */ - entrypoint_experiment: this.relier.get('entrypointExperiment'), - entrypoint_variation: this.relier.get('entrypointVariation'), - /* eslint-enable camelcase */ - service: this.relier.get('service'), - /* eslint-disable camelcase */ - utm_campaign: this.relier.get('utmCampaign'), - utm_content: this.relier.get('utmContent'), - utm_medium: this.relier.get('utmMedium'), - utm_source: this.relier.get('utmSource'), - utm_term: this.relier.get('utmTerm'), - /* eslint-enable camelcase */ - }); - // Navigate via the back-end in order to regenerate - // flow id and flow begin time for the next session. - this.navigateAway(`/signin${queryString}`); - }, -}; - -Mixin.notifications[Notifier.SIGNED_OUT] = 'clearSessionAndNavigateToSignIn'; - -export default Mixin; diff --git a/packages/fxa-content-server/app/scripts/views/mixins/upgrade-session-redirect-mixin.js b/packages/fxa-content-server/app/scripts/views/mixins/upgrade-session-redirect-mixin.js deleted file mode 100644 index d27513c1ed3..00000000000 --- a/packages/fxa-content-server/app/scripts/views/mixins/upgrade-session-redirect-mixin.js +++ /dev/null @@ -1,48 +0,0 @@ -/* 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/. */ - -import AuthErrors from '../../lib/auth-errors'; - -/** - * View mixin to support a user upgrading their session to - * be verified. If a form is submitted and returns with an - * `unverified session` error, then the user will be redirected - * to either verify the session with either a sign-in code - * or a TOTP code. - * - */ -export default { - initialize() { - this.skipBaseOnFormSubmit = true; - }, - - async onFormSubmit() { - try { - await this.validateAndSubmit(); - } catch (err) { - if (AuthErrors.is(err, 'UNVERIFIED_SESSION')) { - const account = this.getSignedInAccount(); - const profile = await account.accountProfile(); - - // Check to see if the account has 2FA and redirect to that - // page to verify - if ( - profile.authenticationMethods && - profile.authenticationMethods.includes('otp') - ) { - return this.replaceCurrentPage('/signin_totp_code', { - redirectPathname: this.window.location.pathname, - }); - } else { - // otw try to do a verification via an email code - return account.verifySessionResendCode().then(() => { - return this.replaceCurrentPage('/signin_token_code', { - redirectPathname: this.window.location.pathname, - }); - }); - } - } - } - }, -}; diff --git a/packages/fxa-content-server/app/scripts/views/sub_panels.js b/packages/fxa-content-server/app/scripts/views/sub_panels.js deleted file mode 100644 index f0d14793c33..00000000000 --- a/packages/fxa-content-server/app/scripts/views/sub_panels.js +++ /dev/null @@ -1,127 +0,0 @@ -/* 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/. */ - -// This component renders multiple childViews -import Backbone from 'backbone'; -import BaseView from './base'; -import Logger from '../lib/logger'; -import Template from 'templates/sub_panels.mustache'; - -const proto = BaseView.prototype; -const View = BaseView.extend({ - template: Template, - className: 'sub-panels', - - initialize(options) { - options = options || {}; - - this._panelViews = options.panelViews || []; - this._parent = options.parent; - this._createView = options.createView; - this._logger = new Logger(); - }, - - showChildView(ChildView, options) { - if (this._panelViews.indexOf(ChildView) === -1) { - this._logger.warn('Tried to show a view that is not a subpanel'); - return Promise.resolve(null); - } - - return this._createChildViewIfNeeded(ChildView, options).then( - (childView) => { - if (childView) { - this._currentChildView = childView; - childView.openPanel(); - - return childView; - } - } - ); - }, - - _childViewInstanceFromClass(ChildView) { - return this.childViews.filter(function (childView) { - if (childView instanceof ChildView) { - return true; - } - })[0]; - }, - - _isModalView(ChildView) { - return !!ChildView.prototype.isModal; - }, - - _childViewClassName(ChildView) { - return ChildView.prototype.className; - }, - - // Render childView if an instance doesn't already exist - _createChildViewIfNeeded(ChildView, options) { - options = options || {}; - - var childView = this._childViewInstanceFromClass(ChildView); - if (childView) { - return Promise.resolve(childView); - } - - var className = this._childViewClassName(ChildView); - var selector = '.' + className; - - this.$('.child-views').append( - '
' - ); - - // Each child view receives its own model on creation. The - // child view's model will be updated with the appropriate data - // when shown. Sandboxed models prevent child views from sharing - // status messages and other data with each other and the parent. - // Without the sandbox, one child view can overwrite the model data - // of another child view. - var childModel = new Backbone.Model(); - - // If a model was passed in, immediately copy that data to the - // child in case the data is needed for the initial render - if (options.model) { - childModel.set(options.model.toJSON()); - } - - var view = this._createView(ChildView, { - el: this.$(selector), - model: childModel, - parentView: this._parent, - }); - - this.trackChildView(view); - - return this.renderChildView(view); - }, - - renderChildView(viewToShow) { - return viewToShow.render().then(function (shown) { - if (!shown) { - viewToShow.destroy(true); - return; - } - - viewToShow.afterVisible(); - - return viewToShow; - }); - }, - - afterRender() { - // Initial childViews to render; excludes modal views - var initialChildViews = this._panelViews.filter((ChildView) => { - return !this._isModalView(ChildView); - }); - - return Promise.all( - initialChildViews.map((ChildView) => { - return this._createChildViewIfNeeded(ChildView); - }) - ).then(proto.afterRender.bind(this)); - }, -}); - -export default View; diff --git a/packages/fxa-content-server/app/tests/spec/models/attached-clients.js b/packages/fxa-content-server/app/tests/spec/models/attached-clients.js deleted file mode 100644 index 873fa9fa29a..00000000000 --- a/packages/fxa-content-server/app/tests/spec/models/attached-clients.js +++ /dev/null @@ -1,192 +0,0 @@ -/* 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/. */ - -import { assert } from 'chai'; -import AttachedClients from 'models/attached-clients'; -import Constants from 'lib/constants'; -import Notifier from 'lib/channels/notifier'; -import sinon from 'sinon'; -import User from 'models/user'; - -describe('models/attached-clients', function () { - var attachedClients; - var notifier; - var user; - - beforeEach(function () { - notifier = new Notifier(); - user = new User(); - - attachedClients = new AttachedClients([], { - notifier: notifier, - }); - }); - - function shuffle(items) { - for (var i = items.length - 1; i > 0; i--) { - var j = Math.floor(Math.random() * (i + 1)); - var tmp = items[i]; - items[i] = items[j]; - items[j] = tmp; - } - return items; - } - - describe('properly orders the attached clients', function () { - var now = Date.now(); - - beforeEach(function () { - // We input the list of clients in a random order, - // so that the tests won't accidentally depend on implementation - // details of how the underlying sort algorithm works. - attachedClients.set( - shuffle([ - { - deviceId: 'device1', - isCurrentSession: false, - lastAccessTime: null, - name: 'xi', - sessionTokenId: 'session1', - }, - { - deviceId: 'device2', - isCurrentSession: false, - lastAccessTime: null, - name: 'xi', - sessionTokenId: 'session2', - }, - { - deviceId: 'device3', - isCurrentSession: false, - lastAccessTime: null, - name: 'phi', - sessionTokenId: 'session3', - }, - { - deviceId: 'device4', - isCurrentSession: true, - lastAccessTime: now - 20, - name: 'zeta', - sessionTokenId: 'session4', - }, - { - clientId: 'client1', - isCurrentSession: false, - lastAccessTime: now - 10, - name: 'mu', - }, - { - deviceId: 'device5', - isCurrentSession: false, - lastAccessTime: now, - name: 'tau', - sessionTokenId: 'session5', - }, - { - deviceId: 'device6', - isCurrentSession: false, - lastAccessTime: now, - name: 'sigma', - sessionTokenId: 'session6', - }, - { - deviceId: 'device7', - isCurrentSession: false, - lastAccessTime: now - 20, - name: 'theta', - sessionTokenId: 'session7', - }, - { - clientId: 'client2', - lastAccessTime: null, - name: 'an oauth', - }, - { - lastAccessTime: now, - name: 'a web session', - sessionTokenId: 'session8', - }, - ]) - ); - }); - - it('places the `current` device first', function () { - assert.equal(attachedClients.at(0).get('name'), 'zeta'); - }); - - it('sorts those with lastAccessTime next, by access time (descending) grouped by clientType', function () { - assert.equal(attachedClients.at(1).get('name'), 'sigma'); - assert.equal(attachedClients.at(2).get('name'), 'tau'); - assert.equal(attachedClients.at(3).get('name'), 'theta'); - }); - - it('sorts the rest alphabetically grouped by clientType', function () { - assert.equal(attachedClients.at(4).get('name'), 'phi'); - assert.equal(attachedClients.at(5).get('name'), 'xi'); - assert.equal(attachedClients.at(6).get('name'), 'xi'); - assert.equal(attachedClients.at(7).get('name'), 'mu'); - assert.equal(attachedClients.at(8).get('name'), 'an oauth'); - assert.equal(attachedClients.at(9).get('name'), 'a web session'); - }); - - it('groups by clientType', () => { - const types = attachedClients.map((cl) => cl.get('clientType')); - assert.equal(types[0], Constants.CLIENT_TYPE_DEVICE); - assert.equal(types[1], Constants.CLIENT_TYPE_DEVICE); - assert.equal(types[2], Constants.CLIENT_TYPE_DEVICE); - assert.equal(types[3], Constants.CLIENT_TYPE_DEVICE); - assert.equal(types[4], Constants.CLIENT_TYPE_DEVICE); - assert.equal(types[5], Constants.CLIENT_TYPE_DEVICE); - assert.equal(types[6], Constants.CLIENT_TYPE_DEVICE); - assert.equal(types[7], Constants.CLIENT_TYPE_OAUTH_APP); - assert.equal(types[8], Constants.CLIENT_TYPE_OAUTH_APP); - assert.equal(types[9], Constants.CLIENT_TYPE_WEB_SESSION); - }); - }); - - describe('device name change', function () { - beforeEach(function () { - attachedClients.set([ - { - clientType: Constants.CLIENT_TYPE_DEVICE, - id: 'device-1', - isCurrentSession: false, - name: 'zeta', - }, - { - clientType: Constants.CLIENT_TYPE_DEVICE, - id: 'device-2', - isCurrentSession: true, - name: 'upsilon', - }, - ]); - }); - }); - - describe('fetchClients', function () { - beforeEach(function () { - sinon.stub(user, 'fetchAccountAttachedClients').callsFake(function () { - return Promise.resolve([ - { - deviceId: 'device-1', - isCurrentSession: true, - name: 'zeta', - }, - { - clientId: 'oauth-1', - name: 'oauthy', - }, - ]); - }); - }); - - it('fetches the list of attached clients', function () { - return attachedClients.fetchClients(user).then(() => { - assert.equal(attachedClients.length, 2); - assert.equal(attachedClients.at(0).get('clientType'), 'device'); - assert.equal(attachedClients.at(1).get('clientType'), 'oAuthApp'); - }); - }); - }); -}); diff --git a/packages/fxa-content-server/app/tests/spec/models/email.js b/packages/fxa-content-server/app/tests/spec/models/email.js deleted file mode 100644 index de17ca36d80..00000000000 --- a/packages/fxa-content-server/app/tests/spec/models/email.js +++ /dev/null @@ -1,25 +0,0 @@ -/* 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/. */ - -import { assert } from 'chai'; -import Email from 'models/email'; - -describe('models/email', function () { - let email; - const emailOpts = { - email: 'some@email.com', - isPrimary: false, - verified: false, - }; - - beforeEach(function () { - email = new Email(emailOpts); - }); - - describe('create', function () { - it('correctly sets model properties', function () { - assert.deepEqual(email.attributes, emailOpts); - }); - }); -}); diff --git a/packages/fxa-content-server/app/tests/spec/models/security-events.js b/packages/fxa-content-server/app/tests/spec/models/security-events.js deleted file mode 100644 index d4409b5223a..00000000000 --- a/packages/fxa-content-server/app/tests/spec/models/security-events.js +++ /dev/null @@ -1,35 +0,0 @@ -/* 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/. */ - -import { assert } from 'chai'; -import SecurityEvent from 'models/security-events'; - -describe('models/security-events', () => { - let securityEvent; - const events = [ - { - name: 'account.login', - verified: true, - createdAt: new Date().getTime(), - }, - { - name: 'account.create', - verified: false, - createdAt: new Date().getTime(), - }, - ]; - - beforeEach(() => { - securityEvent = events.map((event) => { - return new SecurityEvent(event); - }); - }); - - describe('create', () => { - it('correctly sets model properties', () => { - assert.deepEqual(securityEvent[0].attributes, events[0]); - assert.deepEqual(securityEvent[1].attributes, events[1]); - }); - }); -}); diff --git a/packages/fxa-content-server/app/tests/spec/views/mixins/account-by-uid-mixin.js b/packages/fxa-content-server/app/tests/spec/views/mixins/account-by-uid-mixin.js deleted file mode 100644 index 9f0868b6fb6..00000000000 --- a/packages/fxa-content-server/app/tests/spec/views/mixins/account-by-uid-mixin.js +++ /dev/null @@ -1,95 +0,0 @@ -/* 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/. */ - -import AccountByUidMixin from 'views/mixins/account-by-uid-mixin'; -import { assert } from 'chai'; -import BaseView from 'views/base'; -import Cocktail from 'cocktail'; -import Notifier from 'lib/channels/notifier'; -import NullChannel from 'lib/channels/null'; -import Relier from 'models/reliers/relier'; -import Session from 'lib/session'; -import sinon from 'sinon'; -import TestHelpers from '../../../lib/helpers'; -import User from 'models/user'; - -const UID = TestHelpers.createUid(); -const View = BaseView.extend({}); -Cocktail.mixin(View, AccountByUidMixin); - -describe('views/mixins/account-by-uid-mixin', function () { - let notifier; - let relier; - let tabChannelMock; - let user; - let view; - - beforeEach(function () { - relier = new Relier(); - tabChannelMock = new NullChannel(); - user = new User(); - notifier = new Notifier({ - tabChannel: tabChannelMock, - }); - - sinon.stub(relier, 'get').withArgs('uid').returns(UID); - - view = new View({ - notifier: notifier, - relier: relier, - user: user, - }); - }); - - afterEach(function () { - relier.get.restore(); - - view.remove(); - view.destroy(); - view = null; - }); - - describe('getUidAndSetSignedInAccount', function () { - it('gets the uid from the relier', function () { - sinon.spy(notifier, 'trigger'); - sinon.stub(user, 'clearSignedInAccountUid'); - - view.getUidAndSetSignedInAccount(); - assert.isTrue(relier.get.calledWith('uid')); - assert.isTrue(notifier.trigger.calledWith('set-uid', UID)); - - notifier.trigger.restore(); - user.clearSignedInAccountUid.restore(); - }); - - it('sets signed in account with uid when account exists', function () { - sinon.stub(user, 'getAccountByUid').returns({ isDefault: () => false }); - sinon.stub(user, 'setSignedInAccountByUid'); - - view.getUidAndSetSignedInAccount(); - assert.isTrue(user.getAccountByUid.calledWith(UID)); - assert.isTrue(user.setSignedInAccountByUid.calledWith(UID)); - - user.setSignedInAccountByUid.restore(); - user.getAccountByUid.restore(); - }); - - it('forces the user to sign in when account does not exist', function () { - sinon.stub(user, 'getAccountByUid').returns({ isDefault: () => true }); - sinon.stub(user, 'clearSignedInAccountUid'); - sinon.stub(Session, 'clear'); - sinon.stub(view, 'logViewEvent'); - - view.getUidAndSetSignedInAccount(); - assert.isTrue(Session.clear.calledOnce); - assert.isTrue(user.clearSignedInAccountUid.calledOnce); - assert.isTrue(view.logViewEvent.calledWith('signout.forced')); - - view.logViewEvent.restore(); - Session.clear.restore(); - user.clearSignedInAccountUid.restore(); - user.getAccountByUid.restore(); - }); - }); -}); diff --git a/packages/fxa-content-server/app/tests/spec/views/mixins/email-autocomplete-domains-mixin.js b/packages/fxa-content-server/app/tests/spec/views/mixins/email-autocomplete-domains-mixin.js deleted file mode 100644 index d5f515de68a..00000000000 --- a/packages/fxa-content-server/app/tests/spec/views/mixins/email-autocomplete-domains-mixin.js +++ /dev/null @@ -1,108 +0,0 @@ -/* 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/. */ - -import { assert } from 'chai'; -import sinon from 'sinon'; -import FormView from 'views/form'; -import Cocktail from 'cocktail'; -import EmailAutocompleteDomainsMixin, { - DOMAINS, - EMAIL_SELECTOR, - DATALIST_OPTIONS_SELECTOR, - FOCUS_HACK_SELECTOR, -} from 'views/mixins/email-autocomplete-domains-mixin'; - -describe('views/mixins/email-autocomplete-domains-mixin', () => { - let view; - - const EmailDatalistView = FormView.extend({ - template: (context) => context.unsafeEmailAutocompleteDomainsHTML, - }); - - Cocktail.mixin(EmailDatalistView, EmailAutocompleteDomainsMixin); - - beforeEach(() => { - view = new EmailDatalistView(); - sinon.stub(view, 'getUserAgent').callsFake(() => ({ - isAndroid: () => false, - isChrome: () => false, - isEdge: () => false, - })); - return view.render(); - }); - - afterEach(() => { - view.remove(); - view.destroy(); - view = null; - }); - - describe('renders correctly', () => { - it('contains `option` elements the same length as the domains array', () => { - assert.lengthOf(view.$(DATALIST_OPTIONS_SELECTOR), DOMAINS.length); - }); - it('contains #focus-hack element for cross-browser compatibility', () => { - assert.lengthOf(view.$(FOCUS_HACK_SELECTOR), 1); - }); - }); - - describe('_toggleDomainAutocomplete', () => { - it('renders datalist with options from domains list on keyup when "@" is present', () => { - view.$(EMAIL_SELECTOR).val('foo@').trigger('keyup'); - assert.isTrue( - view.$(EMAIL_SELECTOR).attr('list') === 'autocomplete-domain' - ); - assert.isTrue( - view.$(DATALIST_OPTIONS_SELECTOR)[0].getAttribute('value') === - `foo@${DOMAINS[0]}` - ); - assert.isTrue( - view - .$(DATALIST_OPTIONS_SELECTOR) - [DOMAINS.length - 1].getAttribute('value') === - `foo@${DOMAINS[DOMAINS.length - 1]}` - ); - }); - - it('does nothing if conditions are not met,', () => { - // input is too short - view.$(EMAIL_SELECTOR).val('a').trigger('keyup'); - assert.isTrue(view.$(EMAIL_SELECTOR).attr('list') === undefined); - - // "@" is not present - view.$(EMAIL_SELECTOR).val('foo').trigger('keyup'); - assert.isTrue(view.$(EMAIL_SELECTOR).attr('list') === undefined); - - // username doesn't change but user keeps typing - view.$(EMAIL_SELECTOR).val('foo@').trigger('keyup'); - view.$(EMAIL_SELECTOR).val('foo@gmail').trigger('keyup'); - assert.isTrue( - view.$(DATALIST_OPTIONS_SELECTOR)[0].getAttribute('value') === - `foo@${DOMAINS[0]}` - ); - }); - - it('updates options on username change when "@" sign is present', () => { - view.$(EMAIL_SELECTOR).val('foo').trigger('keyup'); - assert.isTrue( - view.$(DATALIST_OPTIONS_SELECTOR)[0].getAttribute('value') === null - ); - view.$(EMAIL_SELECTOR).val('foo@').trigger('keyup'); - view.$(EMAIL_SELECTOR).val('fo@').trigger('keyup'); - assert.isTrue( - view.$(DATALIST_OPTIONS_SELECTOR)[0].getAttribute('value') === - `fo@${DOMAINS[0]}` - ); - }); - - it('clears options when "@" is present and then removed', () => { - view.$(EMAIL_SELECTOR).val('foo@').trigger('keyup'); - view.$(EMAIL_SELECTOR).val('foo').trigger('keyup'); - assert.isTrue(view.$(EMAIL_SELECTOR).attr('list') === undefined); - assert.isTrue( - view.$(DATALIST_OPTIONS_SELECTOR)[0].getAttribute('value') === null - ); - }); - }); -}); diff --git a/packages/fxa-content-server/app/tests/spec/views/mixins/last-checked-time-mixin.js b/packages/fxa-content-server/app/tests/spec/views/mixins/last-checked-time-mixin.js deleted file mode 100644 index 5fb423d58d9..00000000000 --- a/packages/fxa-content-server/app/tests/spec/views/mixins/last-checked-time-mixin.js +++ /dev/null @@ -1,49 +0,0 @@ -/* 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/. */ - -import { assert } from 'chai'; -import BaseView from 'views/base'; -import Cocktail from 'cocktail'; -import LastCheckedTimeMixin from 'views/mixins/last-checked-time-mixin'; -import sinon from 'sinon'; - -const View = BaseView.extend({}); - -Cocktail.mixin(View, LastCheckedTimeMixin); - -describe('views/mixins/last-checked-time-mixin', () => { - let view; - - beforeEach(() => { - view = new View({}); - }); - - it('should set last checked time', () => { - assert.equal(view.lastCheckedTime, undefined, 'no time set'); - view.setLastCheckedTime(); - assert.equal(typeof view.lastCheckedTime, 'string', 'time is set'); - }); - - it('should return `none` if lastCheckTime is not set', () => { - assert.equal(view.lastCheckedTime, undefined, 'no time set'); - const lastCheckString = view.getLastCheckedTimeString(); - assert.equal( - lastCheckString, - 'Last checked: none', - 'time is set to `none`' - ); - }); - - it('should return formatted time', () => { - const date = new Date(); - sinon.stub(date, 'toLocaleTimeString').callsFake(() => '4:58 PM'); - view.setLastCheckedTime(date); - const lastCheckString = view.getLastCheckedTimeString(); - assert.equal( - lastCheckString, - 'Last checked: 4:58 PM', - 'formatted time is set' - ); - }); -}); diff --git a/packages/fxa-content-server/app/tests/spec/views/mixins/modal-panel-mixin.js b/packages/fxa-content-server/app/tests/spec/views/mixins/modal-panel-mixin.js deleted file mode 100644 index af1be734ba2..00000000000 --- a/packages/fxa-content-server/app/tests/spec/views/mixins/modal-panel-mixin.js +++ /dev/null @@ -1,92 +0,0 @@ -/* 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/. */ - -import $ from 'jquery'; -import { assert } from 'chai'; -import BaseView from 'views/base'; -import Cocktail from 'cocktail'; -import ModalPanelMixin from 'views/mixins/modal-panel-mixin'; -import Notifier from 'lib/channels/notifier'; -import sinon from 'sinon'; -import TestTemplate from 'templates/test_template.mustache'; - -const ModalPanelView = BaseView.extend({ - template: TestTemplate, -}); - -Cocktail.mixin(ModalPanelView, ModalPanelMixin); - -describe('views/mixins/modal-panel-mixin', () => { - let notifier; - let view; - - beforeEach(() => { - notifier = new Notifier(); - view = new ModalPanelView({ - notifier, - }); - - return view.render(); - }); - - afterEach(() => { - view.remove(); - view.destroy(); - - view = null; - }); - - describe('open and close', () => { - it('does the right thing', () => { - assert.equal($('body').css('min-height'), '0px'); - view.openPanel(); - assert.isTrue($.modal.isActive()); - assert.equal($('body').css('min-height'), '0px'); - - view.closePanel(); - assert.isFalse($.modal.isActive()); - assert.equal($('body').css('min-height'), '0px'); - }); - }); - - it('closePanel destroys the view', () => { - sinon.spy(view, 'destroy'); - view.openPanel(); - view.closePanel(); - - assert.isTrue(view.destroy.calledOnce); - }); - - describe('click handler', () => { - beforeEach(() => { - $('#container').html(view.$el); - - sinon.spy(view, 'trigger'); - sinon.spy(view, 'closePanel'); - - view.openPanel(); - }); - - it('click on a child of the blocker has no effect', () => { - $('#back').click(); - - assert.isFalse(view.trigger.called); - assert.isFalse(view.closePanel.called); - }); - }); - - describe('notifications', () => { - function testNotificationClosesPanel(notification) { - it(`${notification} closes the panel`, () => { - sinon.spy(view, 'closePanel'); - notifier.trigger(notification); - - assert.isTrue(view.closePanel.calledOnce); - }); - } - - testNotificationClosesPanel('navigate'); - testNotificationClosesPanel('navigate-back'); - }); -}); diff --git a/packages/fxa-content-server/app/tests/spec/views/mixins/session-verified-notification-mixin.js b/packages/fxa-content-server/app/tests/spec/views/mixins/session-verified-notification-mixin.js deleted file mode 100644 index 588c02ea561..00000000000 --- a/packages/fxa-content-server/app/tests/spec/views/mixins/session-verified-notification-mixin.js +++ /dev/null @@ -1,60 +0,0 @@ -/* 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/. */ - -import { assert } from 'chai'; -import Backbone from 'backbone'; -import BaseView from 'views/base'; -import Cocktail from 'cocktail'; -import Notifier from 'lib/channels/notifier'; -import SessionVerifiedNotificationMixin from 'views/mixins/session-verified-notification-mixin'; -import sinon from 'sinon'; - -const View = BaseView.extend({}); -Cocktail.mixin(View, SessionVerifiedNotificationMixin); - -describe('views/mixins/session-verified-notification-mixin', () => { - it('exports correct interface', () => { - assert.lengthOf(Object.keys(SessionVerifiedNotificationMixin), 2); - assert.isObject(SessionVerifiedNotificationMixin.notifications); - assert.isFunction(SessionVerifiedNotificationMixin._render); - }); - - describe('new View', () => { - let model; - let notifier; - let view; - - before(() => { - model = new Backbone.Model(); - notifier = new Notifier(); - notifier.on = sinon.spy(); - view = new View({ - model: model, - notifier: notifier, - }); - }); - - after(() => { - view.destroy(); - view = null; - }); - - it('has the expected notifications', () => { - assert.lengthOf(Object.keys(view.notifications), 1); - assert.isTrue(notifier.COMMANDS.SESSION_VERIFIED in view.notifications); - }); - - describe('_render', () => { - before(() => { - view.render = sinon.spy(); - notifier.triggerAll = sinon.spy(); - return notifier.on.args[0][1](); - }); - - it('calls render correctly', () => { - assert.equal(view.render.callCount, 1); - }); - }); - }); -}); diff --git a/packages/fxa-content-server/app/tests/spec/views/mixins/signed-out-notification-mixin.js b/packages/fxa-content-server/app/tests/spec/views/mixins/signed-out-notification-mixin.js deleted file mode 100644 index 7bdb2babf4b..00000000000 --- a/packages/fxa-content-server/app/tests/spec/views/mixins/signed-out-notification-mixin.js +++ /dev/null @@ -1,110 +0,0 @@ -/* 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/. */ - -import { assert } from 'chai'; -import BaseView from 'views/base'; -import Cocktail from 'cocktail'; -import Notifier from 'lib/channels/notifier'; -import SignedOutNotificationMixin from 'views/mixins/signed-out-notification-mixin'; -import sinon from 'sinon'; -import WindowMock from '../../../mocks/window'; - -const View = BaseView.extend({}); -Cocktail.mixin(View, SignedOutNotificationMixin); - -describe('views/mixins/signed-out-notification-mixin', () => { - it('exports correct interface', () => { - assert.lengthOf(Object.keys(SignedOutNotificationMixin), 3); - assert.isObject(SignedOutNotificationMixin.notifications); - assert.isFunction( - SignedOutNotificationMixin.clearSessionAndNavigateToSignIn - ); - }); - - describe('new View', () => { - let notifier; - let view; - let windowMock; - - beforeEach(() => { - notifier = new Notifier(); - notifier.on = sinon.spy(); - windowMock = new WindowMock(); - view = new View({ - notifier: notifier, - window: windowMock, - }); - view.navigate = sinon.spy(); - view.navigateAway = sinon.spy(); - view.relier = { - get: sinon.spy(), - }; - view.user = { - clearSignedInAccountUid: sinon.spy(), - }; - notifier.triggerAll = sinon.spy(); - }); - - afterEach(() => { - view.destroy(); - }); - - it('has the expected notifications', () => { - assert.lengthOf(Object.keys(view.notifications), 1); - assert.isTrue(Notifier.SIGNED_OUT in view.notifications); - }); - - describe('clearSessionAndNavigateToSignIn', () => { - beforeEach(() => { - notifier.on.args[0][1](); - }); - - it('calls user.clearSignedInAccountUid correctly', () => { - assert.equal(view.user.clearSignedInAccountUid.callCount, 1); - assert.lengthOf(view.user.clearSignedInAccountUid.args[0], 0); - }); - - it('navigated correctly', () => { - assert.equal(view.navigateAway.callCount, 1); - assert.equal(view.navigateAway.args[0][0], '/signin'); - }); - - it('does not call notifier.triggerAll', () => { - assert.equal(notifier.triggerAll.callCount, 0); - }); - }); - - describe('clearSessionAndNavigateToSignIn with relier data', () => { - beforeEach(() => { - view.relier.get = sinon.spy((key) => `mock_${key}`); - notifier.on.args[0][1](); - }); - - it('calls user.clearSignedInAccountUid correctly', () => { - assert.equal(view.user.clearSignedInAccountUid.callCount, 1); - assert.lengthOf(view.user.clearSignedInAccountUid.args[0], 0); - }); - - it('navigated correctly', () => { - assert.equal(view.navigateAway.callCount, 1); - assert.equal( - view.navigateAway.args[0][0], - '/signin?' + - [ - 'context=mock_context', - 'entrypoint=mock_entrypoint', - 'entrypoint_experiment=mock_entrypointExperiment', - 'entrypoint_variation=mock_entrypointVariation', - 'service=mock_service', - 'utm_campaign=mock_utmCampaign', - 'utm_content=mock_utmContent', - 'utm_medium=mock_utmMedium', - 'utm_source=mock_utmSource', - 'utm_term=mock_utmTerm', - ].join('&') - ); - }); - }); - }); -}); diff --git a/packages/fxa-content-server/app/tests/test_start.js b/packages/fxa-content-server/app/tests/test_start.js index 80592952f31..f97371f311f 100644 --- a/packages/fxa-content-server/app/tests/test_start.js +++ b/packages/fxa-content-server/app/tests/test_start.js @@ -94,7 +94,6 @@ require('./spec/lib/xhr'); require('./spec/lib/xss'); require('./spec/models/account'); require('./spec/models/attached-client'); -require('./spec/models/attached-clients'); require('./spec/models/auth_brokers/base'); require('./spec/models/auth_brokers/fx-desktop-v3'); require('./spec/models/auth_brokers/fx-ios-v1'); @@ -111,7 +110,6 @@ require('./spec/models/auth_brokers/pairing/supplicant'); require('./spec/models/auth_brokers/pairing/supplicant-webchannel'); require('./spec/models/auth_brokers/pairing/mixins/supplicant'); require('./spec/models/auth_brokers/web'); -require('./spec/models/email'); require('./spec/models/email-resend'); require('./spec/models/flow'); require('./spec/models/form-prefill'); @@ -135,7 +133,6 @@ require('./spec/models/reliers/pairing/supplicant'); require('./spec/models/reliers/relier'); require('./spec/models/reliers/browser'); require('./spec/models/resume-token'); -require('./spec/models/security-events'); require('./spec/models/subscription'); require('./spec/models/sync-engines'); require('./spec/models/unique-user-id'); @@ -170,7 +167,6 @@ require('./spec/views/elements/recovery-code-input'); require('./spec/views/form'); require('./spec/views/index'); require('./spec/views/marketing_snippet'); -require('./spec/views/mixins/account-by-uid-mixin'); require('./spec/views/mixins/account-reset-mixin'); require('./spec/views/mixins/account-suggestion-mixin'); require('./spec/views/mixins/avatar-mixin'); @@ -180,7 +176,6 @@ require('./spec/views/mixins/cwts-on-signup-password'); require('./spec/views/mixins/connect-another-device-mixin'); require('./spec/views/mixins/coppa-mixin'); require('./spec/views/mixins/device-connected-poll-mixin'); -require('./spec/views/mixins/email-autocomplete-domains-mixin'); require('./spec/views/mixins/email-opt-in-mixin'); require('./spec/views/mixins/experiment-mixin'); require('./spec/views/mixins/external-links-mixin'); @@ -189,10 +184,8 @@ require('./spec/views/mixins/flow-events-mixin'); require('./spec/views/mixins/form-prefill-mixin'); require('./spec/views/mixins/has-modal-child-view-mixin'); require('./spec/views/mixins/third-party-auth-mixin'); -require('./spec/views/mixins/last-checked-time-mixin'); require('./spec/views/mixins/loading-mixin'); require('./spec/views/mixins/marketing-mixin'); -require('./spec/views/mixins/modal-panel-mixin'); require('./spec/views/mixins/one-visible-of-type-mixin'); require('./spec/views/mixins/open-webmail-mixin'); require('./spec/views/mixins/pairing-graphics-mixin'); @@ -205,9 +198,7 @@ require('./spec/views/mixins/resume-token-mixin'); require('./spec/views/mixins/save-options-mixin'); require('./spec/views/mixins/service-mixin'); require('./spec/views/mixins/session-verification-poll-mixin'); -require('./spec/views/mixins/session-verified-notification-mixin'); require('./spec/views/mixins/signed-in-notification-mixin'); -require('./spec/views/mixins/signed-out-notification-mixin'); require('./spec/views/mixins/signin-mixin'); require('./spec/views/mixins/signup-mixin'); require('./spec/views/mixins/sync-auth-mixin');