Родитель
8c0217d8a8
Коммит
b706c7182c
|
@ -9,7 +9,7 @@ import ScreenShots from 'amo/components/ScreenShots';
|
|||
import 'amo/css/AddonDetail.scss';
|
||||
import fallbackIcon from 'amo/img/icons/default-64.png';
|
||||
import InstallButton from 'core/components/InstallButton';
|
||||
import { THEME_TYPE } from 'core/constants';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
import { withInstallHelpers } from 'core/installAddon';
|
||||
import { isAllowedOrigin, nl2br, sanitizeHTML } from 'core/utils';
|
||||
import translate from 'core/i18n/translate';
|
||||
|
@ -77,7 +77,7 @@ export class AddonDetailBase extends React.Component {
|
|||
const iconUrl = isAllowedOrigin(addon.icon_url) ? addon.icon_url :
|
||||
fallbackIcon;
|
||||
|
||||
if (type === THEME_TYPE) {
|
||||
if (type === ADDON_TYPE_THEME) {
|
||||
const label = i18n.gettext('Press to preview');
|
||||
const imageClassName = 'AddonDetail-theme-header-image';
|
||||
let headerImage;
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, { PropTypes } from 'react';
|
|||
import { compose } from 'redux';
|
||||
|
||||
import Link from 'amo/components/Link';
|
||||
import { visibleAddonType } from 'core/utils';
|
||||
import translate from 'core/i18n/translate';
|
||||
|
||||
import './Categories.scss';
|
||||
|
@ -50,7 +51,7 @@ export class CategoriesBase extends React.Component {
|
|||
{Object.values(categories).map((category) => (
|
||||
<li className="Categories-list-item">
|
||||
<Link className="Categories-link"
|
||||
to={`/${addonType}s/${category.slug}/`}>
|
||||
to={`/${visibleAddonType(addonType)}/${category.slug}/`}>
|
||||
{category.name}
|
||||
</Link>
|
||||
</li>
|
||||
|
|
|
@ -6,6 +6,13 @@ import { connect } from 'react-redux';
|
|||
|
||||
import LandingAddonsCard from 'amo/components/LandingAddonsCard';
|
||||
import { loadLandingAddons } from 'amo/utils';
|
||||
import {
|
||||
ADDON_TYPE_EXTENSION,
|
||||
ADDON_TYPE_THEME,
|
||||
SEARCH_SORT_POPULAR,
|
||||
SEARCH_SORT_TOP_RATED,
|
||||
} from 'core/constants';
|
||||
import { apiAddonType } from 'core/utils';
|
||||
import translate from 'core/i18n/translate';
|
||||
|
||||
import './LandingPage.scss';
|
||||
|
@ -24,47 +31,47 @@ export class LandingPageBase extends React.Component {
|
|||
const { i18n } = this.props;
|
||||
|
||||
const contentForTypes = {
|
||||
extension: {
|
||||
[ADDON_TYPE_EXTENSION]: {
|
||||
featuredHeader: i18n.gettext('Featured extensions'),
|
||||
// TODO: Add this search/route, see:
|
||||
// https://github.com/mozilla/addons-frontend/issues/1535
|
||||
featuredFooterLink: {
|
||||
pathname: '#/extensions/featured',
|
||||
query: { addonType: 'theme' },
|
||||
query: { addonType: ADDON_TYPE_EXTENSION },
|
||||
},
|
||||
featuredFooterText: i18n.gettext('More featured extensions'),
|
||||
popularHeader: i18n.gettext('Most popular extensions'),
|
||||
popularFooterLink: {
|
||||
pathname: '/search/',
|
||||
query: { addonType: 'extension', sort: 'hotness' },
|
||||
query: { sort: SEARCH_SORT_POPULAR, type: ADDON_TYPE_EXTENSION },
|
||||
},
|
||||
popularFooterText: i18n.gettext('More popular extensions'),
|
||||
highlyRatedHeader: i18n.gettext('Top rated extensions'),
|
||||
highlyRatedFooterLink: {
|
||||
pathname: '/search/',
|
||||
query: { addonType: 'extension', sort: 'rating' },
|
||||
query: { sort: SEARCH_SORT_TOP_RATED, type: ADDON_TYPE_EXTENSION },
|
||||
},
|
||||
highlyRatedFooterText: i18n.gettext('More highly rated extensions'),
|
||||
},
|
||||
theme: {
|
||||
[ADDON_TYPE_THEME]: {
|
||||
featuredHeader: i18n.gettext('Featured themes'),
|
||||
// TODO: Add this search/route, see:
|
||||
// https://github.com/mozilla/addons-frontend/issues/1535
|
||||
featuredFooterLink: {
|
||||
pathname: '#/themes/featured',
|
||||
query: { addonType: 'theme' },
|
||||
query: { addonType: ADDON_TYPE_THEME },
|
||||
},
|
||||
featuredFooterText: i18n.gettext('More featured themes'),
|
||||
popularHeader: i18n.gettext('Most popular themes'),
|
||||
popularFooterLink: {
|
||||
pathname: '/search/',
|
||||
query: { addonType: 'theme', sort: 'hotness' },
|
||||
query: { sort: SEARCH_SORT_POPULAR, type: ADDON_TYPE_THEME },
|
||||
},
|
||||
popularFooterText: i18n.gettext('More popular themes'),
|
||||
highlyRatedHeader: i18n.gettext('Top rated themes'),
|
||||
highlyRatedFooterLink: {
|
||||
pathname: '/search/',
|
||||
query: { addonType: 'theme', sort: 'rating' },
|
||||
query: { sort: SEARCH_SORT_TOP_RATED, type: ADDON_TYPE_THEME },
|
||||
},
|
||||
highlyRatedFooterText: i18n.gettext('More highly rated themes'),
|
||||
},
|
||||
|
@ -105,12 +112,9 @@ export class LandingPageBase extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export function singularizeAddonType(state, ownProps) {
|
||||
return { addonType: ownProps.params.pluralAddonType.replace(/s$/, '') };
|
||||
}
|
||||
|
||||
export function mapStateToProps(state) {
|
||||
export function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
addonType: apiAddonType(ownProps.params.visibleAddonType),
|
||||
featuredAddons: state.landing.featured.results,
|
||||
highlyRatedAddons: state.landing.highlyRated.results,
|
||||
popularAddons: state.landing.popular.results,
|
||||
|
@ -122,6 +126,5 @@ export default compose(
|
|||
{ deferred: true, promise: loadLandingAddons },
|
||||
]),
|
||||
connect(mapStateToProps),
|
||||
connect(singularizeAddonType),
|
||||
translate({ withRef: true }),
|
||||
)(LandingPageBase);
|
||||
|
|
|
@ -3,11 +3,11 @@ import { asyncConnect } from 'redux-connect';
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import Categories from 'amo/components/Categories';
|
||||
import { loadCategoriesIfNeeded } from 'core/utils';
|
||||
import { loadCategoriesIfNeeded, apiAddonType } from 'core/utils';
|
||||
|
||||
|
||||
export function mapStateToProps(state, ownProps) {
|
||||
const addonType = ownProps.params.addonType.replace(/s$/, '');
|
||||
const addonType = apiAddonType(ownProps.params.visibleAddonType);
|
||||
const clientApp = state.api.clientApp;
|
||||
const categories = state.categories.categories[clientApp][addonType] ?
|
||||
state.categories.categories[clientApp][addonType] : {};
|
||||
|
|
|
@ -5,15 +5,16 @@ import { compose } from 'redux';
|
|||
|
||||
import SearchPage from 'amo/components/SearchPage';
|
||||
import { loadByCategoryIfNeeded, parsePage } from 'core/searchUtils';
|
||||
import { apiAddonType } from 'core/utils';
|
||||
|
||||
|
||||
export function mapStateToProps(state, ownProps) {
|
||||
const filters = {
|
||||
addonType: ownProps.params.addonType,
|
||||
addonType: apiAddonType(ownProps.params.visibleAddonType),
|
||||
category: ownProps.params.slug,
|
||||
clientApp: ownProps.params.application,
|
||||
};
|
||||
const pathname = `/${filters.addonType}s/${filters.category}/`;
|
||||
const pathname = `/${ownProps.params.visibleAddonType}/${filters.category}/`;
|
||||
const queryParams = { page: parsePage(ownProps.location.query.page) };
|
||||
|
||||
const filtersMatchState = deepEqual(
|
||||
|
|
|
@ -18,10 +18,10 @@ export default (
|
|||
<IndexRoute component={Home} />
|
||||
<Route path="addon/:slug/" component={DetailPage} />
|
||||
<Route path="addon/:slug/review/:reviewId/" component={AddonReview} />
|
||||
<Route path=":addonType/categories/" component={CategoryList} />
|
||||
<Route path=":addonType/:slug/" component={CategoryPage} />
|
||||
<Route path=":visibleAddonType/categories/" component={CategoryList} />
|
||||
<Route path=":visibleAddonType/:slug/" component={CategoryPage} />
|
||||
<Route path="fxa-authenticate" component={HandleLogin} />
|
||||
<Route path="search/" component={SearchPage} />
|
||||
<Route path=":pluralAddonType/" component={LandingPage} />
|
||||
<Route path=":visibleAddonType/" component={LandingPage} />
|
||||
</Route>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { LANDING_PAGE_ADDON_COUNT } from 'amo/constants';
|
||||
import { getLanding, loadLanding, failLanding } from 'amo/actions/landing';
|
||||
import { featured as featuredAPI, search } from 'core/api';
|
||||
import { SEARCH_SORT_POPULAR, SEARCH_SORT_TOP_RATED } from 'core/constants';
|
||||
import { apiAddonType } from 'core/utils';
|
||||
|
||||
|
||||
export function fetchLandingAddons({ addonType, api, dispatch }) {
|
||||
|
@ -9,8 +11,12 @@ export function fetchLandingAddons({ addonType, api, dispatch }) {
|
|||
const filters = { addonType, page_size: LANDING_PAGE_ADDON_COUNT };
|
||||
const landingRequests = [
|
||||
featuredAPI({ api, filters }),
|
||||
search({ api, filters: { ...filters, sort: 'rating' }, page: 1 }),
|
||||
search({ api, filters: { ...filters, sort: 'hotness' }, page: 1 }),
|
||||
search({
|
||||
api, filters: { ...filters, sort: SEARCH_SORT_TOP_RATED }, page: 1,
|
||||
}),
|
||||
search({
|
||||
api, filters: { ...filters, sort: SEARCH_SORT_POPULAR }, page: 1,
|
||||
}),
|
||||
];
|
||||
|
||||
return Promise.all(landingRequests)
|
||||
|
@ -22,7 +28,7 @@ export function fetchLandingAddons({ addonType, api, dispatch }) {
|
|||
|
||||
export function loadLandingAddons({ store: { dispatch, getState }, params }) {
|
||||
const state = getState();
|
||||
const addonType = params.pluralAddonType.replace(/s$/, '');
|
||||
const addonType = apiAddonType(params.visibleAddonType);
|
||||
|
||||
return fetchLandingAddons({ addonType, api: state.api, dispatch });
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, { PropTypes } from 'react';
|
|||
import { compose } from 'redux';
|
||||
|
||||
import InstallSwitch from 'core/components/InstallSwitch';
|
||||
import { THEME_TYPE } from 'core/constants';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
import translate from 'core/i18n/translate';
|
||||
import { getThemeData } from 'core/themePreview';
|
||||
import Button from 'ui/components/Button';
|
||||
|
@ -32,7 +32,7 @@ export class InstallButtonBase extends React.Component {
|
|||
const { addon, className, hasAddonManager, i18n, size } = this.props;
|
||||
const useButton = hasAddonManager !== undefined && !hasAddonManager;
|
||||
let button;
|
||||
if (addon.type === THEME_TYPE) {
|
||||
if (addon.type === ADDON_TYPE_THEME) {
|
||||
button = (
|
||||
<Button
|
||||
data-browsertheme={JSON.stringify(getThemeData(addon))}
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
ENABLING,
|
||||
INSTALLING,
|
||||
INSTALLED,
|
||||
THEME_TYPE,
|
||||
ADDON_TYPE_THEME,
|
||||
UNINSTALLED,
|
||||
UNINSTALLING,
|
||||
UNKNOWN,
|
||||
|
@ -88,7 +88,7 @@ export class InstallSwitchBase extends React.Component {
|
|||
const {
|
||||
addon, guid, enable, install, installURL, name, status, installTheme, type, uninstall,
|
||||
} = this.props;
|
||||
if (type === THEME_TYPE && [UNINSTALLED, DISABLED].includes(status)) {
|
||||
if (type === ADDON_TYPE_THEME && [UNINSTALLED, DISABLED].includes(status)) {
|
||||
installTheme(this.themeData, { ...addon, status });
|
||||
} else if (status === UNINSTALLED) {
|
||||
install();
|
||||
|
|
|
@ -35,13 +35,36 @@ export const FATAL_UNINSTALL_ERROR = 'FATAL_UNINSTALL_ERROR';
|
|||
export const FATAL_ERROR = 'FATAL_ERROR';
|
||||
|
||||
// Add-on types.
|
||||
export const API_THEME_TYPE = 'persona';
|
||||
export const EXTENSION_TYPE = 'extension';
|
||||
export const THEME_TYPE = 'theme';
|
||||
export const ADDON_TYPE_EXTENSION = 'extension';
|
||||
export const ADDON_TYPE_THEME = 'persona';
|
||||
export const validAddonTypes = [
|
||||
EXTENSION_TYPE,
|
||||
THEME_TYPE,
|
||||
ADDON_TYPE_EXTENSION,
|
||||
ADDON_TYPE_THEME,
|
||||
];
|
||||
// Mapping of the add-on types we show in URLs, etc. and what they map
|
||||
// to in the API (and how they're represented internally in the app).
|
||||
//
|
||||
// Examples:
|
||||
// * '/extensions/' -> 'extension'
|
||||
// * '/themes/' -> 'persona'
|
||||
export const API_ADDON_TYPES_MAPPING = {
|
||||
extensions: ADDON_TYPE_EXTENSION,
|
||||
themes: ADDON_TYPE_THEME,
|
||||
};
|
||||
export const VISIBLE_ADDON_TYPES_MAPPING = Object.keys(API_ADDON_TYPES_MAPPING)
|
||||
.reduce((object, key) => ({
|
||||
...object,
|
||||
[API_ADDON_TYPES_MAPPING[key]]: key,
|
||||
}), {});
|
||||
|
||||
// Tracking add-on types
|
||||
export const TRACKING_TYPE_EXTENSION = 'addon';
|
||||
export const TRACKING_TYPE_THEME = 'theme';
|
||||
export const TRACKING_TYPE_INVALID = 'invalid';
|
||||
|
||||
// Add-on Search Sort Values
|
||||
export const SEARCH_SORT_POPULAR = 'hotness';
|
||||
export const SEARCH_SORT_TOP_RATED = 'rating';
|
||||
|
||||
// Action types.
|
||||
export const CATEGORIES_GET = 'CATEGORIES_GET';
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
DOWNLOAD_PROGRESS,
|
||||
ENABLED,
|
||||
ERROR,
|
||||
EXTENSION_TYPE,
|
||||
FATAL_ERROR,
|
||||
FATAL_INSTALL_ERROR,
|
||||
FATAL_UNINSTALL_ERROR,
|
||||
|
@ -29,7 +28,10 @@ import {
|
|||
THEME_INSTALL,
|
||||
THEME_PREVIEW,
|
||||
THEME_RESET_PREVIEW,
|
||||
THEME_TYPE,
|
||||
TRACKING_TYPE_EXTENSION,
|
||||
TRACKING_TYPE_THEME,
|
||||
ADDON_TYPE_EXTENSION,
|
||||
ADDON_TYPE_THEME,
|
||||
UNINSTALL_CATEGORY,
|
||||
UNINSTALLED,
|
||||
UNINSTALLING,
|
||||
|
@ -37,13 +39,21 @@ import {
|
|||
} from 'core/constants';
|
||||
import * as addonManager from 'core/addonManager';
|
||||
|
||||
|
||||
export function installTheme(
|
||||
node, addon, { _themeAction = themeAction, _tracking = tracking } = {},
|
||||
) {
|
||||
const { name, status, type } = addon;
|
||||
if (type === THEME_TYPE && [DISABLED, UNINSTALLED, UNKNOWN].includes(status)) {
|
||||
if (
|
||||
type === ADDON_TYPE_THEME &&
|
||||
[DISABLED, UNINSTALLED, UNKNOWN].includes(status)
|
||||
) {
|
||||
_themeAction(node, THEME_INSTALL);
|
||||
_tracking.sendEvent({ action: 'theme', category: INSTALL_CATEGORY, label: name });
|
||||
_tracking.sendEvent({
|
||||
action: TRACKING_TYPE_THEME,
|
||||
category: INSTALL_CATEGORY,
|
||||
label: name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +98,7 @@ export function makeMapDispatchToProps({ WrappedComponent, src }) {
|
|||
return { WrappedComponent };
|
||||
}
|
||||
|
||||
if (ownProps.type === EXTENSION_TYPE && ownProps.installURL === undefined) {
|
||||
if (ownProps.type === ADDON_TYPE_EXTENSION && ownProps.installURL === undefined) {
|
||||
throw new Error(oneLine`installURL is required, ensure component props are set before
|
||||
withInstallHelpers is called`);
|
||||
}
|
||||
|
@ -161,7 +171,7 @@ export function makeMapDispatchToProps({ WrappedComponent, src }) {
|
|||
return _addonManager.install(installURL, makeProgressHandler(dispatch, guid), { src })
|
||||
.then(() => {
|
||||
_tracking.sendEvent({
|
||||
action: 'addon',
|
||||
action: TRACKING_TYPE_EXTENSION,
|
||||
category: INSTALL_CATEGORY,
|
||||
label: name,
|
||||
});
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { API_THEME_TYPE, THEME_TYPE } from 'core/constants';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
|
||||
const initialState = {};
|
||||
|
||||
export function getGuid(result) {
|
||||
if (result.type === API_THEME_TYPE) {
|
||||
if (result.type === ADDON_TYPE_THEME) {
|
||||
return `${result.id}@personas.mozilla.org`;
|
||||
}
|
||||
return result.guid;
|
||||
|
@ -20,7 +20,7 @@ export default function addon(state = initialState, action) {
|
|||
...thisAddon,
|
||||
...thisAddon.theme_data,
|
||||
guid: getGuid(thisAddon),
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
};
|
||||
delete newState[key].theme_data;
|
||||
} else if (thisAddon.current_version && thisAddon.current_version.files.length > 0) {
|
||||
|
|
|
@ -2,6 +2,7 @@ import deepEqual from 'deep-eql';
|
|||
|
||||
import { search } from 'core/api';
|
||||
import { searchStart, searchLoad, searchFail } from 'core/actions/search';
|
||||
import { apiAddonType } from 'core/utils';
|
||||
|
||||
|
||||
export const paramsToFilter = {
|
||||
|
@ -108,7 +109,7 @@ export function loadByCategoryIfNeeded(
|
|||
) {
|
||||
const state = getState();
|
||||
const filters = {
|
||||
addonType: params.addonType,
|
||||
addonType: apiAddonType(params.visibleAddonType),
|
||||
category: params.slug,
|
||||
clientApp: params.application,
|
||||
};
|
||||
|
|
|
@ -5,8 +5,11 @@ import config from 'config';
|
|||
import { convertBoolean } from 'core/utils';
|
||||
import log from 'core/logger';
|
||||
import {
|
||||
EXTENSION_TYPE,
|
||||
THEME_TYPE,
|
||||
TRACKING_TYPE_EXTENSION,
|
||||
TRACKING_TYPE_INVALID,
|
||||
TRACKING_TYPE_THEME,
|
||||
ADDON_TYPE_EXTENSION,
|
||||
ADDON_TYPE_THEME,
|
||||
} from 'core/constants';
|
||||
|
||||
|
||||
|
@ -97,9 +100,9 @@ export class Tracking {
|
|||
|
||||
export function getAction(type) {
|
||||
return {
|
||||
[EXTENSION_TYPE]: 'addon',
|
||||
[THEME_TYPE]: 'theme',
|
||||
}[type] || 'invalid';
|
||||
[ADDON_TYPE_EXTENSION]: TRACKING_TYPE_EXTENSION,
|
||||
[ADDON_TYPE_THEME]: TRACKING_TYPE_THEME,
|
||||
}[type] || TRACKING_TYPE_INVALID;
|
||||
}
|
||||
|
||||
export default new Tracking({
|
||||
|
|
|
@ -9,9 +9,14 @@ import {
|
|||
categoriesFail,
|
||||
} from 'core/actions/categories';
|
||||
import { categories, fetchAddon } from 'core/api';
|
||||
import {
|
||||
API_ADDON_TYPES_MAPPING,
|
||||
VISIBLE_ADDON_TYPES_MAPPING,
|
||||
} from 'core/constants';
|
||||
import log from 'core/logger';
|
||||
import purify from 'core/purify';
|
||||
|
||||
|
||||
export function gettext(str) {
|
||||
return str;
|
||||
}
|
||||
|
@ -153,3 +158,21 @@ export function browserBase64Decode(str) {
|
|||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join(''));
|
||||
}
|
||||
|
||||
export function apiAddonType(addonType) {
|
||||
if (!Object.prototype.hasOwnProperty.call(
|
||||
API_ADDON_TYPES_MAPPING, addonType
|
||||
)) {
|
||||
throw new Error(`"${addonType}" not found in API_ADDON_TYPES_MAPPING`);
|
||||
}
|
||||
return API_ADDON_TYPES_MAPPING[addonType];
|
||||
}
|
||||
|
||||
export function visibleAddonType(addonType) {
|
||||
if (!Object.prototype.hasOwnProperty.call(
|
||||
VISIBLE_ADDON_TYPES_MAPPING, addonType
|
||||
)) {
|
||||
throw new Error(`"${addonType}" not found in VISIBLE_ADDON_TYPES_MAPPING`);
|
||||
}
|
||||
return VISIBLE_ADDON_TYPES_MAPPING[addonType];
|
||||
}
|
||||
|
|
|
@ -16,12 +16,12 @@ import {
|
|||
CLICK_CATEGORY,
|
||||
DOWNLOAD_FAILED,
|
||||
ERROR,
|
||||
EXTENSION_TYPE,
|
||||
ADDON_TYPE_EXTENSION,
|
||||
FATAL_ERROR,
|
||||
FATAL_INSTALL_ERROR,
|
||||
FATAL_UNINSTALL_ERROR,
|
||||
INSTALL_FAILED,
|
||||
THEME_TYPE,
|
||||
ADDON_TYPE_THEME,
|
||||
UNINSTALLING,
|
||||
validAddonTypes,
|
||||
validInstallStates,
|
||||
|
@ -76,7 +76,7 @@ export class AddonBase extends React.Component {
|
|||
|
||||
getLogo() {
|
||||
const { iconUrl } = this.props;
|
||||
if (this.props.type === EXTENSION_TYPE) {
|
||||
if (this.props.type === ADDON_TYPE_EXTENSION) {
|
||||
return <div className="logo"><img src={iconUrl} alt="" /></div>;
|
||||
}
|
||||
return null;
|
||||
|
@ -84,7 +84,7 @@ export class AddonBase extends React.Component {
|
|||
|
||||
getThemeImage() {
|
||||
const { getBrowserThemeData, i18n, name, previewURL } = this.props;
|
||||
if (this.props.type === THEME_TYPE) {
|
||||
if (this.props.type === ADDON_TYPE_THEME) {
|
||||
// eslint-disable-next-line jsx-a11y/href-no-hash
|
||||
return (<a href="#" className="theme-image"
|
||||
data-browsertheme={getBrowserThemeData()}
|
||||
|
@ -102,7 +102,7 @@ export class AddonBase extends React.Component {
|
|||
|
||||
getDescription() {
|
||||
const { i18n, description, type } = this.props;
|
||||
if (type === THEME_TYPE) {
|
||||
if (type === ADDON_TYPE_THEME) {
|
||||
return (
|
||||
<p className="editorial-description">{i18n.gettext('Hover over the image to preview')}</p>
|
||||
);
|
||||
|
@ -181,8 +181,8 @@ export class AddonBase extends React.Component {
|
|||
}
|
||||
|
||||
const addonClasses = classNames('addon', {
|
||||
theme: type === THEME_TYPE,
|
||||
extension: type === EXTENSION_TYPE,
|
||||
theme: type === ADDON_TYPE_THEME,
|
||||
extension: type === ADDON_TYPE_EXTENSION,
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { getLanding, loadLanding, failLanding } from 'amo/actions/landing';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
|
||||
|
||||
describe('LANDING_GET', () => {
|
||||
const action = getLanding({ addonType: 'theme' });
|
||||
const action = getLanding({ addonType: ADDON_TYPE_THEME });
|
||||
|
||||
it('sets the type', () => {
|
||||
assert.equal(action.type, 'LANDING_GET');
|
||||
});
|
||||
|
||||
it('sets the filters', () => {
|
||||
assert.deepEqual(action.payload, { addonType: 'theme' });
|
||||
assert.deepEqual(action.payload, { addonType: ADDON_TYPE_THEME });
|
||||
});
|
||||
|
||||
it('throws if no addonType is set', () => {
|
||||
|
@ -23,14 +24,14 @@ describe('LANDING_LOADED', () => {
|
|||
highlyRated: sinon.stub(),
|
||||
popular: sinon.stub(),
|
||||
};
|
||||
const action = loadLanding({ addonType: 'theme', ...response });
|
||||
const action = loadLanding({ addonType: ADDON_TYPE_THEME, ...response });
|
||||
|
||||
it('sets the type', () => {
|
||||
assert.equal(action.type, 'LANDING_LOADED');
|
||||
});
|
||||
|
||||
it('sets the payload', () => {
|
||||
assert.deepEqual(action.payload, { addonType: 'theme', ...response });
|
||||
assert.deepEqual(action.payload, { addonType: ADDON_TYPE_THEME, ...response });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
import AddonMeta from 'amo/components/AddonMeta';
|
||||
import { RatingManagerWithI18n } from 'amo/components/RatingManager';
|
||||
import createStore from 'amo/store';
|
||||
import { THEME_TYPE } from 'core/constants';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
import InstallButton from 'core/components/InstallButton';
|
||||
import I18nProvider from 'core/i18n/Provider';
|
||||
import { fakeAddon } from 'tests/client/amo/helpers';
|
||||
|
@ -233,7 +233,7 @@ describe('AddonDetail', () => {
|
|||
const root = render({
|
||||
addon: {
|
||||
...fakeAddon,
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
previewURL: 'https://amo/preview.png',
|
||||
},
|
||||
getBrowserThemeData: () => '{}',
|
||||
|
@ -252,7 +252,7 @@ describe('AddonDetail', () => {
|
|||
const root = render({
|
||||
addon: {
|
||||
...fakeAddon,
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
previewURL: 'https://amo/preview.png',
|
||||
},
|
||||
getBrowserThemeData: () => '{}',
|
||||
|
@ -268,7 +268,7 @@ describe('AddonDetail', () => {
|
|||
const rootNode = renderAsDOMNode({
|
||||
addon: {
|
||||
...fakeAddon,
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
previewURL: 'https://amo/preview.png',
|
||||
},
|
||||
getBrowserThemeData: () => '{}',
|
||||
|
@ -281,7 +281,7 @@ describe('AddonDetail', () => {
|
|||
const rootNode = renderAsDOMNode({
|
||||
addon: {
|
||||
...fakeAddon,
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
previewURL: 'https://amo/preview.png',
|
||||
},
|
||||
getBrowserThemeData: () => '{"the":"themedata"}',
|
||||
|
@ -295,7 +295,7 @@ describe('AddonDetail', () => {
|
|||
const rootNode = renderAsDOMNode({
|
||||
addon: {
|
||||
...fakeAddon,
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
},
|
||||
getBrowserThemeData: () => '{}',
|
||||
previewTheme,
|
||||
|
@ -310,7 +310,7 @@ describe('AddonDetail', () => {
|
|||
const rootNode = renderAsDOMNode({
|
||||
addon: {
|
||||
...fakeAddon,
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
},
|
||||
getBrowserThemeData: () => '{}',
|
||||
resetPreviewTheme,
|
||||
|
|
|
@ -7,12 +7,9 @@ import { findDOMNode } from 'react-dom';
|
|||
import { Provider } from 'react-redux';
|
||||
|
||||
import * as landingActions from 'amo/actions/landing';
|
||||
import {
|
||||
LandingPageBase,
|
||||
mapStateToProps,
|
||||
singularizeAddonType,
|
||||
} from 'amo/components/LandingPage';
|
||||
import { LandingPageBase, mapStateToProps } from 'amo/components/LandingPage';
|
||||
import createStore from 'amo/store';
|
||||
import { ADDON_TYPE_EXTENSION, ADDON_TYPE_THEME } from 'core/constants';
|
||||
import I18nProvider from 'core/i18n/Provider';
|
||||
import { fakeAddon } from 'tests/client/amo/helpers';
|
||||
import { getFakeI18nInst } from 'tests/client/helpers';
|
||||
|
@ -33,7 +30,7 @@ describe('<LandingPage />', () => {
|
|||
|
||||
it('renders a LandingPage with no addons set', () => {
|
||||
const root = render({
|
||||
addonType: 'extension',
|
||||
addonType: ADDON_TYPE_EXTENSION,
|
||||
});
|
||||
|
||||
assert.include(root.textContent, 'Featured extensions');
|
||||
|
@ -42,7 +39,7 @@ describe('<LandingPage />', () => {
|
|||
|
||||
it('renders a LandingPage with themes HTML', () => {
|
||||
const root = render({
|
||||
addonType: 'theme',
|
||||
addonType: ADDON_TYPE_THEME,
|
||||
});
|
||||
|
||||
assert.include(root.textContent, 'Featured themes');
|
||||
|
@ -93,8 +90,8 @@ describe('<LandingPage />', () => {
|
|||
},
|
||||
}));
|
||||
const root = render({
|
||||
...singularizeAddonType(null, { params: { pluralAddonType: 'themes' } }),
|
||||
...mapStateToProps(store.getState()),
|
||||
...mapStateToProps(
|
||||
store.getState(), { params: { visibleAddonType: 'themes' } }),
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { mapStateToProps } from 'amo/containers/CategoryList';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
|
||||
|
||||
describe('<CategoryList />', () => {
|
||||
|
@ -8,7 +9,7 @@ describe('<CategoryList />', () => {
|
|||
categories: {
|
||||
categories: {
|
||||
android: {
|
||||
theme: {
|
||||
[ADDON_TYPE_THEME]: {
|
||||
nature: {
|
||||
name: 'Nature',
|
||||
slug: 'nature',
|
||||
|
@ -21,11 +22,11 @@ describe('<CategoryList />', () => {
|
|||
loading: true,
|
||||
},
|
||||
}, {
|
||||
params: { addonType: 'themes' },
|
||||
params: { visibleAddonType: 'themes' },
|
||||
});
|
||||
|
||||
assert.deepEqual(props, {
|
||||
addonType: 'theme',
|
||||
addonType: ADDON_TYPE_THEME,
|
||||
categories: {
|
||||
nature: {
|
||||
name: 'Nature',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { mapStateToProps } from 'amo/containers/CategoryPage';
|
||||
import createStore from 'amo/store';
|
||||
import { searchStart } from 'core/actions/search';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
|
||||
|
||||
describe('CategoryPage.mapStateToProps()', () => {
|
||||
|
@ -9,7 +10,7 @@ describe('CategoryPage.mapStateToProps()', () => {
|
|||
|
||||
before(() => {
|
||||
filters = {
|
||||
addonType: 'theme',
|
||||
addonType: ADDON_TYPE_THEME,
|
||||
category: 'ad-block',
|
||||
clientApp: 'firefox',
|
||||
};
|
||||
|
@ -17,7 +18,7 @@ describe('CategoryPage.mapStateToProps()', () => {
|
|||
location: { query: {} },
|
||||
params: {
|
||||
application: 'firefox',
|
||||
addonType: 'theme',
|
||||
visibleAddonType: 'themes',
|
||||
slug: 'ad-block',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { getLanding } from 'amo/actions/landing';
|
||||
import landing from 'amo/reducers/landing';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
|
||||
|
||||
describe('landing reducer', () => {
|
||||
let initialData;
|
||||
|
@ -48,9 +50,9 @@ describe('landing reducer', () => {
|
|||
};
|
||||
const {
|
||||
addonType, featured, highlyRated, loading, popular,
|
||||
} = landing(initialState, getLanding({ addonType: 'theme' }));
|
||||
} = landing(initialState, getLanding({ addonType: ADDON_TYPE_THEME }));
|
||||
|
||||
assert.equal(addonType, 'theme');
|
||||
assert.equal(addonType, ADDON_TYPE_THEME);
|
||||
assert.equal(loading, true);
|
||||
assert.deepEqual(featured, { foo: 'bar' });
|
||||
assert.deepEqual(highlyRated, { count: 0 });
|
||||
|
@ -70,7 +72,7 @@ describe('landing reducer', () => {
|
|||
const { featured, highlyRated, popular } = landing(initialData, {
|
||||
type: 'LANDING_LOADED',
|
||||
payload: {
|
||||
addonType: 'theme',
|
||||
addonType: ADDON_TYPE_THEME,
|
||||
featured: {
|
||||
entities,
|
||||
result: { count: 2, results: ['foo', 'food'] },
|
||||
|
@ -99,7 +101,7 @@ describe('landing reducer', () => {
|
|||
}, {
|
||||
type: 'LANDING_LOADED',
|
||||
payload: {
|
||||
addonType: 'theme',
|
||||
addonType: ADDON_TYPE_THEME,
|
||||
featured: {
|
||||
entities,
|
||||
result: { count: 2, results: ['foo', 'food'] },
|
||||
|
@ -113,12 +115,12 @@ describe('landing reducer', () => {
|
|||
|
||||
describe('LANDING_FAILED', () => {
|
||||
it('sets loading to false on failure', () => {
|
||||
const initialState = landing(initialData, { type: 'LANDING_GET', payload: { addonType: 'theme' } });
|
||||
const initialState = landing(initialData, { type: 'LANDING_GET', payload: { addonType: ADDON_TYPE_THEME } });
|
||||
const state = landing(initialState,
|
||||
{ type: 'LANDING_FAILED', payload: { page: 2, addonType: 'theme' } });
|
||||
{ type: 'LANDING_FAILED', payload: { page: 2, addonType: ADDON_TYPE_THEME } });
|
||||
|
||||
assert.deepEqual(state, {
|
||||
addonType: 'theme',
|
||||
addonType: ADDON_TYPE_THEME,
|
||||
featured: { count: 0, results: [] },
|
||||
highlyRated: { count: 0, results: [] },
|
||||
loading: false,
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
import createStore from 'amo/store';
|
||||
import * as landingActions from 'amo/actions/landing';
|
||||
import * as api from 'core/api';
|
||||
import {
|
||||
ADDON_TYPE_THEME,
|
||||
SEARCH_SORT_POPULAR,
|
||||
SEARCH_SORT_TOP_RATED,
|
||||
} from 'core/constants';
|
||||
import { loadLandingAddons } from 'amo/utils';
|
||||
|
||||
|
||||
describe('amo/utils', () => {
|
||||
describe('loadLandingAddons()', () => {
|
||||
const addonType = 'theme';
|
||||
const addonType = ADDON_TYPE_THEME;
|
||||
let ownProps;
|
||||
|
||||
beforeEach(() => {
|
||||
ownProps = {
|
||||
params: {
|
||||
application: 'android',
|
||||
pluralAddonType: 'themes',
|
||||
visibleAddonType: 'themes',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -35,7 +40,7 @@ describe('amo/utils', () => {
|
|||
.once()
|
||||
.withArgs({
|
||||
api: {},
|
||||
filters: { addonType, page_size: 4, sort: 'rating' },
|
||||
filters: { addonType, page_size: 4, sort: SEARCH_SORT_TOP_RATED },
|
||||
page: 1,
|
||||
})
|
||||
.returns(Promise.resolve({ entities, result }));
|
||||
|
@ -44,7 +49,7 @@ describe('amo/utils', () => {
|
|||
.once()
|
||||
.withArgs({
|
||||
api: {},
|
||||
filters: { addonType, page_size: 4, sort: 'hotness' },
|
||||
filters: { addonType, page_size: 4, sort: SEARCH_SORT_POPULAR },
|
||||
page: 1,
|
||||
})
|
||||
.returns(Promise.resolve({ entities, result }));
|
||||
|
|
|
@ -4,13 +4,14 @@ import { renderIntoDocument } from 'react-addons-test-utils';
|
|||
|
||||
import createStore from 'amo/store';
|
||||
import {
|
||||
ADDON_TYPE_EXTENSION,
|
||||
ADDON_TYPE_THEME,
|
||||
CLOSE_INFO,
|
||||
DISABLED,
|
||||
DOWNLOAD_FAILED,
|
||||
DOWNLOAD_PROGRESS,
|
||||
ENABLED,
|
||||
ERROR,
|
||||
EXTENSION_TYPE,
|
||||
FATAL_ERROR,
|
||||
FATAL_INSTALL_ERROR,
|
||||
FATAL_UNINSTALL_ERROR,
|
||||
|
@ -24,7 +25,8 @@ import {
|
|||
THEME_INSTALL,
|
||||
THEME_PREVIEW,
|
||||
THEME_RESET_PREVIEW,
|
||||
THEME_TYPE,
|
||||
TRACKING_TYPE_EXTENSION,
|
||||
TRACKING_TYPE_THEME,
|
||||
UNINSTALL_CATEGORY,
|
||||
UNINSTALLED,
|
||||
UNINSTALLING,
|
||||
|
@ -115,7 +117,11 @@ describe('withInstallHelpers inner functions', () => {
|
|||
const installURL = 'http://the.url';
|
||||
const { setCurrentStatus } = mapDispatchToProps(dispatch, {
|
||||
_addonManager: getFakeAddonManagerWrapper({
|
||||
getAddon: Promise.resolve({ type: EXTENSION_TYPE, isActive: false, isEnabled: false }),
|
||||
getAddon: Promise.resolve({
|
||||
isActive: false,
|
||||
isEnabled: false,
|
||||
type: ADDON_TYPE_EXTENSION,
|
||||
}),
|
||||
}),
|
||||
guid,
|
||||
installURL,
|
||||
|
@ -135,7 +141,11 @@ describe('withInstallHelpers inner functions', () => {
|
|||
const installURL = 'http://the.url';
|
||||
const { setCurrentStatus } = mapDispatchToProps(dispatch, {
|
||||
_addonManager: getFakeAddonManagerWrapper({
|
||||
getAddon: Promise.resolve({ type: EXTENSION_TYPE, isActive: false, isEnabled: true }),
|
||||
getAddon: Promise.resolve({
|
||||
isActive: false,
|
||||
isEnabled: true,
|
||||
type: ADDON_TYPE_EXTENSION,
|
||||
}),
|
||||
}),
|
||||
guid,
|
||||
installURL,
|
||||
|
@ -151,7 +161,7 @@ describe('withInstallHelpers inner functions', () => {
|
|||
|
||||
it('sets the status to ENABLED when an enabled theme is found', () => {
|
||||
const fakeAddonManager = getFakeAddonManagerWrapper({
|
||||
getAddon: Promise.resolve({ type: THEME_TYPE, isActive: true, isEnabled: true }),
|
||||
getAddon: Promise.resolve({ type: ADDON_TYPE_THEME, isActive: true, isEnabled: true }),
|
||||
});
|
||||
const dispatch = sinon.spy();
|
||||
const guid = '@foo';
|
||||
|
@ -169,7 +179,11 @@ describe('withInstallHelpers inner functions', () => {
|
|||
|
||||
it('sets the status to DISABLED when an inactive theme is found', () => {
|
||||
const fakeAddonManager = getFakeAddonManagerWrapper({
|
||||
getAddon: Promise.resolve({ type: THEME_TYPE, isActive: false, isEnabled: true }),
|
||||
getAddon: Promise.resolve({
|
||||
isActive: false,
|
||||
isEnabled: true,
|
||||
type: ADDON_TYPE_THEME,
|
||||
}),
|
||||
});
|
||||
const dispatch = sinon.spy();
|
||||
const guid = '@foo';
|
||||
|
@ -187,7 +201,11 @@ describe('withInstallHelpers inner functions', () => {
|
|||
|
||||
it('sets the status to DISABLED when a disabled theme is found', () => {
|
||||
const fakeAddonManager = getFakeAddonManagerWrapper({
|
||||
getAddon: Promise.resolve({ type: THEME_TYPE, isActive: true, isEnabled: false }),
|
||||
getAddon: Promise.resolve({
|
||||
isActive: true,
|
||||
isEnabled: false,
|
||||
type: ADDON_TYPE_THEME,
|
||||
}),
|
||||
});
|
||||
const dispatch = sinon.spy();
|
||||
const guid = '@foo';
|
||||
|
@ -353,7 +371,7 @@ describe('withInstallHelpers inner functions', () => {
|
|||
it('tracks an addon install', () => {
|
||||
const fakeAddonManager = getFakeAddonManagerWrapper();
|
||||
const name = 'hai-addon';
|
||||
const type = 'extension';
|
||||
const type = ADDON_TYPE_EXTENSION;
|
||||
const dispatch = sinon.spy();
|
||||
const fakeTracking = {
|
||||
sendEvent: sinon.spy(),
|
||||
|
@ -364,7 +382,7 @@ describe('withInstallHelpers inner functions', () => {
|
|||
return install({ guid, installURL, name, type })
|
||||
.then(() => {
|
||||
assert(fakeTracking.sendEvent.calledWith({
|
||||
action: 'addon',
|
||||
action: TRACKING_TYPE_EXTENSION,
|
||||
category: INSTALL_CATEGORY,
|
||||
label: 'hai-addon',
|
||||
}));
|
||||
|
@ -494,7 +512,7 @@ describe('withInstallHelpers inner functions', () => {
|
|||
return uninstall({ guid, installURL, name, type })
|
||||
.then(() => {
|
||||
assert.ok(fakeTracking.sendEvent.calledWith({
|
||||
action: 'addon',
|
||||
action: TRACKING_TYPE_EXTENSION,
|
||||
category: UNINSTALL_CATEGORY,
|
||||
label: 'whatevs',
|
||||
}), 'correctly called');
|
||||
|
@ -510,10 +528,10 @@ describe('withInstallHelpers inner functions', () => {
|
|||
};
|
||||
const { uninstall } = mapDispatchToProps(dispatch,
|
||||
{ _tracking: fakeTracking, _addonManager: fakeAddonManager });
|
||||
return uninstall({ guid, installURL, name, type: THEME_TYPE })
|
||||
return uninstall({ guid, installURL, name, type: ADDON_TYPE_THEME })
|
||||
.then(() => {
|
||||
assert(fakeTracking.sendEvent.calledWith({
|
||||
action: 'theme',
|
||||
action: TRACKING_TYPE_THEME,
|
||||
category: UNINSTALL_CATEGORY,
|
||||
label: 'whatevs',
|
||||
}));
|
||||
|
@ -551,13 +569,16 @@ describe('withInstallHelpers inner functions', () => {
|
|||
|
||||
it('requires an installURL for extensions', () => {
|
||||
assert.throws(() => {
|
||||
makeMapDispatchToProps({})(sinon.spy(), { type: EXTENSION_TYPE });
|
||||
makeMapDispatchToProps({})(sinon.spy(), { type: ADDON_TYPE_EXTENSION });
|
||||
}, /installURL is required/);
|
||||
assert.doesNotThrow(() => {
|
||||
makeMapDispatchToProps({})(sinon.spy(), { type: EXTENSION_TYPE, installURL: 'foo.com' });
|
||||
makeMapDispatchToProps({})(sinon.spy(), {
|
||||
installURL: 'foo.com',
|
||||
type: ADDON_TYPE_EXTENSION,
|
||||
});
|
||||
});
|
||||
assert.doesNotThrow(() => {
|
||||
makeMapDispatchToProps({})(sinon.spy(), { type: THEME_TYPE });
|
||||
makeMapDispatchToProps({})(sinon.spy(), { type: ADDON_TYPE_THEME });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -567,7 +588,7 @@ describe('withInstallHelpers inner functions', () => {
|
|||
name: 'hai-theme',
|
||||
guid: '{install-theme}',
|
||||
status: UNINSTALLED,
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
};
|
||||
|
||||
function installThemeStubs() {
|
||||
|
@ -593,7 +614,7 @@ describe('withInstallHelpers inner functions', () => {
|
|||
const stubs = installThemeStubs();
|
||||
installTheme(node, addon, stubs);
|
||||
assert(stubs._tracking.sendEvent.calledWith({
|
||||
action: 'theme',
|
||||
action: TRACKING_TYPE_THEME,
|
||||
category: INSTALL_CATEGORY,
|
||||
label: 'hai-theme',
|
||||
}));
|
||||
|
@ -609,7 +630,7 @@ describe('withInstallHelpers inner functions', () => {
|
|||
});
|
||||
|
||||
it('does not try to install theme if it is an extension', () => {
|
||||
const addon = { ...baseAddon, type: EXTENSION_TYPE };
|
||||
const addon = { ...baseAddon, type: ADDON_TYPE_EXTENSION };
|
||||
const node = sinon.stub();
|
||||
const stubs = installThemeStubs();
|
||||
installTheme(node, addon, stubs);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import config from 'config';
|
||||
|
||||
import * as api from 'core/api';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
import { ErrorHandler } from 'core/errorHandler';
|
||||
import { unexpectedSuccess } from 'tests/client/helpers';
|
||||
|
||||
|
@ -234,12 +235,12 @@ describe('api', () => {
|
|||
|
||||
it('sets the app, lang, and type query', () => {
|
||||
mockWindow.expects('fetch')
|
||||
.withArgs(`${apiHost}/api/v3/addons/featured/?app=android&type=theme&page=&lang=en-US`)
|
||||
.withArgs(`${apiHost}/api/v3/addons/featured/?app=android&type=persona&page=&lang=en-US`)
|
||||
.once()
|
||||
.returns(mockResponse());
|
||||
return api.featured({
|
||||
api: { clientApp: 'android', lang: 'en-US' },
|
||||
filters: { addonType: 'theme' },
|
||||
filters: { addonType: ADDON_TYPE_THEME },
|
||||
})
|
||||
.then((response) => {
|
||||
assert.deepEqual(response, {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { findDOMNode } from 'react-dom';
|
|||
|
||||
import { InstallButtonBase } from 'core/components/InstallButton';
|
||||
import InstallSwitch from 'core/components/InstallSwitch';
|
||||
import { EXTENSION_TYPE, THEME_TYPE, UNKNOWN } from 'core/constants';
|
||||
import { ADDON_TYPE_EXTENSION, ADDON_TYPE_THEME, UNKNOWN } from 'core/constants';
|
||||
import * as themePreview from 'core/themePreview';
|
||||
import { getFakeI18nInst, shallowRender } from 'tests/client/helpers';
|
||||
import Button from 'ui/components/Button';
|
||||
|
@ -12,7 +12,7 @@ import Button from 'ui/components/Button';
|
|||
describe('<InstallButton />', () => {
|
||||
it('renders InstallSwitch when mozAddonManager is available', () => {
|
||||
const i18n = getFakeI18nInst();
|
||||
const addon = { type: THEME_TYPE, id: 'foo@personas.mozilla.org' };
|
||||
const addon = { type: ADDON_TYPE_THEME, id: 'foo@personas.mozilla.org' };
|
||||
const root = shallowRender(
|
||||
<InstallButtonBase foo="foo" addon={addon} hasAddonManager i18n={i18n} />);
|
||||
assert.equal(root.type, 'div');
|
||||
|
@ -30,7 +30,7 @@ describe('<InstallButton />', () => {
|
|||
|
||||
it('renders a theme button when mozAddonManager is not available', () => {
|
||||
const i18n = getFakeI18nInst();
|
||||
const addon = { type: THEME_TYPE, id: 'foo@personas.mozilla.org' };
|
||||
const addon = { type: ADDON_TYPE_THEME, id: 'foo@personas.mozilla.org' };
|
||||
const root = shallowRender(
|
||||
<InstallButtonBase
|
||||
addon={addon} foo="foo" hasAddonManager={false} i18n={i18n} />);
|
||||
|
@ -48,7 +48,7 @@ describe('<InstallButton />', () => {
|
|||
it('calls installTheme when clicked', () => {
|
||||
const installTheme = sinon.spy();
|
||||
const i18n = getFakeI18nInst();
|
||||
const addon = { type: THEME_TYPE, id: 'foo@personas.mozilla.org' };
|
||||
const addon = { type: ADDON_TYPE_THEME, id: 'foo@personas.mozilla.org' };
|
||||
const root = findDOMNode(renderIntoDocument(
|
||||
<InstallButtonBase
|
||||
addon={addon} foo="foo" hasAddonManager={false} i18n={i18n}
|
||||
|
@ -63,7 +63,7 @@ describe('<InstallButton />', () => {
|
|||
|
||||
it('renders an add-on button when mozAddonManager is not available', () => {
|
||||
const i18n = getFakeI18nInst();
|
||||
const addon = { type: EXTENSION_TYPE, installURL: 'https://addons.mozilla.org/download' };
|
||||
const addon = { type: ADDON_TYPE_EXTENSION, installURL: 'https://addons.mozilla.org/download' };
|
||||
const root = shallowRender(
|
||||
<InstallButtonBase addon={addon} foo="foo" hasAddonManager={false} i18n={i18n} />);
|
||||
assert.equal(root.type, 'div');
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
ENABLING,
|
||||
INSTALLED,
|
||||
INSTALLING,
|
||||
THEME_TYPE,
|
||||
ADDON_TYPE_THEME,
|
||||
UNINSTALLED,
|
||||
UNINSTALLING,
|
||||
UNKNOWN,
|
||||
|
@ -145,7 +145,7 @@ describe('<InstallSwitch />', () => {
|
|||
const name = 'hai';
|
||||
const button = renderButton({
|
||||
installTheme,
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
guid,
|
||||
name,
|
||||
status: UNINSTALLED,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import addons from 'core/reducers/addons';
|
||||
import { API_THEME_TYPE, THEME_TYPE } from 'core/constants';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
|
||||
describe('addon reducer', () => {
|
||||
let originalState;
|
||||
|
@ -60,7 +60,7 @@ describe('addon reducer', () => {
|
|||
});
|
||||
|
||||
it('flattens theme data', () => {
|
||||
const type = API_THEME_TYPE;
|
||||
const type = ADDON_TYPE_THEME;
|
||||
const state = addons(originalState, {
|
||||
payload: {
|
||||
entities: {
|
||||
|
@ -80,7 +80,7 @@ describe('addon reducer', () => {
|
|||
slug: 'baz',
|
||||
theme_thing: 'some-data',
|
||||
guid: '42@personas.mozilla.org',
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { ADDON_TYPE_EXTENSION, ADDON_TYPE_THEME } from 'core/constants';
|
||||
import categories, { emptyCategoryList } from 'core/reducers/categories';
|
||||
|
||||
|
||||
|
@ -62,19 +63,19 @@ describe('categories reducer', () => {
|
|||
application: 'android',
|
||||
name: 'Naturé',
|
||||
slug: 'naturé',
|
||||
type: 'theme',
|
||||
type: ADDON_TYPE_THEME,
|
||||
},
|
||||
{
|
||||
application: 'android',
|
||||
name: 'Painting',
|
||||
slug: 'painting',
|
||||
type: 'theme',
|
||||
type: ADDON_TYPE_THEME,
|
||||
},
|
||||
{
|
||||
application: 'firefox',
|
||||
name: 'Anime',
|
||||
slug: 'anime',
|
||||
type: 'theme',
|
||||
type: ADDON_TYPE_THEME,
|
||||
},
|
||||
{
|
||||
application: 'firefox',
|
||||
|
@ -104,7 +105,7 @@ describe('categories reducer', () => {
|
|||
it('sets the categories', () => {
|
||||
assert.deepEqual(state.categories, {
|
||||
firefox: {
|
||||
extension: {
|
||||
[ADDON_TYPE_EXTENSION]: {
|
||||
'alert-update': {
|
||||
application: 'firefox',
|
||||
name: 'Alerts & Update',
|
||||
|
@ -118,17 +119,17 @@ describe('categories reducer', () => {
|
|||
type: 'extension',
|
||||
},
|
||||
},
|
||||
theme: {
|
||||
[ADDON_TYPE_THEME]: {
|
||||
anime: {
|
||||
application: 'firefox',
|
||||
name: 'Anime',
|
||||
slug: 'anime',
|
||||
type: 'theme',
|
||||
type: ADDON_TYPE_THEME,
|
||||
},
|
||||
},
|
||||
},
|
||||
android: {
|
||||
extension: {
|
||||
[ADDON_TYPE_EXTENSION]: {
|
||||
'alert-update': {
|
||||
application: 'android',
|
||||
name: 'Alerts & Update',
|
||||
|
@ -148,18 +149,18 @@ describe('categories reducer', () => {
|
|||
type: 'extension',
|
||||
},
|
||||
},
|
||||
theme: {
|
||||
[ADDON_TYPE_THEME]: {
|
||||
naturé: {
|
||||
application: 'android',
|
||||
name: 'Naturé',
|
||||
slug: 'naturé',
|
||||
type: 'theme',
|
||||
type: ADDON_TYPE_THEME,
|
||||
},
|
||||
painting: {
|
||||
application: 'android',
|
||||
name: 'Painting',
|
||||
slug: 'painting',
|
||||
type: 'theme',
|
||||
type: ADDON_TYPE_THEME,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import createStore from 'amo/store';
|
||||
import * as searchActions from 'core/actions/search';
|
||||
import * as api from 'core/api';
|
||||
import { ADDON_TYPE_THEME } from 'core/constants';
|
||||
import { loadByCategoryIfNeeded, mapStateToProps } from 'core/searchUtils';
|
||||
|
||||
|
||||
|
@ -27,15 +28,15 @@ describe('searchUtils loadByCategoryIfNeeded()', () => {
|
|||
|
||||
before(() => {
|
||||
filters = {
|
||||
addonType: 'theme',
|
||||
addonType: ADDON_TYPE_THEME,
|
||||
category: 'anime',
|
||||
clientApp: 'android',
|
||||
};
|
||||
ownProps = {
|
||||
location: { query: {} },
|
||||
params: {
|
||||
addonType: 'theme',
|
||||
application: 'android',
|
||||
visibleAddonType: 'themes',
|
||||
slug: 'anime',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
/* global window */
|
||||
|
||||
import { Tracking, getAction } from 'core/tracking';
|
||||
import { EXTENSION_TYPE, THEME_TYPE } from 'core/constants';
|
||||
import {
|
||||
ADDON_TYPE_EXTENSION,
|
||||
ADDON_TYPE_THEME,
|
||||
TRACKING_TYPE_EXTENSION,
|
||||
TRACKING_TYPE_THEME,
|
||||
} from 'core/constants';
|
||||
|
||||
|
||||
describe('Tracking', () => {
|
||||
|
@ -108,12 +113,12 @@ describe('Tracking', () => {
|
|||
});
|
||||
|
||||
describe('getAction', () => {
|
||||
it('returns addon for EXTENSION_TYPE', () => {
|
||||
assert.equal(getAction(EXTENSION_TYPE), 'addon');
|
||||
it('returns addon for TYPE_EXTENSION', () => {
|
||||
assert.equal(getAction(ADDON_TYPE_EXTENSION), TRACKING_TYPE_EXTENSION);
|
||||
});
|
||||
|
||||
it('returns theme for THEME_TYPE', () => {
|
||||
assert.equal(getAction(THEME_TYPE), 'theme');
|
||||
it('returns theme for TYPE_THEME', () => {
|
||||
assert.equal(getAction(ADDON_TYPE_THEME), TRACKING_TYPE_THEME);
|
||||
});
|
||||
|
||||
it('returns invalid for unknown type', () => {
|
||||
|
|
|
@ -8,6 +8,7 @@ import * as categoriesActions from 'core/actions/categories';
|
|||
import * as api from 'core/api';
|
||||
import {
|
||||
addQueryParams,
|
||||
apiAddonType,
|
||||
convertBoolean,
|
||||
findAddon,
|
||||
getClientApp,
|
||||
|
@ -18,10 +19,33 @@ import {
|
|||
loadAddonIfNeeded,
|
||||
loadCategoriesIfNeeded,
|
||||
nl2br,
|
||||
visibleAddonType,
|
||||
} from 'core/utils';
|
||||
import { unexpectedSuccess } from 'tests/client/helpers';
|
||||
|
||||
|
||||
describe('apiAddonType', () => {
|
||||
it('maps plural/visible addonTypes to internal types', () => {
|
||||
assert.equal(apiAddonType('extensions'), 'extension');
|
||||
assert.equal(apiAddonType('themes'), 'persona');
|
||||
});
|
||||
|
||||
it('fails on unrecognised plural/visible addonType', () => {
|
||||
assert.throws(() => {
|
||||
// "theme" is not a valid pluralAddonType mapping; it should be "themes".
|
||||
apiAddonType('theme');
|
||||
}, '"theme" not found in API_ADDON_TYPES_MAPPING');
|
||||
});
|
||||
|
||||
// See:
|
||||
// https://github.com/mozilla/addons-frontend/pull/1541#discussion_r95861202
|
||||
it('does not return a false positive on a method', () => {
|
||||
assert.throws(() => {
|
||||
apiAddonType('hasOwnProperty');
|
||||
}, '"hasOwnProperty" not found in API_ADDON_TYPES_MAPPING');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getClientConfig', () => {
|
||||
const fakeConfig = new Map();
|
||||
fakeConfig.set('hai', 'there');
|
||||
|
@ -472,3 +496,25 @@ describe('ngettext', () => {
|
|||
assert.equal('2 files', fileCount(2));
|
||||
});
|
||||
});
|
||||
|
||||
describe('visibleAddonType', () => {
|
||||
it('maps internal addonTypes to plural/visible types', () => {
|
||||
assert.equal(visibleAddonType('extension'), 'extensions');
|
||||
assert.equal(visibleAddonType('persona'), 'themes');
|
||||
});
|
||||
|
||||
it('fails on unrecognised internal addonType', () => {
|
||||
assert.throws(() => {
|
||||
// "theme" is not a valid visible addonType; it should be "themes".
|
||||
visibleAddonType('personas');
|
||||
}, '"personas" not found in VISIBLE_ADDON_TYPES_MAPPING');
|
||||
});
|
||||
|
||||
// See:
|
||||
// https://github.com/mozilla/addons-frontend/pull/1541#discussion_r95861202
|
||||
it('does not return a false positive on a method', () => {
|
||||
assert.throws(() => {
|
||||
visibleAddonType('hasOwnProperty');
|
||||
}, '"hasOwnProperty" not found in VISIBLE_ADDON_TYPES_MAPPING');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,15 +12,16 @@ import {
|
|||
mapStateToProps,
|
||||
} from 'disco/components/Addon';
|
||||
import {
|
||||
ADDON_TYPE_EXTENSION,
|
||||
ADDON_TYPE_THEME,
|
||||
CLICK_CATEGORY,
|
||||
DOWNLOAD_FAILED,
|
||||
ERROR,
|
||||
EXTENSION_TYPE,
|
||||
FATAL_ERROR,
|
||||
FATAL_INSTALL_ERROR,
|
||||
FATAL_UNINSTALL_ERROR,
|
||||
INSTALL_FAILED,
|
||||
THEME_TYPE,
|
||||
TRACKING_TYPE_EXTENSION,
|
||||
UNINSTALLED,
|
||||
UNINSTALLING,
|
||||
} from 'core/constants';
|
||||
|
@ -245,7 +246,7 @@ describe('<Addon />', () => {
|
|||
_tracking: fakeTracking,
|
||||
name: 'foo',
|
||||
heading: 'This is <span>an <a href="https://addons.mozilla.org">add-on</a>/span>',
|
||||
type: EXTENSION_TYPE,
|
||||
type: ADDON_TYPE_EXTENSION,
|
||||
};
|
||||
const root = renderAddon({ addon: data, ...data });
|
||||
const heading = findDOMNode(root).querySelector('.heading');
|
||||
|
@ -253,7 +254,7 @@ describe('<Addon />', () => {
|
|||
// bubbling.
|
||||
Simulate.click(heading, { target: { nodeName: 'A' } });
|
||||
assert.ok(fakeTracking.sendEvent.calledWith({
|
||||
action: 'addon',
|
||||
action: TRACKING_TYPE_EXTENSION,
|
||||
category: CLICK_CATEGORY,
|
||||
label: 'foo',
|
||||
}), sinon.format(fakeTracking.sendEvent.firstCall.args));
|
||||
|
@ -265,7 +266,7 @@ describe('<Addon />', () => {
|
|||
let root;
|
||||
|
||||
beforeEach(() => {
|
||||
const data = { ...result, type: THEME_TYPE };
|
||||
const data = { ...result, type: ADDON_TYPE_THEME };
|
||||
root = renderAddon({ addon: data, ...data });
|
||||
});
|
||||
|
||||
|
@ -288,7 +289,7 @@ describe('<Addon />', () => {
|
|||
beforeEach(() => {
|
||||
previewTheme = sinon.spy();
|
||||
resetPreviewTheme = sinon.spy();
|
||||
const data = { ...result, type: THEME_TYPE, previewTheme, resetPreviewTheme };
|
||||
const data = { ...result, type: ADDON_TYPE_THEME, previewTheme, resetPreviewTheme };
|
||||
root = renderAddon({ addon: data, ...data });
|
||||
themeImage = findDOMNode(root).querySelector('.theme-image');
|
||||
});
|
||||
|
@ -318,7 +319,7 @@ describe('<Addon />', () => {
|
|||
const data = {
|
||||
...result,
|
||||
addon: sinon.stub(),
|
||||
type: THEME_TYPE,
|
||||
type: ADDON_TYPE_THEME,
|
||||
status: UNINSTALLED,
|
||||
installTheme,
|
||||
};
|
||||
|
|
|
@ -4,8 +4,8 @@ import { findDOMNode } from 'react-dom';
|
|||
|
||||
import { loadEntities } from 'core/actions';
|
||||
import {
|
||||
ADDON_TYPE_EXTENSION,
|
||||
GLOBAL_EVENTS,
|
||||
EXTENSION_TYPE,
|
||||
INSTALL_STATE,
|
||||
} from 'core/constants';
|
||||
import * as InfoDialog from 'core/containers/InfoDialog';
|
||||
|
@ -29,10 +29,10 @@ describe('AddonPage', () => {
|
|||
// Stub InfoDialog since it uses the store and is irrelevant.
|
||||
sinon.stub(InfoDialog, 'default', () => <p>InfoDialog</p>);
|
||||
const store = createStore({
|
||||
addons: { foo: { type: EXTENSION_TYPE } },
|
||||
addons: { foo: { type: ADDON_TYPE_EXTENSION } },
|
||||
discoResults: [{ addon: 'foo' }],
|
||||
});
|
||||
const results = [{ addon: 'foo', type: EXTENSION_TYPE }];
|
||||
const results = [{ addon: 'foo', type: ADDON_TYPE_EXTENSION }];
|
||||
const i18n = getFakeI18nInst();
|
||||
|
||||
// We need the providers for i18n and since InstallButton will pull data from the store.
|
||||
|
|
|
@ -4,7 +4,7 @@ import moment from 'moment';
|
|||
import React from 'react';
|
||||
import { createRenderer } from 'react-addons-test-utils';
|
||||
|
||||
import { EXTENSION_TYPE } from 'core/constants';
|
||||
import { ADDON_TYPE_EXTENSION } from 'core/constants';
|
||||
import { ngettext } from 'core/utils';
|
||||
|
||||
/*
|
||||
|
@ -48,7 +48,11 @@ export function findByTag(root, tag) {
|
|||
return matches[0];
|
||||
}
|
||||
|
||||
const enabledExtension = Promise.resolve({ type: EXTENSION_TYPE, isActive: true, isEnabled: true });
|
||||
const enabledExtension = Promise.resolve({
|
||||
isActive: true,
|
||||
isEnabled: true,
|
||||
type: ADDON_TYPE_EXTENSION,
|
||||
});
|
||||
|
||||
export function getFakeAddonManagerWrapper({ getAddon = enabledExtension } = {}) {
|
||||
return {
|
||||
|
|
Загрузка…
Ссылка в новой задаче