use graphs_tui::{
detect_format, render_d2_to_tui, render_diagram, render_mermaid_to_tui, render_pie_chart,
render_sequence_diagram, render_state_diagram, DiagramFormat, MermaidError, RenderOptions,
};
#[test]
fn test_simple_lr_flowchart() {
let input = "flowchart LR\nA[Start] --> B[End]";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_simple_tb_flowchart() {
let input = "flowchart TB\nA[Start] --> B[End]";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_labels_correctly() {
let input = "flowchart LR\nA[Node A] --> B[Node B]";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_ascii_mode() {
let input = "flowchart LR\nA --> B";
let result = render_mermaid_to_tui(
input,
RenderOptions {
ascii: true,
..Default::default()
},
)
.unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_unsupported_diagram_type() {
let input = "sequenceDiagram\nA->B: hi";
let result = render_mermaid_to_tui(input, RenderOptions::default());
assert!(matches!(
result,
Err(MermaidError::ParseError { line: 1, .. })
));
}
#[test]
fn test_chained_edges() {
let input = "flowchart LR\nA --> B --> C --> D";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_labels_with_spaces_and_special_chars() {
let input = "flowchart LR\nA[Start Here] --> B[Wait... what?]\nB --> C[Done!]";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_rl_direction() {
let input = "flowchart RL\nA --> B";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_bt_direction() {
let input = "flowchart BT\nA --> B";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_empty_input() {
let result = render_mermaid_to_tui("", RenderOptions::default());
assert!(matches!(result, Err(MermaidError::EmptyInput)));
}
#[test]
fn test_comments_ignored() {
let input = "flowchart LR\n%% this is a comment\nA --> B";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
assert!(!result.output.contains("comment"));
insta::assert_snapshot!(result.output);
}
#[test]
fn test_node_label_update() {
let input = "flowchart LR\nA\nB[Label B]\nA --> B\nA[Label A]";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_graph_td_supported() {
let input = "graph TD\nA --> B";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_web_architecture_simplified() {
let input = r#"flowchart TB
User[User] --> CDN[CDN]
CDN --> Browser[Browser]
Browser --> API[API Gateway]
API --> App[App Server]
App --> Auth[Auth Service]
App --> Cache[Cache]
App --> DB[Database]
App --> Queue[Message Queue]
Queue --> Worker[Worker]
Worker --> DB
App --> Payment[Payment]
Worker --> Email[Email]"#;
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_edge_labels() {
let input = r#"flowchart LR
A[Client] -->|HTTP| B[Server]"#;
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_web_architecture_with_edge_labels() {
let input = r#"flowchart TB
User[User] -->|HTTPS| CDN[CDN]
CDN -->|static| Browser[Browser]
Browser -->|API call| API[API Gateway]
API -->|route| App[App Server]
App -->|validate| Auth[Auth Service]
App -->|read/write| Cache[Cache]
App -->|persist| DB[Database]
App -->|enqueue| Queue[Message Queue]
Queue -->|process| Worker[Worker]
Worker -->|update| DB
App -->|charge| Payment[Payment]
Worker -->|notify| Email[Email]"#;
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_node_shapes() {
let input = r#"flowchart LR
A[Rectangle] --> B(Rounded)
B --> C((Circle))
C --> D{Diamond}
D --> E[(Database)]
E --> F([Stadium])"#;
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_subgraph_parsing() {
let input = r#"flowchart TB
subgraph Backend [Backend Services]
API[API Server]
DB[(Database)]
end
API --> DB"#;
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_hexagon_shape() {
let input = "flowchart LR\nA{{Prepare}} --> B{{Execute}}";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_edge_styles() {
let input1 = "flowchart LR\nA --> B";
let r1 = render_mermaid_to_tui(input1, RenderOptions::default()).unwrap();
insta::assert_snapshot!("solid_arrow", r1.output);
let input2 = "flowchart LR\nA --- B";
let r2 = render_mermaid_to_tui(input2, RenderOptions::default()).unwrap();
insta::assert_snapshot!("solid_line", r2.output);
let input3 = "flowchart LR\nA -.-> B";
let r3 = render_mermaid_to_tui(input3, RenderOptions::default()).unwrap();
insta::assert_snapshot!("dotted_arrow", r3.output);
let input4 = "flowchart LR\nA ==> B";
let r4 = render_mermaid_to_tui(input4, RenderOptions::default()).unwrap();
insta::assert_snapshot!("thick_arrow", r4.output);
}
#[test]
fn test_parallelogram_trapezoid_shapes() {
let input = "flowchart LR\nA[/Input/] --> B[/Process\\]";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_simple() {
let input = "A -> B";
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_labels() {
let input = r#"
server: "Web Server"
db: Database
server -> db
"#;
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_edge_labels() {
let input = r#"client -> server: "HTTP request"
server -> db: "SQL query""#;
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_shapes() {
let input = r#"
db: Database
db.shape: cylinder
circle_node: Circle
circle_node.shape: circle
db -> circle_node
"#;
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_containers() {
let input = r#"
backend {
api: "API Server"
db: Database
}
frontend {
web: "Web App"
}
web -> api
api -> db
"#;
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_backward_arrow() {
let input = "A <- B";
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_line() {
let input = "A -- B";
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_format_detection_mermaid() {
assert_eq!(
detect_format("flowchart LR\nA --> B"),
DiagramFormat::Mermaid
);
assert_eq!(detect_format("graph TD\nA --> B"), DiagramFormat::Mermaid);
assert_eq!(detect_format("A --> B --> C"), DiagramFormat::Mermaid);
}
#[test]
fn test_format_detection_d2() {
assert_eq!(detect_format("A -> B"), DiagramFormat::D2);
assert_eq!(
detect_format("server: Web Server\nserver -> db"),
DiagramFormat::D2
);
}
#[test]
fn test_render_diagram_auto() {
let mermaid = "flowchart LR\nA[Start] --> B[End]";
let result1 = render_diagram(mermaid, RenderOptions::default()).unwrap();
insta::assert_snapshot!("mermaid_auto", result1.output);
let d2 = r#"start: Start
end: End
start -> end"#;
let result2 = render_diagram(d2, RenderOptions::default()).unwrap();
insta::assert_snapshot!("d2_auto", result2.output);
}
#[test]
fn test_d2_web_architecture() {
let input = r#"
user: User
cdn: CDN
browser: Browser
api: "API Gateway"
app: "App Server"
db: Database
cache: Cache
queue: "Message Queue"
worker: Worker
user -> cdn: HTTPS
cdn -> browser: static
browser -> api: "API call"
api -> app: route
app -> db: persist
app -> cache: read
app -> queue: enqueue
queue -> worker: process
worker -> db: update
"#;
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_state_diagram_simple() {
let input = r#"stateDiagram-v2
[*] --> Idle
Idle --> Running
Running --> [*]
"#;
let result = render_state_diagram(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_state_diagram_descriptions() {
let input = r#"stateDiagram-v2
state "Waiting for input" as Waiting
state "Processing data" as Processing
Waiting --> Processing: submit
Processing --> Waiting: reset
"#;
let result = render_state_diagram(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_state_diagram_composite() {
let input = r#"stateDiagram-v2
[*] --> Active
state Active {
[*] --> Running
Running --> Paused
Paused --> Running
}
Active --> [*]
"#;
let result = render_state_diagram(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_state_diagram_v1() {
let input = r#"stateDiagram
s1 --> s2
s2 --> s3
"#;
let result = render_state_diagram(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_pie_chart_simple() {
let input = r#"pie
title Browser Market Share
"Chrome" : 65
"Firefox" : 15
"Safari" : 12
"Edge" : 8
"#;
let result = render_pie_chart(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_pie_chart_no_title() {
let input = r#"pie
"Yes" : 70
"No" : 30
"#;
let result = render_pie_chart(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_pie_chart_show_data() {
let input = r#"pie showData
title Project Status
"Completed" : 45
"In Progress" : 35
"Not Started" : 20
"#;
let result = render_pie_chart(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_sequence_diagram_simple() {
let input = r#"sequenceDiagram
Alice->>Bob: Hello Bob!
Bob-->>Alice: Hi Alice!
"#;
let result = render_sequence_diagram(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_sequence_diagram_participants() {
let input = r#"sequenceDiagram
participant A as Alice
participant B as Bob
A->>B: Message
"#;
let result = render_sequence_diagram(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_sequence_diagram_detection() {
let input = "sequenceDiagram\n A->>B: Hi";
assert_eq!(detect_format(input), DiagramFormat::SequenceDiagram);
}
#[test]
fn test_sequence_diagram_auto() {
let input = r#"sequenceDiagram
Client->>Server: Request
Server-->>Client: Response
"#;
let result = render_diagram(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_sql_table_shape() {
let input = r#"
users: Users
users.shape: sql_table
orders: Orders
orders.shape: sql_table
users -> orders
"#;
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_max_width_constraint() {
let input = "flowchart LR\nA[Very Long Label Here] --> B[Another Long Label]";
let result = render_mermaid_to_tui(
input,
RenderOptions {
max_width: Some(30),
..Default::default()
},
)
.unwrap();
for line in result.output.lines() {
assert!(
line.chars().count() <= 30,
"Line too long: {} chars",
line.chars().count()
);
}
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_person_shape() {
let input = "user: {shape: person}\nuser -> server";
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_cloud_shape() {
let input = "cloud: {shape: cloud}\ncloud -> server";
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_d2_document_shape() {
let input = "doc: {shape: document}\ndoc -> server";
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
insta::assert_snapshot!(result.output);
}
#[test]
fn test_multiline_label() {
let input = "flowchart LR\nA[Line1<br/>Line2] --> B[End]";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
assert!(result.output.contains("Line1"));
assert!(result.output.contains("Line2"));
insta::assert_snapshot!(result.output);
}
#[test]
fn test_sequence_diagram_note() {
let input = r#"sequenceDiagram
Alice->>Bob: Hello
Note right of Bob: Think
Bob->>Alice: Hi
"#;
let result = render_sequence_diagram(input, RenderOptions::default()).unwrap();
assert!(result.output.contains("Think"));
insta::assert_snapshot!(result.output);
}
#[test]
fn test_sequence_diagram_loop() {
let input = r#"sequenceDiagram
Alice->>Bob: Hello
loop Every minute
Bob->>Alice: Ping
end
"#;
let result = render_sequence_diagram(input, RenderOptions::default()).unwrap();
assert!(result.output.contains("[loop Every minute]"));
insta::assert_snapshot!(result.output);
}
#[test]
fn test_sequence_diagram_alt() {
let input = r#"sequenceDiagram
Alice->>Bob: Request
alt Success
Bob->>Alice: 200 OK
else Failure
Bob->>Alice: 500 Error
end
"#;
let result = render_sequence_diagram(input, RenderOptions::default()).unwrap();
assert!(result.output.contains("[alt Success]"));
assert!(result.output.contains("[Failure]"));
insta::assert_snapshot!(result.output);
}
#[test]
fn test_sequence_diagram_activation() {
let input = r#"sequenceDiagram
Alice->>+Bob: Hello
Bob->>-Alice: Bye
"#;
let result = render_sequence_diagram(input, RenderOptions::default()).unwrap();
assert!(result.output.contains('┃'));
insta::assert_snapshot!(result.output);
}