Saga helpers for editing a collection (#4209)

This commit is contained in:
Kumar McMillan 2018-01-24 15:17:15 -06:00 коммит произвёл GitHub
Родитель 4a86a7defe
Коммит a1891d5480
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 336 добавлений и 0 удалений

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

@ -28,6 +28,9 @@ export const ADDON_ADDED_TO_COLLECTION: 'ADDON_ADDED_TO_COLLECTION'
= 'ADDON_ADDED_TO_COLLECTION';
export const LOAD_COLLECTION_ADDONS: 'LOAD_COLLECTION_ADDONS'
= 'LOAD_COLLECTION_ADDONS';
export const FINISH_UPDATE_COLLECTION: 'FINISH_UPDATE_COLLECTION'
= 'FINISH_UPDATE_COLLECTION';
export const UPDATE_COLLECTION: 'UPDATE_COLLECTION' = 'UPDATE_COLLECTION';
export type CollectionType = {
addons: Array<AddonType> | null,
@ -73,6 +76,9 @@ export type CollectionsState = {
|};
},
},
collectionUpdates: {
[collectionSlug: string]: {| updating: boolean, successful?: boolean |},
},
};
export const initialState: CollectionsState = {
@ -81,6 +87,7 @@ export const initialState: CollectionsState = {
current: { id: null, loading: false },
userCollections: {},
addonInCollections: {},
collectionUpdates: {},
};
type FetchCurrentCollectionParams = {|
@ -440,6 +447,80 @@ export const addAddonToCollection = ({
};
};
type UpdateCollectionParams = {|
errorHandlerId: string,
collectionSlug: string,
defaultLocale?: string,
description?: string,
isPublic?: boolean,
name?: string,
user: number | string,
|};
type UpdateCollectionAction = {|
type: typeof UPDATE_COLLECTION,
payload: UpdateCollectionParams,
|};
export const updateCollection = ({
errorHandlerId,
collectionSlug,
defaultLocale,
description,
isPublic,
name,
user,
}: UpdateCollectionParams = {}): UpdateCollectionAction => {
if (!errorHandlerId) {
throw new Error('errorHandlerId is required');
}
if (!collectionSlug) {
throw new Error('collectionSlug is required');
}
if (!user) {
throw new Error('user is required');
}
return {
type: UPDATE_COLLECTION,
payload: {
errorHandlerId,
collectionSlug,
defaultLocale,
description,
isPublic,
name,
user,
},
};
};
type FinishUpdateCollectionParams = {|
collectionSlug: string,
successful: boolean,
|};
type FinishUpdateCollectionAction = {|
type: typeof FINISH_UPDATE_COLLECTION,
payload: FinishUpdateCollectionParams,
|};
export const finishUpdateCollection = (
{ collectionSlug, successful }: FinishUpdateCollectionParams = {}
): FinishUpdateCollectionAction => {
if (!collectionSlug) {
throw new Error('The collectionSlug parameter is required');
}
if (typeof successful === 'undefined') {
throw new Error('The successful parameter is required');
}
return {
type: FINISH_UPDATE_COLLECTION,
payload: { collectionSlug, successful },
};
};
export const createInternalAddons = (
items: ExternalCollectionAddons
): Array<AddonType> => {
@ -570,10 +651,12 @@ type Action =
| FetchCurrentCollectionAction
| FetchCurrentCollectionPageAction
| FetchUserCollectionsAction
| FinishUpdateCollectionAction
| LoadCollectionAddonsAction
| LoadCurrentCollectionAction
| LoadCurrentCollectionPageAction
| LoadUserCollectionsAction
| UpdateCollectionAction
;
const reducer = (
@ -790,6 +873,35 @@ const reducer = (
});
}
case UPDATE_COLLECTION: {
const { collectionSlug } = action.payload;
return {
...state,
collectionUpdates: {
[collectionSlug]: {
...state.collectionUpdates[collectionSlug],
updating: true,
},
},
};
}
case FINISH_UPDATE_COLLECTION: {
const { collectionSlug, successful } = action.payload;
return {
...state,
collectionUpdates: {
[collectionSlug]: {
...state.collectionUpdates[collectionSlug],
updating: false,
successful,
},
},
};
}
default:
return state;
}

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

@ -4,10 +4,12 @@ import {
FETCH_CURRENT_COLLECTION,
FETCH_CURRENT_COLLECTION_PAGE,
FETCH_USER_COLLECTIONS,
UPDATE_COLLECTION,
abortAddAddonToCollection,
abortFetchCurrentCollection,
abortFetchUserCollections,
addonAddedToCollection,
finishUpdateCollection,
loadCurrentCollection,
loadCurrentCollectionPage,
loadUserCollections,
@ -132,6 +134,42 @@ export function* addAddonToCollection({
}
}
export function* updateCollection({
payload: {
errorHandlerId,
collectionSlug,
defaultLocale,
description,
isPublic,
name,
slug,
user,
},
}) {
const errorHandler = createErrorHandler(errorHandlerId);
yield put(errorHandler.createClearingAction());
try {
const state = yield select(getState);
yield call(api.updateCollection, {
api: state.api,
collectionSlug,
defaultLocale,
description,
isPublic,
name,
slug,
user,
});
yield put(finishUpdateCollection({ collectionSlug, successful: true }));
} catch (error) {
log.warn(`Failed to update collection: ${error}`);
yield put(errorHandler.createErrorAction(error));
yield put(finishUpdateCollection({ collectionSlug, successful: false }));
}
}
export default function* collectionsSaga() {
yield takeLatest(FETCH_CURRENT_COLLECTION, fetchCurrentCollection);
yield takeLatest(
@ -139,4 +177,5 @@ export default function* collectionsSaga() {
);
yield takeLatest(FETCH_USER_COLLECTIONS, fetchUserCollections);
yield takeLatest(ADD_ADDON_TO_COLLECTION, addAddonToCollection);
yield takeLatest(UPDATE_COLLECTION, updateCollection);
}

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

@ -9,6 +9,7 @@ import reducer, {
fetchCurrentCollection,
fetchCurrentCollectionPage,
fetchUserCollections,
finishUpdateCollection,
getCollectionById,
getCurrentCollection,
initialState,
@ -17,6 +18,7 @@ import reducer, {
loadCurrentCollection,
loadCurrentCollectionPage,
loadUserCollections,
updateCollection,
} from 'amo/reducers/collections';
import { parsePage } from 'core/utils';
import { createStubErrorHandler } from 'tests/unit/helpers';
@ -825,4 +827,105 @@ describe(__filename, () => {
.toThrow(/collectionSlug parameter is required/);
});
});
describe('updateCollection', () => {
const getParams = (params = {}) => {
return {
errorHandlerId: 'error-handler-id',
collectionSlug: 'some-collection',
user: 'some-user-name',
...params,
};
};
it('requires errorHandlerId parameter', () => {
const params = getParams();
delete params.errorHandlerId;
expect(() => updateCollection(params))
.toThrow(/errorHandlerId is required/);
});
it('requires collectionSlug parameter', () => {
const params = getParams();
delete params.collectionSlug;
expect(() => updateCollection(params))
.toThrow(/collectionSlug is required/);
});
it('requires user parameter', () => {
const params = getParams();
delete params.user;
expect(() => updateCollection(params))
.toThrow(/user is required/);
});
it('changes update state', () => {
const collectionSlug = 'some-collection';
const state = reducer(initialState, updateCollection(getParams({
collectionSlug,
})));
expect(state.collectionUpdates[collectionSlug].updating)
.toEqual(true);
});
});
describe('finishUpdateCollection', () => {
const getParams = (params = {}) => {
return {
collectionSlug: 'some-collection', successful: true, ...params,
};
};
it('requires collectionSlug parameter', () => {
const params = getParams();
delete params.collectionSlug;
expect(() => finishUpdateCollection(params))
.toThrow(/collectionSlug parameter is required/);
});
it('requires successful parameter', () => {
const params = getParams();
delete params.successful;
expect(() => finishUpdateCollection(params))
.toThrow(/successful parameter is required/);
});
it('handles a falsy successful parameter', () => {
const params = getParams({ successful: false });
// Make sure this doesn't throw.
finishUpdateCollection(params);
});
it('finishes a successful update', () => {
const collectionSlug = 'some-collection';
const params = getParams({ collectionSlug, successful: true });
const state = reducer(initialState, finishUpdateCollection(params));
expect(state.collectionUpdates[collectionSlug].updating)
.toEqual(false);
expect(state.collectionUpdates[collectionSlug].successful)
.toEqual(true);
});
it('finishes an unsuccessful update', () => {
const collectionSlug = 'some-collection';
const params = getParams({ collectionSlug, successful: false });
const state = reducer(initialState, finishUpdateCollection(params));
expect(state.collectionUpdates[collectionSlug].updating)
.toEqual(false);
expect(state.collectionUpdates[collectionSlug].successful)
.toEqual(false);
});
});
});

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

@ -10,9 +10,11 @@ import collectionsReducer, {
fetchCurrentCollection,
fetchCurrentCollectionPage,
fetchUserCollections,
finishUpdateCollection,
loadCurrentCollection,
loadCurrentCollectionPage,
loadUserCollections,
updateCollection,
} from 'amo/reducers/collections';
import collectionsSaga from 'amo/sagas/collections';
import apiReducer from 'core/reducers/api';
@ -334,4 +336,84 @@ describe(__filename, () => {
.toEqual(abortAddAddonToCollection({ addonId, userId }));
});
});
describe('updateCollection', () => {
const _updateCollection = (params = {}) => {
sagaTester.dispatch(updateCollection({
errorHandlerId: errorHandler.id,
collectionSlug: 'some-collection',
user: 321,
...params,
}));
};
it('sends a patch to the collection API', async () => {
const collectionSlug = 'a-collection';
const params = {
collectionSlug,
name: 'New collection name',
user: 543,
};
const state = sagaTester.getState();
mockApi
.expects('updateCollection')
.withArgs({
api: state.api,
collectionSlug,
defaultLocale: undefined,
description: undefined,
isPublic: undefined,
name: params.name,
slug: undefined,
user: params.user,
})
.once()
.returns(Promise.resolve());
_updateCollection(params);
const expectedLoadAction = finishUpdateCollection({
collectionSlug: params.collectionSlug,
successful: true,
});
await sagaTester.waitFor(expectedLoadAction.type);
mockApi.verify();
const calledActions = sagaTester.getCalledActions();
const loadAction = calledActions[2];
expect(loadAction).toEqual(expectedLoadAction);
});
it('clears the error handler', async () => {
_updateCollection();
const expectedAction = errorHandler.createClearingAction();
await sagaTester.waitFor(expectedAction.type);
expect(sagaTester.getCalledActions()[1])
.toEqual(errorHandler.createClearingAction());
});
it('handles errors', async () => {
const collectionSlug = 'a-collection';
const error = new Error('some API error maybe');
mockApi
.expects('updateCollection')
.returns(Promise.reject(error));
_updateCollection({ collectionSlug });
const errorAction = errorHandler.createErrorAction(error);
await sagaTester.waitFor(errorAction.type);
expect(sagaTester.getCalledActions()[2]).toEqual(errorAction);
expect(sagaTester.getCalledActions()[3])
.toEqual(finishUpdateCollection({
collectionSlug, successful: false,
}));
});
});
});