+ flowchart LR + id1([This is the text in the box]) ++
diff --git a/.build/common.ts b/.build/common.ts
index 274977fa26..e2190974f9 100644
--- a/.build/common.ts
+++ b/.build/common.ts
@@ -22,9 +22,9 @@ export const packageOptions = {
packageName: 'mermaid-zenuml',
file: 'detector.ts',
},
- 'mermaid-flowchart-elk': {
- name: 'mermaid-flowchart-elk',
- packageName: 'mermaid-flowchart-elk',
- file: 'detector.ts',
+ 'mermaid-layout-elk': {
+ name: 'mermaid-layout-elk',
+ packageName: 'mermaid-layout-elk',
+ file: 'layouts.ts',
},
} as const;
diff --git a/.build/jisonTransformer.ts b/.build/jisonTransformer.ts
index 314df8a33f..b603502306 100644
--- a/.build/jisonTransformer.ts
+++ b/.build/jisonTransformer.ts
@@ -1,6 +1,7 @@
import jison from 'jison';
export const transformJison = (src: string): string => {
+ // @ts-ignore - Jison is not typed properly
const parser = new jison.Generator(src, {
moduleType: 'js',
'token-stack': true,
diff --git a/.build/types.ts b/.build/types.ts
index 4192407824..9dec05a68b 100644
--- a/.build/types.ts
+++ b/.build/types.ts
@@ -1,3 +1,4 @@
+/* eslint-disable no-console */
import { packageOptions } from './common.js';
import { execSync } from 'child_process';
@@ -5,11 +6,17 @@ const buildType = (packageName: string) => {
console.log(`Building types for ${packageName}`);
try {
const out = execSync(`tsc -p ./packages/${packageName}/tsconfig.json --emitDeclarationOnly`);
- out.length > 0 && console.log(out.toString());
+ if (out.length > 0) {
+ console.log(out.toString());
+ }
} catch (e) {
console.error(e);
- e.stdout.length > 0 && console.error(e.stdout.toString());
- e.stderr.length > 0 && console.error(e.stderr.toString());
+ if (e.stdout.length > 0) {
+ console.error(e.stdout.toString());
+ }
+ if (e.stderr.length > 0) {
+ console.error(e.stderr.toString());
+ }
}
};
diff --git a/.cspell/code-terms.txt b/.cspell/code-terms.txt
index fa063616a7..9d2f700fcb 100644
--- a/.cspell/code-terms.txt
+++ b/.cspell/code-terms.txt
@@ -13,6 +13,7 @@ bqstring
BQUOTE
bramp
BRKT
+brotli
callbackargs
callbackname
classdef
@@ -27,6 +28,7 @@ controly
CSSCLASS
CYLINDEREND
CYLINDERSTART
+DAGA
datakey
DEND
descr
@@ -89,6 +91,7 @@ reqs
rewritelinks
rgba
RIGHTOF
+roughjs
sankey
sequencenumber
shrc
@@ -108,9 +111,11 @@ strikethrough
stringifying
struct
STYLECLASS
+STYLEDEF
STYLEOPTS
subcomponent
subcomponents
+subconfig
SUBROUTINEEND
SUBROUTINESTART
Subschemas
@@ -125,6 +130,7 @@ titlevalue
topbar
TRAPEND
TRAPSTART
+treemap
ts-nocheck
tsdoc
typeof
diff --git a/.cspell/contributors.txt b/.cspell/contributors.txt
index bd3ad9da24..b7f52f8d0c 100644
--- a/.cspell/contributors.txt
+++ b/.cspell/contributors.txt
@@ -4,5 +4,6 @@ cpettitt
Dong Cai
Nikolay Rozhkov
Peng Xiao
+Per Brolin
subhash-halder
Vinod Sidharth
diff --git a/.cspell/libraries.txt b/.cspell/libraries.txt
index 9d29261868..c185429b09 100644
--- a/.cspell/libraries.txt
+++ b/.cspell/libraries.txt
@@ -20,6 +20,7 @@ dagre-d3
Deepdwn
Docsify
Docsy
+Doctave
DokuWiki
dompurify
elkjs
@@ -54,13 +55,16 @@ presetAttributify
pyplot
redmine
rehype
+roughjs
rscratch
+shiki
sparkline
sphinxcontrib
ssim
stylis
Swimm
tsbuildinfo
+tseslint
Tuleap
Typora
unocss
diff --git a/.cspell/mermaid-terms.txt b/.cspell/mermaid-terms.txt
index 3fa5eff269..46ad6dddb1 100644
--- a/.cspell/mermaid-terms.txt
+++ b/.cspell/mermaid-terms.txt
@@ -9,6 +9,7 @@ elems
gantt
gitgraph
gzipped
+handDrawn
knsv
Knut
marginx
@@ -17,6 +18,7 @@ Markdownish
mermaidjs
mindmap
mindmaps
+mrtree
multigraph
nodesep
NOTEGROUP
diff --git a/.cspell/misc-terms.txt b/.cspell/misc-terms.txt
index 467e48891e..3fc0943094 100644
--- a/.cspell/misc-terms.txt
+++ b/.cspell/misc-terms.txt
@@ -1 +1,6 @@
+BRANDES
+circo
+handDrawn
+KOEPF
+neato
newbranch
diff --git a/.esbuild/build.ts b/.esbuild/build.ts
index 3c87f9d621..2bb42a557f 100644
--- a/.esbuild/build.ts
+++ b/.esbuild/build.ts
@@ -2,13 +2,14 @@ import { build } from 'esbuild';
import { mkdir, writeFile } from 'node:fs/promises';
import { packageOptions } from '../.build/common.js';
import { generateLangium } from '../.build/generateLangium.js';
-import { MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js';
+import type { MermaidBuildOptions } from './util.js';
+import { defaultOptions, getBuildConfig } from './util.js';
const shouldVisualize = process.argv.includes('--visualize');
const buildPackage = async (entryName: keyof typeof packageOptions) => {
- const commonOptions = { ...defaultOptions, entryName } as const;
- const buildConfigs = [
+ const commonOptions: MermaidBuildOptions = { ...defaultOptions, entryName } as const;
+ const buildConfigs: MermaidBuildOptions[] = [
// package.mjs
{ ...commonOptions },
// package.min.mjs
@@ -35,11 +36,11 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
if (shouldVisualize) {
for (const { metafile } of results) {
- if (!metafile) {
+ if (!metafile?.outputs) {
continue;
}
const fileName = Object.keys(metafile.outputs)
- .filter((file) => !file.includes('chunks') && file.endsWith('js'))[0]
+ .find((file) => !file.includes('chunks') && file.endsWith('js'))
.replace('dist/', '');
// Upload metafile into https://esbuild.github.io/analyze/
await writeFile(`stats/${fileName}.meta.json`, JSON.stringify(metafile));
@@ -48,13 +49,14 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
};
const handler = (e) => {
+ // eslint-disable-next-line no-console
console.error(e);
process.exit(1);
};
const main = async () => {
await generateLangium();
- await mkdir('stats').catch(() => {});
+ await mkdir('stats', { recursive: true });
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
// it should build `parser` before `mermaid` because it's a dependency
for (const pkg of packageNames) {
diff --git a/.esbuild/jisonPlugin.ts b/.esbuild/jisonPlugin.ts
index de801ea9f3..007516f089 100644
--- a/.esbuild/jisonPlugin.ts
+++ b/.esbuild/jisonPlugin.ts
@@ -1,6 +1,6 @@
import { readFile } from 'node:fs/promises';
import { transformJison } from '../.build/jisonTransformer.js';
-import { Plugin } from 'esbuild';
+import type { Plugin } from 'esbuild';
export const jisonPlugin: Plugin = {
name: 'jison',
diff --git a/.esbuild/server.ts b/.esbuild/server.ts
index 9102c7de83..ef61ebec22 100644
--- a/.esbuild/server.ts
+++ b/.esbuild/server.ts
@@ -1,11 +1,12 @@
-import express from 'express';
-import type { NextFunction, Request, Response } from 'express';
+/* eslint-disable no-console */
+import chokidar from 'chokidar';
import cors from 'cors';
-import { getBuildConfig, defaultOptions } from './util.js';
import { context } from 'esbuild';
-import chokidar from 'chokidar';
-import { generateLangium } from '../.build/generateLangium.js';
+import type { Request, Response } from 'express';
+import express from 'express';
import { packageOptions } from '../.build/common.js';
+import { generateLangium } from '../.build/generateLangium.js';
+import { defaultOptions, getBuildConfig } from './util.js';
const configs = Object.values(packageOptions).map(({ packageName }) =>
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: packageName })
@@ -19,16 +20,28 @@ const mermaidIIFEConfig = getBuildConfig({
});
configs.push(mermaidIIFEConfig);
-const contexts = await Promise.all(configs.map((config) => context(config)));
+const contexts = await Promise.all(
+ configs.map(async (config) => ({ config, context: await context(config) }))
+);
+let rebuildCounter = 1;
const rebuildAll = async () => {
- console.time('Rebuild time');
- await Promise.all(contexts.map((ctx) => ctx.rebuild())).catch((e) => console.error(e));
- console.timeEnd('Rebuild time');
+ const buildNumber = rebuildCounter++;
+ const timeLabel = `Rebuild ${buildNumber} Time (total)`;
+ console.time(timeLabel);
+ await Promise.all(
+ contexts.map(async ({ config, context }) => {
+ const buildVariant = `Rebuild ${buildNumber} Time (${Object.keys(config.entryPoints!)[0]} ${config.format})`;
+ console.time(buildVariant);
+ await context.rebuild();
+ console.timeEnd(buildVariant);
+ })
+ ).catch((e) => console.error(e));
+ console.timeEnd(timeLabel);
};
let clients: { id: number; response: Response }[] = [];
-function eventsHandler(request: Request, response: Response, next: NextFunction) {
+function eventsHandler(request: Request, response: Response) {
const headers = {
'Content-Type': 'text/event-stream',
Connection: 'keep-alive',
@@ -45,19 +58,20 @@ function eventsHandler(request: Request, response: Response, next: NextFunction)
});
}
-let timeoutId: NodeJS.Timeout | undefined = undefined;
+let timeoutID: NodeJS.Timeout | undefined = undefined;
/**
* Debounce file change events to avoid rebuilding multiple times.
*/
function handleFileChange() {
- if (timeoutId !== undefined) {
- clearTimeout(timeoutId);
+ if (timeoutID !== undefined) {
+ clearTimeout(timeoutID);
}
- timeoutId = setTimeout(async () => {
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
+ timeoutID = setTimeout(async () => {
await rebuildAll();
sendEventsToAll();
- timeoutId = undefined;
+ timeoutID = undefined;
}, 100);
}
@@ -74,15 +88,16 @@ async function createServer() {
ignoreInitial: true,
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
})
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
.on('all', async (event, path) => {
// Ignore other events.
if (!['add', 'change'].includes(event)) {
return;
}
- if (/\.langium$/.test(path)) {
+ console.log(`${path} changed. Rebuilding...`);
+ if (path.endsWith('.langium')) {
await generateLangium();
}
- console.log(`${path} changed. Rebuilding...`);
handleFileChange();
});
@@ -99,4 +114,4 @@ async function createServer() {
});
}
-createServer();
+void createServer();
diff --git a/.esbuild/util.ts b/.esbuild/util.ts
index 5c21cbf452..5221761138 100644
--- a/.esbuild/util.ts
+++ b/.esbuild/util.ts
@@ -8,7 +8,7 @@ import { jisonPlugin } from './jisonPlugin.js';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
-export interface MermaidBuildOptions {
+export interface MermaidBuildOptions extends BuildOptions {
minify: boolean;
core: boolean;
metafile: boolean;
@@ -56,7 +56,7 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const external: string[] = ['require', 'fs', 'path'];
const { name, file, packageName } = packageOptions[entryName];
const outFileName = getFileName(name, options);
- let output: BuildOptions = buildOptions({
+ const output: BuildOptions = buildOptions({
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
entryPoints: {
[outFileName]: `src/${file}`,
diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index 08b265ba06..0000000000
--- a/.eslintignore
+++ /dev/null
@@ -1,11 +0,0 @@
-dist/**
-.github/**
-docs/Setup.md
-cypress.config.js
-cypress/plugins/index.js
-coverage
-*.json
-node_modules
-
-# autogenereated by langium-cli
-generated/
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
deleted file mode 100644
index dceb314c8e..0000000000
--- a/.eslintrc.cjs
+++ /dev/null
@@ -1,189 +0,0 @@
-module.exports = {
- env: {
- browser: true,
- es6: true,
- 'jest/globals': true,
- node: true,
- },
- root: true,
- parser: '@typescript-eslint/parser',
- parserOptions: {
- ecmaFeatures: {
- experimentalObjectRestSpread: true,
- jsx: true,
- },
- tsconfigRootDir: __dirname,
- sourceType: 'module',
- ecmaVersion: 2022,
- allowAutomaticSingleRunInference: true,
- project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'],
- parser: '@typescript-eslint/parser',
- },
- extends: [
- 'eslint:recommended',
- 'plugin:@typescript-eslint/recommended',
- 'plugin:json/recommended',
- 'plugin:markdown/recommended-legacy',
- 'plugin:@cspell/recommended',
- 'prettier',
- ],
- plugins: [
- '@typescript-eslint',
- 'no-only-tests',
- 'html',
- 'jest',
- 'jsdoc',
- 'json',
- '@cspell',
- 'lodash',
- 'unicorn',
- ],
- ignorePatterns: [
- // this file is automatically generated by `pnpm run --filter mermaid types:build-config`
- 'packages/mermaid/src/config.type.ts',
- ],
- rules: {
- curly: 'error',
- 'no-console': 'error',
- 'no-prototype-builtins': 'off',
- 'no-unused-vars': 'off',
- 'cypress/no-async-tests': 'off',
- '@typescript-eslint/consistent-type-imports': 'error',
- '@typescript-eslint/no-explicit-any': 'warn',
- '@typescript-eslint/no-floating-promises': 'error',
- '@typescript-eslint/no-misused-promises': 'error',
- '@typescript-eslint/no-unused-vars': 'warn',
- '@typescript-eslint/ban-ts-comment': [
- 'error',
- {
- 'ts-expect-error': 'allow-with-description',
- 'ts-ignore': 'allow-with-description',
- 'ts-nocheck': 'allow-with-description',
- 'ts-check': 'allow-with-description',
- minimumDescriptionLength: 10,
- },
- ],
- '@typescript-eslint/naming-convention': [
- 'error',
- {
- selector: 'typeLike',
- format: ['PascalCase'],
- custom: {
- regex: '^I[A-Z]',
- match: false,
- },
- },
- ],
- 'json/*': ['error', 'allowComments'],
- '@cspell/spellchecker': [
- 'error',
- {
- checkIdentifiers: true,
- checkStrings: true,
- checkStringTemplates: true,
- },
- ],
- 'no-empty': [
- 'error',
- {
- allowEmptyCatch: true,
- },
- ],
- 'no-only-tests/no-only-tests': 'error',
- 'lodash/import-scope': ['error', 'method'],
- 'unicorn/better-regex': 'error',
- 'unicorn/no-abusive-eslint-disable': 'error',
- 'unicorn/no-array-push-push': 'error',
- 'unicorn/no-for-loop': 'error',
- 'unicorn/no-instanceof-array': 'error',
- 'unicorn/no-typeof-undefined': 'error',
- 'unicorn/no-unnecessary-await': 'error',
- 'unicorn/no-unsafe-regex': 'warn',
- 'unicorn/no-useless-promise-resolve-reject': 'error',
- 'unicorn/prefer-array-find': 'error',
- 'unicorn/prefer-array-flat-map': 'error',
- 'unicorn/prefer-array-index-of': 'error',
- 'unicorn/prefer-array-some': 'error',
- 'unicorn/prefer-default-parameters': 'error',
- 'unicorn/prefer-includes': 'error',
- 'unicorn/prefer-negative-index': 'error',
- 'unicorn/prefer-object-from-entries': 'error',
- 'unicorn/prefer-string-starts-ends-with': 'error',
- 'unicorn/prefer-string-trim-start-end': 'error',
- 'unicorn/string-content': 'error',
- 'unicorn/prefer-spread': 'error',
- 'unicorn/no-lonely-if': 'error',
- },
- overrides: [
- {
- files: ['cypress/**', 'demos/**'],
- rules: {
- 'no-console': 'off',
- },
- },
- {
- files: ['*.{js,jsx,mjs,cjs}'],
- extends: ['plugin:jsdoc/recommended'],
- rules: {
- 'jsdoc/check-indentation': 'off',
- 'jsdoc/check-alignment': 'off',
- 'jsdoc/check-line-alignment': 'off',
- 'jsdoc/multiline-blocks': 'off',
- 'jsdoc/newline-after-description': 'off',
- 'jsdoc/tag-lines': 'off',
- 'jsdoc/require-param-description': 'off',
- 'jsdoc/require-param-type': 'off',
- 'jsdoc/require-returns': 'off',
- 'jsdoc/require-returns-description': 'off',
- },
- },
- {
- files: ['*.{ts,tsx}'],
- plugins: ['tsdoc'],
- rules: {
- 'no-restricted-syntax': [
- 'error',
- {
- selector: 'TSEnumDeclaration',
- message:
- 'Prefer using TypeScript union types over TypeScript enum, since TypeScript enums have a bunch of issues, see https://dev.to/dvddpl/whats-the-problem-with-typescript-enums-2okj',
- },
- ],
- 'tsdoc/syntax': 'error',
- },
- },
- {
- files: ['*.spec.{ts,js}', 'cypress/**', 'demos/**', '**/docs/**'],
- rules: {
- 'jsdoc/require-jsdoc': 'off',
- '@typescript-eslint/no-unused-vars': 'off',
- },
- },
- {
- files: ['*.spec.{ts,js}', 'tests/**', 'cypress/**/*.js'],
- rules: {
- '@cspell/spellchecker': [
- 'error',
- {
- checkIdentifiers: false,
- checkStrings: false,
- checkStringTemplates: false,
- },
- ],
- },
- },
- {
- files: ['*.html', '*.md', '**/*.md/*'],
- rules: {
- 'no-var': 'error',
- 'no-undef': 'off',
- '@typescript-eslint/no-unused-vars': 'off',
- '@typescript-eslint/no-floating-promises': 'off',
- '@typescript-eslint/no-misused-promises': 'off',
- },
- parserOptions: {
- project: null,
- },
- },
- ],
-};
diff --git a/.github/lychee.toml b/.github/lychee.toml
index c5a2f0e452..2e3b08c413 100644
--- a/.github/lychee.toml
+++ b/.github/lychee.toml
@@ -41,7 +41,10 @@ exclude = [
"https://bundlephobia.com",
# Chrome webstore migration issue. Temporary
-"https://chromewebstore.google.com"
+"https://chromewebstore.google.com",
+
+# Drupal 403
+"https://(www.)?drupal.org"
]
# Exclude all private IPs from checking.
diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml
new file mode 100644
index 0000000000..ef170fb710
--- /dev/null
+++ b/.github/workflows/autofix.yml
@@ -0,0 +1,41 @@
+name: autofix.ci # needed to securely identify the workflow
+
+on:
+ pull_request:
+permissions:
+ contents: read
+
+jobs:
+ autofix:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: pnpm/action-setup@v4
+ # uses version from "packageManager" field in package.json
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ cache: pnpm
+ node-version-file: '.node-version'
+
+ - name: Install Packages
+ run: |
+ pnpm install --frozen-lockfile
+ env:
+ CYPRESS_CACHE_FOLDER: .cache/Cypress
+
+ - name: Fix Linting
+ shell: bash
+ run: pnpm -w run lint:fix
+
+ - name: Sync `./src/config.type.ts` with `./src/schemas/config.schema.yaml`
+ shell: bash
+ run: pnpm run --filter mermaid types:build-config
+
+ - name: Build Docs
+ working-directory: ./packages/mermaid
+ run: pnpm run docs:build
+
+ - uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c
diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index 87607bc2ff..0ce7789573 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -18,7 +18,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bf54772bce..c6e96912ef 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -18,7 +18,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml
index 1238fe3713..5e5407a23b 100644
--- a/.github/workflows/e2e-applitools.yml
+++ b/.github/workflows/e2e-applitools.yml
@@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 6477c9eb5c..2600b3fb84 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -1,9 +1,3 @@
-# We use github cache to save snapshots between runs.
-# For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
-# If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
-# These are then downloaded before running the E2E, providing the reference snapshots.
-# If there are any errors, the diff image is uploaded to artifacts, and the user is notified.
-
name: E2E
on:
@@ -30,6 +24,7 @@ env:
) ||
github.event.before
}}
+ shouldRunParallel: ${{ secrets.CYPRESS_RECORD_KEY != '' && !(github.event_name == 'push' && github.ref == 'refs/heads/develop') }}
jobs:
cache:
runs-on: ubuntu-latest
@@ -38,7 +33,7 @@ jobs:
options: --user 1001
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
@@ -72,16 +67,6 @@ jobs:
mkdir -p cypress/snapshots/stats/base
mv stats cypress/snapshots/stats/base
- - name: Cypress run
- uses: cypress-io/github-action@v6
- id: cypress-snapshot-gen
- if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
- with:
- install: false
- start: pnpm run dev
- wait-on: 'http://localhost:9000'
- browser: chrome
-
e2e:
runs-on: ubuntu-latest
container:
@@ -95,7 +80,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
@@ -132,7 +117,7 @@ jobs:
id: cypress
# If CYPRESS_RECORD_KEY is set, run in parallel on all containers
# Otherwise (e.g. if running from fork), we run on a single container only
- if: ${{ ( env.CYPRESS_RECORD_KEY != '' ) || ( matrix.containers == 1 ) }}
+ if: ${{ env.shouldRunParallel == 'true' || ( matrix.containers == 1 ) }}
with:
install: false
start: pnpm run dev:coverage
@@ -140,12 +125,16 @@ jobs:
browser: chrome
# Disable recording if we don't have an API key
# e.g. if this action was run from a fork
- record: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
- parallel: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
+ record: ${{ env.shouldRunParallel == 'true' }}
+ parallel: ${{ env.shouldRunParallel == 'true' }}
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
VITEST_COVERAGE: true
CYPRESS_COMMIT: ${{ github.sha }}
+ ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
+ ARGOS_PARALLEL: ${{ env.shouldRunParallel == 'true' }}
+ ARGOS_PARALLEL_TOTAL: 4
+ ARGOS_PARALLEL_INDEX: ${{ matrix.containers }}
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v4
@@ -158,55 +147,3 @@ jobs:
fail_ci_if_error: false
verbose: true
token: 6845cc80-77ee-4e17-85a1-026cd95e0766
-
- # We upload the artifacts into numbered archives to prevent overwriting
- - name: Upload Artifacts
- uses: actions/upload-artifact@v4
- if: ${{ always() }}
- with:
- name: snapshots-${{ matrix.containers }}
- retention-days: 1
- path: ./cypress/snapshots
-
- combineArtifacts:
- needs: e2e
- runs-on: ubuntu-latest
- if: ${{ always() }}
- steps:
- # Download all snapshot artifacts and merge them into a single folder
- - name: Download All Artifacts
- uses: actions/download-artifact@v4
- with:
- path: snapshots
- pattern: snapshots-*
- merge-multiple: true
-
- # For successful push events, we save the snapshots cache
- - name: Save snapshots cache
- id: cache-upload
- if: ${{ github.event_name == 'push' && needs.e2e.result != 'failure' }}
- uses: actions/cache/save@v4
- with:
- path: ./snapshots
- key: ${{ runner.os }}-snapshots-${{ github.event.after }}
-
- - name: Flatten images to a folder
- if: ${{ needs.e2e.result == 'failure' }}
- run: |
- mkdir errors
- cd snapshots
- find . -mindepth 2 -type d -name "*__diff_output__*" -exec sh -c 'mv "$0"/*.png ../errors/' {} \;
-
- - name: Upload Error snapshots
- if: ${{ needs.e2e.result == 'failure' }}
- uses: actions/upload-artifact@v4
- id: upload-artifacts
- with:
- name: error-snapshots
- retention-days: 10
- path: errors/
-
- - name: Notify Users
- if: ${{ needs.e2e.result == 'failure' }}
- run: |
- echo "::error title=Visual tests failed::You can view images that failed by downloading the error-snapshots artifact: ${{ steps.upload-artifacts.outputs.artifact-url }}"
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 8f5995d717..632cd6ddc4 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -19,7 +19,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
@@ -82,15 +82,3 @@ jobs:
working-directory: ./packages/mermaid
continue-on-error: ${{ github.event_name == 'push' }}
run: pnpm run docs:verify
-
- - name: Rebuild Docs
- if: ${{ steps.verifyDocs.outcome == 'failure' && github.event_name == 'push' }}
- working-directory: ./packages/mermaid
- run: pnpm run docs:build
-
- - name: Commit changes
- uses: EndBug/add-and-commit@v9
- if: ${{ steps.verifyDocs.outcome == 'failure' && github.event_name == 'push' }}
- with:
- message: 'Update docs'
- add: 'docs/*'
diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml
index fb70a90ecd..4ff5f41170 100644
--- a/.github/workflows/publish-docs.yml
+++ b/.github/workflows/publish-docs.yml
@@ -25,7 +25,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
diff --git a/.github/workflows/release-preview-publish.yml b/.github/workflows/release-preview-publish.yml
index c763430b04..91e3ac9813 100644
--- a/.github/workflows/release-preview-publish.yml
+++ b/.github/workflows/release-preview-publish.yml
@@ -13,7 +13,7 @@ jobs:
with:
fetch-depth: 0
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml
index dce461cf57..4dcf709c01 100644
--- a/.github/workflows/release-publish.yml
+++ b/.github/workflows/release-publish.yml
@@ -11,7 +11,7 @@ jobs:
- uses: actions/checkout@v4
- uses: fregante/setup-git-user@v2
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index a4bd264e05..a0b284a68f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -11,7 +11,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
diff --git a/.github/workflows/update-browserlist.yml b/.github/workflows/update-browserlist.yml
index 9aac3d7b3b..f8f7696cde 100644
--- a/.github/workflows/update-browserlist.yml
+++ b/.github/workflows/update-browserlist.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
- run: npx update-browserslist-db@latest
- name: Commit changes
uses: EndBug/add-and-commit@v9
diff --git a/.gitignore b/.gitignore
index a0fd1c50b8..7448f2a810 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,7 +35,7 @@ cypress/snapshots/
.tsbuildinfo
tsconfig.tsbuildinfo
-knsv*.html
+#knsv*.html
local*.html
stats/
@@ -48,6 +48,7 @@ demos/dev/**
!/demos/dev/example.html
!/demos/dev/reload.js
tsx-0/**
+vite.config.ts.timestamp-*
# autogenereated by langium-cli
-generated/
\ No newline at end of file
+generated/
diff --git a/.node-version b/.node-version
index ee09fac75c..87834047a6 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1 @@
-v20.11.1
+20.12.2
diff --git a/.prettierignore b/.prettierignore
index 7da0646e32..c700804260 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -16,3 +16,5 @@ generated/
# Ignore the files creates in /demos/dev except for example.html
demos/dev/**
!/demos/dev/example.html
+# TODO: Lots of errors to fix
+cypress/platform/state-refactor.html
diff --git a/.vite/build.ts b/.vite/build.ts
index 7ce93a497f..486d594525 100644
--- a/.vite/build.ts
+++ b/.vite/build.ts
@@ -1,4 +1,5 @@
-import { build, InlineConfig, type PluginOption } from 'vite';
+import type { InlineConfig } from 'vite';
+import { build, type PluginOption } from 'vite';
import { resolve } from 'path';
import { fileURLToPath } from 'url';
import jisonPlugin from './jisonPlugin.js';
@@ -46,9 +47,10 @@ interface BuildOptions {
export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions): InlineConfig => {
const external: (string | RegExp)[] = ['require', 'fs', 'path'];
+ // eslint-disable-next-line no-console
console.log(entryName, packageOptions[entryName]);
const { name, file, packageName } = packageOptions[entryName];
- let output: OutputOptions = [
+ const output: OutputOptions = [
{
name,
format: 'esm',
@@ -83,7 +85,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
plugins: [
jisonPlugin(),
jsonSchemaPlugin(), // handles `.schema.yaml` files
- // @ts-expect-error According to the type definitions, rollup plugins are incompatible with vite
typescript({ compilerOptions: { declaration: false } }),
istanbul({
exclude: ['node_modules', 'test/', '__mocks__', 'generated'],
@@ -121,10 +122,10 @@ await generateLangium();
if (watch) {
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
- build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
+ void build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
if (!mermaidOnly) {
- build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
- build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
+ void build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
+ void build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
}
} else if (visualize) {
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
diff --git a/.vite/jsonSchemaPlugin.ts b/.vite/jsonSchemaPlugin.ts
index 2e5b5cc0b5..3614d5b457 100644
--- a/.vite/jsonSchemaPlugin.ts
+++ b/.vite/jsonSchemaPlugin.ts
@@ -1,4 +1,4 @@
-import { PluginOption } from 'vite';
+import type { PluginOption } from 'vite';
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
/**
diff --git a/.vite/server.ts b/.vite/server.ts
index 99d16f6f24..078599dc1c 100644
--- a/.vite/server.ts
+++ b/.vite/server.ts
@@ -23,8 +23,9 @@ async function createServer() {
app.use(express.static('cypress/platform'));
app.listen(9000, () => {
+ // eslint-disable-next-line no-console
console.log(`Listening on http://localhost:9000`);
});
}
-createServer();
+void createServer();
diff --git a/Dockerfile b/Dockerfile
index 1cc9ef0306..7bec3bd4b7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,2 +1,2 @@
-FROM node:20.11.1-alpine3.19 AS base
+FROM node:20.12.2-alpine3.19 AS base
RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" sh -
diff --git a/FUNDING.json b/FUNDING.json
new file mode 100644
index 0000000000..a1df876f47
--- /dev/null
+++ b/FUNDING.json
@@ -0,0 +1,7 @@
+{
+ "drips": {
+ "ethereum": {
+ "ownedBy": "0x0831DDFe60d009d9448CC976157b539089aB821E"
+ }
+ }
+}
diff --git a/README.md b/README.md
index d368a43499..8d5eebfebe 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,7 @@ Try Live Editor previews of future releases:
diff --git a/cypress.config.ts b/cypress.config.ts
index 4182d92a87..3346b5549b 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -2,6 +2,8 @@ import { defineConfig } from 'cypress';
import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin';
import coverage from '@cypress/code-coverage/task';
import eyesPlugin from '@applitools/eyes-cypress';
+import { registerArgosTask } from '@argos-ci/cypress/task';
+
export default eyesPlugin(
defineConfig({
projectId: 'n2sma2',
@@ -17,10 +19,17 @@ export default eyesPlugin(
}
return launchOptions;
});
- addMatchImageSnapshotPlugin(on, config);
// 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;
+ if (config.env.useArgos) {
+ registerArgosTask(on, config, {
+ token: 'fc3a35cf5200db928d65b2047861582d9444032b',
+ });
+ } else {
+ addMatchImageSnapshotPlugin(on, config);
+ }
// do not forget to return the changed config object!
return config;
},
diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts
index aed5d7973c..133a350328 100644
--- a/cypress/helpers/util.ts
+++ b/cypress/helpers/util.ts
@@ -35,7 +35,7 @@ export const mermaidUrl = (
};
const objStr: string = JSON.stringify(codeObject);
let url = `http://localhost:9000/e2e.html?graph=${utf8ToB64(objStr)}`;
- if (api) {
+ if (api && typeof graphStr === 'string') {
url = `http://localhost:9000/xss.html?graph=${graphStr}`;
}
@@ -54,16 +54,15 @@ export const imgSnapshotTest = (
): void => {
const options: CypressMermaidConfig = {
..._options,
- fontFamily: _options.fontFamily || 'courier',
+ fontFamily: _options.fontFamily ?? 'courier',
// @ts-ignore TODO: Fix type of fontSize
- fontSize: _options.fontSize || '16px',
+ fontSize: _options.fontSize ?? '16px',
sequence: {
- ...(_options.sequence || {}),
+ ...(_options.sequence ?? {}),
actorFontFamily: 'courier',
- noteFontFamily:
- _options.sequence && _options.sequence.noteFontFamily
- ? _options.sequence.noteFontFamily
- : 'courier',
+ noteFontFamily: _options.sequence?.noteFontFamily
+ ? _options.sequence.noteFontFamily
+ : 'courier',
messageFontFamily: 'courier',
},
};
@@ -95,18 +94,7 @@ export const openURLAndVerifyRendering = (
options: CypressMermaidConfig,
validation?: any
): void => {
- const useAppli: boolean = Cypress.env('useAppli');
- const name: string = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
-
- if (useAppli) {
- cy.log(`Opening eyes ${Cypress.spec.name} --- ${name}`);
- cy.eyesOpen({
- appName: 'Mermaid',
- testName: name,
- batchName: Cypress.spec.name,
- batchId: batchId + Cypress.spec.name,
- });
- }
+ const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
@@ -116,11 +104,29 @@ export const openURLAndVerifyRendering = (
cy.get('svg').should(validation);
}
+ verifyScreenshot(name);
+};
+
+export const verifyScreenshot = (name: string): void => {
+ const useAppli: boolean = Cypress.env('useAppli');
+ const useArgos: boolean = Cypress.env('useArgos');
+
if (useAppli) {
+ cy.log(`Opening eyes ${Cypress.spec.name} --- ${name}`);
+ cy.eyesOpen({
+ appName: 'Mermaid',
+ testName: name,
+ batchName: Cypress.spec.name,
+ batchId: batchId + Cypress.spec.name,
+ });
cy.log(`Check eyes ${Cypress.spec.name}`);
cy.eyesCheckWindow('Click!');
cy.log(`Closing eyes ${Cypress.spec.name}`);
cy.eyesClose();
+ } else if (useArgos) {
+ cy.argosScreenshot(name, {
+ threshold: 0.01,
+ });
} else {
cy.matchImageSnapshot(name);
}
diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js
index 544eab40fb..ad6b21e298 100644
--- a/cypress/integration/other/configuration.spec.js
+++ b/cypress/integration/other/configuration.spec.js
@@ -1,4 +1,4 @@
-import { renderGraph } from '../../helpers/util.ts';
+import { renderGraph, verifyScreenshot } from '../../helpers/util.ts';
describe('Configuration', () => {
describe('arrowMarkerAbsolute', () => {
it('should handle default value false of arrowMarkerAbsolute', () => {
@@ -119,8 +119,7 @@ describe('Configuration', () => {
const url = 'http://localhost:9000/regression/issue-1874.html';
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
- cy.get('svg').should('be.visible');
- cy.matchImageSnapshot(
+ verifyScreenshot(
'configuration.spec-should-not-taint-initial-configuration-when-using-multiple-directives'
);
});
@@ -145,7 +144,7 @@ describe('Configuration', () => {
// none of the diagrams should be error diagrams
expect($svg).to.not.contain('Syntax error');
});
- cy.matchImageSnapshot(
+ verifyScreenshot(
'configuration.spec-should-not-render-error-diagram-if-suppressErrorRendering-is-set'
);
});
@@ -162,7 +161,7 @@ describe('Configuration', () => {
// some of the diagrams should be error diagrams
expect($svg).to.contain('Syntax error');
});
- cy.matchImageSnapshot(
+ verifyScreenshot(
'configuration.spec-should-render-error-diagram-if-suppressErrorRendering-is-not-set'
);
});
diff --git a/cypress/integration/other/flowchart-elk.spec.js b/cypress/integration/other/flowchart-elk.spec.js
deleted file mode 100644
index 22a6efc0f5..0000000000
--- a/cypress/integration/other/flowchart-elk.spec.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { urlSnapshotTest, openURLAndVerifyRendering } from '../../helpers/util.ts';
-
-describe('Flowchart elk', () => {
- it('should use dagre as fallback', () => {
- urlSnapshotTest('http://localhost:9000/flow-elk.html', {
- name: 'flow-elk fallback to dagre',
- });
- });
- it('should allow overriding with external package', () => {
- urlSnapshotTest('http://localhost:9000/flow-elk.html?elk=true', {
- name: 'flow-elk overriding dagre with elk',
- });
- });
-});
diff --git a/cypress/integration/other/xss.spec.js b/cypress/integration/other/xss.spec.js
index 678040f98a..1e51d2f234 100644
--- a/cypress/integration/other/xss.spec.js
+++ b/cypress/integration/other/xss.spec.js
@@ -10,7 +10,6 @@ describe('XSS', () => {
cy.wait(1000).then(() => {
cy.get('.mermaid').should('exist');
});
- cy.get('svg');
});
it('should not allow tags in the css', () => {
@@ -137,4 +136,9 @@ describe('XSS', () => {
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
+ it('should sanitize backticks block diagram labels properly', () => {
+ cy.visit('http://localhost:9000/xss25.html');
+ cy.wait(1000);
+ cy.get('#the-malware').should('not.exist');
+ });
});
diff --git a/cypress/integration/rendering/appli.spec.js b/cypress/integration/rendering/appli.spec.js
index 5def968157..51eeb657eb 100644
--- a/cypress/integration/rendering/appli.spec.js
+++ b/cypress/integration/rendering/appli.spec.js
@@ -11,6 +11,27 @@ describe('Git Graph diagram', () => {
{}
);
});
+ it('Should render subgraphs with title margins and edge labels', () => {
+ imgSnapshotTest(
+ `flowchart LR
+
+ subgraph TOP
+ direction TB
+ subgraph B1
+ direction RL
+ i1 --lb1-->f1
+ end
+ subgraph B2
+ direction BT
+ i2 --lb2-->f2
+ end
+ end
+ A --lb3--> TOP --lb4--> B
+ B1 --lb5--> B2
+ `,
+ { flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } }
+ );
+ });
// it(`ultraFastTest`, function () {
// // Navigate to the url we want to test
// // ⭐️ Note to see visual bugs, run the test using the above URL for the 1st run.
diff --git a/cypress/integration/rendering/block.spec.js b/cypress/integration/rendering/block.spec.js
index 9d62c642dc..f5d5103e89 100644
--- a/cypress/integration/rendering/block.spec.js
+++ b/cypress/integration/rendering/block.spec.js
@@ -236,7 +236,7 @@ describe('Block diagram', () => {
);
});
- it('BL16: width alignment - blocks shold be equal in width', () => {
+ it('BL17: width alignment - blocks shold be equal in width', () => {
imgSnapshotTest(
`block-beta
A("This is the text")
@@ -247,7 +247,7 @@ describe('Block diagram', () => {
);
});
- it('BL17: block types 1 - square, rounded and circle', () => {
+ it('BL18: block types 1 - square, rounded and circle', () => {
imgSnapshotTest(
`block-beta
A["square"]
@@ -258,7 +258,7 @@ describe('Block diagram', () => {
);
});
- it('BL18: block types 2 - odd, diamond and hexagon', () => {
+ it('BL19: block types 2 - odd, diamond and hexagon', () => {
imgSnapshotTest(
`block-beta
A>"rect_left_inv_arrow"]
@@ -269,7 +269,7 @@ describe('Block diagram', () => {
);
});
- it('BL19: block types 3 - stadium', () => {
+ it('BL20: block types 3 - stadium', () => {
imgSnapshotTest(
`block-beta
A(["stadium"])
@@ -278,7 +278,7 @@ describe('Block diagram', () => {
);
});
- it('BL20: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => {
+ it('BL21: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => {
imgSnapshotTest(
`block-beta
A[/"lean right"/]
@@ -290,7 +290,7 @@ describe('Block diagram', () => {
);
});
- it('BL21: block types 1 - square, rounded and circle', () => {
+ it('BL22: block types 1 - square, rounded and circle', () => {
imgSnapshotTest(
`block-beta
A["square"]
@@ -301,7 +301,7 @@ describe('Block diagram', () => {
);
});
- it('BL22: sizing - it should be possible to make a block wider', () => {
+ it('BL23: sizing - it should be possible to make a block wider', () => {
imgSnapshotTest(
`block-beta
A("rounded"):2
@@ -312,7 +312,7 @@ describe('Block diagram', () => {
);
});
- it('BL23: sizing - it should be possible to make a composite block wider', () => {
+ it('BL24: sizing - it should be possible to make a composite block wider', () => {
imgSnapshotTest(
`block-beta
block:2
@@ -324,7 +324,7 @@ describe('Block diagram', () => {
);
});
- it('BL24: block in the middle with space on each side', () => {
+ it('BL25: block in the middle with space on each side', () => {
imgSnapshotTest(
`block-beta
columns 3
@@ -335,7 +335,7 @@ describe('Block diagram', () => {
{}
);
});
- it('BL25: space and an edge', () => {
+ it('BL26: space and an edge', () => {
imgSnapshotTest(
`block-beta
columns 5
@@ -345,7 +345,7 @@ describe('Block diagram', () => {
{}
);
});
- it('BL26: block sizes for regular blocks', () => {
+ it('BL27: block sizes for regular blocks', () => {
imgSnapshotTest(
`block-beta
columns 3
@@ -354,7 +354,7 @@ describe('Block diagram', () => {
{}
);
});
- it('BL27: composite block with a set width - f should use the available space', () => {
+ it('BL28: composite block with a set width - f should use the available space', () => {
imgSnapshotTest(
`block-beta
columns 3
@@ -363,11 +363,12 @@ describe('Block diagram', () => {
f
end
g
- `,
+ `,
{}
);
});
- it('BL23: composite block with a set width - f and g should split the available space', () => {
+
+ it('BL29: composite block with a set width - f and g should split the available space', () => {
imgSnapshotTest(
`block-beta
columns 3
@@ -379,7 +380,7 @@ describe('Block diagram', () => {
h
i
j
- `,
+ `,
{}
);
});
diff --git a/cypress/integration/rendering/c4.spec.js b/cypress/integration/rendering/c4.spec.js
index 59af6504b9..00e71adec8 100644
--- a/cypress/integration/rendering/c4.spec.js
+++ b/cypress/integration/rendering/c4.spec.js
@@ -1,7 +1,7 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
describe('C4 diagram', () => {
- it('should render a simple C4Context diagram', () => {
+ it('C4.1 should render a simple C4Context diagram', () => {
imgSnapshotTest(
`
C4Context
@@ -30,9 +30,8 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
- it('should render a simple C4Container diagram', () => {
+ it('C4.2 should render a simple C4Container diagram', () => {
imgSnapshotTest(
`
C4Container
@@ -50,9 +49,8 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
- it('should render a simple C4Component diagram', () => {
+ it('C4.3 should render a simple C4Component diagram', () => {
imgSnapshotTest(
`
C4Component
@@ -69,9 +67,8 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
- it('should render a simple C4Dynamic diagram', () => {
+ it('C4.4 should render a simple C4Dynamic diagram', () => {
imgSnapshotTest(
`
C4Dynamic
@@ -93,9 +90,8 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
- it('should render a simple C4Deployment diagram', () => {
+ it('C4.5 should render a simple C4Deployment diagram', () => {
imgSnapshotTest(
`
C4Deployment
@@ -117,6 +113,5 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
});
diff --git a/cypress/integration/rendering/classDiagram-v2.spec.js b/cypress/integration/rendering/classDiagram-v2.spec.js
index 20a1aea0ab..258f8529f6 100644
--- a/cypress/integration/rendering/classDiagram-v2.spec.js
+++ b/cypress/integration/rendering/classDiagram-v2.spec.js
@@ -76,7 +76,7 @@ describe('Class diagram V2', () => {
);
});
- it('should render a simple class diagram with different visibilities', () => {
+ it('2.1 should render a simple class diagram with different visibilities', () => {
imgSnapshotTest(
`
classDiagram-v2
@@ -93,7 +93,7 @@ describe('Class diagram V2', () => {
);
});
- it('should render multiple class diagrams', () => {
+ it('3: should render multiple class diagrams', () => {
imgSnapshotTest(
[
`
diff --git a/cypress/integration/rendering/classDiagram.spec.js b/cypress/integration/rendering/classDiagram.spec.js
index cab3649df4..a98a359edf 100644
--- a/cypress/integration/rendering/classDiagram.spec.js
+++ b/cypress/integration/rendering/classDiagram.spec.js
@@ -32,7 +32,6 @@ describe('Class diagram', () => {
`,
{ logLevel: 1 }
);
- cy.get('svg');
});
it('2: should render a simple class diagrams with cardinality', () => {
@@ -61,7 +60,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('3: should render a simple class diagram with different visibilities', () => {
@@ -79,7 +77,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('4: should render a simple class diagram with comments', () => {
@@ -109,7 +106,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('5: should render a simple class diagram with abstract method', () => {
@@ -121,7 +117,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('6: should render a simple class diagram with static method', () => {
@@ -133,7 +128,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('7: should render a simple class diagram with Generic class', () => {
@@ -153,7 +147,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('8: should render a simple class diagram with Generic class and relations', () => {
@@ -174,7 +167,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('9: should render a simple class diagram with clickable link', () => {
@@ -196,7 +188,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('10: should render a simple class diagram with clickable callback', () => {
@@ -218,7 +209,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('11: should render a simple class diagram with return type on method', () => {
@@ -233,7 +223,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('12: should render a simple class diagram with generic types', () => {
@@ -249,7 +238,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('13: should render a simple class diagram with css classes applied', () => {
@@ -267,7 +255,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('14: should render a simple class diagram with css classes applied directly', () => {
@@ -283,7 +270,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('15: should render a simple class diagram with css classes applied to multiple classes', () => {
@@ -298,7 +284,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('16: should render multiple class diagrams', () => {
@@ -351,7 +336,6 @@ describe('Class diagram', () => {
],
{}
);
- cy.get('svg');
});
// it('17: should render a class diagram when useMaxWidth is true (default)', () => {
@@ -421,7 +405,6 @@ describe('Class diagram', () => {
`,
{ logLevel: 1 }
);
- cy.get('svg');
});
it('should render class diagram with newlines in title', () => {
@@ -439,7 +422,6 @@ describe('Class diagram', () => {
+quack()
}
`);
- cy.get('svg');
});
it('should render class diagram with many newlines in title', () => {
diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js
index 578f5a3984..1a2340906a 100644
--- a/cypress/integration/rendering/erDiagram.spec.js
+++ b/cypress/integration/rendering/erDiagram.spec.js
@@ -218,7 +218,6 @@ describe('Entity Relationship Diagram', () => {
`,
{ loglevel: 1 }
);
- cy.get('svg');
});
it('should render entities with keys', () => {
diff --git a/cypress/integration/rendering/errorDiagram.spec.js b/cypress/integration/rendering/errorDiagram.spec.js
index e837565d3a..9eeb2e3106 100644
--- a/cypress/integration/rendering/errorDiagram.spec.js
+++ b/cypress/integration/rendering/errorDiagram.spec.js
@@ -3,7 +3,7 @@ import { imgSnapshotTest } from '../../helpers/util';
describe('Error Diagrams', () => {
beforeEach(() => {
cy.on('uncaught:exception', (err) => {
- expect(err.message).to.include('Parse error');
+ expect(err.message).to.include('error');
// return false to prevent the error from
// failing this test
return false;
diff --git a/cypress/integration/rendering/flowchart-elk.spec.js b/cypress/integration/rendering/flowchart-elk.spec.js
index e931025e91..b5caef9733 100644
--- a/cypress/integration/rendering/flowchart-elk.spec.js
+++ b/cypress/integration/rendering/flowchart-elk.spec.js
@@ -837,6 +837,26 @@ subgraph "\`**Two**\`"
in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
end
+`,
+ { flowchart: { titleTopMargin: 0 } }
+ );
+ });
+ it('Sub graphs and markdown strings', () => {
+ imgSnapshotTest(
+ `---
+config:
+ layout: elk
+---
+
+flowchart LR
+ subgraph subgraph_ko6czgs5u["Untitled subgraph"]
+ D["Option 1"]
+ end
+ C{"Evaluate"} -- One --> D
+ C -- Two --> E(("Option 2"))
+ D --> E
+ A["A"]
+
`,
{ flowchart: { titleTopMargin: 0 } }
);
@@ -855,7 +875,7 @@ describe('Title and arrow styling #4813', () => {
flowchart LR
A-->B
A-->C`,
- { flowchart: { defaultRenderer: 'elk' } }
+ { layout: 'elk' }
);
cy.get('svg').should((svg) => {
const title = svg[0].querySelector('text');
@@ -871,15 +891,14 @@ describe('Title and arrow styling #4813', () => {
B-.-oC
C==xD
D ~~~ A`,
- { flowchart: { defaultRenderer: 'elk' } }
+ { layout: 'elk' }
);
cy.get('svg').should((svg) => {
const edges = svg[0].querySelectorAll('.edges path');
- console.log(edges);
- expect(edges[0]).to.have.attr('pattern', 'solid');
- expect(edges[1]).to.have.attr('pattern', 'dotted');
- expect(edges[2]).to.have.css('stroke-width', '3.5px');
- expect(edges[3]).to.have.css('stroke-width', '1.5px');
+ expect(edges[0].getAttribute('class')).to.contain('edge-pattern-solid');
+ expect(edges[1].getAttribute('class')).to.contain('edge-pattern-dotted');
+ expect(edges[2].getAttribute('class')).to.contain('edge-thickness-thick');
+ expect(edges[3].getAttribute('class')).to.contain('edge-thickness-invisible');
});
});
});
diff --git a/cypress/integration/rendering/flowchart-handDrawn.spec.js b/cypress/integration/rendering/flowchart-handDrawn.spec.js
new file mode 100644
index 0000000000..d2e3edde0f
--- /dev/null
+++ b/cypress/integration/rendering/flowchart-handDrawn.spec.js
@@ -0,0 +1,1060 @@
+import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
+
+describe('Flowchart HandDrawn', () => {
+ it('FHD1: should render a simple flowchart no htmlLabels', () => {
+ imgSnapshotTest(
+ `graph 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]
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FHD2: should render a simple flowchart with htmlLabels', () => {
+ imgSnapshotTest(
+ `graph 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]
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: true },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FHD3: should render a simple flowchart with line breaks', () => {
+ imgSnapshotTest(
+ `
+ graph TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me thinksssss
ssssssssssssssssssssss
sssssssssssssssssssssssssss}
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[Car]
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, fontFamily: 'courier' }
+ );
+ });
+
+ it('FHD4: should render a simple flowchart with trapezoid and inverse trapezoid vertex options.', () => {
+ imgSnapshotTest(
+ `
+ graph TD
+ A[/Christmas\\]
+ A -->|Get money| B[\\Go shopping/]
+ B --> C{Let me thinksssss
ssssssssssssssssssssss
sssssssssssssssssssssssssss}
+ C -->|One| D[/Laptop/]
+ C -->|Two| E[\\iPhone\\]
+ C -->|Three| F[Car]
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, fontFamily: 'courier' }
+ );
+ });
+
+ it('FHD5: should style nodes via a class.', () => {
+ imgSnapshotTest(
+ `
+ graph TD
+ 1A --> 1B
+ 1B --> 1C
+ 1C --> D
+ 1C --> E
+
+ classDef processHead fill:#888888,color:white,font-weight:bold,stroke-width:3px,stroke:#001f3f
+ class 1A,1B,D,E processHead
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, fontFamily: 'courier' }
+ );
+ });
+
+ it('FHD6: should render a flowchart full of circles', () => {
+ imgSnapshotTest(
+ `
+ graph LR
+ 47(SAM.CommonFA.FMESummary)-->48(SAM.CommonFA.CommonFAFinanceBudget)
+ 37(SAM.CommonFA.BudgetSubserviceLineVolume)-->48(SAM.CommonFA.CommonFAFinanceBudget)
+ 35(SAM.CommonFA.PopulationFME)-->47(SAM.CommonFA.FMESummary)
+ 41(SAM.CommonFA.MetricCost)-->47(SAM.CommonFA.FMESummary)
+ 44(SAM.CommonFA.MetricOutliers)-->47(SAM.CommonFA.FMESummary)
+ 46(SAM.CommonFA.MetricOpportunity)-->47(SAM.CommonFA.FMESummary)
+ 40(SAM.CommonFA.OPVisits)-->47(SAM.CommonFA.FMESummary)
+ 38(SAM.CommonFA.CommonFAFinanceRefund)-->47(SAM.CommonFA.FMESummary)
+ 43(SAM.CommonFA.CommonFAFinancePicuDays)-->47(SAM.CommonFA.FMESummary)
+ 42(SAM.CommonFA.CommonFAFinanceNurseryDays)-->47(SAM.CommonFA.FMESummary)
+ 45(SAM.CommonFA.MetricPreOpportunity)-->46(SAM.CommonFA.MetricOpportunity)
+ 35(SAM.CommonFA.PopulationFME)-->45(SAM.CommonFA.MetricPreOpportunity)
+ 41(SAM.CommonFA.MetricCost)-->45(SAM.CommonFA.MetricPreOpportunity)
+ 41(SAM.CommonFA.MetricCost)-->44(SAM.CommonFA.MetricOutliers)
+ 39(SAM.CommonFA.ChargeDetails)-->43(SAM.CommonFA.CommonFAFinancePicuDays)
+ 39(SAM.CommonFA.ChargeDetails)-->42(SAM.CommonFA.CommonFAFinanceNurseryDays)
+ 39(SAM.CommonFA.ChargeDetails)-->41(SAM.CommonFA.MetricCost)
+ 39(SAM.CommonFA.ChargeDetails)-->40(SAM.CommonFA.OPVisits)
+ 35(SAM.CommonFA.PopulationFME)-->39(SAM.CommonFA.ChargeDetails)
+ 36(SAM.CommonFA.PremetricCost)-->39(SAM.CommonFA.ChargeDetails)
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, fontFamily: 'courier' }
+ );
+ });
+
+ it('FHD7: should render a flowchart full of icons', () => {
+ imgSnapshotTest(
+ `
+ graph TD
+ 9e122290_1ec3_e711_8c5a_005056ad0002("fa:fa-creative-commons My System | Test Environment")
+ 82072290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Business Logic Server:Service 1")
+ db052290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Business Logic Server:Service 2")
+ 4e112290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Report Server:Service 1")
+ 30122290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Shared Report Server:Service 2")
+ 5e112290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Dedicated Test Business Logic Server:Service 1")
+ c1112290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs Dedicated Test Business Logic Server:Service 2")
+ b7042290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[SupportDb]")
+ 8f102290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[DevelopmentDb]")
+ 0e102290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[TestDb]")
+ 07132290_1ec3_e711_8c5a_005056ad0002("fa:fa-circle [DBServer\\SharedDbInstance].[SharedReportingDb]")
+ c7072290_1ec3_e711_8c5a_005056ad0002("fa:fa-server Shared Business Logic Server")
+ ca122290_1ec3_e711_8c5a_005056ad0002("fa:fa-server Shared Report Server")
+ 68102290_1ec3_e711_8c5a_005056ad0002("fa:fa-server Dedicated Test Business Logic Server")
+ f4112290_1ec3_e711_8c5a_005056ad0002("fa:fa-database [DBServer\\SharedDbInstance]")
+ d6072290_1ec3_e711_8c5a_005056ad0002("fa:fa-server DBServer")
+ 71082290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs DBServer\\:MSSQLSERVER")
+ c0102290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs DBServer\\:SQLAgent")
+ 9a072290_1ec3_e711_8c5a_005056ad0002("fa:fa-cogs DBServer\\:SQLBrowser")
+ 1d0a2290_1ec3_e711_8c5a_005056ad0002("fa:fa-server VmHost1")
+ 200a2290_1ec3_e711_8c5a_005056ad0002("fa:fa-server VmHost2")
+ 1c0a2290_1ec3_e711_8c5a_005056ad0002("fa:fa-server VmHost3")
+ 9e122290_1ec3_e711_8c5a_005056ad0002-->82072290_1ec3_e711_8c5a_005056ad0002
+ 9e122290_1ec3_e711_8c5a_005056ad0002-->db052290_1ec3_e711_8c5a_005056ad0002
+ 9e122290_1ec3_e711_8c5a_005056ad0002-->4e112290_1ec3_e711_8c5a_005056ad0002
+ 9e122290_1ec3_e711_8c5a_005056ad0002-->30122290_1ec3_e711_8c5a_005056ad0002
+ 9e122290_1ec3_e711_8c5a_005056ad0002-->5e112290_1ec3_e711_8c5a_005056ad0002
+ 9e122290_1ec3_e711_8c5a_005056ad0002-->c1112290_1ec3_e711_8c5a_005056ad0002
+ 82072290_1ec3_e711_8c5a_005056ad0002-->b7042290_1ec3_e711_8c5a_005056ad0002
+ 82072290_1ec3_e711_8c5a_005056ad0002-->8f102290_1ec3_e711_8c5a_005056ad0002
+ 82072290_1ec3_e711_8c5a_005056ad0002-->0e102290_1ec3_e711_8c5a_005056ad0002
+ 82072290_1ec3_e711_8c5a_005056ad0002-->c7072290_1ec3_e711_8c5a_005056ad0002
+ db052290_1ec3_e711_8c5a_005056ad0002-->c7072290_1ec3_e711_8c5a_005056ad0002
+ db052290_1ec3_e711_8c5a_005056ad0002-->82072290_1ec3_e711_8c5a_005056ad0002
+ 4e112290_1ec3_e711_8c5a_005056ad0002-->b7042290_1ec3_e711_8c5a_005056ad0002
+ 4e112290_1ec3_e711_8c5a_005056ad0002-->8f102290_1ec3_e711_8c5a_005056ad0002
+ 4e112290_1ec3_e711_8c5a_005056ad0002-->0e102290_1ec3_e711_8c5a_005056ad0002
+ 4e112290_1ec3_e711_8c5a_005056ad0002-->07132290_1ec3_e711_8c5a_005056ad0002
+ 4e112290_1ec3_e711_8c5a_005056ad0002-->ca122290_1ec3_e711_8c5a_005056ad0002
+ 30122290_1ec3_e711_8c5a_005056ad0002-->ca122290_1ec3_e711_8c5a_005056ad0002
+ 30122290_1ec3_e711_8c5a_005056ad0002-->4e112290_1ec3_e711_8c5a_005056ad0002
+ 5e112290_1ec3_e711_8c5a_005056ad0002-->8f102290_1ec3_e711_8c5a_005056ad0002
+ 5e112290_1ec3_e711_8c5a_005056ad0002-->68102290_1ec3_e711_8c5a_005056ad0002
+ c1112290_1ec3_e711_8c5a_005056ad0002-->68102290_1ec3_e711_8c5a_005056ad0002
+ c1112290_1ec3_e711_8c5a_005056ad0002-->5e112290_1ec3_e711_8c5a_005056ad0002
+ b7042290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002
+ 8f102290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002
+ 0e102290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002
+ 07132290_1ec3_e711_8c5a_005056ad0002-->f4112290_1ec3_e711_8c5a_005056ad0002
+ c7072290_1ec3_e711_8c5a_005056ad0002-->1d0a2290_1ec3_e711_8c5a_005056ad0002
+ ca122290_1ec3_e711_8c5a_005056ad0002-->200a2290_1ec3_e711_8c5a_005056ad0002
+ 68102290_1ec3_e711_8c5a_005056ad0002-->1c0a2290_1ec3_e711_8c5a_005056ad0002
+ f4112290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002
+ f4112290_1ec3_e711_8c5a_005056ad0002-->71082290_1ec3_e711_8c5a_005056ad0002
+ f4112290_1ec3_e711_8c5a_005056ad0002-->c0102290_1ec3_e711_8c5a_005056ad0002
+ f4112290_1ec3_e711_8c5a_005056ad0002-->9a072290_1ec3_e711_8c5a_005056ad0002
+ d6072290_1ec3_e711_8c5a_005056ad0002-->1c0a2290_1ec3_e711_8c5a_005056ad0002
+ 71082290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002
+ c0102290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002
+ c0102290_1ec3_e711_8c5a_005056ad0002-->71082290_1ec3_e711_8c5a_005056ad0002
+ 9a072290_1ec3_e711_8c5a_005056ad0002-->d6072290_1ec3_e711_8c5a_005056ad0002
+ 9a072290_1ec3_e711_8c5a_005056ad0002-->71082290_1ec3_e711_8c5a_005056ad0002
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, fontFamily: 'courier' }
+ );
+ });
+
+ it('FHD8: should render labels with numbers at the start', () => {
+ imgSnapshotTest(
+ `
+ graph TB;subgraph "number as labels";1;end;
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, fontFamily: 'courier' }
+ );
+ });
+
+ it('FHD9: should render subgraphs', () => {
+ imgSnapshotTest(
+ `
+ graph TB
+ subgraph One
+ a1-->a2
+ end
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, fontFamily: 'courier' }
+ );
+ });
+
+ it('FHD10: should render subgraphs with a title starting with a digit', () => {
+ imgSnapshotTest(
+ `
+ graph TB
+ subgraph 2Two
+ a1-->a2
+ end
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, fontFamily: 'courier' }
+ );
+ });
+
+ it('FHD11: should render styled subgraphs', () => {
+ imgSnapshotTest(
+ `
+ graph TB
+ A
+ B
+ subgraph foo[Foo SubGraph]
+ C
+ D
+ end
+ subgraph bar[Bar SubGraph]
+ E
+ F
+ end
+ G
+
+ A-->B
+ B-->C
+ C-->D
+ B-->D
+ D-->E
+ E-->A
+ E-->F
+ F-->D
+ F-->G
+ B-->G
+ G-->D
+
+ style foo fill:#F99,stroke-width:2px,stroke:#F0F,color:darkred
+ style bar fill:#999,stroke-width:10px,stroke:#0F0,color:blue
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, fontFamily: 'courier' }
+ );
+ });
+
+ it('FHD12: should render a flowchart with long names and class definitions', () => {
+ imgSnapshotTest(
+ `graph LR
+ sid-B3655226-6C29-4D00-B685-3D5C734DC7E1["
+
+ 提交申请
+ 熊大
+ "];
+ class sid-B3655226-6C29-4D00-B685-3D5C734DC7E1 node-executed;
+ sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A["
+ 负责人审批
+ 强子
+ "];
+ class sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A node-executed;
+ sid-E27C0367-E6D6-497F-9736-3CDC21FDE221["
+ DBA审批
+ 强子
+ "];
+ class sid-E27C0367-E6D6-497F-9736-3CDC21FDE221 node-executed;
+ sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD["
+ SA审批
+ 阿美
+ "];
+ class sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD node-executed;
+ sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7["
+ 主管审批
+ 光头强
+ "];
+ class sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7 node-executed;
+ sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89["
+ DBA确认
+ 强子
+ "];
+ class sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89 node-executed;
+ sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937["
+ SA确认
+ 阿美
+ "];
+ class sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937 node-executed;
+ sid-4FC27B48-A6F9-460A-A675-021F5854FE22["
+ 结束
+ "];
+ class sid-4FC27B48-A6F9-460A-A675-021F5854FE22 node-executed;
+ sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E["
+ SA执行1
+ 强子
+ "];
+ class sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E node-executed;
+ sid-6C2120F3-D940-4958-A067-0903DCE879C4["
+ SA执行2
+ 强子
+ "];
+ class sid-6C2120F3-D940-4958-A067-0903DCE879C4 node-executed;
+ sid-9180E2A0-5C4B-435F-B42F-0D152470A338["
+ DBA执行1
+ 强子
+ "];
+ class sid-9180E2A0-5C4B-435F-B42F-0D152470A338 node-executed;
+ sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD["
+ DBA执行3
+ 强子
+ "];
+ class sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD node-executed;
+ sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756["
+ DBA执行2
+ 强子
+ "];
+ class sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756 node-executed;
+ sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93["
+ DBA执行4
+ 强子
+ "];
+ class sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93 node-executed;
+ sid-1897B30A-9C5C-4D5B-B80B-76A038785070["
+ 负责人确认
+ 梁静茹
+ "];
+ class sid-1897B30A-9C5C-4D5B-B80B-76A038785070 node-executed;
+ sid-B3655226-6C29-4D00-B685-3D5C734DC7E1-->sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7;
+ sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A-->sid-1897B30A-9C5C-4D5B-B80B-76A038785070;
+ sid-E27C0367-E6D6-497F-9736-3CDC21FDE221-->sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89;
+ sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD-->sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937;
+ sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E-->sid-6C2120F3-D940-4958-A067-0903DCE879C4;
+ sid-9180E2A0-5C4B-435F-B42F-0D152470A338-->sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756;
+ sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD-->sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93;
+ sid-6C2120F3-D940-4958-A067-0903DCE879C4-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A;
+ sid-1897B30A-9C5C-4D5B-B80B-76A038785070-->sid-4FC27B48-A6F9-460A-A675-021F5854FE22;
+ sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937-->sid-19DD9E9F-98C1-44EE-B604-842AFEE76F1E;
+ sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89-->sid-9180E2A0-5C4B-435F-B42F-0D152470A338;
+ sid-A1B3CD96-7697-4D7C-BEAA-73D187B1BE89-->sid-03A2C3AC-5337-48A5-B154-BB3FD0EC8DAD;
+ sid-D5E1F2F4-306C-47A2-BF74-F66E3D769756-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A;
+ sid-8C3F2F1D-F014-4F99-B966-095DC1A2BD93-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A;
+ sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-BED98281-9585-4D1B-934E-BD1AC6AC0EFD;
+ sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-E27C0367-E6D6-497F-9736-3CDC21FDE221;
+ sid-3E35A7FF-A2F4-4E07-9247-DBF884C81937-->sid-6C2120F3-D940-4958-A067-0903DCE879C4;
+ sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-4DA958A0-26D9-4D47-93A7-70F39FD7D51A;
+ sid-7CE72B24-E0C1-46D3-8132-8BA66BE05AA7-->sid-4FC27B48-A6F9-460A-A675-021F5854FE22;
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, fontFamily: 'courier' }
+ );
+ });
+
+ it('FHD13: should render color of styled nodes', () => {
+ imgSnapshotTest(
+ `
+ graph LR
+ foo-->bar
+
+ classDef foo fill:lightblue,color:green,stroke:#FF9E2C,font-weight:bold
+ style foo fill:#F99,stroke-width:2px,stroke:#F0F
+ style bar fill:#999,color: #00ff00, stroke-width:10px,stroke:#0F0
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ listUrl: false,
+ listId: 'color styling',
+ fontFamily: 'courier',
+ logLevel: 0,
+ }
+ );
+ });
+
+ it('FHD14: should render hexagons', () => {
+ imgSnapshotTest(
+ `
+ graph TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{{Let me think...
Do I want something for work,
something to spend every free second with,
or something to get around?}}
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[Car]
+ click A "index.html#link-clicked" "link test"
+ click B testClick "click test"
+ classDef someclass fill:#f96;
+ class A someclass;
+ class C someclass;
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ listUrl: false,
+ listId: 'color styling',
+ fontFamily: 'courier',
+ logLevel: 0,
+ }
+ );
+ });
+
+ it('FHD15: should render a simple flowchart with comments', () => {
+ imgSnapshotTest(
+ `graph TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ %% this is a comment
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FHD16: Render Stadium shape', () => {
+ imgSnapshotTest(
+ ` graph TD
+ A([stadium shape test])
+ A -->|Get money| B([Go shopping])
+ B --> C([Let me think...
Do I want something for work,
something to spend every free second with,
or something to get around?])
+ C -->|One| D([Laptop])
+ C -->|Two| E([iPhone])
+ C -->|Three| F([Car
wroom wroom])
+ click A "index.html#link-clicked" "link test"
+ click B testClick "click test"
+ classDef someclass fill:#f96;
+ class A someclass;
+ class C someclass;
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FHD17: Render multiline texts', () => {
+ imgSnapshotTest(
+ `graph LR
+ A1[Multi
Line] -->|Multi
Line| B1(Multi
Line)
+ C1[Multi
Line] -->|Multi
Line| D1(Multi
Line)
+ E1[Multi
Line] -->|Multi
Line| F1(Multi
Line)
+ A2[Multi
Line] -->|Multi
Line| B2(Multi
Line)
+ C2[Multi
Line] -->|Multi
Line| D2(Multi
Line)
+ E2[Multi
Line] -->|Multi
Line| F2(Multi
Line)
+ linkStyle 0 stroke:DarkGray,stroke-width:2px
+ linkStyle 1 stroke:DarkGray,stroke-width:2px
+ linkStyle 2 stroke:DarkGray,stroke-width:2px
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FHD18: Chaining of nodes', () => {
+ imgSnapshotTest(
+ `graph LR
+ a --> b --> c
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FHD19: Multiple nodes and chaining in one statement', () => {
+ imgSnapshotTest(
+ `graph LR
+ a --> b & c--> d
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FHD20: Multiple nodes and chaining in one statement', () => {
+ imgSnapshotTest(
+ `graph TD
+ A[ h ] -- hello --> B[" test "]:::exClass & C --> D;
+ classDef exClass background:#bbb,border:1px solid red;
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FDH21: Render cylindrical shape', () => {
+ imgSnapshotTest(
+ `graph LR
+ A[(cylindrical
shape
test)]
+ A -->|Get money| B1[(Go shopping 1)]
+ A -->|Get money| B2[(Go shopping 2)]
+ A -->|Get money| B3[(Go shopping 3)]
+ C[(Let me think...
Do I want something for work,
something to spend every free second with,
or something to get around?)]
+ B1 --> C
+ B2 --> C
+ B3 --> C
+ C -->|One| D[(Laptop)]
+ C -->|Two| E[(iPhone)]
+ C -->|Three| F[(Car)]
+ click A "index.html#link-clicked" "link test"
+ click B testClick "click test"
+ classDef someclass fill:#f96;
+ class A someclass;`,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FDH22: Render a simple flowchart with nodeSpacing set to 100', () => {
+ imgSnapshotTest(
+ `graph TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ %% this is a comment
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, flowchart: { nodeSpacing: 50 }, fontFamily: 'courier' }
+ );
+ });
+
+ it('FDH23: Render a simple flowchart with rankSpacing set to 100', () => {
+ imgSnapshotTest(
+ `graph TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ %% this is a comment
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { rankSpacing: '100' },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FDH24: Keep node label text (if already defined) when a style is applied', () => {
+ imgSnapshotTest(
+ `graph LR
+ A(( )) -->|step 1| B(( ))
+ B(( )) -->|step 2| C(( ))
+ C(( )) -->|step 3| D(( ))
+ linkStyle 1 stroke:greenyellow,stroke-width:2px
+ style C fill:greenyellow,stroke:green,stroke-width:4px
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FDH25: Handle link click events (link, anchor, mailto, other protocol, script)', () => {
+ imgSnapshotTest(
+ `graph TB
+ TITLE["Link Click Events
(click the nodes below)"]
+ A["link test (open in same tab)"]
+ B["link test (open in new tab)"]
+ C[anchor test]
+ D[mailto test]
+ E[other protocol test]
+ F[script test]
+ TITLE --> A & B & C & D & E & F
+ click A "https://mermaid-js.github.io/mermaid/#/" "link test (open in same tab)"
+ click B "https://mermaid-js.github.io/mermaid/#/" "link test (open in new tab)" _blank
+ click C "#link-clicked"
+ click D "mailto:user@user.user" "mailto test"
+ click E "notes://do-your-thing/id" "other protocol test"
+ click F "javascript:alert('test')" "script test"
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, securityLevel: 'loose', fontFamily: 'courier' }
+ );
+ });
+
+ it('FDH26: Set text color of nodes and links according to styles when html labels are enabled', () => {
+ imgSnapshotTest(
+ `graph LR
+ A[red
text] -->|red
text| B(blue
text)
+ C[/red
text/] -->|blue
text| D{blue
text}
+ E{{default
style}} -->|default
style| F([default
style])
+ linkStyle default color:Sienna;
+ linkStyle 0 color:red;
+ linkStyle 1 stroke:DarkGray,stroke-width:2px,color:#0000ff
+ style A color:red;
+ style B color:blue;
+ style C stroke:#ff0000,fill:#ffcccc,color:#ff0000
+ style D stroke:#0000ff,fill:#ccccff,color:#0000ff
+ click B "index.html#link-clicked" "link test"
+ click D testClick "click test"
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, flowchart: { htmlLabels: true } }
+ );
+ });
+
+ it('FDH27: Set text color of nodes and links according to styles when html labels are disabled', () => {
+ imgSnapshotTest(
+ `graph LR
+ A[red
text] -->|red
text| B(blue
text)
+ C[/red
text/] -->|blue
text| D{blue
text}
+ E{{default
style}} -->|default
style| F([default
style])
+ linkStyle default color:Sienna;
+ linkStyle 0 color:red;
+ linkStyle 1 stroke:DarkGray,stroke-width:2px,color:#0000ff
+ style A color:red;
+ style B color:blue;
+ style C stroke:#ff0000,fill:#ffcccc,color:#ff0000
+ style D stroke:#0000ff,fill:#ccccff,color:#0000ff
+ click B "index.html#link-clicked" "link test"
+ click D testClick "click test"
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FDH28: Apply default class to all nodes which do not have another class assigned (htmlLabels enabled)', () => {
+ imgSnapshotTest(
+ `graph TD
+ A[myClass1] --> B[default] & C[default]
+ B[default] & C[default] --> D[myClass2]
+ classDef default stroke-width:2px,fill:none,stroke:silver
+ classDef node color:red
+ classDef myClass1 color:#0000ff
+ classDef myClass2 stroke:#0000ff,fill:#ccccff
+ class A myClass1
+ class D myClass2
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, flowchart: { htmlLabels: true } }
+ );
+ });
+
+ it('FDH29: Apply default class to all nodes which do not have another class assigned (htmlLabels disabled)', () => {
+ imgSnapshotTest(
+ `graph TD
+ A[myClass1] --> B[default] & C[default]
+ B[default] & C[default] --> D[myClass2]
+ classDef default stroke-width:2px,fill:none,stroke:silver
+ classDef node color:red
+ classDef myClass1 color:#0000ff
+ classDef myClass2 stroke:#0000ff,fill:#ccccff
+ class A myClass1
+ class D myClass2
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FDH30: Possibility to style text color of nodes and subgraphs as well as apply classes to subgraphs', () => {
+ imgSnapshotTest(
+ `graph LR
+ subgraph id1 [title is set]
+ A-->B
+ end
+ subgraph id2 [title]
+ E
+ end
+
+ B-->C
+
+ subgraph id3
+ C-->D
+ end
+ class id3,id2,A redBg;
+ class id3,A whiteTxt;
+ classDef redBg fill:#622;
+ classDef whiteTxt color: white;
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FDH31: should not slice off edges that are to the left of the left-most vertex', () => {
+ imgSnapshotTest(
+ `graph TD
+ work --> sleep
+ sleep --> work
+ eat --> sleep
+ work --> eat
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FDH32: Render Subroutine shape', () => {
+ imgSnapshotTest(
+ `graph LR
+ A[[subroutine shape test]]
+ A -->|Get money| B[[Go shopping]]
+ B --> C[[Let me think...
Do I want something for work,
something to spend every free second with,
or something to get around?]]
+ C -->|One| D[[Laptop]]
+ C -->|Two| E[[iPhone]]
+ C -->|Three| F[[Car
wroom wroom]]
+ click A "index.html#link-clicked" "link test"
+ click B testClick "click test"
+ classDef someclass fill:#f96;
+ class A someclass;
+ class C someclass;
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: false },
+ fontFamily: 'courier',
+ }
+ );
+ });
+
+ it('FDH33: should render a simple flowchart with diagramPadding set to 0', () => {
+ imgSnapshotTest(
+ `graph TD
+ A[Christmas] -->|Get money| B(Go shopping)
+ B --> C{Let me think}
+ %% this is a comment
+ C -->|One| D[Laptop]
+ C -->|Two| E[iPhone]
+ C -->|Three| F[fa:fa-car Car]
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, flowchart: { diagramPadding: 0 } }
+ );
+ });
+
+ it('FDH34: testing the label width in percy', () => {
+ imgSnapshotTest(
+ `graph TD
+ A[Christmas]
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0 }
+ );
+ });
+
+ it('FDH35: should honor minimum edge length as specified by the user', () => {
+ imgSnapshotTest(
+ `graph TD
+ L1 --- L2
+ L2 --- C
+ M1 ---> C
+ R1 .-> R2
+ R2 <.-> C
+ C -->|Label 1| E1
+ C -- Label 2 ---> E2
+ C ----> E3
+ C -----> E4
+ C ======> E5
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0 }
+ );
+ });
+ it('FDH36: should render escaped without html labels', () => {
+ imgSnapshotTest(
+ `graph TD
+ a["Haiya"]-->b
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, htmlLabels: false, flowchart: { htmlLabels: false } }
+ );
+ });
+ it('FDH37: should render non-escaped with html labels', () => {
+ imgSnapshotTest(
+ `graph TD
+ a["Haiya"]-->b
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ htmlLabels: true,
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+ it('FDH38: should render a flowchart when useMaxWidth is true (default)', () => {
+ 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]
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, flowchart: { useMaxWidth: true } }
+ );
+ cy.get('svg').should((svg) => {
+ expect(svg).to.have.attr('width', '100%');
+ // expect(svg).to.have.attr('height');
+ // use within because the absolute value can be slightly different depending on the environment ±10%
+ // const height = parseFloat(svg.attr('height'));
+ // expect(height).to.be.within(446 * 0.95, 446 * 1.05);
+ const style = svg.attr('style');
+ expect(style).to.match(/^max-width: [\d.]+px;$/);
+ const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
+ expect(maxWidthValue).to.be.within(446 * 0.9, 446 * 1.1);
+ });
+ });
+ it('FDH39: should render a flowchart when useMaxWidth is false', () => {
+ renderGraph(
+ `graph 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]
+ `,
+ { look: 'handDrawn', handDrawnSeed: 0, flowchart: { useMaxWidth: false } }
+ );
+ cy.get('svg').should((svg) => {
+ // const height = parseFloat(svg.attr('height'));
+ const width = parseFloat(svg.attr('width'));
+ // use within because the absolute value can be slightly different depending on the environment ±10%
+ // expect(height).to.be.within(446 * 0.95, 446 * 1.05);
+ expect(width).to.be.within(446 * 0.9, 446 * 1.1);
+ expect(svg).to.not.have.attr('style');
+ });
+ });
+ it('FDH40: handle styling with style expressions', () => {
+ imgSnapshotTest(
+ `
+ graph LR
+ id1(Start)-->id2(Stop)
+ style id1 fill:#f9f,stroke:#333,stroke-width:4px
+ style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ htmlLabels: true,
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+ it('FDH41: handle styling for all node shapes', () => {
+ imgSnapshotTest(
+ `
+ graph LR
+ A[red text] -->|default style| B(blue text)
+ C([red text]) -->|default style| D[[blue text]]
+ E[(red text)] -->|default style| F((blue text))
+ G>red text] -->|default style| H{blue text}
+ I{{red text}} -->|default style| J[/blue text/]
+ linkStyle default color:Sienna;
+ style A stroke:#ff0000,fill:#ffcccc,color:#ff0000
+ style B stroke:#0000ff,fill:#ccccff,color:#0000ff
+ style C stroke:#ff0000,fill:#ffcccc,color:#ff0000
+ style D stroke:#0000ff,fill:#ccccff,color:#0000ff
+ style E stroke:#ff0000,fill:#ffcccc,color:#ff0000
+ style F stroke:#0000ff,fill:#ccccff,color:#0000ff
+ style G stroke:#ff0000,fill:#ffcccc,color:#ff0000
+ style H stroke:#0000ff,fill:#ccccff,color:#0000ff
+ style I stroke:#ff0000,fill:#ffcccc,color:#ff0000
+ style J stroke:#0000ff,fill:#ccccff,color:#0000ff
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ htmlLabels: true,
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+ it('FDH42: fontawesome icons in edge labels', () => {
+ imgSnapshotTest(
+ `
+graph TD
+ C -->|fa:fa-car Car| F[fa:fa-car Car]
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ htmlLabels: true,
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+ it('FDH43: fontawesome icons in edge labels', () => {
+ imgSnapshotTest(
+ `
+ graph TB
+ subgraph bar[Bar]
+ F
+ end
+ style bar fill:#999,stroke-width:10px,stroke:#0F0,color:blue
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ htmlLabels: true,
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+ it('FDH44: fontawesome icons in edge labels', () => {
+ imgSnapshotTest(
+ `
+ graph TB
+ A
+ B
+ subgraph foo[Foo SubGraph]
+ C
+ D
+ end
+ subgraph bar[Bar SubGraph]
+ E
+ F
+ end
+ G
+
+ A-->B
+ B-->C
+ C-->D
+ B-->D
+ D-->E
+ E-->A
+ E-->F
+ F-->D
+ F-->G
+ B-->G
+ G-->D
+
+ style foo fill:#F99,stroke-width:2px,stroke:#F0F,color:darkred
+ style bar fill:#999,stroke-width:10px,stroke:#0F0,color:blue
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ htmlLabels: true,
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+ it('FDH45: fontawesome icons in edge labels', () => {
+ imgSnapshotTest(
+ `
+ %%{init:{"theme":"base", "themeVariables": {"primaryColor":"#411d4e", "titleColor":"white", "darkMode":true}}}%%
+ flowchart LR
+ subgraph A
+ a --> b
+ end
+ subgraph B
+ i -->f
+ end
+ A --> B
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ htmlLabels: true,
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+ it('FDH46: text-color from classes', () => {
+ imgSnapshotTest(
+ `
+ flowchart LR
+ classDef dark fill:#000,stroke:#000,stroke-width:4px,color:#fff
+ Lorem --> Ipsum --> Dolor
+ class Lorem,Dolor dark
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ htmlLabels: true,
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+ it('FDH47: apply class called default on node called default', () => {
+ imgSnapshotTest(
+ `
+ graph TD
+ classDef default fill:#a34,stroke:#000,stroke-width:4px,color:#fff
+ hello --> default
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ htmlLabels: true,
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+
+ it('FDH48: should be able to style default node independently', () => {
+ imgSnapshotTest(
+ `
+ flowchart TD
+ classDef default fill:#a34
+ hello --> default
+
+ style default stroke:#000,stroke-width:4px
+ `,
+ {
+ look: 'handDrawn',
+ handDrawnSeed: 0,
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
+});
diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js
index 3eb2a0432a..c2fd0b0119 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(290 * 0.95 - 1, 290 * 1.05);
+ expect(maxWidthValue).to.be.within(446 * 0.95 - 1, 446 * 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(290 * 0.95 - 1, 290 * 1.05);
+ expect(width).to.be.within(446 * 0.95 - 1, 446 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
@@ -1047,7 +1047,9 @@ end
A --lb3--> TOP --lb4--> B
B1 --lb5--> B2
`,
- { flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } }
+ {
+ flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } },
+ }
);
});
});
diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js
index e4766e7923..d3a83ae5f2 100644
--- a/cypress/integration/rendering/flowchart.spec.js
+++ b/cypress/integration/rendering/flowchart.spec.js
@@ -733,7 +733,7 @@ describe('Graph', () => {
});
it('38: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
- `graph TD
+ `flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
@@ -751,7 +751,7 @@ describe('Graph', () => {
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(300 * 0.9, 300 * 1.1);
+ expect(maxWidthValue).to.be.within(446 * 0.9, 446 * 1.1);
});
});
it('39: should render a flowchart when useMaxWidth is false', () => {
@@ -770,7 +770,7 @@ describe('Graph', () => {
const width = parseFloat(svg.attr('width'));
// use within because the absolute value can be slightly different depending on the environment ±10%
// expect(height).to.be.within(446 * 0.95, 446 * 1.05);
- expect(width).to.be.within(300 * 0.9, 300 * 1.1);
+ expect(width).to.be.within(446 * 0.9, 446 * 1.1);
expect(svg).to.not.have.attr('style');
});
});
@@ -905,13 +905,16 @@ graph TD
it('67: should be able to style default node independently', () => {
imgSnapshotTest(
`
- flowchart TD
+ flowchart TD
classDef default fill:#a34
hello --> default
style default stroke:#000,stroke-width:4px
`,
- { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
+ {
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
);
});
});
diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js
index 4e8f7fdcac..249febd08d 100644
--- a/cypress/integration/rendering/gitGraph.spec.js
+++ b/cypress/integration/rendering/gitGraph.spec.js
@@ -1458,5 +1458,115 @@ gitGraph TB:
{ gitGraph: { parallelCommits: true } }
);
});
+ it('73: should render a simple gitgraph with three branches and tagged merge commit using switch instead of checkout', () => {
+ imgSnapshotTest(
+ `gitGraph
+ commit id: "1"
+ commit id: "2"
+ branch nice_feature
+ switch nice_feature
+ commit id: "3"
+ switch main
+ commit id: "4"
+ switch nice_feature
+ branch very_nice_feature
+ switch very_nice_feature
+ commit id: "5"
+ switch main
+ commit id: "6"
+ switch nice_feature
+ commit id: "7"
+ switch main
+ merge nice_feature id: "12345" tag: "my merge commit"
+ switch very_nice_feature
+ commit id: "8"
+ switch main
+ commit id: "9"
+ `,
+ {}
+ );
+ });
+ it('74: should render commits for more than 8 branches using switch instead of checkout', () => {
+ imgSnapshotTest(
+ `
+ gitGraph
+ switch main
+ %% Make sure to manually set the ID of all commits, for consistent visual tests
+ commit id: "1-abcdefg"
+ switch main
+ branch branch1
+ commit id: "2-abcdefg"
+ switch main
+ merge branch1
+ branch branch2
+ commit id: "3-abcdefg"
+ switch main
+ merge branch2
+ branch branch3
+ commit id: "4-abcdefg"
+ switch main
+ merge branch3
+ branch branch4
+ commit id: "5-abcdefg"
+ switch main
+ merge branch4
+ branch branch5
+ commit id: "6-abcdefg"
+ switch main
+ merge branch5
+ branch branch6
+ commit id: "7-abcdefg"
+ switch main
+ merge branch6
+ branch branch7
+ commit id: "8-abcdefg"
+ switch main
+ merge branch7
+ branch branch8
+ commit id: "9-abcdefg"
+ switch main
+ merge branch8
+ branch branch9
+ commit id: "10-abcdefg"
+ `,
+ {}
+ );
+ });
+ it('75: should render a gitGraph with multiple tags on a merge commit on bottom-to-top orientation', () => {
+ imgSnapshotTest(
+ `gitGraph BT:
+ commit id: "ZERO"
+ branch develop
+ commit id:"A"
+ checkout main
+ commit id:"ONE"
+ checkout develop
+ commit id:"B"
+ checkout main
+ merge develop id:"Release 1.0" type:HIGHLIGHT tag: "SAML v2.0" tag: "OpenID v1.1"
+ commit id:"TWO"
+ checkout develop
+ commit id:"C"`,
+ {}
+ );
+ });
+ });
+ it('76: should render a gitGraph with multiple tags on a merge commit on left-to-right orientation', () => {
+ imgSnapshotTest(
+ `gitGraph
+ commit id: "ZERO"
+ branch develop
+ commit id:"A"
+ checkout main
+ commit id:"ONE"
+ checkout develop
+ commit id:"B"
+ checkout main
+ merge develop id:"Release 1.0" type:HIGHLIGHT tag: "SAML v2.0" tag: "OpenID v1.1"
+ commit id:"TWO"
+ checkout develop
+ commit id:"C"`,
+ {}
+ );
});
});
diff --git a/cypress/integration/rendering/packet.spec.ts b/cypress/integration/rendering/packet.spec.ts
index 61555ea530..c64538875d 100644
--- a/cypress/integration/rendering/packet.spec.ts
+++ b/cypress/integration/rendering/packet.spec.ts
@@ -10,6 +10,15 @@ describe('packet structure', () => {
);
});
+ it('should render a simple packet diagram without ranges', () => {
+ imgSnapshotTest(
+ `packet-beta
+ 0: "h"
+ 1: "i"
+`
+ );
+ });
+
it('should render a complex packet diagram', () => {
imgSnapshotTest(
`packet-beta
diff --git a/cypress/integration/rendering/quadrantChart.spec.js b/cypress/integration/rendering/quadrantChart.spec.js
index 1be1f7deff..4830db6568 100644
--- a/cypress/integration/rendering/quadrantChart.spec.js
+++ b/cypress/integration/rendering/quadrantChart.spec.js
@@ -1,4 +1,4 @@
-import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
+import { imgSnapshotTest } from '../../helpers/util.ts';
describe('Quadrant Chart', () => {
it('should render if only chart type is provided', () => {
@@ -8,7 +8,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a complete quadrant chart', () => {
imgSnapshotTest(
@@ -30,7 +29,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render without points', () => {
imgSnapshotTest(
@@ -46,7 +44,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should able to render y-axix on right side', () => {
imgSnapshotTest(
@@ -63,7 +60,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should able to render x-axix on bottom', () => {
imgSnapshotTest(
@@ -80,7 +76,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should able to render x-axix on bottom and y-axis on right', () => {
imgSnapshotTest(
@@ -97,7 +92,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render without title', () => {
imgSnapshotTest(
@@ -112,7 +106,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should use all the config', () => {
imgSnapshotTest(
@@ -135,7 +128,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should use all the theme variable', () => {
imgSnapshotTest(
@@ -158,7 +150,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render x-axis labels in the center, if x-axis has two labels', () => {
imgSnapshotTest(
@@ -180,7 +171,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render y-axis labels in the center, if y-axis has two labels', () => {
imgSnapshotTest(
@@ -202,7 +192,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render both axes labels on the left and bottom, if both axes have only one label', () => {
imgSnapshotTest(
@@ -224,6 +213,52 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
+ });
+
+ it('it should render data points with styles', () => {
+ imgSnapshotTest(
+ `
+ quadrantChart
+ title Reach and engagement of campaigns
+ x-axis Reach -->
+ y-axis Engagement -->
+ quadrant-1 We should expand
+ quadrant-2 Need to promote
+ quadrant-3 Re-evaluate
+ quadrant-4 May be improved
+ Campaign A: [0.3, 0.6] radius: 20
+ Campaign B: [0.45, 0.23] color: #ff0000
+ Campaign C: [0.57, 0.69] stroke-color: #ff00ff
+ Campaign D: [0.78, 0.34] stroke-width: 3px
+ Campaign E: [0.40, 0.34] radius: 20, color: #ff0000 , stroke-color : #ff00ff, stroke-width : 3px
+ Campaign F: [0.35, 0.78] stroke-width: 3px , color: #ff0000, radius: 20, stroke-color: #ff00ff
+ Campaign G: [0.22, 0.22] stroke-width: 3px , color: #309708 , radius : 20 , stroke-color: #5060ff
+ Campaign H: [0.22, 0.44]
+ `,
+ {}
+ );
+ });
+
+ it('it should render data points with styles + classes', () => {
+ imgSnapshotTest(
+ `
+ quadrantChart
+ title Reach and engagement of campaigns
+ x-axis Reach -->
+ y-axis Engagement -->
+ quadrant-1 We should expand
+ quadrant-2 Need to promote
+ quadrant-3 Re-evaluate
+ quadrant-4 May be improved
+ Campaign A:::class1: [0.3, 0.6] radius: 20
+ Campaign B: [0.45, 0.23] color: #ff0000
+ Campaign C: [0.57, 0.69] stroke-color: #ff00ff
+ Campaign D:::class2: [0.78, 0.34] stroke-width: 3px
+ Campaign E:::class2: [0.40, 0.34] radius: 20, color: #ff0000, stroke-color: #ff00ff, stroke-width: 3px
+ Campaign F:::class1: [0.35, 0.78]
+ classDef class1 color: #908342, radius : 10, stroke-color: #310085, stroke-width: 10px
+ classDef class2 color: #f00fff, radius : 10
+ `
+ );
});
});
diff --git a/cypress/integration/rendering/requirement.spec.js b/cypress/integration/rendering/requirement.spec.js
index f33ae7a0cb..3434418483 100644
--- a/cypress/integration/rendering/requirement.spec.js
+++ b/cypress/integration/rendering/requirement.spec.js
@@ -44,6 +44,5 @@ describe('Requirement diagram', () => {
`,
{}
);
- cy.get('svg');
});
});
diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js
index 1285a0832d..f18e99abf8 100644
--- a/cypress/integration/rendering/sequencediagram.spec.js
+++ b/cypress/integration/rendering/sequencediagram.spec.js
@@ -1,8 +1,6 @@
-///
+ + | Dagre | +Dagre with rough | +ELK | +ELK with rough | +
---|---|---|---|---|
+
+
+
+
+
+ + flowchart LR + id1([This is the text in the box]) ++ |
+
+ +flowchart LR + id1([This is the text in the box]) + ++ |
+
+ +%%{init: {"look": "handDrawn"} }%% +flowchart LR + id1([This is the text in the box]) ++ |
+
+ +%%{init: {"handDrawn": false, "layout": "elk"} }%% +flowchart LR + id1([This is the text in the box]) ++ |
+
+ +%%{init: {"look": "handDrawn", "layout": "elk"} }%% +flowchart LR + id1([This is the text in the box]) ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart LR + id1[[This is the text in the box]] ++ |
+
+ +flowchart LR + id1[[This is the text in the box]] ++ |
+
+ +%%{init: {"look": "handDrawn"} }%% +flowchart LR + id1[[This is the text in the box]] ++ |
+
+ +%%{init: {"handDrawn": false, "layout": "elk"} }%% +flowchart LR + id1[[This is the text in the box]] ++ |
+
+ +%%{init: {"look": "handDrawn", "layout": "elk"} }%% +flowchart LR + id1[[This is the text in the box]] ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart LR + id1[(Database)] ++ |
+
+ + flowchart LR + id1[(Database)] ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart LR + id1[(Database)] ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart LR + id1[(Database)] ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart LR + id1[(Database)] ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart LR + id1((This is the text in the circle)) ++ |
+
+ + flowchart LR + id1((This is the text in the circle)) ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart LR + id1((This is the text in the circle)) ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart LR + id1((This is the text in the circle)) ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart LR + id1((This is the text in the circle)) ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart TD + id1(((This is the text in the circle))) ++ |
+
+ + flowchart TD + id1(((This is the text in the circle))) ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart TD + id1(((This is the text in the circle))) ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart TD + id1(((This is the text in the circle))) ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart TD + id1(((This is the text in the circle))) ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart LR + id1>This is the text in the box] ++ |
+
+ + flowchart LR + id1>This is the text in the box] ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart LR + id1>This is the text in the box] ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart LR + id1>This is the text in the box] ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart LR + id1>This is the text in the box] ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart LR + id1{This is the text in the box} ++ |
+
+ + flowchart LR + id1{This is the text in the box} ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart LR + id1{This is the text in the box} ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart LR + id1{This is the text in the box} ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart LR + id1{This is the text in the box} ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart LR + id1{{This is the text in the box}} ++ |
+
+ + flowchart LR + id1{{This is the text in the box}} ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart LR + id1{{This is the text in the box}} ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart LR + id1{{This is the text in the box}} ++ |
+ |
+ + | ||||
+
+
+
+
+
+ + flowchart TD + id1[/This is the text in the box/] ++ |
+
+ + flowchart TD + id1[/This is the text in the box/] ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart TD + id1[/This is the text in the box/] ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart TD + id1[/This is the text in the box/] ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart TD + id1[/This is the text in the box/] ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart TD + id1[\This is the text in the box\] ++ |
+
+ + flowchart TD + id1[\This is the text in the box\] ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart TD + id1[\This is the text in the box\] ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart TD + id1[\This is the text in the box\] ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart TD + id1[\This is the text in the box\] + ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart TD + A[/Christmas\] ++ |
+
+ + flowchart TD + A[/Christmas\] ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart TD + A[/Christmas\] ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart TD + A[/Christmas\] ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart TD + A[/Christmas\] ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart TD + A[\Christmas/] ++ |
+
+ + flowchart TD + A[\Christmas/] ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart TD + A[\Christmas/] ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart TD + A[\Christmas/] ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart TD + A[\Christmas/] ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart LR + id1(This is the text in the box) ++ |
+
+ + flowchart LR + id1(This is the text in the box) ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart LR + id1(This is the text in the box) ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart LR + id1(This is the text in the box) ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart LR + id1(This is the text in the box) ++ |
+
+ + | ||||
+
+
+
+
+
+ + flowchart LR + id1[This is the text in the box] ++ |
+
+ + flowchart LR + id1[This is the text in the box] ++ |
+
+ + %%{init: {"look": "handDrawn"} }%% + flowchart LR + id1[This is the text in the box] ++ |
+
+ + %%{init: {"handDrawn": false, "layout": "elk"} }%% + flowchart LR + id1[This is the text in the box] ++ |
+
+ + %%{init: {"look": "handDrawn", "layout": "elk"} }%% + flowchart LR + id1[This is the text in the box] ++ |
+
+ + |