diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 49e1aaaa6..9b3426ce8 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -63,6 +63,17 @@ module.exports = {
minimumDescriptionLength: 10,
},
],
+ '@typescript-eslint/naming-convention': [
+ 'error',
+ {
+ selector: 'typeLike',
+ format: ['PascalCase'],
+ custom: {
+ regex: '^I[A-Z]',
+ match: false,
+ },
+ },
+ ],
'json/*': ['error', 'allowComments'],
'@cspell/spellchecker': [
'error',
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 7e0c78ff1..6be6f3b5d 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -3,9 +3,9 @@ contact_links:
- name: GitHub Discussions
url: https://github.com/mermaid-js/mermaid/discussions
about: Ask the Community questions or share your own graphs in our discussions.
- - name: Slack
- url: https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE
- about: Join our Community on Slack for Help and a casual chat.
+ - name: Discord
+ url: https://discord.gg/AgrbSrBer3
+ about: Join our Community on Discord for Help and a casual chat.
- name: Documentation
url: https://mermaid.js.org
about: Read our documentation for all that Mermaid.js can offer.
diff --git a/.github/lychee.toml b/.github/lychee.toml
index b13e53616..4af304a99 100644
--- a/.github/lychee.toml
+++ b/.github/lychee.toml
@@ -34,8 +34,8 @@ exclude = [
# Don't check files that are generated during the build via `pnpm docs:code`
'packages/mermaid/src/docs/config/setup/*',
-# Ignore slack invite
-"https://join.slack.com/"
+# Ignore Discord invite
+"https://discord.gg"
]
# Exclude all private IPs from checking.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index ff34d24fd..f20204a71 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -14,5 +14,5 @@ Make sure you
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
- [ ] :computer: have added necessary unit/e2e tests.
-- [ ] :notebook: have added documentation. Make sure [`MERMAID_RELEASE_VERSION`](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/docs/community/development.md#3-update-documentation) is used for all new features.
+- [ ] :notebook: have added documentation. Make sure [`MERMAID_RELEASE_VERSION`](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/docs/community/contributing.md#update-documentation) is used for all new features.
- [ ] :bookmark: targeted `develop` branch
diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index acfb1887e..87607bc2f 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -24,7 +24,7 @@ jobs:
uses: actions/setup-node@v4
with:
cache: pnpm
- node-version: 18
+ node-version-file: '.node-version'
- name: Install Packages
run: pnpm install --frozen-lockfile
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 605dea9ab..e0ab76607 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -15,20 +15,17 @@ permissions:
jobs:
build-mermaid:
runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [18.x]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json
- - name: Setup Node.js ${{ matrix.node-version }}
+ - name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
- node-version: ${{ matrix.node-version }}
+ node-version-file: '.node-version'
- name: Install Packages
run: |
diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml
index fd32e59ad..1238fe371 100644
--- a/.github/workflows/e2e-applitools.yml
+++ b/.github/workflows/e2e-applitools.yml
@@ -21,9 +21,9 @@ env:
jobs:
e2e-applitools:
runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [18.x]
+ container:
+ image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
+ options: --user 1001
steps:
- if: ${{ ! env.USE_APPLI }}
name: Warn if not using Applitools
@@ -35,10 +35,10 @@ jobs:
- uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json
- - name: Setup Node.js ${{ matrix.node-version }}
+ - name: Setup Node.js
uses: actions/setup-node@v4
with:
- node-version: ${{ matrix.node-version }}
+ node-version-file: '.node-version'
- if: ${{ env.USE_APPLI }}
name: Notify applitools of new batch
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 71806a9c4..b97686db4 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -1,20 +1,71 @@
+# We use github cache to save snapshots between runs.
+# For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
+# If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
+# These are then downloaded before running the E2E, providing the reference snapshots.
+# If there are any errors, the diff image is uploaded to artifacts, and the user is notified.
+
name: E2E
on:
push:
+ branches-ignore:
+ - 'gh-readonly-queue/**'
pull_request:
merge_group:
permissions:
contents: read
+env:
+ # For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
+ targetHash: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || (github.event.before == '0000000000000000000000000000000000000000' && 'develop' || github.event.before) }}
+
jobs:
+ cache:
+ runs-on: ubuntu-latest
+ container:
+ image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
+ options: --user 1001
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v2
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: '.node-version'
+ - name: Cache snapshots
+ id: cache-snapshot
+ uses: actions/cache@v4
+ with:
+ save-always: true
+ path: ./cypress/snapshots
+ key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
+
+ # If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
+ - name: Switch to base branch
+ if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ env.targetHash }}
+
+ - name: Cypress run
+ uses: cypress-io/github-action@v4
+ id: cypress-snapshot-gen
+ if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
+ with:
+ start: pnpm run dev
+ wait-on: 'http://localhost:9000'
+ browser: chrome
+
e2e:
runs-on: ubuntu-latest
+ container:
+ image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
+ options: --user 1001
+ needs: cache
strategy:
fail-fast: false
matrix:
- node-version: [18.x]
containers: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
@@ -22,10 +73,18 @@ jobs:
- uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json
- - name: Setup Node.js ${{ matrix.node-version }}
+ - name: Setup Node.js
uses: actions/setup-node@v4
with:
- node-version: ${{ matrix.node-version }}
+ node-version-file: '.node-version'
+
+ # These cached snapshots are downloaded, providing the reference snapshots.
+ - name: Cache snapshots
+ id: cache-snapshot
+ uses: actions/cache/restore@v3
+ with:
+ path: ./cypress/snapshots
+ key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
@@ -38,6 +97,7 @@ jobs:
with:
start: pnpm run dev:coverage
wait-on: 'http://localhost:9000'
+ browser: chrome
# Disable recording if we don't have an API key
# e.g. if this action was run from a fork
record: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
@@ -46,6 +106,7 @@ jobs:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
VITEST_COVERAGE: true
CYPRESS_COMMIT: ${{ github.sha }}
+
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v3
# Run step only pushes to develop and pull_requests
@@ -57,9 +118,55 @@ jobs:
fail_ci_if_error: false
verbose: true
token: 6845cc80-77ee-4e17-85a1-026cd95e0766
+
+ # We upload the artifacts into numbered archives to prevent overwriting
- name: Upload Artifacts
- uses: actions/upload-artifact@v3
- if: ${{ failure() && steps.cypress.conclusion == 'failure' }}
+ uses: actions/upload-artifact@v4
+ if: ${{ always() }}
+ with:
+ name: snapshots-${{ matrix.containers }}
+ retention-days: 1
+ path: ./cypress/snapshots
+
+ combineArtifacts:
+ needs: e2e
+ runs-on: ubuntu-latest
+ if: ${{ always() }}
+ steps:
+ # Download all snapshot artifacts and merge them into a single folder
+ - name: Download All Artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: snapshots
+ pattern: snapshots-*
+ merge-multiple: true
+
+ # For successful push events, we save the snapshots cache
+ - name: Save snapshots cache
+ id: cache-upload
+ if: ${{ github.event_name == 'push' && needs.e2e.result != 'failure' }}
+ uses: actions/cache/save@v3
+ with:
+ path: ./snapshots
+ key: ${{ runner.os }}-snapshots-${{ github.event.after }}
+
+ - name: Flatten images to a folder
+ if: ${{ needs.e2e.result == 'failure' }}
+ run: |
+ mkdir errors
+ cd snapshots
+ find . -mindepth 2 -type d -name "*__diff_output__*" -exec sh -c 'mv "$0"/*.png ../errors/' {} \;
+
+ - name: Upload Error snapshots
+ if: ${{ needs.e2e.result == 'failure' }}
+ uses: actions/upload-artifact@v4
+ id: upload-artifacts
with:
name: error-snapshots
- path: cypress/snapshots/**/__diff_output__/*
+ retention-days: 10
+ path: errors/
+
+ - name: Notify Users
+ if: ${{ needs.e2e.result == 'failure' }}
+ run: |
+ echo "::error title=Visual tests failed::You can view images that failed by downloading the error-snapshots artifact: ${{ steps.upload-artifacts.outputs.artifact-url }}"
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index f0c5560a1..8f5995d71 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -16,20 +16,17 @@ permissions:
jobs:
lint:
runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [18.x]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json
- - name: Setup Node.js ${{ matrix.node-version }}
+ - name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
- node-version: ${{ matrix.node-version }}
+ node-version-file: '.node-version'
- name: Install Packages
run: |
diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml
index 05cd68aff..6efd90c7f 100644
--- a/.github/workflows/publish-docs.yml
+++ b/.github/workflows/publish-docs.yml
@@ -31,7 +31,7 @@ jobs:
uses: actions/setup-node@v4
with:
cache: pnpm
- node-version: 18
+ node-version-file: '.node-version'
- name: Install Packages
run: pnpm install --frozen-lockfile
diff --git a/.github/workflows/release-preview-publish.yml b/.github/workflows/release-preview-publish.yml
index c6503847d..c763430b0 100644
--- a/.github/workflows/release-preview-publish.yml
+++ b/.github/workflows/release-preview-publish.yml
@@ -19,7 +19,7 @@ jobs:
uses: actions/setup-node@v4
with:
cache: pnpm
- node-version: 18.x
+ node-version-file: '.node-version'
- name: Install Packages
run: |
diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml
index 69ef74940..dce461cf5 100644
--- a/.github/workflows/release-publish.yml
+++ b/.github/workflows/release-publish.yml
@@ -14,11 +14,11 @@ jobs:
- uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json
- - name: Setup Node.js v18
+ - name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
- node-version: 18.x
+ node-version-file: '.node-version'
- name: Install Packages
run: |
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index a18b31c9c..7160ecc5f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -8,20 +8,17 @@ permissions:
jobs:
unit-test:
runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [18.x]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json
- - name: Setup Node.js ${{ matrix.node-version }}
+ - name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
- node-version: ${{ matrix.node-version }}
+ node-version-file: '.node-version'
- name: Install Packages
run: |
diff --git a/.github/workflows/update-browserlist.yml b/.github/workflows/update-browserlist.yml
index 0a83df795..f4fa2a982 100644
--- a/.github/workflows/update-browserlist.yml
+++ b/.github/workflows/update-browserlist.yml
@@ -9,10 +9,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - run: npx browserslist@latest --update-db
+ - uses: pnpm/action-setup@v2
+ - run: npx update-browserslist-db@latest
- name: Commit changes
uses: EndBug/add-and-commit@v9
with:
author_name: ${{ github.actor }}
author_email: ${{ github.actor }}@users.noreply.github.com
message: 'chore: update browsers list'
+ push: false
+ - name: Create Pull Request
+ uses: peter-evans/create-pull-request@v5
+ with:
+ branch: update-browserslist
+ title: Update Browserslist
diff --git a/.gitignore b/.gitignore
index 6a1cc85e5..e6728b03f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,4 @@ stats/
demos/dev/**
!/demos/dev/example.html
+tsx-0/**
diff --git a/.node-version b/.node-version
new file mode 100644
index 000000000..7ea6a59d3
--- /dev/null
+++ b/.node-version
@@ -0,0 +1 @@
+v20.11.0
diff --git a/.vite/jsonSchemaPlugin.ts b/.vite/jsonSchemaPlugin.ts
index ad3d9863d..dd9af8cc5 100644
--- a/.vite/jsonSchemaPlugin.ts
+++ b/.vite/jsonSchemaPlugin.ts
@@ -25,6 +25,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [
'gitGraph',
'c4',
'sankey',
+ 'block',
] as const;
/**
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 3142c5760..000000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,78 +0,0 @@
-# Contributing
-
-Please read in detail about how to contribute documentation and code on the [Mermaid documentation site.](https://mermaid-js.github.io/mermaid/#/development)
-
----
-
-# Mermaid contribution cheat-sheet
-
-## Requirements
-
-- [volta](https://volta.sh/) to manage node versions.
-- [Node.js](https://nodejs.org/en/). `volta install node`
-- [pnpm](https://pnpm.io/) package manager. `volta install pnpm`
-
-## Development Installation
-
-If you don't have direct access to push to mermaid repositories, make a fork first. Then clone. Or clone directly from mermaid-js:
-
-```bash
-git clone git@github.com:mermaid-js/mermaid.git
-cd mermaid
-```
-
-Install required packages:
-
-```bash
-# npx is required for first install as volta support for pnpm is not added yet.
-npx pnpm install
-pnpm test # run unit tests
-pnpm dev # starts a dev server
-```
-
-Open
- 📖 Documentation | 🚀 Getting Started | 🌐 CDN | 🙌 Join Us + 📖 Documentation | 🚀 Getting Started | 🌐 CDN | 🙌 Join Us
简体中文
@@ -33,7 +33,7 @@ Try Live Editor previews of future releases:
@@ -74,12 +74,12 @@ Mermaid addresses this problem by enabling users to create easily modifiable dia
Mermaid allows even non-programmers to easily create detailed diagrams through the [Mermaid Live Editor](https://mermaid.live/).
-For video tutorials, visit our [Tutorials](./docs/config/Tutorials.md) page.
+For video tutorials, visit our [Tutorials](./docs/ecosystem/tutorials.md) page.
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations-community.md).
You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) as well many of your other favorite applications—check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations-community.md).
-For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/intro/getting-started.md), [Usage](./docs/config/usage.md) and [Tutorials](./docs/config/Tutorials.md).
+For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/intro/getting-started.md), [Usage](./docs/config/usage.md) and [Tutorials](./docs/ecosystem/tutorials.md).
In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 98975ea33..667a8113a 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -15,7 +15,7 @@ Mermaid
实时编辑器!
- 📖 文档 | 🚀 入门 | 🌐 CDN | 🙌 加入我们 + 📖 文档 | 🚀 入门 | 🌐 CDN | 🙌 加入我们
English
@@ -34,7 +34,7 @@ Mermaid
[](https://app.codecov.io/github/mermaid-js/mermaid/tree/develop)
[](https://www.jsdelivr.com/package/npm/mermaid)
[](https://www.npmjs.com/package/mermaid)
-[](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
+[](https://discord.gg/AgrbSrBer3)
[](https://twitter.com/mermaidjs_)
@@ -57,9 +57,9 @@ Mermaid 是一个基于 Javascript 的图表绘制工具,通过解析类 Markd
Mermaid 通过允许用户创建便于修改的图表来解决这一难题,它也可以作为生产脚本(或其他代码)的一部分。
Mermaid 甚至能让非程序员也能通过 [Mermaid Live Editor](https://mermaid.live/) 轻松创建详细的图表。
-你可以访问 [教程](./docs/config/Tutorials.md) 来查看 Live Editor 的视频教程,也可以查看 [Mermaid 的集成和使用](./docs/ecosystem/integrations-community.md) 这个清单来检查你的文档工具是否已经集成了 Mermaid 支持。
+你可以访问 [教程](./docs/ecosystem/tutorials.md) 来查看 Live Editor 的视频教程,也可以查看 [Mermaid 的集成和使用](./docs/ecosystem/integrations-community.md) 这个清单来检查你的文档工具是否已经集成了 Mermaid 支持。
-如果想要查看关于 Mermaid 更详细的介绍及基础使用方式,可以查看 [入门指引](./docs/intro/getting-started.md), [用法](./docs/config/usage.md) 和 [教程](./docs/config/Tutorials.md).
+如果想要查看关于 Mermaid 更详细的介绍及基础使用方式,可以查看 [入门指引](./docs/intro/getting-started.md), [用法](./docs/config/usage.md) 和 [教程](./docs/ecosystem/tutorials.md).
diff --git a/applitools.config.js b/applitools.config.js
deleted file mode 100644
index 4cf02220a..000000000
--- a/applitools.config.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/no-var-requires
-const { defineConfig } = require('cypress');
-
-module.exports = defineConfig({
- testConcurrency: 1,
- browser: [
- // Add browsers with different viewports
- // { width: 800, height: 600, name: 'chrome' },
- // { width: 700, height: 500, name: 'firefox' },
- // { width: 1600, height: 1200, name: 'ie11' },
- // { width: 1024, height: 768, name: 'edgechromium' },
- // { width: 800, height: 600, name: 'safari' },
- // // Add mobile emulation devices in Portrait mode
- // { deviceName: 'iPhone X', screenOrientation: 'portrait' },
- // { deviceName: 'Pixel 2', screenOrientation: 'portrait' },
- ],
- // set batch name to the configuration
- // batchName: `Mermaid ${process.env.APPLI_BRANCH ?? "'no APPLI_BRANCH set'"}`,
-});
diff --git a/cSpell.json b/cSpell.json
index 3ea9594f7..fc5eb6f9d 100644
--- a/cSpell.json
+++ b/cSpell.json
@@ -28,6 +28,7 @@
"codedoc",
"codemia",
"colour",
+ "colours",
"commitlint",
"cpettitt",
"customizability",
@@ -102,6 +103,7 @@
"pathe",
"pbrolin",
"phpbb",
+ "pixelmatch",
"plantuml",
"playfair",
"pnpm",
@@ -124,6 +126,7 @@
"sidharth",
"sidharthv",
"sphinxcontrib",
+ "ssim",
"startx",
"starty",
"statediagram",
diff --git a/cypress.config.cjs b/cypress.config.cjs
deleted file mode 100644
index 30076c56e..000000000
--- a/cypress.config.cjs
+++ /dev/null
@@ -1,24 +0,0 @@
-/* eslint-disable @typescript-eslint/no-var-requires */
-
-const { defineConfig } = require('cypress');
-const { addMatchImageSnapshotPlugin } = require('cypress-image-snapshot/plugin');
-const coverage = require('@cypress/code-coverage/task');
-
-module.exports = defineConfig({
- projectId: 'n2sma2',
- e2e: {
- specPattern: 'cypress/integration/**/*.{js,jsx,ts,tsx}',
- setupNodeEvents(on, config) {
- coverage(on, config);
- addMatchImageSnapshotPlugin(on, config);
- // copy any needed variables from process.env to config.env
- config.env.useAppli = process.env.USE_APPLI ? true : false;
-
- // do not forget to return the changed config object!
- return config;
- },
- },
- video: false,
-});
-
-require('@applitools/eyes-cypress')(module);
diff --git a/cypress.config.ts b/cypress.config.ts
new file mode 100644
index 000000000..4182d92a8
--- /dev/null
+++ b/cypress.config.ts
@@ -0,0 +1,30 @@
+import { defineConfig } from 'cypress';
+import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin';
+import coverage from '@cypress/code-coverage/task';
+import eyesPlugin from '@applitools/eyes-cypress';
+export default eyesPlugin(
+ defineConfig({
+ projectId: 'n2sma2',
+ viewportWidth: 1440,
+ viewportHeight: 1024,
+ e2e: {
+ specPattern: 'cypress/integration/**/*.{js,ts}',
+ setupNodeEvents(on, config) {
+ coverage(on, config);
+ on('before:browser:launch', (browser, launchOptions) => {
+ if (browser.name === 'chrome' && browser.isHeadless) {
+ launchOptions.args.push('--window-size=1440,1024', '--force-device-scale-factor=1');
+ }
+ return launchOptions;
+ });
+ addMatchImageSnapshotPlugin(on, config);
+ // copy any needed variables from process.env to config.env
+ config.env.useAppli = process.env.USE_APPLI ? true : false;
+
+ // do not forget to return the changed config object!
+ return config;
+ },
+ },
+ video: false,
+ })
+);
diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js
index 7cbc5d105..23338271f 100644
--- a/cypress/integration/other/configuration.spec.js
+++ b/cypress/integration/other/configuration.spec.js
@@ -117,7 +117,6 @@ describe('Configuration', () => {
});
it('should not taint the initial configuration when using multiple directives', () => {
const url = 'http://localhost:9000/regression/issue-1874.html';
- cy.viewport(1440, 1024);
cy.visit(url);
cy.get('svg');
diff --git a/cypress/integration/other/rerender.spec.js b/cypress/integration/other/rerender.spec.js
index f160a2e27..d14c6257e 100644
--- a/cypress/integration/other/rerender.spec.js
+++ b/cypress/integration/other/rerender.spec.js
@@ -1,14 +1,12 @@
describe('Rerendering', () => {
it('should be able to render after an error has occurred', () => {
const url = 'http://localhost:9000/render-after-error.html';
- cy.viewport(1440, 1024);
cy.visit(url);
cy.get('#graphDiv').should('exist');
});
it('should be able to render and rerender a graph via API', () => {
const url = 'http://localhost:9000/rerender.html';
- cy.viewport(1440, 1024);
cy.visit(url);
cy.get('#graph [id^=flowchart-A]').should('have.text', 'XMas');
diff --git a/cypress/integration/rendering/block.spec.js b/cypress/integration/rendering/block.spec.js
new file mode 100644
index 000000000..9d62c642d
--- /dev/null
+++ b/cypress/integration/rendering/block.spec.js
@@ -0,0 +1,386 @@
+import { imgSnapshotTest } from '../../helpers/util';
+/* eslint-disable no-useless-escape */
+describe('Block diagram', () => {
+ it('BL1: should calculate the block widths', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 2
+ block
+ id2["I am a wide one"]
+ id1
+ end
+ id["Next row"]
+ `
+ );
+ });
+
+ it('BL2: should handle colums statement in sub-blocks', () => {
+ imgSnapshotTest(
+ `block-beta
+ id1["Hello"]
+ block
+ columns 3
+ id2["to"]
+ id3["the"]
+ id4["World"]
+ id5["World"]
+ end
+ `,
+ {}
+ );
+ });
+
+ it('BL3: should align block widths and handle colums statement in sub-blocks', () => {
+ imgSnapshotTest(
+ `block-beta
+ block
+ columns 1
+ id1
+ id2
+ id2.1
+ end
+ id3
+ id4
+ `,
+ {}
+ );
+ });
+
+ it('BL4: should align block widths and handle colums statements in deeper sub-blocks then 1 level', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 1
+ block
+ columns 1
+ block
+ columns 3
+ id1
+ id2
+ id2.1(("XYZ"))
+ end
+ id48
+ end
+ id3
+ `,
+ {}
+ );
+ });
+
+ it('BL5: should align block widths and handle colums statements in deeper sub-blocks then 1 level (alt)', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 1
+ block
+ id1
+ id2
+ block
+ columns 1
+ id3("Wider then")
+ id5(("id5"))
+ end
+ end
+ id4
+ `,
+ {}
+ );
+ });
+
+ it('BL6: should handle block arrows and spece statements', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 3
+ space:3
+ ida idb idc
+ id1 id2
+ blockArrowId<["Label"]>(right)
+ blockArrowId2<["Label"]>(left)
+ blockArrowId3<["Label"]>(up)
+ blockArrowId4<["Label"]>(down)
+ blockArrowId5<["Label"]>(x)
+ blockArrowId6<["Label"]>(y)
+ blockArrowId6<["Label"]>(x, down)
+ `,
+ {}
+ );
+ });
+
+ it('BL7: should handle different types of edges', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 3
+ A space:5
+ A --o B
+ A --> C
+ A --x D
+ `,
+ {}
+ );
+ });
+
+ it('BL8: should handle sub-blocks without columns statements', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 2
+ C A B
+ block
+ D
+ E
+ end
+ `,
+ {}
+ );
+ });
+
+ it('BL9: should handle edges from blocks in sub blocks to other blocks', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 3
+ B space
+ block
+ D
+ end
+ D --> B
+ `,
+ {}
+ );
+ });
+
+ it('BL10: should handle edges from composite blocks', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 3
+ B space
+ block BL
+ D
+ end
+ BL --> B
+ `,
+ {}
+ );
+ });
+
+ it('BL11: should handle edges to composite blocks', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 3
+ B space
+ block BL
+ D
+ end
+ B --> BL
+ `,
+ {}
+ );
+ });
+
+ it('BL12: edges should handle labels', () => {
+ imgSnapshotTest(
+ `block-beta
+ A
+ space
+ A -- "apa" --> E
+ `,
+ {}
+ );
+ });
+
+ it('BL13: should handle block arrows in different directions', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 3
+ space blockArrowId1<["down"]>(down) space
+ blockArrowId2<["right"]>(right) blockArrowId3<["Sync"]>(x, y) blockArrowId4<["left"]>(left)
+ space blockArrowId5<["up"]>(up) space
+ blockArrowId6<["x"]>(x) space blockArrowId7<["y"]>(y)
+ `,
+ {}
+ );
+ });
+
+ it('BL14: should style statements and class statements', () => {
+ imgSnapshotTest(
+ `block-beta
+ A
+ B
+ classDef blue fill:#66f,stroke:#333,stroke-width:2px;
+ class A blue
+ style B fill:#f9F,stroke:#333,stroke-width:4px
+ `,
+ {}
+ );
+ });
+
+ it('BL15: width alignment - D and E should share available space', () => {
+ imgSnapshotTest(
+ `block-beta
+ block
+ D
+ E
+ end
+ db("This is the text in the box")
+ `,
+ {}
+ );
+ });
+
+ it('BL16: width alignment - C should be as wide as the composite block', () => {
+ imgSnapshotTest(
+ `block-beta
+ block
+ A("This is the text")
+ B
+ end
+ C
+ `,
+ {}
+ );
+ });
+
+ it('BL16: width alignment - blocks shold be equal in width', () => {
+ imgSnapshotTest(
+ `block-beta
+ A("This is the text")
+ B
+ C
+ `,
+ {}
+ );
+ });
+
+ it('BL17: block types 1 - square, rounded and circle', () => {
+ imgSnapshotTest(
+ `block-beta
+ A["square"]
+ B("rounded")
+ C(("circle"))
+ `,
+ {}
+ );
+ });
+
+ it('BL18: block types 2 - odd, diamond and hexagon', () => {
+ imgSnapshotTest(
+ `block-beta
+ A>"rect_left_inv_arrow"]
+ B{"diamond"}
+ C{{"hexagon"}}
+ `,
+ {}
+ );
+ });
+
+ it('BL19: block types 3 - stadium', () => {
+ imgSnapshotTest(
+ `block-beta
+ A(["stadium"])
+ `,
+ {}
+ );
+ });
+
+ it('BL20: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => {
+ imgSnapshotTest(
+ `block-beta
+ A[/"lean right"/]
+ B[\"lean left"\]
+ C[/"trapezoid"\]
+ D[\"trapezoid alt"/]
+ `,
+ {}
+ );
+ });
+
+ it('BL21: block types 1 - square, rounded and circle', () => {
+ imgSnapshotTest(
+ `block-beta
+ A["square"]
+ B("rounded")
+ C(("circle"))
+ `,
+ {}
+ );
+ });
+
+ it('BL22: sizing - it should be possible to make a block wider', () => {
+ imgSnapshotTest(
+ `block-beta
+ A("rounded"):2
+ B:2
+ C
+ `,
+ {}
+ );
+ });
+
+ it('BL23: sizing - it should be possible to make a composite block wider', () => {
+ imgSnapshotTest(
+ `block-beta
+ block:2
+ A
+ end
+ B
+ `,
+ {}
+ );
+ });
+
+ it('BL24: block in the middle with space on each side', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 3
+ space
+ middle["In the middle"]
+ space
+ `,
+ {}
+ );
+ });
+ it('BL25: space and an edge', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 5
+ A space B
+ A --x B
+ `,
+ {}
+ );
+ });
+ it('BL26: block sizes for regular blocks', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 3
+ a["A wide one"] b:2 c:2 d
+ `,
+ {}
+ );
+ });
+ it('BL27: composite block with a set width - f should use the available space', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 3
+ a:3
+ block:e:3
+ f
+ end
+ g
+ `,
+ {}
+ );
+ });
+ it('BL23: composite block with a set width - f and g should split the available space', () => {
+ imgSnapshotTest(
+ `block-beta
+ columns 3
+ a:3
+ block:e:3
+ f
+ g
+ end
+ h
+ i
+ j
+ `,
+ {}
+ );
+ });
+});
diff --git a/cypress/integration/rendering/debug.spec.js b/cypress/integration/rendering/debug.spec.js
deleted file mode 100644
index 56ad0f15f..000000000
--- a/cypress/integration/rendering/debug.spec.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { imgSnapshotTest } from '../../helpers/util.ts';
-
-describe('Flowchart', () => {
- it('34: testing the label width in percy', () => {
- imgSnapshotTest(
- `graph TD
- A[Christmas]
- `,
- { theme: 'forest', fontFamily: '"Noto Sans SC", sans-serif' }
- );
- });
-});
diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js
index b7583ccf1..857d395be 100644
--- a/cypress/integration/rendering/flowchart-v2.spec.js
+++ b/cypress/integration/rendering/flowchart-v2.spec.js
@@ -741,6 +741,25 @@ A ~~~ B
);
});
+ it('5059: Should render when subgraph contains only subgraphs, has link to outside and itself is part of a link', () => {
+ imgSnapshotTest(
+ `flowchart
+
+ subgraph Main
+ subgraph Child1
+ Node1
+ Node2
+ end
+ subgraph Child2
+ Node3
+ Node4
+ end
+ end
+ Main --> Out1
+ Child2 --> Out2`
+ );
+ });
+
describe('Markdown strings flowchart (#4220)', () => {
describe('html labels', () => {
it('With styling and classes', () => {
@@ -886,4 +905,93 @@ end
});
});
});
+ describe('Subgraph title margins', () => {
+ it('Should render subgraphs with title margins set (LR)', () => {
+ imgSnapshotTest(
+ `flowchart LR
+
+ subgraph TOP
+ direction TB
+ subgraph B1
+ direction RL
+ i1 -->f1
+ end
+ subgraph B2
+ direction BT
+ i2 -->f2
+ end
+ end
+ A --> TOP --> B
+ B1 --> B2
+ `,
+ { flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } }
+ );
+ });
+ it('Should render subgraphs with title margins set (TD)', () => {
+ imgSnapshotTest(
+ `flowchart TD
+
+ subgraph TOP
+ direction LR
+ subgraph B1
+ direction RL
+ i1 -->f1
+ end
+ subgraph B2
+ direction BT
+ i2 -->f2
+ end
+ end
+ A --> TOP --> B
+ B1 --> B2
+ `,
+ { flowchart: { subGraphTitleMargin: { top: 8, bottom: 16 } } }
+ );
+ });
+ it('Should render subgraphs with title margins set (LR) and htmlLabels set to false', () => {
+ imgSnapshotTest(
+ `flowchart LR
+
+ subgraph TOP
+ direction TB
+ subgraph B1
+ direction RL
+ i1 -->f1
+ end
+ subgraph B2
+ direction BT
+ i2 -->f2
+ end
+ end
+ A --> TOP --> B
+ B1 --> B2
+ `,
+ {
+ htmlLabels: false,
+ flowchart: { htmlLabels: false, subGraphTitleMargin: { top: 10, bottom: 5 } },
+ }
+ );
+ });
+ it('Should render subgraphs with title margins and edge labels', () => {
+ imgSnapshotTest(
+ `flowchart LR
+
+ subgraph TOP
+ direction TB
+ subgraph B1
+ direction RL
+ i1 --lb1-->f1
+ end
+ subgraph B2
+ direction BT
+ i2 --lb2-->f2
+ end
+ end
+ A --lb3--> TOP --lb4--> B
+ B1 --lb5--> B2
+ `,
+ { flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } }
+ );
+ });
+ });
});
diff --git a/cypress/integration/rendering/gantt.spec.js b/cypress/integration/rendering/gantt.spec.js
index 998a092c2..4abde9d44 100644
--- a/cypress/integration/rendering/gantt.spec.js
+++ b/cypress/integration/rendering/gantt.spec.js
@@ -245,7 +245,10 @@ describe('Gantt diagram', () => {
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
- expect(maxWidthValue).to.be.within(984 * 0.95, 984 * 1.05);
+ expect(maxWidthValue).to.be.within(
+ Cypress.config().viewportWidth * 0.95,
+ Cypress.config().viewportWidth * 1.05
+ );
});
});
@@ -285,11 +288,11 @@ describe('Gantt diagram', () => {
{ gantt: { useMaxWidth: false } }
);
cy.get('svg').should((svg) => {
- // const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
- // use within because the absolute value can be slightly different depending on the environment ±5%
- // expect(height).to.be.within(484 * 0.95, 484 * 1.05);
- expect(width).to.be.within(984 * 0.95, 984 * 1.05);
+ expect(width).to.be.within(
+ Cypress.config().viewportWidth * 0.95,
+ Cypress.config().viewportWidth * 1.05
+ );
expect(svg).to.not.have.attr('style');
});
});
@@ -580,4 +583,106 @@ describe('Gantt diagram', () => {
{}
);
});
+
+ it("should render when there's a semicolon in the title", () => {
+ imgSnapshotTest(
+ `
+ gantt
+ title ;Gantt With a Semicolon in the Title
+ dateFormat YYYY-MM-DD
+ section Section
+ A task :a1, 2014-01-01, 30d
+ Another task :after a1 , 20d
+ section Another
+ Task in sec :2014-01-12 , 12d
+ another task : 24d
+ `,
+ {}
+ );
+ });
+
+ it("should render when there's a semicolon in a section is true", () => {
+ imgSnapshotTest(
+ `
+ gantt
+ title Gantt Digram
+ dateFormat YYYY-MM-DD
+ section ;Section With a Semicolon
+ A task :a1, 2014-01-01, 30d
+ Another task :after a1 , 20d
+ section Another
+ Task in sec :2014-01-12 , 12d
+ another task : 24d
+ `,
+ {}
+ );
+ });
+
+ it("should render when there's a semicolon in the task data", () => {
+ imgSnapshotTest(
+ `
+ gantt
+ title Gantt Digram
+ dateFormat YYYY-MM-DD
+ section Section
+ ;A task with a semiclon :a1, 2014-01-01, 30d
+ Another task :after a1 , 20d
+ section Another
+ Task in sec :2014-01-12 , 12d
+ another task : 24d
+ `,
+ {}
+ );
+ });
+
+ it("should render when there's a hashtag in the title", () => {
+ imgSnapshotTest(
+ `
+ gantt
+ title #Gantt With a Hashtag in the Title
+ dateFormat YYYY-MM-DD
+ section Section
+ A task :a1, 2014-01-01, 30d
+ Another task :after a1 , 20d
+ section Another
+ Task in sec :2014-01-12 , 12d
+ another task : 24d
+ `,
+ {}
+ );
+ });
+
+ it("should render when there's a hashtag in a section is true", () => {
+ imgSnapshotTest(
+ `
+ gantt
+ title Gantt Digram
+ dateFormat YYYY-MM-DD
+ section #Section With a Hashtag
+ A task :a1, 2014-01-01, 30d
+ Another task :after a1 , 20d
+ section Another
+ Task in sec :2014-01-12 , 12d
+ another task : 24d
+ `,
+ {}
+ );
+ });
+
+ it("should render when there's a hashtag in the task data", () => {
+ imgSnapshotTest(
+ `
+ gantt
+ title Gantt Digram
+ dateFormat YYYY-MM-DD
+ section Section
+ #A task with a hashtag :a1, 2014-01-01, 30d
+ Another task :after a1 , 20d
+ section Another
+ Task in sec :2014-01-12 , 12d
+ another task : 24d
+ `,
+ {}
+ );
+ });
});
diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js
index d3e4dd9dd..19ddde31d 100644
--- a/cypress/integration/rendering/gitGraph.spec.js
+++ b/cypress/integration/rendering/gitGraph.spec.js
@@ -826,4 +826,121 @@ gitGraph TB:
cherry-pick id: "M" parent:"B"`
);
});
+ it('41: should render default GitGraph with parallelCommits set to false', () => {
+ imgSnapshotTest(
+ `gitGraph
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ branch develop
+ commit id:"3-abcdefg"
+ commit id:"4-abcdefg"
+ checkout main
+ branch feature
+ commit id:"5-abcdefg"
+ commit id:"6-abcdefg"
+ checkout main
+ commit id:"7-abcdefg"
+ commit id:"8-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: false } }
+ );
+ });
+ it('42: should render GitGraph with parallel commits', () => {
+ imgSnapshotTest(
+ `gitGraph
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ branch develop
+ commit id:"3-abcdefg"
+ commit id:"4-abcdefg"
+ checkout main
+ branch feature
+ commit id:"5-abcdefg"
+ commit id:"6-abcdefg"
+ checkout main
+ commit id:"7-abcdefg"
+ commit id:"8-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: true } }
+ );
+ });
+ it('43: should render GitGraph with parallel commits | Vertical Branch', () => {
+ imgSnapshotTest(
+ `gitGraph TB:
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ branch develop
+ commit id:"3-abcdefg"
+ commit id:"4-abcdefg"
+ checkout main
+ branch feature
+ commit id:"5-abcdefg"
+ commit id:"6-abcdefg"
+ checkout main
+ commit id:"7-abcdefg"
+ commit id:"8-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: true } }
+ );
+ });
+ it('44: should render GitGraph with unconnected branches and no parallel commits', () => {
+ imgSnapshotTest(
+ `gitGraph
+ branch dev
+ branch v2
+ branch feat
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ checkout main
+ commit id:"3-abcdefg"
+ checkout dev
+ commit id:"4-abcdefg"
+ checkout v2
+ commit id:"5-abcdefg"
+ checkout main
+ commit id:"6-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: false } }
+ );
+ });
+ it('45: should render GitGraph with unconnected branches and parallel commits', () => {
+ imgSnapshotTest(
+ `gitGraph
+ branch dev
+ branch v2
+ branch feat
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ checkout main
+ commit id:"3-abcdefg"
+ checkout dev
+ commit id:"4-abcdefg"
+ checkout v2
+ commit id:"5-abcdefg"
+ checkout main
+ commit id:"6-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: true } }
+ );
+ });
+ it('46: should render GitGraph with unconnected branches and parallel commits | Vertical Branch', () => {
+ imgSnapshotTest(
+ `gitGraph TB:
+ branch dev
+ branch v2
+ branch feat
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ checkout main
+ commit id:"3-abcdefg"
+ checkout dev
+ commit id:"4-abcdefg"
+ checkout v2
+ commit id:"5-abcdefg"
+ checkout main
+ commit id:"6-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: true } }
+ );
+ });
});
diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js
index 27e03da9c..306b6c79f 100644
--- a/cypress/integration/rendering/sequencediagram.spec.js
+++ b/cypress/integration/rendering/sequencediagram.spec.js
@@ -375,6 +375,26 @@ context('Sequence diagram', () => {
{}
);
});
+ it('should have actor-top and actor-bottom classes on top and bottom actor box and symbol', () => {
+ imgSnapshotTest(
+ `
+ sequenceDiagram
+ actor Bob
+ Alice->>Bob: Hi Bob
+ Bob->>Alice: Hi Alice
+ `,
+ {}
+ );
+ cy.get('.actor').should('have.class', 'actor-top');
+ cy.get('.actor-man').should('have.class', 'actor-top');
+ cy.get('.actor.actor-top').should('not.have.class', 'actor-bottom');
+ cy.get('.actor-man.actor-top').should('not.have.class', 'actor-bottom');
+
+ cy.get('.actor').should('have.class', 'actor-bottom');
+ cy.get('.actor-man').should('have.class', 'actor-bottom');
+ cy.get('.actor.actor-bottom').should('not.have.class', 'actor-top');
+ cy.get('.actor-man.actor-bottom').should('not.have.class', 'actor-top');
+ });
it('should render long notes left of actor', () => {
imgSnapshotTest(
`
@@ -792,6 +812,34 @@ context('Sequence diagram', () => {
});
});
context('links', () => {
+ it('should support actor links', () => {
+ renderGraph(
+ `
+ sequenceDiagram
+ link Alice: Dashboard @ https://dashboard.contoso.com/alice
+ link Alice: Wiki @ https://wiki.contoso.com/alice
+ link John: Dashboard @ https://dashboard.contoso.com/john
+ link John: Wiki @ https://wiki.contoso.com/john
+ Alice->>John: Hello John
+ John-->>Alice: Great
day!
+ `,
+ { securityLevel: 'loose' }
+ );
+ cy.get('#actor0_popup').should((popupMenu) => {
+ const style = popupMenu.attr('style');
+ expect(style).to.undefined;
+ });
+ cy.get('#root-0').click();
+ cy.get('#actor0_popup').should((popupMenu) => {
+ const style = popupMenu.attr('style');
+ expect(style).to.match(/^display: block;$/);
+ });
+ cy.get('#root-0').click();
+ cy.get('#actor0_popup').should((popupMenu) => {
+ const style = popupMenu.attr('style');
+ expect(style).to.match(/^display: none;$/);
+ });
+ });
it('should support actor links and properties EXPERIMENTAL: USE WITH CAUTION', () => {
//Be aware that the syntax for "properties" is likely to be changed.
imgSnapshotTest(
diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html
index fa205cfaa..f77f6b0e7 100644
--- a/cypress/platform/knsv2.html
+++ b/cypress/platform/knsv2.html
@@ -17,24 +17,30 @@