diff --git a/.build/common.ts b/.build/common.ts
index efd0e3a85..2497d443f 100644
--- a/.build/common.ts
+++ b/.build/common.ts
@@ -33,6 +33,11 @@ export const packageOptions = {
packageName: 'mermaid-layout-elk',
file: 'layouts.ts',
},
+ 'mermaid-layout-tidy-tree': {
+ name: 'mermaid-layout-tidy-tree',
+ packageName: 'mermaid-layout-tidy-tree',
+ file: 'index.ts',
+ },
examples: {
name: 'mermaid-examples',
packageName: 'examples',
diff --git a/.changeset/brave-baths-behave.md b/.changeset/brave-baths-behave.md
new file mode 100644
index 000000000..b688a1faf
--- /dev/null
+++ b/.changeset/brave-baths-behave.md
@@ -0,0 +1,5 @@
+---
+'mermaid': patch
+---
+
+fix: Prevent HTML tags from being escaped in sandbox label rendering
diff --git a/.changeset/brave-memes-flash.md b/.changeset/brave-memes-flash.md
new file mode 100644
index 000000000..720cd7202
--- /dev/null
+++ b/.changeset/brave-memes-flash.md
@@ -0,0 +1,5 @@
+---
+'mermaid': patch
+---
+
+fix: Support edge animation in hand drawn look
diff --git a/.changeset/busy-mirrors-try.md b/.changeset/busy-mirrors-try.md
new file mode 100644
index 000000000..7e5d3b632
--- /dev/null
+++ b/.changeset/busy-mirrors-try.md
@@ -0,0 +1,5 @@
+---
+'mermaid': patch
+---
+
+fix: Resolved parsing error where direction TD was not recognized within subgraphs
diff --git a/.changeset/chilly-words-march.md b/.changeset/chilly-words-march.md
new file mode 100644
index 000000000..54c0b4ebf
--- /dev/null
+++ b/.changeset/chilly-words-march.md
@@ -0,0 +1,5 @@
+---
+'mermaid': patch
+---
+
+fix: Correct viewBox casing and make SVGs responsive
diff --git a/.changeset/curly-apes-prove.md b/.changeset/curly-apes-prove.md
new file mode 100644
index 000000000..2acf3d1a3
--- /dev/null
+++ b/.changeset/curly-apes-prove.md
@@ -0,0 +1,5 @@
+---
+'mermaid': patch
+---
+
+fix: Improve participant parsing and prevent recursive loops on invalid syntax
diff --git a/.changeset/loud-results-melt.md b/.changeset/loud-results-melt.md
new file mode 100644
index 000000000..7005750c6
--- /dev/null
+++ b/.changeset/loud-results-melt.md
@@ -0,0 +1,5 @@
+---
+'mermaid': minor
+---
+
+feat: Add half-arrowheads (solid & stick) and central connection support
diff --git a/.changeset/short-seals-sort.md b/.changeset/short-seals-sort.md
new file mode 100644
index 000000000..db8309c7f
--- /dev/null
+++ b/.changeset/short-seals-sort.md
@@ -0,0 +1,5 @@
+---
+'mermaid': minor
+---
+
+feat: allow to put notes in namespaces on classDiagram
diff --git a/.changeset/slow-lemons-know.md b/.changeset/slow-lemons-know.md
new file mode 100644
index 000000000..49eb48543
--- /dev/null
+++ b/.changeset/slow-lemons-know.md
@@ -0,0 +1,5 @@
+---
+'@mermaid': patch
+---
+
+fix: Mindmap breaking in ELK layout
diff --git a/.changeset/sweet-games-build.md b/.changeset/sweet-games-build.md
new file mode 100644
index 000000000..a71e3de25
--- /dev/null
+++ b/.changeset/sweet-games-build.md
@@ -0,0 +1,5 @@
+---
+'mermaid': patch
+---
+
+fix(er-diagram): prevent syntax error when using 'u', numbers, and decimals in node names
diff --git a/.changeset/ten-plums-bet.md b/.changeset/ten-plums-bet.md
new file mode 100644
index 000000000..f00a41090
--- /dev/null
+++ b/.changeset/ten-plums-bet.md
@@ -0,0 +1,5 @@
+---
+'mermaid': patch
+---
+
+fix: Support ComponentQueue_Ext to prevent parsing error
diff --git a/.cspell/code-terms.txt b/.cspell/code-terms.txt
index 8b549f888..5f72ea221 100644
--- a/.cspell/code-terms.txt
+++ b/.cspell/code-terms.txt
@@ -1,3 +1,5 @@
+!viewbox
+# It should be viewBox
# This file contains coding related terms
ALPHANUM
antiscript
diff --git a/.cspell/libraries.txt b/.cspell/libraries.txt
index feee10fd1..4fceed5bb 100644
--- a/.cspell/libraries.txt
+++ b/.cspell/libraries.txt
@@ -64,6 +64,7 @@ rscratch
shiki
Slidev
sparkline
+speccharts
sphinxcontrib
ssim
stylis
diff --git a/.cspell/mermaid-terms.txt b/.cspell/mermaid-terms.txt
index b0cfa0a1d..45152a0ce 100644
--- a/.cspell/mermaid-terms.txt
+++ b/.cspell/mermaid-terms.txt
@@ -5,8 +5,10 @@ bmatrix
braintree
catmull
compositTitleSize
+cose
curv
doublecircle
+elem
elems
gantt
gitgraph
diff --git a/.cspell/misc-terms.txt b/.cspell/misc-terms.txt
index 1820e3c86..2906a02fa 100644
--- a/.cspell/misc-terms.txt
+++ b/.cspell/misc-terms.txt
@@ -1,4 +1,5 @@
BRANDES
+Buzan
circo
handDrawn
KOEPF
diff --git a/.esbuild/util.ts b/.esbuild/util.ts
index 3a0ec6b41..a3e2ffe55 100644
--- a/.esbuild/util.ts
+++ b/.esbuild/util.ts
@@ -71,6 +71,9 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const external: string[] = ['require', 'fs', 'path'];
const outFileName = getFileName(name, options);
+ const { dependencies, version } = JSON.parse(
+ readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
+ );
const output: BuildOptions = buildOptions({
...rest,
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
@@ -82,15 +85,13 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
define: {
// This needs to be stringified for esbuild
- includeLargeFeatures: `${includeLargeFeatures}`,
+ 'injected.includeLargeFeatures': `${includeLargeFeatures}`,
+ 'injected.version': `'${version}'`,
'import.meta.vitest': 'undefined',
},
});
if (core) {
- const { dependencies } = JSON.parse(
- readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
- );
// Core build is used to generate file without bundled dependencies.
// This is used by downstream projects to bundle dependencies themselves.
// Ignore dependencies and any dependencies of dependencies
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index a6400a86a..64de2eb66 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -26,8 +26,8 @@ jobs:
strategy:
fail-fast: false
matrix:
- language: ['javascript']
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ language: ['javascript', 'actions']
+ # CodeQL supports [ 'actions', 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
@@ -36,7 +36,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
+ uses: github/codeql-action/init@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
with:
config-file: ./.github/codeql/codeql-config.yml
languages: ${{ matrix.language }}
@@ -48,7 +48,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
+ uses: github/codeql-action/autobuild@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -62,4 +62,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
+ uses: github/codeql-action/analyze@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml
index dd97b49e1..853818425 100644
--- a/.github/workflows/e2e-applitools.yml
+++ b/.github/workflows/e2e-applitools.yml
@@ -23,9 +23,6 @@ env:
jobs:
e2e-applitools:
runs-on: ubuntu-latest
- container:
- image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
- options: --user 1001
steps:
- if: ${{ ! env.USE_APPLI }}
name: Warn if not using Applitools
@@ -56,7 +53,7 @@ jobs:
args: -X POST "$APPLITOOLS_SERVER_URL/api/externals/github/push?apiKey=$APPLITOOLS_API_KEY&CommitSha=$GITHUB_SHA&BranchName=${APPLITOOLS_BRANCH}$&ParentBranchName=$APPLITOOLS_PARENT_BRANCH"
- name: Cypress run
- uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
+ uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
id: cypress
with:
start: pnpm run dev
diff --git a/.github/workflows/e2e-timings.yml b/.github/workflows/e2e-timings.yml
index 2bbfa8412..21f6b4049 100644
--- a/.github/workflows/e2e-timings.yml
+++ b/.github/workflows/e2e-timings.yml
@@ -27,12 +27,12 @@ jobs:
with:
node-version-file: '.node-version'
- name: Install dependencies
- uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
+ uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
with:
runTests: false
- name: Cypress run
- uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
+ uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
id: cypress
with:
install: false
@@ -58,7 +58,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT
- name: Commit and create pull request
- uses: peter-evans/create-pull-request@cb4d3bfce175d44325c6b7697f81e0afe8a79bdf
+ uses: peter-evans/create-pull-request@0edc001d28a2959cd7a6b505629f1d82f0a6e67d
with:
add-paths: |
cypress/timings.json
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 56883b987..8fbf6d6f6 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -45,7 +45,7 @@ jobs:
node-version-file: '.node-version'
- name: Cache snapshots
id: cache-snapshot
- uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
+ uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: ./cypress/snapshots
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
@@ -59,7 +59,7 @@ jobs:
- name: Install dependencies
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
- uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
+ uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
with:
# just perform install
runTests: false
@@ -95,13 +95,13 @@ jobs:
# These cached snapshots are downloaded, providing the reference snapshots.
- name: Cache snapshots
id: cache-snapshot
- uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
+ uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: ./cypress/snapshots
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
- name: Install dependencies
- uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
+ uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
with:
runTests: false
@@ -117,7 +117,7 @@ jobs:
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
- uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
+ uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
id: cypress
with:
install: false
diff --git a/.github/workflows/link-checker.yml b/.github/workflows/link-checker.yml
index f855ed23b..ce43c2ed7 100644
--- a/.github/workflows/link-checker.yml
+++ b/.github/workflows/link-checker.yml
@@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Restore lychee cache
- uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
+ uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 649c40034..ece84ac20 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -36,11 +36,10 @@ jobs:
- name: Create Release Pull Request or Publish to npm
id: changesets
- uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9
+ uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc # v1.4.10
with:
version: pnpm changeset:version
publish: pnpm changeset:publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 4901b3781..3177ca15a 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -20,18 +20,18 @@ jobs:
with:
persist-credentials: false
- name: Run analysis
- uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
+ uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: Upload artifact
- uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: Upload to code-scanning
- uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
+ uses: github/codeql-action/upload-sarif@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
with:
sarif_file: results.sarif
diff --git a/.github/workflows/update-browserlist.yml b/.github/workflows/update-browserlist.yml
index 94de12ad3..54ef39b11 100644
--- a/.github/workflows/update-browserlist.yml
+++ b/.github/workflows/update-browserlist.yml
@@ -19,7 +19,7 @@ jobs:
message: 'chore: update browsers list'
push: false
- name: Create Pull Request
- uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
+ uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
with:
branch: update-browserslist
title: Update Browserslist
diff --git a/.github/workflows/validate-lockfile.yml b/.github/workflows/validate-lockfile.yml
index 6eb0a63ca..dcfb255b6 100644
--- a/.github/workflows/validate-lockfile.yml
+++ b/.github/workflows/validate-lockfile.yml
@@ -1,7 +1,7 @@
name: Validate pnpm-lock.yaml
on:
- pull_request:
+ pull_request_target:
paths:
- 'pnpm-lock.yaml'
- '**/package.json'
@@ -15,13 +15,8 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
-
- - name: Set up Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
-
- - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
+ ref: ${{ github.event.pull_request.head.sha }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Validate pnpm-lock.yaml entries
id: validate # give this step an ID so we can reference its outputs
@@ -35,7 +30,7 @@ jobs:
# 2) No unwanted vitepress paths
if grep -qF 'packages/mermaid/src/vitepress' pnpm-lock.yaml; then
- issues+=("• Disallowed path 'packages/mermaid/src/vitepress' present. Run `rm -rf packages/mermaid/src/vitepress && pnpm install` to regenerate.")
+ issues+=("• Disallowed path 'packages/mermaid/src/vitepress' present. Run \`rm -rf packages/mermaid/src/vitepress && pnpm install\` to regenerate.")
fi
# 3) Lockfile only changes when package.json changes
@@ -55,16 +50,41 @@ jobs:
exit 1
fi
+ - name: Find existing lockfile validation comment
+ if: always()
+ uses: peter-evans/find-comment@v3
+ id: find-comment
+ with:
+ issue-number: ${{ github.event.pull_request.number }}
+ comment-author: 'github-actions[bot]'
+ body-includes: 'Lockfile Validation Failed'
+
- name: Comment on PR if validation failed
if: failure()
- uses: peter-evans/create-or-update-comment@v4
+ uses: peter-evans/create-or-update-comment@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
+ comment-id: ${{ steps.find-comment.outputs.comment-id }}
+ edit-mode: replace
body: |
+ ❌ **Lockfile Validation Failed**
+
The following issue(s) were detected:
${{ steps.validate.outputs.errors }}
Please address these and push an update.
_Posted automatically by GitHub Actions_
+
+ - name: Delete comment if validation passed
+ if: success() && steps.find-comment.outputs.comment-id != ''
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ await github.rest.issues.deleteComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: ${{ steps.find-comment.outputs.comment-id }},
+ });
diff --git a/.gitignore b/.gitignore
index 7448f2a81..7eb55d5cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ node_modules/
coverage/
.idea/
.pnpm-store/
+.instructions/
dist
v8-compile-cache-0
diff --git a/.vite/build.ts b/.vite/build.ts
index 480dd6b30..d59f0fac3 100644
--- a/.vite/build.ts
+++ b/.vite/build.ts
@@ -78,6 +78,8 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
},
define: {
'import.meta.vitest': 'undefined',
+ 'injected.includeLargeFeatures': 'true',
+ 'injected.version': `'0.0.0'`,
},
resolve: {
extensions: [],
@@ -94,10 +96,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
}),
...visualizerOptions(packageName, core),
],
- define: {
- // Needs to be string
- includeLargeFeatures: 'true',
- },
};
if (watch && config.build) {
diff --git a/Dockerfile b/Dockerfile
index 533604407..d7e6c8f84 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,7 +5,7 @@ USER 0:0
RUN corepack enable \
&& corepack enable pnpm
-RUN apk add --no-cache git~=2.43.4 \
+RUN apk add --no-cache git~=2.43 \
&& git config --add --system safe.directory /mermaid
ENV NODE_OPTIONS="--max_old_space_size=8192"
diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts
index ab4bbef64..51268c2a9 100644
--- a/cypress/helpers/util.ts
+++ b/cypress/helpers/util.ts
@@ -6,6 +6,7 @@ interface CypressConfig {
listUrl?: boolean;
listId?: string;
name?: string;
+ screenshot?: boolean;
}
type CypressMermaidConfig = MermaidConfig & CypressConfig;
@@ -90,20 +91,33 @@ export const renderGraph = (
export const openURLAndVerifyRendering = (
url: string,
- options: CypressMermaidConfig,
+ { screenshot = true, ...options }: CypressMermaidConfig,
validation?: any
): void => {
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
- cy.get('svg').should('be.visible');
- if (validation) {
- cy.get('svg').should(validation);
+ // Handle sandbox mode where SVG is inside an iframe
+ if (options.securityLevel === 'sandbox') {
+ cy.get('iframe').should('be.visible');
+ if (validation) {
+ cy.get('iframe').should(validation);
+ }
+ } else {
+ cy.get('svg').should('be.visible');
+ // cspell:ignore viewbox
+ cy.get('svg').should('not.have.attr', 'viewbox');
+
+ if (validation) {
+ cy.get('svg').should(validation);
+ }
}
- verifyScreenshot(name);
+ if (screenshot) {
+ verifyScreenshot(name);
+ }
};
export const verifyScreenshot = (name: string): void => {
diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js
index b48a197a4..a699e03a7 100644
--- a/cypress/integration/other/configuration.spec.js
+++ b/cypress/integration/other/configuration.spec.js
@@ -98,12 +98,12 @@ describe('Configuration', () => {
it('should handle arrowMarkerAbsolute set to true', () => {
renderGraph(
`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]
- `,
+ 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]
+ `,
{
arrowMarkerAbsolute: true,
}
@@ -113,8 +113,7 @@ describe('Configuration', () => {
cy.get('path')
.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/rendering/c4.spec.js b/cypress/integration/rendering/c4.spec.js
index 00e71adec..92b834d41 100644
--- a/cypress/integration/rendering/c4.spec.js
+++ b/cypress/integration/rendering/c4.spec.js
@@ -114,4 +114,28 @@ describe('C4 diagram', () => {
{}
);
});
+ it('C4.6 should render C4Context diagram with ComponentQueue_Ext', () => {
+ imgSnapshotTest(
+ `
+ C4Context
+ title System Context diagram with ComponentQueue_Ext
+
+ 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") {
+ ComponentQueue_Ext(msgQueue, "Message Queue", "RabbitMQ", "External message queue system for processing banking transactions")
+ System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
+ }
+ }
+
+ BiRel(customerA, SystemAA, "Uses")
+ Rel(SystemAA, msgQueue, "Sends messages to")
+ Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
+ `,
+ {}
+ );
+ });
});
diff --git a/cypress/integration/rendering/classDiagram-v2.spec.js b/cypress/integration/rendering/classDiagram-v2.spec.js
index 0c5dbc04b..f54768d9a 100644
--- a/cypress/integration/rendering/classDiagram-v2.spec.js
+++ b/cypress/integration/rendering/classDiagram-v2.spec.js
@@ -562,6 +562,20 @@ class C13["With Città foreign language"]
`
);
});
+ it('should add notes in namespaces', function () {
+ imgSnapshotTest(
+ `
+ classDiagram
+ note "This is a outer note"
+ note for C1 "This is a outer note for C1"
+ namespace Namespace1 {
+ note "This is a inner note"
+ note for C1 "This is a inner note for C1"
+ class C1
+ }
+ `
+ );
+ });
it('should render a simple class diagram with no members', () => {
imgSnapshotTest(
`
diff --git a/cypress/integration/rendering/classDiagram-v3.spec.js b/cypress/integration/rendering/classDiagram-v3.spec.js
index 626d6fcea..7e8d2ff0a 100644
--- a/cypress/integration/rendering/classDiagram-v3.spec.js
+++ b/cypress/integration/rendering/classDiagram-v3.spec.js
@@ -709,6 +709,20 @@ class C13["With Città foreign language"]
`
);
});
+ it('should add notes in namespaces', function () {
+ imgSnapshotTest(
+ `
+ classDiagram
+ note "This is a outer note"
+ note for C1 "This is a outer note for C1"
+ namespace Namespace1 {
+ note "This is a inner note"
+ note for C1 "This is a inner note for C1"
+ class C1
+ }
+ `
+ );
+ });
it('should render a simple class diagram with no members', () => {
imgSnapshotTest(
`
diff --git a/cypress/integration/rendering/classDiagram.spec.js b/cypress/integration/rendering/classDiagram.spec.js
index bd2a96b34..6cea402f8 100644
--- a/cypress/integration/rendering/classDiagram.spec.js
+++ b/cypress/integration/rendering/classDiagram.spec.js
@@ -524,5 +524,18 @@ describe('Class diagram', () => {
`,
{}
);
+ it('should handle an empty class body with empty braces', () => {
+ imgSnapshotTest(
+ ` classDiagram
+ class FooBase~T~ {}
+ class Bar {
+ +Zip
+ +Zap()
+ }
+ FooBase <|-- Ba
+ `,
+ { flowchart: { defaultRenderer: 'elk' } }
+ );
+ });
});
});
diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js
index 7d59a5793..fc2ae9919 100644
--- a/cypress/integration/rendering/erDiagram.spec.js
+++ b/cypress/integration/rendering/erDiagram.spec.js
@@ -381,4 +381,92 @@ ORDER ||--|{ LINE-ITEM : contains
);
});
});
+
+ describe('Special characters and numbers syntax', () => {
+ it('should render ER diagram with numeric entity names', () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ 1 ||--|| ORDER : places
+ ORDER ||--|{ 2 : contains
+ 2 ||--o{ 3.5 : references
+ `,
+ { logLevel: 1 }
+ );
+ });
+
+ it('should render ER diagram with "u" character in entity names and cardinality', () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER ||--|| u : has
+ u ||--|| ORDER : places
+ PROJECT u--o{ TEAM_MEMBER : "parent"
+ `,
+ { logLevel: 1 }
+ );
+ });
+
+ it('should render ER diagram with decimal numbers in relationships', () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ 2.5 ||--|| 1.5 : has
+ CUSTOMER ||--o{ 3.14 : references
+ 1.0 ||--|{ ORDER : contains
+ `,
+ { logLevel: 1 }
+ );
+ });
+
+ it('should render ER diagram with numeric entity names and attributes', () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ 1 {
+ string name
+ int value
+ }
+ 1 ||--|| ORDER : places
+ ORDER {
+ float price
+ string description
+ }
+ `,
+ { logLevel: 1 }
+ );
+ });
+
+ it('should render complex ER diagram with mixed special entity names', () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER ||--o{ 1 : places
+ 1 ||--|{ u : contains
+ 1.5
+ u ||--|| 2.5 : processes
+ 2.5 {
+ string id
+ float value
+ }
+ u {
+ varchar(50) name
+ int count
+ }
+ `,
+ { logLevel: 1 }
+ );
+ });
+ it('should render ER diagram with standalone numeric entities', () => {
+ imgSnapshotTest(
+ `erDiagram
+ PRODUCT ||--o{ ORDER-ITEM : has
+ 1.5
+ u
+ 1
+ `,
+ { logLevel: 1 }
+ );
+ });
+ });
});
diff --git a/cypress/integration/rendering/flowchart-elk.spec.js b/cypress/integration/rendering/flowchart-elk.spec.js
index 312e1d5b4..fac4f3b4b 100644
--- a/cypress/integration/rendering/flowchart-elk.spec.js
+++ b/cypress/integration/rendering/flowchart-elk.spec.js
@@ -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(''));
- verifyNumber(maxWidthValue, 380);
+ verifyNumber(maxWidthValue, 380, 15);
});
});
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);
- verifyNumber(width, 380);
+ verifyNumber(width, 380, 15);
expect(svg).to.not.have.attr('style');
});
});
diff --git a/cypress/integration/rendering/flowchart-handDrawn.spec.js b/cypress/integration/rendering/flowchart-handDrawn.spec.js
index 49c55c628..d3ca1d1f1 100644
--- a/cypress/integration/rendering/flowchart-handDrawn.spec.js
+++ b/cypress/integration/rendering/flowchart-handDrawn.spec.js
@@ -1029,4 +1029,19 @@ graph TD
}
);
});
+
+ it('FDH49: should add edge animation', () => {
+ renderGraph(
+ `
+ flowchart TD
+ A(["Start"]) L_A_B_0@--> B{"Decision"}
+ B --> C["Option A"] & D["Option B"]
+ style C stroke-width:4px,stroke-dasharray: 5
+ L_A_B_0@{ animation: slow }
+ L_B_D_0@{ animation: fast }`,
+ { look: 'handDrawn', screenshot: false }
+ );
+ cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
+ cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
+ });
});
diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js
index 8c6cde57a..cd3676fbf 100644
--- a/cypress/integration/rendering/flowchart-v2.spec.js
+++ b/cypress/integration/rendering/flowchart-v2.spec.js
@@ -79,6 +79,18 @@ describe('Flowchart v2', () => {
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
+ it('6a: should render complex HTML in labels with sandbox security', () => {
+ 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]
+ `,
+ { securityLevel: 'sandbox', flowchart: { htmlLabels: true } }
+ );
+ });
it('7: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
`flowchart TD
@@ -1186,4 +1198,17 @@ end
imgSnapshotTest(graph, { htmlLabels: false });
});
});
+
+ it('V2 - 17: should apply class def colour to edge label', () => {
+ imgSnapshotTest(
+ ` graph LR
+ id1(Start) link@-- "Label" -->id2(Stop)
+ style id1 fill:#f9f,stroke:#333,stroke-width:4px
+
+class id2 myClass
+classDef myClass fill:#bbf,stroke:#f66,stroke-width:2px,color:white,stroke-dasharray: 5 5
+class link myClass
+`
+ );
+ });
});
diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js
index 40713ac4e..5e1984377 100644
--- a/cypress/integration/rendering/flowchart.spec.js
+++ b/cypress/integration/rendering/flowchart.spec.js
@@ -774,6 +774,21 @@ describe('Graph', () => {
expect(svg).to.not.have.attr('style');
});
});
+ it('40: should add edge animation', () => {
+ renderGraph(
+ `
+ flowchart TD
+ A(["Start"]) L_A_B_0@--> B{"Decision"}
+ B --> C["Option A"] & D["Option B"]
+ style C stroke-width:4px,stroke-dasharray: 5
+ L_A_B_0@{ animation: slow }
+ L_B_D_0@{ animation: fast }`,
+ { screenshot: false }
+ );
+ // Verify animation classes are applied to both edges
+ cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
+ cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
+ });
it('58: handle styling with style expressions', () => {
imgSnapshotTest(
`
@@ -973,4 +988,19 @@ graph TD
}
);
});
+
+ it('70: should render a subgraph with direction TD', () => {
+ imgSnapshotTest(
+ `
+ flowchart LR
+ subgraph A
+ direction TD
+ a --> b
+ end
+ `,
+ {
+ fontFamily: 'courier',
+ }
+ );
+ });
});
diff --git a/cypress/integration/rendering/gantt.spec.js b/cypress/integration/rendering/gantt.spec.js
index 32dbcb4d9..72cb6ea29 100644
--- a/cypress/integration/rendering/gantt.spec.js
+++ b/cypress/integration/rendering/gantt.spec.js
@@ -803,4 +803,34 @@ describe('Gantt diagram', () => {
{}
);
});
+ it('should handle numeric timestamps with dateFormat x', () => {
+ imgSnapshotTest(
+ `
+ gantt
+ title Process time profile (ms)
+ dateFormat x
+ axisFormat %L
+ tickInterval 250millisecond
+
+ section Pipeline
+ Parse JSON p1: 000, 120
+ `,
+ {}
+ );
+ });
+ it('should handle numeric timestamps with dateFormat X', () => {
+ imgSnapshotTest(
+ `
+ gantt
+ title Process time profile (ms)
+ dateFormat X
+ axisFormat %L
+ tickInterval 250millisecond
+
+ section Pipeline
+ Parse JSON p1: 000, 120
+ `,
+ {}
+ );
+ });
});
diff --git a/cypress/integration/rendering/mindmap-tidy-tree.spec.js b/cypress/integration/rendering/mindmap-tidy-tree.spec.js
new file mode 100644
index 000000000..e111c281a
--- /dev/null
+++ b/cypress/integration/rendering/mindmap-tidy-tree.spec.js
@@ -0,0 +1,79 @@
+import { imgSnapshotTest } from '../../helpers/util.ts';
+
+describe('Mindmap Tidy Tree', () => {
+ it('1-tidy-tree: should render a simple mindmap without children', () => {
+ imgSnapshotTest(
+ ` ---
+ config:
+ layout: tidy-tree
+ ---
+ mindmap
+ root((mindmap))
+ A
+ B
+ `
+ );
+ });
+ it('2-tidy-tree: should render a simple mindmap', () => {
+ imgSnapshotTest(
+ ` ---
+ config:
+ layout: tidy-tree
+ ---
+ mindmap
+ root((mindmap is a long thing))
+ A
+ B
+ C
+ D
+ `
+ );
+ });
+ it('3-tidy-tree: should render a mindmap with different shapes', () => {
+ imgSnapshotTest(
+ ` ---
+ config:
+ layout: tidy-tree
+ ---
+ mindmap
+ root((mindmap))
+ Origins
+ Long history
+ ::icon(fa fa-book)
+ Popularisation
+ British popular psychology author Tony Buzan
+ Research
+ On effectiveness<br/>and features
+ On Automatic creation
+ Uses
+ Creative techniques
+ Strategic planning
+ Argument mapping
+ Tools
+ id)I am a cloud(
+ id))I am a bang((
+ Tools
+ `
+ );
+ });
+ it('4-tidy-tree: should render a mindmap with children', () => {
+ imgSnapshotTest(
+ ` ---
+ config:
+ layout: tidy-tree
+ ---
+ mindmap
+ ((This is a mindmap))
+ child1
+ grandchild 1
+ grandchild 2
+ child2
+ grandchild 3
+ grandchild 4
+ child3
+ grandchild 5
+ grandchild 6
+ `
+ );
+ });
+});
diff --git a/cypress/integration/rendering/mindmap.spec.ts b/cypress/integration/rendering/mindmap.spec.ts
index d76e58c56..e0409ed46 100644
--- a/cypress/integration/rendering/mindmap.spec.ts
+++ b/cypress/integration/rendering/mindmap.spec.ts
@@ -159,12 +159,10 @@ root
});
it('square shape', () => {
imgSnapshotTest(
- `
-mindmap
+ `mindmap
root[
The root
- ]
- `,
+ ]`,
{},
undefined,
shouldHaveRoot
@@ -172,12 +170,10 @@ mindmap
});
it('rounded rect shape', () => {
imgSnapshotTest(
- `
-mindmap
+ `mindmap
root((
The root
- ))
- `,
+ ))`,
{},
undefined,
shouldHaveRoot
@@ -185,12 +181,10 @@ mindmap
});
it('circle shape', () => {
imgSnapshotTest(
- `
-mindmap
+ `mindmap
root(
The root
- )
- `,
+ )`,
{},
undefined,
shouldHaveRoot
@@ -198,10 +192,8 @@ mindmap
});
it('default shape', () => {
imgSnapshotTest(
- `
-mindmap
- The root
- `,
+ `mindmap
+ The root`,
{},
undefined,
shouldHaveRoot
@@ -209,12 +201,10 @@ mindmap
});
it('adding children', () => {
imgSnapshotTest(
- `
-mindmap
+ `mindmap
The root
child1
- child2
- `,
+ child2`,
{},
undefined,
shouldHaveRoot
@@ -222,13 +212,11 @@ mindmap
});
it('adding grand children', () => {
imgSnapshotTest(
- `
-mindmap
+ `mindmap
The root
child1
child2
- child3
- `,
+ child3`,
{},
undefined,
shouldHaveRoot
@@ -240,25 +228,21 @@ mindmap
`mindmap
id1[\`**Start** with
a second line 😎\`]
- id2[\`The dog in **the** hog... a *very long text* about it
-Word!\`]
-`
+ id2[\`The dog in **the** hog... a *very long text* about it Word!\`]`
);
});
});
describe('Include char sequence "graph" in text (#6795)', () => {
it('has a label with char sequence "graph"', () => {
imgSnapshotTest(
- `
- mindmap
+ ` mindmap
root
Photograph
Waterfall
Landscape
Geography
Mountains
- Rocks
- `,
+ Rocks`,
{ flowchart: { defaultRenderer: 'elk' } }
);
});
diff --git a/cypress/integration/rendering/sequencediagram-v2.spec.js b/cypress/integration/rendering/sequencediagram-v2.spec.js
new file mode 100644
index 000000000..42db4001d
--- /dev/null
+++ b/cypress/integration/rendering/sequencediagram-v2.spec.js
@@ -0,0 +1,780 @@
+import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
+
+const looks = ['classic'];
+const participantTypes = [
+ { type: 'participant', display: 'participant' },
+ { type: 'actor', display: 'actor' },
+ { type: 'boundary', display: 'boundary' },
+ { type: 'control', display: 'control' },
+ { type: 'entity', display: 'entity' },
+ { type: 'database', display: 'database' },
+ { type: 'collections', display: 'collections' },
+ { type: 'queue', display: 'queue' },
+];
+
+const restrictedTypes = ['boundary', 'control', 'entity', 'database', 'collections', 'queue'];
+
+const interactionTypes = ['->>', '-->>', '->', '-->', '-x', '--x', '->>+', '-->>+'];
+
+const notePositions = ['left of', 'right of', 'over'];
+
+function getParticipantLine(name, type, alias) {
+ if (restrictedTypes.includes(type)) {
+ return ` participant ${name}@{ "type" : "${type}" }\n`;
+ } else if (alias) {
+ return ` participant ${name}@{ "type" : "${type}" } \n`;
+ } else {
+ return ` participant ${name}@{ "type" : "${type}" }\n`;
+ }
+}
+
+looks.forEach((look) => {
+ describe(`Sequence Diagram Tests - ${look} look`, () => {
+ it('should render all participant types', () => {
+ let diagramCode = `sequenceDiagram\n`;
+ participantTypes.forEach((pt, index) => {
+ const name = `${pt.display}${index}`;
+ diagramCode += getParticipantLine(name, pt.type);
+ });
+ for (let i = 0; i < participantTypes.length - 1; i++) {
+ diagramCode += ` ${participantTypes[i].display}${i} ->> ${participantTypes[i + 1].display}${i + 1}: Message ${i}\n`;
+ }
+ imgSnapshotTest(diagramCode, { look, sequence: { diagramMarginX: 50, diagramMarginY: 10 } });
+ });
+
+ it('should render all interaction types', () => {
+ let diagramCode = `sequenceDiagram\n`;
+ diagramCode += getParticipantLine('A', 'actor');
+ diagramCode += getParticipantLine('B', 'boundary');
+ interactionTypes.forEach((interaction, index) => {
+ diagramCode += ` A ${interaction} B: ${interaction} message ${index}\n`;
+ });
+ imgSnapshotTest(diagramCode, { look });
+ });
+
+ it('should render participant creation and destruction', () => {
+ let diagramCode = `sequenceDiagram\n`;
+ participantTypes.forEach((pt, index) => {
+ const name = `${pt.display}${index}`;
+ diagramCode += getParticipantLine('A', pt.type);
+ diagramCode += getParticipantLine('B', pt.type);
+ diagramCode += ` create participant ${name}@{ "type" : "${pt.type}" }\n`;
+ diagramCode += ` A ->> ${name}: Hello ${pt.display}\n`;
+ if (index % 2 === 0) {
+ diagramCode += ` destroy ${name}\n`;
+ }
+ });
+ imgSnapshotTest(diagramCode, { look });
+ });
+
+ it('should render notes in all positions', () => {
+ let diagramCode = `sequenceDiagram\n`;
+ diagramCode += getParticipantLine('A', 'actor');
+ diagramCode += getParticipantLine('B', 'boundary');
+ notePositions.forEach((position, index) => {
+ diagramCode += ` Note ${position} A: Note ${position} ${index}\n`;
+ });
+ diagramCode += ` A ->> B: Message with notes\n`;
+ imgSnapshotTest(diagramCode, { look });
+ });
+
+ it('should render parallel interactions', () => {
+ let diagramCode = `sequenceDiagram\n`;
+ participantTypes.slice(0, 4).forEach((pt, index) => {
+ diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
+ });
+ diagramCode += ` par Parallel actions\n`;
+ for (let i = 0; i < 3; i += 2) {
+ diagramCode += ` ${participantTypes[i].display}${i} ->> ${participantTypes[i + 1].display}${i + 1}: Message ${i}\n`;
+ if (i < participantTypes.length - 2) {
+ diagramCode += ` and\n`;
+ }
+ }
+ diagramCode += ` end\n`;
+ imgSnapshotTest(diagramCode, { look });
+ });
+
+ it('should render alternative flows', () => {
+ let diagramCode = `sequenceDiagram\n`;
+ diagramCode += getParticipantLine('A', 'actor');
+ diagramCode += getParticipantLine('B', 'boundary');
+ diagramCode += ` alt Successful case\n`;
+ diagramCode += ` A ->> B: Request\n`;
+ diagramCode += ` B -->> A: Success\n`;
+ diagramCode += ` else Failure case\n`;
+ diagramCode += ` A ->> B: Request\n`;
+ diagramCode += ` B --x A: Failure\n`;
+ diagramCode += ` end\n`;
+ imgSnapshotTest(diagramCode, { look });
+ });
+
+ it('should render loops', () => {
+ let diagramCode = `sequenceDiagram\n`;
+ participantTypes.slice(0, 3).forEach((pt, index) => {
+ diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
+ });
+ diagramCode += ` loop For each participant\n`;
+ for (let i = 0; i < 3; i++) {
+ diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[1].display}1: Message ${i}\n`;
+ }
+ diagramCode += ` end\n`;
+ imgSnapshotTest(diagramCode, { look });
+ });
+
+ it('should render boxes around groups', () => {
+ let diagramCode = `sequenceDiagram\n`;
+ diagramCode += ` box Group 1\n`;
+ participantTypes.slice(0, 3).forEach((pt, index) => {
+ diagramCode += ` ${getParticipantLine(`${pt.display}${index}`, pt.type)}`;
+ });
+ diagramCode += ` end\n`;
+ diagramCode += ` box rgb(200,220,255) Group 2\n`;
+ participantTypes.slice(3, 6).forEach((pt, index) => {
+ diagramCode += ` ${getParticipantLine(`${pt.display}${index}`, pt.type)}`;
+ });
+ diagramCode += ` end\n`;
+ diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[3].display}0: Cross-group message\n`;
+ imgSnapshotTest(diagramCode, { look });
+ });
+
+ it('should render with different font settings', () => {
+ let diagramCode = `sequenceDiagram\n`;
+ participantTypes.slice(0, 3).forEach((pt, index) => {
+ diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
+ });
+ diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[1].display}1: Regular message\n`;
+ diagramCode += ` Note right of ${participantTypes[1].display}1: Regular note\n`;
+ imgSnapshotTest(diagramCode, {
+ look,
+ sequence: {
+ actorFontFamily: 'courier',
+ actorFontSize: 14,
+ messageFontFamily: 'Arial',
+ messageFontSize: 12,
+ noteFontFamily: 'times',
+ noteFontSize: 16,
+ noteAlign: 'left',
+ },
+ });
+ });
+ });
+});
+
+// Additional tests for specific combinations
+describe('Sequence Diagram Special Cases', () => {
+ it('should render complex sequence with all features', () => {
+ const diagramCode = `
+ sequenceDiagram
+ box rgb(200,220,255) Authentication
+ actor User
+ participant LoginUI@{ "type": "boundary" }
+ participant AuthService@{ "type": "control" }
+ participant UserDB@{ "type": "database" }
+ end
+
+ box rgb(200,255,220) Order Processing
+ participant Order@{ "type": "entity" }
+ participant OrderQueue@{ "type": "queue" }
+ participant AuditLogs@{ "type": "collections" }
+ end
+
+ User ->> LoginUI: Enter credentials
+ LoginUI ->> AuthService: Validate
+ AuthService ->> UserDB: Query user
+ UserDB -->> AuthService: User data
+ alt Valid credentials
+ AuthService -->> LoginUI: Success
+ LoginUI -->> User: Welcome
+
+ par Place order
+ User ->> Order: New order
+ Order ->> OrderQueue: Process
+ and
+ Order ->> AuditLogs: Record
+ end
+
+ loop Until confirmed
+ OrderQueue ->> Order: Update status
+ Order -->> User: Notification
+ end
+ else Invalid credentials
+ AuthService --x LoginUI: Failure
+ LoginUI --x User: Retry
+ end
+ `;
+ imgSnapshotTest(diagramCode, {});
+ });
+
+ it('should render with wrapped messages and notes', () => {
+ const diagramCode = `
+ sequenceDiagram
+ participant A
+ participant B
+
+ A ->> B: This is a very long message that should wrap properly in the diagram rendering
+ Note over A,B: This is a very long note that should also wrap properly when rendered in the diagram
+
+ par Wrapped parallel
+ A ->> B: Parallel message 1
with explicit line break
+ and
+ B ->> A: Parallel message 2
with explicit line break
+ end
+
+ loop Wrapped loop
+ Note right of B: This is a long note
in a loop
+ A ->> B: Message in loop
+ end
+ `;
+ imgSnapshotTest(diagramCode, { sequence: { wrap: true } });
+ });
+ describe('Sequence Diagram Rendering with Different Participant Types', () => {
+ it('should render a sequence diagram with various participant types', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant User@{ "type": "actor" }
+ participant AuthService@{ "type": "control" }
+ participant UI@{ "type": "boundary" }
+ participant OrderController@{ "type": "control" }
+ participant Product@{ "type": "entity" }
+ participant MongoDB@{ "type": "database" }
+ participant Products@{ "type": "collections" }
+ participant OrderQueue@{ "type": "queue" }
+ User ->> UI: Login request
+ UI ->> AuthService: Validate credentials
+ AuthService -->> UI: Authentication token
+ UI ->> OrderController: Place order
+ OrderController ->> Product: Check availability
+ Product -->> OrderController: Available
+ OrderController ->> MongoDB: Save order
+ MongoDB -->> OrderController: Order saved
+ OrderController ->> OrderQueue: Process payment
+ OrderQueue -->> User: Order confirmation
+ `
+ );
+ });
+
+ it('should render participant creation and destruction with different types', () => {
+ imgSnapshotTest(`
+ sequenceDiagram
+ participant Alice@{ "type" : "boundary" }
+ Alice->>Bob: Hello Bob, how are you ?
+ Bob->>Alice: Fine, thank you. And you?
+ create participant Carl@{ "type" : "control" }
+ Alice->>Carl: Hi Carl!
+ create actor D as Donald
+ Carl->>D: Hi!
+ destroy Carl
+ Alice-xCarl: We are too many
+ destroy Bob
+ Bob->>Alice: I agree
+ `);
+ });
+
+ it('should handle complex interactions between different participant types', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ box rgb(200,220,255) Authentication
+ participant User@{ "type": "actor" }
+ participant LoginUI@{ "type": "boundary" }
+ participant AuthService@{ "type": "control" }
+ participant UserDB@{ "type": "database" }
+ end
+
+ box rgb(200,255,220) Order Processing
+ participant Order@{ "type": "entity" }
+ participant OrderQueue@{ "type": "queue" }
+ participant AuditLogs@{ "type": "collections" }
+ end
+
+ User ->> LoginUI: Enter credentials
+ LoginUI ->> AuthService: Validate
+ AuthService ->> UserDB: Query user
+ UserDB -->> AuthService: User data
+
+ alt Valid credentials
+ AuthService -->> LoginUI: Success
+ LoginUI -->> User: Welcome
+
+ par Place order
+ User ->> Order: New order
+ Order ->> OrderQueue: Process
+ and
+ Order ->> AuditLogs: Record
+ end
+
+ loop Until confirmed
+ OrderQueue ->> Order: Update status
+ Order -->> User: Notification
+ end
+ else Invalid credentials
+ AuthService --x LoginUI: Failure
+ LoginUI --x User: Retry
+ end
+ `,
+ { sequence: { useMaxWidth: false } }
+ );
+ });
+
+ it('should render parallel processes with different participant types', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant Customer@{ "type": "actor" }
+ participant Frontend@{ "type": "participant" }
+ participant PaymentService@{ "type": "boundary" }
+ participant InventoryManager@{ "type": "control" }
+ participant Order@{ "type": "entity" }
+ participant OrdersDB@{ "type": "database" }
+ participant NotificationQueue@{ "type": "queue" }
+
+ Customer ->> Frontend: Place order
+ Frontend ->> Order: Create order
+ par Parallel Processing
+ Order ->> PaymentService: Process payment
+ and
+ Order ->> InventoryManager: Reserve items
+ end
+ PaymentService -->> Order: Payment confirmed
+ InventoryManager -->> Order: Items reserved
+ Order ->> OrdersDB: Save finalized order
+ OrdersDB -->> Order: Order saved
+ Order ->> NotificationQueue: Send confirmation
+ NotificationQueue -->> Customer: Order confirmation
+ `
+ );
+ });
+ });
+ it('should render different participant types with notes and loops', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ actor Admin
+ participant Dashboard
+ participant AuthService@{ "type" : "boundary" }
+ participant UserManager@{ "type" : "control" }
+ participant UserProfile@{ "type" : "entity" }
+ participant UserDB@{ "type" : "database" }
+ participant Logs@{ "type" : "database" }
+
+ Admin ->> Dashboard: Open user management
+ loop Authentication check
+ Dashboard ->> AuthService: Verify admin rights
+ AuthService ->> Dashboard: Access granted
+ end
+ Dashboard ->> UserManager: List users
+ UserManager ->> UserDB: Query users
+ UserDB ->> UserManager: Return user data
+ Note right of UserDB: Encrypted data
requires decryption
+ UserManager ->> UserProfile: Format profiles
+ UserProfile ->> UserManager: Formatted data
+ UserManager ->> Dashboard: Display users
+ Dashboard ->> Logs: Record access
+ Logs ->> Admin: Audit trail
+ `
+ );
+ });
+
+ it('should render different participant types with alternative flows', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ actor Client
+ participant MobileApp
+ participant CloudService@{ "type" : "boundary" }
+ participant DataProcessor@{ "type" : "control" }
+ participant Transaction@{ "type" : "entity" }
+ participant TransactionsDB@{ "type" : "database" }
+ participant EventBus@{ "type" : "queue" }
+
+ Client ->> MobileApp: Initiate transaction
+ MobileApp ->> CloudService: Authenticate
+ alt Authentication successful
+ CloudService -->> MobileApp: Auth token
+ MobileApp ->> DataProcessor: Process data
+ DataProcessor ->> Transaction: Create transaction
+ Transaction ->> TransactionsDB: Save record
+ TransactionsDB -->> Transaction: Confirmation
+ Transaction ->> EventBus: Publish event
+ EventBus -->> Client: Notification
+ else Authentication failed
+ CloudService -->> MobileApp: Error
+ MobileApp -->> Client: Show error
+ end
+ `
+ );
+ });
+
+ it('should render different participant types with wrapping text', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant B@{ "type" : "boundary" }
+ participant C@{ "type" : "control" }
+ participant E@{ "type" : "entity" }
+ participant DB@{ "type" : "database" }
+ participant COL@{ "type" : "collections" }
+ participant Q@{ "type" : "queue" }
+
+ FE ->> B: Another long message
with explicit
line breaks
+ B -->> FE: Response message that is also quite long and needs to wrap
+ FE ->> C: Process data
+ C ->> E: Validate
+ E -->> C: Validation result
+ C ->> DB: Save
+ DB -->> C: Save result
+ C ->> COL: Log
+ COL -->> Q: Forward
+ Q -->> LongNameUser: Final response with confirmation of all actions taken
+ `,
+ { sequence: { wrap: true } }
+ );
+ });
+
+ describe('Sequence Diagram - New Participant Types with Long Notes and Messages', () => {
+ it('should render long notes left of boundary', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant Alice@{ "type" : "boundary" }
+ actor Bob
+ Alice->>Bob: Hola
+ Note left of Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
+ Bob->>Alice: I'm short though
+ `,
+ {}
+ );
+ });
+
+ it('should render wrapped long notes left of control', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant Alice@{ "type" : "control" }
+ actor Bob
+ Alice->>Bob: Hola
+ Note left of Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
+ Bob->>Alice: I'm short though
+ `,
+ {}
+ );
+ });
+
+ it('should render long notes right of entity', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant Alice@{ "type" : "entity" }
+ actor Bob
+ Alice->>Bob: Hola
+ Note right of Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
+ Bob->>Alice: I'm short though
+ `,
+ {}
+ );
+ });
+
+ it('should render wrapped long notes right of database', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant Alice@{ "type" : "database" }
+ actor Bob
+ Alice->>Bob: Hola
+ Note right of Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
+ Bob->>Alice: I'm short though
+ `,
+ {}
+ );
+ });
+
+ it('should render long notes over collections', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant Alice@{ "type" : "collections" }
+ actor Bob
+ Alice->>Bob: Hola
+ Note over Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
+ Bob->>Alice: I'm short though
+ `,
+ {}
+ );
+ });
+
+ it('should render wrapped long notes over queue', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant Alice@{ "type" : "queue" }
+ actor Bob
+ Alice->>Bob: Hola
+ Note over Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
+ Bob->>Alice: I'm short though
+ `,
+ {}
+ );
+ });
+
+ it('should render notes over actor and boundary', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ actor Alice
+ participant Charlie@{ "type" : "boundary" }
+ note over Alice: Some note
+ note over Charlie: Other note
+ `,
+ {}
+ );
+ });
+
+ it('should render long messages from database to collections', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant Alice@{ "type" : "database" }
+ participant Bob@{ "type" : "collections" }
+ Alice->>Bob: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
+ Bob->>Alice: I'm short though
+ `,
+ {}
+ );
+ });
+
+ it('should render wrapped long messages from control to entity', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant Alice@{ "type" : "control" }
+ participant Bob@{ "type" : "entity" }
+ Alice->>Bob:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
+ Bob->>Alice: I'm short though
+ `,
+ {}
+ );
+ });
+
+ it('should render long messages from queue to boundary', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ participant Alice@{ "type" : "queue" }
+ participant Bob@{ "type" : "boundary" }
+ Alice->>Bob: I'm short
+ Bob->>Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
+ `,
+ {}
+ );
+ });
+
+ it('should render wrapped long messages from actor to database', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ actor Alice
+ participant Bob@{ "type" : "database" }
+ Alice->>Bob: I'm short
+ Bob->>Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
+ `,
+ {}
+ );
+ });
+ });
+
+ describe('svg size', () => {
+ it('should render a sequence diagram when useMaxWidth is true (default)', () => {
+ renderGraph(
+ `
+ sequenceDiagram
+ actor Alice
+ participant Bob@{ "type" : "boundary" }
+ participant John@{ "type" : "control" }
+ 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: true } }
+ );
+ cy.get('svg').should((svg) => {
+ expect(svg).to.have.attr('width', '100%');
+ 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(820 * 0.95, 820 * 1.05);
+ });
+ });
+
+ it('should render a sequence diagram when useMaxWidth is false', () => {
+ renderGraph(
+ `
+ sequenceDiagram
+ actor Alice
+ participant Bob@{ "type" : "boundary" }
+ participant John@{ "type" : "control" }
+ 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(820 * 0.95, 820 * 1.05);
+ expect(svg).to.not.have.attr('style');
+ });
+ });
+
+ describe('Central Connection Rendering Tests', () => {
+ it('should render central connection circles on actor vertical lines', () => {
+ imgSnapshotTest(
+ `sequenceDiagram
+ participant Alice
+ participant Bob
+ participant Charlie
+ Alice ()->>() Bob: Central connection
+ Bob ()-->> Charlie: Reverse central connection
+ Charlie ()<<-->>() Alice: Dual central connection`,
+ { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
+ );
+ });
+
+ it('should render central connections with different arrow types', () => {
+ imgSnapshotTest(
+ `sequenceDiagram
+ participant Alice
+ participant Bob
+ Alice ()->>() Bob: Solid open arrow
+ Alice ()-->>() Bob: Dotted open arrow
+ Alice ()-x() Bob: Solid cross
+ Alice ()--x() Bob: Dotted cross
+ Alice ()->() Bob: Solid arrow`,
+ { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
+ );
+ });
+
+ it('should render central connections with bidirectional arrows', () => {
+ imgSnapshotTest(
+ `sequenceDiagram
+ participant Alice
+ participant Bob
+ Alice ()<<->>() Bob: Bidirectional solid
+ Alice ()<<-->>() Bob: Bidirectional dotted`,
+ { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
+ );
+ });
+
+ it('should render central connections with activations', () => {
+ imgSnapshotTest(
+ `sequenceDiagram
+ participant Alice
+ participant Bob
+ participant Charlie
+ Alice ()->>() Bob: Activate Bob
+ activate Bob
+ Bob ()-->> Charlie: Message to Charlie
+ Bob ()->>() Alice: Response to Alice
+ deactivate Bob`,
+ { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
+ );
+ });
+
+ it('should render central connections mixed with normal messages', () => {
+ imgSnapshotTest(
+ `sequenceDiagram
+ participant Alice
+ participant Bob
+ participant Charlie
+ Alice ->> Bob: Normal message
+ Bob ()->>() Charlie: Central connection
+ Charlie -->> Alice: Normal dotted message
+ Alice ()<<-->>() Bob: Dual central connection
+ Bob -x Charlie: Normal cross message`,
+ { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
+ );
+ });
+
+ it('should render central connections with notes', () => {
+ imgSnapshotTest(
+ `sequenceDiagram
+ participant Alice
+ participant Bob
+ participant Charlie
+ Alice ()->>() Bob: Central connection
+ Note over Alice,Bob: Central connection note
+ Bob ()-->> Charlie: Reverse central connection
+ Note right of Charlie: Response note
+ Charlie ()<<-->>() Alice: Dual central connection`,
+ { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
+ );
+ });
+
+ it('should render central connections with loops and alternatives', () => {
+ imgSnapshotTest(
+ `sequenceDiagram
+ participant Alice
+ participant Bob
+ participant Charlie
+ loop Every minute
+ Alice ()->>() Bob: Central heartbeat
+ Bob ()-->> Charlie: Forward heartbeat
+ end
+ alt Success
+ Charlie ()<<-->>() Alice: Success response
+ else Failure
+ Charlie ()-x() Alice: Failure response
+ end`,
+ { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
+ );
+ });
+
+ it('should render central connections with different participant types', () => {
+ imgSnapshotTest(
+ `sequenceDiagram
+ participant Alice
+ actor Bob
+ participant Charlie@{"type":"boundary"}
+ participant David@{"type":"control"}
+ participant Eve@{"type":"entity"}
+ Alice ()->>() Bob: To actor
+ Bob ()-->> Charlie: To boundary
+ Charlie ()->>() David: To control
+ David ()<<-->>() Eve: To entity
+ Eve ()-x() Alice: Back to participant`,
+ { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
+ );
+ });
+ });
+ });
+});
diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js
index f18e99abf..0ec913a8c 100644
--- a/cypress/integration/rendering/sequencediagram.spec.js
+++ b/cypress/integration/rendering/sequencediagram.spec.js
@@ -893,6 +893,17 @@ describe('Sequence diagram', () => {
}
);
});
+
+ it('should handle bidirectional arrows with autonumber', () => {
+ imgSnapshotTest(`
+ sequenceDiagram
+ autonumber
+ participant A
+ participant B
+ A<<->>B: This is a bidirectional message
+ A->B: This is a normal message`);
+ });
+
it('should support actor links and properties when not mirrored EXPERIMENTAL: USE WITH CAUTION', () => {
//Be aware that the syntax for "properties" is likely to be changed.
imgSnapshotTest(
@@ -1042,4 +1053,167 @@ describe('Sequence diagram', () => {
]);
});
});
+ describe('render new arrow type', () => {
+ it('should render Solid half arrow top', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice -|\\ John: Hello John, how are you?
+ Alice-|\\ John: Hi Alice, I can hear you!
+ Alice -|\\ John: Test
+ `
+ );
+ });
+ it('should render Solid half arrow bottom', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice-|/John: Hello John, how are you?
+ Alice-|/John: Hi Alice, I can hear you!
+ Alice-|/John: Test
+ `
+ );
+ });
+
+ it('should render Stick half arrow top ', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice-\\\\John: Hello John, how are you?
+ Alice-\\\\John: Hi Alice, I can hear you!
+ Alice-\\\\John: Test
+ `
+ );
+ });
+ it('should render Stick half arrow bottom ', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice-//John: Hello John, how are you?
+ Alice-//John: Hi Alice, I can hear you!
+ Alice-//John: Test
+ `
+ );
+ });
+ it('should render Solid half arrow top reverse ', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice/|-John: Hello Alice, how are you?
+ Alice/|-John: Hi Alice, I can hear you!
+ Alice/|-John: Test
+
+ `
+ );
+ });
+
+ it('should render Solid half arrow bottom reverse ', () => {
+ imgSnapshotTest(
+ `sequenceDiagram
+ Alice \\|- John: Hello Alice, how are you?
+ Alice \\|- John: Hi Alice, I can hear you!
+ Alice \\|- John: Test`
+ );
+ });
+
+ it('should render Stick half arrow top reverse ', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice //-John: Hello Alice, how are you?
+ Alice //-John: Hi Alice, I can hear you!
+ Alice //-John: Test`
+ );
+ });
+
+ it('should render Stick half arrow bottom reverse ', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice \\\\-John: Hello Alice, how are you?
+ Alice \\\\-John: Hi Alice, I can hear you!
+ Alice \\\\-John: Test`
+ );
+ });
+
+ it('should render Solid half arrow top dotted', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice --|\\John: Hello John, how are you?
+ Alice --|\\John: Hi Alice, I can hear you!
+ Alice --|\\John: Test`
+ );
+ });
+
+ it('should render Solid half arrow bottom dotted', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice --|/John: Hello John, how are you?
+ Alice --|/John: Hi Alice, I can hear you!
+ Alice --|/John: Test`
+ );
+ });
+
+ it('should render Stick half arrow top dotted', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice--\\\\John: Hello John, how are you?
+ Alice--\\\\John: Hi Alice, I can hear you!
+ Alice--\\\\John: Test`
+ );
+ });
+
+ it('should render Stick half arrow bottom dotted', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice--//John: Hello John, how are you?
+ Alice--//John: Hi Alice, I can hear you!
+ Alice--//John: Test`
+ );
+ });
+
+ it('should render Solid half arrow top reverse dotted', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice/|--John: Hello Alice, how are you?
+ Alice/|--John: Hi Alice, I can hear you!
+ Alice/|--John: Test`
+ );
+ });
+
+ it('should render Solid half arrow bottom reverse dotted', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice\\|--John: Hello Alice, how are you?
+ Alice\\|--John: Hi Alice, I can hear you!
+ Alice\\|--John: Test`
+ );
+ });
+
+ it('should render Stick half arrow top reverse dotted ', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice//--John: Hello Alice, how are you?
+ Alice//--John: Hi Alice, I can hear you!
+ Alice//--John: Test`
+ );
+ });
+
+ it('should render Stick half arrow bottom reverse dotted ', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ Alice\\\\--John: Hello Alice, how are you?
+ Alice\\\\--John: Hi Alice, I can hear you!
+ Alice\\\\--John: Test`
+ );
+ });
+ });
});
diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html
index eb5528844..90f40003a 100644
--- a/cypress/platform/knsv2.html
+++ b/cypress/platform/knsv2.html
@@ -32,26 +32,8 @@
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
rel="stylesheet"
/>
-
-
-
+
+
+
+ --- + config: + layout: tidy-tree + --- + mindmap + root((mindmap)) + A + B ++
+ --- + config: + layout: dagre + --- + mindmap + root((mindmap)) + A + B ++
+ --- + config: + layout: elk + --- + mindmap + root((mindmap)) + A + B ++
+ --- + config: + layout: cose-bilkent + --- + mindmap + root((mindmap)) + A + B ++
+ --- + config: + layout: tidy-tree + --- + mindmap + root((mindmap is a long thing)) + A + B + C + D ++
+ --- + config: + layout: dagre + --- + mindmap + root((mindmap is a long thing)) + A + B + C + D ++
+ --- + config: + layout: elk + --- + mindmap + root((mindmap is a long thing)) + A + B + C + D ++
+ --- + config: + layout: cose-bilkent + --- + mindmap + root((mindmap is a long thing)) + A + B + C + D ++ +
+ --- + config: + layout: tidy-tree + --- + mindmap + root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectiveness<br/>and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping + Tools + id)I am a cloud( + id))I am a bang(( + Tools ++
+ --- + config: + layout: dagre + --- + mindmap + root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectiveness<br/>and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping + Tools + id)I am a cloud( + id))I am a bang(( + Tools ++
+ --- + config: + layout: elk + --- + mindmap + root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectiveness<br/>and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping + Tools + id)I am a cloud( + id))I am a bang(( + Tools ++
+ --- + config: + layout: cose-bilkent + --- + mindmap + root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectiveness<br/>and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping + Tools + id)I am a cloud( + id))I am a bang(( + Tools ++
+ --- + config: + layout: tidy-tree + --- + mindmap + root((mindmap)) + A + a + apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + b + c + d + B + apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + D + apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + ++
+ --- + config: + layout: dagre + --- + mindmap + root((mindmap)) + A + a + apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + b + c + d + B + apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + D + apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + ++
+ --- + config: + layout: elk + --- + mindmap + root((mindmap)) + A + a + apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + b + c + d + B + apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + D + apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + ++
+ --- + config: + layout: cose-bilkent + --- + mindmap + root((mindmap)) + A + a + apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + b + c + d + B + apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + D + apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on] + ++ +
+ --- + config: + layout: tidy-tree + --- + mindmap + ((This is a mindmap)) + child1 + grandchild 1 + grandchild 2 + child2 + grandchild 3 + grandchild 4 + child3 + grandchild 5 + grandchild 6 + ++ +
+ --- + config: + layout: dagre + --- + mindmap + ((This is a mindmap)) + child1 + grandchild 1 + grandchild 2 + child2 + grandchild 3 + grandchild 4 + child3 + grandchild 5 + grandchild 6 + ++ +
+ --- + config: + layout: elk + --- + mindmap + ((This is a mindmap)) + child1 + grandchild 1 + grandchild 2 + child2 + grandchild 3 + grandchild 4 + child3 + grandchild 5 + grandchild 6 + ++ +
+ --- + config: + layout: cose-bilkent + --- + mindmap + ((This is a mindmap)) + child1 + grandchild 1 + grandchild 2 + child2 + grandchild 3 + grandchild 4 + child3 + grandchild 5 + grandchild 6 + ++ +