UNPKG

create-gojs-kit

Version:

A CLI for downloading GoJS samples, extensions, and docs

698 lines (625 loc) 26.8 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/> <link rel="preconnect" href="https://rsms.me/"> <link rel="stylesheet" href="../assets/css/style.css"> <!-- Copyright 1998-2025 by Northwoods Software Corporation. --> <meta itemprop="name" content="Testing GoJS Apps" /> <meta property="og:title" content="Testing GoJS Apps" /> <meta name="twitter:title" content="Testing GoJS Apps" /> <meta property="og:image" content="https://gojs.net/latest/assets/images/fp/defaultCard.png" /> <meta itemprop="image" content="https://gojs.net/latest/assets/images/fp/defaultCard.png" /> <meta name="twitter:image" content="https://gojs.net/latest/assets/images/fp/defaultCard.png" /> <meta property="og:url" content="https://gojs.net/latest/intro/testing.html" /> <meta property="twitter:url" content="https://gojs.net/latest/intro/testing.html" /> <meta name="twitter:card" content="summary_large_image" /> <meta property="og:type" content="website" /> <meta property="twitter:domain" content="gojs.net" /> <title> Testing GoJS Apps | GoJS </title> <link rel="stylesheet" href="../assets/css/prism.css"/> </head> <script> window.diagrams = []; window.goCode = function (pre, w, h, parentid, animation) { window .diagrams .push([pre, w, h, parentid, animation]); } </script> <body> <nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200"> <div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4"> <a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo" href="../"> GoJS </a> <div class="relative"> <button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation"> <svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> <div id="topnavList" class="hidden md:block"> <div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit"> <a href="../learn/">Learn</a> <a href="../samples/">Samples</a> <a href="../intro/">Intro</a> <a href="../api/">API</a> <a href="../download.html">Download</a> <a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a> <a id="tc" href="https://nwoods.com/contact.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a> <a id="tb" href="https://nwoods.com/sales/index.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a> </div> </div> </div> </div> </nav> <script> window.addEventListener("DOMContentLoaded", function () { // topnav var topButton = document.getElementById("topnavButton"); var topnavList = document.getElementById("topnavList"); if (topButton && topnavList) { topButton.addEventListener("click", function (e) { topnavList .classList .toggle("hidden"); e.stopPropagation(); }); document.addEventListener("click", function (e) { // if the clicked element isn't the list, close the list if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) { topButton.click(); } }); // set active <a> element var url = window .location .href .toLowerCase(); var aTags = topnavList.getElementsByTagName('a'); for (var i = 0; i < aTags.length; i++) { var lowerhref = aTags[i] .href .toLowerCase(); if (lowerhref.endsWith('.html')) lowerhref = lowerhref.slice(0, -5); if (url.startsWith(lowerhref)) { aTags[i] .classList .add('active'); break; } } } }); </script> <div class="sticky top-0 left-0 z-10 px-2 w-full bg-white border-b border-b-gray-200 md:hidden"> <button id="sidenavButton" class="flex p-2 text-gray-900 bg-inherit shadow-none items-center text-sm font-semibold hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation"> <svg class="h-7 w-7 block mr-2" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> <span>Menu</span> </button> </div> <script> window.addEventListener("DOMContentLoaded", function () { // sidenav var sideButton = document.getElementById("sidenavButton"); var sidenav = document.getElementById("sidenav"); if (sideButton && sidenav) { sideButton.addEventListener("click", function (e) { sidenav .classList .toggle("hidden"); e.stopPropagation(); }); document.addEventListener("click", function (e) { // if the clicked element isn't the list, close the list if (!sidenav.classList.contains("hidden") && !e.target.closest("#sidenavList")) { sideButton.click(); } }); } }); </script> <div class="flex flex-row md:min-h-screen w-full max-w-screen-xl mx-auto"> <aside id="sidenav" class="hidden fixed top-0 left-0 z-10 w-full bg-black/10 min-h-screen max-h-screen overflow-x-hidden overflow-y-auto shrink-0 md:block md:sticky md:w-52 md:min-h-0 md:bg-inherit md:border-r md:border-r-gray-200 md:overscroll-auto"> <nav id="sidenavList" class="flex flex-col bg-white w-52 min-h-screen pl-2 pt-4 pb-24 md:w-full md:min-h-0"> <a href="index.html">Basics</a> <a href="buildingObjects.html">Building Parts</a> <a href="usingModels.html">Using Models</a> <a href="dataBinding.html">Data Binding</a> <a href="react.html">GoJS with React</a> <a href="svelte.html">GoJS with Svelte</a> <a href="angular.html">GoJS with Angular</a> <a href="textBlocks.html">TextBlocks</a> <a href="shapes.html">Shapes</a> <a href="pictures.html">Pictures</a> <a href="panels.html">Panels</a> <a href="tablePanels.html">Table Panels</a> <a href="brush.html">Brushes</a> <a href="sizing.html">Sizing Objects</a> <a href="itemArrays.html">Item Arrays</a> <a href="changedEvents.html">Changed Events</a> <a href="transactions.html">Transactions</a> <a href="viewport.html">Coordinates</a> <a href="initialView.html">Initial View</a> <a href="collections.html">Collections</a> <a href="links.html">Links</a> <a href="linkLabels.html">Link Labels</a> <a href="connectionPoints.html">Link Points</a> <a href="ports.html">Ports</a> <a href="nodes.html">Nodes</a> <a href="typings.html">Typings</a> <a href="debugging.html">Debugging</a> <a href="layouts.html">Layouts</a> <a href="routers.html">Routers</a> <a href="trees.html">Trees</a> <a href="subtrees.html">SubTrees</a> <a href="groups.html">Groups</a> <a href="subgraphs.html">SubGraphs</a> <a href="sizedGroups.html">Sized Groups</a> <a href="selection.html">Selection</a> <a href="highlighting.html">Highlighting</a> <a href="theming.html">Theming</a> <a href="tooltips.html">ToolTips</a> <a href="contextMenus.html">Context Menus</a> <a href="events.html">Diagram Events</a> <a href="tools.html">Tools</a> <a href="commands.html">Commands</a> <a href="accessibility.html">Accessibility</a> <a href="buttons.html">Buttons</a> <a href="permissions.html">Permissions</a> <a href="validation.html">Validation</a> <a href="animation.html">Animation</a> <a href="HTMLInteraction.html">HTML Interaction</a> <a href="layers.html">Layers &amp; Z-ordering</a> <a href="palette.html">Palette</a> <a href="overview.html">Overview</a> <a href="replacingDeleting.html">Replacing and Deleting</a> <a href="templateMaps.html">Template Maps</a> <a href="legends.html">Legends and Titles</a> <a href="extensions.html">Extensions</a> <a href="geometry.html">Geometry Strings</a> <a href="grids.html">Grid Patterns</a> <a href="graduatedPanels.html">Graduated Panels</a> <a href="SVGContext.html">Rendering to SVG</a> <a href="makingSVG.html">Snapshot to SVG</a> <a href="makingImages.html">Diagram Images</a> <a href="printing.html">Printing</a> <a href="serverSideImages.html">Server-side Images</a> <a href="nodeScript.html">GoJS in Node.js</a> <a href="testing.html">Testing</a> <a href="performance.html">Performance</a> <a href="platforms.html">Platforms</a> <a href="deployment.html">Deployment</a> </nav> </aside> <script> var navList = document.getElementById('sidenavList'); if (navList !== null) { var url = window.location.href; var lindex = url.lastIndexOf('/'); url = url .slice(lindex + 1) .toLowerCase(); var aTags = navList.getElementsByTagName('a'); var currentindex = -1; for (var i = 0; i < aTags.length; i++) { var lowerhref = aTags[i] .href .toLowerCase(); if (lowerhref.indexOf('/' + url) !== -1) { currentindex = i; aTags[i] .classList .add('active'); break; } } } </script> <div class="px-4 pb-16 w-full overflow-hidden prose"> <h1>Testing GoJS apps</h1> <p> The <a>Robot</a> extension is useful for pretending to interact with a Diagram without actually generating any keyboard or mouse or pointer events. See the sample at <a href="../samples/Robot.html">Simulating Input with Robot</a>. </p> <p> One convenient feature of the <a>Robot</a> 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. </p> <h2 id="TestingwithJest">Testing with Jest</h2> <p>Testing with Jest depends on installing: <code>jest</code>, <code>puppeteer</code>, and <code>jest-puppeteer</code>.</p> <pre class="lang-js"><code> // jest.config.js module.exports = { preset: "jest-puppeteer" };</code></pre> <pre class="lang-js"><code> // 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(); }); });</code></pre> <h3 id="TestingwithJestInReact">Testing with Jest in React</h3> <p> In our <a href="https://github.com/NorthwoodsSoftware/gojs-react-samples/tree/main/gojs-react-jest">GoJS React samples</a>, we have an example of using the gojs-react component with Jest: <a href="https://github.com/NorthwoodsSoftware/gojs-react-samples/tree/main/gojs-react-jest">gojs-react-jest</a>. </p> <h2 id="TestingwithCypress">Testing with Cypress</h2> <pre class="lang-js"><code> // 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) }) }) })</code></pre> <h2 id="TestingwithPlaywright">Testing with Playwright</h2> <pre class="lang-js"><code> // @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); } }); }); </code></pre> <!-- <h2 id="TestingwithTestCafe">Testing with TestCafe</h2> <pre class="lang-js"><code> import { ClientFunction, Selector } from 'testcafe'; 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 testcafe firefox tests/test.js --live fixture('minimal tests') .page('https://gojs.net/latest/samples/minimal.html') // Tests test('copy node', async t => { const numNodesLinks = await t .expect(Selector('#myDiagramDiv').exists).ok() .eval(() => { const div = document.getElementById('myDiagramDiv'); const diagram = go.Diagram.fromDiv(div); if (!diagram) return; const node2 = diagram.findNodeForKey(2); if (!node2 || node2.isSelected) 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) : '']; }); await t .expect(Array.isArray(numNodesLinks)).ok() .expect(numNodesLinks[0]).eql(5) .expect(numNodesLinks[1]).eql(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]); await t .expect(pt.x).within(123, 125) .expect(pt.y).within(-70, -68) }) const loadRobot = ClientFunction(() => { const script = document.createElement('script'); script.src = 'https://gojs.net/latest/extensions/Robot.js'; document.body.append(script); }); const checkRobot = ClientFunction(() => typeof Robot); test('drag node', async t => { await loadRobot(); await checkRobot(); const newloc = await t.eval(() => { const div = document.getElementById('myDiagramDiv'); const diagram = go.Diagram.fromDiv(div); if (!diagram) return; const robot = new Robot(diagram); if (!robot) return; const node2 = diagram.findNodeForKey(2); if (!node2 || node2.isSelected) 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); await t .expect(pt.x).within(55, 56) .expect(pt.y).within(-51, -50) } }) // This doesn't work because client scripts are executed BEFORE loading the page //.clientScripts('../node_modules/create-gojs-kit/dist/extensions/Robot.js') </code></pre> --> </div> </div> <footer class="bg-white text-gray-900 border-t border-t-gray-200"> <div class="w-full max-w-screen-lg mx-auto px-4 py-6"> <p id="version" class="text-xs text-gray-900 m-0"></p> <div class="text-sm px-0 mb-4 grid grid-cols-2 sm:grid-cols-3 gap-y-10"> <div> <h2 class="text-base font-semibold text-nwoods-primary">GoJS</h2> <ul class="list-none space-y-4 md:space-y-1 px-0"> <li> <a href="../samples/index.html">Samples</a> </li> <li> <a href="../learn/index.html">Learn</a> </li> <li> <a href="../intro/index.html">Intro</a> </li> <li> <a href="../api/index.html">API</a> </li> <li> <a href="../changelog.html">Changelog</a> </li> <li> <a href="https://github.com/NorthwoodsSoftware/GoJS" target="_blank" rel="noopener">GitHub</a> </li> </ul> </div> <div> <h2 class="text-base font-semibold text-nwoods-primary">Support</h2> <ul class="list-none space-y-4 md:space-y-1 px-0"> <li> <a href="https://nwoods.com/contact.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a> </li> <li> <a href="https://forum.nwoods.com/c/gojs" target="_blank" rel="noopener">Forum</a> </li> <li> <a href="https://nwoods.com/app/activate.aspx?sku=gojs" target="_blank" rel="noopener">Activate</a> </li> <li> <a href="https://nwoods.com/sales/index.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a> </li> <li> <a href="https://nwoods.com/register.html" target="_blank" rel="noopener">Register</a> </li> </ul> </div> <div> <h2 class="text-base font-semibold text-nwoods-primary">Company</h2> <ul class="list-none space-y-4 md:space-y-1 px-0"> <li> <a target="_blank" href="https://nwoods.com" target="_blank" rel="noopener">Northwoods</a> </li> <li> <a target="_blank" href="https://nwoods.com/about.html" target="_blank" rel="noopener">About Us</a> </li> <li> <a target="_blank" href="https://nwoods.com/contact.html" target="_blank" rel="noopener">Contact Us</a> </li> <li> <a target="_blank" href="https://nwoods.com/consulting.html" target="_blank" rel="noopener">Consulting</a> </li> <li> <a target="_blank" href="https://twitter.com/northwoodsgo" target="_blank" rel="noopener">Twitter</a> </li> </ul> </div> </div> <p class="text-sm text-gray-900 md:mb-6"> Copyright 1998-2025 <a href="https://nwoods.com">Northwoods Software</a> </p> </div> </footer> </body> <script async src="https://www.googletagmanager.com/gtag/js?id=G-S5QK8VSK84"></script> <script> window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'G-S5QK8VSK84'); var getOutboundLink = function (url, label) { gtag('event', 'click', { 'event_category': 'outbound', 'event_label': label, 'transport_type': 'beacon' }); } const params = new URL(document.location).searchParams let a = params.get('a'); if (a) localStorage.setItem('a', a); a = localStorage.getItem('a'); if (a) { const links = [...document.body.getElementsByTagName("a")].filter((l) => l.href.includes('nwoods.com')); for (const l of links) { const url = new URL(l.href); url.searchParams.set('a', a); l.href = url; } } </script> <script src="../assets/js/prism.js"></script> <script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script> <script src="../assets/js/goDoc.js"></script> <script> document.addEventListener("DOMContentLoaded", function () { if (window.go) document .getElementById('version') .textContent = "GoJS version " + go.version; if (window.goDoc) window.goDoc(); var d = window.diagrams; for (var i = 0; i < d.length; i++) { var dargs = d[i]; goCodeExecute(dargs[0], dargs[1], dargs[2], dargs[3], dargs[4]); } if (window.extra) window.extra(); } ); </script> </html>