Ctrl K
ComunidadGuías…

src/lib/chart-series-view.test.ts

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

Archivo original ↗Preguntar a KiBot
import { beforeEach, describe, expect, it } from "vitest";
import {
  parseSeriesVisibility,
  readStoredSeries,
  toggleSeries,
  writeSeriesVisibility,
} from "@/lib/chart-series-view";
import { memoryStorage, useStorage } from "@/test/memory-storage";

const KEY = "opendiving:test-series";

// The three the profile chart has, standing in for any chart's list.
const ALLOWED = ["depth", "temperature", "pressure"] as const;

// The pair the charts actually compose: read the raw entry, then parse it.
function readSeries() {
  return parseSeriesVisibility(readStoredSeries(KEY), ALLOWED);
}

// `window.localStorage` is installed per test rather than used as jsdom provides
// it - see `test/memory-storage.ts` for why.
describe("readStoredSeries / writeSeriesVisibility", () => {
  beforeEach(() => {
    useStorage(memoryStorage());
  });

  it("round-trips a selection", () => {
    writeSeriesVisibility(KEY, ["depth", "pressure"]);

    expect(readSeries()).toEqual(["depth", "pressure"]);
  });

  it("round-trips a selection of nothing", () => {
    // Distinct from having no entry at all, which means "plot everything".
    // Hiding every series is a choice, and the charts honor it.
    writeSeriesVisibility(KEY, []);

    expect(readSeries()).toEqual([]);
  });

  it("has nothing to restore on a first visit", () => {
    expect(readSeries()).toBeNull();
  });

  it("keeps entries separate by key", () => {
    writeSeriesVisibility("opendiving:other-series", ["depth"]);

    expect(readSeries()).toBeNull();
  });

  it("ignores a value that isn't JSON", () => {
    window.localStorage.setItem(KEY, "not json");

    expect(readSeries()).toBeNull();
  });

  it("ignores a value that isn't an array", () => {
    for (const stored of ["null", '"depth"', "{}", "3"]) {
      window.localStorage.setItem(KEY, stored);
      expect(readSeries()).toBeNull();
    }
  });

  it("drops a series this build no longer plots", () => {
    // A stale entry from a version that had a fourth channel. The rest of the
    // selection is perfectly good and worth restoring.
    window.localStorage.setItem(KEY, JSON.stringify(["depth", "ppo2"]));

    expect(readSeries()).toEqual(["depth"]);
  });

  it("gives up on an entry naming nothing it recognizes", () => {
    // Not the same as an empty entry: this one asked for something, and none of
    // it exists. Restoring it as "hide everything" would open on a blank plot
    // for no reason the diver could account for, so it falls back to all.
    window.localStorage.setItem(KEY, JSON.stringify(["ppo2", "ceiling"]));

    expect(readSeries()).toBeNull();
  });

  it("returns the chart's order, not the stored one", () => {
    // The order is what the chart draws and lists in; a hand-edited or
    // reordered entry must not reorder the legend.
    window.localStorage.setItem(KEY, JSON.stringify(["pressure", "depth"]));

    expect(readSeries()).toEqual(["depth", "pressure"]);
  });

  it("survives storage that refuses to answer", () => {
    // Storage disabled by policy, or a quota-exceeded write.
    const denied = memoryStorage();
    denied.getItem = () => {
      throw new Error("denied");
    };
    denied.setItem = () => {
      throw new Error("denied");
    };
    useStorage(denied);

    expect(() => writeSeriesVisibility(KEY, ["depth"])).not.toThrow();
    expect(readSeries()).toBeNull();
  });

  it("survives storage not being there at all", () => {
    // Not hypothetical: it is exactly the state this test file's own runner is
    // in before `useStorage` runs, and reaching through a missing
    // `window.localStorage` is a `TypeError`, not a storage error.
    useStorage(undefined);

    expect(() => writeSeriesVisibility(KEY, ["depth"])).not.toThrow();
    expect(readSeries()).toBeNull();
  });
});

describe("toggleSeries", () => {
  it("turns a hidden series on", () => {
    expect(toggleSeries(["depth"], "pressure", ALLOWED)).toEqual([
      "depth",
      "pressure",
    ]);
  });

  it("turns a visible series off", () => {
    expect(toggleSeries(["depth", "pressure"], "depth", ALLOWED)).toEqual([
      "pressure",
    ]);
  });

  it("will turn the last one off", () => {
    // Deliberate: an empty plot with the legend still under it is recoverable
    // in one click, and a toggle that silently refuses is worse than one that
    // does what it says.
    expect(toggleSeries(["depth"], "depth", ALLOWED)).toEqual([]);
  });

  it("returns the chart's order however the toggles were clicked", () => {
    const clicked = toggleSeries(
      toggleSeries([], "pressure", ALLOWED),
      "depth",
      ALLOWED,
    );

    expect(clicked).toEqual(["depth", "pressure"]);
  });

  it("orders by the list it's given, which is what the chart offers", () => {
    // The profile chart passes only the channels *this dive* recorded, so a
    // selection can never name one that isn't on screen.
    const available = ["temperature", "pressure"] as const;

    expect(toggleSeries(["pressure"], "temperature", available)).toEqual([
      "temperature",
      "pressure",
    ]);
  });
});