-English | [简体中文](./README.zh-CN.md)
+
+
+
+[](https://www.npmjs.com/package/mermaid)
+[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml)
+[](https://bundlephobia.com/package/mermaid)
+[](https://coveralls.io/github/mermaid-js/mermaid?branch=master)
+[](https://www.jsdelivr.com/package/npm/mermaid)
+[](https://www.npmjs.com/package/mermaid)
+[](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
+[](https://twitter.com/mermaidjs_)
@@ -10,7 +39,7 @@ English | [简体中文](./README.zh-CN.md)
**Thanks to all involved, people committing pull requests, people answering questions! 🙏**
-
+
## About
@@ -27,14 +56,12 @@ Mermaid addresses this problem by enabling users to create easily modifiable dia
Mermaid allows even non-programmers to easily create detailed diagrams through the [Mermaid Live Editor](https://mermaid.live/).
[Tutorials](./docs/config/Tutorials.md) has video tutorials.
-Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/misc/integrations.md).
+Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations.md).
-You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) as well many of your other favorite applications—check out the list of [Integrations and Usages of Mermaid](./docs/misc/integrations.md).
+You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) as well many of your other favorite applications—check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations.md).
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/community/n00b-overview.md), [Usage](./docs/config/usage.md) and [Tutorials](./docs/config/Tutorials.md).
-🌐 [CDN](https://unpkg.com/mermaid/) | 📖 [Documentation](https://mermaidjs.github.io) | 🙌 [Contribution](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md) | 📜 [Changelog](./docs/CHANGELOG.md)
-
In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 4bdbc4ae7..65cf38645 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,8 +1,37 @@
-# mermaid
+
Mermaid 甚至能让非程序员也能通过 [Mermaid Live Editor](https://mermaid.live/) 轻松创建详细的图表。
-你可以访问 [教程](./docs/config/Tutorials.md) 来查看 Live Editor 的视频教程,也可以查看 [Mermaid 的集成和使用](./docs/misc/integrations.md) 这个清单来检查你的文档工具是否已经集成了 Mermaid 支持。
+你可以访问 [教程](./docs/config/Tutorials.md) 来查看 Live Editor 的视频教程,也可以查看 [Mermaid 的集成和使用](./docs/ecosystem/integrations.md) 这个清单来检查你的文档工具是否已经集成了 Mermaid 支持。
如果想要查看关于 Mermaid 更详细的介绍及基础使用方式,可以查看 [入门指引](./docs/community/n00b-overview.md), [用法](./docs/config/usage.md) 和 [教程](./docs/config/Tutorials.md).
-🌐 [CDN](https://unpkg.com/mermaid/) | 📖 [文档](https://mermaidjs.github.io) | 🙌 [贡献](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md) | 📜 [更新日志](./docs/CHANGELOG.md)
-
## 示例
@@ -325,7 +352,7 @@ _很不幸的是,鱼与熊掌不可兼得,在这个场景下它意味着在
来自 Knut Sveidqvist:
-> _特别感谢 [d3](https://d3js.org/) 和 [dagre-d3](https://github.com/cpettitt/dagre-d3) 这两个优秀的项目,它们提供了图形布局和绘图工具库! _ >_同样感谢 [js-sequence-diagram](https://bramp.github.io/js-sequence-diagrams) 提供了时序图语法的使用。 感谢 Jessica Peter 提供了甘特图渲染的灵感。_ >_感谢 [Tyler Long](https://github.com/tylerlong) 从 2017 年四月开始成为了项目的合作者。_
+> _特别感谢 [d3](https://d3js.org/) 和 [dagre-d3](https://github.com/cpettitt/dagre-d3) 这两个优秀的项目,它们提供了图形布局和绘图工具库!_ > _同样感谢 [js-sequence-diagram](https://bramp.github.io/js-sequence-diagrams) 提供了时序图语法的使用。 感谢 Jessica Peter 提供了甘特图渲染的灵感。_ > _感谢 [Tyler Long](https://github.com/tylerlong) 从 2017 年四月开始成为了项目的合作者。_
>
> _感谢越来越多的 [贡献者们](https://github.com/knsv/mermaid/graphs/contributors),没有你们,就没有这个项目的今天!_
diff --git a/V10-BreakingChanges.md b/V10-BreakingChanges.md
index bd9110d1a..121fdd596 100644
--- a/V10-BreakingChanges.md
+++ b/V10-BreakingChanges.md
@@ -1,5 +1,58 @@
-# A collection of updates that change the behaviour
+# A collection of updates that change the behavior
+
+## Async
+
+`parse`, `render` are now async.
## Lazy loading and asynchronisity
- Invalid dates are rendered as syntax error instead of returning best guess or the current date
+
+## ParseError is removed
+
+```js
+//< v10.0.0
+mermaid.parse(text, parseError);
+
+//>= v10.0.0
+await mermaid.parse(text).catch(parseError);
+// or
+try {
+ await mermaid.parse(text);
+} catch (err) {
+ parseError(err);
+}
+```
+
+## Init deprecated and InitThrowsErrors removed
+
+The config passed to `init` was not being used eariler.
+It will now be used.
+The `init` function is deprecated and will be removed in the next major release.
+init currently works as a wrapper to `initialize` and `run`.
+
+```js
+//< v10.0.0
+mermaid.init(config, selector, cb);
+
+//>= v10.0.0
+mermaid.initialize(config);
+mermaid.run({
+ querySelector: selector,
+ postRenderCallback: cb,
+ suppressErrors: true,
+});
+```
+
+```js
+//< v10.0.0
+mermaid.initThrowsErrors(config, selector, cb);
+
+//>= v10.0.0
+mermaid.initialize(config);
+mermaid.run({
+ querySelector: selector,
+ postRenderCallback: cb,
+ suppressErrors: false,
+});
+```
diff --git a/__mocks__/c4Renderer.js b/__mocks__/c4Renderer.js
new file mode 100644
index 000000000..576d5d863
--- /dev/null
+++ b/__mocks__/c4Renderer.js
@@ -0,0 +1,21 @@
+/**
+ * Mocked C4Context diagram renderer
+ */
+
+import { vi } from 'vitest';
+
+export const drawPersonOrSystemArray = vi.fn();
+export const drawBoundary = vi.fn();
+
+export const setConf = vi.fn();
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ drawPersonOrSystemArray,
+ drawBoundary,
+ setConf,
+ draw,
+};
diff --git a/__mocks__/classRenderer-v2.js b/__mocks__/classRenderer-v2.js
new file mode 100644
index 000000000..1ad95806f
--- /dev/null
+++ b/__mocks__/classRenderer-v2.js
@@ -0,0 +1,16 @@
+/**
+ * Mocked class diagram v2 renderer
+ */
+
+import { vi } from 'vitest';
+
+export const setConf = vi.fn();
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ setConf,
+ draw,
+};
diff --git a/__mocks__/classRenderer.js b/__mocks__/classRenderer.js
new file mode 100644
index 000000000..1c20de4b1
--- /dev/null
+++ b/__mocks__/classRenderer.js
@@ -0,0 +1,13 @@
+/**
+ * Mocked class diagram renderer
+ */
+
+import { vi } from 'vitest';
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ draw,
+};
diff --git a/__mocks__/d3.ts b/__mocks__/d3.ts
index f90d93557..af35020c5 100644
--- a/__mocks__/d3.ts
+++ b/__mocks__/d3.ts
@@ -1,79 +1,14 @@
// @ts-nocheck TODO: Fix TS
-import { vi } from 'vitest';
-
-const NewD3 = function () {
- /**
- *
- */
- function returnThis() {
- return this;
- }
- return {
- append: function () {
- return NewD3();
- },
- lower: returnThis,
- attr: returnThis,
- style: returnThis,
- text: returnThis,
- 0: {
- 0: {
- getBBox: function () {
- return {
- height: 10,
- width: 20,
- };
- },
- },
- },
- };
-};
+import { MockedD3 } from '../packages/mermaid/src/tests/MockedD3';
export const select = function () {
- return new NewD3();
+ return new MockedD3();
};
export const selectAll = function () {
- return new NewD3();
+ return new MockedD3();
};
export const curveBasis = 'basis';
export const curveLinear = 'linear';
export const curveCardinal = 'cardinal';
-
-export const MockD3 = (name, parent) => {
- const children = [];
- const elem = {
- get __children() {
- return children;
- },
- get __name() {
- return name;
- },
- get __parent() {
- return parent;
- },
- node() {
- return {
- getBBox() {
- return {
- x: 5,
- y: 10,
- height: 15,
- width: 20,
- };
- },
- };
- },
- };
- elem.append = (name) => {
- const mockElem = MockD3(name, elem);
- children.push(mockElem);
- return mockElem;
- };
- elem.lower = vi.fn(() => elem);
- elem.attr = vi.fn(() => elem);
- elem.text = vi.fn(() => elem);
- elem.style = vi.fn(() => elem);
- return elem;
-};
diff --git a/__mocks__/erRenderer.js b/__mocks__/erRenderer.js
new file mode 100644
index 000000000..845d641f7
--- /dev/null
+++ b/__mocks__/erRenderer.js
@@ -0,0 +1,16 @@
+/**
+ * Mocked er diagram renderer
+ */
+
+import { vi } from 'vitest';
+
+export const setConf = vi.fn();
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ setConf,
+ draw,
+};
diff --git a/__mocks__/flowRenderer-v2.js b/__mocks__/flowRenderer-v2.js
new file mode 100644
index 000000000..89cc86031
--- /dev/null
+++ b/__mocks__/flowRenderer-v2.js
@@ -0,0 +1,24 @@
+/**
+ * Mocked flow (flowchart) diagram v2 renderer
+ */
+
+import { vi } from 'vitest';
+
+export const setConf = vi.fn();
+export const addVertices = vi.fn();
+export const addEdges = vi.fn();
+export const getClasses = vi.fn().mockImplementation(() => {
+ return {};
+});
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ setConf,
+ addVertices,
+ addEdges,
+ getClasses,
+ draw,
+};
diff --git a/__mocks__/ganttRenderer.js b/__mocks__/ganttRenderer.js
new file mode 100644
index 000000000..957249832
--- /dev/null
+++ b/__mocks__/ganttRenderer.js
@@ -0,0 +1,16 @@
+/**
+ * Mocked gantt diagram renderer
+ */
+
+import { vi } from 'vitest';
+
+export const setConf = vi.fn();
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ setConf,
+ draw,
+};
diff --git a/__mocks__/gitGraphRenderer.js b/__mocks__/gitGraphRenderer.js
new file mode 100644
index 000000000..1daa82ca4
--- /dev/null
+++ b/__mocks__/gitGraphRenderer.js
@@ -0,0 +1,13 @@
+/**
+ * Mocked git (graph) diagram renderer
+ */
+
+import { vi } from 'vitest';
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ draw,
+};
diff --git a/__mocks__/journeyRenderer.js b/__mocks__/journeyRenderer.js
new file mode 100644
index 000000000..2bc77c0b1
--- /dev/null
+++ b/__mocks__/journeyRenderer.js
@@ -0,0 +1,15 @@
+/**
+ * Mocked pie (picChart) diagram renderer
+ */
+
+import { vi } from 'vitest';
+export const setConf = vi.fn();
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ setConf,
+ draw,
+};
diff --git a/__mocks__/pieRenderer.js b/__mocks__/pieRenderer.js
new file mode 100644
index 000000000..317c69901
--- /dev/null
+++ b/__mocks__/pieRenderer.js
@@ -0,0 +1,13 @@
+/**
+ * Mocked pie (picChart) diagram renderer
+ */
+
+import { vi } from 'vitest';
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ draw,
+};
diff --git a/__mocks__/requirementRenderer.js b/__mocks__/requirementRenderer.js
new file mode 100644
index 000000000..48d8997ac
--- /dev/null
+++ b/__mocks__/requirementRenderer.js
@@ -0,0 +1,13 @@
+/**
+ * Mocked requirement diagram renderer
+ */
+
+import { vi } from 'vitest';
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ draw,
+};
diff --git a/__mocks__/sequenceRenderer.js b/__mocks__/sequenceRenderer.js
new file mode 100644
index 000000000..11080c6bb
--- /dev/null
+++ b/__mocks__/sequenceRenderer.js
@@ -0,0 +1,23 @@
+/**
+ * Mocked sequence diagram renderer
+ */
+
+import { vi } from 'vitest';
+
+export const bounds = vi.fn();
+export const drawActors = vi.fn();
+export const drawActorsPopup = vi.fn();
+
+export const setConf = vi.fn();
+
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ bounds,
+ drawActors,
+ drawActorsPopup,
+ setConf,
+ draw,
+};
diff --git a/__mocks__/stateRenderer-v2.js b/__mocks__/stateRenderer-v2.js
new file mode 100644
index 000000000..a2d103b50
--- /dev/null
+++ b/__mocks__/stateRenderer-v2.js
@@ -0,0 +1,22 @@
+/**
+ * Mocked state diagram v2 renderer
+ */
+
+import { vi } from 'vitest';
+
+export const setConf = vi.fn();
+export const getClasses = vi.fn().mockImplementation(() => {
+ return {};
+});
+export const stateDomId = vi.fn().mockImplementation(() => {
+ return 'mocked-stateDiagram-stateDomId';
+});
+export const draw = vi.fn().mockImplementation(() => {
+ return '';
+});
+
+export default {
+ setConf,
+ getClasses,
+ draw,
+};
diff --git a/cSpell.json b/cSpell.json
index 64187e1ca..6f93af103 100644
--- a/cSpell.json
+++ b/cSpell.json
@@ -6,6 +6,7 @@
"adamiecki",
"alois",
"antiscript",
+ "appli",
"applitools",
"asciidoctor",
"ashish",
@@ -13,7 +14,9 @@
"bbox",
"bilkent",
"bisheng",
+ "blrs",
"braintree",
+ "brkt",
"brolin",
"brotli",
"classdef",
@@ -30,6 +33,7 @@
"doku",
"dompurify",
"edgechromium",
+ "elkjs",
"faber",
"flatmap",
"ftplugin",
@@ -38,6 +42,7 @@
"gitgraph",
"globby",
"graphlib",
+ "graphviz",
"grav",
"greywolf",
"inkdrop",
@@ -51,8 +56,10 @@
"knut",
"laganeckas",
"lintstagedrc",
+ "logmsg",
"lucida",
"matthieu",
+ "mdast",
"mdbook",
"mermerd",
"mindaugas",
@@ -60,26 +67,38 @@
"mindmaps",
"mitigations",
"mkdocs",
+ "mult",
"orlandoni",
"phpbb",
"plantuml",
"playfair",
+ "pnpm",
"podlite",
+ "quence",
+ "radious",
"ranksep",
+ "rect",
+ "rects",
"redmine",
+ "roledescription",
"sandboxed",
"setupgraphviewbox",
"shiki",
"sidharth",
+ "sidharthv",
"sphinxcontrib",
"statediagram",
"stylis",
"substate",
"sveidqvist",
+ "swimm",
"techn",
+ "teststr",
+ "textlength",
"treemap",
"ts-nocheck",
"tuleap",
+ "ugge",
"unist",
"verdana",
"viewports",
diff --git a/cypress/helpers/util.js b/cypress/helpers/util.js
index 5213f634a..533cca499 100644
--- a/cypress/helpers/util.js
+++ b/cypress/helpers/util.js
@@ -2,7 +2,7 @@ const utf8ToB64 = (str) => {
return window.btoa(unescape(encodeURIComponent(str)));
};
-const batchId = 'mermid-batch' + new Date().getTime();
+const batchId = 'mermaid-batch' + new Date().getTime();
export const mermaidUrl = (graphStr, options, api) => {
const obj = {
@@ -46,8 +46,22 @@ export const imgSnapshotTest = (graphStr, _options, api = false, validation) =>
if (!options.fontSize) {
options.fontSize = '16px';
}
+ const url = mermaidUrl(graphStr, options, api);
+ openURLAndVerifyRendering(url, options, validation);
+};
+
+export const urlSnapshotTest = (url, _options, api = false, validation) => {
+ const options = Object.assign(_options);
+ openURLAndVerifyRendering(url, options, validation);
+};
+
+export const renderGraph = (graphStr, options, api) => {
+ const url = mermaidUrl(graphStr, options, api);
+ openURLAndVerifyRendering(url, options);
+};
+
+const openURLAndVerifyRendering = (url, options, validation = undefined) => {
const useAppli = Cypress.env('useAppli');
- cy.log('Hello ' + useAppli ? 'Appli' : 'image-snapshot');
const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
if (useAppli) {
@@ -60,82 +74,20 @@ export const imgSnapshotTest = (graphStr, _options, api = false, validation) =>
});
}
- const url = mermaidUrl(graphStr, options, api);
-
cy.visit(url);
+ cy.window().should('have.property', 'rendered', true);
+ cy.get('svg').should('be.visible');
+
if (validation) {
cy.get('svg').should(validation);
}
- cy.get('svg');
- // Default name to test title
if (useAppli) {
cy.log('Check eyes' + Cypress.spec.name);
cy.eyesCheckWindow('Click!');
- cy.log('Closing eyes: ' + Cypress.spec.name);
+ cy.log('Closing eyes' + Cypress.spec.name);
cy.eyesClose();
} else {
cy.matchImageSnapshot(name);
}
};
-
-export const urlSnapshotTest = (url, _options, api = false, validation) => {
- cy.log(_options);
- const options = Object.assign(_options);
- if (!options.fontFamily) {
- options.fontFamily = 'courier';
- }
- if (!options.sequence) {
- options.sequence = {};
- }
- if (!options.sequence || (options.sequence && !options.sequence.actorFontFamily)) {
- options.sequence.actorFontFamily = 'courier';
- }
- if (options.sequence && !options.sequence.noteFontFamily) {
- options.sequence.noteFontFamily = 'courier';
- }
- options.sequence.actorFontFamily = 'courier';
- options.sequence.noteFontFamily = 'courier';
- options.sequence.messageFontFamily = 'courier';
- if (options.sequence && !options.sequence.actorFontFamily) {
- options.sequence.actorFontFamily = 'courier';
- }
- if (!options.fontSize) {
- options.fontSize = '16px';
- }
- const useAppli = Cypress.env('useAppli');
- cy.log('Hello ' + useAppli ? 'Appli' : 'image-snapshot');
- const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
-
- if (useAppli) {
- cy.log('Opening eyes 2' + Cypress.spec.name);
- cy.eyesOpen({
- appName: 'Mermaid',
- testName: name,
- batchName: Cypress.spec.name,
- batchId: batchId + Cypress.spec.name,
- });
- }
-
- cy.visit(url);
- if (validation) {
- cy.get('svg').should(validation);
- }
- cy.get('body');
- // Default name to test title
-
- if (useAppli) {
- cy.log('Check eyes 2' + Cypress.spec.name);
- cy.eyesCheckWindow('Click!');
- cy.log('Closing eyes 2' + Cypress.spec.name);
- cy.eyesClose();
- } else {
- cy.matchImageSnapshot(name);
- }
-};
-
-export const renderGraph = (graphStr, options, api) => {
- const url = mermaidUrl(graphStr, options, api);
-
- cy.visit(url);
-};
diff --git a/cypress/integration/other/external-diagrams.spec.js b/cypress/integration/other/external-diagrams.spec.js
index 3a6c37e88..c94235162 100644
--- a/cypress/integration/other/external-diagrams.spec.js
+++ b/cypress/integration/other/external-diagrams.spec.js
@@ -1,13 +1,10 @@
+import { urlSnapshotTest } from '../../helpers/util';
+
describe('mermaid', () => {
describe('registerDiagram', () => {
- it('should work on @mermaid-js/mermaid-mindmap and mermaid-example-diagram', () => {
- const url = 'http://localhost:9000/external-diagrams-mindmap.html';
- cy.visit(url);
-
- cy.get('svg', {
- // may be a bit slower than normal, since vite might need to re-compile mermaid/mermaid-mindmap/mermaid-example-diagram
- timeout: 10000,
- }).matchImageSnapshot();
+ it('should work on @mermaid-js/mermaid-example-diagram', () => {
+ const url = 'http://localhost:9000/external-diagrams-example-diagram.html';
+ urlSnapshotTest(url, {}, false, false);
});
});
});
diff --git a/cypress/integration/other/ghsa.spec.js b/cypress/integration/other/ghsa.spec.js
index 5b168a8a8..4fadc7855 100644
--- a/cypress/integration/other/ghsa.spec.js
+++ b/cypress/integration/other/ghsa.spec.js
@@ -7,4 +7,10 @@ describe('CSS injections', () => {
flowchart: { htmlLabels: false },
});
});
+ it('should not allow adding styletags affecting the page', () => {
+ urlSnapshotTest('http://localhost:9000/ghsa3.html', {
+ logLevel: 1,
+ flowchart: { htmlLabels: false },
+ });
+ });
});
diff --git a/cypress/integration/rendering/c4.spec.js b/cypress/integration/rendering/c4.spec.js
new file mode 100644
index 000000000..0cf128ff6
--- /dev/null
+++ b/cypress/integration/rendering/c4.spec.js
@@ -0,0 +1,122 @@
+import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
+
+describe('C4 diagram', () => {
+ it('should render a simple C4Context diagram', () => {
+ imgSnapshotTest(
+ `
+ C4Context
+ accTitle: C4 context demo
+ accDescr: Many large C4 diagrams
+
+ title System Context diagram for Internet Banking System
+
+ Enterprise_Boundary(b0, "BankBoundary0") {
+ Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
+
+ System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
+
+ Enterprise_Boundary(b1, "BankBoundary") {
+ System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
+ }
+ }
+
+ BiRel(customerA, SystemAA, "Uses")
+ Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
+ Rel(SystemC, customerA, "Sends e-mails to")
+
+ UpdateElementStyle(customerA, $fontColor="red", $bgColor="grey", $borderColor="red")
+ UpdateRelStyle(customerA, SystemAA, $textColor="blue", $lineColor="blue", $offsetX="5")
+ UpdateRelStyle(SystemC, customerA, $textColor="red", $lineColor="red", $offsetX="-50", $offsetY="20")
+ `,
+ {}
+ );
+ cy.get('svg');
+ });
+ it('should render a simple C4Container diagram', () => {
+ imgSnapshotTest(
+ `
+ C4Container
+ title Container diagram for Internet Banking System
+
+ System_Ext(email_system, "E-Mail System", "The internal Microsoft Exchange system", $tags="v1.0")
+ Person(customer, Customer, "A customer of the bank, with personal bank accounts", $tags="v1.0")
+
+ Container_Boundary(c1, "Internet Banking") {
+ Container(spa, "Single-Page App", "JavaScript, Angular", "Provides all the Internet banking functionality to customers via their web browser")
+ }
+
+ Rel(customer, spa, "Uses", "HTTPS")
+ Rel(email_system, customer, "Sends e-mails to")
+ `,
+ {}
+ );
+ cy.get('svg');
+ });
+ it('should render a simple C4Component diagram', () => {
+ imgSnapshotTest(
+ `
+ C4Component
+ title Component diagram for Internet Banking System - API Application
+
+ Container(spa, "Single Page Application", "javascript and angular", "Provides all the internet banking functionality to customers via their web browser.")
+
+ Container_Boundary(api, "API Application") {
+ Component(sign, "Sign In Controller", "MVC Rest Controller", "Allows users to sign in to the internet banking system")
+ }
+
+ Rel_Back(spa, sign, "Uses", "JSON/HTTPS")
+ UpdateRelStyle(spa, sign, $offsetY="-40")
+ `,
+ {}
+ );
+ cy.get('svg');
+ });
+ it('should render a simple C4Dynamic diagram', () => {
+ imgSnapshotTest(
+ `
+ C4Dynamic
+ title Dynamic diagram for Internet Banking System - API Application
+
+ ContainerDb(c4, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.")
+ Container(c1, "Single-Page Application", "JavaScript and Angular", "Provides all of the Internet banking functionality to customers via their web browser.")
+ Container_Boundary(b, "API Application") {
+ Component(c3, "Security Component", "Spring Bean", "Provides functionality Related to signing in, changing passwords, etc.")
+ Component(c2, "Sign In Controller", "Spring MVC Rest Controller", "Allows users to sign in to the Internet Banking System.")
+ }
+ Rel(c1, c2, "Submits credentials to", "JSON/HTTPS")
+ Rel(c2, c3, "Calls isAuthenticated() on")
+ Rel(c3, c4, "select * from users where username = ?", "JDBC")
+
+ UpdateRelStyle(c1, c2, $textColor="red", $offsetY="-40")
+ UpdateRelStyle(c2, c3, $textColor="red", $offsetX="-40", $offsetY="60")
+ UpdateRelStyle(c3, c4, $textColor="red", $offsetY="-40", $offsetX="10")
+ `,
+ {}
+ );
+ cy.get('svg');
+ });
+ it('should render a simple C4Deployment diagram', () => {
+ imgSnapshotTest(
+ `
+ C4Deployment
+ title Deployment Diagram for Internet Banking System - Live
+
+ Deployment_Node(mob, "Customer's mobile device", "Apple IOS or Android"){
+ Container(mobile, "Mobile App", "Xamarin", "Provides a limited subset of the Internet Banking functionality to customers via their mobile device.")
+ }
+
+ Deployment_Node(plc, "Big Bank plc", "Big Bank plc data center"){
+ Deployment_Node(dn, "bigbank-api*** x8", "Ubuntu 16.04 LTS"){
+ Deployment_Node(apache, "Apache Tomcat", "Apache Tomcat 8.x"){
+ Container(api, "API Application", "Java and Spring MVC", "Provides Internet Banking functionality via a JSON/HTTPS API.")
+ }
+ }
+ }
+
+ Rel(mobile, api, "Makes API calls to", "json/HTTPS")
+ `,
+ {}
+ );
+ cy.get('svg');
+ });
+});
diff --git a/cypress/integration/rendering/classDiagram-v2.spec.js b/cypress/integration/rendering/classDiagram-v2.spec.js
index f97458857..9536a074d 100644
--- a/cypress/integration/rendering/classDiagram-v2.spec.js
+++ b/cypress/integration/rendering/classDiagram-v2.spec.js
@@ -485,8 +485,7 @@ describe('Class diagram V2', () => {
classDiagram-v2
note "I love this diagram!\nDo you love it?"
class Class10 {
- <>
- int id
+ int id
size()
}
note for Class10 "Cool class\nI said it's very cool class!"
diff --git a/cypress/integration/rendering/classDiagram.spec.js b/cypress/integration/rendering/classDiagram.spec.js
index 16601652d..e21be67ec 100644
--- a/cypress/integration/rendering/classDiagram.spec.js
+++ b/cypress/integration/rendering/classDiagram.spec.js
@@ -414,7 +414,6 @@ describe('Class diagram', () => {
classDiagram
note "I love this diagram!\nDo you love it?"
class Class10 {
- <>
int id
size()
}
diff --git a/cypress/integration/rendering/current.spec.js b/cypress/integration/rendering/current.spec.js
index 56b5f774b..033752c66 100644
--- a/cypress/integration/rendering/current.spec.js
+++ b/cypress/integration/rendering/current.spec.js
@@ -1,6 +1,6 @@
import { imgSnapshotTest } from '../../helpers/util';
-describe('State diagram', () => {
+describe('Current diagram', () => {
it('should render a state with states in it', () => {
imgSnapshotTest(
`
diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js
index 8e8946170..df1fac0cd 100644
--- a/cypress/integration/rendering/erDiagram.spec.js
+++ b/cypress/integration/rendering/erDiagram.spec.js
@@ -182,6 +182,20 @@ describe('Entity Relationship Diagram', () => {
cy.get('svg');
});
+ it('should render entities with length in attributes type', () => {
+ renderGraph(
+ `
+ erDiagram
+ CLUSTER {
+ varchar(99) name
+ string(255) description
+ }
+ `,
+ { logLevel: 1 }
+ );
+ cy.get('svg');
+ });
+
it('should render entities and attributes with big and small entity names', () => {
renderGraph(
`
diff --git a/cypress/integration/rendering/flowchart-elk.spec.js b/cypress/integration/rendering/flowchart-elk.spec.js
new file mode 100644
index 000000000..9b9c76378
--- /dev/null
+++ b/cypress/integration/rendering/flowchart-elk.spec.js
@@ -0,0 +1,687 @@
+import { imgSnapshotTest, renderGraph } from '../../helpers/util';
+
+describe('Flowchart ELK', () => {
+ it('1-elk: should render a simple flowchart', () => {
+ imgSnapshotTest(
+ `flowchart-elk TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+ `,
+ {}
+ );
+ imgSnapshotTest(
+ `flowchart TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+ `,
+ { flowchart: { defaultRenderer: 'elk' } }
+ );
+ });
+
+ it('2-elk: should render a simple flowchart with diagramPadding set to 0', () => {
+ imgSnapshotTest(
+ `flowchart-elk TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ %% this is a comment
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+ `,
+ { flowchart: { diagramPadding: 0 } }
+ );
+ });
+
+ it('3-elk: a link with correct arrowhead to a subgraph', () => {
+ imgSnapshotTest(
+ `flowchart-elk TD
+ P1
+ P1 -->P1.5
+ subgraph P1.5
+ P2
+ P2.5(( A ))
+ P3
+ end
+ P2 --> P4
+ P3 --> P6
+ P1.5 --> P5
+ `,
+ {}
+ );
+ });
+
+ it('4-elk: Length of edges', () => {
+ imgSnapshotTest(
+ `flowchart-elk TD
+ L1 --- L2
+ L2 --- C
+ M1 ---> C
+ R1 .-> R2
+ R2 <.-> C
+ C -->|Label 1| E1
+ C <-- Label 2 ---> E2
+ C ----> E3
+ C <-...-> E4
+ C ======> E5
+ `,
+ {}
+ );
+ });
+ it('5-elk: should render escaped without html labels', () => {
+ imgSnapshotTest(
+ `flowchart-elk TD
+ a["Haiya"]---->b
+ `,
+ { htmlLabels: false, flowchart: { htmlLabels: false } }
+ );
+ });
+ it('6-elk: should render non-escaped with html labels', () => {
+ imgSnapshotTest(
+ `flowchart-elk TD
+ a["Haiya"]===>b
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('7-elk: should render a flowchart when useMaxWidth is true (default)', () => {
+ renderGraph(
+ `flowchart-elk TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+ `,
+ { flowchart: { useMaxWidth: true } }
+ );
+ cy.get('svg').should((svg) => {
+ expect(svg).to.have.attr('width', '100%');
+ // expect(svg).to.have.attr('height');
+ // use within because the absolute value can be slightly different depending on the environment ±5%
+ // const height = parseFloat(svg.attr('height'));
+ // expect(height).to.be.within(446 * 0.95, 446 * 1.05);
+ const style = svg.attr('style');
+ expect(style).to.match(/^max-width: [\d.]+px;$/);
+ const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
+ expect(maxWidthValue).to.be.within(230 * 0.95, 230 * 1.05);
+ });
+ });
+ it('8-elk: should render a flowchart when useMaxWidth is false', () => {
+ renderGraph(
+ `flowchart-elk TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+ `,
+ { flowchart: { useMaxWidth: false } }
+ );
+ cy.get('svg').should((svg) => {
+ // const height = parseFloat(svg.attr('height'));
+ const width = parseFloat(svg.attr('width'));
+ // use within because the absolute value can be slightly different depending on the environment ±5%
+ // expect(height).to.be.within(446 * 0.95, 446 * 1.05);
+ expect(width).to.be.within(230 * 0.95, 230 * 1.05);
+ expect(svg).to.not.have.attr('style');
+ });
+ });
+
+ it.skip('V2 elk - 16: Render Stadium shape', () => {
+ imgSnapshotTest(
+ ` flowchart-elk TD
+ A([stadium shape test])
+ A -->|Get money| B([Go shopping])
+ B --> C([Let me think... Do I want something for work, something to spend every free second with, or something to get around?])
+ C -->|One| D([Laptop])
+ C -->|Two| E([iPhone])
+ C -->|Three| F([Car wroom wroom])
+ click A "index.html#link-clicked" "link test"
+ click B testClick "click test"
+ classDef someclass fill:#f96;
+ class A someclass;
+ class C someclass;
+ `,
+ { flowchart: { htmlLabels: false }, fontFamily: 'courier' }
+ );
+ });
+
+ it('50-elk: handle nested subgraphs in reverse order', () => {
+ imgSnapshotTest(
+ `flowchart-elk LR
+ a -->b
+ subgraph A
+ B
+ end
+ subgraph B
+ b
+ end
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+
+ it('51-elk: handle nested subgraphs in reverse order', () => {
+ imgSnapshotTest(
+ `flowchart-elk LR
+ a -->b
+ subgraph A
+ B
+ end
+ subgraph B
+ b
+ end
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+
+ it('52-elk: handle nested subgraphs in several levels', () => {
+ imgSnapshotTest(
+ `flowchart-elk TB
+ b-->B
+ a-->c
+ subgraph O
+ A
+ end
+ subgraph B
+ c
+ end
+ subgraph A
+ a
+ b
+ B
+ end
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+
+ it('53-elk: handle nested subgraphs with edges in and out', () => {
+ imgSnapshotTest(
+ `flowchart-elk TB
+ internet
+ nat
+ routeur
+ lb1
+ lb2
+ compute1
+ compute2
+ subgraph project
+ routeur
+ nat
+ subgraph subnet1
+ compute1
+ lb1
+ end
+ subgraph subnet2
+ compute2
+ lb2
+ end
+ end
+ internet --> routeur
+ routeur --> subnet1 & subnet2
+ subnet1 & subnet2 --> nat --> internet
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+
+ it('54-elk: handle nested subgraphs with outgoing links', () => {
+ imgSnapshotTest(
+ `flowchart-elk TD
+ subgraph main
+ subgraph subcontainer
+ subcontainer-child
+ end
+ subcontainer-child--> subcontainer-sibling
+ end
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+
+ it('55-elk: handle nested subgraphs with outgoing links 2', () => {
+ imgSnapshotTest(
+ `flowchart-elk TD
+
+subgraph one[One]
+ subgraph sub_one[Sub One]
+ _sub_one
+ end
+ subgraph sub_two[Sub Two]
+ _sub_two
+ end
+ _one
+end
+
+%% here, either the first or the second one
+sub_one --> sub_two
+_one --> b
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+
+ it('56-elk: handle nested subgraphs with outgoing links 3', () => {
+ imgSnapshotTest(
+ `flowchart-elk TB
+ subgraph container_Beta
+ process_C-->Process_D
+ end
+ subgraph container_Alpha
+ process_A-->process_B
+ process_A-->|messages|process_C
+ end
+ process_B-->|via_AWSBatch|container_Beta
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it.skip('57-elk: handle nested subgraphs with outgoing links 4', () => {
+ imgSnapshotTest(
+ `flowchart-elk LR
+subgraph A
+a -->b
+end
+subgraph B
+b
+end
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+
+ it('57-elk: handle nested subgraphs with outgoing links 2', () => {
+ imgSnapshotTest(
+ `flowchart-elk TB
+ c1-->a2
+ subgraph one
+ a1-->a2
+ end
+ subgraph two
+ b1-->b2
+ end
+ subgraph three
+ c1-->c2
+ end
+ one --> two
+ three --> two
+ two --> c2
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('57.x: handle nested subgraphs with outgoing links 5', () => {
+ imgSnapshotTest(
+ `%% this does not produce the desired result
+flowchart-elk TB
+ subgraph container_Beta
+ process_C-->Process_D
+ end
+ subgraph container_Alpha
+ process_A-->process_B
+ process_B-->|via_AWSBatch|container_Beta
+ process_A-->|messages|process_C
+ end
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('58-elk: handle styling with style expressions', () => {
+ imgSnapshotTest(
+ `
+ flowchart-elk LR
+ id1(Start)-->id2(Stop)
+ style id1 fill:#f9f,stroke:#333,stroke-width:4px
+ style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('59-elk: handle styling of subgraphs and links', () => {
+ imgSnapshotTest(
+ `
+flowchart-elk TD
+ A[Christmas] ==> D
+ A[Christmas] -->|Get money| B(Go shopping)
+ A[Christmas] ==> C
+ subgraph T ["Test"]
+ A
+ B
+ C
+ end
+
+ classDef Test fill:#F84E68,stroke:#333,color:white;
+ class A,T Test
+ classDef TestSub fill:green;
+ class T TestSub
+ linkStyle 0,1 color:orange, stroke: orange;
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('60-elk: handle styling for all node shapes - v2', () => {
+ imgSnapshotTest(
+ `
+ flowchart-elk LR
+ A[red text] -->|default style| B(blue text)
+ C([red text]) -->|default style| D[[blue text]]
+ E[(red text)] -->|default style| F((blue text))
+ G>red text] -->|default style| H{blue text}
+ I{{red text}} -->|default style| J[/blue text/]
+ K[\\ red text\\] -->|default style| L[/blue text\\]
+ M[\\ red text/] -->|default style| N[blue text];
+ O(((red text))) -->|default style| P(((blue text)));
+ linkStyle default color:Sienna;
+ style A stroke:#ff0000,fill:#ffcccc,color:#ff0000;
+ style B stroke:#0000ff,fill:#ccccff,color:#0000ff;
+ style C stroke:#ff0000,fill:#ffcccc,color:#ff0000;
+ style D stroke:#0000ff,fill:#ccccff,color:#0000ff;
+ style E stroke:#ff0000,fill:#ffcccc,color:#ff0000;
+ style F stroke:#0000ff,fill:#ccccff,color:#0000ff;
+ style G stroke:#ff0000,fill:#ffcccc,color:#ff0000;
+ style H stroke:#0000ff,fill:#ccccff,color:#0000ff;
+ style I stroke:#ff0000,fill:#ffcccc,color:#ff0000;
+ style J stroke:#0000ff,fill:#ccccff,color:#0000ff;
+ style K stroke:#ff0000,fill:#ffcccc,color:#ff0000;
+ style L stroke:#0000ff,fill:#ccccff,color:#0000ff;
+ style M stroke:#ff0000,fill:#ffcccc,color:#ff0000;
+ style N stroke:#0000ff,fill:#ccccff,color:#0000ff;
+ style O stroke:#ff0000,fill:#ffcccc,color:#ff0000;
+ style P stroke:#0000ff,fill:#ccccff,color:#0000ff;
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose', logLevel: 2 }
+ );
+ });
+ it('61-elk: fontawesome icons in edge labels', () => {
+ imgSnapshotTest(
+ `
+ flowchart-elk TD
+ C -->|fa:fa-car Car| F[fa:fa-car Car]
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('62-elk: should render styled subgraphs', () => {
+ imgSnapshotTest(
+ `
+ flowchart-elk TB
+ A
+ B
+ subgraph foo[Foo SubGraph]
+ C
+ D
+ end
+ subgraph bar[Bar SubGraph]
+ E
+ F
+ end
+ G
+
+ A-->B
+ B-->C
+ C-->D
+ B-->D
+ D-->E
+ E-->A
+ E-->F
+ F-->D
+ F-->G
+ B-->G
+ G-->D
+
+ style foo fill:#F99,stroke-width:2px,stroke:#F0F,color:darkred
+ style bar fill:#999,stroke-width:10px,stroke:#0F0,color:blue
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('63-elk: title on subgraphs should be themable', () => {
+ imgSnapshotTest(
+ `
+ %%{init:{"theme":"base", "themeVariables": {"primaryColor":"#411d4e", "titleColor":"white", "darkMode":true}}}%%
+ flowchart-elk LR
+ subgraph A
+ a --> b
+ end
+ subgraph B
+ i -->f
+ end
+ A --> B
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('65-elk: text-color from classes', () => {
+ imgSnapshotTest(
+ `
+ flowchart-elk LR
+ classDef dark fill:#000,stroke:#000,stroke-width:4px,color:#fff
+ Lorem --> Ipsum --> Dolor
+ class Lorem,Dolor dark
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('66-elk: More nested subgraph cases (TB)', () => {
+ imgSnapshotTest(
+ `
+flowchart-elk TB
+ subgraph two
+ b1
+ end
+ subgraph three
+ c2
+ end
+
+ three --> two
+ two --> c2
+
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('67-elk: More nested subgraph cases (RL)', () => {
+ imgSnapshotTest(
+ `
+flowchart-elk RL
+ subgraph two
+ b1
+ end
+ subgraph three
+ c2
+ end
+
+ three --> two
+ two --> c2
+
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('68-elk: More nested subgraph cases (BT)', () => {
+ imgSnapshotTest(
+ `
+flowchart-elk BT
+ subgraph two
+ b1
+ end
+ subgraph three
+ c2
+ end
+
+ three --> two
+ two --> c2
+
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('69-elk: More nested subgraph cases (LR)', () => {
+ imgSnapshotTest(
+ `
+flowchart-elk LR
+ subgraph two
+ b1
+ end
+ subgraph three
+ c2
+ end
+
+ three --> two
+ two --> c2
+
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('70-elk: Handle nested subgraph cases (TB) link out and link between subgraphs', () => {
+ imgSnapshotTest(
+ `
+flowchart-elk TB
+ subgraph S1
+ sub1 -->sub2
+ end
+ subgraph S2
+ sub4
+ end
+ S1 --> S2
+ sub1 --> sub4
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('71-elk: Handle nested subgraph cases (RL) link out and link between subgraphs', () => {
+ imgSnapshotTest(
+ `
+flowchart-elk RL
+ subgraph S1
+ sub1 -->sub2
+ end
+ subgraph S2
+ sub4
+ end
+ S1 --> S2
+ sub1 --> sub4
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('72-elk: Handle nested subgraph cases (BT) link out and link between subgraphs', () => {
+ imgSnapshotTest(
+ `
+flowchart-elk BT
+ subgraph S1
+ sub1 -->sub2
+ end
+ subgraph S2
+ sub4
+ end
+ S1 --> S2
+ sub1 --> sub4
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('74-elk: Handle nested subgraph cases (RL) link out and link between subgraphs', () => {
+ imgSnapshotTest(
+ `
+flowchart-elk RL
+ subgraph S1
+ sub1 -->sub2
+ end
+ subgraph S2
+ sub4
+ end
+ S1 --> S2
+ sub1 --> sub4
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('74-elk: Handle labels for multiple edges from and to the same couple of nodes', () => {
+ imgSnapshotTest(
+ `
+flowchart-elk RL
+ subgraph one
+ a1 -- l1 --> a2
+ a1 -- l2 --> a2
+ end
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+
+ it('76-elk: handle unicode encoded character with HTML labels true', () => {
+ imgSnapshotTest(
+ `flowchart-elk TB
+ a{{"Lorem 'ipsum' dolor 'sit' amet, 'consectetur' adipiscing 'elit'."}}
+ --> b{{"Lorem #quot;ipsum#quot; dolor #quot;sit#quot; amet,#quot;consectetur#quot; adipiscing #quot;elit#quot;."}}
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+
+ it('2050-elk: handling of different rendering direction in subgraphs', () => {
+ imgSnapshotTest(
+ `
+ flowchart-elk LR
+
+ subgraph TOP
+ direction TB
+ subgraph B1
+ direction RL
+ i1 -->f1
+ end
+ subgraph B2
+ direction BT
+ i2 -->f2
+ end
+ end
+ A --> TOP --> B
+ B1 --> B2
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+
+ it('2388-elk: handling default in the node name', () => {
+ imgSnapshotTest(
+ `
+ flowchart-elk LR
+ default-index.js --> dot.template.js
+ index.js --> module-utl.js
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('2824-elk: Clipping of edges', () => {
+ imgSnapshotTest(
+ `
+ flowchart-elk TD
+ A --> B
+ A --> C
+ B --> C
+ `,
+ { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ );
+ });
+ it('1433-elk: should render a titled flowchart with titleTopMargin set to 0', () => {
+ imgSnapshotTest(
+ `---
+title: Simple flowchart
+---
+flowchart-elk TD
+A --> B
+`,
+ { titleTopMargin: 0 }
+ );
+ });
+});
diff --git a/cypress/integration/rendering/gantt.spec.js b/cypress/integration/rendering/gantt.spec.js
index 325cca065..c0156eee3 100644
--- a/cypress/integration/rendering/gantt.spec.js
+++ b/cypress/integration/rendering/gantt.spec.js
@@ -310,38 +310,6 @@ describe('Gantt diagram', () => {
);
});
- it('should render accessibility tags', function () {
- const expectedTitle = 'Gantt Diagram';
- const expectedAccDescription = 'Tasks for Q4';
- renderGraph(
- `
- gantt
- accTitle: ${expectedTitle}
- accDescr: ${expectedAccDescription}
- dateFormat YYYY-MM-DD
- section Section
- A task :a1, 2014-01-01, 30d
- `,
- {}
- );
- cy.get('svg').should((svg) => {
- const el = svg.get(0);
- const children = [...el.children];
-
- const titleEl = children.find(function (node) {
- return node.tagName === 'title';
- });
- const descriptionEl = children.find(function (node) {
- return node.tagName === 'desc';
- });
-
- expect(titleEl).to.exist;
- expect(titleEl.textContent).to.equal(expectedTitle);
- expect(descriptionEl).to.exist;
- expect(descriptionEl.textContent).to.equal(expectedAccDescription);
- });
- });
-
it('should render a gantt diagram with tick is 15 minutes', () => {
imgSnapshotTest(
`
diff --git a/cypress/integration/rendering/mindmap.spec.ts b/cypress/integration/rendering/mindmap.spec.ts
index 62c7e785b..4663f6225 100644
--- a/cypress/integration/rendering/mindmap.spec.ts
+++ b/cypress/integration/rendering/mindmap.spec.ts
@@ -1,4 +1,4 @@
-import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
+import { imgSnapshotTest } from '../../helpers/util.js';
/**
* Check whether the SVG Element has a Mindmap root
@@ -158,7 +158,6 @@ mindmap
undefined,
shouldHaveRoot
);
- cy.get('svg');
});
it('rounded rect shape', () => {
imgSnapshotTest(
@@ -172,7 +171,6 @@ mindmap
undefined,
shouldHaveRoot
);
- cy.get('svg');
});
it('circle shape', () => {
imgSnapshotTest(
@@ -186,7 +184,6 @@ mindmap
undefined,
shouldHaveRoot
);
- cy.get('svg');
});
it('default shape', () => {
imgSnapshotTest(
@@ -198,7 +195,6 @@ mindmap
undefined,
shouldHaveRoot
);
- cy.get('svg');
});
it('adding children', () => {
imgSnapshotTest(
@@ -212,7 +208,6 @@ mindmap
undefined,
shouldHaveRoot
);
- cy.get('svg');
});
it('adding grand children', () => {
imgSnapshotTest(
@@ -227,7 +222,6 @@ mindmap
undefined,
shouldHaveRoot
);
- cy.get('svg');
});
/* The end */
});
diff --git a/cypress/integration/rendering/requirement.spec.js b/cypress/integration/rendering/requirement.spec.js
index 8a8d188ff..0bf9014bf 100644
--- a/cypress/integration/rendering/requirement.spec.js
+++ b/cypress/integration/rendering/requirement.spec.js
@@ -46,69 +46,4 @@ describe('Requirement diagram', () => {
);
cy.get('svg');
});
-
- it('should render accessibility tags', function () {
- const expectedTitle = 'Gantt Diagram';
- const expectedAccDescription = 'Tasks for Q4';
- renderGraph(
- `
- requirementDiagram
- accTitle: ${expectedTitle}
- accDescr: ${expectedAccDescription}
-
- requirement test_req {
- id: 1
- text: the test text.
- risk: high
- verifymethod: test
- }
-
- functionalRequirement test_req2 {
- id: 1.1
- text: the second test text.
- risk: low
- verifymethod: inspection
- }
-
- performanceRequirement test_req3 {
- id: 1.2
- text: the third test text.
- risk: medium
- verifymethod: demonstration
- }
-
- element test_entity {
- type: simulation
- }
-
- element test_entity2 {
- type: word doc
- docRef: reqs/test_entity
- }
-
-
- test_entity - satisfies -> test_req2
- test_req - traces -> test_req2
- test_req - contains -> test_req3
- test_req <- copies - test_entity2
- `,
- {}
- );
- cy.get('svg').should((svg) => {
- const el = svg.get(0);
- const children = [...el.children];
-
- const titleEl = children.find(function (node) {
- return node.tagName === 'title';
- });
- const descriptionEl = children.find(function (node) {
- return node.tagName === 'desc';
- });
-
- expect(titleEl).to.exist;
- expect(titleEl.textContent).to.equal(expectedTitle);
- expect(descriptionEl).to.exist;
- expect(descriptionEl.textContent).to.equal(expectedAccDescription);
- });
- });
});
diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js
index b5ff92c8c..1f063c13e 100644
--- a/cypress/integration/rendering/sequencediagram.spec.js
+++ b/cypress/integration/rendering/sequencediagram.spec.js
@@ -3,6 +3,42 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
context('Sequence diagram', () => {
+ it('should render a sequence diagram with boxes', () => {
+ renderGraph(
+ `
+ sequenceDiagram
+ box LightGrey Alice and Bob
+ participant Alice
+ participant Bob
+ end
+ participant John as John Second Line
+ Alice ->> Bob: Hello Bob, how are you?
+ Bob-->>John: How about you John?
+ Bob--x Alice: I am good thanks!
+ Bob-x John: I am good thanks!
+ Note right of John: Bob thinks a long long time, so long that the text does not fit on a row.
+ Bob-->Alice: Checking with John...
+ alt either this
+ Alice->>John: Yes
+ else or this
+ Alice->>John: No
+ else or this will happen
+ Alice->John: Maybe
+ end
+ par this happens in parallel
+ Alice -->> Bob: Parallel message 1
+ and
+ Alice -->> John: Parallel message 2
+ end
+ `,
+ { sequence: { useMaxWidth: false } }
+ );
+ cy.get('svg').should((svg) => {
+ const width = parseFloat(svg.attr('width'));
+ expect(width).to.be.within(830 * 0.95, 830 * 1.05);
+ expect(svg).to.not.have.attr('style');
+ });
+ });
it('should render a simple sequence diagram', () => {
imgSnapshotTest(
`
diff --git a/cypress/integration/rendering/stateDiagram-v2.spec.js b/cypress/integration/rendering/stateDiagram-v2.spec.js
index 0eca01873..047e240fc 100644
--- a/cypress/integration/rendering/stateDiagram-v2.spec.js
+++ b/cypress/integration/rendering/stateDiagram-v2.spec.js
@@ -328,7 +328,7 @@ describe('State diagram', () => {
}
);
});
- it('v2 it should be possibel to use a choice', () => {
+ it('v2 it should be possible to use a choice', () => {
imgSnapshotTest(
`
stateDiagram-v2
diff --git a/cypress/integration/rendering/timeline.spec.ts b/cypress/integration/rendering/timeline.spec.ts
new file mode 100644
index 000000000..6fae82fb4
--- /dev/null
+++ b/cypress/integration/rendering/timeline.spec.ts
@@ -0,0 +1,164 @@
+import { imgSnapshotTest } from '../../helpers/util.js';
+
+describe('Timeline diagram', () => {
+ it('1: should render a simple timeline with no specific sections', () => {
+ imgSnapshotTest(
+ `timeline
+ title History of Social Media Platform
+ 2002 : LinkedIn
+ 2004 : Facebook : Google
+ 2005 : Youtube
+ 2006 : Twitter
+ `,
+ {}
+ );
+ });
+ it('2: should render a timeline diagram with sections', () => {
+ imgSnapshotTest(
+ `timeline
+ title Timeline of Industrial Revolution
+ section 17th-20th century
+ Industry 1.0 : Machinery, Water power, Steam power
+ Industry 2.0 : Electricity, Internal combustion engine, Mass production
+ Industry 3.0 : Electronics, Computers, Automation
+ section 21st century
+ Industry 4.0 : Internet, Robotics, Internet of Things
+ Industry 5.0 : Artificial intelligence, Big data,3D printing
+ `,
+ {}
+ );
+ });
+ it('3: should render a complex timeline with sections, and long events text with ', () => {
+ imgSnapshotTest(
+ `timeline
+ title England's History Timeline
+ section Stone Age
+ 7600 BC : Britain's oldest known house was built in Orkney, Scotland
+ 6000 BC : Sea levels rise and Britain becomes an island. The people who live here are hunter-gatherers.
+ section Broze Age
+ 2300 BC : People arrive from Europe and settle in Britain. They bring farming and metalworking.
+ : New styles of pottery and ways of burying the dead appear.
+ 2200 BC : The last major building works are completed at Stonehenge. People now bury their dead in stone circles.
+ : The first metal objects are made in Britain.Some other nice things happen. it is a good time to be alive.
+ `,
+ {}
+ );
+ });
+ it('4: should render a simple timeline with directives and disableMultiColor:true ', () => {
+ imgSnapshotTest(
+ `%%{init: { 'logLevel': 'debug', 'theme': 'base', 'timeline': {'disableMulticolor': true}}}%%
+ timeline
+ title History of Social Media Platform
+ 2002 : LinkedIn
+ 2004 : Facebook : Google
+ 2005 : Youtube
+ 2006 : Twitter
+ `,
+ {}
+ );
+ });
+ it('5: should render a simple timeline with directive overriden colors', () => {
+ imgSnapshotTest(
+ ` %%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
+ 'cScale0': '#ff0000',
+ 'cScale1': '#00ff00',
+ 'cScale2': '#0000ff'
+ } } }%%
+ timeline
+ title History of Social Media Platform
+ 2002 : LinkedIn
+ 2004 : Facebook : Google
+ 2005 : Youtube
+ 2006 : Twitter
+ 2007 : Tumblr
+ 2008 : Instagram
+ 2010 : Pinterest
+ `,
+ {}
+ );
+ });
+ it('6: should render a simple timeline in base theme', () => {
+ imgSnapshotTest(
+ `%%{init: { 'logLevel': 'debug', 'theme': 'base' } }%%
+ timeline
+ title History of Social Media Platform
+ 2002 : LinkedIn
+ 2004 : Facebook : Google
+ 2005 : Youtube
+ 2006 : Twitter
+ 2007 : Tumblr
+ 2008 : Instagram
+ 2010 : Pinterest
+ `,
+ {}
+ );
+ });
+
+ it('7: should render a simple timeline in default theme', () => {
+ imgSnapshotTest(
+ `%%{init: { 'logLevel': 'debug', 'theme': 'default' } }%%
+ timeline
+ title History of Social Media Platform
+ 2002 : LinkedIn
+ 2004 : Facebook : Google
+ 2005 : Youtube
+ 2006 : Twitter
+ 2007 : Tumblr
+ 2008 : Instagram
+ 2010 : Pinterest
+ `,
+ {}
+ );
+ });
+
+ it('8: should render a simple timeline in dark theme', () => {
+ imgSnapshotTest(
+ `%%{init: { 'logLevel': 'debug', 'theme': 'dark' } }%%
+ timeline
+ title History of Social Media Platform
+ 2002 : LinkedIn
+ 2004 : Facebook : Google
+ 2005 : Youtube
+ 2006 : Twitter
+ 2007 : Tumblr
+ 2008 : Instagram
+ 2010 : Pinterest
+ `,
+ {}
+ );
+ });
+
+ it('9: should render a simple timeline in neutral theme', () => {
+ imgSnapshotTest(
+ `%%{init: { 'logLevel': 'debug', 'theme': 'neutral' } }%%
+ timeline
+ title History of Social Media Platform
+ 2002 : LinkedIn
+ 2004 : Facebook : Google
+ 2005 : Youtube
+ 2006 : Twitter
+ 2007 : Tumblr
+ 2008 : Instagram
+ 2010 : Pinterest
+ `,
+ {}
+ );
+ });
+
+ it('10: should render a simple timeline in forest theme', () => {
+ imgSnapshotTest(
+ `%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
+ timeline
+ title History of Social Media Platform
+ 2002 : LinkedIn
+ 2004 : Facebook : Google
+ 2005 : Youtube
+ 2006 : Twitter
+ 2007 : Tumblr
+ 2008 : Instagram
+ 2010 : Pinterest
+ `,
+ {}
+ );
+ });
+});
diff --git a/cypress/platform/ashish2.html b/cypress/platform/ashish2.html
new file mode 100644
index 000000000..bcea4f4cc
--- /dev/null
+++ b/cypress/platform/ashish2.html
@@ -0,0 +1,231 @@
+
+
+
+
+
+
+
+
+
+
+
Security check
+
+ timeline
+ title My day
+ section section with no tasks
+ section Go to work at the dog office
+ 1930 : first step : second step is a long step
+ : third step
+ 1940 : fourth step : fifth step
+ section Go home
+ 1950 : India got independent and already won war against Pakistan
+ 1960 : India fights poverty, looses war to China and gets nuclear weapons from USA and USSR
+ 1970 : Green Revolution comes to india
+ section Another section with no tasks
+ I am a big big big tasks
+ I am not so big tasks
+
+ timeline
+ title England's History Timeline
+ section Stone Age
+ 7600 BC : Britain's oldest known house was built in Orkney, Scotland
+ 6000 BC : Sea levels rise and Britain becomes an island. The people who live here are hunter-gatherers.
+ section Broze Age
+ 2300 BC : People arrive from Europe and settle in Britain. They bring farming and metalworking.
+ : New styles of pottery and ways of burying the dead appear.
+ 2200 BC : The last major building works are completed at Stonehenge. People now bury their dead in stone circles.
+ : The first metal objects are made in Britain.Some other nice things happen. it is a good time to be alive.
+
+
+
+ %%{'init': { 'logLevel': 'debug', 'theme': 'default', 'timeline': {'disableMulticolor':false} } }%%
+ timeline
+ title History of Social Media Platform
+ 2002 : LinkedIn
+ 2004 : Facebook : Google : Pixar
+ 2005 : Youtube
+ 2006 : Twitter
+ 2007 : Tumblr
+ 2008s : Instagram
+ 2010 : Pinterest
+