Files
driver.js/packages/driver/tests/custom-buttons.test.ts
Kamran Ahmed 75edd1b4bc Restructure as a Turborepo monorepo
packages/driver is the library (still published as driver.js, with
repository.directory set); playground and docs move to apps/. One root
pnpm workspace and lockfile replace the three standalone projects, with
turbo orchestrating build/test/lint:package (apps depend on the library
build; local + CI caching via .turbo).

Apps keep importing the library from source for HMR — the aliases now
point at ../../packages/driver/src and each app gains a workspace:*
dependency so the task graph knows the edge. The dev-server fs scope
widens to the repo root accordingly.

Releases run from packages/driver (root `pnpm release` delegates). The
changelog still lives in the docs app, so the release scripts reach it
via ../../../apps/docs and the after:bump hook stages it explicitly —
release-it no longer sits above both directories.

The merged workspace settings keep sharp unbuilt on purpose (prebuilt
binaries; building from source needs node-gyp and fails) — the docs app
had this right and the playground setting was the accident. postcss and
postcss-scss were unreferenced devDependencies and are dropped; prettier
moves to the root as the only shared tool.
2026-07-16 19:45:23 +01:00

101 lines
3.4 KiB
TypeScript

import { readFileSync } from "fs";
import { resolve } from "path";
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { createDriver, navButton, SAMPLE_STEPS, useDriverHarness } from "./utils";
useDriverHarness();
// The footer button styling lives in src/popover.css (shared with the hints
// entry). jsdom resolves the selectors below through getComputedStyle but does
// not follow driver.css's @import, so we inject the popover stylesheet directly.
// The harness only resets <body>, so it survives each test.
beforeAll(() => {
const style = document.createElement("style");
style.textContent = readFileSync(resolve(process.cwd(), "src/popover.css"), "utf8");
document.head.appendChild(style);
});
afterEach(() => {
vi.restoreAllMocks();
});
function addCustomFooterButton(opts: { keepDefaultStyle: boolean }): {
button: HTMLButtonElement;
onClick: ReturnType<typeof vi.fn>;
} {
const onClick = vi.fn();
const button = document.createElement("button");
button.id = "custom-footer-btn";
button.innerText = "Custom";
if (opts.keepDefaultStyle) {
button.classList.add("driver-popover-footer-btn");
}
button.addEventListener("click", onClick);
const d = createDriver({
animate: false,
steps: SAMPLE_STEPS,
onPopoverRender: popover => {
popover.footerButtons.appendChild(button);
},
});
d.drive();
return { button, onClick };
}
describe("footer button styling", () => {
it("styles the built-in navigation buttons through the footer button class", () => {
const d = createDriver({ animate: false, steps: SAMPLE_STEPS });
d.drive();
const next = navButton("next")!;
expect(next.classList.contains("driver-popover-footer-btn")).toBe(true);
expect(navButton("prev")!.classList.contains("driver-popover-footer-btn")).toBe(true);
const styles = getComputedStyle(next);
expect(styles.padding).toBe("3px 7px");
expect(styles.borderRadius).toBe("3px");
});
it("lets a custom footer button opt out of the default button styling", () => {
const { button } = addCustomFooterButton({ keepDefaultStyle: false });
const styles = getComputedStyle(button);
expect(styles.padding).not.toBe("3px 7px");
expect(styles.borderRadius).not.toBe("3px");
});
it("lets a custom footer button opt in to the default button styling", () => {
const { button } = addCustomFooterButton({ keepDefaultStyle: true });
const styles = getComputedStyle(button);
expect(styles.padding).toBe("3px 7px");
expect(styles.borderRadius).toBe("3px");
});
});
describe("custom footer button click handling", () => {
it("does not swallow clicks on a custom footer button that opted in to the style", () => {
const { button, onClick } = addCustomFooterButton({ keepDefaultStyle: true });
const event = new MouseEvent("click", { bubbles: true, cancelable: true });
button.dispatchEvent(event);
expect(onClick).toHaveBeenCalledTimes(1);
expect(event.defaultPrevented).toBe(false);
});
it("still intercepts clicks on the built-in navigation buttons", () => {
const onNextClick = vi.fn();
const d = createDriver({ animate: false, steps: SAMPLE_STEPS, onNextClick });
d.drive();
const event = new MouseEvent("click", { bubbles: true, cancelable: true });
navButton("next")!.dispatchEvent(event);
expect(onNextClick).toHaveBeenCalledTimes(1);
expect(event.defaultPrevented).toBe(true);
});
});