ssi-patterns / ssi-pattern-connections-visualization / test / e2e / suites.ts
suites.ts
Raw
import { Page } from "puppeteer";
import Graph, { GraphConstructor } from "graphology-types";

import Sigma from "../../src";
import { NodeDisplayData, EdgeDisplayData } from "../../src/types";

type TestDependencies = {
  Graph: GraphConstructor;
  Sigma: typeof Sigma;
  data: { [key: string]: Graph };
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  programs: { [key: string]: any };
  container: HTMLElement;
};

declare global {
  const dependencies: TestDependencies;
}

export type Tests = Array<{
  name: string; // Name of the screenshot, without the extension like for example 'example-basic'
  waitFor?: number; // Time to wait in ms before to take the screenshot
  scenario: (page: Page) => Promise<void>;
  failureThreshold?: number; // between 0 and 1, it's a percent. By default it's a small epsilon.
  dimensions?: { width: number; height: number };
}>;

export const tests: Tests = [
  {
    name: "single-node",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const { Graph, Sigma, container } = dependencies;

        const graph = new Graph();
        graph.addNode("test", { x: 0, y: 0, size: 10, color: "#1E90FF" });

        new Sigma(graph, container);
      });
    },
  },
  {
    name: "square",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const { Graph, Sigma, container } = dependencies;

        const graph = new Graph();
        graph.addNode("upper-left", { x: 0, y: 0, size: 10, label: "upper left" });
        graph.addNode("upper-right", { x: 10, y: 0, size: 10, label: "upper right" });
        graph.addNode("lower-left", { x: 0, y: 10, size: 10, label: "lower left" });
        graph.addNode("lower-right", { x: 10, y: 10, size: 10, label: "lower right" });

        graph.addEdge("upper-left", "upper-right", { type: "arrow", size: 5, label: "right" });
        graph.addEdge("upper-right", "lower-right", { type: "arrow", size: 5, label: "down" });
        graph.addEdge("lower-right", "lower-left", { type: "arrow", size: 5, label: "left" });
        graph.addEdge("lower-left", "upper-left", { type: "arrow", size: 5, label: "up" });

        graph.addEdge("upper-left", "lower-right", { color: "#f00" });
        graph.addEdge("upper-right", "lower-left", { color: "#f00" });

        new Sigma(graph, container, { renderEdgeLabels: true, labelRenderedSizeThreshold: -Infinity });
      });
    },
  },
  {
    name: "aspect-ratio-vertical-graph-horizontal-container",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const { Graph, Sigma, container } = dependencies;

        const graph = new Graph();
        graph.addNode("upper-left", { x: 0, y: 0, size: 10 });
        graph.addNode("upper-right", { x: 5, y: 0, size: 10 });
        graph.addNode("lower-left", { x: 0, y: 10, size: 10 });
        graph.addNode("lower-right", { x: 5, y: 10, size: 10 });

        graph.addEdge("upper-left", "lower-right", { size: 5, color: "#F00" });
        graph.addEdge("upper-right", "lower-left", { size: 5, color: "#F00" });

        new Sigma(graph, container);
      });
    },
    dimensions: { width: 800, height: 400 },
  },
  {
    name: "aspect-ratio-horizontal-graph-horizontal-container",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const { Graph, Sigma, container } = dependencies;

        const graph = new Graph();
        graph.addNode("upper-left", { x: 0, y: 0, size: 10 });
        graph.addNode("upper-right", { x: 10, y: 0, size: 10 });
        graph.addNode("lower-left", { x: 0, y: 5, size: 10 });
        graph.addNode("lower-right", { x: 10, y: 5, size: 10 });

        graph.addEdge("upper-left", "lower-right", { size: 5, color: "#F00" });
        graph.addEdge("upper-right", "lower-left", { size: 5, color: "#F00" });

        new Sigma(graph, container);
      });
    },
    dimensions: { width: 800, height: 400 },
  },
  {
    name: "aspect-ratio-horizontal-graph-vertical-container",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const { Graph, Sigma, container } = dependencies;

        const graph = new Graph();
        graph.addNode("upper-left", { x: 0, y: 0, size: 10 });
        graph.addNode("upper-right", { x: 10, y: 0, size: 10 });
        graph.addNode("lower-left", { x: 0, y: 5, size: 10 });
        graph.addNode("lower-right", { x: 10, y: 5, size: 10 });

        graph.addEdge("upper-left", "lower-right", { size: 5, color: "#F00" });
        graph.addEdge("upper-right", "lower-left", { size: 5, color: "#F00" });

        new Sigma(graph, container);
      });
    },
    dimensions: { width: 400, height: 800 },
  },
  {
    name: "aspect-ratio-vertical-graph-vertical-container",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const { Graph, Sigma, container } = dependencies;

        const graph = new Graph();
        graph.addNode("upper-left", { x: 0, y: 0, size: 10 });
        graph.addNode("upper-right", { x: 5, y: 0, size: 10 });
        graph.addNode("lower-left", { x: 0, y: 10, size: 10 });
        graph.addNode("lower-right", { x: 5, y: 10, size: 10 });

        graph.addEdge("upper-left", "lower-right", { size: 5, color: "#F00" });
        graph.addEdge("upper-right", "lower-left", { size: 5, color: "#F00" });

        new Sigma(graph, container);
      });
    },
    dimensions: { width: 400, height: 800 },
  },
  {
    name: "settings",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const { Graph, Sigma, container } = dependencies;

        const graph = new Graph();

        graph.addNode("John", { x: 6, y: 4, size: 10 });
        graph.addNode("Mary", { x: 4, y: 2, size: 10 });
        graph.addNode("Sue", { x: 4, y: 6, size: 10 });

        graph.addEdge("John", "Mary", { size: 5 });
        graph.addEdge("Mary", "Sue", { size: 5 });
        graph.addEdge("Sue", "John", { size: 5 });

        new Sigma(graph, container, { defaultNodeColor: "#7FFFD4", defaultEdgeColor: "#AA4A44" });
      });
    },
  },
  {
    name: "les-miserables",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const {
          data: { lesMiserables },
          Sigma,
          container,
        } = dependencies;

        new Sigma(lesMiserables, container);
      });
    },
  },
  {
    name: "arctic",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const {
          data: { arctic },
          Sigma,
          container,
        } = dependencies;

        new Sigma(arctic, container);
      });
    },
  },
  {
    name: "camera-state-unzoom-pan",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const {
          data: { lesMiserables },
          Sigma,
          container,
        } = dependencies;

        const renderer = new Sigma(lesMiserables, container);
        renderer.getCamera().setState({ ratio: 3, x: 0.8, y: 0.7 });
      });
    },
  },
  {
    name: "camera-state-zoom-pan",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const {
          data: { lesMiserables },
          Sigma,
          container,
        } = dependencies;

        const renderer = new Sigma(lesMiserables, container);
        renderer.getCamera().setState({ ratio: 1 / 3, x: 0.8, y: 0.7 });
      });
    },
  },
  {
    name: "camera-state-rotation",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const {
          data: { arctic },
          Sigma,
          container,
        } = dependencies;

        const renderer = new Sigma(arctic, container);
        renderer.getCamera().setState({ angle: 30 });
      });
    },
  },
  {
    name: "reducers",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const {
          data: { lesMiserables },
          Sigma,
          container,
        } = dependencies;

        const nodeReducer = (key: string, attr: Partial<NodeDisplayData>) => {
          const data = attr as NodeDisplayData;
          return Object.assign({}, data, { color: (data.label || "").charCodeAt(0) % 2 === 0 ? "#1E90FF" : "#FF0000" });
        };

        const edgeReducer = (key: string, attr: Partial<EdgeDisplayData>) => {
          const data = attr as EdgeDisplayData;
          return Object.assign({}, data, { color: +key % 2 === 0 ? "#FFFF00" : "#008000" });
        };

        new Sigma(lesMiserables, container, { nodeReducer, edgeReducer });
      });
    },
  },
  // {
  //   name: "les-miserables-mouse-wheel",
  //   waitFor: 2000,
  //   scenario: async (page: Page): Promise<void> => {
  //     await page.evaluate(() => {
  //       const {
  //         data: { lesMiserables },
  //         Sigma,
  //         container,
  //       } = dependencies;

  //       new Sigma(lesMiserables, container);

  //       const element = document.getElementsByClassName("sigma-mouse")[0];
  //       const cEvent: Event & { clientX?: number; clientY?: number; deltaY?: number } = new Event("wheel");
  //       cEvent.clientX = 0;
  //       cEvent.clientY = 0;
  //       cEvent.deltaY = -100;
  //       element.dispatchEvent(cEvent);
  //     });
  //   },
  // },
  {
    name: "node-edge-state",
    waitFor: 2000,
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const { Graph, Sigma, container } = dependencies;

        const graph = new Graph({ type: "directed" });

        graph.addNode("Alice", {
          label: "Alice",
          x: -2,
          y: 1,
          color: "#FF0",
          size: 10,
        });

        graph.addNode("Bob", {
          label: "Bob",
          x: 1,
          y: 2,
          color: "#00F",
          size: 5,
        });

        graph.addNode("Charles", {
          label: "Charles",
          x: 2,
          y: -1,
          color: "#00F",
          size: 5,
        });

        graph.addNode("Deborah", {
          label: "Deborah",
          x: -1,
          y: -2,
          color: "#00F",
          size: 5,
        });

        graph.addEdge("Alice", "Bob", {
          label: "likes to play with",
          size: 1,
        });

        graph.addEdge("Bob", "Charles", {
          label: "likes to be with",
          color: "#fc0",
          size: 2,
        });

        graph.addEdge("Charles", "Deborah", {
          label: "likes to talk with",
          color: "#CCC",
          size: 3,
        });

        graph.addEdge("Deborah", "Alice", {
          label: "likes to talk with",
          color: "#000",
          size: 20,
        });

        new Sigma(graph, container, {
          defaultEdgeType: "arrow",
          defaultEdgeColor: "#888",
          renderEdgeLabels: true,
        });

        graph.setNodeAttribute("Alice", "highlighted", true);
        graph.setNodeAttribute("Bob", "size", 50);
        graph.setNodeAttribute("Bob", "color", "#FF0000");
        graph.setNodeAttribute("Deborah", "hidden", true);
        graph.setEdgeAttribute("Alice", "Bob", "hidden", true);
      });
    },
  },
  {
    name: "programs",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const { Graph, Sigma, container, programs } = dependencies;
        const {
          NodeProgram,
          NodeFastProgram,
          getNodeImageProgram,
          EdgeProgram,
          EdgeFastProgram,
          EdgeArrowProgram,
          EdgeTriangleProgram,
        } = programs;

        const graph = new Graph();
        graph.addNode("n1", { x: 30, y: 120, size: 15, label: "Node 1", type: "node", color: "#ffcc00" });
        graph.addNode("n2", { x: 120, y: -30, size: 15, label: "Node 2", type: "fast", color: "#00ffcc" });
        graph.addNode("n3", { x: -30, y: -120, size: 15, label: "Node 3", type: "image", color: "#cc00ff" });
        graph.addNode("n4", {
          x: -120,
          y: 30,
          size: 15,
          label: "Node 4",
          type: "image",
          image:
            "data:image/png;base64,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",
          color: "#ccff00",
        });

        graph.addEdge("n1", "n2", { type: "edge", size: 30 });
        graph.addEdge("n2", "n3", { type: "fast", size: 30 });
        graph.addEdge("n3", "n4", { type: "arrow", size: 30 });
        graph.addEdge("n4", "n1", { type: "triangle", size: 30 });

        new Sigma(graph, container, {
          nodeProgramClasses: {
            node: NodeProgram,
            fast: NodeFastProgram,
            image: getNodeImageProgram(),
          },
          edgeProgramClasses: {
            edge: EdgeProgram,
            fast: EdgeFastProgram,
            arrow: EdgeArrowProgram,
            triangle: EdgeTriangleProgram,
          },
        });
      });
    },
  },
  {
    name: "force-labels",
    scenario: async (page: Page): Promise<void> => {
      await page.evaluate(() => {
        const { Graph, Sigma, container } = dependencies;

        const graph = new Graph();
        graph.addNode("upper-left", { x: 0, y: 0, size: 5, label: "upper left", forceLabel: true });
        graph.addNode("upper-right", { x: 10, y: 0, size: 5, label: "upper right", forceLabel: true });
        graph.addNode("lower-left", { x: 0, y: 10, size: 5, label: "lower left" });
        graph.addNode("lower-right", { x: 10, y: 10, size: 15, label: "lower right" });

        graph.addEdge("upper-left", "upper-right", { type: "arrow", size: 5, label: "right" });
        graph.addEdge("upper-right", "lower-right", { type: "arrow", size: 5, label: "down" });
        graph.addEdge("lower-right", "lower-left", { type: "arrow", size: 5, label: "left", forceLabel: true });
        graph.addEdge("lower-left", "upper-left", { type: "arrow", size: 5, label: "up", forceLabel: true });

        new Sigma(graph, container, { renderEdgeLabels: true, labelRenderedSizeThreshold: 10 });
      });
    },
  },
];