import { afterEach, describe, expect, it, vi } from "vitest"; import { formatLowStockText, initLowStockText, resolveLowStockCount, updateLowStockTextForForm, type LowStockSettings, } from "./lowStockText"; const base: LowStockSettings = { enabled: true, format: "Only {qty} left", hideOnBackorder: true, }; afterEach(() => { vi.useRealTimers(); document.body.innerHTML = ""; delete (globalThis as { b3WvsData?: unknown }).b3WvsData; delete (window as unknown as { jQuery?: unknown }).jQuery; }); describe("resolveLowStockCount", () => { it("returns the count when enabled and stock is low", () => { expect(resolveLowStockCount({ b3wvs_low_stock: 3 }, base)).toBe(3); }); it("returns null when disabled, absent, zero, or false", () => { expect( resolveLowStockCount({ b3wvs_low_stock: 3 }, { ...base, enabled: false }), ).toBeNull(); expect(resolveLowStockCount({ b3wvs_low_stock: false }, base)).toBeNull(); expect(resolveLowStockCount({ b3wvs_low_stock: 0 }, base)).toBeNull(); expect(resolveLowStockCount(undefined, base)).toBeNull(); }); it("hides backorderable variations using WooCommerce's native backorders_allowed flag", () => { // Shape of a real Woo 11.x get_available_variation() payload: no is_on_backorder key. const native = { b3wvs_low_stock: 2, backorders_allowed: true }; expect(resolveLowStockCount(native, base)).toBeNull(); expect( resolveLowStockCount(native, { ...base, hideOnBackorder: false }), ).toBe(2); }); it("still honors the legacy is_on_backorder flag", () => { const legacy = { b3wvs_low_stock: 2, is_on_backorder: true }; expect(resolveLowStockCount(legacy, base)).toBeNull(); expect( resolveLowStockCount(legacy, { ...base, hideOnBackorder: false }), ).toBe(2); }); it("shows the text when backorders are not allowed, in every serialized false shape", () => { for (const falsy of [false, "false", "0", 0, "", null, undefined]) { expect( resolveLowStockCount( { b3wvs_low_stock: 2, backorders_allowed: falsy as boolean | string | number, }, base, ), ).toBe(2); expect( resolveLowStockCount( { b3wvs_low_stock: 2, is_on_backorder: falsy as boolean | string | number, }, base, ), ).toBe(2); } }); it("treats serialized true shapes as on backorder", () => { for (const truthy of [true, "true", "1", 1]) { expect( resolveLowStockCount( { b3wvs_low_stock: 2, backorders_allowed: truthy as boolean | string | number, }, base, ), ).toBeNull(); } }); }); describe("formatLowStockText", () => { it("substitutes every {qty} placeholder", () => { expect(formatLowStockText("Only {qty} left", 4)).toBe("Only 4 left"); expect(formatLowStockText("{qty} left ({qty})", 1)).toBe("1 left (1)"); }); it("falls back to the default when the placeholder is missing", () => { expect(formatLowStockText("bad format", 5)).toBe("Only 5 left"); }); }); describe("low-stock live region", () => { it("reads the variation from jQuery's second callback argument", () => { (globalThis as { b3WvsData?: unknown }).b3WvsData = { settings: { low_stock_text_enabled: true }, }; document.body.innerHTML = `
`; const form = document.querySelector("form")!; let foundHandler: ((this: HTMLElement, ...args: unknown[]) => void) | null = null; const on = vi.fn( ( events: string, _selector: string, handler: (this: HTMLElement, ...args: unknown[]) => void, ) => { if (events === "found_variation") foundHandler = handler; }, ); (window as unknown as { jQuery?: unknown }).jQuery = () => ({ on }); initLowStockText(); expect(foundHandler).not.toBeNull(); (foundHandler as unknown as Function).call( form, { type: "found_variation" }, { b3wvs_low_stock: 3 }, ); expect(form.querySelector("[role=status]")?.textContent).toBe( "Only 3 left", ); }); it("mounts in the stable variation wrapper before announcing its first value", () => { vi.useFakeTimers(); (globalThis as { b3WvsData?: unknown }).b3WvsData = { settings: { low_stock_text_enabled: true, low_stock_text_format: "Only {qty} left", }, }; document.body.innerHTML = `
`; const form = document.querySelector("form")!; updateLowStockTextForForm(form, { b3wvs_low_stock: 2 }); const region = form.querySelector("[role=status]")!; expect( region.parentElement?.classList.contains("single_variation_wrap"), ).toBe(true); expect(region.textContent).toBe(""); vi.runAllTimers(); expect(region.textContent).toBe("Only 2 left"); form.querySelector(".single_variation")?.remove(); updateLowStockTextForForm(form, { b3wvs_low_stock: 1 }); expect(region.isConnected).toBe(true); expect(region.textContent).toBe("Only 1 left"); }); });