diff --git a/.build/jsonSchema.ts b/.build/jsonSchema.ts index dd66d984c..6fd8ca3f5 100644 --- a/.build/jsonSchema.ts +++ b/.build/jsonSchema.ts @@ -1,7 +1,6 @@ import { load, JSON_SCHEMA } from 'js-yaml'; import assert from 'node:assert'; import Ajv2019, { type JSONSchemaType } from 'ajv/dist/2019.js'; - import type { MermaidConfig, BaseDiagramConfig } from '../packages/mermaid/src/config.type.js'; /** @@ -24,6 +23,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [ 'gitGraph', 'c4', 'sankey', + 'block', 'packet', ] as const; 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 fa15f39e1..6be6f3b5d 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -4,7 +4,7 @@ contact_links: url: https://github.com/mermaid-js/mermaid/discussions about: Ask the Community questions or share your own graphs in our discussions. - name: Discord - url: https://discord.gg/wwtabKgp8y + url: https://discord.gg/AgrbSrBer3 about: Join our Community on Discord for Help and a casual chat. - name: Documentation url: https://mermaid.js.org 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 b8232b8c0..b97686db4 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -23,13 +23,16 @@ env: 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: 18.x + node-version-file: '.node-version' - name: Cache snapshots id: cache-snapshot uses: actions/cache@v4 @@ -56,11 +59,13 @@ jobs: 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 @@ -68,10 +73,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' # These cached snapshots are downloaded, providing the reference snapshots. - name: Cache snapshots diff --git a/.github/workflows/link-checker.yml b/.github/workflows/link-checker.yml index 3d4956945..59d25b7c5 100644 --- a/.github/workflows/link-checker.yml +++ b/.github/workflows/link-checker.yml @@ -36,7 +36,7 @@ jobs: restore-keys: cache-lychee- - name: Link Checker - uses: lycheeverse/lychee-action@v1.9.1 + uses: lycheeverse/lychee-action@v1.9.3 with: args: >- --config .github/lychee.toml 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/.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/.npmrc b/.npmrc index e72930ead..91750f557 100644 --- a/.npmrc +++ b/.npmrc @@ -1,3 +1,4 @@ registry=https://registry.npmjs.org auto-install-peers=true strict-peer-dependencies=false +package-import-method=clone-or-copy diff --git a/.prettierignore b/.prettierignore index a0cd771e3..7da0646e3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,7 @@ dist cypress/platform/xss3.html .cache +.pnpm-store coverage # Autogenerated by PNPM pnpm-lock.yaml diff --git a/Dockerfile b/Dockerfile index a62800109..33a1ebd37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,2 +1,2 @@ -FROM node:18.19.0-alpine3.18 AS base +FROM node:20.11.0-alpine3.19 AS base RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" sh - diff --git a/README.md b/README.md index 58287c634..f0d9f7b09 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Generate diagrams from markdown-like text. Live Editor!

- ๐Ÿ“– Documentation | ๐Ÿš€ Getting Started | ๐ŸŒ CDN | ๐Ÿ™Œ Join Us + ๐Ÿ“– Documentation | ๐Ÿš€ Getting Started | ๐ŸŒ CDN | ๐Ÿ™Œ Join Us

็ฎ€ไฝ“ไธญๆ–‡ @@ -33,7 +33,7 @@ Try Live Editor previews of future releases: diff --git a/README.zh-CN.md b/README.zh-CN.md index c468b2d9f..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 [![Coverage Status](https://codecov.io/github/mermaid-js/mermaid/branch/develop/graph/badge.svg)](https://app.codecov.io/github/mermaid-js/mermaid/tree/develop) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM Downloads](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) -[![Join our Discord!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=discord&label=discord)](https://discord.gg/wwtabKgp8y) +[![Join our Discord!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=discord&label=discord)](https://discord.gg/AgrbSrBer3) [![Twitter Follow](https://img.shields.io/badge/Social-mermaidjs__-blue?style=social&logo=X)](https://twitter.com/mermaidjs_) diff --git a/cSpell.json b/cSpell.json index 5566b673f..3f605c6d6 100644 --- a/cSpell.json +++ b/cSpell.json @@ -28,6 +28,7 @@ "codedoc", "codemia", "colour", + "colours", "commitlint", "cpettitt", "customizability", diff --git a/cypress/integration/rendering/block.spec.ts b/cypress/integration/rendering/block.spec.ts new file mode 100644 index 000000000..9d62c642d --- /dev/null +++ b/cypress/integration/rendering/block.spec.ts @@ -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/gantt.spec.js b/cypress/integration/rendering/gantt.spec.js index 73ff4ee00..4abde9d44 100644 --- a/cypress/integration/rendering/gantt.spec.js +++ b/cypress/integration/rendering/gantt.spec.js @@ -583,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/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 020ea8b48..f77f6b0e7 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -17,24 +17,30 @@