diff --git a/.build/jsonSchema.ts b/.build/jsonSchema.ts
index 7a700c1e2..48a9883de 100644
--- a/.build/jsonSchema.ts
+++ b/.build/jsonSchema.ts
@@ -27,6 +27,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [
'block',
'packet',
'architecture',
+ 'radar',
] as const;
/**
diff --git a/.changeset/angry-bags-brake.md b/.changeset/angry-bags-brake.md
deleted file mode 100644
index 472e486ec..000000000
--- a/.changeset/angry-bags-brake.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-fix: architecture diagrams no longer grow to extreme heights due to conflicting alignments
diff --git a/.cspell/code-terms.txt b/.cspell/code-terms.txt
index f4862006f..e28e9d735 100644
--- a/.cspell/code-terms.txt
+++ b/.cspell/code-terms.txt
@@ -47,13 +47,13 @@ edgesep
EMPTYSTR
enddate
ERDIAGRAM
+eslint
flatmap
forwardable
frontmatter
funs
gantt
GENERICTYPE
-getBoundarys
grammr
graphtype
halign
diff --git a/.cspell/contributors.txt b/.cspell/contributors.txt
index b7f52f8d0..80f4df22a 100644
--- a/.cspell/contributors.txt
+++ b/.cspell/contributors.txt
@@ -2,8 +2,10 @@
Ashish Jain
cpettitt
Dong Cai
+knsv
+Knut Sveidqvist
Nikolay Rozhkov
Peng Xiao
Per Brolin
+Sidharth Vinod
subhash-halder
-Vinod Sidharth
diff --git a/.cspell/libraries.txt b/.cspell/libraries.txt
index 73a2dceeb..feee10fd1 100644
--- a/.cspell/libraries.txt
+++ b/.cspell/libraries.txt
@@ -26,6 +26,8 @@ dompurify
elkjs
fcose
fontawesome
+Fonticons
+Forgejo
Foswiki
Gitea
graphlib
diff --git a/.cspell/mermaid-terms.txt b/.cspell/mermaid-terms.txt
index cb6db41de..b0cfa0a1d 100644
--- a/.cspell/mermaid-terms.txt
+++ b/.cspell/mermaid-terms.txt
@@ -13,11 +13,10 @@ gitgraph
gzipped
handDrawn
kanban
-knsv
-Knut
marginx
marginy
Markdownish
+mermaidchart
mermaidjs
mindmap
mindmaps
@@ -35,7 +34,6 @@ sandboxed
siebling
statediagram
substate
-Sveidqvist
unfixable
Viewbox
viewports
diff --git a/.esbuild/build.ts b/.esbuild/build.ts
index 423e8f047..72c0af869 100644
--- a/.esbuild/build.ts
+++ b/.esbuild/build.ts
@@ -1,5 +1,5 @@
import { build } from 'esbuild';
-import { mkdir, writeFile } from 'node:fs/promises';
+import { cp, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
import { packageOptions } from '../.build/common.js';
import { generateLangium } from '../.build/generateLangium.js';
import type { MermaidBuildOptions } from './util.js';
@@ -31,6 +31,27 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
// mermaid.js
{ ...iifeOptions },
// mermaid.min.js
+ { ...iifeOptions, minify: true, metafile: shouldVisualize },
+ // mermaid.tiny.min.js
+ {
+ ...iifeOptions,
+ minify: true,
+ includeLargeFeatures: false,
+ metafile: shouldVisualize,
+ sourcemap: false,
+ }
+ );
+ }
+ if (entryName === 'mermaid-zenuml') {
+ const iifeOptions: MermaidBuildOptions = {
+ ...commonOptions,
+ format: 'iife',
+ globalName: 'mermaid-zenuml',
+ };
+ buildConfigs.push(
+ // mermaid-zenuml.js
+ { ...iifeOptions },
+ // mermaid-zenuml.min.js
{ ...iifeOptions, minify: true, metafile: shouldVisualize }
);
}
@@ -57,6 +78,21 @@ const handler = (e) => {
process.exit(1);
};
+const buildTinyMermaid = async () => {
+ await mkdir('./packages/tiny/dist', { recursive: true });
+ await rename(
+ './packages/mermaid/dist/mermaid.tiny.min.js',
+ './packages/tiny/dist/mermaid.tiny.js'
+ );
+ // Copy version from mermaid's package.json to tiny's package.json
+ const mermaidPkg = JSON.parse(await readFile('./packages/mermaid/package.json', 'utf8'));
+ const tinyPkg = JSON.parse(await readFile('./packages/tiny/package.json', 'utf8'));
+ tinyPkg.version = mermaidPkg.version;
+
+ await writeFile('./packages/tiny/package.json', JSON.stringify(tinyPkg, null, 2) + '\n');
+ await cp('./packages/mermaid/CHANGELOG.md', './packages/tiny/CHANGELOG.md');
+};
+
const main = async () => {
await generateLangium();
await mkdir('stats', { recursive: true });
@@ -65,6 +101,7 @@ const main = async () => {
for (const pkg of packageNames) {
await buildPackage(pkg).catch(handler);
}
+ await buildTinyMermaid();
};
void main();
diff --git a/.esbuild/util.ts b/.esbuild/util.ts
index 6d6d1d59b..3a0ec6b41 100644
--- a/.esbuild/util.ts
+++ b/.esbuild/util.ts
@@ -14,6 +14,7 @@ export interface MermaidBuildOptions extends BuildOptions {
metafile: boolean;
format: 'esm' | 'iife';
options: PackageOptions;
+ includeLargeFeatures: boolean;
}
export const defaultOptions: Omit
- 📖 Documentation | 🚀 Getting Started | 🌐 CDN | 🙌 Join Us + 📖 Documentation | 🚀 Getting Started | 🌐 CDN | 🙌 Join Us
简体中文
@@ -33,7 +33,7 @@ Try Live Editor previews of future releases:
+
## Table of content
@@ -253,6 +253,34 @@ pie
### Git graph [experimental - live editor]
+```
+gitGraph
+ commit
+ commit
+ branch develop
+ checkout develop
+ commit
+ commit
+ checkout main
+ merge develop
+ commit
+ commit
+```
+
+```mermaid
+gitGraph
+ commit
+ commit
+ branch develop
+ checkout develop
+ commit
+ commit
+ checkout main
+ merge develop
+ commit
+ commit
+```
+
### Bar chart (using gantt chart) [docs - live editor]
```
@@ -419,7 +447,7 @@ For public sites, it can be precarious to retrieve text from users on the intern
As an extra level of security for sites with external users we are happy to introduce a new security level in which the diagram is rendered in a sandboxed iframe preventing javascript in the code from being executed. This is a great step forward for better security.
-_Unfortunately you can not have a cake and eat it at the same time which in this case means that some of the interactive functionality gets blocked along with the possible malicious code._
+_Unfortunately you cannot have a cake and eat it at the same time which in this case means that some of the interactive functionality gets blocked along with the possible malicious code._
## Reporting vulnerabilities
@@ -435,7 +463,7 @@ A quick note from Knut Sveidqvist:
>
> _Thank you to [Tyler Long](https://github.com/tylerlong) who has been a collaborator since April 2017._
>
-> _Thank you to the ever-growing list of [contributors](https://github.com/knsv/mermaid/graphs/contributors) that brought the project this far!_
+> _Thank you to the ever-growing list of [contributors](https://github.com/mermaid-js/mermaid/graphs/contributors) that brought the project this far!_
---
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 942f54bff..cd44e56b6 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -15,7 +15,7 @@ Mermaid
实时编辑器!
- 📖 文档 | 🚀 入门 | 🌐 CDN | 🙌 加入我们 + 📖 文档 | 🚀 入门 | 🌐 CDN | 🙌 加入我们
English
@@ -34,7 +34,7 @@ Mermaid
[](https://app.codecov.io/github/mermaid-js/mermaid/tree/develop)
[](https://www.jsdelivr.com/package/npm/mermaid)
[](https://www.npmjs.com/package/mermaid)
-[](https://discord.gg/AgrbSrBer3)
+[](https://discord.gg/sKeNQX4Wtj)
[](https://twitter.com/mermaidjs_)
@@ -43,13 +43,13 @@ Mermaid
**感谢所有参与进来提交 PR,解答疑问的人们! 🙏**
-
+
## 关于 Mermaid
-Mermaid 是一个基于 Javascript 的图表绘制工具,通过解析类 Markdown 的文本语法来实现图表的创建和动态修改。Mermaid 诞生的主要目的是让文档的更新能够及时跟上开发进度。
+Mermaid 是一个基于 JavaScript 的图表绘制工具,通过解析类 Markdown 的文本语法来实现图表的创建和动态修改。Mermaid 诞生的主要目的是让文档的更新能够及时跟上开发进度。
> Doc-Rot 是 Mermaid 致力于解决的一个难题。
@@ -358,7 +358,7 @@ _很不幸的是,鱼与熊掌不可兼得,在这个场景下它意味着在
> _特别感谢 [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),没有你们,就没有这个项目的今天!_
+> _感谢越来越多的 [贡献者们](https://github.com/mermaid-js/mermaid/graphs/contributors),没有你们,就没有这个项目的今天!_
---
diff --git a/cypress.config.ts b/cypress.config.ts
index b0257b9b2..50ea940e9 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -1,8 +1,8 @@
import eyesPlugin from '@applitools/eyes-cypress';
import { registerArgosTask } from '@argos-ci/cypress/task';
-import coverage from '@cypress/code-coverage/task';
+import coverage from '@cypress/code-coverage/task.js';
import { defineConfig } from 'cypress';
-import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin';
+import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin.js';
import cypressSplit from 'cypress-split';
export default eyesPlugin(
@@ -23,12 +23,10 @@ export default eyesPlugin(
});
// copy any needed variables from process.env to config.env
config.env.useAppli = process.env.USE_APPLI ? true : false;
- config.env.useArgos = !!process.env.CI;
+ config.env.useArgos = process.env.RUN_VISUAL_TEST === 'true';
if (config.env.useArgos) {
- registerArgosTask(on, config, {
- token: 'fc3a35cf5200db928d65b2047861582d9444032b',
- });
+ registerArgosTask(on, config);
} else {
addMatchImageSnapshotPlugin(on, config);
}
diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts
index 52da4a72e..81b7036af 100644
--- a/cypress/helpers/util.ts
+++ b/cypress/helpers/util.ts
@@ -132,3 +132,10 @@ export const verifyScreenshot = (name: string): void => {
cy.matchImageSnapshot(name);
}
};
+
+export const verifyNumber = (value: number, expected: number, deltaPercent = 10): void => {
+ expect(value).to.be.within(
+ expected * (1 - deltaPercent / 100),
+ expected * (1 + deltaPercent / 100)
+ );
+};
diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js
index ad6b21e29..b48a197a4 100644
--- a/cypress/integration/other/configuration.spec.js
+++ b/cypress/integration/other/configuration.spec.js
@@ -69,7 +69,9 @@ describe('Configuration', () => {
.and('include', 'url(#');
});
});
- it('should handle arrowMarkerAbsolute explicitly set to "false" as false', () => {
+ // This has been broken for a long time, but something about the Cypress environment was
+ // rewriting the URL to be relative, causing the test to incorrectly pass.
+ it.skip('should handle arrowMarkerAbsolute explicitly set to "false" as false', () => {
renderGraph(
`graph TD
A[Christmas] -->|Get money| B(Go shopping)
@@ -112,7 +114,7 @@ describe('Configuration', () => {
.first()
.should('have.attr', 'marker-end')
.should('exist')
- .and('include', 'url(http://localhost');
+ .and('include', 'url(http\\:\\/\\/localhost');
});
});
it('should not taint the initial configuration when using multiple directives', () => {
diff --git a/cypress/integration/other/interaction.spec.js b/cypress/integration/other/interaction.spec.js
index 857141b5b..e57701fd9 100644
--- a/cypress/integration/other/interaction.spec.js
+++ b/cypress/integration/other/interaction.spec.js
@@ -20,7 +20,7 @@ describe('Interaction', () => {
});
it('Graph: should handle a click on a node with a bound url', () => {
- // When there is a URL, cy.contains selects the a tag instead of the span. The .node is a child of a, so we have to use find instead of parent.
+ // When there is a URL, `cy.contains()` selects the `a` tag instead of the `span` tag. The .node is a child of `a`, so we have to use `find()` instead of `parent`.
cy.contains('URLTest1').find('.node').click();
cy.location().should(({ href }) => {
expect(href).to.eq('http://localhost:9000/empty.html');
@@ -146,7 +146,7 @@ describe('Interaction', () => {
});
});
- describe('Interaction - security level other, missspelling', () => {
+ describe('Interaction - security level other, misspelling', () => {
beforeEach(() => {
cy.visit('http://localhost:9000/click_security_other.html');
});
diff --git a/cypress/integration/rendering/architecture.spec.ts b/cypress/integration/rendering/architecture.spec.ts
index 25326ff80..997a6654e 100644
--- a/cypress/integration/rendering/architecture.spec.ts
+++ b/cypress/integration/rendering/architecture.spec.ts
@@ -19,6 +19,25 @@ describe.skip('architecture diagram', () => {
`
);
});
+ it('should render a simple architecture diagram with titleAndAccessibilities', () => {
+ imgSnapshotTest(
+ `architecture-beta
+ title Simple Architecture Diagram
+ accTitle: Accessibility Title
+ accDescr: Accessibility Description
+ group api(cloud)[API]
+
+ service db(database)[Database] in api
+ service disk1(disk)[Storage] in api
+ service disk2(disk)[Storage] in api
+ service server(server)[Server] in api
+
+ db:L -- R:server
+ disk1:T -- B:server
+ disk2:T -- B:db
+ `
+ );
+ });
it('should render an architecture diagram with groups within groups', () => {
imgSnapshotTest(
`architecture-beta
@@ -172,7 +191,7 @@ describe.skip('architecture diagram', () => {
);
});
- it('should render an architecture diagram with a resonable height', () => {
+ it('should render an architecture diagram with a reasonable height', () => {
imgSnapshotTest(
`architecture-beta
group federated(cloud)[Federated Environment]
diff --git a/cypress/integration/rendering/block.spec.js b/cypress/integration/rendering/block.spec.js
index f5d5103e8..589a30fde 100644
--- a/cypress/integration/rendering/block.spec.js
+++ b/cypress/integration/rendering/block.spec.js
@@ -14,7 +14,7 @@ describe('Block diagram', () => {
);
});
- it('BL2: should handle colums statement in sub-blocks', () => {
+ it('BL2: should handle columns statement in sub-blocks', () => {
imgSnapshotTest(
`block-beta
id1["Hello"]
@@ -30,7 +30,7 @@ describe('Block diagram', () => {
);
});
- it('BL3: should align block widths and handle colums statement in sub-blocks', () => {
+ it('BL3: should align block widths and handle columns statement in sub-blocks', () => {
imgSnapshotTest(
`block-beta
block
@@ -46,7 +46,7 @@ describe('Block diagram', () => {
);
});
- it('BL4: should align block widths and handle colums statements in deeper sub-blocks then 1 level', () => {
+ it('BL4: should align block widths and handle columns statements in deeper sub-blocks then 1 level', () => {
imgSnapshotTest(
`block-beta
columns 1
@@ -66,7 +66,7 @@ describe('Block diagram', () => {
);
});
- it('BL5: should align block widths and handle colums statements in deeper sub-blocks then 1 level (alt)', () => {
+ it('BL5: should align block widths and handle columns statements in deeper sub-blocks then 1 level (alt)', () => {
imgSnapshotTest(
`block-beta
columns 1
@@ -236,7 +236,7 @@ describe('Block diagram', () => {
);
});
- it('BL17: width alignment - blocks shold be equal in width', () => {
+ it('BL17: width alignment - blocks should be equal in width', () => {
imgSnapshotTest(
`block-beta
A("This is the text")
diff --git a/cypress/integration/rendering/classDiagram.spec.js b/cypress/integration/rendering/classDiagram.spec.js
index a98a359ed..e7d201b5d 100644
--- a/cypress/integration/rendering/classDiagram.spec.js
+++ b/cypress/integration/rendering/classDiagram.spec.js
@@ -429,7 +429,7 @@ describe('Class diagram', () => {
classDiagram
class \`This\nTitle\nHas\nMany\nNewlines\` {
+String Also
- -Stirng Many
+ -String Many
#int Members
+And()
-Many()
@@ -443,7 +443,7 @@ describe('Class diagram', () => {
classDiagram
class \`This\nTitle\nHas\nMany\nNewlines\` {
+String Also
- -Stirng Many
+ -String Many
#int Members
+And()
-Many()
@@ -459,7 +459,7 @@ describe('Class diagram', () => {
namespace testingNamespace {
class \`This\nTitle\nHas\nMany\nNewlines\` {
+String Also
- -Stirng Many
+ -String Many
#int Members
+And()
-Many()
diff --git a/cypress/integration/rendering/erDiagram-unified.spec.js b/cypress/integration/rendering/erDiagram-unified.spec.js
new file mode 100644
index 000000000..8cecba21d
--- /dev/null
+++ b/cypress/integration/rendering/erDiagram-unified.spec.js
@@ -0,0 +1,652 @@
+import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
+
+const testOptions = [
+ { description: '', options: { logLevel: 1 } },
+ { description: 'ELK: ', options: { logLevel: 1, layout: 'elk' } },
+ { description: 'HD: ', options: { logLevel: 1, look: 'handDrawn' } },
+];
+
+describe('Entity Relationship Diagram Unified', () => {
+ testOptions.forEach(({ description, options }) => {
+ it(`${description}should render a simple ER diagram`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a simple ER diagram without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render an ER diagram with a recursive relationship`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER ||..o{ CUSTOMER : refers
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with multiple relationships between the same two entities`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER ||--|{ ADDRESS : "invoiced at"
+ CUSTOMER ||--|{ ADDRESS : "receives goods at"
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a cyclical ER diagram`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ A ||--|{ B : likes
+ B ||--|{ C : likes
+ C ||--|{ A : likes
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a not-so-simple ER diagram`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER }|..|{ DELIVERY-ADDRESS : has
+ CUSTOMER ||--o{ ORDER : places
+ CUSTOMER ||--o{ INVOICE : "liable for"
+ DELIVERY-ADDRESS ||--o{ ORDER : receives
+ INVOICE ||--|{ ORDER : covers
+ ORDER ||--|{ ORDER-ITEM : includes
+ PRODUCT-CATEGORY ||--|{ PRODUCT : contains
+ PRODUCT ||--o{ ORDER-ITEM : "ordered in"
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a not-so-simple ER diagram without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER }|..|{ DELIVERY-ADDRESS : has
+ CUSTOMER ||--o{ ORDER : places
+ CUSTOMER ||--o{ INVOICE : "liable for"
+ DELIVERY-ADDRESS ||--o{ ORDER : receives
+ INVOICE ||--|{ ORDER : covers
+ ORDER ||--|{ ORDER-ITEM : includes
+ PRODUCT-CATEGORY ||--|{ PRODUCT : contains
+ PRODUCT ||--o{ ORDER-ITEM : "ordered in"
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render multiple ER diagrams`, () => {
+ imgSnapshotTest(
+ [
+ `
+ erDiagram
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ `
+ erDiagram
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ ],
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with blank or empty labels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ BOOK }|..|{ AUTHOR : ""
+ BOOK }|..|{ GENRE : " "
+ AUTHOR }|..|{ GENRE : " "
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities that have no relationships`, () => {
+ renderGraph(
+ `
+ erDiagram
+ DEAD_PARROT
+ HERMIT
+ RECLUSE
+ SOCIALITE }o--o{ SOCIALITE : "interacts with"
+ RECLUSE }o--o{ SOCIALITE : avoids
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with and without attributes`, () => {
+ renderGraph(
+ `
+ erDiagram
+ BOOK { string title }
+ AUTHOR }|..|{ BOOK : writes
+ BOOK { float price }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with generic and array attributes`, () => {
+ renderGraph(
+ `
+ erDiagram
+ BOOK {
+ string title
+ string[] authors
+ type~T~ type
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with generic and array attributes without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ BOOK {
+ string title
+ string[] authors
+ type~T~ type
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with length in attributes type`, () => {
+ renderGraph(
+ `
+ erDiagram
+ CLUSTER {
+ varchar(99) name
+ string(255) description
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with length in attributes type without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ CLUSTER {
+ varchar(99) name
+ string(255) description
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities and attributes with big and small entity names`, () => {
+ renderGraph(
+ `
+ erDiagram
+ PRIVATE_FINANCIAL_INSTITUTION {
+ string name
+ int turnover
+ }
+ PRIVATE_FINANCIAL_INSTITUTION ||..|{ EMPLOYEE : employs
+ EMPLOYEE { bool officer_of_firm }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities and attributes with big and small entity names without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ PRIVATE_FINANCIAL_INSTITUTION {
+ string name
+ int turnover
+ }
+ PRIVATE_FINANCIAL_INSTITUTION ||..|{ EMPLOYEE : employs
+ EMPLOYEE { bool officer_of_firm }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with attributes that begin with asterisk`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ BOOK {
+ int *id
+ string name
+ varchar(99) summary
+ }
+ BOOK }o..o{ STORE : soldBy
+ STORE {
+ int *id
+ string name
+ varchar(50) address
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with attributes that begin with asterisk without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ BOOK {
+ int *id
+ string name
+ varchar(99) summary
+ }
+ BOOK }o..o{ STORE : soldBy
+ STORE {
+ int *id
+ string name
+ varchar(50) address
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with keys`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name PK
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ float price
+ string author FK
+ string title PK
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with keys without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name PK
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ float price
+ string author FK
+ string title PK
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with comments`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name "comment"
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ string author
+ string title "author comment"
+ float price "price comment"
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with comments without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name "comment"
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ string author
+ string title "author comment"
+ float price "price comment"
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with keys and comments`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name PK "comment"
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ string description
+ float price "price comment"
+ string title PK "title comment"
+ string author FK
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with keys and comments without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name PK "comment"
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ string description
+ float price "price comment"
+ string title PK "title comment"
+ string author FK
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with aliases`, () => {
+ renderGraph(
+ `
+ erDiagram
+ T1 one or zero to one or more T2 : test
+ T2 one or many optionally to zero or one T3 : test
+ T3 zero or more to zero or many T4 : test
+ T4 many(0) to many(1) T5 : test
+ T5 many optionally to one T6 : test
+ T6 only one optionally to only one T1 : test
+ T4 0+ to 1+ T6 : test
+ T1 1 to 1 T3 : test
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a simple ER diagram with a title`, () => {
+ imgSnapshotTest(
+ `---
+ title: simple ER diagram
+ ---
+ erDiagram
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with entity name aliases`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ p[Person] {
+ varchar(64) firstName
+ varchar(64) lastName
+ }
+ c["Customer Account"] {
+ varchar(128) email
+ }
+ p ||--o| c : has
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render relationship labels with line breaks`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ p[Person] {
+ string firstName
+ string lastName
+ }
+ a["Customer Account"] {
+ string email
+ }
+
+ b["Customer Account Secondary"] {
+ string email
+ }
+
+ c["Customer Account Tertiary"] {
+ string email
+ }
+
+ d["Customer Account Nth"] {
+ string email
+ }
+
+ p ||--o| a : "has
one"
+ p ||--o| b : "has
one
two"
+ p ||--o| c : "has
one
two
three"
+ p ||--o| d : "has
one
two
three
...
Nth"
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with unicode text`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ _**testẽζ➕Ø😀㌕ぼ**_ {
+ *__List~List~int~~sdfds__* **driversLicense** PK "***The l😀icense #***"
+ *string(99)~T~~~~~~* firstName "Only __99__
characters are a
llowed dsfsdfsdfsdfs"
+ string last*Name*
+ string __phone__ UK
+ int _age_
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with unicode text without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ _**testẽζ➕Ø😀㌕ぼ**_ {
+ *__List~List~int~~sdfds__* **driversLicense** PK "***The l😀icense #***"
+ *string(99)~T~~~~~~* firstName "Only __99__
characters are a
llowed dsfsdfsdfsdfs"
+ string last*Name*
+ string __phone__ UK
+ int _age_
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render an ER diagram with relationships with unicode text`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ person[😀] {
+ string *first*Name
+ string _**last**Name_
+ }
+ a["*Customer Account*"] {
+ **string** ema*i*l
+ }
+ person ||--o| a : __hẽ😀__
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with relationships with unicode text without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ person[😀] {
+ string *first*Name
+ string _**last**Name_
+ }
+ a["*Customer Account*"] {
+ **string** ema*i*l
+ }
+ person ||--o| a : __hẽ😀__
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render an ER diagram with TB direction`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ direction TB
+ CAR ||--|{ NAMED-DRIVER : allows
+ PERSON ||..o{ NAMED-DRIVER : is
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with BT direction`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ direction BT
+ CAR ||--|{ NAMED-DRIVER : allows
+ PERSON ||..o{ NAMED-DRIVER : is
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with LR direction`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ direction LR
+ CAR ||--|{ NAMED-DRIVER : allows
+ PERSON ||..o{ NAMED-DRIVER : is
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with RL direction`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ direction RL
+ CAR ||--|{ NAMED-DRIVER : allows
+ PERSON ||..o{ NAMED-DRIVER : is
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with styles applied from style statement`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ c[CUSTOMER]
+ p[PERSON]
+ style c,p fill:#f9f,stroke:blue, color:grey, font-size:24px,font-weight:bold
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with styles applied from style statement without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ c[CUSTOMER]
+ p[PERSON]
+ style c,p fill:#f9f,stroke:blue, color:grey, font-size:24px,font-weight:bold
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with styles applied from class statement`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ c[CUSTOMER]
+ p[PERSON]:::blue
+ classDef bold font-size:24px, font-weight: bold
+ classDef blue stroke:lightblue, color: #0000FF
+ class c,p bold
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with styles applied from class statement without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ c[CUSTOMER]
+ p[PERSON]:::blue
+ classDef bold font-size:24px, font-weight: bold
+ classDef blue stroke:lightblue, color: #0000FF
+ class c,p bold
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with styles applied from the default class and other styles`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ c[CUSTOMER]
+ p[PERSON]:::blue
+ classDef blue stroke:lightblue, color: #0000FF
+ classDef default fill:pink
+ style c color:green
+ `,
+ { ...options }
+ );
+ });
+ });
+});
diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js
index aad9b1cf7..cbfec8218 100644
--- a/cypress/integration/rendering/erDiagram.spec.js
+++ b/cypress/integration/rendering/erDiagram.spec.js
@@ -109,8 +109,8 @@ describe('Entity Relationship Diagram', () => {
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
- // use within because the absolute value can be slightly different depending on the environment ±5%
- expect(maxWidthValue).to.be.within(140 * 0.95, 140 * 1.05);
+ // use within because the absolute value can be slightly different depending on the environment ±6%
+ expect(maxWidthValue).to.be.within(140 * 0.96, 140 * 1.06);
});
});
@@ -125,8 +125,8 @@ describe('Entity Relationship Diagram', () => {
);
cy.get('svg').should((svg) => {
const width = parseFloat(svg.attr('width'));
- // use within because the absolute value can be slightly different depending on the environment ±5%
- expect(width).to.be.within(140 * 0.95, 140 * 1.05);
+ // use within because the absolute value can be slightly different depending on the environment ±6%
+ expect(width).to.be.within(140 * 0.96, 140 * 1.06);
// expect(svg).to.have.attr('height', '465');
expect(svg).to.not.have.attr('style');
});
diff --git a/cypress/integration/rendering/flowchart-elk.spec.js b/cypress/integration/rendering/flowchart-elk.spec.js
index 38bfe6440..27af2c40c 100644
--- a/cypress/integration/rendering/flowchart-elk.spec.js
+++ b/cypress/integration/rendering/flowchart-elk.spec.js
@@ -1,4 +1,4 @@
-import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
+import { imgSnapshotTest, renderGraph, verifyNumber } from '../../helpers/util.ts';
describe('Flowchart ELK', () => {
it('1-elk: should render a simple flowchart', () => {
@@ -109,7 +109,7 @@ describe('Flowchart ELK', () => {
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);
+ verifyNumber(maxWidthValue, 380);
});
});
it('8-elk: should render a flowchart when useMaxWidth is false', () => {
@@ -128,7 +128,7 @@ describe('Flowchart ELK', () => {
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);
+ verifyNumber(width, 380);
expect(svg).to.not.have.attr('style');
});
});
@@ -208,13 +208,13 @@ describe('Flowchart ELK', () => {
`flowchart-elk TB
internet
nat
- routeur
+ router
lb1
lb2
compute1
compute2
subgraph project
- routeur
+ router
nat
subgraph subnet1
compute1
@@ -225,8 +225,8 @@ describe('Flowchart ELK', () => {
lb2
end
end
- internet --> routeur
- routeur --> subnet1 & subnet2
+ internet --> router
+ router --> subnet1 & subnet2
subnet1 & subnet2 --> nat --> internet
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
@@ -443,7 +443,7 @@ flowchart-elk TD
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
- it('63-elk: title on subgraphs should be themable', () => {
+ it('63-elk: title on subgraphs should be themeable', () => {
imgSnapshotTest(
`
%%{init:{"theme":"base", "themeVariables": {"primaryColor":"#411d4e", "titleColor":"white", "darkMode":true}}}%%
@@ -692,7 +692,7 @@ A --> B
{}
);
cy.get('svg').should((svg) => {
- const edges = svg.querySelectorAll('.edges > path');
+ const edges = svg[0].querySelectorAll('.edges > path');
edges.forEach((edge) => {
expect(edge).to.have.class('flowchart-link');
});
@@ -739,7 +739,7 @@ NL\`") --"\`1o **bold**\`"--> c
{ flowchart: { titleTopMargin: 0 } }
);
});
- it('Wrapping long text with a new line', () => {
+ it.skip('Wrapping long text with a new line', () => {
imgSnapshotTest(
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
flowchart-elk LR
@@ -841,7 +841,7 @@ end
{ flowchart: { titleTopMargin: 0 } }
);
});
- it('Sub graphs and markdown strings', () => {
+ it('Sub graphs', () => {
imgSnapshotTest(
`---
config:
diff --git a/cypress/integration/rendering/flowchart-icon.spec.js b/cypress/integration/rendering/flowchart-icon.spec.js
new file mode 100644
index 000000000..a76e8aabd
--- /dev/null
+++ b/cypress/integration/rendering/flowchart-icon.spec.js
@@ -0,0 +1,28 @@
+import { imgSnapshotTest } from '../../helpers/util.ts';
+
+const themes = ['default', 'forest', 'dark', 'base', 'neutral'];
+
+describe('when rendering flowchart with icons', () => {
+ for (const theme of themes) {
+ it(`should render icons from fontawesome library on theme ${theme}`, () => {
+ imgSnapshotTest(
+ `flowchart TD
+ A("fab:fa-twitter Twitter") --> B("fab:fa-facebook Facebook")
+ B --> C("fa:fa-coffee Coffee")
+ C --> D("fa:fa-car Car")
+ D --> E("fab:fa-github GitHub")
+ `,
+ { theme }
+ );
+ });
+
+ it(`should render registered icons on theme ${theme}`, () => {
+ imgSnapshotTest(
+ `flowchart TD
+ A("fa:fa-bell Bell")
+ `,
+ { theme }
+ );
+ });
+ }
+});
diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js
index 66452f4b2..97fc1ecbd 100644
--- a/cypress/integration/rendering/flowchart-v2.spec.js
+++ b/cypress/integration/rendering/flowchart-v2.spec.js
@@ -99,7 +99,7 @@ describe('Flowchart v2', () => {
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(417 * 0.95, 417 * 1.05);
+ expect(maxWidthValue).to.be.within(440 * 0.95, 440 * 1.05);
});
});
it('8: should render a flowchart when useMaxWidth is false', () => {
@@ -118,7 +118,7 @@ describe('Flowchart v2', () => {
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(417 * 0.95, 417 * 1.05);
+ expect(width).to.be.within(440 * 0.95, 440 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
@@ -198,13 +198,13 @@ describe('Flowchart v2', () => {
`flowchart TB
internet
nat
- routeur
+ router
lb1
lb2
compute1
compute2
subgraph project
- routeur
+ router
nat
subgraph subnet1
compute1
@@ -215,8 +215,8 @@ describe('Flowchart v2', () => {
lb2
end
end
- internet --> routeur
- routeur --> subnet1 & subnet2
+ internet --> router
+ router --> subnet1 & subnet2
subnet1 & subnet2 --> nat --> internet
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
@@ -433,7 +433,7 @@ flowchart TD
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
- it('63: title on subgraphs should be themable', () => {
+ it('63: title on subgraphs should be themeable', () => {
imgSnapshotTest(
`
%%{init:{"theme":"base", "themeVariables": {"primaryColor":"#411d4e", "titleColor":"white", "darkMode":true}}}%%
@@ -699,7 +699,7 @@ A --> B
{ flowchart: { titleTopMargin: 10 } }
);
});
- it('3192: It should be possieble to render flowcharts with invisible edges', () => {
+ it('3192: It should be possible to render flowcharts with invisible edges', () => {
imgSnapshotTest(
`---
title: Simple flowchart with invisible edges
@@ -1076,4 +1076,41 @@ end
);
});
});
+ describe('New @ syntax for node metadata edge cases', () => {
+ it('should be possible to use @ syntax to add labels on multi nodes', () => {
+ imgSnapshotTest(
+ `flowchart TB
+ n2["label for n2"] & n4@{ label: "label for n4"} & n5@{ label: "label for n5"}
+ `,
+ {}
+ );
+ });
+ it('should be possible to use @ syntax to add labels with trail spaces and &', () => {
+ imgSnapshotTest(
+ `flowchart TB
+ n2["label for n2"] & n4@{ label: "label for n4"} & n5@{ label: "label for n5"}
+ `,
+ {}
+ );
+ });
+ it('should be possible to use @ syntax to add labels with trail spaces', () => {
+ imgSnapshotTest(
+ `flowchart TB
+ n2["label for n2"]
+ n4@{ label: "label for n4"}
+ n5@{ label: "label for n5"}
+ `,
+ {}
+ );
+ });
+ it('should be possible to use @ syntax to add labels with trail spaces and edge/link', () => {
+ imgSnapshotTest(
+ `flowchart TD
+ A["A"] --> B["for B"] & C@{ label: "for c"} & E@{label : "for E"}
+ D@{label: "for D"}
+ `,
+ {}
+ );
+ });
+ });
});
diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js
index d3a83ae5f..40713ac4e 100644
--- a/cypress/integration/rendering/flowchart.spec.js
+++ b/cypress/integration/rendering/flowchart.spec.js
@@ -895,7 +895,7 @@ graph TD
imgSnapshotTest(
`
graph TD
- classDef default fill:#a34,stroke:#000,stroke-width:4px,color:#fff
+ classDef default fill:#a34,stroke:#000,stroke-width:4px,color:#fff
hello --> default
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
@@ -917,4 +917,60 @@ graph TD
}
);
});
+ it('#6369: edge color should affect arrow head', () => {
+ imgSnapshotTest(
+ `
+ flowchart LR
+ A --> B
+ A --> C
+ C --> D
+
+ linkStyle 0 stroke:#D50000
+ linkStyle 2 stroke:#D50000
+ `,
+ {
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+ it('68: should honor subgraph direction when inheritDir is false', () => {
+ imgSnapshotTest(
+ `
+ %%{init: {"flowchart": { "inheritDir": false }}}%%
+ flowchart TB
+ direction LR
+ subgraph A
+ direction TB
+ a --> b
+ end
+ subgraph B
+ c --> d
+ end
+ `,
+ {
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('69: should inherit global direction when inheritDir is true', () => {
+ imgSnapshotTest(
+ `
+ %%{init: {"flowchart": { "inheritDir": true }}}%%
+ flowchart TB
+ direction LR
+ subgraph A
+ direction TB
+ a --> b
+ end
+ subgraph B
+ c --> d
+ end
+ `,
+ {
+ fontFamily: 'courier',
+ }
+ );
+ });
});
diff --git a/cypress/integration/rendering/gantt.spec.js b/cypress/integration/rendering/gantt.spec.js
index a0c2dbcb9..2cc67918c 100644
--- a/cypress/integration/rendering/gantt.spec.js
+++ b/cypress/integration/rendering/gantt.spec.js
@@ -117,7 +117,7 @@ describe('Gantt diagram', () => {
{}
);
});
- it('should FAIL redering a gantt chart for issue #1060 with invalid date', () => {
+ it('should FAIL rendering a gantt chart for issue #1060 with invalid date', () => {
imgSnapshotTest(
`
gantt
@@ -358,6 +358,23 @@ describe('Gantt diagram', () => {
);
});
+ it('should render a gantt diagram with a vert tag', () => {
+ imgSnapshotTest(
+ `
+ gantt
+ title A Gantt Diagram
+ dateFormat ss
+ axisFormat %Ss
+
+ section Section
+ A task : a1, 00, 6s
+ Milestone : vert, 01,
+ section Another
+ Task in sec : 06, 3s
+ another task : 3s
+ `
+ );
+ });
it('should render a gantt diagram with tick is 2 milliseconds', () => {
imgSnapshotTest(
`
@@ -573,7 +590,7 @@ describe('Gantt diagram', () => {
`
);
});
- it('should render a gantt diagram exculding friday and saturday', () => {
+ it('should render a gantt diagram excluding friday and saturday', () => {
imgSnapshotTest(
`gantt
title A Gantt Diagram
@@ -584,7 +601,7 @@ describe('Gantt diagram', () => {
A task :a1, 2024-02-28, 10d`
);
});
- it('should render a gantt diagram exculding saturday and sunday', () => {
+ it('should render a gantt diagram excluding saturday and sunday', () => {
imgSnapshotTest(
`gantt
title A Gantt Diagram
@@ -671,7 +688,7 @@ describe('Gantt diagram', () => {
title Gantt Digram
dateFormat YYYY-MM-DD
section Section
- ;A task with a semiclon :a1, 2014-01-01, 30d
+ ;A task with a semicolon :a1, 2014-01-01, 30d
Another task :after a1 , 20d
section Another
Task in sec :2014-01-12 , 12d
diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js
index 249febd08..42dc57439 100644
--- a/cypress/integration/rendering/gitGraph.spec.js
+++ b/cypress/integration/rendering/gitGraph.spec.js
@@ -11,7 +11,7 @@ describe('Git Graph diagram', () => {
{}
);
});
- it('2: should render a simple gitgraph with commit on main branch with Id', () => {
+ it('2: should render a simple gitgraph with commit on main branch with id', () => {
imgSnapshotTest(
`gitGraph
commit id: "One"
@@ -253,7 +253,7 @@ describe('Git Graph diagram', () => {
`
gitGraph
checkout main
- %% Make sure to manually set the ID of all commits, for consistent visual tests
+ %% Make sure to manually set the id of all commits, for consistent visual tests
commit id: "1-abcdefg"
checkout main
branch branch1
@@ -343,7 +343,7 @@ gitGraph
{}
);
});
- it('16: should render a simple gitgraph with commit on main branch with Id | Vertical Branch', () => {
+ it('16: should render a simple gitgraph with commit on main branch with id | Vertical Branch', () => {
imgSnapshotTest(
`gitGraph TB:
commit id: "One"
@@ -585,7 +585,7 @@ gitGraph
`
gitGraph TB:
checkout main
- %% Make sure to manually set the ID of all commits, for consistent visual tests
+ %% Make sure to manually set the id of all commits, for consistent visual tests
commit id: "1-abcdefg"
checkout main
branch branch1
@@ -1024,7 +1024,7 @@ gitGraph TB:
{}
);
});
- it('51: should render a simple gitgraph with commit on main branch with Id | Vertical Branch - Bottom-to-top', () => {
+ it('51: should render a simple gitgraph with commit on main branch with id | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id: "One"
@@ -1266,7 +1266,7 @@ gitGraph TB:
`
gitGraph BT:
checkout main
- %% Make sure to manually set the ID of all commits, for consistent visual tests
+ %% Make sure to manually set the id of all commits, for consistent visual tests
commit id: "1-abcdefg"
checkout main
branch branch1
@@ -1491,7 +1491,7 @@ gitGraph TB:
`
gitGraph
switch main
- %% Make sure to manually set the ID of all commits, for consistent visual tests
+ %% Make sure to manually set the id of all commits, for consistent visual tests
commit id: "1-abcdefg"
switch main
branch branch1
diff --git a/cypress/integration/rendering/journey.spec.js b/cypress/integration/rendering/journey.spec.js
index d8bef6d1b..70d5574b8 100644
--- a/cypress/integration/rendering/journey.spec.js
+++ b/cypress/integration/rendering/journey.spec.js
@@ -63,4 +63,199 @@ section Checkout from website
{ journey: { useMaxWidth: false } }
);
});
+
+ it('should initialize with a left margin of 150px for user journeys', () => {
+ renderGraph(
+ `
+ ---
+ config:
+ journey:
+ maxLabelWidth: 320
+ ---
+ journey
+ title User Journey Example
+ section Onboarding
+ Sign Up: 5:
+ Browse Features: 3:
+ Use Core Functionality: 4:
+ section Engagement
+ Browse Features: 3
+ Use Core Functionality: 4
+ `,
+ { journey: { useMaxWidth: true } }
+ );
+
+ let diagramStartX;
+
+ cy.contains('foreignobject', 'Sign Up').then(($diagram) => {
+ diagramStartX = parseFloat($diagram.attr('x'));
+ expect(diagramStartX).to.be.closeTo(150, 2);
+ });
+ });
+
+ it('should maintain sufficient space between legend and diagram when legend labels are longer', () => {
+ renderGraph(
+ `journey
+ title Web hook life cycle
+ section Darkoob
+ Make preBuilt:5: Darkoob user
+ register slug : 5: Darkoob userf deliberately increasing the size of this label to check if distance between legend and diagram is maintained
+ Map slug to a Prebuilt Job:5: Darkoob user
+ section External Service
+ set Darkoob slug as hook for an Event : 5 : admin Exjjjnjjjj qwerty
+ listen to the events : 5 : External Service
+ call darkoob endpoint : 5 : External Service
+ section Darkoob
+ check for inputs : 5 : DarkoobAPI
+ run the prebuilt job : 5 : DarkoobAPI
+ `,
+ { journey: { useMaxWidth: true } }
+ );
+
+ let LabelEndX, diagramStartX;
+
+ // Get right edge of the legend
+ cy.contains('tspan', 'Darkoob userf').then((textBox) => {
+ const bbox = textBox[0].getBBox();
+ LabelEndX = bbox.x + bbox.width;
+ });
+
+ // Get left edge of the diagram
+ cy.contains('foreignobject', 'Make preBuilt').then((rect) => {
+ diagramStartX = parseFloat(rect.attr('x'));
+ });
+
+ // Assert right edge of the diagram is greater than or equal to the right edge of the label
+ cy.then(() => {
+ expect(diagramStartX).to.be.gte(LabelEndX);
+ });
+ });
+
+ it('should wrap a single long word with hyphenation', () => {
+ renderGraph(
+ `
+ ---
+ config:
+ journey:
+ maxLabelWidth: 100
+ ---
+ journey
+ title Long Word Test
+ section Test
+ VeryLongWord: 5: Supercalifragilisticexpialidocious
+ `,
+ { journey: { useMaxWidth: true } }
+ );
+
+ // Verify that the line ends with a hyphen, indicating proper hyphenation for words exceeding maxLabelWidth.
+ cy.get('tspan').then((tspans) => {
+ const hasHyphen = [...tspans].some((t) => t.textContent.trim().endsWith('-'));
+ return expect(hasHyphen).to.be.true;
+ });
+ });
+
+ it('should wrap text on whitespace without adding hyphens', () => {
+ renderGraph(
+ `
+ ---
+ config:
+ journey:
+ maxLabelWidth: 200
+ ---
+ journey
+ title Whitespace Test
+ section Test
+ TextWithSpaces: 5: Gustavo Fring is played by Giancarlo Esposito and is a character in Breaking Bad.
+ `,
+ { journey: { useMaxWidth: true } }
+ );
+
+ // Verify that none of the text spans end with a hyphen.
+ cy.get('tspan').each(($el) => {
+ const text = $el.text();
+ expect(text.trim()).not.to.match(/-$/);
+ });
+ });
+
+ it('should wrap long labels into multiple lines, keep them under max width, and maintain margins', () => {
+ renderGraph(
+ `
+ ---
+ config:
+ journey:
+ maxLabelWidth: 320
+ ---
+ journey
+ title User Journey Example
+ section Onboarding
+ Sign Up: 5: This is a long label that will be split into multiple lines to test the wrapping functionality
+ Browse Features: 3: This is another long label that will be split into multiple lines to test the wrapping functionality
+ Use Core Functionality: 4: This is yet another long label that will be split into multiple lines to test the wrapping functionality
+ section Engagement
+ Browse Features: 3
+ Use Core Functionality: 4
+ `,
+ { journey: { useMaxWidth: true } }
+ );
+
+ let diagramStartX, maxLineWidth;
+
+ // Get the diagram's left edge x-coordinate
+ cy.contains('foreignobject', 'Sign Up')
+ .then(($diagram) => {
+ diagramStartX = parseFloat($diagram.attr('x'));
+ })
+ .then(() => {
+ cy.get('text.legend').then(($lines) => {
+ // Check that there are multiple lines
+ expect($lines.length).to.be.equal(9);
+
+ // Check that all lines are under the maxLabelWidth
+ $lines.each((index, el) => {
+ const bbox = el.getBBox();
+ expect(bbox.width).to.be.lte(320);
+ maxLineWidth = Math.max(maxLineWidth || 0, bbox.width);
+ });
+
+ /** The expected margin between the diagram and the legend is 150px, as defined by
+ * conf.leftMargin in user-journey-config.js
+ */
+ expect(diagramStartX - maxLineWidth).to.be.closeTo(150, 2);
+ });
+ });
+ });
+
+ it('should correctly render the user journey diagram title with the specified styling', () => {
+ renderGraph(
+ `---
+config:
+ journey:
+ titleColor: "#2900A5"
+ titleFontFamily: "Times New Roman"
+ titleFontSize: "5rem"
+---
+
+journey
+ title User Journey Example
+ section Onboarding
+ Sign Up: 5: John, Shahir
+ Complete Profile: 4: John
+ section Engagement
+ Browse Features: 3: John
+ Use Core Functionality: 4: John
+ section Retention
+ Revisit Application: 5: John
+ Invite Friends: 3: John
+
+ size: 2rem
+ `
+ );
+
+ cy.get('text').contains('User Journey Example').as('title');
+ cy.get('@title').then(($title) => {
+ expect($title).to.have.attr('fill', '#2900A5');
+ expect($title).to.have.attr('font-family', 'Times New Roman');
+ expect($title).to.have.attr('font-size', '5rem');
+ });
+ });
});
diff --git a/cypress/integration/rendering/kanban.spec.ts b/cypress/integration/rendering/kanban.spec.ts
index 6293776d6..0ff564cc6 100644
--- a/cypress/integration/rendering/kanban.spec.ts
+++ b/cypress/integration/rendering/kanban.spec.ts
@@ -62,7 +62,7 @@ describe('Kanban diagram', () => {
{}
);
});
- it('6: should handle assigments', () => {
+ it('6: should handle assignments', () => {
imgSnapshotTest(
`kanban
id1[Todo]
@@ -118,7 +118,7 @@ kanban
docs[Create Documentation]
docs[Create Blog about the new diagram]
id7[In progress]
- id6[Create renderer so that it works in all cases. We also add som extra text here for testing purposes. And some more just for the extra flare.]
+ id6[Create renderer so that it works in all cases. We also add some extra text here for testing purposes. And some more just for the extra flare.]
id8[Design grammar]@{ assigned: 'knsv' }
id9[Ready for deploy]
id10[Ready for test]
diff --git a/cypress/integration/rendering/mindmap.spec.ts b/cypress/integration/rendering/mindmap.spec.ts
index a77459f58..731f861ee 100644
--- a/cypress/integration/rendering/mindmap.spec.ts
+++ b/cypress/integration/rendering/mindmap.spec.ts
@@ -146,7 +146,7 @@ root
shouldHaveRoot
);
});
- it('text shouhld wrap with icon', () => {
+ it('text should wrap with icon', () => {
imgSnapshotTest(
`mindmap
root
diff --git a/cypress/integration/rendering/pie.spec.ts b/cypress/integration/rendering/pie.spec.ts
index 4a1d774c0..171a83057 100644
--- a/cypress/integration/rendering/pie.spec.ts
+++ b/cypress/integration/rendering/pie.spec.ts
@@ -64,7 +64,7 @@ describe('pie chart', () => {
});
});
- it('should render a pie diagram when textPosition is setted', () => {
+ it('should render a pie diagram when textPosition is set', () => {
imgSnapshotTest(
`pie
"Dogs": 50
diff --git a/cypress/integration/rendering/quadrantChart.spec.js b/cypress/integration/rendering/quadrantChart.spec.js
index 4830db656..3a1e768aa 100644
--- a/cypress/integration/rendering/quadrantChart.spec.js
+++ b/cypress/integration/rendering/quadrantChart.spec.js
@@ -45,7 +45,7 @@ describe('Quadrant Chart', () => {
{}
);
});
- it('should able to render y-axix on right side', () => {
+ it('should able to render y-axis on right side', () => {
imgSnapshotTest(
`
%%{init: {"quadrantChart": {"yAxisPosition": "right"}}}%%
@@ -61,7 +61,7 @@ describe('Quadrant Chart', () => {
{}
);
});
- it('should able to render x-axix on bottom', () => {
+ it('should able to render x-axis on bottom', () => {
imgSnapshotTest(
`
%%{init: {"quadrantChart": {"xAxisPosition": "bottom"}}}%%
@@ -77,7 +77,7 @@ describe('Quadrant Chart', () => {
{}
);
});
- it('should able to render x-axix on bottom and y-axis on right', () => {
+ it('should able to render x-axis on bottom and y-axis on right', () => {
imgSnapshotTest(
`
%%{init: {"quadrantChart": {"xAxisPosition": "bottom", "yAxisPosition": "right"}}}%%
diff --git a/cypress/integration/rendering/radar.spec.js b/cypress/integration/rendering/radar.spec.js
new file mode 100644
index 000000000..b0bc3f6e0
--- /dev/null
+++ b/cypress/integration/rendering/radar.spec.js
@@ -0,0 +1,79 @@
+import { imgSnapshotTest } from '../../helpers/util';
+
+describe('radar structure', () => {
+ it('should render a simple radar diagram', () => {
+ imgSnapshotTest(
+ `radar-beta
+ title Best Radar Ever
+ axis A, B, C
+ curve c1{1, 2, 3}
+ `
+ );
+ });
+
+ it('should render a radar diagram with multiple curves', () => {
+ imgSnapshotTest(
+ `radar-beta
+ title Best Radar Ever
+ axis A, B, C
+ curve c1{1, 2, 3}
+ curve c2{2, 3, 1}
+ `
+ );
+ });
+
+ it('should render a complex radar diagram', () => {
+ imgSnapshotTest(
+ `radar-beta
+ title My favorite ninjas
+ axis Agility, Speed, Strength
+ axis Stam["Stamina"] , Intel["Intelligence"]
+
+ curve Ninja1["Naruto Uzumaki"]{
+ Agility 2, Speed 2,
+ Strength 3, Stam 5,
+ Intel 0
+ }
+ curve Ninja2["Sasuke"]{2, 3, 4, 1, 5}
+ curve Ninja3 {3, 2, 1, 5, 4}
+
+ showLegend true
+ ticks 3
+ max 8
+ min 0
+ graticule polygon
+ `
+ );
+ cy.get('svg').should((svg) => {
+ expect(svg).to.have.length(1);
+ });
+ });
+
+ it('should render radar diagram with config override', () => {
+ imgSnapshotTest(
+ `radar-beta
+ title Best Radar Ever
+ axis A,B,C
+ curve mycurve{1,2,3}`,
+ { radar: { marginTop: 100, axisScaleFactor: 0.5 } }
+ );
+ });
+
+ it('should parse radar diagram with theme override', () => {
+ imgSnapshotTest(
+ `radar-beta
+ axis A,B,C
+ curve mycurve{1,2,3}`,
+ { theme: 'base', themeVariables: { fontSize: 80, cScale0: '#FF0000' } }
+ );
+ });
+
+ it('should handle radar diagram with radar style override', () => {
+ imgSnapshotTest(
+ `radar-beta
+ axis A,B,C
+ curve mycurve{1,2,3}`,
+ { theme: 'base', themeVariables: { radar: { axisColor: '#FF0000' } } }
+ );
+ });
+});
diff --git a/cypress/integration/rendering/requirementDiagram-unified.spec.js b/cypress/integration/rendering/requirementDiagram-unified.spec.js
new file mode 100644
index 000000000..48b1a0d61
--- /dev/null
+++ b/cypress/integration/rendering/requirementDiagram-unified.spec.js
@@ -0,0 +1,703 @@
+import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
+
+const testOptions = [
+ { description: '', options: { logLevel: 1 } },
+ { description: 'ELK: ', options: { logLevel: 1, layout: 'elk' } },
+ { description: 'HD: ', options: { logLevel: 1, look: 'handDrawn' } },
+];
+
+describe('Requirement Diagram Unified', () => {
+ testOptions.forEach(({ description, options }) => {
+ it(`${description}should render a simple Requirement diagram`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a simple Requirement diagram without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render a not-so-simple Requirement diagram`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+
+ 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
+ }
+
+ interfaceRequirement test_req4 {
+ id: 1.2.1
+ text: the fourth test text.
+ risk: medium
+ verifymethod: analysis
+ }
+
+ physicalRequirement test_req5 {
+ id: 1.2.2
+ text: the fifth test text.
+ risk: medium
+ verifymethod: analysis
+ }
+
+ designConstraint test_req6 {
+ id: 1.2.3
+ text: the sixth test text.
+ risk: medium
+ verifymethod: analysis
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ element test_entity2 {
+ type: word doc
+ docRef: reqs/test_entity
+ }
+
+ element test_entity3 {
+ type: "test suite"
+ docRef: github.com/all_the_tests
+ }
+
+
+ test_entity - satisfies -> test_req2
+ test_req - traces -> test_req2
+ test_req - contains -> test_req3
+ test_req3 - contains -> test_req4
+ test_req4 - derives -> test_req5
+ test_req5 - refines -> test_req6
+ test_entity3 - verifies -> test_req5
+ test_req <- copies - test_entity2
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a not-so-simple Requirement diagram without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+
+ 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
+ }
+
+ interfaceRequirement test_req4 {
+ id: 1.2.1
+ text: the fourth test text.
+ risk: medium
+ verifymethod: analysis
+ }
+
+ physicalRequirement test_req5 {
+ id: 1.2.2
+ text: the fifth test text.
+ risk: medium
+ verifymethod: analysis
+ }
+
+ designConstraint test_req6 {
+ id: 1.2.3
+ text: the sixth test text.
+ risk: medium
+ verifymethod: analysis
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ element test_entity2 {
+ type: word doc
+ docRef: reqs/test_entity
+ }
+
+ element test_entity3 {
+ type: "test suite"
+ docRef: github.com/all_the_tests
+ }
+
+
+ test_entity - satisfies -> test_req2
+ test_req - traces -> test_req2
+ test_req - contains -> test_req3
+ test_req3 - contains -> test_req4
+ test_req4 - derives -> test_req5
+ test_req5 - refines -> test_req6
+ test_entity3 - verifies -> test_req5
+ test_req <- copies - test_entity2
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render multiple Requirement diagrams`, () => {
+ imgSnapshotTest(
+ [
+ `
+ requirementDiagram
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ `,
+ `
+ requirementDiagram
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ `,
+ ],
+ options
+ );
+ });
+
+ it(`${description}should render a Requirement diagram with empty information`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+ requirement test_req {
+ }
+ element test_entity {
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render requirements and elements with and without information`, () => {
+ renderGraph(
+ `
+ requirementDiagram
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+ element test_entity {
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render requirements and elements with long and short text`, () => {
+ renderGraph(
+ `
+ requirementDiagram
+ requirement test_req {
+ id: 1
+ text: the test text that is long and takes up a lot of space.
+ risk: high
+ verifymethod: test
+ }
+ element test_entity_name_that_is_extra_long {
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render requirements and elements with long and short text without htmlLabels`, () => {
+ renderGraph(
+ `
+ requirementDiagram
+ requirement test_req {
+ id: 1
+ text: the test text that is long and takes up a lot of space.
+ risk: high
+ verifymethod: test
+ }
+ element test_entity_name_that_is_extra_long {
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render requirements and elements with quoted text for spaces`, () => {
+ renderGraph(
+ `
+ requirementDiagram
+ requirement "test req name with spaces" {
+ id: 1
+ text: the test text that is long and takes up a lot of space.
+ risk: high
+ verifymethod: test
+ }
+ element "test entity name that is extra long with spaces" {
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render requirements and elements with markdown text`, () => {
+ renderGraph(
+ `
+ requirementDiagram
+ requirement "__my bolded name__" {
+ id: 1
+ text: "**Bolded text** _italicized text_"
+ risk: high
+ verifymethod: test
+ }
+ element "*my italicized name*" {
+ type: "**Bolded type** _italicized type_"
+ docref: "*Italicized* __Bolded__"
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render requirements and elements with markdown text without htmlLabels`, () => {
+ renderGraph(
+ `
+ requirementDiagram
+ requirement "__my bolded name__" {
+ id: 1
+ text: "**Bolded text** _italicized text_"
+ risk: high
+ verifymethod: test
+ }
+ element "*my italicized name*" {
+ type: "**Bolded type** _italicized type_"
+ docref: "*Italicized* __Bolded__"
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render a simple Requirement diagram with a title`, () => {
+ imgSnapshotTest(
+ `---
+ title: simple Requirement diagram
+ ---
+ requirementDiagram
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a Requirement diagram with TB direction`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+ direction TB
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a Requirement diagram with BT direction`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+ direction BT
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a Requirement diagram with LR direction`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+ direction LR
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a Requirement diagram with RL direction`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+ direction RL
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render requirements and elements with styles applied from style statement`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+
+ style test_req,test_entity fill:#f9f,stroke:blue, color:grey, font-weight:bold
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render requirements and elements with styles applied from style statement without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+
+ style test_req,test_entity fill:#f9f,stroke:blue, color:grey, font-weight:bold
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render requirements and elements with styles applied from class statement`, () => {
+ imgSnapshotTest(
+ `
+requirementDiagram
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ classDef bold font-weight: bold
+ classDef blue stroke:lightblue, color: #0000FF
+ class test_entity bold
+ class test_req blue, bold
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render requirements and elements with styles applied from class statement without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+
+ requirement test_req {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ classDef bold font-weight: bold
+ classDef blue stroke:lightblue, color: #0000FF
+ class test_entity bold
+ class test_req blue, bold
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render requirements and elements with styles applied from classes with shorthand syntax`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+
+ requirement test_req:::blue {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ classDef bold font-weight: bold
+ classDef blue stroke:lightblue, color: #0000FF
+ test_entity:::bold
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render requirements and elements with styles applied from classes with shorthand syntax without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+
+ requirement test_req:::blue {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ classDef bold font-weight: bold
+ classDef blue stroke:lightblue, color: #0000FF
+ test_entity:::bold
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render requirements and elements with styles applied from the default class and other styles`, () => {
+ imgSnapshotTest(
+ `
+requirementDiagram
+
+ requirement test_req:::blue {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ classDef blue stroke:lightblue, color:blue
+ classDef default fill:pink
+ style test_entity color:green
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render requirements and elements with styles applied from the default class and other styles without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ requirementDiagram
+
+ requirement test_req:::blue {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ classDef blue stroke:lightblue, color:blue
+ classDef default fill:pink
+ style test_entity color:green
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render a Requirement diagram with a theme`, () => {
+ imgSnapshotTest(
+ `
+---
+ theme: forest
+---
+ requirementDiagram
+
+ requirement test_req:::blue {
+ id: 1
+ text: the test text.
+ risk: high
+ verifymethod: test
+ }
+
+ element test_entity {
+ type: simulation
+ }
+
+ test_entity - satisfies -> test_req
+ `,
+ options
+ );
+ });
+ });
+});
diff --git a/cypress/integration/rendering/stateDiagram-v2.spec.js b/cypress/integration/rendering/stateDiagram-v2.spec.js
index 606a1a3f5..83190dbc7 100644
--- a/cypress/integration/rendering/stateDiagram-v2.spec.js
+++ b/cypress/integration/rendering/stateDiagram-v2.spec.js
@@ -138,8 +138,8 @@ describe('State diagram', () => {
imgSnapshotTest(
`
stateDiagram-v2
- State1: This a a single line description
- State2: This a a multi line description
+ State1: This a single line description
+ State2: This a multi line description
State2: here comes the multi part
[*] --> State1
State1 --> State2
@@ -345,7 +345,7 @@ stateDiagram
}
);
});
- it('v2 width of compond state should grow with title if title is wider', () => {
+ it('v2 width of compound state should grow with title if title is wider', () => {
imgSnapshotTest(
`
stateDiagram-v2
@@ -402,8 +402,8 @@ stateDiagram-v2
`
stateDiagram-v2
MyState
- note left of MyState : I am a leftie
- note right of MyState : I am a rightie
+ note left of MyState : I am a lefty
+ note right of MyState : I am a righty
`,
{
logLevel: 0,
@@ -552,7 +552,7 @@ style AState fill:#636,border:1px solid red,color:white;
{ logLevel: 0, fontFamily: 'courier' }
);
});
- it(' should let styles take preceedence over classes', () => {
+ it(' should let styles take precedence over classes', () => {
imgSnapshotTest(
`
stateDiagram-v2
@@ -565,7 +565,7 @@ style AState fill:#636,border:1px solid red,color:white;
{ logLevel: 0, fontFamily: 'courier' }
);
});
- it(' should allow styles to take effect in stubgraphs', () => {
+ it(' should allow styles to take effect in subgraphs', () => {
imgSnapshotTest(
`
stateDiagram
diff --git a/cypress/integration/rendering/stateDiagram.spec.js b/cypress/integration/rendering/stateDiagram.spec.js
index 9be1f2322..b4c5fa8c1 100644
--- a/cypress/integration/rendering/stateDiagram.spec.js
+++ b/cypress/integration/rendering/stateDiagram.spec.js
@@ -129,8 +129,8 @@ describe('State diagram', () => {
imgSnapshotTest(
`
stateDiagram
- State1: This a a single line description
- State2: This a a multi line description
+ State1: This a single line description
+ State2: This a multi line description
State2: here comes the multi part
[*] --> State1
State1 --> State2
diff --git a/cypress/integration/rendering/timeline.spec.ts b/cypress/integration/rendering/timeline.spec.ts
index c748b54d3..3785f5fcc 100644
--- a/cypress/integration/rendering/timeline.spec.ts
+++ b/cypress/integration/rendering/timeline.spec.ts
@@ -7,7 +7,7 @@ describe('Timeline diagram', () => {
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
- 2005 : Youtube
+ 2005 : YouTube
2006 : Twitter
`,
{}
@@ -35,7 +35,7 @@ describe('Timeline diagram', () => {
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
+ section Bronze 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.
@@ -51,7 +51,7 @@ describe('Timeline diagram', () => {
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
- 2005 : Youtube
+ 2005 : YouTube
2006 : Twitter
`,
{}
@@ -68,7 +68,7 @@ describe('Timeline diagram', () => {
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
- 2005 : Youtube
+ 2005 : YouTube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
@@ -84,7 +84,7 @@ describe('Timeline diagram', () => {
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
- 2005 : Youtube
+ 2005 : YouTube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
@@ -101,7 +101,7 @@ describe('Timeline diagram', () => {
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
- 2005 : Youtube
+ 2005 : YouTube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
@@ -118,7 +118,7 @@ describe('Timeline diagram', () => {
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
- 2005 : Youtube
+ 2005 : YouTube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
@@ -135,7 +135,7 @@ describe('Timeline diagram', () => {
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
- 2005 : Youtube
+ 2005 : YouTube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
@@ -152,7 +152,7 @@ describe('Timeline diagram', () => {
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
- 2005 : Youtube
+ 2005 : YouTube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
@@ -161,4 +161,68 @@ describe('Timeline diagram', () => {
{}
);
});
+
+ it('11: should render timeline with many stacked events and proper timeline line length', () => {
+ imgSnapshotTest(
+ `timeline
+ title Medical Device Lifecycle
+ section Pre-Development
+ Quality Management System : Regulatory Compliance : Risk Management
+ section Development
+ Management Responsibility : Planning Activities : Human Resources
+ Resource Management : Management Reviews : Infrastructure
+ section Post-Development
+ Product Realization Activities : Planning Activities : Customer-related Processes
+ Post-Production Activities : Feedback : Complaints : Adverse Events
+ : Research and Development : Purchasing Activities
+ : Production Activities : Installation Activities
+ : Servicing Activities : Post-Market Surveillance
+ `,
+ {}
+ );
+ });
+
+ it('12: should render timeline with proper vertical line lengths for all columns', () => {
+ imgSnapshotTest(
+ `---
+config:
+ theme: base
+ themeVariables:
+ fontFamily: Fira Sans
+ fontSize: 17px
+ cScale0: '#b3cde0'
+ cScale1: '#f49090'
+ cScale2: '#85d5b8'
+---
+
+timeline
+ title Medical Device Lifecycle
+ section Planning
+ Quality Management System (4): Regulatory Compliance (4.1.1)
+ : Risk Management (4.1.2)
+ Management Resposibility (5): Planning Activities (5.4)
+ : Management Reviews (5.6)
+ Resource Management (6): Human Resources (6.2)
+ : Infrastructure (6.3)
+ section Realization
+ Research and Development (7.3): RnD Planning (7.3.2)
+ : Inputs (7.3.3)
+ : Outputs (7.3.4)
+ : Review (7.3.5)
+ : Verification (7.3.6)
+ : Validation (7.3.7)
+ Purchasing (7.4): Purchasing Process (7.4.1)
+ : Purchasing Information (7.4.2)
+ Production (7.5): Production Activities (7.5.1)
+ : Production Feedback (8.2.1)
+ Installation (7.5.3): Installation Activities (7.5.3)
+ Servicing (7.5.4): Servicing Activities (7.5.4)
+ section Post-Production
+ Post-Market Activities (8): Feedback (8.2.1)
+ : Complaints (8.2.2)
+ : Adverse Events (8.2.3)
+ `,
+ {}
+ );
+ });
});
diff --git a/cypress/integration/rendering/xyChart.spec.js b/cypress/integration/rendering/xyChart.spec.js
index 1245760e8..a582355e8 100644
--- a/cypress/integration/rendering/xyChart.spec.js
+++ b/cypress/integration/rendering/xyChart.spec.js
@@ -179,6 +179,7 @@ describe('XY Chart', () => {
axisLineWidth: 5
chartOrientation: horizontal
plotReservedSpacePercent: 60
+ showDataLabel: true
---
xychart-beta
title "Sales Revenue"
@@ -315,4 +316,516 @@ describe('XY Chart', () => {
);
cy.get('svg');
});
+
+ it('should render vertical bar chart with labels', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ ---
+ xychart-beta
+ title "Sales Revenue"
+ x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
+ y-axis "Revenue (in $)" 4000 --> 11000
+ bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
+ `,
+ {}
+ );
+ });
+
+ it('should render horizontal bar chart with labels', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ chartOrientation: horizontal
+ ---
+ xychart-beta
+ title "Sales Revenue"
+ x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
+ y-axis "Revenue (in $)" 4000 --> 11000
+ bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
+ `,
+ {}
+ );
+ });
+
+ it('should render vertical bar chart without labels by default', () => {
+ imgSnapshotTest(
+ `
+ xychart-beta
+ title "Sales Revenue"
+ x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
+ y-axis "Revenue (in $)" 4000 --> 11000
+ bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
+ `,
+ {}
+ );
+ });
+
+ it('should render horizontal bar chart without labels by default', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ chartOrientation: horizontal
+ ---
+ xychart-beta
+ title "Sales Revenue"
+ x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
+ y-axis "Revenue (in $)" 4000 --> 11000
+ bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
+ `,
+ {}
+ );
+ });
+
+ it('should render multiple bar plots vertically with labels correctly', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ ---
+ xychart-beta
+ title "Multiple Bar Plots"
+ x-axis Categories [A, B, C]
+ y-axis "Values" 0 --> 100
+ bar [10, 50, 90]
+ `,
+ {}
+ );
+ });
+
+ it('should render multiple bar plots horizontally with labels correctly', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ chartOrientation: horizontal
+ ---
+ xychart-beta
+ title "Multiple Bar Plots"
+ x-axis Categories [A, B, C]
+ y-axis "Values" 0 --> 100
+ bar [10, 50, 90]
+ `,
+ {}
+ );
+ });
+
+ it('should render a single bar with label for a vertical xy-chart', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ ---
+ xychart-beta
+ title "Single Bar Chart"
+ x-axis Categories [A]
+ y-axis "Value" 0 --> 100
+ bar [75]
+ `,
+ {}
+ );
+ });
+
+ it('should render a single bar with label for a horizontal xy-chart', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ chartOrientation: horizontal
+ ---
+ xychart-beta
+ title "Single Bar Chart"
+ x-axis Categories [A]
+ y-axis "Value" 0 --> 100
+ bar [75]
+ `,
+ {}
+ );
+ });
+
+ it('should render negative and decimal values with correct labels for vertical xy-chart', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ ---
+ xychart-beta
+ title "Decimal and Negative Values"
+ x-axis Categories [A, B, C]
+ y-axis -10 --> 10
+ bar [ -2.5, 0.75, 5.1 ]
+ `,
+ {}
+ );
+ });
+
+ it('should render negative and decimal values with correct labels for horizontal xy-chart', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ chartOrientation: horizontal
+ ---
+ xychart-beta
+ title "Decimal and Negative Values"
+ x-axis Categories [A, B, C]
+ y-axis -10 --> 10
+ bar [ -2.5, 0.75, 5.1 ]
+ `,
+ {}
+ );
+ });
+
+ it('should render data labels within each bar in the vertical xy-chart', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ ---
+ xychart-beta
+ title "Sales Revenue"
+ x-axis Months [jan,b,c]
+ y-axis "Revenue (in $)" 4000 --> 12000
+ bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000, 3000, 2000, 500, 2000, 3000, 11000, 5000, 6000]
+ `,
+ {}
+ );
+
+ cy.get('g.bar-plot-0').within(() => {
+ cy.get('rect').each(($rect, index) => {
+ // Extract bar properties
+ const barProps = {
+ x: parseFloat($rect.attr('x')),
+ y: parseFloat($rect.attr('y')),
+ width: parseFloat($rect.attr('width')),
+ height: parseFloat($rect.attr('height')),
+ };
+
+ // Get the text element corresponding to this bar by index.
+ cy.get('text')
+ .eq(index)
+ .then(($text) => {
+ const bbox = $text[0].getBBox();
+ const textProps = {
+ x: bbox.x,
+ y: bbox.y,
+ width: bbox.width,
+ height: bbox.height,
+ };
+
+ // Verify that the text label is positioned within the boundaries of the bar.
+ expect(textProps.x).to.be.greaterThan(barProps.x);
+ expect(textProps.x + textProps.width).to.be.lessThan(barProps.x + barProps.width);
+
+ // Check horizontal alignment (within tolerance)
+ expect(textProps.x + textProps.width / 2).to.be.closeTo(
+ barProps.x + barProps.width / 2,
+ 5
+ );
+
+ expect(textProps.y).to.be.greaterThan(barProps.y);
+ expect(textProps.y + textProps.height).to.be.lessThan(barProps.y + barProps.height);
+ });
+ });
+ });
+ });
+
+ it('should render data labels within each bar in the horizontal xy-chart', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ chartOrientation: horizontal
+ ---
+ xychart-beta
+ title "Sales Revenue"
+ x-axis Months [jan,b,c]
+ y-axis "Revenue (in $)" 4000 --> 12000
+ bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000, 3000, 2000, 500, 2000, 3000, 11000, 5000, 6000]
+ `,
+ {}
+ );
+
+ cy.get('g.bar-plot-0').within(() => {
+ cy.get('rect').each(($rect, index) => {
+ // Extract bar properties
+ const barProps = {
+ x: parseFloat($rect.attr('x')),
+ y: parseFloat($rect.attr('y')),
+ width: parseFloat($rect.attr('width')),
+ height: parseFloat($rect.attr('height')),
+ };
+
+ // Get the text element corresponding to this bar by index.
+ cy.get('text')
+ .eq(index)
+ .then(($text) => {
+ const bbox = $text[0].getBBox();
+ const textProps = {
+ x: bbox.x,
+ y: bbox.y,
+ width: bbox.width,
+ height: bbox.height,
+ };
+
+ // Verify that the text label is positioned within the boundaries of the bar.
+ expect(textProps.x).to.be.greaterThan(barProps.x);
+ expect(textProps.x + textProps.width).to.be.lessThan(barProps.x + barProps.width);
+
+ expect(textProps.y).to.be.greaterThan(barProps.y);
+ expect(textProps.y + textProps.height).to.be.lessThan(barProps.y + barProps.height);
+ expect(textProps.y + textProps.height / 2).to.be.closeTo(
+ barProps.y + barProps.height / 2,
+ 5
+ );
+ });
+ });
+ });
+ });
+
+ it('should render data labels within each bar in the vertical xy-chart with a lot of bars of different sizes', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ ---
+ xychart-beta
+ title "Sales Revenue"
+ x-axis Months [jan,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s]
+ y-axis "Revenue (in $)" 4000 --> 12000
+ bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000, 8000, 10000, 5000, 7600, 4999,11000 ,5000,6000]
+ `,
+ {}
+ );
+
+ cy.get('g.bar-plot-0').within(() => {
+ cy.get('rect').each(($rect, index) => {
+ // Extract bar properties
+ const barProps = {
+ x: parseFloat($rect.attr('x')),
+ y: parseFloat($rect.attr('y')),
+ width: parseFloat($rect.attr('width')),
+ height: parseFloat($rect.attr('height')),
+ };
+
+ // Get the text element corresponding to this bar by index.
+ cy.get('text')
+ .eq(index)
+ .then(($text) => {
+ const bbox = $text[0].getBBox();
+ const textProps = {
+ x: bbox.x,
+ y: bbox.y,
+ width: bbox.width,
+ height: bbox.height,
+ };
+
+ // Verify that the text label is positioned within the boundaries of the bar.
+ expect(textProps.x).to.be.greaterThan(barProps.x);
+ expect(textProps.x + textProps.width).to.be.lessThan(barProps.x + barProps.width);
+
+ // Check horizontal alignment (within tolerance)
+ expect(textProps.x + textProps.width / 2).to.be.closeTo(
+ barProps.x + barProps.width / 2,
+ 5
+ );
+
+ expect(textProps.y).to.be.greaterThan(barProps.y);
+ expect(textProps.y + textProps.height).to.be.lessThan(barProps.y + barProps.height);
+ });
+ });
+ });
+ });
+
+ it('should render data labels within each bar in the horizontal xy-chart with a lot of bars of different sizes', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ chartOrientation: horizontal
+ ---
+ xychart-beta
+ title "Sales Revenue"
+ x-axis Months [jan,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s]
+ y-axis "Revenue (in $)" 4000 --> 12000
+ bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000, 8000, 10000, 5000, 7600, 4999,11000 ,5000,6000]
+ `,
+ {}
+ );
+
+ cy.get('g.bar-plot-0').within(() => {
+ cy.get('rect').each(($rect, index) => {
+ // Extract bar properties
+ const barProps = {
+ x: parseFloat($rect.attr('x')),
+ y: parseFloat($rect.attr('y')),
+ width: parseFloat($rect.attr('width')),
+ height: parseFloat($rect.attr('height')),
+ };
+
+ // Get the text element corresponding to this bar by index.
+ cy.get('text')
+ .eq(index)
+ .then(($text) => {
+ const bbox = $text[0].getBBox();
+ const textProps = {
+ x: bbox.x,
+ y: bbox.y,
+ width: bbox.width,
+ height: bbox.height,
+ };
+
+ // Verify that the text label is positioned within the boundaries of the bar.
+ expect(textProps.x).to.be.greaterThan(barProps.x);
+ expect(textProps.x + textProps.width).to.be.lessThan(barProps.x + barProps.width);
+
+ expect(textProps.y).to.be.greaterThan(barProps.y);
+ expect(textProps.y + textProps.height).to.be.lessThan(barProps.y + barProps.height);
+ expect(textProps.y + textProps.height / 2).to.be.closeTo(
+ barProps.y + barProps.height / 2,
+ 5
+ );
+ });
+ });
+ });
+ });
+
+ it('should render data labels correctly for a bar in the vertical xy-chart', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ ---
+ xychart-beta
+ title "Sales Revenue"
+ x-axis Months [jan]
+ y-axis "Revenue (in $)" 3000 --> 12000
+ bar [4000]
+ `,
+ {}
+ );
+
+ cy.get('g.bar-plot-0').within(() => {
+ cy.get('rect').each(($rect, index) => {
+ // Extract bar properties
+ const barProps = {
+ x: parseFloat($rect.attr('x')),
+ y: parseFloat($rect.attr('y')),
+ width: parseFloat($rect.attr('width')),
+ height: parseFloat($rect.attr('height')),
+ };
+
+ // Get the text element corresponding to this bar by index.
+ cy.get('text')
+ .eq(index)
+ .then(($text) => {
+ const bbox = $text[0].getBBox();
+ const textProps = {
+ x: bbox.x,
+ y: bbox.y,
+ width: bbox.width,
+ height: bbox.height,
+ };
+
+ // Verify that the text label is positioned within the boundaries of the bar.
+ expect(textProps.x).to.be.greaterThan(barProps.x);
+ expect(textProps.x + textProps.width).to.be.lessThan(barProps.x + barProps.width);
+
+ // Check horizontal alignment (within tolerance)
+ expect(textProps.x + textProps.width / 2).to.be.closeTo(
+ barProps.x + barProps.width / 2,
+ 5
+ );
+
+ expect(textProps.y).to.be.greaterThan(barProps.y);
+ expect(textProps.y + textProps.height).to.be.lessThan(barProps.y + barProps.height);
+ });
+ });
+ });
+ });
+
+ it('should render data labels correctly for a bar in the horizontal xy-chart', () => {
+ imgSnapshotTest(
+ `
+ ---
+ config:
+ xyChart:
+ showDataLabel: true
+ chartOrientation: horizontal
+ ---
+ xychart-beta
+ title "Sales Revenue"
+ x-axis Months [jan]
+ y-axis "Revenue (in $)" 3000 --> 12000
+ bar [4000]
+ `,
+ {}
+ );
+
+ cy.get('g.bar-plot-0').within(() => {
+ cy.get('rect').each(($rect, index) => {
+ // Extract bar properties
+ const barProps = {
+ x: parseFloat($rect.attr('x')),
+ y: parseFloat($rect.attr('y')),
+ width: parseFloat($rect.attr('width')),
+ height: parseFloat($rect.attr('height')),
+ };
+
+ // Get the text element corresponding to this bar by index.
+ cy.get('text')
+ .eq(index)
+ .then(($text) => {
+ const bbox = $text[0].getBBox();
+ const textProps = {
+ x: bbox.x,
+ y: bbox.y,
+ width: bbox.width,
+ height: bbox.height,
+ };
+
+ // Verify that the text label is positioned within the boundaries of the bar.
+ expect(textProps.x).to.be.greaterThan(barProps.x);
+ expect(textProps.x + textProps.width).to.be.lessThan(barProps.x + barProps.width);
+
+ expect(textProps.y).to.be.greaterThan(barProps.y);
+ expect(textProps.y + textProps.height).to.be.lessThan(barProps.y + barProps.height);
+ expect(textProps.y + textProps.height / 2).to.be.closeTo(
+ barProps.y + barProps.height / 2,
+ 5
+ );
+ });
+ });
+ });
+ });
});
diff --git a/cypress/platform/ashish2.html b/cypress/platform/ashish2.html
index f9132d2e2..30584295d 100644
--- a/cypress/platform/ashish2.html
+++ b/cypress/platform/ashish2.html
@@ -4,7 +4,7 @@
timeline
title My day
- section section with no tasks
+ section Section with no tasks
section Go to work at the dog office
1930 : first step : second step is a long step
: third step
@@ -70,18 +70,18 @@
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
+ I am a very, very big task
+ I am not so big task
timeline title MermaidChart 2023 Timeline section 2023 Q1
Release Personal Tier - Buttet 1 : sub-point 1a : sub-point 1b + Bullet 1 : sub-point 1a : sub-point 1b : sub-point 1c Bullet 2 : sub-point 2a : sub-point 2b section 2023 Q2
Release XYZ Tier - Buttet 3 : sub-point
3a : sub-point 3b + Bullet 3 : sub-point
3a : sub-point 3b : sub-point 3c Bullet 4 : sub-point 4a : sub-point 4b @@ -93,7 +93,7 @@ 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 + section Bronze 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. @@ -106,7 +106,7 @@ title History of Social Media Platform 2002 : LinkedIn 2004 : Facebook : Google : Pixar - 2005 : Youtube + 2005 : YouTube 2006 : Twitter 2007 : Tumblr 2008s : Instagram @@ -122,7 +122,7 @@ title History of Social Media Platform 2002 : LinkedIn 2004 : Facebook : Google : Pixar - 2005 : Youtube + 2005 : YouTube 2006 : Twitter 2007 : Tumblr 2008s : Instagram @@ -139,7 +139,7 @@ title History of Social Media Platform 2002 : LinkedIn 2004 : Facebook : Google - 2005 : Youtube + 2005 : YouTube 2006 : Twitter 2007 : Tumblr 2008 : Instagram @@ -152,7 +152,7 @@ title History of Social Media Platform 2002 : LinkedIn 2004 : Facebook : Google - 2005 : Youtube + 2005 : YouTube 2006 : Twitter 2007 : Tumblr 2008s : Instagram diff --git a/cypress/platform/class.html b/cypress/platform/class.html index 0b1a5729a..b649d1184 100644 --- a/cypress/platform/class.html +++ b/cypress/platform/class.html @@ -3,7 +3,7 @@