Testing GoJS apps

The Robot extension is useful for pretending to interact with a Diagram without actually generating any keyboard or mouse or pointer events. See the sample at Simulating Input with Robot.

One convenient feature of the Robot class is that it uses document coordinates, so that changes in the viewport size or diagram content alignment, scroll position, or zoom level will not affect the calls.

Testing with Jest

Testing with Jest depends on installing: jest, puppeteer, and jest-puppeteer.


// jest.config.js
module.exports = {
  preset: "jest-puppeteer"
};

// An example Jest test script for testing the unmodified Minimal sample,
// which is available at https://gojs.net/latest/samples/minimal.html.
// Copyright 1998-2025 by Northwoods Software Corporation

describe("minimal", () => {
  beforeAll(async () => {
    await page.goto('https://gojs.net/latest/samples/minimal.html');
  });

  it('should show 4 nodes and 5 links', async () => {
    expect(await page.$("#myDiagramDiv")).not.toBeNull();

    expect(await page.waitForFunction(() => {
      const myDiagram = document.getElementById("myDiagramDiv").goDiagram;
      return myDiagram.nodes.count === 4 &&
              myDiagram.links.count === 5;
    })).toBeTruthy();
  });

  it('show that the Beta node is orange', async () => {
    expect(await page.waitForFunction(() => {
      const myDiagram = document.getElementById("myDiagramDiv").goDiagram;
      const beta = myDiagram.findNodeForKey("Beta");
      return beta !== null && beta.data.color === "orange";
    })).toBeTruthy();
  });

  it('demonstrates deleting a node', async () => {
    expect(await page.waitForFunction(() => {
      const myDiagram = document.getElementById("myDiagramDiv").goDiagram;
      const beta = myDiagram.findNodeForKey("Beta");
      myDiagram.select(beta);
      myDiagram.commandHandler.deleteSelection();
      return true;
    })).toBeTruthy();

    expect(await page.waitForFunction(() => {
      const myDiagram = document.getElementById("myDiagramDiv").goDiagram;
      return myDiagram.nodes.count === 3 &&
              myDiagram.links.count === 3 &&
              myDiagram.findNodeForKey("Beta") === null;
    })).toBeTruthy();
  });

});

Testing with Jest in React

In our GoJS React samples, we have an example of using the gojs-react component with Jest: gojs-react-jest.

Testing with Cypress


// An example Cypress test script for testing the unmodified Minimal sample,
// which is available at https://gojs.net/latest/samples/minimal.html.
// Copyright 1998-2025 by Northwoods Software Corporation

describe("minimal", () => {
  let myDiagram = null
  let myRobot = null

  beforeEach(() => {
    // more likely you would be running the sample locally,
    // say at http://localhost:5500/samples/minimal.html
    cy.visit("https://gojs.net/latest/samples/minimal.html")

    // make sure the HTMLDivElement exists holding the Diagram
    cy.get("#myDiagramDiv")

    // load extensions/Robot.js dynamically
    cy.window().then(win => {
      const scr = win.document.createElement("script");
      scr.src = "https://cdn.jsdelivr.net/npm/create-gojs-kit/dist/extensions/Robot.js";
      win.document.body.appendChild(scr);
    })

    // make sure it's loaded
    cy.window().should("have.property", "Robot")

    // save these references for each test, which simplifies each test code
    cy.window().then(win => {
      myDiagram = win.go.Diagram.fromDiv(win.document.getElementById("myDiagramDiv"));
      myRobot = new win.Robot(myDiagram);
    })

    // wait for the Diagram to finish initializing; is there a better way?
    cy.wait(1)
  })

  // A minimal test to make sure the Diagram got set up OK.
  it("has nodes and links", () => {
    cy.window().then(win => {
      expect(myDiagram.nodes.count).to.equal(4)
      expect(myDiagram.links.count).to.equal(5)
      const delta = myDiagram.findNodeForKey("Delta");
      expect(delta).to.not.equal(null)
      expect(delta.elt(0).fill).to.equal("pink")
    })
  })

  // A test that uses Robot to simulate input by producing InputEvents.
  it("copies a node with Robot", () => {
    cy.window().then(win => {
      // Find the center of the Delta node in document coordinates.
      const delta = myDiagram.findNodeForKey("Delta");
      const loc = delta.actualBounds.center;

      // Simulate a mouse drag to move the Delta node:
      const options = { control: true, alt: true };
      myRobot.mouseDown(loc.x, loc.y, 0, options);
      myRobot.mouseMove(loc.x + 80, loc.y + 50, 50, options);
      myRobot.mouseMove(loc.x + 20, loc.y + 100, 100, options);
      myRobot.mouseUp(loc.x + 20, loc.y + 100, 150, options);
      // If successful, this will have made a copy of the "Delta" node below it.

      // Check that the new node is at the drop point,
      // and that it is a copy, different from the original node.
      const newloc = new win.go.Point(loc.x + 20, loc.y + 100);
      const newnode = myDiagram.findPartAt(newloc);
      expect(newnode).to.not.equal(delta)
      expect(newnode.key).to.equal("Delta2")
      expect(newnode.isSelected).to.equal(true)
    })
  })

})

Testing with Playwright


// @ts-check
import { test, expect } from '@playwright/test';
import * as go from 'gojs';  // just for go.Point.parse

// First make sure you install gojs and create-gojs-kit:
// $ npm i gojs
// $ npm i create-gojs-kit

// $ npx playwright test

// Then your tests can open your web app and evaluate JavaScript test code on the page.
// Optionally you can load the Robot extension and simulate mouse events in the diagram.

test.describe('minimal tests', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('https://gojs.net/latest/samples/minimal.html');
    await expect(page.locator('#myDiagramDiv')).toBeVisible();
  });

  // This selects node "Beta" (key: 2) and then copies and pastes.
  // This should add both one Node and one Link to the Diagram.
  // The paste is performed a bit away from where the nodes are, and that is checked too.
  test('copy node', async ({ page }) => {
    const numNodesLinks = await page.evaluate(() => {
      const div = document.getElementById('myDiagramDiv');
      if (!div) return;
      const diagram = go.Diagram.fromDiv(div);
      if (!diagram) return;
      const node2 = diagram.findNodeForKey(2);
      if (!node2) return;
      diagram.select(node2);
      diagram.commandHandler.copySelection();
      diagram.commandHandler.pasteSelection(new go.Point(150, -50));
      const newnode = diagram.selection.first();
      return [diagram.nodes.count, diagram.links.count, newnode ? go.Point.stringify(newnode.location) : ''];
    });
    expect(Array.isArray(numNodesLinks)).toBe(true)
    expect(numNodesLinks[0]).toBe(5);
    expect(numNodesLinks[1]).toBe(6);
    // Note that this is executing the static Point.parse function in the test environment, not on the page.
    const pt = go.Point.parse(numNodesLinks[2]);
    expect(pt.x).toBeCloseTo(124, 0);
    expect(pt.y).toBeCloseTo(-68, 0);
  });

  // This uses the Robot extension to simulate mouse events dragging node "Beta" (key: 2).
  // It checks the new location.
  test('drag node', async ({ page }) => {
    await page.addScriptTag({ path: 'node_modules/create-gojs-kit/dist/extensions/Robot.js' });
    const newloc = await page.evaluate(() => {
      const div = document.getElementById('myDiagramDiv');
      if (!div) return;
      const diagram = go.Diagram.fromDiv(div);
      if (!diagram) return;
      const robot = new Robot(diagram);
      if (!robot) return;
      const node2 = diagram.findNodeForKey(2);
      if (!node2) return;
      const loc2 = node2.actualBounds.center;
      robot.mouseDown(loc2.x, loc2.y, 0);
      robot.mouseMove(loc2.x-10, loc2.y-20, 100);
      robot.mouseMove(loc2.x-20, loc2.y-30, 200);
      robot.mouseUp(loc2.x-25, loc2.y-50, 300);
      return go.Point.stringify(node2.location);
    });
    if (typeof newloc === 'string') {
      const pt = go.Point.parse(newloc);
      expect(pt.x).toBeCloseTo(55, 0);
      expect(pt.y).toBeCloseTo(-50, 0);
    }
  });
});