mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-10-25 00:44:10 +02:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
			fix/flowch
			...
			gh-readonl
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ![github-merge-queue[bot]](/assets/img/avatar_default.png)  | 055faaa4cc | ||
|   | 130f77bcbd | ||
| ![renovate[bot]](/assets/img/avatar_default.png)  | 472a883c73 | 
| @@ -1,9 +1,3 @@ | ||||
| export interface PackageOptions { | ||||
|   name: string; | ||||
|   packageName: string; | ||||
|   file: string; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Shared common options for both ESBuild and Vite | ||||
|  */ | ||||
| @@ -28,19 +22,9 @@ export const packageOptions = { | ||||
|     packageName: 'mermaid-zenuml', | ||||
|     file: 'detector.ts', | ||||
|   }, | ||||
|   'mermaid-layout-elk': { | ||||
|     name: 'mermaid-layout-elk', | ||||
|     packageName: 'mermaid-layout-elk', | ||||
|     file: 'layouts.ts', | ||||
|   'mermaid-flowchart-elk': { | ||||
|     name: 'mermaid-flowchart-elk', | ||||
|     packageName: 'mermaid-flowchart-elk', | ||||
|     file: 'detector.ts', | ||||
|   }, | ||||
|   'mermaid-layout-tidy-tree': { | ||||
|     name: 'mermaid-layout-tidy-tree', | ||||
|     packageName: 'mermaid-layout-tidy-tree', | ||||
|     file: 'index.ts', | ||||
|   }, | ||||
|   examples: { | ||||
|     name: 'mermaid-examples', | ||||
|     packageName: 'examples', | ||||
|     file: 'index.ts', | ||||
|   }, | ||||
| } as const satisfies Record<string, PackageOptions>; | ||||
| } as const; | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| 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, | ||||
|   | ||||
| @@ -19,15 +19,12 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [ | ||||
|   'xyChart', | ||||
|   'requirement', | ||||
|   'mindmap', | ||||
|   'kanban', | ||||
|   'timeline', | ||||
|   'gitGraph', | ||||
|   'c4', | ||||
|   'sankey', | ||||
|   'block', | ||||
|   'packet', | ||||
|   'architecture', | ||||
|   'radar', | ||||
| ] as const; | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| /* eslint-disable no-console */ | ||||
| import { packageOptions } from './common.js'; | ||||
| import { execSync } from 'child_process'; | ||||
|  | ||||
| @@ -6,20 +5,11 @@ const buildType = (packageName: string) => { | ||||
|   console.log(`Building types for ${packageName}`); | ||||
|   try { | ||||
|     const out = execSync(`tsc -p ./packages/${packageName}/tsconfig.json --emitDeclarationOnly`); | ||||
|     if (out.length > 0) { | ||||
|       console.log(out.toString()); | ||||
|     } | ||||
|     out.length > 0 && console.log(out.toString()); | ||||
|   } catch (e) { | ||||
|     if (e.stdout.length > 0) { | ||||
|       console.error(e.stdout.toString()); | ||||
|     } | ||||
|     if (e.stderr.length > 0) { | ||||
|       console.error(e.stderr.toString()); | ||||
|     } | ||||
|     // Exit the build process if we are in CI | ||||
|     if (process.env.CI) { | ||||
|       throw new Error(`Failed to build types for ${packageName}`); | ||||
|     } | ||||
|     console.error(e); | ||||
|     e.stdout.length > 0 && console.error(e.stdout.toString()); | ||||
|     e.stderr.length > 0 && console.error(e.stderr.toString()); | ||||
|   } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,8 +0,0 @@ | ||||
| # Changesets | ||||
|  | ||||
| Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works | ||||
| with multi-package repos, or single-package repos to help you version and publish your code. You can | ||||
| find the full documentation for it [in our repository](https://github.com/changesets/changesets) | ||||
|  | ||||
| We have a quick list of common questions to get you started engaging with this project in | ||||
| [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) | ||||
| @@ -1,5 +0,0 @@ | ||||
| --- | ||||
| 'mermaid': patch | ||||
| --- | ||||
|  | ||||
| fix: Render newlines as spaces in class diagrams | ||||
| @@ -1,12 +0,0 @@ | ||||
| { | ||||
|   "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", | ||||
|   "changelog": ["@changesets/changelog-github", { "repo": "mermaid-js/mermaid" }], | ||||
|   "commit": false, | ||||
|   "fixed": [], | ||||
|   "linked": [], | ||||
|   "access": "public", | ||||
|   "baseBranch": "master", | ||||
|   "updateInternalDependencies": "patch", | ||||
|   "bumpVersionsWithWorkspaceProtocolOnly": true, | ||||
|   "ignore": ["@mermaid-js/docs", "@mermaid-js/webpack-test", "@mermaid-js/mermaid-example-diagram"] | ||||
| } | ||||
| @@ -1,5 +0,0 @@ | ||||
| --- | ||||
| 'mermaid': patch | ||||
| --- | ||||
|  | ||||
| fix: Handle arrows correctly when auto number is enabled | ||||
| @@ -1,5 +0,0 @@ | ||||
| --- | ||||
| 'mermaid': minor | ||||
| --- | ||||
|  | ||||
| Add IDs in architecture diagrams | ||||
| @@ -1,5 +0,0 @@ | ||||
| --- | ||||
| 'mermaid': patch | ||||
| --- | ||||
|  | ||||
| fix: Ensure edge label color is applied when using classDef with edge IDs | ||||
| @@ -1,5 +0,0 @@ | ||||
| --- | ||||
| 'mermaid': minor | ||||
| --- | ||||
|  | ||||
| feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`. | ||||
| @@ -1,7 +0,0 @@ | ||||
| --- | ||||
| 'mermaid': minor | ||||
| '@mermaid-js/layout-tidy-tree': minor | ||||
| '@mermaid-js/layout-elk': minor | ||||
| --- | ||||
|  | ||||
| feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes | ||||
| @@ -1,5 +0,0 @@ | ||||
| --- | ||||
| 'mermaid': patch | ||||
| --- | ||||
|  | ||||
| fix: Ensure flowchart htmlLabels resolution respects both global and flowchart config | ||||
| @@ -1,5 +0,0 @@ | ||||
| --- | ||||
| 'mermaid': minor | ||||
| --- | ||||
|  | ||||
| feat: Add IDs in architecture diagrams | ||||
| @@ -1,9 +0,0 @@ | ||||
| --- | ||||
| 'mermaid': patch | ||||
| --- | ||||
|  | ||||
| chore: revert marked dependency from ^15.0.7 to ^16.0.0 | ||||
|  | ||||
| - Reverted marked package version to ^16.0.0 for better compatibility | ||||
| - This is a dependency update that maintains API compatibility | ||||
| - All tests pass with the updated version | ||||
| @@ -13,7 +13,6 @@ bqstring | ||||
| BQUOTE | ||||
| bramp | ||||
| BRKT | ||||
| brotli | ||||
| callbackargs | ||||
| callbackname | ||||
| classdef | ||||
| @@ -26,10 +25,8 @@ concat | ||||
| controlx | ||||
| controly | ||||
| CSSCLASS | ||||
| curv | ||||
| CYLINDEREND | ||||
| CYLINDERSTART | ||||
| DAGA | ||||
| datakey | ||||
| DEND | ||||
| descr | ||||
| @@ -47,16 +44,15 @@ edgesep | ||||
| EMPTYSTR | ||||
| enddate | ||||
| ERDIAGRAM | ||||
| eslint | ||||
| flatmap | ||||
| forwardable | ||||
| frontmatter | ||||
| funs | ||||
| gantt | ||||
| GENERICTYPE | ||||
| getBoundarys | ||||
| grammr | ||||
| graphtype | ||||
| halign | ||||
| iife | ||||
| interp | ||||
| introdcued | ||||
| @@ -68,7 +64,6 @@ Kaufmann | ||||
| keyify | ||||
| LABELPOS | ||||
| LABELTYPE | ||||
| layoutstop | ||||
| lcov | ||||
| LEFTOF | ||||
| Lexa | ||||
| @@ -88,14 +83,12 @@ NODIR | ||||
| NSTR | ||||
| outdir | ||||
| Qcontrolx | ||||
| QSTR | ||||
| reinit | ||||
| rels | ||||
| reqs | ||||
| rewritelinks | ||||
| rgba | ||||
| RIGHTOF | ||||
| roughjs | ||||
| sankey | ||||
| sequencenumber | ||||
| shrc | ||||
| @@ -115,17 +108,13 @@ strikethrough | ||||
| stringifying | ||||
| struct | ||||
| STYLECLASS | ||||
| STYLEDEF | ||||
| STYLEOPTS | ||||
| subcomponent | ||||
| subcomponents | ||||
| subconfig | ||||
| SUBROUTINEEND | ||||
| SUBROUTINESTART | ||||
| Subschemas | ||||
| substr | ||||
| SVGG | ||||
| SVGSVG | ||||
| TAGEND | ||||
| TAGSTART | ||||
| techn | ||||
| @@ -136,13 +125,11 @@ titlevalue | ||||
| topbar | ||||
| TRAPEND | ||||
| TRAPSTART | ||||
| treemap | ||||
| ts-nocheck | ||||
| tsdoc | ||||
| typeof | ||||
| typestr | ||||
| unshift | ||||
| urlsafe | ||||
| verifymethod | ||||
| VERIFYMTHD | ||||
| WARN_DOCSDIR_DOESNT_MATCH | ||||
|   | ||||
| @@ -2,11 +2,7 @@ | ||||
| Ashish Jain | ||||
| cpettitt | ||||
| Dong Cai | ||||
| fourcube | ||||
| knsv | ||||
| Knut Sveidqvist | ||||
| Nikolay Rozhkov | ||||
| Peng Xiao | ||||
| Per Brolin | ||||
| Sidharth Vinod | ||||
| subhash-halder | ||||
| Vinod Sidharth | ||||
|   | ||||
| @@ -28,9 +28,6 @@ dictionaryDefinitions: | ||||
|   - name: suggestions | ||||
|     words: | ||||
|       - none | ||||
|       - disp | ||||
|       - subproc | ||||
|       - tria | ||||
|     suggestWords: | ||||
|       - seperator:separator | ||||
|       - vertice:vertex | ||||
|   | ||||
| @@ -20,19 +20,14 @@ dagre-d3 | ||||
| Deepdwn | ||||
| Docsify | ||||
| Docsy | ||||
| Doctave | ||||
| DokuWiki | ||||
| dompurify | ||||
| elkjs | ||||
| fcose | ||||
| fontawesome | ||||
| Fonticons | ||||
| Forgejo | ||||
| Foswiki | ||||
| Gitea | ||||
| graphlib | ||||
| Grav | ||||
| icones | ||||
| iconify | ||||
| Inkdrop | ||||
| jiti | ||||
| @@ -59,17 +54,13 @@ presetAttributify | ||||
| pyplot | ||||
| redmine | ||||
| rehype | ||||
| roughjs | ||||
| rscratch | ||||
| shiki | ||||
| Slidev | ||||
| sparkline | ||||
| sphinxcontrib | ||||
| ssim | ||||
| stylis | ||||
| Swimm | ||||
| tsbuildinfo | ||||
| tseslint | ||||
| Tuleap | ||||
| Typora | ||||
| unocss | ||||
|   | ||||
| @@ -1,33 +1,26 @@ | ||||
| Adamiecki | ||||
| arrowend | ||||
| Bendpoints | ||||
| bmatrix | ||||
| braintree | ||||
| catmull | ||||
| compositTitleSize | ||||
| cose | ||||
| curv | ||||
| doublecircle | ||||
| elem | ||||
| elems | ||||
| gantt | ||||
| gitgraph | ||||
| gzipped | ||||
| handDrawn | ||||
| kanban | ||||
| knsv | ||||
| Knut | ||||
| marginx | ||||
| marginy | ||||
| Markdownish | ||||
| mermaidchart | ||||
| mermaidjs | ||||
| mindmap | ||||
| mindmaps | ||||
| mrtree | ||||
| multigraph | ||||
| nodesep | ||||
| NOTEGROUP | ||||
| Pinterest | ||||
| procs | ||||
| rankdir | ||||
| ranksep | ||||
| rect | ||||
| @@ -36,6 +29,7 @@ sandboxed | ||||
| siebling | ||||
| statediagram | ||||
| substate | ||||
| Sveidqvist | ||||
| unfixable | ||||
| Viewbox | ||||
| viewports | ||||
|   | ||||
| @@ -1,8 +1 @@ | ||||
| BRANDES | ||||
| Buzan | ||||
| circo | ||||
| handDrawn | ||||
| KOEPF | ||||
| neato | ||||
| newbranch | ||||
| validify | ||||
|   | ||||
| @@ -1,18 +1,14 @@ | ||||
| import { build } from 'esbuild'; | ||||
| import { cp, mkdir, readFile, rename, writeFile } from 'node:fs/promises'; | ||||
| import { mkdir, writeFile } from 'node:fs/promises'; | ||||
| import { packageOptions } from '../.build/common.js'; | ||||
| import { generateLangium } from '../.build/generateLangium.js'; | ||||
| import type { MermaidBuildOptions } from './util.js'; | ||||
| import { defaultOptions, getBuildConfig } from './util.js'; | ||||
| import { MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js'; | ||||
|  | ||||
| const shouldVisualize = process.argv.includes('--visualize'); | ||||
|  | ||||
| const buildPackage = async (entryName: keyof typeof packageOptions) => { | ||||
|   const commonOptions: MermaidBuildOptions = { | ||||
|     ...defaultOptions, | ||||
|     options: packageOptions[entryName], | ||||
|   } as const; | ||||
|   const buildConfigs: MermaidBuildOptions[] = [ | ||||
|   const commonOptions = { ...defaultOptions, entryName } as const; | ||||
|   const buildConfigs = [ | ||||
|     // package.mjs | ||||
|     { ...commonOptions }, | ||||
|     // package.min.mjs | ||||
| @@ -31,27 +27,6 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => { | ||||
|       // mermaid.js | ||||
|       { ...iifeOptions }, | ||||
|       // mermaid.min.js | ||||
|       { ...iifeOptions, minify: true, metafile: shouldVisualize }, | ||||
|       // mermaid.tiny.min.js | ||||
|       { | ||||
|         ...iifeOptions, | ||||
|         minify: true, | ||||
|         includeLargeFeatures: false, | ||||
|         metafile: shouldVisualize, | ||||
|         sourcemap: false, | ||||
|       } | ||||
|     ); | ||||
|   } | ||||
|   if (entryName === 'mermaid-zenuml') { | ||||
|     const iifeOptions: MermaidBuildOptions = { | ||||
|       ...commonOptions, | ||||
|       format: 'iife', | ||||
|       globalName: 'mermaid-zenuml', | ||||
|     }; | ||||
|     buildConfigs.push( | ||||
|       // mermaid-zenuml.js | ||||
|       { ...iifeOptions }, | ||||
|       // mermaid-zenuml.min.js | ||||
|       { ...iifeOptions, minify: true, metafile: shouldVisualize } | ||||
|     ); | ||||
|   } | ||||
| @@ -60,11 +35,11 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => { | ||||
|  | ||||
|   if (shouldVisualize) { | ||||
|     for (const { metafile } of results) { | ||||
|       if (!metafile?.outputs) { | ||||
|       if (!metafile) { | ||||
|         continue; | ||||
|       } | ||||
|       const fileName = Object.keys(metafile.outputs) | ||||
|         .find((file) => !file.includes('chunks') && file.endsWith('js'))! | ||||
|         .filter((file) => !file.includes('chunks') && file.endsWith('js'))[0] | ||||
|         .replace('dist/', ''); | ||||
|       // Upload metafile into https://esbuild.github.io/analyze/ | ||||
|       await writeFile(`stats/${fileName}.meta.json`, JSON.stringify(metafile)); | ||||
| @@ -73,35 +48,18 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => { | ||||
| }; | ||||
|  | ||||
| const handler = (e) => { | ||||
|   // eslint-disable-next-line no-console | ||||
|   console.error(e); | ||||
|   process.exit(1); | ||||
| }; | ||||
|  | ||||
| const buildTinyMermaid = async () => { | ||||
|   await mkdir('./packages/tiny/dist', { recursive: true }); | ||||
|   await rename( | ||||
|     './packages/mermaid/dist/mermaid.tiny.min.js', | ||||
|     './packages/tiny/dist/mermaid.tiny.js' | ||||
|   ); | ||||
|   // Copy version from mermaid's package.json to tiny's package.json | ||||
|   const mermaidPkg = JSON.parse(await readFile('./packages/mermaid/package.json', 'utf8')); | ||||
|   const tinyPkg = JSON.parse(await readFile('./packages/tiny/package.json', 'utf8')); | ||||
|   tinyPkg.version = mermaidPkg.version; | ||||
|  | ||||
|   await writeFile('./packages/tiny/package.json', JSON.stringify(tinyPkg, null, 2) + '\n'); | ||||
|   await cp('./packages/mermaid/CHANGELOG.md', './packages/tiny/CHANGELOG.md'); | ||||
| }; | ||||
|  | ||||
| const main = async () => { | ||||
|   await generateLangium(); | ||||
|   await mkdir('stats', { recursive: true }); | ||||
|   await mkdir('stats').catch(() => {}); | ||||
|   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) { | ||||
|     await buildPackage(pkg).catch(handler); | ||||
|   } | ||||
|   await buildTinyMermaid(); | ||||
| }; | ||||
|  | ||||
| void main(); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { readFile } from 'node:fs/promises'; | ||||
| import { transformJison } from '../.build/jisonTransformer.js'; | ||||
| import type { Plugin } from 'esbuild'; | ||||
| import { Plugin } from 'esbuild'; | ||||
|  | ||||
| export const jisonPlugin: Plugin = { | ||||
|   name: 'jison', | ||||
|   | ||||
| @@ -1,52 +1,34 @@ | ||||
| /* eslint-disable no-console */ | ||||
| import chokidar from 'chokidar'; | ||||
| import cors from 'cors'; | ||||
| import { context } from 'esbuild'; | ||||
| import type { Request, Response } from 'express'; | ||||
| import express from 'express'; | ||||
| import { packageOptions } from '../.build/common.js'; | ||||
| import type { NextFunction, Request, Response } from 'express'; | ||||
| 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 { defaultOptions, getBuildConfig } from './util.js'; | ||||
| import { packageOptions } from '../.build/common.js'; | ||||
|  | ||||
| const configs = Object.values(packageOptions).map(({ packageName }) => | ||||
|   getBuildConfig({ | ||||
|     ...defaultOptions, | ||||
|     minify: false, | ||||
|     core: false, | ||||
|     options: packageOptions[packageName], | ||||
|   }) | ||||
|   getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: packageName }) | ||||
| ); | ||||
| const mermaidIIFEConfig = getBuildConfig({ | ||||
|   ...defaultOptions, | ||||
|   minify: false, | ||||
|   core: false, | ||||
|   options: packageOptions.mermaid, | ||||
|   entryName: 'mermaid', | ||||
|   format: 'iife', | ||||
| }); | ||||
| configs.push(mermaidIIFEConfig); | ||||
|  | ||||
| const contexts = await Promise.all( | ||||
|   configs.map(async (config) => ({ config, context: await context(config) })) | ||||
| ); | ||||
| const contexts = await Promise.all(configs.map((config) => context(config))); | ||||
|  | ||||
| let rebuildCounter = 1; | ||||
| const rebuildAll = async () => { | ||||
|   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); | ||||
|   console.time('Rebuild time'); | ||||
|   await Promise.all(contexts.map((ctx) => ctx.rebuild())).catch((e) => console.error(e)); | ||||
|   console.timeEnd('Rebuild time'); | ||||
| }; | ||||
|  | ||||
| let clients: { id: number; response: Response }[] = []; | ||||
| function eventsHandler(request: Request, response: Response) { | ||||
| function eventsHandler(request: Request, response: Response, next: NextFunction) { | ||||
|   const headers = { | ||||
|     'Content-Type': 'text/event-stream', | ||||
|     Connection: 'keep-alive', | ||||
| @@ -63,20 +45,19 @@ function eventsHandler(request: Request, response: Response) { | ||||
|   }); | ||||
| } | ||||
|  | ||||
| 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); | ||||
|   } | ||||
|   // eslint-disable-next-line @typescript-eslint/no-misused-promises | ||||
|   timeoutID = setTimeout(async () => { | ||||
|   timeoutId = setTimeout(async () => { | ||||
|     await rebuildAll(); | ||||
|     sendEventsToAll(); | ||||
|     timeoutID = undefined; | ||||
|     timeoutId = undefined; | ||||
|   }, 100); | ||||
| } | ||||
|  | ||||
| @@ -93,16 +74,15 @@ 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; | ||||
|       } | ||||
|       console.log(`${path} changed. Rebuilding...`); | ||||
|       if (path.endsWith('.langium')) { | ||||
|       if (/\.langium$/.test(path)) { | ||||
|         await generateLangium(); | ||||
|       } | ||||
|       console.log(`${path} changed. Rebuilding...`); | ||||
|       handleFileChange(); | ||||
|     }); | ||||
|  | ||||
| @@ -119,4 +99,4 @@ async function createServer() { | ||||
|   }); | ||||
| } | ||||
|  | ||||
| void createServer(); | ||||
| createServer(); | ||||
|   | ||||
| @@ -3,26 +3,24 @@ import { fileURLToPath } from 'url'; | ||||
| import type { BuildOptions } from 'esbuild'; | ||||
| import { readFileSync } from 'fs'; | ||||
| import jsonSchemaPlugin from './jsonSchemaPlugin.js'; | ||||
| import type { PackageOptions } from '../.build/common.js'; | ||||
| import { packageOptions } from '../.build/common.js'; | ||||
| import { jisonPlugin } from './jisonPlugin.js'; | ||||
|  | ||||
| const __dirname = fileURLToPath(new URL('.', import.meta.url)); | ||||
|  | ||||
| export interface MermaidBuildOptions extends BuildOptions { | ||||
| export interface MermaidBuildOptions { | ||||
|   minify: boolean; | ||||
|   core: boolean; | ||||
|   metafile: boolean; | ||||
|   format: 'esm' | 'iife'; | ||||
|   options: PackageOptions; | ||||
|   includeLargeFeatures: boolean; | ||||
|   entryName: keyof typeof packageOptions; | ||||
| } | ||||
|  | ||||
| export const defaultOptions: Omit<MermaidBuildOptions, 'entryName' | 'options'> = { | ||||
| export const defaultOptions: Omit<MermaidBuildOptions, 'entryName'> = { | ||||
|   minify: false, | ||||
|   metafile: false, | ||||
|   core: false, | ||||
|   format: 'esm', | ||||
|   includeLargeFeatures: true, | ||||
| } as const; | ||||
|  | ||||
| const buildOptions = (override: BuildOptions): BuildOptions => { | ||||
| @@ -41,18 +39,12 @@ const buildOptions = (override: BuildOptions): BuildOptions => { | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| const getFileName = ( | ||||
|   fileName: string, | ||||
|   { core, format, minify, includeLargeFeatures }: MermaidBuildOptions | ||||
| ) => { | ||||
| const getFileName = (fileName: string, { core, format, minify }: MermaidBuildOptions) => { | ||||
|   if (core) { | ||||
|     fileName += '.core'; | ||||
|   } else if (format === 'esm') { | ||||
|     fileName += '.esm'; | ||||
|   } | ||||
|   if (!includeLargeFeatures) { | ||||
|     fileName += '.tiny'; | ||||
|   } | ||||
|   if (minify) { | ||||
|     fileName += '.min'; | ||||
|   } | ||||
| @@ -60,29 +52,20 @@ const getFileName = ( | ||||
| }; | ||||
|  | ||||
| export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => { | ||||
|   const { | ||||
|     core, | ||||
|     format, | ||||
|     options: { name, file, packageName }, | ||||
|     globalName = 'mermaid', | ||||
|     includeLargeFeatures, | ||||
|     ...rest | ||||
|   } = options; | ||||
|  | ||||
|   const { core, entryName, metafile, format, minify } = options; | ||||
|   const external: string[] = ['require', 'fs', 'path']; | ||||
|   const { name, file, packageName } = packageOptions[entryName]; | ||||
|   const outFileName = getFileName(name, options); | ||||
|   const output: BuildOptions = buildOptions({ | ||||
|     ...rest, | ||||
|   let output: BuildOptions = buildOptions({ | ||||
|     absWorkingDir: resolve(__dirname, `../packages/${packageName}`), | ||||
|     entryPoints: { | ||||
|       [outFileName]: `src/${file}`, | ||||
|     }, | ||||
|     globalName, | ||||
|     metafile, | ||||
|     minify, | ||||
|     logLevel: 'info', | ||||
|     chunkNames: `chunks/${outFileName}/[name]-[hash]`, | ||||
|     define: { | ||||
|       // This needs to be stringified for esbuild | ||||
|       includeLargeFeatures: `${includeLargeFeatures}`, | ||||
|       'import.meta.vitest': 'undefined', | ||||
|     }, | ||||
|   }); | ||||
| @@ -101,12 +84,11 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => { | ||||
|   if (format === 'iife') { | ||||
|     output.format = 'iife'; | ||||
|     output.splitting = false; | ||||
|     const originalGlobalName = output.globalName ?? 'mermaid'; | ||||
|     output.globalName = `__esbuild_esm_mermaid_nm[${JSON.stringify(originalGlobalName)}]`; | ||||
|     output.globalName = '__esbuild_esm_mermaid'; | ||||
|     // Workaround for removing the .default access in esbuild IIFE. | ||||
|     // https://github.com/mermaid-js/mermaid/pull/4109#discussion_r1292317396 | ||||
|     output.footer = { | ||||
|       js: `globalThis[${JSON.stringify(originalGlobalName)}] = globalThis.${output.globalName}.default;`, | ||||
|       js: 'globalThis.mermaid = globalThis.__esbuild_esm_mermaid.default;', | ||||
|     }; | ||||
|     output.outExtension = { '.js': '.js' }; | ||||
|   } else { | ||||
|   | ||||
							
								
								
									
										1
									
								
								.eslintignore
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								.eslintignore
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| .gitignore | ||||
							
								
								
									
										190
									
								
								.eslintrc.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								.eslintrc.cjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | ||||
| 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/consistent-type-definitions': 'error', | ||||
|     '@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, | ||||
|       }, | ||||
|     }, | ||||
|   ], | ||||
| }; | ||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							| @@ -4,7 +4,7 @@ contact_links: | ||||
|     url: https://github.com/mermaid-js/mermaid/discussions | ||||
|     about: Ask the Community questions or share your own graphs in our discussions. | ||||
|   - name: Discord | ||||
|     url: https://discord.gg/sKeNQX4Wtj | ||||
|     url: https://discord.gg/AgrbSrBer3 | ||||
|     about: Join our Community on Discord for Help and a casual chat. | ||||
|   - name: Documentation | ||||
|     url: https://mermaid.js.org | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/theme_proposal.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/theme_proposal.yml
									
									
									
									
										vendored
									
									
								
							| @@ -29,7 +29,7 @@ body: | ||||
|       label: Colors | ||||
|       description: |- | ||||
|         A detailed list of the different colour values to use. | ||||
|         See the [list of currently used variable names](https://mermaid-js.github.io/mermaid/#/theming?id=theme-variables-reference-table) | ||||
|         A list of currently used variable names can be found [here](https://mermaid-js.github.io/mermaid/#/theming?id=theme-variables-reference-table) | ||||
|       placeholder: |- | ||||
|         - background: #f4f4f4 | ||||
|         - primaryColor: #fff4dd | ||||
|   | ||||
							
								
								
									
										21
									
								
								.github/lychee.toml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								.github/lychee.toml
									
									
									
									
										vendored
									
									
								
							| @@ -41,26 +41,7 @@ exclude = [ | ||||
| "https://bundlephobia.com", | ||||
|  | ||||
| # Chrome webstore migration issue. Temporary | ||||
| "https://chromewebstore.google.com", | ||||
|  | ||||
| # Drupal 403 | ||||
| "https://(www.)?drupal.org", | ||||
|  | ||||
| # Phbpp 403 | ||||
| "https://(www.)?phpbb.com", | ||||
|  | ||||
| # Swimm returns 404, even though the link is valid | ||||
| "https://docs.swimm.io", | ||||
|  | ||||
| # Certificate Error | ||||
| "https://noteshub.app", | ||||
|  | ||||
| # Timeout | ||||
| "https://huehive.co", | ||||
| "https://foswiki.org", | ||||
| "https://www.gnu.org", | ||||
| "https://redmine.org", | ||||
| "https://mermaid-preview.com" | ||||
| "https://chromewebstore.google.com" | ||||
| ] | ||||
|  | ||||
| # Exclude all private IPs from checking. | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
								
							| @@ -15,4 +15,4 @@ Make sure you | ||||
| - [ ] :book: have read the [contribution guidelines](https://mermaid.js.org/community/contributing.html) | ||||
| - [ ] :computer: have added necessary unit/e2e tests. | ||||
| - [ ] :notebook: have added documentation. Make sure [`MERMAID_RELEASE_VERSION`](https://mermaid.js.org/community/contributing.html#update-documentation) is used for all new features. | ||||
| - [ ] :butterfly: If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpm changeset` and following the prompts. Changesets that add features should be `minor` and those that fix bugs should be `patch`. Please prefix changeset messages with `feat:`, `fix:`, or `chore:`. | ||||
| - [ ] :bookmark: targeted `develop` branch | ||||
|   | ||||
							
								
								
									
										36
									
								
								.github/release-drafter.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								.github/release-drafter.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| name-template: '$NEXT_PATCH_VERSION' | ||||
| tag-template: '$NEXT_PATCH_VERSION' | ||||
| categories: | ||||
|   - title: '🚨 **Breaking Changes**' | ||||
|     labels: | ||||
|       - 'Breaking Change' | ||||
|   - title: '🚀 Features' | ||||
|     labels: | ||||
|       - 'Type: Enhancement' | ||||
|       - 'feature' # deprecated, new PRs shouldn't have this | ||||
|   - title: '🐛 Bug Fixes' | ||||
|     labels: | ||||
|       - 'Type: Bug / Error' | ||||
|       - 'fix' # deprecated, new PRs shouldn't have this | ||||
|   - title: '🧰 Maintenance' | ||||
|     labels: | ||||
|       - 'Type: Other' | ||||
|       - 'chore' # deprecated, new PRs shouldn't have this | ||||
|   - title: '⚡️ Performance' | ||||
|     labels: | ||||
|       - 'Type: Performance' | ||||
|   - title: '📚 Documentation' | ||||
|     labels: | ||||
|       - 'Area: Documentation' | ||||
| change-template: '- $TITLE (#$NUMBER) @$AUTHOR' | ||||
| sort-by: title | ||||
| sort-direction: ascending | ||||
| exclude-labels: | ||||
|   - 'Skip changelog' | ||||
| no-changes-template: 'This release contains minor changes and bugfixes.' | ||||
| template: | | ||||
|   # Release Notes | ||||
|  | ||||
|   $CHANGES | ||||
|  | ||||
|   🎉 **Thanks to all contributors helping with this release!** 🎉 | ||||
							
								
								
									
										2
									
								
								.github/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/stale.yml
									
									
									
									
										vendored
									
									
								
							| @@ -15,5 +15,5 @@ markComment: > | ||||
|   If you are still interested in this issue and it is still relevant you can comment to revive it. | ||||
| # Comment to post when closing a stale issue. Set to `false` to disable | ||||
| closeComment: > | ||||
|   This issue has been automatically closed due to a lack of activity.  | ||||
|   This issue has been been automatically closed due to a lack of activity.  | ||||
|   This is done to maintain a clean list of issues that the community is interested in developing. | ||||
|   | ||||
							
								
								
									
										45
									
								
								.github/workflows/autofix.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										45
									
								
								.github/workflows/autofix.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,45 +0,0 @@ | ||||
| name: autofix.ci # needed to securely identify the workflow | ||||
|  | ||||
| on: | ||||
|   pull_request: | ||||
|     branches-ignore: | ||||
|       - 'renovate/**' | ||||
| permissions: | ||||
|   contents: read | ||||
|  | ||||
| concurrency: ${{ github.workflow }}-${{ github.ref }} | ||||
|  | ||||
| jobs: | ||||
|   autofix: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|         # uses version from "packageManager" field in package.json | ||||
|  | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         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@635ffb0c9798bd160680f18fd73371e355b85f27 # main | ||||
							
								
								
									
										8
									
								
								.github/workflows/build-docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/build-docs.yml
									
									
									
									
										vendored
									
									
								
							| @@ -8,8 +8,6 @@ on: | ||||
|   pull_request: | ||||
|   merge_group: | ||||
|  | ||||
| concurrency: ${{ github.workflow }}-${{ github.ref }} | ||||
|  | ||||
| permissions: | ||||
|   contents: read | ||||
|  | ||||
| @@ -18,12 +16,12 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|         uses: actions/checkout@v4 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|  | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           cache: pnpm | ||||
|           node-version-file: '.node-version' | ||||
|   | ||||
							
								
								
									
										49
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| name: Build | ||||
|  | ||||
| on: | ||||
|   push: {} | ||||
|   merge_group: | ||||
|   pull_request: | ||||
|     types: | ||||
|       - opened | ||||
|       - synchronize | ||||
|       - ready_for_review | ||||
|  | ||||
| permissions: | ||||
|   contents: read | ||||
|  | ||||
| jobs: | ||||
|   build-mermaid: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|  | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|         # 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: Run Build | ||||
|         run: pnpm run build | ||||
|  | ||||
|       - name: Upload Mermaid Build as Artifact | ||||
|         uses: actions/upload-artifact@v4 | ||||
|         with: | ||||
|           name: mermaid-build | ||||
|           path: packages/mermaid/dist | ||||
|  | ||||
|       - name: Upload Mermaid Mindmap Build as Artifact | ||||
|         uses: actions/upload-artifact@v4 | ||||
|         with: | ||||
|           name: mermaid-mindmap-build | ||||
|           path: packages/mermaid-mindmap/dist | ||||
							
								
								
									
										2
									
								
								.github/workflows/check-readme-in-sync.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/check-readme-in-sync.yml
									
									
									
									
										vendored
									
									
								
							| @@ -18,7 +18,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout repository | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|         uses: actions/checkout@v4 | ||||
|  | ||||
|       - name: Check for difference in README.md and docs/README.md | ||||
|         run: | | ||||
|   | ||||
							
								
								
									
										26
									
								
								.github/workflows/checks.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.github/workflows/checks.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| on: | ||||
|   push: | ||||
|   merge_group: | ||||
|   pull_request: | ||||
|     types: | ||||
|       - opened | ||||
|       - synchronize | ||||
|       - ready_for_review | ||||
|  | ||||
| name: Static analysis on Test files | ||||
|  | ||||
| jobs: | ||||
|   check-tests: | ||||
|     runs-on: ubuntu-latest | ||||
|     name: check tests | ||||
|     if: github.repository_owner == 'mermaid-js' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - uses: testomatio/check-tests@stable | ||||
|         with: | ||||
|           framework: cypress | ||||
|           tests: './cypress/e2e/**/**.spec.js' | ||||
|           token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           has-tests-label: true | ||||
							
								
								
									
										11
									
								
								.github/workflows/codeql.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/codeql.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,9 +11,6 @@ on: | ||||
|       - synchronize | ||||
|       - ready_for_review | ||||
|  | ||||
| permissions: # added using https://github.com/step-security/secure-repo | ||||
|   contents: read | ||||
|  | ||||
| jobs: | ||||
|   analyze: | ||||
|     name: Analyze | ||||
| @@ -32,11 +29,11 @@ jobs: | ||||
|  | ||||
|     steps: | ||||
|       - name: Checkout repository | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|         uses: actions/checkout@v4 | ||||
|  | ||||
|       # Initializes the CodeQL tools for scanning. | ||||
|       - name: Initialize CodeQL | ||||
|         uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 | ||||
|         uses: github/codeql-action/init@v3 | ||||
|         with: | ||||
|           config-file: ./.github/codeql/codeql-config.yml | ||||
|           languages: ${{ matrix.language }} | ||||
| @@ -48,7 +45,7 @@ jobs: | ||||
|       # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java). | ||||
|       # If this step fails, then you should remove it and run the build manually (see below) | ||||
|       - name: Autobuild | ||||
|         uses: github/codeql-action/autobuild@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 | ||||
|         uses: github/codeql-action/autobuild@v3 | ||||
|  | ||||
|       # ℹ️ Command-line programs to run using the OS shell. | ||||
|       # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun | ||||
| @@ -62,4 +59,4 @@ jobs: | ||||
|       #   make release | ||||
|  | ||||
|       - name: Perform CodeQL Analysis | ||||
|         uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 | ||||
|         uses: github/codeql-action/analyze@v3 | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/dependency-review.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/dependency-review.yml
									
									
									
									
										vendored
									
									
								
							| @@ -15,6 +15,6 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: 'Checkout Repository' | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|         uses: actions/checkout@v4 | ||||
|       - name: 'Dependency Review' | ||||
|         uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0 | ||||
|         uses: actions/dependency-review-action@v4 | ||||
|   | ||||
							
								
								
									
										17
									
								
								.github/workflows/e2e-applitools.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.github/workflows/e2e-applitools.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,8 +11,6 @@ on: | ||||
|         default: master | ||||
|         description: 'Parent branch to use for PRs' | ||||
|  | ||||
| concurrency: ${{ github.workflow }}-${{ github.ref }} | ||||
|  | ||||
| permissions: | ||||
|   contents: read | ||||
|  | ||||
| @@ -23,37 +21,38 @@ env: | ||||
| jobs: | ||||
|   e2e-applitools: | ||||
|     runs-on: ubuntu-latest | ||||
|     container: | ||||
|       image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1 | ||||
|       options: --user 1001 | ||||
|     steps: | ||||
|       - if: ${{ ! env.USE_APPLI }} | ||||
|         name: Warn if not using Applitools | ||||
|         run: | | ||||
|           echo "::error,title=Not using Applitools::APPLITOOLS_API_KEY is empty, disabling Applitools for this run." | ||||
|  | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|       - uses: actions/checkout@v4 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|         # uses version from "packageManager" field in package.json | ||||
|  | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version-file: '.node-version' | ||||
|  | ||||
|       - if: ${{ env.USE_APPLI }} | ||||
|         name: Notify applitools of new batch | ||||
|         # Copied from docs https://applitools.com/docs/topics/integrations/github-integration-ci-setup.html | ||||
|         run: curl -L -d '' -X POST "$APPLITOOLS_SERVER_URL/api/externals/github/push?apiKey=$APPLITOOLS_API_KEY&CommitSha=$GITHUB_SHA&BranchName=${APPLITOOLS_BRANCH}$&ParentBranchName=$APPLITOOLS_PARENT_BRANCH" | ||||
|         env: | ||||
|           # e.g. mermaid-js/mermaid/my-branch | ||||
|           APPLITOOLS_BRANCH: ${{ github.repository }}/${{ github.ref_name }} | ||||
|           APPLITOOLS_PARENT_BRANCH: ${{ github.event.inputs.parent_branch }} | ||||
|           APPLITOOLS_API_KEY: ${{ secrets.APPLITOOLS_API_KEY }} | ||||
|           APPLITOOLS_SERVER_URL: 'https://eyesapi.applitools.com' | ||||
|         uses: wei/curl@012398a392d02480afa2720780031f8621d5f94c | ||||
|         with: | ||||
|           args: -X POST "$APPLITOOLS_SERVER_URL/api/externals/github/push?apiKey=$APPLITOOLS_API_KEY&CommitSha=$GITHUB_SHA&BranchName=${APPLITOOLS_BRANCH}$&ParentBranchName=$APPLITOOLS_PARENT_BRANCH" | ||||
|  | ||||
|       - name: Cypress run | ||||
|         uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 | ||||
|         uses: cypress-io/github-action@v4 | ||||
|         id: cypress | ||||
|         with: | ||||
|           start: pnpm run dev | ||||
|   | ||||
							
								
								
									
										70
									
								
								.github/workflows/e2e-timings.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								.github/workflows/e2e-timings.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,70 +0,0 @@ | ||||
| name: E2E - Generate Timings | ||||
|  | ||||
| on: | ||||
|   # run this workflow every night at 3am | ||||
|   schedule: | ||||
|     - cron: '28 3 * * *' | ||||
|   # or when the user triggers it from GitHub Actions page | ||||
|   workflow_dispatch: | ||||
|  | ||||
| concurrency: ${{ github.workflow }}-${{ github.ref }} | ||||
|  | ||||
| permissions: | ||||
|   contents: write | ||||
|   pull-requests: write | ||||
|  | ||||
| jobs: | ||||
|   timings: | ||||
|     runs-on: ubuntu-latest | ||||
|     container: | ||||
|       image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1 | ||||
|       options: --user 1001 | ||||
|     steps: | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         with: | ||||
|           node-version-file: '.node-version' | ||||
|       - name: Install dependencies | ||||
|         uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 | ||||
|         with: | ||||
|           runTests: false | ||||
|  | ||||
|       - name: Cypress run | ||||
|         uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 | ||||
|         id: cypress | ||||
|         with: | ||||
|           install: false | ||||
|           start: pnpm run dev:coverage | ||||
|           wait-on: 'http://localhost:9000' | ||||
|           browser: chrome | ||||
|           publish-summary: false | ||||
|         env: | ||||
|           VITEST_COVERAGE: true | ||||
|           CYPRESS_COMMIT: ${{ github.sha }} | ||||
|           SPLIT: 1 | ||||
|           SPLIT_INDEX: 0 | ||||
|           SPLIT_FILE: 'cypress/timings.json' | ||||
|  | ||||
|       - name: Compare timings | ||||
|         id: compare | ||||
|         run: | | ||||
|           OUTPUT=$(pnpm tsx scripts/compare-timings.ts) | ||||
|           echo "$OUTPUT" >> $GITHUB_STEP_SUMMARY | ||||
|  | ||||
|           echo "output<<EOF" >> $GITHUB_OUTPUT | ||||
|           echo "$OUTPUT" >> $GITHUB_OUTPUT | ||||
|           echo "EOF" >> $GITHUB_OUTPUT | ||||
|  | ||||
|       - name: Commit and create pull request | ||||
|         uses: peter-evans/create-pull-request@18e469570b1cf0dfc11d60ec121099f8ff3e617a | ||||
|         with: | ||||
|           add-paths: | | ||||
|             cypress/timings.json | ||||
|           commit-message: 'chore: update E2E timings' | ||||
|           branch: update-timings | ||||
|           title: Update E2E Timings | ||||
|           body: ${{ steps.compare.outputs.output }} | ||||
|           delete-branch: true | ||||
|           sign-commits: true | ||||
							
								
								
									
										123
									
								
								.github/workflows/e2e.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										123
									
								
								.github/workflows/e2e.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,16 +1,18 @@ | ||||
| # 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: | ||||
|   push: | ||||
|     branches: | ||||
|       - develop | ||||
|       - master | ||||
|       - release/** | ||||
|     branches-ignore: | ||||
|       - 'gh-readonly-queue/**' | ||||
|   pull_request: | ||||
|   merge_group: | ||||
|  | ||||
| concurrency: ${{ github.workflow }}-${{ github.ref }} | ||||
|  | ||||
| permissions: | ||||
|   contents: read | ||||
|  | ||||
| @@ -28,8 +30,6 @@ env: | ||||
|       ) ||  | ||||
|       github.event.before | ||||
|     }} | ||||
|   RUN_VISUAL_TEST: >- | ||||
|     ${{ github.repository == 'mermaid-js/mermaid' && (github.event_name != 'pull_request' || !startsWith(github.head_ref, 'renovate/')) }} | ||||
| jobs: | ||||
|   cache: | ||||
|     runs-on: ubuntu-latest | ||||
| @@ -37,29 +37,30 @@ jobs: | ||||
|       image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1 | ||||
|       options: --user 1001 | ||||
|     steps: | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version-file: '.node-version' | ||||
|       - name: Cache snapshots | ||||
|         id: cache-snapshot | ||||
|         uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 | ||||
|         uses: actions/cache@v4 | ||||
|         with: | ||||
|           save-always: true | ||||
|           path: ./cypress/snapshots | ||||
|           key: ${{ runner.os }}-snapshots-${{ env.targetHash }} | ||||
|  | ||||
|       # If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots. | ||||
|       - name: Switch to base branch | ||||
|         if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }} | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           ref: ${{ env.targetHash }} | ||||
|  | ||||
|       - name: Install dependencies | ||||
|         if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }} | ||||
|         uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 | ||||
|         uses: cypress-io/github-action@v6 | ||||
|         with: | ||||
|           # just perform install | ||||
|           runTests: false | ||||
| @@ -71,6 +72,16 @@ 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: | ||||
| @@ -80,28 +91,28 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         containers: [1, 2, 3, 4, 5] | ||||
|         containers: [1, 2, 3, 4] | ||||
|     steps: | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|       - uses: actions/checkout@v4 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|         # uses version from "packageManager" field in package.json | ||||
|  | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version-file: '.node-version' | ||||
|  | ||||
|       # These cached snapshots are downloaded, providing the reference snapshots. | ||||
|       - name: Cache snapshots | ||||
|         id: cache-snapshot | ||||
|         uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 | ||||
|         uses: actions/cache/restore@v4 | ||||
|         with: | ||||
|           path: ./cypress/snapshots | ||||
|           key: ${{ runner.os }}-snapshots-${{ env.targetHash }} | ||||
|  | ||||
|       - name: Install dependencies | ||||
|         uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 | ||||
|         uses: cypress-io/github-action@v6 | ||||
|         with: | ||||
|           runTests: false | ||||
|  | ||||
| @@ -117,8 +128,11 @@ jobs: | ||||
|       # Install NPM dependencies, cache them correctly | ||||
|       # and run all Cypress tests | ||||
|       - name: Cypress run | ||||
|         uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 | ||||
|         uses: cypress-io/github-action@v6 | ||||
|         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 ) }} | ||||
|         with: | ||||
|           install: false | ||||
|           start: pnpm run dev:coverage | ||||
| @@ -126,20 +140,15 @@ jobs: | ||||
|           browser: chrome | ||||
|           # Disable recording if we don't have an API key | ||||
|           # e.g. if this action was run from a fork | ||||
|           record: ${{ env.RUN_VISUAL_TEST == 'true' && secrets.CYPRESS_RECORD_KEY != '' }} | ||||
|           record: ${{ secrets.CYPRESS_RECORD_KEY != '' }} | ||||
|           parallel: ${{ secrets.CYPRESS_RECORD_KEY != '' }} | ||||
|         env: | ||||
|           ARGOS_PARALLEL: ${{ env.RUN_VISUAL_TEST == 'true' }} | ||||
|           ARGOS_PARALLEL_TOTAL: ${{ env.RUN_VISUAL_TEST == 'true' && strategy.job-total || 1 }} | ||||
|           ARGOS_PARALLEL_INDEX: ${{ env.RUN_VISUAL_TEST == 'true' && matrix.containers || 1 }} | ||||
|           CYPRESS_COMMIT: ${{ github.sha }} | ||||
|           CYPRESS_RECORD_KEY: ${{ env.RUN_VISUAL_TEST == 'true' && secrets.CYPRESS_RECORD_KEY || ''}} | ||||
|           SPLIT: ${{ strategy.job-total }} | ||||
|           SPLIT_INDEX: ${{ strategy.job-index }} | ||||
|           SPLIT_FILE: 'cypress/timings.json' | ||||
|           CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} | ||||
|           VITEST_COVERAGE: true | ||||
|           CYPRESS_COMMIT: ${{ github.sha }} | ||||
|  | ||||
|       - name: Upload Coverage to Codecov | ||||
|         uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1 | ||||
|         uses: codecov/codecov-action@v4 | ||||
|         # Run step only pushes to develop and pull_requests | ||||
|         if: ${{ steps.cypress.conclusion == 'success' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/develop')}} | ||||
|         with: | ||||
| @@ -149,3 +158,55 @@ 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 }}" | ||||
|   | ||||
							
								
								
									
										8
									
								
								.github/workflows/issue-triage.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/issue-triage.yml
									
									
									
									
										vendored
									
									
								
							| @@ -4,17 +4,11 @@ on: | ||||
|   issues: | ||||
|     types: [opened] | ||||
|  | ||||
| permissions: # added using https://github.com/step-security/secure-repo | ||||
|   contents: read | ||||
|  | ||||
| jobs: | ||||
|   triage: | ||||
|     permissions: | ||||
|       issues: write # for andymckay/labeler to label issues | ||||
|       pull-requests: write # for andymckay/labeler to label PRs | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # 1.0.4 | ||||
|       - uses: andymckay/labeler@1.0.4 | ||||
|         with: | ||||
|           repo-token: '${{ secrets.GITHUB_TOKEN }}' | ||||
|           add-labels: 'Status: Triage' | ||||
|   | ||||
							
								
								
									
										9
									
								
								.github/workflows/link-checker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/link-checker.yml
									
									
									
									
										vendored
									
									
								
							| @@ -19,9 +19,6 @@ on: | ||||
|     # * is a special character in YAML so you have to quote this string | ||||
|     - cron: '30 8 * * *' | ||||
|  | ||||
| permissions: # added using https://github.com/step-security/secure-repo | ||||
|   contents: read | ||||
|  | ||||
| jobs: | ||||
|   link-checker: | ||||
|     runs-on: ubuntu-latest | ||||
| @@ -29,17 +26,17 @@ jobs: | ||||
|       # lychee only uses the GITHUB_TOKEN to avoid rate-limiting | ||||
|       contents: read | ||||
|     steps: | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|       - uses: actions/checkout@v4 | ||||
|  | ||||
|       - name: Restore lychee cache | ||||
|         uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 | ||||
|         uses: actions/cache@v4 | ||||
|         with: | ||||
|           path: .lycheecache | ||||
|           key: cache-lychee-${{ github.sha }} | ||||
|           restore-keys: cache-lychee- | ||||
|  | ||||
|       - name: Link Checker | ||||
|         uses: lycheeverse/lychee-action@f613c4a64e50d792e0b31ec34bbcbba12263c6a6 # v2.3.0 | ||||
|         uses: lycheeverse/lychee-action@v1.9.3 | ||||
|         with: | ||||
|           args: >- | ||||
|             --config .github/lychee.toml | ||||
|   | ||||
							
								
								
									
										35
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							| @@ -4,32 +4,26 @@ on: | ||||
|   push: | ||||
|   merge_group: | ||||
|   pull_request: | ||||
|     types: | ||||
|       - opened | ||||
|       - synchronize | ||||
|       - ready_for_review | ||||
|   workflow_dispatch: | ||||
|  | ||||
| concurrency: ${{ github.workflow }}-${{ github.ref }} | ||||
|  | ||||
| permissions: | ||||
|   contents: write | ||||
|  | ||||
| jobs: | ||||
|   docker-lint: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|  | ||||
|       - uses: hadolint/hadolint-action@54c9adbab1582c2ef04b2016b760714a4bfde3cf # v3.1.0 | ||||
|         with: | ||||
|           verbose: true | ||||
|   lint: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|       - uses: actions/checkout@v4 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|         # uses version from "packageManager" field in package.json | ||||
|  | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           cache: pnpm | ||||
|           node-version-file: '.node-version' | ||||
| @@ -89,9 +83,14 @@ jobs: | ||||
|         continue-on-error: ${{ github.event_name == 'push' }} | ||||
|         run: pnpm run docs:verify | ||||
|  | ||||
|       - uses: testomatio/check-tests@0ea638fcec1820cf2e7b9854fdbdd04128a55bd4 # stable | ||||
|       - 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: | ||||
|           framework: cypress | ||||
|           tests: './cypress/e2e/**/**.spec.js' | ||||
|           token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           has-tests-label: true | ||||
|           message: 'Update docs' | ||||
|           add: 'docs/*' | ||||
|   | ||||
							
								
								
									
										28
									
								
								.github/workflows/pr-labeler.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								.github/workflows/pr-labeler.yml
									
									
									
									
										vendored
									
									
								
							| @@ -22,36 +22,10 @@ jobs: | ||||
|       pull-requests: write # write permission is required to label PRs | ||||
|     steps: | ||||
|       - name: Label PR | ||||
|         uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 | ||||
|         uses: release-drafter/release-drafter@v6 | ||||
|         with: | ||||
|           config-name: pr-labeler.yml | ||||
|           disable-autolabeler: false | ||||
|           disable-releaser: true | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|  | ||||
|       - name: Add "Sponsored by MermaidChart" label | ||||
|         uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 | ||||
|         with: | ||||
|           github-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           script: | | ||||
|             const prNumber = context.payload.pull_request.number; | ||||
|             const { data: commits } = await github.rest.pulls.listCommits({ | ||||
|               owner: context.repo.owner, | ||||
|               repo: context.repo.repo, | ||||
|               pull_number: prNumber, | ||||
|             }); | ||||
|  | ||||
|             const isSponsored = commits.every( | ||||
|               (c) => c.commit.author.email?.endsWith('@mermaidchart.com') | ||||
|             ); | ||||
|  | ||||
|             if (isSponsored) { | ||||
|               console.log('PR is sponsored. Adding label.'); | ||||
|               await github.rest.issues.addLabels({ | ||||
|                 owner: context.repo.owner, | ||||
|                 repo: context.repo.repo, | ||||
|                 issue_number: prNumber, | ||||
|                 labels: ['Sponsored by MermaidChart'], | ||||
|               }); | ||||
|             } | ||||
|   | ||||
							
								
								
									
										12
									
								
								.github/workflows/publish-docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/publish-docs.yml
									
									
									
									
										vendored
									
									
								
							| @@ -23,12 +23,12 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|         uses: actions/checkout@v4 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|  | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           cache: pnpm | ||||
|           node-version-file: '.node-version' | ||||
| @@ -37,13 +37,13 @@ jobs: | ||||
|         run: pnpm install --frozen-lockfile | ||||
|  | ||||
|       - name: Setup Pages | ||||
|         uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0 | ||||
|         uses: actions/configure-pages@v4 | ||||
|  | ||||
|       - name: Run Build | ||||
|         run: pnpm --filter mermaid run docs:build:vitepress | ||||
|  | ||||
|       - name: Upload artifact | ||||
|         uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 | ||||
|         uses: actions/upload-pages-artifact@v3 | ||||
|         with: | ||||
|           path: packages/mermaid/src/vitepress/.vitepress/dist | ||||
|  | ||||
| @@ -56,4 +56,4 @@ jobs: | ||||
|     steps: | ||||
|       - name: Deploy to GitHub Pages | ||||
|         id: deployment | ||||
|         uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 | ||||
|         uses: actions/deploy-pages@v4 | ||||
|   | ||||
							
								
								
									
										23
									
								
								.github/workflows/release-draft.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								.github/workflows/release-draft.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| name: Draft Release | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - master | ||||
|  | ||||
| permissions: | ||||
|   contents: read | ||||
|  | ||||
| jobs: | ||||
|   draft-release: | ||||
|     runs-on: ubuntu-latest | ||||
|     permissions: | ||||
|       contents: write # write permission is required to create a GitHub release | ||||
|       pull-requests: read # required to read PR titles/labels | ||||
|     steps: | ||||
|       - name: Draft Release | ||||
|         uses: release-drafter/release-drafter@v6 | ||||
|         with: | ||||
|           disable-autolabeler: true | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
| @@ -9,14 +9,14 @@ jobs: | ||||
|   publish-preview: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|       - uses: actions/checkout@v4 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|  | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           cache: pnpm | ||||
|           node-version-file: '.node-version' | ||||
| @@ -28,7 +28,7 @@ jobs: | ||||
|           CYPRESS_CACHE_FOLDER: .cache/Cypress | ||||
|  | ||||
|       - name: Install Json | ||||
|         run: npm i json@11.0.0 --global | ||||
|         run: npm i json --global | ||||
|  | ||||
|       - name: Publish | ||||
|         working-directory: ./packages/mermaid | ||||
|   | ||||
							
								
								
									
										43
									
								
								.github/workflows/release-preview.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								.github/workflows/release-preview.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,43 +0,0 @@ | ||||
| name: Preview release | ||||
|  | ||||
| on: | ||||
|   pull_request: | ||||
|     branches: [develop] | ||||
|     types: [opened, synchronize, labeled, ready_for_review] | ||||
|  | ||||
| concurrency: | ||||
|   group: ${{ github.workflow }}-${{ github.event.number }} | ||||
|   cancel-in-progress: true | ||||
|  | ||||
| permissions: | ||||
|   contents: read | ||||
|   actions: write | ||||
|  | ||||
| jobs: | ||||
|   preview: | ||||
|     if: ${{ github.repository_owner == 'mermaid-js' }} | ||||
|     runs-on: ubuntu-latest | ||||
|     permissions: | ||||
|       contents: read | ||||
|       id-token: write | ||||
|       issues: write | ||||
|       pull-requests: write | ||||
|     name: Publish preview release | ||||
|     timeout-minutes: 5 | ||||
|     steps: | ||||
|       - name: Checkout Repo | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|  | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         with: | ||||
|           cache: pnpm | ||||
|           node-version-file: '.node-version' | ||||
|  | ||||
|       - name: Install Packages | ||||
|         run: pnpm install --frozen-lockfile | ||||
|  | ||||
|       - name: Publish packages | ||||
|         run: pnpx pkg-pr-new publish --pnpm './packages/*' | ||||
							
								
								
									
										47
									
								
								.github/workflows/release-publish.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								.github/workflows/release-publish.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| name: Publish release | ||||
|  | ||||
| on: | ||||
|   release: | ||||
|     types: [published] | ||||
|  | ||||
| jobs: | ||||
|   publish: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: fregante/setup-git-user@v2 | ||||
|  | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|         # 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 | ||||
|           npm i json --global | ||||
|         env: | ||||
|           CYPRESS_CACHE_FOLDER: .cache/Cypress | ||||
|  | ||||
|       - name: Prepare release | ||||
|         run: | | ||||
|           VERSION=${GITHUB_REF:10} | ||||
|           echo "Preparing release $VERSION" | ||||
|           git checkout -t origin/release/$VERSION | ||||
|           npm version --no-git-tag-version --allow-same-version $VERSION | ||||
|           git add package.json | ||||
|           git commit -nm "Bump version $VERSION" | ||||
|           git checkout -t origin/master | ||||
|           git merge -m "Release $VERSION" --no-ff release/$VERSION | ||||
|           git push --no-verify | ||||
|  | ||||
|       - name: Publish | ||||
|         run: | | ||||
|           npm set //registry.npmjs.org/:_authToken $NPM_TOKEN | ||||
|           npm publish | ||||
|         env: | ||||
|           NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | ||||
							
								
								
									
										46
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,46 +0,0 @@ | ||||
| name: Release | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - master | ||||
|  | ||||
| concurrency: ${{ github.workflow }}-${{ github.ref }} | ||||
|  | ||||
| permissions: # added using https://github.com/step-security/secure-repo | ||||
|   contents: read | ||||
|  | ||||
| jobs: | ||||
|   release: | ||||
|     if: github.repository == 'mermaid-js/mermaid' | ||||
|     permissions: | ||||
|       contents: write # to create release (changesets/action) | ||||
|       id-token: write # OpenID Connect token needed for provenance | ||||
|       pull-requests: write # to create pull request (changesets/action) | ||||
|     name: Release | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout Repo | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|  | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         with: | ||||
|           cache: pnpm | ||||
|           node-version-file: '.node-version' | ||||
|  | ||||
|       - name: Install Packages | ||||
|         run: pnpm install --frozen-lockfile | ||||
|  | ||||
|       - name: Create Release Pull Request or Publish to npm | ||||
|         id: changesets | ||||
|         uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9 | ||||
|         with: | ||||
|           version: pnpm changeset:version | ||||
|           publish: pnpm changeset:publish | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|           NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | ||||
|           NPM_CONFIG_PROVENANCE: true | ||||
							
								
								
									
										37
									
								
								.github/workflows/scorecard.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								.github/workflows/scorecard.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,37 +0,0 @@ | ||||
| name: Scorecard supply-chain security | ||||
| on: | ||||
|   branch_protection_rule: | ||||
|   push: | ||||
|     branches: | ||||
|       - develop | ||||
|   schedule: | ||||
|     - cron: 29 15 * * 0 | ||||
| permissions: read-all | ||||
| jobs: | ||||
|   analysis: | ||||
|     name: Scorecard analysis | ||||
|     permissions: | ||||
|       id-token: write | ||||
|       security-events: write | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout code | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
|       - name: Run analysis | ||||
|         uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 | ||||
|         with: | ||||
|           results_file: results.sarif | ||||
|           results_format: sarif | ||||
|           publish_results: true | ||||
|       - name: Upload artifact | ||||
|         uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 | ||||
|         with: | ||||
|           name: SARIF file | ||||
|           path: results.sarif | ||||
|           retention-days: 5 | ||||
|       - name: Upload to code-scanning | ||||
|         uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 | ||||
|         with: | ||||
|           sarif_file: results.sarif | ||||
							
								
								
									
										12
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| @@ -9,13 +9,13 @@ jobs: | ||||
|   unit-test: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|       - uses: actions/checkout@v4 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|         # uses version from "packageManager" field in package.json | ||||
|  | ||||
|       - name: Setup Node.js | ||||
|         uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           cache: pnpm | ||||
|           node-version-file: '.node-version' | ||||
| @@ -38,12 +38,8 @@ jobs: | ||||
|         run: | | ||||
|           pnpm exec vitest run ./packages/mermaid/src/diagrams/gantt/ganttDb.spec.ts --coverage | ||||
|  | ||||
|       - name: Verify out-of-tree build with TypeScript | ||||
|         run: | | ||||
|           pnpm test:check:tsc | ||||
|  | ||||
|       - name: Upload Coverage to Codecov | ||||
|         uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1 | ||||
|         uses: codecov/codecov-action@v4 | ||||
|         # Run step only pushes to develop and pull_requests | ||||
|         if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/develop' }} | ||||
|         with: | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/unlock-reopened-issues.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/unlock-reopened-issues.yml
									
									
									
									
										vendored
									
									
								
							| @@ -8,6 +8,6 @@ jobs: | ||||
|   triage: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: Dunning-Kruger/unlock-issues@b06b7f7e5c3f2eaa1c6d5d89f40930e4d6d9699e # v1 | ||||
|       - uses: Dunning-Kruger/unlock-issues@v1 | ||||
|         with: | ||||
|           repo-token: '${{ secrets.GITHUB_TOKEN }}' | ||||
|   | ||||
							
								
								
									
										8
									
								
								.github/workflows/update-browserlist.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/update-browserlist.yml
									
									
									
									
										vendored
									
									
								
							| @@ -8,18 +8,18 @@ jobs: | ||||
|   update-browser-list: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: pnpm/action-setup@v2 | ||||
|       - run: npx update-browserslist-db@latest | ||||
|       - name: Commit changes | ||||
|         uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4 | ||||
|         uses: EndBug/add-and-commit@v9 | ||||
|         with: | ||||
|           author_name: ${{ github.actor }} | ||||
|           author_email: ${{ github.actor }}@users.noreply.github.com | ||||
|           message: 'chore: update browsers list' | ||||
|           push: false | ||||
|       - name: Create Pull Request | ||||
|         uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6 | ||||
|         uses: peter-evans/create-pull-request@v6 | ||||
|         with: | ||||
|           branch: update-browserslist | ||||
|           title: Update Browserslist | ||||
|   | ||||
							
								
								
									
										70
									
								
								.github/workflows/validate-lockfile.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								.github/workflows/validate-lockfile.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,70 +0,0 @@ | ||||
| name: Validate pnpm-lock.yaml | ||||
|  | ||||
| on: | ||||
|   pull_request: | ||||
|     paths: | ||||
|       - 'pnpm-lock.yaml' | ||||
|       - '**/package.json' | ||||
|       - '.github/workflows/validate-lockfile.yml' | ||||
|  | ||||
| jobs: | ||||
|   validate-lockfile: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout code | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|  | ||||
|       - name: Set up Node.js | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version: 20 | ||||
|  | ||||
|       - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | ||||
|  | ||||
|       - name: Validate pnpm-lock.yaml entries | ||||
|         id: validate # give this step an ID so we can reference its outputs | ||||
|         run: | | ||||
|           issues=() | ||||
|  | ||||
|           # 1) No tarball references | ||||
|           if grep -qF 'tarball:' pnpm-lock.yaml; then | ||||
|             issues+=("• Tarball references found (forbidden)") | ||||
|           fi | ||||
|  | ||||
|           # 2) No unwanted vitepress paths | ||||
|           if grep -qF 'packages/mermaid/src/vitepress' pnpm-lock.yaml; then | ||||
|             issues+=("• Disallowed path 'packages/mermaid/src/vitepress' present. Run \`rm -rf packages/mermaid/src/vitepress && pnpm install\` to regenerate.") | ||||
|           fi | ||||
|  | ||||
|           # 3) Lockfile only changes when package.json changes | ||||
|           git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }} > changed.txt | ||||
|           if grep -q '^pnpm-lock.yaml$' changed.txt && ! grep -q 'package.json' changed.txt; then | ||||
|             issues+=("• pnpm-lock.yaml changed without any package.json modification") | ||||
|           fi | ||||
|  | ||||
|           # If any issues, output them and fail | ||||
|           if [ ${#issues[@]} -gt 0 ]; then | ||||
|             # Use the new GITHUB_OUTPUT approach to set a multiline output | ||||
|             { | ||||
|               echo "errors<<EOF" | ||||
|               printf '%s\n' "${issues[@]}" | ||||
|               echo "EOF" | ||||
|             } >> $GITHUB_OUTPUT | ||||
|             exit 1 | ||||
|           fi | ||||
|  | ||||
|       - name: Comment on PR if validation failed | ||||
|         if: failure() | ||||
|         uses: peter-evans/create-or-update-comment@v4 | ||||
|         with: | ||||
|           token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           issue-number: ${{ github.event.pull_request.number }} | ||||
|           body: | | ||||
|             The following issue(s) were detected: | ||||
|             ${{ steps.validate.outputs.errors }} | ||||
|  | ||||
|             Please address these and push an update. | ||||
|  | ||||
|             _Posted automatically by GitHub Actions_ | ||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -4,7 +4,6 @@ node_modules/ | ||||
| coverage/ | ||||
| .idea/ | ||||
| .pnpm-store/ | ||||
| .instructions/ | ||||
|  | ||||
| dist | ||||
| v8-compile-cache-0 | ||||
| @@ -36,7 +35,7 @@ cypress/snapshots/ | ||||
| .tsbuildinfo | ||||
| tsconfig.tsbuildinfo | ||||
|  | ||||
| #knsv*.html | ||||
| knsv*.html | ||||
| local*.html | ||||
| stats/ | ||||
|  | ||||
| @@ -49,7 +48,6 @@ demos/dev/** | ||||
| !/demos/dev/example.html | ||||
| !/demos/dev/reload.js | ||||
| tsx-0/** | ||||
| vite.config.ts.timestamp-* | ||||
|  | ||||
| # autogenereated by langium-cli | ||||
| generated/ | ||||
| @@ -1,2 +0,0 @@ | ||||
| ignored: | ||||
|   - DL3002 # TODO: Last USER should not be root | ||||
| @@ -1,2 +1,4 @@ | ||||
| #!/usr/bin/env sh | ||||
| #!/bin/sh | ||||
| . "$(dirname "$0")/_/husky.sh" | ||||
|  | ||||
| NODE_OPTIONS="--max_old_space_size=8192" pnpm run pre-commit | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| 22.14.0 | ||||
| 20.12.2 | ||||
|   | ||||
| @@ -16,5 +16,3 @@ 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 | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| import type { InlineConfig } from 'vite'; | ||||
| import { build, type PluginOption } from 'vite'; | ||||
| import { build, InlineConfig, type PluginOption } from 'vite'; | ||||
| import { resolve } from 'path'; | ||||
| import { fileURLToPath } from 'url'; | ||||
| import jisonPlugin from './jisonPlugin.js'; | ||||
| @@ -47,10 +46,9 @@ 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]; | ||||
|   const output: OutputOptions = [ | ||||
|   let output: OutputOptions = [ | ||||
|     { | ||||
|       name, | ||||
|       format: 'esm', | ||||
| @@ -85,6 +83,7 @@ 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'], | ||||
| @@ -94,10 +93,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions) | ||||
|       }), | ||||
|       ...visualizerOptions(packageName, core), | ||||
|     ], | ||||
|     define: { | ||||
|       // Needs to be string | ||||
|       includeLargeFeatures: 'true', | ||||
|     }, | ||||
|   }; | ||||
|  | ||||
|   if (watch && config.build) { | ||||
| @@ -126,10 +121,10 @@ await generateLangium(); | ||||
|  | ||||
| if (watch) { | ||||
|   await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' })); | ||||
|   void build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' })); | ||||
|   build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' })); | ||||
|   if (!mermaidOnly) { | ||||
|     void build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' })); | ||||
|     void build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' })); | ||||
|     build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' })); | ||||
|     build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' })); | ||||
|   } | ||||
| } else if (visualize) { | ||||
|   await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' })); | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { PluginOption } from 'vite'; | ||||
| import { PluginOption } from 'vite'; | ||||
| import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js'; | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -23,9 +23,8 @@ 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`); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| void createServer(); | ||||
| createServer(); | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| ./packages/mermaid/CHANGELOG.md | ||||
							
								
								
									
										1005
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1005
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										15
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,13 +1,2 @@ | ||||
| FROM node:22.12.0-alpine3.19@sha256:40dc4b415c17b85bea9be05314b4a753f45a4e1716bb31c01182e6c53d51a654 | ||||
|  | ||||
| USER 0:0 | ||||
|  | ||||
| RUN corepack enable \ | ||||
|     && corepack enable pnpm | ||||
|  | ||||
| RUN apk add --no-cache git~=2.43.4 \ | ||||
|     && git config --add --system safe.directory /mermaid | ||||
|  | ||||
| ENV NODE_OPTIONS="--max_old_space_size=8192" | ||||
|  | ||||
| EXPOSE 9000 3333 | ||||
| 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 - | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| { | ||||
|   "drips": { | ||||
|     "ethereum": { | ||||
|       "ownedBy": "0x0831DDFe60d009d9448CC976157b539089aB821E" | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										44
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								README.md
									
									
									
									
									
								
							| @@ -15,7 +15,7 @@ Generate diagrams from markdown-like text. | ||||
| <a href="https://mermaid.live/"><b>Live Editor!</b></a> | ||||
| </p> | ||||
| <p align="center"> | ||||
|  <a href="https://mermaid.js.org">📖 Documentation</a> | <a href="https://mermaid.js.org/intro/">🚀 Getting Started</a> | <a href="https://www.jsdelivr.com/package/npm/mermaid">🌐 CDN</a> | <a href="https://discord.gg/sKeNQX4Wtj" title="Discord invite">🙌 Join Us</a> | ||||
|  <a href="https://mermaid.js.org">📖 Documentation</a> | <a href="https://mermaid.js.org/intro/">🚀 Getting Started</a> | <a href="https://www.jsdelivr.com/package/npm/mermaid">🌐 CDN</a> | <a href="https://discord.gg/AgrbSrBer3" title="Discord invite">🙌 Join Us</a> | ||||
| </p> | ||||
| <p align="center"> | ||||
| <a href="./README.zh-CN.md">简体中文</a> | ||||
| @@ -33,10 +33,8 @@ Try Live Editor previews of future releases: <a href="https://develop.git.mermai | ||||
| [](https://app.codecov.io/github/mermaid-js/mermaid/tree/develop) | ||||
| [](https://www.jsdelivr.com/package/npm/mermaid) | ||||
| [](https://www.npmjs.com/package/mermaid) | ||||
| [](https://discord.gg/sKeNQX4Wtj) | ||||
| [](https://discord.gg/AgrbSrBer3) | ||||
| [](https://twitter.com/mermaidjs_) | ||||
| [](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss) | ||||
| [](https://securityscorecards.dev/viewer/?uri=github.com/mermaid-js/mermaid) | ||||
|  | ||||
| <img src="./img/header.png" alt="" /> | ||||
|  | ||||
| @@ -44,7 +42,7 @@ Try Live Editor previews of future releases: <a href="https://develop.git.mermai | ||||
|  | ||||
| **Thanks to all involved, people committing pull requests, people answering questions! 🙏** | ||||
|  | ||||
| <a href="https://mermaid.js.org/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/intro/img/book-banner-post-release.jpg" alt='Banner for "The Official Guide to Mermaid.js" book'></a> | ||||
| <a href="https://mermaid.js.org/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/intro/img/book-banner-post-release.jpg" alt="Explore Mermaid.js in depth, with real-world examples, tips & tricks from the creator... The first official book on Mermaid is available for purchase. Check it out!"></a> | ||||
|  | ||||
| ## Table of content | ||||
|  | ||||
| @@ -83,10 +81,6 @@ You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include- | ||||
|  | ||||
| For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](https://mermaid.js.org/intro/getting-started.html), [Usage](https://mermaid.js.org/config/usage.html) and [Tutorials](https://mermaid.js.org/ecosystem/tutorials.html). | ||||
|  | ||||
| Our PR Visual Regression Testing is powered by [Argos](https://argos-ci.com/?utm_source=mermaid&utm_campaign=oss) with their generous Open Source plan. It makes the process of reviewing PRs with visual changes a breeze. | ||||
|  | ||||
| [](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss) | ||||
|  | ||||
| In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests. | ||||
|  | ||||
| <a href="https://applitools.com/"> | ||||
| @@ -253,34 +247,6 @@ pie | ||||
|  | ||||
| ### Git graph [experimental - <a href="https://mermaid.live/edit#pako:eNqNkMFugzAMhl8F-VyVAR1tOW_aA-zKxSSGRCMJCk6lCvHuNZPKZdM0n-zf3_8r8QIqaIIGMqnB8kfEybQ--y4VnLP8-9RF9Mpkmm40hmlnDKmvkPiH_kfS7nFo_VN0FAf6XwocQGgxa_nGsm1bYEOOWmik1dRjGrmF1q-Cpkkj07u2HCI0PY4zHQATh8-7V9BwTPSE3iwOEd1OjQE1iWkBvk_bzQY7s0Sq4Hs7bHqKo8iGeZqbPN_WR7mpSd1RHpvPVhuMbG7XOq_L-oJlRfW5wteq0qorrpe-PBW9Pr8UJcK6rg-BLYPQ">live editor</a>] | ||||
|  | ||||
| ``` | ||||
| gitGraph | ||||
|   commit | ||||
|   commit | ||||
|   branch develop | ||||
|   checkout develop | ||||
|   commit | ||||
|   commit | ||||
|   checkout main | ||||
|   merge develop | ||||
|   commit | ||||
|   commit | ||||
| ``` | ||||
|  | ||||
| ```mermaid | ||||
| gitGraph | ||||
|   commit | ||||
|   commit | ||||
|   branch develop | ||||
|   checkout develop | ||||
|   commit | ||||
|   commit | ||||
|   checkout main | ||||
|   merge develop | ||||
|   commit | ||||
|   commit | ||||
| ``` | ||||
|  | ||||
| ### Bar chart (using gantt chart) [<a href="https://mermaid.js.org/syntax/gantt.html">docs</a> - <a href="https://mermaid.live/edit#pako:eNptkU1vhCAQhv8KIenNugiI4rkf6bmXpvEyFVxJFDYyNt1u9r8X63Z7WQ9m5pknLzieaBeMpQ3dg0dsPUkPOhwteXZIXmJcbCT3xMAxkuh8Z8kIEclyMIB209fqKcwTICFvG4IvFy_oLrZ-g9F26ILfQgvNFN94VaRXQ1iWqpumZBcu1J8p1E1TXDx59eQNr5LyEqjJn6hv5QnGNlxevZJmdLLpy5xJSzut45biYCfb0iaVxvawjNjS1p-TCguG16PvaIPzYjO67e3BwX6GiTY9jPFKH43DMF_hGMDY1J4oHg-_f8hFTJFd8L3br3yZx4QHxENsdrt1nO8dDstH3oVpF50ZYMbhU6ud4qoGLqyqBJRCmO6j0HXPZdGbihUc6Pmc0QP49xD-b5X69ZQv2gjO81IwzWqhC1lKrjJ6pA3nVS7SMiVjrKirWlYp5fs3osgrWeo00lorLWvOzz8JVbXm">live editor</a>] | ||||
|  | ||||
| ``` | ||||
| @@ -447,7 +413,7 @@ For public sites, it can be precarious to retrieve text from users on the intern | ||||
|  | ||||
| As an extra level of security for sites with external users we are happy to introduce a new security level in which the diagram is rendered in a sandboxed iframe preventing javascript in the code from being executed. This is a great step forward for better security. | ||||
|  | ||||
| _Unfortunately you cannot have a cake and eat it at the same time which in this case means that some of the interactive functionality gets blocked along with the possible malicious code._ | ||||
| _Unfortunately you can not have a cake and eat it at the same time which in this case means that some of the interactive functionality gets blocked along with the possible malicious code._ | ||||
|  | ||||
| ## Reporting vulnerabilities | ||||
|  | ||||
| @@ -463,7 +429,7 @@ A quick note from Knut Sveidqvist: | ||||
| > | ||||
| > _Thank you to [Tyler Long](https://github.com/tylerlong) who has been a collaborator since April 2017._ | ||||
| > | ||||
| > _Thank you to the ever-growing list of [contributors](https://github.com/mermaid-js/mermaid/graphs/contributors) that brought the project this far!_ | ||||
| > _Thank you to the ever-growing list of [contributors](https://github.com/knsv/mermaid/graphs/contributors) that brought the project this far!_ | ||||
|  | ||||
| --- | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,7 @@ Mermaid | ||||
| <a href="https://mermaid.live/"><b>实时编辑器!</b></a> | ||||
| </p> | ||||
| <p align="center"> | ||||
|  <a href="https://mermaid.js.org">📖 文档</a> | <a href="https://mermaid.js.org/intro/">🚀 入门</a> | <a href="https://www.jsdelivr.com/package/npm/mermaid">🌐 CDN</a> | <a href="https://discord.gg/sKeNQX4Wtj" title="Discord invite">🙌 加入我们</a> | ||||
|  <a href="https://mermaid.js.org">📖 文档</a> | <a href="https://mermaid.js.org/intro/">🚀 入门</a> | <a href="https://www.jsdelivr.com/package/npm/mermaid">🌐 CDN</a> | <a href="https://discord.gg/AgrbSrBer3" title="Discord invite">🙌 加入我们</a> | ||||
| </p> | ||||
| <p align="center"> | ||||
| <a href="./README.md">English</a> | ||||
| @@ -34,7 +34,7 @@ Mermaid | ||||
| [](https://app.codecov.io/github/mermaid-js/mermaid/tree/develop) | ||||
| [](https://www.jsdelivr.com/package/npm/mermaid) | ||||
| [](https://www.npmjs.com/package/mermaid) | ||||
| [](https://discord.gg/sKeNQX4Wtj) | ||||
| [](https://discord.gg/AgrbSrBer3) | ||||
| [](https://twitter.com/mermaidjs_) | ||||
|  | ||||
| <img src="./img/header.png" alt="" /> | ||||
| @@ -43,13 +43,13 @@ Mermaid | ||||
|  | ||||
| **感谢所有参与进来提交 PR,解答疑问的人们! 🙏** | ||||
|  | ||||
| <a href="https://mermaid.js.org/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/intro/img/book-banner-post-release.jpg" alt='Banner for "The Official Guide to Mermaid.js" book'></a> | ||||
| <a href="https://mermaid.js.org/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/intro/img/book-banner-post-release.jpg" alt="Explore Mermaid.js in depth, with real-world examples, tips & tricks from the creator... The first official book on Mermaid is available for purchase. Check it out!"></a> | ||||
|  | ||||
| ## 关于 Mermaid | ||||
|  | ||||
| <!-- <Main description>   --> | ||||
|  | ||||
| Mermaid 是一个基于 JavaScript 的图表绘制工具,通过解析类 Markdown 的文本语法来实现图表的创建和动态修改。Mermaid 诞生的主要目的是让文档的更新能够及时跟上开发进度。 | ||||
| Mermaid 是一个基于 Javascript 的图表绘制工具,通过解析类 Markdown 的文本语法来实现图表的创建和动态修改。Mermaid 诞生的主要目的是让文档的更新能够及时跟上开发进度。 | ||||
|  | ||||
| > Doc-Rot 是 Mermaid 致力于解决的一个难题。 | ||||
|  | ||||
| @@ -358,7 +358,7 @@ _很不幸的是,鱼与熊掌不可兼得,在这个场景下它意味着在 | ||||
|  | ||||
| > _特别感谢 [d3](https://d3js.org/) 和 [dagre-d3](https://github.com/cpettitt/dagre-d3) 这两个优秀的项目,它们提供了图形布局和绘图工具库!_ > _同样感谢 [js-sequence-diagram](https://bramp.github.io/js-sequence-diagrams) 提供了时序图语法的使用。 感谢 Jessica Peter 提供了甘特图渲染的灵感。_ > _感谢 [Tyler Long](https://github.com/tylerlong) 从 2017 年四月开始成为了项目的合作者。_ | ||||
| > | ||||
| > _感谢越来越多的 [贡献者们](https://github.com/mermaid-js/mermaid/graphs/contributors),没有你们,就没有这个项目的今天!_ | ||||
| > _感谢越来越多的 [贡献者们](https://github.com/knsv/mermaid/graphs/contributors),没有你们,就没有这个项目的今天!_ | ||||
|  | ||||
| --- | ||||
|  | ||||
|   | ||||
							
								
								
									
										13
									
								
								__mocks__/d3.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								__mocks__/d3.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import { MockedD3 } from '../packages/mermaid/src/tests/MockedD3.js'; | ||||
|  | ||||
| export const select = function () { | ||||
|   return new MockedD3(); | ||||
| }; | ||||
|  | ||||
| export const selectAll = function () { | ||||
|   return new MockedD3(); | ||||
| }; | ||||
|  | ||||
| export const curveBasis = 'basis'; | ||||
| export const curveLinear = 'linear'; | ||||
| export const curveCardinal = 'cardinal'; | ||||
| @@ -1,10 +1,7 @@ | ||||
| import eyesPlugin from '@applitools/eyes-cypress'; | ||||
| import { registerArgosTask } from '@argos-ci/cypress/task'; | ||||
| import coverage from '@cypress/code-coverage/task.js'; | ||||
| import { defineConfig } from 'cypress'; | ||||
| import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin.js'; | ||||
| import cypressSplit from 'cypress-split'; | ||||
|  | ||||
| import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin'; | ||||
| import coverage from '@cypress/code-coverage/task'; | ||||
| import eyesPlugin from '@applitools/eyes-cypress'; | ||||
| export default eyesPlugin( | ||||
|   defineConfig({ | ||||
|     projectId: 'n2sma2', | ||||
| @@ -14,25 +11,16 @@ export default eyesPlugin( | ||||
|       specPattern: 'cypress/integration/**/*.{js,ts}', | ||||
|       setupNodeEvents(on, config) { | ||||
|         coverage(on, config); | ||||
|         cypressSplit(on, config); | ||||
|         on('before:browser:launch', (browser, launchOptions) => { | ||||
|           if (browser.name === 'chrome' && browser.isHeadless) { | ||||
|             launchOptions.args.push('--window-size=1440,1024', '--force-device-scale-factor=1'); | ||||
|           } | ||||
|           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.RUN_VISUAL_TEST === 'true'; | ||||
|  | ||||
|         if (config.env.useArgos) { | ||||
|           registerArgosTask(on, config, { | ||||
|             // Enable upload to Argos only when it runs on CI. | ||||
|             uploadToArgos: !!process.env.CI, | ||||
|           }); | ||||
|         } else { | ||||
|           addMatchImageSnapshotPlugin(on, config); | ||||
|         } | ||||
|         // do not forget to return the changed config object! | ||||
|         return config; | ||||
|       }, | ||||
|   | ||||
| @@ -14,7 +14,7 @@ interface CodeObject { | ||||
|   mermaid: CypressMermaidConfig; | ||||
| } | ||||
|  | ||||
| export const utf8ToB64 = (str: string): string => { | ||||
| const utf8ToB64 = (str: string): string => { | ||||
|   return Buffer.from(decodeURIComponent(encodeURIComponent(str))).toString('base64'); | ||||
| }; | ||||
|  | ||||
| @@ -22,21 +22,20 @@ const batchId: string = | ||||
|   'mermaid-batch-' + | ||||
|   (Cypress.env('useAppli') | ||||
|     ? Date.now().toString() | ||||
|     : (Cypress.env('CYPRESS_COMMIT') ?? Date.now().toString())); | ||||
|     : Cypress.env('CYPRESS_COMMIT') || Date.now().toString()); | ||||
|  | ||||
| export const mermaidUrl = ( | ||||
|   graphStr: string | string[], | ||||
|   options: CypressMermaidConfig, | ||||
|   api: boolean | ||||
| ): string => { | ||||
|   options.handDrawnSeed = 1; | ||||
|   const codeObject: CodeObject = { | ||||
|     code: graphStr, | ||||
|     mermaid: options, | ||||
|   }; | ||||
|   const objStr: string = JSON.stringify(codeObject); | ||||
|   let url = `http://localhost:9000/e2e.html?graph=${utf8ToB64(objStr)}`; | ||||
|   if (api && typeof graphStr === 'string') { | ||||
|   if (api) { | ||||
|     url = `http://localhost:9000/xss.html?graph=${graphStr}`; | ||||
|   } | ||||
|  | ||||
| @@ -55,13 +54,16 @@ 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?.noteFontFamily ?? 'courier', | ||||
|       noteFontFamily: | ||||
|         _options.sequence && _options.sequence.noteFontFamily | ||||
|           ? _options.sequence.noteFontFamily | ||||
|           : 'courier', | ||||
|       messageFontFamily: 'courier', | ||||
|     }, | ||||
|   }; | ||||
| @@ -72,7 +74,7 @@ export const imgSnapshotTest = ( | ||||
|  | ||||
| export const urlSnapshotTest = ( | ||||
|   url: string, | ||||
|   options: CypressMermaidConfig = {}, | ||||
|   options: CypressMermaidConfig, | ||||
|   _api = false, | ||||
|   validation?: any | ||||
| ): void => { | ||||
| @@ -93,22 +95,8 @@ export const openURLAndVerifyRendering = ( | ||||
|   options: CypressMermaidConfig, | ||||
|   validation?: any | ||||
| ): void => { | ||||
|   const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); | ||||
|  | ||||
|   cy.visit(url); | ||||
|   cy.window().should('have.property', 'rendered', true); | ||||
|   cy.get('svg').should('be.visible'); | ||||
|  | ||||
|   if (validation) { | ||||
|     cy.get('svg').should(validation); | ||||
|   } | ||||
|  | ||||
|   verifyScreenshot(name); | ||||
| }; | ||||
|  | ||||
| export const verifyScreenshot = (name: string): void => { | ||||
|   const useAppli: boolean = Cypress.env('useAppli'); | ||||
|   const useArgos: boolean = Cypress.env('useArgos'); | ||||
|   const name: string = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); | ||||
|  | ||||
|   if (useAppli) { | ||||
|     cy.log(`Opening eyes ${Cypress.spec.name} --- ${name}`); | ||||
| @@ -118,22 +106,22 @@ export const verifyScreenshot = (name: string): void => { | ||||
|       batchName: Cypress.spec.name, | ||||
|       batchId: batchId + Cypress.spec.name, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   cy.visit(url); | ||||
|   cy.window().should('have.property', 'rendered', true); | ||||
|   cy.get('svg').should('be.visible'); | ||||
|  | ||||
|   if (validation) { | ||||
|     cy.get('svg').should(validation); | ||||
|   } | ||||
|  | ||||
|   if (useAppli) { | ||||
|     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); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| export const verifyNumber = (value: number, expected: number, deltaPercent = 10): void => { | ||||
|   expect(value).to.be.within( | ||||
|     expected * (1 - deltaPercent / 100), | ||||
|     expected * (1 + deltaPercent / 100) | ||||
|   ); | ||||
| }; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { renderGraph, verifyScreenshot } from '../../helpers/util.ts'; | ||||
| import { renderGraph } from '../../helpers/util.ts'; | ||||
| describe('Configuration', () => { | ||||
|   describe('arrowMarkerAbsolute', () => { | ||||
|     it('should handle default value false of arrowMarkerAbsolute', () => { | ||||
| @@ -69,9 +69,7 @@ describe('Configuration', () => { | ||||
|           .and('include', 'url(#'); | ||||
|       }); | ||||
|     }); | ||||
|     // This has been broken for a long time, but something about the Cypress environment was | ||||
|     // rewriting the URL to be relative, causing the test to incorrectly pass. | ||||
|     it.skip('should handle arrowMarkerAbsolute explicitly set to "false" as false', () => { | ||||
|     it('should handle arrowMarkerAbsolute explicitly set to "false" as false', () => { | ||||
|       renderGraph( | ||||
|         `graph TD | ||||
|         A[Christmas] -->|Get money| B(Go shopping) | ||||
| @@ -98,12 +96,12 @@ describe('Configuration', () => { | ||||
|     it('should handle arrowMarkerAbsolute set to true', () => { | ||||
|       renderGraph( | ||||
|         `flowchart TD | ||||
|     A[Christmas] -->|Get money| B(Go shopping) | ||||
|     B --> C{Let me think} | ||||
|     C -->|One| D[Laptop] | ||||
|     C -->|Two| E[iPhone] | ||||
|     C -->|Three| F[fa:fa-car Car] | ||||
|     `, | ||||
|         A[Christmas] -->|Get money| B(Go shopping) | ||||
|         B --> C{Let me think} | ||||
|         C -->|One| D[Laptop] | ||||
|         C -->|Two| E[iPhone] | ||||
|         C -->|Three| F[fa:fa-car Car] | ||||
|         `, | ||||
|         { | ||||
|           arrowMarkerAbsolute: true, | ||||
|         } | ||||
| @@ -113,6 +111,7 @@ describe('Configuration', () => { | ||||
|         cy.get('path') | ||||
|           .first() | ||||
|           .should('have.attr', 'marker-end') | ||||
|           .should('exist') | ||||
|           .and('include', 'url(http://localhost'); | ||||
|       }); | ||||
|     }); | ||||
| @@ -120,7 +119,8 @@ describe('Configuration', () => { | ||||
|       const url = 'http://localhost:9000/regression/issue-1874.html'; | ||||
|       cy.visit(url); | ||||
|       cy.window().should('have.property', 'rendered', true); | ||||
|       verifyScreenshot( | ||||
|       cy.get('svg').should('be.visible'); | ||||
|       cy.matchImageSnapshot( | ||||
|         'configuration.spec-should-not-taint-initial-configuration-when-using-multiple-directives' | ||||
|       ); | ||||
|     }); | ||||
| @@ -145,7 +145,7 @@ describe('Configuration', () => { | ||||
|           // none of the diagrams should be error diagrams | ||||
|           expect($svg).to.not.contain('Syntax error'); | ||||
|         }); | ||||
|       verifyScreenshot( | ||||
|       cy.matchImageSnapshot( | ||||
|         'configuration.spec-should-not-render-error-diagram-if-suppressErrorRendering-is-set' | ||||
|       ); | ||||
|     }); | ||||
| @@ -162,7 +162,7 @@ describe('Configuration', () => { | ||||
|           // some of the diagrams should be error diagrams | ||||
|           expect($svg).to.contain('Syntax error'); | ||||
|         }); | ||||
|       verifyScreenshot( | ||||
|       cy.matchImageSnapshot( | ||||
|         'configuration.spec-should-render-error-diagram-if-suppressErrorRendering-is-not-set' | ||||
|       ); | ||||
|     }); | ||||
|   | ||||
							
								
								
									
										14
									
								
								cypress/integration/other/flowchart-elk.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								cypress/integration/other/flowchart-elk.spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| 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', | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -20,7 +20,7 @@ describe('Interaction', () => { | ||||
|     }); | ||||
|  | ||||
|     it('Graph: should handle a click on a node with a bound url', () => { | ||||
|       // When there is a URL, `cy.contains()` selects the `a` tag instead of the `span` tag. The .node is a child of `a`, so we have to use `find()` instead of `parent`. | ||||
|       // When there is a URL, cy.contains selects the a tag instead of the span. The .node is a child of a, so we have to use find instead of parent. | ||||
|       cy.contains('URLTest1').find('.node').click(); | ||||
|       cy.location().should(({ href }) => { | ||||
|         expect(href).to.eq('http://localhost:9000/empty.html'); | ||||
| @@ -146,7 +146,7 @@ describe('Interaction', () => { | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('Interaction - security level other, misspelling', () => { | ||||
|   describe('Interaction - security level other, missspelling', () => { | ||||
|     beforeEach(() => { | ||||
|       cy.visit('http://localhost:9000/click_security_other.html'); | ||||
|     }); | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { imgSnapshotTest, mermaidUrl, utf8ToB64 } from '../../helpers/util.ts'; | ||||
| import { mermaidUrl } from '../../helpers/util.ts'; | ||||
| describe('XSS', () => { | ||||
|   it('should handle xss in tags', () => { | ||||
|     const str = | ||||
| @@ -10,6 +10,7 @@ describe('XSS', () => { | ||||
|     cy.wait(1000).then(() => { | ||||
|       cy.get('.mermaid').should('exist'); | ||||
|     }); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('should not allow tags in the css', () => { | ||||
| @@ -136,42 +137,4 @@ 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'); | ||||
|   }); | ||||
|  | ||||
|   it('should sanitize icon labels in architecture diagrams', () => { | ||||
|     const str = JSON.stringify({ | ||||
|       code: `architecture-beta | ||||
|     group api(cloud)[API] | ||||
|     service db "<img src=x onerror=\\"xssAttack()\\">" [Database] in api`, | ||||
|     }); | ||||
|     imgSnapshotTest(utf8ToB64(str), {}, true); | ||||
|     cy.wait(1000); | ||||
|     cy.get('#the-malware').should('not.exist'); | ||||
|   }); | ||||
|  | ||||
|   it('should sanitize katex blocks', () => { | ||||
|     const str = JSON.stringify({ | ||||
|       code: `sequenceDiagram | ||||
|     participant A as Alice<img src="x" onerror="xssAttack()">$$\\text{Alice}$$ | ||||
|     A->>John: Hello John, how are you?`, | ||||
|     }); | ||||
|     imgSnapshotTest(utf8ToB64(str), {}, true); | ||||
|     cy.wait(1000); | ||||
|     cy.get('#the-malware').should('not.exist'); | ||||
|   }); | ||||
|  | ||||
|   it('should sanitize labels', () => { | ||||
|     const str = JSON.stringify({ | ||||
|       code: `erDiagram | ||||
|     "<img src=x onerror=xssAttack()>" ||--|| ENTITY2 : "<img src=x onerror=xssAttack()>" | ||||
|     `, | ||||
|     }); | ||||
|     imgSnapshotTest(utf8ToB64(str), {}, true); | ||||
|     cy.wait(1000); | ||||
|     cy.get('#the-malware').should('not.exist'); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -11,27 +11,6 @@ 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. | ||||
|   | ||||
| @@ -1,252 +0,0 @@ | ||||
| import { imgSnapshotTest, urlSnapshotTest } from '../../helpers/util.ts'; | ||||
|  | ||||
| describe.skip('architecture diagram', () => { | ||||
|   it('should render a simple architecture diagram with groups', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|                 group api(cloud)[API] | ||||
|  | ||||
|                 service db(database)[Database] in api | ||||
|                 service disk1(disk)[Storage] in api | ||||
|                 service disk2(disk)[Storage] in api | ||||
|                 service server(server)[Server] in api | ||||
|                 service gateway(internet)[Gateway]  | ||||
|  | ||||
|                 db L--R server | ||||
|                 disk1 T--B server | ||||
|                 disk2 T--B db | ||||
|                 server T--B gateway | ||||
|             ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render a simple architecture diagram with titleAndAccessibilities', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|           title Simple Architecture Diagram | ||||
|           accTitle: Accessibility Title | ||||
|           accDescr: Accessibility Description | ||||
|           group api(cloud)[API] | ||||
|  | ||||
|           service db(database)[Database] in api | ||||
|           service disk1(disk)[Storage] in api | ||||
|           service disk2(disk)[Storage] in api | ||||
|           service server(server)[Server] in api | ||||
|  | ||||
|           db:L -- R:server | ||||
|           disk1:T -- B:server | ||||
|           disk2:T -- B:db | ||||
|       ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render an architecture diagram with groups within groups', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|                 group api[API] | ||||
|                 group public[Public API] in api | ||||
|                 group private[Private API] in api | ||||
|          | ||||
|                 service serv1(server)[Server] in public | ||||
|          | ||||
|                 service serv2(server)[Server] in private | ||||
|                 service db(database)[Database] in private | ||||
|          | ||||
|                 service gateway(internet)[Gateway] in api | ||||
|          | ||||
|                 serv1 B--T serv2 | ||||
|                 serv2 L--R db | ||||
|                 serv1 L--R gateway | ||||
|             ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render an architecture diagram with the fallback icon', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|                 service unknown(iconnamedoesntexist)[Unknown Icon] | ||||
|             ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render an architecture diagram with split directioning', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|                 service db(database)[Database] | ||||
|                 service s3(disk)[Storage] | ||||
|                 service serv1(server)[Server 1] | ||||
|                 service serv2(server)[Server 2] | ||||
|                 service disk(disk)[Disk] | ||||
|          | ||||
|                 db L--R s3 | ||||
|                 serv1 L--T s3 | ||||
|                 serv2 L--B s3 | ||||
|                 serv1 T--B disk | ||||
|             ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render an architecture diagram with directional arrows', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|                 service servC(server)[Server 1] | ||||
|                 service servL(server)[Server 2] | ||||
|                 service servR(server)[Server 3] | ||||
|                 service servT(server)[Server 4] | ||||
|                 service servB(server)[Server 5] | ||||
|          | ||||
|                 servC (L--R) servL | ||||
|                 servC (R--L) servR | ||||
|                 servC (T--B) servT | ||||
|                 servC (B--T) servB | ||||
|          | ||||
|                 servL (T--L) servT | ||||
|                 servL (B--L) servB | ||||
|                 servR (T--R) servT | ||||
|                 servR (B--R) servB | ||||
|             ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render an architecture diagram with group edges', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|                 group left_group(cloud)[Left] | ||||
|                 group right_group(cloud)[Right] | ||||
|                 group top_group(cloud)[Top] | ||||
|                 group bottom_group(cloud)[Bottom] | ||||
|                 group center_group(cloud)[Center] | ||||
|          | ||||
|                 service left_disk(disk)[Disk] in left_group | ||||
|                 service right_disk(disk)[Disk] in right_group | ||||
|                 service top_disk(disk)[Disk] in top_group | ||||
|                 service bottom_disk(disk)[Disk] in bottom_group | ||||
|                 service center_disk(disk)[Disk] in center_group | ||||
|          | ||||
|                 left_disk{group} (R--L) center_disk{group} | ||||
|                 right_disk{group} (L--R) center_disk{group} | ||||
|                 top_disk{group} (B--T) center_disk{group} | ||||
|                 bottom_disk{group} (T--B) center_disk{group} | ||||
|             ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render an architecture diagram with edge labels', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|                 service servC(server)[Server 1] | ||||
|                 service servL(server)[Server 2] | ||||
|                 service servR(server)[Server 3] | ||||
|                 service servT(server)[Server 4] | ||||
|                 service servB(server)[Server 5] | ||||
|          | ||||
|                 servC L-[Label]-R servL | ||||
|                 servC R-[Label]-L servR | ||||
|                 servC T-[Label]-B servT | ||||
|                 servC B-[Label]-T servB | ||||
|          | ||||
|                 servL T-[Label]-L servT | ||||
|                 servL B-[Label]-L servB | ||||
|                 servR T-[Label]-R servT | ||||
|                 servR B-[Label]-R servB | ||||
|             ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render an architecture diagram with simple junction edges', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|                 service left_disk(disk)[Disk] | ||||
|                 service top_disk(disk)[Disk] | ||||
|                 service bottom_disk(disk)[Disk] | ||||
|                 service top_gateway(internet)[Gateway] | ||||
|                 service bottom_gateway(internet)[Gateway] | ||||
|                 junction juncC | ||||
|                 junction juncR | ||||
|          | ||||
|                 left_disk R--L juncC | ||||
|                 top_disk B--T juncC | ||||
|                 bottom_disk T--B juncC | ||||
|                 juncC R--L juncR | ||||
|                 top_gateway B--T juncR | ||||
|                 bottom_gateway T--B juncR | ||||
|             ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render an architecture diagram with complex junction edges', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|                 group left | ||||
|                 group right | ||||
|                 service left_disk(disk)[Disk] in left | ||||
|                 service top_disk(disk)[Disk] in left | ||||
|                 service bottom_disk(disk)[Disk] in left | ||||
|                 service top_gateway(internet)[Gateway] in right | ||||
|                 service bottom_gateway(internet)[Gateway] in right | ||||
|                 junction juncC in left | ||||
|                 junction juncR in right | ||||
|          | ||||
|                 left_disk R--L juncC | ||||
|                 top_disk B--T juncC | ||||
|                 bottom_disk T--B juncC | ||||
|          | ||||
|          | ||||
|                 top_gateway (B--T juncR | ||||
|                 bottom_gateway (T--B juncR | ||||
|          | ||||
|                 juncC{group} R--L) juncR{group} | ||||
|             ` | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('should render an architecture diagram with a reasonable height', () => { | ||||
|     imgSnapshotTest( | ||||
|       `architecture-beta | ||||
|               group federated(cloud)[Federated Environment] | ||||
|                   service server1(server)[System] in federated | ||||
|                   service edge(server)[Edge Device] in federated | ||||
|                   server1:R -- L:edge | ||||
|  | ||||
|               group on_prem(cloud)[Hub] | ||||
|                   service firewall(server)[Firewall Device] in on_prem | ||||
|                   service server(server)[Server] in on_prem | ||||
|                   firewall:R -- L:server | ||||
|  | ||||
|                   service db1(database)[db1] in on_prem | ||||
|                   service db2(database)[db2] in on_prem | ||||
|                   service db3(database)[db3] in on_prem | ||||
|                   service db4(database)[db4] in on_prem | ||||
|                   service db5(database)[db5] in on_prem | ||||
|                   service db6(database)[db6] in on_prem | ||||
|  | ||||
|                   junction mid in on_prem | ||||
|                   server:B -- T:mid | ||||
|  | ||||
|                   junction 1Leftofmid in on_prem | ||||
|                   1Leftofmid:R -- L:mid | ||||
|                   1Leftofmid:B -- T:db1 | ||||
|  | ||||
|                   junction 2Leftofmid in on_prem | ||||
|                   2Leftofmid:R -- L:1Leftofmid | ||||
|                   2Leftofmid:B -- T:db2 | ||||
|  | ||||
|                   junction 3Leftofmid in on_prem | ||||
|                   3Leftofmid:R -- L:2Leftofmid | ||||
|                   3Leftofmid:B -- T:db3 | ||||
|  | ||||
|                   junction 1RightOfMid in on_prem | ||||
|                   mid:R -- L:1RightOfMid | ||||
|                   1RightOfMid:B -- T:db4 | ||||
|                    | ||||
|                   junction 2RightOfMid in on_prem | ||||
|                   1RightOfMid:R -- L:2RightOfMid | ||||
|                   2RightOfMid:B -- T:db5         | ||||
|                    | ||||
|                   junction 3RightOfMid in on_prem | ||||
|                   2RightOfMid:R -- L:3RightOfMid | ||||
|                   3RightOfMid:B -- T:db6          | ||||
|  | ||||
|                   edge:R -- L:firewall | ||||
|       ` | ||||
|     ); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| // Skipped as the layout is not deterministic, and causes issues in E2E tests. | ||||
| describe.skip('architecture - external', () => { | ||||
|   it('should allow adding external icons', () => { | ||||
|     urlSnapshotTest('http://localhost:9000/architecture-external.html'); | ||||
|   }); | ||||
| }); | ||||
| @@ -14,9 +14,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL2: should handle columns statement in sub-blocks', () => { | ||||
|   it('BL2: should handle colums statement in sub-blocks', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|   id1["Hello"] | ||||
|   block | ||||
|     columns 3 | ||||
| @@ -30,9 +30,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL3: should align block widths and handle columns statement in sub-blocks', () => { | ||||
|   it('BL3: should align block widths and handle colums statement in sub-blocks', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|   block | ||||
|     columns 1 | ||||
|     id1 | ||||
| @@ -46,9 +46,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL4: should align block widths and handle columns statements in deeper sub-blocks then 1 level', () => { | ||||
|   it('BL4: should align block widths and handle colums statements in deeper sub-blocks then 1 level', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|   columns 1 | ||||
|   block | ||||
|     columns 1 | ||||
| @@ -66,9 +66,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL5: should align block widths and handle columns statements in deeper sub-blocks then 1 level (alt)', () => { | ||||
|   it('BL5: should align block widths and handle colums statements in deeper sub-blocks then 1 level (alt)', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|   columns 1 | ||||
|   block | ||||
|     id1 | ||||
| @@ -87,7 +87,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL6: should handle block arrows and spece statements', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|     columns 3 | ||||
|     space:3 | ||||
|     ida idb idc | ||||
| @@ -106,7 +106,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL7: should handle different types of edges', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|       columns 3 | ||||
|       A space:5 | ||||
|       A --o B | ||||
| @@ -119,7 +119,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL8: should handle sub-blocks without columns statements', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|       columns 2 | ||||
|       C A B | ||||
|       block | ||||
| @@ -133,7 +133,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL9: should handle edges from blocks in sub blocks to other blocks', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|       columns 3 | ||||
|       B space | ||||
|       block | ||||
| @@ -147,7 +147,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL10: should handle edges from composite blocks', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|       columns 3 | ||||
|       B space | ||||
|       block BL | ||||
| @@ -161,7 +161,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL11: should handle edges to composite blocks', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|       columns 3 | ||||
|       B space | ||||
|       block BL | ||||
| @@ -175,7 +175,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL12: edges should handle labels', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|       A | ||||
|       space | ||||
|       A -- "apa" --> E | ||||
| @@ -186,7 +186,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL13: should handle block arrows in different directions', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|       columns 3 | ||||
|       space blockArrowId1<["down"]>(down) space | ||||
|       blockArrowId2<["right"]>(right) blockArrowId3<["Sync"]>(x, y) blockArrowId4<["left"]>(left) | ||||
| @@ -199,7 +199,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL14: should style statements and class statements', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|     A | ||||
|     B | ||||
|     classDef blue fill:#66f,stroke:#333,stroke-width:2px; | ||||
| @@ -212,7 +212,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL15: width alignment - D and E should share available space', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|   block | ||||
|     D | ||||
|     E | ||||
| @@ -225,7 +225,7 @@ describe('Block diagram', () => { | ||||
|  | ||||
|   it('BL16: width alignment - C should be as wide as the composite block', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|   block | ||||
|     A("This is the text") | ||||
|     B | ||||
| @@ -236,9 +236,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL17: width alignment - blocks should be equal in width', () => { | ||||
|   it('BL16: width alignment - blocks shold be equal in width', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|     A("This is the text") | ||||
|     B | ||||
|     C | ||||
| @@ -247,9 +247,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL18: block types 1 - square, rounded and circle', () => { | ||||
|   it('BL17: block types 1 - square, rounded and circle', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|     A["square"] | ||||
|     B("rounded") | ||||
|     C(("circle")) | ||||
| @@ -258,9 +258,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL19: block types 2 - odd, diamond and hexagon', () => { | ||||
|   it('BL18: block types 2 - odd, diamond and hexagon', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|     A>"rect_left_inv_arrow"] | ||||
|     B{"diamond"} | ||||
|     C{{"hexagon"}} | ||||
| @@ -269,18 +269,18 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL20: block types 3 - stadium', () => { | ||||
|   it('BL19: block types 3 - stadium', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|     A(["stadium"]) | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL21: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => { | ||||
|   it('BL20: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|     A[/"lean right"/] | ||||
|     B[\"lean left"\] | ||||
|     C[/"trapezoid"\] | ||||
| @@ -290,9 +290,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL22: block types 1 - square, rounded and circle', () => { | ||||
|   it('BL21: block types 1 - square, rounded and circle', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|     A["square"] | ||||
|     B("rounded") | ||||
|     C(("circle")) | ||||
| @@ -301,9 +301,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL23: sizing - it should be possible to make a block wider', () => { | ||||
|   it('BL22: sizing - it should be possible to make a block wider', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|       A("rounded"):2 | ||||
|       B:2 | ||||
|       C | ||||
| @@ -312,9 +312,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL24: sizing - it should be possible to make a composite block wider', () => { | ||||
|   it('BL23: sizing - it should be possible to make a composite block wider', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|       block:2 | ||||
|         A | ||||
|       end | ||||
| @@ -324,9 +324,9 @@ describe('Block diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL25: block in the middle with space on each side', () => { | ||||
|   it('BL24: block in the middle with space on each side', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|         columns 3 | ||||
|         space | ||||
|         middle["In the middle"] | ||||
| @@ -335,9 +335,9 @@ describe('Block diagram', () => { | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|   it('BL26: space and an edge', () => { | ||||
|   it('BL25: space and an edge', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|   columns 5 | ||||
|     A space B | ||||
|     A --x B | ||||
| @@ -345,32 +345,31 @@ describe('Block diagram', () => { | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|   it('BL27: block sizes for regular blocks', () => { | ||||
|   it('BL26: block sizes for regular blocks', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|   columns 3 | ||||
|     a["A wide one"] b:2 c:2 d | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|   it('BL28: composite block with a set width - f should use the available space', () => { | ||||
|   it('BL27: composite block with a set width - f should use the available space', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|   columns 3 | ||||
|   a:3 | ||||
|   block:e:3 | ||||
|       f | ||||
|   end | ||||
|   g | ||||
|   `, | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL29: composite block with a set width - f and g should split the available space', () => { | ||||
|   it('BL23: composite block with a set width - f and g should split the available space', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block | ||||
|       `block-beta | ||||
|   columns 3 | ||||
|   a:3 | ||||
|   block:e:3 | ||||
| @@ -380,31 +379,7 @@ describe('Block diagram', () => { | ||||
|   h | ||||
|   i | ||||
|   j | ||||
|   `, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL30: block should overflow if too wide for columns', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block-beta | ||||
|   columns 2 | ||||
|   fit:2 | ||||
|   overflow:3 | ||||
|   short:1 | ||||
|   also_overflow:2 | ||||
| `, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('BL31: edge without arrow syntax should render with no arrowheads', () => { | ||||
|     imgSnapshotTest( | ||||
|       `block-beta | ||||
|   a | ||||
|   b | ||||
|   a --- b | ||||
| `, | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; | ||||
|  | ||||
| describe('C4 diagram', () => { | ||||
|   it('C4.1 should render a simple C4Context diagram', () => { | ||||
|   it('should render a simple C4Context diagram', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       C4Context | ||||
| @@ -30,8 +30,9 @@ describe('C4 diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|   it('C4.2 should render a simple C4Container diagram', () => { | ||||
|   it('should render a simple C4Container diagram', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       C4Container | ||||
| @@ -49,8 +50,9 @@ describe('C4 diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|   it('C4.3 should render a simple C4Component diagram', () => { | ||||
|   it('should render a simple C4Component diagram', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       C4Component | ||||
| @@ -67,8 +69,9 @@ describe('C4 diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|   it('C4.4 should render a simple C4Dynamic diagram', () => { | ||||
|   it('should render a simple C4Dynamic diagram', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       C4Dynamic | ||||
| @@ -90,8 +93,9 @@ describe('C4 diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|   it('C4.5 should render a simple C4Deployment diagram', () => { | ||||
|   it('should render a simple C4Deployment diagram', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       C4Deployment | ||||
| @@ -113,5 +117,6 @@ describe('C4 diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -76,7 +76,7 @@ describe('Class diagram V2', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('2.1 should render a simple class diagram with different visibilities', () => { | ||||
|   it('should render a simple class diagram with different visibilities', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|     classDiagram-v2 | ||||
| @@ -93,7 +93,7 @@ describe('Class diagram V2', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('3: should render multiple class diagrams', () => { | ||||
|   it('should render multiple class diagrams', () => { | ||||
|     imgSnapshotTest( | ||||
|       [ | ||||
|         ` | ||||
| @@ -581,63 +581,4 @@ class C13["With Città foreign language"] | ||||
|       { logLevel: 1, flowchart: { htmlLabels: false } } | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('renders a class diagram with a generic class in a namespace', () => { | ||||
|     const diagramDefinition = ` | ||||
|       classDiagram-v2 | ||||
|       namespace Company.Project.Module { | ||||
|         class GenericClass~T~ { | ||||
|           +addItem(item: T) | ||||
|           +getItem() T | ||||
|         } | ||||
|       } | ||||
|     `; | ||||
|  | ||||
|     imgSnapshotTest(diagramDefinition); | ||||
|   }); | ||||
|  | ||||
|   it('renders a class diagram with nested namespaces and relationships', () => { | ||||
|     const diagramDefinition = ` | ||||
|       classDiagram-v2 | ||||
|       namespace Company.Project.Module.SubModule { | ||||
|         class Report { | ||||
|           +generatePDF(data: List) | ||||
|           +generateCSV(data: List) | ||||
|         } | ||||
|       } | ||||
|       namespace Company.Project.Module { | ||||
|         class Admin { | ||||
|           +generateReport() | ||||
|         } | ||||
|       } | ||||
|       Admin --> Report : generates | ||||
|     `; | ||||
|  | ||||
|     imgSnapshotTest(diagramDefinition); | ||||
|   }); | ||||
|  | ||||
|   it('renders a class diagram with multiple classes and relationships in a namespace', () => { | ||||
|     const diagramDefinition = ` | ||||
|       classDiagram-v2 | ||||
|       namespace Company.Project.Module { | ||||
|         class User { | ||||
|           +login(username: String, password: String) | ||||
|           +logout() | ||||
|         } | ||||
|         class Admin { | ||||
|           +addUser(user: User) | ||||
|           +removeUser(user: User) | ||||
|           +generateReport() | ||||
|         } | ||||
|         class Report { | ||||
|           +generatePDF(reportData: List) | ||||
|           +generateCSV(reportData: List) | ||||
|         } | ||||
|       } | ||||
|       Admin --> User : manages | ||||
|       Admin --> Report : generates | ||||
|     `; | ||||
|  | ||||
|     imgSnapshotTest(diagramDefinition); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -32,6 +32,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       { logLevel: 1 } | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('2: should render a simple class diagrams with cardinality', () => { | ||||
| @@ -60,6 +61,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('3: should render a simple class diagram with different visibilities', () => { | ||||
| @@ -77,6 +79,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('4: should render a simple class diagram with comments', () => { | ||||
| @@ -106,6 +109,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('5: should render a simple class diagram with abstract method', () => { | ||||
| @@ -117,6 +121,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('6: should render a simple class diagram with static method', () => { | ||||
| @@ -128,6 +133,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('7: should render a simple class diagram with Generic class', () => { | ||||
| @@ -147,6 +153,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('8: should render a simple class diagram with Generic class and relations', () => { | ||||
| @@ -167,6 +174,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('9: should render a simple class diagram with clickable link', () => { | ||||
| @@ -188,6 +196,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('10: should render a simple class diagram with clickable callback', () => { | ||||
| @@ -209,6 +218,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('11: should render a simple class diagram with return type on method', () => { | ||||
| @@ -223,6 +233,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('12: should render a simple class diagram with generic types', () => { | ||||
| @@ -238,6 +249,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('13: should render a simple class diagram with css classes applied', () => { | ||||
| @@ -255,6 +267,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('14: should render a simple class diagram with css classes applied directly', () => { | ||||
| @@ -270,6 +283,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('15: should render a simple class diagram with css classes applied to multiple classes', () => { | ||||
| @@ -284,6 +298,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('16: should render multiple class diagrams', () => { | ||||
| @@ -336,6 +351,7 @@ describe('Class diagram', () => { | ||||
|       ], | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   // it('17: should render a class diagram when useMaxWidth is true (default)', () => { | ||||
| @@ -405,6 +421,7 @@ describe('Class diagram', () => { | ||||
|       `, | ||||
|       { logLevel: 1 } | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('should render class diagram with newlines in title', () => { | ||||
| @@ -422,6 +439,7 @@ describe('Class diagram', () => { | ||||
|           +quack() | ||||
|         } | ||||
|       `); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('should render class diagram with many newlines in title', () => { | ||||
| @@ -429,7 +447,7 @@ describe('Class diagram', () => { | ||||
|     classDiagram | ||||
|       class \`This\nTitle\nHas\nMany\nNewlines\` { | ||||
|         +String Also | ||||
|         -String Many | ||||
|         -Stirng Many | ||||
|         #int Members | ||||
|         +And() | ||||
|         -Many() | ||||
| @@ -443,7 +461,7 @@ describe('Class diagram', () => { | ||||
|     classDiagram | ||||
|       class \`This\nTitle\nHas\nMany\nNewlines\` { | ||||
|         +String Also | ||||
|         -String Many | ||||
|         -Stirng Many | ||||
|         #int Members | ||||
|         +And() | ||||
|         -Many() | ||||
| @@ -459,7 +477,7 @@ describe('Class diagram', () => { | ||||
|       namespace testingNamespace { | ||||
|       class \`This\nTitle\nHas\nMany\nNewlines\` { | ||||
|         +String Also | ||||
|         -String Many | ||||
|         -Stirng Many | ||||
|         #int Members | ||||
|         +And() | ||||
|         -Many() | ||||
| @@ -495,47 +513,4 @@ describe('Class diagram', () => { | ||||
|       cy.get('a').should('have.attr', 'target', '_blank').should('have.attr', 'rel', 'noopener'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('Include char sequence "graph" in text (#6795)', () => { | ||||
|     it('has a label with char sequence "graph"', () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|         classDiagram | ||||
|           class Person { | ||||
|             +String name | ||||
|             -Int id | ||||
|             #double age | ||||
|             +Text demographicProfile | ||||
|           } | ||||
|         `, | ||||
|         { flowchart: { defaultRenderer: 'elk' } } | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('should handle backticks for namespace and class names', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       classDiagram | ||||
|           namespace \`A::B\` { | ||||
|               class \`IPC::Sender\` | ||||
|           } | ||||
|           RenderProcessHost --|> \`IPC::Sender\` | ||||
|       `, | ||||
|       {} | ||||
|     ); | ||||
|     it('should handle an empty class body with empty braces', () => { | ||||
|       imgSnapshotTest( | ||||
|         ` classDiagram | ||||
|         class FooBase~T~ {} | ||||
|     class Bar { | ||||
|         +Zip | ||||
|         +Zap() | ||||
|     } | ||||
|     FooBase <|-- Ba | ||||
|         `, | ||||
|         { flowchart: { defaultRenderer: 'elk' } } | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -1,652 +0,0 @@ | ||||
| import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; | ||||
|  | ||||
| const testOptions = [ | ||||
|   { description: '', options: { logLevel: 1 } }, | ||||
|   { description: 'ELK: ', options: { logLevel: 1, layout: 'elk' } }, | ||||
|   { description: 'HD: ', options: { logLevel: 1, look: 'handDrawn' } }, | ||||
| ]; | ||||
|  | ||||
| describe('Entity Relationship Diagram Unified', () => { | ||||
|   testOptions.forEach(({ description, options }) => { | ||||
|     it(`${description}should render a simple ER diagram`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           CUSTOMER ||--o{ ORDER : places | ||||
|           ORDER ||--|{ LINE-ITEM : contains | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render a simple ER diagram without htmlLabels`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           CUSTOMER ||--o{ ORDER : places | ||||
|           ORDER ||--|{ LINE-ITEM : contains | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with a recursive relationship`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           CUSTOMER ||..o{ CUSTOMER : refers | ||||
|           CUSTOMER ||--o{ ORDER : places | ||||
|           ORDER ||--|{ LINE-ITEM : contains | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with multiple relationships between the same two entities`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           CUSTOMER ||--|{ ADDRESS : "invoiced at" | ||||
|           CUSTOMER ||--|{ ADDRESS : "receives goods at" | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render a cyclical ER diagram`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           A ||--|{ B : likes | ||||
|           B ||--|{ C : likes | ||||
|           C ||--|{ A : likes | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render a not-so-simple ER diagram`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           CUSTOMER }|..|{ DELIVERY-ADDRESS : has | ||||
|           CUSTOMER ||--o{ ORDER : places | ||||
|           CUSTOMER ||--o{ INVOICE : "liable for" | ||||
|           DELIVERY-ADDRESS ||--o{ ORDER : receives | ||||
|           INVOICE ||--|{ ORDER : covers | ||||
|           ORDER ||--|{ ORDER-ITEM : includes | ||||
|           PRODUCT-CATEGORY ||--|{ PRODUCT : contains | ||||
|           PRODUCT ||--o{ ORDER-ITEM : "ordered in" | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render a not-so-simple ER diagram without htmlLabels`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           CUSTOMER }|..|{ DELIVERY-ADDRESS : has | ||||
|           CUSTOMER ||--o{ ORDER : places | ||||
|           CUSTOMER ||--o{ INVOICE : "liable for" | ||||
|           DELIVERY-ADDRESS ||--o{ ORDER : receives | ||||
|           INVOICE ||--|{ ORDER : covers | ||||
|           ORDER ||--|{ ORDER-ITEM : includes | ||||
|           PRODUCT-CATEGORY ||--|{ PRODUCT : contains | ||||
|           PRODUCT ||--o{ ORDER-ITEM : "ordered in" | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render multiple ER diagrams`, () => { | ||||
|       imgSnapshotTest( | ||||
|         [ | ||||
|           ` | ||||
|       erDiagram | ||||
|           CUSTOMER ||--o{ ORDER : places | ||||
|           ORDER ||--|{ LINE-ITEM : contains | ||||
|         `, | ||||
|           ` | ||||
|       erDiagram | ||||
|           CUSTOMER ||--o{ ORDER : places | ||||
|           ORDER ||--|{ LINE-ITEM : contains | ||||
|         `, | ||||
|         ], | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with blank or empty labels`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           BOOK }|..|{ AUTHOR : "" | ||||
|           BOOK }|..|{ GENRE : " " | ||||
|           AUTHOR }|..|{ GENRE : "  " | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities that have no relationships`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|           DEAD_PARROT | ||||
|           HERMIT | ||||
|           RECLUSE | ||||
|           SOCIALITE }o--o{ SOCIALITE : "interacts with" | ||||
|           RECLUSE }o--o{ SOCIALITE : avoids | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with and without attributes`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|           BOOK { string title } | ||||
|           AUTHOR }|..|{ BOOK : writes | ||||
|           BOOK { float price } | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with generic and array attributes`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|           BOOK { | ||||
|             string title | ||||
|             string[] authors | ||||
|             type~T~ type | ||||
|           } | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with generic and array attributes without htmlLabels`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|           BOOK { | ||||
|             string title | ||||
|             string[] authors | ||||
|             type~T~ type | ||||
|           } | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with length in attributes type`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|           CLUSTER { | ||||
|             varchar(99) name | ||||
|             string(255) description | ||||
|           } | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with length in attributes type without htmlLabels`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|           CLUSTER { | ||||
|             varchar(99) name | ||||
|             string(255) description | ||||
|           } | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities and attributes with big and small entity names`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|           PRIVATE_FINANCIAL_INSTITUTION { | ||||
|             string name | ||||
|             int    turnover | ||||
|           } | ||||
|           PRIVATE_FINANCIAL_INSTITUTION ||..|{ EMPLOYEE : employs | ||||
|           EMPLOYEE { bool officer_of_firm } | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities and attributes with big and small entity names without htmlLabels`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|           PRIVATE_FINANCIAL_INSTITUTION { | ||||
|             string name | ||||
|             int    turnover | ||||
|           } | ||||
|           PRIVATE_FINANCIAL_INSTITUTION ||..|{ EMPLOYEE : employs | ||||
|           EMPLOYEE { bool officer_of_firm } | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with attributes that begin with asterisk`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           BOOK { | ||||
|             int         *id | ||||
|             string      name | ||||
|             varchar(99) summary | ||||
|           } | ||||
|           BOOK }o..o{ STORE : soldBy | ||||
|           STORE { | ||||
|             int         *id | ||||
|             string      name | ||||
|             varchar(50) address | ||||
|           } | ||||
|           `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with attributes that begin with asterisk without htmlLabels`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           BOOK { | ||||
|             int         *id | ||||
|             string      name | ||||
|             varchar(99) summary | ||||
|           } | ||||
|           BOOK }o..o{ STORE : soldBy | ||||
|           STORE { | ||||
|             int         *id | ||||
|             string      name | ||||
|             varchar(50) address | ||||
|           } | ||||
|           `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with keys`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME { | ||||
|           string name PK | ||||
|         } | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes | ||||
|         BOOK { | ||||
|             float price | ||||
|             string author FK | ||||
|             string title PK | ||||
|           } | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with keys without htmlLabels`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME { | ||||
|           string name PK | ||||
|         } | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes | ||||
|         BOOK { | ||||
|             float price | ||||
|             string author FK | ||||
|             string title PK | ||||
|           } | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with comments`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME { | ||||
|           string name "comment" | ||||
|         } | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes | ||||
|         BOOK { | ||||
|             string author | ||||
|             string title "author comment" | ||||
|             float price "price comment" | ||||
|           } | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with comments without htmlLabels`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME { | ||||
|           string name "comment" | ||||
|         } | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes | ||||
|         BOOK { | ||||
|             string author | ||||
|             string title "author comment" | ||||
|             float price "price comment" | ||||
|           } | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with keys and comments`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME { | ||||
|           string name PK "comment" | ||||
|         } | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes | ||||
|         BOOK { | ||||
|             string description | ||||
|             float price "price comment" | ||||
|             string title PK "title comment" | ||||
|             string author FK | ||||
|           } | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with keys and comments without htmlLabels`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME { | ||||
|           string name PK "comment" | ||||
|         } | ||||
|         AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes | ||||
|         BOOK { | ||||
|             string description | ||||
|             float price "price comment" | ||||
|             string title PK "title comment" | ||||
|             string author FK | ||||
|           } | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with aliases`, () => { | ||||
|       renderGraph( | ||||
|         ` | ||||
|       erDiagram | ||||
|         T1 one or zero to one or more T2 : test | ||||
|         T2 one or many optionally to zero or one T3 : test | ||||
|         T3 zero or more to zero or many T4 : test | ||||
|         T4 many(0) to many(1) T5 : test | ||||
|         T5 many optionally to one T6 : test | ||||
|         T6 only one optionally to only one T1 : test | ||||
|         T4 0+ to 1+ T6 : test | ||||
|         T1 1 to 1 T3 : test | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render a simple ER diagram with a title`, () => { | ||||
|       imgSnapshotTest( | ||||
|         `--- | ||||
|   title: simple ER diagram | ||||
|   --- | ||||
|   erDiagram | ||||
|   CUSTOMER ||--o{ ORDER : places | ||||
|   ORDER ||--|{ LINE-ITEM : contains | ||||
|   `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with entity name aliases`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|         p[Person] { | ||||
|           varchar(64) firstName | ||||
|           varchar(64) lastName | ||||
|         } | ||||
|         c["Customer Account"] { | ||||
|           varchar(128) email | ||||
|         } | ||||
|         p ||--o| c : has | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render relationship labels with line breaks`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|         p[Person] { | ||||
|             string firstName | ||||
|             string lastName | ||||
|         } | ||||
|         a["Customer Account"] { | ||||
|             string email | ||||
|         } | ||||
|    | ||||
|         b["Customer Account Secondary"] { | ||||
|           string email | ||||
|         } | ||||
|          | ||||
|         c["Customer Account Tertiary"] { | ||||
|           string email | ||||
|         } | ||||
|          | ||||
|         d["Customer Account Nth"] { | ||||
|           string email | ||||
|         } | ||||
|    | ||||
|         p ||--o| a : "has<br />one" | ||||
|         p ||--o| b : "has<br />one<br />two" | ||||
|         p ||--o| c : "has<br />one<br/>two<br />three" | ||||
|         p ||--o| d : "has<br />one<br />two<br/>three<br />...<br/>Nth" | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with unicode text`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           _**testẽζ➕Ø😀㌕ぼ**_ { | ||||
|               *__List~List~int~~sdfds__* **driversLicense** PK "***The l😀icense #***" | ||||
|               *string(99)~T~~~~~~* firstName "Only __99__ <br>characters are a<br>llowed dsfsdfsdfsdfs" | ||||
|               string last*Name* | ||||
|               string __phone__ UK | ||||
|               int _age_ | ||||
|           } | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with unicode text without htmlLabels`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|       erDiagram | ||||
|           _**testẽζ➕Ø😀㌕ぼ**_ { | ||||
|               *__List~List~int~~sdfds__* **driversLicense** PK "***The l😀icense #***" | ||||
|               *string(99)~T~~~~~~* firstName "Only __99__ <br>characters are a<br>llowed dsfsdfsdfsdfs" | ||||
|               string last*Name* | ||||
|               string __phone__ UK | ||||
|               int _age_ | ||||
|           } | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with relationships with unicode text`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|           erDiagram | ||||
|             person[😀] { | ||||
|                 string *first*Name | ||||
|                 string _**last**Name_ | ||||
|             } | ||||
|             a["*Customer Account*"] { | ||||
|                 **string** ema*i*l | ||||
|             } | ||||
|             person ||--o| a : __hẽ😀__ | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with relationships with unicode text without htmlLabels`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|           erDiagram | ||||
|             person[😀] { | ||||
|                 string *first*Name | ||||
|                 string _**last**Name_ | ||||
|             } | ||||
|             a["*Customer Account*"] { | ||||
|                 **string** ema*i*l | ||||
|             } | ||||
|             person ||--o| a : __hẽ😀__ | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with TB direction`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|           erDiagram | ||||
|           direction TB | ||||
|           CAR ||--|{ NAMED-DRIVER : allows | ||||
|           PERSON ||..o{ NAMED-DRIVER : is | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with BT direction`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|           erDiagram | ||||
|           direction BT | ||||
|           CAR ||--|{ NAMED-DRIVER : allows | ||||
|           PERSON ||..o{ NAMED-DRIVER : is | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with LR direction`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|           erDiagram | ||||
|           direction LR | ||||
|           CAR ||--|{ NAMED-DRIVER : allows | ||||
|           PERSON ||..o{ NAMED-DRIVER : is | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render an ER diagram with RL direction`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|           erDiagram | ||||
|           direction RL | ||||
|           CAR ||--|{ NAMED-DRIVER : allows | ||||
|           PERSON ||..o{ NAMED-DRIVER : is | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with styles applied from style statement`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|             erDiagram | ||||
|               c[CUSTOMER] | ||||
|               p[PERSON] | ||||
|               style c,p fill:#f9f,stroke:blue, color:grey, font-size:24px,font-weight:bold | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with styles applied from style statement without htmlLabels`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|             erDiagram | ||||
|               c[CUSTOMER] | ||||
|               p[PERSON] | ||||
|               style c,p fill:#f9f,stroke:blue, color:grey, font-size:24px,font-weight:bold | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with styles applied from class statement`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|             erDiagram | ||||
|               c[CUSTOMER] | ||||
|               p[PERSON]:::blue | ||||
|               classDef bold font-size:24px, font-weight: bold | ||||
|               classDef blue stroke:lightblue, color: #0000FF | ||||
|               class c,p bold | ||||
|         `, | ||||
|         options | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with styles applied from class statement without htmlLabels`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|             erDiagram | ||||
|               c[CUSTOMER] | ||||
|               p[PERSON]:::blue | ||||
|               classDef bold font-size:24px, font-weight: bold | ||||
|               classDef blue stroke:lightblue, color: #0000FF | ||||
|               class c,p bold | ||||
|         `, | ||||
|         { ...options, htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`${description}should render entities with styles applied from the default class and other styles`, () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|             erDiagram | ||||
|               c[CUSTOMER] | ||||
|               p[PERSON]:::blue | ||||
|               classDef blue stroke:lightblue, color: #0000FF | ||||
|               classDef default fill:pink | ||||
|               style c color:green | ||||
|         `, | ||||
|         { ...options } | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -109,8 +109,8 @@ describe('Entity Relationship Diagram', () => { | ||||
|       const style = svg.attr('style'); | ||||
|       expect(style).to.match(/^max-width: [\d.]+px;$/); | ||||
|       const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join('')); | ||||
|       // use within because the absolute value can be slightly different depending on the environment ±6% | ||||
|       expect(maxWidthValue).to.be.within(140 * 0.96, 140 * 1.06); | ||||
|       // use within because the absolute value can be slightly different depending on the environment ±5% | ||||
|       expect(maxWidthValue).to.be.within(140 * 0.95, 140 * 1.05); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
| @@ -125,8 +125,8 @@ describe('Entity Relationship Diagram', () => { | ||||
|     ); | ||||
|     cy.get('svg').should((svg) => { | ||||
|       const width = parseFloat(svg.attr('width')); | ||||
|       // use within because the absolute value can be slightly different depending on the environment ±6% | ||||
|       expect(width).to.be.within(140 * 0.96, 140 * 1.06); | ||||
|       // use within because the absolute value can be slightly different depending on the environment ±5% | ||||
|       expect(width).to.be.within(140 * 0.95, 140 * 1.05); | ||||
|       // expect(svg).to.have.attr('height', '465'); | ||||
|       expect(svg).to.not.have.attr('style'); | ||||
|     }); | ||||
| @@ -218,6 +218,7 @@ describe('Entity Relationship Diagram', () => { | ||||
|         `, | ||||
|       { loglevel: 1 } | ||||
|     ); | ||||
|     cy.get('svg'); | ||||
|   }); | ||||
|  | ||||
|   it('should render entities with keys', () => { | ||||
| @@ -321,52 +322,4 @@ ORDER ||--|{ LINE-ITEM : contains | ||||
|       { logLevel: 1 } | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('should render relationship labels with line breaks', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|     erDiagram | ||||
|       p[Person] { | ||||
|           string firstName | ||||
|           string lastName | ||||
|       } | ||||
|       a["Customer Account"] { | ||||
|           string email | ||||
|       } | ||||
|  | ||||
|       b["Customer Account Secondary"] { | ||||
|         string email | ||||
|       } | ||||
|        | ||||
|       c["Customer Account Tertiary"] { | ||||
|         string email | ||||
|       } | ||||
|        | ||||
|       d["Customer Account Nth"] { | ||||
|         string email | ||||
|       } | ||||
|  | ||||
|       p ||--o| a : "has<br />one" | ||||
|       p ||--o| b : "has<br />one<br />two" | ||||
|       p ||--o| c : "has<br />one<br/>two<br />three" | ||||
|       p ||--o| d : "has<br />one<br />two<br/>three<br />...<br/>Nth" | ||||
|       `, | ||||
|       { logLevel: 1 } | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   describe('Include char sequence "graph" in text (#6795)', () => { | ||||
|     it('has a label with char sequence "graph"', () => { | ||||
|       imgSnapshotTest( | ||||
|         ` | ||||
|         erDiagram | ||||
|           p[Photograph] { | ||||
|             varchar(12) jobId | ||||
|             date dateCreated | ||||
|           } | ||||
|         `, | ||||
|         { flowchart: { defaultRenderer: 'elk' } } | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import { imgSnapshotTest } from '../../helpers/util'; | ||||
| describe('Error Diagrams', () => { | ||||
|   beforeEach(() => { | ||||
|     cy.on('uncaught:exception', (err) => { | ||||
|       expect(err.message).to.include('error'); | ||||
|       expect(err.message).to.include('Parse error'); | ||||
|       // return false to prevent the error from | ||||
|       // failing this test | ||||
|       return false; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { imgSnapshotTest, renderGraph, verifyNumber } from '../../helpers/util.ts'; | ||||
| import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; | ||||
|  | ||||
| describe('Flowchart ELK', () => { | ||||
| describe.skip('Flowchart ELK', () => { | ||||
|   it('1-elk: should render a simple flowchart', () => { | ||||
|     imgSnapshotTest( | ||||
|       `flowchart-elk TD | ||||
| @@ -109,7 +109,7 @@ describe('Flowchart ELK', () => { | ||||
|       const style = svg.attr('style'); | ||||
|       expect(style).to.match(/^max-width: [\d.]+px;$/); | ||||
|       const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join('')); | ||||
|       verifyNumber(maxWidthValue, 380, 15); | ||||
|       expect(maxWidthValue).to.be.within(230 * 0.95, 230 * 1.05); | ||||
|     }); | ||||
|   }); | ||||
|   it('8-elk: should render a flowchart when useMaxWidth is false', () => { | ||||
| @@ -128,7 +128,7 @@ describe('Flowchart ELK', () => { | ||||
|       const width = parseFloat(svg.attr('width')); | ||||
|       // use within because the absolute value can be slightly different depending on the environment ±5% | ||||
|       // expect(height).to.be.within(446 * 0.95, 446 * 1.05); | ||||
|       verifyNumber(width, 380, 15); | ||||
|       expect(width).to.be.within(230 * 0.95, 230 * 1.05); | ||||
|       expect(svg).to.not.have.attr('style'); | ||||
|     }); | ||||
|   }); | ||||
| @@ -208,13 +208,13 @@ describe('Flowchart ELK', () => { | ||||
|       `flowchart-elk TB | ||||
|   internet | ||||
|   nat | ||||
|   router | ||||
|   routeur | ||||
|   lb1 | ||||
|   lb2 | ||||
|   compute1 | ||||
|   compute2 | ||||
|   subgraph project | ||||
|   router | ||||
|   routeur | ||||
|   nat | ||||
|     subgraph subnet1 | ||||
|       compute1 | ||||
| @@ -225,8 +225,8 @@ describe('Flowchart ELK', () => { | ||||
|       lb2 | ||||
|     end | ||||
|   end | ||||
|   internet --> router | ||||
|   router --> subnet1 & subnet2 | ||||
|   internet --> routeur | ||||
|   routeur --> subnet1 & subnet2 | ||||
|   subnet1 & subnet2 --> nat --> internet | ||||
|       `, | ||||
|       { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } | ||||
| @@ -443,7 +443,7 @@ flowchart-elk TD | ||||
|       { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } | ||||
|     ); | ||||
|   }); | ||||
|   it('63-elk: title on subgraphs should be themeable', () => { | ||||
|   it('63-elk: title on subgraphs should be themable', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       %%{init:{"theme":"base", "themeVariables": {"primaryColor":"#411d4e", "titleColor":"white", "darkMode":true}}}%% | ||||
| @@ -692,7 +692,7 @@ A --> B | ||||
|       {} | ||||
|     ); | ||||
|     cy.get('svg').should((svg) => { | ||||
|       const edges = svg[0].querySelectorAll('.edges > path'); | ||||
|       const edges = svg.querySelectorAll('.edges > path'); | ||||
|       edges.forEach((edge) => { | ||||
|         expect(edge).to.have.class('flowchart-link'); | ||||
|       }); | ||||
| @@ -739,7 +739,7 @@ NL\`") --"\`1o **bold**\`"--> c | ||||
|           { flowchart: { titleTopMargin: 0 } } | ||||
|         ); | ||||
|       }); | ||||
|       it.skip('Wrapping long text with a new line', () => { | ||||
|       it('Wrapping long text with a new line', () => { | ||||
|         imgSnapshotTest( | ||||
|           `%%{init: {"flowchart": {"htmlLabels": true}} }%% | ||||
| flowchart-elk LR | ||||
| @@ -837,237 +837,12 @@ subgraph "\`**Two**\`" | ||||
|   in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog") | ||||
| end | ||||
|  | ||||
| `, | ||||
|           { flowchart: { titleTopMargin: 0 } } | ||||
|         ); | ||||
|       }); | ||||
|       it('Sub graphs', () => { | ||||
|         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 } } | ||||
|         ); | ||||
|       }); | ||||
|       it('6080: should handle diamond shape intersections', () => { | ||||
|         imgSnapshotTest( | ||||
|           `--- | ||||
| config: | ||||
|   layout: elk | ||||
| --- | ||||
| flowchart LR | ||||
|  subgraph s1["Untitled subgraph"] | ||||
|         n1["Evaluate"] | ||||
|         n2["Option 1"] | ||||
|         n3["Option 2"] | ||||
|         n4["fa:fa-car Option 3"] | ||||
|   end | ||||
|  subgraph s2["Untitled subgraph"] | ||||
|         n5["Evaluate"] | ||||
|         n6["Option 1"] | ||||
|         n7["Option 2"] | ||||
|         n8["fa:fa-car Option 3"] | ||||
|   end | ||||
|     A["Start"] -- Some text --> B("Continue") | ||||
|     B --> C{"Evaluate"} | ||||
|     C -- One --> D["Option 1"] | ||||
|     C -- Two --> E["Option 2"] | ||||
|     C -- Three --> F["fa:fa-car Option 3"] | ||||
|     n1 -- One --> n2 | ||||
|     n1 -- Two --> n3 | ||||
|     n1 -- Three --> n4 | ||||
|     n5 -- One --> n6 | ||||
|     n5 -- Two --> n7 | ||||
|     n5 -- Three --> n8 | ||||
|     n1@{ shape: diam} | ||||
|     n2@{ shape: rect} | ||||
|     n3@{ shape: rect} | ||||
|     n4@{ shape: rect} | ||||
|     n5@{ shape: diam} | ||||
|     n6@{ shape: rect} | ||||
|     n7@{ shape: rect} | ||||
|     n8@{ shape: rect} | ||||
|  | ||||
| `, | ||||
|           { flowchart: { titleTopMargin: 0 } } | ||||
|         ); | ||||
|       }); | ||||
|  | ||||
|       it('6088-1: should handle diamond shape intersections', () => { | ||||
|         imgSnapshotTest( | ||||
|           `--- | ||||
| config: | ||||
|   layout: elk | ||||
| --- | ||||
|       flowchart LR | ||||
|       subgraph S2 | ||||
|       subgraph s1["APA"] | ||||
|       D{"Use the editor"} | ||||
|       end | ||||
|  | ||||
|  | ||||
|       D -- Mermaid js --> I{"fa:fa-code Text"} | ||||
|             D --> I | ||||
|             D --> I | ||||
|  | ||||
|       end | ||||
| `, | ||||
|           { flowchart: { titleTopMargin: 0 } } | ||||
|         ); | ||||
|       }); | ||||
|  | ||||
|       it('6088-2: should handle diamond shape intersections', () => { | ||||
|         imgSnapshotTest( | ||||
|           `--- | ||||
| config: | ||||
|   layout: elk | ||||
| --- | ||||
|       flowchart LR | ||||
|       a | ||||
|       subgraph s0["APA"] | ||||
|       subgraph s8["APA"] | ||||
|       subgraph s1["APA"] | ||||
|         D{"X"} | ||||
|         E[Q] | ||||
|       end | ||||
|       subgraph s3["BAPA"] | ||||
|         F[Q] | ||||
|         I | ||||
|       end | ||||
|             D --> I | ||||
|             D --> I | ||||
|             D --> I | ||||
|  | ||||
|       I{"X"} | ||||
|       end | ||||
|       end | ||||
|  | ||||
| `, | ||||
|           { flowchart: { titleTopMargin: 0 } } | ||||
|         ); | ||||
|       }); | ||||
|  | ||||
|       it('6088-3: should handle diamond shape intersections', () => { | ||||
|         imgSnapshotTest( | ||||
|           `--- | ||||
| config: | ||||
|   layout: elk | ||||
| --- | ||||
|       flowchart LR | ||||
|       a | ||||
|         D{"Use the editor"} | ||||
|  | ||||
|       D -- Mermaid js --> I{"fa:fa-code Text"} | ||||
|       D-->I | ||||
|       D-->I | ||||
|  | ||||
| `, | ||||
|           { flowchart: { titleTopMargin: 0 } } | ||||
|         ); | ||||
|       }); | ||||
|  | ||||
|       it('6088-4: should handle diamond shape intersections', () => { | ||||
|         imgSnapshotTest( | ||||
|           `--- | ||||
| config: | ||||
|   layout: elk | ||||
| --- | ||||
| flowchart LR | ||||
|  subgraph s1["Untitled subgraph"] | ||||
|         n1["Evaluate"] | ||||
|         n2["Option 1"] | ||||
|         n3["Option 2"] | ||||
|         n4["fa:fa-car Option 3"] | ||||
|   end | ||||
|  subgraph s2["Untitled subgraph"] | ||||
|         n5["Evaluate"] | ||||
|         n6["Option 1"] | ||||
|         n7["Option 2"] | ||||
|         n8["fa:fa-car Option 3"] | ||||
|   end | ||||
|     A["Start"] -- Some text --> B("Continue") | ||||
|     B --> C{"Evaluate"} | ||||
|     C -- One --> D["Option 1"] | ||||
|     C -- Two --> E["Option 2"] | ||||
|     C -- Three --> F["fa:fa-car Option 3"] | ||||
|     n1 -- One --> n2 | ||||
|     n1 -- Two --> n3 | ||||
|     n1 -- Three --> n4 | ||||
|     n5 -- One --> n6 | ||||
|     n5 -- Two --> n7 | ||||
|     n5 -- Three --> n8 | ||||
|     n1@{ shape: diam} | ||||
|     n2@{ shape: rect} | ||||
|     n3@{ shape: rect} | ||||
|     n4@{ shape: rect} | ||||
|     n5@{ shape: diam} | ||||
|     n6@{ shape: rect} | ||||
|     n7@{ shape: rect} | ||||
|     n8@{ shape: rect} | ||||
|  | ||||
| `, | ||||
|           { flowchart: { titleTopMargin: 0 } } | ||||
|         ); | ||||
|       }); | ||||
|  | ||||
|       it('6088-5: should handle diamond shape intersections', () => { | ||||
|         imgSnapshotTest( | ||||
|           `--- | ||||
| config: | ||||
|   layout: elk | ||||
| --- | ||||
| flowchart LR | ||||
|     A{A} --> B & C | ||||
|  | ||||
| `, | ||||
|           { flowchart: { titleTopMargin: 0 } } | ||||
|         ); | ||||
|       }); | ||||
|       it('6088-6: should handle diamond shape intersections', () => { | ||||
|         imgSnapshotTest( | ||||
|           `--- | ||||
| config: | ||||
|   layout: elk | ||||
| --- | ||||
| flowchart LR | ||||
|     A{A} --> B & C | ||||
|     subgraph "subbe" | ||||
|       A | ||||
|     end | ||||
|  | ||||
| `, | ||||
|           { flowchart: { titleTopMargin: 0 } } | ||||
|         ); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('6647-elk: should keep node order when using elk layout unless it would add crossings', () => { | ||||
|     imgSnapshotTest( | ||||
|       `--- | ||||
| config: | ||||
|   layout: elk | ||||
| --- | ||||
|       flowchart TB | ||||
|         a --> a1 & a2 & a3 & a4 | ||||
|         b --> b1 & b2 | ||||
|         b2 --> b3 | ||||
|         b1 --> b4 | ||||
|       ` | ||||
|     ); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| describe('Title and arrow styling #4813', () => { | ||||
| @@ -1080,7 +855,7 @@ describe('Title and arrow styling #4813', () => { | ||||
|       flowchart LR | ||||
|       A-->B | ||||
|       A-->C`, | ||||
|       { layout: 'elk' } | ||||
|       { flowchart: { defaultRenderer: 'elk' } } | ||||
|     ); | ||||
|     cy.get('svg').should((svg) => { | ||||
|       const title = svg[0].querySelector('text'); | ||||
| @@ -1096,14 +871,15 @@ describe('Title and arrow styling #4813', () => { | ||||
|       B-.-oC | ||||
|       C==xD | ||||
|       D ~~~ A`, | ||||
|       { layout: 'elk' } | ||||
|       { flowchart: { defaultRenderer: 'elk' } } | ||||
|     ); | ||||
|     cy.get('svg').should((svg) => { | ||||
|       const edges = svg[0].querySelectorAll('.edges path'); | ||||
|       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'); | ||||
|       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'); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,28 +0,0 @@ | ||||
| import { imgSnapshotTest } from '../../helpers/util.ts'; | ||||
|  | ||||
| const themes = ['default', 'forest', 'dark', 'base', 'neutral']; | ||||
|  | ||||
| describe('when rendering flowchart with icons', () => { | ||||
|   for (const theme of themes) { | ||||
|     it(`should render icons from fontawesome library on theme ${theme}`, () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart TD | ||||
|             A("fab:fa-twitter Twitter") --> B("fab:fa-facebook Facebook") | ||||
|             B --> C("fa:fa-coffee Coffee") | ||||
|             C --> D("fa:fa-car Car") | ||||
|             D --> E("fab:fa-github GitHub") | ||||
|         `, | ||||
|         { theme } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it(`should render registered icons on theme ${theme}`, () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart TD | ||||
|             A("fa:fa-bell Bell")  | ||||
|         `, | ||||
|         { theme } | ||||
|       ); | ||||
|     }); | ||||
|   } | ||||
| }); | ||||
| @@ -1,142 +0,0 @@ | ||||
| import { imgSnapshotTest } from '../../helpers/util.ts'; | ||||
|  | ||||
| const aliasSet1 = ['process', 'rect', 'proc', 'rectangle'] as const; | ||||
|  | ||||
| const aliasSet2 = ['event', 'rounded'] as const; | ||||
|  | ||||
| const aliasSet3 = ['stadium', 'pill', 'terminal'] as const; | ||||
|  | ||||
| const aliasSet4 = ['fr-rect', 'subproc', 'subprocess', 'framed-rectangle', 'subroutine'] as const; | ||||
|  | ||||
| const aliasSet5 = ['db', 'database', 'cylinder', 'cyl'] as const; | ||||
|  | ||||
| const aliasSet6 = ['diam', 'decision', 'diamond'] as const; | ||||
|  | ||||
| const aliasSet7 = ['hex', 'hexagon', 'prepare'] as const; | ||||
|  | ||||
| const aliasSet8 = ['lean-r', 'lean-right', 'in-out'] as const; | ||||
|  | ||||
| const aliasSet9 = ['lean-l', 'lean-left', 'out-in'] as const; | ||||
|  | ||||
| const aliasSet10 = ['trap-b', 'trapezoid-bottom', 'priority'] as const; | ||||
|  | ||||
| const aliasSet11 = ['trap-t', 'trapezoid-top', 'manual'] as const; | ||||
|  | ||||
| const aliasSet12 = ['dbl-circ', 'double-circle'] as const; | ||||
|  | ||||
| const aliasSet13 = ['notched-rectangle', 'card', 'notch-rect'] as const; | ||||
|  | ||||
| const aliasSet14 = [ | ||||
|   'lin-rect', | ||||
|   'lined-rectangle', | ||||
|   'lin-proc', | ||||
|   'lined-process', | ||||
|   'shaded-process', | ||||
| ] as const; | ||||
|  | ||||
| const aliasSet15 = ['sm-circ', 'small-circle', 'start'] as const; | ||||
|  | ||||
| const aliasSet16 = ['fr-circ', 'framed-circle', 'stop'] as const; | ||||
|  | ||||
| const aliasSet17 = ['fork', 'join'] as const; | ||||
| // brace-r', 'braces' | ||||
| const aliasSet18 = ['comment', 'brace-l'] as const; | ||||
|  | ||||
| const aliasSet19 = ['bolt', 'com-link', 'lightning-bolt'] as const; | ||||
|  | ||||
| const aliasSet20 = ['doc', 'document'] as const; | ||||
|  | ||||
| const aliasSet21 = ['delay', 'half-rounded-rectangle'] as const; | ||||
|  | ||||
| const aliasSet22 = ['h-cyl', 'das', 'horizontal-cylinder'] as const; | ||||
|  | ||||
| const aliasSet23 = ['lin-cyl', 'disk', 'lined-cylinder'] as const; | ||||
|  | ||||
| const aliasSet24 = ['curv-trap', 'display', 'curved-trapezoid'] as const; | ||||
|  | ||||
| const aliasSet25 = ['div-rect', 'div-proc', 'divided-rectangle', 'divided-process'] as const; | ||||
|  | ||||
| const aliasSet26 = ['extract', 'tri', 'triangle'] as const; | ||||
|  | ||||
| const aliasSet27 = ['win-pane', 'internal-storage', 'window-pane'] as const; | ||||
|  | ||||
| const aliasSet28 = ['f-circ', 'junction', 'filled-circle'] as const; | ||||
|  | ||||
| const aliasSet29 = ['lin-doc', 'lined-document'] as const; | ||||
|  | ||||
| const aliasSet30 = ['notch-pent', 'loop-limit', 'notched-pentagon'] as const; | ||||
|  | ||||
| const aliasSet31 = ['flip-tri', 'manual-file', 'flipped-triangle'] as const; | ||||
|  | ||||
| const aliasSet32 = ['sl-rect', 'manual-input', 'sloped-rectangle'] as const; | ||||
|  | ||||
| const aliasSet33 = ['docs', 'documents', 'st-doc', 'stacked-document'] as const; | ||||
|  | ||||
| const aliasSet34 = ['procs', 'processes', 'st-rect', 'stacked-rectangle'] as const; | ||||
|  | ||||
| const aliasSet35 = ['flag', 'paper-tape'] as const; | ||||
|  | ||||
| const aliasSet36 = ['bow-rect', 'stored-data', 'bow-tie-rectangle'] as const; | ||||
|  | ||||
| const aliasSet37 = ['cross-circ', 'summary', 'crossed-circle'] as const; | ||||
|  | ||||
| const aliasSet38 = ['tag-doc', 'tagged-document'] as const; | ||||
|  | ||||
| const aliasSet39 = ['tag-rect', 'tag-proc', 'tagged-rectangle', 'tagged-process'] as const; | ||||
|  | ||||
| const aliasSet40 = ['collate', 'hourglass'] as const; | ||||
|  | ||||
| // Aggregate all alias sets into a single array | ||||
| const aliasSets = [ | ||||
|   aliasSet1, | ||||
|   aliasSet2, | ||||
|   aliasSet3, | ||||
|   aliasSet4, | ||||
|   aliasSet5, | ||||
|   aliasSet6, | ||||
|   aliasSet7, | ||||
|   aliasSet8, | ||||
|   aliasSet9, | ||||
|   aliasSet10, | ||||
|   aliasSet11, | ||||
|   aliasSet12, | ||||
|   aliasSet13, | ||||
|   aliasSet14, | ||||
|   aliasSet15, | ||||
|   aliasSet16, | ||||
|   aliasSet17, | ||||
|   aliasSet18, | ||||
|   aliasSet19, | ||||
|   aliasSet20, | ||||
|   aliasSet21, | ||||
|   aliasSet22, | ||||
|   aliasSet23, | ||||
|   aliasSet24, | ||||
|   aliasSet25, | ||||
|   aliasSet26, | ||||
|   aliasSet27, | ||||
|   aliasSet28, | ||||
|   aliasSet29, | ||||
|   aliasSet30, | ||||
|   aliasSet31, | ||||
|   aliasSet32, | ||||
|   aliasSet33, | ||||
|   aliasSet34, | ||||
|   aliasSet35, | ||||
|   aliasSet36, | ||||
|   aliasSet37, | ||||
|   aliasSet38, | ||||
|   aliasSet39, | ||||
| ] as const; | ||||
|  | ||||
| aliasSets.forEach((aliasSet) => { | ||||
|   describe(`Test ${aliasSet.join(',')} `, () => { | ||||
|     it(`All ${aliasSet.join(',')} should render same shape`, () => { | ||||
|       let flowchartCode = `flowchart \n`; | ||||
|       aliasSet.forEach((alias, index) => { | ||||
|         flowchartCode += ` n${index}@{ shape: ${alias}, label: "${alias}" }\n`; | ||||
|       }); | ||||
|       imgSnapshotTest(flowchartCode); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -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(440 * 0.95, 440 * 1.05); | ||||
|       expect(maxWidthValue).to.be.within(290 * 0.95 - 1, 290 * 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(440 * 0.95, 440 * 1.05); | ||||
|       expect(width).to.be.within(290 * 0.95 - 1, 290 * 1.05); | ||||
|       expect(svg).to.not.have.attr('style'); | ||||
|     }); | ||||
|   }); | ||||
| @@ -198,13 +198,13 @@ describe('Flowchart v2', () => { | ||||
|       `flowchart TB | ||||
|   internet | ||||
|   nat | ||||
|   router | ||||
|   routeur | ||||
|   lb1 | ||||
|   lb2 | ||||
|   compute1 | ||||
|   compute2 | ||||
|   subgraph project | ||||
|   router | ||||
|   routeur | ||||
|   nat | ||||
|     subgraph subnet1 | ||||
|       compute1 | ||||
| @@ -215,8 +215,8 @@ describe('Flowchart v2', () => { | ||||
|       lb2 | ||||
|     end | ||||
|   end | ||||
|   internet --> router | ||||
|   router --> subnet1 & subnet2 | ||||
|   internet --> routeur | ||||
|   routeur --> subnet1 & subnet2 | ||||
|   subnet1 & subnet2 --> nat --> internet | ||||
|       `, | ||||
|       { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } | ||||
| @@ -433,7 +433,7 @@ flowchart TD | ||||
|       { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } | ||||
|     ); | ||||
|   }); | ||||
|   it('63: title on subgraphs should be themeable', () => { | ||||
|   it('63: title on subgraphs should be themable', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       %%{init:{"theme":"base", "themeVariables": {"primaryColor":"#411d4e", "titleColor":"white", "darkMode":true}}}%% | ||||
| @@ -699,7 +699,7 @@ A --> B | ||||
|       { flowchart: { titleTopMargin: 10 } } | ||||
|     ); | ||||
|   }); | ||||
|   it('3192: It should be possible to render flowcharts with invisible edges', () => { | ||||
|   it('3192: It should be possieble to render flowcharts with invisible edges', () => { | ||||
|     imgSnapshotTest( | ||||
|       `--- | ||||
| title: Simple flowchart with invisible edges | ||||
| @@ -1047,213 +1047,8 @@ end | ||||
|           A --lb3--> TOP --lb4--> B | ||||
|           B1 --lb5--> B2 | ||||
|         `, | ||||
|         { | ||||
|           flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } }, | ||||
|         } | ||||
|         { flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } } | ||||
|       ); | ||||
|     }); | ||||
|     it('Should render self-loops', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart | ||||
|           A --> A | ||||
|           subgraph B | ||||
|             B1 --> B1 | ||||
|           end | ||||
|           subgraph C | ||||
|             subgraph C1 | ||||
|               C2 --> C2 | ||||
|               subgraph D | ||||
|                 D1 --> D1 | ||||
|               end | ||||
|               D --> D | ||||
|             end | ||||
|             C1 --> C1 | ||||
|           end | ||||
|         `, | ||||
|         { | ||||
|           flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } }, | ||||
|         } | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
|   describe('New @ syntax for node metadata edge cases', () => { | ||||
|     it('should be possible to use @  syntax to add labels on multi nodes', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart TB | ||||
|        n2["label for n2"] &   n4@{ label: "label for n4"}   & n5@{ label: "label for n5"} | ||||
|         `, | ||||
|         {} | ||||
|       ); | ||||
|     }); | ||||
|     it('should be possible to use @  syntax to add labels with trail spaces and &', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart TB | ||||
|        n2["label for n2"] &   n4@{ label: "label for n4"}   & n5@{ label: "label for n5"}    | ||||
|         `, | ||||
|         {} | ||||
|       ); | ||||
|     }); | ||||
|     it('should be possible to use @  syntax to add labels with trail spaces', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart TB | ||||
|        n2["label for n2"] | ||||
|        n4@{ label: "label for n4"} | ||||
|        n5@{ label: "label for n5"}   | ||||
|         `, | ||||
|         {} | ||||
|       ); | ||||
|     }); | ||||
|     it('should be possible to use @  syntax to add labels with trail spaces and edge/link', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart TD | ||||
|     A["A"] --> B["for B"] &    C@{ label: "for c"} & E@{label : "for E"}   | ||||
|     D@{label: "for D"}      | ||||
|         `, | ||||
|         {} | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
|   describe('Flowchart Node Shape Rendering', () => { | ||||
|     it('should render a stadium-shaped node', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart TB | ||||
|           A(["Start"]) --> n1["Untitled Node"] | ||||
|           A --> n2["Untitled Node"] | ||||
|         `, | ||||
|         {} | ||||
|       ); | ||||
|     }); | ||||
|     it('should render a diamond-shaped node using shape config', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart BT | ||||
|           n2["Untitled Node"] --> n1["Diamond"] | ||||
|           n1@{ shape: diam} | ||||
|         `, | ||||
|         {} | ||||
|       ); | ||||
|     }); | ||||
|     it('should render a rounded rectangle and a normal rectangle', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart BT | ||||
|         n2["Untitled Node"] --> n1["Rounded Rectangle"] | ||||
|         n3["Untitled Node"] --> n1 | ||||
|         n1@{ shape: rounded} | ||||
|         n3@{ shape: rect} | ||||
|     `, | ||||
|         {} | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('6617: Per Link Curve Styling using edge Ids', () => { | ||||
|     imgSnapshotTest( | ||||
|       `flowchart TD | ||||
|       A e1@-->B e5@--> E | ||||
|       E e7@--> D | ||||
|       B e3@-->D | ||||
|       A e2@-->C e4@-->D | ||||
|       C e6@--> F | ||||
|       F e8@--> D | ||||
|       e1@{ curve: natural } | ||||
|       e2@{ curve: stepAfter } | ||||
|       e3@{ curve: monotoneY } | ||||
|       e4@{ curve: bumpY } | ||||
|       e5@{ curve: linear } | ||||
|       e6@{ curve: catmullRom } | ||||
|       e7@{ curve: cardinal } | ||||
|       ` | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   describe('when rendering unsuported markdown', () => { | ||||
|     const graph = `flowchart TB | ||||
|     mermaid{"What is\nyourmermaid version?"} --> v10["<11"] --"\`<**1**1\`"--> fine["No bug"] | ||||
|     mermaid --> v11[">= v11"] -- ">= v11" --> broken["Affected by https://github.com/mermaid-js/mermaid/issues/5824"] | ||||
|     subgraph subgraph1["\`How to fix **fix**\`"] | ||||
|         broken --> B["B"] | ||||
|     end | ||||
|     githost["Github, Gitlab, BitBucket, etc."] | ||||
|     githost2["\`Github, Gitlab, BitBucket, etc.\`"] | ||||
|     a["1."] | ||||
|     b["- x"] | ||||
|       `; | ||||
|  | ||||
|     it('should render raw strings', () => { | ||||
|       imgSnapshotTest(graph); | ||||
|     }); | ||||
|  | ||||
|     it('should render raw strings with htmlLabels: false', () => { | ||||
|       imgSnapshotTest(graph, { htmlLabels: false }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('V2 - 17: should apply class def colour to edge label', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` graph LR | ||||
|     id1(Start) link@-- "Label" -->id2(Stop) | ||||
|     style id1 fill:#f9f,stroke:#333,stroke-width:4px | ||||
|  | ||||
| class id2 myClass | ||||
| classDef myClass fill:#bbf,stroke:#f66,stroke-width:2px,color:white,stroke-dasharray: 5 5 | ||||
| class link myClass | ||||
| ` | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   describe('htmlLabels rendering', () => { | ||||
|     it('should not render with htmlLabels when disabled via flowchart config', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart LR | ||||
|           A["HTML label <br> with breaks"] --> B["Another label"] | ||||
|           C --> D | ||||
|         `, | ||||
|         { flowchart: { htmlLabels: false } } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('should not render with htmlLabels when disabled via global config', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart LR | ||||
|           A["HTML label <br> with breaks"] --> B["Another label"] | ||||
|           C --> D | ||||
|         `, | ||||
|         { htmlLabels: false } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('should render with htmlLabels when enabled', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart LR | ||||
|           A["HTML label <br> with breaks"] --> B["Another label"] | ||||
|           C --> D | ||||
|         `, | ||||
|         { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('should not render with htmlLabels when disabled via flowchart config, even when enabled in global config', () => { | ||||
|       imgSnapshotTest( | ||||
|         `flowchart LR | ||||
|           A["HTML label <br> with breaks"] --> B["Another label"] | ||||
|           C --> D | ||||
|         `, | ||||
|         { htmlLabels: true, flowchart: { htmlLabels: false } }, | ||||
|         undefined, | ||||
|         ($svg) => { | ||||
|           expect($svg.find('foreignObject').length).to.equal(0); | ||||
|         } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('should create foreignObject elements when htmlLabels enabled', () => { | ||||
|       renderGraph( | ||||
|         `flowchart TD | ||||
|           A["Node with <br> HTML"] -- "edge <br> label" --> B["Another node"] | ||||
|           C --> D | ||||
|         `, | ||||
|         { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } | ||||
|       ); | ||||
|       cy.get('svg foreignObject').should('exist'); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -733,7 +733,7 @@ describe('Graph', () => { | ||||
|   }); | ||||
|   it('38: should render a flowchart when useMaxWidth is true (default)', () => { | ||||
|     renderGraph( | ||||
|       `flowchart TD | ||||
|       `graph 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(446 * 0.9, 446 * 1.1); | ||||
|       expect(maxWidthValue).to.be.within(300 * 0.9, 300 * 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(446 * 0.9, 446 * 1.1); | ||||
|       expect(width).to.be.within(300 * 0.9, 300 * 1.1); | ||||
|       expect(svg).to.not.have.attr('style'); | ||||
|     }); | ||||
|   }); | ||||
| @@ -905,72 +905,13 @@ 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 | ||||
|     `, | ||||
|       { | ||||
|         flowchart: { htmlLabels: true }, | ||||
|         securityLevel: 'loose', | ||||
|       } | ||||
|     ); | ||||
|   }); | ||||
|   it('#6369: edge color should affect arrow head', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|     flowchart LR | ||||
|         A --> B | ||||
|         A --> C | ||||
|         C --> D | ||||
|  | ||||
|         linkStyle 0 stroke:#D50000 | ||||
|         linkStyle 2 stroke:#D50000 | ||||
|     `, | ||||
|       { | ||||
|         flowchart: { htmlLabels: true }, | ||||
|         securityLevel: 'loose', | ||||
|       } | ||||
|     ); | ||||
|   }); | ||||
|   it('68: should honor subgraph direction when inheritDir is false', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       %%{init: {"flowchart": { "inheritDir": false }}}%% | ||||
|       flowchart TB | ||||
|         direction LR | ||||
|         subgraph A | ||||
|           direction TB | ||||
|           a --> b | ||||
|         end | ||||
|         subgraph B | ||||
|           c --> d | ||||
|         end | ||||
|       `, | ||||
|       { | ||||
|         fontFamily: 'courier', | ||||
|       } | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('69: should inherit global direction when inheritDir is true', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       %%{init: {"flowchart": { "inheritDir": true }}}%% | ||||
|       flowchart TB | ||||
|         direction LR | ||||
|         subgraph A | ||||
|           direction TB | ||||
|           a --> b | ||||
|         end | ||||
|         subgraph B | ||||
|           c --> d | ||||
|         end | ||||
|       `, | ||||
|       { | ||||
|         fontFamily: 'courier', | ||||
|       } | ||||
|       { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } | ||||
|     ); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -117,7 +117,7 @@ describe('Gantt diagram', () => { | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|   it('should FAIL rendering a gantt chart for issue #1060 with invalid date', () => { | ||||
|   it('should FAIL redering a gantt chart for issue #1060 with invalid date', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       gantt | ||||
| @@ -358,23 +358,6 @@ describe('Gantt diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('should render a gantt diagram with a vert tag', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       gantt | ||||
|         title A Gantt Diagram | ||||
|         dateFormat   ss | ||||
|         axisFormat   %Ss | ||||
|  | ||||
|         section Section | ||||
|         A task           : a1, 00, 6s | ||||
|         Milestone     : vert, 01, | ||||
|         section Another | ||||
|         Task in sec      : 06, 3s | ||||
|         another task     : 3s | ||||
|       ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render a gantt diagram with tick is 2 milliseconds', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
| @@ -565,18 +548,6 @@ describe('Gantt diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('should render only the day when using dateFormat D', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|     gantt | ||||
|       title Test | ||||
|       dateFormat D | ||||
|       A :a, 1, 1d | ||||
|     `, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   // TODO: fix it | ||||
|   // | ||||
|   // This test is skipped deliberately | ||||
| @@ -602,7 +573,7 @@ describe('Gantt diagram', () => { | ||||
|       ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render a gantt diagram excluding friday and saturday', () => { | ||||
|   it('should render a gantt diagram exculding friday and saturday', () => { | ||||
|     imgSnapshotTest( | ||||
|       `gantt | ||||
|       title A Gantt Diagram | ||||
| @@ -613,7 +584,7 @@ describe('Gantt diagram', () => { | ||||
|       A task :a1, 2024-02-28, 10d` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render a gantt diagram excluding saturday and sunday', () => { | ||||
|   it('should render a gantt diagram exculding saturday and sunday', () => { | ||||
|     imgSnapshotTest( | ||||
|       `gantt | ||||
|       title A Gantt Diagram | ||||
| @@ -659,49 +630,6 @@ describe('Gantt diagram', () => { | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('should render a gantt diagram excluding a specific date in YYYY-MM-DD HH:mm:ss format', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|     gantt | ||||
|       dateFormat  YYYY-MM-DD HH:mm:ss | ||||
|       excludes    2025-07-07 | ||||
|       section     Section | ||||
|       A task      :a1, 2025-07-04 20:30:30, 2025-07-08 10:30:30 | ||||
|       Another task:after a1, 20h | ||||
|     `, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('should render a gantt diagram excluding saturday and sunday in YYYY-MM-DD HH:mm:ss format', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|         gantt | ||||
|       dateFormat  YYYY-MM-DD HH:mm:ss | ||||
|       excludes    weekends | ||||
|        weekend saturday | ||||
|       section     Section | ||||
|       A task      :a1, 2025-07-04 20:30:30, 2025-07-08 10:30:30 | ||||
|       Another task:after a1, 20h | ||||
|     `, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|   it('should render a gantt diagram excluding friday and saturday in YYYY-MM-DD HH:mm:ss format', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|         gantt | ||||
|       dateFormat  YYYY-MM-DD HH:mm:ss | ||||
|       excludes    weekends | ||||
|        weekend friday | ||||
|       section     Section | ||||
|       A task      :a1, 2025-07-04 20:30:30, 2025-07-08 10:30:30 | ||||
|       Another task:after a1, 20h | ||||
|     `, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it("should render when there's a semicolon in the title", () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
| @@ -743,7 +671,7 @@ describe('Gantt diagram', () => { | ||||
|       title Gantt Digram | ||||
|       dateFormat  YYYY-MM-DD | ||||
|       section Section | ||||
|       ;A task with a semicolon           :a1, 2014-01-01, 30d | ||||
|       ;A task with a semiclon           :a1, 2014-01-01, 30d | ||||
|       Another task     :after a1  , 20d | ||||
|       section Another | ||||
|       Task in sec      :2014-01-12  , 12d | ||||
|   | ||||
| @@ -11,7 +11,7 @@ describe('Git Graph diagram', () => { | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|   it('2: should render a simple gitgraph with commit on main branch with id', () => { | ||||
|   it('2: should render a simple gitgraph with commit on main branch with Id', () => { | ||||
|     imgSnapshotTest( | ||||
|       `gitGraph | ||||
|        commit id: "One" | ||||
| @@ -253,7 +253,7 @@ describe('Git Graph diagram', () => { | ||||
|       ` | ||||
|       gitGraph | ||||
|       checkout main | ||||
|       %% Make sure to manually set the id of all commits, for consistent visual tests | ||||
|       %% Make sure to manually set the ID of all commits, for consistent visual tests | ||||
|       commit id: "1-abcdefg" | ||||
|       checkout main | ||||
|       branch branch1 | ||||
| @@ -343,7 +343,7 @@ gitGraph | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
|   it('16: should render a simple gitgraph with commit on main branch with id | Vertical Branch', () => { | ||||
|   it('16: should render a simple gitgraph with commit on main branch with Id | Vertical Branch', () => { | ||||
|     imgSnapshotTest( | ||||
|       `gitGraph TB: | ||||
|        commit id: "One" | ||||
| @@ -585,7 +585,7 @@ gitGraph | ||||
|       ` | ||||
|       gitGraph TB: | ||||
|       checkout main | ||||
|       %% Make sure to manually set the id of all commits, for consistent visual tests | ||||
|       %% Make sure to manually set the ID of all commits, for consistent visual tests | ||||
|       commit id: "1-abcdefg" | ||||
|       checkout main | ||||
|       branch branch1 | ||||
| @@ -1024,7 +1024,7 @@ gitGraph TB: | ||||
|         {} | ||||
|       ); | ||||
|     }); | ||||
|     it('51: should render a simple gitgraph with commit on main branch with id | Vertical Branch - Bottom-to-top', () => { | ||||
|     it('51: should render a simple gitgraph with commit on main branch with Id | Vertical Branch - Bottom-to-top', () => { | ||||
|       imgSnapshotTest( | ||||
|         `gitGraph BT: | ||||
|          commit id: "One" | ||||
| @@ -1266,7 +1266,7 @@ gitGraph TB: | ||||
|         ` | ||||
|         gitGraph BT: | ||||
|         checkout main | ||||
|         %% Make sure to manually set the id of all commits, for consistent visual tests | ||||
|         %% Make sure to manually set the ID of all commits, for consistent visual tests | ||||
|         commit id: "1-abcdefg" | ||||
|         checkout main | ||||
|         branch branch1 | ||||
| @@ -1491,7 +1491,7 @@ gitGraph TB: | ||||
|         ` | ||||
|         gitGraph | ||||
|         switch main | ||||
|         %% Make sure to manually set the id of all commits, for consistent visual tests | ||||
|         %% Make sure to manually set the ID of all commits, for consistent visual tests | ||||
|         commit id: "1-abcdefg" | ||||
|         switch main | ||||
|         branch branch1 | ||||
| @@ -1532,41 +1532,5 @@ gitGraph TB: | ||||
|         {} | ||||
|       ); | ||||
|     }); | ||||
|     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"`, | ||||
|       {} | ||||
|     ); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -1,143 +0,0 @@ | ||||
| import { imgSnapshotTest } from '../../helpers/util'; | ||||
|  | ||||
| const looks = ['classic', 'handDrawn'] as const; | ||||
| const directions = [ | ||||
|   'TB', | ||||
|   //'BT', | ||||
|   'LR', | ||||
|   //  'RL' | ||||
| ] as const; | ||||
| const forms = [undefined, 'square', 'circle', 'rounded'] as const; | ||||
| const labelPos = [undefined, 't', 'b'] as const; | ||||
|  | ||||
| looks.forEach((look) => { | ||||
|   directions.forEach((direction) => { | ||||
|     forms.forEach((form) => { | ||||
|       labelPos.forEach((pos) => { | ||||
|         describe(`Test iconShape in ${form ? `${form} form,` : ''} ${look} look and dir ${direction} with label position ${pos ? pos : 'not defined'}`, () => { | ||||
|           it(`without label`, () => { | ||||
|             let flowchartCode = `flowchart ${direction}\n`; | ||||
|             flowchartCode += `  nA --> nAA@{ icon: 'fa:bell'`; | ||||
|             if (form) { | ||||
|               flowchartCode += `, form: '${form}'`; | ||||
|             } | ||||
|             flowchartCode += ` }\n`; | ||||
|             imgSnapshotTest(flowchartCode, { look }); | ||||
|           }); | ||||
|  | ||||
|           it(`with label`, () => { | ||||
|             let flowchartCode = `flowchart ${direction}\n`; | ||||
|             flowchartCode += `  nA --> nAA@{ icon: 'fa:bell', label: 'This is a label for icon shape'`; | ||||
|             if (form) { | ||||
|               flowchartCode += `, form: '${form}'`; | ||||
|             } | ||||
|             if (pos) { | ||||
|               flowchartCode += `, pos: '${pos}'`; | ||||
|             } | ||||
|             flowchartCode += ` }\n`; | ||||
|             imgSnapshotTest(flowchartCode, { look }); | ||||
|           }); | ||||
|  | ||||
|           it(`with very long label`, () => { | ||||
|             let flowchartCode = `flowchart ${direction}\n`; | ||||
|             flowchartCode += `  nA --> nAA@{ icon: 'fa:bell', label: 'This is a very very very very very long long long label for icon shape'`; | ||||
|             if (form) { | ||||
|               flowchartCode += `, form: '${form}'`; | ||||
|             } | ||||
|             if (pos) { | ||||
|               flowchartCode += `, pos: '${pos}'`; | ||||
|             } | ||||
|             flowchartCode += ` }\n`; | ||||
|             imgSnapshotTest(flowchartCode, { look }); | ||||
|           }); | ||||
|  | ||||
|           it(`with markdown htmlLabels:true`, () => { | ||||
|             let flowchartCode = `flowchart ${direction}\n`; | ||||
|             flowchartCode += `  nA --> nAA@{ icon: 'fa:bell', label: 'This is **bold** </br>and <strong>strong</strong> for icon shape'`; | ||||
|             if (form) { | ||||
|               flowchartCode += `, form: '${form}'`; | ||||
|             } | ||||
|             if (pos) { | ||||
|               flowchartCode += `, pos: '${pos}'`; | ||||
|             } | ||||
|             flowchartCode += ` }\n`; | ||||
|             imgSnapshotTest(flowchartCode, { look }); | ||||
|           }); | ||||
|  | ||||
|           it(`with markdown htmlLabels:false`, () => { | ||||
|             let flowchartCode = `flowchart ${direction}\n`; | ||||
|             flowchartCode += `  nA --> nAA@{ icon: 'fa:bell', label: 'This is **bold** </br>and <strong>strong</strong> for icon shape'`; | ||||
|             if (form) { | ||||
|               flowchartCode += `, form: '${form}'`; | ||||
|             } | ||||
|             if (pos) { | ||||
|               flowchartCode += `, pos: '${pos}'`; | ||||
|             } | ||||
|             flowchartCode += ` }\n`; | ||||
|             imgSnapshotTest(flowchartCode, { | ||||
|               look, | ||||
|               htmlLabels: false, | ||||
|               flowchart: { htmlLabels: false }, | ||||
|             }); | ||||
|           }); | ||||
|  | ||||
|           it(`with styles`, () => { | ||||
|             let flowchartCode = `flowchart ${direction}\n`; | ||||
|             flowchartCode += `  nA --> nAA@{ icon: 'fa:bell', label: 'new icon shape'`; | ||||
|             if (form) { | ||||
|               flowchartCode += `, form: '${form}'`; | ||||
|             } | ||||
|             if (pos) { | ||||
|               flowchartCode += `, pos: '${pos}'`; | ||||
|             } | ||||
|             flowchartCode += ` }\n`; | ||||
|             flowchartCode += `  style nAA fill:#f9f,stroke:#333,stroke-width:4px \n`; | ||||
|             imgSnapshotTest(flowchartCode, { look }); | ||||
|           }); | ||||
|  | ||||
|           it(`with classDef`, () => { | ||||
|             let flowchartCode = `flowchart ${direction}\n`; | ||||
|             flowchartCode += `  classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`; | ||||
|             flowchartCode += `  nA --> nAA@{ icon: 'fa:bell', label: 'new icon shape'`; | ||||
|             if (form) { | ||||
|               flowchartCode += `, form: '${form}'`; | ||||
|             } | ||||
|             if (pos) { | ||||
|               flowchartCode += `, pos: '${pos}'`; | ||||
|             } | ||||
|             flowchartCode += ` }\n`; | ||||
|             flowchartCode += `  nAA:::customClazz\n`; | ||||
|             imgSnapshotTest(flowchartCode, { look }); | ||||
|           }); | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| describe('Test iconShape with different h', () => { | ||||
|   it('with different h', () => { | ||||
|     let flowchartCode = `flowchart TB\n`; | ||||
|     const icon = 'fa:bell'; | ||||
|     const iconHeight = 64; | ||||
|     flowchartCode += `  nA --> nAA@{ icon: '${icon}', label: 'icon with different h', h: ${iconHeight} }\n`; | ||||
|     imgSnapshotTest(flowchartCode); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| describe('Test colored iconShape', () => { | ||||
|   it('with no styles', () => { | ||||
|     let flowchartCode = `flowchart TB\n`; | ||||
|     const icon = 'fluent-emoji:tropical-fish'; | ||||
|     flowchartCode += `  nA --> nAA@{ icon: '${icon}', form: 'square', label: 'icon with color' }\n`; | ||||
|     imgSnapshotTest(flowchartCode); | ||||
|   }); | ||||
|  | ||||
|   it('with styles', () => { | ||||
|     let flowchartCode = `flowchart TB\n`; | ||||
|     const icon = 'fluent-emoji:tropical-fish'; | ||||
|     flowchartCode += `  nA --> nAA@{ icon: '${icon}', form: 'square', label: 'icon with color' }\n`; | ||||
|     flowchartCode += `  style nAA fill:#f9f,stroke:#333,stroke-width:4px \n`; | ||||
|     imgSnapshotTest(flowchartCode); | ||||
|   }); | ||||
| }); | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user