Expose whether a version is soft or hard-blocked in the blocked page (#13285)
* Expose whether a version is soft or hard-blocked in the blocked page
This commit is contained in:
Родитель
12e5d2ee1a
Коммит
65d99c8084
|
@ -83,52 +83,26 @@ export class BlockBase extends React.Component<InternalProps> {
|
|||
);
|
||||
}
|
||||
|
||||
renderDateAndURL(): React.Node | Array<React.Node | string> {
|
||||
renderURL(): React.Node | Array<React.Node | string> {
|
||||
const { block, i18n } = this.props;
|
||||
|
||||
if (!block) {
|
||||
return <LoadingText />;
|
||||
}
|
||||
|
||||
const content: Array<React.Node | string> = [
|
||||
i18n.sprintf(i18n.gettext('Blocked on %(date)s.'), {
|
||||
date: i18n.moment(block.created).format('ll'),
|
||||
}),
|
||||
];
|
||||
|
||||
if (block.url) {
|
||||
content.push(
|
||||
' ',
|
||||
return (
|
||||
<a key={block.url.url} href={block.url.outgoing} title={block.url.url}>
|
||||
{i18n.gettext('View block request')}
|
||||
</a>,
|
||||
'.',
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
renderVersions(): React.Node | string {
|
||||
const { block, i18n } = this.props;
|
||||
|
||||
if (!block) {
|
||||
return <LoadingText />;
|
||||
}
|
||||
|
||||
if (block.is_all_versions) {
|
||||
return i18n.gettext('Versions blocked: all versions.');
|
||||
}
|
||||
|
||||
const versions = block.versions.join(', ');
|
||||
|
||||
return i18n.sprintf(i18n.gettext('Versions blocked: %(versions)s.'), {
|
||||
versions,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const { block, errorHandler, i18n } = this.props;
|
||||
const { block, errorHandler, i18n, match } = this.props;
|
||||
|
||||
if (errorHandler.hasError()) {
|
||||
log.warn(`Captured API Error: ${errorHandler.capturedError.messages}`);
|
||||
|
@ -140,15 +114,40 @@ export class BlockBase extends React.Component<InternalProps> {
|
|||
return <ServerErrorPage />;
|
||||
}
|
||||
|
||||
const title =
|
||||
block && block.name
|
||||
? i18n.sprintf(
|
||||
i18n.gettext(`%(addonName)s has been blocked for your protection.`),
|
||||
{
|
||||
addonName: block.name,
|
||||
},
|
||||
)
|
||||
: i18n.gettext(`This add-on has been blocked for your protection.`);
|
||||
const isSoftBlocked =
|
||||
block?.soft_blocked.length &&
|
||||
(block?.soft_blocked.includes(match.params.versionId) ||
|
||||
!block?.blocked.length);
|
||||
let title;
|
||||
if (isSoftBlocked) {
|
||||
title =
|
||||
block && block.name
|
||||
? i18n.sprintf(
|
||||
i18n.gettext(
|
||||
`%(addonName)s is restricted for violating Mozilla policies.`,
|
||||
),
|
||||
{
|
||||
addonName: block.name,
|
||||
},
|
||||
)
|
||||
: i18n.gettext(
|
||||
`This add-on is restricted for violating Mozilla policies.`,
|
||||
);
|
||||
} else {
|
||||
title =
|
||||
block && block.name
|
||||
? i18n.sprintf(
|
||||
i18n.gettext(
|
||||
`%(addonName)s is blocked for violating Mozilla policies.`,
|
||||
),
|
||||
{
|
||||
addonName: block.name,
|
||||
},
|
||||
)
|
||||
: i18n.gettext(
|
||||
`This add-on is blocked for violating Mozilla policies.`,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Page>
|
||||
|
@ -159,13 +158,13 @@ export class BlockBase extends React.Component<InternalProps> {
|
|||
</Helmet>
|
||||
|
||||
<Card className="Block-content" header={title}>
|
||||
<h2>{i18n.gettext('Why was it blocked?')}</h2>
|
||||
<h2>{i18n.gettext('Why did this happen?')}</h2>
|
||||
<p
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={sanitizeHTML(
|
||||
i18n.sprintf(
|
||||
i18n.gettext(`This add-on violates %(startLink)sMozilla's
|
||||
Add-on Policies%(endLink)s.`),
|
||||
i18n.gettext(`This extension, theme, or plugin violates
|
||||
%(startLink)sMozilla's Add-on Policies%(endLink)s.`),
|
||||
{
|
||||
startLink: `<a href="${POLICIES_URL}">`,
|
||||
endLink: '</a>',
|
||||
|
@ -178,8 +177,16 @@ export class BlockBase extends React.Component<InternalProps> {
|
|||
|
||||
<h2>{i18n.gettext('What does this mean?')}</h2>
|
||||
<p>
|
||||
{i18n.gettext(`The problematic add-on or plugin will be
|
||||
automatically disabled and no longer usable.`)}
|
||||
{isSoftBlocked
|
||||
? i18n.gettext(`Until the violation is resolved, this add-on
|
||||
won't be available for download from addons.mozilla.org.
|
||||
If it’s already installed, it will be disabled and users
|
||||
will be informed about the violation. They may choose to
|
||||
enable the add-on again at their own risk.`)
|
||||
: i18n.gettext(`Until the violation is resolved, this add-on
|
||||
won't be available for download from addons.mozilla.org.
|
||||
It will be automatically disabled and no longer usable in
|
||||
Firefox.`)}
|
||||
</p>
|
||||
<p
|
||||
// eslint-disable-next-line react/no-danger
|
||||
|
@ -189,9 +196,10 @@ export class BlockBase extends React.Component<InternalProps> {
|
|||
or other third-party software that seriously compromises
|
||||
Firefox security, stability, or performance and meets
|
||||
%(criteriaStartLink)scertain criteria%(criteriaEndLink)s,
|
||||
the software may be blocked from general use. For more
|
||||
information, please read %(supportStartLink)sthis support
|
||||
article%(supportEndLink)s.`),
|
||||
the software may be blocked or restricted from general
|
||||
use. For more information, please read
|
||||
%(supportStartLink)sthis support article%(supportEndLink)s.
|
||||
`),
|
||||
{
|
||||
criteriaStartLink: `<a href="${CRITERIA_URL}">`,
|
||||
criteriaEndLink: '</a>',
|
||||
|
@ -202,11 +210,7 @@ export class BlockBase extends React.Component<InternalProps> {
|
|||
['a'],
|
||||
)}
|
||||
/>
|
||||
<p className="Block-metadata">
|
||||
{this.renderVersions()}
|
||||
<br />
|
||||
{this.renderDateAndURL()}
|
||||
</p>
|
||||
<p className="Block-metadata">{this.renderURL()}</p>
|
||||
</Card>
|
||||
</div>
|
||||
</Page>
|
||||
|
|
|
@ -14,7 +14,8 @@ export type ExternalBlockType = {|
|
|||
created: string,
|
||||
guid: string,
|
||||
id: number,
|
||||
versions: [string],
|
||||
soft_blocked: [string],
|
||||
blocked: [string],
|
||||
is_all_versions?: boolean,
|
||||
modified: string,
|
||||
reason: string | null,
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
createFakeBlockResult,
|
||||
createLocalizedString,
|
||||
dispatchClientMetadata,
|
||||
fakeI18n,
|
||||
getElement,
|
||||
renderPage as defaultRender,
|
||||
screen,
|
||||
|
@ -129,16 +128,14 @@ describe(__filename, () => {
|
|||
expect(
|
||||
within(screen.getByClassName('Block-reason')).getAllByRole('alert'),
|
||||
).toHaveLength(1);
|
||||
// 1. versions blocked
|
||||
// 2. date and URL
|
||||
expect(
|
||||
within(screen.getByClassName('Block-metadata')).getAllByRole('alert'),
|
||||
).toHaveLength(2);
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders a generic header/title when the block has no add-on name', async () => {
|
||||
const block = _createFakeBlockResult({ addonName: null });
|
||||
const title = 'This add-on has been blocked for your protection.';
|
||||
const title = 'This add-on is blocked for violating Mozilla policies.';
|
||||
store.dispatch(loadBlock({ block }));
|
||||
render();
|
||||
|
||||
|
@ -155,7 +152,7 @@ describe(__filename, () => {
|
|||
const block = _createFakeBlockResult({
|
||||
addonName: createLocalizedString(name),
|
||||
});
|
||||
const title = `${name} has been blocked for your protection.`;
|
||||
const title = `${name} is blocked for violating Mozilla policies.`;
|
||||
store.dispatch(loadBlock({ block }));
|
||||
render();
|
||||
|
||||
|
@ -168,6 +165,24 @@ describe(__filename, () => {
|
|||
expect(screen.getByText(title)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a generic soft-block header/title when the block has no add-on name', async () => {
|
||||
const block = _createFakeBlockResult({
|
||||
addonName: null,
|
||||
soft_blocked: ['42.0'],
|
||||
blocked: [],
|
||||
});
|
||||
const title = 'This add-on is restricted for violating Mozilla policies.';
|
||||
store.dispatch(loadBlock({ block }));
|
||||
render();
|
||||
|
||||
await waitFor(() =>
|
||||
expect(getElement('title')).toHaveTextContent(
|
||||
`${title} – Add-ons for Firefox (${lang})`,
|
||||
),
|
||||
);
|
||||
expect(screen.getByText(title)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a paragraph with the reason when the block has one', () => {
|
||||
const reason = 'this is a reason for a block';
|
||||
const block = _createFakeBlockResult({ reason });
|
||||
|
@ -186,69 +201,6 @@ describe(__filename, () => {
|
|||
expect(screen.queryByClassName('Block-reason')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders "all versions" when "is_all_versions" is true', () => {
|
||||
const block = _createFakeBlockResult({
|
||||
is_all_versions: true,
|
||||
});
|
||||
const i18n = fakeI18n();
|
||||
store.dispatch(loadBlock({ block }));
|
||||
render();
|
||||
|
||||
// The version info and the block date are inside the same tag, separated
|
||||
// by a </br>.
|
||||
expect(
|
||||
screen.getByTextAcrossTags(
|
||||
`Versions blocked: all versions.Blocked on ${i18n
|
||||
.moment(block.created)
|
||||
.format('ll')}.`,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the versions if "is_all_versions" is false', () => {
|
||||
const v1 = '12';
|
||||
const v2 = '34';
|
||||
const block = _createFakeBlockResult({
|
||||
versions: [v1, v2],
|
||||
is_all_versions: false,
|
||||
});
|
||||
const i18n = fakeI18n();
|
||||
store.dispatch(loadBlock({ block }));
|
||||
render();
|
||||
|
||||
// The version info and the block date are inside the same tag, separated
|
||||
// by a </br>.
|
||||
expect(
|
||||
screen.getByTextAcrossTags(
|
||||
`Versions blocked: ${v1}, ${v2}.Blocked on ${i18n
|
||||
.moment(block.created)
|
||||
.format('ll')}.`,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the versions if "is_all_versions" is missing', () => {
|
||||
const v1 = '12';
|
||||
const v2 = '34';
|
||||
const block = _createFakeBlockResult({
|
||||
versions: [v1, v2],
|
||||
is_all_versions: undefined,
|
||||
});
|
||||
const i18n = fakeI18n();
|
||||
store.dispatch(loadBlock({ block }));
|
||||
render();
|
||||
|
||||
// The version info and the block date are inside the same tag, separated
|
||||
// by a </br>.
|
||||
expect(
|
||||
screen.getByTextAcrossTags(
|
||||
`Versions blocked: ${v1}, ${v2}.Blocked on ${i18n
|
||||
.moment(block.created)
|
||||
.format('ll')}.`,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the reason with HTML tags removed', () => {
|
||||
const reason = 'this is a <a>reason for a block</a>';
|
||||
const block = _createFakeBlockResult({ reason });
|
||||
|
@ -307,7 +259,56 @@ describe(__filename, () => {
|
|||
});
|
||||
|
||||
expect(
|
||||
screen.getByText(`${name} has been blocked for your protection.`),
|
||||
screen.getByText(`${name} is blocked for violating Mozilla policies.`),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/It will be automatically disabled and no longer usable in Firefox./,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a soft-block page when the block has only soft-blocks', () => {
|
||||
const name = 'some-addon-name';
|
||||
const block = _createFakeBlockResult({
|
||||
addonName: createLocalizedString(name),
|
||||
soft_blocked: ['42.0'],
|
||||
blocked: [],
|
||||
});
|
||||
store.dispatch(loadBlock({ block }));
|
||||
|
||||
render();
|
||||
|
||||
expect(
|
||||
screen.getByText(`${name} is restricted for violating Mozilla policies.`),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/They may choose to enable the add-on again at their own risk./,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a soft-block page when a soft-blocked versionId is present in the URL', () => {
|
||||
const name = 'some-addon-name';
|
||||
const block = _createFakeBlockResult({
|
||||
addonName: createLocalizedString(name),
|
||||
soft_blocked: ['42.0'],
|
||||
});
|
||||
store.dispatch(loadBlock({ block }));
|
||||
|
||||
defaultRender({
|
||||
initialEntries: [`${getLocation()}42.0/`],
|
||||
store,
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByText(`${name} is restricted for violating Mozilla policies.`),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
/They may choose to enable the add-on again at their own risk./,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
|
|
@ -1312,7 +1312,8 @@ export const createFakeBlockResult = ({
|
|||
created: '2020-01-22T10:09:01Z',
|
||||
modified: '2020-01-22T10:09:01Z',
|
||||
guid,
|
||||
versions: ['0.1', '4.56'],
|
||||
blocked: ['0.1', '4.56'],
|
||||
soft_blocked: [],
|
||||
is_all_versions: false,
|
||||
addon_name: addonName,
|
||||
reason,
|
||||
|
|
Загрузка…
Ссылка в новой задаче