test: add 14 comprehensive template service tests
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 12m48s
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:
parent
4887e8ffbe
commit
bb0a17a6f3
1 changed files with 182 additions and 0 deletions
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue