mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-14 06:43:25 +08:00
fix unit tests
This commit is contained in:
parent
543e4de0c8
commit
c9c4320f89
@ -7,6 +7,7 @@ import type {
|
||||
ExternalDiagramDefinition,
|
||||
} from './types';
|
||||
import { frontMatterRegex } from './frontmatter';
|
||||
import { getDiagram, registerDiagram } from './diagramAPI';
|
||||
import { UnknownDiagramError } from '../errors';
|
||||
|
||||
const directive = /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
|
||||
@ -54,6 +55,39 @@ export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinitio
|
||||
}
|
||||
};
|
||||
|
||||
export const loadRegisteredDiagrams = async () => {
|
||||
log.debug(`Loading registered diagrams`);
|
||||
// Load all lazy loaded diagrams in parallel
|
||||
const results = await Promise.allSettled(
|
||||
Object.entries(detectors).map(async ([key, { detector, loader }]) => {
|
||||
if (loader) {
|
||||
try {
|
||||
getDiagram(key);
|
||||
} catch (error) {
|
||||
try {
|
||||
// Register diagram if it is not already registered
|
||||
const { diagram, id } = await loader();
|
||||
registerDiagram(id, diagram, detector);
|
||||
} catch (err) {
|
||||
// Remove failed diagram from detectors
|
||||
log.error(`Failed to load external diagram with key ${key}. Remozing from detectors.`);
|
||||
delete detectors[key];
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const failed = results.filter((result) => result.status === 'rejected');
|
||||
if (failed.length > 0) {
|
||||
log.error(`Failed to load ${failed.length} external diagrams`);
|
||||
for (const res of failed) {
|
||||
log.error(res);
|
||||
}
|
||||
throw new Error(`Failed to load ${failed.length} external diagrams`);
|
||||
}
|
||||
};
|
||||
|
||||
export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => {
|
||||
if (detectors[key]) {
|
||||
log.error(`Detector with key ${key} already exists`);
|
||||
|
@ -18,6 +18,7 @@ import flowchartElk from '../diagrams/flowchart/elk/detector';
|
||||
import timeline from '../diagrams/timeline/detector';
|
||||
import mindmap from '../diagrams/mindmap/detector';
|
||||
import { registerLazyLoadedDiagrams } from './detectType';
|
||||
import { registerDiagram } from './diagramAPI';
|
||||
|
||||
let hasLoadedDiagrams = false;
|
||||
export const addDiagrams = () => {
|
||||
@ -27,6 +28,33 @@ export const addDiagrams = () => {
|
||||
// This is added here to avoid race-conditions.
|
||||
// We could optimize the loading logic somehow.
|
||||
hasLoadedDiagrams = true;
|
||||
registerDiagram(
|
||||
'---',
|
||||
// --- diagram type may appear if YAML front-matter is not parsed correctly
|
||||
{
|
||||
db: {
|
||||
clear: () => {
|
||||
// Quite ok, clear needs to be there for --- to work as a regular diagram
|
||||
},
|
||||
},
|
||||
styles: {}, // should never be used
|
||||
renderer: {}, // should never be used
|
||||
parser: {
|
||||
parser: { yy: {} },
|
||||
parse: () => {
|
||||
throw new Error(
|
||||
'Diagrams beginning with --- are not valid. ' +
|
||||
'If you were trying to use a YAML front-matter, please ensure that ' +
|
||||
"you've correctly opened and closed the YAML front-matter with unindented `---` blocks"
|
||||
);
|
||||
},
|
||||
},
|
||||
init: () => null, // no op
|
||||
},
|
||||
(text) => {
|
||||
return text.toLowerCase().trimStart().startsWith('---');
|
||||
}
|
||||
);
|
||||
registerLazyLoadedDiagrams(
|
||||
error,
|
||||
c4,
|
||||
|
@ -2,8 +2,12 @@ import { detectType } from './detectType';
|
||||
import { getDiagram, registerDiagram } from './diagramAPI';
|
||||
import { addDiagrams } from './diagram-orchestration';
|
||||
import { DiagramDetector } from './types';
|
||||
import { getDiagramFromText } from '../Diagram';
|
||||
|
||||
addDiagrams();
|
||||
beforeAll(async () => {
|
||||
await getDiagramFromText('sequenceDiagram');
|
||||
});
|
||||
|
||||
describe('DiagramAPI', () => {
|
||||
it('should return default diagrams', () => {
|
||||
|
@ -4,7 +4,7 @@ import { getConfig as _getConfig } from '../config';
|
||||
import { sanitizeText as _sanitizeText } from '../diagrams/common/common';
|
||||
import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox';
|
||||
import { addStylesForDiagram } from '../styles';
|
||||
import { DiagramDefinition, DiagramDetector, ExternalDiagramDefinition } from './types';
|
||||
import { DiagramDefinition, DiagramDetector } from './types';
|
||||
import * as _commonDb from '../commonDb';
|
||||
import { parseDirective as _parseDirective } from '../directiveUtils';
|
||||
|
||||
@ -77,27 +77,3 @@ export class DiagramNotFoundError extends Error {
|
||||
super(`Diagram ${message} not found.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an internal function and should not be made public, as it will likely change.
|
||||
* @internal
|
||||
* @param diagrams - Array of {@link ExternalDiagramDefinition}.
|
||||
*/
|
||||
export const loadExternalDiagrams = async (...diagrams: ExternalDiagramDefinition[]) => {
|
||||
log.debug(`Loading ${diagrams.length} external diagrams`);
|
||||
// Load all lazy loaded diagrams in parallel
|
||||
const results = await Promise.allSettled(
|
||||
diagrams.map(async ({ id, detector, loader }) => {
|
||||
const { diagram } = await loader();
|
||||
registerDiagram(id, diagram, detector);
|
||||
})
|
||||
);
|
||||
const failed = results.filter((result) => result.status === 'rejected');
|
||||
if (failed.length > 0) {
|
||||
log.error(`Failed to load ${failed.length} external diagrams`);
|
||||
for (const res of failed) {
|
||||
log.error(res);
|
||||
}
|
||||
throw new Error(`Failed to load ${failed.length} external diagrams`);
|
||||
}
|
||||
};
|
||||
|
@ -1,21 +1,24 @@
|
||||
import flowDb from './flowDb';
|
||||
import flowParser from './parser/flow';
|
||||
import { parser } from './parser/flow';
|
||||
import flowRenderer from './flowRenderer';
|
||||
import { Diagram } from '../../Diagram';
|
||||
import { addDiagrams } from '../../diagram-api/diagram-orchestration';
|
||||
|
||||
const diag = {
|
||||
db: flowDb,
|
||||
};
|
||||
addDiagrams();
|
||||
|
||||
describe('when using mermaid and ', function () {
|
||||
describe('when calling addEdges ', function () {
|
||||
beforeEach(function () {
|
||||
flowParser.parser.yy = flowDb;
|
||||
parser.yy = flowDb;
|
||||
flowDb.clear();
|
||||
flowDb.setGen('gen-2');
|
||||
});
|
||||
it('should handle edges with text', function () {
|
||||
const diag = new Diagram('graph TD;A-->|text ex|B;');
|
||||
diag.db.getVertices();
|
||||
const edges = diag.db.getEdges();
|
||||
it('should handle edges with text', () => {
|
||||
parser.parse('graph TD;A-->|text ex|B;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
|
||||
const mockG = {
|
||||
setEdge: function (start, end, options) {
|
||||
@ -29,10 +32,10 @@ describe('when using mermaid and ', function () {
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
|
||||
it('should handle edges without text', function () {
|
||||
const diag = new Diagram('graph TD;A-->B;');
|
||||
diag.db.getVertices();
|
||||
const edges = diag.db.getEdges();
|
||||
it('should handle edges without text', async function () {
|
||||
parser.parse('graph TD;A-->B;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
|
||||
const mockG = {
|
||||
setEdge: function (start, end, options) {
|
||||
@ -45,10 +48,10 @@ describe('when using mermaid and ', function () {
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
|
||||
it('should handle open-ended edges', function () {
|
||||
const diag = new Diagram('graph TD;A---B;');
|
||||
diag.db.getVertices();
|
||||
const edges = diag.db.getEdges();
|
||||
it('should handle open-ended edges', () => {
|
||||
parser.parse('graph TD;A---B;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
|
||||
const mockG = {
|
||||
setEdge: function (start, end, options) {
|
||||
@ -61,10 +64,10 @@ describe('when using mermaid and ', function () {
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
|
||||
it('should handle edges with styles defined', function () {
|
||||
const diag = new Diagram('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||
diag.db.getVertices();
|
||||
const edges = diag.db.getEdges();
|
||||
it('should handle edges with styles defined', () => {
|
||||
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
|
||||
const mockG = {
|
||||
setEdge: function (start, end, options) {
|
||||
@ -77,10 +80,10 @@ describe('when using mermaid and ', function () {
|
||||
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
it('should handle edges with interpolation defined', function () {
|
||||
const diag = new Diagram('graph TD;A---B; linkStyle 0 interpolate basis');
|
||||
diag.db.getVertices();
|
||||
const edges = diag.db.getEdges();
|
||||
it('should handle edges with interpolation defined', () => {
|
||||
parser.parse('graph TD;A---B; linkStyle 0 interpolate basis');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
|
||||
const mockG = {
|
||||
setEdge: function (start, end, options) {
|
||||
@ -93,12 +96,10 @@ describe('when using mermaid and ', function () {
|
||||
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
it('should handle edges with text and styles defined', function () {
|
||||
const diag = new Diagram(
|
||||
'graph TD;A---|the text|B; linkStyle 0 stroke:val1,stroke-width:val2;'
|
||||
);
|
||||
diag.db.getVertices();
|
||||
const edges = diag.db.getEdges();
|
||||
it('should handle edges with text and styles defined', () => {
|
||||
parser.parse('graph TD;A---|the text|B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
|
||||
const mockG = {
|
||||
setEdge: function (start, end, options) {
|
||||
@ -113,10 +114,10 @@ describe('when using mermaid and ', function () {
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
|
||||
it('should set fill to "none" by default when handling edges', function () {
|
||||
const diag = new Diagram('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||
diag.db.getVertices();
|
||||
const edges = diag.db.getEdges();
|
||||
it('should set fill to "none" by default when handling edges', () => {
|
||||
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
|
||||
const mockG = {
|
||||
setEdge: function (start, end, options) {
|
||||
@ -130,12 +131,10 @@ describe('when using mermaid and ', function () {
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
|
||||
it('should not set fill to none if fill is set in linkStyle', function () {
|
||||
const diag = new Diagram(
|
||||
'graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2,fill:blue;'
|
||||
);
|
||||
diag.db.getVertices();
|
||||
const edges = diag.db.getEdges();
|
||||
it('should not set fill to none if fill is set in linkStyle', () => {
|
||||
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2,fill:blue;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
const mockG = {
|
||||
setEdge: function (start, end, options) {
|
||||
expect(start).toContain('flowchart-A-');
|
||||
|
@ -2,9 +2,14 @@ import { vi } from 'vitest';
|
||||
|
||||
import * as configApi from '../../config';
|
||||
import mermaidAPI from '../../mermaidAPI';
|
||||
import { Diagram } from '../../Diagram';
|
||||
import { Diagram, getDiagramFromText } from '../../Diagram';
|
||||
import { addDiagrams } from '../../diagram-api/diagram-orchestration';
|
||||
|
||||
beforeAll(async () => {
|
||||
// Is required to load the sequence diagram
|
||||
await getDiagramFromText('sequenceDiagram');
|
||||
});
|
||||
|
||||
/**
|
||||
* Sequence diagrams require their own very special version of a mocked d3 module
|
||||
* diagrams/sequence/svgDraw uses statements like this with d3 nodes: (note the [0][0])
|
||||
@ -183,7 +188,7 @@ Alice->Bob:Hello Bob, how are you?
|
||||
Note right of Bob: Bob thinks
|
||||
Bob-->Alice: I am good thanks!`;
|
||||
|
||||
await mermaidAPI.parse(str, diagram);
|
||||
await mermaidAPI.parse(str);
|
||||
const actors = diagram.db.getActors();
|
||||
expect(actors.Alice.description).toBe('Alice');
|
||||
actors.Bob.description = 'Bob';
|
||||
|
@ -1,6 +1,11 @@
|
||||
import mermaid from './mermaid';
|
||||
import { mermaidAPI } from './mermaidAPI';
|
||||
import './diagram-api/diagram-orchestration';
|
||||
import { addDiagrams } from './diagram-api/diagram-orchestration';
|
||||
|
||||
beforeAll(async () => {
|
||||
addDiagrams();
|
||||
});
|
||||
const spyOn = vi.spyOn;
|
||||
|
||||
vi.mock('./mermaidAPI');
|
||||
@ -66,9 +71,9 @@ describe('when using mermaid and ', () => {
|
||||
mermaid.registerExternalDiagrams(
|
||||
[
|
||||
{
|
||||
id: 'dummy',
|
||||
detector: (text) => /dummy/.test(text),
|
||||
loader: () => Promise.reject('error'),
|
||||
id: 'dummyError',
|
||||
detector: (text) => /dummyError/.test(text),
|
||||
loader: () => Promise.reject('dummyError'),
|
||||
},
|
||||
],
|
||||
{ lazyLoad: false }
|
||||
|
@ -7,11 +7,10 @@ import { MermaidConfig } from './config.type';
|
||||
import { log } from './logger';
|
||||
import utils from './utils';
|
||||
import { mermaidAPI, ParseOptions, RenderResult } from './mermaidAPI';
|
||||
import { registerLazyLoadedDiagrams } from './diagram-api/detectType';
|
||||
import { registerLazyLoadedDiagrams, loadRegisteredDiagrams } from './diagram-api/detectType';
|
||||
import type { ParseErrorFunction } from './Diagram';
|
||||
import { isDetailedError } from './utils';
|
||||
import type { DetailedError } from './utils';
|
||||
import { registerDiagram } from './diagram-api/diagramAPI';
|
||||
import { ExternalDiagramDefinition } from './diagram-api/types';
|
||||
|
||||
export type {
|
||||
@ -64,30 +63,6 @@ const handleError = (error: unknown, errors: DetailedError[], parseError?: Parse
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is an internal function and should not be made public, as it will likely change.
|
||||
* @internal
|
||||
* @param diagrams - Array of {@link ExternalDiagramDefinition}.
|
||||
*/
|
||||
const loadExternalDiagrams = async (...diagrams: ExternalDiagramDefinition[]) => {
|
||||
log.debug(`Loading ${diagrams.length} external diagrams`);
|
||||
// Load all lazy loaded diagrams in parallel
|
||||
const results = await Promise.allSettled(
|
||||
diagrams.map(async ({ id, detector, loader }) => {
|
||||
const { diagram } = await loader();
|
||||
registerDiagram(id, diagram, detector);
|
||||
})
|
||||
);
|
||||
const failed = results.filter((result) => result.status === 'rejected');
|
||||
if (failed.length > 0) {
|
||||
log.error(`Failed to load ${failed.length} external diagrams`);
|
||||
for (const res of failed) {
|
||||
log.error(res);
|
||||
}
|
||||
throw new Error(`Failed to load ${failed.length} external diagrams`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ## run
|
||||
*
|
||||
@ -251,7 +226,7 @@ const init = async function (
|
||||
/**
|
||||
* Used to register external diagram types.
|
||||
* @param diagrams - Array of {@link ExternalDiagramDefinition}.
|
||||
* @param opts - If opts.lazyLoad is true, the diagram will be loaded on demand.
|
||||
* @param opts - If opts.lazyLoad is false, the diagrams will be loaded immediately.
|
||||
*/
|
||||
const registerExternalDiagrams = async (
|
||||
diagrams: ExternalDiagramDefinition[],
|
||||
@ -261,10 +236,9 @@ const registerExternalDiagrams = async (
|
||||
lazyLoad?: boolean;
|
||||
} = {}
|
||||
) => {
|
||||
if (lazyLoad) {
|
||||
registerLazyLoadedDiagrams(...diagrams);
|
||||
} else {
|
||||
await loadExternalDiagrams(...diagrams);
|
||||
registerLazyLoadedDiagrams(...diagrams);
|
||||
if (lazyLoad === false) {
|
||||
await loadRegisteredDiagrams();
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user