Skip to content

Commit

Permalink
chore: add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
usamaidrsk committed Nov 1, 2024
1 parent 8bdb1c3 commit 7a47e5e
Show file tree
Hide file tree
Showing 9 changed files with 414 additions and 6 deletions.
40 changes: 40 additions & 0 deletions __mocks__/order-price-data.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { OrderPriceData } from '@openmrs/esm-patient-orders-app/src/types/order';

export const mockOrderPriceData: OrderPriceData = {
resourceType: 'Bundle',
id: 'test-id',
meta: {
lastUpdated: '2024-01-01T00:00:00Z',
},
type: 'searchset',
link: [
{
relation: 'self',
url: 'test-url',
},
],
entry: [
{
resource: {
resourceType: 'ChargeItemDefinition',
id: 'test-resource-id',
name: 'Test Item',
status: 'active',
date: '2024-01-01',
propertyGroup: [
{
priceComponent: [
{
type: 'base',
amount: {
value: 99.99,
currency: 'USD',
},
},
],
},
],
},
},
],
};
46 changes: 46 additions & 0 deletions __mocks__/order-stock-data.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export const mockOrderStockData = {
resourceType: 'Bundle',
id: 'test-id',
meta: {
lastUpdated: '2024-01-01T00:00:00Z',
},
type: 'searchset',
link: [
{
relation: 'self',
url: 'test-url',
},
],
entry: [
{
resource: {
resourceType: 'InventoryItem',
id: 'test-resource-id',
meta: {
profile: ['test-profile'],
},
status: 'active',
code: [
{
coding: [
{
system: 'test-system',
code: 'test-code',
display: 'Test Item',
},
],
},
],
name: [
{
name: 'Test Item',
},
],
netContent: {
value: 10,
unit: 'units',
},
},
},
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface OrderPriceDetailsComponentProps {
}

const OrderPriceDetailsComponent: React.FC<OrderPriceDetailsComponentProps> = ({ orderItemUuid }) => {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const { data: priceData, isLoading } = useOrderPrice(orderItemUuid);

const amount = useMemo(() => {
Expand All @@ -20,18 +20,35 @@ const OrderPriceDetailsComponent: React.FC<OrderPriceDetailsComponentProps> = ({
return priceData.entry[0].resource.propertyGroup[0]?.priceComponent[0]?.amount;
}, [priceData]);

const formatPrice = (
amount: {
value: number;
currency: string;
},
locale: string,
): string => {
if (!amount) return '';

return new Intl.NumberFormat(locale, {
style: 'currency',
currency: amount.currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(amount.value);
};

if (isLoading || !priceData) {
return <SkeletonText width="100px" />;
return <SkeletonText width="100px" data-testid="skeleton-text" />;
}

if (!amount) {
if (!priceData || !amount) {
return null;
}

return (
<div className={styles.priceDetailsContainer}>
<span className={styles.priceLabel}>{t('price', 'Price')}:</span>
{`${amount.currency} ${amount.value}`}
{formatPrice(amount, i18n.language)}
<Tooltip
align="bottom-left"
className={styles.priceToolTip}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from 'react';
import { screen } from '@testing-library/react';
import OrderPriceDetailsComponent from './order-price-details.component';
import { useOrderPrice } from '../hooks/useOrderPrice';
import { renderWithSwr } from 'tools';
import { useTranslation } from 'react-i18next';
import { mockOrderPriceData } from '../../../../__mocks__/order-price-data.mock';

jest.mock('../hooks/useOrderPrice');
jest.mock('react-i18next', () => ({
useTranslation: jest.fn(),
}));

const mockUseTranslation = useTranslation as jest.Mock;

describe('OrderPriceDetailsComponent', () => {
const mockOrderItemUuid = 'test-uuid';

beforeEach(() => {
jest.resetAllMocks();
mockUseTranslation.mockImplementation(() => ({
t: (key: string, fallback: string) => fallback,
i18n: { language: 'en-US' },
}));
});

it('renders loading skeleton when data is loading', () => {
(useOrderPrice as jest.Mock).mockReturnValue({
data: null,
isLoading: true,
});

renderWithSwr(<OrderPriceDetailsComponent orderItemUuid={mockOrderItemUuid} />);
expect(screen.getByTestId('skeleton-text')).toBeInTheDocument();
});

it('renders nothing when amount is null', () => {
(useOrderPrice as jest.Mock).mockReturnValue({
data: {
...mockOrderPriceData,
entry: [],
},
isLoading: false,
});

const { container } = renderWithSwr(<OrderPriceDetailsComponent orderItemUuid={mockOrderItemUuid} />);
expect(container).toBeEmptyDOMElement();
});

it('renders price correctly with USD currency', () => {
(useOrderPrice as jest.Mock).mockReturnValue({
data: mockOrderPriceData,
isLoading: false,
});

renderWithSwr(<OrderPriceDetailsComponent orderItemUuid={mockOrderItemUuid} />);

expect(screen.getByText('Price:')).toBeInTheDocument();
expect(screen.getByText('$99.99')).toBeInTheDocument();
});

it('formats price correctly for different locales', () => {
(useOrderPrice as jest.Mock).mockReturnValue({
data: mockOrderPriceData,
isLoading: false,
});

// Change to German locale for this test
mockUseTranslation.mockImplementation(() => ({
t: (key: string, fallback: string) => fallback,
i18n: { language: 'de-DE' },
}));

renderWithSwr(<OrderPriceDetailsComponent orderItemUuid={mockOrderItemUuid} />);

// German locale uses comma as decimal separator
expect(screen.getByText('99,99 $')).toBeInTheDocument();
});

it('displays tooltip with price disclaimer', () => {
(useOrderPrice as jest.Mock).mockReturnValue({
data: mockOrderPriceData,
isLoading: false,
});

renderWithSwr(<OrderPriceDetailsComponent orderItemUuid={mockOrderItemUuid} />);

expect(screen.getByRole('button')).toBeInTheDocument();
expect(screen.getByLabelText(/This price is indicative/)).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ const OrderStockDetailsComponent: React.FC<OrderStockDetailsComponentProps> = ({
return resource.status === 'active' && resource.netContent?.value > 0;
}, [stockData]);

if (isLoading || !stockData) {
return <SkeletonText width="100px" />;
if (isLoading) {
return <SkeletonText width="100px" data-testid="skeleton-text" />;
}

if (!stockData) {
return null;
}

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@use '@carbon/layout';
@use '@openmrs/esm-styleguide/src/vars' as *;

.itemInStock {
Expand All @@ -9,6 +10,7 @@

.itemInStockIcon {
fill: $support-02;
margin-right: layout.$spacing-02;
}
}

Expand All @@ -21,5 +23,6 @@

.itemOutOfStockIcon {
fill: $danger;
margin-right: layout.$spacing-02;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import React from 'react';
import { screen } from '@testing-library/react';
import OrderStockDetailsComponent from './order-stock-details.component';
import { useOrderStockInfo } from '../hooks/useOrderStockInfo';
import { renderWithSwr } from 'tools';
import { useTranslation } from 'react-i18next';
import { mockOrderStockData } from '../../../../__mocks__/order-stock-data.mock';

jest.mock('../hooks/useOrderStockInfo');
jest.mock('react-i18next', () => ({
useTranslation: jest.fn(),
}));

const mockUseTranslation = useTranslation as jest.Mock;

describe('OrderStockDetailsComponent', () => {
const mockOrderItemUuid = 'test-uuid';

beforeEach(() => {
jest.resetAllMocks();
mockUseTranslation.mockImplementation(() => ({
t: (key: string, fallback: string) => fallback,
}));
});

it('renders loading skeleton when data is loading', () => {
(useOrderStockInfo as jest.Mock).mockReturnValue({
data: null,
isLoading: true,
});

renderWithSwr(<OrderStockDetailsComponent orderItemUuid={mockOrderItemUuid} />);
expect(screen.getByTestId('skeleton-text')).toBeInTheDocument();
});

it('renders nothing when stock data is null', () => {
(useOrderStockInfo as jest.Mock).mockReturnValue({
data: null,
isLoading: false,
});

const { container } = renderWithSwr(<OrderStockDetailsComponent orderItemUuid={mockOrderItemUuid} />);
expect(container).toBeEmptyDOMElement();
});

it('renders "In Stock" when item is active and has positive quantity', () => {
(useOrderStockInfo as jest.Mock).mockReturnValue({
data: mockOrderStockData,
isLoading: false,
});

renderWithSwr(<OrderStockDetailsComponent orderItemUuid={mockOrderItemUuid} />);

expect(screen.getByText('In Stock')).toBeInTheDocument();
expect(screen.getByText('CheckmarkFilledIcon')).toBeInTheDocument();
});

it('renders "Out of Stock" when item has zero quantity', () => {
const outOfStockData = {
...mockOrderStockData,
entry: [
{
...mockOrderStockData.entry[0],
resource: {
...mockOrderStockData.entry[0].resource,
netContent: {
value: 0,
unit: 'units',
},
},
},
],
};

(useOrderStockInfo as jest.Mock).mockReturnValue({
data: outOfStockData,
isLoading: false,
});

renderWithSwr(<OrderStockDetailsComponent orderItemUuid={mockOrderItemUuid} />);

expect(screen.getByText('Out of Stock')).toBeInTheDocument();
expect(screen.getByText('CloseFilledIcon')).toBeInTheDocument();
});

it('renders "Out of Stock" when item is inactive', () => {
const inactiveData = {
...mockOrderStockData,
entry: [
{
...mockOrderStockData.entry[0],
resource: {
...mockOrderStockData.entry[0].resource,
status: 'inactive',
},
},
],
};

(useOrderStockInfo as jest.Mock).mockReturnValue({
data: inactiveData,
isLoading: false,
});

renderWithSwr(<OrderStockDetailsComponent orderItemUuid={mockOrderItemUuid} />);

expect(screen.getByText('Out of Stock')).toBeInTheDocument();
expect(screen.getByText('CloseFilledIcon')).toBeInTheDocument();
});

it('renders "Out of Stock" when entry array is empty', () => {
const emptyData = {
...mockOrderStockData,
entry: [],
};

(useOrderStockInfo as jest.Mock).mockReturnValue({
data: emptyData,
isLoading: false,
});

renderWithSwr(<OrderStockDetailsComponent orderItemUuid={mockOrderItemUuid} />);

expect(screen.getByText('Out of Stock')).toBeInTheDocument();
expect(screen.getByText('CloseFilledIcon')).toBeInTheDocument();
});
});
Loading

0 comments on commit 7a47e5e

Please sign in to comment.