Merge branch 'develop' into 1903_journey_config

This commit is contained in:
Knut Sveidqvist
2021-02-27 13:03:55 +01:00
30 changed files with 6379 additions and 1703 deletions

View File

@@ -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
}
};

View File

@@ -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
};

View File

@@ -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');

View File

@@ -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) +

View 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;
%%

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

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

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

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

View File

@@ -0,0 +1,3 @@
const getStyles = () => ``;
export default getStyles;

View File

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

View File

@@ -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);

View File

@@ -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';
};