Skip to content

Commit

Permalink
fix: add tests, apply changes
Browse files Browse the repository at this point in the history
  • Loading branch information
louisewang1 committed Mar 7, 2022
1 parent 1299274 commit ea1328c
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 13 deletions.
3 changes: 2 additions & 1 deletion src/config/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,6 @@ export const MUTATION_KEYS = {
EXPORT_ZIP: 'exportZip',
POST_ITEM_LIKE: 'postItemLike',
DELETE_ITEM_LIKE: 'deleteItemLike',
UPDATE_FAVORITE_ITEMS: 'updateFavoriteItems',
ADD_FAVORITE_ITEM: 'addFavoriteItem',
DELETE_FAVORITE_ITEM: 'deleteFavoriteItem'
};
137 changes: 136 additions & 1 deletion src/mutations/member.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
import { Member } from '../types';
import { REQUEST_METHODS } from '../api/utils';
import { THUMBNAIL_SIZES } from '../config/constants';
import { uploadAvatarRoutine } from '../routines';
import { addFavoriteItemRoutine, uploadAvatarRoutine } from '../routines';

jest.spyOn(Cookies, 'get').mockReturnValue({ session: 'somesession' });

Expand Down Expand Up @@ -246,4 +246,139 @@ describe('Member Mutations', () => {
});
});
});

describe(MUTATION_KEYS.ADD_FAVORITE_ITEM, () => {
const id = 'member-id';
const itemId = 'item-id';
const extra = {
favoriteItems: [],
}
const route = `/${buildPatchMember(id)}`;
const mutation = () => useMutation(MUTATION_KEYS.ADD_FAVORITE_ITEM);

it(`Successfully add favorite item`, async () => {
const response = { ...MEMBER_RESPONSE, extra: { favoriteItems: ['item-id'] } };
// set random data in cache
queryClient.setQueryData(CURRENT_MEMBER_KEY, Map(MEMBER_RESPONSE));
const endpoints = [
{
response,
method: REQUEST_METHODS.PATCH,
route,
},
];
const mockedMutation = await mockMutation({
mutation,
wrapper,
endpoints,
});

await act(async () => {
await mockedMutation.mutate({ memberId: id, itemId, extra });
await waitForMutation();
});

expect(queryClient.getQueryState(CURRENT_MEMBER_KEY)?.isInvalidated).toBeTruthy();
expect(mockedNotifier).toHaveBeenCalledWith({
type: addFavoriteItemRoutine.SUCCESS,
});
});

it(`Unauthorized`, async () => {
// set random data in cache
queryClient.setQueryData(CURRENT_MEMBER_KEY, Map(MEMBER_RESPONSE));
const endpoints = [
{
response: UNAUTHORIZED_RESPONSE,
statusCode: StatusCodes.UNAUTHORIZED,
method: REQUEST_METHODS.PATCH,
route,
},
];
const mockedMutation = await mockMutation({
mutation,
wrapper,
endpoints,
});

await act(async () => {
await mockedMutation.mutate({ memberId: id, itemId, extra });
await waitForMutation();
});

// verify cache keys
const oldData = queryClient.getQueryData(
CURRENT_MEMBER_KEY,
) as Record<Member>;
expect(oldData.toJS()).toEqual(MEMBER_RESPONSE);
});
});

describe(MUTATION_KEYS.DELETE_FAVORITE_ITEM, () => {
const id = 'member-id';
const route = `/${buildPatchMember(id)}`;
const itemId = 'item-id';
const extra = {
favoriteItems: ['item-id', 'item-id2'],
}
const mutation = () => useMutation(MUTATION_KEYS.DELETE_FAVORITE_ITEM);

it(`Successfully delete favorite item`, async () => {
const response = { ...MEMBER_RESPONSE, extra: {favoriteItems: ['item-id2']} };
// set random data in cache
queryClient.setQueryData(CURRENT_MEMBER_KEY, Map(MEMBER_RESPONSE));
const endpoints = [
{
response,
method: REQUEST_METHODS.PATCH,
route,
},
];
const mockedMutation = await mockMutation({
mutation,
wrapper,
endpoints,
});

await act(async () => {
await mockedMutation.mutate({ memberId: id, itemId, extra });
await waitForMutation();
});

// verify cache keys
const newData = queryClient.getQueryData(
CURRENT_MEMBER_KEY,
) as Record<Member>;
expect(newData.toJS()).toEqual(response);
});

it(`Unauthorized`, async () => {
// set random data in cache
queryClient.setQueryData(CURRENT_MEMBER_KEY, Map(MEMBER_RESPONSE));
const endpoints = [
{
response: UNAUTHORIZED_RESPONSE,
statusCode: StatusCodes.UNAUTHORIZED,
method: REQUEST_METHODS.PATCH,
route,
},
];
const mockedMutation = await mockMutation({
mutation,
wrapper,
endpoints,
});

await act(async () => {
await mockedMutation.mutate({ memberId: id, itemId, extra });
await waitForMutation();
});

// verify cache keys
const oldData = queryClient.getQueryData(
CURRENT_MEMBER_KEY,
) as Record<Member>;
expect(oldData.toJS()).toEqual(MEMBER_RESPONSE);
});
});
});
66 changes: 55 additions & 11 deletions src/mutations/member.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Map, Record } from 'immutable';
import Cookies from 'js-cookie';
import * as Api from '../api';
import {
addFavoriteItemRoutine,
deleteFavoriteItemRoutine,
editMemberRoutine,
signOutRoutine,
uploadAvatarRoutine,
Expand Down Expand Up @@ -98,18 +100,13 @@ export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
});

// mutation to update favorite items of given member
queryClient.setMutationDefaults(MUTATION_KEYS.UPDATE_FAVORITE_ITEMS, {
queryClient.setMutationDefaults(MUTATION_KEYS.ADD_FAVORITE_ITEM, {
mutationFn: (payload) => {
const {memberId, itemId, extra, action} = payload;
if (action === 'add') {
extra.favoriteItems = extra.favoriteItems? extra.favoriteItems.concat([itemId]) : [itemId]
}
if (action === 'remove') {
extra.favoriteItems = extra.favoriteItems?.filter((id: UUID) => id !== itemId)
}
const {memberId, itemId, extra} = payload;
extra.favoriteItems = extra.favoriteItems? extra.favoriteItems.concat([itemId]) : [itemId]
return Api.editMember({id: memberId, extra}, queryConfig).then((member) => Map(member));
},
onMutate: async ({ member }) => {
onMutate: async (payload) => {
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
await queryClient.cancelQueries(CURRENT_MEMBER_KEY);

Expand All @@ -119,6 +116,10 @@ export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
) as Record<Member>;

// Optimistically update to the new value
const { itemId, extra } = payload;
extra.favoriteItems = extra.favoriteItems? extra.favoriteItems.concat([itemId]) : [itemId];
const member = { extra };

queryClient.setQueryData(
CURRENT_MEMBER_KEY,
previousMember.merge(member),
Expand All @@ -128,11 +129,54 @@ export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
return { previousMember };
},
onSuccess: () => {
notifier?.({ type: editMemberRoutine.SUCCESS });
notifier?.({ type: addFavoriteItemRoutine.SUCCESS });
},
// If the mutation fails, use the context returned from onMutate to roll back
onError: (error, _, context) => {
notifier?.({ type: editMemberRoutine.FAILURE, payload: { error } });
notifier?.({ type: addFavoriteItemRoutine.FAILURE, payload: { error } });
queryClient.setQueryData(CURRENT_MEMBER_KEY, context.previousMember);
},
// Always refetch after error or success:
onSettled: () => {
// invalidate all queries
queryClient.invalidateQueries(CURRENT_MEMBER_KEY);
},
});

queryClient.setMutationDefaults(MUTATION_KEYS.DELETE_FAVORITE_ITEM, {
mutationFn: (payload) => {
const {memberId, itemId, extra} = payload;
extra.favoriteItems = extra.favoriteItems?.filter((id: UUID) => id !== itemId);
return Api.editMember({id: memberId, extra}, queryConfig).then((member) => Map(member));
},
onMutate: async (payload) => {
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
await queryClient.cancelQueries(CURRENT_MEMBER_KEY);

// Snapshot the previous value
const previousMember = queryClient.getQueryData(
CURRENT_MEMBER_KEY,
) as Record<Member>;

// Optimistically update to the new value
const { itemId, extra } = payload;
extra.favoriteItems = extra.favoriteItems?.filter((id: UUID) => id !== itemId);
const member = { extra };

queryClient.setQueryData(
CURRENT_MEMBER_KEY,
previousMember.merge(member),
);

// Return a context object with the snapshotted value
return { previousMember };
},
onSuccess: () => {
notifier?.({ type: deleteFavoriteItemRoutine.SUCCESS });
},
// If the mutation fails, use the context returned from onMutate to roll back
onError: (error, _, context) => {
notifier?.({ type: deleteFavoriteItemRoutine.FAILURE, payload: { error } });
queryClient.setQueryData(CURRENT_MEMBER_KEY, context.previousMember);
},
// Always refetch after error or success:
Expand Down
2 changes: 2 additions & 0 deletions src/routines/member.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ import createRoutine from './utils';
export const signOutRoutine = createRoutine('SIGN_OUT');
export const editMemberRoutine = createRoutine('EDIT_MEMBER');
export const uploadAvatarRoutine = createRoutine('UPLOAD_AVATAR');
export const addFavoriteItemRoutine = createRoutine('ADD_FAVORITE_ITEM');
export const deleteFavoriteItemRoutine = createRoutine('DELETE_FAVORITE_ITEM');

0 comments on commit ea1328c

Please sign in to comment.