Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"Favorited",
"favorited",
"dtype",
"cnstrc"
"cnstrc",
"Catchify",
]
}
6 changes: 3 additions & 3 deletions spec/ProductCard.server.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('ProductCard: React Server-Side Rendering', () => {
<ProductCard item={transformResultItem(testItem)} />
</CioPlp>,
);
expect(html).toContain('$79.00');
expect(html).toContain('$90.00');
});

test('Should render custom price formatting if overridden at the PlpContext level', () => {
Expand All @@ -46,7 +46,7 @@ describe('ProductCard: React Server-Side Rendering', () => {
<ProductCard item={transformResultItem(testItem)} />
</CioPlp>,
);
expect(html).toContain('USD$79.00');
expect(html).toContain('USD$90.00');
});

test('Should retrieve custom price if overridden at the PlpContext level', () => {
Expand All @@ -72,7 +72,7 @@ describe('ProductCard: React Server-Side Rendering', () => {
);

// React injects <!-- --> on the server to mark dynamic content for rehydration
expect(html).toContain('My Rendered Price: <!-- -->$79.00');
expect(html).toContain('My Rendered Price: <!-- -->$90.00');
});

test('Should throw error for invalid item format', () => {
Expand Down
4 changes: 2 additions & 2 deletions spec/local_examples/item.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"data": {
"id": "KNITS00423-park bench dot",
"url": "https://constructorio-integrations.s3.amazonaws.com/tikus-threads/2022-06-29/KNIT-CASUAL-SHIRT_BUTTON-DOWN-KNIT-SHIRT_BKT00110SG1733_3_category.jpg",
"price": 79,
"price": 90,
"altPrice": 69,
"sale_price": 23,
"image_url": "https://constructorio-integrations.s3.amazonaws.com/tikus-threads/2022-06-29/KNIT-CASUAL-SHIRT_BUTTON-DOWN-KNIT-SHIRT_BKT00110SG1733_3_category.jpg",
Expand All @@ -23,7 +23,7 @@
],
"variation_id": "BKT00110DG1733LR"
},
"value": "Jersey Riviera Shirt (Park Bench Dot)",
"value": "Jersey Riviera Shirt (Red Park Bench Dot)",
"is_slotted": false,
"labels": {},
"variations": [
Expand Down
111 changes: 111 additions & 0 deletions spec/useProductInfo.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import '@testing-library/jest-dom';
import { renderHook, waitFor } from '@testing-library/react';
import useProductInfo from '../src/hooks/useProduct';
import { transformResultItem } from '../src/utils/transformers';
import mockItem from './local_examples/item.json';
import { renderHookWithCioPlp } from './test-utils';

describe('Testing Hook: useProductInfo', () => {
beforeEach(() => {
// Mock console error to de-clutter the console for expected errors
const spy = jest.spyOn(console, 'error');
spy.mockImplementation(() => {});
});

afterEach(() => {
jest.restoreAllMocks(); // This will reset all mocks after each test
});

it('Should throw error if called outside of PlpContext', () => {
expect(() => renderHook(() => useProductInfo())).toThrow();
});

const transformedItem = transformResultItem(mockItem);

it('Should return productSwatch, itemName, itemImageUrl, itemUrl, itemPrice', async () => {
const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem }));

await waitFor(() => {
const {
current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice },
} = result;

expect(productSwatch).not.toBeNull();
expect(itemName).toEqual(transformedItem.itemName);
expect(itemImageUrl).toEqual(transformedItem.imageUrl);
expect(itemUrl).toEqual(transformedItem.url);
expect(itemPrice).toEqual(transformedItem.data.price);
});
});

it('Should return nothing properly with getters that return nothing', async () => {
const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem }), {
initialProps: {
itemFieldGetters: {
getPrice: () => {},
getSwatches: () => {},
getSwatchPreview: () => {},
},
},
});

await waitFor(() => {
const {
current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice },
} = result;

expect(productSwatch).not.toBeNull();
expect(itemName).toEqual(transformedItem.itemName);
expect(itemImageUrl).toEqual(transformedItem.imageUrl);
expect(itemUrl).toEqual(transformedItem.url);
expect(itemPrice).toBeUndefined();
});
});

it('Should return correctly after different variation is selected', async () => {
const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem }));

await waitFor(() => {
const {
current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice },
} = result;
const { selectVariation, swatchList } = productSwatch;
selectVariation(swatchList[1]);

expect(itemName).toEqual(swatchList[1].itemName);
expect(itemImageUrl).toEqual(swatchList[1].imageUrl || transformedItem.imageUrl);
expect(itemUrl).toEqual(swatchList[1].url || transformedItem.url);
expect(itemPrice).toEqual(swatchList[1].price || transformedItem.data.price);
});
});

it('Should return nothing properly with getters that throw errors', async () => {
const { result } = renderHookWithCioPlp(() => useProductInfo({ item: transformedItem }), {
initialProps: {
itemFieldGetters: {
getPrice: () => {
throw new Error();
},
getSwatches: () => {
throw new Error();
},
getSwatchPreview: () => {
throw new Error();
},
},
},
});

await waitFor(() => {
const {
current: { productSwatch, itemName, itemImageUrl, itemUrl, itemPrice },
} = result;

expect(productSwatch).not.toBeNull();
expect(itemName).toEqual(transformedItem.itemName);
expect(itemImageUrl).toEqual(transformedItem.imageUrl);
expect(itemUrl).toEqual(transformedItem.url);
expect(itemPrice).toBeUndefined();
});
});
});
3 changes: 2 additions & 1 deletion src/hooks/useProduct.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import useProductSwatch from '../components/ProductSwatch/useProductSwatch';
import { useCioPlpContext } from './useCioPlpContext';
import { UseProductInfo } from '../types';
import { tryCatchify } from '../utils';

const useProductInfo: UseProductInfo = ({ item }) => {
const state = useCioPlpContext();
Expand All @@ -10,7 +11,7 @@ const useProductInfo: UseProductInfo = ({ item }) => {
throw new Error('data, itemId, or itemName are required.');
}

const getPrice = state?.itemFieldGetters?.getPrice;
const getPrice = tryCatchify(state?.itemFieldGetters?.getPrice);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I don't know what we should be doing here other than try catching it.

I was thinking of doing this to everything in itemFieldGetters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cspeller still complaining. Lets add the word Catchify to the list of cspeller words


const itemName = productSwatch?.selectedVariation?.itemName || item.itemName;
const itemPrice = productSwatch?.selectedVariation?.price || getPrice(item);
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export type UseProductSwatch = (props: UseProductSwatchProps) => ProductSwatchOb
export interface ProductInfoObject {
productSwatch: ProductSwatchObject | undefined;
itemName: string;
itemPrice: number;
itemPrice: number | undefined;
itemUrl: string | undefined;
itemImageUrl: string | undefined;
}
Expand Down
24 changes: 24 additions & 0 deletions src/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,30 @@ export function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

/* istanbul ignore next */
export const logger = (error: any) => {
try {
if (typeof process !== 'undefined' && process?.env?.LOGGER) {
// eslint-disable-next-line no-console
console.log(error);
}
} catch (e) {
// process variable is not available and logger should not be active
}
};

// eslint-disable-next-line @cspell/spellchecker
export function tryCatchify(func: Function) {
return (...args: any) => {
try {
return func(...args);
} catch (e) {
logger(e);
}
return undefined;
};
}

export function removeNullValuesFromObject(obj: Object) {
const filteredListOfEntries = Object.entries(obj).filter(([, val]) => val != null);

Expand Down