Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,7 @@ mod snapshot_tests {
// snapshots stay stable across runs.

use crate::app::{App, Config};
use crate::network::geoip::GeoIpInfo;
use crate::network::interface_stats::{InterfaceRates, InterfaceStats};
use crate::network::types::{Connection, Protocol, ProtocolState, TcpState, TrafficHistory};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
Expand Down Expand Up @@ -928,6 +929,106 @@ mod snapshot_tests {
});
}

#[test]
fn details_tab_keeps_section_anchors_across_metadata_shapes() {
let app = test_app();
let mut connections = sample_connections();
connections[0].geoip_info = Some(GeoIpInfo {
country_code: Some("DE".to_string()),
country_name: Some("Germany".to_string()),
city: Some("Falkenstein".to_string()),
postal_code: None,
asn: Some(24_940),
as_org: Some("Hetzner Online GmbH".to_string()),
});
app.set_connections_snapshot_for_test(connections.clone());
let stats = app.get_stats();

let render_selected = |selected: usize| {
let ui_state = UIState {
selected_tab: 1,
selected_connection_key: Some(connections[selected].key()),
..Default::default()
};
let mut click_regions = ClickableRegions::default();
render(140, 40, |f| {
draw(
f,
&app,
&ui_state,
&connections,
None,
&stats,
&mut click_regions,
)
.expect("draw details");
})
};

let enriched_tcp = render_selected(0);
let plain_udp = render_selected(1);
assert!(
enriched_tcp
.lines()
.any(|line| line.contains("Network Context") && line.contains("Transport Health")),
"second-row card headings should share one visual anchor"
);
let card_header = enriched_tcp
.lines()
.find(|line| line.contains("Connection") && line.contains("Application"))
.expect("missing dashboard card header");
let traffic_header = enriched_tcp
.lines()
.find(|line| line.contains("↓ RX") && line.contains("↑ TX"))
.expect("missing traffic card header");
let cell_position = |line: &str, needle: &str| {
let byte_index = line.find(needle).unwrap();
line[..byte_index].chars().count()
};
let left_card_x = cell_position(card_header, "Connection");
let right_card_x = cell_position(card_header, "Application");
let rx_x = cell_position(traffic_header, "↓ RX");
let tx_x = cell_position(traffic_header, "↑ TX");
assert_eq!(
rx_x, left_card_x,
"RX must align with the left dashboard card"
);
assert_eq!(
tx_x, right_card_x,
"TX must align with the right dashboard card"
);
let peak_positions: Vec<usize> = traffic_header
.match_indices("peak")
.map(|(byte_index, _)| traffic_header[..byte_index].chars().count())
.collect();
assert_eq!(peak_positions.len(), 2);
assert_eq!(
peak_positions[0] - rx_x,
peak_positions[1] - tx_x,
"peak labels must use the same offset within both traffic cards"
);
for heading in [
"Connection",
"Network Context",
"Application",
"Transport Health",
"Traffic Statistics",
] {
let enriched_row = enriched_tcp
.lines()
.position(|line| line.contains(heading))
.unwrap_or_else(|| panic!("missing {heading} in enriched render"));
let plain_row = plain_udp
.lines()
.position(|line| line.contains(heading))
.unwrap_or_else(|| panic!("missing {heading} in plain render"));
assert_eq!(
enriched_row, plain_row,
"{heading} moved between enriched TCP and plain UDP records"
);
}
}

#[test]
fn interfaces_tab() {
let app = test_app();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,32 @@ expression: output
sshd (1500) 10.0.0.5:51022 192.168.1.10:22 ssh TCP ESTABLISHED -/-

▎ firefox → 140.82.121.4:443 · click a field to copy
Protocol TCP TCP Analytics
Status Active (last seen <T> ago) TCP Retransmits 0
Local Address 192.168.1.10:51234 Out-of-Order Packets 0
Remote Address 140.82.121.4:443 Duplicate ACKs 0
Scope PUBLIC Fast Retransmits 0
State ESTABLISHED Window Size 0
Connection Application
Protocol TCP Detected -
Status Active (last seen <T> ago)
Local Address 192.168.1.10:51234
Remote Address 140.82.121.4:443
Scope PUBLIC
State ESTABLISHED
Process firefox
PID 2001
Service https

▎ Traffic Statistics
Bytes Sent 12.21 KB
Bytes Received 234.38 KB
Packets Sent 12
Packets Received 234
Current Rate (In) -
Current Rate (Out) -








Network Context Transport Health
Local Hostname - Initial RTT -
Remote Hostname - TCP Retransmits 0
Country - Out-of-Order Packets 0
City - Duplicate ACKs 0
ASN - Fast Retransmits 0
Window Size 0

▎ Traffic Statistics
↓ RX - → peak - ↑ TX - → peak -
Total 234.38 KB · 234 packets Total 12.21 KB · 12 packets
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀



Expand Down
Loading
Loading