diff --git a/.github/lychee.toml b/.github/lychee.toml
index c5a2f0e45..2e3b08c41 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 6477c9eb5..a75bbf83d 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/README.md b/README.md
index d368a4349..8d5eebfeb 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 4182d92a8..3346b5549 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 d7db7dfb5..89feb8a29 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 544eab40f..ad6b21e29 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 d041fa5f4..1e51d2f23 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', () => {
diff --git a/cypress/integration/rendering/c4.spec.js b/cypress/integration/rendering/c4.spec.js
index 59af6504b..f699bd429 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 cab3649df..a98a359ed 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 578f5a398..1a2340906 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 83a1455c6..4830db656 100644
--- a/cypress/integration/rendering/quadrantChart.spec.js
+++ b/cypress/integration/rendering/quadrantChart.spec.js
@@ -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,7 +213,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('it should render data points with styles', () => {
@@ -249,7 +237,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('it should render data points with styles + classes', () => {
diff --git a/cypress/integration/rendering/requirement.spec.js b/cypress/integration/rendering/requirement.spec.js
index f33ae7a0c..343441848 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 1285a0832..7b4e98b4d 100644
--- a/cypress/integration/rendering/sequencediagram.spec.js
+++ b/cypress/integration/rendering/sequencediagram.spec.js
@@ -1,4 +1,4 @@
-///
+ sequenceDiagram + participant Alice + participant Bob + Alice<<->>Bob: Hello! + Alice<<->>Bob: Wow, we said that at the same time! + Bob<<-->>Alice: Bidirectional Arrows are so cool +