From d60ce53e05a8ccdf5d70cce7813226cab59d5f8f Mon Sep 17 00:00:00 2001 From: ashishj Date: Tue, 13 Sep 2022 18:23:24 +0200 Subject: [PATCH 01/23] #3409 Fixed the truncated tags issue --- cypress/platform/gitgraph.html | 25 +++++++++++++++++++++++++ src/diagrams/git/gitGraphRenderer.js | 17 +++-------------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/cypress/platform/gitgraph.html b/cypress/platform/gitgraph.html index da81427f8..d8d16f6c9 100644 --- a/cypress/platform/gitgraph.html +++ b/cypress/platform/gitgraph.html @@ -30,7 +30,31 @@

info below

+
+  %%{init: { "logLevel": "debug", "theme": "default" , "gitGraph" : {"showBranches":"false","rotateCommitLabel":"true"},"themeVariables": {
+              "gitBranchLabel0": "#ff0000",
+              "gitBranchLabel1": "#00ff00",
+              "gitBranchLabel2": "#0000ff",
+              "git0": "#550055"
+       } } }%%
+    gitGraph
+      commit
+       branch develop
+       commit
+       commit
+       branch release/1.0.0
+       checkout release/1.0.0
+       commit tag:"1.0.0-beta1"
+       checkout develop
+       commit
+       commit
+       commit
+       commit
+       checkout release/1.0.0
+       merge develop tag: "1.0.0-beta2"
+    
+
     %%{init: { "logLevel": "debug", "theme": "default" , "gitGraph" : {"showBranches":"false"},"themeVariables": {
               "gitBranchLabel0": "#ff0000",
               "gitBranchLabel1": "#00ff00",
@@ -131,6 +155,7 @@
         // arrowMarkerAbsolute: true,
         // themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
         logLevel: 1,
+        gitGraph: {rotateCommitLabel: false},
         flowchart: { curve: 'linear', htmlLabels: true },
         // gantt: { axisFormat: '%m/%d/%Y' },
         sequence: { actorMargin: 50, showSequenceNumbers: true },
diff --git a/src/diagrams/git/gitGraphRenderer.js b/src/diagrams/git/gitGraphRenderer.js
index 5a9036b88..79339d727 100644
--- a/src/diagrams/git/gitGraphRenderer.js
+++ b/src/diagrams/git/gitGraphRenderer.js
@@ -1,7 +1,6 @@
 import { select } from 'd3';
-import { configureSvgSize } from '../../setupGraphViewbox';
+import { getConfig,setupGraphViewbox } from '../../diagram-api/diagramAPI';
 import { log } from '../../logger';
-import { getConfig } from '../../config';
 import addSVGAccessibilityFields from '../../accessibility';
 
 let allCommitsDict = {};
@@ -542,18 +541,8 @@ export const draw = function (txt, id, ver, diagObj) {
   drawArrows(diagram, allCommitsDict);
   drawCommits(diagram, allCommitsDict, true);
 
-  const padding = gitGraphConfig.diagramPadding;
-  const svgBounds = diagram.node().getBBox();
-  const width = svgBounds.width + padding * 2;
-  const height = svgBounds.height + padding * 2;
-
-  configureSvgSize(diagram, height, width, conf.useMaxWidth);
-  const vBox = `${
-    svgBounds.x -
-    padding -
-    (gitGraphConfig.showBranches && gitGraphConfig.rotateCommitLabel === true ? 30 : 0)
-  } ${svgBounds.y - padding} ${width} ${height}`;
-  diagram.attr('viewBox', vBox);
+   // Setup the view box and size of the svg element
+    setupGraphViewbox(undefined, diagram, gitGraphConfig.diagramPadding, conf.useMaxWidth);
 };
 
 export default {

From 8c4808a681f87aca40ca2cb291c97f1322a95de5 Mon Sep 17 00:00:00 2001
From: ashishj 
Date: Tue, 13 Sep 2022 18:27:25 +0200
Subject: [PATCH 02/23] #3409 Clean up dead code

---
 src/diagrams/git/gitGraphRenderer.js | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/diagrams/git/gitGraphRenderer.js b/src/diagrams/git/gitGraphRenderer.js
index 79339d727..ef85dcb97 100644
--- a/src/diagrams/git/gitGraphRenderer.js
+++ b/src/diagrams/git/gitGraphRenderer.js
@@ -4,7 +4,6 @@ import { log } from '../../logger';
 import addSVGAccessibilityFields from '../../accessibility';
 
 let allCommitsDict = {};
-let branchNum;
 
 const commitType = {
   NORMAL: 0,
@@ -82,7 +81,7 @@ const drawCommits = (svg, commits, modifyGraph) => {
   const sortedKeys = keys.sort((a, b) => {
     return commits[a].seq - commits[b].seq;
   });
-  sortedKeys.forEach((key, index) => {
+  sortedKeys.forEach((key) => {
     const commit = commits[key];
 
     const y = branchPos[commit.branch].pos;
@@ -298,9 +297,6 @@ const drawCommits = (svg, commits, modifyGraph) => {
  * @returns {boolean} if there are commits between commit1's x-position and commit2's x-position
  */
 const hasOverlappingCommits = (commit1, commit2, allCommits) => {
-  const commit1Pos = commitPos[commit2.id];
-  const commit2Pos = commitPos[commit1.id];
-
   // Find commits on the same branch as commit2
   const keys = Object.keys(allCommits);
   const overlappingComits = keys.filter((key) => {

From f05c790248b9e99261aed5ead359ab69d782505f Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Wed, 14 Sep 2022 02:38:13 +0100
Subject: [PATCH 03/23] test(e2e): fix failing xss9 test

Fixes xx9 test to continue running even if JavaScript throws an
exception.
---
 cypress/integration/other/xss.spec.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/cypress/integration/other/xss.spec.js b/cypress/integration/other/xss.spec.js
index 912354f7d..4911dd8df 100644
--- a/cypress/integration/other/xss.spec.js
+++ b/cypress/integration/other/xss.spec.js
@@ -81,6 +81,9 @@ describe('XSS', () => {
     cy.get('#the-malware').should('not.exist');
   });
   it('should not allow manipulating antiscript to run javascript using onerror in state diagrams with dagre d3', () => {
+    cy.on('uncaught:exception', (_err, _runnable) => {
+      return false; // continue rendering even if there if mermaid throws an error
+    });
     cy.visit('http://localhost:9000/xss9.html');
     cy.wait(1000);
     cy.get('#the-malware').should('not.exist');

From 05b8a6e77f8d7311a98457418ca6fd0b64efe77d Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Wed, 14 Sep 2022 03:05:40 +0100
Subject: [PATCH 04/23] test(e2e): widen flowchart width to within 10%

Widen flowchart width acceptable values to within 10% of 300px.
On my local environment, I'm getting 283px, which is 5.5% of 300px.
---
 cypress/integration/rendering/flowchart.spec.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js
index b4e94d1ab..c4ef54fcf 100644
--- a/cypress/integration/rendering/flowchart.spec.js
+++ b/cypress/integration/rendering/flowchart.spec.js
@@ -745,13 +745,13 @@ describe('Graph', () => {
     cy.get('svg').should((svg) => {
       expect(svg).to.have.attr('width', '100%');
       // expect(svg).to.have.attr('height');
-      // use within because the absolute value can be slightly different depending on the environment ±5%
+      // use within because the absolute value can be slightly different depending on the environment ±10%
       // const height = parseFloat(svg.attr('height'));
       // expect(height).to.be.within(446 * 0.95, 446 * 1.05);
       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(300 * 0.95, 300 * 1.05);
+      expect(maxWidthValue).to.be.within(300 * 0.9, 300 * 1.1);
     });
   });
   it('39: should render a flowchart when useMaxWidth is false', () => {
@@ -768,9 +768,9 @@ describe('Graph', () => {
     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%
+      // use within because the absolute value can be slightly different depending on the environment ±10%
       // expect(height).to.be.within(446 * 0.95, 446 * 1.05);
-      expect(width).to.be.within(300 * 0.95, 300 * 1.05);
+      expect(width).to.be.within(300 * 0.9, 300 * 1.1);
       expect(svg).to.not.have.attr('style');
     });
   });

From a25c9a30d0fa51a5e84b499595489c9d495b14ae Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Wed, 14 Sep 2022 03:50:03 +0100
Subject: [PATCH 05/23] text(e2e): give git tests consistent commit id

gitGraph render tests are currently using random commit ids,
which causes visual regression tests to fail.

I couldn't find a way to make the random commit IDs consistent
(JavaScript/Cypress doesn't seem to have a random.seed() function),
so I've just hardcoded all the commit ids.
---
 .../integration/rendering/gitGraph.spec.js    | 21 ++++++++++---------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js
index 80981c31c..b0d65d0cc 100644
--- a/cypress/integration/rendering/gitGraph.spec.js
+++ b/cypress/integration/rendering/gitGraph.spec.js
@@ -213,42 +213,43 @@ describe('Git Graph diagram', () => {
       `
       gitGraph
       checkout main
-      commit
+      %% Make sure to manually set the ID of all commits, for consistent visual tests
+      commit id: "1-abcdefg"
       checkout main
       branch branch1
-      commit
+      commit id: "2-abcdefg"
       checkout main
       merge branch1
       branch branch2
-      commit
+      commit id: "3-abcdefg"
       checkout main
       merge branch2
       branch branch3
-      commit
+      commit id: "4-abcdefg"
       checkout main
       merge branch3
       branch branch4
-      commit
+      commit id: "5-abcdefg"
       checkout main
       merge branch4
       branch branch5
-      commit
+      commit id: "6-abcdefg"
       checkout main
       merge branch5
       branch branch6
-      commit
+      commit id: "7-abcdefg"
       checkout main
       merge branch6
       branch branch7
-      commit
+      commit id: "8-abcdefg"
       checkout main
       merge branch7
       branch branch8
-      commit
+      commit id: "9-abcdefg"
       checkout main
       merge branch8
       branch branch9
-      commit
+      commit id: "10-abcdefg"
       `,
       {}
     );

From 6e7037bafd880966014fa21f0dc197357c00c8ee Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Wed, 14 Sep 2022 01:50:11 +0100
Subject: [PATCH 06/23] test(e2e): fix most arrowMarkerAbsolute tests

The arrows between flowcharts do not have the class edgePath.

Instead, I'm loading all ``s within the ``
---
 .../integration/other/configuration.spec.js   | 60 +++++++++++--------
 1 file changed, 35 insertions(+), 25 deletions(-)

diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js
index a67758d9c..db2602a17 100644
--- a/cypress/integration/other/configuration.spec.js
+++ b/cypress/integration/other/configuration.spec.js
@@ -15,11 +15,13 @@ describe('Configuration', () => {
 
       // Check the marker-end property to make sure it is properly set to
       // start with #
-      cy.get('.edgePath path')
-        .first()
-        .should('have.attr', 'marker-end')
-        .should('exist')
-        .and('include', 'url(#');
+      cy.get('.edgePaths').within(() => {
+        cy.get('path')
+          .first()
+          .should('have.attr', 'marker-end')
+          .should('exist')
+          .and('include', 'url(#');
+      });
     });
     it('should handle default value false of arrowMarkerAbsolute', () => {
       renderGraph(
@@ -35,11 +37,13 @@ describe('Configuration', () => {
 
       // Check the marker-end property to make sure it is properly set to
       // start with #
-      cy.get('.edgePath path')
-        .first()
-        .should('have.attr', 'marker-end')
-        .should('exist')
-        .and('include', 'url(#');
+      cy.get('.edgePaths').within(() => {
+        cy.get('path')
+          .first()
+          .should('have.attr', 'marker-end')
+          .should('exist')
+          .and('include', 'url(#');
+      });
     });
     it('should handle arrowMarkerAbsolute explicitly set to false', () => {
       renderGraph(
@@ -57,11 +61,13 @@ describe('Configuration', () => {
 
       // Check the marker-end property to make sure it is properly set to
       // start with #
-      cy.get('.edgePath path')
-        .first()
-        .should('have.attr', 'marker-end')
-        .should('exist')
-        .and('include', 'url(#');
+      cy.get('.edgePaths').within(() => {
+        cy.get('path')
+          .first()
+          .should('have.attr', 'marker-end')
+          .should('exist')
+          .and('include', 'url(#');
+      });
     });
     it('should handle arrowMarkerAbsolute explicitly set to "false" as false', () => {
       renderGraph(
@@ -79,11 +85,13 @@ describe('Configuration', () => {
 
       // Check the marker-end property to make sure it is properly set to
       // start with #
-      cy.get('.edgePath path')
-        .first()
-        .should('have.attr', 'marker-end')
-        .should('exist')
-        .and('include', 'url(#');
+      cy.get('.edgePaths').within(() => {
+        cy.get('path')
+          .first()
+          .should('have.attr', 'marker-end')
+          .should('exist')
+          .and('include', 'url(#');
+      });
     });
     it('should handle arrowMarkerAbsolute set to true', () => {
       renderGraph(
@@ -99,11 +107,13 @@ describe('Configuration', () => {
         }
       );
 
-      cy.get('.edgePath path')
-        .first()
-        .should('have.attr', 'marker-end')
-        .should('exist')
-        .and('include', 'url(http://localhost');
+      cy.get('.edgePaths').within(() => {
+        cy.get('path')
+          .first()
+          .should('have.attr', 'marker-end')
+          .should('exist')
+          .and('include', 'url(http://localhost');
+      });
     });
     it('should not taint the initial configuration when using multiple directives', () => {
       const url = 'http://localhost:9000/regression/issue-1874.html';

From 595f7680e93b03c0454eb11efdb92cc52f4d27f7 Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Wed, 14 Sep 2022 02:12:08 +0100
Subject: [PATCH 07/23] fix(flowchart-v2): fix arrowMarkerAbsolute=true

The if-check for arrowMarkerAbsolute in the flowchart-v2 code is in
dagre-wrapper. Unfortunately, I can't seem to find a way to find the
local conf (e.g. the one set via `flowRenderer.setConf()`, so instead
I'm using global mermaid config from `src/config`.

Flowchart v1 arrowMarkerAbsolute=true is still broken, but I'm not
really sure how to fix that.
---
 cypress/integration/other/configuration.spec.js | 2 +-
 src/dagre-wrapper/edges.js                      | 3 ++-
 src/diagram-api/diagram-orchestration.ts        | 8 ++++++--
 3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js
index db2602a17..513cf0714 100644
--- a/cypress/integration/other/configuration.spec.js
+++ b/cypress/integration/other/configuration.spec.js
@@ -95,7 +95,7 @@ describe('Configuration', () => {
     });
     it('should handle arrowMarkerAbsolute set to true', () => {
       renderGraph(
-        `graph TD
+        `flowchart TD
         A[Christmas] -->|Get money| B(Go shopping)
         B --> C{Let me think}
         C -->|One| D[Laptop]
diff --git a/src/dagre-wrapper/edges.js b/src/dagre-wrapper/edges.js
index 86e41de1d..6ed08e924 100644
--- a/src/dagre-wrapper/edges.js
+++ b/src/dagre-wrapper/edges.js
@@ -472,7 +472,8 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph
   // });
 
   let url = '';
-  if (getConfig().state.arrowMarkerAbsolute) {
+  // // TODO: Can we load this config only from the rendered graph type?
+  if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) {
     url =
       window.location.protocol +
       '//' +
diff --git a/src/diagram-api/diagram-orchestration.ts b/src/diagram-api/diagram-orchestration.ts
index b9b0520c9..9c2ca5a51 100644
--- a/src/diagram-api/diagram-orchestration.ts
+++ b/src/diagram-api/diagram-orchestration.ts
@@ -96,6 +96,7 @@ import { journeyDetector } from '../diagrams/user-journey/journeyDetector';
 import journeyDb from '../diagrams/user-journey/journeyDb';
 import journeyRenderer from '../diagrams/user-journey/journeyRenderer';
 import journeyStyles from '../diagrams/user-journey/styles';
+import { getConfig, setConfig } from '../config';
 
 export const addDiagrams = () => {
   registerDiagram(
@@ -275,11 +276,12 @@ export const addDiagrams = () => {
       renderer: flowRendererV2,
       styles: flowStyles,
       init: (cnf) => {
-        flowRenderer.setConf(cnf.flowchart);
         if (!cnf.flowchart) {
           cnf.flowchart = {};
         }
+        // TODO, broken as of 2022-09-14 (13809b50251845475e6dca65cc395761be38fbd2)
         cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
+        flowRenderer.setConf(cnf.flowchart);
         flowDb.clear();
         flowDb.setGen('gen-1');
       },
@@ -294,11 +296,13 @@ export const addDiagrams = () => {
       renderer: flowRendererV2,
       styles: flowStyles,
       init: (cnf) => {
-        flowRendererV2.setConf(cnf.flowchart);
         if (!cnf.flowchart) {
           cnf.flowchart = {};
         }
         cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
+        // flowchart-v2 uses dagre-wrapper, which doesn't have access to flowchart cnf
+        setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
+        flowRendererV2.setConf(cnf.flowchart);
         flowDb.clear();
         flowDb.setGen('gen-2');
       },

From 625ec813b9459740320730b38cb94b5e1ccdeafe Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Wed, 14 Sep 2022 01:43:07 +0100
Subject: [PATCH 08/23] chore: upgrade cypress to v10

Cypress v10 had some minor breaking changes,
(e.g. the cypress/plugins folder dissapeared)
so I had to change some things.

It looks like the Cypress was previously reverted in [dee9cfe][1],
due to applitools not working properly,
but I think my changes fix this, so it should be okay.

[1]: https://github.com/mermaid-js/mermaid/commit/dee9cfea85f6fb57577efcf0a5cbd407e28866bb
---
 applitools.cnfig.js                  | 16 --------------
 applitools.config.js                 | 19 ++++++++++++++++
 cypress.config.js                    |  7 +++---
 cypress/downloads/downloads.html     |  1 -
 cypress/helpers/util.js              |  8 ++-----
 cypress/plugins/index.js             | 33 ----------------------------
 cypress/support/{index.js => e2e.js} |  2 --
 package.json                         |  2 +-
 yarn.lock                            | 18 +++++++--------
 9 files changed, 34 insertions(+), 72 deletions(-)
 delete mode 100644 applitools.cnfig.js
 create mode 100644 applitools.config.js
 delete mode 100644 cypress/downloads/downloads.html
 delete mode 100644 cypress/plugins/index.js
 rename cypress/support/{index.js => e2e.js} (90%)

diff --git a/applitools.cnfig.js b/applitools.cnfig.js
deleted file mode 100644
index 900aabf2e..000000000
--- a/applitools.cnfig.js
+++ /dev/null
@@ -1,16 +0,0 @@
-module.exports = {
-  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: 'Ultrafast Batch',
-};
diff --git a/applitools.config.js b/applitools.config.js
new file mode 100644
index 000000000..1c0607868
--- /dev/null
+++ b/applitools.config.js
@@ -0,0 +1,19 @@
+// 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/cypress.config.js b/cypress.config.js
index d7c9831d4..b434cec47 100644
--- a/cypress.config.js
+++ b/cypress.config.js
@@ -2,21 +2,20 @@
 
 const { defineConfig } = require('cypress');
 const { addMatchImageSnapshotPlugin } = require('cypress-image-snapshot/plugin');
-require('@applitools/eyes-cypress')(module);
 
 module.exports = defineConfig({
   e2e: {
-    specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
+    specPattern: 'cypress/integration/**/*.{js,jsx,ts,tsx}',
     setupNodeEvents(on, config) {
       addMatchImageSnapshotPlugin(on, config);
       // copy any needed variables from process.env to config.env
       config.env.useAppli = process.env.USE_APPLI ? true : false;
-      config.env.codeBranch = process.env.APPLI_BRANCH;
 
       // do not forget to return the changed config object!
       return config;
     },
-    supportFile: 'cypress/support/index.js',
   },
   video: false,
 });
+
+require('@applitools/eyes-cypress')(module);
diff --git a/cypress/downloads/downloads.html b/cypress/downloads/downloads.html
deleted file mode 100644
index b42fc821e..000000000
--- a/cypress/downloads/downloads.html
+++ /dev/null
@@ -1 +0,0 @@
-Cr24
\ No newline at end of file
diff --git a/cypress/helpers/util.js b/cypress/helpers/util.js
index b5e64a1e8..d138dfcfe 100644
--- a/cypress/helpers/util.js
+++ b/cypress/helpers/util.js
@@ -44,15 +44,13 @@ export const imgSnapshotTest = (graphStr, _options, api = false, validation) =>
   }
   const useAppli = Cypress.env('useAppli');
   //const useAppli = false;
-  const branch = Cypress.env('codeBranch');
   cy.log('Hello ' + useAppli ? 'Appli' : 'image-snapshot');
   const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
 
   if (useAppli) {
     cy.eyesOpen({
-      appName: 'Mermaid-' + branch,
+      appName: 'Mermaid',
       testName: name,
-      batchName: branch,
     });
   }
 
@@ -96,15 +94,13 @@ export const urlSnapshotTest = (url, _options, api = false, validation) => {
     options.fontSize = '16px';
   }
   const useAppli = Cypress.env('useAppli');
-  const branch = Cypress.env('codeBranch');
   cy.log('Hello ' + useAppli ? 'Appli' : 'image-snapshot');
   const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
 
   if (useAppli) {
     cy.eyesOpen({
-      appName: 'Mermaid-' + branch,
+      appName: 'Mermaid',
       testName: name,
-      batchName: branch,
     });
   }
 
diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js
deleted file mode 100644
index ca5a37d5a..000000000
--- a/cypress/plugins/index.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* eslint-disable @typescript-eslint/no-var-requires */
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
-
-// module.exports = (on, config) => {
-//   // `on` is used to hook into various events Cypress emits
-//   // `config` is the resolved Cypress config
-// }
-
-const { addMatchImageSnapshotPlugin } = require('cypress-image-snapshot/plugin');
-require('@applitools/eyes-cypress')(module);
-
-module.exports = (on, config) => {
-  addMatchImageSnapshotPlugin(on, config);
-  // copy any needed variables from process.env to config.env
-  config.env.useAppli = process.env.USE_APPLI ? true : false;
-  config.env.codeBranch = process.env.APPLI_BRANCH;
-
-  // do not forget to return the changed config object!
-  return config;
-};
-
-require('@applitools/eyes-cypress')(module);
diff --git a/cypress/support/index.js b/cypress/support/e2e.js
similarity index 90%
rename from cypress/support/index.js
rename to cypress/support/e2e.js
index 1d23c59bf..69d93b4a4 100644
--- a/cypress/support/index.js
+++ b/cypress/support/e2e.js
@@ -17,8 +17,6 @@ import '@applitools/eyes-cypress/commands';
 
 // Import commands.js using ES2015 syntax:
 import './commands';
-// import '@percy/cypress';
-import '@applitools/eyes-cypress/commands';
 
 // Alternatively you can use CommonJS syntax:
 // require('./commands')
diff --git a/package.json b/package.json
index 96523bc22..0e533497a 100644
--- a/package.json
+++ b/package.json
@@ -93,7 +93,7 @@
     "babel-loader": "^8.2.2",
     "concurrently": "^7.4.0",
     "css-to-string-loader": "^0.1.3",
-    "cypress": "9.7.0",
+    "cypress": "^10.0.0",
     "cypress-image-snapshot": "^4.0.1",
     "documentation": "13.2.0",
     "eslint": "^8.23.1",
diff --git a/yarn.lock b/yarn.lock
index a78af620d..4a7264451 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4662,10 +4662,10 @@ cypress-image-snapshot@^4.0.1:
     pkg-dir "^3.0.0"
     term-img "^4.0.0"
 
-cypress@9.7.0:
-  version "9.7.0"
-  resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.7.0.tgz#bf55b2afd481f7a113ef5604aa8b693564b5e744"
-  integrity sha512-+1EE1nuuuwIt/N1KXRR2iWHU+OiIt7H28jJDyyI4tiUftId/DrXYEwoDa5+kH2pki1zxnA0r6HrUGHV5eLbF5Q==
+cypress@^10.0.0:
+  version "10.8.0"
+  resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.8.0.tgz#12a681f2642b6f13d636bab65d5b71abdb1497a5"
+  integrity sha512-QVse0dnLm018hgti2enKMVZR9qbIO488YGX06nH5j3Dg1isL38DwrBtyrax02CANU6y8F4EJUuyW6HJKw1jsFA==
   dependencies:
     "@cypress/request" "^2.88.10"
     "@cypress/xvfb" "^1.2.4"
@@ -4686,7 +4686,7 @@ cypress@9.7.0:
     dayjs "^1.10.4"
     debug "^4.3.2"
     enquirer "^2.3.6"
-    eventemitter2 "^6.4.3"
+    eventemitter2 "6.4.7"
     execa "4.1.0"
     executable "^4.1.1"
     extract-zip "2.0.1"
@@ -5803,10 +5803,10 @@ event-target-shim@^5.0.0:
   resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
   integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
 
-eventemitter2@^6.4.3:
-  version "6.4.5"
-  resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.5.tgz#97380f758ae24ac15df8353e0cc27f8b95644655"
-  integrity sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==
+eventemitter2@6.4.7:
+  version "6.4.7"
+  resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d"
+  integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==
 
 eventemitter3@^4.0.0:
   version "4.0.7"

From c5033b92b468316cfc4cff8cbdc5712b7044ce86 Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Wed, 14 Sep 2022 04:12:45 +0100
Subject: [PATCH 09/23] style: fix .github/workflow/e2e styling

Uses prettier to fix e2e style issues.
---
 .github/workflows/e2e | 40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/.github/workflows/e2e b/.github/workflows/e2e
index 338869490..4211410fb 100644
--- a/.github/workflows/e2e
+++ b/.github/workflows/e2e
@@ -12,32 +12,32 @@ jobs:
       matrix:
         node-version: [16.x]
     steps:
-    - uses: actions/checkout@v3
+      - uses: actions/checkout@v3
 
-    - name: Setup Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v3
-      with:
-        cache: yarn
-        node-version: ${{ matrix.node-version }}
+      - name: Setup Node.js ${{ matrix.node-version }}
+        uses: actions/setup-node@v3
+        with:
+          cache: yarn
+          node-version: ${{ matrix.node-version }}
 
-    - name: Install Yarn
-      run: npm i yarn --global
+      - name: Install Yarn
+        run: npm i yarn --global
 
-    - name: Install Packages
-      run: |
-        yarn install --frozen-lockfile
-      env:
-        CYPRESS_CACHE_FOLDER: .cache/Cypress
+      - name: Install Packages
+        run: |
+          yarn install --frozen-lockfile
+        env:
+          CYPRESS_CACHE_FOLDER: .cache/Cypress
 
-    - name: Run Build
-      run: yarn build
+      - name: Run Build
+        run: yarn build
 
-    - name: Run E2E Tests
-      run: yarn e2e
-      env:
-        CYPRESS_CACHE_FOLDER: .cache/Cypress
+      - name: Run E2E Tests
+        run: yarn e2e
+        env:
+          CYPRESS_CACHE_FOLDER: .cache/Cypress
 
-    - name: Upload Coverage to Coveralls
+      - name: Upload Coverage to Coveralls
         uses: coverallsapp/github-action@master
         with:
           github-token: ${{ secrets.GITHUB_TOKEN }}

From 20d22a6468688def32cda24648a58510107a6674 Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Wed, 14 Sep 2022 03:20:05 +0100
Subject: [PATCH 10/23] ci(e2e): re-enable e2e tests

I've removed the coverage upload bit, since although the
[@cypress/code-coverage][1] plugin is pretty easy to setup,
it requires babel, which we're about to remove and replace
with ESBuild.
---
 .github/workflows/{e2e => e2e.yml} | 6 ------
 1 file changed, 6 deletions(-)
 rename .github/workflows/{e2e => e2e.yml} (80%)

diff --git a/.github/workflows/e2e b/.github/workflows/e2e.yml
similarity index 80%
rename from .github/workflows/e2e
rename to .github/workflows/e2e.yml
index 4211410fb..06a346aeb 100644
--- a/.github/workflows/e2e
+++ b/.github/workflows/e2e.yml
@@ -36,9 +36,3 @@ jobs:
         run: yarn e2e
         env:
           CYPRESS_CACHE_FOLDER: .cache/Cypress
-
-      - name: Upload Coverage to Coveralls
-        uses: coverallsapp/github-action@master
-        with:
-          github-token: ${{ secrets.GITHUB_TOKEN }}
-          flag-name: e2e

From 4c5d813e585dbc7d40950b80da60c1d1d5b82193 Mon Sep 17 00:00:00 2001
From: ashishj 
Date: Fri, 16 Sep 2022 09:25:22 +0200
Subject: [PATCH 11/23] Fixed Linting issues

---
 cypress/platform/gitgraph.html       |  2 +-
 src/diagrams/git/gitGraphRenderer.js | 14 +++++++-------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/cypress/platform/gitgraph.html b/cypress/platform/gitgraph.html
index d8d16f6c9..0186d6209 100644
--- a/cypress/platform/gitgraph.html
+++ b/cypress/platform/gitgraph.html
@@ -155,7 +155,7 @@
         // arrowMarkerAbsolute: true,
         // themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
         logLevel: 1,
-        gitGraph: {rotateCommitLabel: false},
+        gitGraph: { rotateCommitLabel: false },
         flowchart: { curve: 'linear', htmlLabels: true },
         // gantt: { axisFormat: '%m/%d/%Y' },
         sequence: { actorMargin: 50, showSequenceNumbers: true },
diff --git a/src/diagrams/git/gitGraphRenderer.js b/src/diagrams/git/gitGraphRenderer.js
index ef85dcb97..811a7f85c 100644
--- a/src/diagrams/git/gitGraphRenderer.js
+++ b/src/diagrams/git/gitGraphRenderer.js
@@ -1,5 +1,5 @@
 import { select } from 'd3';
-import { getConfig,setupGraphViewbox } from '../../diagram-api/diagramAPI';
+import { getConfig, setupGraphViewbox } from '../../diagram-api/diagramAPI';
 import { log } from '../../logger';
 import addSVGAccessibilityFields from '../../accessibility';
 
@@ -288,13 +288,13 @@ const drawCommits = (svg, commits, modifyGraph) => {
 };
 
 /**
- * Detect if there are other commits between commit1's x-position and commit2's x-position on the same
- * branch as commit2.
+ * Detect if there are other commits between commit1's x-position and commit2's x-position on the
+ * same branch as commit2.
  *
  * @param {any} commit1
  * @param {any} commit2
  * @param allCommits
- * @returns {boolean} if there are commits between commit1's x-position and commit2's x-position
+ * @returns {boolean} If there are commits between commit1's x-position and commit2's x-position
  */
 const hasOverlappingCommits = (commit1, commit2, allCommits) => {
   // Find commits on the same branch as commit2
@@ -317,7 +317,7 @@ const hasOverlappingCommits = (commit1, commit2, allCommits) => {
  * @param {any} y1
  * @param {any} y2
  * @param {any} _depth
- * @returns {number} y value between y1 and y2
+ * @returns {number} Y value between y1 and y2
  */
 const findLane = (y1, y2, _depth) => {
   const depth = _depth || 0;
@@ -537,8 +537,8 @@ export const draw = function (txt, id, ver, diagObj) {
   drawArrows(diagram, allCommitsDict);
   drawCommits(diagram, allCommitsDict, true);
 
-   // Setup the view box and size of the svg element
-    setupGraphViewbox(undefined, diagram, gitGraphConfig.diagramPadding, conf.useMaxWidth);
+  // Setup the view box and size of the svg element
+  setupGraphViewbox(undefined, diagram, gitGraphConfig.diagramPadding, conf.useMaxWidth);
 };
 
 export default {

From 27e40248ff50a465f7ed4e00406eefa7a0ad23a7 Mon Sep 17 00:00:00 2001
From: Sidharth Vinod 
Date: Fri, 16 Sep 2022 19:39:38 +0530
Subject: [PATCH 12/23] Cleanup docs

---
 docs/index.html | 1 -
 1 file changed, 1 deletion(-)

diff --git a/docs/index.html b/docs/index.html
index 0a41b516c..22892d60b 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -15,7 +15,6 @@
       name="viewport"
       content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
     />
-    
     
     
Date: Fri, 16 Sep 2022 19:55:30 +0530
Subject: [PATCH 13/23] Test docs:verify

---
 src/docs/classDiagram.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/docs/classDiagram.md b/src/docs/classDiagram.md
index 87d76cd37..f46d3689c 100644
--- a/src/docs/classDiagram.md
+++ b/src/docs/classDiagram.md
@@ -493,14 +493,14 @@ It is also possible to attach a class to a list of nodes in one statement:
 
 A shorter form of adding a class is to attach the classname to the node using the `:::` operator:
 
-```mmd
+```mermaid-example
 classDiagram
     class Animal:::cssClass
 ```
 
 Or:
 
-```mmd
+```mermaid-example
 classDiagram
     class Animal:::cssClass {
         -int sizeInFeet

From 5c8fda3891427993cba4d383e40afb83b63304b3 Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Sat, 17 Sep 2022 22:27:49 +0100
Subject: [PATCH 14/23] ci(e2e-applitols): add applitools CI action

Adds a [`workflow_dispatch`][1] GitHub Actions CI workflow
that performs an E2E Cypress rendering test with Applitools
integration.

If the APPLITOOLS_API_KEY secret is not set, this action fallsback to
just doing a normal E2E Cypress test without Applitools support.

[1]: https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow
---
 .github/workflows/e2e-applitools.yml | 73 ++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)
 create mode 100644 .github/workflows/e2e-applitools.yml

diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml
new file mode 100644
index 000000000..b0319b072
--- /dev/null
+++ b/.github/workflows/e2e-applitools.yml
@@ -0,0 +1,73 @@
+name: E2E (Applitools)
+
+on:
+  workflow_dispatch:
+    # Because we want to limit Applitools usage, so we only want to start this
+    # workflow on rare occasions/manually.
+    inputs:
+      parent_branch:
+        required: true
+        type: string
+        default: master
+        description: 'Parent branch to use for PRs'
+
+permissions:
+  contents: read
+
+env:
+  # on PRs from forks, this secret will always be empty, for security reasons
+  USE_APPLI: ${{ secrets.APPLITOOLS_API_KEY && 'true' || '' }}
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node-version: [16.x]
+    steps:
+      - if: ${{ ! env.USE_APPLI }}
+        name: Warn if not using Applitools
+        run: |
+          echo "::error,title=Not using Applitols::APPLITOOLS_API_KEY is empty, disabling Applitools for this run."
+      - uses: actions/checkout@v3
+      - name: Setup Node.js ${{ matrix.node-version }}
+        uses: actions/setup-node@v3
+        with:
+          cache: yarn
+          node-version: ${{ matrix.node-version }}
+
+      - name: Install Yarn
+        run: npm i yarn --global
+
+      - name: Install Packages
+        run: |
+          yarn install --frozen-lockfile
+        env:
+          CYPRESS_CACHE_FOLDER: .cache/Cypress
+
+      - name: Run Build
+        run: yarn build
+
+      - if: ${{ env.USE_APPLI }}
+        name: Notify applitools of new batch
+        # Copied from docs https://applitools.com/docs/topics/integrations/github-integration-ci-setup.html
+        run: curl -L -d '' -X POST "$APPLITOOLS_SERVER_URL/api/externals/github/push?apiKey=$APPLITOOLS_API_KEY&CommitSha=$GITHUB_SHA&BranchName=${APPLITOOLS_BRANCH}$&ParentBranchName=$APPLITOOLS_PARENT_BRANCH"
+        env:
+          # e.g. mermaid-js/mermaid/my-branch
+          APPLITOOLS_BRANCH: ${{ github.repository }}/${{ github.ref_name }}
+          APPLITOOLS_PARENT_BRANCH: ${{ github.inputs.parent_branch }}
+          APPLITOOLS_API_KEY: ${{ secrets.APPLITOOLS_API_KEY }}
+          APPLITOOLS_SERVER_URL: 'https://eyesapi.applitools.com'
+
+      - name: Run E2E Tests
+        run: yarn e2e
+        env:
+          CYPRESS_CACHE_FOLDER: .cache/Cypress
+          # Mermaid applitools.config.js uses this to pick batch name.
+          APPLI_BRANCH: ${{ github.ref_name }}
+          APPLITOOLS_BATCH_ID: ${{ github.sha }}
+          # e.g. mermaid-js/mermaid/my-branch
+          APPLITOOLS_BRANCH: ${{ github.repository }}/${{ github.ref_name }}
+          APPLITOOLS_PARENT_BRANCH: ${{ github.inputs.parent_branch }}
+          APPLITOOLS_API_KEY: ${{ secrets.APPLITOOLS_API_KEY }}
+          APPLITOOLS_SERVER_URL: 'https://eyesapi.applitools.com'

From ae920eaa9384a9f11f8aff11ba73968f19e59c27 Mon Sep 17 00:00:00 2001
From: Elliot Nelson 
Date: Sat, 17 Sep 2022 23:57:32 -0400
Subject: [PATCH 15/23] feat(git): cherry-pick keyword supports tag attribute

---
 .../integration/rendering/gitGraph.spec.js    | 23 ++++++++++--
 docs/gitgraph.md                              | 36 +++++++++++++++++++
 src/diagrams/git/gitGraphAst.js               |  8 +++--
 src/diagrams/git/parser/gitGraph.jison        |  4 ++-
 4 files changed, 65 insertions(+), 6 deletions(-)

diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js
index b0d65d0cc..3331f0d33 100644
--- a/cypress/integration/rendering/gitGraph.spec.js
+++ b/cypress/integration/rendering/gitGraph.spec.js
@@ -180,7 +180,27 @@ describe('Git Graph diagram', () => {
       {}
     );
   });
-
+  it('11: should render a gitgraph with cherry pick commit with custom tag', () => {
+    imgSnapshotTest(
+      `
+    gitGraph
+       commit id: "ZERO"
+       branch develop
+       commit id:"A"
+       checkout main
+       commit id:"ONE"
+       checkout develop
+       commit id:"B"
+       checkout main
+       commit id:"TWO"
+       cherry-pick id:"A" tag: "snapshot"
+       commit id:"THREE"
+       checkout develop
+       commit id:"C"
+      `,
+      {}
+    );
+  });
   it('11: should render a simple gitgraph with two cherry pick commit', () => {
     imgSnapshotTest(
       `
@@ -207,7 +227,6 @@ describe('Git Graph diagram', () => {
       {}
     );
   });
-
   it('12: should render commits for more than 8 branches', () => {
     imgSnapshotTest(
       `
diff --git a/docs/gitgraph.md b/docs/gitgraph.md
index 5f86cf53c..bdf2f3a5a 100644
--- a/docs/gitgraph.md
+++ b/docs/gitgraph.md
@@ -393,6 +393,42 @@ Let see an example:
        commit id:"C"
 ```
 
+By default, the cherry-picked commit from commit with id `A` will be labeled `cherry-pick:A`. You can provide your own custom tag instead to override this behavior, using the same syntax as the `commit` keyword:
+
+```mermaid-example
+    gitGraph
+       commit id: "ZERO"
+       branch develop
+       commit id:"A"
+       checkout main
+       commit id:"ONE"
+       checkout develop
+       commit id:"B"
+       checkout main
+       commit id:"TWO"
+       cherry-pick id:"A" tag:"fix"
+       commit id:"THREE"
+       checkout develop
+       commit id:"C"
+```
+
+```mermaid
+    gitGraph
+       commit id: "ZERO"
+       branch develop
+       commit id:"A"
+       checkout main
+       commit id:"ONE"
+       checkout develop
+       commit id:"B"
+       checkout main
+       commit id:"TWO"
+       cherry-pick id:"A" tag:"fix"
+       commit id:"THREE"
+       checkout develop
+       commit id:"C"
+```
+
 ## Gitgraph specific configuration options
 
 In Mermaid, you have the option to configure the gitgraph diagram. You can configure the following options:
diff --git a/src/diagrams/git/gitGraphAst.js b/src/diagrams/git/gitGraphAst.js
index fb9bb100d..e222a046b 100644
--- a/src/diagrams/git/gitGraphAst.js
+++ b/src/diagrams/git/gitGraphAst.js
@@ -258,9 +258,11 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
   log.debug('in mergeBranch');
 };
 
-export const cherryPick = function (sourceId, targetId) {
+export const cherryPick = function (sourceId, targetId, tag) {
+  log.debug('Entering cherryPick:', sourceId, targetId, tag);
   sourceId = common.sanitizeText(sourceId, configApi.getConfig());
   targetId = common.sanitizeText(targetId, configApi.getConfig());
+  tag = common.sanitizeText(tag, configApi.getConfig());
 
   if (!sourceId || typeof commits[sourceId] === 'undefined') {
     let error = new Error(
@@ -328,13 +330,13 @@ export const cherryPick = function (sourceId, targetId) {
       parents: [head == null ? null : head.id, sourceCommit.id],
       branch: curBranch,
       type: commitType.CHERRY_PICK,
-      tag: 'cherry-pick:' + sourceCommit.id,
+      tag: tag ? tag : 'cherry-pick:' + sourceCommit.id,
     };
     head = commit;
     commits[commit.id] = commit;
     branches[curBranch] = commit.id;
     log.debug(branches);
-    log.debug('in cheeryPick');
+    log.debug('in cherryPick');
   }
 };
 export const checkout = function (branch) {
diff --git a/src/diagrams/git/parser/gitGraph.jison b/src/diagrams/git/parser/gitGraph.jison
index f35dbcde3..b3fb805af 100644
--- a/src/diagrams/git/parser/gitGraph.jison
+++ b/src/diagrams/git/parser/gitGraph.jison
@@ -117,7 +117,9 @@ branchStatement
     ;
 
 cherryPickStatement
-    : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3)}
+    : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '')}
+    | CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, $4)}
+    | CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($4, $3)}
     ;
 
 mergeStatement

From c39a6f27d47f4ea0f67e8a58d31ae717902c6794 Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Sun, 18 Sep 2022 06:59:44 +0100
Subject: [PATCH 16/23] test(git): add basic parsing test for cherry-pick

Currently, cherry-pick in gitGraphs only has e2e tests,
no parsing unit tests.
---
 src/diagrams/git/gitGraphParserV2.spec.js | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/diagrams/git/gitGraphParserV2.spec.js b/src/diagrams/git/gitGraphParserV2.spec.js
index b6c9c2459..8db1d6074 100644
--- a/src/diagrams/git/gitGraphParserV2.spec.js
+++ b/src/diagrams/git/gitGraphParserV2.spec.js
@@ -611,6 +611,22 @@ describe('when parsing a gitGraph', function () {
     ]);
   });
 
+  it('should support cherry-picking commits', function () {
+    const str = `gitGraph
+    commit id: "ZERO"
+    branch develop
+    commit id:"A"
+    checkout main
+    cherry-pick id:"A"
+    `;
+
+    parser.parse(str);
+    const commits = parser.yy.getCommits();
+    const cherryPickCommitID = Object.keys(commits)[2];
+    expect(commits[cherryPickCommitID].tag).toBe('cherry-pick:A');
+    expect(commits[cherryPickCommitID].branch).toBe('main');
+  });
+
   it('should throw error when try to branch existing branch: main', function () {
     const str = `gitGraph
     commit

From aba458b832bd85f25d5c5b205500c16bb93c6eb3 Mon Sep 17 00:00:00 2001
From: Alois Klink 
Date: Sun, 18 Sep 2022 07:00:55 +0100
Subject: [PATCH 17/23] fix(git): fix cherry-pick regex parsing error

I forgot to escape the `-` character in a regex statement.

Fixes: 152795666932cf92af33635d2f98dcbe93e911ba
---
 src/diagrams/git/parser/gitGraph.jison | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/diagrams/git/parser/gitGraph.jison b/src/diagrams/git/parser/gitGraph.jison
index f35dbcde3..a7a10912b 100644
--- a/src/diagrams/git/parser/gitGraph.jison
+++ b/src/diagrams/git/parser/gitGraph.jison
@@ -47,7 +47,7 @@ commit(?=\s|$)                          return 'COMMIT';
 branch(?=\s|$)                          return 'BRANCH';
 "order:"                                return 'ORDER';
 merge(?=\s|$)                           return 'MERGE';
-cherry-pick(?=\s|$)                     return 'CHERRY_PICK';
+cherry\-pick(?=\s|$)                    return 'CHERRY_PICK';
 // "reset"                                 return 'RESET';
 checkout(?=\s|$)                        return 'CHECKOUT';
 "LR"                                    return 'DIR';

From 4f96116c43257d7e34e5d09b2c8db524e8e51156 Mon Sep 17 00:00:00 2001
From: Elliot Nelson 
Date: Sun, 18 Sep 2022 08:59:47 -0400
Subject: [PATCH 18/23] Update src/diagrams/git/parser/gitGraph.jison

Co-authored-by: Alois Klink 
---
 src/diagrams/git/parser/gitGraph.jison | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/diagrams/git/parser/gitGraph.jison b/src/diagrams/git/parser/gitGraph.jison
index b3fb805af..18eca1f08 100644
--- a/src/diagrams/git/parser/gitGraph.jison
+++ b/src/diagrams/git/parser/gitGraph.jison
@@ -117,9 +117,9 @@ branchStatement
     ;
 
 cherryPickStatement
-    : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '')}
-    | CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, $4)}
-    | CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($4, $3)}
+    : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', '')}
+    | CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, '', $5)}
+    | CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($5, '', $3)}
     ;
 
 mergeStatement

From 4e4b5ccf8d4cd08a4d2174784802ff159e6e9a7d Mon Sep 17 00:00:00 2001
From: Elliot Nelson 
Date: Sun, 18 Sep 2022 09:22:35 -0400
Subject: [PATCH 19/23] feat(git): allow cherry-pick to suppress tag altogether

---
 .../integration/rendering/gitGraph.spec.js    | 21 +++++++++++++++++++
 docs/gitgraph.md                              |  2 ++
 src/diagrams/git/gitGraphAst.js               |  6 +++++-
 src/diagrams/git/parser/gitGraph.jison        |  2 +-
 4 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js
index 3331f0d33..afb39b62e 100644
--- a/cypress/integration/rendering/gitGraph.spec.js
+++ b/cypress/integration/rendering/gitGraph.spec.js
@@ -201,6 +201,27 @@ describe('Git Graph diagram', () => {
       {}
     );
   });
+  it('11: should render a gitgraph with cherry pick commit with no tag', () => {
+    imgSnapshotTest(
+      `
+    gitGraph
+       commit id: "ZERO"
+       branch develop
+       commit id:"A"
+       checkout main
+       commit id:"ONE"
+       checkout develop
+       commit id:"B"
+       checkout main
+       commit id:"TWO"
+       cherry-pick id:"A" tag: ""
+       commit id:"THREE"
+       checkout develop
+       commit id:"C"
+      `,
+      {}
+    );
+  });
   it('11: should render a simple gitgraph with two cherry pick commit', () => {
     imgSnapshotTest(
       `
diff --git a/docs/gitgraph.md b/docs/gitgraph.md
index bdf2f3a5a..863681085 100644
--- a/docs/gitgraph.md
+++ b/docs/gitgraph.md
@@ -429,6 +429,8 @@ By default, the cherry-picked commit from commit with id `A` will be labeled `ch
        commit id:"C"
 ```
 
+To suppress the tag entirely, use `tag:""` (empty string).
+
 ## Gitgraph specific configuration options
 
 In Mermaid, you have the option to configure the gitgraph diagram. You can configure the following options:
diff --git a/src/diagrams/git/gitGraphAst.js b/src/diagrams/git/gitGraphAst.js
index e222a046b..3a6260d4f 100644
--- a/src/diagrams/git/gitGraphAst.js
+++ b/src/diagrams/git/gitGraphAst.js
@@ -262,6 +262,10 @@ export const cherryPick = function (sourceId, targetId, tag) {
   log.debug('Entering cherryPick:', sourceId, targetId, tag);
   sourceId = common.sanitizeText(sourceId, configApi.getConfig());
   targetId = common.sanitizeText(targetId, configApi.getConfig());
+
+  if (tag === 'cherry-pick:') {
+    tag = 'cherry-pick:' + sourceCommit.id;
+  }
   tag = common.sanitizeText(tag, configApi.getConfig());
 
   if (!sourceId || typeof commits[sourceId] === 'undefined') {
@@ -330,7 +334,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
       parents: [head == null ? null : head.id, sourceCommit.id],
       branch: curBranch,
       type: commitType.CHERRY_PICK,
-      tag: tag ? tag : 'cherry-pick:' + sourceCommit.id,
+      tag: tag,
     };
     head = commit;
     commits[commit.id] = commit;
diff --git a/src/diagrams/git/parser/gitGraph.jison b/src/diagrams/git/parser/gitGraph.jison
index 18eca1f08..057186dee 100644
--- a/src/diagrams/git/parser/gitGraph.jison
+++ b/src/diagrams/git/parser/gitGraph.jison
@@ -117,7 +117,7 @@ branchStatement
     ;
 
 cherryPickStatement
-    : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', '')}
+    : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', 'cherry-pick:')}
     | CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, '', $5)}
     | CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($5, '', $3)}
     ;

From 9cbacb01590cf2824da5436ce5d1ab9ac407189d Mon Sep 17 00:00:00 2001
From: Elliot Nelson 
Date: Sun, 18 Sep 2022 16:25:10 -0400
Subject: [PATCH 20/23] Use undefined to mean default tagging behavior

---
 src/diagrams/git/gitGraphAst.js        | 6 +-----
 src/diagrams/git/parser/gitGraph.jison | 2 +-
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/diagrams/git/gitGraphAst.js b/src/diagrams/git/gitGraphAst.js
index 3a6260d4f..41130c780 100644
--- a/src/diagrams/git/gitGraphAst.js
+++ b/src/diagrams/git/gitGraphAst.js
@@ -262,10 +262,6 @@ export const cherryPick = function (sourceId, targetId, tag) {
   log.debug('Entering cherryPick:', sourceId, targetId, tag);
   sourceId = common.sanitizeText(sourceId, configApi.getConfig());
   targetId = common.sanitizeText(targetId, configApi.getConfig());
-
-  if (tag === 'cherry-pick:') {
-    tag = 'cherry-pick:' + sourceCommit.id;
-  }
   tag = common.sanitizeText(tag, configApi.getConfig());
 
   if (!sourceId || typeof commits[sourceId] === 'undefined') {
@@ -334,7 +330,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
       parents: [head == null ? null : head.id, sourceCommit.id],
       branch: curBranch,
       type: commitType.CHERRY_PICK,
-      tag: tag,
+      tag: tag ?? 'cherry-pick:' + sourceCommit.id,
     };
     head = commit;
     commits[commit.id] = commit;
diff --git a/src/diagrams/git/parser/gitGraph.jison b/src/diagrams/git/parser/gitGraph.jison
index 68b6a7af5..74d785f83 100644
--- a/src/diagrams/git/parser/gitGraph.jison
+++ b/src/diagrams/git/parser/gitGraph.jison
@@ -117,7 +117,7 @@ branchStatement
     ;
 
 cherryPickStatement
-    : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', 'cherry-pick:')}
+    : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', undefined)}
     | CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, '', $5)}
     | CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($5, '', $3)}
     ;

From 183fc35fea95d9d78bf9b9c1200d6ee33cc4035b Mon Sep 17 00:00:00 2001
From: Elliot Nelson 
Date: Mon, 19 Sep 2022 00:04:23 -0400
Subject: [PATCH 21/23] Support EMPTYSTR in jison parser, add unit tests for
 git graph parser

---
 src/diagrams/git/gitGraphParserV2.spec.js | 32 +++++++++++++++++++++++
 src/diagrams/git/parser/gitGraph.jison    |  3 +++
 2 files changed, 35 insertions(+)

diff --git a/src/diagrams/git/gitGraphParserV2.spec.js b/src/diagrams/git/gitGraphParserV2.spec.js
index 8db1d6074..7aab8fc7c 100644
--- a/src/diagrams/git/gitGraphParserV2.spec.js
+++ b/src/diagrams/git/gitGraphParserV2.spec.js
@@ -627,6 +627,38 @@ describe('when parsing a gitGraph', function () {
     expect(commits[cherryPickCommitID].branch).toBe('main');
   });
 
+  it('should support cherry-picking commits with custom tag', function () {
+    const str = `gitGraph
+    commit id: "ZERO"
+    branch develop
+    commit id:"A"
+    checkout main
+    cherry-pick id:"A" tag:"MyTag"
+    `;
+
+    parser.parse(str);
+    const commits = parser.yy.getCommits();
+    const cherryPickCommitID = Object.keys(commits)[2];
+    expect(commits[cherryPickCommitID].tag).toBe('MyTag');
+    expect(commits[cherryPickCommitID].branch).toBe('main');
+  });
+
+  it('should support cherry-picking commits with no tag', function () {
+    const str = `gitGraph
+    commit id: "ZERO"
+    branch develop
+    commit id:"A"
+    checkout main
+    cherry-pick id:"A" tag:""
+    `;
+
+    parser.parse(str);
+    const commits = parser.yy.getCommits();
+    const cherryPickCommitID = Object.keys(commits)[2];
+    expect(commits[cherryPickCommitID].tag).toBe('');
+    expect(commits[cherryPickCommitID].branch).toBe('main');
+  });
+
   it('should throw error when try to branch existing branch: main', function () {
     const str = `gitGraph
     commit
diff --git a/src/diagrams/git/parser/gitGraph.jison b/src/diagrams/git/parser/gitGraph.jison
index 74d785f83..dbe220e15 100644
--- a/src/diagrams/git/parser/gitGraph.jison
+++ b/src/diagrams/git/parser/gitGraph.jison
@@ -57,6 +57,7 @@ checkout(?=\s|$)                        return 'CHECKOUT';
 "options"\r?\n                          this.begin("options"); //
 [ \r\n\t]+"end"                this.popState();       // not used anymore in the renderer, fixed for backward compatibility
 [\s\S]+(?=[ \r\n\t]+"end")     return 'OPT';          //
+["]["]                                  return 'EMPTYSTR';
 ["]                                     this.begin("string");
 ["]                             this.popState();
 [^"]*                           return 'STR';
@@ -119,7 +120,9 @@ branchStatement
 cherryPickStatement
     : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', undefined)}
     | CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, '', $5)}
+    | CHERRY_PICK COMMIT_ID STR COMMIT_TAG EMPTYSTR {yy.cherryPick($3, '', '')}
     | CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($5, '', $3)}
+    | CHERRY_PICK COMMIT_TAG EMPTYSTR COMMIT_ID STR {yy.cherryPick($3, '', '')}
     ;
 
 mergeStatement

From 8e3f9868ebf4c275f28d98329f111c4ac02f3965 Mon Sep 17 00:00:00 2001
From: mmorel-35 
Date: Mon, 19 Sep 2022 07:18:46 +0000
Subject: [PATCH 22/23] chore: update browsers list

---
 yarn.lock | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index d09bf07ad..0d2aeda30 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4087,15 +4087,10 @@ camelcase@^6.2.0:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
   integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
 
-caniuse-lite@^1.0.30001359:
-  version "1.0.30001397"
-  resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001397.tgz"
-  integrity sha512-SW9N2TbCdLf0eiNDRrrQXx2sOkaakNZbCjgNpPyMJJbiOrU5QzMIrXOVMRM1myBXTD5iTkdrtU/EguCrBocHlA==
-
-caniuse-lite@^1.0.30001400:
-  version "1.0.30001402"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001402.tgz#aa29e1f47f5055b0d0c07696a67b8b08023d14c8"
-  integrity sha512-Mx4MlhXO5NwuvXGgVb+hg65HZ+bhUYsz8QtDGDo2QmaJS2GBX47Xfi2koL86lc8K+l+htXeTEB/Aeqvezoo6Ew==
+caniuse-lite@^1.0.30001359, caniuse-lite@^1.0.30001400:
+  version "1.0.30001406"
+  resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001406.tgz"
+  integrity sha512-bWTlaXUy/rq0BBtYShc/jArYfBPjEV95euvZ8JVtO43oQExEN/WquoqpufFjNu4kSpi5cy5kMbNvzztWDfv1Jg==
 
 capture-exit@^2.0.0:
   version "2.0.0"

From b72dad9c5b4dcce79e1d7f4bc7fef76e985f15e8 Mon Sep 17 00:00:00 2001
From: Sidharth Vinod 
Date: Tue, 20 Sep 2022 22:23:59 +0530
Subject: [PATCH 23/23] Revert #3475

Was an accidental merge.
---
 docs/classDiagram.md | 17 +++++++++++++++--
 docs/gitgraph.md     | 38 --------------------------------------
 docs/index.html      |  1 +
 3 files changed, 16 insertions(+), 40 deletions(-)

diff --git a/docs/classDiagram.md b/docs/classDiagram.md
index 6c9ae96fe..3f12dddf8 100644
--- a/docs/classDiagram.md
+++ b/docs/classDiagram.md
@@ -660,14 +660,27 @@ It is also possible to attach a class to a list of nodes in one statement:
 
 A shorter form of adding a class is to attach the classname to the node using the `:::` operator:
 
-```mmd
+```mermaid-example
+classDiagram
+    class Animal:::cssClass
+```
+
+```mermaid
 classDiagram
     class Animal:::cssClass
 ```
 
 Or:
 
-```mmd
+```mermaid-example
+classDiagram
+    class Animal:::cssClass {
+        -int sizeInFeet
+        -canEat()
+    }
+```
+
+```mermaid
 classDiagram
     class Animal:::cssClass {
         -int sizeInFeet
diff --git a/docs/gitgraph.md b/docs/gitgraph.md
index 863681085..5f86cf53c 100644
--- a/docs/gitgraph.md
+++ b/docs/gitgraph.md
@@ -393,44 +393,6 @@ Let see an example:
        commit id:"C"
 ```
 
-By default, the cherry-picked commit from commit with id `A` will be labeled `cherry-pick:A`. You can provide your own custom tag instead to override this behavior, using the same syntax as the `commit` keyword:
-
-```mermaid-example
-    gitGraph
-       commit id: "ZERO"
-       branch develop
-       commit id:"A"
-       checkout main
-       commit id:"ONE"
-       checkout develop
-       commit id:"B"
-       checkout main
-       commit id:"TWO"
-       cherry-pick id:"A" tag:"fix"
-       commit id:"THREE"
-       checkout develop
-       commit id:"C"
-```
-
-```mermaid
-    gitGraph
-       commit id: "ZERO"
-       branch develop
-       commit id:"A"
-       checkout main
-       commit id:"ONE"
-       checkout develop
-       commit id:"B"
-       checkout main
-       commit id:"TWO"
-       cherry-pick id:"A" tag:"fix"
-       commit id:"THREE"
-       checkout develop
-       commit id:"C"
-```
-
-To suppress the tag entirely, use `tag:""` (empty string).
-
 ## Gitgraph specific configuration options
 
 In Mermaid, you have the option to configure the gitgraph diagram. You can configure the following options:
diff --git a/docs/index.html b/docs/index.html
index 22892d60b..0a41b516c 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -15,6 +15,7 @@
       name="viewport"
       content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
     />
+