Bug 1566013 - Part 1: Process canonical manifest and hook it with the Manifest component r=Ola

Differential Revision: https://phabricator.services.mozilla.com/D43954

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Belén Albeza 2019-09-02 06:44:14 +00:00
Родитель a04d027dce
Коммит 22cb75164e
7 изменённых файлов: 158 добавлений и 81 удалений

Просмотреть файл

@ -4,6 +4,7 @@
"use strict";
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const {
createFactory,
PureComponent,
@ -12,63 +13,38 @@ const {
section,
} = require("devtools/client/shared/vendor/react-dom-factories");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const ManifestLoader = createFactory(require("../manifest/ManifestLoader"));
const Manifest = createFactory(require("./Manifest"));
const ManifestEmpty = createFactory(require("./ManifestEmpty"));
class ManifestPage extends PureComponent {
render() {
// TODO: needs to be replaced with data from Redux
const data = {
validation: [
{ level: "warning", message: "Icons item at index 0 is invalid." },
{ level: "error", message: "Random JSON error" },
{
level: "warning",
message:
"Icons item at index 2 is invalid. Icons item at index 2 is invalid. Icons item at index 2 is invalid. Icons item at index 2 is invalid.",
},
],
icons: [],
identity: [
{
key: "name",
value:
"Name is a verrry long name and the name is longer tha you thinnk because it is loooooooooooooooooooooooooooooooooooooooooooooooong",
},
{
key: "short_name",
value: "Na",
},
],
presentation: [
{ key: "display", value: "browser" },
{ key: "orientation", value: "landscape" },
{ key: "start_url", value: "root" },
{ key: "theme_color", value: "#345" },
{ key: "background_color", value: "#F9D" },
],
// TODO: Use well-defined types
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1576881
static get propTypes() {
return {
manifest: PropTypes.object,
};
}
const isManifestEmpty = !data;
render() {
const { manifest } = this.props;
return section(
{
className: `app-page ${isManifestEmpty ? "app-page--empty" : ""}`,
className: `app-page ${!manifest ? "app-page--empty" : ""}`,
},
ManifestLoader({}),
isManifestEmpty
? ManifestEmpty({})
: Manifest({
icons: data.icons,
identity: data.identity,
presentation: data.presentation,
validation: data.validation,
})
manifest ? Manifest({ ...manifest }) : ManifestEmpty({})
);
}
}
function mapStateToProps(state) {
return {
manifest: state.manifest.manifest,
};
}
// Exports
module.exports = ManifestPage;
module.exports = connect(mapStateToProps)(ManifestPage);

Просмотреть файл

@ -23,6 +23,12 @@ const PAGE_TYPES = {
const DEFAULT_PAGE = PAGE_TYPES.MANIFEST;
const MANIFEST_CATEGORIES = {
IDENTITY: "identity",
PRESENTATION: "presentation",
ICONS: "icons",
};
const MANIFEST_ISSUE_LEVELS = {
ERROR: "error",
WARNING: "warning",
@ -34,6 +40,7 @@ module.exports = Object.assign(
{
DEFAULT_PAGE,
PAGE_TYPES,
MANIFEST_CATEGORIES,
MANIFEST_ISSUE_LEVELS,
},
actionTypes

Просмотреть файл

@ -4,7 +4,83 @@
"use strict";
const { UPDATE_MANIFEST } = require("../constants");
const {
MANIFEST_CATEGORIES,
MANIFEST_ISSUE_LEVELS,
UPDATE_MANIFEST,
} = require("../constants");
function _processRawManifestIcons(rawIcons) {
// TODO: we are currently ignoring the following fields present in the data
// from the canonical manifest:
// - purpose
// - type
// We should include this info so we can pass it as props to the relevant
// component
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1577446
return rawIcons.map(icon => {
return {
key: icon.sizes.join(" "),
value: icon.src,
};
});
}
function _processRawManifestMembers(rawManifest) {
function getCategoryForMember(key) {
switch (key) {
case "name":
case "short_name":
return MANIFEST_CATEGORIES.IDENTITY;
default:
return MANIFEST_CATEGORIES.PRESENTATION;
}
}
const res = {
[MANIFEST_CATEGORIES.IDENTITY]: [],
[MANIFEST_CATEGORIES.PRESENTATION]: [],
};
// filter out extra metadata members (those with moz_ prefix) and icons
const rawMembers = Object.entries(rawManifest).filter(
([key, value]) => !key.startsWith("moz_") && !(key === "icons")
);
for (const [key, value] of rawMembers) {
const category = getCategoryForMember(key);
res[category].push({ key, value });
}
return res;
}
function _processRawManifestIssues(issues) {
return issues.map(x => {
return {
level: x.warn
? MANIFEST_ISSUE_LEVELS.WARNING
: MANIFEST_ISSUE_LEVELS.ERROR,
message: x.warn || x.error,
type: x.type || null,
};
});
}
function _processRawManifest(rawManifest) {
const res = {
url: rawManifest.moz_manifest_url,
};
// group manifest members by category
Object.assign(res, _processRawManifestMembers(rawManifest));
// process icons
res.icons = _processRawManifestIcons(rawManifest.icons || []);
// process error messages
res.validation = _processRawManifestIssues(rawManifest.moz_validation || []);
return res;
}
function ManifestState() {
return {
@ -18,7 +94,7 @@ function manifestReducer(state = ManifestState(), action) {
case UPDATE_MANIFEST:
const { manifest, errorMessage } = action;
return Object.assign({}, state, {
manifest,
manifest: manifest ? _processRawManifest(manifest) : null,
errorMessage,
});
default:

Просмотреть файл

@ -1,45 +1,36 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ManifestPage renders the expected snapshot 1`] = `
exports[`ManifestPage renders the expected snapshot when there is a manifest 1`] = `
<section
className="app-page "
>
<Connect(ManifestLoader) />
<Manifest
icons={Array []}
icons={
Array [
Object {
"key": "1x1",
"value": "something.png",
},
]
}
identity={
Array [
Object {
"key": "name",
"value": "Name is a verrry long name and the name is longer tha you thinnk because it is loooooooooooooooooooooooooooooooooooooooooooooooong",
},
Object {
"key": "short_name",
"value": "Na",
"value": "foo",
},
]
}
presentation={
Array [
Object {
"key": "display",
"value": "browser",
"key": "lorem",
"value": "ipsum",
},
Object {
"key": "orientation",
"value": "landscape",
},
Object {
"key": "start_url",
"value": "root",
},
Object {
"key": "theme_color",
"value": "#345",
},
Object {
"key": "background_color",
"value": "#F9D",
"key": "foo",
"value": "bar",
},
]
}
@ -47,18 +38,19 @@ exports[`ManifestPage renders the expected snapshot 1`] = `
Array [
Object {
"level": "warning",
"message": "Icons item at index 0 is invalid.",
},
Object {
"level": "error",
"message": "Random JSON error",
},
Object {
"level": "warning",
"message": "Icons item at index 2 is invalid. Icons item at index 2 is invalid. Icons item at index 2 is invalid. Icons item at index 2 is invalid.",
"message": "This is a warning",
},
]
}
/>
</section>
`;
exports[`ManifestPage renders the expected snapshot when there is no manifest 1`] = `
<section
className="app-page app-page--empty"
>
<Connect(ManifestLoader) />
<ManifestEmpty />
</section>
`;

Просмотреть файл

@ -7,6 +7,13 @@
const { shallow } = require("enzyme");
const { createFactory } = require("react");
const {
setupStore,
} = require("devtools/client/application/test/components/helpers/helpers");
const {
MANIFEST_SIMPLE,
} = require("devtools/client/application/test/components/fixtures/data/constants");
const ManifestPage = createFactory(
require("devtools/client/application/src/components/manifest/ManifestPage")
);
@ -16,8 +23,26 @@ const ManifestPage = createFactory(
*/
describe("ManifestPage", () => {
it("renders the expected snapshot", () => {
const wrapper = shallow(ManifestPage({}));
function buildStoreWithManifest(manifest) {
return setupStore({
preloadedState: {
manifest: {
manifest,
errorMessage: "",
},
},
});
}
it("renders the expected snapshot when there is a manifest", () => {
const store = buildStoreWithManifest(MANIFEST_SIMPLE);
const wrapper = shallow(ManifestPage({ store })).dive();
expect(wrapper).toMatchSnapshot();
});
it("renders the expected snapshot when there is no manifest", () => {
const store = buildStoreWithManifest(null);
const wrapper = shallow(ManifestPage({ store })).dive();
expect(wrapper).toMatchSnapshot();
});
});

Просмотреть файл

@ -4,6 +4,6 @@ exports[`PageSwitcher renders nothing when an invalid page is selected 1`] = `""
exports[`PageSwitcher renders nothing when no page is selected 1`] = `""`;
exports[`PageSwitcher renders the ManifestPage component when manifest page is selected 1`] = `<ManifestPage />`;
exports[`PageSwitcher renders the ManifestPage component when manifest page is selected 1`] = `<Connect(ManifestPage) />`;
exports[`PageSwitcher renders the WorkersPage component when workers page is selected 1`] = `<Connect(WorkersPage) />`;

Просмотреть файл

@ -18,6 +18,7 @@ add_task(async function() {
const action = updateManifest({ name: "foo" }, "some error");
const newState = manifestReducer(state, action);
equal(newState.manifest.name, "foo");
equal(newState.manifest.identity[0].key, "name");
equal(newState.manifest.identity[0].value, "foo");
equal(newState.errorMessage, "some error");
});