mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-25 02:09:50 +02:00
test: test that styles and themes return valid CSS
Test that `src/diagrams/*/styles.ts` module returns a valid CSS stylesheet that can be parsed via [stylis][1] and then becomes a valid CSS that [csstree-validator][2] validates. We test this for every diagram and for every theme, because many of the invalid CSS bugs are caused by missing theme vars. There are some CSS errors that I couldn't easily fix, so I've written the tests to ignore the following CSS errors: - 'Unknown property `rx`' (Valid in SVG2 draft and in some browsers) - 'Unknown property `ry`' (Valid in SVG2 draft and in some browsers) - 'Unknown property `dy`' - This doesn't seem to be valid CSS in any SVG version, but maybe some browsers support it 🤷 I feel like we should probably change this though. [1]: https://github.com/thysultan/stylis [2]: https://github.com/csstree/validator
This commit is contained in:
@@ -87,6 +87,7 @@
|
||||
"coveralls": "^3.1.1",
|
||||
"cpy-cli": "^4.2.0",
|
||||
"cspell": "^6.14.3",
|
||||
"csstree-validator": "^3.0.0",
|
||||
"globby": "^13.1.2",
|
||||
"jison": "^0.4.18",
|
||||
"js-base64": "^3.7.2",
|
||||
|
120
packages/mermaid/src/styles.spec.ts
Normal file
120
packages/mermaid/src/styles.spec.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { vi } from 'vitest';
|
||||
|
||||
// @ts-expect-error This module has no TypeScript types
|
||||
import { validate } from 'csstree-validator';
|
||||
import { compile, serialize, stringify } from 'stylis';
|
||||
|
||||
import { getConfig } from './config';
|
||||
import theme from './themes';
|
||||
|
||||
/**
|
||||
* Import the getStyles function from each diagram.
|
||||
*
|
||||
* Unfortunately, we can't use the `diagrams/*?/*Detector.ts` functions,
|
||||
* because many of the diagrams have a circular dependency import error
|
||||
* (they import mermaidAPI.js, which imports diagramOrchestrator.js, which causes a loop)
|
||||
*/
|
||||
import c4 from './diagrams/c4/styles';
|
||||
import classDiagram from './diagrams/class/styles';
|
||||
import flowchart from './diagrams/flowchart/styles';
|
||||
import flowchartElk from './diagrams/flowchart/elk/styles';
|
||||
import er from './diagrams/er/styles';
|
||||
import error from './diagrams/error/styles';
|
||||
import git from './diagrams/git/styles';
|
||||
import gantt from './diagrams/gantt/styles';
|
||||
import info from './diagrams/info/styles';
|
||||
import pie from './diagrams/pie/styles';
|
||||
import requirement from './diagrams/requirement/styles';
|
||||
import sequence from './diagrams/sequence/styles';
|
||||
import state from './diagrams/state/styles';
|
||||
import journey from './diagrams/user-journey/styles';
|
||||
import timeline from './diagrams/timeline/styles';
|
||||
import mindmap from './diagrams/mindmap/styles';
|
||||
import themes from './themes';
|
||||
|
||||
async function checkValidStylisCSSStyleSheet(stylisString: string) {
|
||||
const cssString = serialize(compile(`#my-svg-id{${stylisString}}`), stringify);
|
||||
const errors = validate(cssString, 'this-file-was-created-by-tests.css') as Error[];
|
||||
|
||||
const unexpectedErrors = errors.filter((error) => {
|
||||
const cssErrorsToIgnore = [
|
||||
// Valid in SVG2, see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/rx
|
||||
// Ideally, we'd remove this, since some browsers do not support SVG2.
|
||||
'Unknown property `rx`',
|
||||
// Valid in SVG2, see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/ry
|
||||
'Unknown property `ry`',
|
||||
// TODO: I'm pretty sure that even in SVG2, this isn't allowed to be a CSS
|
||||
// attribute.
|
||||
'Unknown property `dy`',
|
||||
];
|
||||
return !cssErrorsToIgnore.some((cssErrorToIgnore) => error.message.match(cssErrorToIgnore));
|
||||
});
|
||||
|
||||
if (unexpectedErrors.length > 0) {
|
||||
throw new Error(
|
||||
`The given CSS string was invalid: ${errors}.\n\n` +
|
||||
'Copy the below CSS into https://jigsaw.w3.org/css-validator/validator to help debug where the invalid CSS is:\n\n' +
|
||||
`Original CSS value was ${cssString}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
describe('styles', () => {
|
||||
beforeEach(() => {
|
||||
// resets the styles added to addStylesForDiagram()
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
describe('getStyles', () => {
|
||||
test('should return a valid style for an empty type', async () => {
|
||||
const { default: getStyles, addStylesForDiagram } = await import('./styles');
|
||||
|
||||
const diagramType = 'my-custom-mocked-type-with-no-styles';
|
||||
const myTypeGetStylesFunc = vi.fn().mockReturnValue('');
|
||||
|
||||
addStylesForDiagram(diagramType, myTypeGetStylesFunc);
|
||||
|
||||
const styles = getStyles(diagramType, '', getConfig().themeVariables);
|
||||
|
||||
await checkValidStylisCSSStyleSheet(styles);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test CSS for each diagram type and each theme.
|
||||
*/
|
||||
for (const themeId of Object.keys(theme) as (keyof typeof theme)[]) {
|
||||
for (const [diagramId, getDiagramStyles] of Object.entries({
|
||||
c4,
|
||||
classDiagram,
|
||||
er,
|
||||
error,
|
||||
flowchart,
|
||||
flowchartElk,
|
||||
gantt,
|
||||
git,
|
||||
info,
|
||||
journey,
|
||||
mindmap,
|
||||
pie,
|
||||
requirement,
|
||||
sequence,
|
||||
state,
|
||||
timeline,
|
||||
})) {
|
||||
test(`should return a valid style for diagram ${diagramId} and theme ${themeId}`, async () => {
|
||||
const { default: getStyles, addStylesForDiagram } = await import('./styles');
|
||||
|
||||
addStylesForDiagram(diagramId, getDiagramStyles);
|
||||
const styles = getStyles(
|
||||
diagramId,
|
||||
'',
|
||||
// @ts-expect-error This will probably be broken until we create a proper Themes type.
|
||||
themes[themeId].getThemeVariables()
|
||||
);
|
||||
|
||||
await checkValidStylisCSSStyleSheet(styles);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user