mirror of
https://github.com/kamranahmedse/driver.js.git
synced 2026-09-01 14:56:01 +08:00
232 lines
7.3 KiB
TypeScript
232 lines
7.3 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { createDriver, navButton, nextFrame, popoverTitle, SAMPLE_STEPS, useDriverHarness } from "./utils";
|
|
|
|
useDriverHarness();
|
|
|
|
function clickOverlay(): void {
|
|
document.querySelector(".driver-overlay path")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
}
|
|
|
|
describe("button interactions", () => {
|
|
it("advances when the next button is clicked", () => {
|
|
const d = createDriver({ animate: false, steps: SAMPLE_STEPS });
|
|
d.drive();
|
|
navButton("next")?.click();
|
|
|
|
expect(d.getActiveIndex()).toBe(1);
|
|
expect(popoverTitle()).toBe("Step 2");
|
|
});
|
|
|
|
it("goes back when the previous button is clicked", () => {
|
|
const d = createDriver({ animate: false, steps: SAMPLE_STEPS });
|
|
d.drive(1);
|
|
navButton("prev")?.click();
|
|
|
|
expect(d.getActiveIndex()).toBe(0);
|
|
expect(popoverTitle()).toBe("Step 1");
|
|
});
|
|
|
|
it("closes the tour when the close button is clicked", () => {
|
|
const d = createDriver({ animate: false, steps: SAMPLE_STEPS });
|
|
d.drive();
|
|
navButton("close")?.click();
|
|
|
|
expect(d.isActive()).toBe(false);
|
|
});
|
|
|
|
it("destroys the tour when next is clicked on the final step without onDoneClick", () => {
|
|
const d = createDriver({ animate: false, steps: SAMPLE_STEPS });
|
|
d.drive(SAMPLE_STEPS.length - 1);
|
|
navButton("next")?.click();
|
|
|
|
expect(d.isActive()).toBe(false);
|
|
});
|
|
|
|
it("runs onNextClick instead of auto-advancing when provided", () => {
|
|
const onNextClick = vi.fn();
|
|
const d = createDriver({ animate: false, steps: SAMPLE_STEPS, onNextClick });
|
|
d.drive();
|
|
navButton("next")?.click();
|
|
|
|
expect(onNextClick).toHaveBeenCalledTimes(1);
|
|
expect(d.getActiveIndex()).toBe(0);
|
|
});
|
|
|
|
it("runs onPrevClick instead of going back when provided", () => {
|
|
const onPrevClick = vi.fn();
|
|
const d = createDriver({ animate: false, steps: SAMPLE_STEPS, onPrevClick });
|
|
d.drive(1);
|
|
navButton("prev")?.click();
|
|
|
|
expect(onPrevClick).toHaveBeenCalledTimes(1);
|
|
expect(d.getActiveIndex()).toBe(1);
|
|
});
|
|
|
|
it("runs onNextClick when overlayClickBehavior is 'nextStep'", async () => {
|
|
const onNextClick = vi.fn();
|
|
const d = createDriver({
|
|
animate: false,
|
|
overlayClickBehavior: "nextStep",
|
|
steps: SAMPLE_STEPS,
|
|
onNextClick,
|
|
});
|
|
d.drive();
|
|
await nextFrame();
|
|
|
|
clickOverlay();
|
|
|
|
expect(onNextClick).toHaveBeenCalledTimes(1);
|
|
expect(d.getActiveIndex()).toBe(0);
|
|
});
|
|
|
|
it("supports a step-level onNextClick override", () => {
|
|
const onNextClick = vi.fn();
|
|
const d = createDriver({
|
|
animate: false,
|
|
steps: [
|
|
{ element: "#intro", popover: { title: "Step 1", onNextClick } },
|
|
{ element: "#card-1", popover: { title: "Step 2" } },
|
|
],
|
|
});
|
|
d.drive();
|
|
navButton("next")?.click();
|
|
|
|
expect(onNextClick).toHaveBeenCalledTimes(1);
|
|
expect(d.getActiveIndex()).toBe(0);
|
|
});
|
|
|
|
it("runs onDoneClick instead of onNextClick on the final step", () => {
|
|
const onDoneClick = vi.fn();
|
|
const onNextClick = vi.fn();
|
|
const d = createDriver({ animate: false, steps: SAMPLE_STEPS, onDoneClick, onNextClick });
|
|
d.drive(SAMPLE_STEPS.length - 1);
|
|
navButton("next")?.click();
|
|
|
|
expect(onDoneClick).toHaveBeenCalledTimes(1);
|
|
expect(onNextClick).not.toHaveBeenCalled();
|
|
|
|
const [element, step, options] = onDoneClick.mock.calls[0];
|
|
expect(element).toBe(document.querySelector(".feature-list"));
|
|
expect(step.popover?.title).toBe("Step 3");
|
|
expect(options.driver).toBe(d);
|
|
});
|
|
|
|
it("leaves teardown to onDoneClick instead of auto-destroying", () => {
|
|
const onDoneClick = vi.fn();
|
|
const d = createDriver({ animate: false, steps: SAMPLE_STEPS, onDoneClick });
|
|
d.drive(SAMPLE_STEPS.length - 1);
|
|
navButton("next")?.click();
|
|
|
|
expect(onDoneClick).toHaveBeenCalledTimes(1);
|
|
expect(d.isActive()).toBe(true);
|
|
});
|
|
|
|
it("does not fire onDoneClick on non-final steps", () => {
|
|
const onDoneClick = vi.fn();
|
|
const onNextClick = vi.fn();
|
|
const d = createDriver({ animate: false, steps: SAMPLE_STEPS, onDoneClick, onNextClick });
|
|
d.drive();
|
|
navButton("next")?.click();
|
|
|
|
expect(onDoneClick).not.toHaveBeenCalled();
|
|
expect(onNextClick).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("supports a step-level onDoneClick override", () => {
|
|
const onDoneClick = vi.fn();
|
|
const d = createDriver({
|
|
animate: false,
|
|
steps: [
|
|
{ element: "#intro", popover: { title: "Step 1" } },
|
|
{ element: "#card-1", popover: { title: "Step 2", onDoneClick } },
|
|
],
|
|
});
|
|
d.drive(1);
|
|
navButton("next")?.click();
|
|
|
|
expect(onDoneClick).toHaveBeenCalledTimes(1);
|
|
expect(d.isActive()).toBe(true);
|
|
});
|
|
|
|
it("runs onCloseClick instead of destroying when the close button is clicked", () => {
|
|
const onCloseClick = vi.fn();
|
|
const d = createDriver({ animate: false, steps: SAMPLE_STEPS, onCloseClick });
|
|
d.drive();
|
|
navButton("close")?.click();
|
|
|
|
expect(onCloseClick).toHaveBeenCalledTimes(1);
|
|
expect(d.isActive()).toBe(true);
|
|
|
|
const [element, step, options] = onCloseClick.mock.calls[0];
|
|
expect(element).toBe(document.querySelector("#intro"));
|
|
expect(step.popover?.title).toBe("Step 1");
|
|
expect(options.driver).toBe(d);
|
|
});
|
|
|
|
it("supports a step-level onCloseClick override", () => {
|
|
const onCloseClick = vi.fn();
|
|
const d = createDriver({
|
|
animate: false,
|
|
steps: [
|
|
{ element: "#intro", popover: { title: "Step 1", onCloseClick } },
|
|
{ element: "#card-1", popover: { title: "Step 2" } },
|
|
],
|
|
});
|
|
d.drive();
|
|
navButton("close")?.click();
|
|
|
|
expect(onCloseClick).toHaveBeenCalledTimes(1);
|
|
expect(d.isActive()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("overlay click behaviour", () => {
|
|
it("closes the tour when the overlay is clicked and behaviour is 'close'", async () => {
|
|
const d = createDriver({ animate: false, overlayClickBehavior: "close", steps: SAMPLE_STEPS });
|
|
d.drive();
|
|
await nextFrame();
|
|
|
|
clickOverlay();
|
|
|
|
expect(d.isActive()).toBe(false);
|
|
});
|
|
|
|
it("runs a custom overlayClickBehavior with the active element, step and driver", async () => {
|
|
const overlayClickBehavior = vi.fn();
|
|
const d = createDriver({ animate: false, overlayClickBehavior, steps: SAMPLE_STEPS });
|
|
d.drive();
|
|
await nextFrame();
|
|
|
|
clickOverlay();
|
|
|
|
expect(overlayClickBehavior).toHaveBeenCalledTimes(1);
|
|
const [element, step, options] = overlayClickBehavior.mock.calls[0];
|
|
expect(element).toBe(document.querySelector("#intro"));
|
|
expect(step.popover?.title).toBe("Step 1");
|
|
expect(options.driver).toBe(d);
|
|
expect(d.isActive()).toBe(true);
|
|
});
|
|
|
|
it("advances to the next step when 'nextStep' overlay is clicked without onNextClick", async () => {
|
|
const d = createDriver({ animate: false, overlayClickBehavior: "nextStep", steps: SAMPLE_STEPS });
|
|
d.drive();
|
|
await nextFrame();
|
|
|
|
clickOverlay();
|
|
|
|
expect(d.getActiveIndex()).toBe(1);
|
|
});
|
|
|
|
it("runs onDoneClick when a 'nextStep' overlay is clicked on the final step", async () => {
|
|
const onDoneClick = vi.fn();
|
|
const d = createDriver({ animate: false, overlayClickBehavior: "nextStep", onDoneClick, steps: SAMPLE_STEPS });
|
|
d.drive(SAMPLE_STEPS.length - 1);
|
|
await nextFrame();
|
|
|
|
clickOverlay();
|
|
|
|
expect(onDoneClick).toHaveBeenCalledTimes(1);
|
|
expect(d.isActive()).toBe(true);
|
|
});
|
|
});
|