Compare commits

..

38 Commits

Author SHA1 Message Date
Justin Greywolf
6629b20d37 Merge branch 'develop' into bugfix/5459_abstract-static-methods 2025-10-22 10:40:02 -07:00
Knut Sveidqvist
fed8a523a4 Merge pull request #7076 from mermaid-js/6919-fix-incorrect-viewBox-casing
6919: correct viewBox casing in Radar & Packet
2025-10-21 12:20:04 +00:00
Knut Sveidqvist
33b4946e21 Merge branch 'develop' into 6919-fix-incorrect-viewBox-casing 2025-10-21 10:33:21 +02:00
Justin Greywolf
3d768f3adf Merge pull request #6975 from ilovelinux/fix/6974-ilovelinux-fix-classdiagram-example
Fix classDiagram example
2025-10-17 02:56:34 +00:00
darshanr0107
76e17ffd20 fix: add validation to ensure correct casing of 'viewBox' in all rendering tests
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-15 18:56:49 +05:30
darshanr0107
60f633101c chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-13 19:14:22 +05:30
Shubham P
18f51eb14e Merge pull request #6843 from saurabhg772244/saurabh/fix-edge-animation-for-hand-dranw-shapes
Fixed edge animation for hand drawn shapes
2025-10-13 13:14:24 +00:00
Shubham P
2bb57bf7d2 Merge pull request #6989 from mermaid-js/subgraph-td-direction
6338: allow direction TD inside subgraphs
2025-10-13 13:13:46 +00:00
Shubham P
a6276daffd Merge pull request #7055 from mermaid-js/sequence-diagram-participant-name-parsing
6853:  prevent browser freeze caused by invalid participant name in sequenceDiagram
2025-10-13 13:12:08 +00:00
darshanr0107
7def6eecbf fix: correct viewBox casing in radar and packet diagram
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-13 18:15:38 +05:30
Antonio Spadaro
ac411a7d7e Fix class diagram example 2025-10-11 22:56:37 +02:00
Justin Greywolf
d80a638e55 Merge pull request #6023 from yari-dewalt/fix_lollipop_stroke
Fix: Delete 'stroke: black' from lollipop marker so it matches themes and edge paths
2025-10-11 20:43:08 +00:00
Justin Greywolf
7a869c08a2 Merge pull request #6026 from yari-dewalt/bug/5669_class-diagram-fix-namespace-themes
Fix: Class diagram namespaces black lines, not responding to theme variables
2025-10-11 20:42:53 +00:00
Justin Greywolf
44e8cbb1de Merge branch 'develop' into bug/5669_class-diagram-fix-namespace-themes 2025-10-11 13:27:47 -07:00
Justin Greywolf
efe38b8425 Merge branch 'develop' into fix_lollipop_stroke 2025-10-11 13:26:59 -07:00
autofix-ci[bot]
6cf17a9852 [autofix.ci] apply automated fixes 2025-10-11 20:25:57 +00:00
Justin Greywolf
07bd381197 Re-added tests, including new tests and separate files to keep size down 2025-10-11 13:14:50 -07:00
Justin Greywolf
3bb9416537 refactor: streamline classTypes tests and add comprehensive coverage for abstract+static combinations
- Replaced the 980-line monolithic test file with focused, concise tests
- Added comprehensive test coverage for new abstract+static classifier combinations (*$ and $*)
- Verified both methods and attributes work with combined classifiers
- Ensured backward compatibility with existing single classifiers
- All 9 tests passing, covering the core functionality and edge cases
- Tests validate CSS styling (underline + italic) for combined classifiers
2025-10-11 12:36:23 -07:00
Justin Greywolf
3e6b7bc3df Add comprehensive tests for abstract static classifiers 2025-10-11 12:10:23 -07:00
darshanr0107
6fecb985e8 fix: failing argos
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-09 11:50:44 +05:30
darshanr0107
69b338d8af fix: formatting issues
Co-authored-by: omkarht <omkar@mermaidchart.com>
2025-10-09 11:37:05 +05:30
darshanr0107
fa15ce8502 chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-08 13:23:28 +05:30
darshanr0107
6d0650918f fix: handle participant names with spaces correctly in sequenceDiagram
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-08 13:09:30 +05:30
darshanr0107
1a9d45abf0 fix: allow direction TD inside subgraphs
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-09-24 12:22:31 +05:30
Saurabh Gore
bbb93b263d Merge branch 'develop' into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-13 11:37:21 +05:30
saurabhg772244
4240340a18 Merge branch 'develop' of github.com:mermaid-js/mermaid into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-11 11:36:26 +05:30
omkarht
ca10a259fa Merge branch 'develop' into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-05 16:28:39 +05:30
Saurabh Gore
0ed9c65572 Merge branch 'develop' into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-05 12:56:27 +05:30
saurabhg772244
56cc12690f Added option to skip screenshot for cypress tests 2025-09-05 12:47:07 +05:30
saurabhg772244
e6fb4a84da code refactor 2025-08-11 16:32:03 +05:30
saurabhg772244
32723b2de1 Added change set 2025-08-11 15:59:07 +05:30
saurabhg772244
18703782ee Fixed edge animation for hand drawn shapes 2025-08-11 15:52:49 +05:30
autofix-ci[bot]
47297f7c26 [autofix.ci] apply automated fixes 2025-02-26 17:49:51 +00:00
Yari DeWalt
967aa0629e Merge branch 'develop' into bug/5669_class-diagram-fix-namespace-themes 2025-02-26 09:45:14 -08:00
Yari DeWalt
04b20a79b9 Merge branch 'develop' into fix_lollipop_stroke 2025-02-26 09:20:16 -08:00
yari-dewalt
4ff2ae9f4e Add styles for clusters and remove hard-coded styling for namespace nodes 2024-11-06 09:00:21 -08:00
Yari DeWalt
7a729e8f16 Merge branch 'develop' into fix_lollipop_stroke 2024-11-04 10:21:00 -08:00
yari-dewalt
3c7fd95617 Remove black stroke from lollipops so it matches edge paths 2024-11-04 10:15:51 -08:00
26 changed files with 1561 additions and 831 deletions

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Support edge animation in hand drawn look

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Resolved parsing error where direction TD was not recognized within subgraphs

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Correct viewBox casing and make SVGs responsive

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Improve participant parsing and prevent recursive loops on invalid syntax

View File

@@ -6,6 +6,7 @@ interface CypressConfig {
listUrl?: boolean; listUrl?: boolean;
listId?: string; listId?: string;
name?: string; name?: string;
screenshot?: boolean;
} }
type CypressMermaidConfig = MermaidConfig & CypressConfig; type CypressMermaidConfig = MermaidConfig & CypressConfig;
@@ -90,7 +91,7 @@ export const renderGraph = (
export const openURLAndVerifyRendering = ( export const openURLAndVerifyRendering = (
url: string, url: string,
options: CypressMermaidConfig, { screenshot = true, ...options }: CypressMermaidConfig,
validation?: any validation?: any
): void => { ): void => {
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
@@ -98,12 +99,15 @@ export const openURLAndVerifyRendering = (
cy.visit(url); cy.visit(url);
cy.window().should('have.property', 'rendered', true); cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible'); cy.get('svg').should('be.visible');
cy.get('svg').should('not.have.attr', 'viewbox');
if (validation) { if (validation) {
cy.get('svg').should(validation); cy.get('svg').should(validation);
} }
if (screenshot) {
verifyScreenshot(name); verifyScreenshot(name);
}
}; };
export const verifyScreenshot = (name: string): void => { export const verifyScreenshot = (name: string): void => {

View File

@@ -1029,4 +1029,19 @@ graph TD
} }
); );
}); });
it('FDH49: should add edge animation', () => {
renderGraph(
`
flowchart TD
A(["Start"]) L_A_B_0@--> B{"Decision"}
B --> C["Option A"] & D["Option B"]
style C stroke-width:4px,stroke-dasharray: 5
L_A_B_0@{ animation: slow }
L_B_D_0@{ animation: fast }`,
{ look: 'handDrawn', screenshot: false }
);
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
});
}); });

View File

@@ -774,6 +774,21 @@ describe('Graph', () => {
expect(svg).to.not.have.attr('style'); expect(svg).to.not.have.attr('style');
}); });
}); });
it('40: should add edge animation', () => {
renderGraph(
`
flowchart TD
A(["Start"]) L_A_B_0@--> B{"Decision"}
B --> C["Option A"] & D["Option B"]
style C stroke-width:4px,stroke-dasharray: 5
L_A_B_0@{ animation: slow }
L_B_D_0@{ animation: fast }`,
{ screenshot: false }
);
// Verify animation classes are applied to both edges
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
});
it('58: handle styling with style expressions', () => { it('58: handle styling with style expressions', () => {
imgSnapshotTest( imgSnapshotTest(
` `
@@ -973,4 +988,19 @@ graph TD
} }
); );
}); });
it('70: should render a subgraph with direction TD', () => {
imgSnapshotTest(
`
flowchart LR
subgraph A
direction TD
a --> b
end
`,
{
fontFamily: 'courier',
}
);
});
}); });

View File

@@ -603,6 +603,10 @@
</div> </div>
<div class="test"> <div class="test">
<pre class="mermaid"> <pre class="mermaid">
---
config:
theme: dark
---
classDiagram classDiagram
test ()--() test2 test ()--() test2
</pre> </pre>

View File

@@ -21,7 +21,7 @@ title: Animal example
classDiagram classDiagram
note "From Duck till Zebra" note "From Duck till Zebra"
Animal <|-- Duck Animal <|-- Duck
note for Duck "can fly\ncan swim\ncan dive\ncan help in debugging" note for Duck "can fly<br>can swim<br>can dive<br>can help in debugging"
Animal <|-- Fish Animal <|-- Fish
Animal <|-- Zebra Animal <|-- Zebra
Animal : +int age Animal : +int age
@@ -50,7 +50,7 @@ title: Animal example
classDiagram classDiagram
note "From Duck till Zebra" note "From Duck till Zebra"
Animal <|-- Duck Animal <|-- Duck
note for Duck "can fly\ncan swim\ncan dive\ncan help in debugging" note for Duck "can fly<br>can swim<br>can dive<br>can help in debugging"
Animal <|-- Fish Animal <|-- Fish
Animal <|-- Zebra Animal <|-- Zebra
Animal : +int age Animal : +int age
@@ -287,6 +287,7 @@ To describe the visibility (or encapsulation) of an attribute or method/function
> >
> - `*` Abstract e.g.: `someAbstractMethod()*` or `someAbstractMethod() int*` > - `*` Abstract e.g.: `someAbstractMethod()*` or `someAbstractMethod() int*`
> - `$` Static e.g.: `someStaticMethod()$` or `someStaticMethod() String$` > - `$` Static e.g.: `someStaticMethod()$` or `someStaticMethod() String$`
> - `$*` OR `*$` Both e.g: `someAbstractStaticMethod()$*` or `someAbstractStaticMethod() int$*`
> _note_ you can also include additional _classifiers_ to a field definition by adding the following notation to the very end: > _note_ you can also include additional _classifiers_ to a field definition by adding the following notation to the very end:
> >

View File

@@ -265,6 +265,10 @@ export class ClassDB implements DiagramDB {
theClass.annotations.push(sanitizeText(memberString.substring(2, memberString.length - 2))); theClass.annotations.push(sanitizeText(memberString.substring(2, memberString.length - 2)));
} else if (memberString.indexOf(')') > 0) { } else if (memberString.indexOf(')') > 0) {
//its a method //its a method
if (memberString.length < 2) {
// Too short to be a method, ignore
return;
}
theClass.methods.push(new ClassMember(memberString, 'method')); theClass.methods.push(new ClassMember(memberString, 'method'));
} else if (memberString) { } else if (memberString) {
theClass.members.push(new ClassMember(memberString, 'attribute')); theClass.members.push(new ClassMember(memberString, 'attribute'));
@@ -627,7 +631,7 @@ export class ClassDB implements DiagramDB {
padding: config.class!.padding ?? 16, padding: config.class!.padding ?? 16,
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider] // parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
shape: 'rect', shape: 'rect',
cssStyles: ['fill: none', 'stroke: black'], cssStyles: [],
look: config.look, look: config.look,
}; };
nodes.push(node); nodes.push(node);

View File

@@ -0,0 +1,317 @@
import { ClassMember } from './classTypes.js';
import { vi, describe, it, expect } from 'vitest';
const spyOn = vi.spyOn;
const staticCssStyle = 'text-decoration:underline;';
const abstractCssStyle = 'font-style:italic;';
const abstractStaticCssStyle = 'text-decoration:underline;font-style:italic;';
describe('ClassTypes - Attribute Tests', () => {
describe('Basic attribute parsing without classifiers', () => {
it('should parse attribute with no modifiers', () => {
const str = 'name String';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('name String');
expect(displayDetails.cssStyle).toBe('');
});
it('should parse attribute with public "+" visibility', () => {
const str = '+name String';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('+name String');
expect(displayDetails.cssStyle).toBe('');
});
it('should parse attribute with protected "#" visibility', () => {
const str = '#name String';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('#name String');
expect(displayDetails.cssStyle).toBe('');
});
it('should parse attribute with private "-" visibility', () => {
const str = '-name String';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('-name String');
expect(displayDetails.cssStyle).toBe('');
});
it('should parse attribute with internal "~" visibility', () => {
const str = '~name String';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('~name String');
expect(displayDetails.cssStyle).toBe('');
});
it('should parse simple attribute name only', () => {
const str = 'id';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('id');
expect(displayDetails.cssStyle).toBe('');
});
it('should parse attribute with visibility and name only', () => {
const str = '+id';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('+id');
expect(displayDetails.cssStyle).toBe('');
});
});
describe('Static classifier ($) attributes', () => {
it('should parse static attribute without visibility', () => {
const str = 'count int$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('count int');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
it('should parse static attribute with public visibility', () => {
const str = '+count int$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('+count int');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
it('should parse static attribute with protected visibility', () => {
const str = '#count int$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('#count int');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
it('should parse static attribute with private visibility', () => {
const str = '-count int$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('-count int');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
it('should parse static attribute with internal visibility', () => {
const str = '~count int$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('~count int');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
it('should parse static attribute name only', () => {
const str = 'MAX_SIZE$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('MAX_SIZE');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
});
describe('Abstract classifier (*) attributes', () => {
it('should parse abstract attribute without visibility', () => {
const str = 'data String*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('data String');
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
});
it('should parse abstract attribute with public visibility', () => {
const str = '+data String*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('+data String');
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
});
it('should parse abstract attribute with protected visibility', () => {
const str = '#data String*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('#data String');
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
});
it('should parse abstract attribute with private visibility', () => {
const str = '-data String*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('-data String');
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
});
it('should parse abstract attribute with internal visibility', () => {
const str = '~data String*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('~data String');
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
});
it('should parse abstract attribute name only', () => {
const str = 'value*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('value');
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
});
});
describe('Abstract and Static combined classifiers', () => {
it('should parse abstract+static ($*) attribute without visibility', () => {
const str = 'config Map$*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('config Map');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse static+abstract (*$) attribute without visibility', () => {
const str = 'config Map*$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('config Map');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse abstract+static ($*) attribute with public visibility', () => {
const str = '+config Map$*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('+config Map');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse static+abstract (*$) attribute with public visibility', () => {
const str = '+config Map*$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('+config Map');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse abstract+static ($*) attribute with protected visibility', () => {
const str = '#registry HashMap$*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('#registry HashMap');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse static+abstract (*$) attribute with protected visibility', () => {
const str = '#registry HashMap*$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('#registry HashMap');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse abstract+static ($*) attribute with private visibility', () => {
const str = '-cache LRUCache$*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('-cache LRUCache');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse static+abstract (*$) attribute with private visibility', () => {
const str = '-cache LRUCache*$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('-cache LRUCache');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse abstract+static ($*) attribute with internal visibility', () => {
const str = '~pool ThreadPool$*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('~pool ThreadPool');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse static+abstract (*$) attribute with internal visibility', () => {
const str = '~pool ThreadPool*$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('~pool ThreadPool');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse abstract+static ($*) attribute name only', () => {
const str = 'INSTANCE$*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('INSTANCE');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse static+abstract (*$) attribute name only', () => {
const str = 'INSTANCE*$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('INSTANCE');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('Complex attribute type scenarios', () => {
it('should parse generic type attribute with static classifier', () => {
const str = '+items List~String~$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('+items List<String>');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
it('should parse nested generic type attribute with abstract classifier', () => {
const str = '#mapping Map~String, List~Integer~~*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('#mapping Map~String, List<Integer~>');
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
});
it('should parse complex generic type with abstract+static classifiers', () => {
const str = '+factory Function~Map~String, Object~, Promise~Result~~$*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe(
'+factory Function<Map>String, Object~, Promise<Result~>'
);
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should parse attribute with spaces in type name', () => {
const str = '+fullName Full Name String$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('+fullName Full Name String');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
it('should parse attribute with special characters in name', () => {
const str = '+user_name String*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('+user_name String');
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
});
it('should parse attribute with numeric suffix', () => {
const str = '-value123 int$*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('-value123 int');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
});
});

View File

@@ -0,0 +1,170 @@
import { describe, it, expect } from 'vitest';
import { ClassMember } from './classTypes.js';
describe('ClassTypes - Enhanced Abstract and Static Combinations', () => {
// Test constants to match original test structure
const staticCssStyle = 'text-decoration:underline;';
const abstractCssStyle = 'font-style:italic;';
const abstractStaticCssStyle = 'text-decoration:underline;font-style:italic;';
describe('Enhanced parseClassifier functionality', () => {
describe('when the attribute has static "$" modifier', () => {
it('should parse the display text correctly and apply static css style', () => {
const str = 'name String$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('name String');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
});
describe('when the attribute has abstract "*" modifier', () => {
it('should parse the display text correctly and apply abstract css style', () => {
const str = 'name String*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('name String');
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
});
});
describe('when the attribute has abstract static "*$" modifier', () => {
it('should parse the display text correctly and apply abstract static css style', () => {
const str = 'name String*$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('name String');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when the attribute has static abstract "$*" modifier', () => {
it('should parse the display text correctly and apply abstract static css style', () => {
const str = 'name String$*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('name String');
expect(displayDetails.cssStyle).toBe(abstractStaticCssStyle);
});
it('should handle abstract and static combined (*$) on methods', () => {
const str = 'getTime()*$';
const classMember = new ClassMember(str, 'method');
const details = classMember.getDisplayDetails();
expect(details.displayText).toBe('getTime()');
expect(details.cssStyle).toBe(abstractStaticCssStyle);
});
it('should handle static and abstract combined ($*) on methods', () => {
const str = 'getTime()$*';
const classMember = new ClassMember(str, 'method');
const details = classMember.getDisplayDetails();
expect(details.displayText).toBe('getTime()');
expect(details.cssStyle).toBe(abstractStaticCssStyle);
});
it('should handle abstract and static combined (*$) on attributes', () => {
const str = 'data String*$';
const classMember = new ClassMember(str, 'attribute');
const details = classMember.getDisplayDetails();
expect(details.displayText).toBe('data String');
expect(details.cssStyle).toBe(abstractStaticCssStyle);
});
it('should handle static and abstract combined ($*) on attributes', () => {
const str = 'data String$*';
const classMember = new ClassMember(str, 'attribute');
const details = classMember.getDisplayDetails();
expect(details.displayText).toBe('data String');
expect(details.cssStyle).toBe(abstractStaticCssStyle);
});
it('should handle complex method with abstract static combination', () => {
const str = '+processData(Map~String, List~Integer~~) Optional~Result~*$';
const classMember = new ClassMember(str, 'method');
const details = classMember.getDisplayDetails();
expect(details.displayText).toBe(
'+processData(Map~String, List<Integer~>) : Optional<Result>'
);
expect(details.cssStyle).toBe(abstractStaticCssStyle);
});
it('should handle attribute with visibility and abstract static combination', () => {
const str = '#config Settings$*';
const classMember = new ClassMember(str, 'attribute');
const details = classMember.getDisplayDetails();
expect(details.displayText).toBe('#config Settings');
expect(details.cssStyle).toBe(abstractStaticCssStyle);
});
// Verify existing classifier functionality still works
it('should still handle single static classifier correctly', () => {
const str = 'getName()$';
const classMember = new ClassMember(str, 'method');
const details = classMember.getDisplayDetails();
expect(details.displayText).toBe('getName()');
expect(details.cssStyle).toBe(staticCssStyle);
});
it('should still handle single abstract classifier correctly', () => {
const str = 'name String*';
const classMember = new ClassMember(str, 'attribute');
const details = classMember.getDisplayDetails();
expect(details.displayText).toBe('name String');
expect(details.cssStyle).toBe(abstractCssStyle);
});
it('should handle empty classifier correctly', () => {
const str = 'getValue()';
const classMember = new ClassMember(str, 'method');
const details = classMember.getDisplayDetails();
expect(details.displayText).toBe('getValue()');
expect(details.cssStyle).toBe('');
});
});
it('should return correct css for static classifier', function () {
const str = `getTime()$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime()*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTime()*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTime()$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
});

View File

@@ -0,0 +1,827 @@
import { ClassMember } from './classTypes.js';
import { vi, describe, it, expect } from 'vitest';
const spyOn = vi.spyOn;
const staticCssStyle = 'text-decoration:underline;';
const abstractCssStyle = 'font-style:italic;';
const abstractStaticCssStyle = 'text-decoration:underline;font-style:italic;';
describe('given text representing a method, ', function () {
describe('when method has no parameters', function () {
it('should parse correctly', function () {
const str = `getTime()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle public visibility', function () {
const str = `+getTime()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTime()');
});
it('should handle private visibility', function () {
const str = `-getTime()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTime()');
});
it('should handle protected visibility', function () {
const str = `#getTime()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTime()');
});
it('should handle internal visibility', function () {
const str = `~getTime()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTime()');
});
});
describe('when method has single parameter value', function () {
it('should parse correctly', function () {
const str = `getTime(int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle public visibility', function () {
const str = `+getTime(int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTime(int)');
});
it('should handle private visibility', function () {
const str = `-getTime(int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTime(int)');
});
it('should handle protected visibility', function () {
const str = `#getTime(int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTime(int)');
});
it('should handle internal visibility', function () {
const str = `~getTime(int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTime(int)');
});
it('should return correct css for static classifier', function () {
const str = `getTime(int)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime(int)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTime(int)*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTime(int)$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when method has single parameter type and name (type first)', function () {
it('should parse correctly', function () {
const str = `getTime(int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle public visibility', function () {
const str = `+getTime(int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTime(int count)');
});
it('should handle private visibility', function () {
const str = `-getTime(int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTime(int count)');
});
it('should handle protected visibility', function () {
const str = `#getTime(int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTime(int count)');
});
it('should handle internal visibility', function () {
const str = `~getTime(int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTime(int count)');
});
it('should return correct css for static classifier', function () {
const str = `getTime(int count)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime(int count)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTime(int count)*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTime(int count)$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when method has single parameter type and name (name first)', function () {
it('should parse correctly', function () {
const str = `getTime(count int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle public visibility', function () {
const str = `+getTime(count int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTime(count int)');
});
it('should handle private visibility', function () {
const str = `-getTime(count int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTime(count int)');
});
it('should handle protected visibility', function () {
const str = `#getTime(count int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTime(count int)');
});
it('should handle internal visibility', function () {
const str = `~getTime(count int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTime(count int)');
});
it('should return correct css for static classifier', function () {
const str = `getTime(count int)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(count int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime(count int)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(count int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTime(count int)*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(count int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTime(count int)$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(count int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when method has multiple parameters', function () {
it('should parse correctly', function () {
const str = `getTime(string text, int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle public visibility', function () {
const str = `+getTime(string text, int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle private visibility', function () {
const str = `-getTime(string text, int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle protected visibility', function () {
const str = `#getTime(string text, int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle internal visibility', function () {
const str = `~getTime(string text, int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should return correct css for static classifier', function () {
const str = `getTime(string text, int count)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(string text, int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime(string text, int count)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(string text, int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTime(string text, int count)*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(string text, int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTime(string text, int count)$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(string text, int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when method has return type', function () {
it('should parse correctly', function () {
const str = `getTime() DateTime`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
});
it('should handle public visibility', function () {
const str = `+getTime() DateTime`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTime() : DateTime');
});
it('should handle private visibility', function () {
const str = `-getTime() DateTime`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTime() : DateTime');
});
it('should handle protected visibility', function () {
const str = `#getTime() DateTime`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTime() : DateTime');
});
it('should handle internal visibility', function () {
const str = `~getTime() DateTime`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTime() : DateTime');
});
it('should return correct css for static classifier', function () {
const str = `getTime() DateTime$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime() DateTime*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTime() DateTime*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTime() DateTime$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when method parameter is generic', function () {
it('should parse correctly', function () {
const str = `getTimes(List~T~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
});
it('should handle public visibility', function () {
const str = `+getTimes(List~T~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTimes(List<T>)');
});
it('should handle private visibility', function () {
const str = `-getTimes(List~T~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTimes(List<T>)');
});
it('should handle protected visibility', function () {
const str = `#getTimes(List~T~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTimes(List<T>)');
});
it('should handle internal visibility', function () {
const str = `~getTimes(List~T~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTimes(List<T>)');
});
it('should return correct css for static classifier', function () {
const str = `getTimes(List~T~)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTimes(List~T~)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTimes(List~T~)*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTimes(List~T~)$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when method parameter contains two generic', function () {
it('should parse correctly', function () {
const str = `getTimes(List~T~, List~OT~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
});
it('should handle public visibility', function () {
const str = `+getTimes(List~T~, List~OT~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTimes(List<T>, List<OT>)');
});
it('should handle private visibility', function () {
const str = `-getTimes(List~T~, List~OT~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTimes(List<T>, List<OT>)');
});
it('should handle protected visibility', function () {
const str = `#getTimes(List~T~, List~OT~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTimes(List<T>, List<OT>)');
});
it('should handle internal visibility', function () {
const str = `~getTimes(List~T~, List~OT~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTimes(List<T>, List<OT>)');
});
it('should return correct css for static classifier', function () {
const str = `getTimes(List~T~, List~OT~)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTimes(List~T~, List~OT~)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTimes(List~T~, List~OT~)*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTimes(List~T~, List~OT~)$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when method parameter is a nested generic', function () {
it('should parse correctly', function () {
const str = `getTimetableList(List~List~T~~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
});
it('should handle public visibility', function () {
const str = `+getTimetableList(List~List~T~~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTimetableList(List<List<T>>)');
});
it('should handle private visibility', function () {
const str = `-getTimetableList(List~List~T~~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTimetableList(List<List<T>>)');
});
it('should handle protected visibility', function () {
const str = `#getTimetableList(List~List~T~~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTimetableList(List<List<T>>)');
});
it('should handle internal visibility', function () {
const str = `~getTimetableList(List~List~T~~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTimetableList(List<List<T>>)');
});
it('should return correct css for static classifier', function () {
const str = `getTimetableList(List~List~T~~)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTimetableList(List~List~T~~)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTimetableList(List~List~T~~)*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTimetableList(List~List~T~~)$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when method parameter is a composite generic', function () {
const methodNameAndParameters = 'getTimes(List~K, V~)';
const expectedMethodNameAndParameters = 'getTimes(List<K, V>)';
it('should parse correctly', function () {
const str = methodNameAndParameters;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
});
it('should handle public visibility', function () {
const str = '+' + methodNameAndParameters;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'+' + expectedMethodNameAndParameters
);
});
it('should handle private visibility', function () {
const str = '-' + methodNameAndParameters;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'-' + expectedMethodNameAndParameters
);
});
it('should handle protected visibility', function () {
const str = '#' + methodNameAndParameters;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'#' + expectedMethodNameAndParameters
);
});
it('should handle internal visibility', function () {
const str = '~' + methodNameAndParameters;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'~' + expectedMethodNameAndParameters
);
});
it('should return correct css for static classifier', function () {
const str = methodNameAndParameters + '$';
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = methodNameAndParameters + '*';
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = methodNameAndParameters + '*$';
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = methodNameAndParameters + '$*';
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when method return type is generic', function () {
it('should parse correctly', function () {
const str = `getTimes() List~T~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
});
it('should handle public visibility', function () {
const str = `+getTimes() List~T~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTimes() : List<T>');
});
it('should handle private visibility', function () {
const str = `-getTimes() List~T~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTimes() : List<T>');
});
it('should handle protected visibility', function () {
const str = `#getTimes() List~T~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTimes() : List<T>');
});
it('should handle internal visibility', function () {
const str = `~getTimes() List~T~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTimes() : List<T>');
});
it('should return correct css for static classifier', function () {
const str = `getTimes() List~T~$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTimes() List~T~*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTimes() List~T~*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTimes() List~T~$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
describe('when method return type is a nested generic', function () {
it('should parse correctly', function () {
const str = `getTimetableList() List~List~T~~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'getTimetableList() : List<List<T>>'
);
});
it('should handle public visibility', function () {
const str = `+getTimetableList() List~List~T~~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'+getTimetableList() : List<List<T>>'
);
});
it('should handle private visibility', function () {
const str = `-getTimetableList() List~List~T~~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'-getTimetableList() : List<List<T>>'
);
});
it('should handle protected visibility', function () {
const str = `#getTimetableList() List~List~T~~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'#getTimetableList() : List<List<T>>'
);
});
it('should handle internal visibility', function () {
const str = `~getTimetableList() List~List~T~~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'~getTimetableList() : List<List<T>>'
);
});
it('should return correct css for static classifier', function () {
const str = `getTimetableList() List~List~T~~$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'getTimetableList() : List<List<T>>'
);
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTimetableList() List~List~T~~*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'getTimetableList() : List<List<T>>'
);
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
it('should return correct css for abstract static classifier', function () {
const str = `getTimetableList() List~List~T~~*$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'getTimetableList() : List<List<T>>'
);
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
it('should return correct css for static abstract classifier', function () {
const str = `getTimetableList() List~List~T~~$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'getTimetableList() : List<List<T>>'
);
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractStaticCssStyle);
});
});
});

View File

@@ -4,664 +4,9 @@ const spyOn = vi.spyOn;
const staticCssStyle = 'text-decoration:underline;'; const staticCssStyle = 'text-decoration:underline;';
const abstractCssStyle = 'font-style:italic;'; const abstractCssStyle = 'font-style:italic;';
const abstractStaticCssStyle = 'text-decoration:underline;font-style:italic;';
describe('given text representing a method, ', function () { describe('given text representing a method, ', function () {
describe('when method has no parameters', function () {
it('should parse correctly', function () {
const str = `getTime()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle public visibility', function () {
const str = `+getTime()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTime()');
});
it('should handle private visibility', function () {
const str = `-getTime()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTime()');
});
it('should handle protected visibility', function () {
const str = `#getTime()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTime()');
});
it('should handle internal visibility', function () {
const str = `~getTime()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTime()');
});
it('should return correct css for static classifier', function () {
const str = `getTime()$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime()*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime()');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method has single parameter value', function () {
it('should parse correctly', function () {
const str = `getTime(int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle public visibility', function () {
const str = `+getTime(int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTime(int)');
});
it('should handle private visibility', function () {
const str = `-getTime(int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTime(int)');
});
it('should handle protected visibility', function () {
const str = `#getTime(int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTime(int)');
});
it('should handle internal visibility', function () {
const str = `~getTime(int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTime(int)');
});
it('should return correct css for static classifier', function () {
const str = `getTime(int)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime(int)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method has single parameter type and name (type first)', function () {
it('should parse correctly', function () {
const str = `getTime(int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle public visibility', function () {
const str = `+getTime(int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTime(int count)');
});
it('should handle private visibility', function () {
const str = `-getTime(int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTime(int count)');
});
it('should handle protected visibility', function () {
const str = `#getTime(int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTime(int count)');
});
it('should handle internal visibility', function () {
const str = `~getTime(int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTime(int count)');
});
it('should return correct css for static classifier', function () {
const str = `getTime(int count)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime(int count)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method has single parameter type and name (name first)', function () {
it('should parse correctly', function () {
const str = `getTime(count int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle public visibility', function () {
const str = `+getTime(count int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTime(count int)');
});
it('should handle private visibility', function () {
const str = `-getTime(count int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTime(count int)');
});
it('should handle protected visibility', function () {
const str = `#getTime(count int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTime(count int)');
});
it('should handle internal visibility', function () {
const str = `~getTime(count int)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTime(count int)');
});
it('should return correct css for static classifier', function () {
const str = `getTime(count int)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(count int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime(count int)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(count int)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method has multiple parameters', function () {
it('should parse correctly', function () {
const str = `getTime(string text, int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle public visibility', function () {
const str = `+getTime(string text, int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle private visibility', function () {
const str = `-getTime(string text, int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle protected visibility', function () {
const str = `#getTime(string text, int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should handle internal visibility', function () {
const str = `~getTime(string text, int count)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(str);
});
it('should return correct css for static classifier', function () {
const str = `getTime(string text, int count)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(string text, int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime(string text, int count)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime(string text, int count)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method has return type', function () {
it('should parse correctly', function () {
const str = `getTime() DateTime`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
});
it('should handle public visibility', function () {
const str = `+getTime() DateTime`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTime() : DateTime');
});
it('should handle private visibility', function () {
const str = `-getTime() DateTime`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTime() : DateTime');
});
it('should handle protected visibility', function () {
const str = `#getTime() DateTime`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTime() : DateTime');
});
it('should handle internal visibility', function () {
const str = `~getTime() DateTime`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTime() : DateTime');
});
it('should return correct css for static classifier', function () {
const str = `getTime() DateTime$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTime() DateTime*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTime() : DateTime');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method parameter is generic', function () {
it('should parse correctly', function () {
const str = `getTimes(List~T~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
});
it('should handle public visibility', function () {
const str = `+getTimes(List~T~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTimes(List<T>)');
});
it('should handle private visibility', function () {
const str = `-getTimes(List~T~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTimes(List<T>)');
});
it('should handle protected visibility', function () {
const str = `#getTimes(List~T~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTimes(List<T>)');
});
it('should handle internal visibility', function () {
const str = `~getTimes(List~T~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTimes(List<T>)');
});
it('should return correct css for static classifier', function () {
const str = `getTimes(List~T~)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTimes(List~T~)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method parameter contains two generic', function () {
it('should parse correctly', function () {
const str = `getTimes(List~T~, List~OT~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
});
it('should handle public visibility', function () {
const str = `+getTimes(List~T~, List~OT~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTimes(List<T>, List<OT>)');
});
it('should handle private visibility', function () {
const str = `-getTimes(List~T~, List~OT~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTimes(List<T>, List<OT>)');
});
it('should handle protected visibility', function () {
const str = `#getTimes(List~T~, List~OT~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTimes(List<T>, List<OT>)');
});
it('should handle internal visibility', function () {
const str = `~getTimes(List~T~, List~OT~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTimes(List<T>, List<OT>)');
});
it('should return correct css for static classifier', function () {
const str = `getTimes(List~T~, List~OT~)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTimes(List~T~, List~OT~)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes(List<T>, List<OT>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method parameter is a nested generic', function () {
it('should parse correctly', function () {
const str = `getTimetableList(List~List~T~~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
});
it('should handle public visibility', function () {
const str = `+getTimetableList(List~List~T~~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTimetableList(List<List<T>>)');
});
it('should handle private visibility', function () {
const str = `-getTimetableList(List~List~T~~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTimetableList(List<List<T>>)');
});
it('should handle protected visibility', function () {
const str = `#getTimetableList(List~List~T~~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTimetableList(List<List<T>>)');
});
it('should handle internal visibility', function () {
const str = `~getTimetableList(List~List~T~~)`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTimetableList(List<List<T>>)');
});
it('should return correct css for static classifier', function () {
const str = `getTimetableList(List~List~T~~)$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTimetableList(List~List~T~~)*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimetableList(List<List<T>>)');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method parameter is a composite generic', function () {
const methodNameAndParameters = 'getTimes(List~K, V~)';
const expectedMethodNameAndParameters = 'getTimes(List<K, V>)';
it('should parse correctly', function () {
const str = methodNameAndParameters;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
});
it('should handle public visibility', function () {
const str = '+' + methodNameAndParameters;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'+' + expectedMethodNameAndParameters
);
});
it('should handle private visibility', function () {
const str = '-' + methodNameAndParameters;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'-' + expectedMethodNameAndParameters
);
});
it('should handle protected visibility', function () {
const str = '#' + methodNameAndParameters;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'#' + expectedMethodNameAndParameters
);
});
it('should handle internal visibility', function () {
const str = '~' + methodNameAndParameters;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'~' + expectedMethodNameAndParameters
);
});
it('should return correct css for static classifier', function () {
const str = methodNameAndParameters + '$';
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = methodNameAndParameters + '*';
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(expectedMethodNameAndParameters);
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method return type is generic', function () {
it('should parse correctly', function () {
const str = `getTimes() List~T~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
});
it('should handle public visibility', function () {
const str = `+getTimes() List~T~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('+getTimes() : List<T>');
});
it('should handle private visibility', function () {
const str = `-getTimes() List~T~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('-getTimes() : List<T>');
});
it('should handle protected visibility', function () {
const str = `#getTimes() List~T~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('#getTimes() : List<T>');
});
it('should handle internal visibility', function () {
const str = `~getTimes() List~T~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('~getTimes() : List<T>');
});
it('should return correct css for static classifier', function () {
const str = `getTimes() List~T~$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTimes() List~T~*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getTimes() : List<T>');
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('when method return type is a nested generic', function () {
it('should parse correctly', function () {
const str = `getTimetableList() List~List~T~~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'getTimetableList() : List<List<T>>'
);
});
it('should handle public visibility', function () {
const str = `+getTimetableList() List~List~T~~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'+getTimetableList() : List<List<T>>'
);
});
it('should handle private visibility', function () {
const str = `-getTimetableList() List~List~T~~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'-getTimetableList() : List<List<T>>'
);
});
it('should handle protected visibility', function () {
const str = `#getTimetableList() List~List~T~~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'#getTimetableList() : List<List<T>>'
);
});
it('should handle internal visibility', function () {
const str = `~getTimetableList() List~List~T~~`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'~getTimetableList() : List<List<T>>'
);
});
it('should return correct css for static classifier', function () {
const str = `getTimetableList() List~List~T~~$`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'getTimetableList() : List<List<T>>'
);
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
});
it('should return correct css for abstract classifier', function () {
const str = `getTimetableList() List~List~T~~*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe(
'getTimetableList() : List<List<T>>'
);
expect(classMember.getDisplayDetails().cssStyle).toBe(abstractCssStyle);
});
});
describe('--uncategorized tests--', function () { describe('--uncategorized tests--', function () {
it('member name should handle double colons', function () { it('member name should handle double colons', function () {
const str = `std::map ~int,string~ pMap;`; const str = `std::map ~int,string~ pMap;`;
@@ -680,83 +25,82 @@ describe('given text representing a method, ', function () {
expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle); expect(classMember.getDisplayDetails().cssStyle).toBe(staticCssStyle);
}); });
}); });
});
describe('given text representing an attribute', () => { describe('Edge Cases and Additional Scenarios', () => {
describe('when the attribute has no modifiers', () => { it('should handle method with special characters in name', function () {
it('should parse the display text correctly', () => { const str = `operator++(int value)`;
const str = 'name String'; const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('operator++(int value)');
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails(); expect(classMember.id).toBe('operator++');
expect(displayDetails.displayText).toBe('name String');
expect(displayDetails.cssStyle).toBe('');
});
}); });
describe('when the attribute has public "+" modifier', () => { it('should handle method with numbers in name', function () {
it('should parse the display text correctly', () => { const str = `method123(param)`;
const str = '+name String'; const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('method123(param)');
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails(); expect(classMember.id).toBe('method123');
expect(displayDetails.displayText).toBe('+name String');
expect(displayDetails.cssStyle).toBe('');
});
}); });
describe('when the attribute has protected "#" modifier', () => { it('should handle method with underscores and hyphens', function () {
it('should parse the display text correctly', () => { const str = `get_user_data(user_id int)`;
const str = '#name String'; const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('get_user_data(user_id int)');
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails(); expect(classMember.id).toBe('get_user_data');
expect(displayDetails.displayText).toBe('#name String');
expect(displayDetails.cssStyle).toBe('');
});
}); });
describe('when the attribute has private "-" modifier', () => { it('should handle method with no spaces around parentheses', function () {
it('should parse the display text correctly', () => { const str = `method(param)`;
const str = '-name String'; const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('method(param)');
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('-name String');
expect(displayDetails.cssStyle).toBe('');
});
}); });
describe('when the attribute has internal "~" modifier', () => { it('should handle method with array parameters', function () {
it('should parse the display text correctly', () => { const str = `processArray(int[] numbers)`;
const str = '~name String'; const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('processArray(int[] numbers)');
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('~name String');
expect(displayDetails.cssStyle).toBe('');
});
}); });
describe('when the attribute has static "$" modifier', () => { it('should handle method with function pointer parameter', function () {
it('should parse the display text correctly and apply static css style', () => { const str = `callback(void (*fn)(int))`;
const str = 'name String$'; const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('callback(void (*fn)(int))');
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('name String');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
}); });
describe('when the attribute has abstract "*" modifier', () => { it('should handle method with complex nested generics (HTML encoded)', function () {
it('should parse the display text correctly and apply abstract css style', () => { const str = `process(Map<String, List<Map<Integer, String>>> data)`;
const str = 'name String*'; const classMember = new ClassMember(str, 'method');
// Current behavior: parseGenericTypes converts < > to HTML entities
expect(classMember.getDisplayDetails().displayText).toBe('process(Map&gt;&gt; data)');
});
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails(); it('should handle method with colon in return type', function () {
const str = `getNamespace() std::string`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('getNamespace() : std::string');
});
expect(displayDetails.displayText).toBe('name String'); it('should handle malformed input gracefully - no parentheses', function () {
expect(displayDetails.cssStyle).toBe(abstractCssStyle); const str = `not_a_method_missing_parentheses`;
const classMember = new ClassMember(str, 'method');
// This will not match the method regex, so should handle gracefully
// But currently throws when parseGenericTypes gets undefined
expect(() => classMember.getDisplayDetails()).toThrow();
});
it('should handle empty parameter list with classifier', function () {
const str = `emptyMethod()$*`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('emptyMethod()');
expect(classMember.getDisplayDetails().cssStyle).toBe(
'text-decoration:underline;font-style:italic;'
);
});
it('should handle method with constructor-like name', function () {
const str = `Class()`;
const classMember = new ClassMember(str, 'method');
expect(classMember.getDisplayDetails().displayText).toBe('Class()');
expect(classMember.id).toBe('Class');
}); });
}); });
}); });

View File

@@ -81,64 +81,42 @@ export class ClassMember {
let potentialClassifier = ''; let potentialClassifier = '';
if (this.memberType === 'method') { if (this.memberType === 'method') {
const methodRegEx = /([#+~-])?(.+)\((.*)\)([\s$*])?(.*)([$*])?/; const methodRegEx = /([#+~-])?(.+)\((.*)\)([$*]{0,2})(.*?)([$*]{0,2})$/;
const match = methodRegEx.exec(input); const match = methodRegEx.exec(input);
if (match) { if (match) {
const detectedVisibility = match[1] ? match[1].trim() : ''; this.visibility = (match[1] ? match[1].trim() : '') as Visibility;
this.id = match[2].trim();
if (visibilityValues.includes(detectedVisibility)) {
this.visibility = detectedVisibility as Visibility;
}
this.id = match[2];
this.parameters = match[3] ? match[3].trim() : ''; this.parameters = match[3] ? match[3].trim() : '';
potentialClassifier = match[4] ? match[4].trim() : ''; potentialClassifier = match[4] ? match[4].trim() : '';
this.returnType = match[5] ? match[5].trim() : ''; this.returnType = match[5] ? match[5].trim() : '';
if (potentialClassifier === '') { if (potentialClassifier === '') {
const lastChar = this.returnType.substring(this.returnType.length - 1); potentialClassifier = match[6] ? match[6].trim() : '';
if (/[$*]/.exec(lastChar)) {
potentialClassifier = lastChar;
this.returnType = this.returnType.substring(0, this.returnType.length - 1);
}
} }
} }
} else { } else {
const length = input.length; const fieldRegEx = /([#+~-])?(.*?)([$*]{0,2})$/;
const firstChar = input.substring(0, 1); const match = fieldRegEx.exec(input);
const lastChar = input.substring(length - 1);
if (visibilityValues.includes(firstChar)) { if (match) {
this.visibility = firstChar as Visibility; this.visibility = (match[1] ? match[1].trim() : '') as Visibility;
this.id = match[2] ? match[2].trim() : '';
potentialClassifier = match[3] ? match[3].trim() : '';
} }
if (/[$*]/.exec(lastChar)) {
potentialClassifier = lastChar;
}
this.id = input.substring(
this.visibility === '' ? 0 : 1,
potentialClassifier === '' ? length : length - 1
);
} }
this.classifier = potentialClassifier; this.classifier = potentialClassifier;
// Preserve one space only
this.id = this.id.startsWith(' ') ? ' ' + this.id.trim() : this.id.trim();
const combinedText = `${this.visibility ? '\\' + this.visibility : ''}${parseGenericTypes(this.id)}${this.memberType === 'method' ? `(${parseGenericTypes(this.parameters)})${this.returnType ? ' : ' + parseGenericTypes(this.returnType) : ''}` : ''}`;
this.text = combinedText.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
if (this.text.startsWith('\\&lt;')) {
this.text = this.text.replace('\\&lt;', '~');
}
} }
parseClassifier() { parseClassifier() {
switch (this.classifier) { switch (this.classifier) {
case '*':
return 'font-style:italic;';
case '$': case '$':
return 'text-decoration:underline;'; return 'text-decoration:underline;';
case '*':
return 'font-style:italic;';
case '$*':
case '*$':
return 'text-decoration:underline;font-style:italic;';
default: default:
return ''; return '';
} }

View File

@@ -13,6 +13,30 @@ const getStyles = (options) =>
} }
.cluster-label text {
fill: ${options.titleColor};
}
.cluster-label span {
color: ${options.titleColor};
}
.cluster-label span p {
background-color: transparent;
}
.cluster rect {
fill: ${options.clusterBkg};
stroke: ${options.clusterBorder};
stroke-width: 1px;
}
.cluster text {
fill: ${options.titleColor};
}
.cluster span {
color: ${options.titleColor};
}
.nodeLabel, .edgeLabel { .nodeLabel, .edgeLabel {
color: ${options.classText}; color: ${options.classText};
} }

View File

@@ -140,6 +140,7 @@ that id.
.*direction\s+BT[^\n]* return 'direction_bt'; .*direction\s+BT[^\n]* return 'direction_bt';
.*direction\s+RL[^\n]* return 'direction_rl'; .*direction\s+RL[^\n]* return 'direction_rl';
.*direction\s+LR[^\n]* return 'direction_lr'; .*direction\s+LR[^\n]* return 'direction_lr';
.*direction\s+TD[^\n]* return 'direction_td';
[^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; } [^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; }
[0-9]+ return 'NUM'; [0-9]+ return 'NUM';
@@ -626,6 +627,8 @@ direction
{ $$={stmt:'dir', value:'RL'};} { $$={stmt:'dir', value:'RL'};}
| direction_lr | direction_lr
{ $$={stmt:'dir', value:'LR'};} { $$={stmt:'dir', value:'LR'};}
| direction_td
{ $$={stmt:'dir', value:'TD'};}
; ;
%% %%

View File

@@ -309,4 +309,21 @@ describe('when parsing subgraphs', function () {
expect(subgraphA.nodes).toContain('a'); expect(subgraphA.nodes).toContain('a');
expect(subgraphA.nodes).not.toContain('c'); expect(subgraphA.nodes).not.toContain('c');
}); });
it('should correctly parse direction TD inside a subgraph', function () {
const res = flow.parser.parse(`
graph LR
subgraph WithTD
direction TD
A1 --> A2
end
`);
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.dir).toBe('TD');
expect(subgraph.nodes).toContain('A1');
expect(subgraph.nodes).toContain('A2');
});
}); });

View File

@@ -16,7 +16,7 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
const svgWidth = bitWidth * bitsPerRow + 2; const svgWidth = bitWidth * bitsPerRow + 2;
const svg: SVG = selectSvgElement(id); const svg: SVG = selectSvgElement(id);
svg.attr('viewbox', `0 0 ${svgWidth} ${svgHeight}`); svg.attr('viewBox', `0 0 ${svgWidth} ${svgHeight}`);
configureSvgSize(svg, svgHeight, svgWidth, config.useMaxWidth); configureSvgSize(svg, svgHeight, svgWidth, config.useMaxWidth);
for (const [word, packet] of words.entries()) { for (const [word, packet] of words.entries()) {

View File

@@ -2,6 +2,7 @@ import type { Diagram } from '../../Diagram.js';
import type { RadarDiagramConfig } from '../../config.type.js'; import type { RadarDiagramConfig } from '../../config.type.js';
import type { DiagramRenderer, DrawDefinition, SVG, SVGGroup } from '../../diagram-api/types.js'; import type { DiagramRenderer, DrawDefinition, SVG, SVGGroup } from '../../diagram-api/types.js';
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
import { configureSvgSize } from '../../setupGraphViewbox.js';
import type { RadarDB, RadarAxis, RadarCurve } from './types.js'; import type { RadarDB, RadarAxis, RadarCurve } from './types.js';
const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => { const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
@@ -53,11 +54,9 @@ const drawFrame = (svg: SVG, config: Required<RadarDiagramConfig>): SVGGroup =>
x: config.marginLeft + config.width / 2, x: config.marginLeft + config.width / 2,
y: config.marginTop + config.height / 2, y: config.marginTop + config.height / 2,
}; };
// Initialize the SVG configureSvgSize(svg, totalHeight, totalWidth, config.useMaxWidth ?? true);
svg
.attr('viewbox', `0 0 ${totalWidth} ${totalHeight}`) svg.attr('viewBox', `0 0 ${totalWidth} ${totalHeight}`);
.attr('width', totalWidth)
.attr('height', totalHeight);
// g element to center the radar chart // g element to center the radar chart
return svg.append('g').attr('transform', `translate(${center.x}, ${center.y})`); return svg.append('g').attr('transform', `translate(${center.x}, ${center.y})`);
}; };

View File

@@ -32,13 +32,14 @@
<CONFIG>[^\}]+ { return 'CONFIG_CONTENT'; } <CONFIG>[^\}]+ { return 'CONFIG_CONTENT'; }
<CONFIG>\} { this.popState(); this.popState(); return 'CONFIG_END'; } <CONFIG>\} { this.popState(); this.popState(); return 'CONFIG_END'; }
<ID>[^\<->\->:\n,;@\s]+(?=\@\{) { yytext = yytext.trim(); return 'ACTOR'; } <ID>[^\<->\->:\n,;@\s]+(?=\@\{) { yytext = yytext.trim(); return 'ACTOR'; }
<ID>[^\<->\->:\n,;@]+?([\-]*[^\<->\->:\n,;@]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; } <ID>[^<>:\n,;@\s]+(?=\s+as\s) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
<ID>[^<>:\n,;@]+(?=\s*[\n;#]|$) { yytext = yytext.trim(); this.popState(); return 'ACTOR'; }
<ID>[^<>:\n,;@]*\<[^\n]* { this.popState(); return 'INVALID'; }
"box" { this.begin('LINE'); return 'box'; } "box" { this.begin('LINE'); return 'box'; }
"participant" { this.begin('ID'); return 'participant'; } "participant" { this.begin('ID'); return 'participant'; }
"actor" { this.begin('ID'); return 'participant_actor'; } "actor" { this.begin('ID'); return 'participant_actor'; }
"create" return 'create'; "create" return 'create';
"destroy" { this.begin('ID'); return 'destroy'; } "destroy" { this.begin('ID'); return 'destroy'; }
<ID>[^<\->\->:\n,;]+?([\-]*[^<\->\->:\n,;]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; } <ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; } <ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
"loop" { this.begin('LINE'); return 'loop'; } "loop" { this.begin('LINE'); return 'loop'; }
@@ -145,6 +146,7 @@ line
: SPACE statement { $$ = $2 } : SPACE statement { $$ = $2 }
| statement { $$ = $1 } | statement { $$ = $1 }
| NEWLINE { $$=[]; } | NEWLINE { $$=[]; }
| INVALID { $$=[]; }
; ;
box_section box_section

View File

@@ -2609,5 +2609,17 @@ Bob->>Alice:Got it!
expect(actors.get('E').type).toBe('entity'); expect(actors.get('E').type).toBe('entity');
expect(actors.get('E').description).toBe('E'); expect(actors.get('E').description).toBe('E');
}); });
it('should handle fail parsing when alias token causes conflicts in participant definition', async () => {
let error = false;
try {
await Diagram.fromText(`
sequenceDiagram
participant SAS MyServiceWithMoreThan20Chars <br> service decription
`);
} catch (e) {
error = true;
}
expect(error).toBe(true);
});
}); });
}); });

View File

@@ -15,7 +15,7 @@ title: Animal example
classDiagram classDiagram
note "From Duck till Zebra" note "From Duck till Zebra"
Animal <|-- Duck Animal <|-- Duck
note for Duck "can fly\ncan swim\ncan dive\ncan help in debugging" note for Duck "can fly<br>can swim<br>can dive<br>can help in debugging"
Animal <|-- Fish Animal <|-- Fish
Animal <|-- Zebra Animal <|-- Zebra
Animal : +int age Animal : +int age
@@ -175,6 +175,7 @@ To describe the visibility (or encapsulation) of an attribute or method/function
> >
> - `*` Abstract e.g.: `someAbstractMethod()*` or `someAbstractMethod() int*` > - `*` Abstract e.g.: `someAbstractMethod()*` or `someAbstractMethod() int*`
> - `$` Static e.g.: `someStaticMethod()$` or `someStaticMethod() String$` > - `$` Static e.g.: `someStaticMethod()$` or `someStaticMethod() String$`
> - `$*` OR `*$` Both e.g: `someAbstractStaticMethod()$*` or `someAbstractStaticMethod() int$*`
> _note_ you can also include additional _classifiers_ to a field definition by adding the following notation to the very end: > _note_ you can also include additional _classifiers_ to a field definition by adding the following notation to the very end:
> >

View File

@@ -27,53 +27,6 @@ import { log } from '../../../logger.js';
import { getSubGraphTitleMargins } from '../../../utils/subGraphTitleMargins.js'; import { getSubGraphTitleMargins } from '../../../utils/subGraphTitleMargins.js';
import { getConfig } from '../../../diagram-api/diagramAPI.js'; import { getConfig } from '../../../diagram-api/diagramAPI.js';
/**
* Apply absolute note positioning after dagre layout
* This fixes the issue where TB and LR directions position notes differently
* by making note positioning truly absolute
*/
const positionNotes = (graph) => {
const noteStatePairs = [];
graph.nodes().forEach((nodeId) => {
const node = graph.node(nodeId);
if (node.position && node.shape === 'note') {
const edges = graph.nodeEdges(nodeId);
for (const edge of edges) {
const otherNodeId = edge.v === nodeId ? edge.w : edge.v;
const otherNode = graph.node(otherNodeId);
if (otherNode && otherNode.shape !== 'note' && otherNode.shape !== 'noteGroup') {
noteStatePairs.push({
noteId: nodeId,
noteNode: node,
stateId: otherNodeId,
stateNode: otherNode,
position: node.position,
});
}
}
}
});
noteStatePairs.forEach(({ noteNode, stateNode, position }) => {
const spacing = 60;
let noteX = noteNode.x;
let noteY = stateNode.y;
if (position === 'right of') {
noteX = stateNode.x + stateNode.width / 2 + spacing + noteNode.width / 2;
} else if (position === 'left of') {
noteX = stateNode.x - stateNode.width / 2 - spacing - noteNode.width / 2;
}
noteNode.x = noteX;
noteNode.y = noteY;
});
};
const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, siteConfig) => { const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, siteConfig) => {
log.warn('Graph in recursive render:XAX', graphlibJson.write(graph), parentCluster); log.warn('Graph in recursive render:XAX', graphlibJson.write(graph), parentCluster);
const dir = graph.graph().rankdir; const dir = graph.graph().rankdir;
@@ -211,9 +164,6 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
dagreLayout(graph); dagreLayout(graph);
// Apply absolute note positioning after dagre layout
positionNotes(graph);
log.info('Graph after layout:', JSON.stringify(graphlibJson.write(graph))); log.info('Graph after layout:', JSON.stringify(graphlibJson.write(graph)));
// Move the nodes to the correct place // Move the nodes to the correct place
let diff = 0; let diff = 0;

View File

@@ -605,6 +605,14 @@ export const insertEdge = function (
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style]; const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:')); let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:'));
let animationClass = '';
if (edge.animate) {
animationClass = 'edge-animation-fast';
}
if (edge.animation) {
animationClass = 'edge-animation-' + edge.animation;
}
let animatedEdge = false; let animatedEdge = false;
if (edge.look === 'handDrawn') { if (edge.look === 'handDrawn') {
const rc = rough.svg(elem); const rc = rough.svg(elem);
@@ -620,7 +628,13 @@ export const insertEdge = function (
svgPath = select(svgPathNode) svgPath = select(svgPathNode)
.select('path') .select('path')
.attr('id', edge.id) .attr('id', edge.id)
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '')) .attr(
'class',
' ' +
strokeClasses +
(edge.classes ? ' ' + edge.classes : '') +
(animationClass ? ' ' + animationClass : '')
)
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : ''); .attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
let d = svgPath.attr('d'); let d = svgPath.attr('d');
svgPath.attr('d', d); svgPath.attr('d', d);
@@ -628,13 +642,6 @@ export const insertEdge = function (
} else { } else {
const stylesFromClasses = edgeClassStyles.join(';'); const stylesFromClasses = edgeClassStyles.join(';');
const styles = edgeStyles ? edgeStyles.reduce((acc, style) => acc + style + ';', '') : ''; const styles = edgeStyles ? edgeStyles.reduce((acc, style) => acc + style + ';', '') : '';
let animationClass = '';
if (edge.animate) {
animationClass = ' edge-animation-fast';
}
if (edge.animation) {
animationClass = ' edge-animation-' + edge.animation;
}
const pathStyle = const pathStyle =
(stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles) + (stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles) +
@@ -646,7 +653,10 @@ export const insertEdge = function (
.attr('id', edge.id) .attr('id', edge.id)
.attr( .attr(
'class', 'class',
' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '') + (animationClass ?? '') ' ' +
strokeClasses +
(edge.classes ? ' ' + edge.classes : '') +
(animationClass ? ' ' + animationClass : '')
) )
.attr('style', pathStyle); .attr('style', pathStyle);

View File

@@ -130,7 +130,6 @@ const lollipop = (elem, type, id) => {
.attr('markerHeight', 240) .attr('markerHeight', 240)
.attr('orient', 'auto') .attr('orient', 'auto')
.append('circle') .append('circle')
.attr('stroke', 'black')
.attr('fill', 'transparent') .attr('fill', 'transparent')
.attr('cx', 7) .attr('cx', 7)
.attr('cy', 7) .attr('cy', 7)
@@ -147,7 +146,6 @@ const lollipop = (elem, type, id) => {
.attr('markerHeight', 240) .attr('markerHeight', 240)
.attr('orient', 'auto') .attr('orient', 'auto')
.append('circle') .append('circle')
.attr('stroke', 'black')
.attr('fill', 'transparent') .attr('fill', 'transparent')
.attr('cx', 7) .attr('cx', 7)
.attr('cy', 7) .attr('cy', 7)