mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-18 06:49:47 +02:00
Merge branch 'develop' into 1903_journey_config
This commit is contained in:
@@ -943,6 +943,36 @@ const config = {
|
||||
***Default value: true**.
|
||||
*/
|
||||
useMaxWidth: true
|
||||
},
|
||||
|
||||
/**
|
||||
* The object containing configurations specific for req diagrams
|
||||
*/
|
||||
requirement: {
|
||||
useWidth: undefined,
|
||||
|
||||
/**
|
||||
*| Parameter | Description |Type | Required | Values|
|
||||
*| --- | --- | --- | --- | --- |
|
||||
*| useMaxWidth | See Notes | Boolean | Required | true, false |
|
||||
*
|
||||
***Notes:**
|
||||
*When this flag is set to true, the diagram width is locked to 100% and
|
||||
*scaled based on available space. If set to false, the diagram reserves its
|
||||
*absolute width.
|
||||
***Default value: true**.
|
||||
*/
|
||||
useMaxWidth: true,
|
||||
|
||||
rect_fill: '#f9f9f9',
|
||||
text_color: '#333',
|
||||
rect_border_size: '0.5px',
|
||||
rect_border_color: '#bbb',
|
||||
rect_min_width: 200,
|
||||
rect_min_height: 200,
|
||||
fontSize: 14,
|
||||
rect_padding: 10,
|
||||
line_height: 20
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -74,11 +74,28 @@ const placeholderToBreak = s => {
|
||||
return s.replace(/#br#/g, '<br/>');
|
||||
};
|
||||
|
||||
const getUrl = useAbsolute => {
|
||||
let url = '';
|
||||
if (useAbsolute) {
|
||||
url =
|
||||
window.location.protocol +
|
||||
'//' +
|
||||
window.location.host +
|
||||
window.location.pathname +
|
||||
window.location.search;
|
||||
url = url.replace(/\(/g, '\\(');
|
||||
url = url.replace(/\)/g, '\\)');
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
export default {
|
||||
getRows,
|
||||
sanitizeText,
|
||||
hasBreaks,
|
||||
splitBreaks,
|
||||
lineBreakRegex,
|
||||
removeScript
|
||||
removeScript,
|
||||
getUrl
|
||||
};
|
||||
|
@@ -1,20 +1,19 @@
|
||||
import { setConfig } from '../../../config';
|
||||
import erDb from '../erDb';
|
||||
import erDiagram from './erDiagram';
|
||||
import { setConfig } from '../../../config';
|
||||
import log from '../../../logger';
|
||||
|
||||
setConfig({
|
||||
securityLevel: 'strict'
|
||||
});
|
||||
|
||||
describe('when parsing ER diagram it...', function() {
|
||||
describe('when parsing ER diagram it...', function () {
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function () {
|
||||
erDiagram.parser.yy = erDb;
|
||||
erDiagram.parser.yy.clear();
|
||||
});
|
||||
|
||||
it ('should allow stand-alone entities with no relationships', function() {
|
||||
it('should allow stand-alone entities with no relationships', function () {
|
||||
const line1 = 'ISLAND';
|
||||
const line2 = 'MAINLAND';
|
||||
erDiagram.parser.parse(`erDiagram\n${line1}\n${line2}`);
|
||||
@@ -23,7 +22,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(erDb.getRelationships().length).toBe(0);
|
||||
});
|
||||
|
||||
it ('should allow hyphens and underscores in entity names', function() {
|
||||
it('should allow hyphens and underscores in entity names', function () {
|
||||
const line1 = 'DUCK-BILLED-PLATYPUS';
|
||||
const line2 = 'CHARACTER_SET';
|
||||
erDiagram.parser.parse(`erDiagram\n${line1}\n${line2}`);
|
||||
@@ -33,7 +32,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(entities.hasOwnProperty('CHARACTER_SET')).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow an entity with a single attribute to be defined', function() {
|
||||
it('should allow an entity with a single attribute to be defined', function () {
|
||||
const entity = 'BOOK';
|
||||
const attribute = 'string title';
|
||||
|
||||
@@ -43,7 +42,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(entities[entity].attributes.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should allow an entity with multiple attributes to be defined', function() {
|
||||
it('should allow an entity with multiple attributes to be defined', function () {
|
||||
const entity = 'BOOK';
|
||||
const attribute1 = 'string title';
|
||||
const attribute2 = 'string author';
|
||||
@@ -54,7 +53,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(entities[entity].attributes.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should allow attribute definitions to be split into multiple blocks', function() {
|
||||
it('should allow attribute definitions to be split into multiple blocks', function () {
|
||||
const entity = 'BOOK';
|
||||
const attribute1 = 'string title';
|
||||
const attribute2 = 'string author';
|
||||
@@ -65,7 +64,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(entities[entity].attributes.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should allow an empty attribute block', function() {
|
||||
it('should allow an empty attribute block', function () {
|
||||
const entity = 'BOOK';
|
||||
|
||||
erDiagram.parser.parse(`erDiagram\n${entity} {}`);
|
||||
@@ -74,7 +73,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(entities[entity].attributes.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should allow an attribute block to start immediately after the entity name', function() {
|
||||
it('should allow an attribute block to start immediately after the entity name', function () {
|
||||
const entity = 'BOOK';
|
||||
|
||||
erDiagram.parser.parse(`erDiagram\n${entity}{}`);
|
||||
@@ -83,7 +82,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(entities[entity].attributes.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should allow an attribute block to be separated from the entity name by spaces', function() {
|
||||
it('should allow an attribute block to be separated from the entity name by spaces', function () {
|
||||
const entity = 'BOOK';
|
||||
|
||||
erDiagram.parser.parse(`erDiagram\n${entity} {}`);
|
||||
@@ -92,7 +91,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(entities[entity].attributes.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should allow whitespace before and after attribute definitions', function() {
|
||||
it('should allow whitespace before and after attribute definitions', function () {
|
||||
const entity = 'BOOK';
|
||||
const attribute = 'string title';
|
||||
|
||||
@@ -102,7 +101,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(entities[entity].attributes.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should allow no whitespace before and after attribute definitions', function() {
|
||||
it('should allow no whitespace before and after attribute definitions', function () {
|
||||
const entity = 'BOOK';
|
||||
const attribute = 'string title';
|
||||
|
||||
@@ -112,7 +111,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(entities[entity].attributes.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should associate two entities correctly', function() {
|
||||
it('should associate two entities correctly', function () {
|
||||
erDiagram.parser.parse('erDiagram\nCAR ||--o{ DRIVER : "insured for"');
|
||||
const entities = erDb.getEntities();
|
||||
const relationships = erDb.getRelationships();
|
||||
@@ -125,7 +124,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(relationships[0].relSpec.relType).toBe(erDb.Identification.IDENTIFYING);
|
||||
});
|
||||
|
||||
it('should not create duplicate entities', function() {
|
||||
it('should not create duplicate entities', function () {
|
||||
const line1 = 'CAR ||--o{ DRIVER : "insured for"';
|
||||
const line2 = 'DRIVER ||--|| LICENSE : has';
|
||||
erDiagram.parser.parse(`erDiagram\n${line1}\n${line2}`);
|
||||
@@ -134,7 +133,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(Object.keys(entities).length).toBe(3);
|
||||
});
|
||||
|
||||
it('should create the role specified', function() {
|
||||
it('should create the role specified', function () {
|
||||
const teacherRole = 'is teacher of';
|
||||
const line1 = `TEACHER }o--o{ STUDENT : "${teacherRole}"`;
|
||||
erDiagram.parser.parse(`erDiagram\n${line1}`);
|
||||
@@ -143,32 +142,32 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].roleA).toBe(`${teacherRole}`);
|
||||
});
|
||||
|
||||
it('should allow recursive relationships', function() {
|
||||
it('should allow recursive relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nNODE ||--o{ NODE : "leads to"');
|
||||
expect(Object.keys(erDb.getEntities()).length).toBe(1);
|
||||
});
|
||||
|
||||
it('should allow more than one relationship between the same two entities', function() {
|
||||
it('should allow more than one relationship between the same two entities', function () {
|
||||
const line1 = 'CAR ||--o{ PERSON : "insured for"';
|
||||
const line2 = 'CAR }o--|| PERSON : "owned by"';
|
||||
erDiagram.parser.parse(`erDiagram\n${line1}\n${line2}`);
|
||||
const entities = erDb.getEntities();
|
||||
const rels = erDb.getRelationships();
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
expect(Object.keys(entities).length).toBe(2);
|
||||
expect(rels.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should limit the number of relationships between the same two entities', function() {
|
||||
it('should limit the number of relationships between the same two entities', function () {
|
||||
/* TODO */
|
||||
});
|
||||
|
||||
it ('should not allow multiple relationships between the same two entities unless the roles are different', function() {
|
||||
it('should not allow multiple relationships between the same two entities unless the roles are different', function () {
|
||||
/* TODO */
|
||||
});
|
||||
|
||||
|
||||
it('should handle only-one-to-one-or-more relationships', function() {
|
||||
it('should handle only-one-to-one-or-more relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA ||--|{ B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -178,7 +177,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONLY_ONE);
|
||||
});
|
||||
|
||||
it('should handle only-one-to-zero-or-more relationships', function() {
|
||||
it('should handle only-one-to-zero-or-more relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA ||..o{ B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -189,7 +188,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
|
||||
});
|
||||
|
||||
it('should handle zero-or-one-to-zero-or-more relationships', function() {
|
||||
it('should handle zero-or-one-to-zero-or-more relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA |o..o{ B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -199,7 +198,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_ONE);
|
||||
});
|
||||
|
||||
it('should handle zero-or-one-to-one-or-more relationships', function() {
|
||||
it('should handle zero-or-one-to-one-or-more relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA |o--|{ B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -209,7 +208,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_ONE);
|
||||
});
|
||||
|
||||
it('should handle one-or-more-to-only-one relationships', function() {
|
||||
it('should handle one-or-more-to-only-one relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA }|--|| B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -219,7 +218,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONE_OR_MORE);
|
||||
});
|
||||
|
||||
it('should handle zero-or-more-to-only-one relationships', function() {
|
||||
it('should handle zero-or-more-to-only-one relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA }o--|| B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -229,7 +228,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
|
||||
});
|
||||
|
||||
it('should handle zero-or-more-to-zero-or-one relationships', function() {
|
||||
it('should handle zero-or-more-to-zero-or-one relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA }o..o| B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -239,7 +238,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
|
||||
});
|
||||
|
||||
it('should handle one-or-more-to-zero-or-one relationships', function() {
|
||||
it('should handle one-or-more-to-zero-or-one relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA }|..o| B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -249,7 +248,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONE_OR_MORE);
|
||||
});
|
||||
|
||||
it('should handle zero-or-one-to-only-one relationships', function() {
|
||||
it('should handle zero-or-one-to-only-one relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA |o..|| B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -259,7 +258,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_ONE);
|
||||
});
|
||||
|
||||
it('should handle only-one-to-only-one relationships', function() {
|
||||
it('should handle only-one-to-only-one relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA ||..|| B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -269,7 +268,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONLY_ONE);
|
||||
});
|
||||
|
||||
it('should handle only-one-to-zero-or-one relationships', function() {
|
||||
it('should handle only-one-to-zero-or-one relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA ||--o| B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -279,7 +278,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONLY_ONE);
|
||||
});
|
||||
|
||||
it('should handle zero-or-one-to-zero-or-one relationships', function() {
|
||||
it('should handle zero-or-one-to-zero-or-one relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA |o..o| B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -289,7 +288,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_ONE);
|
||||
});
|
||||
|
||||
it('should handle zero-or-more-to-zero-or-more relationships', function() {
|
||||
it('should handle zero-or-more-to-zero-or-more relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA }o--o{ B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -299,7 +298,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
|
||||
});
|
||||
|
||||
it('should handle one-or-more-to-one-or-more relationships', function() {
|
||||
it('should handle one-or-more-to-one-or-more relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA }|..|{ B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -309,7 +308,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONE_OR_MORE);
|
||||
});
|
||||
|
||||
it('should handle zero-or-more-to-one-or-more relationships', function() {
|
||||
it('should handle zero-or-more-to-one-or-more relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA }o--|{ B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -319,7 +318,7 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
|
||||
});
|
||||
|
||||
it('should handle one-or-more-to-zero-or-more relationships', function() {
|
||||
it('should handle one-or-more-to-zero-or-more relationships', function () {
|
||||
erDiagram.parser.parse('erDiagram\nA }|..o{ B : has');
|
||||
const rels = erDb.getRelationships();
|
||||
|
||||
@@ -329,38 +328,38 @@ describe('when parsing ER diagram it...', function() {
|
||||
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONE_OR_MORE);
|
||||
});
|
||||
|
||||
it('should represent identifying relationships properly', function() {
|
||||
it('should represent identifying relationships properly', function () {
|
||||
erDiagram.parser.parse('erDiagram\nHOUSE ||--|{ ROOM : contains');
|
||||
const rels = erDb.getRelationships();
|
||||
expect(rels[0].relSpec.relType).toBe(erDb.Identification.IDENTIFYING);
|
||||
});
|
||||
|
||||
it('should represent non-identifying relationships properly', function() {
|
||||
it('should represent non-identifying relationships properly', function () {
|
||||
erDiagram.parser.parse('erDiagram\n PERSON ||..o{ POSSESSION : owns');
|
||||
const rels = erDb.getRelationships();
|
||||
expect(rels[0].relSpec.relType).toBe(erDb.Identification.NON_IDENTIFYING);
|
||||
});
|
||||
|
||||
it('should not accept a syntax error', function() {
|
||||
it('should not accept a syntax error', function () {
|
||||
const doc = 'erDiagram\nA xxx B : has';
|
||||
expect(() => {
|
||||
erDiagram.parser.parse(doc);
|
||||
}).toThrowError();
|
||||
});
|
||||
|
||||
it('should allow an empty quoted label', function() {
|
||||
it('should allow an empty quoted label', function () {
|
||||
erDiagram.parser.parse('erDiagram\nCUSTOMER ||--|{ ORDER : ""');
|
||||
const rels = erDb.getRelationships();
|
||||
expect(rels[0].roleA).toBe('');
|
||||
});
|
||||
|
||||
it('should allow an non-empty quoted label', function() {
|
||||
it('should allow an non-empty quoted label', function () {
|
||||
erDiagram.parser.parse('erDiagram\nCUSTOMER ||--|{ ORDER : "places"');
|
||||
const rels = erDb.getRelationships();
|
||||
expect(rels[0].roleA).toBe('places');
|
||||
});
|
||||
|
||||
it('should allow an non-empty unquoted label', function() {
|
||||
it('should allow an non-empty unquoted label', function () {
|
||||
erDiagram.parser.parse('erDiagram\nCUSTOMER ||--|{ ORDER : places');
|
||||
const rels = erDb.getRelationships();
|
||||
expect(rels[0].roleA).toBe('places');
|
||||
|
@@ -189,6 +189,9 @@ export const draw = function(text, id) {
|
||||
})
|
||||
.attr('height', theBarHeight)
|
||||
.attr('transform-origin', function(d, i) {
|
||||
// Ignore the incoming i value and use our order instead
|
||||
i = d.order;
|
||||
|
||||
return (
|
||||
(
|
||||
timeScale(d.startTime) +
|
||||
|
199
src/diagrams/requirement/parser/requirementDiagram.jison
Normal file
199
src/diagrams/requirement/parser/requirementDiagram.jison
Normal file
@@ -0,0 +1,199 @@
|
||||
/** mermaid
|
||||
* https://knsv.github.io/mermaid
|
||||
* (c) 2015 Knut Sveidqvist
|
||||
* MIT license.
|
||||
*/
|
||||
%lex
|
||||
%options case-insensitive
|
||||
|
||||
%x string
|
||||
%x token
|
||||
%x unqString
|
||||
%x open_directive
|
||||
%x type_directive
|
||||
%x arg_directive
|
||||
%x close_directive
|
||||
|
||||
%%
|
||||
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
|
||||
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
|
||||
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
||||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
||||
|
||||
(\r?\n)+ return 'NEWLINE';
|
||||
\s+ /* skip all whitespace */
|
||||
\#[^\n]* /* skip comments */
|
||||
\%%[^\n]* /* skip comments */
|
||||
<<EOF>> return 'EOF';
|
||||
|
||||
"requirementDiagram" return 'RD';
|
||||
|
||||
"{" return 'STRUCT_START';
|
||||
"}" return 'STRUCT_STOP';
|
||||
":" return 'COLONSEP';
|
||||
|
||||
"id" return 'ID';
|
||||
"text" return 'TEXT';
|
||||
"risk" return 'RISK';
|
||||
"verifyMethod" return 'VERIFYMTHD';
|
||||
|
||||
"requirement" return 'REQUIREMENT';
|
||||
"functionalRequirement" return 'FUNCTIONAL_REQUIREMENT';
|
||||
"interfaceRequirement" return 'INTERFACE_REQUIREMENT';
|
||||
"performanceRequirement" return 'PERFORMANCE_REQUIREMENT';
|
||||
"physicalRequirement" return 'PHYSICAL_REQUIREMENT';
|
||||
"designConstraint" return 'DESIGN_CONSTRAINT';
|
||||
|
||||
"low" return 'LOW_RISK';
|
||||
"medium" return 'MED_RISK';
|
||||
"high" return 'HIGH_RISK';
|
||||
|
||||
"analysis" return 'VERIFY_ANALYSIS';
|
||||
"demonstration" return 'VERIFY_DEMONSTRATION';
|
||||
"inspection" return 'VERIFY_INSPECTION';
|
||||
"test" return 'VERIFY_TEST';
|
||||
|
||||
"element" return 'ELEMENT';
|
||||
|
||||
"contains" return 'CONTAINS';
|
||||
"copies" return 'COPIES';
|
||||
"derives" return 'DERIVES';
|
||||
"satisfies" return 'SATISFIES';
|
||||
"verifies" return 'VERIFIES';
|
||||
"refines" return 'REFINES';
|
||||
"traces" return 'TRACES';
|
||||
|
||||
"type" return 'TYPE';
|
||||
"docref" return 'DOCREF';
|
||||
|
||||
"<-" return 'END_ARROW_L';
|
||||
"->" {return 'END_ARROW_R';}
|
||||
"-" {return 'LINE';}
|
||||
|
||||
["] { this.begin("string"); }
|
||||
<string>["] { this.popState(); }
|
||||
<string>[^"]* { return "qString"; }
|
||||
|
||||
[\w][^\r\n\{\<\>\-\=]* { yytext = yytext.trim(); return 'unqString';}
|
||||
|
||||
/lex
|
||||
|
||||
%start start
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
start
|
||||
: directive start
|
||||
| RD NEWLINE diagram EOF;
|
||||
|
||||
directive
|
||||
: openDirective typeDirective closeDirective
|
||||
| openDirective typeDirective ':' argDirective closeDirective;
|
||||
|
||||
openDirective
|
||||
: open_directive { yy.parseDirective('%%{', 'open_directive'); };
|
||||
|
||||
typeDirective
|
||||
: type_directive { yy.parseDirective($1, 'type_directive'); };
|
||||
|
||||
argDirective
|
||||
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); };
|
||||
|
||||
closeDirective
|
||||
: close_directive { yy.parseDirective('}%%', 'close_directive', 'pie'); };
|
||||
|
||||
diagram
|
||||
: /* empty */ { $$ = [] }
|
||||
| requirementDef diagram
|
||||
| elementDef diagram
|
||||
| relationshipDef diagram
|
||||
| NEWLINE diagram;
|
||||
|
||||
requirementDef
|
||||
: requirementType requirementName STRUCT_START NEWLINE requirementBody
|
||||
{ yy.addRequirement($2, $1) };
|
||||
|
||||
requirementBody
|
||||
: ID COLONSEP id NEWLINE requirementBody
|
||||
{ yy.setNewReqId($3); }
|
||||
| TEXT COLONSEP text NEWLINE requirementBody
|
||||
{ yy.setNewReqText($3); }
|
||||
| RISK COLONSEP riskLevel NEWLINE requirementBody
|
||||
{ yy.setNewReqRisk($3); }
|
||||
| VERIFYMTHD COLONSEP verifyType NEWLINE requirementBody
|
||||
{ yy.setNewReqVerifyMethod($3); }
|
||||
| NEWLINE requirementBody
|
||||
| STRUCT_STOP;
|
||||
|
||||
requirementType
|
||||
: REQUIREMENT
|
||||
{ $$=yy.RequirementType.REQUIREMENT;}
|
||||
| FUNCTIONAL_REQUIREMENT
|
||||
{ $$=yy.RequirementType.FUNCTIONAL_REQUIREMENT;}
|
||||
| INTERFACE_REQUIREMENT
|
||||
{ $$=yy.RequirementType.INTERFACE_REQUIREMENT;}
|
||||
| PERFORMANCE_REQUIREMENT
|
||||
{ $$=yy.RequirementType.PERFORMANCE_REQUIREMENT;}
|
||||
| PHYSICAL_REQUIREMENT
|
||||
{ $$=yy.RequirementType.PHYSICAL_REQUIREMENT;}
|
||||
| DESIGN_CONSTRAINT
|
||||
{ $$=yy.RequirementType.DESIGN_CONSTRAINT;};
|
||||
|
||||
riskLevel
|
||||
: LOW_RISK { $$=yy.RiskLevel.LOW_RISK;}
|
||||
| MED_RISK { $$=yy.RiskLevel.MED_RISK;}
|
||||
| HIGH_RISK { $$=yy.RiskLevel.HIGH_RISK;};
|
||||
|
||||
verifyType
|
||||
: VERIFY_ANALYSIS
|
||||
{ $$=yy.VerifyType.VERIFY_ANALYSIS;}
|
||||
| VERIFY_DEMONSTRATION
|
||||
{ $$=yy.VerifyType.VERIFY_DEMONSTRATION;}
|
||||
| VERIFY_INSPECTION
|
||||
{ $$=yy.VerifyType.VERIFY_INSPECTION;}
|
||||
| VERIFY_TEST
|
||||
{ $$=yy.VerifyType.VERIFY_TEST;};
|
||||
|
||||
elementDef
|
||||
: ELEMENT elementName STRUCT_START NEWLINE elementBody
|
||||
{ yy.addElement($2) };
|
||||
|
||||
elementBody
|
||||
: TYPE COLONSEP type NEWLINE elementBody
|
||||
{ yy.setNewElementType($3); }
|
||||
| DOCREF COLONSEP ref NEWLINE elementBody
|
||||
{ yy.setNewElementDocRef($3); }
|
||||
| NEWLINE elementBody
|
||||
| STRUCT_STOP;
|
||||
|
||||
relationshipDef
|
||||
: id END_ARROW_L relationship LINE id
|
||||
{ yy.addRelationship($3, $5, $1) }
|
||||
| id LINE relationship END_ARROW_R id
|
||||
{ yy.addRelationship($3, $1, $5) };
|
||||
|
||||
relationship
|
||||
: CONTAINS
|
||||
{ $$=yy.Relationships.CONTAINS;}
|
||||
| COPIES
|
||||
{ $$=yy.Relationships.COPIES;}
|
||||
| DERIVES
|
||||
{ $$=yy.Relationships.DERIVES;}
|
||||
| SATISFIES
|
||||
{ $$=yy.Relationships.SATISFIES;}
|
||||
| VERIFIES
|
||||
{ $$=yy.Relationships.VERIFIES;}
|
||||
| REFINES
|
||||
{ $$=yy.Relationships.REFINES;}
|
||||
| TRACES
|
||||
{ $$=yy.Relationships.TRACES;};
|
||||
|
||||
requirementName: unqString | qString;
|
||||
id : unqString | qString;
|
||||
text : unqString | qString;
|
||||
elementName : unqString | qString;
|
||||
type : unqString | qString;
|
||||
ref : unqString | qString;
|
||||
|
||||
%%
|
577
src/diagrams/requirement/parser/requirementDiagram.spec.js
Normal file
577
src/diagrams/requirement/parser/requirementDiagram.spec.js
Normal file
@@ -0,0 +1,577 @@
|
||||
import { setConfig } from '../../../config';
|
||||
import requirementDb from '../requirementDb';
|
||||
import reqDiagram from './requirementDiagram';
|
||||
|
||||
setConfig({
|
||||
securityLevel: 'strict'
|
||||
});
|
||||
|
||||
describe('when parsing requirement diagram it...', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
reqDiagram.parser.yy = requirementDb;
|
||||
reqDiagram.parser.yy.clear();
|
||||
});
|
||||
|
||||
it('will accept full requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`requirement ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
expect(Object.keys(requirementDb.getRequirements()).length).toBe(1);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.id).toBe(expectedId);
|
||||
expect(foundReq.text).toBe(expectedText);
|
||||
|
||||
expect(Object.keys(requirementDb.getElements()).length).toBe(0);
|
||||
expect(Object.keys(requirementDb.getRelationships()).length).toBe(0);
|
||||
});
|
||||
|
||||
it('will accept full element definition', function() {
|
||||
const expectedName = "test_el";
|
||||
const expectedType = "test_type";
|
||||
const expectedDocRef = "test_ref"
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`element ${expectedName} {`,
|
||||
`type: ${expectedType}`,
|
||||
`docref: ${expectedDocRef}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
expect(Object.keys(requirementDb.getRequirements()).length).toBe(0);
|
||||
expect(Object.keys(requirementDb.getElements()).length).toBe(1);
|
||||
|
||||
let foundElement = requirementDb.getElements()[expectedName];
|
||||
expect(foundElement).toBeDefined();
|
||||
expect(foundElement.type).toBe(expectedType);
|
||||
expect(foundElement.docRef).toBe(
|
||||
expectedDocRef);
|
||||
|
||||
expect(Object.keys(requirementDb.getRelationships()).length).toBe(0);
|
||||
});
|
||||
|
||||
it('will accept full relationship definition', function() {
|
||||
const expectedSrc = "a";
|
||||
const expectedDest = "b";
|
||||
const expectedType = requirementDb.Relationships.CONTAINS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedSrc} - ${expectedType} -> ${expectedDest}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
expect(Object.keys(requirementDb.getRequirements()).length).toBe(0);
|
||||
expect(Object.keys(requirementDb.getElements()).length).toBe(0);
|
||||
expect(Object.keys(requirementDb.getRelationships()).length).toBe(1);
|
||||
|
||||
let foundRelationship = requirementDb.getRelationships()[0];
|
||||
expect(foundRelationship.src).toBe(expectedSrc);
|
||||
expect(foundRelationship.dst).toBe(expectedDest);
|
||||
});
|
||||
|
||||
it('will accept "requirement" type of requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = requirementDb.RequirementType.REQUIREMENT;
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`requirement ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept "functionalRequirement" type of requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = requirementDb.RequirementType.FUNCTIONAL_REQUIREMENT;
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`functionalRequirement ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept "interfaceRequirement" type of requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = requirementDb.RequirementType.INTERFACE_REQUIREMENT;
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`interfaceRequirement ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept "performanceRequirement" type of requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = requirementDb.RequirementType.PERFORMANCE_REQUIREMENT;
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`performanceRequirement ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept "physicalRequirement" type of requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = requirementDb.RequirementType.PHYSICAL_REQUIREMENT;
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`physicalRequirement ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept "designConstraint" type of requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = requirementDb.RequirementType.DESIGN_CONSTRAINT;
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`designConstraint ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept "low" type of risk requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = "designConstraint";
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.LOW_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedType} ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.risk).toBe(expectedRisk);
|
||||
});
|
||||
|
||||
it('will accept "medium" type of risk requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = "designConstraint";
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.MED_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedType} ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.risk).toBe(expectedRisk);
|
||||
});
|
||||
|
||||
it('will accept "high" type of risk requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = "designConstraint";
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedType} ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.risk).toBe(expectedRisk);
|
||||
});
|
||||
|
||||
it('will accept "Analysis" type of verification method requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = "designConstraint";
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_ANALYSIS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedType} ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.verifyMethod).toBe(expectedVerifyMethod);
|
||||
});
|
||||
|
||||
it('will accept "Inspection" type of verification method requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = "designConstraint";
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_INSPECTION;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedType} ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.verifyMethod).toBe(expectedVerifyMethod);
|
||||
});
|
||||
|
||||
it('will accept "Test" type of verification method requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = "designConstraint";
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_TEST;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedType} ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.verifyMethod).toBe(expectedVerifyMethod);
|
||||
});
|
||||
|
||||
it('will accept "Demonstration" type of verification method requirement definition', function() {
|
||||
const expectedName = "test_req";
|
||||
const expectedType = requirementDb.RequirementType.DESIGN_CONSTRAINT;
|
||||
const expectedId = "test_id";
|
||||
const expectedText = "the test text."
|
||||
const expectedRisk = requirementDb.RiskLevel.HIGH_RISK;
|
||||
const expectedVerifyMethod = requirementDb.VerifyType.VERIFY_DEMONSTRATION;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`designConstraint ${expectedName} {`,
|
||||
`id: ${expectedId}`,
|
||||
`text: ${expectedText}`,
|
||||
`risk: ${expectedRisk}`,
|
||||
`verifymethod: ${expectedVerifyMethod}`,
|
||||
`}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundReq = requirementDb.getRequirements()[expectedName];
|
||||
expect(foundReq).toBeDefined();
|
||||
expect(foundReq.verifyMethod).toBe(expectedVerifyMethod);
|
||||
});
|
||||
|
||||
it('will accept contains relationship definition', function() {
|
||||
const expectedSrc = "a";
|
||||
const expectedDest = "b";
|
||||
const expectedType = requirementDb.Relationships.CONTAINS;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedSrc} - ${expectedType} -> ${expectedDest}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundRelationship = requirementDb.getRelationships()[0];
|
||||
expect(foundRelationship.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept copies relationship definition', function() {
|
||||
const expectedSrc = "a";
|
||||
const expectedDest = "b";
|
||||
const expectedType = requirementDb.Relationships.COPIES;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedSrc} - ${expectedType} -> ${expectedDest}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundRelationship = requirementDb.getRelationships()[0];
|
||||
expect(foundRelationship.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept derives relationship definition', function() {
|
||||
const expectedSrc = "a";
|
||||
const expectedDest = "b";
|
||||
const expectedType = requirementDb.Relationships.DERIVES;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedSrc} - ${expectedType} -> ${expectedDest}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundRelationship = requirementDb.getRelationships()[0];
|
||||
expect(foundRelationship.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept satisfies relationship definition', function() {
|
||||
const expectedSrc = "a";
|
||||
const expectedDest = "b";
|
||||
const expectedType = requirementDb.Relationships.SATISFIES;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedSrc} - ${expectedType} -> ${expectedDest}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundRelationship = requirementDb.getRelationships()[0];
|
||||
expect(foundRelationship.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept verifies relationship definition', function() {
|
||||
const expectedSrc = "a";
|
||||
const expectedDest = "b";
|
||||
const expectedType = requirementDb.Relationships.VERIFIES;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedSrc} - ${expectedType} -> ${expectedDest}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundRelationship = requirementDb.getRelationships()[0];
|
||||
expect(foundRelationship.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept refines relationship definition', function() {
|
||||
const expectedSrc = "a";
|
||||
const expectedDest = "b";
|
||||
const expectedType = requirementDb.Relationships.REFINES;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedSrc} - ${expectedType} -> ${expectedDest}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundRelationship = requirementDb.getRelationships()[0];
|
||||
expect(foundRelationship.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
it('will accept traces relationship definition', function() {
|
||||
const expectedSrc = "a";
|
||||
const expectedDest = "b";
|
||||
const expectedType = requirementDb.Relationships.TRACES;
|
||||
|
||||
let lines = [
|
||||
`requirementDiagram`,
|
||||
``,
|
||||
`${expectedSrc} - ${expectedType} -> ${expectedDest}`,
|
||||
];
|
||||
let doc = lines.join("\n");
|
||||
|
||||
reqDiagram.parser.parse(doc);
|
||||
|
||||
let foundRelationship = requirementDb.getRelationships()[0];
|
||||
expect(foundRelationship.type).toBe(expectedType);
|
||||
});
|
||||
|
||||
});
|
162
src/diagrams/requirement/requirementDb.js
Normal file
162
src/diagrams/requirement/requirementDb.js
Normal file
@@ -0,0 +1,162 @@
|
||||
import * as configApi from '../../config';
|
||||
import { log } from '../../logger';
|
||||
import mermaidAPI from '../../mermaidAPI';
|
||||
|
||||
let relations = [];
|
||||
let latestRequirement = {};
|
||||
let requirements = {};
|
||||
let latestElement = {};
|
||||
let elements = {};
|
||||
|
||||
const RequirementType = {
|
||||
REQUIREMENT: 'Requirement',
|
||||
FUNCTIONAL_REQUIREMENT: 'Functional Requirement',
|
||||
INTERFACE_REQUIREMENT: 'Interface Requirement',
|
||||
PERFORMANCE_REQUIREMENT: 'Performance Requirement',
|
||||
PHYSICAL_REQUIREMENT: 'Physical Requirement',
|
||||
DESIGN_CONSTRAINT: 'Design Constraint'
|
||||
};
|
||||
|
||||
const RiskLevel = {
|
||||
LOW_RISK: 'Low',
|
||||
MED_RISK: 'Medium',
|
||||
HIGH_RISK: 'High'
|
||||
};
|
||||
|
||||
const VerifyType = {
|
||||
VERIFY_ANALYSIS: 'Analysis',
|
||||
VERIFY_DEMONSTRATION: 'Demonstration',
|
||||
VERIFY_INSPECTION: 'Inspection',
|
||||
VERIFY_TEST: 'Test'
|
||||
};
|
||||
|
||||
const Relationships = {
|
||||
CONTAINS: 'contains',
|
||||
COPIES: 'copies',
|
||||
DERIVES: 'derives',
|
||||
SATISFIES: 'satisfies',
|
||||
VERIFIES: 'verifies',
|
||||
REFINES: 'refines',
|
||||
TRACES: 'traces'
|
||||
};
|
||||
|
||||
export const parseDirective = function(statement, context, type) {
|
||||
mermaidAPI.parseDirective(this, statement, context, type);
|
||||
};
|
||||
|
||||
const addRequirement = (name, type) => {
|
||||
if (typeof requirements[name] === 'undefined') {
|
||||
requirements[name] = {
|
||||
name,
|
||||
type,
|
||||
|
||||
id: latestRequirement.id,
|
||||
text: latestRequirement.text,
|
||||
risk: latestRequirement.risk,
|
||||
verifyMethod: latestRequirement.verifyMethod
|
||||
};
|
||||
}
|
||||
latestRequirement = {};
|
||||
|
||||
return requirements[name];
|
||||
};
|
||||
|
||||
const getRequirements = () => requirements;
|
||||
|
||||
const setNewReqId = id => {
|
||||
if (typeof latestRequirement != 'undefined') {
|
||||
latestRequirement.id = id;
|
||||
}
|
||||
};
|
||||
|
||||
const setNewReqText = text => {
|
||||
if (typeof latestRequirement != 'undefined') {
|
||||
latestRequirement.text = text;
|
||||
}
|
||||
};
|
||||
|
||||
const setNewReqRisk = risk => {
|
||||
if (typeof latestRequirement != 'undefined') {
|
||||
latestRequirement.risk = risk;
|
||||
}
|
||||
};
|
||||
|
||||
const setNewReqVerifyMethod = verifyMethod => {
|
||||
if (typeof latestRequirement != 'undefined') {
|
||||
latestRequirement.verifyMethod = verifyMethod;
|
||||
}
|
||||
};
|
||||
|
||||
const addElement = name => {
|
||||
if (typeof elements[name] === 'undefined') {
|
||||
elements[name] = {
|
||||
name,
|
||||
|
||||
type: latestElement.type,
|
||||
docRef: latestElement.docRef
|
||||
};
|
||||
log.info('Added new requirement: ', name);
|
||||
}
|
||||
latestElement = {};
|
||||
|
||||
return elements[name];
|
||||
};
|
||||
|
||||
const getElements = () => elements;
|
||||
|
||||
const setNewElementType = type => {
|
||||
if (typeof latestElement != 'undefined') {
|
||||
latestElement.type = type;
|
||||
}
|
||||
};
|
||||
|
||||
const setNewElementDocRef = docRef => {
|
||||
if (typeof latestElement != 'undefined') {
|
||||
latestElement.docRef = docRef;
|
||||
}
|
||||
};
|
||||
|
||||
const addRelationship = (type, src, dst) => {
|
||||
relations.push({
|
||||
type,
|
||||
src,
|
||||
dst
|
||||
});
|
||||
};
|
||||
|
||||
const getRelationships = () => relations;
|
||||
|
||||
const clear = () => {
|
||||
relations = [];
|
||||
latestRequirement = {};
|
||||
requirements = {};
|
||||
latestElement = {};
|
||||
elements = {};
|
||||
};
|
||||
|
||||
export default {
|
||||
RequirementType,
|
||||
RiskLevel,
|
||||
VerifyType,
|
||||
Relationships,
|
||||
|
||||
parseDirective,
|
||||
getConfig: () => configApi.getConfig().req,
|
||||
|
||||
addRequirement,
|
||||
getRequirements,
|
||||
setNewReqId,
|
||||
setNewReqText,
|
||||
setNewReqRisk,
|
||||
setNewReqVerifyMethod,
|
||||
|
||||
addElement,
|
||||
getElements,
|
||||
setNewElementType,
|
||||
setNewElementDocRef,
|
||||
|
||||
addRelationship,
|
||||
getRelationships,
|
||||
|
||||
clear
|
||||
};
|
69
src/diagrams/requirement/requirementMarkers.js
Normal file
69
src/diagrams/requirement/requirementMarkers.js
Normal file
@@ -0,0 +1,69 @@
|
||||
const ReqMarkers = {
|
||||
CONTAINS: 'contains',
|
||||
ARROW: 'arrow'
|
||||
};
|
||||
|
||||
const insertLineEndings = (parentNode, conf) => {
|
||||
let containsNode = parentNode
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', ReqMarkers.CONTAINS + '_line_ending')
|
||||
.attr('refX', 0)
|
||||
.attr('refY', conf.line_height / 2)
|
||||
.attr('markerWidth', conf.line_height)
|
||||
.attr('markerHeight', conf.line_height)
|
||||
.attr('orient', 'auto')
|
||||
.append('g');
|
||||
|
||||
containsNode
|
||||
.append('circle')
|
||||
.attr('cx', conf.line_height / 2)
|
||||
.attr('cy', conf.line_height / 2)
|
||||
.attr('r', conf.line_height / 2)
|
||||
.attr('stroke', conf.rect_border_color)
|
||||
.attr('stroke-width', 1)
|
||||
.attr('fill', 'none');
|
||||
|
||||
containsNode
|
||||
.append('line')
|
||||
.attr('x1', 0)
|
||||
.attr('x2', conf.line_height)
|
||||
.attr('y1', conf.line_height / 2)
|
||||
.attr('y2', conf.line_height / 2)
|
||||
.attr('stroke', conf.rect_border_color)
|
||||
.attr('stroke-width', 1);
|
||||
|
||||
containsNode
|
||||
.append('line')
|
||||
.attr('y1', 0)
|
||||
.attr('y2', conf.line_height)
|
||||
.attr('x1', conf.line_height / 2)
|
||||
.attr('x2', conf.line_height / 2)
|
||||
.attr('stroke', conf.rect_border_color)
|
||||
.attr('stroke-width', 1);
|
||||
|
||||
parentNode
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', ReqMarkers.ARROW + '_line_ending')
|
||||
.attr('refX', conf.line_height)
|
||||
.attr('refY', 0.5 * conf.line_height)
|
||||
.attr('markerWidth', conf.line_height)
|
||||
.attr('markerHeight', conf.line_height)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr(
|
||||
'd',
|
||||
`M0,0
|
||||
L${conf.line_height},${conf.line_height / 2}
|
||||
M${conf.line_height},${conf.line_height / 2}
|
||||
L0,${conf.line_height}`
|
||||
)
|
||||
.attr('stroke-width', 1)
|
||||
.attr('stroke', conf.rect_border_color);
|
||||
};
|
||||
|
||||
export default {
|
||||
ReqMarkers,
|
||||
insertLineEndings
|
||||
};
|
382
src/diagrams/requirement/requirementRenderer.js
Normal file
382
src/diagrams/requirement/requirementRenderer.js
Normal file
@@ -0,0 +1,382 @@
|
||||
import { line, select } from 'd3';
|
||||
import dagre from 'dagre';
|
||||
import graphlib from 'graphlib';
|
||||
import * as configApi from '../../config';
|
||||
import { log } from '../../logger';
|
||||
import { configureSvgSize } from '../../utils';
|
||||
import common from '../common/common';
|
||||
import { parser } from './parser/requirementDiagram';
|
||||
import requirementDb from './requirementDb';
|
||||
import markers from './requirementMarkers';
|
||||
|
||||
const conf = {};
|
||||
let relCnt = 0;
|
||||
|
||||
export const setConf = function(cnf) {
|
||||
if (typeof cnf === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const keys = Object.keys(cnf);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
conf[keys[i]] = cnf[keys[i]];
|
||||
}
|
||||
};
|
||||
|
||||
const newRectNode = (parentNode, id) => {
|
||||
return parentNode
|
||||
.insert('rect', '#' + id)
|
||||
.attr('class', 'req reqBox')
|
||||
.attr('fill', conf.rect_fill)
|
||||
.attr('fill-opacity', '100%')
|
||||
.attr('stroke', conf.rect_border_color)
|
||||
.attr('stroke-size', conf.rect_border_size)
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('width', conf.rect_min_width + 'px')
|
||||
.attr('height', conf.rect_min_height + 'px');
|
||||
};
|
||||
|
||||
const newTitleNode = (parentNode, id, txts) => {
|
||||
let x = conf.rect_min_width / 2;
|
||||
|
||||
let title = parentNode
|
||||
.append('text')
|
||||
.attr('class', 'req reqLabel reqTitle')
|
||||
.attr('id', id)
|
||||
.attr('x', x)
|
||||
.attr('y', conf.rect_padding)
|
||||
.attr('dominant-baseline', 'hanging')
|
||||
.attr(
|
||||
'style',
|
||||
'font-family: ' + configApi.getConfig().fontFamily + '; font-size: ' + conf.fontSize + 'px'
|
||||
);
|
||||
|
||||
let i = 0;
|
||||
txts.forEach(textStr => {
|
||||
if (i == 0) {
|
||||
title
|
||||
.append('tspan')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', conf.rect_min_width / 2)
|
||||
.attr('dy', 0)
|
||||
.text(textStr);
|
||||
} else {
|
||||
title
|
||||
.append('tspan')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', conf.rect_min_width / 2)
|
||||
.attr('dy', conf.line_height * 0.75)
|
||||
.text(textStr);
|
||||
}
|
||||
i++;
|
||||
});
|
||||
|
||||
let yPadding = 1.5 * conf.rect_padding;
|
||||
let linePadding = i * conf.line_height * 0.75;
|
||||
let totalY = yPadding + linePadding;
|
||||
|
||||
parentNode
|
||||
.append('line')
|
||||
.attr('x1', '0')
|
||||
.attr('x2', conf.rect_min_width)
|
||||
.attr('y1', totalY)
|
||||
.attr('y2', totalY)
|
||||
.attr('style', `stroke: ${conf.rect_border_color}; stroke-width: 1`);
|
||||
|
||||
return {
|
||||
titleNode: title,
|
||||
y: totalY
|
||||
};
|
||||
};
|
||||
|
||||
const newBodyNode = (parentNode, id, txts, yStart) => {
|
||||
let body = parentNode
|
||||
.append('text')
|
||||
.attr('class', 'req reqLabel')
|
||||
.attr('id', id)
|
||||
.attr('x', conf.rect_padding)
|
||||
.attr('y', yStart)
|
||||
.attr('dominant-baseline', 'hanging')
|
||||
.attr(
|
||||
'style',
|
||||
'font-family: ' + configApi.getConfig().fontFamily + '; font-size: ' + conf.fontSize + 'px'
|
||||
);
|
||||
|
||||
let currentRow = 0;
|
||||
const charLimit = 30;
|
||||
let wrappedTxts = [];
|
||||
txts.forEach(textStr => {
|
||||
let currentTextLen = textStr.length;
|
||||
while (currentTextLen > charLimit && currentRow < 3) {
|
||||
let firstPart = textStr.substring(0, charLimit);
|
||||
textStr = textStr.substring(charLimit, textStr.length);
|
||||
currentTextLen = textStr.length;
|
||||
wrappedTxts[wrappedTxts.length] = firstPart;
|
||||
currentRow++;
|
||||
}
|
||||
if (currentRow == 3) {
|
||||
let lastStr = wrappedTxts[wrappedTxts.length - 1];
|
||||
wrappedTxts[wrappedTxts.length - 1] = lastStr.substring(0, lastStr.length - 4) + '...';
|
||||
} else {
|
||||
wrappedTxts[wrappedTxts.length] = textStr;
|
||||
}
|
||||
currentRow = 0;
|
||||
});
|
||||
|
||||
wrappedTxts.forEach(textStr => {
|
||||
body
|
||||
.append('tspan')
|
||||
.attr('x', conf.rect_padding)
|
||||
.attr('dy', conf.line_height)
|
||||
.text(textStr);
|
||||
});
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
const addEdgeLabel = (parentNode, svgPath, conf, txt) => {
|
||||
// Find the half-way point
|
||||
const len = svgPath.node().getTotalLength();
|
||||
const labelPoint = svgPath.node().getPointAtLength(len * 0.5);
|
||||
|
||||
// Append a text node containing the label
|
||||
const labelId = 'rel' + relCnt;
|
||||
relCnt++;
|
||||
|
||||
const labelNode = parentNode
|
||||
.append('text')
|
||||
.attr('class', 'er relationshipLabel')
|
||||
.attr('id', labelId)
|
||||
.attr('x', labelPoint.x)
|
||||
.attr('y', labelPoint.y)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('style', 'font-family: ' + conf.fontFamily + '; font-size: ' + conf.fontSize + 'px')
|
||||
.text(txt);
|
||||
|
||||
// Figure out how big the opaque 'container' rectangle needs to be
|
||||
const labelBBox = labelNode.node().getBBox();
|
||||
|
||||
// Insert the opaque rectangle before the text label
|
||||
parentNode
|
||||
.insert('rect', '#' + labelId)
|
||||
.attr('class', 'req reqLabelBox')
|
||||
.attr('x', labelPoint.x - labelBBox.width / 2)
|
||||
.attr('y', labelPoint.y - labelBBox.height / 2)
|
||||
.attr('width', labelBBox.width)
|
||||
.attr('height', labelBBox.height)
|
||||
.attr('fill', 'white')
|
||||
.attr('fill-opacity', '85%');
|
||||
};
|
||||
|
||||
const drawRelationshipFromLayout = function(svg, rel, g, insert) {
|
||||
// Find the edge relating to this relationship
|
||||
const edge = g.edge(elementString(rel.src), elementString(rel.dst));
|
||||
|
||||
// Get a function that will generate the line path
|
||||
const lineFunction = line()
|
||||
.x(function(d) {
|
||||
return d.x;
|
||||
})
|
||||
.y(function(d) {
|
||||
return d.y;
|
||||
});
|
||||
|
||||
// Insert the line at the right place
|
||||
const svgPath = svg
|
||||
.insert('path', '#' + insert)
|
||||
.attr('class', 'er relationshipLine')
|
||||
.attr('d', lineFunction(edge.points))
|
||||
.attr('stroke', conf.rect_border_color)
|
||||
.attr('fill', 'none');
|
||||
|
||||
if (rel.type == requirementDb.Relationships.CONTAINS) {
|
||||
svgPath.attr(
|
||||
'marker-start',
|
||||
'url(' + common.getUrl(conf.arrowMarkerAbsolute) + '#' + rel.type + '_line_ending' + ')'
|
||||
);
|
||||
} else {
|
||||
svgPath.attr('stroke-dasharray', '10,7');
|
||||
svgPath.attr(
|
||||
'marker-end',
|
||||
'url(' +
|
||||
common.getUrl(conf.arrowMarkerAbsolute) +
|
||||
'#' +
|
||||
markers.ReqMarkers.ARROW +
|
||||
'_line_ending' +
|
||||
')'
|
||||
);
|
||||
}
|
||||
|
||||
addEdgeLabel(svg, svgPath, conf, `<<${rel.type}>>`);
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
export const drawReqs = (reqs, graph, svgNode) => {
|
||||
Object.keys(reqs).forEach(reqName => {
|
||||
let req = reqs[reqName];
|
||||
reqName = elementString(reqName);
|
||||
console.log('reqName: ', reqName);
|
||||
log.info('Added new requirement: ', reqName);
|
||||
|
||||
const groupNode = svgNode.append('g').attr('id', reqName);
|
||||
const textId = 'req-' + reqName;
|
||||
const rectNode = newRectNode(groupNode, textId);
|
||||
|
||||
let nodes = [];
|
||||
|
||||
let titleNodeInfo = newTitleNode(groupNode, reqName + '_title', [
|
||||
`<<${req.type}>>`,
|
||||
`${req.name}`
|
||||
]);
|
||||
|
||||
nodes.push(titleNodeInfo.titleNode);
|
||||
|
||||
let bodyNode = newBodyNode(
|
||||
groupNode,
|
||||
reqName + '_body',
|
||||
[
|
||||
`Id: ${req.id}`,
|
||||
`Text: ${req.text}`,
|
||||
`Risk: ${req.risk}`,
|
||||
`Verification: ${req.verifyMethod}`
|
||||
],
|
||||
titleNodeInfo.y
|
||||
);
|
||||
|
||||
nodes.push(bodyNode);
|
||||
|
||||
const rectBBox = rectNode.node().getBBox();
|
||||
|
||||
// Add the entity to the graph
|
||||
graph.setNode(reqName, {
|
||||
width: rectBBox.width,
|
||||
height: rectBBox.height,
|
||||
shape: 'rect',
|
||||
id: reqName
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const drawElements = (els, graph, svgNode) => {
|
||||
Object.keys(els).forEach(elName => {
|
||||
let el = els[elName];
|
||||
const id = elementString(elName);
|
||||
|
||||
const groupNode = svgNode.append('g').attr('id', id);
|
||||
const textId = 'element-' + id;
|
||||
const rectNode = newRectNode(groupNode, textId);
|
||||
|
||||
let nodes = [];
|
||||
|
||||
let titleNodeInfo = newTitleNode(groupNode, textId + '_title', [`<<Element>>`, `${elName}`]);
|
||||
|
||||
nodes.push(titleNodeInfo.titleNode);
|
||||
|
||||
let bodyNode = newBodyNode(
|
||||
groupNode,
|
||||
textId + '_body',
|
||||
[`Type: ${el.type || 'Not Specified'}`, `Doc Ref: ${el.docref || 'None'}`],
|
||||
titleNodeInfo.y
|
||||
);
|
||||
|
||||
nodes.push(bodyNode);
|
||||
|
||||
const rectBBox = rectNode.node().getBBox();
|
||||
|
||||
// Add the entity to the graph
|
||||
graph.setNode(id, {
|
||||
width: rectBBox.width,
|
||||
height: rectBBox.height,
|
||||
shape: 'rect',
|
||||
id: id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const addRelationships = (relationships, g) => {
|
||||
relationships.forEach(function(r) {
|
||||
let src = elementString(r.src);
|
||||
let dst = elementString(r.dst);
|
||||
g.setEdge(src, dst, { relationship: r });
|
||||
});
|
||||
return relationships;
|
||||
};
|
||||
|
||||
const adjustEntities = function(svgNode, graph) {
|
||||
graph.nodes().forEach(function(v) {
|
||||
if (typeof v !== 'undefined' && typeof graph.node(v) !== 'undefined') {
|
||||
svgNode.select('#' + v);
|
||||
svgNode
|
||||
.select('#' + v)
|
||||
.attr(
|
||||
'transform',
|
||||
'translate(' +
|
||||
(graph.node(v).x - graph.node(v).width / 2) +
|
||||
',' +
|
||||
(graph.node(v).y - graph.node(v).height / 2) +
|
||||
' )'
|
||||
);
|
||||
}
|
||||
});
|
||||
return;
|
||||
};
|
||||
|
||||
const elementString = str => {
|
||||
return str.replace(/\s/g, '').replace(/\./g, '_');
|
||||
};
|
||||
|
||||
export const draw = (text, id) => {
|
||||
parser.yy = requirementDb;
|
||||
parser.parse(text);
|
||||
|
||||
const svg = select(`[id='${id}']`);
|
||||
markers.insertLineEndings(svg, conf);
|
||||
|
||||
const g = new graphlib.Graph({
|
||||
multigraph: false,
|
||||
compound: false,
|
||||
directed: true
|
||||
})
|
||||
.setGraph({
|
||||
rankdir: conf.layoutDirection,
|
||||
marginx: 20,
|
||||
marginy: 20,
|
||||
nodesep: 100,
|
||||
edgesep: 100,
|
||||
ranksep: 100
|
||||
})
|
||||
.setDefaultEdgeLabel(function() {
|
||||
return {};
|
||||
});
|
||||
|
||||
let requirements = requirementDb.getRequirements();
|
||||
let elements = requirementDb.getElements();
|
||||
let relationships = requirementDb.getRelationships();
|
||||
|
||||
drawReqs(requirements, g, svg);
|
||||
drawElements(elements, g, svg);
|
||||
addRelationships(relationships, g);
|
||||
dagre.layout(g);
|
||||
adjustEntities(svg, g);
|
||||
|
||||
relationships.forEach(function(rel) {
|
||||
drawRelationshipFromLayout(svg, rel, g, id);
|
||||
});
|
||||
|
||||
// svg.attr('height', '500px');
|
||||
const padding = conf.rect_padding;
|
||||
const svgBounds = svg.node().getBBox();
|
||||
const width = svgBounds.width + padding * 2;
|
||||
const height = svgBounds.height + padding * 2;
|
||||
|
||||
configureSvgSize(svg, height, width, conf.useMaxWidth);
|
||||
|
||||
svg.attr('viewBox', `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`);
|
||||
};
|
||||
|
||||
export default {
|
||||
setConf,
|
||||
draw
|
||||
};
|
3
src/diagrams/requirement/styles.js
Normal file
3
src/diagrams/requirement/styles.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const getStyles = () => ``;
|
||||
|
||||
export default getStyles;
|
@@ -13,9 +13,47 @@
|
||||
*
|
||||
* @name mermaidAPI
|
||||
*/
|
||||
import Stylis from 'stylis';
|
||||
import { select } from 'd3';
|
||||
import Stylis from 'stylis';
|
||||
import pkg from '../package.json';
|
||||
import * as configApi from './config';
|
||||
import classDb from './diagrams/class/classDb';
|
||||
import classRenderer from './diagrams/class/classRenderer';
|
||||
import classRendererV2 from './diagrams/class/classRenderer-v2';
|
||||
import classParser from './diagrams/class/parser/classDiagram';
|
||||
import erDb from './diagrams/er/erDb';
|
||||
import erRenderer from './diagrams/er/erRenderer';
|
||||
import erParser from './diagrams/er/parser/erDiagram';
|
||||
import flowDb from './diagrams/flowchart/flowDb';
|
||||
import flowRenderer from './diagrams/flowchart/flowRenderer';
|
||||
import flowRendererV2 from './diagrams/flowchart/flowRenderer-v2';
|
||||
import flowParser from './diagrams/flowchart/parser/flow';
|
||||
import ganttDb from './diagrams/gantt/ganttDb';
|
||||
import ganttRenderer from './diagrams/gantt/ganttRenderer';
|
||||
import ganttParser from './diagrams/gantt/parser/gantt';
|
||||
import gitGraphAst from './diagrams/git/gitGraphAst';
|
||||
import gitGraphRenderer from './diagrams/git/gitGraphRenderer';
|
||||
import gitGraphParser from './diagrams/git/parser/gitGraph';
|
||||
import infoDb from './diagrams/info/infoDb';
|
||||
import infoRenderer from './diagrams/info/infoRenderer';
|
||||
import infoParser from './diagrams/info/parser/info';
|
||||
import pieParser from './diagrams/pie/parser/pie';
|
||||
import pieDb from './diagrams/pie/pieDb';
|
||||
import pieRenderer from './diagrams/pie/pieRenderer';
|
||||
import requirementParser from './diagrams/requirement/parser/requirementDiagram';
|
||||
import requirementDb from './diagrams/requirement/requirementDb';
|
||||
import requirementRenderer from './diagrams/requirement/requirementRenderer';
|
||||
import sequenceParser from './diagrams/sequence/parser/sequenceDiagram';
|
||||
import sequenceDb from './diagrams/sequence/sequenceDb';
|
||||
import sequenceRenderer from './diagrams/sequence/sequenceRenderer';
|
||||
import stateParser from './diagrams/state/parser/stateDiagram';
|
||||
import stateDb from './diagrams/state/stateDb';
|
||||
import stateRenderer from './diagrams/state/stateRenderer';
|
||||
import stateRendererV2 from './diagrams/state/stateRenderer-v2';
|
||||
import journeyDb from './diagrams/user-journey/journeyDb';
|
||||
import journeyRenderer from './diagrams/user-journey/journeyRenderer';
|
||||
import journeyParser from './diagrams/user-journey/parser/journey';
|
||||
import errorRenderer from './errorRenderer';
|
||||
// import * as configApi from './config';
|
||||
// // , {
|
||||
// // setConfig,
|
||||
@@ -26,44 +64,9 @@ import pkg from '../package.json';
|
||||
// // configApi.defaultConfig
|
||||
// // }
|
||||
import { log, setLogLevel } from './logger';
|
||||
import utils, { assignWithDepth } from './utils';
|
||||
import flowRenderer from './diagrams/flowchart/flowRenderer';
|
||||
import flowRendererV2 from './diagrams/flowchart/flowRenderer-v2';
|
||||
import flowParser from './diagrams/flowchart/parser/flow';
|
||||
import flowDb from './diagrams/flowchart/flowDb';
|
||||
import sequenceRenderer from './diagrams/sequence/sequenceRenderer';
|
||||
import sequenceParser from './diagrams/sequence/parser/sequenceDiagram';
|
||||
import sequenceDb from './diagrams/sequence/sequenceDb';
|
||||
import ganttRenderer from './diagrams/gantt/ganttRenderer';
|
||||
import ganttParser from './diagrams/gantt/parser/gantt';
|
||||
import ganttDb from './diagrams/gantt/ganttDb';
|
||||
import classRenderer from './diagrams/class/classRenderer';
|
||||
import classRendererV2 from './diagrams/class/classRenderer-v2';
|
||||
import classParser from './diagrams/class/parser/classDiagram';
|
||||
import classDb from './diagrams/class/classDb';
|
||||
import stateRenderer from './diagrams/state/stateRenderer';
|
||||
import stateRendererV2 from './diagrams/state/stateRenderer-v2';
|
||||
import stateParser from './diagrams/state/parser/stateDiagram';
|
||||
import stateDb from './diagrams/state/stateDb';
|
||||
import gitGraphRenderer from './diagrams/git/gitGraphRenderer';
|
||||
import gitGraphParser from './diagrams/git/parser/gitGraph';
|
||||
import gitGraphAst from './diagrams/git/gitGraphAst';
|
||||
import infoRenderer from './diagrams/info/infoRenderer';
|
||||
import errorRenderer from './errorRenderer';
|
||||
import infoParser from './diagrams/info/parser/info';
|
||||
import infoDb from './diagrams/info/infoDb';
|
||||
import pieRenderer from './diagrams/pie/pieRenderer';
|
||||
import pieParser from './diagrams/pie/parser/pie';
|
||||
import pieDb from './diagrams/pie/pieDb';
|
||||
import erDb from './diagrams/er/erDb';
|
||||
import erParser from './diagrams/er/parser/erDiagram';
|
||||
import erRenderer from './diagrams/er/erRenderer';
|
||||
import journeyParser from './diagrams/user-journey/parser/journey';
|
||||
import journeyDb from './diagrams/user-journey/journeyDb';
|
||||
import journeyRenderer from './diagrams/user-journey/journeyRenderer';
|
||||
import * as configApi from './config';
|
||||
import getStyles from './styles';
|
||||
import theme from './themes';
|
||||
import utils, { assignWithDepth } from './utils';
|
||||
|
||||
function parse(text) {
|
||||
const graphInit = utils.detectInit(text);
|
||||
@@ -134,6 +137,13 @@ function parse(text) {
|
||||
parser = journeyParser;
|
||||
parser.parser.yy = journeyDb;
|
||||
break;
|
||||
case 'requirement':
|
||||
case 'requirementDiagram':
|
||||
console.log('RequirementDiagram');
|
||||
log.debug('RequirementDiagram');
|
||||
parser = requirementParser;
|
||||
parser.parser.yy = requirementDb;
|
||||
break;
|
||||
}
|
||||
parser.parser.yy.graphType = graphType;
|
||||
parser.parser.yy.parseError = (str, hash) => {
|
||||
@@ -393,6 +403,10 @@ const render = function(id, _txt, cb, container) {
|
||||
journeyRenderer.setConf(cnf.journey);
|
||||
journeyRenderer.draw(txt, id, pkg.version);
|
||||
break;
|
||||
case 'requirement':
|
||||
requirementRenderer.setConf(cnf.requirement);
|
||||
requirementRenderer.draw(txt, id, pkg.version);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
// errorRenderer.setConf(cnf.class);
|
||||
@@ -539,6 +553,7 @@ function updateRendererConfigs(conf) {
|
||||
pieRenderer.setConf(conf.class);
|
||||
erRenderer.setConf(conf.er);
|
||||
journeyRenderer.setConf(conf.journey);
|
||||
requirementRenderer.setConf(conf.requirement);
|
||||
errorRenderer.setConf(conf.class);
|
||||
}
|
||||
|
||||
|
@@ -5,6 +5,7 @@ import gantt from './diagrams/gantt/styles';
|
||||
import git from './diagrams/git/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 stateDiagram from './diagrams/state/styles';
|
||||
import journey from './diagrams/user-journey/styles';
|
||||
@@ -23,7 +24,8 @@ const themes = {
|
||||
info,
|
||||
pie,
|
||||
er,
|
||||
journey
|
||||
journey,
|
||||
requirement
|
||||
};
|
||||
|
||||
export const calcThemeVariables = (theme, userOverRides) => theme.calcColors(userOverRides);
|
||||
|
@@ -147,9 +147,8 @@ export const detectDirective = function(text, type = null) {
|
||||
return result.length === 1 ? result[0] : result;
|
||||
} catch (error) {
|
||||
log.error(
|
||||
`ERROR: ${error.message} - Unable to parse directive${
|
||||
type !== null ? ' type:' + type : ''
|
||||
} based on the text:${text}`
|
||||
`ERROR: ${error.message} - Unable to parse directive
|
||||
${type !== null ? ' type:' + type : ''} based on the text:${text}`
|
||||
);
|
||||
return { type: null, args: null };
|
||||
}
|
||||
@@ -221,6 +220,10 @@ export const detectType = function(text) {
|
||||
return 'journey';
|
||||
}
|
||||
|
||||
if (text.match(/^\s*requirement/) || text.match(/^\s*requirementDiagram/)) {
|
||||
return 'requirement';
|
||||
}
|
||||
|
||||
return 'flowchart';
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user