diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..bbda16a66 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [knsv] +#patreon: # Replace with a single Patreon username +#open_collective: # Replace with a single Open Collective username +#ko_fi: # Replace with a single Ko-fi username +#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +#liberapay: # Replace with a single Liberapay username +#issuehunt: # Replace with a single IssueHunt username +#otechie: # Replace with a single Otechie username +#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/pr-labeler.yml b/.github/pr-labeler.yml new file mode 100644 index 000000000..077cc568b --- /dev/null +++ b/.github/pr-labeler.yml @@ -0,0 +1,3 @@ +'Type: Bug / Error': 'bug/*' +'Type: Enhancement': 'feature/*' +'Type: Other': 'other/*' diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 000000000..311b03c8e --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,23 @@ +name-template: '$NEXT_PATCH_VERSION' +tag-template: '$NEXT_PATCH_VERSION' +categories: + - title: '🚀 Features' + labels: + - 'Type: Enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'Type: Bug / Error' + - title: '🧰 Maintenance' + label: 'Type: Other' +change-template: '- $TITLE (#$NUMBER) @$AUTHOR' +branches: + - develop +exclude-labels: + - 'Skip changelog' +no-changes-template: 'This release contains minor changes and bugfixes.' +template: | + # Release Notes + + $CHANGES + + 🎉 **Thanks to all contributors helping with this release!** 🎉 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..8f0f3b95d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,58 @@ +name: Build + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [10.x, 12.x] + steps: + - uses: actions/checkout@v1 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: Install Yarn + run: npm i yarn --global + + - name: Cache Node Modules + uses: actions/cache@v1 + with: + path: .cache + key: ${{ runner.OS }}-build-${{ hashFiles('**/yarn.lock') }} + + - name: Install Packages + run: | + yarn config set cache-folder $GITHUB_WORKSPACE/.cache/yarn + yarn install --frozen-lockfile + env: + CYPRESS_CACHE_FOLDER: ../../.cache/Cypress + + - name: Run Build + run: yarn build + + - name: Run Unit Tests + run: | + yarn test --coverage + + - name: Upload Test Results + uses: coverallsapp/github-action@v1.0.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + parallel: true + + - name: Run E2E Tests + run: yarn e2e + env: + PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} + CYPRESS_CACHE_FOLDER: .cache/Cypress + + - name: Post Upload Test Results + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + parallel-finished: true diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml new file mode 100644 index 000000000..b6fab95ce --- /dev/null +++ b/.github/workflows/pr-labeler.yml @@ -0,0 +1,13 @@ +name: Apply labels to PR +on: + pull_request: + types: [opened] + +jobs: + pr-labeler: + runs-on: ubuntu-latest + steps: + - name: Label PR + uses: TimonVS/pr-labeler-action@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-draft.yml b/.github/workflows/release-draft.yml new file mode 100644 index 000000000..68be3a1e5 --- /dev/null +++ b/.github/workflows/release-draft.yml @@ -0,0 +1,15 @@ +name: Draft Release + +on: + push: + branches: + - develop + +jobs: + draft-release: + runs-on: ubuntu-latest + steps: + - name: Draft Release + uses: toolmantim/release-drafter@v5.2.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-preview-publish.yml b/.github/workflows/release-preview-publish.yml index b6c29b6a3..e7d5b994c 100644 --- a/.github/workflows/release-preview-publish.yml +++ b/.github/workflows/release-preview-publish.yml @@ -8,15 +8,12 @@ on: jobs: publish: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [10.x] steps: - uses: actions/checkout@v1 - - name: Setup Node.js ${{ matrix.node-version }} + - name: Setup Node.js uses: actions/setup-node@v1 with: - node-version: ${{ matrix.node-version }} + node-version: 10.x - name: Install Yarn run: npm i yarn --global @@ -24,7 +21,7 @@ jobs: run: npm i json --global - name: Install Packages - run: yarn install + run: yarn install --frozen-lockfile - name: Publish run: | diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml new file mode 100644 index 000000000..0f8227359 --- /dev/null +++ b/.github/workflows/release-publish.yml @@ -0,0 +1,46 @@ +name: Publish release + +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: fregante/setup-git-token@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v1 + with: + node-version: 10.x + - name: Install Yarn + run: npm i yarn --global + + - name: Install Json + run: npm i json --global + + - name: Install Packages + run: yarn install --frozen-lockfile + + - name: Prepare release + run: | + VERSION=${GITHUB_REF:10} + echo "Preparing release $VERSION" + git checkout -t origin/release/$VERSION + npm version --no-git-tag-version --allow-same-version $VERSION + git add package.json + git commit -m "Bump version $VERSION" + git checkout -t origin/master + git merge -m "Release $VERSION" --no-ff release/$VERSION + git push --no-verify + + - name: Publish + run: | + npm set //registry.npmjs.org/:_authToken $NPM_TOKEN + npm publish + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index ee8fae482..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "typescript.format.enable": false, - "typescript.reportStyleChecksAsWarnings": false, - "typescript.validate.enable": false, - "javascript.validate.enable": false, - "editor.formatOnSave": false, - "editor.snippetSuggestions": "top" -} diff --git a/README.md b/README.md index 4fe123369..d09567c52 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ C -->|One| D[Result 1] C -->|Two| E[Result 2] - + @@ -49,7 +49,7 @@ John->>Bob: How about you? Bob-->>John: Jolly good! - + @@ -70,7 +70,7 @@ Parallel 3 : des5, after des3, 1d Parallel 4 : des6, after des4, 1d - + @@ -99,7 +99,7 @@ class Class10 { } - + @@ -119,7 +119,7 @@ Moving --> Crash Crash --> [*] - + @@ -136,7 +136,7 @@ pie "Rats" : 15 - + diff --git a/babel.config.js b/babel.config.js index c76a0d028..c0f6a11d4 100644 --- a/babel.config.js +++ b/babel.config.js @@ -3,9 +3,7 @@ module.exports = { [ '@babel/preset-env', { - targets: { - node: 'current' - } + targets: "defaults, ie >= 11, current node" } ] ] diff --git a/cypress/helpers/util.js b/cypress/helpers/util.js index 143fc7ed7..a3f92defd 100644 --- a/cypress/helpers/util.js +++ b/cypress/helpers/util.js @@ -26,3 +26,9 @@ export const imgSnapshotTest = (graphStr, options, api) => { cy.get('svg'); cy.percySnapshot(); }; + +export const renderGraph = (graphStr, options, api) => { + const url = mermaidUrl(graphStr, options, api); + + cy.visit(url); +}; diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js new file mode 100644 index 000000000..c2f1533b2 --- /dev/null +++ b/cypress/integration/other/configuration.spec.js @@ -0,0 +1,100 @@ +import { renderGraph } from '../../helpers/util'; +/* eslint-env jest */ +describe('Configuration', () => { + describe('arrowMarkerAbsolute', () => { + it('should handle default value false of arrowMarkerAbsolute', () => { + renderGraph( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { } + ); + + // 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(#'); + }); + it('should handle default value false of arrowMarkerAbsolute', () => { + renderGraph( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { } + ); + + // 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(#'); + }); + it('should handle arrowMarkerAbsolute excplicitly set to false', () => { + renderGraph( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { + arrowMarkerAbsolute: false + } + ); + + // 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(#'); + }); + it('should handle arrowMarkerAbsolute excplicitly set to "false" as false', () => { + renderGraph( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { + arrowMarkerAbsolute: "false" + } + ); + + // 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(#'); + }); + it('should handle arrowMarkerAbsolute set to true', () => { + renderGraph( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { + arrowMarkerAbsolute: true + } + ); + + cy.get('.edgePath path').first().should('have.attr', 'marker-end') + .should('exist') + .and('include', 'url(http://localhost'); + }); + }); +}); diff --git a/cypress/integration/other/interaction.spec.js b/cypress/integration/other/interaction.spec.js index cfd937e63..6d37af273 100644 --- a/cypress/integration/other/interaction.spec.js +++ b/cypress/integration/other/interaction.spec.js @@ -16,7 +16,7 @@ describe('Interaction', () => { cy.viewport(1440, 1024); cy.visit(url); cy.get('body') - .find('g#mermaid-dom-id-1Function') + .find('g[id="1Function"]') .click(); cy.get('.created-by-click').should('have.text', 'Clicked By Flow'); @@ -38,7 +38,7 @@ describe('Interaction', () => { cy.viewport(1440, 1024); cy.visit(url); cy.get('body') - .find('g#mermaid-dom-id-2URL') + .find('g[id="2URL"]') .click(); cy.location().should(location => { @@ -108,7 +108,7 @@ describe('Interaction', () => { cy.viewport(1440, 1024); cy.visit(url); cy.get('body') - .find('g#mermaid-dom-id-1Function') + .find('g[id="1Function"]') .click(); cy.get('.created-by-click').should('not.have.text', 'Clicked By Flow'); @@ -130,7 +130,7 @@ describe('Interaction', () => { cy.viewport(1440, 1024); cy.visit(url); cy.get('body') - .find('g#mermaid-dom-id-2URL') + .find('g[id="2URL"]') .click(); cy.location().should(location => { @@ -200,7 +200,7 @@ describe('Interaction', () => { cy.viewport(1440, 1024); cy.visit(url); cy.get('body') - .find('g#mermaid-dom-id-1Function') + .find('g[id="1Function"]') .click(); cy.get('.created-by-click').should('not.have.text', 'Clicked By Flow'); diff --git a/cypress/integration/other/xss.spec.js b/cypress/integration/other/xss.spec.js index a550b6783..7f2ea8d4d 100644 --- a/cypress/integration/other/xss.spec.js +++ b/cypress/integration/other/xss.spec.js @@ -9,8 +9,27 @@ describe('XSS', () => { const url = mermaidUrl(str,{}, true); cy.visit(url); + cy.wait(1000).then(()=>{ + cy.get('.mermaid').should('exist'); + }); cy.get('svg') - cy.percySnapshot() + // cy.percySnapshot() + + }) + it('should handle xss in tags in non-html mode', () => { + const str = 'eyJjb2RlIjoiXG5ncmFwaCBMUlxuICAgICAgQi0tPkQoPGltZyBvbmVycm9yPWxvY2F0aW9uPWBqYXZhc2NyaXB0XFx1MDAzYXhzc0F0dGFja1xcdTAwMjhkb2N1bWVudC5kb21haW5cXHUwMDI5YCBzcmM9eD4pOyIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0IiwiZmxvd2NoYXJ0Ijp7Imh0bWxMYWJlbHMiOmZhbHNlfX19'; + + const url = mermaidUrl(str,{ + "theme": "default", + "flowchart": { + "htmlMode": false + } + }, true); + + cy.visit(url); + // cy.get('svg') + // cy.percySnapshot() + cy.get('.malware').should('not.exist'); }) }) diff --git a/cypress/integration/rendering/classDiagram.spec.js b/cypress/integration/rendering/classDiagram.spec.js index 1e4585149..29c4aba86 100644 --- a/cypress/integration/rendering/classDiagram.spec.js +++ b/cypress/integration/rendering/classDiagram.spec.js @@ -2,7 +2,7 @@ import { imgSnapshotTest } from '../../helpers/util'; describe('Class diagram', () => { - it('should render a simple class diagram', () => { + it('1: should render a simple class diagram', () => { imgSnapshotTest( ` classDiagram @@ -19,6 +19,9 @@ describe('Class diagram', () => { Class01 : size() Class01 : int chimp Class01 : int gorilla + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset Class08 <--> C2: Cool label class Class10 { <<service>> @@ -30,7 +33,8 @@ describe('Class diagram', () => { ); cy.get('svg'); }); - it('should render a simple class diagrams with cardinality', () => { + + it('2: should render a simple class diagrams with cardinality', () => { imgSnapshotTest( ` classDiagram @@ -58,6 +62,25 @@ describe('Class diagram', () => { ); cy.get('svg'); }); + + it('should render a simple class diagram with different visibilities', () => { + imgSnapshotTest( + ` + classDiagram + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class01 : -int privateMethod() + Class01 : +int publicMethod() + Class01 : #int protectedMethod() + Class01 : -int privateChimp + Class01 : +int publicGorilla + Class01 : #int protectedMarmoset + `, + {} + ); + cy.get('svg'); + }); + it('should render multiple class diagrams', () => { imgSnapshotTest( [ @@ -110,4 +133,34 @@ describe('Class diagram', () => { ); cy.get('svg'); }); + + it('4: should render a simple class diagram with comments', () => { + imgSnapshotTest( + ` + classDiagram + %% this is a comment + Class01 <|-- AveryLongClass : Cool + <<interface>> Class01 + Class03 *-- Class04 + Class05 o-- Class06 + Class07 .. Class08 + Class09 --> C2 : Where am i? + Class09 --* C3 + Class09 --|> Class07 + Class07 : equals() + Class07 : Object[] elementData + Class01 : size() + Class01 : int chimp + Class01 : int gorilla + Class08 <--> C2: Cool label + class Class10 { + <<service>> + int id + test() + } + `, + {} + ); + cy.get('svg'); + }); }); diff --git a/cypress/integration/rendering/current.spec.js b/cypress/integration/rendering/current.spec.js index 1ed1c9d5c..bd4eb888b 100644 --- a/cypress/integration/rendering/current.spec.js +++ b/cypress/integration/rendering/current.spec.js @@ -2,19 +2,20 @@ import { imgSnapshotTest } from '../../helpers/util'; describe('State diagram', () => { - it('should render a flowchart full of circles', () => { + it('should render a state with states in it', () => { imgSnapshotTest( ` - stateDiagram - State1: The state with a note - note right of State1 - Important information! You\ncan write - notes with multiple lines... - Here is another line... - And another line... - end note + stateDiagram + state PersonalizedCockpit { + Other + state Parent { + C + } + } `, - {} + { + logLevel: 0, + } ); }); }); diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index 5cf75f53e..a27f3b50c 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -14,6 +14,7 @@ describe('Flowcart', () => { { flowchart: { htmlLabels: false } } ); }); + it('2: should render a simple flowchart with htmlLabels', () => { imgSnapshotTest( `graph TD @@ -26,6 +27,7 @@ describe('Flowcart', () => { { flowchart: { htmlLabels: true } } ); }); + it('3: should render a simple flowchart with line breaks', () => { imgSnapshotTest( ` @@ -99,6 +101,7 @@ describe('Flowcart', () => { {} ); }); + it('6: should render a flowchart full of icons', () => { imgSnapshotTest( ` @@ -178,6 +181,7 @@ describe('Flowcart', () => { {} ); }); + it('8: should render subgraphs', () => { imgSnapshotTest( ` @@ -190,7 +194,7 @@ describe('Flowcart', () => { ); }); - it('9: should render subgraphs with a title startign with a digit', () => { + it('9: should render subgraphs with a title starting with a digit', () => { imgSnapshotTest( ` graph TB @@ -237,7 +241,7 @@ describe('Flowcart', () => { ); }); - it('11: should render a flowchart with ling sames and class definitoins', () => { + it('11: should render a flowchart with long names and class definitions', () => { imgSnapshotTest( `graph LR sid-B3655226-6C29-4D00-B685-3D5C734DC7E1[" @@ -356,6 +360,7 @@ describe('Flowcart', () => { } ); }); + it('13: should render hexagons', () => { imgSnapshotTest( ` @@ -377,4 +382,18 @@ describe('Flowcart', () => { } ); }); + + it('14: should render a simple flowchart with comments', () => { + imgSnapshotTest( + `graph TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + %% this is a comment + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, + { flowchart: { htmlLabels: false } } + ); + }); }); diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js index 0a72cdee2..47d7bce9d 100644 --- a/cypress/integration/rendering/gitGraph.spec.js +++ b/cypress/integration/rendering/gitGraph.spec.js @@ -2,20 +2,20 @@ import { imgSnapshotTest } from '../../helpers/util.js'; describe('Sequencediagram', () => { - it('should render a simple git graph', () => { - imgSnapshotTest( - ` - gitGraph: - commit - branch newbranch - checkout newbranch - commit - commit - checkout master - commit - commit - merge newbranch`, - { logLevel: 0 } - ); - }); + // it('should render a simple git graph', () => { + // imgSnapshotTest( + // ` + // gitGraph: + // commit + // branch newbranch + // checkout newbranch + // commit + // commit + // checkout master + // commit + // commit + // merge newbranch`, + // { logLevel: 0 } + // ); + // }); }); diff --git a/cypress/integration/rendering/stateDiagram.spec.js b/cypress/integration/rendering/stateDiagram.spec.js index e4231261e..0ab68713e 100644 --- a/cypress/integration/rendering/stateDiagram.spec.js +++ b/cypress/integration/rendering/stateDiagram.spec.js @@ -106,6 +106,22 @@ describe('State diagram', () => { ); cy.get('svg'); }); + it('should render a note with multiple lines in it', () => { + imgSnapshotTest( + ` + stateDiagram + State1: The state with a note + note right of State1 + Important information! You\ncan write + notes with multiple lines... + Here is another line... + And another line... + end note + `, + {} + ); + }); + it('should render a states with descriptions including multi-line descriptions', () => { imgSnapshotTest( ` @@ -276,4 +292,33 @@ describe('State diagram', () => { ); cy.get('svg'); }); + it('should render a state with states in it', () => { + imgSnapshotTest( + ` + stateDiagram + state PilotCockpit { + state Parent { + C + } + } + `, + { + logLevel: 0, + } + ); + }); + it('Simplest compone state', () => { + imgSnapshotTest( + ` + stateDiagram + state Parent { + C + } + `, + { + logLevel: 0, + } + ); + }); + }); diff --git a/cypress/platform/current.html b/cypress/platform/current.html new file mode 100644 index 000000000..283deff93 --- /dev/null +++ b/cypress/platform/current.html @@ -0,0 +1,41 @@ + + + + + + +

info below

+
+
graph TD + A ==> B + A --> C + A -.-> D +
+
+ + + + + diff --git a/cypress/platform/e2e.html b/cypress/platform/e2e.html index 4384fd0ec..010b11cb2 100644 --- a/cypress/platform/e2e.html +++ b/cypress/platform/e2e.html @@ -4,9 +4,8 @@