import { beforeEach, describe, expect, it } from "vitest";
import { mount } from "@vue/test-utils";
import { defineComponent, h, nextTick } from "vue";
import GnToastProvider from "../../src/vue/components/GnToastProvider.js";
import { useToast } from "../../src/vue/composables/useToast.js";
import { raf } from "../setup.js";
let toast;
const Host = defineComponent({
setup() {
toast = useToast();
return () => h("div", { class: "host" });
}
});
const mountProvider = (props = {}) => {
const wrapper = mount(GnToastProvider, {
props,
slots: { default: () => h(Host) },
attachTo: document.body
});
return wrapper;
};
beforeEach(() => {
document.body.innerHTML = "";
});
describe("GnToastProvider", () => {
it("shows one toast at a time: a new toast replaces the previous one", async () => {
const wrapper = mountProvider({ lifetime: 0 });
toast.success({ title: "First" });
await nextTick();
await raf();
let toasts = document.querySelectorAll(".toast");
expect(toasts).toHaveLength(1);
expect(toasts[0].textContent).toContain("First");
expect(toasts[0].className).toContain("a-show");
toast.success({ title: "Second" });
await nextTick();
await raf();
toasts = document.querySelectorAll(".toast");
expect(toasts).toHaveLength(1);
expect(toasts[0].textContent).toContain("Second");
expect(toasts[0].textContent).not.toContain("First");
wrapper.unmount();
});
it("maps variants and icons, renders the progress bar when lifetime > 0", async () => {
const wrapper = mountProvider({ lifetime: 4000 });
toast.error({ title: "Boom" });
await nextTick();
await raf();
const node = document.querySelector(".toast");
expect(node.className).toContain("toast-danger");
expect(node.className).not.toContain("toast-error");
expect(node.querySelector(".toast-progress-bar")).not.toBeNull();
wrapper.unmount();
});
it("omits the progress bar when lifetime is 0", async () => {
const wrapper = mountProvider({ lifetime: 0 });
toast.info({ title: "Sticky" });
await nextTick();
await raf();
expect(document.querySelector(".toast")).not.toBeNull();
expect(document.querySelector(".toast-progress")).toBeNull();
wrapper.unmount();
});
it("close() removes the toast immediately", async () => {
const wrapper = mountProvider({ lifetime: 4000 });
toast.warning({ title: "Notice" });
await nextTick();
await raf();
expect(document.querySelector(".toast")).not.toBeNull();
toast.close();
await nextTick();
expect(document.querySelector(".toast")).toBeNull();
wrapper.unmount();
});
it("renders text when only text is passed", async () => {
const wrapper = mountProvider({ lifetime: 0 });
toast.info({ text: "Plain body" });
await nextTick();
await raf();
expect(document.querySelector(".toast-text")?.textContent).toBe("Plain body");
wrapper.unmount();
});
});
describe("useToast without a provider", () => {
it("throws with a helpful message", () => {
let lonelyToast;
const Lonely = defineComponent({
setup() {
lonelyToast = useToast();
return () => null;
}
});
mount(Lonely);
expect(() => lonelyToast.success({ title: "x" })).toThrow("<GnToastProvider>");
});
});