Skip to content

Commit

Permalink
feat: SB-800, SB-768, SB-820 migrate use favorite queries
Browse files Browse the repository at this point in the history
* feat: SB-800, SB-820, SB-768 migrate queries:
- useFavoriteDemoItemListQuery,
- useFavoriteDemoItemListCreateMutation,
- useFavoriteDemoItemListDeleteMutation
update useFavoriteDemoItem hook,

* SB-800 update useHandleDelte and useHandleCreate to use cache properly

* SB-800 create factories for removing and creating favorite items

Approved-by: Michał Kleszcz
  • Loading branch information
voytek98 committed Feb 15, 2023
1 parent def1470 commit 9862584
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 301 deletions.
26 changes: 26 additions & 0 deletions packages/webapp/src/mocks/factories/demoItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ import demoItemQueryGraphql from '../../routes/demoItem/__generated__/demoItemQu
import { demoItemQuery } from '../../routes/demoItem/demoItem.graphql';
import demoItemsAllQueryGraphql from '../../routes/demoItems/__generated__/demoItemsAllQuery.graphql';
import UseFavoriteDemoItemListQuery from '../../shared/hooks/useFavoriteDemoItem/__generated__/useFavoriteDemoItemListQuery.graphql';
import {
useFavoriteDemoItemListCreateMutation,
useFavoriteDemoItemListDeleteMutation,
} from '../../shared/hooks/useFavoriteDemoItem/useFavoriteDemoItem.graphql';
import { ContentfulDemoItem } from '../../shared/services/contentful';
import { ContentfulDemoItemFavoriteType } from '../../shared/services/graphqlApi';
import {
UseFavoriteDemoItemListCreateMutationMutation,
UseFavoriteDemoItemListDeleteMutationMutation,
} from '../../shared/services/graphqlApi/__generated/gql/graphql';
import { composeMockedQueryResult, makeId } from '../../tests/utils/fixtures';
import { createDeepFactory } from './factoryCreators';
import { contentfulSysFactory } from './helpers';
Expand Down Expand Up @@ -84,3 +92,21 @@ export const fillUseFavouriteDemoItemListQuery = (
);
env.mock.queuePendingOperation(UseFavoriteDemoItemListQuery, {});
};

export const fillRemoveFavouriteDemoItemQuery = (id: string, data: UseFavoriteDemoItemListDeleteMutationMutation) => {
const removeFavoriteItemMockResponse = composeMockedQueryResult(useFavoriteDemoItemListDeleteMutation, {
variables: { input: { item: id } },
data,
});

return removeFavoriteItemMockResponse;
};

export const fillCreateFavouriteDemoItemQuery = (id: string, data: UseFavoriteDemoItemListCreateMutationMutation) => {
const createFavoriteItemMockResponse = composeMockedQueryResult(useFavoriteDemoItemListCreateMutation, {
variables: { input: { item: id } },
data: data,
});

return createFavoriteItemMockResponse;
};
Original file line number Diff line number Diff line change
@@ -1,27 +1,49 @@
import { Suspense } from 'react';
import { act, screen, waitFor } from '@testing-library/react';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import graphql from 'babel-plugin-relay/macro';
import { useLazyLoadQuery } from 'react-relay';
import { MockPayloadGenerator } from 'relay-test-utils';
import { Route, Routes, useParams } from 'react-router';
import { ConnectionHandler } from 'relay-runtime';

import { demoItemFactory } from '../../../../mocks/factories';
import { DemoItemListItem, DemoItemListItemProps } from '../demoItemListItem.component';
import { useFavoriteDemoItemsLoader } from '../../../../shared/hooks/useFavoriteDemoItem/useFavoriteDemoItem.hook';
import { render } from '../../../../tests/utils/rendering';
import { RoutesConfig } from '../../../../app/config/routes';
import { getRelayEnv as getBaseRelayEnv } from '../../../../tests/utils/relay';
import useFavoriteDemoItemListQueryGraphql from '../../../../shared/hooks/useFavoriteDemoItem/__generated__/useFavoriteDemoItemListQuery.graphql';
import demoItemListItemTestQueryGraphql, {
demoItemListItemTestQuery,
} from './__generated__/demoItemListItemTestQuery.graphql';
import {
demoItemFactory,
fillCreateFavouriteDemoItemQuery,
fillRemoveFavouriteDemoItemQuery,
} from '../../../../mocks/factories';
import { useFavoriteDemoItemListQuery } from '../../../../shared/hooks/useFavoriteDemoItem/useFavoriteDemoItem.graphql';
import { composeMockedListQueryResult } from '../../../../tests/utils/fixtures';
import { render } from '../../../../tests/utils/rendering';
import { DemoItemListItem, DemoItemListItemProps } from '../demoItemListItem.component';

const allItems = [...Array(3)].map((_, i) => ({
id: `item-${i + 1}`,
item: {
pk: `item-${i + 1}`,
__typename: 'ContentfulDemoItemType',
},
__typename: 'ContentfulDemoItemFavoriteType',
}));

const mockItemsResponse = () => {
return composeMockedListQueryResult(
useFavoriteDemoItemListQuery,
'allContentfulDemoItemFavorites',
'UseFavoriteDemoItemListQueryQuery',
{
data: allItems,
}
);
};

const demoItem = {
...demoItemFactory({
sys: { id: 'item-1' },
title: 'Example title',
image: { title: 'first image title', url: 'https://image.url' },
}),
};

describe('DemoItemListItem: Component', () => {
const defaultProps: Omit<DemoItemListItemProps, 'item' | 'queryRef'> = {
const defaultProps: Omit<DemoItemListItemProps, 'item'> = {
id: 'item-1',
refreshFavorites: jest.fn(),
};

const DemoItemMockRoute = () => {
Expand All @@ -30,185 +52,103 @@ describe('DemoItemListItem: Component', () => {
};

const Component = (props: Partial<DemoItemListItemProps>) => {
const [queryRef] = useFavoriteDemoItemsLoader();
const data = useLazyLoadQuery<demoItemListItemTestQuery>(
graphql`
query demoItemListItemTestQuery @relay_test_operation {
testItem: demoItem(id: "contentful-item-1") {
...demoItemListItem_item
}
}
`,
{}
);

if (!queryRef) {
return null;
}

return (
<Routes>
<Route
path="/"
element={
<Suspense fallback={null}>
<DemoItemListItem {...defaultProps} {...props} queryRef={queryRef} item={data.testItem} />
</Suspense>
}
/>
<Route path="/" element={<DemoItemListItem {...defaultProps} {...props} item={demoItem} />} />
<Route path={RoutesConfig.getLocalePath(['demoItem'])} element={<DemoItemMockRoute />} />
</Routes>
);
};

describe('item is marked as favorite', () => {
const getRelayEnv = () => {
const relayEnvironment = getBaseRelayEnv();
relayEnvironment.mock.queueOperationResolver((operation) =>
MockPayloadGenerator.generate(operation, {
DemoItem() {
return demoItemFactory({
sys: { id: 'item-1' },
title: 'Example title',
image: { title: 'first image title', url: 'https://image.url' },
});
},
})
);
relayEnvironment.mock.queueOperationResolver((operation) =>
MockPayloadGenerator.generate(operation, {
ContentfulDemoItemFavoriteType: () => ({ id: 'fav-item-1', item: { pk: 'item-1' } }),
})
);

relayEnvironment.mock.queuePendingOperation(demoItemListItemTestQueryGraphql, {});
relayEnvironment.mock.queuePendingOperation(useFavoriteDemoItemListQueryGraphql, {});

return relayEnvironment;
};

// TODO: useFavoriteDemoItemListCreateMutation have to be migrated to apollo
it.skip('should render link to single item page', async () => {
const relayEnvironment = getRelayEnv();
render(<Component />, { relayEnvironment });

it('should render link to single item page', async () => {
const itemsMock = mockItemsResponse();
const { waitForApolloMocks } = render(<Component id="item-1" />, {
apolloMocks: (defaultMocks) => defaultMocks.concat(itemsMock),
});
await waitForApolloMocks(1);
expect(await screen.findByText('Example title')).toBeInTheDocument();
expect(screen.getByLabelText(/is favorite/i)).toBeChecked();
expect(await screen.findByLabelText(/is favorite/i)).toBeChecked();
await userEvent.click(screen.getByText('Example title'));

expect(screen.getByText('Demo item mock route item-1')).toBeInTheDocument();
});

it('should display checked checkbox', async () => {
const relayEnvironment = getRelayEnv();
render(<Component />, { relayEnvironment });
const itemsMock = mockItemsResponse();
const { waitForApolloMocks } = render(<Component id="item-1" />, {
apolloMocks: (defaultMocks) => defaultMocks.concat(itemsMock),
});
await waitForApolloMocks();

expect(await screen.findByLabelText(/is favorite/i)).toBeChecked();
});

describe('item checkbox is clicked', () => {
it('should call delete mutation', async () => {
const relayEnvironment = getRelayEnv();
render(<Component />, { relayEnvironment });
const itemsMock = mockItemsResponse();
const id = 'item-1';
const mutationData = {
deleteFavoriteContentfulDemoItem: {
deletedIds: [id],
},
};
const removeFavoriteItemMockResponse = fillRemoveFavouriteDemoItemQuery(id, mutationData);
removeFavoriteItemMockResponse.newData = jest.fn(() => ({
data: mutationData,
}));

const { waitForApolloMocks } = render(<Component id={id} />, {
apolloMocks: (defaultMocks) => defaultMocks.concat(itemsMock, removeFavoriteItemMockResponse),
});
await waitForApolloMocks(1);

const checkbox = await screen.findByLabelText(/is favorite/i);
expect(checkbox).toBeChecked();
await userEvent.click(checkbox);

const mutationOperation = relayEnvironment.mock.getMostRecentOperation();
expect(mutationOperation?.request.variables).toEqual({
input: { item: 'item-1' },
connections: [
ConnectionHandler.getConnectionID('root', 'useFavoriteDemoItemListQuery__allContentfulDemoItemFavorites'),
],
});

await act(async () => {
relayEnvironment.mock.resolve(
mutationOperation,
MockPayloadGenerator.generate(mutationOperation, {
DeleteFavoriteContentfulDemoItemMutationPayload() {
return { deletedIds: ['fav-item-1'] };
},
})
);
});

await waitFor(() => {
expect(screen.getByLabelText(/is favorite/i)).not.toBeChecked();
});
await userEvent.click(checkbox);
expect(removeFavoriteItemMockResponse.newData).toHaveBeenCalled();
});
});
});

describe('item is not marked as favorite', () => {
const getRelayEnv = () => {
const relayEnvironment = getBaseRelayEnv();
relayEnvironment.mock.queueOperationResolver((operation) =>
MockPayloadGenerator.generate(operation, {
DemoItem() {
return demoItemFactory({
sys: { id: 'item-1' },
title: 'Example title',
image: { title: 'first image title', url: 'https://image.url' },
});
},
})
);
relayEnvironment.mock.queueOperationResolver((operation) =>
MockPayloadGenerator.generate(operation, {
ContentfulDemoItemFavoriteType: () => ({ id: 'fav-other-item', item: { pk: 'other-item' } }),
})
);

relayEnvironment.mock.queuePendingOperation(demoItemListItemTestQueryGraphql, {});
relayEnvironment.mock.queuePendingOperation(useFavoriteDemoItemListQueryGraphql, {});

return relayEnvironment;
};

it('should display unchecked checkbox', async () => {
const relayEnvironment = getRelayEnv();
render(<Component />, { relayEnvironment });
render(<Component />);
expect(await screen.findByLabelText(/is favorite/i)).not.toBeChecked();
});

describe('item checkbox is clicked', () => {
it('should call delete mutation', async () => {
const relayEnvironment = getRelayEnv();
render(<Component />, { relayEnvironment });

const id = 'item-999';
const mutationData = {
createFavoriteContentfulDemoItem: {
contentfulDemoItemFavoriteEdge: {
node: {
id,
item: {
pk: id,
},
},
},
},
};
const createFavoriteItemMockResponse = fillCreateFavouriteDemoItemQuery(id, mutationData);
createFavoriteItemMockResponse.newData = jest.fn(() => ({
data: mutationData,
}));

it('should call create mutation', async () => {
const itemsMock = mockItemsResponse();
const { waitForApolloMocks } = render(<Component id={id} />, {
apolloMocks: (defaultMocks) => defaultMocks.concat(itemsMock, createFavoriteItemMockResponse),
});
await waitForApolloMocks(1);
const checkbox = await screen.findByLabelText(/is favorite/i);
expect(checkbox).not.toBeChecked();

await userEvent.click(checkbox);

const mutationOperation = relayEnvironment.mock.getMostRecentOperation();
expect(mutationOperation?.request.variables).toEqual({
input: { item: 'item-1' },
connections: [
ConnectionHandler.getConnectionID('root', 'useFavoriteDemoItemListQuery__allContentfulDemoItemFavorites'),
],
});

await act(async () => {
relayEnvironment.mock.resolve(
mutationOperation,
MockPayloadGenerator.generate(mutationOperation, {
ContentfulDemoItemFavoriteType() {
return {
id: 'fav-item-1',
item: {
pk: 'item-1',
},
};
},
})
);
});

await waitFor(() => {
expect(screen.getByLabelText(/is favorite/i)).toBeChecked();
});
expect(createFavoriteItemMockResponse.newData).toHaveBeenCalled();
});
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
import { useIntl } from 'react-intl';
import favoriteIconFilled from '@iconify-icons/ion/star';
import favoriteIconOutlined from '@iconify-icons/ion/star-outline';
import { PreloadedQuery, UseQueryLoaderLoadQueryOptions } from 'react-relay';

import { RoutesConfig } from '../../../app/config/routes';
import { useFavoriteDemoItem } from '../../../shared/hooks/useFavoriteDemoItem';
import { imageProps } from '../../../shared/services/contentful';
import { Icon } from '../../../shared/components/icon';
import { useGenerateLocalePath } from '../../../shared/hooks/localePaths';
import { useFavoriteDemoItemListQuery } from '../../../shared/hooks/useFavoriteDemoItem/__generated__/useFavoriteDemoItemListQuery.graphql';
import { DemoItemListItem_ItemFragment } from '../../../shared/services/graphqlApi/__generated/gql/graphql';
import { Container, FavoriteIcon, Link, Thumbnail, Title } from './demoItemListItem.styles';

export type DemoItemListItemProps = {
id: string;
item: DemoItemListItem_ItemFragment;
refreshFavorites: (options?: UseQueryLoaderLoadQueryOptions) => void;
queryRef: PreloadedQuery<useFavoriteDemoItemListQuery>;
};

export const DemoItemListItem = ({ id, queryRef, item }: DemoItemListItemProps) => {
export const DemoItemListItem = ({ id, item }: DemoItemListItemProps) => {
const intl = useIntl();
const { setFavorite, isFavorite } = useFavoriteDemoItem(id, queryRef);
const { setFavorite, isFavorite } = useFavoriteDemoItem(id);
const generateLocalePath = useGenerateLocalePath();

return (
Expand Down
Loading

0 comments on commit 9862584

Please sign in to comment.