First pass at a requirement diagram.

This commit is contained in:
Josh Sharpe 2021-02-23 23:26:37 -05:00
parent 1f523ee843
commit 55251e1024
22 changed files with 2983 additions and 1305 deletions

359
dist/index.html vendored
View File

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
@ -12,12 +13,13 @@
}
</style>
</head>
<body>
<div class="mermaid">
info
</div>
<hr/>
<hr />
<div class="mermaid">
gantt
@ -57,7 +59,7 @@
Today: 1, -1h
</div>
<hr/>
<hr />
<div class="mermaid">
graph LR
@ -159,7 +161,7 @@
<div class="mermaid">
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me thinksssssx<br/>sssssssssssssssssssuuu<br />tttsssssssssssssssssssssss}
B --> C{Let me thinksssssx<br />sssssssssssssssssssuuu<br />tttsssssssssssssssssssssss}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
@ -168,33 +170,33 @@
graph TD
A[/Christmas\]
A -->|Get money| B[\Go shopping/]
B --> C{Let me thinksssss<br/>ssssssssssssssssssssss<br/>sssssssssssssssssssssssssss}
B --> C{Let me thinksssss<br />ssssssssssssssssssssss<br />sssssssssssssssssssssssssss}
C -->|One| D[/Laptop/]
C -->|Two| E[\iPhone\]
C -->|Three| F[Car]
</div>
<div class="mermaid">
graph LR
47(SAM.CommonFA.FMESummary)-->48(SAM.CommonFA.CommonFAFinanceBudget)
37(SAM.CommonFA.BudgetSubserviceLineVolume)-->48(SAM.CommonFA.CommonFAFinanceBudget)
35(SAM.CommonFA.PopulationFME)-->47(SAM.CommonFA.FMESummary)
41(SAM.CommonFA.MetricCost)-->47(SAM.CommonFA.FMESummary)
44(SAM.CommonFA.MetricOutliers)-->47(SAM.CommonFA.FMESummary)
46(SAM.CommonFA.MetricOpportunity)-->47(SAM.CommonFA.FMESummary)
40(SAM.CommonFA.OPVisits)-->47(SAM.CommonFA.FMESummary)
38(SAM.CommonFA.CommonFAFinanceRefund)-->47(SAM.CommonFA.FMESummary)
43(SAM.CommonFA.CommonFAFinancePicuDays)-->47(SAM.CommonFA.FMESummary)
42(SAM.CommonFA.CommonFAFinanceNurseryDays)-->47(SAM.CommonFA.FMESummary)
45(SAM.CommonFA.MetricPreOpportunity)-->46(SAM.CommonFA.MetricOpportunity)
35(SAM.CommonFA.PopulationFME)-->45(SAM.CommonFA.MetricPreOpportunity)
41(SAM.CommonFA.MetricCost)-->45(SAM.CommonFA.MetricPreOpportunity)
41(SAM.CommonFA.MetricCost)-->44(SAM.CommonFA.MetricOutliers)
39(SAM.CommonFA.ChargeDetails)-->43(SAM.CommonFA.CommonFAFinancePicuDays)
39(SAM.CommonFA.ChargeDetails)-->42(SAM.CommonFA.CommonFAFinanceNurseryDays)
39(SAM.CommonFA.ChargeDetails)-->41(SAM.CommonFA.MetricCost)
39(SAM.CommonFA.ChargeDetails)-->40(SAM.CommonFA.OPVisits)
35(SAM.CommonFA.PopulationFME)-->39(SAM.CommonFA.ChargeDetails)
36(SAM.CommonFA.PremetricCost)-->39(SAM.CommonFA.ChargeDetails)
47(SAM.CommonFA.FMESummary)-->48(SAM.CommonFA.CommonFAFinanceBudget)
37(SAM.CommonFA.BudgetSubserviceLineVolume)-->48(SAM.CommonFA.CommonFAFinanceBudget)
35(SAM.CommonFA.PopulationFME)-->47(SAM.CommonFA.FMESummary)
41(SAM.CommonFA.MetricCost)-->47(SAM.CommonFA.FMESummary)
44(SAM.CommonFA.MetricOutliers)-->47(SAM.CommonFA.FMESummary)
46(SAM.CommonFA.MetricOpportunity)-->47(SAM.CommonFA.FMESummary)
40(SAM.CommonFA.OPVisits)-->47(SAM.CommonFA.FMESummary)
38(SAM.CommonFA.CommonFAFinanceRefund)-->47(SAM.CommonFA.FMESummary)
43(SAM.CommonFA.CommonFAFinancePicuDays)-->47(SAM.CommonFA.FMESummary)
42(SAM.CommonFA.CommonFAFinanceNurseryDays)-->47(SAM.CommonFA.FMESummary)
45(SAM.CommonFA.MetricPreOpportunity)-->46(SAM.CommonFA.MetricOpportunity)
35(SAM.CommonFA.PopulationFME)-->45(SAM.CommonFA.MetricPreOpportunity)
41(SAM.CommonFA.MetricCost)-->45(SAM.CommonFA.MetricPreOpportunity)
41(SAM.CommonFA.MetricCost)-->44(SAM.CommonFA.MetricOutliers)
39(SAM.CommonFA.ChargeDetails)-->43(SAM.CommonFA.CommonFAFinancePicuDays)
39(SAM.CommonFA.ChargeDetails)-->42(SAM.CommonFA.CommonFAFinanceNurseryDays)
39(SAM.CommonFA.ChargeDetails)-->41(SAM.CommonFA.MetricCost)
39(SAM.CommonFA.ChargeDetails)-->40(SAM.CommonFA.OPVisits)
35(SAM.CommonFA.PopulationFME)-->39(SAM.CommonFA.ChargeDetails)
36(SAM.CommonFA.PremetricCost)-->39(SAM.CommonFA.ChargeDetails)
</div>
<div class="mermaid">
graph TD
@ -262,7 +264,7 @@
9a072290_1ec3_e711_8c5a_005056ad0002-->71082290_1ec3_e711_8c5a_005056ad0002
</div>
<div class="mermaid">
graph TB
graph TB
subgraph One
a1-->a2
end
@ -314,7 +316,8 @@ graph TB
<div class="mermaid">
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{{Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?}}
B --> C{{Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or
something to get around?}}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
@ -328,10 +331,11 @@ graph TB
graph TD
A([stadium shape test])
A -->|Get money| B([Go shopping])
B --> C([Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?])
B --> C([Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or
something to get around?])
C -->|One| D([Laptop])
C -->|Two| E([iPhone])
C -->|Three| F([Car<br/>wroom wroom])
C -->|Three| F([Car<br />wroom wroom])
click A "index.html#link-clicked" "link test"
click B testClick "click test"
classDef someclass fill:#f96;
@ -342,10 +346,11 @@ graph TB
graph LR
A[[subroutine shape test]]
A -->|Get money| B[[Go shopping]]
B --> C[[Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?]]
B --> C[[Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or
something to get around?]]
C -->|One| D[[Laptop]]
C -->|Two| E[[iPhone]]
C -->|Three| F[[Car<br/>wroom wroom]]
C -->|Three| F[[Car<br />wroom wroom]]
click A "index.html#link-clicked" "link test"
click B testClick "click test"
classDef someclass fill:#f96;
@ -358,7 +363,8 @@ graph TB
A -->|Get money| B1[(Go shopping 1)]
A -->|Get money| B2[(Go shopping 2)]
A -->|Get money| B3[(Go shopping 3)]
C[(Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?)]
C[(Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or
something to get around?)]
B1 --> C
B2 --> C
B3 --> C
@ -373,10 +379,10 @@ graph TB
<div class="mermaid">
graph LR
A1[Multi<br>Line] -->|Multi<br>Line| B1(Multi<br>Line)
C1[Multi<br/>Line] -->|Multi<br/>Line| D1(Multi<br/>Line)
C1[Multi<br />Line] -->|Multi<br />Line| D1(Multi<br />Line)
E1[Multi<br />Line] -->|Multi<br />Line| F1(Multi<br />Line)
A2[Multi<br>Line] -->|Multi<br>Line| B2(Multi<br>Line)
C2[Multi<br/>Line] -->|Multi<br/>Line| D2(Multi<br/>Line)
C2[Multi<br />Line] -->|Multi<br />Line| D2(Multi<br />Line)
E2[Multi<br />Line] -->|Multi<br />Line| F2(Multi<br />Line)
linkStyle 0 stroke:DarkGray,stroke-width:2px
linkStyle 1 stroke:DarkGray,stroke-width:2px
@ -407,11 +413,11 @@ graph TB
click E "notes://do-your-thing/id" "other protocol test"
click F "javascript:alert('test')" "script test"
</div>
<hr/>
<hr />
<div class="mermaid">
graph LR
A[red<br>text] -->|red<br>text| B(blue<br>text)
C[/red<br/>text/] -->|blue<br/>text| D{blue<br/>text}
C[/red<br />text/] -->|blue<br />text| D{blue<br />text}
E{{default<br />style}} -->|default<br />style| F([default<br />style])
linkStyle default color:Sienna;
linkStyle 0 color:red;
@ -435,51 +441,51 @@ graph TB
class D myClass2
</div>
<hr/>
<hr />
<div class="mermaid">
sequenceDiagram
participant Alice
participant Bob
participant John as John<br/>Second Line
rect rgb(200, 220, 100)
rect rgb(200, 255, 200)
Alice ->> Bob: Hello Bob, how are you?
Bob-->>John: How about you John?
end
Bob--x Alice: I am good thanks!
Bob-x John: I am good thanks!
Note right of John: John thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
Bob-->Alice: Checking with John...
Note over John:wrap: John looks like he's still thinking, so Bob prods him a bit.
Bob-x John: Hey John - we're still waiting to know<br/>how you're doing
Note over John:nowrap: John's trying hard not to break his train of thought.
Bob-x John:wrap: John! Are you still debating about how you're doing? How long does it take??
Note over John: After a few more moments, John<br/>finally snaps out of it.
end
alt either this
Alice->>John: Yes
else or this
Alice->>John: No
else or this will happen
Alice->John: Maybe
end
par this happens in parallel
Alice -->> Bob: Parallel message 1
and
Alice -->> John: Parallel message 2
end
sequenceDiagram
participant Alice
participant Bob
participant John as John<br />Second Line
rect rgb(200, 220, 100)
rect rgb(200, 255, 200)
Alice ->> Bob: Hello Bob, how are you?
Bob-->>John: How about you John?
end
Bob--x Alice: I am good thanks!
Bob-x John: I am good thanks!
Note right of John: John thinks a long<br />long time, so long<br />that the text does<br />not fit on a row.
Bob-->Alice: Checking with John...
Note over John:wrap: John looks like he's still thinking, so Bob prods him a bit.
Bob-x John: Hey John - we're still waiting to know<br />how you're doing
Note over John:nowrap: John's trying hard not to break his train of thought.
Bob-x John:wrap: John! Are you still debating about how you're doing? How long does it take??
Note over John: After a few more moments, John<br />finally snaps out of it.
end
alt either this
Alice->>John: Yes
else or this
Alice->>John: No
else or this will happen
Alice->John: Maybe
end
par this happens in parallel
Alice -->> Bob: Parallel message 1
and
Alice -->> John: Parallel message 2
end
</div>
<div class="mermaid">
sequenceDiagram
participant 1 as multiline<br>using #lt;br#gt;
participant 2 as multiline<br/>using #lt;br/#gt;
participant 2 as multiline<br />using #lt;br/#gt;
participant 3 as multiline<br />using #lt;br /#gt;
participant 4 as multiline<br />using #lt;br /#gt;
1->>2: multiline<br>using #lt;br#gt;
note right of 2: multiline<br>using #lt;br#gt;
2->>3: multiline<br/>using #lt;br/#gt;
note right of 3: multiline<br/>using #lt;br/#gt;
2->>3: multiline<br />using #lt;br/#gt;
note right of 3: multiline<br />using #lt;br/#gt;
3->>4: multiline<br />using #lt;br /#gt;
note right of 4: multiline<br />using #lt;br /#gt;
4->>1: multiline<br />using #lt;br /#gt;
@ -489,50 +495,50 @@ end
sequenceDiagram
autonumber
Alice->>John: Hello John,<br>how are you?
Alice->>John: John,<br/>can you hear me?
Alice->>John: John,<br />can you hear me?
John-->>Alice: Hi Alice,<br />I can hear you!
John-->>Alice: I feel great!
</div>
<hr/>
<hr />
<div class="mermaid">
gantt
dateFormat YYYY-MM-DD
axisFormat %d/%m
title Adding GANTT diagram to mermaid
excludes weekdays 2014-01-10
gantt
dateFormat YYYY-MM-DD
axisFormat %d/%m
title Adding GANTT diagram to mermaid
excludes weekdays 2014-01-10
section A section
Completed task :done, des1, 2014-01-06,2014-01-08
Active task :active, des2, 2014-01-09, 3d
Future task : des3, after des2, 5d
Future task2 : des4, after des3, 5d
section A section
Completed task :done, des1, 2014-01-06,2014-01-08
Active task :active, des2, 2014-01-09, 3d
Future task : des3, after des2, 5d
Future task2 : des4, after des3, 5d
section Critical tasks
Completed task in the critical line :crit, done, 2014-01-06,24h
Implement parser and jison :crit, done, after des1, 2d
Create tests for parser :crit, active, 3d
Future task in critical line :crit, 5d
Create tests for renderer :2d
Add to mermaid :1d
section Critical tasks
Completed task in the critical line :crit, done, 2014-01-06,24h
Implement parser and jison :crit, done, after des1, 2d
Create tests for parser :crit, active, 3d
Future task in critical line :crit, 5d
Create tests for renderer :2d
Add to mermaid :1d
section Documentation
Describe gantt syntax :active, a1, after des1, 3d
Add gantt diagram to demo page :after a1 , 20h
Add another diagram to demo page :doc1, after a1 , 48h
section Documentation
Describe gantt syntax :active, a1, after des1, 3d
Add gantt diagram to demo page :after a1 , 20h
Add another diagram to demo page :doc1, after a1 , 48h
section Clickable
Visit mermaidjs :active, cl1, 2014-01-07,2014-01-10
Calling a Callback (look at the console log) :cl2, after cl1, 3d
section Clickable
Visit mermaidjs :active, cl1, 2014-01-07,2014-01-10
Calling a Callback (look at the console log) :cl2, after cl1, 3d
click cl1 href "https://mermaidjs.github.io/"
click cl2 call ganttTestClick("test", test, test)
click cl1 href "https://mermaidjs.github.io/"
click cl2 call ganttTestClick("test", test, test)
section Last section
Describe gantt syntax :after doc1, 3d
Add gantt diagram to demo page : 20h
Add another diagram to demo page : 48h
section Last section
Describe gantt syntax :after doc1, 3d
Add gantt diagram to demo page : 20h
Add another diagram to demo page : 48h
</div>
<div class="mermaid">
gantt
@ -547,7 +553,7 @@ Add another diagram to demo page : 48h
Future task : des3, after des2, 5d
Future task2 : des4, after des3, 5d
section Critical tasks<br/>multiline
section Critical tasks<br />multiline
Completed task in the critical line : crit, done, 2014-01-06, 24h
Implement parser and jison : crit, done, after des1, 2d
Create tests for parser : crit, active, 3d
@ -566,50 +572,46 @@ Add another diagram to demo page : 48h
Add another diagram to demo page : 48h
</div>
<hr/>
<hr />
<div class="mermaid">
gitGraph:
options
{
gitGraph:
options
{
"nodeSpacing": 150,
"nodeRadius": 10
}
end
commit
branch newbranch
checkout newbranch
commit
commit
checkout master
commit
commit
merge newbranch
}
end
commit
branch newbranch
checkout newbranch
commit
commit
checkout master
commit
commit
merge newbranch
</div>
<hr/>
<hr />
<div class="mermaid">
classDiagram
Class01 <|-- AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "0" *-- "0..n" Class04
Class05 "1" o-- "many" Class06
Class07 .. Class08
Class09 "many" --> "1" C2 : Where am i?
Class09 "0" --* "1..n" C3
Class09 --|> Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : #size()
Class01 : -int chimp
Class01 : +int gorilla
Class08 <--> C2: Cool label
class Class10 {
classDiagram
Class01 <|-- AveryLongClass : Cool &lt;&lt;interface&gt;&gt; Class01 Class03 "0" *-- "0..n" Class04 Class05 "1"
o-- "many" Class06 Class07 .. Class08 Class09 "many" --> "1" C2 : Where am i?
Class09 "0" --* "1..n" C3
Class09 --|> Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : #size()
Class01 : -int chimp
Class01 : +int gorilla
Class08 <--> C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
size()
}
}
</div>
<div class="mermaid">
@ -627,12 +629,8 @@ class Class10 {
<div class="mermaid">
classDiagram
Class01~T~ <|-- AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03~T~ "0" *-- "0..n" Class04
Class05 "1" o-- "many" Class06
Class07~T~ .. Class08
Class09 "many" --> "1" C2 : Where am i?
Class01~T~ <|-- AveryLongClass : Cool &lt;&lt;interface&gt;&gt; Class01 Class03~T~ "0" *-- "0..n" Class04
Class05 "1" o-- "many" Class06 Class07~T~ .. Class08 Class09 "many" --> "1" C2 : Where am i?
Class09 "0" --* "1..n" C3
Class09 --|> Class07
Class07 : equals()
@ -677,10 +675,80 @@ class Class10 {
stateDiagram
State1
note right of State1
Line1<br>Line2<br/>Line3<br />Line4<br />Line5
Line1<br>Line2<br />Line3<br />Line4<br />Line5
end note
</div>
<div class="mermaid">
requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
functionalRequirement test_req2 {
id: 1.1
text: the second test text.
risk: low
verifymethod: inspection
}
performanceRequirement test_req3 {
id: 1.2
text: the third test text.
risk: medium
verifymethod: demonstration
}
interfaceRequirement test_req4 {
id: 1.2.1
text: the fourth test text.
risk: medium
verifymethod: analysis
}
physicalRequirement test_req5 {
id: 1.2.2
text: the fifth test text.
risk: medium
verifymethod: analysis
}
designConstraint test_req6 {
id: 1.2.3
text: really long text to test overflow. really long text to test overflow. really long text to test overflow.
risk: medium
verifymethod: analysis
}
element test_entity {
type: simulation
}
element test_entity2 {
type: word doc
docRef: reqs/test_entity
}
element test_entity3 {
type: "test suite"
docRef: github.com/all_the_tests
}
test_entity - satisfies -> test_req2
test_req - traces -> test_req2
test_req - contains -> test_req3
test_req3 - contains -> test_req4
test_req4 - derives -> test_req5
test_req5 - refines -> test_req6
test_entity3 - verifies -> test_req5
test_req <- copies - test_entity2 </div>
</div>
<h1 id="link-clicked">Anchor for "link-clicked" test</h1>
<script src="./mermaid.js"></script>
@ -697,7 +765,7 @@ class Class10 {
});
</script>
<script>
function ganttTestClick(a, b, c){
function ganttTestClick(a, b, c) {
console.log("a:", a)
console.log("b:", b)
console.log("c:", c)
@ -714,7 +782,7 @@ class Class10 {
<script>
const testLineEndings = (test, input) => {
try {
mermaid.render(test, input, () => {});
mermaid.render(test, input, () => { });
} catch (err) {
console.error("Error in %s:\n\n%s", test, err);
}
@ -725,4 +793,5 @@ class Class10 {
testLineEndings("CRLF", "graph LR\r\nsubgraph CRLF\r\nA --> B\r\nend");
</script>
</body>
</html>

775
dist/mermaid.core.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

775
dist/mermaid.js vendored

File diff suppressed because one or more lines are too long

2
dist/mermaid.js.map vendored

File diff suppressed because one or more lines are too long

10
dist/mermaid.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,7 @@
- [User Journey](user-journey.md)
- [Gantt](gantt.md)
- [Pie Chart](pie.md)
- [Requirement Diagram](requirementDiagram.md)
- [Other Examples](examples.md)
- Adding mermaid✒

173
docs/requirementDiagram.md Normal file
View File

@ -0,0 +1,173 @@
# Requirement Diagram
**Edit this Page** [![N|Solid](img/GitHub-Mark-32px.png)](https://github.com/mermaid-js/mermaid/blob/develop/docs/requirementDiagram.md)
> A Requirement diagram provides a visualization for requirements and their connections, to each other and other documented elements. The modeling specs follow those defined by SysML v1.6.
Rendering requirements is straightforward.
```
requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
element test_entity {
type: simulation
}
test_entity - satisfies -> test_req
```
```mermaid
requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
element test_entity {
type: simulation
}
test_entity - satisfies -> test_req
```
## Syntax
There are three types of components to a requirement diagram: requirement, element, and relationship.
The grammar for defining each is defined below. Words denoted in angule brackets, such as ```<word>```, are enumerated keywords that have options elaborated in a table. ```user_defined_...``` is use in any place where user input is expected.
An important note on user text: all input can be surrounded in quotes or not. For example, both ```Id: "here is an example"``` and ```Id: here is an example``` are both valid. However, users must be careful with unquoted input. The parser will fail if another keyword is detected.
### Requirement
A requirement definition contains a requirement type, name, id, text, risk, and verification method. The syntax follows:
```
<type> user_defined_name {
id: user_defined_id
text: user_defined text
risk: <risk>
verifymethod: <method>
}
```
Type, risk, and method are enumerations defined in SysML.
| Keyword | Options |
| Type | requirement, functionalRequirement, interfaceRequirement, performanceRequirement, physicalRequirement, designConstraint |
| Risk | Low, Medium, High |
| VerifcationMethod | Analysis, Inspection, Test, Demonstration |
### Element
An element definition contains an element name, type, and document reference. These three are all user defined. The element feature is intended to be lightweight but allow requirements to be connected to portions of other documents.
```
element user_defined_name {
type: user_defined_type
docref: user_defined_ref
}
```
### Relationship
Relationships are comprised of a source node, destination node, and relationship type.
Each follows the definition format of
```
{name of source} - <type> -> {name of destination}
```
or
```
{name of destination} <- <type> - {name of source}
```
"name of source" and "name of destination" should be names of requirement or element nodes defined elsewhere.
A relationship type can be one of contains, copies, derives, satisfies, verifies, refines, or traces.
Each relationship is labeled in the diagram.
## Larger Example
This example uses all features of the diagram.
```mermaid
requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
functionalRequirement test_req2 {
id: 1.1
text: the second test text.
risk: low
verifymethod: inspection
}
performanceRequirement test_req3 {
id: 1.2
text: the third test text.
risk: medium
verifymethod: demonstration
}
interfaceRequirement test_req4 {
id: 1.2.1
text: the fourth test text.
risk: medium
verifymethod: analysis
}
physicalRequirement test_req5 {
id: 1.2.2
text: the fifth test text.
risk: medium
verifymethod: analysis
}
designConstraint test_req6 {
id: 1.2.3
text: the sixth test text.
risk: medium
verifymethod: analysis
}
element test_entity {
type: simulation
}
element test_entity2 {
type: word doc
docRef: reqs/test_entity
}
element test_entity3 {
type: "test suite"
docRef: github.com/all_the_tests
}
test_entity - satisfies -> test_req2
test_req - traces -> test_req2
test_req - contains -> test_req3
test_req3 - contains -> test_req4
test_req4 - derives -> test_req5
test_req5 - refines -> test_req6
test_entity3 - verifies -> test_req5
test_req <- copies - test_entity2 </div>
```

View File

@ -27,7 +27,7 @@
"e2e-upd": "yarn lint && jest e2e -u --config e2e/jest.config.js",
"dev": "webpack-dev-server --config webpack.config.e2e.js",
"test": "yarn lint && jest src/.*",
"test:watch": "jest --watch src",
"test:watch": "jest --watch src/diagrams/requirement/*",
"prepublishOnly": "yarn build && yarn test",
"prepare": "yarn build"
},

View File

@ -931,6 +931,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,12 +142,12 @@ 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}`);
@ -159,16 +158,16 @@ describe('when parsing ER diagram it...', function() {
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

@ -0,0 +1,174 @@
/** mermaid
* https://knsv.github.io/mermaid
* (c) 2015 Knut Sveidqvist
* MIT license.
*/
%lex
%options case-insensitive
%x string
%x token
%x unqString
%%
(\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;
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 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,374 @@
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 { insertLineEndings, ReqMarkers } 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', 0)
.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', conf.rect_padding)
.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(rel.src, 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) +
'#' +
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];
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 = elName.replace(/\./g, '_');
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) {
g.setEdge(r.src, r.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;
};
export const draw = (text, id) => {
log.info('Drawing requirements!');
parser.yy = requirementDb;
parser.parse(text);
const svg = select(`[id='${id}']`);
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';
};