diff --git a/.github/lychee.toml b/.github/lychee.toml
index c5a2f0e452..5f435fd37a 100644
--- a/.github/lychee.toml
+++ b/.github/lychee.toml
@@ -41,7 +41,10 @@ exclude = [
"https://bundlephobia.com",
# Chrome webstore migration issue. Temporary
-"https://chromewebstore.google.com"
+"https://chromewebstore.google.com",
+
+# Drupal 403
+"https://www.drupal.org"
]
# Exclude all private IPs from checking.
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 6477c9eb5c..a75bbf83d8 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -1,9 +1,3 @@
-# We use github cache to save snapshots between runs.
-# For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
-# If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
-# These are then downloaded before running the E2E, providing the reference snapshots.
-# If there are any errors, the diff image is uploaded to artifacts, and the user is notified.
-
name: E2E
on:
@@ -72,16 +66,6 @@ jobs:
mkdir -p cypress/snapshots/stats/base
mv stats cypress/snapshots/stats/base
- - name: Cypress run
- uses: cypress-io/github-action@v6
- id: cypress-snapshot-gen
- if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
- with:
- install: false
- start: pnpm run dev
- wait-on: 'http://localhost:9000'
- browser: chrome
-
e2e:
runs-on: ubuntu-latest
container:
@@ -146,6 +130,10 @@ jobs:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
VITEST_COVERAGE: true
CYPRESS_COMMIT: ${{ github.sha }}
+ ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
+ ARGOS_PARALLEL: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
+ ARGOS_PARALLEL_TOTAL: 4
+ ARGOS_PARALLEL_INDEX: ${{ matrix.containers }}
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v4
@@ -158,55 +146,3 @@ jobs:
fail_ci_if_error: false
verbose: true
token: 6845cc80-77ee-4e17-85a1-026cd95e0766
-
- # We upload the artifacts into numbered archives to prevent overwriting
- - name: Upload Artifacts
- uses: actions/upload-artifact@v4
- if: ${{ always() }}
- with:
- name: snapshots-${{ matrix.containers }}
- retention-days: 1
- path: ./cypress/snapshots
-
- combineArtifacts:
- needs: e2e
- runs-on: ubuntu-latest
- if: ${{ always() }}
- steps:
- # Download all snapshot artifacts and merge them into a single folder
- - name: Download All Artifacts
- uses: actions/download-artifact@v4
- with:
- path: snapshots
- pattern: snapshots-*
- merge-multiple: true
-
- # For successful push events, we save the snapshots cache
- - name: Save snapshots cache
- id: cache-upload
- if: ${{ github.event_name == 'push' && needs.e2e.result != 'failure' }}
- uses: actions/cache/save@v4
- with:
- path: ./snapshots
- key: ${{ runner.os }}-snapshots-${{ github.event.after }}
-
- - name: Flatten images to a folder
- if: ${{ needs.e2e.result == 'failure' }}
- run: |
- mkdir errors
- cd snapshots
- find . -mindepth 2 -type d -name "*__diff_output__*" -exec sh -c 'mv "$0"/*.png ../errors/' {} \;
-
- - name: Upload Error snapshots
- if: ${{ needs.e2e.result == 'failure' }}
- uses: actions/upload-artifact@v4
- id: upload-artifacts
- with:
- name: error-snapshots
- retention-days: 10
- path: errors/
-
- - name: Notify Users
- if: ${{ needs.e2e.result == 'failure' }}
- run: |
- echo "::error title=Visual tests failed::You can view images that failed by downloading the error-snapshots artifact: ${{ steps.upload-artifacts.outputs.artifact-url }}"
diff --git a/.gitignore b/.gitignore
index a0fd1c50b8..7948faee49 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,6 +48,7 @@ demos/dev/**
!/demos/dev/example.html
!/demos/dev/reload.js
tsx-0/**
+vite.config.ts.timestamp-*
# autogenereated by langium-cli
-generated/
\ No newline at end of file
+generated/
diff --git a/FUNDING.json b/FUNDING.json
new file mode 100644
index 0000000000..a1df876f47
--- /dev/null
+++ b/FUNDING.json
@@ -0,0 +1,7 @@
+{
+ "drips": {
+ "ethereum": {
+ "ownedBy": "0x0831DDFe60d009d9448CC976157b539089aB821E"
+ }
+ }
+}
diff --git a/README.md b/README.md
index d368a43499..8d5eebfebe 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,7 @@ Try Live Editor previews of future releases:
diff --git a/cypress.config.ts b/cypress.config.ts
index 4182d92a87..3346b5549b 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -2,6 +2,8 @@ import { defineConfig } from 'cypress';
import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin';
import coverage from '@cypress/code-coverage/task';
import eyesPlugin from '@applitools/eyes-cypress';
+import { registerArgosTask } from '@argos-ci/cypress/task';
+
export default eyesPlugin(
defineConfig({
projectId: 'n2sma2',
@@ -17,10 +19,17 @@ export default eyesPlugin(
}
return launchOptions;
});
- addMatchImageSnapshotPlugin(on, config);
// copy any needed variables from process.env to config.env
config.env.useAppli = process.env.USE_APPLI ? true : false;
+ config.env.useArgos = !!process.env.CI;
+ if (config.env.useArgos) {
+ registerArgosTask(on, config, {
+ token: 'fc3a35cf5200db928d65b2047861582d9444032b',
+ });
+ } else {
+ addMatchImageSnapshotPlugin(on, config);
+ }
// do not forget to return the changed config object!
return config;
},
diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts
index aed5d7973c..17bebeaef8 100644
--- a/cypress/helpers/util.ts
+++ b/cypress/helpers/util.ts
@@ -95,19 +95,8 @@ export const openURLAndVerifyRendering = (
options: CypressMermaidConfig,
validation?: any
): void => {
- const useAppli: boolean = Cypress.env('useAppli');
const name: string = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
- if (useAppli) {
- cy.log(`Opening eyes ${Cypress.spec.name} --- ${name}`);
- cy.eyesOpen({
- appName: 'Mermaid',
- testName: name,
- batchName: Cypress.spec.name,
- batchId: batchId + Cypress.spec.name,
- });
- }
-
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
@@ -116,11 +105,27 @@ export const openURLAndVerifyRendering = (
cy.get('svg').should(validation);
}
+ verifyScreenshot(name);
+};
+
+export const verifyScreenshot = (name: string): void => {
+ const useAppli: boolean = Cypress.env('useAppli');
+ const useArgos: boolean = Cypress.env('useArgos');
+
if (useAppli) {
+ cy.log(`Opening eyes ${Cypress.spec.name} --- ${name}`);
+ cy.eyesOpen({
+ appName: 'Mermaid',
+ testName: name,
+ batchName: Cypress.spec.name,
+ batchId: batchId + Cypress.spec.name,
+ });
cy.log(`Check eyes ${Cypress.spec.name}`);
cy.eyesCheckWindow('Click!');
cy.log(`Closing eyes ${Cypress.spec.name}`);
cy.eyesClose();
+ } else if (useArgos) {
+ cy.argosScreenshot(name);
} else {
cy.matchImageSnapshot(name);
}
diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js
index 544eab40fb..ad6b21e298 100644
--- a/cypress/integration/other/configuration.spec.js
+++ b/cypress/integration/other/configuration.spec.js
@@ -1,4 +1,4 @@
-import { renderGraph } from '../../helpers/util.ts';
+import { renderGraph, verifyScreenshot } from '../../helpers/util.ts';
describe('Configuration', () => {
describe('arrowMarkerAbsolute', () => {
it('should handle default value false of arrowMarkerAbsolute', () => {
@@ -119,8 +119,7 @@ describe('Configuration', () => {
const url = 'http://localhost:9000/regression/issue-1874.html';
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
- cy.get('svg').should('be.visible');
- cy.matchImageSnapshot(
+ verifyScreenshot(
'configuration.spec-should-not-taint-initial-configuration-when-using-multiple-directives'
);
});
@@ -145,7 +144,7 @@ describe('Configuration', () => {
// none of the diagrams should be error diagrams
expect($svg).to.not.contain('Syntax error');
});
- cy.matchImageSnapshot(
+ verifyScreenshot(
'configuration.spec-should-not-render-error-diagram-if-suppressErrorRendering-is-set'
);
});
@@ -162,7 +161,7 @@ describe('Configuration', () => {
// some of the diagrams should be error diagrams
expect($svg).to.contain('Syntax error');
});
- cy.matchImageSnapshot(
+ verifyScreenshot(
'configuration.spec-should-render-error-diagram-if-suppressErrorRendering-is-not-set'
);
});
diff --git a/cypress/integration/other/xss.spec.js b/cypress/integration/other/xss.spec.js
index 678040f98a..1e51d2f234 100644
--- a/cypress/integration/other/xss.spec.js
+++ b/cypress/integration/other/xss.spec.js
@@ -10,7 +10,6 @@ describe('XSS', () => {
cy.wait(1000).then(() => {
cy.get('.mermaid').should('exist');
});
- cy.get('svg');
});
it('should not allow tags in the css', () => {
@@ -137,4 +136,9 @@ describe('XSS', () => {
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
+ it('should sanitize backticks block diagram labels properly', () => {
+ cy.visit('http://localhost:9000/xss25.html');
+ cy.wait(1000);
+ cy.get('#the-malware').should('not.exist');
+ });
});
diff --git a/cypress/integration/rendering/c4.spec.js b/cypress/integration/rendering/c4.spec.js
index 59af6504b9..f699bd4298 100644
--- a/cypress/integration/rendering/c4.spec.js
+++ b/cypress/integration/rendering/c4.spec.js
@@ -30,7 +30,6 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a simple C4Container diagram', () => {
imgSnapshotTest(
@@ -50,7 +49,6 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a simple C4Component diagram', () => {
imgSnapshotTest(
@@ -69,7 +67,6 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a simple C4Dynamic diagram', () => {
imgSnapshotTest(
@@ -93,7 +90,6 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a simple C4Deployment diagram', () => {
imgSnapshotTest(
@@ -117,6 +113,5 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
});
diff --git a/cypress/integration/rendering/classDiagram.spec.js b/cypress/integration/rendering/classDiagram.spec.js
index cab3649df4..a98a359edf 100644
--- a/cypress/integration/rendering/classDiagram.spec.js
+++ b/cypress/integration/rendering/classDiagram.spec.js
@@ -32,7 +32,6 @@ describe('Class diagram', () => {
`,
{ logLevel: 1 }
);
- cy.get('svg');
});
it('2: should render a simple class diagrams with cardinality', () => {
@@ -61,7 +60,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('3: should render a simple class diagram with different visibilities', () => {
@@ -79,7 +77,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('4: should render a simple class diagram with comments', () => {
@@ -109,7 +106,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('5: should render a simple class diagram with abstract method', () => {
@@ -121,7 +117,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('6: should render a simple class diagram with static method', () => {
@@ -133,7 +128,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('7: should render a simple class diagram with Generic class', () => {
@@ -153,7 +147,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('8: should render a simple class diagram with Generic class and relations', () => {
@@ -174,7 +167,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('9: should render a simple class diagram with clickable link', () => {
@@ -196,7 +188,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('10: should render a simple class diagram with clickable callback', () => {
@@ -218,7 +209,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('11: should render a simple class diagram with return type on method', () => {
@@ -233,7 +223,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('12: should render a simple class diagram with generic types', () => {
@@ -249,7 +238,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('13: should render a simple class diagram with css classes applied', () => {
@@ -267,7 +255,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('14: should render a simple class diagram with css classes applied directly', () => {
@@ -283,7 +270,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('15: should render a simple class diagram with css classes applied to multiple classes', () => {
@@ -298,7 +284,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('16: should render multiple class diagrams', () => {
@@ -351,7 +336,6 @@ describe('Class diagram', () => {
],
{}
);
- cy.get('svg');
});
// it('17: should render a class diagram when useMaxWidth is true (default)', () => {
@@ -421,7 +405,6 @@ describe('Class diagram', () => {
`,
{ logLevel: 1 }
);
- cy.get('svg');
});
it('should render class diagram with newlines in title', () => {
@@ -439,7 +422,6 @@ describe('Class diagram', () => {
+quack()
}
`);
- cy.get('svg');
});
it('should render class diagram with many newlines in title', () => {
diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js
index 578f5a3984..1a2340906a 100644
--- a/cypress/integration/rendering/erDiagram.spec.js
+++ b/cypress/integration/rendering/erDiagram.spec.js
@@ -218,7 +218,6 @@ describe('Entity Relationship Diagram', () => {
`,
{ loglevel: 1 }
);
- cy.get('svg');
});
it('should render entities with keys', () => {
diff --git a/cypress/integration/rendering/quadrantChart.spec.js b/cypress/integration/rendering/quadrantChart.spec.js
index 1be1f7deff..4830db6568 100644
--- a/cypress/integration/rendering/quadrantChart.spec.js
+++ b/cypress/integration/rendering/quadrantChart.spec.js
@@ -1,4 +1,4 @@
-import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
+import { imgSnapshotTest } from '../../helpers/util.ts';
describe('Quadrant Chart', () => {
it('should render if only chart type is provided', () => {
@@ -8,7 +8,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a complete quadrant chart', () => {
imgSnapshotTest(
@@ -30,7 +29,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render without points', () => {
imgSnapshotTest(
@@ -46,7 +44,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should able to render y-axix on right side', () => {
imgSnapshotTest(
@@ -63,7 +60,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should able to render x-axix on bottom', () => {
imgSnapshotTest(
@@ -80,7 +76,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should able to render x-axix on bottom and y-axis on right', () => {
imgSnapshotTest(
@@ -97,7 +92,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render without title', () => {
imgSnapshotTest(
@@ -112,7 +106,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should use all the config', () => {
imgSnapshotTest(
@@ -135,7 +128,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should use all the theme variable', () => {
imgSnapshotTest(
@@ -158,7 +150,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render x-axis labels in the center, if x-axis has two labels', () => {
imgSnapshotTest(
@@ -180,7 +171,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render y-axis labels in the center, if y-axis has two labels', () => {
imgSnapshotTest(
@@ -202,7 +192,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render both axes labels on the left and bottom, if both axes have only one label', () => {
imgSnapshotTest(
@@ -224,6 +213,52 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
+ });
+
+ it('it should render data points with styles', () => {
+ imgSnapshotTest(
+ `
+ quadrantChart
+ title Reach and engagement of campaigns
+ x-axis Reach -->
+ y-axis Engagement -->
+ quadrant-1 We should expand
+ quadrant-2 Need to promote
+ quadrant-3 Re-evaluate
+ quadrant-4 May be improved
+ Campaign A: [0.3, 0.6] radius: 20
+ Campaign B: [0.45, 0.23] color: #ff0000
+ Campaign C: [0.57, 0.69] stroke-color: #ff00ff
+ Campaign D: [0.78, 0.34] stroke-width: 3px
+ Campaign E: [0.40, 0.34] radius: 20, color: #ff0000 , stroke-color : #ff00ff, stroke-width : 3px
+ Campaign F: [0.35, 0.78] stroke-width: 3px , color: #ff0000, radius: 20, stroke-color: #ff00ff
+ Campaign G: [0.22, 0.22] stroke-width: 3px , color: #309708 , radius : 20 , stroke-color: #5060ff
+ Campaign H: [0.22, 0.44]
+ `,
+ {}
+ );
+ });
+
+ it('it should render data points with styles + classes', () => {
+ imgSnapshotTest(
+ `
+ quadrantChart
+ title Reach and engagement of campaigns
+ x-axis Reach -->
+ y-axis Engagement -->
+ quadrant-1 We should expand
+ quadrant-2 Need to promote
+ quadrant-3 Re-evaluate
+ quadrant-4 May be improved
+ Campaign A:::class1: [0.3, 0.6] radius: 20
+ Campaign B: [0.45, 0.23] color: #ff0000
+ Campaign C: [0.57, 0.69] stroke-color: #ff00ff
+ Campaign D:::class2: [0.78, 0.34] stroke-width: 3px
+ Campaign E:::class2: [0.40, 0.34] radius: 20, color: #ff0000, stroke-color: #ff00ff, stroke-width: 3px
+ Campaign F:::class1: [0.35, 0.78]
+ classDef class1 color: #908342, radius : 10, stroke-color: #310085, stroke-width: 10px
+ classDef class2 color: #f00fff, radius : 10
+ `
+ );
});
});
diff --git a/cypress/integration/rendering/requirement.spec.js b/cypress/integration/rendering/requirement.spec.js
index f33ae7a0cb..3434418483 100644
--- a/cypress/integration/rendering/requirement.spec.js
+++ b/cypress/integration/rendering/requirement.spec.js
@@ -44,6 +44,5 @@ describe('Requirement diagram', () => {
`,
{}
);
- cy.get('svg');
});
});
diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js
index 1285a0832d..7b4e98b4d5 100644
--- a/cypress/integration/rendering/sequencediagram.spec.js
+++ b/cypress/integration/rendering/sequencediagram.spec.js
@@ -1,4 +1,4 @@
-///