import {
cleanup,
fireEvent,
render,
renderHook,
waitFor,
} from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import SwatchGroup from "./SwatchGroup";
import { useSwatchSettings } from "../hooks/useSwatchSettings";
import { initQuantityButtons } from "../quantityButtons";
afterEach(() => {
cleanup();
document.body.innerHTML = "";
vi.unstubAllGlobals();
});
describe("Free presentation without the extension bundle", () => {
it("publishes the shared label size through the normalized typography variable", async () => {
const select = document.createElement("select");
select.innerHTML =
'';
document.body.append(select);
const { container } = render(
,
);
await waitFor(() =>
expect(container.querySelector('[role="radio"]')).not.toBeNull(),
);
const label = container.querySelector(
'[role="radio"] [data-b3-wvs-swatch-text]',
);
expect(label?.style.getPropertyValue("--b3-wvs-swatch-font-size")).toBe(
"19px",
);
});
it("applies mobile overrides and independent button sizing", () => {
const settings = {
use_mobile_layout: true,
mobile_cutoff: 768,
layout: "wrap",
shape: "square",
swatch_style: "underline",
mobile_layout: "grid",
mobile_shape: "circle",
mobile_swatch_style: "background",
mobile_grid_max_columns: 3,
mobile_swatch_gap: 14,
button_custom_sizing: true,
button_swatch_width: 54,
button_swatch_height: 42,
};
const { result, rerender } = renderHook(
({ width }) => useSwatchSettings(settings, width),
{ initialProps: { width: 390 } },
);
expect(result.current).toMatchObject({
layout: "grid",
shape: "circle",
swatchStyle: "background",
gridMaxColumns: 3,
swatchGap: 14,
buttonSwatchWidth: 54,
buttonSwatchHeight: 42,
});
rerender({ width: 1200 });
expect(result.current).toMatchObject({
layout: "wrap",
shape: "square",
swatchStyle: "underline",
});
});
it("renders container styling and swatch-derived glow, and clears on reselect", async () => {
const select = document.createElement("select");
select.innerHTML =
'';
document.body.append(select);
const { container } = render(
,
);
await waitFor(() =>
expect(container.querySelector('[role="radio"]')).not.toBeNull(),
);
const radio = container.querySelector('[role="radio"]')!;
expect(
container.querySelector('[role="radiogroup"]')!.style
.borderStyle,
).toBe("dashed");
expect(radio.classList.contains("b3-wvs-swatch-glow")).toBe(true);
expect(radio.style.getPropertyValue("--b3-wvs-glow-color")).toBe("#ff0000");
fireEvent.click(radio);
expect(select.value).toBe("red");
fireEvent.click(radio);
expect(select.value).toBe("");
expect(radio.getAttribute("aria-checked")).toBe("false");
});
it("initializes quantity controls once and respects WooCommerce limits", () => {
vi.stubGlobal("b3WvsData", {
settings: {
quantity_custom_buttons: true,
quantity_style: "rounded",
quantity_button_position: "both-sides",
},
});
document.body.innerHTML =
'';
initQuantityButtons();
initQuantityButtons();
expect(document.querySelectorAll(".b3-wvs-qty-plus")).toHaveLength(1);
const input = document.querySelector(".qty")!;
const plus = document.querySelector(".b3-wvs-qty-plus")!;
plus.click();
plus.click();
expect(input.value).toBe("3");
const minus =
document.querySelector(".b3-wvs-qty-minus")!;
minus.click();
minus.click();
minus.click();
expect(input.value).toBe("1");
});
it("uses custom quantity steps while honoring changed stock limits", () => {
vi.stubGlobal("b3WvsData", {
settings: {
quantity_custom_buttons: true,
quantity_style: "rounded",
quantity_custom_limits: true,
quantity_min: 2,
quantity_max: 10,
quantity_step: 2,
},
});
document.body.innerHTML =
'';
initQuantityButtons();
const input = document.querySelector(".qty")!;
const plus = document.querySelector(".b3-wvs-qty-plus")!;
plus.click();
expect(input.value).toBe("4");
input.max = "5";
plus.click();
expect(input.value).toBe("4");
input.disabled = true;
plus.click();
expect(input.value).toBe("4");
input.disabled = false;
input.max = "0";
plus.click();
expect(input.value).toBe("0");
});
});