Compare commits

..

75 Commits

Author SHA1 Message Date
Sidharth Vinod
1f07a781e4 Merge pull request #6660 from mermaid-js/changeset-release/master
Version Packages
2025-06-20 10:32:20 +05:30
github-actions[bot]
3132387ede Version Packages 2025-06-19 12:43:20 +00:00
Sidharth Vinod
f41b4c96f9 Update bitter-colts-remain.md 2025-06-19 18:11:16 +05:30
Sidharth Vinod
26bd456c01 chore: Revert manual version upgrade 2025-06-19 13:59:46 +05:30
Sidharth Vinod
8ef7040a8d chore: Update changeset 2025-06-19 13:55:00 +05:30
Sidharth Vinod
e7ec8ed527 fix: Copy changelog to tiny 2025-06-17 16:35:46 +05:30
Sidharth Vinod
7c1e655675 chore: Update changeset to minor bump 2025-06-17 16:34:31 +05:30
Sidharth Vinod
df2ccaa14b Merge pull request #6658 from mermaid-js/develop
Pre-Release
2025-06-17 16:13:14 +05:30
Sidharth Vinod
797ba43d6e chore: lychee ignore mermaid-preview 2025-06-17 16:05:45 +05:30
Sidharth Vinod
d628a971ef Merge branch 'master' into develop
* master: (46 commits)
  Add canonicals to pages migrated to docs.mermaidchart.com
  chore: minor update
  chore: dynamically change text size depending on route
  chore: update text size to lg
  chore: remove tooltip
  chore: update as per PR comment
  chore: Update banner ad
  chore: Use mermaid.live as fallback links in button
  chore: Tweak shadows
  chore : fix UI
  chore: Update URL
  chore: UI refactor
  chore: Update UI design
  chore: removed index and used flag
  chore: code refactor
  chore: Update as per PR suggestion.
  chore: add event listener for button.
  Feat: Add editor popup modal
  Skip foswiki link check
  feat: Pause animation when hovering
  ...
2025-06-17 15:53:29 +05:30
shubham-mermaid
8049b6aef8 Merge pull request #6616 from mermaid-js/fix/timeline-vertical-lines-6610
fix(timeline): ensure consistent vertical line lengths with visible arrowheads
2025-06-17 10:05:42 +00:00
shubham-mermaid
84802118be Merge branch 'develop' into fix/timeline-vertical-lines-6610 2025-06-17 15:18:44 +05:30
shubham-mermaid
e2ef425245 Merge branch 'develop' into fix/timeline-vertical-lines-6610 2025-06-17 14:48:24 +05:30
shubham-mermaid
da98732961 Sync with develop for pnpm-lock 2025-06-17 14:43:13 +05:30
shubham-mermaid
efa11a7325 Merge pull request #6648 from mermaid-js/6647-handle-node-order-elk
#6647 Make elk respect the order of nodes based from the code
2025-06-17 09:08:31 +00:00
Ashish Jain
e5f6ea0b13 Add canonicals to pages migrated to docs.mermaidchart.com 2025-06-17 10:16:46 +02:00
Sidharth Vinod
9562a769db Merge pull request #6624 from mermaid-js/rajat-ht/chore-update-banner-ad
chore: update banner ad
2025-06-16 11:09:18 +05:30
rajat-ht
5cfda33d04 chore: minor update 2025-06-13 22:57:21 +05:30
Knut Sveidqvist
e682172823 Reverting the elk.layered.wrapping.validify.strategy setting 2025-06-11 17:06:17 +02:00
Knut Sveidqvist
85c5b9b4c0 #6647 Make elk respect the order of nodes based from the code 2025-06-11 17:00:29 +02:00
rajat-ht
1e68ed9cc1 chore: dynamically change text size depending on route 2025-05-30 22:50:55 +05:30
rajat-ht
1337c60784 chore: update text size to lg 2025-05-29 22:34:40 +05:30
rajat-ht
a12d1f731a chore: remove tooltip 2025-05-29 17:16:19 +05:30
rajat-ht
b6ba9b79df chore: update as per PR comment 2025-05-29 17:06:25 +05:30
rajat-ht
ce227b5519 chore: Update banner ad 2025-05-29 16:22:20 +05:30
Sidharth Vinod
c4de3cdc57 Merge pull request #6609 from mermaid-js/rajat-ht/feat-add-editor-popup-modal
Feat: Add editor popup modal
2025-05-28 21:54:21 +05:30
Sidharth Vinod
ef449a601b chore: Use mermaid.live as fallback links in button 2025-05-28 21:53:24 +05:30
Sidharth Vinod
8e84a1a8af chore: Tweak shadows 2025-05-28 21:47:21 +05:30
rajat-ht
a3552e7936 chore : fix UI 2025-05-28 21:27:55 +05:30
rajat-ht
646cdf0d32 chore: Update URL 2025-05-28 02:32:32 +05:30
rajat-ht
99048bfa01 chore: UI refactor 2025-05-27 18:34:32 +05:30
Ashish Jain
ca80f719ea fix(timeline): ensure consistent vertical line lengths with visible arrowheads
- Fixed inconsistent vertical line lengths in timeline diagrams
- All vertical lines now extend to same depth regardless of event count
- Added sufficient padding for clear visibility of dashed lines and arrowheads
- Added comprehensive test case and demo example

Fixes #6610
2025-05-26 12:16:58 +02:00
rajat-ht
0cabc6e8ed chore: Update UI design 2025-05-23 22:14:18 +05:30
rajat-ht
5fe0f53179 chore: removed index and used flag 2025-05-23 16:53:06 +05:30
rajat-ht
e2e3cd2bbd chore: code refactor 2025-05-23 16:30:59 +05:30
rajat-ht
ffb9016d65 Merge branch 'master' of https://github.com/mermaid-js/mermaid into rajat-ht/feat-add-editor-popup-modal 2025-05-23 15:55:04 +05:30
rajat-ht
3c03f289b4 chore: Update as per PR suggestion. 2025-05-23 15:49:12 +05:30
Sidharth Vinod
c6035936b6 Merge pull request #6594 from mermaid-js/rajat-ht/chore-update-tooltip
Chore: Update tooltip
2025-05-22 12:35:56 +05:30
rajat-ht
f23a26e528 chore: add event listener for button. 2025-05-21 17:11:29 +05:30
rajat-ht
48e6e60753 Feat: Add editor popup modal 2025-05-20 20:00:30 +05:30
Sidharth Vinod
2372cd7b21 Skip foswiki link check 2025-05-20 15:31:12 +05:30
Sidharth Vinod
51f0cb2892 feat: Pause animation when hovering 2025-05-20 15:17:56 +05:30
Sidharth Vinod
14e3d77c75 fix: Specifically hide tooltip, instead of using a very specific selector to show 2025-05-20 15:13:38 +05:30
Sidharth Vinod
875fef691b fix: Use CSS instead of JS to hide/show tooltip 2025-05-20 15:01:39 +05:30
rajat-ht
1f44143936 Chore update tooltip 2025-05-13 15:28:24 +05:30
Sidharth Vinod
d702a7164e Merge pull request #6574 from mermaid-js/sidv/updateImages
fix: Update images
2025-05-07 15:13:12 +05:30
Sidharth Vinod
8a0bc64282 Merge pull request #6573 from mermaid-js/sidv/fixAvatarFetch
chore: Batch avatar download to avoid GitHub rate limiting.
2025-05-07 15:12:08 +05:30
Sidharth Vinod
ac0283ff97 fix: Update images 2025-05-07 15:10:22 +05:30
Sidharth Vinod
84faddc9e5 chore: Batch avatar download to avoid GitHub rate limiting. 2025-05-07 12:24:29 +05:30
Sidharth Vinod
5e3f21f572 Merge pull request #6570 from mermaid-js/sidv/ogTags
feat: Add og tags
2025-05-06 20:51:26 +05:30
Sidharth Vinod
05c74ed9ce feat: Add og tags 2025-05-06 20:44:15 +05:30
Sidharth Vinod
b106346119 Merge pull request #6568 from mermaid-js/announcement-bar-update
chore: Update announcement bar
2025-05-06 10:43:04 +05:30
rajat-ht
c09232c44d chore: update utm_campaign 2025-05-06 03:52:21 +05:30
rajat-ht
714496af6d chore: minor change 2025-05-06 03:46:01 +05:30
rajat-ht
5d61a30237 chore: update as per suggestion 2025-05-06 03:30:49 +05:30
rajat-ht
02ff3c1216 chore: Update announcement bar 2025-05-05 21:17:09 +05:30
Sidharth Vinod
cee43a45f6 Merge pull request #6355 from mermaid-js/update-homepage
DOCS: Update homepage
2025-04-24 21:12:40 +05:30
Sidharth Vinod
8005262d76 Merge branch 'master' into update-homepage
* master: (242 commits)
  update logic
  Version Packages
  Add missing changeset for requirement diagram fix
  Update mermaid version palceholder
  test(e2e): fix Cypress config for Node.JS v20.19.0
  fix: restore curve type configuration functionality for flowcharts (#6193)
  update promo bar content
  rework contains geometry to avoid clipping
  try arrowhead
  #6380: move requirements containment arrow to start
  ⚗️ Add radar renderer tests
  🩹 Fix: text-color ➙ color
  [autofix.ci] apply automated fixes
  🆙 Run pnpm changeset
  🖼️ Add Radar Demo with custom opacity
  📚 Add documentation
  🖼️ Add Radar Demo
  ⚗️ Add Cypress test
  🎨 Add themes for radar
  📊 Add radar chart
  ...
2025-04-09 19:13:35 +05:30
Sidharth Vinod
a3cf9de01b fix: Move Tooltip as vue component 2025-04-09 15:50:27 +05:30
Sidharth Vinod
9a90aca8e6 chore: Add mermaid logo 2025-04-09 15:35:58 +05:30
Sidharth Vinod
963b1e3ec5 chore: Fix intro link 2025-04-09 15:32:31 +05:30
Sidharth Vinod
208d29e3d6 chore: Update live editor link 2025-04-09 15:31:25 +05:30
Sidharth Vinod
5c865e7b71 chore: Remove duplicate cspell 2025-04-09 15:29:08 +05:30
Sidharth Vinod
7ce9dbcece docs: Add target to external link 2025-04-09 15:27:53 +05:30
Sidharth Vinod
ff786b4ef8 docs: Update homepage links 2025-04-09 15:25:02 +05:30
Sidharth Vinod
b81b665137 feat: Add tooltip for MermaidChart links 2025-04-09 15:24:23 +05:30
Sidharth Vinod
a9be1a4146 Merge pull request #6398 from mermaid-js/update-content
DOCS: update promo bar content
2025-04-02 02:22:53 -07:00
Steph
617c7393bf update logic 2025-04-01 23:41:43 -07:00
Steph
ce68da3fee update promo bar content 2025-03-20 20:11:59 -07:00
Steph
fa4d4806fa update button text 2025-03-18 13:08:12 -07:00
Steph
c8f01b14a8 update links 2025-03-11 13:21:07 -07:00
Steph
91931531a6 update link 2025-03-06 11:48:17 -08:00
Steph
50e4641666 minor updates 2025-03-05 22:26:12 -08:00
Steph
1bed6432ac update styling and add assets 2025-03-05 21:24:38 -08:00
Steph
11f7f426cb verbiage updates 2025-02-28 08:05:42 -08:00
154 changed files with 2463 additions and 17440 deletions

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
feat: Add Vertical Line To Gantt Plot At Specified Time

View File

@@ -1,5 +0,0 @@
---
'@mermaid-js/mermaid-zenuml': major
---
Upgraded the dependency @zenuml/core

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
Fix stroke styles for ER diagram to correctly apply path and row-specific styles

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
chore: Convert StateDB into TypeScript

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Remove incorrect `style="undefined;"` attributes in some Mermaid diagrams

View File

@@ -1,7 +0,0 @@
---
'@mermaid-js/mermaid-zenuml': patch
---
chore: bump minimum ZenUML version to 3.23.28
commit: 9d06d8f31e7f12af9e9e092214f907f2dc93ad75

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
feat: Add support for styling Journey Diagram title (color, font-family, and font-size)

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
FontAwesome icons can now be embedded as SVGs in flowcharts if they are registered via `mermaid.registerIconPacks`.

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
Added support for the click directive in stateDiagram syntax

View File

@@ -1,6 +0,0 @@
---
'mermaid': minor
'@mermaid-js/parser': minor
---
feat: Add shorter `+<count>: Label` syntax in packet diagram

View File

@@ -1,6 +0,0 @@
---
'mermaid': patch
'@mermaid-js/parser': patch
---
Refactor grammar so that title don't break Architecture Diagrams

View File

@@ -1,5 +0,0 @@
---
'mermaid': major
---
fix: allow sequence diagram arrows with a trailing colon but no message

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
feat: Dynamically Render Data Labels Within Bar Charts

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: allow colons in events

View File

@@ -1,7 +0,0 @@
---
'@mermaid-js/mermaid-zenuml': patch
---
fix(zenuml): limit `peerDependencies` to Mermaid v10 and v11
commit: 0ad44c12feead9d20c6a870a49327ada58d6e657

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Fix incomplete string escaping in URL manipulation logic when `arrowMarkerAbsolute: true` by ensuring all unsafe characters are escaped.

View File

@@ -47,6 +47,7 @@ edgesep
EMPTYSTR
enddate
ERDIAGRAM
eslint
flatmap
forwardable
frontmatter

View File

@@ -2,8 +2,10 @@
Ashish Jain
cpettitt
Dong Cai
knsv
Knut Sveidqvist
Nikolay Rozhkov
Peng Xiao
Per Brolin
Sidharth Vinod
subhash-halder
Vinod Sidharth

View File

@@ -13,11 +13,10 @@ gitgraph
gzipped
handDrawn
kanban
knsv
Knut
marginx
marginy
Markdownish
mermaidchart
mermaidjs
mindmap
mindmaps
@@ -35,7 +34,6 @@ sandboxed
siebling
statediagram
substate
Sveidqvist
unfixable
Viewbox
viewports

View File

@@ -1,5 +1,5 @@
import { build } from 'esbuild';
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
import { cp, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
import { packageOptions } from '../.build/common.js';
import { generateLangium } from '../.build/generateLangium.js';
import type { MermaidBuildOptions } from './util.js';
@@ -90,6 +90,7 @@ const buildTinyMermaid = async () => {
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 () => {

1
.github/lychee.toml vendored
View File

@@ -53,6 +53,7 @@ exclude = [
"https://huehive.co",
"https://foswiki.org",
"https://www.gnu.org",
"https://mermaid-preview.com"
]
# Exclude all private IPs from checking.

View File

@@ -161,4 +161,68 @@ describe('Timeline diagram', () => {
{}
);
});
it('11: should render timeline with many stacked events and proper timeline line length', () => {
imgSnapshotTest(
`timeline
title Medical Device Lifecycle
section Pre-Development
Quality Management System : Regulatory Compliance : Risk Management
section Development
Management Responsibility : Planning Activities : Human Resources
Resource Management : Management Reviews : Infrastructure
section Post-Development
Product Realization Activities : Planning Activities : Customer-related Processes
Post-Production Activities : Feedback : Complaints : Adverse Events
: Research and Development : Purchasing Activities
: Production Activities : Installation Activities
: Servicing Activities : Post-Market Surveillance
`,
{}
);
});
it('12: should render timeline with proper vertical line lengths for all columns', () => {
imgSnapshotTest(
`---
config:
theme: base
themeVariables:
fontFamily: Fira Sans
fontSize: 17px
cScale0: '#b3cde0'
cScale1: '#f49090'
cScale2: '#85d5b8'
---
timeline
title Medical Device Lifecycle
section Planning
Quality Management System (4): Regulatory Compliance (4.1.1)
: Risk Management (4.1.2)
Management Resposibility (5): Planning Activities (5.4)
: Management Reviews (5.6)
Resource Management (6): Human Resources (6.2)
: Infrastructure (6.3)
section Realization
Research and Development (7.3): RnD Planning (7.3.2)
: Inputs (7.3.3)
: Outputs (7.3.4)
: Review (7.3.5)
: Verification (7.3.6)
: Validation (7.3.7)
Purchasing (7.4): Purchasing Process (7.4.1)
: Purchasing Information (7.4.2)
Production (7.5): Production Activities (7.5.1)
: Production Feedback (8.2.1)
Installation (7.5.3): Installation Activities (7.5.3)
Servicing (7.5.4): Servicing Activities (7.5.4)
section Post-Production
Post-Market Activities (8): Feedback (8.2.1)
: Complaints (8.2.2)
: Adverse Events (8.2.3)
`,
{}
);
});
});

View File

@@ -106,15 +106,13 @@
<body>
<pre id="diagram4" class="mermaid">
flowchart RL
flowchart LR
AB["apa@apa@"] --> B(("`apa@apa`"))
</pre>
<pre id="diagram4" class="mermaid">
flowchart
D(("for D"))
</pre>
<h1>below</h1>
<pre id="diagram4" class="mermaid">
flowchart LR
A e1@==> B
@@ -253,7 +251,7 @@ flowchart LR
A{A} --> B & C
</pre
>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk

View File

@@ -1,31 +0,0 @@
import { FlowDB } from './packages/mermaid/src/diagrams/flowchart/flowDb.ts';
import flow from './packages/mermaid/src/diagrams/flowchart/parser/flowParserAdapter.ts';
// Set up the test environment
flow.yy = new FlowDB();
flow.yy.clear();
console.log('=== Testing basic edge parsing ===');
console.log('Input: "graph TD;A-->B;"');
try {
const result = flow.parse('graph TD;A-->B;');
console.log('Parse result:', result);
const vertices = flow.yy.getVertices();
const edges = flow.yy.getEdges();
console.log('Vertices:', vertices);
console.log('Vertices size:', vertices.size);
console.log('Vertices keys:', Array.from(vertices.keys()));
console.log('Edges:', edges);
console.log('Edges length:', edges.length);
// Check specific vertices
console.log('Vertex A:', vertices.get('A'));
console.log('Vertex B:', vertices.get('B'));
} catch (error) {
console.error('Parse error:', error);
console.error('Error stack:', error.stack);
}

View File

@@ -1,27 +0,0 @@
// Debug script for interpolate functionality
import { FlowDB } from './packages/mermaid/src/diagrams/flowchart/flowDb.js';
import flow from './packages/mermaid/src/diagrams/flowchart/parser/flowParserAdapter.js';
// Set up test
flow.yy = new FlowDB();
flow.yy.clear();
console.log('Testing interpolate functionality...');
try {
const input = 'graph TD\nA-->B\nlinkStyle default interpolate basis';
console.log('Input:', input);
const result = flow.parse(input);
console.log('Parse result:', result);
const edges = flow.yy.getEdges();
console.log('Edges:', edges);
console.log('edges.defaultInterpolate:', edges.defaultInterpolate);
// Check if updateLinkInterpolate method exists
console.log('updateLinkInterpolate method exists:', typeof flow.yy.updateLinkInterpolate);
} catch (error) {
console.error('Error:', error);
}

View File

@@ -23,6 +23,23 @@
1940 : fourth step : fifth step
</pre>
<h2>Medical Device Lifecycle Timeline</h2>
<pre class="mermaid">
timeline
title Medical Device Lifecycle
section Planning
Quality Management System (4) : Regulatory Compliance (4.1) : Risk Management (4.1.3) : Management Review (5.6) : Infrastructure (6.3)
Management Responsibility (5) : Planning Activities (5.2) : Human Resources (6.2) : RnD Planning (7.3.2) : Purchasing Process (7.4.1) : Production Activities (7.5.1) : Installation Activities (7.5.3) : Servicing Activities (7.5.4)
section Realization
Research and Development (7.3) : Inputs (7.3.3) : Outputs (7.3.4) : Review (7.3.5) : Verification (7.3.6) : Validation (7.3.7)
Purchasing (7.4) : Purchasing Information (7.4.2) : Production Feedback (8.2.1)
Production (7.5) : Production Feedback (8.2.1)
Installation (7.5.3) : Installation Activities (7.5.3)
Servicing (7.5.4) : Servicing Activities (7.5.4)
section Post-Production
Post-Market Activities (8) : Feedback (8.2.1) : Complaints (8.2.2) : Adverse Events (8.2.3)
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({

View File

@@ -0,0 +1,9 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="48" height="48" rx="12" fill="#E0095F"/>
<mask id="mask0_1_24" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="9" y="9" width="30" height="30">
<rect x="9" y="9" width="30" height="30" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_1_24)">
<path d="M24 30.875C25.2083 30.875 26.3229 30.5833 27.3438 30C28.3646 29.4167 29.1875 28.625 29.8125 27.625C29.9375 27.375 29.9271 27.125 29.7812 26.875C29.6354 26.625 29.4167 26.5 29.125 26.5H18.875C18.5833 26.5 18.3646 26.625 18.2188 26.875C18.0729 27.125 18.0625 27.375 18.1875 27.625C18.8125 28.625 19.6406 29.4167 20.6719 30C21.7031 30.5833 22.8125 30.875 24 30.875ZM20.125 21.4375L20.7813 22.0938C20.9688 22.2813 21.1875 22.375 21.4375 22.375C21.6875 22.375 21.9063 22.2813 22.0938 22.0938C22.2813 21.9063 22.3698 21.6875 22.3594 21.4375C22.349 21.1875 22.2604 20.9688 22.0938 20.7813L21 19.6563C20.75 19.4063 20.4531 19.2812 20.1094 19.2812C19.7656 19.2812 19.4688 19.4063 19.2188 19.6563L18.0938 20.7813C17.9063 20.9688 17.8125 21.1875 17.8125 21.4375C17.8125 21.6875 17.9063 21.9063 18.0938 22.0938C18.2604 22.2604 18.474 22.349 18.7344 22.3594C18.9948 22.3698 19.2188 22.2917 19.4063 22.125L20.125 21.4375ZM27.875 21.4375L28.5938 22.125C28.7813 22.2917 29 22.375 29.25 22.375C29.5 22.375 29.7188 22.2813 29.9063 22.0938C30.0938 21.9063 30.1875 21.6875 30.1875 21.4375C30.1875 21.1875 30.0938 20.9688 29.9063 20.7813L28.7812 19.6563C28.5312 19.4063 28.2344 19.2812 27.8906 19.2812C27.5469 19.2812 27.25 19.4063 27 19.6563L25.875 20.7813C25.7083 20.9688 25.625 21.1875 25.625 21.4375C25.625 21.6875 25.7188 21.9063 25.9063 22.0938C26.0938 22.2813 26.3125 22.375 26.5625 22.375C26.8125 22.375 27.0312 22.2813 27.2187 22.0938L27.875 21.4375ZM24 36.5C22.2708 36.5 20.6458 36.1719 19.125 35.5156C17.6042 34.8594 16.2812 33.9688 15.1562 32.8438C14.0312 31.7188 13.1406 30.3958 12.4844 28.875C11.8281 27.3542 11.5 25.7292 11.5 24C11.5 22.2708 11.8281 20.6458 12.4844 19.125C13.1406 17.6042 14.0312 16.2812 15.1562 15.1562C16.2812 14.0312 17.6042 13.1406 19.125 12.4844C20.6458 11.8281 22.2708 11.5 24 11.5C25.7292 11.5 27.3542 11.8281 28.875 12.4844C30.3958 13.1406 31.7188 14.0312 32.8438 15.1562C33.9688 16.2812 34.8594 17.6042 35.5156 19.125C36.1719 20.6458 36.5 22.2708 36.5 24C36.5 25.7292 36.1719 27.3542 35.5156 28.875C34.8594 30.3958 33.9688 31.7188 32.8438 32.8438C31.7188 33.9688 30.3958 34.8594 28.875 35.5156C27.3542 36.1719 25.7292 36.5 24 36.5ZM24 34C26.7917 34 29.1563 33.0313 31.0938 31.0938C33.0313 29.1563 34 26.7917 34 24C34 21.2083 33.0313 18.8438 31.0938 16.9063C29.1563 14.9688 26.7917 14 24 14C21.2083 14 18.8438 14.9688 16.9063 16.9063C14.9688 18.8438 14 21.2083 14 24C14 26.7917 14.9688 29.1563 16.9063 31.0938C18.8438 33.0313 21.2083 34 24 34Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,9 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="48" height="48" rx="12" fill="#E0095F"/>
<mask id="mask0_2_44" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="9" y="9" width="30" height="30">
<rect x="9" y="9" width="30" height="30" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_2_44)">
<path d="M32.75 27.75C32.3958 27.75 32.099 27.6302 31.8594 27.3906C31.6198 27.151 31.5 26.8542 31.5 26.5C31.5 26.1458 31.6198 25.849 31.8594 25.6094C32.099 25.3698 32.3958 25.25 32.75 25.25H34C34.3542 25.25 34.651 25.3698 34.8906 25.6094C35.1302 25.849 35.25 26.1458 35.25 26.5C35.25 26.8542 35.1302 27.151 34.8906 27.3906C34.651 27.6302 34.3542 27.75 34 27.75H32.75ZM32.75 32.75C32.3958 32.75 32.099 32.6302 31.8594 32.3906C31.6198 32.151 31.5 31.8542 31.5 31.5C31.5 31.1458 31.6198 30.849 31.8594 30.6094C32.099 30.3698 32.3958 30.25 32.75 30.25H34C34.3542 30.25 34.651 30.3698 34.8906 30.6094C35.1302 30.849 35.25 31.1458 35.25 31.5C35.25 31.8542 35.1302 32.151 34.8906 32.3906C34.651 32.6302 34.3542 32.75 34 32.75H32.75ZM26.5 34C25.8125 34 25.224 33.7552 24.7344 33.2656C24.2448 32.776 24 32.1875 24 31.5H22.75C22.3958 31.5 22.099 31.3802 21.8594 31.1406C21.6198 30.901 21.5 30.6042 21.5 30.25V27.75C21.5 27.3958 21.6198 27.099 21.8594 26.8594C22.099 26.6198 22.3958 26.5 22.75 26.5H24C24 25.8125 24.2448 25.224 24.7344 24.7344C25.224 24.2448 25.8125 24 26.5 24H29C29.3542 24 29.651 24.1198 29.8906 24.3594C30.1302 24.599 30.25 24.8958 30.25 25.25V32.75C30.25 33.1042 30.1302 33.401 29.8906 33.6406C29.651 33.8802 29.3542 34 29 34H26.5ZM17.75 30.25C16.375 30.25 15.1979 29.7604 14.2188 28.7812C13.2396 27.8021 12.75 26.625 12.75 25.25C12.75 23.875 13.2396 22.6979 14.2188 21.7188C15.1979 20.7396 16.375 20.25 17.75 20.25H19.625C20.1458 20.25 20.5885 20.0677 20.9531 19.7031C21.3177 19.3385 21.5 18.8958 21.5 18.375C21.5 17.8542 21.3177 17.4115 20.9531 17.0469C20.5885 16.6823 20.1458 16.5 19.625 16.5H15.25C14.8958 16.5 14.599 16.3802 14.3594 16.1406C14.1198 15.901 14 15.6042 14 15.25C14 14.8958 14.1198 14.599 14.3594 14.3594C14.599 14.1198 14.8958 14 15.25 14H19.625C20.8333 14 21.8646 14.4271 22.7187 15.2812C23.5729 16.1354 24 17.1667 24 18.375C24 19.5833 23.5729 20.6146 22.7187 21.4687C21.8646 22.3229 20.8333 22.75 19.625 22.75H17.75C17.0625 22.75 16.474 22.9948 15.9844 23.4844C15.4948 23.974 15.25 24.5625 15.25 25.25C15.25 25.9375 15.4948 26.526 15.9844 27.0156C16.474 27.5052 17.0625 27.75 17.75 27.75H19C19.3542 27.75 19.651 27.8698 19.8906 28.1094C20.1302 28.349 20.25 28.6458 20.25 29C20.25 29.3542 20.1302 29.651 19.8906 29.8906C19.651 30.1302 19.3542 30.25 19 30.25H17.75Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,9 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="48" height="48" rx="12" fill="#E0095F"/>
<mask id="mask0_2_49" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="9" y="9" width="30" height="30">
<rect x="9" y="9" width="30" height="30" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_2_49)">
<path d="M22.75 32.75V28.875C21.7292 28.6458 20.8177 28.2135 20.0156 27.5781C19.2135 26.9427 18.625 26.1458 18.25 25.1875C16.6875 25 15.3802 24.3177 14.3281 23.1406C13.276 21.9635 12.75 20.5833 12.75 19V17.75C12.75 17.0625 12.9948 16.474 13.4844 15.9844C13.974 15.4948 14.5625 15.25 15.25 15.25H17.75C17.75 14.5625 17.9948 13.974 18.4844 13.4844C18.974 12.9948 19.5625 12.75 20.25 12.75H27.75C28.4375 12.75 29.026 12.9948 29.5156 13.4844C30.0052 13.974 30.25 14.5625 30.25 15.25H32.75C33.4375 15.25 34.026 15.4948 34.5156 15.9844C35.0052 16.474 35.25 17.0625 35.25 17.75V19C35.25 20.5833 34.724 21.9635 33.6719 23.1406C32.6198 24.3177 31.3125 25 29.75 25.1875C29.375 26.1458 28.7865 26.9427 27.9844 27.5781C27.1823 28.2135 26.2708 28.6458 25.25 28.875V32.75H29C29.3542 32.75 29.651 32.8698 29.8906 33.1094C30.1302 33.349 30.25 33.6458 30.25 34C30.25 34.3542 30.1302 34.651 29.8906 34.8906C29.651 35.1302 29.3542 35.25 29 35.25H19C18.6458 35.25 18.349 35.1302 18.1094 34.8906C17.8698 34.651 17.75 34.3542 17.75 34C17.75 33.6458 17.8698 33.349 18.1094 33.1094C18.349 32.8698 18.6458 32.75 19 32.75H22.75ZM17.75 22.5V17.75H15.25V19C15.25 19.7917 15.4792 20.5052 15.9375 21.1406C16.3958 21.776 17 22.2292 17.75 22.5ZM24 26.5C25.0417 26.5 25.9271 26.1354 26.6562 25.4062C27.3854 24.6771 27.75 23.7917 27.75 22.75V15.25H20.25V22.75C20.25 23.7917 20.6146 24.6771 21.3438 25.4062C22.0729 26.1354 22.9583 26.5 24 26.5ZM30.25 22.5C31 22.2292 31.6042 21.776 32.0625 21.1406C32.5208 20.5052 32.75 19.7917 32.75 19V17.75H30.25V22.5Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 200 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 200 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<mask
id="mask0_89_26528"
style="mask-type: alpha"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect width="20" height="20" fill="currentColor" />
</mask>
<g mask="url(#mask0_89_26528)">
<path
d="M10.25 12.9792C11.2083 12.9792 12.0312 12.6667 12.7188 12.0417C13.4062 11.4167 13.75 10.6671 13.75 9.79292C13.75 9.01431 13.4965 8.35069 12.9896 7.80208C12.4826 7.25347 11.8747 6.97917 11.1656 6.97917C10.5248 6.97917 9.98285 7.1875 9.53979 7.60417C9.0966 8.02083 8.875 8.52083 8.875 9.10417C8.875 9.38194 8.92528 9.63847 9.02583 9.87375C9.12639 10.109 9.27056 10.3247 9.45833 10.5208L10.5208 9.45833C10.4722 9.41667 10.4358 9.36458 10.4115 9.30208C10.3872 9.23958 10.375 9.17687 10.375 9.11396C10.375 8.94076 10.4514 8.79167 10.6042 8.66667C10.7569 8.54167 10.9444 8.47917 11.1667 8.47917C11.4583 8.47917 11.7118 8.60806 11.9271 8.86583C12.1424 9.12361 12.25 9.43222 12.25 9.79167C12.25 10.25 12.0521 10.6458 11.6562 10.9792C11.2604 11.3125 10.7933 11.4792 10.255 11.4792C9.57111 11.4792 8.98264 11.191 8.48958 10.6146C7.99653 10.0382 7.75 9.35417 7.75 8.5625C7.75 8.13514 7.82986 7.72625 7.98958 7.33583C8.14931 6.94528 8.38194 6.59722 8.6875 6.29167L7.625 5.25C7.18056 5.69444 6.84028 6.20139 6.60417 6.77083C6.36806 7.34028 6.25 7.9375 6.25 8.5625C6.25 9.78472 6.63889 10.8264 7.41667 11.6875C8.19444 12.5486 9.13889 12.9792 10.25 12.9792ZM5.5 18V14.3542C4.70833 13.6875 4.09375 12.8849 3.65625 11.9465C3.21875 11.008 3 10.0189 3 8.97917C3 7.04056 3.68257 5.39271 5.04771 4.03563C6.41285 2.67854 8.07056 2 10.0208 2C11.5347 2 12.8958 2.40625 14.1042 3.21875C15.3125 4.03125 16.1111 5.09028 16.5 6.39583L17.875 11.0417C17.9444 11.2844 17.9062 11.5049 17.7604 11.7029C17.6146 11.901 17.4167 12 17.1667 12H16V14.5C16 14.9125 15.8531 15.2656 15.5594 15.5594C15.2656 15.8531 14.9125 16 14.5 16H12.5V18H11V14.5H14.5V10.5H16.1667L15.0833 6.89583C14.7778 5.89583 14.1562 5.07986 13.2188 4.44792C12.2812 3.81597 11.2153 3.5 10.0208 3.5C8.47917 3.5 7.17361 4.03215 6.10417 5.09646C5.03472 6.16076 4.5 7.455 4.5 8.97917C4.5 9.79764 4.67361 10.5751 5.02083 11.3117C5.36806 12.0483 5.85417 12.6806 6.47917 13.2083L7 13.6667V18H5.5Z"
fill="currentColor"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<mask
id="mask0_89_26616"
style="mask-type: alpha"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect width="20" height="20" fill="currentColor" />
</mask>
<g mask="url(#mask0_89_26616)">
<path
d="M4 16.9999V14.4999H5.875L4.4375 8.77077C3.99306 8.56244 3.64236 8.25341 3.38542 7.84369C3.12847 7.43396 3 6.98605 3 6.49993C3 5.7916 3.24306 5.19785 3.72917 4.71868C4.21528 4.23952 4.8125 3.99993 5.52083 3.99993C6.0625 3.99993 6.55208 4.15966 6.98958 4.4791C7.42708 4.79855 7.72222 5.22216 7.875 5.74993H9.5V4.74993C9.5 4.5416 9.57292 4.36452 9.71875 4.21868C9.86458 4.07285 10.0417 3.99993 10.25 3.99993C10.4583 3.99993 10.6354 4.07285 10.7812 4.21868C10.9271 4.36452 11 4.5416 11 4.74993V4.89577L13 3.18743C13.1111 3.09021 13.2361 3.03466 13.375 3.02077C13.5139 3.00688 13.6528 3.02771 13.7917 3.08327L16.5625 4.33327C16.7569 4.4166 16.8924 4.55202 16.9688 4.73952C17.0451 4.92702 17.0347 5.11799 16.9375 5.31243C16.8542 5.49299 16.7153 5.61799 16.5208 5.68743C16.3264 5.75688 16.1319 5.75688 15.9375 5.68743L13.6042 4.64577L11.4167 6.49993L13.6042 8.37494L15.9375 7.33327C16.1319 7.24993 16.3229 7.24299 16.5104 7.31244C16.6979 7.38188 16.8403 7.51382 16.9375 7.70827C17.0208 7.88882 17.0243 8.06938 16.9479 8.24994C16.8715 8.43049 16.7431 8.57632 16.5625 8.68744L13.7917 9.93744C13.6667 10.0069 13.5312 10.0312 13.3854 10.0104C13.2396 9.98952 13.1111 9.93049 13 9.83327L11 8.12494V8.24994C11 8.45827 10.9271 8.63535 10.7812 8.78119C10.6354 8.92702 10.4583 8.99994 10.25 8.99994C10.0417 8.99994 9.86458 8.92702 9.71875 8.78119C9.57292 8.63535 9.5 8.45827 9.5 8.24994V7.24994H7.875C7.81944 7.44438 7.73958 7.62146 7.63542 7.78119C7.53125 7.94091 7.40972 8.09716 7.27083 8.24994L10.8542 14.4999H14V16.9999H4ZM5.5 7.62494C5.81944 7.62494 6.08681 7.5173 6.30208 7.30202C6.51736 7.08674 6.625 6.81938 6.625 6.49993C6.625 6.19438 6.51736 5.93396 6.30208 5.71868C6.08681 5.50341 5.81944 5.39577 5.5 5.39577C5.18056 5.39577 4.91319 5.50341 4.69792 5.71868C4.48264 5.93396 4.375 6.19438 4.375 6.49993C4.375 6.81938 4.48264 7.08674 4.69792 7.30202C4.91319 7.5173 5.18056 7.62494 5.5 7.62494ZM7.41667 14.4999H9.125L6.08333 9.18744L7.41667 14.4999Z"
fill="currentColor"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<mask
id="mask0_89_26596"
style="mask-type: alpha"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect width="20" height="20" fill="currentColor" />
</mask>
<g mask="url(#mask0_89_26596)">
<path
d="M5 12H15V10.5H5V12ZM5 9.25H15V7.75H5V9.25ZM5 6.5H15V5H5V6.5ZM18 18L15 15H3.5C3.0875 15 2.73438 14.8531 2.44063 14.5594C2.14688 14.2656 2 13.9125 2 13.5V3.5C2 3.0875 2.14688 2.73438 2.44063 2.44063C2.73438 2.14688 3.0875 2 3.5 2H16.5C16.9125 2 17.2656 2.14688 17.5594 2.44063C17.8531 2.73438 18 3.0875 18 3.5V18ZM3.5 13.5H15.625L16.5 14.375V3.5H3.5V13.5Z"
fill="currentColor"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 764 B

View File

@@ -0,0 +1,11 @@
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="currentColor"
>
<path
d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640v400q0 33-23.5 56.5T800-160H160Zm0-80h640v-400H447l-80-80H160v480Zm0 0v-480 480Z"
/>
</svg>

After

Width:  |  Height:  |  Size: 329 B

View File

@@ -0,0 +1,11 @@
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="currentColor"
>
<path
d="M40-160v-112q0-34 17.5-62.5T104-378q62-31 126-46.5T360-440q66 0 130 15.5T616-378q29 15 46.5 43.5T680-272v112H40Zm720 0v-120q0-44-24.5-84.5T666-434q51 6 96 20.5t84 35.5q36 20 55 44.5t19 53.5v120H760ZM360-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47Zm400-160q0 66-47 113t-113 47q-11 0-28-2.5t-28-5.5q27-32 41.5-71t14.5-81q0-42-14.5-81T544-792q14-5 28-6.5t28-1.5q66 0 113 47t47 113ZM120-240h480v-32q0-11-5.5-20T580-306q-54-27-109-40.5T360-360q-56 0-111 13.5T140-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T440-640q0-33-23.5-56.5T360-720q-33 0-56.5 23.5T280-640q0 33 23.5 56.5T360-560Zm0 320Zm0-400Z"
/>
</svg>

After

Width:  |  Height:  |  Size: 794 B

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<mask
id="mask0_89_26621"
style="mask-type: alpha"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect width="20" height="20" fill="currentColor" />
</mask>
<g mask="url(#mask0_89_26621)">
<path
d="M3.5 11.5L5.5 9.5L3.5 7.5L1.5 9.5L3.5 11.5ZM14.5 11.5L16.5 8L18.5 11.5H14.5ZM9.98958 10.5C9.30208 10.5 8.71528 10.2569 8.22917 9.77083C7.74306 9.28472 7.5 8.69444 7.5 8C7.5 7.30556 7.74306 6.71528 8.22917 6.22917C8.71528 5.74306 9.30556 5.5 10 5.5C10.6944 5.5 11.2847 5.74306 11.7708 6.22917C12.2569 6.71528 12.5 7.30903 12.5 8.01042C12.5 8.69792 12.2569 9.28472 11.7708 9.77083C11.2847 10.2569 10.691 10.5 9.98958 10.5ZM10 7C9.71667 7 9.47917 7.09583 9.2875 7.2875C9.09583 7.47917 9 7.71667 9 8C9 8.28333 9.09583 8.52083 9.2875 8.7125C9.47917 8.90417 9.71667 9 10 9C10.2833 9 10.5208 8.90417 10.7125 8.7125C10.9042 8.52083 11 8.28333 11 8C11 7.71667 10.9042 7.47917 10.7125 7.2875C10.5208 7.09583 10.2833 7 10 7ZM0 15.5V14.25C0 13.5139 0.34375 12.9722 1.03125 12.625C1.71875 12.2778 2.54167 12.1042 3.5 12.1042C3.70833 12.1042 3.91667 12.1146 4.125 12.1354C4.33333 12.1563 4.54167 12.1875 4.75 12.2292C4.5 12.4792 4.3125 12.7951 4.1875 13.1771C4.0625 13.559 4 13.9167 4 14.25V15.5H0ZM5 15.5V14.2604C5 13.434 5.42361 12.7674 6.27083 12.2604C7.11806 11.7535 8.36111 11.5 10 11.5C11.6389 11.5 12.8819 11.7528 13.7292 12.2585C14.5764 12.7644 15 13.4282 15 14.25V15.5H5ZM16.5 12.1042C17.4583 12.1042 18.2812 12.2778 18.9688 12.625C19.6562 12.9722 20 13.5139 20 14.25V15.5H16V14.25C16 13.9167 15.941 13.559 15.8229 13.1771C15.7049 12.7951 15.5139 12.4792 15.25 12.2292C15.4583 12.1875 15.6652 12.1563 15.8706 12.1354C16.0759 12.1146 16.2857 12.1042 16.5 12.1042ZM10 13C9.11111 13 8.40972 13.0799 7.89583 13.2396C7.38194 13.3993 6.93056 13.6528 6.54167 14H13.4583C13.0556 13.6528 12.6007 13.3993 12.0938 13.2396C11.5868 13.0799 10.8889 13 10 13Z"
fill="currentColor"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
<!-- Icon from Tabler Icons by Paweł Kuna - https://github.com/tabler/tabler-icons/blob/master/LICENSE -->
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 3a9 9 0 0 1 3.618 17.243l-2.193-5.602a3 3 0 1 0-2.849 0l-2.193 5.603A9 9 0 0 1 12 3"
/>
</svg>

After

Width:  |  Height:  |  Size: 428 B

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<mask
id="mask0_89_26548"
style="mask-type: alpha"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect width="20" height="20" fill="currentColor" />
</mask>
<g mask="url(#mask0_89_26548)">
<path
d="M8 17H4.5C4.0875 17 3.73437 16.8531 3.44062 16.5594C3.14687 16.2656 3 15.9125 3 15.5V12C3.55556 11.9722 4.02778 11.7674 4.41667 11.3854C4.80556 11.0035 5 10.5417 5 10C5 9.45833 4.80556 8.99653 4.41667 8.61458C4.02778 8.23264 3.55556 8.02778 3 8V4.5C3 4.0875 3.14687 3.73438 3.44062 3.44063C3.73437 3.14688 4.0875 3 4.5 3H8C8 2.44444 8.19285 1.97222 8.57854 1.58333C8.96438 1.19444 9.4366 1 9.99521 1C10.554 1 11.0278 1.19333 11.4167 1.58C11.8056 1.96667 12 2.44 12 3H15.5C15.9125 3 16.2656 3.14688 16.5594 3.44063C16.8531 3.73438 17 4.0875 17 4.5V8C17.5556 8 18.0278 8.19285 18.4167 8.57854C18.8056 8.96438 19 9.4366 19 9.99521C19 10.554 18.8067 11.0278 18.42 11.4167C18.0333 11.8056 17.56 12 17 12V15.5C17 15.9125 16.8531 16.2656 16.5594 16.5594C16.2656 16.8531 15.9125 17 15.5 17H12C11.9722 16.4444 11.7682 15.9722 11.3879 15.5833C11.0076 15.1944 10.5458 15 10.0025 15C9.45917 15 8.99653 15.1944 8.61458 15.5833C8.23264 15.9722 8.02778 16.4444 8 17ZM4.5 15.5H6.83333C7.11111 14.875 7.53819 14.3854 8.11458 14.0312C8.69097 13.6771 9.31944 13.5 10 13.5C10.6806 13.5 11.309 13.6771 11.8854 14.0312C12.4618 14.3854 12.8889 14.875 13.1667 15.5H15.5V10.5H17C17.1333 10.5 17.25 10.45 17.35 10.35C17.45 10.25 17.5 10.1333 17.5 10C17.5 9.86667 17.45 9.75 17.35 9.65C17.25 9.55 17.1333 9.5 17 9.5H15.5V4.5H10.5V3C10.5 2.86667 10.45 2.75 10.35 2.65C10.25 2.55 10.1333 2.5 10 2.5C9.86667 2.5 9.75 2.55 9.65 2.65C9.55 2.75 9.5 2.86667 9.5 3V4.5H4.5V6.83333C5.125 7.11111 5.61458 7.53819 5.96875 8.11458C6.32292 8.69097 6.5 9.31944 6.5 10C6.5 10.6974 6.32292 11.3335 5.96875 11.9083C5.61458 12.4833 5.125 12.9028 4.5 13.1667V15.5Z"
fill="currentColor"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<mask
id="mask0_89_26538"
style="mask-type: alpha"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect width="20" height="20" fill="currentColor" />
</mask>
<g mask="url(#mask0_89_26538)">
<path
d="M8 13.5L13.5 10L8 6.5V13.5ZM4.5 17C4.0875 17 3.73437 16.8531 3.44062 16.5594C3.14687 16.2656 3 15.9125 3 15.5V4.5C3 4.0875 3.14687 3.73438 3.44062 3.44063C3.73437 3.14688 4.0875 3 4.5 3H15.5C15.9125 3 16.2656 3.14688 16.5594 3.44063C16.8531 3.73438 17 4.0875 17 4.5V15.5C17 15.9125 16.8531 16.2656 16.5594 16.5594C16.2656 16.8531 15.9125 17 15.5 17H4.5ZM4.5 15.5H15.5V4.5H4.5V15.5Z"
fill="currentColor"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 792 B

View File

@@ -0,0 +1,11 @@
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="currentColor"
>
<path
d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm-40-82v-78q-33 0-56.5-23.5T360-320v-40L168-552q-3 18-5.5 36t-2.5 36q0 121 79.5 212T440-162Zm276-102q41-45 62.5-100.5T800-480q0-98-54.5-179T600-776v16q0 33-23.5 56.5T520-680h-80v80q0 17-11.5 28.5T400-560h-80v80h240q17 0 28.5 11.5T600-440v120h40q26 0 47 15.5t29 40.5Z"
/>
</svg>

After

Width:  |  Height:  |  Size: 608 B

View File

@@ -0,0 +1,11 @@
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="currentColor"
>
<path
d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm0-80h640v-400H160v400Zm140-40-56-56 103-104-104-104 57-56 160 160-160 160Zm180 0v-80h240v80H480Z"
/>
</svg>

After

Width:  |  Height:  |  Size: 368 B

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<mask
id="mask0_89_26543"
style="mask-type: alpha"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect width="20" height="20" fill="currentColor" />
</mask>
<g mask="url(#mask0_89_26543)">
<path
d="M10 17C8.05556 17 6.40278 16.3194 5.04167 14.9583C3.68056 13.5972 3 11.9444 3 10H4.5C4.5 11.5139 5.03819 12.809 6.11458 13.8854C7.19097 14.9618 8.48611 15.5 10 15.5C11.5139 15.5 12.809 14.9618 13.8854 13.8854C14.9618 12.809 15.5 11.5139 15.5 10C15.5 8.48611 14.9618 7.19097 13.8854 6.11458C12.809 5.03819 11.5139 4.5 10 4.5C9.13889 4.5 8.3434 4.67778 7.61354 5.03333C6.88382 5.38889 6.26958 5.87778 5.77083 6.5H8V8H3V3H4.5V5.70833C5.13889 4.875 5.93403 4.21528 6.88542 3.72917C7.83681 3.24306 8.875 3 10 3C10.9722 3 11.8828 3.18472 12.7319 3.55417C13.581 3.92361 14.3199 4.42271 14.9485 5.05146C15.5773 5.68007 16.0764 6.41896 16.4458 7.26813C16.8153 8.11715 17 9.02778 17 10C17 10.9722 16.8153 11.8828 16.4458 12.7319C16.0764 13.581 15.5773 14.3199 14.9485 14.9485C14.3199 15.5773 13.581 16.0764 12.7319 16.4458C11.8828 16.8153 10.9722 17 10 17ZM12.0833 12.8333L9.25 10V6H10.75V9.375L13.1458 11.7708L12.0833 12.8333Z"
fill="currentColor"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<mask
id="mask0_89_26533"
style="mask-type: alpha"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect width="20" height="20" fill="currentColor" />
</mask>
<g mask="url(#mask0_89_26533)">
<path
d="M6.54165 17L5.83332 16.2917L4.56249 17.5625C4.27082 17.8542 3.91665 18 3.49999 18C3.08332 18 2.72915 17.8542 2.43749 17.5625C2.15971 17.2569 2.01735 16.8993 2.0104 16.4896C2.00346 16.0799 2.14582 15.7292 2.43749 15.4375L3.70832 14.1667L2.99999 13.4583L13.0208 3.4375C13.3125 3.14583 13.6632 3 14.0729 3C14.4826 3 14.8403 3.14583 15.1458 3.4375L16.5625 4.85417C16.8542 5.13194 17 5.47917 17 5.89583C17 6.3125 16.8542 6.66667 16.5625 6.95833L6.54165 17ZM9.27082 9.33333L5.12499 13.4583L6.54165 14.875L10.6875 10.7292L9.27082 9.33333Z"
fill="#2B2542"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 937 B

View File

@@ -0,0 +1,11 @@
<svg width="1200" height="630" viewBox="0 0 1200 630" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1_2)">
<rect width="1200" height="630" fill="#FF3670"/>
<path d="M807.836 142.655C715.59 138.706 630.888 195.464 599.461 282.281C568.034 195.464 483.332 138.706 391.086 142.655C388.013 215.854 423.032 285.658 483.546 326.958C514.556 348.257 533.087 383.583 532.984 421.202V486.46H665.951V421.202C665.843 383.585 684.37 348.258 715.376 326.958C775.907 285.674 810.931 215.859 807.836 142.655Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_1_2">
<rect width="1200" height="630" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 647 B

View File

@@ -1949,7 +1949,7 @@ flowchart TD
There are two ways to display these FontAwesome icons:
### Register FontAwesome icon packs (v\<MERMAID_RELEASE_VERSION>+)
### Register FontAwesome icon packs (v11.7.0+)
You can register your own FontAwesome icon pack following the ["Registering icon packs" instructions](../config/icons.md).

View File

@@ -23,7 +23,7 @@ start-end: "Block name" %% Multi-bit blocks
... More Fields ...
```
### Bits Syntax (v\<MERMAID_RELEASE_VERSION>+)
### Bits Syntax (v11.7.0+)
Using start and end bit counts can be difficult, especially when modifying a design. For this we add a bit count field, which starts from the end of the previous field automagically. Use `+<count>` to set the number of bits, thus:

View File

@@ -1,256 +0,0 @@
# Jison to Chevrotain Parser Conversion Instructions
## Overview
This guide provides step-by-step instructions for converting a Jison-based parser to Chevrotain, specifically for the flowchart parser located at `src/diagrams/flowchart/parser/flow.jison`.
## Critical Requirements
- **Multi-mode lexing is MANDATORY** - This is crucial for mirroring Jison's lexical states
- Preserve the existing parser structure to maintain compatibility
- All original test cases must be included in the converted test suite
- Minimize changes to test implementation
## Understanding Jison States
The Jison parser uses multiple lexical states defined with `%x`:
- string, md_string, acc_title, acc_descr, acc_descr_multiline
- dir, vertex, text, ellipseText, trapText, edgeText
- thickEdgeText, dottedEdgeText, click, href, callbackname
- callbackargs, shapeData, shapeDataStr, shapeDataEndBracket
### State Management in Jison:
- `this.pushState(stateName)` or `this.begin(stateName)` - Enter a new state
- `this.popState()` - Return to the previous state
- States operate as a stack (LIFO - Last In, First Out)
## Conversion Process
### Phase 1: Analysis
1. **Study the Jison file thoroughly**
- Map all lexical states and their purposes
- Document which tokens are available in each state
- Note all state transitions (when states are entered/exited)
- Identify semantic actions and their data transformations
2. **Create a state transition diagram**
- Document which tokens trigger state changes
- Map the relationships between states
- Identify any nested state scenarios
### Phase 2: Lexer Implementation
1. **Set up Chevrotain multi-mode lexer structure**
- Create a mode for each Jison state
- Define a default mode corresponding to Jison's INITIAL state
- Ensure mode names match Jison state names for clarity
2. **Convert token definitions**
- For each Jison token rule, create equivalent Chevrotain token
- Pay special attention to tokens that trigger state changes
- Preserve token precedence and ordering from Jison
3. **Implement state transitions**
- Tokens that call `pushState` should use Chevrotain's push_mode
- Tokens that call `popState` should use Chevrotain's pop_mode
- Maintain the stack-based behavior of Jison states
### Phase 3: Parser Implementation
1. **Convert grammar rules**
- Translate each Jison grammar rule to Chevrotain's format
- Preserve the rule hierarchy and structure
- Maintain the same rule names where possible
2. **Handle semantic actions**
- Convert Jison's semantic actions to Chevrotain's visitor pattern
- Ensure data structures remain compatible
- Preserve any side effects or state mutations
### Phase 4: Testing Strategy
1. **Test file naming convention**
- Original: `*.spec.js`
- Converted: `*-chev.spec.ts`
- Keep test files in the same directory: `src/diagrams/flowchart/parser/`
2. **Test conversion approach**
- Copy each original test file
- Rename with `-chev.spec.ts` suffix
- Modify only the import statements and parser initialization
- Keep test cases and assertions unchanged
- Run tests individually: `vitest packages/mermaid/src/diagrams/flowchart/parser/flow-chev.spec.ts --run`
3. **Validation checklist**
- All original test cases must pass
- Test coverage should match the original
- Performance should be comparable or better
### Phase 5: Integration
1. **API compatibility**
- Ensure the new parser exposes the same public interface
- Return values should match the original parser
- Error messages should be equivalent
2. **Gradual migration**
- Create a feature flag to switch between parsers
- Allow parallel testing of both implementations
- Monitor for any behavioral differences
## Common Pitfalls to Avoid
1. **State management differences**
- Chevrotain's modes are more rigid than Jison's states
- Ensure proper mode stack behavior is maintained
- Test deeply nested state scenarios
2. **Token precedence**
- Chevrotain's token ordering matters more than in Jison
- Longer patterns should generally come before shorter ones
- Test edge cases with ambiguous inputs
3. **Semantic action timing**
- Chevrotain processes semantic actions differently
- Ensure actions execute at the correct parse phase
- Validate that data flows correctly through the parse tree
## Success Criteria
- All original tests pass with the new parser
- No changes required to downstream code
- Performance is equal or better
- Parser behavior is identical for all valid inputs
- Error handling remains consistent
# This is a reference to how Chevrotain handles multi-mode lexing
## Summary: Using Multi-Mode Lexing in Chevrotain
Chevrotain supports *multi-mode lexing*, allowing you to define different sets of tokenization rules (modes) that the lexer can switch between based on context. This is essential for parsing languages with embedded or context-sensitive syntax, such as HTML or templating languages[3][2].
**Key Concepts:**
- **Modes:** Each mode is an array of token types (constructors) defining the valid tokens in that context.
- **Mode Stack:** The lexer maintains a stack of modes. Only the top (current) mode's tokens are active at any time[2].
- **Switching Modes:**
- Use `PUSH_MODE` on a token to switch to a new mode after matching that token.
- Use `POP_MODE` on a token to return to the previous mode.
**Implementation Steps:**
1. **Define Tokens with Mode Switching:**
- Tokens can specify `PUSH_MODE` or `POP_MODE` to control mode transitions.
```javascript
const EnterLetters = createToken({ name: "EnterLetters", pattern: /LETTERS/, push_mode: "letter_mode" });
const ExitLetters = createToken({ name: "ExitLetters", pattern: /EXIT_LETTERS/, pop_mode: true });
```
2. **Create the Multi-Mode Lexer Definition:**
- Structure your modes as an object mapping mode names to arrays of token constructors.
```javascript
const multiModeLexerDefinition = {
modes: {
numbers_mode: [One, Two, EnterLetters, ExitNumbers, Whitespace],
letter_mode: [Alpha, Beta, ExitLetters, Whitespace],
},
defaultMode: "numbers_mode"
};
```
3. **Instantiate the Lexer:**
- Pass the multi-mode definition to the Chevrotain `Lexer` constructor.
```javascript
const MultiModeLexer = new Lexer(multiModeLexerDefinition);
```
4. **Tokenize Input:**
- The lexer will automatically switch modes as it encounters tokens with `PUSH_MODE` or `POP_MODE`.
```javascript
const lexResult = MultiModeLexer.tokenize(input);
```
5. **Parser Integration:**
- When constructing the parser, provide a flat array of all token constructors used in all modes, as the parser does not natively accept the multi-mode structure[1].
```javascript
// Flatten all tokens from all modes for the parser
let tokenCtors = [];
for (let mode in multiModeLexerDefinition.modes) {
tokenCtors = tokenCtors.concat(multiModeLexerDefinition.modes[mode]);
}
class MultiModeParser extends Parser {
constructor(tokens) {
super(tokens, tokenCtors);
}
}
```
**Best Practices:**
- Place more specific tokens before more general ones to avoid prefix-matching issues[2].
- Use the mode stack judiciously to manage nested or recursive language constructs.
**References:**
- Chevrotain documentation on [lexer modes][3]
- Example code and integration notes from Chevrotain issues and docs[1][2]
This approach enables robust, context-sensitive lexing for complex language grammars in Chevrotain.
[1] https://github.com/chevrotain/chevrotain/issues/395
[2] https://chevrotain.io/documentation/0_7_2/classes/lexer.html
[3] https://chevrotain.io/docs/features/lexer_modes.html
[4] https://github.com/SAP/chevrotain/issues/370
[5] https://galaxy.ai/youtube-summarizer/understanding-lexers-parsers-and-interpreters-with-chevrotain-l-jMsoAY64k
[6] https://chevrotain.io/documentation/8_0_1/classes/lexer.html
[7] https://fastly.jsdelivr.net/npm/chevrotain@11.0.3/src/scan/lexer.ts
[8] https://chevrotain.io/docs/guide/resolving_lexer_errors.html
[9] https://www.youtube.com/watch?v=l-jMsoAY64k
[10] https://github.com/SAP/chevrotain/blob/master/packages/chevrotain/test/scan/lexer_spec.ts
**Important**
Always assume I want the exact code edit!
Always assume I want you to apply this fixes directly!
# Running tests
Run tests in one file from the project root using this command:
`vitest #filename-relative-to-project-root# --run`
Example:
`vitest packages/mermaid/src/diagrams/flowchart/parser/flow-chev.spec.ts --run`
To run all flowchart test for the migration
`vitest packages/mermaid/src/diagrams/flowchart/parser/*flow*-chev.spec.ts --run`
To run a specific test in a test file:
`vitest #filename-relative-to-project-root# -t "string-matching-test" --run`
Example:
`vitest packages/mermaid/src/diagrams/flowchart/parser/flow-chev-singlenode.spec.js -t "diamond node with html in it (SN3)" --run`
# Current Status of Chevrotain Parser Migration
## ✅ COMPLETED TASKS:
- **Interaction parsing**: Successfully fixed callback functions with multiple comma-separated arguments
- **Tooltip handling**: Fixed tooltip support for both href and callback syntax patterns
- **Test coverage**: All 13 interaction tests passing, 24 style tests passing, 2 node data tests passing
## ❌ CRITICAL ISSUES REMAINING:
- **Edge creation completely broken**: Most tests show `edges.length` is 0 when should be non-zero
- **Core parsing regression**: Changes to `clickStatement` parser rule affected broader parsing functionality
- **Vertex chaining broken**: All vertex chaining tests failing due to missing edges
- **Overall test status**: 126 failed | 524 passed | 3 skipped (653 total tests)
## 🎯 IMMEDIATE NEXT TASKS:
1. **URGENT**: Fix edge creation regression - core parsing functionality is broken
2. Investigate why changes to interaction parsing affected edge parsing
3. Restore edge parsing without breaking interaction functionality
4. Run full test suite to ensure no other regressions
## 📝 KEY FILES MODIFIED:
- `packages/mermaid/src/diagrams/flowchart/parser/flowParser.ts` - Parser grammar rules
- `packages/mermaid/src/diagrams/flowchart/parser/flowAst.ts` - AST visitor implementation
## 🔧 RECENT CHANGES MADE:
1. **Parser**: Modified `clickCall` rule to accept multiple tokens for complex arguments using `MANY()`
2. **AST Visitor**: Updated `clickCall` method to correctly extract function names and combine argument tokens
3. **Interaction Handling**: Fixed tooltip handling for both href and callback syntax patterns
## ⚠️ REGRESSION ANALYSIS:
The interaction parsing fix introduced a critical regression where edge creation is completely broken. This suggests that modifications to the `clickStatement` parser rule had unintended side effects on the core parsing functionality. The parser can still tokenize correctly (as evidenced by passing style tests) but fails to create edges from link statements.
## 🧪 TEST COMMAND:
Use this command to run all Chevrotain tests:
`pnpm vitest packages/mermaid/src/diagrams/flowchart/parser/flow*chev*.spec.js --run`

View File

@@ -83,7 +83,7 @@
"@vitest/spy": "^3.0.6",
"@vitest/ui": "^3.0.6",
"ajv": "^8.17.1",
"chokidar": "3.6.0",
"chokidar": "^4.0.3",
"concurrently": "^9.1.2",
"cors": "^2.8.5",
"cpy-cli": "^5.0.0",

View File

@@ -1,5 +1,14 @@
# @mermaid-js/layout-elk
## 0.1.8
### Patch Changes
- [#6648](https://github.com/mermaid-js/mermaid/pull/6648) [`85c5b9b`](https://github.com/mermaid-js/mermaid/commit/85c5b9b4c064e2edabf21757c8215a1018d4d288) Thanks [@knsv](https://github.com/knsv)! - Make elk respect the order of nodes based from the code
- Updated dependencies [[`97b79c3`](https://github.com/mermaid-js/mermaid/commit/97b79c3578a2004c63fa32f6d5e17bd8a536e13a), [`b1cf291`](https://github.com/mermaid-js/mermaid/commit/b1cf29127348602137552405e3300dee1697f0de), [`a4754ad`](https://github.com/mermaid-js/mermaid/commit/a4754ad195e70d52fbd46ef44f40797d2d215e41), [`2b05d7e`](https://github.com/mermaid-js/mermaid/commit/2b05d7e1edef635e6c80cb383b10ea0a89279f41), [`41e84b7`](https://github.com/mermaid-js/mermaid/commit/41e84b726a1f2df002b77c4b0071e2c15e47838e), [`d63d3bf`](https://github.com/mermaid-js/mermaid/commit/d63d3bf1e7596ac7eeb24ba06cbc7a70f9c8b070), [`aa6cb86`](https://github.com/mermaid-js/mermaid/commit/aa6cb86899968c65561eebfc1d54dd086b1518a2), [`df9df9d`](https://github.com/mermaid-js/mermaid/commit/df9df9dc32b80a8c320cc0efd5483b9485f15bde), [`cdbd3e5`](https://github.com/mermaid-js/mermaid/commit/cdbd3e58a3a35d63a79258115dedca4a535c1038), [`c17277e`](https://github.com/mermaid-js/mermaid/commit/c17277e743b1c12e4134fba44c62a7d5885f2574), [`a1ba65c`](https://github.com/mermaid-js/mermaid/commit/a1ba65c0c08432ec36e772570c3a5899cb57c102), [`1ddaf10`](https://github.com/mermaid-js/mermaid/commit/1ddaf10b89d8c7311c5e10d466b42fa36b61210b), [`ca80f71`](https://github.com/mermaid-js/mermaid/commit/ca80f719eac86cf4c31392105d5d896f39b84bbc), [`bca6ed6`](https://github.com/mermaid-js/mermaid/commit/bca6ed67c3e0db910bf498fdd0fc0346c02d392b)]:
- mermaid@11.7.0
## 0.1.7
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@mermaid-js/layout-elk",
"version": "0.1.7",
"version": "0.1.8",
"description": "ELK layout engine for mermaid",
"module": "dist/mermaid-layout-elk.core.mjs",
"types": "dist/layouts.d.ts",

View File

@@ -766,6 +766,7 @@ export const render = async (
id: 'root',
layoutOptions: {
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
'elk.layered.crossingMinimization.forceNodeModelOrder': true,
'elk.algorithm': algorithm,
'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy,
'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,
@@ -780,7 +781,6 @@ export const render = async (
// 'spacing.edgeEdge': 10,
// 'spacing.edgeEdgeBetweenLayers': 20,
// 'spacing.nodeSelfLoop': 20,
// Tweaking options
// 'elk.layered.nodePlacement.favorStraightEdges': true,
// 'nodePlacement.feedbackEdges': true,

View File

@@ -0,0 +1,14 @@
# @mermaid-js/mermaid-zenuml
## 0.2.1
### Patch Changes
- [#6581](https://github.com/mermaid-js/mermaid/pull/6581) [`941bc69`](https://github.com/mermaid-js/mermaid/commit/941bc698350bd103b2a431ed8fed0c7b0d92fff0) Thanks [@MrCoder](https://github.com/MrCoder)! - Upgraded the dependency @zenuml/core
- [#6319](https://github.com/mermaid-js/mermaid/pull/6319) [`9d06d8f`](https://github.com/mermaid-js/mermaid/commit/9d06d8f31e7f12af9e9e092214f907f2dc93ad75) Thanks [@renovate](https://github.com/apps/renovate)! - chore: bump minimum ZenUML version to 3.23.28
- [#5737](https://github.com/mermaid-js/mermaid/pull/5737) [`0ad44c1`](https://github.com/mermaid-js/mermaid/commit/0ad44c12feead9d20c6a870a49327ada58d6e657) Thanks [@sidharthv96](https://github.com/sidharthv96)! - fix(zenuml): limit `peerDependencies` to Mermaid v10 and v11
- Updated dependencies [[`97b79c3`](https://github.com/mermaid-js/mermaid/commit/97b79c3578a2004c63fa32f6d5e17bd8a536e13a), [`b1cf291`](https://github.com/mermaid-js/mermaid/commit/b1cf29127348602137552405e3300dee1697f0de), [`a4754ad`](https://github.com/mermaid-js/mermaid/commit/a4754ad195e70d52fbd46ef44f40797d2d215e41), [`2b05d7e`](https://github.com/mermaid-js/mermaid/commit/2b05d7e1edef635e6c80cb383b10ea0a89279f41), [`41e84b7`](https://github.com/mermaid-js/mermaid/commit/41e84b726a1f2df002b77c4b0071e2c15e47838e), [`d63d3bf`](https://github.com/mermaid-js/mermaid/commit/d63d3bf1e7596ac7eeb24ba06cbc7a70f9c8b070), [`aa6cb86`](https://github.com/mermaid-js/mermaid/commit/aa6cb86899968c65561eebfc1d54dd086b1518a2), [`df9df9d`](https://github.com/mermaid-js/mermaid/commit/df9df9dc32b80a8c320cc0efd5483b9485f15bde), [`cdbd3e5`](https://github.com/mermaid-js/mermaid/commit/cdbd3e58a3a35d63a79258115dedca4a535c1038), [`c17277e`](https://github.com/mermaid-js/mermaid/commit/c17277e743b1c12e4134fba44c62a7d5885f2574), [`a1ba65c`](https://github.com/mermaid-js/mermaid/commit/a1ba65c0c08432ec36e772570c3a5899cb57c102), [`1ddaf10`](https://github.com/mermaid-js/mermaid/commit/1ddaf10b89d8c7311c5e10d466b42fa36b61210b), [`ca80f71`](https://github.com/mermaid-js/mermaid/commit/ca80f719eac86cf4c31392105d5d896f39b84bbc), [`bca6ed6`](https://github.com/mermaid-js/mermaid/commit/bca6ed67c3e0db910bf498fdd0fc0346c02d392b)]:
- mermaid@11.7.0

View File

@@ -1,6 +1,6 @@
{
"name": "@mermaid-js/mermaid-zenuml",
"version": "0.3.0",
"version": "0.2.1",
"description": "MermaidJS plugin for ZenUML integration",
"module": "dist/mermaid-zenuml.core.mjs",
"types": "dist/detector.d.ts",

View File

@@ -1,5 +1,44 @@
# mermaid
## 11.7.0
### Minor Changes
- [#6479](https://github.com/mermaid-js/mermaid/pull/6479) [`97b79c3`](https://github.com/mermaid-js/mermaid/commit/97b79c3578a2004c63fa32f6d5e17bd8a536e13a) Thanks [@monicanguyen25](https://github.com/monicanguyen25)! - feat: Add Vertical Line To Gantt Plot At Specified Time
- [#6225](https://github.com/mermaid-js/mermaid/pull/6225) [`41e84b7`](https://github.com/mermaid-js/mermaid/commit/41e84b726a1f2df002b77c4b0071e2c15e47838e) Thanks [@Shahir-47](https://github.com/Shahir-47)! - feat: Add support for styling Journey Diagram title (color, font-family, and font-size)
- [#6423](https://github.com/mermaid-js/mermaid/pull/6423) [`aa6cb86`](https://github.com/mermaid-js/mermaid/commit/aa6cb86899968c65561eebfc1d54dd086b1518a2) Thanks [@BambioGaming](https://github.com/BambioGaming)! - Added support for the click directive in stateDiagram syntax
- [#5980](https://github.com/mermaid-js/mermaid/pull/5980) [`df9df9d`](https://github.com/mermaid-js/mermaid/commit/df9df9dc32b80a8c320cc0efd5483b9485f15bde) Thanks [@BryanCrotazGivEnergy](https://github.com/BryanCrotazGivEnergy)! - feat: Add shorter `+<count>: Label` syntax in packet diagram
- [#6523](https://github.com/mermaid-js/mermaid/pull/6523) [`c17277e`](https://github.com/mermaid-js/mermaid/commit/c17277e743b1c12e4134fba44c62a7d5885f2574) Thanks [@NourBenz](https://github.com/NourBenz)! - fix: allow sequence diagram arrows with a trailing colon but no message
- [#6475](https://github.com/mermaid-js/mermaid/pull/6475) [`a1ba65c`](https://github.com/mermaid-js/mermaid/commit/a1ba65c0c08432ec36e772570c3a5899cb57c102) Thanks [@Shahir-47](https://github.com/Shahir-47)! - feat: Dynamically Render Data Labels Within Bar Charts
### Patch Changes
- [#6588](https://github.com/mermaid-js/mermaid/pull/6588) [`b1cf291`](https://github.com/mermaid-js/mermaid/commit/b1cf29127348602137552405e3300dee1697f0de) Thanks [@omkarht](https://github.com/omkarht)! - Fix stroke styles for ER diagram to correctly apply path and row-specific styles
- [#6296](https://github.com/mermaid-js/mermaid/pull/6296) [`a4754ad`](https://github.com/mermaid-js/mermaid/commit/a4754ad195e70d52fbd46ef44f40797d2d215e41) Thanks [@sidharthv96](https://github.com/sidharthv96)! - chore: Convert StateDB into TypeScript
- [#6463](https://github.com/mermaid-js/mermaid/pull/6463) [`2b05d7e`](https://github.com/mermaid-js/mermaid/commit/2b05d7e1edef635e6c80cb383b10ea0a89279f41) Thanks [@AaronMoat](https://github.com/AaronMoat)! - fix: Remove incorrect `style="undefined;"` attributes in some Mermaid diagrams
- [#6282](https://github.com/mermaid-js/mermaid/pull/6282) [`d63d3bf`](https://github.com/mermaid-js/mermaid/commit/d63d3bf1e7596ac7eeb24ba06cbc7a70f9c8b070) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - FontAwesome icons can now be embedded as SVGs in flowcharts if they are registered via `mermaid.registerIconPacks`.
- [#6407](https://github.com/mermaid-js/mermaid/pull/6407) [`cdbd3e5`](https://github.com/mermaid-js/mermaid/commit/cdbd3e58a3a35d63a79258115dedca4a535c1038) Thanks [@thomascizeron](https://github.com/thomascizeron)! - Refactor grammar so that title don't break Architecture Diagrams
- [#6343](https://github.com/mermaid-js/mermaid/pull/6343) [`1ddaf10`](https://github.com/mermaid-js/mermaid/commit/1ddaf10b89d8c7311c5e10d466b42fa36b61210b) Thanks [@jeswr](https://github.com/jeswr)! - fix: allow colons in events
- [#6616](https://github.com/mermaid-js/mermaid/pull/6616) [`ca80f71`](https://github.com/mermaid-js/mermaid/commit/ca80f719eac86cf4c31392105d5d896f39b84bbc) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix(timeline): ensure consistent vertical line lengths with visible arrowheads
Fixed timeline diagrams where vertical dashed lines from tasks had inconsistent lengths. All vertical lines now extend to the same depth regardless of the number of events in each column, with sufficient padding to clearly display both the dashed line pattern and complete arrowheads.
- [#6566](https://github.com/mermaid-js/mermaid/pull/6566) [`bca6ed6`](https://github.com/mermaid-js/mermaid/commit/bca6ed67c3e0db910bf498fdd0fc0346c02d392b) Thanks [@arpitjain099](https://github.com/arpitjain099)! - fix: Fix incomplete string escaping in URL manipulation logic when `arrowMarkerAbsolute: true` by ensuring all unsafe characters are escaped.
- Updated dependencies [[`df9df9d`](https://github.com/mermaid-js/mermaid/commit/df9df9dc32b80a8c320cc0efd5483b9485f15bde), [`cdbd3e5`](https://github.com/mermaid-js/mermaid/commit/cdbd3e58a3a35d63a79258115dedca4a535c1038)]:
- @mermaid-js/parser@0.5.0
## 11.6.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "11.6.0",
"version": "11.7.0",
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
"type": "module",
"module": "./dist/mermaid.core.mjs",
@@ -71,7 +71,6 @@
"@iconify/utils": "^2.1.33",
"@mermaid-js/parser": "workspace:^",
"@types/d3": "^7.4.3",
"chevrotain": "^11.0.3",
"cytoscape": "^3.29.3",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.2.0",
@@ -106,7 +105,7 @@
"@types/stylis": "^4.2.7",
"@types/uuid": "^10.0.0",
"ajv": "^8.17.1",
"chokidar": "3.6.0",
"chokidar": "^4.0.3",
"concurrently": "^9.1.2",
"csstree-validator": "^4.0.1",
"globby": "^14.0.2",

View File

@@ -66,7 +66,6 @@ export class FlowDB implements DiagramDB {
this.updateLink = this.updateLink.bind(this);
this.addClass = this.addClass.bind(this);
this.setClass = this.setClass.bind(this);
this.setStyle = this.setStyle.bind(this);
this.destructLink = this.destructLink.bind(this);
this.setClickEvent = this.setClickEvent.bind(this);
this.setTooltip = this.setTooltip.bind(this);
@@ -160,9 +159,7 @@ export class FlowDB implements DiagramDB {
if (textObj !== undefined) {
this.config = getConfig();
// Don't trim text that contains newlines to preserve YAML multi-line formatting
const shouldTrim = !textObj.text.includes('\n');
txt = this.sanitizeText(shouldTrim ? textObj.text.trim() : textObj.text);
txt = this.sanitizeText(textObj.text.trim());
vertex.labelType = textObj.type;
// strip quotes if string starts and ends with a quote
if (txt.startsWith('"') && txt.endsWith('"')) {
@@ -447,35 +444,6 @@ You have to call mermaid.initialize.`
}
}
/**
* Called by parser when a style statement is found. Adds styles to a vertex.
*
* @param id - Vertex id
* @param styles - Array of style strings
*/
public setStyle(id: string, styles: string[]) {
let vertex = this.vertices.get(id);
if (!vertex) {
// Create vertex if it doesn't exist
vertex = {
id,
domId: this.version === 'gen-1' ? 'flowchart-' + id + '-' + this.vertexCounter : id,
styles: [],
classes: [],
text: id,
labelType: 'text',
props: {},
parentId: undefined,
};
this.vertices.set(id, vertex);
this.vertexCounter++;
}
// Add styles to the vertex
const styleArray = Array.isArray(styles) ? styles : [styles];
vertex.styles.push(...styleArray);
}
public setTooltip(ids: string, tooltip: string) {
if (tooltip === undefined) {
return;
@@ -719,7 +687,7 @@ You have to call mermaid.initialize.`
}
}
id = id || 'subGraph' + this.subCount;
id = id ?? 'subGraph' + this.subCount;
title = title || '';
title = this.sanitizeText(title);
this.subCount = this.subCount + 1;
@@ -1039,7 +1007,7 @@ You have to call mermaid.initialize.`
} else {
const baseNode = {
id: vertex.id,
label: vertex.text?.replace(/<br>/g, '<br/>'),
label: vertex.text,
labelStyle: '',
parentId,
padding: config.flowchart?.padding || 8,

View File

@@ -2,34 +2,26 @@ import type { MermaidConfig } from '../../config.type.js';
import { setConfig } from '../../diagram-api/diagramAPI.js';
import { FlowDB } from './flowDb.js';
import renderer from './flowRenderer-v3-unified.js';
// Replace the Jison import with Chevrotain parser
import flowParserJison from './parser/flow.jison';
import flowParser from './parser/flowParserAdapter.js';
// @ts-ignore: JISON doesn't support types
//import flowParser from './parser/flow.jison';
import flowParser from './parser/flowParser.ts';
import flowStyles from './styles.js';
// Create a singleton FlowDB instance that the parser can populate
// This ensures the same instance is used by both parser and renderer
let flowDbInstance: FlowDB | null = null;
export const diagram = {
parser: flowParser,
get db() {
// Return the same FlowDB instance that the parser uses
// This is critical for the Chevrotain parser to work correctly
flowDbInstance ??= new FlowDB();
return flowDbInstance;
return new FlowDB();
},
renderer,
styles: flowStyles,
init: (cnf: MermaidConfig) => {
cnf.flowchart ??= {};
if (!cnf.flowchart) {
cnf.flowchart = {};
}
if (cnf.layout) {
setConfig({ layout: cnf.layout });
}
cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
// Reset the FlowDB instance for new diagrams
flowDbInstance = null;
},
};

View File

@@ -1,27 +0,0 @@
import type { MermaidConfig } from '../../config.type.js';
import { setConfig } from '../../diagram-api/diagramAPI.js';
import { FlowDB } from './flowDb.js';
import renderer from './flowRenderer-v3-unified.js';
// @ts-ignore: JISON doesn't support types
//import flowParser from './parser/flow.jison';
import flowParser from './parser/flowParser.ts';
import flowStyles from './styles.js';
export const diagram = {
parser: flowParser,
get db() {
return new FlowDB();
},
renderer,
styles: flowStyles,
init: (cnf: MermaidConfig) => {
if (!cnf.flowchart) {
cnf.flowchart = {};
}
if (cnf.layout) {
setConfig({ layout: cnf.layout });
}
cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
},
};

View File

@@ -1,244 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('[Chevrotain Arrows] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
});
it('should handle basic arrow', function () {
const res = flow.parse('graph TD;A-->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
});
it('should handle arrow with text', function () {
const res = flow.parse('graph TD;A-->|text|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].text).toBe('text');
});
it('should handle dotted arrow', function () {
const res = flow.parse('graph TD;A-.->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_dotted');
});
it('should handle dotted arrow with text', function () {
const res = flow.parse('graph TD;A-.-|text|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].text).toBe('text');
expect(edges[0].type).toBe('arrow_dotted');
});
it('should handle thick arrow', function () {
const res = flow.parse('graph TD;A==>B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_thick');
});
it('should handle thick arrow with text', function () {
const res = flow.parse('graph TD;A==|text|==>B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].text).toBe('text');
expect(edges[0].type).toBe('arrow_thick');
});
it('should handle open arrow', function () {
const res = flow.parse('graph TD;A---B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_open');
});
it('should handle open arrow with text', function () {
const res = flow.parse('graph TD;A---|text|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].text).toBe('text');
expect(edges[0].type).toBe('arrow_open');
});
it('should handle cross arrow', function () {
const res = flow.parse('graph TD;A--xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle circle arrow', function () {
const res = flow.parse('graph TD;A--oB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_circle');
});
it('should handle bidirectional arrow', function () {
const res = flow.parse('graph TD;A<-->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_point');
});
it('should handle bidirectional arrow with text', function () {
const res = flow.parse('graph TD;A<--|text|-->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].text).toBe('text');
expect(edges[0].type).toBe('double_arrow_point');
});
it('should handle multiple arrows in sequence', function () {
const res = flow.parse('graph TD;A-->B-->C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
});
it('should handle multiple arrows with different types', function () {
const res = flow.parse('graph TD;A-->B-.->C==>D;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(3);
expect(edges[0].type).toBe('arrow_point');
expect(edges[1].type).toBe('arrow_dotted');
expect(edges[2].type).toBe('arrow_thick');
});
it('should handle long arrows', function () {
const res = flow.parse('graph TD;A---->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].length).toBe('long');
});
it('should handle extra long arrows', function () {
const res = flow.parse('graph TD;A------>B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].length).toBe('extralong');
});
});

View File

@@ -1,154 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
import { cleanupComments } from '../../../diagram-api/comments.js';
setConfig({
securityLevel: 'strict',
});
describe('[Comments] when parsing with Chevrotain', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
});
it('should handle comments', function () {
const res = flow.parse(cleanupComments('graph TD;\n%% Comment\n A-->B;'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
});
it('should handle comments at the start', function () {
const res = flow.parse(cleanupComments('%% Comment\ngraph TD;\n A-->B;'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
});
it('should handle comments at the end', function () {
const res = flow.parse(cleanupComments('graph TD;\n A-->B\n %% Comment at the end\n'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
});
it('should handle comments at the end no trailing newline', function () {
const res = flow.parse(cleanupComments('graph TD;\n A-->B\n%% Comment'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
});
it('should handle comments at the end many trailing newlines', function () {
const res = flow.parse(cleanupComments('graph TD;\n A-->B\n%% Comment\n\n\n'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
});
it('should handle no trailing newlines', function () {
const res = flow.parse(cleanupComments('graph TD;\n A-->B'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
});
it('should handle many trailing newlines', function () {
const res = flow.parse(cleanupComments('graph TD;\n A-->B\n\n'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
});
it('should handle a comment with blank rows in-between', function () {
const res = flow.parse(cleanupComments('graph TD;\n\n\n %% Comment\n A-->B;'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
});
it('should handle a comment with mermaid flowchart code in them', function () {
const res = flow.parse(
cleanupComments(
'graph TD;\n\n\n %% Test od>Odd shape]-->|Two line<br>edge comment|ro;\n A-->B;'
)
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
});
});

View File

@@ -1,95 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('when parsing directions with Chevrotain', function () {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.yy.setGen('gen-2');
});
it('should use default direction from top level', function () {
const res = flow.parse(`flowchart TB
subgraph A
a --> b
end`);
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
// Chevrotain parser now produces nodes in the correct order: a --> b means ['a', 'b']
expect(subgraph.nodes[0]).toBe('a');
expect(subgraph.nodes[1]).toBe('b');
expect(subgraph.id).toBe('A');
expect(subgraph.dir).toBe(undefined);
});
it('should handle a subgraph with a direction', function () {
const res = flow.parse(`flowchart TB
subgraph A
direction BT
a --> b
end`);
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
// Chevrotain parser now produces nodes in the correct order: a --> b means ['a', 'b']
expect(subgraph.nodes[0]).toBe('a');
expect(subgraph.nodes[1]).toBe('b');
expect(subgraph.id).toBe('A');
expect(subgraph.dir).toBe('BT');
});
it('should use the last defined direction', function () {
const res = flow.parse(`flowchart TB
subgraph A
direction BT
a --> b
direction RL
end`);
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
// Chevrotain parser now produces nodes in the correct order: a --> b means ['a', 'b']
expect(subgraph.nodes[0]).toBe('a');
expect(subgraph.nodes[1]).toBe('b');
expect(subgraph.id).toBe('A');
expect(subgraph.dir).toBe('RL');
});
it('should handle nested subgraphs 1', function () {
const res = flow.parse(`flowchart TB
subgraph A
direction RL
b-->B
a
end
a-->c
subgraph B
direction LR
c
end`);
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = subgraphs.find((o) => o.id === 'A');
const subgraphB = subgraphs.find((o) => o.id === 'B');
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphB.dir).toBe('LR');
expect(subgraphA.nodes).toContain('B');
expect(subgraphA.nodes).toContain('b');
expect(subgraphA.nodes).toContain('a');
expect(subgraphA.nodes).not.toContain('c');
expect(subgraphA.dir).toBe('RL');
});
});

View File

@@ -1,240 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('[Chevrotain Edges] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
});
it('should handle a single edge', function () {
const res = flow.parse('graph TD;A-->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
});
it('should handle multiple edges', function () {
const res = flow.parse('graph TD;A-->B;B-->C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
});
it('should handle chained edges', function () {
const res = flow.parse('graph TD;A-->B-->C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
});
it('should handle edges with text', function () {
const res = flow.parse('graph TD;A-->|text|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].text).toBe('text');
});
it('should handle edges with quoted text', function () {
const res = flow.parse('graph TD;A-->|"quoted text"|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].text).toBe('quoted text');
});
it('should handle edges with complex text', function () {
const res = flow.parse('graph TD;A-->|"text with spaces and symbols!"|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].text).toBe('text with spaces and symbols!');
});
it('should handle multiple edges from one node', function () {
const res = flow.parse('graph TD;A-->B;A-->C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[1].start).toBe('A');
expect(edges[1].end).toBe('C');
});
it('should handle multiple edges to one node', function () {
const res = flow.parse('graph TD;A-->C;B-->C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('C');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
});
it('should handle edges with node shapes', function () {
const res = flow.parse('graph TD;A[Start]-->B{Decision};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('A').type).toBe('square');
expect(vert.get('A').text).toBe('Start');
expect(vert.get('B').id).toBe('B');
expect(vert.get('B').type).toBe('diamond');
expect(vert.get('B').text).toBe('Decision');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
});
it('should handle complex edge patterns', function () {
const res = flow.parse('graph TD;A[Start]-->B{Decision};B-->|Yes|C[Process];B-->|No|D[End];');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(3);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
expect(edges[1].text).toBe('Yes');
expect(edges[2].start).toBe('B');
expect(edges[2].end).toBe('D');
expect(edges[2].text).toBe('No');
});
it('should handle edges with ampersand syntax', function () {
const res = flow.parse('graph TD;A & B --> C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('C');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
});
it('should handle edges with multiple ampersands', function () {
const res = flow.parse('graph TD;A & B & C --> D;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(vert.get('D').id).toBe('D');
expect(edges.length).toBe(3);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('D');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('D');
expect(edges[2].start).toBe('C');
expect(edges[2].end).toBe('D');
});
it('should handle self-referencing edges', function () {
const res = flow.parse('graph TD;A-->A;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('A');
});
it('should handle edges with numeric node IDs', function () {
const res = flow.parse('graph TD;1-->2;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('1').id).toBe('1');
expect(vert.get('2').id).toBe('2');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('1');
expect(edges[0].end).toBe('2');
});
it('should handle edges with mixed alphanumeric node IDs', function () {
const res = flow.parse('graph TD;A1-->B2;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A1').id).toBe('A1');
expect(vert.get('B2').id).toBe('B2');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A1');
expect(edges[0].end).toBe('B2');
});
});

View File

@@ -1,29 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('[Chevrotain Text] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
});
describe('it should handle huge files', function () {
// skipped because this test takes like 2 minutes or more!
it.skip('it should handle huge diagrams', function () {
const nodes = ('A-->B;B-->A;'.repeat(415) + 'A-->B;').repeat(57) + 'A-->B;B-->A;'.repeat(275);
flow.parse(`graph LR;${nodes}`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
expect(edges.length).toBe(47917);
expect(vert.size).toBe(2);
});
});
});

View File

@@ -1,161 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
import { vi } from 'vitest';
const spyOn = vi.spyOn;
setConfig({
securityLevel: 'strict',
});
describe('[Chevrotain Interactions] when parsing', () => {
let flowDb;
beforeEach(function () {
flowDb = new FlowDB();
flow.yy = flowDb;
flow.yy.clear();
});
it('should be possible to use click to a callback', function () {
spyOn(flowDb, 'setClickEvent');
const res = flow.parse('graph TD\nA-->B\nclick A callback');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback');
});
it('should be possible to use click to a click and call callback', function () {
spyOn(flowDb, 'setClickEvent');
const res = flow.parse('graph TD\nA-->B\nclick A call callback()');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback');
});
it('should be possible to use click to a callback with tooltip', function () {
spyOn(flowDb, 'setClickEvent');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A callback "tooltip"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
});
it('should be possible to use click to a click and call callback with tooltip', function () {
spyOn(flowDb, 'setClickEvent');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A call callback() "tooltip"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
});
it('should be possible to use click to a callback with an arbitrary number of args', function () {
spyOn(flowDb, 'setClickEvent');
const res = flow.parse('graph TD\nA-->B\nclick A call callback("test0", test1, test2)');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback', '"test0", test1, test2');
});
it('should handle interaction - click to a link', function () {
spyOn(flowDb, 'setLink');
const res = flow.parse('graph TD\nA-->B\nclick A "click.html"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html');
});
it('should handle interaction - click to a click and href link', function () {
spyOn(flowDb, 'setLink');
const res = flow.parse('graph TD\nA-->B\nclick A href "click.html"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html');
});
it('should handle interaction - click to a link with tooltip', function () {
spyOn(flowDb, 'setLink');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A "click.html" "tooltip"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
});
it('should handle interaction - click to a click and href link with tooltip', function () {
spyOn(flowDb, 'setLink');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A href "click.html" "tooltip"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
});
it('should handle interaction - click to a link with target', function () {
spyOn(flowDb, 'setLink');
const res = flow.parse('graph TD\nA-->B\nclick A "click.html" _blank');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', '_blank');
});
it('should handle interaction - click to a click and href link with target', function () {
spyOn(flowDb, 'setLink');
const res = flow.parse('graph TD\nA-->B\nclick A href "click.html" _blank');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', '_blank');
});
it('should handle interaction - click to a link with tooltip and target', function () {
spyOn(flowDb, 'setLink');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A "click.html" "tooltip" _blank');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', '_blank');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
});
it('should handle interaction - click to a click and href link with tooltip and target', function () {
spyOn(flowDb, 'setLink');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A href "click.html" "tooltip" _blank');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', '_blank');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
});
});

View File

@@ -1,119 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('[Chevrotain Lines] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
});
it('should handle line interpolation default definitions', function () {
const res = flow.parse('graph TD\n' + 'A-->B\n' + 'linkStyle default interpolate basis');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.defaultInterpolate).toBe('basis');
});
it('should handle line interpolation numbered definitions', function () {
const res = flow.parse(
'graph TD\n' +
'A-->B\n' +
'A-->C\n' +
'linkStyle 0 interpolate basis\n' +
'linkStyle 1 interpolate cardinal'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].interpolate).toBe('basis');
expect(edges[1].interpolate).toBe('cardinal');
});
it('should handle line interpolation multi-numbered definitions', function () {
const res = flow.parse(
'graph TD\n' + 'A-->B\n' + 'A-->C\n' + 'linkStyle 0,1 interpolate basis'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].interpolate).toBe('basis');
expect(edges[1].interpolate).toBe('basis');
});
it('should handle line interpolation default with style', function () {
const res = flow.parse(
'graph TD\n' + 'A-->B\n' + 'linkStyle default interpolate basis stroke-width:1px;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.defaultInterpolate).toBe('basis');
});
it('should handle line interpolation numbered with style', function () {
const res = flow.parse(
'graph TD\n' +
'A-->B\n' +
'A-->C\n' +
'linkStyle 0 interpolate basis stroke-width:1px;\n' +
'linkStyle 1 interpolate cardinal stroke-width:1px;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].interpolate).toBe('basis');
expect(edges[1].interpolate).toBe('cardinal');
});
it('should handle line interpolation multi-numbered with style', function () {
const res = flow.parse(
'graph TD\n' + 'A-->B\n' + 'A-->C\n' + 'linkStyle 0,1 interpolate basis stroke-width:1px;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].interpolate).toBe('basis');
expect(edges[1].interpolate).toBe('basis');
});
describe('it should handle new line type notation', function () {
it('should handle regular lines', function () {
const res = flow.parse('graph TD;A-->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].stroke).toBe('normal');
});
it('should handle dotted lines', function () {
const res = flow.parse('graph TD;A-.->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].stroke).toBe('dotted');
});
it('should handle dotted lines', function () {
const res = flow.parse('graph TD;A==>B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].stroke).toBe('thick');
});
});
});

View File

@@ -1,64 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('[Chevrotain] parsing a flow chart with markdown strings', function () {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
});
it('markdown formatting in nodes and labels', function () {
const res = flow.parse(`flowchart
A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in the hog"] -- "The rat in the mat" -->C;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('A').text).toBe('The cat in **the** hat');
expect(vert.get('A').labelType).toBe('markdown');
expect(vert.get('B').id).toBe('B');
expect(vert.get('B').text).toBe('The dog in the hog');
expect(vert.get('B').labelType).toBe('string');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('The *bat* in the chat');
expect(edges[0].labelType).toBe('markdown');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
expect(edges[1].type).toBe('arrow_point');
expect(edges[1].text).toBe('The rat in the mat');
expect(edges[1].labelType).toBe('string');
});
it('markdown formatting in subgraphs', function () {
const res = flow.parse(`flowchart LR
subgraph "One"
a("\`The **cat**
in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
end
subgraph "\`**Two**\`"
c("\`The **cat**
in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
end`);
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
expect(subgraph.title).toBe('One');
expect(subgraph.labelType).toBe('text');
const subgraph2 = subgraphs[1];
expect(subgraph2.nodes.length).toBe(2);
expect(subgraph2.title).toBe('**Two**');
expect(subgraph2.labelType).toBe('markdown');
});
});

View File

@@ -1,415 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('[Chevrotain] when parsing directions', function () {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.yy.setGen('gen-2');
});
it('should handle basic shape data statements', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded}`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
});
it('should handle basic shape data statements', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded }`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
});
it('should handle basic shape data statements with &', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded } & E`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(2);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle shape data statements with edges', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded } --> E`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(2);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle basic shape data statements with amp and edges 1', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded } & E --> F`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle basic shape data statements with amp and edges 2', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded } & E@{ shape: rounded } --> F`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle basic shape data statements with amp and edges 3', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded } & E@{ shape: rounded } --> F & G@{ shape: rounded }`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(4);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle basic shape data statements with amp and edges 4', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded } & E@{ shape: rounded } --> F@{ shape: rounded } & G@{ shape: rounded }`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(4);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle basic shape data statements with amp and edges 5, trailing space', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded } & E@{ shape: rounded } --> F{ shape: rounded } & G{ shape: rounded } `);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(4);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should no matter of there are no leading spaces', function () {
const res = flow.parse(`flowchart TB
D@{shape: rounded}`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
});
it('should no matter of there are many leading spaces', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded}`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
});
it('should be forgiving with many spaces before the end', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded }`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
});
it('should be possible to add multiple properties on the same line', function () {
const res = flow.parse(`flowchart TB
D@{ shape: rounded , label: "DD"}`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('DD');
});
it('should be possible to link to a node with more data', function () {
const res = flow.parse(`flowchart TB
A --> D@{
shape: circle
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(2);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('A');
expect(data4Layout.nodes[1].label).toEqual('D');
expect(data4Layout.nodes[1].shape).toEqual('circle');
expect(data4Layout.edges.length).toBe(1);
});
it('should not disturb adding multiple nodes after each other', function () {
const res = flow.parse(`flowchart TB
A[hello]
B@{
shape: circle
other: "clock"
}
C[Hello]@{
shape: circle
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('hello');
expect(data4Layout.nodes[1].shape).toEqual('circle');
expect(data4Layout.nodes[1].label).toEqual('B');
expect(data4Layout.nodes[2].shape).toEqual('circle');
expect(data4Layout.nodes[2].label).toEqual('Hello');
});
it('should use handle bracket end (}) character inside the shape data', function () {
const res = flow.parse(`flowchart TB
A@{
label: "This is }"
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is }');
});
it('should error on nonexistent shape', function () {
expect(() => {
flow.parse(`flowchart TB
A@{ shape: this-shape-does-not-exist }
`);
}).toThrow('No such shape: this-shape-does-not-exist.');
});
it('should error on internal-only shape', function () {
expect(() => {
// this shape does exist, but it's only supposed to be for internal/backwards compatibility use
flow.parse(`flowchart TB
A@{ shape: rect_left_inv_arrow }
`);
}).toThrow('No such shape: rect_left_inv_arrow. Shape names should be lowercase.');
});
it('Diamond shapes should work as usual', function () {
const res = flow.parse(`flowchart TB
A{This is a label}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('diamond');
expect(data4Layout.nodes[0].label).toEqual('This is a label');
});
it('Multi line strings should be supported', function () {
const res = flow.parse(`flowchart TB
A@{
label: |
This is a
multiline string
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a\nmultiline string\n');
});
it('Multi line strings should be supported', function () {
const res = flow.parse(`flowchart TB
A@{
label: "This is a
multiline string"
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a<br/>multiline string');
});
it('should be possible to use } in strings', function () {
const res = flow.parse(`flowchart TB
A@{
label: "This is a string with }"
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a string with }');
});
it('should be possible to use @ in strings', function () {
const res = flow.parse(`flowchart TB
A@{
label: "This is a string with @"
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a string with @');
});
it('should be possible to use @ in strings', function () {
const res = flow.parse(`flowchart TB
A@{
label: "This is a string with}"
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a string with}');
});
it('should be possible to use @ syntax to add labels on multi nodes', function () {
const res = flow.parse(`flowchart TB
n2["label for n2"] & n4@{ label: "label for n4"} & n5@{ label: "label for n5"}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].label).toEqual('label for n2');
expect(data4Layout.nodes[1].label).toEqual('label for n4');
expect(data4Layout.nodes[2].label).toEqual('label for n5');
});
it('should be possible to use @ syntax to add labels on multi nodes with edge/link', function () {
const res = flow.parse(`flowchart TD
A["A"] --> B["for B"] & C@{ label: "for c"} & E@{label : "for E"}
D@{label: "for D"}
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(5);
expect(data4Layout.nodes[0].label).toEqual('A');
expect(data4Layout.nodes[1].label).toEqual('for B');
expect(data4Layout.nodes[2].label).toEqual('for c');
expect(data4Layout.nodes[3].label).toEqual('for E');
expect(data4Layout.nodes[4].label).toEqual('for D');
});
it('should be possible to use @ syntax in labels', function () {
const res = flow.parse(`flowchart TD
A["@A@"] --> B["@for@ B@"] & C@{ label: "@for@ c@"} & E{"\`@for@ E@\`"} & D(("@for@ D@"))
H1{{"@for@ H@"}}
H2{{"\`@for@ H@\`"}}
Q1{"@for@ Q@"}
Q2{"\`@for@ Q@\`"}
AS1>"@for@ AS@"]
AS2>"\`@for@ AS@\`"]
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(11);
expect(data4Layout.nodes[0].label).toEqual('@A@');
expect(data4Layout.nodes[1].label).toEqual('@for@ B@');
expect(data4Layout.nodes[2].label).toEqual('@for@ c@');
expect(data4Layout.nodes[3].label).toEqual('@for@ E@');
expect(data4Layout.nodes[4].label).toEqual('@for@ D@');
expect(data4Layout.nodes[5].label).toEqual('@for@ H@');
expect(data4Layout.nodes[6].label).toEqual('@for@ H@');
expect(data4Layout.nodes[7].label).toEqual('@for@ Q@');
expect(data4Layout.nodes[8].label).toEqual('@for@ Q@');
expect(data4Layout.nodes[9].label).toEqual('@for@ AS@');
expect(data4Layout.nodes[10].label).toEqual('@for@ AS@');
});
it('should handle unique edge creation with using @ and &', function () {
const res = flow.parse(`flowchart TD
A & B e1@--> C & D
A1 e2@--> C1 & D1
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(7);
expect(data4Layout.edges.length).toBe(6);
expect(data4Layout.edges[0].id).toEqual('L_A_C_0');
expect(data4Layout.edges[1].id).toEqual('L_A_D_0');
expect(data4Layout.edges[2].id).toEqual('e1');
expect(data4Layout.edges[3].id).toEqual('L_B_D_0');
expect(data4Layout.edges[4].id).toEqual('e2');
expect(data4Layout.edges[5].id).toEqual('L_A1_D1_0');
});
it('should handle redefine same edge ids again', function () {
const res = flow.parse(`flowchart TD
A & B e1@--> C & D
A1 e1@--> C1 & D1
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(7);
expect(data4Layout.edges.length).toBe(6);
expect(data4Layout.edges[0].id).toEqual('L_A_C_0');
expect(data4Layout.edges[1].id).toEqual('L_A_D_0');
expect(data4Layout.edges[2].id).toEqual('e1');
expect(data4Layout.edges[3].id).toEqual('L_B_D_0');
expect(data4Layout.edges[4].id).toEqual('L_A1_C1_0');
expect(data4Layout.edges[5].id).toEqual('L_A1_D1_0');
});
it('should handle overriding edge animate again', function () {
const res = flow.parse(`flowchart TD
A e1@--> B
C e2@--> D
E e3@--> F
e1@{ animate: true }
e2@{ animate: false }
e3@{ animate: true }
e3@{ animate: false }
`);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(6);
expect(data4Layout.edges.length).toBe(3);
expect(data4Layout.edges[0].id).toEqual('e1');
expect(data4Layout.edges[0].animate).toEqual(true);
expect(data4Layout.edges[1].id).toEqual('e2');
expect(data4Layout.edges[1].animate).toEqual(false);
expect(data4Layout.edges[2].id).toEqual('e3');
expect(data4Layout.edges[2].animate).toEqual(false);
});
it.skip('should be possible to use @ syntax to add labels with trail spaces', function () {
const res = flow.parse(
`flowchart TB
n2["label for n2"] & n4@{ label: "label for n4"} & n5@{ label: "label for n5"} `
);
const data4Layout = flow.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].label).toEqual('label for n2');
expect(data4Layout.nodes[1].label).toEqual('label for n4');
expect(data4Layout.nodes[2].label).toEqual('label for n5');
});
});

View File

@@ -1,362 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
const keywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'default',
'linkStyle',
'interpolate',
'classDef',
'class',
'href',
'call',
'click',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
];
const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/', '_'];
describe('[Chevrotain Singlenodes] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
});
it('should handle a single node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;A;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('A').styles.length).toBe(0);
});
it('should handle a single node with white space after it (SN1)', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;A ;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('A').styles.length).toBe(0);
});
it('should handle a single square node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a[A];');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').styles.length).toBe(0);
expect(vert.get('a').type).toBe('square');
});
it('should handle a single round square node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a[A];');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').styles.length).toBe(0);
expect(vert.get('a').type).toBe('square');
});
it('should handle a single circle node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a((A));');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('circle');
});
it('should handle a single round node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a(A);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('round');
});
it('should handle a single diamond node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a{A};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('diamond');
});
it('should handle a single diamond node with whitespace after it', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a{A} ;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('diamond');
});
it('should handle a single diamond node with html in it (SN3)', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a{A <br> end};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('diamond');
expect(vert.get('a').text).toBe('A <br> end');
});
it('should handle a single hexagon node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a{{A}};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('hexagon');
});
it('should handle a single hexagon node with html in it', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a{{A <br> end}};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('hexagon');
expect(vert.get('a').text).toBe('A <br> end');
});
it('should handle a single round node with html in it', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a(A <br> end);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('round');
expect(vert.get('a').text).toBe('A <br> end');
});
it('should handle a single double circle node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a(((A)));');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('doublecircle');
});
it('should handle a single double circle node with whitespace after it', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a(((A))) ;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('doublecircle');
});
it('should handle a single double circle node with html in it (SN3)', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a(((A <br> end)));');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('doublecircle');
expect(vert.get('a').text).toBe('A <br> end');
});
it('should handle a single node with alphanumerics starting on a char', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;id1;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('id1').styles.length).toBe(0);
});
it('should handle a single node with a single digit', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;1;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('1').text).toBe('1');
});
it('should handle a single node with a single digit in a subgraph', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;subgraph "hello";1;end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('1').text).toBe('1');
});
it('should handle a single node with alphanumerics starting on a num', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;1id;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('1id').styles.length).toBe(0);
});
it('should handle a single node with alphanumerics containing a minus sign', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;i-d;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('i-d').styles.length).toBe(0);
});
it('should handle a single node with alphanumerics containing a underscore sign', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;i_d;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('i_d').styles.length).toBe(0);
});
it.each(keywords)('should handle keywords between dashes "-"', function (keyword) {
const res = flow.parse(`graph TD;a-${keyword}-node;`);
const vert = flow.yy.getVertices();
expect(vert.get(`a-${keyword}-node`).text).toBe(`a-${keyword}-node`);
});
it.each(keywords)('should handle keywords between periods "."', function (keyword) {
const res = flow.parse(`graph TD;a.${keyword}.node;`);
const vert = flow.yy.getVertices();
expect(vert.get(`a.${keyword}.node`).text).toBe(`a.${keyword}.node`);
});
it.each(keywords)('should handle keywords between underscores "_"', function (keyword) {
const res = flow.parse(`graph TD;a_${keyword}_node;`);
const vert = flow.yy.getVertices();
expect(vert.get(`a_${keyword}_node`).text).toBe(`a_${keyword}_node`);
});
it.each(keywords)('should handle nodes ending in %s', function (keyword) {
const res = flow.parse(`graph TD;node_${keyword};node.${keyword};node-${keyword};`);
const vert = flow.yy.getVertices();
expect(vert.get(`node_${keyword}`).text).toBe(`node_${keyword}`);
expect(vert.get(`node.${keyword}`).text).toBe(`node.${keyword}`);
expect(vert.get(`node-${keyword}`).text).toBe(`node-${keyword}`);
});
const errorKeywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'linkStyle',
'interpolate',
'classDef',
'class',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
];
it.each(errorKeywords)('should throw error at nodes beginning with %s', function (keyword) {
const str = `graph TD;${keyword}.node;${keyword}-node;${keyword}/node`;
const vert = flow.yy.getVertices();
expect(() => flow.parse(str)).toThrowError();
});
const workingKeywords = ['default', 'href', 'click', 'call'];
it.each(workingKeywords)('should parse node beginning with %s', function (keyword) {
flow.parse(`graph TD; ${keyword}.node;${keyword}-node;${keyword}/node;`);
const vert = flow.yy.getVertices();
expect(vert.get(`${keyword}.node`).text).toBe(`${keyword}.node`);
expect(vert.get(`${keyword}-node`).text).toBe(`${keyword}-node`);
expect(vert.get(`${keyword}/node`).text).toBe(`${keyword}/node`);
});
it.each(specialChars)(
'should allow node ids of single special characters',
function (specialChar) {
flow.parse(`graph TD; ${specialChar} --> A`);
const vert = flow.yy.getVertices();
expect(vert.get(`${specialChar}`).text).toBe(`${specialChar}`);
}
);
it.each(specialChars)(
'should allow node ids with special characters at start of id',
function (specialChar) {
flow.parse(`graph TD; ${specialChar}node --> A`);
const vert = flow.yy.getVertices();
expect(vert.get(`${specialChar}node`).text).toBe(`${specialChar}node`);
}
);
it.each(specialChars)(
'should allow node ids with special characters at end of id',
function (specialChar) {
flow.parse(`graph TD; node${specialChar} --> A`);
const vert = flow.yy.getVertices();
expect(vert.get(`node${specialChar}`).text).toBe(`node${specialChar}`);
}
);
});

View File

@@ -1,379 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('[Chevrotain Style] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.yy.setGen('gen-2');
});
// log.debug(flow.parse('graph TD;style Q background:#fff;'));
it('should handle styles for vertices', function () {
const res = flow.parse('graph TD;style Q background:#fff;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('Q').styles.length).toBe(1);
expect(vert.get('Q').styles[0]).toBe('background:#fff');
});
it('should handle multiple styles for a vortex', function () {
const res = flow.parse('graph TD;style R background:#fff,border:1px solid red;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('R').styles.length).toBe(2);
expect(vert.get('R').styles[0]).toBe('background:#fff');
expect(vert.get('R').styles[1]).toBe('border:1px solid red');
});
it('should handle multiple styles in a graph', function () {
const res = flow.parse(
'graph TD;style S background:#aaa;\nstyle T background:#bbb,border:1px solid red;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('S').styles.length).toBe(1);
expect(vert.get('T').styles.length).toBe(2);
expect(vert.get('S').styles[0]).toBe('background:#aaa');
expect(vert.get('T').styles[0]).toBe('background:#bbb');
expect(vert.get('T').styles[1]).toBe('border:1px solid red');
});
it('should handle styles and graph definitions in a graph', function () {
const res = flow.parse(
'graph TD;S-->T;\nstyle S background:#aaa;\nstyle T background:#bbb,border:1px solid red;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('S').styles.length).toBe(1);
expect(vert.get('T').styles.length).toBe(2);
expect(vert.get('S').styles[0]).toBe('background:#aaa');
expect(vert.get('T').styles[0]).toBe('background:#bbb');
expect(vert.get('T').styles[1]).toBe('border:1px solid red');
});
it('should handle styles and graph definitions in a graph', function () {
const res = flow.parse('graph TD;style T background:#bbb,border:1px solid red;');
// const res = flow.parse('graph TD;style T background: #bbb;');
const vert = flow.yy.getVertices();
expect(vert.get('T').styles.length).toBe(2);
expect(vert.get('T').styles[0]).toBe('background:#bbb');
expect(vert.get('T').styles[1]).toBe('border:1px solid red');
});
it('should keep node label text (if already defined) when a style is applied', function () {
const res = flow.parse(
'graph TD;A(( ));B((Test));C;style A background:#fff;style D border:1px solid red;'
);
const vert = flow.yy.getVertices();
expect(vert.get('A').text).toBe('');
expect(vert.get('B').text).toBe('Test');
expect(vert.get('C').text).toBe('C');
expect(vert.get('D').text).toBe('D');
});
it('should be possible to declare a class', function () {
const res = flow.parse('graph TD;classDef exClass background:#bbb,border:1px solid red;');
// const res = flow.parse('graph TD;style T background: #bbb;');
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1px solid red');
});
it('should be possible to declare a class with animations', function () {
// Simplified test - complex escaped comma syntax not yet supported in Chevrotain parser
const res = flow.parse(
'graph TD;classDef exClass stroke-width:2,stroke-dasharray:10,stroke-dashoffset:-180,animation:edge-animation-frame,stroke-linecap:round;'
);
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(5);
expect(classes.get('exClass').styles[0]).toBe('stroke-width:2');
expect(classes.get('exClass').styles[1]).toBe('stroke-dasharray:10');
expect(classes.get('exClass').styles[2]).toBe('stroke-dashoffset:-180');
expect(classes.get('exClass').styles[3]).toBe('animation:edge-animation-frame');
expect(classes.get('exClass').styles[4]).toBe('stroke-linecap:round');
});
it('should be possible to declare multiple classes', function () {
const res = flow.parse(
'graph TD;classDef firstClass,secondClass background:#bbb,border:1px solid red;'
);
const classes = flow.yy.getClasses();
expect(classes.get('firstClass').styles.length).toBe(2);
expect(classes.get('firstClass').styles[0]).toBe('background:#bbb');
expect(classes.get('firstClass').styles[1]).toBe('border:1px solid red');
expect(classes.get('secondClass').styles.length).toBe(2);
expect(classes.get('secondClass').styles[0]).toBe('background:#bbb');
expect(classes.get('secondClass').styles[1]).toBe('border:1px solid red');
});
it('should be possible to declare a class with a dot in the style', function () {
const res = flow.parse('graph TD;classDef exClass background:#bbb,border:1.5px solid red;');
// const res = flow.parse('graph TD;style T background: #bbb;');
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1.5px solid red');
});
it('should be possible to declare a class with a space in the style', function () {
const res = flow.parse('graph TD;classDef exClass background: #bbb,border:1.5px solid red;');
// const res = flow.parse('graph TD;style T background : #bbb;');
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background: #bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1.5px solid red');
});
it('should be possible to apply a class to a vertex', function () {
let statement = '';
statement = statement + 'graph TD;' + '\n';
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'a-->b;' + '\n';
statement = statement + 'class a exClass;';
const res = flow.parse(statement);
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1px solid red');
});
it('should be possible to apply a class to a vertex with an id containing _', function () {
let statement = '';
statement = statement + 'graph TD;' + '\n';
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'a_a-->b_b;' + '\n';
statement = statement + 'class a_a exClass;';
const res = flow.parse(statement);
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1px solid red');
});
it('should be possible to apply a class to a vertex directly', function () {
let statement = '';
statement = statement + 'graph TD;' + '\n';
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'a-->b[test]:::exClass;' + '\n';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(vertices.get('b').classes[0]).toBe('exClass');
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1px solid red');
});
it('should be possible to apply a class to a vertex directly : usecase A[text].class ', function () {
let statement = '';
statement = statement + 'graph TD;' + '\n';
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'b[test]:::exClass;' + '\n';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(vertices.get('b').classes[0]).toBe('exClass');
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1px solid red');
});
it('should be possible to apply a class to a vertex directly : usecase A[text].class-->B[test2] ', function () {
let statement = '';
statement = statement + 'graph TD;' + '\n';
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'A[test]:::exClass-->B[test2];' + '\n';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(vertices.get('A').classes[0]).toBe('exClass');
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1px solid red');
});
it('should be possible to apply a class to a vertex directly 2', function () {
let statement = '';
statement = statement + 'graph TD;' + '\n';
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'a-->b[1 a a text!.]:::exClass;' + '\n';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(vertices.get('b').classes[0]).toBe('exClass');
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1px solid red');
});
it('should be possible to apply a class to a comma separated list of vertices', function () {
let statement = '';
statement = statement + 'graph TD;' + '\n';
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'a-->b;' + '\n';
statement = statement + 'class a,b exClass;';
const res = flow.parse(statement);
const classes = flow.yy.getClasses();
const vertices = flow.yy.getVertices();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1px solid red');
expect(vertices.get('a').classes[0]).toBe('exClass');
expect(vertices.get('b').classes[0]).toBe('exClass');
});
it('should handle style definitions with more then 1 digit in a row', function () {
const res = flow.parse(
'graph TD\n' +
'A-->B1\n' +
'A-->B2\n' +
'A-->B3\n' +
'A-->B4\n' +
'A-->B5\n' +
'A-->B6\n' +
'A-->B7\n' +
'A-->B8\n' +
'A-->B9\n' +
'A-->B10\n' +
'A-->B11\n' +
'linkStyle 10 stroke-width:1px;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle style definitions within number of edges', function () {
expect(() =>
parser.parser
.parse(
`graph TD
A-->B
linkStyle 1 stroke-width:1px;`
)
.toThrow(
'The index 1 for linkStyle is out of bounds. Valid indices for linkStyle are between 0 and 0. (Help: Ensure that the index is within the range of existing edges.)'
)
);
});
it('should handle style definitions within number of edges', function () {
const res = flow.parse(`graph TD
A-->B
linkStyle 0 stroke-width:1px;`);
const edges = flow.yy.getEdges();
expect(edges[0].style[0]).toBe('stroke-width:1px');
});
it('should handle multi-numbered style definitions with more then 1 digit in a row', function () {
const res = flow.parse(
'graph TD\n' +
'A-->B1\n' +
'A-->B2\n' +
'A-->B3\n' +
'A-->B4\n' +
'A-->B5\n' +
'A-->B6\n' +
'A-->B7\n' +
'A-->B8\n' +
'A-->B9\n' +
'A-->B10\n' +
'A-->B11\n' +
'A-->B12\n' +
'linkStyle 10,11 stroke-width:1px;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle classDefs with style in classes', function () {
const res = flow.parse('graph TD\nA-->B\nclassDef exClass font-style:bold;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle classDefs with % in classes', function () {
const res = flow.parse(
'graph TD\nA-->B\nclassDef exClass fill:#f96,stroke:#333,stroke-width:4px,font-size:50%,font-style:bold;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle multiple vertices with style', function () {
const res = flow.parse(`
graph TD
classDef C1 stroke-dasharray:4
classDef C2 stroke-dasharray:6
A & B:::C1 & D:::C1 --> E:::C2
`);
const vert = flow.yy.getVertices();
expect(vert.get('A').classes.length).toBe(0);
expect(vert.get('B').classes[0]).toBe('C1');
expect(vert.get('D').classes[0]).toBe('C1');
expect(vert.get('E').classes[0]).toBe('C2');
});
});

View File

@@ -1,312 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('when parsing subgraphs with Chevrotain', function () {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.yy.setGen('gen-2');
});
it('should handle subgraph with tab indentation', function () {
const res = flow.parse('graph TB\nsubgraph One\n\ta1-->a2\nend');
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
expect(subgraph.nodes[0]).toBe('a1');
expect(subgraph.nodes[1]).toBe('a2');
expect(subgraph.title).toBe('One');
expect(subgraph.id).toBe('One');
});
it('should handle subgraph with chaining nodes indentation', function () {
const res = flow.parse('graph TB\nsubgraph One\n\ta1-->a2-->a3\nend');
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(3);
expect(subgraph.nodes[0]).toBe('a1');
expect(subgraph.nodes[1]).toBe('a2');
expect(subgraph.nodes[2]).toBe('a3');
expect(subgraph.title).toBe('One');
expect(subgraph.id).toBe('One');
});
it('should handle subgraph with multiple words in title', function () {
const res = flow.parse('graph TB\nsubgraph "Some Title"\n\ta1-->a2\nend');
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
expect(subgraph.nodes[0]).toBe('a1');
expect(subgraph.nodes[1]).toBe('a2');
expect(subgraph.title).toBe('Some Title');
expect(subgraph.id).toBe('subGraph0');
});
it('should handle subgraph with id and title notation', function () {
const res = flow.parse('graph TB\nsubgraph some-id[Some Title]\n\ta1-->a2\nend');
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
expect(subgraph.nodes[0]).toBe('a1');
expect(subgraph.nodes[1]).toBe('a2');
expect(subgraph.title).toBe('Some Title');
expect(subgraph.id).toBe('some-id');
});
it.skip('should handle subgraph without id and space in title', function () {
const res = flow.parse('graph TB\nsubgraph Some Title\n\ta1-->a2\nend');
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
expect(subgraph.nodes[0]).toBe('a1');
expect(subgraph.nodes[1]).toBe('a2');
expect(subgraph.title).toBe('Some Title');
expect(subgraph.id).toBe('some-id');
});
it('should handle subgraph id starting with a number', function () {
const res = flow.parse(`graph TD
A[Christmas] -->|Get money| B(Go shopping)
subgraph 1test
A
end`);
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(1);
expect(subgraph.nodes[0]).toBe('A');
expect(subgraph.id).toBe('1test');
});
it('should handle subgraphs1', function () {
const res = flow.parse('graph TD;A-->B;subgraph myTitle;c-->d;end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle subgraphs with title in quotes', function () {
const res = flow.parse('graph TD;A-->B;subgraph "title in quotes";c-->d;end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('title in quotes');
expect(edges[0].type).toBe('arrow_point');
});
it('should handle subgraphs in old style that was broken', function () {
const res = flow.parse('graph TD;A-->B;subgraph old style that is broken;c-->d;end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('old style that is broken');
expect(edges[0].type).toBe('arrow_point');
});
it('should handle subgraphs with dashes in the title', function () {
const res = flow.parse('graph TD;A-->B;subgraph a-b-c;c-->d;end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('a-b-c');
expect(edges[0].type).toBe('arrow_point');
});
it('should handle subgraphs with id and title in brackets', function () {
const res = flow.parse('graph TD;A-->B;subgraph uid1[text of doom];c-->d;end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('text of doom');
expect(subgraph.id).toBe('uid1');
expect(edges[0].type).toBe('arrow_point');
});
it('should handle subgraphs with id and title in brackets and quotes', function () {
const res = flow.parse('graph TD;A-->B;subgraph uid2["text of doom"];c-->d;end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('text of doom');
expect(subgraph.id).toBe('uid2');
expect(edges[0].type).toBe('arrow_point');
});
it('should handle subgraphs with id and title in brackets without spaces', function () {
const res = flow.parse('graph TD;A-->B;subgraph uid2[textofdoom];c-->d;end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.title).toBe('textofdoom');
expect(subgraph.id).toBe('uid2');
expect(edges[0].type).toBe('arrow_point');
});
it('should handle subgraphs2', function () {
const res = flow.parse('graph TD\nA-->B\nsubgraph myTitle\n\n c-->d \nend\n');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle subgraphs3', function () {
const res = flow.parse('graph TD\nA-->B\nsubgraph myTitle \n\n c-->d \nend\n');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle nested subgraphs', function () {
const str =
'graph TD\n' +
'A-->B\n' +
'subgraph myTitle\n\n' +
' c-->d \n\n' +
' subgraph inner\n\n e-->f \n end \n\n' +
' subgraph inner\n\n h-->i \n end \n\n' +
'end\n';
const res = flow.parse(str);
});
it('should handle subgraphs4', function () {
const res = flow.parse('graph TD\nA-->B\nsubgraph myTitle\nc-->d\nend;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle subgraphs5', function () {
const res = flow.parse('graph TD\nA-->B\nsubgraph myTitle\nc-- text -->d\nd-->e\n end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle subgraphs with multi node statements in it', function () {
const res = flow.parse('graph TD\nA-->B\nsubgraph myTitle\na & b --> c & e\n end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle nested subgraphs 1', function () {
const res = flow.parse(`flowchart TB
subgraph A
b-->B
a
end
a-->c
subgraph B
c
end`);
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = subgraphs.find((o) => o.id === 'A');
const subgraphB = subgraphs.find((o) => o.id === 'B');
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphA.nodes).toContain('B');
expect(subgraphA.nodes).toContain('b');
expect(subgraphA.nodes).toContain('a');
expect(subgraphA.nodes).not.toContain('c');
});
it('should handle nested subgraphs 2', function () {
const res = flow.parse(`flowchart TB
b-->B
a-->c
subgraph B
c
end
subgraph A
a
b
B
end`);
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = subgraphs.find((o) => o.id === 'A');
const subgraphB = subgraphs.find((o) => o.id === 'B');
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphA.nodes).toContain('B');
expect(subgraphA.nodes).toContain('b');
expect(subgraphA.nodes).toContain('a');
expect(subgraphA.nodes).not.toContain('c');
});
it('should handle nested subgraphs 3', function () {
const res = flow.parse(`flowchart TB
subgraph B
c
end
a-->c
subgraph A
b-->B
a
end`);
const subgraphs = flow.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = subgraphs.find((o) => o.id === 'A');
const subgraphB = subgraphs.find((o) => o.id === 'B');
expect(subgraphB.nodes[0]).toBe('c');
expect(subgraphA.nodes).toContain('B');
expect(subgraphA.nodes).toContain('b');
expect(subgraphA.nodes).toContain('a');
expect(subgraphA.nodes).not.toContain('c');
});
});

View File

@@ -1,479 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('[Text] when parsing with Chevrotain', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
});
describe('it should handle text on edges', function () {
it('should handle text without space', function () {
const res = flow.parse('graph TD;A--x|textNoSpace|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle with space', function () {
const res = flow.parse('graph TD;A--x|text including space|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle text with /', function () {
const res = flow.parse('graph TD;A--x|text with / should work|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].text).toBe('text with / should work');
});
it('should handle space and space between vertices and link', function () {
const res = flow.parse('graph TD;A --x|textNoSpace| B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle space and CAPS', function () {
const res = flow.parse('graph TD;A--x|text including CAPS space|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle space and dir', function () {
const res = flow.parse('graph TD;A--x|text including URL space|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(edges[0].text).toBe('text including URL space');
});
it('should handle space and send', function () {
const res = flow.parse('graph TD;A--text including URL space and send-->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('text including URL space and send');
});
it('should handle space and send', function () {
const res = flow.parse('graph TD;A-- text including URL space and send -->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('text including URL space and send');
});
it('should handle space and dir (TD)', function () {
const res = flow.parse('graph TD;A--x|text including R TD space|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(edges[0].text).toBe('text including R TD space');
});
it('should handle `', function () {
const res = flow.parse('graph TD;A--x|text including `|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(edges[0].text).toBe('text including `');
});
it('should handle v in node ids only v', function () {
// only v
const res = flow.parse('graph TD;A--xv(my text);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(vert.get('v').text).toBe('my text');
});
it('should handle v in node ids v at end', function () {
// v at end
const res = flow.parse('graph TD;A--xcsv(my text);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(vert.get('csv').text).toBe('my text');
});
it('should handle v in node ids v in middle', function () {
// v in middle
const res = flow.parse('graph TD;A--xava(my text);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(vert.get('ava').text).toBe('my text');
});
it('should handle v in node ids, v at start', function () {
// v at start
const res = flow.parse('graph TD;A--xva(my text);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(vert.get('va').text).toBe('my text');
});
it('should handle keywords', function () {
const res = flow.parse('graph TD;A--x|text including graph space|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].text).toBe('text including graph space');
});
it('should handle keywords', function () {
const res = flow.parse('graph TD;V-->a[v]');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('a').text).toBe('v');
});
it('should handle quoted text', function () {
const res = flow.parse('graph TD;V-- "test string()" -->a[v]');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].text).toBe('test string()');
});
});
describe('it should handle text on lines', () => {
it('should handle normal text on lines', function () {
const res = flow.parse('graph TD;A-- test text with == -->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].stroke).toBe('normal');
});
it('should handle dotted text on lines (TD3)', function () {
const res = flow.parse('graph TD;A-. test text with == .->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].stroke).toBe('dotted');
});
it('should handle thick text on lines', function () {
const res = flow.parse('graph TD;A== test text with - ==>B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].stroke).toBe('thick');
});
});
describe('it should handle text on edges using the new notation', function () {
it('should handle text without space', function () {
const res = flow.parse('graph TD;A-- textNoSpace --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle text with multiple leading space', function () {
const res = flow.parse('graph TD;A-- textNoSpace --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle with space', function () {
const res = flow.parse('graph TD;A-- text including space --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle text with /', function () {
const res = flow.parse('graph TD;A -- text with / should work --x B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].text).toBe('text with / should work');
});
it('should handle space and space between vertices and link', function () {
const res = flow.parse('graph TD;A -- textNoSpace --x B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle space and CAPS', function () {
const res = flow.parse('graph TD;A-- text including CAPS space --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle space and dir', function () {
const res = flow.parse('graph TD;A-- text including URL space --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(edges[0].text).toBe('text including URL space');
});
it('should handle space and dir (TD2)', function () {
const res = flow.parse('graph TD;A-- text including R TD space --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(edges[0].text).toBe('text including R TD space');
});
it('should handle keywords', function () {
const res = flow.parse('graph TD;A-- text including graph space and v --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].text).toBe('text including graph space and v');
});
it('should handle keywords', function () {
const res = flow.parse('graph TD;A-- text including graph space and v --xB[blav]');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].text).toBe('text including graph space and v');
});
});
describe('it should handle text in vertices, ', function () {
it('should handle space', function () {
const res = flow.parse('graph TD;A-->C(Chimpansen hoppar);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('C').type).toBe('round');
expect(vert.get('C').text).toBe('Chimpansen hoppar');
});
const keywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'default',
'linkStyle',
'interpolate',
'classDef',
'class',
'href',
'call',
'click',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
'kitty',
];
const shapes = [
{ start: '[', end: ']', name: 'square' },
{ start: '(', end: ')', name: 'round' },
{ start: '{', end: '}', name: 'diamond' },
{ start: '(-', end: '-)', name: 'ellipse' },
{ start: '([', end: '])', name: 'stadium' },
{ start: '>', end: ']', name: 'odd' },
{ start: '[(', end: ')]', name: 'cylinder' },
{ start: '(((', end: ')))', name: 'doublecircle' },
{ start: '[/', end: '\\]', name: 'trapezoid' },
{ start: '[\\', end: '/]', name: 'inv_trapezoid' },
{ start: '[/', end: '/]', name: 'lean_right' },
{ start: '[\\', end: '\\]', name: 'lean_left' },
{ start: '[[', end: ']]', name: 'subroutine' },
{ start: '{{', end: '}}', name: 'hexagon' },
];
shapes.forEach((shape) => {
it.each(keywords)(`should handle %s keyword in ${shape.name} vertex`, function (keyword) {
const rest = flow.parse(
`graph TD;A_${keyword}_node-->B${shape.start}This node has a ${keyword} as text${shape.end};`
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('B').type).toBe(`${shape.name}`);
expect(vert.get('B').text).toBe(`This node has a ${keyword} as text`);
});
});
it.each(keywords)('should handle %s keyword in rect vertex', function (keyword) {
const rest = flow.parse(
`graph TD;A_${keyword}_node-->B[|borders:lt|This node has a ${keyword} as text];`
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('B').type).toBe('rect');
expect(vert.get('B').text).toBe(`This node has a ${keyword} as text`);
});
it('should handle edge case for odd vertex with node id ending with minus', function () {
flow.parse('graph TD;A_node-->odd->Vertex Text];');
const vert = flow.yy.getVertices();
expect(vert.get('odd-').type).toBe('odd');
expect(vert.get('odd-').text).toBe('Vertex Text');
});
it('should allow forward slashes in lean_right vertices', function () {
const rest = flow.parse(`graph TD;A_node-->B[/This node has a / as text/];`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('B').type).toBe('lean_right');
expect(vert.get('B').text).toBe(`This node has a / as text`);
});
it('should allow back slashes in lean_left vertices', function () {
const rest = flow.parse(`graph TD;A_node-->B[\\This node has a \\ as text\\];`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('B').type).toBe('lean_left');
expect(vert.get('B').text).toBe(`This node has a \\ as text`);
});
it('should handle åäö and minus', function () {
const res = flow.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('C').type).toBe('diamond');
expect(vert.get('C').text).toBe('Chimpansen hoppar åäö-ÅÄÖ');
});
it('should handle with åäö, minus and space and br', function () {
const res = flow.parse('graph TD;A-->C(Chimpansen hoppar åäö <br> - ÅÄÖ);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('C').type).toBe('round');
expect(vert.get('C').text).toBe('Chimpansen hoppar åäö <br> - ÅÄÖ');
});
it('should handle unicode chars', function () {
const res = flow.parse('graph TD;A-->C(Начало);');
const vert = flow.yy.getVertices();
expect(vert.get('C').text).toBe('Начало');
});
it('should handle backslash', function () {
const res = flow.parse('graph TD;A-->C(c:\\windows);');
const vert = flow.yy.getVertices();
expect(vert.get('C').text).toBe('c:\\windows');
});
it('should handle CAPS', function () {
const res = flow.parse('graph TD;A-->C(some CAPS);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('C').type).toBe('round');
expect(vert.get('C').text).toBe('some CAPS');
});
it('should handle directions', function () {
const res = flow.parse('graph TD;A-->C(some URL);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('C').type).toBe('round');
expect(vert.get('C').text).toBe('some URL');
});
});
it('should handle multi-line text', function () {
const res = flow.parse('graph TD;A--o|text space|B;\n B-->|more text with space|C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(edges[0].type).toBe('arrow_circle');
expect(edges[1].type).toBe('arrow_point');
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
expect(edges[1].text).toBe('more text with space');
});
it('should handle text in vertices with space', function () {
const res = flow.parse('graph TD;A[chimpansen hoppar]-->C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').type).toBe('square');
expect(vert.get('A').text).toBe('chimpansen hoppar');
});
it('should handle text in vertices with space with spaces between vertices and link', function () {
const res = flow.parse('graph TD;A[chimpansen hoppar] --> C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').type).toBe('square');
expect(vert.get('A').text).toBe('chimpansen hoppar');
});
});

View File

@@ -1,222 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('when parsing flowcharts with Chevrotain', function () {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.yy.setGen('gen-2');
});
it('should handle chaining of vertices', function () {
const res = flow.parse(`
graph TD
A-->B-->C;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
expect(edges[1].type).toBe('arrow_point');
expect(edges[1].text).toBe('');
});
it('should handle chaining of vertices', function () {
const res = flow.parse(`
graph TD
A & B --> C;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('C');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
expect(edges[1].type).toBe('arrow_point');
expect(edges[1].text).toBe('');
});
it('should multiple vertices in link statement in the beginning', function () {
const res = flow.parse(`
graph TD
A-->B & C;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
expect(edges[1].start).toBe('A');
expect(edges[1].end).toBe('C');
expect(edges[1].type).toBe('arrow_point');
expect(edges[1].text).toBe('');
});
it('should multiple vertices in link statement at the end', function () {
const res = flow.parse(`
graph TD
A & B--> C & D;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(vert.get('D').id).toBe('D');
expect(edges.length).toBe(4);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('C');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
expect(edges[1].start).toBe('A');
expect(edges[1].end).toBe('D');
expect(edges[1].type).toBe('arrow_point');
expect(edges[1].text).toBe('');
expect(edges[2].start).toBe('B');
expect(edges[2].end).toBe('C');
expect(edges[2].type).toBe('arrow_point');
expect(edges[2].text).toBe('');
expect(edges[3].start).toBe('B');
expect(edges[3].end).toBe('D');
expect(edges[3].type).toBe('arrow_point');
expect(edges[3].text).toBe('');
});
it('should handle chaining of vertices at both ends at once', function () {
const res = flow.parse(`
graph TD
A & B--> C & D;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('C').id).toBe('C');
expect(vert.get('D').id).toBe('D');
expect(edges.length).toBe(4);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('C');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
expect(edges[1].start).toBe('A');
expect(edges[1].end).toBe('D');
expect(edges[1].type).toBe('arrow_point');
expect(edges[1].text).toBe('');
expect(edges[2].start).toBe('B');
expect(edges[2].end).toBe('C');
expect(edges[2].type).toBe('arrow_point');
expect(edges[2].text).toBe('');
expect(edges[3].start).toBe('B');
expect(edges[3].end).toBe('D');
expect(edges[3].type).toBe('arrow_point');
expect(edges[3].text).toBe('');
});
it('should handle chaining and multiple nodes in link statement FVC ', function () {
const res = flow.parse(`
graph TD
A --> B & B2 & C --> D2;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('B2').id).toBe('B2');
expect(vert.get('C').id).toBe('C');
expect(vert.get('D2').id).toBe('D2');
expect(edges.length).toBe(6);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
expect(edges[1].start).toBe('A');
expect(edges[1].end).toBe('B2');
expect(edges[1].type).toBe('arrow_point');
expect(edges[1].text).toBe('');
expect(edges[2].start).toBe('A');
expect(edges[2].end).toBe('C');
expect(edges[2].type).toBe('arrow_point');
expect(edges[2].text).toBe('');
expect(edges[3].start).toBe('B');
expect(edges[3].end).toBe('D2');
expect(edges[3].type).toBe('arrow_point');
expect(edges[3].text).toBe('');
expect(edges[4].start).toBe('B2');
expect(edges[4].end).toBe('D2');
expect(edges[4].type).toBe('arrow_point');
expect(edges[4].text).toBe('');
expect(edges[5].start).toBe('C');
expect(edges[5].end).toBe('D2');
expect(edges[5].type).toBe('arrow_point');
expect(edges[5].text).toBe('');
});
it('should handle chaining and multiple nodes in link statement with extra info in statements', function () {
const res = flow.parse(`
graph TD
A[ h ] -- hello --> B[" test "]:::exClass & C --> D;
classDef exClass background:#bbb,border:1px solid red;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const classes = flow.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1px solid red');
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert.get('B').classes[0]).toBe('exClass');
expect(vert.get('C').id).toBe('C');
expect(vert.get('D').id).toBe('D');
expect(edges.length).toBe(4);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('hello');
expect(edges[1].start).toBe('A');
expect(edges[1].end).toBe('C');
expect(edges[1].type).toBe('arrow_point');
expect(edges[1].text).toBe('hello');
expect(edges[2].start).toBe('B');
expect(edges[2].end).toBe('D');
expect(edges[2].type).toBe('arrow_point');
expect(edges[2].text).toBe('');
expect(edges[3].start).toBe('C');
expect(edges[3].end).toBe('D');
expect(edges[3].type).toBe('arrow_point');
expect(edges[3].text).toBe('');
});
});

View File

@@ -1,230 +0,0 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import { cleanupComments } from '../../../diagram-api/comments.js';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('parsing a flow chart with Chevrotain', function () {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
});
it('should handle a trailing whitespaces after statements', function () {
const res = flow.parse(cleanupComments('graph TD;\n\n\n %% Comment\n A-->B; \n B-->C;'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('');
});
it('should handle node names with "end" substring', function () {
const res = flow.parse('graph TD\nendpoint --> sender');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('endpoint').id).toBe('endpoint');
expect(vert.get('sender').id).toBe('sender');
expect(edges[0].start).toBe('endpoint');
expect(edges[0].end).toBe('sender');
});
it('should handle node names ending with keywords', function () {
const res = flow.parse('graph TD\nblend --> monograph');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('blend').id).toBe('blend');
expect(vert.get('monograph').id).toBe('monograph');
expect(edges[0].start).toBe('blend');
expect(edges[0].end).toBe('monograph');
});
it('should allow default in the node name/id', function () {
const res = flow.parse('graph TD\ndefault --> monograph');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('default').id).toBe('default');
expect(vert.get('monograph').id).toBe('monograph');
expect(edges[0].start).toBe('default');
expect(edges[0].end).toBe('monograph');
});
describe('special characters should be handled.', function () {
const charTest = function (char, result) {
const res = flow.parse('graph TD;A(' + char + ')-->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
if (result) {
expect(vert.get('A').text).toBe(result);
} else {
expect(vert.get('A').text).toBe(char);
}
flow.yy.clear();
};
it("should be able to parse a '.'", function () {
charTest('.');
charTest('Start 103a.a1');
});
it("should be able to parse a ':'", function () {
charTest(':');
});
it("should be able to parse a ','", function () {
charTest(',');
});
it("should be able to parse text containing '-'", function () {
charTest('a-b');
});
it("should be able to parse a '+'", function () {
charTest('+');
});
it("should be able to parse a '*'", function () {
charTest('*');
});
it("should be able to parse a '<'", function () {
charTest('<', '&lt;');
});
it("should be able to parse a '&'", function () {
charTest('&');
});
});
it('should be possible to use direction in node ids', function () {
let statement = '';
statement = statement + 'graph TD;' + '\n';
statement = statement + ' node1TB\n';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
const classes = flow.yy.getClasses();
expect(vertices.get('node1TB').id).toBe('node1TB');
});
it('should be possible to use direction in node ids', function () {
let statement = '';
statement = statement + 'graph TD;A--x|text including URL space|B;';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
const classes = flow.yy.getClasses();
expect(vertices.get('A').id).toBe('A');
});
it('should be possible to use numbers as labels', function () {
let statement = '';
statement = statement + 'graph TB;subgraph "number as labels";1;end;';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
expect(vertices.get('1').id).toBe('1');
});
it('should add accTitle and accDescr to flow chart', function () {
const flowChart = `graph LR
accTitle: Big decisions
accDescr: Flow chart of the decision making process
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
`;
flow.parse(flowChart);
expect(flow.yy.getAccTitle()).toBe('Big decisions');
expect(flow.yy.getAccDescription()).toBe('Flow chart of the decision making process');
});
it('should add accTitle and a multi line accDescr to flow chart', function () {
const flowChart = `graph LR
accTitle: Big decisions
accDescr {
Flow chart of the decision making process
with a second line
}
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
`;
flow.parse(flowChart);
expect(flow.yy.getAccTitle()).toBe('Big decisions');
expect(flow.yy.getAccDescription()).toBe(
`Flow chart of the decision making process
with a second line`
);
});
for (const unsafeProp of ['__proto__', 'constructor']) {
it(`should work with node id ${unsafeProp}`, function () {
const flowChart = `graph LR
${unsafeProp} --> A;`;
expect(() => {
flow.parse(flowChart);
}).not.toThrow();
});
it(`should work with tooltip id ${unsafeProp}`, function () {
const flowChart = `graph LR
click ${unsafeProp} callback "${unsafeProp}";`;
expect(() => {
flow.parse(flowChart);
}).not.toThrow();
});
it(`should work with class id ${unsafeProp}`, function () {
const flowChart = `graph LR
${unsafeProp} --> A;
classDef ${unsafeProp} color:#ffffff,fill:#000000;
class ${unsafeProp} ${unsafeProp};`;
expect(() => {
flow.parse(flowChart);
}).not.toThrow();
});
it(`should work with subgraph id ${unsafeProp}`, function () {
const flowChart = `graph LR
${unsafeProp} --> A;
subgraph ${unsafeProp}
C --> D;
end;`;
expect(() => {
flow.parse(flowChart);
}).not.toThrow();
});
}
});

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
import { cleanupComments } from '../../../diagram-api/comments.js';
@@ -9,15 +9,15 @@ setConfig({
describe('[Comments] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});
it('should handle comments', function () {
const res = flow.parse(cleanupComments('graph TD;\n%% Comment\n A-->B;'));
const res = flow.parser.parse(cleanupComments('graph TD;\n%% Comment\n A-->B;'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -29,10 +29,10 @@ describe('[Comments] when parsing', () => {
});
it('should handle comments at the start', function () {
const res = flow.parse(cleanupComments('%% Comment\ngraph TD;\n A-->B;'));
const res = flow.parser.parse(cleanupComments('%% Comment\ngraph TD;\n A-->B;'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -44,10 +44,10 @@ describe('[Comments] when parsing', () => {
});
it('should handle comments at the end', function () {
const res = flow.parse(cleanupComments('graph TD;\n A-->B\n %% Comment at the end\n'));
const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n %% Comment at the end\n'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -59,10 +59,10 @@ describe('[Comments] when parsing', () => {
});
it('should handle comments at the end no trailing newline', function () {
const res = flow.parse(cleanupComments('graph TD;\n A-->B\n%% Comment'));
const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n%% Comment'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -74,10 +74,10 @@ describe('[Comments] when parsing', () => {
});
it('should handle comments at the end many trailing newlines', function () {
const res = flow.parse(cleanupComments('graph TD;\n A-->B\n%% Comment\n\n\n'));
const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n%% Comment\n\n\n'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -89,10 +89,10 @@ describe('[Comments] when parsing', () => {
});
it('should handle no trailing newlines', function () {
const res = flow.parse(cleanupComments('graph TD;\n A-->B'));
const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -104,10 +104,10 @@ describe('[Comments] when parsing', () => {
});
it('should handle many trailing newlines', function () {
const res = flow.parse(cleanupComments('graph TD;\n A-->B\n\n'));
const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n\n'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -119,10 +119,10 @@ describe('[Comments] when parsing', () => {
});
it('should handle a comment with blank rows in-between', function () {
const res = flow.parse(cleanupComments('graph TD;\n\n\n %% Comment\n A-->B;'));
const res = flow.parser.parse(cleanupComments('graph TD;\n\n\n %% Comment\n A-->B;'));
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -134,14 +134,14 @@ describe('[Comments] when parsing', () => {
});
it('should handle a comment with mermaid flowchart code in them', function () {
const res = flow.parse(
const res = flow.parser.parse(
cleanupComments(
'graph TD;\n\n\n %% Test od>Odd shape]-->|Two line<br>edge comment|ro;\n A-->B;'
)
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,65 +8,62 @@ setConfig({
describe('when parsing directions', function () {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.yy.setGen('gen-2');
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});
it('should use default direction from top level', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
subgraph A
a --> b
end`);
const subgraphs = flow.yy.getSubGraphs();
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
// Fix test expectation to match actual parser behavior (both JISON and Chevrotain produce same order)
expect(subgraph.nodes[0]).toBe('a');
expect(subgraph.nodes[1]).toBe('b');
expect(subgraph.nodes[0]).toBe('b');
expect(subgraph.nodes[1]).toBe('a');
expect(subgraph.id).toBe('A');
expect(subgraph.dir).toBe(undefined);
});
it('should handle a subgraph with a direction', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
subgraph A
direction BT
a --> b
end`);
const subgraphs = flow.yy.getSubGraphs();
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
// Fix test expectation to match actual parser behavior (both JISON and Chevrotain produce same order)
expect(subgraph.nodes[0]).toBe('a');
expect(subgraph.nodes[1]).toBe('b');
expect(subgraph.nodes[0]).toBe('b');
expect(subgraph.nodes[1]).toBe('a');
expect(subgraph.id).toBe('A');
expect(subgraph.dir).toBe('BT');
});
it('should use the last defined direction', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
subgraph A
direction BT
a --> b
direction RL
end`);
const subgraphs = flow.yy.getSubGraphs();
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.nodes.length).toBe(2);
// Fix test expectation to match actual parser behavior (both JISON and Chevrotain produce same order)
expect(subgraph.nodes[0]).toBe('a');
expect(subgraph.nodes[1]).toBe('b');
expect(subgraph.nodes[0]).toBe('b');
expect(subgraph.nodes[1]).toBe('a');
expect(subgraph.id).toBe('A');
expect(subgraph.dir).toBe('RL');
});
it('should handle nested subgraphs 1', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
subgraph A
direction RL
b-->B
@@ -78,7 +75,7 @@ describe('when parsing directions', function () {
c
end`);
const subgraphs = flow.yy.getSubGraphs();
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraphA = subgraphs.find((o) => o.id === 'A');

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -63,27 +63,27 @@ const regularEdges = [
describe('[Edges] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});
it('should handle open ended edges', function () {
const res = flow.parse('graph TD;A---B;');
const edges = flow.yy.getEdges();
const res = flow.parser.parse('graph TD;A---B;');
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_open');
});
it('should handle cross ended edges', function () {
const res = flow.parse('graph TD;A--xB;');
const edges = flow.yy.getEdges();
const res = flow.parser.parse('graph TD;A--xB;');
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle open ended edges', function () {
const res = flow.parse('graph TD;A--oB;');
const edges = flow.yy.getEdges();
const res = flow.parser.parse('graph TD;A--oB;');
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_circle');
});
@@ -92,9 +92,11 @@ describe('[Edges] when parsing', () => {
describe('open ended edges with ids and labels', function () {
regularEdges.forEach((edgeType) => {
it(`should handle ${edgeType.stroke} ${edgeType.type} with no text`, function () {
const res = flow.parse(`flowchart TD;\nA e1@${edgeType.edgeStart}${edgeType.edgeEnd} B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const res = flow.parser.parse(
`flowchart TD;\nA e1@${edgeType.edgeStart}${edgeType.edgeEnd} B;`
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
@@ -106,9 +108,11 @@ describe('[Edges] when parsing', () => {
expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
});
it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () {
const res = flow.parse(`flowchart TD;\nA e1@${edgeType.edgeStart}${edgeType.edgeEnd} B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const res = flow.parser.parse(
`flowchart TD;\nA e1@${edgeType.edgeStart}${edgeType.edgeEnd} B;`
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
@@ -121,11 +125,11 @@ describe('[Edges] when parsing', () => {
});
});
it('should handle normal edges where you also have a node with metadata', function () {
const res = flow.parse(`flowchart LR
const res = flow.parser.parse(`flowchart LR
A id1@-->B
A@{ shape: 'rect' }
`);
const edges = flow.yy.getEdges();
const edges = flow.parser.yy.getEdges();
expect(edges[0].id).toBe('id1');
});
@@ -133,11 +137,11 @@ A@{ shape: 'rect' }
describe('double ended edges with ids and labels', function () {
doubleEndedEdges.forEach((edgeType) => {
it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () {
const res = flow.parse(
const res = flow.parser.parse(
`flowchart TD;\nA e1@${edgeType.edgeStart} label ${edgeType.edgeEnd} B;`
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
@@ -155,10 +159,10 @@ A@{ shape: 'rect' }
describe('edges', function () {
doubleEndedEdges.forEach((edgeType) => {
it(`should handle ${edgeType.stroke} ${edgeType.type} with no text`, function () {
const res = flow.parse(`graph TD;\nA ${edgeType.edgeStart}${edgeType.edgeEnd} B;`);
const res = flow.parser.parse(`graph TD;\nA ${edgeType.edgeStart}${edgeType.edgeEnd} B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -171,12 +175,12 @@ A@{ shape: 'rect' }
});
it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () {
const res = flow.parse(
const res = flow.parser.parse(
`graph TD;\nA ${edgeType.edgeStart} text ${edgeType.edgeEnd} B;`
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -191,12 +195,12 @@ A@{ shape: 'rect' }
it.each(keywords)(
`should handle ${edgeType.stroke} ${edgeType.type} with %s text`,
function (keyword) {
const res = flow.parse(
const res = flow.parser.parse(
`graph TD;\nA ${edgeType.edgeStart} ${keyword} ${edgeType.edgeEnd} B;`
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -212,11 +216,11 @@ A@{ shape: 'rect' }
});
it('should handle multiple edges', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD;A---|This is the 123 s text|B;\nA---|This is the second edge|B;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -238,10 +242,10 @@ A@{ shape: 'rect' }
describe('edge length', function () {
for (let length = 1; length <= 3; ++length) {
it(`should handle normal edges with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA -${'-'.repeat(length)}- B;`);
const res = flow.parser.parse(`graph TD;\nA -${'-'.repeat(length)}- B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -257,10 +261,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle normal labelled edges with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA -- Label -${'-'.repeat(length)}- B;`);
const res = flow.parser.parse(`graph TD;\nA -- Label -${'-'.repeat(length)}- B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -276,10 +280,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle normal edges with arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA -${'-'.repeat(length)}> B;`);
const res = flow.parser.parse(`graph TD;\nA -${'-'.repeat(length)}> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -295,10 +299,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle normal labelled edges with arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA -- Label -${'-'.repeat(length)}> B;`);
const res = flow.parser.parse(`graph TD;\nA -- Label -${'-'.repeat(length)}> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -314,10 +318,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle normal edges with double arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA <-${'-'.repeat(length)}> B;`);
const res = flow.parser.parse(`graph TD;\nA <-${'-'.repeat(length)}> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -333,10 +337,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle normal labelled edges with double arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA <-- Label -${'-'.repeat(length)}> B;`);
const res = flow.parser.parse(`graph TD;\nA <-- Label -${'-'.repeat(length)}> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -352,10 +356,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle thick edges with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA =${'='.repeat(length)}= B;`);
const res = flow.parser.parse(`graph TD;\nA =${'='.repeat(length)}= B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -371,10 +375,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle thick labelled edges with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA == Label =${'='.repeat(length)}= B;`);
const res = flow.parser.parse(`graph TD;\nA == Label =${'='.repeat(length)}= B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -390,10 +394,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle thick edges with arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA =${'='.repeat(length)}> B;`);
const res = flow.parser.parse(`graph TD;\nA =${'='.repeat(length)}> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -409,10 +413,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle thick labelled edges with arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA == Label =${'='.repeat(length)}> B;`);
const res = flow.parser.parse(`graph TD;\nA == Label =${'='.repeat(length)}> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -428,10 +432,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle thick edges with double arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA <=${'='.repeat(length)}> B;`);
const res = flow.parser.parse(`graph TD;\nA <=${'='.repeat(length)}> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -447,10 +451,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle thick labelled edges with double arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA <== Label =${'='.repeat(length)}> B;`);
const res = flow.parser.parse(`graph TD;\nA <== Label =${'='.repeat(length)}> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -466,10 +470,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle dotted edges with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA -${'.'.repeat(length)}- B;`);
const res = flow.parser.parse(`graph TD;\nA -${'.'.repeat(length)}- B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -485,10 +489,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle dotted labelled edges with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA -. Label ${'.'.repeat(length)}- B;`);
const res = flow.parser.parse(`graph TD;\nA -. Label ${'.'.repeat(length)}- B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -504,10 +508,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle dotted edges with arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA -${'.'.repeat(length)}-> B;`);
const res = flow.parser.parse(`graph TD;\nA -${'.'.repeat(length)}-> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -523,10 +527,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle dotted labelled edges with arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA -. Label ${'.'.repeat(length)}-> B;`);
const res = flow.parser.parse(`graph TD;\nA -. Label ${'.'.repeat(length)}-> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -542,10 +546,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle dotted edges with double arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA <-${'.'.repeat(length)}-> B;`);
const res = flow.parser.parse(`graph TD;\nA <-${'.'.repeat(length)}-> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -561,10 +565,10 @@ A@{ shape: 'rect' }
for (let length = 1; length <= 3; ++length) {
it(`should handle dotted edges with double arrows with length ${length}`, function () {
const res = flow.parse(`graph TD;\nA <-. Label ${'.'.repeat(length)}-> B;`);
const res = flow.parser.parse(`graph TD;\nA <-. Label ${'.'.repeat(length)}-> B;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
import { vi } from 'vitest';
const spyOn = vi.spyOn;
@@ -12,26 +12,26 @@ describe('[Interactions] when parsing', () => {
let flowDb;
beforeEach(function () {
flowDb = new FlowDB();
flow.yy = flowDb;
flow.yy.clear();
flow.parser.yy = flowDb;
flow.parser.yy.clear();
});
it('should be possible to use click to a callback', function () {
spyOn(flowDb, 'setClickEvent');
const res = flow.parse('graph TD\nA-->B\nclick A callback');
const res = flow.parser.parse('graph TD\nA-->B\nclick A callback');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback');
});
it('should be possible to use click to a click and call callback', function () {
spyOn(flowDb, 'setClickEvent');
const res = flow.parse('graph TD\nA-->B\nclick A call callback()');
const res = flow.parser.parse('graph TD\nA-->B\nclick A call callback()');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback');
});
@@ -39,10 +39,10 @@ describe('[Interactions] when parsing', () => {
it('should be possible to use click to a callback with tooltip', function () {
spyOn(flowDb, 'setClickEvent');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A callback "tooltip"');
const res = flow.parser.parse('graph TD\nA-->B\nclick A callback "tooltip"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
@@ -51,10 +51,10 @@ describe('[Interactions] when parsing', () => {
it('should be possible to use click to a click and call callback with tooltip', function () {
spyOn(flowDb, 'setClickEvent');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A call callback() "tooltip"');
const res = flow.parser.parse('graph TD\nA-->B\nclick A call callback() "tooltip"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
@@ -62,30 +62,30 @@ describe('[Interactions] when parsing', () => {
it('should be possible to use click to a callback with an arbitrary number of args', function () {
spyOn(flowDb, 'setClickEvent');
const res = flow.parse('graph TD\nA-->B\nclick A call callback("test0", test1, test2)');
const res = flow.parser.parse('graph TD\nA-->B\nclick A call callback("test0", test1, test2)');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setClickEvent).toHaveBeenCalledWith('A', 'callback', '"test0", test1, test2');
});
it('should handle interaction - click to a link', function () {
spyOn(flowDb, 'setLink');
const res = flow.parse('graph TD\nA-->B\nclick A "click.html"');
const res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html');
});
it('should handle interaction - click to a click and href link', function () {
spyOn(flowDb, 'setLink');
const res = flow.parse('graph TD\nA-->B\nclick A href "click.html"');
const res = flow.parser.parse('graph TD\nA-->B\nclick A href "click.html"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html');
});
@@ -93,10 +93,10 @@ describe('[Interactions] when parsing', () => {
it('should handle interaction - click to a link with tooltip', function () {
spyOn(flowDb, 'setLink');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A "click.html" "tooltip"');
const res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html" "tooltip"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
@@ -105,10 +105,10 @@ describe('[Interactions] when parsing', () => {
it('should handle interaction - click to a click and href link with tooltip', function () {
spyOn(flowDb, 'setLink');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A href "click.html" "tooltip"');
const res = flow.parser.parse('graph TD\nA-->B\nclick A href "click.html" "tooltip"');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
@@ -116,20 +116,20 @@ describe('[Interactions] when parsing', () => {
it('should handle interaction - click to a link with target', function () {
spyOn(flowDb, 'setLink');
const res = flow.parse('graph TD\nA-->B\nclick A "click.html" _blank');
const res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html" _blank');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', '_blank');
});
it('should handle interaction - click to a click and href link with target', function () {
spyOn(flowDb, 'setLink');
const res = flow.parse('graph TD\nA-->B\nclick A href "click.html" _blank');
const res = flow.parser.parse('graph TD\nA-->B\nclick A href "click.html" _blank');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', '_blank');
});
@@ -137,10 +137,10 @@ describe('[Interactions] when parsing', () => {
it('should handle interaction - click to a link with tooltip and target', function () {
spyOn(flowDb, 'setLink');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A "click.html" "tooltip" _blank');
const res = flow.parser.parse('graph TD\nA-->B\nclick A "click.html" "tooltip" _blank');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', '_blank');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');
@@ -149,10 +149,10 @@ describe('[Interactions] when parsing', () => {
it('should handle interaction - click to a click and href link with tooltip and target', function () {
spyOn(flowDb, 'setLink');
spyOn(flowDb, 'setTooltip');
const res = flow.parse('graph TD\nA-->B\nclick A href "click.html" "tooltip" _blank');
const res = flow.parser.parse('graph TD\nA-->B\nclick A href "click.html" "tooltip" _blank');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(flowDb.setLink).toHaveBeenCalledWith('A', 'click.html', '_blank');
expect(flowDb.setTooltip).toHaveBeenCalledWith('A', 'tooltip');

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,21 +8,21 @@ setConfig({
describe('[Lines] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});
it('should handle line interpolation default definitions', function () {
const res = flow.parse('graph TD\n' + 'A-->B\n' + 'linkStyle default interpolate basis');
const res = flow.parser.parse('graph TD\n' + 'A-->B\n' + 'linkStyle default interpolate basis');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.defaultInterpolate).toBe('basis');
});
it('should handle line interpolation numbered definitions', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD\n' +
'A-->B\n' +
'A-->C\n' +
@@ -30,38 +30,38 @@ describe('[Lines] when parsing', () => {
'linkStyle 1 interpolate cardinal'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].interpolate).toBe('basis');
expect(edges[1].interpolate).toBe('cardinal');
});
it('should handle line interpolation multi-numbered definitions', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD\n' + 'A-->B\n' + 'A-->C\n' + 'linkStyle 0,1 interpolate basis'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].interpolate).toBe('basis');
expect(edges[1].interpolate).toBe('basis');
});
it('should handle line interpolation default with style', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD\n' + 'A-->B\n' + 'linkStyle default interpolate basis stroke-width:1px;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.defaultInterpolate).toBe('basis');
});
it('should handle line interpolation numbered with style', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD\n' +
'A-->B\n' +
'A-->C\n' +
@@ -69,20 +69,20 @@ describe('[Lines] when parsing', () => {
'linkStyle 1 interpolate cardinal stroke-width:1px;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].interpolate).toBe('basis');
expect(edges[1].interpolate).toBe('cardinal');
});
it('should handle line interpolation multi-numbered with style', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD\n' + 'A-->B\n' + 'A-->C\n' + 'linkStyle 0,1 interpolate basis stroke-width:1px;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].interpolate).toBe('basis');
expect(edges[1].interpolate).toBe('basis');
@@ -90,28 +90,28 @@ describe('[Lines] when parsing', () => {
describe('it should handle new line type notation', function () {
it('should handle regular lines', function () {
const res = flow.parse('graph TD;A-->B;');
const res = flow.parser.parse('graph TD;A-->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].stroke).toBe('normal');
});
it('should handle dotted lines', function () {
const res = flow.parse('graph TD;A-.->B;');
const res = flow.parser.parse('graph TD;A-.->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].stroke).toBe('dotted');
});
it('should handle dotted lines', function () {
const res = flow.parse('graph TD;A==>B;');
const res = flow.parser.parse('graph TD;A==>B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].stroke).toBe('thick');
});

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,16 +8,16 @@ setConfig({
describe('parsing a flow chart with markdown strings', function () {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});
it('markdown formatting in nodes and labels', function () {
const res = flow.parse(`flowchart
const res = flow.parser.parse(`flowchart
A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in the hog"] -- "The rat in the mat" -->C;`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('A').text).toBe('The cat in **the** hat');
@@ -38,7 +38,7 @@ A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in t
expect(edges[1].labelType).toBe('string');
});
it('markdown formatting in subgraphs', function () {
const res = flow.parse(`flowchart LR
const res = flow.parser.parse(`flowchart LR
subgraph "One"
a("\`The **cat**
in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
@@ -48,7 +48,7 @@ subgraph "\`**Two**\`"
in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
end`);
const subgraphs = flow.yy.getSubGraphs();
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(2);
const subgraph = subgraphs[0];

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,105 +8,105 @@ setConfig({
describe('when parsing directions', function () {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.yy.setGen('gen-2');
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});
it('should handle basic shape data statements', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded}`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
});
it('should handle basic shape data statements', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded }`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
});
it('should handle basic shape data statements with &', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded } & E`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(2);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle shape data statements with edges', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded } --> E`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(2);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle basic shape data statements with amp and edges 1', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded } & E --> F`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle basic shape data statements with amp and edges 2', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded } & E@{ shape: rounded } --> F`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle basic shape data statements with amp and edges 3', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded } & E@{ shape: rounded } --> F & G@{ shape: rounded }`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(4);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle basic shape data statements with amp and edges 4', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded } & E@{ shape: rounded } --> F@{ shape: rounded } & G@{ shape: rounded }`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(4);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should handle basic shape data statements with amp and edges 5, trailing space', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded } & E@{ shape: rounded } --> F{ shape: rounded } & G{ shape: rounded } `);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(4);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
expect(data4Layout.nodes[1].label).toEqual('E');
});
it('should no matter of there are no leading spaces', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{shape: rounded}`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
@@ -114,10 +114,10 @@ describe('when parsing directions', function () {
});
it('should no matter of there are many leading spaces', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded}`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
@@ -125,27 +125,27 @@ describe('when parsing directions', function () {
});
it('should be forgiving with many spaces before the end', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded }`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('D');
});
it('should be possible to add multiple properties on the same line', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
D@{ shape: rounded , label: "DD"}`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('rounded');
expect(data4Layout.nodes[0].label).toEqual('DD');
});
it('should be possible to link to a node with more data', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
A --> D@{
shape: circle
other: "clock"
@@ -153,7 +153,7 @@ describe('when parsing directions', function () {
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(2);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('A');
@@ -163,7 +163,7 @@ describe('when parsing directions', function () {
expect(data4Layout.edges.length).toBe(1);
});
it('should not disturb adding multiple nodes after each other', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
A[hello]
B@{
shape: circle
@@ -175,7 +175,7 @@ describe('when parsing directions', function () {
}
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('hello');
@@ -185,21 +185,21 @@ describe('when parsing directions', function () {
expect(data4Layout.nodes[2].label).toEqual('Hello');
});
it('should use handle bracket end (}) character inside the shape data', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
A@{
label: "This is }"
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is }');
});
it('should error on nonexistent shape', function () {
expect(() => {
flow.parse(`flowchart TB
flow.parser.parse(`flowchart TB
A@{ shape: this-shape-does-not-exist }
`);
}).toThrow('No such shape: this-shape-does-not-exist.');
@@ -207,23 +207,23 @@ describe('when parsing directions', function () {
it('should error on internal-only shape', function () {
expect(() => {
// this shape does exist, but it's only supposed to be for internal/backwards compatibility use
flow.parse(`flowchart TB
flow.parser.parse(`flowchart TB
A@{ shape: rect_left_inv_arrow }
`);
}).toThrow('No such shape: rect_left_inv_arrow. Shape names should be lowercase.');
});
it('Diamond shapes should work as usual', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
A{This is a label}
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('diamond');
expect(data4Layout.nodes[0].label).toEqual('This is a label');
});
it('Multi line strings should be supported', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
A@{
label: |
This is a
@@ -232,13 +232,13 @@ describe('when parsing directions', function () {
}
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a\nmultiline string\n');
});
it('Multi line strings should be supported', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
A@{
label: "This is a
multiline string"
@@ -246,57 +246,57 @@ describe('when parsing directions', function () {
}
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a<br/>multiline string');
});
it('should be possible to use } in strings', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
A@{
label: "This is a string with }"
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a string with }');
});
it('should be possible to use @ in strings', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
A@{
label: "This is a string with @"
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a string with @');
});
it('should be possible to use @ in strings', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
A@{
label: "This is a string with}"
other: "clock"
}
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(1);
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a string with}');
});
it('should be possible to use @ syntax to add labels on multi nodes', function () {
const res = flow.parse(`flowchart TB
const res = flow.parser.parse(`flowchart TB
n2["label for n2"] & n4@{ label: "label for n4"} & n5@{ label: "label for n5"}
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].label).toEqual('label for n2');
expect(data4Layout.nodes[1].label).toEqual('label for n4');
@@ -304,12 +304,12 @@ describe('when parsing directions', function () {
});
it('should be possible to use @ syntax to add labels on multi nodes with edge/link', function () {
const res = flow.parse(`flowchart TD
const res = flow.parser.parse(`flowchart TD
A["A"] --> B["for B"] & C@{ label: "for c"} & E@{label : "for E"}
D@{label: "for D"}
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(5);
expect(data4Layout.nodes[0].label).toEqual('A');
expect(data4Layout.nodes[1].label).toEqual('for B');
@@ -319,7 +319,7 @@ describe('when parsing directions', function () {
});
it('should be possible to use @ syntax in labels', function () {
const res = flow.parse(`flowchart TD
const res = flow.parser.parse(`flowchart TD
A["@A@"] --> B["@for@ B@"] & C@{ label: "@for@ c@"} & E{"\`@for@ E@\`"} & D(("@for@ D@"))
H1{{"@for@ H@"}}
H2{{"\`@for@ H@\`"}}
@@ -329,7 +329,7 @@ describe('when parsing directions', function () {
AS2>"\`@for@ AS@\`"]
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(11);
expect(data4Layout.nodes[0].label).toEqual('@A@');
expect(data4Layout.nodes[1].label).toEqual('@for@ B@');
@@ -345,12 +345,12 @@ describe('when parsing directions', function () {
});
it('should handle unique edge creation with using @ and &', function () {
const res = flow.parse(`flowchart TD
const res = flow.parser.parse(`flowchart TD
A & B e1@--> C & D
A1 e2@--> C1 & D1
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(7);
expect(data4Layout.edges.length).toBe(6);
expect(data4Layout.edges[0].id).toEqual('L_A_C_0');
@@ -362,12 +362,12 @@ describe('when parsing directions', function () {
});
it('should handle redefine same edge ids again', function () {
const res = flow.parse(`flowchart TD
const res = flow.parser.parse(`flowchart TD
A & B e1@--> C & D
A1 e1@--> C1 & D1
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(7);
expect(data4Layout.edges.length).toBe(6);
expect(data4Layout.edges[0].id).toEqual('L_A_C_0');
@@ -379,7 +379,7 @@ describe('when parsing directions', function () {
});
it('should handle overriding edge animate again', function () {
const res = flow.parse(`flowchart TD
const res = flow.parser.parse(`flowchart TD
A e1@--> B
C e2@--> D
E e3@--> F
@@ -389,7 +389,7 @@ describe('when parsing directions', function () {
e3@{ animate: false }
`);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(6);
expect(data4Layout.edges.length).toBe(3);
expect(data4Layout.edges[0].id).toEqual('e1');
@@ -401,12 +401,12 @@ describe('when parsing directions', function () {
});
it.skip('should be possible to use @ syntax to add labels with trail spaces', function () {
const res = flow.parse(
const res = flow.parser.parse(
`flowchart TB
n2["label for n2"] & n4@{ label: "label for n4"} & n5@{ label: "label for n5"} `
);
const data4Layout = flow.yy.getData();
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].label).toEqual('label for n2');
expect(data4Layout.nodes[1].label).toEqual('label for n4');

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -31,26 +31,26 @@ const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/', '_
describe('[Singlenodes] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});
it('should handle a single node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;A;');
const res = flow.parser.parse('graph TD;A;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('A').styles.length).toBe(0);
});
it('should handle a single node with white space after it (SN1)', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;A ;');
const res = flow.parser.parse('graph TD;A ;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('A').styles.length).toBe(0);
@@ -58,10 +58,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single square node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a[A];');
const res = flow.parser.parse('graph TD;a[A];');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').styles.length).toBe(0);
@@ -70,10 +70,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single round square node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a[A];');
const res = flow.parser.parse('graph TD;a[A];');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').styles.length).toBe(0);
@@ -82,10 +82,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single circle node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a((A));');
const res = flow.parser.parse('graph TD;a((A));');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('circle');
@@ -93,10 +93,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single round node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a(A);');
const res = flow.parser.parse('graph TD;a(A);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('round');
@@ -104,10 +104,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single odd node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a>A];');
const res = flow.parser.parse('graph TD;a>A];');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('odd');
@@ -115,10 +115,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single diamond node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a{A};');
const res = flow.parser.parse('graph TD;a{A};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('diamond');
@@ -126,10 +126,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single diamond node with whitespace after it', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a{A} ;');
const res = flow.parser.parse('graph TD;a{A} ;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('diamond');
@@ -137,10 +137,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single diamond node with html in it (SN3)', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a{A <br> end};');
const res = flow.parser.parse('graph TD;a{A <br> end};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('diamond');
@@ -149,10 +149,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single hexagon node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a{{A}};');
const res = flow.parser.parse('graph TD;a{{A}};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('hexagon');
@@ -160,10 +160,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single hexagon node with html in it', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a{{A <br> end}};');
const res = flow.parser.parse('graph TD;a{{A <br> end}};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('hexagon');
@@ -172,10 +172,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single round node with html in it', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a(A <br> end);');
const res = flow.parser.parse('graph TD;a(A <br> end);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('round');
@@ -184,10 +184,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single double circle node', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a(((A)));');
const res = flow.parser.parse('graph TD;a(((A)));');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('doublecircle');
@@ -195,10 +195,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single double circle node with whitespace after it', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a(((A))) ;');
const res = flow.parser.parse('graph TD;a(((A))) ;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('doublecircle');
@@ -206,10 +206,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single double circle node with html in it (SN3)', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;a(((A <br> end)));');
const res = flow.parser.parse('graph TD;a(((A <br> end)));');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('a').type).toBe('doublecircle');
@@ -218,10 +218,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single node with alphanumerics starting on a char', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;id1;');
const res = flow.parser.parse('graph TD;id1;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('id1').styles.length).toBe(0);
@@ -229,10 +229,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single node with a single digit', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;1;');
const res = flow.parser.parse('graph TD;1;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('1').text).toBe('1');
@@ -241,10 +241,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single node with a single digit in a subgraph', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;subgraph "hello";1;end;');
const res = flow.parser.parse('graph TD;subgraph "hello";1;end;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('1').text).toBe('1');
@@ -252,10 +252,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single node with alphanumerics starting on a num', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;1id;');
const res = flow.parser.parse('graph TD;1id;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('1id').styles.length).toBe(0);
@@ -263,10 +263,10 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single node with alphanumerics containing a minus sign', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;i-d;');
const res = flow.parser.parse('graph TD;i-d;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('i-d').styles.length).toBe(0);
@@ -274,36 +274,36 @@ describe('[Singlenodes] when parsing', () => {
it('should handle a single node with alphanumerics containing a underscore sign', function () {
// Silly but syntactically correct
const res = flow.parse('graph TD;i_d;');
const res = flow.parser.parse('graph TD;i_d;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(0);
expect(vert.get('i_d').styles.length).toBe(0);
});
it.each(keywords)('should handle keywords between dashes "-"', function (keyword) {
const res = flow.parse(`graph TD;a-${keyword}-node;`);
const vert = flow.yy.getVertices();
const res = flow.parser.parse(`graph TD;a-${keyword}-node;`);
const vert = flow.parser.yy.getVertices();
expect(vert.get(`a-${keyword}-node`).text).toBe(`a-${keyword}-node`);
});
it.each(keywords)('should handle keywords between periods "."', function (keyword) {
const res = flow.parse(`graph TD;a.${keyword}.node;`);
const vert = flow.yy.getVertices();
const res = flow.parser.parse(`graph TD;a.${keyword}.node;`);
const vert = flow.parser.yy.getVertices();
expect(vert.get(`a.${keyword}.node`).text).toBe(`a.${keyword}.node`);
});
it.each(keywords)('should handle keywords between underscores "_"', function (keyword) {
const res = flow.parse(`graph TD;a_${keyword}_node;`);
const vert = flow.yy.getVertices();
const res = flow.parser.parse(`graph TD;a_${keyword}_node;`);
const vert = flow.parser.yy.getVertices();
expect(vert.get(`a_${keyword}_node`).text).toBe(`a_${keyword}_node`);
});
it.each(keywords)('should handle nodes ending in %s', function (keyword) {
const res = flow.parse(`graph TD;node_${keyword};node.${keyword};node-${keyword};`);
const vert = flow.yy.getVertices();
const res = flow.parser.parse(`graph TD;node_${keyword};node.${keyword};node-${keyword};`);
const vert = flow.parser.yy.getVertices();
expect(vert.get(`node_${keyword}`).text).toBe(`node_${keyword}`);
expect(vert.get(`node.${keyword}`).text).toBe(`node.${keyword}`);
expect(vert.get(`node-${keyword}`).text).toBe(`node-${keyword}`);
@@ -327,16 +327,16 @@ describe('[Singlenodes] when parsing', () => {
];
it.each(errorKeywords)('should throw error at nodes beginning with %s', function (keyword) {
const str = `graph TD;${keyword}.node;${keyword}-node;${keyword}/node`;
const vert = flow.yy.getVertices();
const vert = flow.parser.yy.getVertices();
expect(() => flow.parse(str)).toThrowError();
expect(() => flow.parser.parse(str)).toThrowError();
});
const workingKeywords = ['default', 'href', 'click', 'call'];
it.each(workingKeywords)('should parse node beginning with %s', function (keyword) {
flow.parse(`graph TD; ${keyword}.node;${keyword}-node;${keyword}/node;`);
const vert = flow.yy.getVertices();
flow.parser.parse(`graph TD; ${keyword}.node;${keyword}-node;${keyword}/node;`);
const vert = flow.parser.yy.getVertices();
expect(vert.get(`${keyword}.node`).text).toBe(`${keyword}.node`);
expect(vert.get(`${keyword}-node`).text).toBe(`${keyword}-node`);
expect(vert.get(`${keyword}/node`).text).toBe(`${keyword}/node`);
@@ -345,8 +345,8 @@ describe('[Singlenodes] when parsing', () => {
it.each(specialChars)(
'should allow node ids of single special characters',
function (specialChar) {
flow.parse(`graph TD; ${specialChar} --> A`);
const vert = flow.yy.getVertices();
flow.parser.parse(`graph TD; ${specialChar} --> A`);
const vert = flow.parser.yy.getVertices();
expect(vert.get(`${specialChar}`).text).toBe(`${specialChar}`);
}
);
@@ -354,8 +354,8 @@ describe('[Singlenodes] when parsing', () => {
it.each(specialChars)(
'should allow node ids with special characters at start of id',
function (specialChar) {
flow.parse(`graph TD; ${specialChar}node --> A`);
const vert = flow.yy.getVertices();
flow.parser.parse(`graph TD; ${specialChar}node --> A`);
const vert = flow.parser.yy.getVertices();
expect(vert.get(`${specialChar}node`).text).toBe(`${specialChar}node`);
}
);
@@ -363,8 +363,8 @@ describe('[Singlenodes] when parsing', () => {
it.each(specialChars)(
'should allow node ids with special characters at end of id',
function (specialChar) {
flow.parse(`graph TD; node${specialChar} --> A`);
const vert = flow.yy.getVertices();
flow.parser.parse(`graph TD; node${specialChar} --> A`);
const vert = flow.parser.yy.getVertices();
expect(vert.get(`node${specialChar}`).text).toBe(`node${specialChar}`);
}
);

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,27 +8,27 @@ setConfig({
describe('[Style] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.yy.setGen('gen-2');
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});
// log.debug(flow.parse('graph TD;style Q background:#fff;'));
// log.debug(flow.parser.parse('graph TD;style Q background:#fff;'));
it('should handle styles for vertices', function () {
const res = flow.parse('graph TD;style Q background:#fff;');
const res = flow.parser.parse('graph TD;style Q background:#fff;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('Q').styles.length).toBe(1);
expect(vert.get('Q').styles[0]).toBe('background:#fff');
});
it('should handle multiple styles for a vortex', function () {
const res = flow.parse('graph TD;style R background:#fff,border:1px solid red;');
const res = flow.parser.parse('graph TD;style R background:#fff,border:1px solid red;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('R').styles.length).toBe(2);
expect(vert.get('R').styles[0]).toBe('background:#fff');
@@ -36,12 +36,12 @@ describe('[Style] when parsing', () => {
});
it('should handle multiple styles in a graph', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD;style S background:#aaa;\nstyle T background:#bbb,border:1px solid red;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('S').styles.length).toBe(1);
expect(vert.get('T').styles.length).toBe(2);
@@ -51,12 +51,12 @@ describe('[Style] when parsing', () => {
});
it('should handle styles and graph definitions in a graph', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD;S-->T;\nstyle S background:#aaa;\nstyle T background:#bbb,border:1px solid red;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('S').styles.length).toBe(1);
expect(vert.get('T').styles.length).toBe(2);
@@ -66,10 +66,10 @@ describe('[Style] when parsing', () => {
});
it('should handle styles and graph definitions in a graph', function () {
const res = flow.parse('graph TD;style T background:#bbb,border:1px solid red;');
// const res = flow.parse('graph TD;style T background: #bbb;');
const res = flow.parser.parse('graph TD;style T background:#bbb,border:1px solid red;');
// const res = flow.parser.parse('graph TD;style T background: #bbb;');
const vert = flow.yy.getVertices();
const vert = flow.parser.yy.getVertices();
expect(vert.get('T').styles.length).toBe(2);
expect(vert.get('T').styles[0]).toBe('background:#bbb');
@@ -77,11 +77,11 @@ describe('[Style] when parsing', () => {
});
it('should keep node label text (if already defined) when a style is applied', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD;A(( ));B((Test));C;style A background:#fff;style D border:1px solid red;'
);
const vert = flow.yy.getVertices();
const vert = flow.parser.yy.getVertices();
expect(vert.get('A').text).toBe('');
expect(vert.get('B').text).toBe('Test');
@@ -90,12 +90,12 @@ describe('[Style] when parsing', () => {
});
it('should be possible to declare a class', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD;classDef exClass background:#bbb,border:1px solid red;'
);
// const res = flow.parse('graph TD;style T background: #bbb;');
// const res = flow.parser.parse('graph TD;style T background: #bbb;');
const classes = flow.yy.getClasses();
const classes = flow.parser.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
@@ -103,11 +103,11 @@ describe('[Style] when parsing', () => {
});
it('should be possible to declare multiple classes', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD;classDef firstClass,secondClass background:#bbb,border:1px solid red;'
);
const classes = flow.yy.getClasses();
const classes = flow.parser.yy.getClasses();
expect(classes.get('firstClass').styles.length).toBe(2);
expect(classes.get('firstClass').styles[0]).toBe('background:#bbb');
@@ -119,24 +119,24 @@ describe('[Style] when parsing', () => {
});
it('should be possible to declare a class with a dot in the style', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD;classDef exClass background:#bbb,border:1.5px solid red;'
);
// const res = flow.parse('graph TD;style T background: #bbb;');
// const res = flow.parser.parse('graph TD;style T background: #bbb;');
const classes = flow.yy.getClasses();
const classes = flow.parser.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exClass').styles[1]).toBe('border:1.5px solid red');
});
it('should be possible to declare a class with a space in the style', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD;classDef exClass background: #bbb,border:1.5px solid red;'
);
// const res = flow.parse('graph TD;style T background : #bbb;');
// const res = flow.parser.parse('graph TD;style T background : #bbb;');
const classes = flow.yy.getClasses();
const classes = flow.parser.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background: #bbb');
@@ -150,9 +150,9 @@ describe('[Style] when parsing', () => {
statement = statement + 'a-->b;' + '\n';
statement = statement + 'class a exClass;';
const res = flow.parse(statement);
const res = flow.parser.parse(statement);
const classes = flow.yy.getClasses();
const classes = flow.parser.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
@@ -166,9 +166,9 @@ describe('[Style] when parsing', () => {
statement = statement + 'a_a-->b_b;' + '\n';
statement = statement + 'class a_a exClass;';
const res = flow.parse(statement);
const res = flow.parser.parse(statement);
const classes = flow.yy.getClasses();
const classes = flow.parser.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
@@ -181,9 +181,9 @@ describe('[Style] when parsing', () => {
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'a-->b[test]:::exClass;' + '\n';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
const classes = flow.yy.getClasses();
const res = flow.parser.parse(statement);
const vertices = flow.parser.yy.getVertices();
const classes = flow.parser.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(vertices.get('b').classes[0]).toBe('exClass');
@@ -198,9 +198,9 @@ describe('[Style] when parsing', () => {
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'b[test]:::exClass;' + '\n';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
const classes = flow.yy.getClasses();
const res = flow.parser.parse(statement);
const vertices = flow.parser.yy.getVertices();
const classes = flow.parser.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(vertices.get('b').classes[0]).toBe('exClass');
@@ -215,9 +215,9 @@ describe('[Style] when parsing', () => {
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'A[test]:::exClass-->B[test2];' + '\n';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
const classes = flow.yy.getClasses();
const res = flow.parser.parse(statement);
const vertices = flow.parser.yy.getVertices();
const classes = flow.parser.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(vertices.get('A').classes[0]).toBe('exClass');
@@ -232,9 +232,9 @@ describe('[Style] when parsing', () => {
statement = statement + 'classDef exClass background:#bbb,border:1px solid red;' + '\n';
statement = statement + 'a-->b[1 a a text!.]:::exClass;' + '\n';
const res = flow.parse(statement);
const vertices = flow.yy.getVertices();
const classes = flow.yy.getClasses();
const res = flow.parser.parse(statement);
const vertices = flow.parser.yy.getVertices();
const classes = flow.parser.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(vertices.get('b').classes[0]).toBe('exClass');
@@ -249,10 +249,10 @@ describe('[Style] when parsing', () => {
statement = statement + 'a-->b;' + '\n';
statement = statement + 'class a,b exClass;';
const res = flow.parse(statement);
const res = flow.parser.parse(statement);
const classes = flow.yy.getClasses();
const vertices = flow.yy.getVertices();
const classes = flow.parser.yy.getClasses();
const vertices = flow.parser.yy.getVertices();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');
@@ -262,7 +262,7 @@ describe('[Style] when parsing', () => {
});
it('should handle style definitions with more then 1 digit in a row', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD\n' +
'A-->B1\n' +
'A-->B2\n' +
@@ -278,8 +278,8 @@ describe('[Style] when parsing', () => {
'linkStyle 10 stroke-width:1px;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
@@ -299,17 +299,17 @@ describe('[Style] when parsing', () => {
});
it('should handle style definitions within number of edges', function () {
const res = flow.parse(`graph TD
const res = flow.parser.parse(`graph TD
A-->B
linkStyle 0 stroke-width:1px;`);
const edges = flow.yy.getEdges();
const edges = flow.parser.yy.getEdges();
expect(edges[0].style[0]).toBe('stroke-width:1px');
});
it('should handle multi-numbered style definitions with more then 1 digit in a row', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD\n' +
'A-->B1\n' +
'A-->B2\n' +
@@ -326,41 +326,41 @@ describe('[Style] when parsing', () => {
'linkStyle 10,11 stroke-width:1px;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle classDefs with style in classes', function () {
const res = flow.parse('graph TD\nA-->B\nclassDef exClass font-style:bold;');
const res = flow.parser.parse('graph TD\nA-->B\nclassDef exClass font-style:bold;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle classDefs with % in classes', function () {
const res = flow.parse(
const res = flow.parser.parse(
'graph TD\nA-->B\nclassDef exClass fill:#f96,stroke:#333,stroke-width:4px,font-size:50%,font-style:bold;'
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
});
it('should handle multiple vertices with style', function () {
const res = flow.parse(`
const res = flow.parser.parse(`
graph TD
classDef C1 stroke-dasharray:4
classDef C2 stroke-dasharray:6
A & B:::C1 & D:::C1 --> E:::C2
`);
const vert = flow.yy.getVertices();
const vert = flow.parser.yy.getVertices();
expect(vert.get('A').classes.length).toBe(0);
expect(vert.get('B').classes[0]).toBe('C1');

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,187 +8,187 @@ setConfig({
describe('[Text] when parsing', () => {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});
describe('it should handle text on edges', function () {
it('should handle text without space', function () {
const res = flow.parse('graph TD;A--x|textNoSpace|B;');
const res = flow.parser.parse('graph TD;A--x|textNoSpace|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle with space', function () {
const res = flow.parse('graph TD;A--x|text including space|B;');
const res = flow.parser.parse('graph TD;A--x|text including space|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle text with /', function () {
const res = flow.parse('graph TD;A--x|text with / should work|B;');
const res = flow.parser.parse('graph TD;A--x|text with / should work|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].text).toBe('text with / should work');
});
it('should handle space and space between vertices and link', function () {
const res = flow.parse('graph TD;A --x|textNoSpace| B;');
const res = flow.parser.parse('graph TD;A --x|textNoSpace| B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle space and CAPS', function () {
const res = flow.parse('graph TD;A--x|text including CAPS space|B;');
const res = flow.parser.parse('graph TD;A--x|text including CAPS space|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle space and dir', function () {
const res = flow.parse('graph TD;A--x|text including URL space|B;');
const res = flow.parser.parse('graph TD;A--x|text including URL space|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(edges[0].text).toBe('text including URL space');
});
it('should handle space and send', function () {
const res = flow.parse('graph TD;A--text including URL space and send-->B;');
const res = flow.parser.parse('graph TD;A--text including URL space and send-->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('text including URL space and send');
});
it('should handle space and send', function () {
const res = flow.parse('graph TD;A-- text including URL space and send -->B;');
const res = flow.parser.parse('graph TD;A-- text including URL space and send -->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_point');
expect(edges[0].text).toBe('text including URL space and send');
});
it('should handle space and dir (TD)', function () {
const res = flow.parse('graph TD;A--x|text including R TD space|B;');
const res = flow.parser.parse('graph TD;A--x|text including R TD space|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(edges[0].text).toBe('text including R TD space');
});
it('should handle `', function () {
const res = flow.parse('graph TD;A--x|text including `|B;');
const res = flow.parser.parse('graph TD;A--x|text including `|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(edges[0].text).toBe('text including `');
});
it('should handle v in node ids only v', function () {
// only v
const res = flow.parse('graph TD;A--xv(my text);');
const res = flow.parser.parse('graph TD;A--xv(my text);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(vert.get('v').text).toBe('my text');
});
it('should handle v in node ids v at end', function () {
// v at end
const res = flow.parse('graph TD;A--xcsv(my text);');
const res = flow.parser.parse('graph TD;A--xcsv(my text);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(vert.get('csv').text).toBe('my text');
});
it('should handle v in node ids v in middle', function () {
// v in middle
const res = flow.parse('graph TD;A--xava(my text);');
const res = flow.parser.parse('graph TD;A--xava(my text);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(vert.get('ava').text).toBe('my text');
});
it('should handle v in node ids, v at start', function () {
// v at start
const res = flow.parse('graph TD;A--xva(my text);');
const res = flow.parser.parse('graph TD;A--xva(my text);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(vert.get('va').text).toBe('my text');
});
it('should handle keywords', function () {
const res = flow.parse('graph TD;A--x|text including graph space|B;');
const res = flow.parser.parse('graph TD;A--x|text including graph space|B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].text).toBe('text including graph space');
});
it('should handle keywords', function () {
const res = flow.parse('graph TD;V-->a[v]');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const res = flow.parser.parse('graph TD;V-->a[v]');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('a').text).toBe('v');
});
it('should handle quoted text', function () {
const res = flow.parse('graph TD;V-- "test string()" -->a[v]');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const res = flow.parser.parse('graph TD;V-- "test string()" -->a[v]');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].text).toBe('test string()');
});
});
describe('it should handle text on lines', () => {
it('should handle normal text on lines', function () {
const res = flow.parse('graph TD;A-- test text with == -->B;');
const res = flow.parser.parse('graph TD;A-- test text with == -->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].stroke).toBe('normal');
});
it('should handle dotted text on lines (TD3)', function () {
const res = flow.parse('graph TD;A-. test text with == .->B;');
const res = flow.parser.parse('graph TD;A-. test text with == .->B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].stroke).toBe('dotted');
});
it('should handle thick text on lines', function () {
const res = flow.parse('graph TD;A== test text with - ==>B;');
const res = flow.parser.parse('graph TD;A== test text with - ==>B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].stroke).toBe('thick');
});
@@ -196,99 +196,99 @@ describe('[Text] when parsing', () => {
describe('it should handle text on edges using the new notation', function () {
it('should handle text without space', function () {
const res = flow.parse('graph TD;A-- textNoSpace --xB;');
const res = flow.parser.parse('graph TD;A-- textNoSpace --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle text with multiple leading space', function () {
const res = flow.parse('graph TD;A-- textNoSpace --xB;');
const res = flow.parser.parse('graph TD;A-- textNoSpace --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle with space', function () {
const res = flow.parse('graph TD;A-- text including space --xB;');
const res = flow.parser.parse('graph TD;A-- text including space --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle text with /', function () {
const res = flow.parse('graph TD;A -- text with / should work --x B;');
const res = flow.parser.parse('graph TD;A -- text with / should work --x B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].text).toBe('text with / should work');
});
it('should handle space and space between vertices and link', function () {
const res = flow.parse('graph TD;A -- textNoSpace --x B;');
const res = flow.parser.parse('graph TD;A -- textNoSpace --x B;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle space and CAPS', function () {
const res = flow.parse('graph TD;A-- text including CAPS space --xB;');
const res = flow.parser.parse('graph TD;A-- text including CAPS space --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
});
it('should handle space and dir', function () {
const res = flow.parse('graph TD;A-- text including URL space --xB;');
const res = flow.parser.parse('graph TD;A-- text including URL space --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(edges[0].text).toBe('text including URL space');
});
it('should handle space and dir (TD2)', function () {
const res = flow.parse('graph TD;A-- text including R TD space --xB;');
const res = flow.parser.parse('graph TD;A-- text including R TD space --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
expect(edges[0].text).toBe('text including R TD space');
});
it('should handle keywords', function () {
const res = flow.parse('graph TD;A-- text including graph space and v --xB;');
const res = flow.parser.parse('graph TD;A-- text including graph space and v --xB;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].text).toBe('text including graph space and v');
});
it('should handle keywords', function () {
const res = flow.parse('graph TD;A-- text including graph space and v --xB[blav]');
const res = flow.parser.parse('graph TD;A-- text including graph space and v --xB[blav]');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].text).toBe('text including graph space and v');
});
// it.skip('should handle text on open links',function(){
// const res = flow.parse('graph TD;A-- text including graph space --B');
// const res = flow.parser.parse('graph TD;A-- text including graph space --B');
//
// const vert = flow.yy.getVertices();
// const edges = flow.yy.getEdges();
// const vert = flow.parser.yy.getVertices();
// const edges = flow.parser.yy.getEdges();
//
// expect(edges[0].text).toBe('text including graph space');
//
@@ -297,10 +297,10 @@ describe('[Text] when parsing', () => {
describe('it should handle text in vertices, ', function () {
it('should handle space', function () {
const res = flow.parse('graph TD;A-->C(Chimpansen hoppar);');
const res = flow.parser.parse('graph TD;A-->C(Chimpansen hoppar);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('C').type).toBe('round');
expect(vert.get('C').text).toBe('Chimpansen hoppar');
@@ -347,109 +347,109 @@ describe('[Text] when parsing', () => {
shapes.forEach((shape) => {
it.each(keywords)(`should handle %s keyword in ${shape.name} vertex`, function (keyword) {
const rest = flow.parse(
const rest = flow.parser.parse(
`graph TD;A_${keyword}_node-->B${shape.start}This node has a ${keyword} as text${shape.end};`
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('B').type).toBe(`${shape.name}`);
expect(vert.get('B').text).toBe(`This node has a ${keyword} as text`);
});
});
it.each(keywords)('should handle %s keyword in rect vertex', function (keyword) {
const rest = flow.parse(
const rest = flow.parser.parse(
`graph TD;A_${keyword}_node-->B[|borders:lt|This node has a ${keyword} as text];`
);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('B').type).toBe('rect');
expect(vert.get('B').text).toBe(`This node has a ${keyword} as text`);
});
it('should handle edge case for odd vertex with node id ending with minus', function () {
const res = flow.parse('graph TD;A_node-->odd->Vertex Text];');
const vert = flow.yy.getVertices();
const res = flow.parser.parse('graph TD;A_node-->odd->Vertex Text];');
const vert = flow.parser.yy.getVertices();
expect(vert.get('odd-').type).toBe('odd');
expect(vert.get('odd-').text).toBe('Vertex Text');
});
it('should allow forward slashes in lean_right vertices', function () {
const rest = flow.parse(`graph TD;A_node-->B[/This node has a / as text/];`);
const rest = flow.parser.parse(`graph TD;A_node-->B[/This node has a / as text/];`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('B').type).toBe('lean_right');
expect(vert.get('B').text).toBe(`This node has a / as text`);
});
it('should allow back slashes in lean_left vertices', function () {
const rest = flow.parse(`graph TD;A_node-->B[\\This node has a \\ as text\\];`);
const rest = flow.parser.parse(`graph TD;A_node-->B[\\This node has a \\ as text\\];`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('B').type).toBe('lean_left');
expect(vert.get('B').text).toBe(`This node has a \\ as text`);
});
it('should handle åäö and minus', function () {
const res = flow.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};');
const res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('C').type).toBe('diamond');
expect(vert.get('C').text).toBe('Chimpansen hoppar åäö-ÅÄÖ');
});
it('should handle with åäö, minus and space and br', function () {
const res = flow.parse('graph TD;A-->C(Chimpansen hoppar åäö <br> - ÅÄÖ);');
const res = flow.parser.parse('graph TD;A-->C(Chimpansen hoppar åäö <br> - ÅÄÖ);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('C').type).toBe('round');
expect(vert.get('C').text).toBe('Chimpansen hoppar åäö <br> - ÅÄÖ');
});
// it.skip('should handle åäö, minus and space and br',function(){
// const res = flow.parse('graph TD; A[Object&#40;foo,bar&#41;]-->B(Thing);');
// const res = flow.parser.parse('graph TD; A[Object&#40;foo,bar&#41;]-->B(Thing);');
//
// const vert = flow.yy.getVertices();
// const edges = flow.yy.getEdges();
// const vert = flow.parser.yy.getVertices();
// const edges = flow.parser.yy.getEdges();
//
// expect(vert.get('C').type).toBe('round');
// expect(vert.get('C').text).toBe(' A[Object&#40;foo,bar&#41;]-->B(Thing);');
// });
it('should handle unicode chars', function () {
const res = flow.parse('graph TD;A-->C(Начало);');
const res = flow.parser.parse('graph TD;A-->C(Начало);');
const vert = flow.yy.getVertices();
const vert = flow.parser.yy.getVertices();
expect(vert.get('C').text).toBe('Начало');
});
it('should handle backslash', function () {
const res = flow.parse('graph TD;A-->C(c:\\windows);');
const res = flow.parser.parse('graph TD;A-->C(c:\\windows);');
const vert = flow.yy.getVertices();
const vert = flow.parser.yy.getVertices();
expect(vert.get('C').text).toBe('c:\\windows');
});
it('should handle CAPS', function () {
const res = flow.parse('graph TD;A-->C(some CAPS);');
const res = flow.parser.parse('graph TD;A-->C(some CAPS);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('C').type).toBe('round');
expect(vert.get('C').text).toBe('some CAPS');
});
it('should handle directions', function () {
const res = flow.parse('graph TD;A-->C(some URL);');
const res = flow.parser.parse('graph TD;A-->C(some URL);');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('C').type).toBe('round');
expect(vert.get('C').text).toBe('some URL');
@@ -457,10 +457,10 @@ describe('[Text] when parsing', () => {
});
it('should handle multi-line text', function () {
const res = flow.parse('graph TD;A--o|text space|B;\n B-->|more text with space|C;');
const res = flow.parser.parse('graph TD;A--o|text space|B;\n B-->|more text with space|C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_circle');
expect(edges[1].type).toBe('arrow_point');
@@ -477,102 +477,102 @@ describe('[Text] when parsing', () => {
});
it('should handle text in vertices with space', function () {
const res = flow.parse('graph TD;A[chimpansen hoppar]-->C;');
const res = flow.parser.parse('graph TD;A[chimpansen hoppar]-->C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').type).toBe('square');
expect(vert.get('A').text).toBe('chimpansen hoppar');
});
it('should handle text in vertices with space with spaces between vertices and link', function () {
const res = flow.parse('graph TD;A[chimpansen hoppar] --> C;');
const res = flow.parser.parse('graph TD;A[chimpansen hoppar] --> C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').type).toBe('square');
expect(vert.get('A').text).toBe('chimpansen hoppar');
});
it('should handle text including _ in vertices', function () {
const res = flow.parse('graph TD;A[chimpansen_hoppar] --> C;');
const res = flow.parser.parse('graph TD;A[chimpansen_hoppar] --> C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').type).toBe('square');
expect(vert.get('A').text).toBe('chimpansen_hoppar');
});
it('should handle quoted text in vertices ', function () {
const res = flow.parse('graph TD;A["chimpansen hoppar ()[]"] --> C;');
const res = flow.parser.parse('graph TD;A["chimpansen hoppar ()[]"] --> C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').type).toBe('square');
expect(vert.get('A').text).toBe('chimpansen hoppar ()[]');
});
it('should handle text in circle vertices with space', function () {
const res = flow.parse('graph TD;A((chimpansen hoppar))-->C;');
const res = flow.parser.parse('graph TD;A((chimpansen hoppar))-->C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').type).toBe('circle');
expect(vert.get('A').text).toBe('chimpansen hoppar');
});
it('should handle text in ellipse vertices', function () {
const res = flow.parse('graph TD\nA(-this is an ellipse-)-->B');
const res = flow.parser.parse('graph TD\nA(-this is an ellipse-)-->B');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').type).toBe('ellipse');
expect(vert.get('A').text).toBe('this is an ellipse');
});
it('should not freeze when ellipse text has a `(`', function () {
expect(() => flow.parse('graph\nX(- My Text (')).toThrowError();
expect(() => flow.parser.parse('graph\nX(- My Text (')).toThrowError();
});
it('should handle text in diamond vertices with space', function () {
const res = flow.parse('graph TD;A(chimpansen hoppar)-->C;');
const res = flow.parser.parse('graph TD;A(chimpansen hoppar)-->C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').type).toBe('round');
expect(vert.get('A').text).toBe('chimpansen hoppar');
});
it('should handle text in with ?', function () {
const res = flow.parse('graph TD;A(?)-->|?|C;');
const res = flow.parser.parse('graph TD;A(?)-->|?|C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').text).toBe('?');
expect(edges[0].text).toBe('?');
});
it('should handle text in with éèêàçô', function () {
const res = flow.parse('graph TD;A(éèêàçô)-->|éèêàçô|C;');
const res = flow.parser.parse('graph TD;A(éèêàçô)-->|éèêàçô|C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').text).toBe('éèêàçô');
expect(edges[0].text).toBe('éèêàçô');
});
it('should handle text in with ,.?!+-*', function () {
const res = flow.parse('graph TD;A(,.?!+-*)-->|,.?!+-*|C;');
const res = flow.parser.parse('graph TD;A(,.?!+-*)-->|,.?!+-*|C;');
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').text).toBe(',.?!+-*');
expect(edges[0].text).toBe(',.?!+-*');
@@ -580,30 +580,30 @@ describe('[Text] when parsing', () => {
it('should throw error at nested set of brackets', function () {
const str = 'graph TD; A[This is a () in text];';
expect(() => flow.parse(str)).toThrowError("got 'PS'");
expect(() => flow.parser.parse(str)).toThrowError("got 'PS'");
});
it('should throw error for strings and text at the same time', function () {
const str = 'graph TD;A(this node has "string" and text)-->|this link has "string" and text|C;';
expect(() => flow.parse(str)).toThrowError("got 'STR'");
expect(() => flow.parser.parse(str)).toThrowError("got 'STR'");
});
it('should throw error for escaping quotes in text state', function () {
//prettier-ignore
const str = 'graph TD; A[This is a \"()\" in text];'; //eslint-disable-line no-useless-escape
expect(() => flow.parse(str)).toThrowError("got 'STR'");
expect(() => flow.parser.parse(str)).toThrowError("got 'STR'");
});
it('should throw error for nested quotation marks', function () {
const str = 'graph TD; A["This is a "()" in text"];';
expect(() => flow.parse(str)).toThrowError("Expecting 'SQE'");
expect(() => flow.parser.parse(str)).toThrowError("Expecting 'SQE'");
});
it('should throw error', function () {
const str = `graph TD; node[hello ) world] --> works`;
expect(() => flow.parse(str)).toThrowError("got 'PE'");
expect(() => flow.parser.parse(str)).toThrowError("got 'PE'");
});
});

View File

@@ -1,5 +1,5 @@
import { FlowDB } from '../flowDb.js';
import flow from './flowParserAdapter.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,19 +8,19 @@ setConfig({
describe('when parsing flowcharts', function () {
beforeEach(function () {
flow.yy = new FlowDB();
flow.yy.clear();
flow.yy.setGen('gen-2');
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});
it('should handle chaining of vertices', function () {
const res = flow.parse(`
const res = flow.parser.parse(`
graph TD
A-->B-->C;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -36,13 +36,13 @@ describe('when parsing flowcharts', function () {
expect(edges[1].text).toBe('');
});
it('should handle chaining of vertices', function () {
const res = flow.parse(`
const res = flow.parser.parse(`
graph TD
A & B --> C;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -58,13 +58,13 @@ describe('when parsing flowcharts', function () {
expect(edges[1].text).toBe('');
});
it('should multiple vertices in link statement in the beginning', function () {
const res = flow.parse(`
const res = flow.parser.parse(`
graph TD
A-->B & C;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -80,13 +80,13 @@ describe('when parsing flowcharts', function () {
expect(edges[1].text).toBe('');
});
it('should multiple vertices in link statement at the end', function () {
const res = flow.parse(`
const res = flow.parser.parse(`
graph TD
A & B--> C & D;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -111,13 +111,13 @@ describe('when parsing flowcharts', function () {
expect(edges[3].text).toBe('');
});
it('should handle chaining of vertices at both ends at once', function () {
const res = flow.parse(`
const res = flow.parser.parse(`
graph TD
A & B--> C & D;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -142,13 +142,13 @@ describe('when parsing flowcharts', function () {
expect(edges[3].text).toBe('');
});
it('should handle chaining and multiple nodes in link statement FVC ', function () {
const res = flow.parse(`
const res = flow.parser.parse(`
graph TD
A --> B & B2 & C --> D2;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
@@ -182,16 +182,16 @@ describe('when parsing flowcharts', function () {
expect(edges[5].text).toBe('');
});
it('should handle chaining and multiple nodes in link statement with extra info in statements', function () {
const res = flow.parse(`
const res = flow.parser.parse(`
graph TD
A[ h ] -- hello --> B[" test "]:::exClass & C --> D;
classDef exClass background:#bbb,border:1px solid red;
`);
const vert = flow.yy.getVertices();
const edges = flow.yy.getEdges();
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
const classes = flow.yy.getClasses();
const classes = flow.parser.yy.getClasses();
expect(classes.get('exClass').styles.length).toBe(2);
expect(classes.get('exClass').styles[0]).toBe('background:#bbb');

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,277 +0,0 @@
import { createToken, Lexer } from 'chevrotain';
// Define lexer mode names following JISON states
const MODES = {
DEFAULT: 'default_mode',
STRING: 'string_mode',
MD_STRING: 'md_string_mode',
ACC_TITLE: 'acc_title_mode',
ACC_DESCR: 'acc_descr_mode',
ACC_DESCR_MULTILINE: 'acc_descr_multiline_mode',
DIR: 'dir_mode',
VERTEX: 'vertex_mode',
TEXT: 'text_mode',
ELLIPSE_TEXT: 'ellipseText_mode',
TRAP_TEXT: 'trapText_mode',
EDGE_TEXT: 'edgeText_mode',
THICK_EDGE_TEXT: 'thickEdgeText_mode',
DOTTED_EDGE_TEXT: 'dottedEdgeText_mode',
CLICK: 'click_mode',
HREF: 'href_mode',
CALLBACK_NAME: 'callbackname_mode',
CALLBACK_ARGS: 'callbackargs_mode',
SHAPE_DATA: 'shapeData_mode',
SHAPE_DATA_STR: 'shapeDataStr_mode',
SHAPE_DATA_END_BRACKET: 'shapeDataEndBracket_mode',
};
// Whitespace and comments (skipped in all modes)
const WhiteSpace = createToken({
name: 'WhiteSpace',
pattern: /\s+/,
group: Lexer.SKIPPED,
});
const Comment = createToken({
name: 'Comment',
pattern: /%%[^\n]*/,
group: Lexer.SKIPPED,
});
// Keywords - following JISON patterns exactly
const Graph = createToken({
name: 'Graph',
pattern: /graph|flowchart|flowchart-elk/i,
});
const Direction = createToken({
name: 'Direction',
pattern: /direction/i,
});
const Subgraph = createToken({
name: 'Subgraph',
pattern: /subgraph/i,
});
const End = createToken({
name: 'End',
pattern: /end/i,
});
// Mode switching tokens - following JISON patterns exactly
// Links with edge text - following JISON lines 154-164
const LINK = createToken({
name: 'LINK',
pattern: /\s*[<ox]?--+[>ox-]\s*/,
});
const START_LINK = createToken({
name: 'START_LINK',
pattern: /\s*[<ox]?--\s*/,
});
const THICK_LINK = createToken({
name: 'THICK_LINK',
pattern: /\s*[<ox]?==+[=>ox]\s*/,
});
const START_THICK_LINK = createToken({
name: 'START_THICK_LINK',
pattern: /\s*[<ox]?==\s*/,
});
const DOTTED_LINK = createToken({
name: 'DOTTED_LINK',
pattern: /\s*[<ox]?-?\.+-[>ox]?\s*/,
});
const START_DOTTED_LINK = createToken({
name: 'START_DOTTED_LINK',
pattern: /\s*[<ox]?-\.\s*/,
});
// Edge text tokens
const EDGE_TEXT = createToken({
name: 'EDGE_TEXT',
pattern: /[^-]+/,
});
// Shape tokens that trigger text mode - following JISON lines 272-283
const PIPE = createToken({
name: 'PIPE',
pattern: /\|/,
});
const PS = createToken({
name: 'PS',
pattern: /\(/,
});
const PE = createToken({
name: 'PE',
pattern: /\)/,
});
const SQS = createToken({
name: 'SQS',
pattern: /\[/,
});
const SQE = createToken({
name: 'SQE',
pattern: /]/,
});
const DIAMOND_START = createToken({
name: 'DIAMOND_START',
pattern: /{/,
});
const DIAMOND_STOP = createToken({
name: 'DIAMOND_STOP',
pattern: /}/,
});
// Text content - following JISON line 283
const TEXT = createToken({
name: 'TEXT',
pattern: /[^"()[\]{|}]+/,
});
// Node string - simplified pattern for now
const NODE_STRING = createToken({
name: 'NODE_STRING',
pattern: /[\w!"#$%&'*+./?\\`]+/,
});
// Basic tokens
const NUM = createToken({
name: 'NUM',
pattern: /\d+/,
});
const MINUS = createToken({
name: 'MINUS',
pattern: /-/,
});
const AMP = createToken({
name: 'AMP',
pattern: /&/,
});
const SEMI = createToken({
name: 'SEMI',
pattern: /;/,
});
const COMMA = createToken({
name: 'COMMA',
pattern: /,/,
});
const COLON = createToken({
name: 'COLON',
pattern: /:/,
});
const QUOTE = createToken({
name: 'QUOTE',
pattern: /"/,
});
const NEWLINE = createToken({
name: 'NEWLINE',
pattern: /(\r?\n)+/,
});
const SPACE = createToken({
name: 'SPACE',
pattern: /\s/,
});
// Create a simple single-mode lexer for now
const allTokens = [
// Whitespace and comments (skipped)
WhiteSpace,
Comment,
// Keywords
Graph,
Direction,
Subgraph,
End,
// Links (must come before MINUS)
LINK,
START_LINK,
THICK_LINK,
START_THICK_LINK,
DOTTED_LINK,
START_DOTTED_LINK,
// Shapes
PS, // (
PE, // )
SQS, // [
SQE, // ]
DIAMOND_START, // {
DIAMOND_STOP, // }
PIPE, // |
// Text and identifiers
NODE_STRING,
TEXT,
NUM,
// Single characters
NEWLINE,
SPACE,
SEMI,
COMMA,
COLON,
AMP,
MINUS,
QUOTE,
];
// Create simple single-mode lexer
const FlowchartMultiModeLexer = new Lexer(allTokens);
// Export tokens and lexer
export {
FlowchartMultiModeLexer,
MODES,
// Export all tokens
Graph,
Direction,
Subgraph,
End,
LINK,
START_LINK,
THICK_LINK,
START_THICK_LINK,
DOTTED_LINK,
START_DOTTED_LINK,
EDGE_TEXT,
PIPE,
PS,
PE,
SQS,
SQE,
DIAMOND_START,
DIAMOND_STOP,
TEXT,
NODE_STRING,
NUM,
MINUS,
AMP,
SEMI,
COMMA,
COLON,
QUOTE,
NEWLINE,
SPACE,
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,501 +0,0 @@
import { FlowchartLexer } from './flowLexer.js';
import { FlowchartParser } from './flowParser.js';
import { FlowchartAstVisitor } from './flowAst.js';
// Interface matching existing Mermaid flowDb expectations
export interface FlowDb {
vertices: Record<string, any>;
edges: any[];
classes: Record<string, string>;
subGraphs: any[];
direction: string;
tooltips: Record<string, string>;
clickEvents: any[];
firstGraph: () => boolean;
setDirection: (dir: string) => void;
addVertex: (
id: string,
text?: string,
type?: string,
style?: string,
classes?: string[],
dir?: string,
props?: any
) => void;
addLink: (start: string | string[], end: string | string[], linkData: any) => void;
updateLink: (positions: ('default' | number)[], style: string[]) => void;
updateLinkInterpolate: (positions: ('default' | number)[], interpolate: string) => void;
addClass: (id: string, style: string) => void;
setClass: (ids: string | string[], className: string) => void;
setClickEvent: (id: string, functionName: string, functionArgs?: string) => void;
setLink: (id: string, link: string, target?: string) => void;
addSubGraph: (id: any, list: any[], title: any) => string;
getVertices: () => Record<string, any>;
getEdges: () => any[];
getClasses: () => Record<string, string>;
getSubGraphs: () => any[];
clear: () => void;
setAccTitle: (title: string) => void;
setAccDescription: (description: string) => void;
}
class FlowchartParserAdapter {
public lexer: any;
public parser: FlowchartParser;
public visitor: FlowchartAstVisitor;
// Mermaid compatibility
public yy: FlowDb;
constructor() {
this.lexer = FlowchartLexer;
this.parser = new FlowchartParser();
this.visitor = new FlowchartAstVisitor();
// Initialize yy object for Mermaid compatibility
this.yy = this.createYY();
}
public createYY(): FlowDb {
const state = {
vertices: new Map<string, any>(),
edges: [] as any[],
classes: {} as Record<string, string>,
subGraphs: [] as any[],
direction: 'TB',
tooltips: {} as Record<string, string>,
clickEvents: [] as any[],
subCount: 0,
accTitle: '',
accDescription: '',
};
return {
vertices: state.vertices,
edges: state.edges,
classes: state.classes,
subGraphs: state.subGraphs,
direction: state.direction,
tooltips: state.tooltips,
clickEvents: state.clickEvents,
firstGraph: () => true,
setDirection: (dir: string) => {
state.direction = dir;
},
addVertex: (
id: string,
text?: string,
type?: string,
style?: string,
classes?: string[],
dir?: string,
props?: any
) => {
state.vertices.set(id, {
id,
text: text || id,
type: type || 'default',
style,
classes,
dir,
props,
});
},
addLink: (start: string | string[], end: string | string[], linkData: any) => {
state.edges.push({
start: Array.isArray(start) ? start[start.length - 1] : start,
end: Array.isArray(end) ? end[end.length - 1] : end,
type: linkData.type || 'arrow',
stroke: linkData.stroke || 'normal',
length: linkData.length,
text: linkData.text,
});
},
updateLink: (positions: ('default' | number)[], style: string[]) => {
positions.forEach((pos) => {
if (typeof pos === 'number' && pos >= state.edges.length) {
throw new Error(
`The index ${pos} for linkStyle is out of bounds. Valid indices for linkStyle are between 0 and ${
state.edges.length - 1
}. (Help: Ensure that the index is within the range of existing edges.)`
);
}
if (pos === 'default') {
(state.edges as any).defaultStyle = style;
} else {
state.edges[pos].style = style;
// if edges[pos].style does have fill not set, set it to none
if (
(state.edges[pos]?.style?.length ?? 0) > 0 &&
!state.edges[pos]?.style?.some((s: string) => s?.startsWith('fill'))
) {
state.edges[pos]?.style?.push('fill:none');
}
}
});
},
updateLinkInterpolate: (positions: ('default' | number)[], interpolate: string) => {
positions.forEach((pos) => {
if (pos === 'default') {
(state.edges as any).defaultInterpolate = interpolate;
} else {
state.edges[pos].interpolate = interpolate;
}
});
},
addClass: (id: string, style: string) => {
state.classes[id] = style;
},
setClass: (ids: string | string[], className: string) => {
const idArray = Array.isArray(ids) ? ids : [ids];
idArray.forEach((id) => {
const vertex = state.vertices.get(id);
if (vertex) {
vertex.classes = [className];
}
});
},
setClickEvent: (id: string, functionName: string, functionArgs?: string) => {
state.clickEvents.push({
id,
functionName,
functionArgs,
});
},
setLink: (id: string, link: string, target?: string) => {
state.clickEvents.push({
id,
link,
target,
});
},
addSubGraph: (id: any, list: any[], title: any) => {
// Handle both string and object formats for compatibility
const idStr = typeof id === 'string' ? id : id?.text || '';
const titleStr = typeof title === 'string' ? title : title?.text || '';
const sgId = idStr || `subGraph${state.subCount++}`;
const subgraph = {
id: sgId,
nodes: list,
title: titleStr || sgId,
};
state.subGraphs.push(subgraph);
return sgId;
},
getVertices: () => state.vertices,
getEdges: () => state.edges,
getClasses: () => state.classes,
getSubGraphs: () => state.subGraphs,
clear: () => {
state.vertices.clear();
state.edges.length = 0;
state.classes = {};
state.subGraphs = [];
state.direction = 'TB';
state.tooltips = {};
state.clickEvents = [];
state.subCount = 0;
state.accTitle = '';
state.accDescription = '';
},
setAccTitle: (title: string) => {
state.accTitle = title;
},
setAccDescription: (description: string) => {
state.accDescription = description;
},
};
}
parse(text: string): any {
// Clear previous state
this.yy.clear();
// Tokenize
const lexResult = this.lexer.tokenize(text);
if (lexResult.errors.length > 0) {
const error = lexResult.errors[0];
throw new Error(
`Lexing error at line ${error.line}, column ${error.column}: ${error.message}`
);
}
// Parse
this.parser.input = lexResult.tokens;
// Clear any previous parser errors
this.parser.errors = [];
const cst = this.parser.flowchart();
if (this.parser.errors.length > 0) {
const error = this.parser.errors[0];
throw new Error(`Parse error: ${error.message}`);
}
// Visit CST and build AST
const ast = this.visitor.visit(cst);
// Update yy state with parsed data
// Convert plain object vertices to Map
Object.entries(ast.vertices).forEach(([id, vertex]) => {
this.yy.vertices.set(id, vertex);
});
this.yy.edges.push(...ast.edges);
Object.assign(this.yy.classes, ast.classes);
this.yy.subGraphs.push(...ast.subGraphs);
this.yy.direction = ast.direction;
Object.assign(this.yy.tooltips, ast.tooltips);
// Click events are handled separately in the main parse method
return ast;
}
// Compatibility method for Mermaid
getYY(): FlowDb {
return this.yy;
}
}
// Export a singleton instance for compatibility
const parserInstance = new FlowchartParserAdapter();
// Create a flow object that can have its yy property reassigned
const flow = {
parser: parserInstance,
yy: parserInstance.yy,
parse: (text: string) => {
// Use the current yy object (which might have been reassigned by tests)
const targetYY = flow.yy;
// Clear previous state
targetYY.clear();
parserInstance.visitor.clear();
// Set FlowDB instance in visitor for direct integration
parserInstance.visitor.setFlowDb(targetYY);
// Tokenize
const lexResult = parserInstance.lexer.tokenize(text);
if (lexResult.errors.length > 0) {
const error = lexResult.errors[0];
throw new Error(
`Lexing error at line ${error.line}, column ${error.column}: ${error.message}`
);
}
// Parse
parserInstance.parser.input = lexResult.tokens;
// Clear any previous parser errors
parserInstance.parser.errors = [];
const cst = parserInstance.parser.flowchart();
if (parserInstance.parser.errors.length > 0) {
const error = parserInstance.parser.errors[0];
throw new Error(`Parse error: ${error.message}`);
}
// Visit CST and build AST
const ast = parserInstance.visitor.visit(cst);
// Update yy state with parsed data
// Only process vertices if visitor didn't have FlowDB instance
// (if visitor had FlowDB, vertices were added directly during parsing)
if (!parserInstance.visitor.flowDb) {
// Convert plain object vertices to Map
Object.entries(ast.vertices).forEach(([id, vertex]) => {
// Use addVertex method if available, otherwise set directly
if (typeof targetYY.addVertex === 'function') {
// Create textObj structure expected by FlowDB
const textObj = vertex.text
? { text: vertex.text, type: vertex.labelType || 'text' }
: undefined;
targetYY.addVertex(
id,
textObj,
vertex.type,
vertex.style || [],
vertex.classes || [],
vertex.dir,
vertex.props || {},
undefined // metadata
);
} else {
targetYY.vertices.set(id, vertex);
}
});
}
// Add edges
// Only process edges if visitor didn't have FlowDB instance
// (if visitor had FlowDB, edges were added directly during parsing)
if (!parserInstance.visitor.flowDb) {
ast.edges.forEach((edge) => {
if (typeof targetYY.addLink === 'function') {
// Create the linkData structure expected by FlowDB
const linkData = {
id: edge.id, // Include edge ID for user-defined edge IDs
type: edge.type,
stroke: edge.stroke,
length: edge.length,
text: edge.text ? { text: edge.text, type: edge.labelType || 'text' } : undefined,
};
targetYY.addLink([edge.start], [edge.end], linkData);
} else {
targetYY.edges.push(edge);
}
});
}
// Apply edge metadata after edges have been created
if (ast.edgeMetadata && typeof targetYY.addVertex === 'function') {
Object.entries(ast.edgeMetadata).forEach(([edgeId, metadata]) => {
// Convert metadata object to YAML string format expected by FlowDB
const yamlMetadata = Object.entries(metadata)
.map(([key, value]) => `${key}: ${value}`)
.join(', ');
// Use FlowDB's addVertex method which can detect edges and apply metadata
const textObj = { text: edgeId, type: 'text' };
targetYY.addVertex(
edgeId,
textObj,
'squareRect', // shape (not used for edges)
[], // style
[], // classes
undefined, // dir
{}, // props (empty for edges)
yamlMetadata // metadata - this will be processed as YAML and applied to the edge
);
});
}
// Apply linkStyles after edges have been added
if (ast.linkStyles) {
ast.linkStyles.forEach((linkStyle) => {
if (linkStyle.interpolate && typeof targetYY.updateLinkInterpolate === 'function') {
targetYY.updateLinkInterpolate(linkStyle.positions, linkStyle.interpolate);
}
if (linkStyle.styles && typeof targetYY.updateLink === 'function') {
targetYY.updateLink(linkStyle.positions, linkStyle.styles);
}
});
}
// Add classes
Object.entries(ast.classes).forEach(([id, className]) => {
if (typeof targetYY.addClass === 'function') {
// FlowDB.addClass expects an array of style strings, not a single string
const styleArray = className.split(',').map((s) => s.trim());
targetYY.addClass(id, styleArray);
} else {
targetYY.classes[id] = className;
}
});
// Add subgraphs
if (targetYY.subGraphs) {
targetYY.subGraphs.push(...ast.subGraphs);
}
// Set direction (only if not already set during parsing)
if (ast.direction && typeof targetYY.setDirection === 'function') {
targetYY.setDirection(ast.direction);
} else if (ast.direction) {
targetYY.direction = ast.direction;
}
// Add tooltips
Object.entries(ast.tooltips).forEach(([id, tooltip]) => {
if (typeof targetYY.setTooltip === 'function') {
targetYY.setTooltip(id, tooltip);
} else if (targetYY.tooltips) {
targetYY.tooltips[id] = tooltip;
}
});
// Add accessibility information
if (ast.accTitle && typeof targetYY.setAccTitle === 'function') {
targetYY.setAccTitle(ast.accTitle);
}
if (ast.accDescription && typeof targetYY.setAccDescription === 'function') {
targetYY.setAccDescription(ast.accDescription);
}
// Click events are now handled directly by the AST visitor during parsing
// to match JISON parser behavior and avoid duplicate calls
// ast.clickEvents.forEach((clickEvent) => {
// if (clickEvent.type === 'href') {
// // Handle href/link events
// if (typeof targetYY.setLink === 'function') {
// if (clickEvent.target !== undefined) {
// targetYY.setLink(clickEvent.id, clickEvent.href, clickEvent.target);
// } else {
// targetYY.setLink(clickEvent.id, clickEvent.href);
// }
// }
// } else if (clickEvent.type === 'call') {
// // Handle callback/function call events
// if (typeof targetYY.setClickEvent === 'function') {
// // Only pass functionArgs if it's defined (for compatibility with JISON parser)
// if (clickEvent.functionArgs !== undefined) {
// targetYY.setClickEvent(clickEvent.id, clickEvent.functionName, clickEvent.functionArgs);
// } else {
// targetYY.setClickEvent(clickEvent.id, clickEvent.functionName);
// }
// }
// }
// });
return ast;
},
};
// Mermaid expects these exports
export const parser = parserInstance;
export const yy = parserInstance.yy;
// Add backward compatibility for JISON parser interface
// The Diagram.fromText method expects parser.parser.yy to exist for JISON parsers
flow.parser = parserInstance;
// CRITICAL FIX: Create a parser object with the expected JISON structure
// This allows the main diagram rendering system to set the yy object correctly
const jisonCompatibleParser = {
...flow,
// Override the yy property to ensure it's properly linked
get yy() {
return flow.yy;
},
set yy(value) {
flow.yy = value;
},
parser: {
...parserInstance,
get yy() {
return flow.yy;
},
set yy(value) {
flow.yy = value;
},
},
};
// Default export for modern imports - use the JISON-compatible version
export default jisonCompatibleParser;

View File

@@ -1,81 +0,0 @@
// Explore JISON parser structure to find lexer access
import jisonParser from './flow.jison';
import { FlowDB } from '../flowDb.js';
console.log('=== JISON Parser Structure Exploration ===');
// Initialize parser
const flowDb = new FlowDB();
jisonParser.yy = flowDb;
console.log('\n1. Main parser object properties:');
console.log(Object.keys(jisonParser));
console.log('\n2. Parser object properties:');
if (jisonParser.parser) {
console.log(Object.keys(jisonParser.parser));
}
console.log('\n3. Lexer object properties:');
if (jisonParser.lexer) {
console.log(Object.keys(jisonParser.lexer));
console.log('\nLexer methods:');
console.log(Object.getOwnPropertyNames(jisonParser.lexer).filter(name =>
typeof jisonParser.lexer[name] === 'function'
));
}
console.log('\n4. Parser.lexer properties:');
if (jisonParser.parser && jisonParser.parser.lexer) {
console.log(Object.keys(jisonParser.parser.lexer));
console.log('\nParser.lexer methods:');
console.log(Object.getOwnPropertyNames(jisonParser.parser.lexer).filter(name =>
typeof jisonParser.parser.lexer[name] === 'function'
));
}
// Test lexer access
console.log('\n5. Testing lexer access:');
const testInput = 'graph TD';
try {
// Try different ways to access the lexer
const lexer = jisonParser.lexer || jisonParser.parser?.lexer;
if (lexer) {
console.log('Found lexer, testing tokenization...');
// Try to set input and get tokens
if (typeof lexer.setInput === 'function') {
lexer.setInput(testInput);
console.log('Input set successfully');
// Try to get tokens one by one
const tokens = [];
let token;
let count = 0;
while ((token = lexer.lex()) !== 'EOF' && count < 10) {
tokens.push({
type: token,
value: lexer.yytext,
line: lexer.yylineno,
column: lexer.yylloc?.first_column || 0
});
count++;
}
console.log('Extracted tokens:', tokens);
} else {
console.log('setInput method not found');
}
} else {
console.log('No lexer found');
}
} catch (error) {
console.log('Error accessing lexer:', error.message);
}
console.log('\n6. Available methods on main parser:');
console.log(Object.getOwnPropertyNames(jisonParser).filter(name =>
typeof jisonParser[name] === 'function'
));

View File

@@ -1,27 +0,0 @@
import { describe, it, expect } from 'vitest';
import type { ExpectedToken } from './lexer-test-utils.js';
import { createLexerTestSuite } from './lexer-test-utils.js';
/**
* LEXER COMPARISON TESTS
*
* Format:
* 1. Input: graph text
* 2. Run both JISON and Chevrotain lexers
* 3. Expected: array of lexical tokens
* 4. Compare actual output with expected
*/
describe('Lexer Comparison Tests', () => {
const { runTest } = createLexerTestSuite();
it('should tokenize "graph TD" correctly', () => {
const input = 'graph TD';
const expected: ExpectedToken[] = [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DirectionValue', value: 'TD' },
];
expect(() => runTest('GRA001', input, expected)).not.toThrow();
});
});

View File

@@ -1,240 +0,0 @@
import { describe, it, expect } from 'vitest';
import { createLexerTestSuite } from './lexer-test-utils.js';
/**
* ARROW SYNTAX LEXER TESTS
*
* Extracted from flow-arrows.spec.js covering all arrow types and variations
* Each test has a unique ID (3 letters + 3 digits) for easy identification
*/
describe('Arrow Syntax Lexer Tests', () => {
const { runTest } = createLexerTestSuite();
// Basic arrows
it('ARR001: should tokenize "A-->B" correctly', () => {
expect(() =>
runTest('ARR001', 'A-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR002: should tokenize "A --- B" correctly', () => {
expect(() =>
runTest('ARR002', 'A --- B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '---' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Double-edged arrows
it('ARR003: should tokenize "A<-->B" correctly', () => {
expect(() =>
runTest('ARR003', 'A<-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '<-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR004: should tokenize "A<-- text -->B" correctly', () => {
// Note: Edge text parsing differs significantly between lexers
// JISON breaks text into individual characters, Chevrotain uses structured tokens
// This test documents the current behavior rather than enforcing compatibility
expect(() =>
runTest('ARR004', 'A<-- text -->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '<--' }, // JISON uses START_LINK for edge text context
{ type: 'EdgeTextContent', value: 'text' }, // Chevrotain structured approach
{ type: 'EdgeTextEnd', value: '-->' }, // Chevrotain end token
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Thick arrows
it('ARR005: should tokenize "A<==>B" correctly', () => {
expect(() =>
runTest('ARR005', 'A<==>B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '<==>' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR006: should tokenize "A<== text ==>B" correctly', () => {
expect(() =>
runTest('ARR006', 'A<== text ==>B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '<==' },
{ type: 'EdgeTextContent', value: 'text' },
{ type: 'EdgeTextEnd', value: '==>' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR007: should tokenize "A==>B" correctly', () => {
expect(() =>
runTest('ARR007', 'A==>B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '==>' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR008: should tokenize "A===B" correctly', () => {
expect(() =>
runTest('ARR008', 'A===B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '===' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Dotted arrows
it('ARR009: should tokenize "A<-.->B" correctly', () => {
expect(() =>
runTest('ARR009', 'A<-.->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '<-.->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR010: should tokenize "A<-. text .->B" correctly', () => {
expect(() =>
runTest('ARR010', 'A<-. text .->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_DOTTED_LINK', value: '<-.' },
{ type: 'EdgeTextContent', value: 'text .' },
{ type: 'EdgeTextEnd', value: '->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR011: should tokenize "A-.->B" correctly', () => {
expect(() =>
runTest('ARR011', 'A-.->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-.->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR012: should tokenize "A-.-B" correctly', () => {
expect(() =>
runTest('ARR012', 'A-.-B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-.-' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Cross arrows
it('ARR013: should tokenize "A--xB" correctly', () => {
expect(() =>
runTest('ARR013', 'A--xB', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '--x' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR014: should tokenize "A--x|text|B" correctly', () => {
expect(() =>
runTest('ARR014', 'A--x|text|B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '--x' },
{ type: 'PIPE', value: '|' },
{ type: 'textToken', value: 'text' },
{ type: 'PIPE', value: '|' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Circle arrows
it('ARR015: should tokenize "A--oB" correctly', () => {
expect(() =>
runTest('ARR015', 'A--oB', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '--o' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR016: should tokenize "A--o|text|B" correctly', () => {
expect(() =>
runTest('ARR016', 'A--o|text|B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '--o' },
{ type: 'PIPE', value: '|' },
{ type: 'textToken', value: 'text' },
{ type: 'PIPE', value: '|' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Long arrows
it('ARR017: should tokenize "A---->B" correctly', () => {
expect(() =>
runTest('ARR017', 'A---->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '---->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR018: should tokenize "A-----B" correctly', () => {
expect(() =>
runTest('ARR018', 'A-----B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-----' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Text on arrows with different syntaxes
it('ARR019: should tokenize "A-- text -->B" correctly', () => {
expect(() =>
runTest('ARR019', 'A-- text -->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '--' },
{ type: 'EdgeTextContent', value: 'text ' },
{ type: 'EdgeTextEnd', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('ARR020: should tokenize "A--text-->B" correctly', () => {
expect(() =>
runTest('ARR020', 'A--text-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '--' },
{ type: 'EdgeTextContent', value: 'text' },
{ type: 'EdgeTextEnd', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
});

View File

@@ -1,144 +0,0 @@
import { describe, it, expect } from 'vitest';
import type { ExpectedToken } from './lexer-test-utils.js';
import { createLexerTestSuite } from './lexer-test-utils.js';
/**
* BASIC SYNTAX LEXER TESTS
*
* Extracted from flow.spec.js and other basic parser tests
* Each test has a unique ID (3 letters + 3 digits) for easy identification
*/
describe('Basic Syntax Lexer Tests', () => {
const { runTest } = createLexerTestSuite();
it('GRA001: should tokenize "graph TD" correctly', () => {
expect(() =>
runTest('GRA001', 'graph TD', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: 'TD' },
])
).not.toThrow();
});
it('GRA002: should tokenize "graph LR" correctly', () => {
expect(() =>
runTest('GRA002', 'graph LR', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: 'LR' },
])
).not.toThrow();
});
it('GRA003: should tokenize "graph TB" correctly', () => {
expect(() =>
runTest('GRA003', 'graph TB', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: 'TB' },
])
).not.toThrow();
});
it('GRA004: should tokenize "graph RL" correctly', () => {
expect(() =>
runTest('GRA004', 'graph RL', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: 'RL' },
])
).not.toThrow();
});
it('GRA005: should tokenize "graph BT" correctly', () => {
expect(() =>
runTest('GRA005', 'graph BT', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: 'BT' },
])
).not.toThrow();
});
it('FLO001: should tokenize "flowchart TD" correctly', () => {
expect(() =>
runTest('FLO001', 'flowchart TD', [
{ type: 'GRAPH', value: 'flowchart' },
{ type: 'DIR', value: 'TD' },
])
).not.toThrow();
});
it('FLO002: should tokenize "flowchart LR" correctly', () => {
expect(() =>
runTest('FLO002', 'flowchart LR', [
{ type: 'GRAPH', value: 'flowchart' },
{ type: 'DIR', value: 'LR' },
])
).not.toThrow();
});
it('NOD001: should tokenize simple node "A" correctly', () => {
expect(() => runTest('NOD001', 'A', [{ type: 'NODE_STRING', value: 'A' }])).not.toThrow();
});
it('NOD002: should tokenize node "A1" correctly', () => {
expect(() => runTest('NOD002', 'A1', [{ type: 'NODE_STRING', value: 'A1' }])).not.toThrow();
});
it('NOD003: should tokenize node "node1" correctly', () => {
expect(() =>
runTest('NOD003', 'node1', [{ type: 'NODE_STRING', value: 'node1' }])
).not.toThrow();
});
it('EDG001: should tokenize "A-->B" correctly', () => {
expect(() =>
runTest('EDG001', 'A-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG002: should tokenize "A --- B" correctly', () => {
expect(() =>
runTest('EDG002', 'A --- B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '---' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('SHP001: should tokenize "A[Square]" correctly', () => {
expect(() =>
runTest('SHP001', 'A[Square]', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'SQS', value: '[' },
{ type: 'textToken', value: 'Square' },
{ type: 'SQE', value: ']' },
])
).not.toThrow();
});
it('SHP002: should tokenize "A(Round)" correctly', () => {
expect(() =>
runTest('SHP002', 'A(Round)', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'PS', value: '(' },
{ type: 'textToken', value: 'Round' },
{ type: 'PE', value: ')' },
])
).not.toThrow();
});
it('SHP003: should tokenize "A{Diamond}" correctly', () => {
expect(() =>
runTest('SHP003', 'A{Diamond}', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'DIAMOND_START', value: '{' },
{ type: 'textToken', value: 'Diamond' },
{ type: 'DIAMOND_STOP', value: '}' },
])
).not.toThrow();
});
});

View File

@@ -1,107 +0,0 @@
import { describe, it, expect } from 'vitest';
import { createLexerTestSuite } from './lexer-test-utils.js';
/**
* COMMENT SYNTAX LEXER TESTS
*
* Extracted from flow-comments.spec.js covering comment handling
* Each test has a unique ID (3 letters + 3 digits) for easy identification
*/
describe('Comment Syntax Lexer Tests', () => {
const { runTest } = createLexerTestSuite();
// Single line comments
it('COM001: should tokenize "%% comment" correctly', () => {
expect(() => runTest('COM001', '%% comment', [
{ type: 'COMMENT', value: '%% comment' },
])).not.toThrow();
});
it('COM002: should tokenize "%%{init: {"theme":"base"}}%%" correctly', () => {
expect(() => runTest('COM002', '%%{init: {"theme":"base"}}%%', [
{ type: 'DIRECTIVE', value: '%%{init: {"theme":"base"}}%%' },
])).not.toThrow();
});
// Comments with graph content
it('COM003: should handle comment before graph', () => {
expect(() => runTest('COM003', '%% This is a comment\ngraph TD', [
{ type: 'COMMENT', value: '%% This is a comment' },
{ type: 'NEWLINE', value: '\n' },
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: 'TD' },
])).not.toThrow();
});
it('COM004: should handle comment after graph', () => {
expect(() => runTest('COM004', 'graph TD\n%% This is a comment', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: 'TD' },
{ type: 'NEWLINE', value: '\n' },
{ type: 'COMMENT', value: '%% This is a comment' },
])).not.toThrow();
});
it('COM005: should handle comment between nodes', () => {
expect(() => runTest('COM005', 'A-->B\n%% comment\nB-->C', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
{ type: 'NEWLINE', value: '\n' },
{ type: 'COMMENT', value: '%% comment' },
{ type: 'NEWLINE', value: '\n' },
{ type: 'NODE_STRING', value: 'B' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'C' },
])).not.toThrow();
});
// Directive comments
it('COM006: should tokenize theme directive', () => {
expect(() => runTest('COM006', '%%{init: {"theme":"dark"}}%%', [
{ type: 'DIRECTIVE', value: '%%{init: {"theme":"dark"}}%%' },
])).not.toThrow();
});
it('COM007: should tokenize config directive', () => {
expect(() => runTest('COM007', '%%{config: {"flowchart":{"htmlLabels":false}}}%%', [
{ type: 'DIRECTIVE', value: '%%{config: {"flowchart":{"htmlLabels":false}}}%%' },
])).not.toThrow();
});
it('COM008: should tokenize wrap directive', () => {
expect(() => runTest('COM008', '%%{wrap}%%', [
{ type: 'DIRECTIVE', value: '%%{wrap}%%' },
])).not.toThrow();
});
// Comments with special characters
it('COM009: should handle comment with special chars', () => {
expect(() => runTest('COM009', '%% Comment with special chars: !@#$%^&*()', [
{ type: 'COMMENT', value: '%% Comment with special chars: !@#$%^&*()' },
])).not.toThrow();
});
it('COM010: should handle comment with unicode', () => {
expect(() => runTest('COM010', '%% Comment with unicode: åäö ÅÄÖ', [
{ type: 'COMMENT', value: '%% Comment with unicode: åäö ÅÄÖ' },
])).not.toThrow();
});
// Multiple comments
it('COM011: should handle multiple comments', () => {
expect(() => runTest('COM011', '%% First comment\n%% Second comment', [
{ type: 'COMMENT', value: '%% First comment' },
{ type: 'NEWLINE', value: '\n' },
{ type: 'COMMENT', value: '%% Second comment' },
])).not.toThrow();
});
// Empty comments
it('COM012: should handle empty comment', () => {
expect(() => runTest('COM012', '%%', [
{ type: 'COMMENT', value: '%%' },
])).not.toThrow();
});
});

View File

@@ -1,281 +0,0 @@
import { describe, it, expect } from 'vitest';
import { createLexerTestSuite } from './lexer-test-utils.js';
/**
* COMPLEX TEXT PATTERNS LEXER TESTS
*
* Tests for complex text patterns with quotes, markdown, unicode, backslashes
* Based on flow-text.spec.js and flow-md-string.spec.js
* Each test has a unique ID (3 letters + 3 digits) for easy identification
*/
describe('Complex Text Patterns Lexer Tests', () => {
const { runTest } = createLexerTestSuite();
// Quoted text patterns
it('CTX001: should tokenize "A-- \\"test string()\\" -->B" correctly', () => {
expect(() =>
runTest('CTX001', 'A-- "test string()" -->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '--' },
{ type: 'EdgeTextContent', value: '"test string()"' },
{ type: 'EdgeTextEnd', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('CTX002: should tokenize "A[\\"quoted text\\"]-->B" correctly', () => {
expect(() =>
runTest('CTX002', 'A["quoted text"]-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'SQS', value: '[' },
{ type: 'textToken', value: '"quoted text"' },
{ type: 'SQE', value: ']' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Markdown text patterns
it('CTX003: should tokenize markdown in vertex text correctly', () => {
expect(() =>
runTest('CTX003', 'A["`The cat in **the** hat`"]-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'SQS', value: '[' },
{ type: 'textToken', value: '"`The cat in **the** hat`"' },
{ type: 'SQE', value: ']' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('CTX004: should tokenize markdown in edge text correctly', () => {
expect(() =>
runTest('CTX004', 'A-- "`The *bat* in the chat`" -->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '--' },
{ type: 'EdgeTextContent', value: '"`The *bat* in the chat`"' },
{ type: 'EdgeTextEnd', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Unicode characters
it('CTX005: should tokenize "A(Начало)-->B" correctly', () => {
expect(() =>
runTest('CTX005', 'A(Начало)-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'PS', value: '(' },
{ type: 'textToken', value: 'Начало' },
{ type: 'PE', value: ')' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('CTX006: should tokenize "A(åäö-ÅÄÖ)-->B" correctly', () => {
expect(() =>
runTest('CTX006', 'A(åäö-ÅÄÖ)-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'PS', value: '(' },
{ type: 'textToken', value: 'åäö-ÅÄÖ' },
{ type: 'PE', value: ')' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Backslash patterns
it('CTX007: should tokenize "A(c:\\\\windows)-->B" correctly', () => {
expect(() =>
runTest('CTX007', 'A(c:\\windows)-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'PS', value: '(' },
{ type: 'textToken', value: 'c:\\windows' },
{ type: 'PE', value: ')' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('CTX008: should tokenize lean_left with backslashes correctly', () => {
expect(() =>
runTest('CTX008', 'A[\\This has \\ backslash\\]-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'SQS', value: '[\\' },
{ type: 'textToken', value: 'This has \\ backslash' },
{ type: 'SQE', value: '\\]' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// HTML break tags
it('CTX009: should tokenize "A(text <br> more)-->B" correctly', () => {
expect(() =>
runTest('CTX009', 'A(text <br> more)-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'PS', value: '(' },
{ type: 'textToken', value: 'text <br> more' },
{ type: 'PE', value: ')' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('CTX010: should tokenize complex HTML with spaces correctly', () => {
expect(() =>
runTest('CTX010', 'A(Chimpansen hoppar åäö <br> - ÅÄÖ)-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'PS', value: '(' },
{ type: 'textToken', value: 'Chimpansen hoppar åäö <br> - ÅÄÖ' },
{ type: 'PE', value: ')' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Forward slash patterns
it('CTX011: should tokenize lean_right with forward slashes correctly', () => {
expect(() =>
runTest('CTX011', 'A[/This has / slash/]-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'SQS', value: '[/' },
{ type: 'textToken', value: 'This has / slash' },
{ type: 'SQE', value: '/]' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('CTX012: should tokenize "A-- text with / should work -->B" correctly', () => {
expect(() =>
runTest('CTX012', 'A-- text with / should work -->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '--' },
{ type: 'EdgeTextContent', value: 'text with / should work' },
{ type: 'EdgeTextEnd', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Mixed special characters
it('CTX013: should tokenize "A(CAPS and URL and TD)-->B" correctly', () => {
expect(() =>
runTest('CTX013', 'A(CAPS and URL and TD)-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'PS', value: '(' },
{ type: 'textToken', value: 'CAPS and URL and TD' },
{ type: 'PE', value: ')' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Underscore patterns
it('CTX014: should tokenize "A(chimpansen_hoppar)-->B" correctly', () => {
expect(() =>
runTest('CTX014', 'A(chimpansen_hoppar)-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'PS', value: '(' },
{ type: 'textToken', value: 'chimpansen_hoppar' },
{ type: 'PE', value: ')' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Complex edge text with multiple keywords
it('CTX015: should tokenize edge text with multiple keywords correctly', () => {
expect(() =>
runTest('CTX015', 'A-- text including graph space and v -->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '--' },
{ type: 'EdgeTextContent', value: 'text including graph space and v' },
{ type: 'EdgeTextEnd', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Pipe text patterns
it('CTX016: should tokenize "A--x|text including space|B" correctly', () => {
expect(() =>
runTest('CTX016', 'A--x|text including space|B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '--x' },
{ type: 'PIPE', value: '|' },
{ type: 'textToken', value: 'text including space' },
{ type: 'PIPE', value: '|' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Multiple leading spaces
it('CTX017: should tokenize "A-- textNoSpace --xB" correctly', () => {
expect(() =>
runTest('CTX017', 'A-- textNoSpace --xB', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '--' },
{ type: 'EdgeTextContent', value: ' textNoSpace ' },
{ type: 'EdgeTextEnd', value: '--x' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Complex markdown patterns
it('CTX018: should tokenize complex markdown with shapes correctly', () => {
expect(() =>
runTest('CTX018', 'A{"`Decision with **bold**`"}-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'DIAMOND_START', value: '{' },
{ type: 'textToken', value: '"`Decision with **bold**`"' },
{ type: 'DIAMOND_STOP', value: '}' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Text with equals signs (from flow-text.spec.js)
it('CTX019: should tokenize "A-- test text with == -->B" correctly', () => {
expect(() =>
runTest('CTX019', 'A-- test text with == -->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '--' },
{ type: 'EdgeTextContent', value: 'test text with ==' },
{ type: 'EdgeTextEnd', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
// Text with dashes in thick arrows
it('CTX020: should tokenize "A== test text with - ==>B" correctly', () => {
expect(() =>
runTest('CTX020', 'A== test text with - ==>B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'START_LINK', value: '==' },
{ type: 'EdgeTextContent', value: 'test text with -' },
{ type: 'EdgeTextEnd', value: '==>' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
});

View File

@@ -1,79 +0,0 @@
import { describe, it, expect } from 'vitest';
import { createLexerTestSuite } from './lexer-test-utils.js';
/**
* COMPLEX SYNTAX LEXER TESTS
*
* Extracted from various parser tests covering complex combinations
* Each test has a unique ID (3 letters + 3 digits) for easy identification
*/
describe('Complex Syntax Lexer Tests', () => {
const { runTest } = createLexerTestSuite();
it('COM001: should tokenize "graph TD; A-->B" correctly', () => {
expect(() =>
runTest('COM001', 'graph TD; A-->B', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: 'TD' },
{ type: 'SEMI', value: ';' },
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('COM002: should tokenize "A & B --> C" correctly', () => {
expect(() =>
runTest('COM002', 'A & B --> C', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'AMP', value: '&' },
{ type: 'NODE_STRING', value: 'B' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'C' },
])
).not.toThrow();
});
it('COM003: should tokenize "A[Text] --> B(Round)" correctly', () => {
expect(() =>
runTest('COM003', 'A[Text] --> B(Round)', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'SQS', value: '[' },
{ type: 'textToken', value: 'Text' },
{ type: 'SQE', value: ']' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
{ type: 'PS', value: '(' },
{ type: 'textToken', value: 'Round' },
{ type: 'PE', value: ')' },
])
).not.toThrow();
});
it('COM004: should tokenize "A --> B --> C" correctly', () => {
expect(() =>
runTest('COM004', 'A --> B --> C', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'C' },
])
).not.toThrow();
});
it('COM005: should tokenize "A-->|label|B" correctly', () => {
expect(() =>
runTest('COM005', 'A-->|label|B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-->' },
{ type: 'PIPE', value: '|' },
{ type: 'textToken', value: 'label' },
{ type: 'PIPE', value: '|' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
});

View File

@@ -1,83 +0,0 @@
import { describe, it, expect } from 'vitest';
import { createLexerTestSuite } from './lexer-test-utils.js';
/**
* DIRECTION SYNTAX LEXER TESTS
*
* Extracted from flow-arrows.spec.js and flow-direction.spec.js
* Each test has a unique ID (3 letters + 3 digits) for easy identification
*/
describe('Direction Syntax Lexer Tests', () => {
const { runTest } = createLexerTestSuite();
it('DIR001: should tokenize "graph >" correctly', () => {
expect(() => runTest('DIR001', 'graph >', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: '>' },
])).not.toThrow();
});
it('DIR002: should tokenize "graph <" correctly', () => {
expect(() => runTest('DIR002', 'graph <', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: '<' },
])).not.toThrow();
});
it('DIR003: should tokenize "graph ^" correctly', () => {
expect(() => runTest('DIR003', 'graph ^', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: '^' },
])).not.toThrow();
});
it('DIR004: should tokenize "graph v" correctly', () => {
expect(() => runTest('DIR004', 'graph v', [
{ type: 'GRAPH', value: 'graph' },
{ type: 'DIR', value: 'v' },
])).not.toThrow();
});
it('DIR005: should tokenize "flowchart >" correctly', () => {
expect(() => runTest('DIR005', 'flowchart >', [
{ type: 'GRAPH', value: 'flowchart' },
{ type: 'DIR', value: '>' },
])).not.toThrow();
});
it('DIR006: should tokenize "flowchart <" correctly', () => {
expect(() => runTest('DIR006', 'flowchart <', [
{ type: 'GRAPH', value: 'flowchart' },
{ type: 'DIR', value: '<' },
])).not.toThrow();
});
it('DIR007: should tokenize "flowchart ^" correctly', () => {
expect(() => runTest('DIR007', 'flowchart ^', [
{ type: 'GRAPH', value: 'flowchart' },
{ type: 'DIR', value: '^' },
])).not.toThrow();
});
it('DIR008: should tokenize "flowchart v" correctly', () => {
expect(() => runTest('DIR008', 'flowchart v', [
{ type: 'GRAPH', value: 'flowchart' },
{ type: 'DIR', value: 'v' },
])).not.toThrow();
});
it('DIR009: should tokenize "flowchart-elk TD" correctly', () => {
expect(() => runTest('DIR009', 'flowchart-elk TD', [
{ type: 'GRAPH', value: 'flowchart-elk' },
{ type: 'DIR', value: 'TD' },
])).not.toThrow();
});
it('DIR010: should tokenize "flowchart-elk LR" correctly', () => {
expect(() => runTest('DIR010', 'flowchart-elk LR', [
{ type: 'GRAPH', value: 'flowchart-elk' },
{ type: 'DIR', value: 'LR' },
])).not.toThrow();
});
});

View File

@@ -1,148 +0,0 @@
import { describe, it, expect } from 'vitest';
import { createLexerTestSuite } from './lexer-test-utils.js';
/**
* EDGE SYNTAX LEXER TESTS
*
* Extracted from flow-edges.spec.js and other edge-related tests
* Each test has a unique ID (3 letters + 3 digits) for easy identification
*/
describe('Edge Syntax Lexer Tests', () => {
const { runTest } = createLexerTestSuite();
it('EDG001: should tokenize "A-->B" correctly', () => {
expect(() =>
runTest('EDG001', 'A-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG002: should tokenize "A --- B" correctly', () => {
expect(() =>
runTest('EDG002', 'A --- B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '---' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG003: should tokenize "A-.-B" correctly', () => {
expect(() =>
runTest('EDG003', 'A-.-B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-.-' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG004: should tokenize "A===B" correctly', () => {
expect(() =>
runTest('EDG004', 'A===B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '===' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG005: should tokenize "A-.->B" correctly', () => {
expect(() =>
runTest('EDG005', 'A-.->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-.->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG006: should tokenize "A==>B" correctly', () => {
expect(() =>
runTest('EDG006', 'A==>B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '==>' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG007: should tokenize "A<-->B" correctly', () => {
expect(() =>
runTest('EDG007', 'A<-->B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '<-->' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG008: should tokenize "A-->|text|B" correctly', () => {
expect(() =>
runTest('EDG008', 'A-->|text|B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-->' },
{ type: 'PIPE', value: '|' },
{ type: 'textToken', value: 'text' },
{ type: 'PIPE', value: '|' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG009: should tokenize "A---|text|B" correctly', () => {
expect(() =>
runTest('EDG009', 'A---|text|B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '---' },
{ type: 'PIPE', value: '|' },
{ type: 'textToken', value: 'text' },
{ type: 'PIPE', value: '|' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG010: should tokenize "A-.-|text|B" correctly', () => {
expect(() =>
runTest('EDG010', 'A-.-|text|B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-.-' },
{ type: 'PIPE', value: '|' },
{ type: 'textToken', value: 'text' },
{ type: 'PIPE', value: '|' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG011: should tokenize "A==>|text|B" correctly', () => {
expect(() =>
runTest('EDG011', 'A==>|text|B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '==>' },
{ type: 'PIPE', value: '|' },
{ type: 'textToken', value: 'text' },
{ type: 'PIPE', value: '|' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
it('EDG012: should tokenize "A-.->|text|B" correctly', () => {
expect(() =>
runTest('EDG012', 'A-.->|text|B', [
{ type: 'NODE_STRING', value: 'A' },
{ type: 'LINK', value: '-.->' },
{ type: 'PIPE', value: '|' },
{ type: 'textToken', value: 'text' },
{ type: 'PIPE', value: '|' },
{ type: 'NODE_STRING', value: 'B' },
])
).not.toThrow();
});
});

Some files were not shown because too many files have changed in this diff Show More