view src/layout/Layout.test.ts @ 118:c2923b20bf5c

support multi labels per column
author drewp@bigasterisk.com
date Sun, 20 Mar 2022 00:54:19 -0700
parents dd3325cc023e
children 8715633f5213
line wrap: on
line source

import { Quad, Store, Term } from "n3";
import { n3Graph } from "./fetchAndParse";
import { AlignedTable, Layout, LayoutResult } from "./Layout";
import { EX, rdf } from "./namespaces";
import { ViewConfig } from "./ViewConfig";

const twoStatements = async (): Promise<Store> => {
  return n3Graph(`
  @prefix : <http://example.com/> .
  :g1 {
    :a0 :b0 :c0 .
    :d0 :e0 :f0 .
  }
  `);
};

const typedStatements = async (): Promise<Store> => {
  return n3Graph(`
  @prefix : <http://example.com/> .
  :g1 {
    :a a :T1 ; :color :red .
    :b a :T1 ; :color :blue .
    :c a :T1 .
    :d a :T2 ; :size :big .
    :e a :T1,:T2; :size :small
  }
  `);
};
function G1(s: Term, p: Term, o: Term): Quad {
  return new Quad(s, p, o, EX("g1"));
}

describe("Layout", () => {
  it("accepts a ViewConfig", async () => {
    const vc = new ViewConfig();
    await vc.readFromGraph(`
      @prefix ex: <http://example.com/> .
      @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

      <> a ex:View; rdfs:label "repos" .`);
    const layout = new Layout(vc);
    const lr = layout.plan(await twoStatements());
  });
  it("returns no sections for empty graph", () => {
    const vc = new ViewConfig();
    const layout = new Layout(vc);
    const lr = layout.plan(new Store());
    expect(lr.sections).toHaveLength(0);
  });
  it("defaults to putting all triples in the ungrouped list", async () => {
    const layout = new Layout();
    const lr = layout.plan(await twoStatements());
    expect(lr).toEqual({
      sections: [
        {
          subjRows: [
            {
              subj: EX("a0"),
              predRows: [{ pred: EX("b0"), objs: [EX("c0")] }],
            },
            {
              subj: EX("d0"),
              predRows: [{ pred: EX("e0"), objs: [EX("f0")] }],
            },
          ],
        },
      ],
    });
  });
  describe("makes a table as requested by ViewConfig", () => {
    let lr: LayoutResult;

    beforeAll(async () => {
      const vc = new ViewConfig();
      await vc.readFromGraph(`
        @prefix : <http://example.com/> .
        @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
  
        <> a :View; :table [ :primaryType :T1 ] .`);
      const layout = new Layout(vc);
      lr = layout.plan(await typedStatements());
    });
    it("returns 2 sections", () => {
      expect(lr.sections).toHaveLength(2);
    });
    it("puts the right type in the table", async () => {
      const section0 = lr.sections[0] as AlignedTable;
      expect(section0.columnHeaders).toEqual([
        { rdfTypes: [EX("T1")], pred: EX("color") },
        { rdfTypes: [EX("T1"), EX("T2")], pred: EX("size") },
        // and doesn't include rdf:type as a column header here
      ]);
      expect(section0.rowHeaders).toEqual([EX("a"), EX("b"), EX("c"), EX("e")]);
      expect(section0.rows).toEqual([
        [[EX("red")], []],
        [[EX("blue")], []],
        [[], []],
        [[], [EX("small")]],
      ]);
    });
    it("leaves the rest ungrouped", async () => {
      expect(lr.sections[1]).toEqual({
        subjRows: [
          {
            subj: EX("d"),
            predRows: [
              { pred: EX("size"), objs: [EX("big")] },
              { pred: rdf.type, objs: [EX("T2")] },
            ],
          },
        ],
      });
    });
  });
  it("makes two tables", async () => {
    const vc = new ViewConfig();
    await vc.readFromGraph(`
      @prefix ex: <http://example.com/> .
      @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
      @prefix : <http://example.com/> .

      <> a :View; :table [ :primaryType :T1 ], [ :primaryType :T2 ] .`);
    const layout = new Layout(vc);
    const lr = layout.plan(await typedStatements());
    expect(lr.sections).toHaveLength(2);
    const section0 = lr.sections[0] as AlignedTable;
    expect(section0.columnHeaders).toEqual([
      { rdfTypes: [EX("T1")], pred: EX("color") },
      { rdfTypes: [EX("T1"), EX("T2")], pred: EX("size") },
    ]);
    expect(section0.rowHeaders).toEqual([EX("a"), EX("b"), EX("c"), EX("e")]);
    expect(section0.rows).toEqual([
      [[EX("red")], []],
      [[EX("blue")], []],
      [[], []],
      [[], [EX("small")]],
    ]);
    const section1 = lr.sections[1] as AlignedTable;
    expect(section1.columnHeaders).toEqual([
      { rdfTypes: [EX("T1"), EX("T2")], pred: EX("size") },
    ]);
    expect(section1.rowHeaders).toEqual([EX("d"), EX("e")]);
    expect(section1.rows).toEqual([
      [[EX("big")]], //
      [[EX("small")]],
    ]);
  });
  describe("joins multiple types into one table", () => {
    it("can simply merge types", async () => {
      const vc = new ViewConfig();
      await vc.readFromGraph(`
        @prefix ex: <http://example.com/> .
        @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
        @prefix : <http://example.com/> .

        <> a :View; :table [ :primaryType :T1; :joinType :T2 ] .
      `);
      vc.graph.forEach((q) => console.log("vc", q), null, null, null, null);
      const layout = new Layout(vc);
      const lr = layout.plan(
        await n3Graph(`
        @prefix : <http://example.com/> .
        :g1 {
          :a a :T1 ; :color :red .
          :b a :T2 ; :size :big .
        }
      `)
      );
      expect(lr.sections).toHaveLength(1);
      const section0 = lr.sections[0] as AlignedTable;
      expect(section0.columnHeaders).toEqual([
        { rdfTypes: [EX("T1")], pred: EX("color") },
        { rdfTypes: [EX("T2")], pred: EX("size") },
      ]);
      expect(section0.rowHeaders).toEqual([EX("a"), EX("b")]);
      expect(section0.rows).toEqual([
        [[EX("red")], []],
        [[], [EX("big")]],
      ]);
    });
  });
  it.skip("makes a table out of ungrouped triples with the same type", async () => {});
});