Files
driver.js/tests/interactions.test.ts
T

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);
});
});