money-lib
Version:
TypeScript library to work with money
544 lines (494 loc) • 13.5 kB
text/typescript
import assert from "node:assert";
import { describe, test, expect } from "bun:test";
import { setupMoney, money } from "../v2";
import { defaultConfig } from "../lib/config";
describe("v2", () => {
test("different initializers", () => {
// Any of these works:
money(100);
money("€100", "EUR");
money("$100.99");
money("€ 100");
money("100eur");
money("100 btc");
// with cents
money("100cents");
money("100 cents");
money("€100cents");
money("€ 100 cents ");
money("100 EUR cents");
money("100EURcents");
money("100 cents EUR");
money("100 cents", "EUR");
const m = money("100 eur").add("€57.99").sub(7.99).mul(5.56399);
expect(m.fmt()).toBe("€834,60");
expect(m.float()).toBe(834.6);
expect(m.cents()).toBe(83460);
expect(money().fmt()).toBe("€0,00");
expect(money({ cents: 50, currency: "EUR" }).fmt()).toBe("€0,50");
expect(money({ decimal: 5.45, currency: "EUR" }).fmt()).toBe("€5,45");
// must not compile:
// money("🎃 15");
// money("15 spooky");
// money("200uah");
});
describe("basic use cased", () => {
test("should do arithm and fmt money", () => {
const m = money(0)
.add({ amount: 4499, currency: "EUR" })
.sub({ amount: 299, currency: "EUR" })
.mul(5.56399)
.div(5.56399)
.fmt();
assert.strictEqual(m, "€42,00");
});
test("should do arithm and fmt money with default currency", () => {
const m = money(0)
.add({ amount: 4499 })
.sub({ amount: 299 })
.mul(5.56399)
.div(5.56399)
.fmt();
assert.strictEqual(m, "€42,00");
});
});
describe("serialization", () => {
test("int", () => {
assert.equal(money(100.42).cents(), 10042);
});
test("float / number", () => {
assert.equal(money(100.42).float(), 100.42);
assert.equal(money(100.42).number(), 100.42);
});
test("centsStr", () => {
assert.equal(money(100.42).centStr(), "10042");
assert.equal(money(0).centStr(), "0");
assert.equal(money(0.01).centStr(), "1");
});
test("string", () => {
assert.equal(money(100.42).string(), "100.42");
assert.equal(money(0.01).string(), "0.01");
assert.equal(money(0).string(), "0.00");
});
});
describe("floatToInt intToFloat", () => {
test("floatToInt", () => {
assert.equal(money(100.42).int(), 10042);
});
test("intToFloat", () => {
assert.equal(money("100.42").float(), 100.42);
});
});
describe("arithmetics", () => {
test("add", () => {
assert.ok(
Bun.deepEquals(money(100.42).add(100.42).json(), {
currency: "EUR",
amount: 20084,
})
);
});
test("add many", () => {
const foo = money(0.01);
const bar = money("0.01eur");
assert.equal(
money("0.01")
.add("0.02", { amount: 3 }, foo, "0.02", money("0.01"), money(0.01))
.number(),
0.11
);
});
test("sub", () => {
assert.ok(
Bun.deepEquals(money("100.42").sub(100.42).json(), {
currency: "EUR",
amount: 0,
})
);
assert.ok(
Bun.deepEquals(money("1").sub({ currency: "EUR", amount: 50 }).json(), {
currency: "EUR",
amount: 50,
})
);
});
test("sub many", () => {
assert.ok(
Bun.deepEquals(
money("0.06").sub({ amount: 3 }, 0.02, money(0.01)).json(),
{ currency: "EUR", amount: 0 }
)
);
});
test("mul", () => {
assert.ok(
Bun.deepEquals(money(100.42).mul(2).json(), {
currency: "EUR",
amount: 20084,
})
);
});
test("div", () => {
assert.ok(
Bun.deepEquals(money(100.42).div(2).json(), {
currency: "EUR",
amount: 5021,
})
);
assert.ok(
Bun.deepEquals(money(70.44).div(100).json(), {
currency: "EUR",
amount: 70,
})
);
});
test("abs", () => {
assert.equal(money(100.42).abs().fmt(), "€100,42");
assert.equal(money(-100.42).abs().fmt(), "€100,42");
});
});
describe("comparison", () => {
test("equal", () => {
assert.strictEqual(
money(100.42).eq({
currency: "EUR",
amount: 10042,
}),
true
);
assert.strictEqual(
money(100.42).eq({
currency: "EUR",
amount: 10043,
}),
false
);
assert.strictEqual(money(100.42).isNeg(), false);
assert.strictEqual(money(-100.42).isNeg(), true);
});
test("lt", () => {
assert.strictEqual(
money(100.42).lt({
currency: "EUR",
amount: 10043,
}),
true
);
assert.strictEqual(
money(100.42).lt({
currency: "EUR",
amount: 10041,
}),
false
);
assert.strictEqual(
money(100.42).lt({
currency: "EUR",
amount: 10042,
}),
false
);
});
test("lte", () => {
assert.strictEqual(
money(100.42).lte({
currency: "EUR",
amount: 10042,
}),
true
);
assert.strictEqual(
money(100.41).lte({
currency: "EUR",
amount: 10043,
}),
true
);
assert.strictEqual(
money(100.42).lte({
currency: "EUR",
amount: 10041,
}),
false
);
});
test("gt", () => {
assert.strictEqual(
money(100.42).gt({
currency: "EUR",
amount: 10042,
}),
false
);
assert.strictEqual(
money(100.42).gt({
currency: "EUR",
amount: 10041,
}),
true
);
assert.strictEqual(
money(100.42).gt({
currency: "EUR",
amount: 10043,
}),
false
);
});
test("gte", () => {
assert.strictEqual(
money(100.42).gte({
currency: "EUR",
amount: 10042,
}),
true
);
assert.strictEqual(
money(100.42).gte({
currency: "EUR",
amount: 10041,
}),
true
);
assert.strictEqual(
money(100.42).gte({
currency: "EUR",
amount: 10043,
}),
false
);
});
test("cmp", () => {
assert.strictEqual(
money(100.42).cmp({
currency: "EUR",
amount: 10042,
}),
0
);
assert.strictEqual(money(100.42).cmp(money(100.41)), 1);
assert.strictEqual(
money(100.42).cmp({
currency: "EUR",
amount: 10043,
}),
-1
);
});
test("min", () => {
const m1 = money(0.01);
const m2 = money(0);
const m3 = money(0.02);
const m4 = money(-0.01);
assert.ok(
Bun.deepEquals(money(0).min(m1, m2, m3, m4).json(), {
currency: "EUR",
amount: -1,
})
);
assert.ok(
Bun.deepEquals(money(0).min(m1, m2, m3, { amount: -2 }, m4).json(), {
currency: "EUR",
amount: -2,
})
);
assert.ok(
Bun.deepEquals(money(0).min(m1, m2).json(), {
currency: "EUR",
amount: 0,
})
);
assert.ok(
Bun.deepEquals(money(0).min(m1).json(), {
currency: "EUR",
amount: 1,
})
);
});
test("max", () => {
const m1 = money(0.01);
const m2 = money(0);
const m3 = money(0.02);
const m4 = money(-0.01);
assert.ok(
Bun.deepEquals(money(0).max(m1, m2, m3, m4).json(), {
currency: "EUR",
amount: 2,
})
);
assert.ok(
Bun.deepEquals(money(0).max(m1, m2, m3, { amount: 3 }, m4).json(), {
currency: "EUR",
amount: 3,
})
);
assert.ok(
Bun.deepEquals(money(0).max(m1, m2).json(), {
currency: "EUR",
amount: 1,
})
);
assert.ok(
Bun.deepEquals(money(0).max(m1).json(), {
currency: "EUR",
amount: 1,
})
);
});
});
describe("formatting", () => {
test("fmt", () => {
assert.equal(money(0).fmt(), "€0,00");
assert.equal(money(0.05).fmt(), "€0,05");
assert.equal(money(0.5).fmt(), "€0,50");
assert.equal(money(1000.42).fmt(), "€1.000,42");
assert.equal(
money(1000.42).fmt({
cents: false,
}),
"€1.000,42"
);
assert.equal(
money(1000).fmt({
cents: false,
}),
"€1.000"
);
assert.equal(
money(0.5).fmt({
cents: false,
}),
"€0,50"
);
assert.equal(
money(0).fmt({
cents: false,
}),
"€0"
);
assert.equal(
money(0).fmt({
cents: true,
}),
"€0,00"
);
// assert.equal(
// money(0).fmt({
// locale: "IE",
// }),
// "€1,000.42"
// );
assert.equal(money(0.01).fmt(), "€0,01");
});
test("fmt negative amount", () => {
assert.ok(Bun.deepEquals(money(-1000.42).fmt(), "-€1.000,42"));
});
test("fmts", () => {
assert.ok(
Bun.deepEquals(money(1000555.42).fmts(), {
cents: "42",
currencySymbol: "€",
decimalSeparator: ",",
base: "1000555",
baseFormatted: "1.000.555",
sign: "+",
})
);
assert.ok(
Bun.deepEquals(money(-1000555.42).fmts(), {
cents: "42",
currencySymbol: "€",
decimalSeparator: ",",
base: "1000555",
baseFormatted: "1.000.555",
sign: "-",
})
);
});
test("fmt BTC", () => {
assert.equal(money("0 BTC").fmt(), "₿0,00000000");
assert.equal(money("0.00000005 BTC").fmt(), "₿0,00000005");
assert.equal(money("0.0000005 BTC").fmt(), "₿0,00000050");
assert.equal(money("0.00000500 BTC").fmt({}), "₿0,00000500");
assert.equal(
money("0.000005 BTC").fmt({ trailingZeros: false }),
"₿0,000005"
);
assert.equal(money("1 BTC").fmt(), "₿1,00000000");
assert.equal(money(" 1 BTC").fmt({ cents: false }), "₿1");
assert.equal(money("₿199").fmt({ cents: false }), "₿199");
assert.equal(money("1999.00000005 BTC").fmt(), "₿1.999,00000005");
assert.equal(money("1999.00000005 BTC").fmt(), "₿1.999,00000005");
});
test("fmt withPlusSign", () => {
assert.equal(money("1 EUR").fmt(), "€1,00");
assert.equal(money("1 EUR").fmt({ withPlusSign: true }), "+€1,00");
assert.equal(money("0 EUR").fmt({ withPlusSign: true }), "€0,00");
assert.equal(money("-1 EUR").fmt({ withPlusSign: true }), "-€1,00");
});
});
describe("validation", () => {});
describe("transformation", () => {});
describe("parsings", () => {
test("works the same as initializer but with wider string type", () => {
expect(money().parse("€100").fmt()).toBe("€100,00");
expect(money().parse("100.45", "EUR").fmt()).toBe("€100,45");
expect(money().parse("100", "EUR").fmt()).toBe("€100,00");
expect(money().parse("100 eur").fmt()).toBe("€100,00");
expect(money().parse("€ 100").fmt()).toBe("€100,00");
expect(money().parse("100,45").fmt()).toBe("€100,45"); // comma treated as decimal separator
expect(money(0).parse("10.500,99", "EUR").fmt()).toBe("€10,50"); // thousand separators are ignored
expect(money(0).parse("10500.99", "EUR").fmt()).toBe("€10.500,99");
});
test("parses to 0 as fallback", () => {
expect(money().parse("xxx").fmt()).toBe("€0,00");
expect(money().parse("foo", "USD").fmt()).toBe("$0,00");
expect(money().parse("").fmt()).toBe("€0,00");
});
test("EUR", () => {
const expected = {
currency: "EUR",
amount: 1050099,
};
assert.ok(
Bun.deepEquals(money(0).parse("10500,99", "EUR").json(), expected)
);
assert.ok(
Bun.deepEquals(money(0).parse("10_500 , 99", "EUR").json(), expected)
);
});
});
describe("custom config", () => {
test("custom config", () => {
const { money: moneyCustom } = setupMoney({
currencies: [
{
code: "EUR" as const,
symbol: "€" as const,
scale: 2,
},
{
code: "BTC" as const,
symbol: "₿" as const,
scale: 8,
},
{
code: "SPOOKY" as const,
symbol: "🎃" as const,
scale: 5,
},
],
defaultCurrency: "EUR" as const,
defaultRoundingMethod: "bankers",
});
expect(moneyCustom("-10.61eur").fmt()).toBe("-€10,61");
expect(moneyCustom("🎃 -10.61").fmt()).toBe("-🎃10,61000");
expect(moneyCustom("0.15 spooky").fmt({ trailingZeros: false })).toBe(
"🎃0,15"
);
// must not compile
// assert.equal(moneyCustom("-10.61pund").fmt(), "-€10,61000");
setupMoney(defaultConfig);
});
});
});