import { useState } from "react";
import { act, fireEvent, waitFor } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { initSwatches } from "./mountSwatches";
const productMarkup = `
`;
const suppressExpectedReactError = (event: ErrorEvent) => {
event.preventDefault();
};
const getProductElements = () => {
const container = document.querySelector(
".b3-wvs-swatches-container",
);
const original = document.querySelector(
".b3-wvs-original-select",
);
const select = document.querySelector("select");
if (!container || !original || !select) {
throw new Error("Product fixture did not render.");
}
return { container, original, select };
};
describe("initSwatches error recovery", () => {
beforeEach(() => {
document.body.innerHTML = productMarkup;
vi.spyOn(console, "error").mockImplementation(() => undefined);
window.addEventListener("error", suppressExpectedReactError);
});
afterEach(() => {
window.removeEventListener("error", suppressExpectedReactError);
vi.restoreAllMocks();
document.body.innerHTML = "";
});
it("reveals the native select when the swatch renderer fails on its initial render", async () => {
const BrokenSwatches = () => {
throw new Error("initial swatch failure");
};
await act(async () => {
initSwatches({ SwatchGroupComponent: BrokenSwatches });
});
const { container, original, select } = getProductElements();
await waitFor(() => {
expect(original.style.display).toBe("");
expect(container.classList.contains("b3-wvs-fallback")).toBe(true);
expect(container.classList.contains("b3-wvs-initialized")).toBe(false);
});
const change = vi.fn();
select.addEventListener("change", change);
fireEvent.change(select, { target: { value: "blue" } });
expect(select.value).toBe("blue");
expect(change).toHaveBeenCalledOnce();
});
it("reveals the native select when the swatch renderer fails after mounting", async () => {
const FailsOnUpdate = () => {
const [failed, setFailed] = useState(false);
if (failed) {
throw new Error("later swatch failure");
}
return (
);
};
await act(async () => {
initSwatches({ SwatchGroupComponent: FailsOnUpdate });
});
const trigger = await waitFor(() =>
document.querySelector("button"),
);
if (!trigger) {
throw new Error("Swatch fixture did not mount.");
}
fireEvent.click(trigger);
const { container, original } = getProductElements();
await waitFor(() => {
expect(original.style.display).toBe("");
expect(container.classList.contains("b3-wvs-fallback")).toBe(true);
expect(container.classList.contains("b3-wvs-initialized")).toBe(false);
});
});
it("keeps the native select hidden after a successful swatch mount", async () => {
const WorkingSwatches = () => Rendered swatches
;
await act(async () => {
initSwatches({ SwatchGroupComponent: WorkingSwatches });
});
const { container, original } = getProductElements();
expect(original.style.display).toBe("none");
expect(container.classList.contains("b3-wvs-initialized")).toBe(true);
expect(container.classList.contains("b3-wvs-fallback")).toBe(false);
expect(document.body.textContent).toContain("Rendered swatches");
});
});
describe("initSwatches with forms cloned after initialization", () => {
const swatchMarkup = `
`;
beforeEach(() => {
document.body.innerHTML = `${swatchMarkup}
`;
});
afterEach(() => {
document.body.innerHTML = "";
});
it("mounts a fresh, selectable renderer into a clone of an initialized form", async () => {
await act(async () => {
initSwatches();
});
const original = document.querySelector(
"#product .b3-wvs-swatches-container",
)!;
await waitFor(() =>
expect(original.querySelectorAll('[role="radio"]')).toHaveLength(2),
);
// A quick-view/drawer script copies the live, initialized markup.
const clone = original.cloneNode(true) as HTMLElement;
document.getElementById("drawer")!.append(clone);
expect(
clone.querySelector(".b3-wvs-swatches-mount")!.dataset
.b3WvsMounted,
).toBe("1");
await act(async () => {
initSwatches();
});
const cloneSelect = clone.querySelector("select")!;
await waitFor(() =>
expect(clone.querySelectorAll('[role="radio"]')).toHaveLength(2),
);
const cloneBlue = clone.querySelector(
'[data-b3-wvs-radio-value="blue"]',
)!;
await act(async () => {
fireEvent.click(cloneBlue);
});
expect(cloneSelect.value).toBe("blue");
expect(cloneBlue.getAttribute("aria-checked")).toBe("true");
expect(
clone.querySelector(".b3-wvs-original-select")!.style
.display,
).toBe("none");
// The original form keeps its own independent selection state.
expect(original.querySelector("select")!.value).toBe("");
});
it("does not remount a mount this module already owns", async () => {
await act(async () => {
initSwatches();
});
const mount = document.querySelector(
".b3-wvs-swatches-mount",
)!;
await waitFor(() =>
expect(mount.querySelectorAll('[role="radio"]')).toHaveLength(2),
);
const firstRadio = mount.querySelector('[role="radio"]');
await act(async () => {
initSwatches();
});
expect(mount.querySelectorAll('[role="radio"]')).toHaveLength(2);
expect(mount.querySelector('[role="radio"]')).toBe(firstRadio);
});
});