From 6087532d466df41ee9f87fe42533737872f6d6d3 Mon Sep 17 00:00:00 2001 From: Stuart Colville Date: Mon, 6 Jun 2016 21:47:19 +0100 Subject: [PATCH 1/5] Use constants for dispatch types --- src/disco/components/Addon.js | 44 ++++++++------ src/disco/constants.js | 57 ++++++++++++++++--- src/disco/reducers/installations.js | 37 ++++++------ tests/client/disco/components/TestAddon.js | 32 ++++++----- .../disco/reducers/test_installations.js | 26 ++++++--- 5 files changed, 128 insertions(+), 68 deletions(-) diff --git a/src/disco/components/Addon.js b/src/disco/components/Addon.js index b57bf53851e..b83bb2c2b8f 100644 --- a/src/disco/components/Addon.js +++ b/src/disco/components/Addon.js @@ -12,20 +12,28 @@ import * as addonManager from 'disco/addonManager'; import InstallButton from 'disco/components/InstallButton'; import { - validAddonTypes, - validInstallStates, DOWNLOAD_FAILED, + DOWNLOAD_PROGRESS, ERROR, EXTENSION_TYPE, + INSTALLED, INSTALL_CATEGORY, + INSTALL_COMPLETE, + INSTALL_ERROR, INSTALL_FAILED, - INSTALLED, + INSTALL_STATE, + START_DOWNLOAD, + START_INSTALL, + START_UNINSTALL, THEME_INSTALL, - THEME_TYPE, THEME_PREVIEW, THEME_RESET_PREVIEW, - UNINSTALL_CATEGORY, + THEME_TYPE, UNINSTALLED, + UNINSTALL_CATEGORY, + UNINSTALL_COMPLETE, + validAddonTypes, + validInstallStates, } from 'disco/constants'; import 'disco/css/Addon.scss'; @@ -196,15 +204,15 @@ export function makeProgressHandler(dispatch, guid) { if (addonInstall.state === 'STATE_DOWNLOADING') { const downloadProgress = parseInt( 100 * addonInstall.progress / addonInstall.maxProgress, 10); - dispatch({type: 'DOWNLOAD_PROGRESS', payload: {guid, downloadProgress}}); + dispatch({type: DOWNLOAD_PROGRESS, payload: {guid, downloadProgress}}); } else if (addonInstall.state === 'STATE_INSTALLING') { - dispatch({type: 'START_INSTALL', payload: {guid}}); + dispatch({type: START_INSTALL, payload: {guid}}); } else if (addonInstall.state === 'STATE_INSTALLED') { - dispatch({type: 'INSTALL_COMPLETE', payload: {guid}}); + dispatch({type: INSTALL_COMPLETE, payload: {guid}}); } else if (e.type === 'onDownloadFailed') { - dispatch({type: 'INSTALL_ERROR', payload: {guid, error: DOWNLOAD_FAILED}}); + dispatch({type: INSTALL_ERROR, payload: {guid, error: DOWNLOAD_FAILED}}); } else if (e.type === 'onInstallFailed') { - dispatch({type: 'INSTALL_ERROR', payload: {guid, error: INSTALL_FAILED}}); + dispatch({type: INSTALL_ERROR, payload: {guid, error: INSTALL_FAILED}}); } }; } @@ -221,15 +229,15 @@ export function mapDispatchToProps(dispatch, { _tracking = tracking, .then( (addon) => { const status = addon.type === THEME_TYPE && !addon.isEnabled ? UNINSTALLED : INSTALLED; - dispatch({type: 'INSTALL_STATE', payload: {...payload, status}}); + dispatch({type: INSTALL_STATE, payload: {...payload, status}}); }, - () => dispatch({type: 'INSTALL_STATE', payload: {...payload, status: UNINSTALLED}})); + () => dispatch({type: INSTALL_STATE, payload: {...payload, status: UNINSTALLED}})); }, - install({ guid, i18n, installURL, name }) { - dispatch({type: 'START_DOWNLOAD', payload: {guid}}); + install({ guid, installURL, name }) { + dispatch({type: START_DOWNLOAD, payload: {guid}}); _tracking.sendEvent({action: 'addon', category: INSTALL_CATEGORY, label: name}); - return _addonManager.install(installURL, makeProgressHandler(dispatch, guid, i18n)); + return _addonManager.install(installURL, makeProgressHandler(dispatch, guid)); }, installTheme(node, guid, name, _themeAction = themeAction) { @@ -237,21 +245,21 @@ export function mapDispatchToProps(dispatch, { _tracking = tracking, _tracking.sendEvent({action: 'theme', category: INSTALL_CATEGORY, label: name}); return new Promise((resolve) => { setTimeout(() => { - dispatch({type: 'INSTALL_STATE', payload: {guid, status: INSTALLED}}); + dispatch({type: INSTALL_STATE, payload: {guid, status: INSTALLED}}); resolve(); }, 250); }); }, uninstall({ guid, name, type }) { - dispatch({type: 'START_UNINSTALL', payload: {guid}}); + dispatch({type: START_UNINSTALL, payload: {guid}}); const action = { [EXTENSION_TYPE]: 'addon', [THEME_TYPE]: 'theme', }[type] || 'invalid'; _tracking.sendEvent({action, category: UNINSTALL_CATEGORY, label: name}); return _addonManager.uninstall(guid) - .then(() => dispatch({type: 'UNINSTALL_COMPLETE', payload: {guid}})); + .then(() => dispatch({type: UNINSTALL_COMPLETE, payload: {guid}})); }, }; } diff --git a/src/disco/constants.js b/src/disco/constants.js index ff16c365c3a..5eb7d2e2f00 100644 --- a/src/disco/constants.js +++ b/src/disco/constants.js @@ -1,13 +1,19 @@ // Addon States. -export const DOWNLOADING = 'downloading'; -export const ERROR = 'error'; -export const INSTALLED = 'installed'; -export const INSTALLING = 'installing'; -export const UNINSTALLED = 'uninstalled'; -export const UNINSTALLING = 'uninstalling'; -export const UNKNOWN = 'unknown'; +export const DOWNLOADING = 'DOWNLOADING'; +export const ERROR = 'ERROR'; +export const INSTALLED = 'INSTALLED'; +export const INSTALLING = 'INSTALLING'; +export const UNINSTALLED = 'UNINSTALLED'; +export const UNINSTALLING = 'UNINSTALLING'; +export const UNKNOWN = 'UNKNOWN'; +// Theme states +export const DISABLED = 'DISABLED'; +export const ENABLED = 'ENABLED'; + export const validInstallStates = [ + DISABLED, DOWNLOADING, + ENABLED, ERROR, INSTALLED, INSTALLING, @@ -16,8 +22,8 @@ export const validInstallStates = [ UNKNOWN, ]; -export const DOWNLOAD_FAILED = 'download-failed'; -export const INSTALL_FAILED = 'install-failed'; +export const DOWNLOAD_FAILED = 'DOWNLOAD_FAILED'; +export const INSTALL_FAILED = 'INSTALL_FAILED'; // Add-on types. export const API_THEME_TYPE = 'persona'; @@ -58,3 +64,36 @@ export const installEventList = [ export const INSTALL_CATEGORY = 'AMO Addon / Theme Installs'; export const UNINSTALL_CATEGORY = 'AMO Addon / Theme Uninstalls'; + +// These are events directly on mozAddonManager +// they will be fired by addons and themes that aren't +// necessarily in the disco pane. +export const globalEventLIist = [ + 'onUninstalling', + 'onInstalled', + 'onInstalling', + 'onUninstalled', + 'onEnabled', + 'onDisabled', +]; + +// Install Types +export const INSTALL_STATE = 'INSTALL_STATE'; +export const START_DOWNLOAD = 'START_DOWNLOAD'; +export const DOWNLOAD_PROGRESS = 'DOWNLOAD_PROGRESS'; +export const START_INSTALL = 'START_INSTALL'; +export const INSTALL_COMPLETE = 'INSTALL_COMPLETE'; +export const START_UNINSTALL = 'START_UNINSTALL'; +export const UNINSTALL_COMPLETE = 'UNINSTALL_COMPLETE'; +export const INSTALL_ERROR = 'INSTALL_ERROR'; + +export const acceptedInstallTypes = [ + INSTALL_STATE, + START_DOWNLOAD, + DOWNLOAD_PROGRESS, + START_INSTALL, + INSTALL_COMPLETE, + START_UNINSTALL, + UNINSTALL_COMPLETE, + INSTALL_ERROR, +]; diff --git a/src/disco/reducers/installations.js b/src/disco/reducers/installations.js index 93c2c904e37..22140e369c2 100644 --- a/src/disco/reducers/installations.js +++ b/src/disco/reducers/installations.js @@ -1,54 +1,53 @@ import { DOWNLOADING, + DOWNLOAD_PROGRESS, ERROR, INSTALLED, INSTALLING, + INSTALL_COMPLETE, + INSTALL_ERROR, + INSTALL_STATE, + START_DOWNLOAD, + START_INSTALL, + START_UNINSTALL, UNINSTALLED, UNINSTALLING, + UNINSTALL_COMPLETE, + acceptedInstallTypes, } from 'disco/constants'; -const acceptedTypes = [ - 'INSTALL_STATE', - 'START_DOWNLOAD', - 'DOWNLOAD_PROGRESS', - 'START_INSTALL', - 'INSTALL_COMPLETE', - 'START_UNINSTALL', - 'UNINSTALL_COMPLETE', - 'INSTALL_ERROR', -]; export default function installations(state = {}, { type, payload }) { - if (!acceptedTypes.includes(type)) { + if (!acceptedInstallTypes.includes(type)) { return state; } let addon; if (state[payload.guid]) { addon = {...state[payload.guid]}; } - if (type === 'INSTALL_STATE') { + if (type === INSTALL_STATE) { addon = { guid: payload.guid, url: payload.url, downloadProgress: 0, status: payload.status, }; - } else if (type === 'START_DOWNLOAD') { + } else if (type === START_DOWNLOAD) { addon.status = DOWNLOADING; - } else if (type === 'DOWNLOAD_PROGRESS') { + } else if (type === DOWNLOAD_PROGRESS) { addon.downloadProgress = payload.downloadProgress; - } else if (type === 'START_INSTALL') { + } else if (type === START_INSTALL) { addon.downloadProgress = 100; addon.status = INSTALLING; - } else if (type === 'INSTALL_COMPLETE') { + } else if (type === INSTALL_COMPLETE) { addon.status = INSTALLED; - } else if (type === 'START_UNINSTALL') { + } else if (type === START_UNINSTALL) { addon.downloadProgress = 0; addon.status = UNINSTALLING; - } else if (type === 'UNINSTALL_COMPLETE') { + } else if (type === UNINSTALL_COMPLETE) { addon.status = UNINSTALLED; /* istanbul ignore else */ - } else if (type === 'INSTALL_ERROR') { + } else if (type === INSTALL_ERROR) { addon.downloadProgress = 0; addon.status = ERROR; addon.error = payload.error; diff --git a/tests/client/disco/components/TestAddon.js b/tests/client/disco/components/TestAddon.js index d5fbbb24be7..a84d169c481 100644 --- a/tests/client/disco/components/TestAddon.js +++ b/tests/client/disco/components/TestAddon.js @@ -13,17 +13,23 @@ import { mapStateToProps, } from 'disco/components/Addon'; import { - ERROR, DOWNLOAD_FAILED, + DOWNLOAD_PROGRESS, + ERROR, + INSTALLED, INSTALL_CATEGORY, + INSTALL_COMPLETE, INSTALL_FAILED, - INSTALLED, + INSTALL_STATE, + START_DOWNLOAD, + START_INSTALL, + START_UNINSTALL, THEME_INSTALL, THEME_PREVIEW, THEME_RESET_PREVIEW, THEME_TYPE, - UNINSTALL_CATEGORY, UNINSTALLED, + UNINSTALL_CATEGORY, } from 'disco/constants'; import { stubAddonManager, getFakeI18nInst } from 'tests/client/helpers'; import I18nProvider from 'core/i18n/Provider'; @@ -232,7 +238,7 @@ describe('', () => { const handler = makeProgressHandler(dispatch, guid); handler({state: 'STATE_DOWNLOADING', progress: 300, maxProgress: 990}); assert(dispatch.calledWith({ - type: 'DOWNLOAD_PROGRESS', + type: DOWNLOAD_PROGRESS, payload: {downloadProgress: 30, guid}, })); }); @@ -243,7 +249,7 @@ describe('', () => { const handler = makeProgressHandler(dispatch, guid); handler({state: 'STATE_INSTALLING'}); assert(dispatch.calledWith({ - type: 'START_INSTALL', + type: START_INSTALL, payload: {guid}, })); }); @@ -254,7 +260,7 @@ describe('', () => { const handler = makeProgressHandler(dispatch, guid); handler({state: 'STATE_INSTALLED'}); assert(dispatch.calledWith({ - type: 'INSTALL_COMPLETE', + type: INSTALL_COMPLETE, payload: {guid}, })); }); @@ -294,7 +300,7 @@ describe('', () => { return setCurrentStatus({guid, installURL}) .then(() => { assert(dispatch.calledWith({ - type: 'INSTALL_STATE', + type: INSTALL_STATE, payload: {guid, status: INSTALLED, url: installURL}, })); }); @@ -310,7 +316,7 @@ describe('', () => { return setCurrentStatus({guid, installURL}) .then(() => { assert(dispatch.calledWith({ - type: 'INSTALL_STATE', + type: INSTALL_STATE, payload: {guid, status: INSTALLED, url: installURL}, })); }); @@ -326,7 +332,7 @@ describe('', () => { return setCurrentStatus({guid, installURL}) .then(() => { assert(dispatch.calledWith({ - type: 'INSTALL_STATE', + type: INSTALL_STATE, payload: {guid, status: UNINSTALLED, url: installURL}, })); }); @@ -341,7 +347,7 @@ describe('', () => { return setCurrentStatus({guid, installURL}) .then(() => { assert(dispatch.calledWith({ - type: 'INSTALL_STATE', + type: INSTALL_STATE, payload: {guid, status: UNINSTALLED, url: installURL}, })); }); @@ -389,7 +395,7 @@ describe('', () => { const { install } = mapDispatchToProps(dispatch, {_addonManager: fakeAddonManager}); return install({guid, installURL}) .then(() => assert(dispatch.calledWith({ - type: 'START_DOWNLOAD', + type: START_DOWNLOAD, payload: {guid}, }))); }); @@ -474,7 +480,7 @@ describe('', () => { const { uninstall } = mapDispatchToProps(dispatch, {_addonManager: fakeAddonManager}); return uninstall({guid, installURL}) .then(() => assert(dispatch.calledWith({ - type: 'START_UNINSTALL', + type: START_UNINSTALL, payload: {guid}, }))); }); @@ -492,7 +498,7 @@ describe('', () => { .then(() => { assert(spyThemeAction.calledWith(node, THEME_INSTALL)); assert(dispatch.calledWith({ - type: 'INSTALL_STATE', + type: INSTALL_STATE, payload: {guid, status: INSTALLED}, })); }); diff --git a/tests/client/disco/reducers/test_installations.js b/tests/client/disco/reducers/test_installations.js index 27a348388c9..853ebf8f5d3 100644 --- a/tests/client/disco/reducers/test_installations.js +++ b/tests/client/disco/reducers/test_installations.js @@ -1,9 +1,17 @@ import { DOWNLOADING, + DOWNLOAD_PROGRESS, ERROR, INSTALLED, + INSTALL_COMPLETE, + INSTALL_ERROR, + INSTALL_STATE, INSTALLING, + START_DOWNLOAD, + START_INSTALL, + START_UNINSTALL, UNINSTALLED, + UNINSTALL_COMPLETE, UNINSTALLING, } from 'disco/constants'; import installations from 'disco/reducers/installations'; @@ -21,7 +29,7 @@ describe('installations reducer', () => { it('adds an add-on on INSTALL_STATE', () => { assert.deepEqual( installations(undefined, { - type: 'INSTALL_STATE', + type: INSTALL_STATE, payload: { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', @@ -41,7 +49,7 @@ describe('installations reducer', () => { it('uses the add-ons status', () => { assert.deepEqual( installations(undefined, { - type: 'INSTALL_STATE', + type: INSTALL_STATE, payload: { guid: 'an-addon@me.com', url: 'https://cdn.amo/download/an-addon.xpi', @@ -69,7 +77,7 @@ describe('installations reducer', () => { }; assert.deepEqual( installations(state, { - type: 'START_DOWNLOAD', + type: START_DOWNLOAD, payload: { guid: 'my-addon@me.com', }, @@ -95,7 +103,7 @@ describe('installations reducer', () => { }; assert.deepEqual( installations(state, { - type: 'DOWNLOAD_PROGRESS', + type: DOWNLOAD_PROGRESS, payload: { guid: 'my-addon@me.com', downloadProgress: 25, @@ -122,7 +130,7 @@ describe('installations reducer', () => { }; assert.deepEqual( installations(state, { - type: 'START_INSTALL', + type: START_INSTALL, payload: { guid: 'my-addon@me.com', }, @@ -148,7 +156,7 @@ describe('installations reducer', () => { }; assert.deepEqual( installations(state, { - type: 'INSTALL_COMPLETE', + type: INSTALL_COMPLETE, payload: { guid: 'my-addon@me.com', }, @@ -174,7 +182,7 @@ describe('installations reducer', () => { }; assert.deepEqual( installations(state, { - type: 'START_UNINSTALL', + type: START_UNINSTALL, payload: { guid: 'my-addon@me.com', }, @@ -200,7 +208,7 @@ describe('installations reducer', () => { }; assert.deepEqual( installations(state, { - type: 'UNINSTALL_COMPLETE', + type: UNINSTALL_COMPLETE, payload: { guid: 'my-addon@me.com', }, @@ -226,7 +234,7 @@ describe('installations reducer', () => { }; assert.deepEqual( installations(state, { - type: 'INSTALL_ERROR', + type: INSTALL_ERROR, payload: { guid: 'my-addon@me.com', error: 'an-error', From 4d5c0de569b7d609b598ba63825c60c13b236493 Mon Sep 17 00:00:00 2001 From: Stuart Colville Date: Mon, 6 Jun 2016 23:59:18 +0100 Subject: [PATCH 2/5] Surface global events --- src/disco/constants.js | 31 +++++---- src/disco/containers/DiscoPane.js | 63 +++++++++++++++++- .../client/disco/containers/TestDiscoPane.js | 66 +++++++++++++++++-- tests/client/helpers.js | 11 ++-- 4 files changed, 150 insertions(+), 21 deletions(-) diff --git a/src/disco/constants.js b/src/disco/constants.js index 5eb7d2e2f00..24d4309cfdc 100644 --- a/src/disco/constants.js +++ b/src/disco/constants.js @@ -65,17 +65,6 @@ export const installEventList = [ export const INSTALL_CATEGORY = 'AMO Addon / Theme Installs'; export const UNINSTALL_CATEGORY = 'AMO Addon / Theme Uninstalls'; -// These are events directly on mozAddonManager -// they will be fired by addons and themes that aren't -// necessarily in the disco pane. -export const globalEventLIist = [ - 'onUninstalling', - 'onInstalled', - 'onInstalling', - 'onUninstalled', - 'onEnabled', - 'onDisabled', -]; // Install Types export const INSTALL_STATE = 'INSTALL_STATE'; @@ -97,3 +86,23 @@ export const acceptedInstallTypes = [ UNINSTALL_COMPLETE, INSTALL_ERROR, ]; + +// These are events directly on mozAddonManager +// they will be fired by addons and themes that aren't +// necessarily in the disco pane. +export const globalEvents = [ + 'onUninstalling', + 'onInstalled', + 'onInstalling', + 'onUninstalled', + 'onEnabled', + 'onDisabled', +]; + +// Global event actions +export const ON_DISABLE = 'ON_DISABLE'; +export const ON_ENABLE = 'ON_ENABLE'; +export const ON_INSTALLING = 'ON_INSTALLING'; +export const ON_UNINSTALLING = 'ON_UNINSTALLING'; +export const ON_INSTALLED = 'ON_INSTALLED'; +export const ON_UNINSTALLED = 'ON_UNINSTALLED'; diff --git a/src/disco/containers/DiscoPane.js b/src/disco/containers/DiscoPane.js index 5e5dfced0ff..6b9570c3203 100644 --- a/src/disco/containers/DiscoPane.js +++ b/src/disco/containers/DiscoPane.js @@ -5,9 +5,11 @@ import { connect } from 'react-redux'; import { asyncConnect } from 'redux-async-connect'; import { camelCaseProps } from 'core/utils'; +import config from 'config'; import { getDiscoveryAddons } from 'disco/api'; import { discoResults } from 'disco/actions'; import { loadEntities } from 'core/actions'; +import log from 'core/logger'; import Addon from 'disco/components/Addon'; import translate from 'core/i18n/translate'; @@ -16,16 +18,29 @@ import videoPoster from 'disco/img/AddOnsPoster.jpg'; import videoMp4 from 'disco/video/AddOns.mp4'; import videoWebm from 'disco/video/AddOns.webm'; +import { + globalEvents, + ON_ENABLE, + ON_DISABLE, + ON_INSTALLING, + ON_UNINSTALLING, + ON_INSTALLED, + ON_UNINSTALLED, +} from 'disco/constants'; + export class DiscoPane extends React.Component { static propTypes = { + handleGlobalEvent: PropTypes.func.isRequired, i18n: PropTypes.object.isRequired, results: PropTypes.arrayOf(PropTypes.object), AddonComponent: PropTypes.object.isRequred, + mozAddonManager: PropTypes.object.isRequired, } static defaultProps = { AddonComponent: Addon, + mozAddonManager: config.get('server') ? {} : navigator.mozAddonManager, } constructor() { @@ -33,6 +48,17 @@ export class DiscoPane extends React.Component { this.state = {showVideo: false}; } + componentDidMount() { + const { handleGlobalEvent, mozAddonManager } = this.props; + if (mozAddonManager && mozAddonManager.addEventListener) { + for (const event of globalEvents) { + mozAddonManager.addEventListener(event, handleGlobalEvent); + } + } else { + log.info('mozAddonManager.addEventListener not available'); + } + } + showVideo = (e) => { e.preventDefault(); this.setState({showVideo: true}); @@ -109,7 +135,42 @@ export function mapStateToProps(state) { }; } +export function mapDispatchToProps(dispatch, { _config = config } = {}) { + if (_config.get('server')) { + return {}; + } + return { + handleGlobalEvent(e) { + const { id, type, needsRestart } = e; + const payload = { guid: id, needsRestart }; + log.info('Event received', type, id, needsRestart); + switch (type) { + case 'onDisabled': + dispatch({type: ON_DISABLE, payload}); + break; + case 'onEnabled': + dispatch({type: ON_ENABLE, payload}); + break; + case 'onInstalling': + dispatch({type: ON_INSTALLING, payload}); + break; + case 'onInstalled': + dispatch({type: ON_INSTALLED, payload}); + break; + case 'onUninstalling': + dispatch({type: ON_UNINSTALLING, payload}); + break; + case 'onUninstalled': + dispatch({type: ON_UNINSTALLED, payload}); + break; + default: + throw new Error(`Unknown global event: ${type}`); + } + }, + }; +} + export default asyncConnect([{ deferred: true, promise: loadDataIfNeeded, -}])(connect(mapStateToProps)(translate()(DiscoPane))); +}])(connect(mapStateToProps, mapDispatchToProps)(translate()(DiscoPane))); diff --git a/tests/client/disco/containers/TestDiscoPane.js b/tests/client/disco/containers/TestDiscoPane.js index 18ad959887c..d2148e0c548 100644 --- a/tests/client/disco/containers/TestDiscoPane.js +++ b/tests/client/disco/containers/TestDiscoPane.js @@ -5,9 +5,18 @@ import { Provider } from 'react-redux'; import { discoResults } from 'disco/actions'; import * as discoApi from 'disco/api'; import createStore from 'disco/store'; -import { EXTENSION_TYPE } from 'disco/constants'; +import { + EXTENSION_TYPE, + ON_DISABLE, + ON_ENABLE, + ON_INSTALLED, + ON_INSTALLING, + ON_UNINSTALLED, + ON_UNINSTALLING, + globalEvents, +} from 'disco/constants'; import * as helpers from 'disco/containers/DiscoPane'; -import { getFakeI18nInst, MockedSubComponent } from 'tests/client/helpers'; +import { getFakeI18nInst, MockedSubComponent, stubAddonManager } from 'tests/client/helpers'; import { loadEntities } from 'core/actions'; import I18nProvider from 'core/i18n/Provider'; @@ -17,7 +26,7 @@ const { DiscoPane } = helpers; describe('AddonPage', () => { - function render() { + function render(props) { const store = createStore({ addons: {foo: {type: EXTENSION_TYPE}}, discoResults: [{addon: 'foo'}], @@ -29,7 +38,8 @@ describe('AddonPage', () => { return findDOMNode(renderIntoDocument( - + )); @@ -98,4 +108,52 @@ describe('AddonPage', () => { assert.deepEqual(props.results, [{slug: 'two', addon: 'two'}]); }); }); + + describe('mapDispatchToProps', () => { + const eventMap = { + onDisabled: ON_DISABLE, + onEnabled: ON_ENABLE, + onInstalling: ON_INSTALLING, + onInstalled: ON_INSTALLED, + onUninstalling: ON_UNINSTALLING, + onUninstalled: ON_UNINSTALLED, + }; + + Object.keys(eventMap).forEach((event) => { + const action = eventMap[event]; + it(`dispatches ${action}`, () => { + const dispatch = sinon.spy(); + const { handleGlobalEvent } = helpers.mapDispatchToProps(dispatch); + const id = 'foo@whatever'; + const needsRestart = false; + handleGlobalEvent({id, type: event, needsRestart}); + assert(dispatch.calledWith({ + type: action, + payload: {guid: id, needsRestart}, + }), `Calls ${action} for ${event}`); + }); + }); + + it('throws on unknown event', () => assert.throws(() => { + const dispatch = sinon.spy(); + const { handleGlobalEvent } = helpers.mapDispatchToProps(dispatch); + handleGlobalEvent({type: 'whateve'}); + }, Error, /Unknown global event/)); + + it('is empty when there is no navigator', () => { + const configStub = { + get: sinon.stub().returns(true), + }; + assert.deepEqual( + helpers.mapDispatchToProps(sinon.spy(), { _config: configStub }), {}); + }); + }); + + describe('componentDidMount', () => { + it('sets events', () => { + const fakeAddonManager = stubAddonManager(); + render({mozAddonManager: fakeAddonManager}); + assert.equal(fakeAddonManager.addEventListener.callCount, globalEvents.length); + }); + }); }); diff --git a/tests/client/helpers.js b/tests/client/helpers.js index 780597345d8..8834c14672a 100644 --- a/tests/client/helpers.js +++ b/tests/client/helpers.js @@ -18,11 +18,12 @@ export function findByTag(root, tag) { } export function stubAddonManager({ getAddon = Promise.resolve({type: 'addon'}) } = {}) { - const fakeAddonManager = {}; - fakeAddonManager.getAddon = sinon.stub().returns(getAddon); - fakeAddonManager.install = sinon.stub().returns(Promise.resolve()); - fakeAddonManager.uninstall = sinon.stub().returns(Promise.resolve()); - return fakeAddonManager; + return { + getAddon: sinon.stub().returns(getAddon), + install: sinon.stub().returns(Promise.resolve()), + uninstall: sinon.stub().returns(Promise.resolve()), + addEventListener: sinon.stub(), + }; } export function unexpectedSuccess() { From 399a139ec76aa33c772a6cbaa1799abfe885fba0 Mon Sep 17 00:00:00 2001 From: Stuart Colville Date: Tue, 7 Jun 2016 12:26:07 +0100 Subject: [PATCH 3/5] Move change listeners + filtering to addonManager --- src/disco/addonManager.js | 52 ++++++++++++++++- src/disco/containers/DiscoPane.js | 57 ++++--------------- tests/client/disco/TestAddonManager.js | 46 ++++++++++++++- .../client/disco/containers/TestDiscoPane.js | 44 +++----------- 4 files changed, 113 insertions(+), 86 deletions(-) diff --git a/src/disco/addonManager.js b/src/disco/addonManager.js index 6f9209f0efb..c3acd2b0f15 100644 --- a/src/disco/addonManager.js +++ b/src/disco/addonManager.js @@ -1,4 +1,15 @@ -import { installEventList } from 'disco/constants'; +import log from 'core/logger'; + +import { + globalEvents, + installEventList, + ON_ENABLE, + ON_DISABLE, + ON_INSTALLING, + ON_UNINSTALLING, + ON_INSTALLED, + ON_UNINSTALLED, +} from 'disco/constants'; export function getAddon(guid, {_mozAddonManager = window.navigator.mozAddonManager} = {}) { @@ -36,3 +47,42 @@ export function uninstall(guid, {_mozAddonManager = window.navigator.mozAddonMan return; }); } + +export function addChangeListeners(callback, mozAddonManager) { + function handleChangeEvent(e) { + const { id, type, needsRestart } = e; + const payload = { guid: id, needsRestart }; + log.info('Event received', type, id, needsRestart); + switch (type) { + case 'onDisabled': + callback({type: ON_DISABLE, payload}); + break; + case 'onEnabled': + callback({type: ON_ENABLE, payload}); + break; + case 'onInstalling': + callback({type: ON_INSTALLING, payload}); + break; + case 'onInstalled': + callback({type: ON_INSTALLED, payload}); + break; + case 'onUninstalling': + callback({type: ON_UNINSTALLING, payload}); + break; + case 'onUninstalled': + callback({type: ON_UNINSTALLED, payload}); + break; + default: + throw new Error(`Unknown global event: ${type}`); + } + } + + if (mozAddonManager && mozAddonManager.addEventListener) { + for (const event of globalEvents) { + mozAddonManager.addEventListener(event, handleChangeEvent); + } + } else { + log.info('mozAddonManager.addEventListener not available'); + } + return handleChangeEvent; +} diff --git a/src/disco/containers/DiscoPane.js b/src/disco/containers/DiscoPane.js index 6b9570c3203..7f5242de300 100644 --- a/src/disco/containers/DiscoPane.js +++ b/src/disco/containers/DiscoPane.js @@ -9,7 +9,7 @@ import config from 'config'; import { getDiscoveryAddons } from 'disco/api'; import { discoResults } from 'disco/actions'; import { loadEntities } from 'core/actions'; -import log from 'core/logger'; +import { addChangeListeners } from 'disco/addonManager'; import Addon from 'disco/components/Addon'; import translate from 'core/i18n/translate'; @@ -18,29 +18,21 @@ import videoPoster from 'disco/img/AddOnsPoster.jpg'; import videoMp4 from 'disco/video/AddOns.mp4'; import videoWebm from 'disco/video/AddOns.webm'; -import { - globalEvents, - ON_ENABLE, - ON_DISABLE, - ON_INSTALLING, - ON_UNINSTALLING, - ON_INSTALLED, - ON_UNINSTALLED, -} from 'disco/constants'; - export class DiscoPane extends React.Component { static propTypes = { handleGlobalEvent: PropTypes.func.isRequired, i18n: PropTypes.object.isRequired, results: PropTypes.arrayOf(PropTypes.object), - AddonComponent: PropTypes.object.isRequred, - mozAddonManager: PropTypes.object.isRequired, + AddonComponent: PropTypes.func.isRequred, + _addChangeListeners: PropTypes.func, + mozAddonManager: PropTypes.object, } static defaultProps = { AddonComponent: Addon, mozAddonManager: config.get('server') ? {} : navigator.mozAddonManager, + _addChangeListeners: addChangeListeners, } constructor() { @@ -49,14 +41,9 @@ export class DiscoPane extends React.Component { } componentDidMount() { - const { handleGlobalEvent, mozAddonManager } = this.props; - if (mozAddonManager && mozAddonManager.addEventListener) { - for (const event of globalEvents) { - mozAddonManager.addEventListener(event, handleGlobalEvent); - } - } else { - log.info('mozAddonManager.addEventListener not available'); - } + const { _addChangeListeners, handleGlobalEvent, mozAddonManager } = this.props; + // Use addonManager.addChangeListener to setup and filter events. + _addChangeListeners(handleGlobalEvent, mozAddonManager); } showVideo = (e) => { @@ -140,32 +127,8 @@ export function mapDispatchToProps(dispatch, { _config = config } = {}) { return {}; } return { - handleGlobalEvent(e) { - const { id, type, needsRestart } = e; - const payload = { guid: id, needsRestart }; - log.info('Event received', type, id, needsRestart); - switch (type) { - case 'onDisabled': - dispatch({type: ON_DISABLE, payload}); - break; - case 'onEnabled': - dispatch({type: ON_ENABLE, payload}); - break; - case 'onInstalling': - dispatch({type: ON_INSTALLING, payload}); - break; - case 'onInstalled': - dispatch({type: ON_INSTALLED, payload}); - break; - case 'onUninstalling': - dispatch({type: ON_UNINSTALLING, payload}); - break; - case 'onUninstalled': - dispatch({type: ON_UNINSTALLED, payload}); - break; - default: - throw new Error(`Unknown global event: ${type}`); - } + handleGlobalEvent({type, payload}) { + dispatch({type, payload}); }, }; } diff --git a/tests/client/disco/TestAddonManager.js b/tests/client/disco/TestAddonManager.js index 0bf1bcf1952..8f1a45166e4 100644 --- a/tests/client/disco/TestAddonManager.js +++ b/tests/client/disco/TestAddonManager.js @@ -1,6 +1,14 @@ import * as addonManager from 'disco/addonManager'; -import { installEventList } from 'disco/constants'; import { unexpectedSuccess } from 'tests/client/helpers'; +import { + installEventList, + ON_ENABLE, + ON_DISABLE, + ON_INSTALLING, + ON_UNINSTALLING, + ON_INSTALLED, + ON_UNINSTALLED, +} from 'disco/constants'; describe('addonManager', () => { @@ -23,6 +31,7 @@ describe('addonManager', () => { fakeMozAddonManager = { createInstall: sinon.stub(), getAddonByID: sinon.stub(), + addEventListener: sinon.stub(), }; fakeMozAddonManager.createInstall.returns(Promise.resolve(fakeInstallObj)); }); @@ -106,4 +115,39 @@ describe('addonManager', () => { return addonManager.uninstall('test-id', {_mozAddonManager: fakeMozAddonManager}); }); }); + + describe('addChangeListener', () => { + const fakeEventCallback = sinon.stub(); + fakeMozAddonManager = { + addEventListener: sinon.stub(), + }; + + const handleChangeEvent = + addonManager.addChangeListeners(fakeEventCallback, fakeMozAddonManager); + const eventMap = { + onDisabled: ON_DISABLE, + onEnabled: ON_ENABLE, + onInstalling: ON_INSTALLING, + onInstalled: ON_INSTALLED, + onUninstalling: ON_UNINSTALLING, + onUninstalled: ON_UNINSTALLED, + }; + + Object.keys(eventMap).forEach((event) => { + const action = eventMap[event]; + it(`dispatches ${action}`, () => { + const id = 'foo@whatever'; + const needsRestart = false; + handleChangeEvent({id, needsRestart, type: event}); + assert(fakeEventCallback.calledWith({ + type: action, + payload: {guid: id, needsRestart}, + }), `Calls ${action} for ${event}`); + }); + }); + + it('throws on unknown event', () => assert.throws(() => { + handleChangeEvent({type: 'whatevs'}); + }, Error, /Unknown global event/)); + }); }); diff --git a/tests/client/disco/containers/TestDiscoPane.js b/tests/client/disco/containers/TestDiscoPane.js index d2148e0c548..5dee104cd94 100644 --- a/tests/client/disco/containers/TestDiscoPane.js +++ b/tests/client/disco/containers/TestDiscoPane.js @@ -5,16 +5,7 @@ import { Provider } from 'react-redux'; import { discoResults } from 'disco/actions'; import * as discoApi from 'disco/api'; import createStore from 'disco/store'; -import { - EXTENSION_TYPE, - ON_DISABLE, - ON_ENABLE, - ON_INSTALLED, - ON_INSTALLING, - ON_UNINSTALLED, - ON_UNINSTALLING, - globalEvents, -} from 'disco/constants'; +import { EXTENSION_TYPE, globalEvents } from 'disco/constants'; import * as helpers from 'disco/containers/DiscoPane'; import { getFakeI18nInst, MockedSubComponent, stubAddonManager } from 'tests/client/helpers'; import { loadEntities } from 'core/actions'; @@ -110,35 +101,14 @@ describe('AddonPage', () => { }); describe('mapDispatchToProps', () => { - const eventMap = { - onDisabled: ON_DISABLE, - onEnabled: ON_ENABLE, - onInstalling: ON_INSTALLING, - onInstalled: ON_INSTALLED, - onUninstalling: ON_UNINSTALLING, - onUninstalled: ON_UNINSTALLED, - }; - - Object.keys(eventMap).forEach((event) => { - const action = eventMap[event]; - it(`dispatches ${action}`, () => { - const dispatch = sinon.spy(); - const { handleGlobalEvent } = helpers.mapDispatchToProps(dispatch); - const id = 'foo@whatever'; - const needsRestart = false; - handleGlobalEvent({id, type: event, needsRestart}); - assert(dispatch.calledWith({ - type: action, - payload: {guid: id, needsRestart}, - }), `Calls ${action} for ${event}`); - }); - }); - - it('throws on unknown event', () => assert.throws(() => { + it('calls dispatch when handleGlobalEvent is called with data', () => { const dispatch = sinon.spy(); const { handleGlobalEvent } = helpers.mapDispatchToProps(dispatch); - handleGlobalEvent({type: 'whateve'}); - }, Error, /Unknown global event/)); + const type = 'foo'; + const payload = {id: 'whatever'}; + handleGlobalEvent({type, payload}); + assert.ok(dispatch.calledWith({type, payload})); + }); it('is empty when there is no navigator', () => { const configStub = { From 32918b2e2d9cea0f387244b0ee8a4f7c7923376b Mon Sep 17 00:00:00 2001 From: Stuart Colville Date: Tue, 7 Jun 2016 18:54:50 +0100 Subject: [PATCH 4/5] Call existing events --- src/disco/addonManager.js | 34 +++---------------- src/disco/components/InstallButton.js | 2 +- src/disco/constants.js | 29 +++++++--------- src/disco/containers/DiscoPane.js | 5 +-- tests/client/disco/TestAddonManager.js | 27 ++++----------- .../client/disco/containers/TestDiscoPane.js | 7 ++-- 6 files changed, 30 insertions(+), 74 deletions(-) diff --git a/src/disco/addonManager.js b/src/disco/addonManager.js index c3acd2b0f15..a160a9c04a3 100644 --- a/src/disco/addonManager.js +++ b/src/disco/addonManager.js @@ -2,13 +2,8 @@ import log from 'core/logger'; import { globalEvents, + globalEventStatusMap, installEventList, - ON_ENABLE, - ON_DISABLE, - ON_INSTALLING, - ON_UNINSTALLING, - ON_INSTALLED, - ON_UNINSTALLED, } from 'disco/constants'; @@ -51,30 +46,11 @@ export function uninstall(guid, {_mozAddonManager = window.navigator.mozAddonMan export function addChangeListeners(callback, mozAddonManager) { function handleChangeEvent(e) { const { id, type, needsRestart } = e; - const payload = { guid: id, needsRestart }; - log.info('Event received', type, id, needsRestart); - switch (type) { - case 'onDisabled': - callback({type: ON_DISABLE, payload}); - break; - case 'onEnabled': - callback({type: ON_ENABLE, payload}); - break; - case 'onInstalling': - callback({type: ON_INSTALLING, payload}); - break; - case 'onInstalled': - callback({type: ON_INSTALLED, payload}); - break; - case 'onUninstalling': - callback({type: ON_UNINSTALLING, payload}); - break; - case 'onUninstalled': - callback({type: ON_UNINSTALLED, payload}); - break; - default: - throw new Error(`Unknown global event: ${type}`); + log.info('Event received', {type, id, needsRestart}); + if (globalEventStatusMap.hasOwnProperty(type)) { + return callback({guid: id, status: globalEventStatusMap[type], needsRestart}); } + throw new Error(`Unknown global event: ${type}`); } if (mozAddonManager && mozAddonManager.addEventListener) { diff --git a/src/disco/components/InstallButton.js b/src/disco/components/InstallButton.js index bf4566b1e55..51097a5a85e 100644 --- a/src/disco/components/InstallButton.js +++ b/src/disco/components/InstallButton.js @@ -60,7 +60,7 @@ export class InstallButton extends React.Component { const isInstalled = status === INSTALLED; const isDisabled = status === UNKNOWN; const isDownloading = status === DOWNLOADING; - const switchClasses = `switch ${status}`; + const switchClasses = `switch ${status.toLowerCase()}`; const identifier = `install-button-${slug}`; return ( diff --git a/src/disco/constants.js b/src/disco/constants.js index 24d4309cfdc..07187c9f7a8 100644 --- a/src/disco/constants.js +++ b/src/disco/constants.js @@ -12,6 +12,7 @@ export const ENABLED = 'ENABLED'; export const validInstallStates = [ DISABLED, + ENABLED, DOWNLOADING, ENABLED, ERROR, @@ -87,22 +88,16 @@ export const acceptedInstallTypes = [ INSTALL_ERROR, ]; -// These are events directly on mozAddonManager +export const globalEventStatusMap = { + onDisabled: DISABLED, + onEnabled: ENABLED, + onInstalling: INSTALLING, + onInstalled: INSTALLED, + onUninstalling: UNINSTALLING, + onUninstalled: UNINSTALLED, +}; + +// The events here are set directly on mozAddonManager // they will be fired by addons and themes that aren't // necessarily in the disco pane. -export const globalEvents = [ - 'onUninstalling', - 'onInstalled', - 'onInstalling', - 'onUninstalled', - 'onEnabled', - 'onDisabled', -]; - -// Global event actions -export const ON_DISABLE = 'ON_DISABLE'; -export const ON_ENABLE = 'ON_ENABLE'; -export const ON_INSTALLING = 'ON_INSTALLING'; -export const ON_UNINSTALLING = 'ON_UNINSTALLING'; -export const ON_INSTALLED = 'ON_INSTALLED'; -export const ON_UNINSTALLED = 'ON_UNINSTALLED'; +export const globalEvents = Object.keys(globalEventStatusMap); diff --git a/src/disco/containers/DiscoPane.js b/src/disco/containers/DiscoPane.js index 7f5242de300..9c463910f01 100644 --- a/src/disco/containers/DiscoPane.js +++ b/src/disco/containers/DiscoPane.js @@ -10,6 +10,7 @@ import { getDiscoveryAddons } from 'disco/api'; import { discoResults } from 'disco/actions'; import { loadEntities } from 'core/actions'; import { addChangeListeners } from 'disco/addonManager'; +import { INSTALL_STATE } from 'disco/constants'; import Addon from 'disco/components/Addon'; import translate from 'core/i18n/translate'; @@ -127,8 +128,8 @@ export function mapDispatchToProps(dispatch, { _config = config } = {}) { return {}; } return { - handleGlobalEvent({type, payload}) { - dispatch({type, payload}); + handleGlobalEvent(payload) { + dispatch({type: INSTALL_STATE, payload}); }, }; } diff --git a/tests/client/disco/TestAddonManager.js b/tests/client/disco/TestAddonManager.js index 8f1a45166e4..a49286a49e5 100644 --- a/tests/client/disco/TestAddonManager.js +++ b/tests/client/disco/TestAddonManager.js @@ -1,13 +1,8 @@ import * as addonManager from 'disco/addonManager'; import { unexpectedSuccess } from 'tests/client/helpers'; import { + globalEventStatusMap, installEventList, - ON_ENABLE, - ON_DISABLE, - ON_INSTALLING, - ON_UNINSTALLING, - ON_INSTALLED, - ON_UNINSTALLED, } from 'disco/constants'; @@ -124,25 +119,15 @@ describe('addonManager', () => { const handleChangeEvent = addonManager.addChangeListeners(fakeEventCallback, fakeMozAddonManager); - const eventMap = { - onDisabled: ON_DISABLE, - onEnabled: ON_ENABLE, - onInstalling: ON_INSTALLING, - onInstalled: ON_INSTALLED, - onUninstalling: ON_UNINSTALLING, - onUninstalled: ON_UNINSTALLED, - }; - Object.keys(eventMap).forEach((event) => { - const action = eventMap[event]; - it(`dispatches ${action}`, () => { + Object.keys(globalEventStatusMap).forEach((event) => { + const status = globalEventStatusMap[event]; + it(`calls callback with status ${status}`, () => { const id = 'foo@whatever'; const needsRestart = false; handleChangeEvent({id, needsRestart, type: event}); - assert(fakeEventCallback.calledWith({ - type: action, - payload: {guid: id, needsRestart}, - }), `Calls ${action} for ${event}`); + assert.ok(fakeEventCallback.calledWith({guid: id, needsRestart, status}), + `Calls callback with "${status}" for "${event}"`); }); }); diff --git a/tests/client/disco/containers/TestDiscoPane.js b/tests/client/disco/containers/TestDiscoPane.js index 5dee104cd94..acbe186dd34 100644 --- a/tests/client/disco/containers/TestDiscoPane.js +++ b/tests/client/disco/containers/TestDiscoPane.js @@ -5,7 +5,7 @@ import { Provider } from 'react-redux'; import { discoResults } from 'disco/actions'; import * as discoApi from 'disco/api'; import createStore from 'disco/store'; -import { EXTENSION_TYPE, globalEvents } from 'disco/constants'; +import { EXTENSION_TYPE, INSTALL_STATE, globalEvents } from 'disco/constants'; import * as helpers from 'disco/containers/DiscoPane'; import { getFakeI18nInst, MockedSubComponent, stubAddonManager } from 'tests/client/helpers'; import { loadEntities } from 'core/actions'; @@ -104,10 +104,9 @@ describe('AddonPage', () => { it('calls dispatch when handleGlobalEvent is called with data', () => { const dispatch = sinon.spy(); const { handleGlobalEvent } = helpers.mapDispatchToProps(dispatch); - const type = 'foo'; const payload = {id: 'whatever'}; - handleGlobalEvent({type, payload}); - assert.ok(dispatch.calledWith({type, payload})); + handleGlobalEvent(payload); + assert.ok(dispatch.calledWith({type: INSTALL_STATE, payload})); }); it('is empty when there is no navigator', () => { From fc07ce9afe163237d61f45febb1ab6e70c2de8c0 Mon Sep 17 00:00:00 2001 From: Stuart Colville Date: Wed, 8 Jun 2016 09:26:47 +0100 Subject: [PATCH 5/5] Rename stubAddonManager --- tests/client/disco/components/TestAddon.js | 26 +++++++++---------- .../client/disco/containers/TestDiscoPane.js | 10 ++++--- tests/client/helpers.js | 4 +-- .../client/search/containers/TestAddonPage.js | 6 +---- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/tests/client/disco/components/TestAddon.js b/tests/client/disco/components/TestAddon.js index a84d169c481..b830fcf0e3b 100644 --- a/tests/client/disco/components/TestAddon.js +++ b/tests/client/disco/components/TestAddon.js @@ -31,7 +31,7 @@ import { UNINSTALLED, UNINSTALL_CATEGORY, } from 'disco/constants'; -import { stubAddonManager, getFakeI18nInst } from 'tests/client/helpers'; +import { getFakeAddonManagerWrapper, getFakeI18nInst } from 'tests/client/helpers'; import I18nProvider from 'core/i18n/Provider'; import translate from 'core/i18n/translate'; @@ -296,7 +296,7 @@ describe('', () => { const guid = '@foo'; const installURL = 'http://the.url'; const { setCurrentStatus } = mapDispatchToProps( - dispatch, {_addonManager: stubAddonManager()}); + dispatch, {_addonManager: getFakeAddonManagerWrapper()}); return setCurrentStatus({guid, installURL}) .then(() => { assert(dispatch.calledWith({ @@ -307,7 +307,7 @@ describe('', () => { }); it('sets the status to INSTALLED when an installed theme is found', () => { - const fakeAddonManager = stubAddonManager( + const fakeAddonManager = getFakeAddonManagerWrapper( {getAddon: Promise.resolve({type: THEME_TYPE, isEnabled: true})}); const dispatch = sinon.spy(); const guid = '@foo'; @@ -323,7 +323,7 @@ describe('', () => { }); it('sets the status to UNINSTALLED when an uninstalled theme is found', () => { - const fakeAddonManager = stubAddonManager( + const fakeAddonManager = getFakeAddonManagerWrapper( {getAddon: Promise.resolve({type: THEME_TYPE, isEnabled: false})}); const dispatch = sinon.spy(); const guid = '@foo'; @@ -339,7 +339,7 @@ describe('', () => { }); it('sets the status to UNINSTALLED when not found', () => { - const fakeAddonManager = stubAddonManager({getAddon: Promise.reject()}); + const fakeAddonManager = getFakeAddonManagerWrapper({getAddon: Promise.reject()}); const dispatch = sinon.spy(); const guid = '@foo'; const installURL = 'http://the.url'; @@ -359,7 +359,7 @@ describe('', () => { const installURL = 'https://mysite.com/download.xpi'; it('calls addonManager.install()', () => { - const fakeAddonManager = stubAddonManager(); + const fakeAddonManager = getFakeAddonManagerWrapper(); const dispatch = sinon.spy(); const { install } = mapDispatchToProps(dispatch, {_addonManager: fakeAddonManager}); return install({guid, installURL}) @@ -369,7 +369,7 @@ describe('', () => { }); it('tracks an addon install', () => { - const fakeAddonManager = stubAddonManager(); + const fakeAddonManager = getFakeAddonManagerWrapper(); const name = 'hai-addon'; const type = 'extension'; const dispatch = sinon.spy(); @@ -390,7 +390,7 @@ describe('', () => { it('should dispatch START_DOWNLOAD', () => { - const fakeAddonManager = stubAddonManager(); + const fakeAddonManager = getFakeAddonManagerWrapper(); const dispatch = sinon.spy(); const { install } = mapDispatchToProps(dispatch, {_addonManager: fakeAddonManager}); return install({guid, installURL}) @@ -406,7 +406,7 @@ describe('', () => { const installURL = 'https://mysite.com/download.xpi'; it('calls addonManager.uninstall()', () => { - const fakeAddonManager = stubAddonManager(); + const fakeAddonManager = getFakeAddonManagerWrapper(); const dispatch = sinon.spy(); const { uninstall } = mapDispatchToProps(dispatch, {_addonManager: fakeAddonManager}); return uninstall({guid, installURL}) @@ -416,7 +416,7 @@ describe('', () => { }); it('tracks an addon uninstall', () => { - const fakeAddonManager = stubAddonManager(); + const fakeAddonManager = getFakeAddonManagerWrapper(); const dispatch = sinon.spy(); const name = 'whatevs'; const type = 'extension'; @@ -436,7 +436,7 @@ describe('', () => { }); it('tracks a theme uninstall', () => { - const fakeAddonManager = stubAddonManager(); + const fakeAddonManager = getFakeAddonManagerWrapper(); const dispatch = sinon.spy(); const name = 'whatevs'; const fakeTracking = { @@ -455,7 +455,7 @@ describe('', () => { }); it('tracks a unknown type uninstall', () => { - const fakeAddonManager = stubAddonManager(); + const fakeAddonManager = getFakeAddonManagerWrapper(); const dispatch = sinon.spy(); const name = 'whatevs'; const type = 'foo'; @@ -475,7 +475,7 @@ describe('', () => { }); it('should dispatch START_UNINSTALL', () => { - const fakeAddonManager = stubAddonManager(); + const fakeAddonManager = getFakeAddonManagerWrapper(); const dispatch = sinon.spy(); const { uninstall } = mapDispatchToProps(dispatch, {_addonManager: fakeAddonManager}); return uninstall({guid, installURL}) diff --git a/tests/client/disco/containers/TestDiscoPane.js b/tests/client/disco/containers/TestDiscoPane.js index acbe186dd34..e81bf669472 100644 --- a/tests/client/disco/containers/TestDiscoPane.js +++ b/tests/client/disco/containers/TestDiscoPane.js @@ -7,7 +7,7 @@ import * as discoApi from 'disco/api'; import createStore from 'disco/store'; import { EXTENSION_TYPE, INSTALL_STATE, globalEvents } from 'disco/constants'; import * as helpers from 'disco/containers/DiscoPane'; -import { getFakeI18nInst, MockedSubComponent, stubAddonManager } from 'tests/client/helpers'; +import { getFakeI18nInst, MockedSubComponent } from 'tests/client/helpers'; import { loadEntities } from 'core/actions'; import I18nProvider from 'core/i18n/Provider'; @@ -120,9 +120,11 @@ describe('AddonPage', () => { describe('componentDidMount', () => { it('sets events', () => { - const fakeAddonManager = stubAddonManager(); - render({mozAddonManager: fakeAddonManager}); - assert.equal(fakeAddonManager.addEventListener.callCount, globalEvents.length); + const fakeMozAddonManager = { + addEventListener: sinon.stub(), + }; + render({mozAddonManager: fakeMozAddonManager}); + assert.equal(fakeMozAddonManager.addEventListener.callCount, globalEvents.length); }); }); }); diff --git a/tests/client/helpers.js b/tests/client/helpers.js index 8834c14672a..692583e1c19 100644 --- a/tests/client/helpers.js +++ b/tests/client/helpers.js @@ -17,12 +17,12 @@ export function findByTag(root, tag) { return matches[0]; } -export function stubAddonManager({ getAddon = Promise.resolve({type: 'addon'}) } = {}) { +export function getFakeAddonManagerWrapper({ getAddon = Promise.resolve({type: 'addon'}) } = {}) { return { getAddon: sinon.stub().returns(getAddon), install: sinon.stub().returns(Promise.resolve()), uninstall: sinon.stub().returns(Promise.resolve()), - addEventListener: sinon.stub(), + addChangeListerners: sinon.stub(), }; } diff --git a/tests/client/search/containers/TestAddonPage.js b/tests/client/search/containers/TestAddonPage.js index d036f3880f5..69bf983aa9f 100644 --- a/tests/client/search/containers/TestAddonPage.js +++ b/tests/client/search/containers/TestAddonPage.js @@ -6,7 +6,7 @@ import AddonPage, { findAddon, loadAddonIfNeeded } from 'search/containers/Addon import createStore from 'search/store'; import * as actions from 'core/actions'; import * as api from 'core/api'; -import { stubAddonManager, unexpectedSuccess } from 'tests/client/helpers'; +import { unexpectedSuccess } from 'tests/client/helpers'; describe('AddonPage', () => { const basicAddon = { @@ -22,10 +22,6 @@ describe('AddonPage', () => { review_url: 'https://addons.mozilla.org/en-US/editors/review/1865', }; - beforeEach(() => { - stubAddonManager(); - }); - function render({props, state}) { const store = createStore(state); return findDOMNode(renderIntoDocument(