Ctrl K
ComunidadGuías…

src/map-renderer/payload.test.ts

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

Archivo original ↗Preguntar a KiBot
import { describe, expect, it } from "vitest";

import { MAX_FIT_ZOOM } from "@/lib/basemap";
import {
  LABEL_LANGUAGES,
  parsePayload,
  parseStyleRequest,
  PayloadError,
} from "./payload";

const TILE = { kind: "tile", theme: "light", z: 9, x: 300, y: 215 };

function rejects(body: unknown, message: RegExp) {
  expect(() => parsePayload(body)).toThrow(PayloadError);
  expect(() => parsePayload(body)).toThrow(message);
}

describe("parsePayload", () => {
  it("takes a tile in either theme", () => {
    expect(parsePayload(TILE)).toEqual(TILE);
    expect(parsePayload({ ...TILE, theme: "dark" })).toEqual({
      ...TILE,
      theme: "dark",
    });
  });

  it("takes the corners of the grid at every zoom it draws", () => {
    for (let z = 0; z <= MAX_FIT_ZOOM; z += 1) {
      const last = 2 ** z - 1;
      expect(parsePayload({ ...TILE, z, x: 0, y: 0 })).toMatchObject({ z });
      expect(parsePayload({ ...TILE, z, x: last, y: last })).toMatchObject({
        x: last,
        y: last,
      });
    }
  });

  // The deepest any of the web's maps is fitted at, and the API's ceiling too.
  it("refuses a zoom past the deepest a map is fitted at", () => {
    expect(MAX_FIT_ZOOM).toBe(9);
    rejects({ ...TILE, z: 10, x: 0, y: 0 }, /z must be from 0 to 9/);
    rejects({ ...TILE, z: -1 }, /z must be from 0 to 9/);
  });

  it("refuses a square outside the grid", () => {
    rejects({ ...TILE, x: 512 }, /x must be from 0 to 511/);
    rejects({ ...TILE, y: 512 }, /y must be from 0 to 511/);
    rejects({ ...TILE, z: 0, x: 1, y: 0 }, /x must be from 0 to 0/);
    rejects({ ...TILE, x: -1 }, /x must be from 0/);
  });

  it("refuses coordinates that are not integers", () => {
    rejects({ ...TILE, x: 1.5 }, /x must be an integer/);
    rejects({ ...TILE, z: "9" }, /z must be an integer/);
    rejects({ ...TILE, y: null }, /y must be an integer/);
  });

  it("refuses a body naming a record's places", () => {
    rejects(
      {
        kind: "dive",
        theme: "light",
        dive_sites: [{ latitude: 28.5721, longitude: 34.5372 }],
        entry_latitude: null,
        entry_longitude: null,
        exit_latitude: null,
        exit_longitude: null,
      },
      /missing z, x, y/,
    );
    rejects({ ...TILE, kind: "trip" }, /kind must be "tile"/);
  });

  it("refuses a missing key and an extra one alike", () => {
    const { y: _y, ...missing } = TILE;
    rejects(missing, /missing y/);
    rejects({ ...TILE, account: "someone" }, /unexpected account/);
  });

  it("refuses a theme it does not draw, and a body that is not an object", () => {
    rejects({ ...TILE, theme: "sepia" }, /theme/);
    rejects([TILE], /object/);
    rejects(null, /object/);
  });

  it("takes a language from the list, and leaves a tile without one in English", () => {
    expect(parsePayload({ ...TILE, language: "de" })).toEqual({
      ...TILE,
      language: "de",
    });
    expect(parsePayload({ ...TILE, language: "pnb" })).toMatchObject({
      language: "pnb",
    });
    expect("language" in parsePayload(TILE)).toBe(false);
  });

  // The API reduces what a browser says to a code of the list or to no key at
  // all, so anything else here is a client that skipped that.
  it.each([["en"], ["de-DE"], ["DE"], [""], [null], [7], [["de"]]])(
    "refuses the language %j",
    (language) => {
      rejects({ ...TILE, language }, /language must be one of/);
    },
  );
});

describe("parseStyleRequest", () => {
  it("takes either theme, with no language or one of the list", () => {
    expect(parseStyleRequest("light", null)).toEqual({ theme: "light" });
    expect(parseStyleRequest("dark", "de")).toEqual({
      theme: "dark",
      language: "de",
    });
  });

  it("refuses what a tile's body is refused", () => {
    for (const theme of ["sepia", "Light", ""]) {
      expect(() => parseStyleRequest(theme, null)).toThrow(PayloadError);
    }
    for (const language of ["en", "de-DE", "DE", ""]) {
      expect(() => parseStyleRequest("light", language)).toThrow(PayloadError);
    }
  });
});

// Mirrored in the API, which bounds the tiles it stores by it.
describe("LABEL_LANGUAGES", () => {
  it("is the basemap's own languages, bare codes, English left out", () => {
    expect(LABEL_LANGUAGES).toHaveLength(71);
    expect(new Set(LABEL_LANGUAGES).size).toBe(LABEL_LANGUAGES.length);
    expect([...LABEL_LANGUAGES].sort()).toEqual([...LABEL_LANGUAGES]);
    expect(LABEL_LANGUAGES.every((code) => /^[a-z]{2,3}$/.test(code))).toBe(
      true,
    );
    expect(LABEL_LANGUAGES).not.toContain("en");
  });
});