test: add 29 tests for template branch coverage
Cover receipt and invoice optional field branches: - from.address present/absent - to.name present/absent - paymentMethod present/absent - dueDate, paymentDetails, notes present/absent - from/to fallback to empty object - Item default values (quantity, unitPrice, amount) Templates branch coverage: 80.76% → 98.07%
This commit is contained in:
parent
0a4fcd2e50
commit
e6188369f1
1 changed files with 276 additions and 0 deletions
276
src/__tests__/templates-branch-coverage.test.ts
Normal file
276
src/__tests__/templates-branch-coverage.test.ts
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { renderTemplate, TemplateData } from "../services/templates.js";
|
||||
|
||||
describe("Receipt template optional branches", () => {
|
||||
const baseReceipt = {
|
||||
receiptNumber: "R-100",
|
||||
date: "2026-03-01",
|
||||
items: [{ description: "Item", amount: 10 }],
|
||||
};
|
||||
|
||||
it("renders from.address when present", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
...baseReceipt,
|
||||
from: { name: "Shop", address: "123 Main St" },
|
||||
} as TemplateData);
|
||||
expect(html).toContain("123 Main St");
|
||||
expect(html).toContain('class="center"');
|
||||
});
|
||||
|
||||
it("omits from.address when absent", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
...baseReceipt,
|
||||
from: { name: "Shop" },
|
||||
} as TemplateData);
|
||||
// The "Thank you!" div has class="center", but no address div should exist
|
||||
const addressDivCount = (html.match(/class="center"/g) || []).length;
|
||||
// Only the "Thank you!" div should have class="center", not an address div
|
||||
expect(addressDivCount).toBe(1);
|
||||
});
|
||||
|
||||
it("renders to.name (Customer line) when present", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
...baseReceipt,
|
||||
from: { name: "Shop" },
|
||||
to: { name: "John Doe" },
|
||||
} as TemplateData);
|
||||
expect(html).toContain("Customer: John Doe");
|
||||
});
|
||||
|
||||
it("omits Customer line when to.name is absent", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
...baseReceipt,
|
||||
from: { name: "Shop" },
|
||||
} as TemplateData);
|
||||
expect(html).not.toContain("Customer:");
|
||||
});
|
||||
|
||||
it("renders paymentMethod when present", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
...baseReceipt,
|
||||
from: { name: "Shop" },
|
||||
paymentMethod: "Credit Card",
|
||||
} as TemplateData);
|
||||
expect(html).toContain("Paid via: Credit Card");
|
||||
});
|
||||
|
||||
it("omits paymentMethod when absent", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
...baseReceipt,
|
||||
from: { name: "Shop" },
|
||||
} as TemplateData);
|
||||
expect(html).not.toContain("Paid via:");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Receipt template fallback branches", () => {
|
||||
it("renders without from (falls back to empty object)", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
receiptNumber: "R-200",
|
||||
date: "2026-03-01",
|
||||
items: [{ description: "Item", amount: 5 }],
|
||||
} as TemplateData);
|
||||
expect(html).toContain("R-200");
|
||||
expect(html).toContain("€5.00");
|
||||
});
|
||||
|
||||
it("renders without to (falls back to empty object)", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
receiptNumber: "R-201",
|
||||
date: "2026-03-01",
|
||||
from: { name: "Shop" },
|
||||
items: [{ description: "Item", amount: 5 }],
|
||||
} as TemplateData);
|
||||
expect(html).not.toContain("Customer:");
|
||||
});
|
||||
|
||||
it("renders with custom currency", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
receiptNumber: "R-202",
|
||||
date: "2026-03-01",
|
||||
from: { name: "Shop" },
|
||||
currency: "$",
|
||||
items: [{ description: "Item", amount: 7 }],
|
||||
} as TemplateData);
|
||||
expect(html).toContain("$7.00");
|
||||
});
|
||||
|
||||
it("renders with empty items array", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
receiptNumber: "R-203",
|
||||
date: "2026-03-01",
|
||||
from: { name: "Shop" },
|
||||
} as TemplateData);
|
||||
expect(html).toContain("€0.00");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Invoice template fallback branches", () => {
|
||||
it("renders without from (falls back to empty object)", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
invoiceNumber: "INV-300",
|
||||
date: "2026-03-01",
|
||||
to: { name: "Buyer" },
|
||||
items: [{ description: "Widget", quantity: 1, unitPrice: 10 }],
|
||||
} as TemplateData);
|
||||
expect(html).toContain("INV-300");
|
||||
});
|
||||
|
||||
it("renders without to (falls back to empty object)", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
invoiceNumber: "INV-301",
|
||||
date: "2026-03-01",
|
||||
from: { name: "Seller" },
|
||||
items: [{ description: "Widget", quantity: 1, unitPrice: 10 }],
|
||||
} as TemplateData);
|
||||
expect(html).toContain("INV-301");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Invoice item default values", () => {
|
||||
it("defaults quantity to 1 when missing", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
invoiceNumber: "INV-400",
|
||||
date: "2026-03-01",
|
||||
from: { name: "S" }, to: { name: "B" },
|
||||
items: [{ description: "Widget", unitPrice: 10 }],
|
||||
} as TemplateData);
|
||||
expect(html).toContain("€10.00"); // 1 * 10
|
||||
});
|
||||
|
||||
it("defaults unitPrice to 0 when missing", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
invoiceNumber: "INV-401",
|
||||
date: "2026-03-01",
|
||||
from: { name: "S" }, to: { name: "B" },
|
||||
items: [{ description: "Free item", quantity: 2 }],
|
||||
} as TemplateData);
|
||||
expect(html).toContain("€0.00");
|
||||
});
|
||||
|
||||
it("defaults items to empty array when missing", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
invoiceNumber: "INV-402",
|
||||
date: "2026-03-01",
|
||||
from: { name: "S" }, to: { name: "B" },
|
||||
} as TemplateData);
|
||||
expect(html).toContain("€0.00");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Receipt item default values", () => {
|
||||
it("defaults amount to 0 when missing/invalid", () => {
|
||||
const html = renderTemplate("receipt", {
|
||||
receiptNumber: "R-300",
|
||||
date: "2026-03-01",
|
||||
from: { name: "Shop" },
|
||||
items: [{ description: "Bad item", amount: "not-a-number" }],
|
||||
} as unknown as TemplateData);
|
||||
expect(html).toContain("€0.00");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Invoice template optional branches", () => {
|
||||
const baseInvoice = {
|
||||
invoiceNumber: "INV-100",
|
||||
date: "2026-03-01",
|
||||
from: { name: "Seller" },
|
||||
to: { name: "Buyer" },
|
||||
items: [{ description: "Widget", quantity: 1, unitPrice: 10 }],
|
||||
};
|
||||
|
||||
it("renders dueDate when present", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
...baseInvoice,
|
||||
dueDate: "2026-04-01",
|
||||
} as TemplateData);
|
||||
expect(html).toContain("Due: 2026-04-01");
|
||||
});
|
||||
|
||||
it("omits dueDate when absent", () => {
|
||||
const html = renderTemplate("invoice", baseInvoice as TemplateData);
|
||||
expect(html).not.toContain("Due:");
|
||||
});
|
||||
|
||||
it("renders from.address when present", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
...baseInvoice,
|
||||
from: { name: "Seller", address: "456 Elm St\nSuite 2" },
|
||||
} as TemplateData);
|
||||
expect(html).toContain("456 Elm St<br>Suite 2");
|
||||
});
|
||||
|
||||
it("omits from.address when absent", () => {
|
||||
const html = renderTemplate("invoice", baseInvoice as TemplateData);
|
||||
// from section should have name but no address paragraph
|
||||
const fromSection = html.split('<h3>From</h3>')[1]?.split('<h3>To</h3>')[0];
|
||||
expect(fromSection).not.toContain('<p>456');
|
||||
});
|
||||
|
||||
it("renders from.email when present", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
...baseInvoice,
|
||||
from: { name: "Seller", email: "seller@test.com" },
|
||||
} as TemplateData);
|
||||
expect(html).toContain("seller@test.com");
|
||||
});
|
||||
|
||||
it("renders from.vatId when present", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
...baseInvoice,
|
||||
from: { name: "Seller", vatId: "ATU12345678" },
|
||||
} as TemplateData);
|
||||
expect(html).toContain("VAT: ATU12345678");
|
||||
});
|
||||
|
||||
it("renders to.address when present", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
...baseInvoice,
|
||||
to: { name: "Buyer", address: "789 Oak Ave" },
|
||||
} as TemplateData);
|
||||
expect(html).toContain("789 Oak Ave");
|
||||
});
|
||||
|
||||
it("renders to.email when present", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
...baseInvoice,
|
||||
to: { name: "Buyer", email: "buyer@test.com" },
|
||||
} as TemplateData);
|
||||
expect(html).toContain("buyer@test.com");
|
||||
});
|
||||
|
||||
it("renders to.vatId when present", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
...baseInvoice,
|
||||
to: { name: "Buyer", vatId: "DE999999999" },
|
||||
} as TemplateData);
|
||||
expect(html).toContain("VAT: DE999999999");
|
||||
});
|
||||
|
||||
it("renders paymentDetails when present", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
...baseInvoice,
|
||||
paymentDetails: "IBAN: AT123456\nBIC: BKAUATWW",
|
||||
} as TemplateData);
|
||||
expect(html).toContain("Payment Details");
|
||||
expect(html).toContain("IBAN: AT123456<br>BIC: BKAUATWW");
|
||||
});
|
||||
|
||||
it("omits paymentDetails when absent", () => {
|
||||
const html = renderTemplate("invoice", baseInvoice as TemplateData);
|
||||
expect(html).not.toContain("Payment Details");
|
||||
});
|
||||
|
||||
it("renders notes when present", () => {
|
||||
const html = renderTemplate("invoice", {
|
||||
...baseInvoice,
|
||||
notes: "Thank you for your business",
|
||||
} as TemplateData);
|
||||
expect(html).toContain("Thank you for your business");
|
||||
});
|
||||
|
||||
it("omits notes when absent", () => {
|
||||
const html = renderTemplate("invoice", baseInvoice as TemplateData);
|
||||
expect(html).not.toContain("<strong>Notes</strong>");
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue