mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-05 20:14:14 +01:00
Fixed some parser issue and added test cases for the parser
This commit is contained in:
@@ -42,13 +42,13 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
|
||||
<acc_descr_multiline>[\}] { this.popState(); }
|
||||
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
|
||||
|
||||
\s*"x-axis"\s* return 'X-AXIS';
|
||||
\s*"y-axis"\s* return 'Y-AXIS';
|
||||
\s*\-\-+\>[^(\r?\n)\s]* return 'AXIS-TEXT-DELIMITER'
|
||||
\s*"quadrant-1"\s* return 'QUADRANT_1';
|
||||
\s*"quadrant-2"\s* return 'QUADRANT_2';
|
||||
\s*"quadrant-3"\s* return 'QUADRANT_3';
|
||||
\s*"quadrant-4"\s* return 'QUADRANT_4';
|
||||
" "*"x-axis"" "* return 'X-AXIS';
|
||||
" "*"y-axis"" "* return 'Y-AXIS';
|
||||
" "*\-\-+\>" "* return 'AXIS-TEXT-DELIMITER'
|
||||
" "*"quadrant-1"" "* return 'QUADRANT_1';
|
||||
" "*"quadrant-2"" "* return 'QUADRANT_2';
|
||||
" "*"quadrant-3"" "* return 'QUADRANT_3';
|
||||
" "*"quadrant-4"" "* return 'QUADRANT_4';
|
||||
|
||||
["][`] { this.begin("md_string");}
|
||||
<md_string>[^`"]+ { return "MD_STR";}
|
||||
@@ -59,13 +59,12 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
|
||||
|
||||
\s*\:\s*\[\s* {this.begin("point_start"); return 'point_start';}
|
||||
<point_start>(1)|(0(.\d+)?) {this.begin('point_x'); return 'point_x';}
|
||||
<point_start>\s*\] {this.popState();}
|
||||
<point_start>\s*\]" "* {this.popState();}
|
||||
<point_x>\s*\,\s* {this.popState(); this.begin('point_y');}
|
||||
<point_y>(1)|(0(.\d+)?) {this.popState(); return 'point_y';}
|
||||
|
||||
"quadrantChart"\s* return 'QUADRANT';
|
||||
" "*"quadrantChart"" "* return 'QUADRANT';
|
||||
|
||||
\[[0-1].?[0-9]{5}\] return 'POINT_VALUE'
|
||||
[A-Za-z]+ return 'ALPHA';
|
||||
":" return 'COLON';
|
||||
\+ return 'PLUS';
|
||||
@@ -92,6 +91,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
|
||||
|
||||
start
|
||||
: eol start
|
||||
| SPACE start
|
||||
| directive start
|
||||
| QUADRANT document
|
||||
;
|
||||
@@ -102,11 +102,12 @@ document
|
||||
;
|
||||
|
||||
line
|
||||
: statement eol { $$ = $1 }
|
||||
: statement eol
|
||||
;
|
||||
|
||||
statement
|
||||
:
|
||||
| SPACE statement
|
||||
| axisDetails
|
||||
| quadrantDetails
|
||||
| points
|
||||
@@ -118,7 +119,8 @@ statement
|
||||
;
|
||||
|
||||
points
|
||||
: text point_start point_x point_y {yy.addPoint($1, $3, $4);};
|
||||
: text point_start point_x point_y {yy.addPoint($1, $3, $4);}
|
||||
;
|
||||
|
||||
axisDetails
|
||||
: X-AXIS text AXIS-TEXT-DELIMITER text {yy.setXAxisLeftText($2); yy.setXAxisRightText($4);}
|
||||
@@ -160,10 +162,10 @@ argDirective
|
||||
;
|
||||
|
||||
closeDirective
|
||||
: close_directive { yy.parseDirective('}%%', 'close_directive', 'pie'); }
|
||||
: close_directive { yy.parseDirective('}%%', 'close_directive', 'quadrantChart'); }
|
||||
;
|
||||
|
||||
text: textNoTagsToken
|
||||
text: alphaNumToken
|
||||
{ $$={text:$1, type: 'text'};}
|
||||
| text textNoTagsToken
|
||||
{ $$={text:$1.text+''+$2, type: $1.type};}
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
// @ts-ignore
|
||||
import { parser } from './quadrant.jison';
|
||||
import { Mock, vi } from 'vitest';
|
||||
|
||||
const parserFnConstructor = (str: string) => {
|
||||
return () => {
|
||||
parser.parse(str);
|
||||
};
|
||||
};
|
||||
|
||||
const mockDB: Record<string, Mock<any, any>> = {
|
||||
setQuadrant1Text: vi.fn(),
|
||||
setQuadrant2Text: vi.fn(),
|
||||
setQuadrant3Text: vi.fn(),
|
||||
setQuadrant4Text: vi.fn(),
|
||||
setXAxisLeftText: vi.fn(),
|
||||
setXAxisRightText: vi.fn(),
|
||||
setYAxisTopText: vi.fn(),
|
||||
setYAxisBottomText: vi.fn(),
|
||||
setDiagramTitle: vi.fn(),
|
||||
parseDirective: vi.fn(),
|
||||
addPoint: vi.fn(),
|
||||
};
|
||||
|
||||
function clearMocks() {
|
||||
for (const key in mockDB) {
|
||||
mockDB[key].mockRestore();
|
||||
}
|
||||
}
|
||||
|
||||
describe('Testing quadrantChart jison file', () => {
|
||||
beforeEach(() => {
|
||||
parser.yy = mockDB;
|
||||
clearMocks();
|
||||
});
|
||||
|
||||
it('should throw error if quadrantChart text is not there', () => {
|
||||
const str = 'quadrant-1 do';
|
||||
expect(parserFnConstructor(str)).toThrow();
|
||||
});
|
||||
|
||||
it('should not throw error if only quadrantChart is there', () => {
|
||||
const str = 'quadrantChart';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should be able to parse directive', () => {
|
||||
let str =
|
||||
'%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%% \n quadrantChart';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.parseDirective.mock.calls[0]).toEqual(['%%{', 'open_directive']);
|
||||
expect(mockDB.parseDirective.mock.calls[1]).toEqual(['init', 'type_directive']);
|
||||
expect(mockDB.parseDirective.mock.calls[2]).toEqual([
|
||||
'{"quadrantChart": {"chartWidth": 600, "chartHeight": 600} }',
|
||||
'arg_directive',
|
||||
]);
|
||||
expect(mockDB.parseDirective.mock.calls[3]).toEqual([
|
||||
'}%%',
|
||||
'close_directive',
|
||||
'quadrantChart',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should be able to parse xAxis text', () => {
|
||||
let str = 'quadrantChart\nx-axis urgent --> not urgent';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({ text: 'urgent', type: 'text' });
|
||||
expect(mockDB.setXAxisRightText).toHaveBeenCalledWith({ text: 'not urgent', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str = 'quadrantChart\n x-AxIs Urgent --> Not Urgent \n';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({ text: 'Urgent', type: 'text' });
|
||||
expect(mockDB.setXAxisRightText).toHaveBeenCalledWith({ text: 'Not Urgent ', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str =
|
||||
'quadrantChart\n x-AxIs "Urgent(* +=[❤" --> "Not Urgent (* +=[❤"\n ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({ text: 'Urgent(* +=[❤', type: 'text' });
|
||||
expect(mockDB.setXAxisRightText).toHaveBeenCalledWith({
|
||||
text: 'Not Urgent (* +=[❤',
|
||||
type: 'text',
|
||||
});
|
||||
|
||||
clearMocks();
|
||||
str = 'quadrantChart\n x-AxIs "Urgent(* +=[❤"';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({ text: 'Urgent(* +=[❤', type: 'text' });
|
||||
expect(mockDB.setXAxisRightText).not.toHaveBeenCalled();
|
||||
|
||||
clearMocks();
|
||||
str = 'quadrantChart\n x-AxIs "Urgent(* +=[❤" --> ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({
|
||||
text: 'Urgent(* +=[❤ --> ',
|
||||
type: 'text',
|
||||
});
|
||||
expect(mockDB.setXAxisRightText).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be able to parse yAxis text', () => {
|
||||
let str = 'quadrantChart\ny-axis urgent --> not urgent';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({ text: 'urgent', type: 'text' });
|
||||
expect(mockDB.setYAxisTopText).toHaveBeenCalledWith({ text: 'not urgent', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str = 'quadrantChart\n y-AxIs Urgent --> Not Urgent \n';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({ text: 'Urgent', type: 'text' });
|
||||
expect(mockDB.setYAxisTopText).toHaveBeenCalledWith({ text: 'Not Urgent ', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str =
|
||||
'quadrantChart\n Y-AxIs "Urgent(* +=[❤" --> "Not Urgent (* +=[❤"\n ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({ text: 'Urgent(* +=[❤', type: 'text' });
|
||||
expect(mockDB.setYAxisTopText).toHaveBeenCalledWith({
|
||||
text: 'Not Urgent (* +=[❤',
|
||||
type: 'text',
|
||||
});
|
||||
|
||||
clearMocks();
|
||||
str = 'quadrantChart\n y-AxIs "Urgent(* +=[❤"';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({ text: 'Urgent(* +=[❤', type: 'text' });
|
||||
expect(mockDB.setYAxisTopText).not.toHaveBeenCalled();
|
||||
|
||||
clearMocks();
|
||||
str = 'quadrantChart\n y-AxIs "Urgent(* +=[❤" --> ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({
|
||||
text: 'Urgent(* +=[❤ --> ',
|
||||
type: 'text',
|
||||
});
|
||||
expect(mockDB.setYAxisTopText).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be able to parse quadrant1 text', () => {
|
||||
let str = 'quadrantChart\nquadrant-1 Plan';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant1Text).toHaveBeenCalledWith({ text: 'Plan', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n QuaDrant-1 Plan ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant1Text).toHaveBeenCalledWith({ text: 'Plan ', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n QuaDrant-1 "Plan(* +=[❤"';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant1Text).toHaveBeenCalledWith({ text: 'Plan(* +=[❤', type: 'text' });
|
||||
});
|
||||
|
||||
it('should be able to parse quadrant2 text', () => {
|
||||
let str = 'quadrantChart\nquadrant-2 do';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant2Text).toHaveBeenCalledWith({ text: 'do', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n QuaDrant-2 Do ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant2Text).toHaveBeenCalledWith({ text: 'Do ', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n QuaDrant-2 "Do(* +=[❤"';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant2Text).toHaveBeenCalledWith({ text: 'Do(* +=[❤', type: 'text' });
|
||||
});
|
||||
|
||||
it('should be able to parse quadrant3 text', () => {
|
||||
let str = 'quadrantChart\nquadrant-3 deligate';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant3Text).toHaveBeenCalledWith({ text: 'deligate', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n QuaDrant-3 Deligate ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant3Text).toHaveBeenCalledWith({ text: 'Deligate ', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n QuaDrant-3 "Deligate(* +=[❤"';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant3Text).toHaveBeenCalledWith({ text: 'Deligate(* +=[❤', type: 'text' });
|
||||
});
|
||||
|
||||
it('should be able to parse quadrant4 text', () => {
|
||||
let str = 'quadrantChart\nquadrant-4 delete';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'delete', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n QuaDrant-4 Delete ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'Delete ', type: 'text' });
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n QuaDrant-4 "Delete(* +=[❤"';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'Delete(* +=[❤', type: 'text' });
|
||||
});
|
||||
|
||||
it('should be able to parse title', () => {
|
||||
let str = 'quadrantChart\ntitle this is title';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('this is title');
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n TiTle this Is title ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('this Is title');
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n title "this is title (* +=[❤"';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('"this is title (* +=[❤"');
|
||||
});
|
||||
|
||||
it('should be able to parse points', () => {
|
||||
let str = 'quadrantChart\npoint1: [0.1, 0.4]';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'point1', type: 'text' }, '0.1', '0.4');
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n Point1 : [0.1, 0.4] ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'Point1', type: 'text' }, '0.1', '0.4');
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n "Point1 : (* +=[❤": [1, 0] ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.addPoint).toHaveBeenCalledWith(
|
||||
{ text: 'Point1 : (* +=[❤', type: 'text' },
|
||||
'1',
|
||||
'0'
|
||||
);
|
||||
|
||||
clearMocks();
|
||||
str = 'QuadRantChart \n Point1 : [1.2, 0.4] ';
|
||||
expect(parserFnConstructor(str)).toThrow();
|
||||
});
|
||||
|
||||
it('should be able to parse the whole chart', () => {
|
||||
let str = `%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%%
|
||||
quadrantChart
|
||||
title Analytics and Business Intelligence Platforms
|
||||
x-axis "Completeness of Vision ❤" --> "x-axis-2"
|
||||
y-axis Ability to Execute --> "y-axis-2"
|
||||
quadrant-1 Leaders
|
||||
quadrant-2 Challengers
|
||||
quadrant-3 Niche
|
||||
quadrant-4 Visionaries
|
||||
Microsoft: [0.75, 0.75]
|
||||
Salesforce: [0.55, 0.60]
|
||||
IBM: [0.51, 0.40]
|
||||
Incorta: [0.20, 0.30]`;
|
||||
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.parseDirective.mock.calls[0]).toEqual(['%%{', 'open_directive']);
|
||||
expect(mockDB.parseDirective.mock.calls[1]).toEqual(['init', 'type_directive']);
|
||||
expect(mockDB.parseDirective.mock.calls[2]).toEqual([
|
||||
'{"quadrantChart": {"chartWidth": 600, "chartHeight": 600} }',
|
||||
'arg_directive',
|
||||
]);
|
||||
expect(mockDB.parseDirective.mock.calls[3]).toEqual([
|
||||
'}%%',
|
||||
'close_directive',
|
||||
'quadrantChart',
|
||||
]);
|
||||
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({
|
||||
text: 'Completeness of Vision ❤',
|
||||
type: 'text',
|
||||
});
|
||||
expect(mockDB.setXAxisRightText).toHaveBeenCalledWith({ text: 'x-axis-2', type: 'text' });
|
||||
expect(mockDB.setYAxisTopText).toHaveBeenCalledWith({ text: 'y-axis-2', type: 'text' });
|
||||
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({
|
||||
text: 'Ability to Execute',
|
||||
type: 'text',
|
||||
});
|
||||
expect(mockDB.setQuadrant1Text).toHaveBeenCalledWith({ text: 'Leaders', type: 'text' });
|
||||
expect(mockDB.setQuadrant2Text).toHaveBeenCalledWith({ text: 'Challengers', type: 'text' });
|
||||
expect(mockDB.setQuadrant3Text).toHaveBeenCalledWith({ text: 'Niche', type: 'text' });
|
||||
expect(mockDB.setQuadrant4Text).toHaveBeenCalledWith({ text: 'Visionaries', type: 'text' });
|
||||
expect(mockDB.addPoint).toHaveBeenCalledWith(
|
||||
{ text: 'Microsoft', type: 'text' },
|
||||
'0.75',
|
||||
'0.75'
|
||||
);
|
||||
expect(mockDB.addPoint).toHaveBeenCalledWith(
|
||||
{ text: 'Salesforce', type: 'text' },
|
||||
'0.55',
|
||||
'0.60'
|
||||
);
|
||||
expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'IBM', type: 'text' }, '0.51', '0.40');
|
||||
expect(mockDB.addPoint).toHaveBeenCalledWith({ text: 'Incorta', type: 'text' }, '0.20', '0.30');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user