Add reducer/saga/api logic for reporting a collection (#12595)
This commit is contained in:
Родитель
3f52cb7a1a
Коммит
2f1458ab4f
|
@ -336,7 +336,7 @@
|
|||
"bundlewatch": [
|
||||
{
|
||||
"path": "./dist/static/amo-!(i18n-)*.js",
|
||||
"maxSize": "355 kB"
|
||||
"maxSize": "356 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/static/amo-i18n-*.js",
|
||||
|
|
|
@ -184,3 +184,56 @@ export function reportRating({
|
|||
apiState: api,
|
||||
});
|
||||
}
|
||||
|
||||
export type ReportCollectionParams = {|
|
||||
api: ApiState,
|
||||
collectionId: number,
|
||||
message: string | null,
|
||||
reason: string | null,
|
||||
reporterName: string | null,
|
||||
reporterEmail: string | null,
|
||||
auth: boolean,
|
||||
|};
|
||||
|
||||
export type ReportCollectionResponse = {|
|
||||
reporter: AbuseReporter | null,
|
||||
reporter_name: string | null,
|
||||
reporter_email: string | null,
|
||||
collection: {|
|
||||
id: number,
|
||||
|},
|
||||
message: string,
|
||||
reason: string | null,
|
||||
|};
|
||||
|
||||
// See: https://addons-server.readthedocs.io/en/latest/topics/api/abuse.html#submitting-a-collection-abuse-report
|
||||
export function reportCollection({
|
||||
api,
|
||||
collectionId,
|
||||
message,
|
||||
reason,
|
||||
reporterName,
|
||||
reporterEmail,
|
||||
auth,
|
||||
}: ReportCollectionParams): Promise<ReportCollectionResponse> {
|
||||
if (!reason) {
|
||||
invariant(
|
||||
message?.trim(),
|
||||
"message is required when reason isn't specified",
|
||||
);
|
||||
}
|
||||
|
||||
return callApi({
|
||||
auth,
|
||||
endpoint: 'abuse/report/collection',
|
||||
method: 'POST',
|
||||
body: {
|
||||
collection: collectionId,
|
||||
message,
|
||||
reason,
|
||||
reporter_name: reporterName,
|
||||
reporter_email: reporterEmail,
|
||||
},
|
||||
apiState: api,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/* @flow */
|
||||
import invariant from 'invariant';
|
||||
|
||||
type CollectionAbuseReport = {|
|
||||
isSubmitting: boolean,
|
||||
hasSubmitted: boolean,
|
||||
|};
|
||||
|
||||
export type CollectionAbuseReportsState = {|
|
||||
byCollectionId: {
|
||||
[collectionId: number]: CollectionAbuseReport,
|
||||
},
|
||||
|};
|
||||
|
||||
export const SEND_COLLECTION_ABUSE_REPORT: 'SEND_COLLECTION_ABUSE_REPORT' =
|
||||
'SEND_COLLECTION_ABUSE_REPORT';
|
||||
export const LOAD_COLLECTION_ABUSE_REPORT: 'LOAD_COLLECTION_ABUSE_REPORT' =
|
||||
'LOAD_COLLECTION_ABUSE_REPORT';
|
||||
export const ABORT_COLLECTION_ABUSE_REPORT: 'ABORT_COLLECTION_ABUSE_REPORT' =
|
||||
'ABORT_COLLECTION_ABUSE_REPORT';
|
||||
|
||||
type SendCollectionAbuseReportParams = {|
|
||||
auth: boolean,
|
||||
errorHandlerId: string,
|
||||
message: string | null,
|
||||
collectionId: number,
|
||||
reason: string | null,
|
||||
reporterEmail: string | null,
|
||||
reporterName: string | null,
|
||||
|};
|
||||
|
||||
export type SendCollectionAbuseReportAction = {|
|
||||
type: typeof SEND_COLLECTION_ABUSE_REPORT,
|
||||
payload: SendCollectionAbuseReportParams,
|
||||
|};
|
||||
|
||||
export const sendCollectionAbuseReport = ({
|
||||
auth,
|
||||
errorHandlerId,
|
||||
message,
|
||||
collectionId,
|
||||
reason,
|
||||
reporterEmail,
|
||||
reporterName,
|
||||
}: SendCollectionAbuseReportParams): SendCollectionAbuseReportAction => {
|
||||
invariant(errorHandlerId, 'errorHandlerId is required');
|
||||
invariant(collectionId, 'collectionId is required');
|
||||
|
||||
return {
|
||||
type: SEND_COLLECTION_ABUSE_REPORT,
|
||||
payload: {
|
||||
auth,
|
||||
errorHandlerId,
|
||||
message,
|
||||
collectionId,
|
||||
reason,
|
||||
reporterEmail,
|
||||
reporterName,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
type AbortCollectionAbuseReportParams = {|
|
||||
collectionId: number,
|
||||
|};
|
||||
|
||||
export type AbortCollectionAbuseReportAction = {|
|
||||
type: typeof ABORT_COLLECTION_ABUSE_REPORT,
|
||||
payload: AbortCollectionAbuseReportParams,
|
||||
|};
|
||||
|
||||
export const abortCollectionAbuseReport = ({
|
||||
collectionId,
|
||||
}: AbortCollectionAbuseReportParams): AbortCollectionAbuseReportAction => {
|
||||
invariant(collectionId, 'collectionId is required');
|
||||
|
||||
return {
|
||||
type: ABORT_COLLECTION_ABUSE_REPORT,
|
||||
payload: { collectionId },
|
||||
};
|
||||
};
|
||||
|
||||
type LoadCollectionAbuseReportParams = {|
|
||||
collectionId: number,
|
||||
|};
|
||||
|
||||
export type LoadCollectionAbuseReportAction = {|
|
||||
type: typeof LOAD_COLLECTION_ABUSE_REPORT,
|
||||
payload: LoadCollectionAbuseReportParams,
|
||||
|};
|
||||
|
||||
export const loadCollectionAbuseReport = ({
|
||||
collectionId,
|
||||
}: LoadCollectionAbuseReportParams): LoadCollectionAbuseReportAction => {
|
||||
invariant(collectionId, 'collectionId is required');
|
||||
|
||||
return {
|
||||
type: LOAD_COLLECTION_ABUSE_REPORT,
|
||||
payload: { collectionId },
|
||||
};
|
||||
};
|
||||
|
||||
const updateCollection = (
|
||||
collectionAbuseReportsState: CollectionAbuseReportsState,
|
||||
collectionId: number,
|
||||
report: CollectionAbuseReport,
|
||||
): CollectionAbuseReportsState => {
|
||||
return {
|
||||
...collectionAbuseReportsState,
|
||||
byCollectionId: {
|
||||
...collectionAbuseReportsState.byCollectionId,
|
||||
[collectionId]: {
|
||||
...collectionAbuseReportsState.byCollectionId[collectionId],
|
||||
...report,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const initialState: CollectionAbuseReportsState = {
|
||||
byCollectionId: {},
|
||||
};
|
||||
|
||||
export default function collectionAbuseReportsReducer(
|
||||
// eslint-disable-next-line default-param-last
|
||||
state: CollectionAbuseReportsState = initialState,
|
||||
action:
|
||||
| SendCollectionAbuseReportAction
|
||||
| LoadCollectionAbuseReportAction
|
||||
| AbortCollectionAbuseReportAction,
|
||||
): CollectionAbuseReportsState {
|
||||
switch (action.type) {
|
||||
case SEND_COLLECTION_ABUSE_REPORT: {
|
||||
const { collectionId } = action.payload;
|
||||
|
||||
return updateCollection(state, collectionId, {
|
||||
isSubmitting: true,
|
||||
hasSubmitted: false,
|
||||
});
|
||||
}
|
||||
case LOAD_COLLECTION_ABUSE_REPORT: {
|
||||
const { collectionId } = action.payload;
|
||||
|
||||
return updateCollection(state, collectionId, {
|
||||
isSubmitting: false,
|
||||
hasSubmitted: true,
|
||||
});
|
||||
}
|
||||
case ABORT_COLLECTION_ABUSE_REPORT: {
|
||||
const { collectionId } = action.payload;
|
||||
|
||||
return updateCollection(state, collectionId, {
|
||||
isSubmitting: false,
|
||||
hasSubmitted: false,
|
||||
});
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* @flow */
|
||||
import { call, put, select, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import {
|
||||
SEND_COLLECTION_ABUSE_REPORT,
|
||||
loadCollectionAbuseReport,
|
||||
abortCollectionAbuseReport,
|
||||
} from 'amo/reducers/collectionAbuseReports';
|
||||
import { reportCollection as reportCollectionApi } from 'amo/api/abuse';
|
||||
import log from 'amo/logger';
|
||||
import { createErrorHandler, getState } from 'amo/sagas/utils';
|
||||
import type { SendCollectionAbuseReportAction } from 'amo/reducers/collectionAbuseReports';
|
||||
import type { ReportCollectionParams } from 'amo/api/abuse';
|
||||
import type { Saga } from 'amo/types/sagas';
|
||||
|
||||
export function* reportCollection({
|
||||
payload: {
|
||||
auth,
|
||||
errorHandlerId,
|
||||
message,
|
||||
reason,
|
||||
reporterEmail,
|
||||
reporterName,
|
||||
collectionId,
|
||||
},
|
||||
}: SendCollectionAbuseReportAction): Saga {
|
||||
const errorHandler = createErrorHandler(errorHandlerId);
|
||||
|
||||
yield put(errorHandler.createClearingAction());
|
||||
|
||||
try {
|
||||
const state = yield select(getState);
|
||||
|
||||
const params: ReportCollectionParams = {
|
||||
api: state.api,
|
||||
auth,
|
||||
message,
|
||||
reason: reason || null,
|
||||
reporterName: reporterName || null,
|
||||
reporterEmail: reporterEmail || null,
|
||||
collectionId,
|
||||
};
|
||||
const response = yield call(reportCollectionApi, params);
|
||||
|
||||
yield put(
|
||||
loadCollectionAbuseReport({ collectionId: response.collection.id }),
|
||||
);
|
||||
} catch (error) {
|
||||
log.warn(`Reporting collection for abuse failed: ${error}`);
|
||||
yield put(errorHandler.createErrorAction(error));
|
||||
|
||||
yield put(abortCollectionAbuseReport({ collectionId }));
|
||||
}
|
||||
}
|
||||
|
||||
export default function* collectionAbcollectioneportsSaga(): Saga {
|
||||
yield takeLatest(SEND_COLLECTION_ABUSE_REPORT, reportCollection);
|
||||
}
|
|
@ -4,6 +4,7 @@ import { all, fork } from 'redux-saga/effects';
|
|||
import addonsByAuthors from 'amo/sagas/addonsByAuthors';
|
||||
import blocks from 'amo/sagas/blocks';
|
||||
import collections from 'amo/sagas/collections';
|
||||
import collectionAbuseReports from 'amo/sagas/collectionAbuseReports';
|
||||
import home from 'amo/sagas/home';
|
||||
import landing from 'amo/sagas/landing';
|
||||
import recommendations from 'amo/sagas/recommendations';
|
||||
|
@ -31,6 +32,7 @@ export default function* rootSaga(): Saga {
|
|||
fork(blocks),
|
||||
fork(categories),
|
||||
fork(collections),
|
||||
fork(collectionAbuseReports),
|
||||
fork(home),
|
||||
fork(landing),
|
||||
fork(languageTools),
|
||||
|
|
|
@ -10,6 +10,7 @@ import { configureStore } from '@reduxjs/toolkit';
|
|||
|
||||
import addonsByAuthors from 'amo/reducers/addonsByAuthors';
|
||||
import collections from 'amo/reducers/collections';
|
||||
import collectionAbuseReports from 'amo/reducers/collectionAbuseReports';
|
||||
import blocks from 'amo/reducers/blocks';
|
||||
import experiments from 'amo/reducers/experiments';
|
||||
import home from 'amo/reducers/home';
|
||||
|
@ -38,6 +39,7 @@ import suggestions from 'amo/reducers/suggestions';
|
|||
import type { AddonsByAuthorsState } from 'amo/reducers/addonsByAuthors';
|
||||
import type { BlocksState } from 'amo/reducers/blocks';
|
||||
import type { CollectionsState } from 'amo/reducers/collections';
|
||||
import type { CollectionAbuseReportsState } from 'amo/reducers/collectionAbuseReports';
|
||||
import type { ExperimentsState } from 'amo/reducers/experiments';
|
||||
import type { HomeState } from 'amo/reducers/home';
|
||||
import type { LandingState } from 'amo/reducers/landing';
|
||||
|
@ -123,6 +125,7 @@ type InternalAppState = {|
|
|||
blocks: BlocksState,
|
||||
categories: CategoriesState,
|
||||
collections: CollectionsState,
|
||||
collectionAbuseReports: CollectionAbuseReportsState,
|
||||
errorPage: ErrorPageState,
|
||||
errors: Object,
|
||||
experiments: ExperimentsState,
|
||||
|
@ -179,6 +182,7 @@ export const reducers: AppReducersType = {
|
|||
blocks,
|
||||
categories,
|
||||
collections,
|
||||
collectionAbuseReports,
|
||||
errors,
|
||||
errorPage,
|
||||
experiments,
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import * as api from 'amo/api';
|
||||
import { reportAddon, reportUser, reportRating } from 'amo/api/abuse';
|
||||
import {
|
||||
reportAddon,
|
||||
reportUser,
|
||||
reportRating,
|
||||
reportCollection,
|
||||
} from 'amo/api/abuse';
|
||||
import {
|
||||
createApiResponse,
|
||||
createFakeAddonAbuseReport,
|
||||
|
@ -295,4 +300,103 @@ describe(__filename, () => {
|
|||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('reportCollection', () => {
|
||||
const mockResponse = ({ collectionId = 123, ...otherProps } = {}) => {
|
||||
return createApiResponse({
|
||||
jsonData: {
|
||||
reporter: null,
|
||||
reporter_name: 'some reporter name',
|
||||
reporter_email: 'some reporter email',
|
||||
collection: {
|
||||
id: collectionId,
|
||||
},
|
||||
message: '',
|
||||
reason: 'illegal',
|
||||
...otherProps,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
it('calls the collection abuse report API', async () => {
|
||||
const apiState = dispatchClientMetadata().store.getState().api;
|
||||
const reason = 'other';
|
||||
const reporterEmail = 'some-reporter-email';
|
||||
const reporterName = 'some-reporter-name';
|
||||
const collectionId = 1234;
|
||||
|
||||
mockApi
|
||||
.expects('callApi')
|
||||
.withArgs({
|
||||
auth: true,
|
||||
endpoint: 'abuse/report/collection',
|
||||
method: 'POST',
|
||||
body: {
|
||||
collection: collectionId,
|
||||
message: undefined,
|
||||
reason,
|
||||
reporter_email: reporterEmail,
|
||||
reporter_name: reporterName,
|
||||
},
|
||||
apiState,
|
||||
})
|
||||
.once()
|
||||
.returns(mockResponse());
|
||||
|
||||
await reportCollection({
|
||||
api: apiState,
|
||||
auth: true,
|
||||
collectionId,
|
||||
reason,
|
||||
reporterEmail,
|
||||
reporterName,
|
||||
});
|
||||
|
||||
mockApi.verify();
|
||||
});
|
||||
|
||||
it('allows the collection abuse report API to be called anonymously', async () => {
|
||||
const apiState = dispatchClientMetadata().store.getState().api;
|
||||
const collectionId = 1234;
|
||||
const message = 'not a great collection';
|
||||
|
||||
mockApi
|
||||
.expects('callApi')
|
||||
.withArgs({
|
||||
auth: false,
|
||||
endpoint: 'abuse/report/collection',
|
||||
method: 'POST',
|
||||
body: {
|
||||
collection: collectionId,
|
||||
message,
|
||||
reason: undefined,
|
||||
reporter_email: undefined,
|
||||
reporter_name: undefined,
|
||||
},
|
||||
apiState,
|
||||
})
|
||||
.once()
|
||||
.returns(mockResponse());
|
||||
|
||||
await reportCollection({
|
||||
api: apiState,
|
||||
auth: false,
|
||||
message,
|
||||
collectionId,
|
||||
});
|
||||
|
||||
mockApi.verify();
|
||||
});
|
||||
|
||||
it.each([undefined, '', null, ' '])(
|
||||
'throws when reason is not supplied and message is %s',
|
||||
async (message) => {
|
||||
const apiState = dispatchClientMetadata().store.getState().api;
|
||||
|
||||
await expect(() =>
|
||||
reportCollection({ api: apiState, collectionId: 123, message }),
|
||||
).toThrow(/message is required when reason isn't specified/);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
import collectionAbuseReportsReducer, {
|
||||
abortCollectionAbuseReport,
|
||||
initialState,
|
||||
loadCollectionAbuseReport,
|
||||
sendCollectionAbuseReport,
|
||||
} from 'amo/reducers/collectionAbuseReports';
|
||||
|
||||
describe(__filename, () => {
|
||||
describe('reducer', () => {
|
||||
it('initializes properly', () => {
|
||||
const state = collectionAbuseReportsReducer(initialState, {
|
||||
type: 'UNRELATED_ACTION',
|
||||
});
|
||||
expect(state).toEqual(initialState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('abortCollectionAbuseReport', () => {
|
||||
it('requires a collection ID', () => {
|
||||
expect(() => {
|
||||
abortCollectionAbuseReport({});
|
||||
}).toThrow('collectionId is required');
|
||||
});
|
||||
|
||||
it('resets a collection report', () => {
|
||||
const collectionId = 123;
|
||||
|
||||
let state = collectionAbuseReportsReducer(
|
||||
undefined,
|
||||
sendCollectionAbuseReport({
|
||||
collectionId,
|
||||
errorHandlerId: 'some-error-handler-id',
|
||||
}),
|
||||
);
|
||||
expect(state.byCollectionId[collectionId]).toMatchObject({
|
||||
isSubmitting: true,
|
||||
});
|
||||
|
||||
state = collectionAbuseReportsReducer(
|
||||
state,
|
||||
abortCollectionAbuseReport({ collectionId }),
|
||||
);
|
||||
expect(state.byCollectionId[collectionId]).toEqual({
|
||||
hasSubmitted: false,
|
||||
isSubmitting: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not change the unrelated reports', () => {
|
||||
const collectionId = 157;
|
||||
const errorHandlerId = 'some-error-handler';
|
||||
let state = collectionAbuseReportsReducer(
|
||||
undefined,
|
||||
sendCollectionAbuseReport({ collectionId, errorHandlerId }),
|
||||
);
|
||||
state = collectionAbuseReportsReducer(
|
||||
state,
|
||||
sendCollectionAbuseReport({ collectionId: 246, errorHandlerId }),
|
||||
);
|
||||
|
||||
state = collectionAbuseReportsReducer(
|
||||
state,
|
||||
abortCollectionAbuseReport({ collectionId }),
|
||||
);
|
||||
|
||||
expect(state.byCollectionId).toMatchObject({
|
||||
157: {
|
||||
isSubmitting: false,
|
||||
hasSubmitted: false,
|
||||
},
|
||||
246: {
|
||||
isSubmitting: true,
|
||||
hasSubmitted: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCollectionAbuseReport', () => {
|
||||
it('requires a collection ID', () => {
|
||||
expect(() => {
|
||||
loadCollectionAbuseReport({});
|
||||
}).toThrow('collectionId is required');
|
||||
});
|
||||
|
||||
it('marks a collection report as submitted', () => {
|
||||
const collectionId = 123;
|
||||
|
||||
const state = collectionAbuseReportsReducer(
|
||||
undefined,
|
||||
loadCollectionAbuseReport({
|
||||
collectionId,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(state.byCollectionId[collectionId]).toEqual({
|
||||
hasSubmitted: true,
|
||||
isSubmitting: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not change the unrelated reports', () => {
|
||||
const collectionId = 157;
|
||||
const errorHandlerId = 'some-error-handler';
|
||||
let state = collectionAbuseReportsReducer(
|
||||
undefined,
|
||||
sendCollectionAbuseReport({ collectionId, errorHandlerId }),
|
||||
);
|
||||
state = collectionAbuseReportsReducer(
|
||||
state,
|
||||
sendCollectionAbuseReport({ collectionId: 246, errorHandlerId }),
|
||||
);
|
||||
|
||||
state = collectionAbuseReportsReducer(
|
||||
state,
|
||||
loadCollectionAbuseReport({ collectionId }),
|
||||
);
|
||||
|
||||
expect(state.byCollectionId).toMatchObject({
|
||||
157: {
|
||||
isSubmitting: false,
|
||||
hasSubmitted: true,
|
||||
},
|
||||
246: {
|
||||
isSubmitting: true,
|
||||
hasSubmitted: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendCollectionAbuseReport', () => {
|
||||
it('requires a collection ID', () => {
|
||||
expect(() => {
|
||||
sendCollectionAbuseReport({ errorHandlerId: 'error-handler-id' });
|
||||
}).toThrow('collectionId is required');
|
||||
});
|
||||
|
||||
it('requires an error handler ID', () => {
|
||||
expect(() => {
|
||||
sendCollectionAbuseReport({ collectionId: 123 });
|
||||
}).toThrow('errorHandlerId is required');
|
||||
});
|
||||
|
||||
it('marks a collection report as being submitted', () => {
|
||||
const collectionId = 123;
|
||||
|
||||
const state = collectionAbuseReportsReducer(
|
||||
undefined,
|
||||
sendCollectionAbuseReport({
|
||||
collectionId,
|
||||
errorHandlerId: 'some-error-handler-id',
|
||||
}),
|
||||
);
|
||||
|
||||
expect(state.byCollectionId[collectionId]).toEqual({
|
||||
hasSubmitted: false,
|
||||
isSubmitting: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('can load multiple reports', () => {
|
||||
const errorHandlerId = 'some-error-handler-id';
|
||||
|
||||
let state = collectionAbuseReportsReducer(
|
||||
undefined,
|
||||
sendCollectionAbuseReport({ collectionId: 1, errorHandlerId }),
|
||||
);
|
||||
state = collectionAbuseReportsReducer(
|
||||
state,
|
||||
sendCollectionAbuseReport({ collectionId: 246, errorHandlerId }),
|
||||
);
|
||||
|
||||
expect(state.byCollectionId).toMatchObject({
|
||||
1: expect.any(Object),
|
||||
246: expect.any(Object),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,121 @@
|
|||
import SagaTester from 'redux-saga-tester';
|
||||
|
||||
import * as api from 'amo/api/abuse';
|
||||
import { clearError } from 'amo/reducers/errors';
|
||||
import collectionAbuseReportsReducer, {
|
||||
abortCollectionAbuseReport,
|
||||
loadCollectionAbuseReport,
|
||||
sendCollectionAbuseReport,
|
||||
} from 'amo/reducers/collectionAbuseReports';
|
||||
import apiReducer from 'amo/reducers/api';
|
||||
import collectionAbuseReportsSaga from 'amo/sagas/collectionAbuseReports';
|
||||
import {
|
||||
createFakeCollectionAbuseReport,
|
||||
createStubErrorHandler,
|
||||
dispatchSignInActions,
|
||||
} from 'tests/unit/helpers';
|
||||
|
||||
describe(__filename, () => {
|
||||
let errorHandler;
|
||||
let mockApi;
|
||||
let sagaTester;
|
||||
|
||||
beforeEach(() => {
|
||||
errorHandler = createStubErrorHandler();
|
||||
mockApi = sinon.mock(api);
|
||||
const initialState = dispatchSignInActions().state;
|
||||
sagaTester = new SagaTester({
|
||||
initialState,
|
||||
reducers: {
|
||||
api: apiReducer,
|
||||
collectionAbuseReports: collectionAbuseReportsReducer,
|
||||
},
|
||||
});
|
||||
sagaTester.start(collectionAbuseReportsSaga);
|
||||
});
|
||||
|
||||
function _sendCollectionAbuseReport(params) {
|
||||
sagaTester.dispatch(
|
||||
sendCollectionAbuseReport({
|
||||
errorHandlerId: errorHandler.id,
|
||||
collectionId: 999999,
|
||||
message: 'abuse report body',
|
||||
...params,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
it('calls the API for abuse', async () => {
|
||||
const collectionId = 123;
|
||||
const message = 'this is not a great collection';
|
||||
const response = createFakeCollectionAbuseReport({ collectionId, message });
|
||||
|
||||
mockApi
|
||||
.expects('reportCollection')
|
||||
.once()
|
||||
.returns(Promise.resolve(response));
|
||||
|
||||
_sendCollectionAbuseReport({ message, collectionId });
|
||||
|
||||
const expectedLoadAction = loadCollectionAbuseReport({
|
||||
message: response.message,
|
||||
reporter: response.reporter,
|
||||
collectionId,
|
||||
});
|
||||
|
||||
const calledAction = await sagaTester.waitFor(expectedLoadAction.type);
|
||||
mockApi.verify();
|
||||
|
||||
expect(calledAction).toEqual(expectedLoadAction);
|
||||
});
|
||||
|
||||
it('calls the API for abuse with a reason', async () => {
|
||||
const collectionId = 123;
|
||||
const reason = 'other';
|
||||
const response = createFakeCollectionAbuseReport({ collectionId, reason });
|
||||
|
||||
mockApi
|
||||
.expects('reportCollection')
|
||||
.once()
|
||||
.returns(Promise.resolve(response));
|
||||
|
||||
_sendCollectionAbuseReport({ collectionId, reason });
|
||||
|
||||
const expectedLoadAction = loadCollectionAbuseReport({ collectionId });
|
||||
|
||||
const calledAction = await sagaTester.waitFor(expectedLoadAction.type);
|
||||
mockApi.verify();
|
||||
|
||||
expect(calledAction).toEqual(expectedLoadAction);
|
||||
});
|
||||
|
||||
it('clears the error handler', async () => {
|
||||
_sendCollectionAbuseReport();
|
||||
|
||||
const calledAction = await sagaTester.waitFor(clearError.type);
|
||||
expect(calledAction).toEqual(errorHandler.createClearingAction());
|
||||
});
|
||||
|
||||
it('dispatches an error', async () => {
|
||||
const error = new Error('some API error maybe');
|
||||
mockApi.expects('reportCollection').returns(Promise.reject(error));
|
||||
|
||||
_sendCollectionAbuseReport();
|
||||
|
||||
const errorAction = errorHandler.createErrorAction(error);
|
||||
const calledAction = await sagaTester.waitFor(errorAction.type);
|
||||
expect(calledAction).toEqual(errorAction);
|
||||
});
|
||||
|
||||
it('resets the state when an error occurs', async () => {
|
||||
const collectionId = 123;
|
||||
const error = new Error('some API error maybe');
|
||||
mockApi.expects('reportCollection').returns(Promise.reject(error));
|
||||
|
||||
_sendCollectionAbuseReport({ collectionId });
|
||||
|
||||
const abortAction = abortCollectionAbuseReport({ collectionId });
|
||||
const calledAction = await sagaTester.waitFor(abortAction.type);
|
||||
expect(calledAction).toEqual(abortAction);
|
||||
});
|
||||
});
|
|
@ -1018,6 +1018,25 @@ export function createFakeUserAbuseReport({
|
|||
};
|
||||
}
|
||||
|
||||
export function createFakeCollectionAbuseReport({
|
||||
collectionId,
|
||||
message = '',
|
||||
reason = 'other',
|
||||
reporter = null,
|
||||
reporterName = null,
|
||||
reporterEmail = null,
|
||||
} = {}) {
|
||||
return {
|
||||
message,
|
||||
reason,
|
||||
reporter,
|
||||
reporter_name: reporterName,
|
||||
reporter_email: reporterEmail,
|
||||
collection: {
|
||||
id: collectionId,
|
||||
},
|
||||
};
|
||||
}
|
||||
export const getFakeConfig = getFakeConfigNode;
|
||||
|
||||
export const getMockConfig = (overrides = {}) => {
|
||||
|
|
Загрузка…
Ссылка в новой задаче