Skip to content

Commit

Permalink
fix unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sidharthv96 committed Feb 19, 2023
1 parent 543e4de commit c9c4320
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 100 deletions.
34 changes: 34 additions & 0 deletions packages/mermaid/src/diagram-api/detectType.ts
Expand Up @@ -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;
Expand Down Expand Up @@ -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`);
Expand Down
28 changes: 28 additions & 0 deletions packages/mermaid/src/diagram-api/diagram-orchestration.ts
Expand Up @@ -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 = () => {
Expand All @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions packages/mermaid/src/diagram-api/diagramAPI.spec.ts
Expand Up @@ -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', () => {
Expand Down
26 changes: 1 addition & 25 deletions packages/mermaid/src/diagram-api/diagramAPI.ts
Expand Up @@ -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';

Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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-');
Expand Down
Expand Up @@ -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])
Expand Down Expand Up @@ -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';
Expand Down
11 changes: 8 additions & 3 deletions packages/mermaid/src/mermaid.spec.ts
@@ -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');
Expand Down Expand Up @@ -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 }
Expand Down
36 changes: 5 additions & 31 deletions packages/mermaid/src/mermaid.ts
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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[],
Expand All @@ -261,10 +236,9 @@ const registerExternalDiagrams = async (
lazyLoad?: boolean;
} = {}
) => {
if (lazyLoad) {
registerLazyLoadedDiagrams(...diagrams);
} else {
await loadExternalDiagrams(...diagrams);
registerLazyLoadedDiagrams(...diagrams);
if (lazyLoad === false) {
await loadRegisteredDiagrams();
}
};

Expand Down

0 comments on commit c9c4320

Please sign in to comment.