diff --git a/src/disco/api.js b/src/disco/api.js index 073aed9f019..05c7aa76b33 100644 --- a/src/disco/api.js +++ b/src/disco/api.js @@ -1,10 +1,18 @@ import { Schema, arrayOf } from 'normalizr'; -import { addon, callApi } from 'core/api'; +import { callApi } from 'core/api'; -export const discoResult = new Schema('discoResults', {idAttribute: (result) => result.addon.slug}); -discoResult.addon = addon; +export function getGuid(result) { + if (result.type === 'persona') { + return `${result.id}@personas.mozilla.org`; + } + return result.guid; +} +export const discoResult = + new Schema('discoResults', {idAttribute: (result) => getGuid(result.addon)}); +export const addon = new Schema('addons', {idAttribute: getGuid}); +discoResult.addon = addon; export function getDiscoveryAddons({ api }) { return callApi({ diff --git a/src/disco/components/Addon.js b/src/disco/components/Addon.js index d89ade0d1de..8bd873fea2d 100644 --- a/src/disco/components/Addon.js +++ b/src/disco/components/Addon.js @@ -34,6 +34,7 @@ export class Addon extends React.Component { editorialDescription: PropTypes.string.isRequired, errorMessage: PropTypes.string, footerURL: PropTypes.string, + guid: PropTypes.string.isRequired, headerURL: PropTypes.string, heading: PropTypes.string.isRequired, i18n: PropTypes.string.isRequired, @@ -41,7 +42,6 @@ export class Addon extends React.Component { id: PropTypes.string.isRequired, previewURL: PropTypes.string, name: PropTypes.string.isRequired, - slug: PropTypes.string.isRequired, status: PropTypes.oneOf(validInstallStates).isRequired, textcolor: PropTypes.string, themeAction: PropTypes.func, @@ -117,7 +117,7 @@ export class Addon extends React.Component { } render() { - const { heading, slug, type } = this.props; + const { guid, heading, type } = this.props; if (!validAddonTypes.includes(type)) { throw new Error(`Invalid addon type "${type}"`); @@ -141,7 +141,7 @@ export class Addon extends React.Component { {this.getDescription()}
- +
diff --git a/src/disco/containers/InstallButton.js b/src/disco/containers/InstallButton.js index 9bf996fece8..7de8e9e1475 100644 --- a/src/disco/containers/InstallButton.js +++ b/src/disco/containers/InstallButton.js @@ -24,7 +24,7 @@ import 'disco/css/InstallButton.scss'; export class InstallButton extends React.Component { static propTypes = { handleChange: PropTypes.func, - guid: PropTypes.string, + guid: PropTypes.string.isRequired, install: PropTypes.func.isRequired, installTheme: PropTypes.func.isRequired, i18n: PropTypes.object.isRequired, @@ -45,20 +45,19 @@ export class InstallButton extends React.Component { } componentDidMount() { - const { guid, installURL, setInitialStatus, slug } = this.props; - setInitialStatus({guid, installURL, slug}); + const { guid, installURL, setInitialStatus } = this.props; + setInitialStatus({guid, installURL}); } handleClick = (e) => { e.preventDefault(); - const { guid, install, installURL, name, slug, - status, installTheme, type, uninstall } = this.props; + const { guid, install, installURL, name, status, installTheme, type, uninstall } = this.props; if (type === THEME_TYPE && status === UNINSTALLED) { - installTheme(this.refs.themeData, slug, name); + installTheme(this.refs.themeData, guid, name); } else if (status === UNINSTALLED) { - install({ guid, installURL, slug, name }); + install({ guid, installURL, name }); } else if (status === INSTALLED) { - uninstall({ guid, installURL, slug, name, type }); + uninstall({ guid, installURL, name, type }); } } @@ -97,21 +96,21 @@ export class InstallButton extends React.Component { } export function mapStateToProps(state, ownProps) { - const installation = state.installations[ownProps.slug] || {}; - const addon = state.addons[ownProps.slug] || {}; + const installation = state.installations[ownProps.guid] || {}; + const addon = state.addons[ownProps.guid] || {}; return {...installation, ...addon}; } -export function makeProgressHandler(dispatch, slug) { +export function makeProgressHandler(dispatch, guid) { return (addonInstall) => { if (addonInstall.state === 'STATE_DOWNLOADING') { const downloadProgress = parseInt( 100 * addonInstall.progress / addonInstall.maxProgress, 10); - dispatch({type: 'DOWNLOAD_PROGRESS', payload: {slug, downloadProgress}}); + dispatch({type: 'DOWNLOAD_PROGRESS', payload: {guid, downloadProgress}}); } else if (addonInstall.state === 'STATE_INSTALLING') { - dispatch({type: 'START_INSTALL', payload: {slug}}); + dispatch({type: 'START_INSTALL', payload: {guid}}); } else if (addonInstall.state === 'STATE_INSTALLED') { - dispatch({type: 'INSTALL_COMPLETE', payload: {slug}}); + dispatch({type: 'INSTALL_COMPLETE', payload: {guid}}); } }; } @@ -121,9 +120,9 @@ export function mapDispatchToProps(dispatch) { return {}; } return { - setInitialStatus({ guid, installURL, slug }) { + setInitialStatus({ guid, installURL }) { const addonManager = new AddonManager(guid, installURL); - const payload = {guid, slug, url: installURL}; + const payload = {guid, url: installURL}; return addonManager.getAddon() .then( (addon) => { @@ -133,34 +132,34 @@ export function mapDispatchToProps(dispatch) { () => dispatch({type: 'INSTALL_STATE', payload: {...payload, status: UNINSTALLED}})); }, - install({ guid, installURL, slug, name }) { - const addonManager = new AddonManager(guid, installURL, makeProgressHandler(dispatch, slug)); - dispatch({type: 'START_DOWNLOAD', payload: {slug}}); + install({ guid, installURL, name }) { + const addonManager = new AddonManager(guid, installURL, makeProgressHandler(dispatch, guid)); + dispatch({type: 'START_DOWNLOAD', payload: {guid}}); tracking.sendEvent({action: 'addon', category: INSTALL_CATEGORY, label: name}); return addonManager.install(); }, - installTheme(node, slug, name, _themeAction = themeAction) { + installTheme(node, guid, name, _themeAction = themeAction) { _themeAction(node, THEME_INSTALL); tracking.sendEvent({action: 'theme', category: INSTALL_CATEGORY, label: name}); return new Promise((resolve) => { setTimeout(() => { - dispatch({type: 'INSTALL_STATE', payload: {slug, status: INSTALLED}}); + dispatch({type: 'INSTALL_STATE', payload: {guid, status: INSTALLED}}); resolve(); }, 250); }); }, - uninstall({ guid, installURL, slug, name, type }) { + uninstall({ guid, installURL, name, type }) { const addonManager = new AddonManager(guid, installURL); - dispatch({type: 'START_UNINSTALL', payload: {slug}}); + dispatch({type: 'START_UNINSTALL', payload: {guid}}); const action = { ADDON_TYPE: 'addon', THEME_TYPE: 'theme', }[type] || 'invalid'; tracking.sendEvent({action, category: UNINSTALL_CATEGORY, label: name}); return addonManager.uninstall() - .then(() => dispatch({type: 'UNINSTALL_COMPLETE', payload: {slug}})); + .then(() => dispatch({type: 'UNINSTALL_COMPLETE', payload: {guid}})); }, }; } diff --git a/src/disco/reducers/installations.js b/src/disco/reducers/installations.js index 7c2922ea922..93c2c904e37 100644 --- a/src/disco/reducers/installations.js +++ b/src/disco/reducers/installations.js @@ -23,12 +23,11 @@ export default function installations(state = {}, { type, payload }) { return state; } let addon; - if (state[payload.slug]) { - addon = {...state[payload.slug]}; + if (state[payload.guid]) { + addon = {...state[payload.guid]}; } if (type === 'INSTALL_STATE') { addon = { - slug: payload.slug, guid: payload.guid, url: payload.url, downloadProgress: 0, @@ -56,6 +55,6 @@ export default function installations(state = {}, { type, payload }) { } return { ...state, - [payload.slug]: addon, + [payload.guid]: addon, }; } diff --git a/tests/client/disco/containers/TestInstallButton.js b/tests/client/disco/containers/TestInstallButton.js index bcfadade83d..e8e9b663275 100644 --- a/tests/client/disco/containers/TestInstallButton.js +++ b/tests/client/disco/containers/TestInstallButton.js @@ -109,94 +109,92 @@ describe('', () => { it('should call installTheme function on click when uninstalled theme', () => { const installTheme = sinon.spy(); - const slug = 'my-theme'; - const button = renderButton({installTheme, type: THEME_TYPE, slug, status: UNINSTALLED}); + const guid = 'test-guid'; + const name = 'hai'; + const button = renderButton({installTheme, type: THEME_TYPE, guid, name, status: UNINSTALLED}); const themeData = button.refs.themeData; const root = findDOMNode(button); Simulate.click(root); - assert(installTheme.calledWith(themeData, slug)); + assert(installTheme.calledWith(themeData, guid, name)); }); it('should call install function on click when uninstalled', () => { const guid = '@foo'; + const name = 'hai'; const install = sinon.spy(); const installURL = 'https://my.url/download'; - const name = 'hai'; - const slug = 'foo'; - const button = renderButton({guid, install, installURL, name, slug, status: UNINSTALLED}); + const button = renderButton({guid, install, installURL, name, status: UNINSTALLED}); const root = findDOMNode(button); Simulate.click(root); - assert(install.calledWith({guid, installURL, slug, name})); + assert(install.calledWith({guid, installURL, name})); }); it('should call uninstall function on click when installed', () => { const guid = '@foo'; const installURL = 'https://my.url/download'; const name = 'hai'; - const slug = 'foo'; const type = 'whatevs'; const uninstall = sinon.spy(); - const button = renderButton({guid, installURL, name, slug, status: INSTALLED, type, uninstall}); + const button = renderButton({guid, installURL, name, status: INSTALLED, type, uninstall}); const root = findDOMNode(button); Simulate.click(root); - assert(uninstall.calledWith({guid, installURL, slug, name, type})); + assert(uninstall.calledWith({guid, installURL, name, type})); }); it('should call setInitialStatus in componentDidMount', () => { const guid = '@foo'; const installURL = 'http://the.url'; const setInitialStatus = sinon.spy(); - const slug = 'foo'; - renderButton({guid, installURL, setInitialStatus, slug, status: UNKNOWN}); - assert(setInitialStatus.calledWith({guid, installURL, slug})); + renderButton({guid, installURL, setInitialStatus, status: UNKNOWN}); + assert(setInitialStatus.calledWith({guid, installURL})); }); describe('mapStateToProps', () => { it('pulls the installation data from the state', () => { const addon = { - slug: 'addon', + guid: 'foo@addon', downloadProgress: 75, }; assert.deepEqual( mapStateToProps({ - installations: {foo: {some: 'data'}, addon}, - addons: {addon: {addonProp: 'addonValue'}}, - }, {slug: 'addon'}), - {slug: 'addon', downloadProgress: 75, addonProp: 'addonValue'}); + installations: {foo: {some: 'data'}, 'foo@addon': addon}, + addons: {'foo@addon': {addonProp: 'addonValue'}}, + }, {guid: 'foo@addon'}), + {guid: 'foo@addon', downloadProgress: 75, addonProp: 'addonValue'}); }); }); describe('makeProgressHandler', () => { it('sets the download progress on STATE_DOWNLOADING', () => { const dispatch = sinon.spy(); - const slug = 'my-addon'; - const handler = makeProgressHandler(dispatch, slug); + const guid = 'foo@addon'; + const handler = makeProgressHandler(dispatch, guid); handler({state: 'STATE_DOWNLOADING', progress: 300, maxProgress: 990}); assert(dispatch.calledWith({ type: 'DOWNLOAD_PROGRESS', - payload: {downloadProgress: 30, slug}, + payload: {downloadProgress: 30, guid}, })); }); it('sets status to installing on STATE_INSTALLING', () => { const dispatch = sinon.spy(); - const slug = 'my-addon'; - const handler = makeProgressHandler(dispatch, slug); + const guid = 'foo@my-addon'; + const handler = makeProgressHandler(dispatch, guid); handler({state: 'STATE_INSTALLING'}); assert(dispatch.calledWith({ type: 'START_INSTALL', - payload: {slug}, + payload: {guid}, })); }); it('sets status to installed on STATE_INSTALLED', () => { const dispatch = sinon.spy(); - const slug = 'my-addon'; - const handler = makeProgressHandler(dispatch, slug); + const guid = '{my-addon}'; + const handler = makeProgressHandler(dispatch, guid); handler({state: 'STATE_INSTALLED'}); assert(dispatch.calledWith({ type: 'INSTALL_COMPLETE', - payload: {slug}, + payload: {guid}, })); }); }); @@ -206,14 +204,13 @@ describe('', () => { stubAddonManager(); const dispatch = sinon.spy(); const guid = '@foo'; - const slug = 'foo'; const installURL = 'http://the.url'; const { setInitialStatus } = mapDispatchToProps(dispatch); - return setInitialStatus({guid, installURL, slug}) + return setInitialStatus({guid, installURL}) .then(() => { assert(dispatch.calledWith({ type: 'INSTALL_STATE', - payload: {guid, slug, status: INSTALLED, url: installURL}, + payload: {guid, status: INSTALLED, url: installURL}, })); }); }); @@ -222,14 +219,13 @@ describe('', () => { stubAddonManager({getAddon: Promise.resolve({type: THEME_TYPE, isEnabled: true})}); const dispatch = sinon.spy(); const guid = '@foo'; - const slug = 'foo'; const installURL = 'http://the.url'; const { setInitialStatus } = mapDispatchToProps(dispatch); - return setInitialStatus({guid, installURL, slug}) + return setInitialStatus({guid, installURL}) .then(() => { assert(dispatch.calledWith({ type: 'INSTALL_STATE', - payload: {guid, slug, status: INSTALLED, url: installURL}, + payload: {guid, status: INSTALLED, url: installURL}, })); }); }); @@ -238,14 +234,13 @@ describe('', () => { stubAddonManager({getAddon: Promise.resolve({type: THEME_TYPE, isEnabled: false})}); const dispatch = sinon.spy(); const guid = '@foo'; - const slug = 'foo'; const installURL = 'http://the.url'; const { setInitialStatus } = mapDispatchToProps(dispatch); - return setInitialStatus({guid, installURL, slug}) + return setInitialStatus({guid, installURL}) .then(() => { assert(dispatch.calledWith({ type: 'INSTALL_STATE', - payload: {guid, slug, status: UNINSTALLED, url: installURL}, + payload: {guid, status: UNINSTALLED, url: installURL}, })); }); }); @@ -254,14 +249,13 @@ describe('', () => { stubAddonManager({getAddon: Promise.reject()}); const dispatch = sinon.spy(); const guid = '@foo'; - const slug = 'foo'; const installURL = 'http://the.url'; const { setInitialStatus } = mapDispatchToProps(dispatch); - return setInitialStatus({guid, installURL, slug}) + return setInitialStatus({guid, installURL}) .then(() => { assert(dispatch.calledWith({ type: 'INSTALL_STATE', - payload: {guid, slug, status: UNINSTALLED, url: installURL}, + payload: {guid, status: UNINSTALLED, url: installURL}, })); }); }); @@ -270,13 +264,12 @@ describe('', () => { describe('install', () => { const guid = '@install'; const installURL = 'https://mysite.com/download.xpi'; - const slug = 'install'; it('installs the addon on a new AddonManager', () => { stubAddonManager(); const dispatch = sinon.spy(); const { install } = mapDispatchToProps(dispatch); - return install({guid, installURL, slug}) + return install({guid, installURL}) .then(() => { assert(addonManager.AddonManager.calledWithNew, 'new AddonManager() called'); assert(addonManager.AddonManager.calledWith(guid, installURL, sinon.match.func)); @@ -287,10 +280,10 @@ describe('', () => { stubAddonManager(); const dispatch = sinon.spy(); const { install } = mapDispatchToProps(dispatch); - return install({guid, installURL, slug}) + return install({guid, installURL}) .then(() => assert(dispatch.calledWith({ type: 'START_DOWNLOAD', - payload: {slug}, + payload: {guid}, }))); }); }); @@ -298,13 +291,12 @@ describe('', () => { describe('uninstall', () => { const guid = '@uninstall'; const installURL = 'https://mysite.com/download.xpi'; - const slug = 'uninstall'; it('prepares the addon on a new AddonManager', () => { stubAddonManager(); const dispatch = sinon.spy(); const { uninstall } = mapDispatchToProps(dispatch); - return uninstall({guid, installURL, slug}) + return uninstall({guid, installURL}) .then(() => { assert(addonManager.AddonManager.calledWithNew, 'new AddonManager() called'); assert(addonManager.AddonManager.calledWith(guid, installURL)); @@ -315,10 +307,10 @@ describe('', () => { stubAddonManager(); const dispatch = sinon.spy(); const { uninstall } = mapDispatchToProps(dispatch); - return uninstall({guid, installURL, slug}) + return uninstall({guid, installURL}) .then(() => assert(dispatch.calledWith({ type: 'START_UNINSTALL', - payload: {slug}, + payload: {guid}, }))); }); }); @@ -326,17 +318,17 @@ describe('', () => { describe('installTheme', () => { it('installs the theme', () => { const name = 'hai-theme'; + const guid = '{install-theme}'; const node = sinon.stub(); - const slug = 'install-theme'; const spyThemeAction = sinon.spy(); const dispatch = sinon.spy(); const { installTheme } = mapDispatchToProps(dispatch); - return installTheme(node, slug, name, spyThemeAction) + return installTheme(node, guid, name, spyThemeAction) .then(() => { assert(spyThemeAction.calledWith(node, THEME_INSTALL)); assert(dispatch.calledWith({ type: 'INSTALL_STATE', - payload: {slug, status: INSTALLED}, + payload: {guid, status: INSTALLED}, })); }); }); diff --git a/tests/client/disco/reducers/test_installations.js b/tests/client/disco/reducers/test_installations.js index 823d4a5ce54..ed93552c2e3 100644 --- a/tests/client/disco/reducers/test_installations.js +++ b/tests/client/disco/reducers/test_installations.js @@ -23,15 +23,13 @@ describe('installations reducer', () => { installations(undefined, { type: 'INSTALL_STATE', payload: { - slug: 'my-addon', guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', status: UNINSTALLED, }, }), { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 0, @@ -45,15 +43,13 @@ describe('installations reducer', () => { installations(undefined, { type: 'INSTALL_STATE', payload: { - slug: 'an-addon', guid: 'an-addon@me.com', url: 'https://cdn.amo/download/an-addon.xpi', status: INSTALLED, }, }), { - 'an-addon': { - slug: 'an-addon', + 'an-addon@me.com': { guid: 'an-addon@me.com', url: 'https://cdn.amo/download/an-addon.xpi', downloadProgress: 0, @@ -64,8 +60,7 @@ describe('installations reducer', () => { it('marks an add-on as installing on START_DOWNLOAD', () => { const state = { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 0, @@ -76,12 +71,11 @@ describe('installations reducer', () => { installations(state, { type: 'START_DOWNLOAD', payload: { - slug: 'my-addon', + guid: 'my-addon@me.com', }, }), { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 0, @@ -92,8 +86,7 @@ describe('installations reducer', () => { it('updates the downloadProgress on DOWNLOAD_PROGRESS', () => { const state = { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 0, @@ -104,13 +97,12 @@ describe('installations reducer', () => { installations(state, { type: 'DOWNLOAD_PROGRESS', payload: { - slug: 'my-addon', + guid: 'my-addon@me.com', downloadProgress: 25, }, }), { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 25, @@ -121,8 +113,7 @@ describe('installations reducer', () => { it('updates the status and downloadProgress on START_INSTALL', () => { const state = { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 75, @@ -133,12 +124,11 @@ describe('installations reducer', () => { installations(state, { type: 'START_INSTALL', payload: { - slug: 'my-addon', + guid: 'my-addon@me.com', }, }), { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 100, @@ -149,8 +139,7 @@ describe('installations reducer', () => { it('updates the status on INSTALL_COMPLETE', () => { const state = { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 100, @@ -161,12 +150,11 @@ describe('installations reducer', () => { installations(state, { type: 'INSTALL_COMPLETE', payload: { - slug: 'my-addon', + guid: 'my-addon@me.com', }, }), { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 100, @@ -177,8 +165,7 @@ describe('installations reducer', () => { it('updates the status on START_UNINSTALL', () => { const state = { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 100, @@ -189,12 +176,11 @@ describe('installations reducer', () => { installations(state, { type: 'START_UNINSTALL', payload: { - slug: 'my-addon', + guid: 'my-addon@me.com', }, }), { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 0, @@ -205,8 +191,7 @@ describe('installations reducer', () => { it('updates the status on UNINSTALL_COMPLETE', () => { const state = { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 0, @@ -217,12 +202,11 @@ describe('installations reducer', () => { installations(state, { type: 'UNINSTALL_COMPLETE', payload: { - slug: 'my-addon', + guid: 'my-addon@me.com', }, }), { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 0, @@ -233,8 +217,7 @@ describe('installations reducer', () => { it('sets an error on INSTALL_ERROR', () => { const state = { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 55, @@ -245,13 +228,12 @@ describe('installations reducer', () => { installations(state, { type: 'INSTALL_ERROR', payload: { - slug: 'my-addon', + guid: 'my-addon@me.com', error: 'Download interrupted, check your network connection.', }, }), { - 'my-addon': { - slug: 'my-addon', + 'my-addon@me.com': { guid: 'my-addon@me.com', url: 'https://cdn.amo/download/my-addon.xpi', downloadProgress: 0, diff --git a/tests/client/disco/test_api.js b/tests/client/disco/test_api.js index cbeea45f3c7..8357c4ac7f8 100644 --- a/tests/client/disco/test_api.js +++ b/tests/client/disco/test_api.js @@ -1,6 +1,6 @@ import { arrayOf, normalize } from 'normalizr'; -import { discoResult, getDiscoveryAddons } from 'disco/api'; +import { discoResult, getDiscoveryAddons, getGuid } from 'disco/api'; import * as coreApi from 'core/api'; describe('disco api', () => { @@ -18,18 +18,28 @@ describe('disco api', () => { }); describe('discoResult', () => { - it("uses the addon's slug as an id", () => { - const normalized = normalize({addon: {slug: 'foo'}}, discoResult); + it("uses the addon's guid as an id", () => { + const normalized = normalize({addon: {guid: '{foo}'}}, discoResult); assert.deepEqual( normalized, { entities: { - addons: {foo: {slug: 'foo'}}, - discoResults: {foo: {addon: 'foo'}}, + addons: {'{foo}': {guid: '{foo}'}}, + discoResults: {'{foo}': {addon: '{foo}'}}, }, - result: 'foo', + result: '{foo}', }, sinon.format(normalized.entities)); }); }); + + describe('getGuid', () => { + it('provides a theme guid for a theme', () => { + const fakeResult = { + type: 'persona', + id: 'awooga', + }; + assert.equal(getGuid(fakeResult), 'awooga@personas.mozilla.org'); + }); + }); });