diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml index dd97b49e1..6aaa91eb8 100644 --- a/.github/workflows/e2e-applitools.yml +++ b/.github/workflows/e2e-applitools.yml @@ -23,9 +23,6 @@ env: jobs: e2e-applitools: runs-on: ubuntu-latest - container: - image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1 - options: --user 1001 steps: - if: ${{ ! env.USE_APPLI }} name: Warn if not using Applitools diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index b52cba06b..af3185bf5 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -68,7 +68,7 @@ }, "dependencies": { "@braintree/sanitize-url": "^7.0.4", - "@iconify/utils": "^2.1.33", + "@iconify/utils": "^3.0.1", "@mermaid-js/parser": "workspace:^", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", diff --git a/packages/mermaid/src/docs/package.json b/packages/mermaid/src/docs/package.json index 6ec43eb93..b5b95e7c0 100644 --- a/packages/mermaid/src/docs/package.json +++ b/packages/mermaid/src/docs/package.json @@ -31,7 +31,7 @@ "fast-glob": "^3.3.3", "https-localhost": "^4.7.1", "pathe": "^2.0.3", - "unocss": "^66.0.0", + "unocss": "^66.4.2", "unplugin-vue-components": "^28.4.0", "vite": "^6.1.1", "vite-plugin-pwa": "^1.0.0", diff --git a/packages/mermaid/src/mermaidAPI.spec.ts b/packages/mermaid/src/mermaidAPI.spec.ts index b2d2d3cd3..ff794abb1 100644 --- a/packages/mermaid/src/mermaidAPI.spec.ts +++ b/packages/mermaid/src/mermaidAPI.spec.ts @@ -41,7 +41,6 @@ import { decodeEntities, encodeEntities } from './utils.js'; import { toBase64 } from './utils/base64.js'; import { StateDB } from './diagrams/state/stateDb.js'; import { ensureNodeFromSelector, jsdomIt } from './tests/util.js'; -import { select } from 'd3'; import { JSDOM } from 'jsdom'; /** @@ -50,7 +49,6 @@ import { JSDOM } from 'jsdom'; */ // ------------------------------------------------------------------------------------- - describe('mermaidAPI', () => { describe('encodeEntities', () => { it('removes the ending ; from style [text1]:[optional word]#[text2]; with ', () => { @@ -913,4 +911,241 @@ graph TD;A--x|text including URL space|B;`) expect(sequenceDiagram1.db.getActors()).not.toEqual(sequenceDiagram2.db.getActors()); }); }); + + describe('mermaidAPI config precedence', () => { + const id = 'mermaid-config-test'; + + beforeEach(() => { + mermaidAPI.globalReset(); + }); + + jsdomIt('renders with YAML config taking precedence over initialize config', async () => { + mermaid.initialize({ + theme: 'forest', + fontFamily: 'Arial', + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + flowchart: { htmlLabels: false }, + }); + + const diagramText = `--- +config: + theme: base + fontFamily: Courier + themeVariables: + fontFamily: "Courier New" + fontSize: "20px" + flowchart: + htmlLabels: true +--- +flowchart TD + A --> B +`; + + const { svg } = await mermaidAPI.render('yaml-over-init', diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.theme).toBe('base'); + expect(config.fontFamily).toBe('Courier'); + expect(config.themeVariables.fontFamily).toBe('Courier New'); + expect(config.themeVariables.fontSize).toBe('20px'); + expect(config.flowchart?.htmlLabels).toBe(true); + + const svgNode = ensureNodeFromSelector('svg', new JSDOM(svg).window.document); + expect(svgNode).not.toBeNull(); + }); + + jsdomIt( + 'renders with YAML themeVariables fully overriding initialize themeVariables', + async () => { + mermaid.initialize({ + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + }); + + const diagramText = `--- +config: + themeVariables: + fontFamily: "Courier New" + fontSize: "20px" +--- +flowchart TD + A --> B +`; + + const { svg } = await mermaidAPI.render(id, diagramText); + const config = mermaidAPI.getConfig(); + + expect(config.themeVariables.fontFamily).toBe('Courier New'); + expect(config.themeVariables.fontSize).toBe('20px'); + expect(config.themeVariables.fontFamily).not.toBe('Arial'); + expect(config.themeVariables.fontSize).not.toBe('16px'); + + const svgNode = ensureNodeFromSelector('svg', new JSDOM(svg).window.document); + expect(svgNode).not.toBeNull(); + } + ); + + jsdomIt( + 'renders with YAML themeVariables overriding only provided keys and keeping others from initialize', + async () => { + mermaid.initialize({ + theme: 'forest', + fontFamily: 'Arial', + themeVariables: { fontFamily: 'Arial', fontSize: '16px', colorPrimary: '#ff0000' }, + }); + + const diagramText = `--- +config: + themeVariables: + fontFamily: "Courier New" +--- +flowchart TD + A --> B +`; + + const { svg } = await mermaidAPI.render(id, diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.themeVariables.fontFamily).toBe('Courier New'); + expect(config.themeVariables.fontSize).toBe('16px'); + expect(config.themeVariables.colorPrimary).toBe('#ff0000'); + + const svgNode = ensureNodeFromSelector('svg', new JSDOM(svg).window.document); + expect(svgNode).not.toBeNull(); + } + ); + + jsdomIt( + 'renders with YAML config (no themeVariables) and falls back to initialize themeVariables', + async () => { + mermaid.initialize({ + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + }); + + const diagramText = `--- +config: + theme: base +--- +flowchart TD + A --> B +`; + + const { svg } = await mermaidAPI.render(id, diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.themeVariables.fontFamily).toBe('Arial'); + expect(config.themeVariables.fontSize).toBe('16px'); + expect(config.theme).toBe('base'); + + const svgNode = ensureNodeFromSelector('svg', new JSDOM(svg).window.document); + expect(svgNode).not.toBeNull(); + } + ); + + jsdomIt( + 'renders with full YAML config block taking full precedence over initialize config', + async () => { + mermaid.initialize({ + theme: 'forest', + fontFamily: 'Arial', + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + flowchart: { htmlLabels: false }, + }); + + const diagramText = `--- +config: + theme: base + fontFamily: Courier + themeVariables: + fontFamily: "Courier New" + fontSize: "20px" + flowchart: + htmlLabels: true +--- +flowchart TD + A --> B +`; + + const { svg } = await mermaidAPI.render('yaml-over-init', diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.theme).toBe('base'); + expect(config.fontFamily).toBe('Courier'); + expect(config.themeVariables.fontFamily).toBe('Courier New'); + expect(config.themeVariables.fontSize).toBe('20px'); + expect(config.flowchart?.htmlLabels).toBe(true); + + const svgNode = ensureNodeFromSelector('svg', new JSDOM(svg).window.document); + expect(svgNode).not.toBeNull(); + } + ); + + jsdomIt( + 'renders with YAML config (no themeVariables) and falls back to initialize themeVariables (duplicate scenario)', + async () => { + mermaid.initialize({ + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + }); + + const diagramText = `--- +config: + theme: base +--- +flowchart TD + A --> B +`; + + await mermaidAPI.render(id, diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.themeVariables.fontFamily).toBe('Arial'); + expect(config.themeVariables.fontSize).toBe('16px'); + expect(config.theme).toBe('base'); + } + ); + + jsdomIt('renders with no YAML config so initialize config is fully applied', async () => { + mermaid.initialize({ + theme: 'forest', + fontFamily: 'Arial', + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + }); + + const diagramText = ` +flowchart TD + A --> B +`; + + await mermaidAPI.render(id, diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.theme).toBe('forest'); + expect(config.fontFamily).toBe('Arial'); + expect(config.themeVariables.fontFamily).toBe('Arial'); + expect(config.themeVariables.fontSize).toBe('16px'); + }); + + jsdomIt( + 'renders with empty YAML config block and falls back to initialize config', + async () => { + mermaid.initialize({ + theme: 'dark', + themeVariables: { fontFamily: 'Times', fontSize: '14px' }, + }); + + const diagramText = `--- +config: {} +--- +flowchart TD + A --> B +`; + + await mermaidAPI.render(id, diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.theme).toBe('dark'); + expect(config.themeVariables.fontFamily).toBe('Times'); + expect(config.themeVariables.fontSize).toBe('14px'); + } + ); + }); });