Merge pull request #4295 from aloisklink/test/test-styles

Fix and test a bunch of invalid CSS issues
This commit is contained in:
Sidharth Vinod
2023-04-13 18:08:11 +05:30
committed by GitHub
10 changed files with 227 additions and 47 deletions

View File

@@ -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",

View File

@@ -41,7 +41,7 @@ const getStyles = (options) =>
.divider {
stroke: ${options.nodeBorder};
stroke: 1;
stroke-width: 1;
}
g.clickable {

View File

@@ -16,7 +16,7 @@ const getStyles = (options) => `
.reqBox {
fill: ${options.requirementBackground};
fill-opacity: 100%;
fill-opacity: 1.0;
stroke: ${options.requirementBorderColor};
stroke-width: ${options.requirementBorderSize};
}
@@ -26,7 +26,7 @@ const getStyles = (options) => `
}
.reqLabelBox {
fill: ${options.relationLabelBackground};
fill-opacity: 100%;
fill-opacity: 1.0;
}
.req-title-line {

View 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);
});
}
}
});
});

View File

@@ -46,8 +46,12 @@ class Theme {
this.secondaryTextColor = this.secondaryTextColor || invert(this.secondaryColor);
this.tertiaryTextColor = this.tertiaryTextColor || invert(this.tertiaryColor);
this.lineColor = this.lineColor || invert(this.background);
this.arrowheadColor = this.arrowheadColor || invert(this.background);
this.textColor = this.textColor || this.primaryTextColor;
// TODO: should this instead default to secondaryBorderColor?
this.border2 = this.border2 || this.tertiaryBorderColor;
/* Flowchart variables */
this.nodeBkg = this.nodeBkg || this.primaryColor;
this.mainBkg = this.mainBkg || this.primaryColor;
@@ -219,7 +223,7 @@ class Theme {
/* requirement-diagram */
this.requirementBackground = this.requirementBackground || this.primaryColor;
this.requirementBorderColor = this.requirementBorderColor || this.primaryBorderColor;
this.requirementBorderSize = this.requirementBorderSize || this.primaryBorderColor;
this.requirementBorderSize = this.requirementBorderSize || '1';
this.requirementTextColor = this.requirementTextColor || this.primaryTextColor;
this.relationColor = this.relationColor || this.lineColor;
this.relationLabelBackground =

View File

@@ -64,6 +64,7 @@ class Theme {
this.sectionBkgColor = darken('#EAE8D9', 30);
this.altSectionBkgColor = 'calculated';
this.sectionBkgColor2 = '#EAE8D9';
this.excludeBkgColor = darken(this.sectionBkgColor, 10);
this.taskBorderColor = rgba(255, 255, 255, 70);
this.taskBkgColor = 'calculated';
this.taskTextColor = 'calculated';
@@ -81,9 +82,8 @@ class Theme {
this.todayLineColor = '#DB5757';
/* C4 Context Diagram variables */
this.personBorder = 'calculated';
this.personBkg = 'calculated';
this.personBorder = this.primaryBorderColor;
this.personBkg = this.mainBkg;
/* state colors */
this.labelColor = 'calculated';
@@ -232,7 +232,7 @@ class Theme {
/* requirement-diagram */
this.requirementBackground = this.requirementBackground || this.primaryColor;
this.requirementBorderColor = this.requirementBorderColor || this.primaryBorderColor;
this.requirementBorderSize = this.requirementBorderSize || this.primaryBorderColor;
this.requirementBorderSize = this.requirementBorderSize || '1';
this.requirementTextColor = this.requirementTextColor || this.primaryTextColor;
this.relationColor = this.relationColor || this.lineColor;
this.relationLabelBackground =
@@ -257,6 +257,14 @@ class Theme {
this.gitInv5 = this.gitInv5 || invert(this.git5);
this.gitInv6 = this.gitInv6 || invert(this.git6);
this.gitInv7 = this.gitInv7 || invert(this.git7);
this.gitBranchLabel0 = this.gitBranchLabel0 || invert(this.labelTextColor);
this.gitBranchLabel1 = this.gitBranchLabel1 || this.labelTextColor;
this.gitBranchLabel2 = this.gitBranchLabel2 || this.labelTextColor;
this.gitBranchLabel3 = this.gitBranchLabel3 || invert(this.labelTextColor);
this.gitBranchLabel4 = this.gitBranchLabel4 || this.labelTextColor;
this.gitBranchLabel5 = this.gitBranchLabel5 || this.labelTextColor;
this.gitBranchLabel6 = this.gitBranchLabel6 || this.labelTextColor;
this.gitBranchLabel7 = this.gitBranchLabel7 || this.labelTextColor;
this.tagLabelColor = this.tagLabelColor || this.primaryTextColor;
this.tagLabelBackground = this.tagLabelBackground || this.primaryColor;

View File

@@ -109,9 +109,8 @@ class Theme {
this.todayLineColor = 'red';
/* C4 Context Diagram variables */
this.personBorder = 'calculated';
this.personBkg = 'calculated';
this.personBorder = this.primaryBorderColor;
this.personBkg = this.mainBkg;
/* state colors */
this.labelColor = 'black';
@@ -251,7 +250,7 @@ class Theme {
/* requirement-diagram */
this.requirementBackground = this.requirementBackground || this.primaryColor;
this.requirementBorderColor = this.requirementBorderColor || this.primaryBorderColor;
this.requirementBorderSize = this.requirementBorderSize || this.primaryBorderColor;
this.requirementBorderSize = this.requirementBorderSize || '1';
this.requirementTextColor = this.requirementTextColor || this.primaryTextColor;
this.relationColor = this.relationColor || this.lineColor;
this.relationLabelBackground = this.relationLabelBackground || this.labelBackground;

View File

@@ -83,9 +83,8 @@ class Theme {
this.todayLineColor = 'red';
/* C4 Context Diagram variables */
this.personBorder = 'calculated';
this.personBkg = 'calculated';
this.personBorder = this.primaryBorderColor;
this.personBkg = this.mainBkg;
/* state colors */
this.labelColor = 'black';
@@ -94,6 +93,15 @@ class Theme {
this.errorTextColor = '#552222';
}
updateColors() {
/* Sequence Diagram variables */
this.actorBorder = darken(this.mainBkg, 20);
this.actorBkg = this.mainBkg;
this.labelBoxBkgColor = this.actorBkg;
this.labelTextColor = this.actorTextColor;
this.loopTextColor = this.actorTextColor;
this.noteBorderColor = this.border2;
this.noteTextColor = this.actorTextColor;
/* Each color-set will have a background, a foreground and a border color */
this.cScale0 = this.cScale0 || this.primaryColor;
this.cScale1 = this.cScale1 || this.secondaryColor;
@@ -145,16 +153,6 @@ class Theme {
this.clusterBorder = this.border2;
this.defaultLinkColor = this.lineColor;
/* Sequence Diagram variables */
this.actorBorder = darken(this.mainBkg, 20);
this.actorBkg = this.mainBkg;
this.labelBoxBkgColor = this.actorBkg;
this.labelTextColor = this.actorTextColor;
this.loopTextColor = this.actorTextColor;
this.noteBorderColor = this.border2;
this.noteTextColor = this.actorTextColor;
/* Gantt chart variables */
this.taskBorderColor = this.border1;
@@ -220,7 +218,7 @@ class Theme {
/* requirement-diagram */
this.requirementBackground = this.requirementBackground || this.primaryColor;
this.requirementBorderColor = this.requirementBorderColor || this.primaryBorderColor;
this.requirementBorderSize = this.requirementBorderSize || this.primaryBorderColor;
this.requirementBorderSize = this.requirementBorderSize || '1';
this.requirementTextColor = this.requirementTextColor || this.primaryTextColor;
this.relationColor = this.relationColor || this.lineColor;
this.relationLabelBackground = this.relationLabelBackground || this.edgeLabelBackground;
@@ -262,6 +260,14 @@ class Theme {
this.gitInv5 = this.gitInv5 || invert(this.git5);
this.gitInv6 = this.gitInv6 || invert(this.git6);
this.gitInv7 = this.gitInv7 || invert(this.git7);
this.gitBranchLabel0 = this.gitBranchLabel0 || invert(this.labelTextColor);
this.gitBranchLabel1 = this.gitBranchLabel1 || this.labelTextColor;
this.gitBranchLabel2 = this.gitBranchLabel2 || this.labelTextColor;
this.gitBranchLabel3 = this.gitBranchLabel3 || invert(this.labelTextColor);
this.gitBranchLabel4 = this.gitBranchLabel4 || this.labelTextColor;
this.gitBranchLabel5 = this.gitBranchLabel5 || this.labelTextColor;
this.gitBranchLabel6 = this.gitBranchLabel6 || this.labelTextColor;
this.gitBranchLabel7 = this.gitBranchLabel7 || this.labelTextColor;
this.tagLabelColor = this.tagLabelColor || this.primaryTextColor;
this.tagLabelBackground = this.tagLabelBackground || this.primaryColor;

View File

@@ -95,9 +95,8 @@ class Theme {
this.todayLineColor = 'calculated';
/* C4 Context Diagram variables */
this.personBorder = 'calculated';
this.personBkg = 'calculated';
this.personBorder = this.primaryBorderColor;
this.personBkg = this.mainBkg;
/* state colors */
this.labelColor = 'black';
@@ -109,6 +108,22 @@ class Theme {
this.secondBkg = lighten(this.contrast, 55);
this.border2 = this.contrast;
/* Sequence Diagram variables */
this.actorBorder = lighten(this.border1, 23);
this.actorBkg = this.mainBkg;
this.actorTextColor = this.text;
this.actorLineColor = this.lineColor;
this.signalColor = this.text;
this.signalTextColor = this.text;
this.labelBoxBkgColor = this.actorBkg;
this.labelBoxBorderColor = this.actorBorder;
this.labelTextColor = this.text;
this.loopTextColor = this.text;
this.noteBorderColor = '#999';
this.noteBkgColor = '#666';
this.noteTextColor = '#fff';
/* Color Scale */
/* Each color-set will have a background, a foreground and a border color */
@@ -162,22 +177,6 @@ class Theme {
this.defaultLinkColor = this.lineColor;
this.titleColor = this.text;
/* Sequence Diagram variables */
this.actorBorder = lighten(this.border1, 23);
this.actorBkg = this.mainBkg;
this.actorTextColor = this.text;
this.actorLineColor = this.lineColor;
this.signalColor = this.text;
this.signalTextColor = this.text;
this.labelBoxBkgColor = this.actorBkg;
this.labelBoxBorderColor = this.actorBorder;
this.labelTextColor = this.text;
this.loopTextColor = this.text;
this.noteBorderColor = '#999';
this.noteBkgColor = '#666';
this.noteTextColor = '#fff';
/* Gantt chart variables */
this.sectionBkgColor = lighten(this.contrast, 30);
@@ -250,7 +249,7 @@ class Theme {
/* requirement-diagram */
this.requirementBackground = this.requirementBackground || this.primaryColor;
this.requirementBorderColor = this.requirementBorderColor || this.primaryBorderColor;
this.requirementBorderSize = this.requirementBorderSize || this.primaryBorderColor;
this.requirementBorderSize = this.requirementBorderSize || '1';
this.requirementTextColor = this.requirementTextColor || this.primaryTextColor;
this.relationColor = this.relationColor || this.lineColor;
this.relationLabelBackground = this.relationLabelBackground || this.edgeLabelBackground;

45
pnpm-lock.yaml generated
View File

@@ -138,6 +138,7 @@ importers:
coveralls: ^3.1.1
cpy-cli: ^4.2.0
cspell: ^6.14.3
csstree-validator: ^3.0.0
cytoscape: ^3.23.0
cytoscape-cose-bilkent: ^4.1.0
cytoscape-fcose: ^2.1.0
@@ -206,6 +207,7 @@ importers:
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
@@ -4065,7 +4067,7 @@ packages:
/axios/0.21.4_debug@4.3.2:
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
dependencies:
follow-redirects: 1.15.2_debug@4.3.4
follow-redirects: 1.15.2_debug@4.3.2
transitivePeerDependencies:
- debug
dev: true
@@ -4509,6 +4511,13 @@ packages:
jsonlint: 1.6.0
dev: true
/clap/3.1.1:
resolution: {integrity: sha512-vp42956Ax06WwaaheYEqEOgXZ3VKJxgccZ0gJL0HpyiupkIS9RVJFo5eDU1BPeQAOqz+cclndZg4DCqG1sJReQ==}
engines: {node: ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
dependencies:
ansi-colors: 4.1.3
dev: true
/clean-regexp/1.0.0:
resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
engines: {node: '>=4'}
@@ -5142,6 +5151,14 @@ packages:
source-map: 0.6.1
dev: true
/css-tree/2.3.1:
resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
dependencies:
mdn-data: 2.0.30
source-map-js: 1.0.2
dev: true
/cssom/0.3.8:
resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
dev: true
@@ -5157,6 +5174,16 @@ packages:
cssom: 0.3.8
dev: true
/csstree-validator/3.0.0:
resolution: {integrity: sha512-Y5OSq3wI0Xz6L7DCgJQtQ97U+v99SkX9r663VjpvUMJPhEr0A149OxiAGqcnokB5bt81irgnMudspBzujzqn0w==}
engines: {node: ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
hasBin: true
dependencies:
clap: 3.1.1
css-tree: 2.3.1
resolve: 1.22.1
dev: true
/csstype/2.6.21:
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
dev: true
@@ -6641,6 +6668,18 @@ packages:
resolution: {integrity: sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA==}
dev: true
/follow-redirects/1.15.2_debug@4.3.2:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
dependencies:
debug: 4.3.2
dev: true
/follow-redirects/1.15.2_debug@4.3.4:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
@@ -8728,6 +8767,10 @@ packages:
resolution: {integrity: sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==}
dev: true
/mdn-data/2.0.30:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
dev: true
/mdn-data/2.0.6:
resolution: {integrity: sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==}
dev: true