Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: enhance error message clarity and verbosity [ROW-252] #160

Merged
merged 8 commits into from
Jul 29, 2024
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
5 changes: 5 additions & 0 deletions .changeset/mighty-oranges-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@razorpay/i18nify-js': minor
---

enhance error message clarity and verbosity
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export const getIntlInstanceWithOptions = (
intlOptions.currency = (options.currency || intlOptions.currency) as string;
}

if (!locale) throw new Error('Pass valid locale !');
if (!locale)
throw new Error(
`The provided locale value is invalid. The received value was: ${locale}. Please ensure you pass a correct locale string for proper formatting.`,
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's attach a link for valid locale list too ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We don't have any such list maintained in our codebase.

);

return new Intl.NumberFormat(
locale || undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ describe('currency - convertToMajorUnit', () => {
convertToMajorUnit(100, {
currency: unsupportedCurrencyCode as CurrencyCodeType,
});
}).toThrow('Unsupported currency XXX');
}).toThrow(
`Error: The provided currency code is either empty or not supported. The received value was : ${unsupportedCurrencyCode}. Please ensure you pass a valid currency code.`,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ describe('currency - convertToMinorUnit', () => {
convertToMinorUnit(100, {
currency: unsupportedCurrencyCode as CurrencyCodeType,
});
}).toThrow('Unsupported currency XXX');
}).toThrow(
`Error: The provided currency code is either empty or not supported. The received value was : ${unsupportedCurrencyCode}. Please ensure you pass a valid currency code.`,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('formatNumber', () => {
currency: 'USD',
}),
).toThrow(
`Error: Parameter 'amount' is not a number. typeof amount: string`,
"Error: Parameter 'amount' is not a valid number. The received value was: invalid-amount of type string. Please ensure you pass a valid number.",
);
});

Expand Down Expand Up @@ -92,7 +92,7 @@ describe('formatNumber', () => {

it('should throw error with thousands separators', () => {
expect(() => formatNumber('1,234,567.89', { currency: 'USD' })).toThrow(
`Error: Parameter 'amount' is not a number. typeof amount: string`,
"Error: Parameter 'amount' is not a valid number. The received value was: 1,234,567.89 of type string. Please ensure you pass a valid number.",
);
});

Expand All @@ -103,7 +103,7 @@ describe('formatNumber', () => {
intlOptions: { useGrouping: false },
}),
).toThrow(
`Error: Parameter 'amount' is not a number. typeof amount: string`,
"Error: Parameter 'amount' is not a valid number. The received value was: 1000,5 of type string. Please ensure you pass a valid number.",
);
});

Expand Down Expand Up @@ -150,7 +150,8 @@ describe('formatNumber', () => {
});

it('should rethrow the caught Error instance with the same message', () => {
const errorMessage = 'Error: Invalid currency code : undefined';
const errorMessage =
'Error: An error occurred while formatting the number: Invalid currency code : undefined';

expect(() => {
formatNumber(123, { intlOptions: { currency: 'undefined' } } as any);
Expand All @@ -162,7 +163,7 @@ describe('formatNumber', () => {
formatNumber(123, { intlOptions: { style: 'hola' } } as any);
}).toThrow(
new Error(
'Error: Value hola out of range for Intl.NumberFormat options property style',
'Error: An error occurred while formatting the number: Value hola out of range for Intl.NumberFormat options property style',
),
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('formatNumberByParts', () => {
locale: 'en-US',
}),
).toThrow(
`Error: Parameter 'amount' is not a number. typeof amount: string`,
"Error: Parameter 'amount' is not a valid number. The received value was: not a number of type string. Please ensure you pass a valid number.",
);
});

Expand Down Expand Up @@ -242,7 +242,7 @@ describe('formatNumberByParts', () => {
formatNumberByParts(123, { intlOptions: { style: 'hola' } } as any);
}).toThrow(
new Error(
'Error: Value hola out of range for Intl.NumberFormat options property style',
'Error: An error occurred while formatting the number: Value hola out of range for Intl.NumberFormat options property style',
),
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ describe('getCurrencySymbol', () => {
it('should throw Error for an invalid currency code', () => {
const currencyCode = 'XYZ'; // An invalid code
expect(() => getCurrencySymbol(currencyCode as CurrencyCodeType)).toThrow(
'Error: Invalid currencyCode: XYZ',
`Error: The provided currency code is invalid. The received value was: ${currencyCode}. Please ensure you pass a valid currency code.`,
);
});

it('should throw Error for an empty string', () => {
const currencyCode = '';
expect(() => getCurrencySymbol(currencyCode as CurrencyCodeType)).toThrow(
'Error: Invalid currencyCode: ',
'Error: The provided currency code is invalid. The received value was: . Please ensure you pass a valid currency code.',
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ const convertToMajorUnit = (
): number => {
const currencyInfo = CURRENCY_INFO[options.currency];

if (!currencyInfo)
throw new Error(`Unsupported currency ${String(options.currency)}`);
if (!options.currency || !currencyInfo) {
throw new Error(
`The provided currency code is either empty or not supported. The received value was ${(options.currency as any) === '' ? 'an empty string' : `: ${String(options.currency)}`}. Please ensure you pass a valid currency code. Check valid currency codes here: https://github.com/razorpay/i18nify/blob/master/i18nify-data/currency/data.json`,
);
}

const minorUnitMultiplier =
Math.pow(10, Number(currencyInfo.minor_unit)) || 100;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ const convertToMinorUnit = (
): number => {
const currencyInfo = CURRENCY_INFO[options.currency];

if (!currencyInfo)
throw new Error(`Unsupported currency ${String(options.currency)}`);
if (!options.currency || !currencyInfo) {
throw new Error(
`The provided currency code is either empty or not supported. The received value was ${(options.currency as any) === '' ? 'an empty string' : `: ${String(options.currency)}`}. Please ensure you pass a valid currency code. Check valid currency codes here: https://github.com/razorpay/i18nify/blob/master/i18nify-data/currency/data.json`,
);
RgnDunes marked this conversation as resolved.
Show resolved Hide resolved
}

const minorUnitMultiplier =
Math.pow(10, Number(currencyInfo.minor_unit)) || 100;
Expand Down
8 changes: 5 additions & 3 deletions packages/i18nify-js/src/modules/currency/formatNumber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const formatNumber = (
// Validate the amount parameter to ensure it is a number
if (!Number(amount) && Number(amount) !== 0)
throw new Error(
`Parameter 'amount' is not a number. typeof amount: ${typeof amount}`,
`Parameter 'amount' is not a valid number. The received value was: ${amount} of type ${typeof amount}. Please ensure you pass a valid number.`,
);

try {
Expand All @@ -34,9 +34,11 @@ const formatNumber = (
return parts.map((p) => p.value).join('');
} catch (err) {
if (err instanceof Error) {
throw new Error(err.message);
throw new Error(
`An error occurred while formatting the number: ${err.message}`,
);
} else {
throw new Error(`An unknown error occurred = ${err}`);
throw new Error(`An unknown error occurred. Error details: ${err}`);
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const formatNumberByParts = (
// Validate the amount parameter to ensure it is a number
if (!Number(amount) && Number(amount) !== 0)
throw new Error(
`Parameter 'amount' is not a number. typeof amount: ${typeof amount}`,
`Parameter 'amount' is not a valid number. The received value was: ${amount} of type ${typeof amount}. Please ensure you pass a valid number.`,
);

try {
Expand Down Expand Up @@ -58,9 +58,11 @@ const formatNumberByParts = (
};
} catch (err) {
if (err instanceof Error) {
throw new Error(err.message);
throw new Error(
`An error occurred while formatting the number: ${err.message}`,
);
} else {
throw new Error(`An unknown error occurred = ${err}`);
throw new Error(`An unknown error occurred. Error details: ${err}`);
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ const getCurrencySymbol = (currencyCode: CurrencyCodeType): string => {
const currencyInformation = CURRENCY_INFO;
if (currencyCode in currencyInformation)
return currencyInformation[currencyCode]?.symbol;
else throw new Error(`Invalid currencyCode: ${String(currencyCode)}`);
else
throw new Error(
`The provided currency code is invalid. The received value was: ${String(currencyCode)}. Please ensure you pass a valid currency code. Check valid currency codes here: https://github.com/razorpay/i18nify/blob/master/i18nify-data/currency/data.json`,
);
};

export default withErrorBoundary<typeof getCurrencySymbol>(getCurrencySymbol);
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ describe('dateTime - formatDateTime', () => {
intlOptions: { weekday: 'dummy' } as any,
}),
).toThrow(
'Error: Value dummy out of range for Intl.DateTimeFormat options property weekday',
'Error: An error occurred while creating the DateFormatter instance: Value dummy out of range for Intl.DateTimeFormat options property weekday. Please ensure the provided options are valid and try again.',
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('dateTime - getRelativeTime', () => {
baseDate: oneDayAgo,
}),
).toThrow(
'Error: Value dummy out of range for Intl.RelativeTimeFormat options property style',
'Error: An error occurred while creating the RelativeTimeFormat instance: Value dummy out of range for Intl.RelativeTimeFormat options property style. Please ensure the provided options are valid and try again.',
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe('dateTime - getWeekdays', () => {
weekday: 'dummy' as any,
}),
).toThrow(
'Error: Value dummy out of range for Intl.DateTimeFormat options property weekday',
'Error: An error occurred while creating the DateFormatter instance or formatting the weekdays: Value dummy out of range for Intl.DateTimeFormat options property weekday. Please ensure the provided options are valid and try again.',
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ describe('dateTime - parseDateTime', () => {
intlOptions: { weekday: 'dummy' } as any,
}),
).toThrow(
'Error: Value dummy out of range for Intl.DateTimeFormat options property weekday',
'Error: An error occurred while parsing the date: Value dummy out of range for Intl.DateTimeFormat options property weekday. Please ensure the provided date and options are valid and try again.',
);
});
});
6 changes: 4 additions & 2 deletions packages/i18nify-js/src/modules/dateTime/formatDateTime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,11 @@ const formatDateTime = (
formatter = new DateFormatter(extractedLocale, finalIntlOptions);
} catch (err) {
if (err instanceof Error) {
throw new Error(err.message);
throw new Error(
`An error occurred while creating the DateFormatter instance: ${err.message}. Please ensure the provided options are valid and try again.`,
);
} else {
throw new Error(`An unknown error occurred = ${err}`);
throw new Error(`An unknown error occurred. Error details: ${err}`);
}
}

Expand Down
6 changes: 4 additions & 2 deletions packages/i18nify-js/src/modules/dateTime/getRelativeTime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ const getRelativeTime = (
relativeTime = rtf.format(Math.round(value), unit);
} catch (err) {
if (err instanceof Error) {
throw new Error(err.message);
throw new Error(
`An error occurred while creating the RelativeTimeFormat instance: ${err.message}. Please ensure the provided options are valid and try again.`,
);
} else {
throw new Error(`An unknown error occurred = ${err}`);
throw new Error(`An unknown error occurred. Error details: ${err}`);
}
}

Expand Down
6 changes: 4 additions & 2 deletions packages/i18nify-js/src/modules/dateTime/getWeekdays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ const getWeekdays = (options: {
);
} catch (err) {
if (err instanceof Error) {
throw new Error(err.message);
throw new Error(
`An error occurred while creating the DateFormatter instance or formatting the weekdays: ${err.message}. Please ensure the provided options are valid and try again.`,
);
} else {
throw new Error(`An unknown error occurred = ${err}`);
throw new Error(`An unknown error occurred. Error details: ${err}`);
}
}
};
Expand Down
6 changes: 4 additions & 2 deletions packages/i18nify-js/src/modules/dateTime/parseDateTime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ const parseDateTime = (
} catch (err) {
// Handle any errors that occur during parsing
if (err instanceof Error) {
throw err;
throw new Error(
`An error occurred while parsing the date: ${err.message}. Please ensure the provided date and options are valid and try again.`,
);
} else {
throw new Error(`An unknown error occurred: ${err}`);
throw new Error(`An unknown error occurred. Error details: ${err}`);
}
}
};
Expand Down
15 changes: 11 additions & 4 deletions packages/i18nify-js/src/modules/dateTime/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,26 @@ export const stringToDate = (dateString: string): Date => {
);

if (dateObj.getTime()) return dateObj;
else throw new Error('Invalid Date!');
else
throw new Error(
`Invalid Date! The constructed date from the provided string "${dateString}" is invalid. Please ensure the date string is correct.`,
);
} catch (err) {
if (err instanceof Error) {
throw new Error(err.message);
throw new Error(
`An error occurred while constructing the date: ${err.message}. Please ensure the date string "${dateString}" is in a supported format.`,
);
} else {
throw new Error(`An unknown error occurred = ${err}`);
throw new Error(`An unknown error occurred. Error details: ${err}`);
}
}
}
}

// If no format matches, throw an error.
throw new Error('Date format not recognized');
throw new Error(
`Date format not recognized. The provided date string "${dateString}" does not match any supported formats. Please use a recognized date format.`,
);
};

export const convertToStandardDate = (date: DateInput): Date => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ describe('getAllCountries', () => {

it('handles API errors', async () => {
global.fetch = jest.fn(() => Promise.reject('API Error'));
await expect(getAllCountries()).rejects.toThrow('Error in API response');
await expect(getAllCountries()).rejects.toThrow(
'An error occurred while fetching country metadata. The error details are: undefined.',
);
});
});
10 changes: 7 additions & 3 deletions packages/i18nify-js/src/modules/geo/__tests__/getCities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,24 @@ describe('getCities', () => {
const invalidCountryCode = 'XYZ';
// @ts-expect-error invalid country code for testing
await expect(getCities(invalidCountryCode)).rejects.toEqual(
`Invalid country code: ${invalidCountryCode}`,
new Error(
`Invalid country code: XYZ. Please ensure you provide a valid country code. Check valid country codes here: https://github.com/razorpay/i18nify/blob/master/i18nify-data/country/metadata/data.json`,
),
);
});

it('should reject with an error when state code is missing in a valid country', async () => {
const validCountryCode = 'IN';
const missingStateCode = 'XYZ';
await expect(getCities(validCountryCode, missingStateCode)).rejects.toThrow(
`State code ${missingStateCode} missing in ${validCountryCode}`,
`An error occurred while fetching city data. The error details are: State code ${missingStateCode} is missing in ${validCountryCode}. Please ensure you provide a valid state code that exists within the specified country..`,
);
});

it('handles API errors', async () => {
global.fetch = jest.fn(() => Promise.reject('API Error'));
await expect(getCities('IN')).rejects.toThrow('Error in API response');
await expect(getCities('IN')).rejects.toThrow(
'An error occurred while fetching city data. The error details are: undefined.',
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ describe('geo - getFlagOfCountry', () => {
const sampleInvalidCodes = 'XX';
expect(() =>
getFlagOfCountry(sampleInvalidCodes as CountryCodeType),
).toThrow(`Invalid country code: ${sampleInvalidCodes}`);
).toThrow(
`Error: The provided country code is invalid. The received value was: ${sampleInvalidCodes}. Please ensure you pass a valid country code.`,
);
});

it('should throw an error for an empty string', () => {
expect(() => getFlagOfCountry('' as CountryCodeType)).toThrow(
'Invalid country code: ',
'Error: The provided country code is invalid. The received value was: . Please ensure you pass a valid country code.',
);
});

Expand Down
Loading
Loading