test: add 14 comprehensive template service tests
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 12m48s

Cover edge cases for invoice and receipt rendering:
- Custom currency (invoice + receipt)
- Multiple items with different tax rates
- Zero tax rate
- Missing optional fields
- All optional fields present
- Receipt with/without to field
- Receipt paymentMethod
- Empty items array (invoice + receipt)
- Missing quantity (defaults to 1)
- Missing unitPrice (defaults to 0)
- Template list completeness check

Total tests: 428 (was 414)
This commit is contained in:
Hoid 2026-03-01 17:03:50 +01:00
parent 4887e8ffbe
commit bb0a17a6f3

View file

@ -54,4 +54,186 @@ describe("Template rendering", () => {
expect(html).toContain("'");
expect(html).toContain("&");
});
// --- New tests ---
it("invoice with custom currency uses $ instead of €", () => {
const html = renderTemplate("invoice", {
invoiceNumber: "INV-100", date: "2026-02-01",
from: { name: "US Corp" }, to: { name: "Client" },
items: [{ description: "Service", quantity: 1, unitPrice: 50 }],
currency: "$",
});
expect(html).toContain("$50.00");
expect(html).not.toContain("€");
});
it("invoice with multiple items calculates correct subtotal, tax, and total", () => {
const html = renderTemplate("invoice", {
invoiceNumber: "INV-200", date: "2026-02-01",
from: { name: "Seller" }, to: { name: "Buyer" },
items: [
{ description: "Item A", quantity: 2, unitPrice: 100, taxRate: 20 }, // 200 + 40 tax
{ description: "Item B", quantity: 1, unitPrice: 50, taxRate: 10 }, // 50 + 5 tax
{ description: "Item C", quantity: 3, unitPrice: 30, taxRate: 0 }, // 90 + 0 tax
],
});
// Subtotal: 200 + 50 + 90 = 340
expect(html).toContain("€340.00");
// Tax: 40 + 5 + 0 = 45
expect(html).toContain("€45.00");
// Total: 385
expect(html).toContain("€385.00");
});
it("invoice with zero tax rate shows 0% and no tax amount", () => {
const html = renderTemplate("invoice", {
invoiceNumber: "INV-300", date: "2026-02-01",
from: { name: "Seller" }, to: { name: "Buyer" },
items: [{ description: "Tax-free item", quantity: 1, unitPrice: 100, taxRate: 0 }],
});
expect(html).toContain("0%");
// Subtotal and total should be the same
expect(html).toContain("Subtotal: €100.00");
expect(html).toContain("Tax: €0.00");
expect(html).toContain("Total: €100.00");
});
it("invoice with missing optional fields renders without errors", () => {
const html = renderTemplate("invoice", {
invoiceNumber: "INV-400", date: "2026-02-01",
from: { name: "Seller" }, to: { name: "Buyer" },
items: [{ description: "Basic", quantity: 1, unitPrice: 10 }],
// no dueDate, no notes, no paymentDetails
});
expect(html).toContain("INVOICE");
expect(html).toContain("INV-400");
expect(html).not.toContain("Due:");
expect(html).not.toContain("Payment Details");
expect(html).not.toContain("Notes");
});
it("invoice with all optional fields renders them all", () => {
const html = renderTemplate("invoice", {
invoiceNumber: "INV-500", date: "2026-02-01",
dueDate: "2026-03-01",
from: { name: "Full Seller", address: "123 Main St", email: "seller@test.com", phone: "+1234", vatId: "AT123" },
to: { name: "Full Buyer", address: "456 Oak Ave", email: "buyer@test.com", vatId: "DE456" },
items: [{ description: "Premium", quantity: 1, unitPrice: 200, taxRate: 10 }],
currency: "€",
notes: "Please pay promptly",
paymentDetails: "IBAN: AT123456",
});
expect(html).toContain("Due: 2026-03-01");
expect(html).toContain("123 Main St");
expect(html).toContain("seller@test.com");
expect(html).toContain("VAT: AT123");
expect(html).toContain("456 Oak Ave");
expect(html).toContain("buyer@test.com");
expect(html).toContain("VAT: DE456");
expect(html).toContain("Please pay promptly");
expect(html).toContain("IBAN: AT123456");
});
it("receipt with custom currency uses £", () => {
const html = renderTemplate("receipt", {
receiptNumber: "R-100", date: "2026-02-01",
from: { name: "UK Shop" },
items: [{ description: "Tea", amount: 3.50 }],
currency: "£",
});
expect(html).toContain("£3.50");
expect(html).not.toContain("€");
});
it("receipt with paymentMethod shows it", () => {
const html = renderTemplate("receipt", {
receiptNumber: "R-200", date: "2026-02-01",
from: { name: "Shop" },
items: [{ description: "Item", amount: 10 }],
paymentMethod: "Credit Card",
});
expect(html).toContain("Paid via: Credit Card");
});
it("receipt with to field shows customer name", () => {
const html = renderTemplate("receipt", {
receiptNumber: "R-300", date: "2026-02-01",
from: { name: "Shop" },
to: { name: "Jane Doe" },
items: [{ description: "Item", amount: 5 }],
});
expect(html).toContain("Customer: Jane Doe");
});
it("receipt without to field renders without error", () => {
const html = renderTemplate("receipt", {
receiptNumber: "R-400", date: "2026-02-01",
from: { name: "Shop" },
items: [{ description: "Item", amount: 5 }],
});
expect(html).toContain("Shop");
expect(html).not.toContain("Customer:");
});
it("invoice with empty items array renders with €0.00 total", () => {
const html = renderTemplate("invoice", {
invoiceNumber: "INV-600", date: "2026-02-01",
from: { name: "Seller" }, to: { name: "Buyer" },
items: [],
});
expect(html).toContain("Total: €0.00");
});
it("receipt with empty items array renders with €0.00 total", () => {
const html = renderTemplate("receipt", {
receiptNumber: "R-500", date: "2026-02-01",
from: { name: "Shop" },
items: [],
});
expect(html).toContain("€0.00");
});
it("invoice items with missing quantity defaults to 1", () => {
const html = renderTemplate("invoice", {
invoiceNumber: "INV-700", date: "2026-02-01",
from: { name: "Seller" }, to: { name: "Buyer" },
items: [{ description: "Widget", unitPrice: 25 }],
});
// quantity defaults to 1, so line total = 25
expect(html).toContain("€25.00");
});
it("invoice items with missing unitPrice defaults to 0", () => {
const html = renderTemplate("invoice", {
invoiceNumber: "INV-800", date: "2026-02-01",
from: { name: "Seller" }, to: { name: "Buyer" },
items: [{ description: "Free item", quantity: 5 }],
});
// unitPrice defaults to 0, line total = 0
expect(html).toContain("Total: €0.00");
});
it("template list contains both invoice and receipt with correct field definitions", () => {
expect(templates).toHaveProperty("invoice");
expect(templates).toHaveProperty("receipt");
expect(templates.invoice.name).toBe("Invoice");
expect(templates.receipt.name).toBe("Receipt");
// Invoice required fields
const invoiceRequired = templates.invoice.fields.filter(f => f.required).map(f => f.name);
expect(invoiceRequired).toContain("invoiceNumber");
expect(invoiceRequired).toContain("date");
expect(invoiceRequired).toContain("from");
expect(invoiceRequired).toContain("to");
expect(invoiceRequired).toContain("items");
// Receipt required fields
const receiptRequired = templates.receipt.fields.filter(f => f.required).map(f => f.name);
expect(receiptRequired).toContain("receiptNumber");
expect(receiptRequired).toContain("date");
expect(receiptRequired).toContain("from");
expect(receiptRequired).toContain("items");
// Receipt 'to' is optional
const receiptTo = templates.receipt.fields.find(f => f.name === "to");
expect(receiptTo?.required).toBe(false);
});
});