You've already forked tf2wikipricing
refactor: split currency and key/metal formatting into separate units
this simplifies functions and tests, and will make currency optional/variable later
This commit is contained in:
17
__tests__/currency.test.ts
Normal file
17
__tests__/currency.test.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { describe, expect, test, jest, mock, beforeEach } from "bun:test";
|
||||||
|
import { convertTF2PriceToUSD } from '../src/content/utils/currency'
|
||||||
|
|
||||||
|
mock.module('../src/content/config', () => ({
|
||||||
|
conversion_ref_usd: 0.05 // Mock conversion rate
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
describe('Currency Service', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
});
|
||||||
|
|
||||||
|
test('convertTF2PriceToUSD returns correct price', () => {
|
||||||
|
expect(convertTF2PriceToUSD(1, 10, 50)).toBe(3);
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -16,34 +16,34 @@ describe('formatPrice', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('formats price with keys and metal', () => {
|
test('formats price with keys and metal', () => {
|
||||||
expect(formatPrice(2, 10, 50)).toBe('2.2 keys (US$5.50)');
|
expect(formatPrice(2, 10, 50)).toBe('2.2 keys');
|
||||||
expect($T).toHaveBeenCalledWith('%@ keys');
|
expect($T).toHaveBeenCalledWith('%@ keys');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('formats price with metal only', () => {
|
test('formats price with metal only', () => {
|
||||||
expect(formatPrice(0, 15.75, 50)).toBe('15.75 ref (US$0.79)');
|
expect(formatPrice(0, 15.75, 50)).toBe('15.75 ref');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('formats price with metal only and whole number', () => {
|
test('formats price with metal only and whole number', () => {
|
||||||
expect(formatPrice(0, 3, 50)).toBe('3 ref (US$0.16)');
|
expect(formatPrice(0, 3, 50)).toBe('3 ref');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('uses singular key form', () => {
|
test('uses singular key form', () => {
|
||||||
expect(formatPrice(1, 0, 50)).toBe('1 key (US$2.50)');
|
expect(formatPrice(1, 0, 50)).toBe('1 key');
|
||||||
expect($T).toHaveBeenCalledWith('%@ key');
|
expect($T).toHaveBeenCalledWith('%@ key');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('rounds USD up to nearest cent', () => {
|
test('rounds USD up to nearest cent', () => {
|
||||||
expect(formatPrice(3, 7.33, 35)).toBe('3.21 keys (US$5.62)'); // (3 * 35 + 7.33) * 0.05 = 5.6165 → 5.62
|
expect(formatPrice(3, 7.33, 35)).toBe('3.21 keys'); // (3 * 35 + 7.33) * 0.05 = 5.6165 → 5.62
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles different locale formatting', () => {
|
test('handles different locale formatting', () => {
|
||||||
expect(formatPrice(2, 10, 50, 'de')).toMatch(/2,2 keys \(US\$5,50\)/);
|
expect(formatPrice(2, 10, 50, 'de')).toMatch(/2,2 keys/);
|
||||||
expect(formatPrice(0, 15.75, 50, 'de')).toMatch(/15,75 ref \(US\$0,79\)/);
|
expect(formatPrice(0, 15.75, 50, 'de')).toMatch(/15,75 ref/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles zero values', () => {
|
test('handles zero values', () => {
|
||||||
expect(formatPrice(0, 0, 50)).toBe('0 ref (US$0.00)');
|
expect(formatPrice(0, 0, 50)).toBe('0 ref');
|
||||||
expect(formatPrice(0, 0, 50, 'de')).toMatch(/0 ref \(US\$0,00\)/);
|
expect(formatPrice(0, 0, 50, 'de')).toMatch(/0 ref/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ItemPriceData } from "./priceService";
|
import { ItemPriceData } from "./priceService";
|
||||||
|
import { convertTF2PriceToUSD } from "./utils/currency";
|
||||||
import { formatPrice } from "./utils/formatting";
|
import { formatPrice } from "./utils/formatting";
|
||||||
import { $T } from "./utils/localization";
|
import { $T } from "./utils/localization";
|
||||||
|
|
||||||
@@ -19,8 +20,22 @@ export function createPriceRow(qualityName: string, data: ItemPriceData, keyPric
|
|||||||
|
|
||||||
const priceData = document.createElement("td");
|
const priceData = document.createElement("td");
|
||||||
const priceLink = document.createElement("span");
|
const priceLink = document.createElement("span");
|
||||||
const priceString = data ? formatPrice(data.keys, data.metal, keyPrice.metal, locale).trim() : $T('Data unavailable')
|
var priceString: string = ''
|
||||||
priceLink.innerHTML = priceString // + `<br>$${data.scmPrice}`
|
|
||||||
|
if(data) {
|
||||||
|
const currencyFormatter = new Intl.NumberFormat(locale, {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
priceString += [
|
||||||
|
formatPrice(data.keys, data.metal, keyPrice.metal, locale).trim(),
|
||||||
|
'(US$' + currencyFormatter.format(convertTF2PriceToUSD(data.keys, data.metal, keyPrice.metal)) + ')'
|
||||||
|
].join(' ').trim()
|
||||||
|
} else {
|
||||||
|
priceString += $T('Data unavailable')
|
||||||
|
}
|
||||||
|
priceLink.innerHTML = priceString
|
||||||
priceData.appendChild(priceLink);
|
priceData.appendChild(priceLink);
|
||||||
priceRow.appendChild(priceData);
|
priceRow.appendChild(priceData);
|
||||||
return priceRow;
|
return priceRow;
|
||||||
|
|||||||
8
src/content/utils/currency.ts
Normal file
8
src/content/utils/currency.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { conversion_ref_usd } from '../config';
|
||||||
|
|
||||||
|
export function convertTF2PriceToUSD(keys: number, metal: number, keyPrice: number): number {
|
||||||
|
const pureMetal = (keys * keyPrice) + metal;
|
||||||
|
|
||||||
|
// Round price up to nearest cent
|
||||||
|
return Math.ceil(pureMetal * conversion_ref_usd * 100) / 100
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ function toFixed(num: number, fixed: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function formatPrice(keys: number, metal: number, keyPrice: number, locale: string = 'en') {
|
export function formatPrice(keys: number, metal: number, keyPrice: number, locale: string = 'en') {
|
||||||
const pureMetal = (keys * keyPrice) + metal;
|
|
||||||
const formattedKeys = +(keys + (metal / keyPrice)).toFixed(2)
|
const formattedKeys = +(keys + (metal / keyPrice)).toFixed(2)
|
||||||
|
|
||||||
var output: string = ''
|
var output: string = ''
|
||||||
@@ -16,13 +15,6 @@ export function formatPrice(keys: number, metal: number, keyPrice: number, local
|
|||||||
} else {
|
} else {
|
||||||
output += $T("%@ ref").replace('%@', (+toFixed(metal, 2)).toLocaleString(locale))
|
output += $T("%@ ref").replace('%@', (+toFixed(metal, 2)).toLocaleString(locale))
|
||||||
}
|
}
|
||||||
const currencyFormatter = new Intl.NumberFormat(locale, {
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Round price up to nearest cent
|
|
||||||
const price = Math.ceil(pureMetal * conversion_ref_usd * 100) / 100
|
|
||||||
output += ` (US$${currencyFormatter.format(price)})`
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user