Skip to content
This repository has been archived by the owner on May 21, 2020. It is now read-only.

Commit

Permalink
add orders history to my account (#213)
Browse files Browse the repository at this point in the history
* Add orders history to my account
  • Loading branch information
Qrzy authored Mar 18, 2020
1 parent b42e37f commit 32aadd1
Show file tree
Hide file tree
Showing 28 changed files with 443 additions and 66 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ cache:
- packages/core/utils/lib
- packages/core/factories/node_modules
- packages/core/factories/lib
- packages/core/interfaces/node_modules
- packages/core/interfaces/lib
- packages/prismic/node_modules
- packages/prismic/lib

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import gql from 'graphql-tag';
import { OrderFragment } from './../../fragments';

export default gql`
${OrderFragment}
query getMe($where: String, $sort: [String!], $limit: Int, $offset: Int, $locale: Locale!) {
me {
orders(where: $where, sort: $sort, limit: $limit, offset: $offset) {
results {
...DefaultOrder
}
}
}
}
`;
18 changes: 18 additions & 0 deletions packages/commercetools/api-client/src/api/getMyOrders/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { apolloClient, locale } from './../../index';
import defaultQuery from './defaultQuery';
import { buildOrderWhere } from './../../helpers/search';
import { OrderSearch, ProfileResponse } from './../../types/Api';

export default async (search: OrderSearch): Promise<ProfileResponse> => {
return await apolloClient.query({
query: defaultQuery,
variables: {
where: buildOrderWhere(search),
sort: search.sort,
limit: search.limit,
offset: search.offset,
locale
},
fetchPolicy: 'no-cache'
});
};
1 change: 1 addition & 0 deletions packages/commercetools/api-client/src/fragments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export const OrderFragment = `
orderState
id
version
createdAt
}
`;

Expand Down
20 changes: 18 additions & 2 deletions packages/commercetools/api-client/src/helpers/search/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { CategorySearch, ProductSearch } from './../../types/Api';
import {
CategorySearch,
ProductSearch,
OrderSearch
} from './../../types/Api';
import { locale } from './../../index';

const buildProductWhere = (search: ProductSearch) => {
Expand Down Expand Up @@ -32,4 +36,16 @@ const buildCategoryWhere = (search: CategorySearch) => {
return '';
};

export { buildProductWhere, buildCategoryWhere };
const buildOrderWhere = (search: OrderSearch): string => {
if (search?.id) {
return `id="${search.id}"`;
}

return null;
};

export {
buildProductWhere,
buildCategoryWhere,
buildOrderWhere
};
2 changes: 2 additions & 0 deletions packages/commercetools/api-client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import customerSignMeUp from './api/customerSignMeUp';
import customerSignMeIn from './api/customerSignMeIn';
import customerSignOut from './api/customerSignOut';
import getStorage from './helpers/createCommerceToolsLink/getStorage';
import getMyOrders from './api/getMyOrders';
import customerChangeMyPassword from './api/customerChangeMyPassword';

let apolloClient: ApolloClient<any> = null;
Expand Down Expand Up @@ -81,5 +82,6 @@ export {
customerSignMeUp,
customerSignMeIn,
customerSignOut,
getMyOrders,
customerChangeMyPassword
};
4 changes: 4 additions & 0 deletions packages/commercetools/api-client/src/types/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export interface CategorySearch extends BaseSearch {
slug?: string;
}

export interface OrderSearch extends BaseSearch {
id?: string;
}

export type QueryResponse <K extends string, V> = ApolloQueryResult<Record<K, V>>
export type MutationResponse <K extends string, V> = FetchResult<Record<K, V>>
export type ProfileResponse = QueryResponse<'me', Me>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import getMyOrders from '../../../src/api/getMyOrders';
import { apolloClient } from '../../../src/index';
import defaultQuery from '../../../src/api/getMyOrders/defaultQuery';
import { OrderSearch } from '../../../src/types/Api';

describe('[commercetools-api-client] getMyOrders', () => {
it('fetches current user data', async () => {
const search: OrderSearch = {
id: 'fvdrt8gaw4r',
limit: 10,
offset: 0
};
const givenVariables = {
locale: 'en',
where: 'id="fvdrt8gaw4r"',
limit: 10,
offset: 0,
sort: undefined
};

(apolloClient.query as any).mockImplementation(({ variables, query }) => {
expect(variables).toEqual(givenVariables);
expect(query).toEqual(defaultQuery);

return { data: 'me response' };
});

const { data } = await getMyOrders(search);

expect(data).toBe('me response');
});
});
15 changes: 14 additions & 1 deletion packages/commercetools/api-client/tests/helpers/search.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { buildProductWhere, buildCategoryWhere } from './../../src/helpers/search';
import {
buildProductWhere,
buildCategoryWhere,
buildOrderWhere
} from './../../src/helpers/search';

describe('[commercetools-api-client] search', () => {
it('returns undefined when parameters are not supported', () => {
Expand All @@ -9,6 +13,10 @@ describe('[commercetools-api-client] search', () => {
expect(buildCategoryWhere(null)).toBe('');
});

it('returns undefined string when parameters are not supported', () => {
expect(buildOrderWhere(null)).toBe(null);
});

it('returns product search query by cat id', () => {
expect(buildProductWhere({ catId: ['cat id'] })).toBe('masterData(current(categories(id in ("cat id"))))');
});
Expand All @@ -24,4 +32,9 @@ describe('[commercetools-api-client] search', () => {
it('returns product search query by slug', () => {
expect(buildProductWhere({ slug: 'product-slug' })).toBe('masterData(current(slug(en="product-slug")))');
});

it('returns order search query by id', () => {
expect(buildOrderWhere({ id: 'orderid' })).toBe('id="orderid"');
});

});
4 changes: 3 additions & 1 deletion packages/commercetools/composables/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import useCart from './useCart';
import useCheckout from './useCheckout';
import useUser from './useUser';
import useLocale from './useLocale';
import useUserOrders from './useUserOrders';

export {
useCategory,
useProduct,
useCart,
useCheckout,
useUser,
useLocale
useLocale,
useUserOrders
};

5 changes: 5 additions & 0 deletions packages/commercetools/composables/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type OrderSearchParams = {
id?: string;
page?: number;
perPage?: number;
};
3 changes: 2 additions & 1 deletion packages/commercetools/composables/src/useUser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export default function useUser(): UseUser<Customer, any> {
try {
const profile = await getMe({ customer: true });
user.value = profile.data.me.customer;
} catch (err) {} // eslint-disable-line
// eslint-disable-next-line no-empty
} catch (err) {}

loading.value = false;
});
Expand Down
14 changes: 14 additions & 0 deletions packages/commercetools/composables/src/useUserOrders/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useUserOrdersFactory, UseUserOrdersFactoryParams, OrdersSearchResult } from '@vue-storefront/factories';
import { Order } from '../types/GraphQL';
import { OrderSearchParams } from '../types';
import { getMyOrders } from '@vue-storefront/commercetools-api';

const params: UseUserOrdersFactoryParams<Order, OrderSearchParams> = {
searchOrders: async (params: OrderSearchParams = {}): Promise<OrdersSearchResult<Order>> => {
const result = await getMyOrders(params);
const { results: data, total } = result.data?.me.orders || { results: [], total: 0 };
return { data, total };
}
};

export default useUserOrdersFactory<Order, OrderSearchParams>(params);
34 changes: 27 additions & 7 deletions packages/commercetools/composables/tests/useUser/useUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ jest.mock('@vue-storefront/commercetools-api', () => ({
customerSignMeUp: jest.fn(),
customerSignMeIn: jest.fn(),
customerSignOut: jest.fn(),
getMe: () => ({ data: { me: { customer: { firstName: 'loaded customer', lastName: 'loaded customer' } } } }),
customerChangeMyPassword: jest.fn()
customerChangeMyPassword: jest.fn(),
getMe: () => ({
data: {
me: {
customer: {
firstName: 'loaded customer',
lastName: 'loaded customer'
}
}
}
})
}));

describe.skip('[commercetools-composables] useUser', () => {
Expand All @@ -22,8 +31,12 @@ describe.skip('[commercetools-composables] useUser', () => {
});

it('registers new customer', async () => {
const user = { customer: { firstName: 'john',
lastName: 'doe' } };
const user = {
customer: {
firstName: 'john',
lastName: 'doe'
}
};
(customerSignMeUp as any).mockReturnValue(Promise.resolve({ data: { user } }));

const wrapper = mountComposable(useUser);
Expand All @@ -39,15 +52,22 @@ describe.skip('[commercetools-composables] useUser', () => {
});

it('login customer and log out', async () => {
const user = { customer: { firstName: 'john', lastName: 'doe' } };
const user = {
customer: {
firstName: 'john',
lastName: 'doe'
}
};
(customerSignMeIn as any).mockReturnValue(Promise.resolve({ data: { user } }));

const wrapper = mountComposable(useUser);
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();

wrapper.vm.$data.login({ email: '[email protected]',
password: '123' });
wrapper.vm.$data.login({
email: '[email protected]',
password: '123'
});

expect(wrapper.vm.$data.loading).toBeTruthy();
await wrapper.vm.$nextTick();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useUserOrders } from '../../src';
import { getMyOrders } from '@vue-storefront/commercetools-api';

jest.mock('@vue-storefront/commercetools-api', () => ({
getMyOrders: jest.fn()
}));

describe('[commercetools-composables] useUserOrders', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('has proper initial state', () => {
const { orders, loading } = useUserOrders();
expect(loading.value).toEqual(false);
expect(orders.data.value).toEqual([]);
expect(orders.total.value).toEqual(0);
});

describe('search orders', () => {
it('gets no params and resolves to orders list', async () => {
(getMyOrders as jest.Mock).mockResolvedValueOnce({ data: { me: { orders: { results: [{ id: 'first' }, { id: 'second' }], total: 10 } } } });
const { orders, searchOrders } = useUserOrders();
await searchOrders();
expect(orders.data.value).toEqual([{ id: 'first' }, { id: 'second' }]);
expect(orders.total.value).toEqual(10);
expect(getMyOrders).toBeCalledWith({});
});

it('gets order id and passes it to api client', async () => {
(getMyOrders as jest.Mock).mockResolvedValueOnce({ data: { me: { orders: { results: [{ id: 'first' }], total: 1 } } } });
const { orders, searchOrders } = useUserOrders();
await searchOrders({ id: 'first' });
expect(getMyOrders).toBeCalledWith({ id: 'first' });
expect(orders.data.value).toEqual([{ id: 'first' }]);
expect(orders.total.value).toEqual(1);
});

it('gets empty result from api client', async () => {
(getMyOrders as jest.Mock).mockResolvedValueOnce({});
const { orders, searchOrders } = useUserOrders();
await searchOrders();
expect(orders.data.value).toEqual([]);
expect(orders.total.value).toEqual(0);
});
});
});
3 changes: 2 additions & 1 deletion packages/commercetools/helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"test": "jest"
},
"dependencies": {
"@vue-storefront/commercetools-api": "^0.0.3"
"@vue-storefront/commercetools-api": "^0.0.3",
"@vue-storefront/interfaces": "^0.0.3"
},
"files": [
"lib/**/*"
Expand Down
Loading

0 comments on commit 32aadd1

Please sign in to comment.