Ctrl K
ComunidadGuías…

src/lib/return-to.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { describe, it, expect } from "vitest";
import {
  isFormPath,
  labelForPath,
  resolveReturnTarget,
  withReturnTo,
} from "./return-to";

const FALLBACK = { href: "/dives", label: "Back to dives" };

describe("labelForPath", () => {
  it("names the section's list page", () => {
    expect(labelForPath("/dives")).toBe("Back to dives");
    expect(labelForPath("/sites")).toBe("Back to dive sites");
    expect(labelForPath("/sites/proposals")).toBe("Back to your proposals");
    expect(labelForPath("/home")).toBe("Back to home");
    expect(labelForPath("/courses")).toBe("Back to courses");
    expect(labelForPath("/people")).toBe("Back to people");
  });

  it("switches to the singular for a single record", () => {
    expect(labelForPath("/trips/abc")).toBe("Back to trip");
    expect(labelForPath("/sites/abc")).toBe("Back to dive site");
    expect(labelForPath("/dives/abc")).toBe("Back to dive");
    expect(labelForPath("/courses/abc")).toBe("Back to course");
    expect(labelForPath("/people/abc")).toBe("Back to person");
    expect(labelForPath("/settings/account")).toBe("Back to settings");
  });

  it("ignores a query string or hash", () => {
    expect(labelForPath("/trips?page=2")).toBe("Back to trips");
    expect(labelForPath("/trips/abc#gear")).toBe("Back to trip");
  });

  it("falls back to a bare label for anything unrecognised", () => {
    expect(labelForPath("/nowhere")).toBe("Back");
    expect(labelForPath("/")).toBe("Back");
  });
});

describe("resolveReturnTarget", () => {
  it("prefers an explicit from over everything else", () => {
    expect(
      resolveReturnTarget({ from: "/trips/abc", trip_uuid: "xyz" }, FALLBACK),
    ).toEqual({ href: "/trips/abc", label: "Back to trip" });
  });

  it("derives the target from the context the form was opened with", () => {
    expect(resolveReturnTarget({ trip_uuid: "abc" }, FALLBACK)).toEqual({
      href: "/trips/abc",
      label: "Back to trip",
    });
    expect(resolveReturnTarget({ dive_site_uuid: "abc" }, FALLBACK)).toEqual({
      href: "/sites/abc",
      label: "Back to dive site",
    });
    // What "Log a dive for this course" hands the form, with no `?from=` of its
    // own - the same wiring the trip's own button gets for free.
    expect(resolveReturnTarget({ course_uuid: "abc" }, FALLBACK)).toEqual({
      href: "/courses/abc",
      label: "Back to course",
    });
  });

  it("uses the caller's fallback when the URL says nothing", () => {
    expect(resolveReturnTarget({}, FALLBACK)).toEqual(FALLBACK);
  });

  // A `from` a visitor can hand-craft must not become an open redirect, and
  // must not point back at a form.
  it("rejects an off-origin from", () => {
    expect(resolveReturnTarget({ from: "//evil.example" }, FALLBACK)).toEqual(
      FALLBACK,
    );
    expect(
      resolveReturnTarget({ from: "https://evil.example" }, FALLBACK),
    ).toEqual(FALLBACK);
    expect(resolveReturnTarget({ from: "/\\evil.example" }, FALLBACK)).toEqual(
      FALLBACK,
    );
  });

  it("rejects a from that points back at a form", () => {
    expect(resolveReturnTarget({ from: "/dives/new" }, FALLBACK)).toEqual(
      FALLBACK,
    );
    expect(resolveReturnTarget({ from: "/dives/abc/edit" }, FALLBACK)).toEqual(
      FALLBACK,
    );
  });

  it("still derives from context when the from is rejected", () => {
    expect(
      resolveReturnTarget(
        { from: "//evil.example", trip_uuid: "abc" },
        FALLBACK,
      ),
    ).toEqual({ href: "/trips/abc", label: "Back to trip" });
  });
});

describe("isFormPath", () => {
  it("recognises the dive form's own routes", () => {
    expect(isFormPath("/dives/new")).toBe(true);
    expect(isFormPath("/dives/abc/edit")).toBe(true);
    expect(isFormPath("/dives/new?trip_uuid=abc")).toBe(true);
  });

  it("leaves ordinary pages alone", () => {
    expect(isFormPath("/dives")).toBe(false);
    expect(isFormPath("/dives/abc")).toBe(false);
    expect(isFormPath("/trips/abc")).toBe(false);
  });
});

describe("withReturnTo", () => {
  // What the opened page reads back, as `useReturnTo` would.
  const backFrom = (href: string) =>
    resolveReturnTarget(
      { from: new URL(href, "http://localhost").searchParams.get("from") },
      FALLBACK,
    );

  it("names the page the link is followed from", () => {
    expect(withReturnTo("/trips/abc", "/home")).toBe("/trips/abc?from=%2Fhome");
    expect(backFrom(withReturnTo("/dives/d", "/trips/abc"))).toEqual({
      href: "/trips/abc",
      label: "Back to trip",
    });
  });

  // Home to trip to dive, and back twice.
  it("keeps that page's own way back", () => {
    const trip = withReturnTo("/trips/abc", "/home");
    const back = backFrom(withReturnTo("/dives/d", trip));
    expect(back).toEqual({ href: trip, label: "Back to trip" });
    expect(backFrom(back.href)).toEqual({
      href: "/home",
      label: "Back to home",
    });
  });

  it("adds nothing where the opened page's back link already goes", () => {
    expect(withReturnTo("/dives/d", "/dives")).toBe("/dives/d");
    expect(withReturnTo("/dives/new", "/dives")).toBe("/dives/new");
    expect(withReturnTo("/dives/d/edit", "/dives/d")).toBe("/dives/d/edit");
    // An edit from the list is not one from its own dive.
    expect(withReturnTo("/dives/d/edit", "/dives")).toBe(
      "/dives/d/edit?from=%2Fdives",
    );
  });

  it("adds nothing from a form, or from nowhere", () => {
    expect(withReturnTo("/dives/d", "/dives/new")).toBe("/dives/d");
    expect(withReturnTo("/dives/d", "/dives/x/edit?from=/trips/t")).toBe(
      "/dives/d",
    );
    expect(withReturnTo("/dives/d", null)).toBe("/dives/d");
  });

  it("leaves a link to the page it is on as that page stands", () => {
    expect(withReturnTo("/courses/c", "/courses/c?from=/home")).toBe(
      "/courses/c?from=/home",
    );
    expect(withReturnTo("/courses/c", "/courses/c")).toBe("/courses/c");
  });

  it("joins a query the link already has", () => {
    expect(withReturnTo("/dives/new?trip_uuid=t", "/people/p")).toBe(
      "/dives/new?trip_uuid=t&from=%2Fpeople%2Fp",
    );
  });

  it("stops a long walk from growing the URL any further", () => {
    const deep = `/sites/s?from=${"x".repeat(1000)}`;
    expect(withReturnTo("/dives/d", deep)).toBe("/dives/d?from=%2Fsites%2Fs");
  });
});