diff --git a/.changeset/brave-memes-flash.md b/.changeset/brave-memes-flash.md new file mode 100644 index 000000000..720cd7202 --- /dev/null +++ b/.changeset/brave-memes-flash.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Support edge animation in hand drawn look diff --git a/.changeset/busy-mirrors-try.md b/.changeset/busy-mirrors-try.md new file mode 100644 index 000000000..7e5d3b632 --- /dev/null +++ b/.changeset/busy-mirrors-try.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Resolved parsing error where direction TD was not recognized within subgraphs diff --git a/.changeset/chilly-words-march.md b/.changeset/chilly-words-march.md new file mode 100644 index 000000000..54c0b4ebf --- /dev/null +++ b/.changeset/chilly-words-march.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Correct viewBox casing and make SVGs responsive diff --git a/.changeset/clean-wolves-turn.md b/.changeset/clean-wolves-turn.md deleted file mode 100644 index 7a44c1c16..000000000 --- a/.changeset/clean-wolves-turn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'mermaid': patch ---- - -fix: Render newlines as spaces in class diagrams diff --git a/.changeset/crazy-loops-matter.md b/.changeset/crazy-loops-matter.md deleted file mode 100644 index e6377a9e5..000000000 --- a/.changeset/crazy-loops-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'mermaid': patch ---- - -fix: Handle arrows correctly when auto number is enabled diff --git a/.changeset/curly-apes-prove.md b/.changeset/curly-apes-prove.md new file mode 100644 index 000000000..2acf3d1a3 --- /dev/null +++ b/.changeset/curly-apes-prove.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Improve participant parsing and prevent recursive loops on invalid syntax diff --git a/.changeset/hungry-baths-glow.md b/.changeset/hungry-baths-glow.md deleted file mode 100644 index b3084bcab..000000000 --- a/.changeset/hungry-baths-glow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'mermaid': minor ---- - -feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`. diff --git a/.changeset/hungry-guests-drive.md b/.changeset/hungry-guests-drive.md deleted file mode 100644 index 1b0e0a07b..000000000 --- a/.changeset/hungry-guests-drive.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'mermaid': minor -'@mermaid-js/layout-tidy-tree': minor -'@mermaid-js/layout-elk': minor ---- - -feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes diff --git a/.changeset/loud-results-melt.md b/.changeset/loud-results-melt.md new file mode 100644 index 000000000..7005750c6 --- /dev/null +++ b/.changeset/loud-results-melt.md @@ -0,0 +1,5 @@ +--- +'mermaid': minor +--- + +feat: Add half-arrowheads (solid & stick) and central connection support diff --git a/.changeset/revert-marked-dependency.md b/.changeset/revert-marked-dependency.md deleted file mode 100644 index aded58871..000000000 --- a/.changeset/revert-marked-dependency.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'mermaid': patch ---- - -chore: revert marked dependency from ^15.0.7 to ^16.0.0 - -- Reverted marked package version to ^16.0.0 for better compatibility -- This is a dependency update that maintains API compatibility -- All tests pass with the updated version diff --git a/.changeset/short-seals-sort.md b/.changeset/short-seals-sort.md new file mode 100644 index 000000000..db8309c7f --- /dev/null +++ b/.changeset/short-seals-sort.md @@ -0,0 +1,5 @@ +--- +'mermaid': minor +--- + +feat: allow to put notes in namespaces on classDiagram diff --git a/.changeset/slow-lemons-know.md b/.changeset/slow-lemons-know.md new file mode 100644 index 000000000..49eb48543 --- /dev/null +++ b/.changeset/slow-lemons-know.md @@ -0,0 +1,5 @@ +--- +'@mermaid': patch +--- + +fix: Mindmap breaking in ELK layout diff --git a/.changeset/sweet-games-build.md b/.changeset/sweet-games-build.md new file mode 100644 index 000000000..a71e3de25 --- /dev/null +++ b/.changeset/sweet-games-build.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix(er-diagram): prevent syntax error when using 'u', numbers, and decimals in node names diff --git a/.cspell/code-terms.txt b/.cspell/code-terms.txt index 8b549f888..5f72ea221 100644 --- a/.cspell/code-terms.txt +++ b/.cspell/code-terms.txt @@ -1,3 +1,5 @@ +!viewbox +# It should be viewBox # This file contains coding related terms ALPHANUM antiscript diff --git a/.cspell/libraries.txt b/.cspell/libraries.txt index feee10fd1..4fceed5bb 100644 --- a/.cspell/libraries.txt +++ b/.cspell/libraries.txt @@ -64,6 +64,7 @@ rscratch shiki Slidev sparkline +speccharts sphinxcontrib ssim stylis diff --git a/.cspell/mermaid-terms.txt b/.cspell/mermaid-terms.txt index 6900c15b0..45152a0ce 100644 --- a/.cspell/mermaid-terms.txt +++ b/.cspell/mermaid-terms.txt @@ -8,6 +8,7 @@ compositTitleSize cose curv doublecircle +elem elems gantt gitgraph diff --git a/.esbuild/util.ts b/.esbuild/util.ts index 3a0ec6b41..a3e2ffe55 100644 --- a/.esbuild/util.ts +++ b/.esbuild/util.ts @@ -71,6 +71,9 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => { const external: string[] = ['require', 'fs', 'path']; const outFileName = getFileName(name, options); + const { dependencies, version } = JSON.parse( + readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8') + ); const output: BuildOptions = buildOptions({ ...rest, absWorkingDir: resolve(__dirname, `../packages/${packageName}`), @@ -82,15 +85,13 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => { chunkNames: `chunks/${outFileName}/[name]-[hash]`, define: { // This needs to be stringified for esbuild - includeLargeFeatures: `${includeLargeFeatures}`, + 'injected.includeLargeFeatures': `${includeLargeFeatures}`, + 'injected.version': `'${version}'`, 'import.meta.vitest': 'undefined', }, }); if (core) { - const { dependencies } = JSON.parse( - readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8') - ); // Core build is used to generate file without bundled dependencies. // This is used by downstream projects to bundle dependencies themselves. // Ignore dependencies and any dependencies of dependencies diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a6400a86a..64de2eb66 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -26,8 +26,8 @@ jobs: strategy: fail-fast: false matrix: - language: ['javascript'] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + language: ['javascript', 'actions'] + # CodeQL supports [ 'actions', 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: @@ -36,7 +36,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/init@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21 with: config-file: ./.github/codeql/codeql-config.yml languages: ${{ matrix.language }} @@ -48,7 +48,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/autobuild@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -62,4 +62,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/analyze@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21 diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml index 6aaa91eb8..853818425 100644 --- a/.github/workflows/e2e-applitools.yml +++ b/.github/workflows/e2e-applitools.yml @@ -53,7 +53,7 @@ jobs: args: -X POST "$APPLITOOLS_SERVER_URL/api/externals/github/push?apiKey=$APPLITOOLS_API_KEY&CommitSha=$GITHUB_SHA&BranchName=${APPLITOOLS_BRANCH}$&ParentBranchName=$APPLITOOLS_PARENT_BRANCH" - name: Cypress run - uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16 id: cypress with: start: pnpm run dev diff --git a/.github/workflows/e2e-timings.yml b/.github/workflows/e2e-timings.yml index 21dbda293..21f6b4049 100644 --- a/.github/workflows/e2e-timings.yml +++ b/.github/workflows/e2e-timings.yml @@ -27,12 +27,12 @@ jobs: with: node-version-file: '.node-version' - name: Install dependencies - uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16 with: runTests: false - name: Cypress run - uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16 id: cypress with: install: false @@ -58,7 +58,7 @@ jobs: echo "EOF" >> $GITHUB_OUTPUT - name: Commit and create pull request - uses: peter-evans/create-pull-request@18e469570b1cf0dfc11d60ec121099f8ff3e617a + uses: peter-evans/create-pull-request@0edc001d28a2959cd7a6b505629f1d82f0a6e67d with: add-paths: | cypress/timings.json diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 56883b987..8fbf6d6f6 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -45,7 +45,7 @@ jobs: node-version-file: '.node-version' - name: Cache snapshots id: cache-snapshot - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: ./cypress/snapshots key: ${{ runner.os }}-snapshots-${{ env.targetHash }} @@ -59,7 +59,7 @@ jobs: - name: Install dependencies if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }} - uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16 with: # just perform install runTests: false @@ -95,13 +95,13 @@ jobs: # These cached snapshots are downloaded, providing the reference snapshots. - name: Cache snapshots id: cache-snapshot - uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 + uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: ./cypress/snapshots key: ${{ runner.os }}-snapshots-${{ env.targetHash }} - name: Install dependencies - uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16 with: runTests: false @@ -117,7 +117,7 @@ jobs: # Install NPM dependencies, cache them correctly # and run all Cypress tests - name: Cypress run - uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12 + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16 id: cypress with: install: false diff --git a/.github/workflows/link-checker.yml b/.github/workflows/link-checker.yml index f855ed23b..ce43c2ed7 100644 --- a/.github/workflows/link-checker.yml +++ b/.github/workflows/link-checker.yml @@ -32,7 +32,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Restore lychee cache - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 with: path: .lycheecache key: cache-lychee-${{ github.sha }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 649c40034..ece84ac20 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,11 +36,10 @@ jobs: - name: Create Release Pull Request or Publish to npm id: changesets - uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9 + uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc # v1.4.10 with: version: pnpm changeset:version publish: pnpm changeset:publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_CONFIG_PROVENANCE: true diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 4901b3781..3177ca15a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -20,18 +20,18 @@ jobs: with: persist-credentials: false - name: Run analysis - uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif publish_results: true - name: Upload artifact - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: SARIF file path: results.sarif retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/upload-sarif@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21 with: sarif_file: results.sarif diff --git a/.github/workflows/update-browserlist.yml b/.github/workflows/update-browserlist.yml index 94de12ad3..54ef39b11 100644 --- a/.github/workflows/update-browserlist.yml +++ b/.github/workflows/update-browserlist.yml @@ -19,7 +19,7 @@ jobs: message: 'chore: update browsers list' push: false - name: Create Pull Request - uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6 + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: branch: update-browserslist title: Update Browserslist diff --git a/.github/workflows/validate-lockfile.yml b/.github/workflows/validate-lockfile.yml index 59a6df96d..dcfb255b6 100644 --- a/.github/workflows/validate-lockfile.yml +++ b/.github/workflows/validate-lockfile.yml @@ -1,7 +1,7 @@ name: Validate pnpm-lock.yaml on: - pull_request: + pull_request_target: paths: - 'pnpm-lock.yaml' - '**/package.json' @@ -15,13 +15,8 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} - name: Validate pnpm-lock.yaml entries id: validate # give this step an ID so we can reference its outputs @@ -55,16 +50,41 @@ jobs: exit 1 fi + - name: Find existing lockfile validation comment + if: always() + uses: peter-evans/find-comment@v3 + id: find-comment + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: 'Lockfile Validation Failed' + - name: Comment on PR if validation failed if: failure() - uses: peter-evans/create-or-update-comment@v4 + uses: peter-evans/create-or-update-comment@v5 with: token: ${{ secrets.GITHUB_TOKEN }} issue-number: ${{ github.event.pull_request.number }} + comment-id: ${{ steps.find-comment.outputs.comment-id }} + edit-mode: replace body: | + ❌ **Lockfile Validation Failed** + The following issue(s) were detected: ${{ steps.validate.outputs.errors }} Please address these and push an update. _Posted automatically by GitHub Actions_ + + - name: Delete comment if validation passed + if: success() && steps.find-comment.outputs.comment-id != '' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.find-comment.outputs.comment-id }}, + }); diff --git a/.vite/build.ts b/.vite/build.ts index 480dd6b30..d59f0fac3 100644 --- a/.vite/build.ts +++ b/.vite/build.ts @@ -78,6 +78,8 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions) }, define: { 'import.meta.vitest': 'undefined', + 'injected.includeLargeFeatures': 'true', + 'injected.version': `'0.0.0'`, }, resolve: { extensions: [], @@ -94,10 +96,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions) }), ...visualizerOptions(packageName, core), ], - define: { - // Needs to be string - includeLargeFeatures: 'true', - }, }; if (watch && config.build) { diff --git a/Dockerfile b/Dockerfile index 533604407..d7e6c8f84 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ USER 0:0 RUN corepack enable \ && corepack enable pnpm -RUN apk add --no-cache git~=2.43.4 \ +RUN apk add --no-cache git~=2.43 \ && git config --add --system safe.directory /mermaid ENV NODE_OPTIONS="--max_old_space_size=8192" diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts index ab4bbef64..0332178f6 100644 --- a/cypress/helpers/util.ts +++ b/cypress/helpers/util.ts @@ -6,6 +6,7 @@ interface CypressConfig { listUrl?: boolean; listId?: string; name?: string; + screenshot?: boolean; } type CypressMermaidConfig = MermaidConfig & CypressConfig; @@ -90,7 +91,7 @@ export const renderGraph = ( export const openURLAndVerifyRendering = ( url: string, - options: CypressMermaidConfig, + { screenshot = true, ...options }: CypressMermaidConfig, validation?: any ): void => { const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); @@ -98,12 +99,16 @@ export const openURLAndVerifyRendering = ( cy.visit(url); cy.window().should('have.property', 'rendered', true); cy.get('svg').should('be.visible'); + // cspell:ignore viewbox + cy.get('svg').should('not.have.attr', 'viewbox'); if (validation) { cy.get('svg').should(validation); } - verifyScreenshot(name); + if (screenshot) { + verifyScreenshot(name); + } }; export const verifyScreenshot = (name: string): void => { diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js index b48a197a4..a699e03a7 100644 --- a/cypress/integration/other/configuration.spec.js +++ b/cypress/integration/other/configuration.spec.js @@ -98,12 +98,12 @@ describe('Configuration', () => { it('should handle arrowMarkerAbsolute set to true', () => { renderGraph( `flowchart TD - A[Christmas] -->|Get money| B(Go shopping) - B --> C{Let me think} - C -->|One| D[Laptop] - C -->|Two| E[iPhone] - C -->|Three| F[fa:fa-car Car] - `, + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, { arrowMarkerAbsolute: true, } @@ -113,8 +113,7 @@ describe('Configuration', () => { cy.get('path') .first() .should('have.attr', 'marker-end') - .should('exist') - .and('include', 'url(http\\:\\/\\/localhost'); + .and('include', 'url(http://localhost'); }); }); it('should not taint the initial configuration when using multiple directives', () => { diff --git a/cypress/integration/rendering/classDiagram-v2.spec.js b/cypress/integration/rendering/classDiagram-v2.spec.js index 0c5dbc04b..f54768d9a 100644 --- a/cypress/integration/rendering/classDiagram-v2.spec.js +++ b/cypress/integration/rendering/classDiagram-v2.spec.js @@ -562,6 +562,20 @@ class C13["With Città foreign language"] ` ); }); + it('should add notes in namespaces', function () { + imgSnapshotTest( + ` + classDiagram + note "This is a outer note" + note for C1 "This is a outer note for C1" + namespace Namespace1 { + note "This is a inner note" + note for C1 "This is a inner note for C1" + class C1 + } + ` + ); + }); it('should render a simple class diagram with no members', () => { imgSnapshotTest( ` diff --git a/cypress/integration/rendering/classDiagram-v3.spec.js b/cypress/integration/rendering/classDiagram-v3.spec.js index 0e66bf757..42a2ee955 100644 --- a/cypress/integration/rendering/classDiagram-v3.spec.js +++ b/cypress/integration/rendering/classDiagram-v3.spec.js @@ -709,6 +709,20 @@ class C13["With Città foreign language"] ` ); }); + it('should add notes in namespaces', function () { + imgSnapshotTest( + ` + classDiagram + note "This is a outer note" + note for C1 "This is a outer note for C1" + namespace Namespace1 { + note "This is a inner note" + note for C1 "This is a inner note for C1" + class C1 + } + ` + ); + }); it('should render a simple class diagram with no members', () => { imgSnapshotTest( ` diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js index 8f6193f96..6df44932d 100644 --- a/cypress/integration/rendering/erDiagram.spec.js +++ b/cypress/integration/rendering/erDiagram.spec.js @@ -369,4 +369,92 @@ ORDER ||--|{ LINE-ITEM : contains ); }); }); + + describe('Special characters and numbers syntax', () => { + it('should render ER diagram with numeric entity names', () => { + imgSnapshotTest( + ` + erDiagram + 1 ||--|| ORDER : places + ORDER ||--|{ 2 : contains + 2 ||--o{ 3.5 : references + `, + { logLevel: 1 } + ); + }); + + it('should render ER diagram with "u" character in entity names and cardinality', () => { + imgSnapshotTest( + ` + erDiagram + CUSTOMER ||--|| u : has + u ||--|| ORDER : places + PROJECT u--o{ TEAM_MEMBER : "parent" + `, + { logLevel: 1 } + ); + }); + + it('should render ER diagram with decimal numbers in relationships', () => { + imgSnapshotTest( + ` + erDiagram + 2.5 ||--|| 1.5 : has + CUSTOMER ||--o{ 3.14 : references + 1.0 ||--|{ ORDER : contains + `, + { logLevel: 1 } + ); + }); + + it('should render ER diagram with numeric entity names and attributes', () => { + imgSnapshotTest( + ` + erDiagram + 1 { + string name + int value + } + 1 ||--|| ORDER : places + ORDER { + float price + string description + } + `, + { logLevel: 1 } + ); + }); + + it('should render complex ER diagram with mixed special entity names', () => { + imgSnapshotTest( + ` + erDiagram + CUSTOMER ||--o{ 1 : places + 1 ||--|{ u : contains + 1.5 + u ||--|| 2.5 : processes + 2.5 { + string id + float value + } + u { + varchar(50) name + int count + } + `, + { logLevel: 1 } + ); + }); + it('should render ER diagram with standalone numeric entities', () => { + imgSnapshotTest( + `erDiagram + PRODUCT ||--o{ ORDER-ITEM : has + 1.5 + u + 1 + `, + { logLevel: 1 } + ); + }); + }); }); diff --git a/cypress/integration/rendering/flowchart-elk.spec.js b/cypress/integration/rendering/flowchart-elk.spec.js index 312e1d5b4..fac4f3b4b 100644 --- a/cypress/integration/rendering/flowchart-elk.spec.js +++ b/cypress/integration/rendering/flowchart-elk.spec.js @@ -109,7 +109,7 @@ describe('Flowchart ELK', () => { const style = svg.attr('style'); expect(style).to.match(/^max-width: [\d.]+px;$/); const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join('')); - verifyNumber(maxWidthValue, 380); + verifyNumber(maxWidthValue, 380, 15); }); }); it('8-elk: should render a flowchart when useMaxWidth is false', () => { @@ -128,7 +128,7 @@ describe('Flowchart ELK', () => { const width = parseFloat(svg.attr('width')); // use within because the absolute value can be slightly different depending on the environment ±5% // expect(height).to.be.within(446 * 0.95, 446 * 1.05); - verifyNumber(width, 380); + verifyNumber(width, 380, 15); expect(svg).to.not.have.attr('style'); }); }); diff --git a/cypress/integration/rendering/flowchart-handDrawn.spec.js b/cypress/integration/rendering/flowchart-handDrawn.spec.js index 49c55c628..d3ca1d1f1 100644 --- a/cypress/integration/rendering/flowchart-handDrawn.spec.js +++ b/cypress/integration/rendering/flowchart-handDrawn.spec.js @@ -1029,4 +1029,19 @@ graph TD } ); }); + + it('FDH49: should add edge animation', () => { + renderGraph( + ` + flowchart TD + A(["Start"]) L_A_B_0@--> B{"Decision"} + B --> C["Option A"] & D["Option B"] + style C stroke-width:4px,stroke-dasharray: 5 + L_A_B_0@{ animation: slow } + L_B_D_0@{ animation: fast }`, + { look: 'handDrawn', screenshot: false } + ); + cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow'); + cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast'); + }); }); diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js index 8c6cde57a..5ef32c269 100644 --- a/cypress/integration/rendering/flowchart-v2.spec.js +++ b/cypress/integration/rendering/flowchart-v2.spec.js @@ -1186,4 +1186,17 @@ end imgSnapshotTest(graph, { htmlLabels: false }); }); }); + + it('V2 - 17: should apply class def colour to edge label', () => { + imgSnapshotTest( + ` graph LR + id1(Start) link@-- "Label" -->id2(Stop) + style id1 fill:#f9f,stroke:#333,stroke-width:4px + +class id2 myClass +classDef myClass fill:#bbf,stroke:#f66,stroke-width:2px,color:white,stroke-dasharray: 5 5 +class link myClass +` + ); + }); }); diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index 40713ac4e..5e1984377 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -774,6 +774,21 @@ describe('Graph', () => { expect(svg).to.not.have.attr('style'); }); }); + it('40: should add edge animation', () => { + renderGraph( + ` + flowchart TD + A(["Start"]) L_A_B_0@--> B{"Decision"} + B --> C["Option A"] & D["Option B"] + style C stroke-width:4px,stroke-dasharray: 5 + L_A_B_0@{ animation: slow } + L_B_D_0@{ animation: fast }`, + { screenshot: false } + ); + // Verify animation classes are applied to both edges + cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow'); + cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast'); + }); it('58: handle styling with style expressions', () => { imgSnapshotTest( ` @@ -973,4 +988,19 @@ graph TD } ); }); + + it('70: should render a subgraph with direction TD', () => { + imgSnapshotTest( + ` + flowchart LR + subgraph A + direction TD + a --> b + end + `, + { + fontFamily: 'courier', + } + ); + }); }); diff --git a/cypress/integration/rendering/gantt.spec.js b/cypress/integration/rendering/gantt.spec.js index 32dbcb4d9..72cb6ea29 100644 --- a/cypress/integration/rendering/gantt.spec.js +++ b/cypress/integration/rendering/gantt.spec.js @@ -803,4 +803,34 @@ describe('Gantt diagram', () => { {} ); }); + it('should handle numeric timestamps with dateFormat x', () => { + imgSnapshotTest( + ` + gantt + title Process time profile (ms) + dateFormat x + axisFormat %L + tickInterval 250millisecond + + section Pipeline + Parse JSON p1: 000, 120 + `, + {} + ); + }); + it('should handle numeric timestamps with dateFormat X', () => { + imgSnapshotTest( + ` + gantt + title Process time profile (ms) + dateFormat X + axisFormat %L + tickInterval 250millisecond + + section Pipeline + Parse JSON p1: 000, 120 + `, + {} + ); + }); }); diff --git a/cypress/integration/rendering/sequencediagram-v2.spec.js b/cypress/integration/rendering/sequencediagram-v2.spec.js index f1c2aafbd..42db4001d 100644 --- a/cypress/integration/rendering/sequencediagram-v2.spec.js +++ b/cypress/integration/rendering/sequencediagram-v2.spec.js @@ -655,5 +655,126 @@ describe('Sequence Diagram Special Cases', () => { expect(svg).to.not.have.attr('style'); }); }); + + describe('Central Connection Rendering Tests', () => { + it('should render central connection circles on actor vertical lines', () => { + imgSnapshotTest( + `sequenceDiagram + participant Alice + participant Bob + participant Charlie + Alice ()->>() Bob: Central connection + Bob ()-->> Charlie: Reverse central connection + Charlie ()<<-->>() Alice: Dual central connection`, + { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } } + ); + }); + + it('should render central connections with different arrow types', () => { + imgSnapshotTest( + `sequenceDiagram + participant Alice + participant Bob + Alice ()->>() Bob: Solid open arrow + Alice ()-->>() Bob: Dotted open arrow + Alice ()-x() Bob: Solid cross + Alice ()--x() Bob: Dotted cross + Alice ()->() Bob: Solid arrow`, + { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } } + ); + }); + + it('should render central connections with bidirectional arrows', () => { + imgSnapshotTest( + `sequenceDiagram + participant Alice + participant Bob + Alice ()<<->>() Bob: Bidirectional solid + Alice ()<<-->>() Bob: Bidirectional dotted`, + { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } } + ); + }); + + it('should render central connections with activations', () => { + imgSnapshotTest( + `sequenceDiagram + participant Alice + participant Bob + participant Charlie + Alice ()->>() Bob: Activate Bob + activate Bob + Bob ()-->> Charlie: Message to Charlie + Bob ()->>() Alice: Response to Alice + deactivate Bob`, + { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } } + ); + }); + + it('should render central connections mixed with normal messages', () => { + imgSnapshotTest( + `sequenceDiagram + participant Alice + participant Bob + participant Charlie + Alice ->> Bob: Normal message + Bob ()->>() Charlie: Central connection + Charlie -->> Alice: Normal dotted message + Alice ()<<-->>() Bob: Dual central connection + Bob -x Charlie: Normal cross message`, + { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } } + ); + }); + + it('should render central connections with notes', () => { + imgSnapshotTest( + `sequenceDiagram + participant Alice + participant Bob + participant Charlie + Alice ()->>() Bob: Central connection + Note over Alice,Bob: Central connection note + Bob ()-->> Charlie: Reverse central connection + Note right of Charlie: Response note + Charlie ()<<-->>() Alice: Dual central connection`, + { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } } + ); + }); + + it('should render central connections with loops and alternatives', () => { + imgSnapshotTest( + `sequenceDiagram + participant Alice + participant Bob + participant Charlie + loop Every minute + Alice ()->>() Bob: Central heartbeat + Bob ()-->> Charlie: Forward heartbeat + end + alt Success + Charlie ()<<-->>() Alice: Success response + else Failure + Charlie ()-x() Alice: Failure response + end`, + { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } } + ); + }); + + it('should render central connections with different participant types', () => { + imgSnapshotTest( + `sequenceDiagram + participant Alice + actor Bob + participant Charlie@{"type":"boundary"} + participant David@{"type":"control"} + participant Eve@{"type":"entity"} + Alice ()->>() Bob: To actor + Bob ()-->> Charlie: To boundary + Charlie ()->>() David: To control + David ()<<-->>() Eve: To entity + Eve ()-x() Alice: Back to participant`, + { look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } } + ); + }); + }); }); }); diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js index 6709b557c..0ec913a8c 100644 --- a/cypress/integration/rendering/sequencediagram.spec.js +++ b/cypress/integration/rendering/sequencediagram.spec.js @@ -1053,4 +1053,167 @@ describe('Sequence diagram', () => { ]); }); }); + describe('render new arrow type', () => { + it('should render Solid half arrow top', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice -|\\ John: Hello John, how are you? + Alice-|\\ John: Hi Alice, I can hear you! + Alice -|\\ John: Test + ` + ); + }); + it('should render Solid half arrow bottom', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice-|/John: Hello John, how are you? + Alice-|/John: Hi Alice, I can hear you! + Alice-|/John: Test + ` + ); + }); + + it('should render Stick half arrow top ', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice-\\\\John: Hello John, how are you? + Alice-\\\\John: Hi Alice, I can hear you! + Alice-\\\\John: Test + ` + ); + }); + it('should render Stick half arrow bottom ', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice-//John: Hello John, how are you? + Alice-//John: Hi Alice, I can hear you! + Alice-//John: Test + ` + ); + }); + it('should render Solid half arrow top reverse ', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice/|-John: Hello Alice, how are you? + Alice/|-John: Hi Alice, I can hear you! + Alice/|-John: Test + + ` + ); + }); + + it('should render Solid half arrow bottom reverse ', () => { + imgSnapshotTest( + `sequenceDiagram + Alice \\|- John: Hello Alice, how are you? + Alice \\|- John: Hi Alice, I can hear you! + Alice \\|- John: Test` + ); + }); + + it('should render Stick half arrow top reverse ', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice //-John: Hello Alice, how are you? + Alice //-John: Hi Alice, I can hear you! + Alice //-John: Test` + ); + }); + + it('should render Stick half arrow bottom reverse ', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice \\\\-John: Hello Alice, how are you? + Alice \\\\-John: Hi Alice, I can hear you! + Alice \\\\-John: Test` + ); + }); + + it('should render Solid half arrow top dotted', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice --|\\John: Hello John, how are you? + Alice --|\\John: Hi Alice, I can hear you! + Alice --|\\John: Test` + ); + }); + + it('should render Solid half arrow bottom dotted', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice --|/John: Hello John, how are you? + Alice --|/John: Hi Alice, I can hear you! + Alice --|/John: Test` + ); + }); + + it('should render Stick half arrow top dotted', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice--\\\\John: Hello John, how are you? + Alice--\\\\John: Hi Alice, I can hear you! + Alice--\\\\John: Test` + ); + }); + + it('should render Stick half arrow bottom dotted', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice--//John: Hello John, how are you? + Alice--//John: Hi Alice, I can hear you! + Alice--//John: Test` + ); + }); + + it('should render Solid half arrow top reverse dotted', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice/|--John: Hello Alice, how are you? + Alice/|--John: Hi Alice, I can hear you! + Alice/|--John: Test` + ); + }); + + it('should render Solid half arrow bottom reverse dotted', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice\\|--John: Hello Alice, how are you? + Alice\\|--John: Hi Alice, I can hear you! + Alice\\|--John: Test` + ); + }); + + it('should render Stick half arrow top reverse dotted ', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice//--John: Hello Alice, how are you? + Alice//--John: Hi Alice, I can hear you! + Alice//--John: Test` + ); + }); + + it('should render Stick half arrow bottom reverse dotted ', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice\\\\--John: Hello Alice, how are you? + Alice\\\\--John: Hi Alice, I can hear you! + Alice\\\\--John: Test` + ); + }); + }); }); diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index fc33a58b4..90f40003a 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -32,26 +32,8 @@ href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap" rel="stylesheet" /> - - -