OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
 
master @ 88 LINES
 
[ HISTORY ]  [ UP ]
 

import { describe, expect, it } from "vitest";
import { buildAsciiTable, buildTransposedTable, type Cell } from "./ascii-table";
import { len } from "./ascii";

const cell = (text: string, align: Cell["align"] = "end"): Cell => ({ text, align 
});

const HEADER = [cell("NAME"), cell("VALUE")];

describe("buildAsciiTable", () => {
  it("emits every line at the same width, within the target", () => {
    const t = buildAsciiTable([HEADER, [cell("a"), cell("b")]], 1, 40);
    const widths = new Set(t.split("\n").map(len));
    expect(widths.size).toBe(1);
    expect([...widths][0]).toBeLessThanOrEqual(40);
  });

  it("keeps single-line tables compact (no row separators)", () => {
    const t = buildAsciiTable([HEADER, [cell("a"), cell("b")], [cell("c"), cell("d
")]], 1, 40);
    expect(t).not.toContain("╌");
    // one solid separator under the header only
    expect(t.split("\n").filter((l) => l.startsWith("├")).length).toBe(1);
  });

  it("adds dashed separators between body rows once any cell wraps", () => {
    const long = "a description long enough that it must wrap onto several lines";
    const t = buildAsciiTable(
      [HEADER, [cell("frame"), cell(long)], [cell("select"), cell("short")]],
      1,
      30,
    );
    expect(t).toContain("╌");
    const dashed = t.split("\n").filter((l) => l.includes("╌"));
    expect(dashed.length).toBe(1); // between the two body rows
  });

  it("right-aligns cells marked align start", () => {
    const t = buildAsciiTable([HEADER, [cell("x"), cell("42", "start")]], 1, 40);
    const row = t.split("\n").find((l) => l.includes("42"))!;
    expect(row).toMatch(/ 42 │$/);
  });

  it("shrinks the widest column until the table fits", () => {
    const wide = "word ".repeat(30).trim();
    const t = buildAsciiTable([HEADER, [cell("k"), cell(wide)]], 1, 48);
    for (const line of t.split("\n")) expect(len(line)).toBeLessThanOrEqual(48);
  });

  it("pads ragged rows to the full column count", () => {
    const t = buildAsciiTable([HEADER, [cell("only-one")]], 1, 40);
    const widths = new Set(t.split("\n").map(len));
    expect(widths.size).toBe(1);
  });
});

describe("buildTransposedTable", () => {
  const rows = [
    [cell("host"), cell("spec"), cell("role"), cell("description"), cell("architec
ture"), cell("status")],
    [cell("ooksdesk"), cell("7500F / RX5700XT / 32 GB DDR5"), cell("Workstation"),
 cell("Primary desktop workstation"), cell("x86_64"), cell("UP")],
    [cell("ooknode"), cell("Linode Nanode"), cell("Server"), cell("VPS for website
"), cell("x86_64"), cell("UP")],
  ];

  it("fits any column count at the narrow width", () => {
    const t = buildTransposedTable(rows, 1, 48);
    const widths = new Set(t.split("\n").map(len));
    expect(widths.size).toBe(1);
    expect([...widths][0]).toBe(48);
  });

  it("keys each row block by the header, separated by dashed rules", () => {
    const t = buildTransposedTable(rows, 1, 48);
    expect(t.match(/│ host /g)?.length).toBe(2);
    expect(t.match(/├╌+┤/g)?.length).toBe(1);
    expect(t).toContain("ooksdesk");
    expect(t).toContain("Linode Nanode");
  });

  it("wraps long values within the value column", () => {
    const t = buildTransposedTable(rows, 1, 48);
    expect(t).toMatch(/│ spec\s+7500F \/ RX5700XT \//);
  });

  it("hard-splits unbreakable cells instead of overflowing", () => {
    const long = [[cell("k")], [cell("x".repeat(120))]];
    const t = buildTransposedTable(long, 1, 30);
    for (const line of t.split("\n")) expect(len(line)).toBe(30);
  });
});

import { describe, expect, it } from "vitest
";
import { buildAsciiTable, buildTransposedTab
le, type Cell } from "./ascii-table";
import { len } from "./ascii";

const cell = (text: string, align: Cell["ali
gn"] = "end"): Cell => ({ text, align });

const HEADER = [cell("NAME"), cell("VALUE")]
;

describe("buildAsciiTable", () => {
  it("emits every line at the same width, wi
thin the target", () => {
    const t = buildAsciiTable([HEADER, [cell
("a"), cell("b")]], 1, 40);
    const widths = new Set(t.split("\n").map
(len));
    expect(widths.size).toBe(1);
    expect([...widths][0]).toBeLessThanOrEqu
al(40);
  });

  it("keeps single-line tables compact (no r
ow separators)", () => {
    const t = buildAsciiTable([HEADER, [cell
("a"), cell("b")], [cell("c"), cell("d")]], 
1, 40);
    expect(t).not.toContain("╌");
    // one solid separator under the header 
only
    expect(t.split("\n").filter((l) => l.sta
rtsWith("├")).length).toBe(1);
  });

  it("adds dashed separators between body ro
ws once any cell wraps", () => {
    const long = "a description long enough 
that it must wrap onto several lines";
    const t = buildAsciiTable(
      [HEADER, [cell("frame"), cell(long)], 
[cell("select"), cell("short")]],
      1,
      30,
    );
    expect(t).toContain("╌");
    const dashed = t.split("\n").filter((l) 
=> l.includes("╌"));
    expect(dashed.length).toBe(1); // betwee
n the two body rows
  });

  it("right-aligns cells marked align start"
, () => {
    const t = buildAsciiTable([HEADER, [cell
("x"), cell("42", "start")]], 1, 40);
    const row = t.split("\n").find((l) => l.
includes("42"))!;
    expect(row).toMatch(/ 42 │$/);
  });

  it("shrinks the widest column until the ta
ble fits", () => {
    const wide = "word ".repeat(30).trim();
    const t = buildAsciiTable([HEADER, [cell
("k"), cell(wide)]], 1, 48);
    for (const line of t.split("\n")) expect
(len(line)).toBeLessThanOrEqual(48);
  });

  it("pads ragged rows to the full column co
unt", () => {
    const t = buildAsciiTable([HEADER, [cell
("only-one")]], 1, 40);
    const widths = new Set(t.split("\n").map
(len));
    expect(widths.size).toBe(1);
  });
});

describe("buildTransposedTable", () => {
  const rows = [
    [cell("host"), cell("spec"), cell("role"
), cell("description"), cell("architecture")
, cell("status")],
    [cell("ooksdesk"), cell("7500F / RX5700X
T / 32 GB DDR5"), cell("Workstation"), cell(
"Primary desktop workstation"), cell("x86_64
"), cell("UP")],
    [cell("ooknode"), cell("Linode Nanode"),
 cell("Server"), cell("VPS for website"), ce
ll("x86_64"), cell("UP")],
  ];

  it("fits any column count at the narrow wi
dth", () => {
    const t = buildTransposedTable(rows, 1, 
48);
    const widths = new Set(t.split("\n").map
(len));
    expect(widths.size).toBe(1);
    expect([...widths][0]).toBe(48);
  });

  it("keys each row block by the header, sep
arated by dashed rules", () => {
    const t = buildTransposedTable(rows, 1, 
48);
    expect(t.match(/│ host /g)?.length).toBe
(2);
    expect(t.match(/├╌+┤/g)?.length).toBe(1)
;
    expect(t).toContain("ooksdesk");
    expect(t).toContain("Linode Nanode");
  });

  it("wraps long values within the value col
umn", () => {
    const t = buildTransposedTable(rows, 1, 
48);
    expect(t).toMatch(/│ spec\s+7500F \/ RX5
700XT \//);
  });

  it("hard-splits unbreakable cells instead 
of overflowing", () => {
    const long = [[cell("k")], [cell("x".rep
eat(120))]];
    const t = buildTransposedTable(long, 1, 
30);
    for (const line of t.split("\n")) expect
(len(line)).toBe(30);
  });
});
 
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET