2026-04-21 17:55:26 -03:00
|
|
|
//! Contract tests for the launcher layout proportions.
|
|
|
|
|
//!
|
|
|
|
|
//! Scope: statically guard the GTK layout constants and sizing glue used by
|
|
|
|
|
//! the launcher shell.
|
2026-04-23 07:00:06 -03:00
|
|
|
//! Targets: split `client/src/launcher/ui_components/*` layout modules.
|
2026-04-21 17:55:26 -03:00
|
|
|
//! Why: the launcher is an operational control surface; accidental spacing
|
|
|
|
|
//! regressions can hide diagnostics or make eye/device previews unusable.
|
|
|
|
|
|
2026-04-23 07:00:06 -03:00
|
|
|
const UI_LAYOUT_SRC: &str = concat!(
|
|
|
|
|
include_str!("../../client/src/launcher/ui_components/types.rs"),
|
|
|
|
|
include_str!("../../client/src/launcher/ui_components/build_shell.rs"),
|
|
|
|
|
include_str!("../../client/src/launcher/ui_components/display_pane.rs"),
|
|
|
|
|
include_str!("../../client/src/launcher/ui_components/build_device_controls.rs"),
|
|
|
|
|
include_str!("../../client/src/launcher/ui_components/build_operations_rail.rs"),
|
2026-04-23 11:14:58 -03:00
|
|
|
include_str!("../../client/src/launcher/ui_components/combo_helpers.rs"),
|
|
|
|
|
include_str!("../../client/src/launcher/ui_components/control_buttons.rs"),
|
|
|
|
|
include_str!("../../client/src/launcher/ui_components/panel_chips.rs"),
|
|
|
|
|
include_str!("../../client/src/launcher/ui_components/style.rs"),
|
2026-04-23 07:00:06 -03:00
|
|
|
);
|
2026-04-21 17:55:26 -03:00
|
|
|
|
|
|
|
|
fn const_i32(name: &str) -> i32 {
|
|
|
|
|
let needle = format!("const {name}: i32 = ");
|
2026-04-23 07:00:06 -03:00
|
|
|
let line = UI_LAYOUT_SRC
|
2026-04-21 17:55:26 -03:00
|
|
|
.lines()
|
|
|
|
|
.find(|line| line.trim_start().starts_with(&needle))
|
|
|
|
|
.unwrap_or_else(|| panic!("missing {name} constant"));
|
|
|
|
|
line.trim_start()
|
|
|
|
|
.trim_start_matches(&needle)
|
|
|
|
|
.trim_end_matches(';')
|
|
|
|
|
.parse()
|
|
|
|
|
.unwrap_or_else(|err| panic!("invalid {name} constant: {err}"))
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 21:38:22 -03:00
|
|
|
fn source_index(needle: &str) -> usize {
|
2026-04-23 07:00:06 -03:00
|
|
|
UI_LAYOUT_SRC
|
2026-04-21 21:38:22 -03:00
|
|
|
.find(needle)
|
|
|
|
|
.unwrap_or_else(|| panic!("missing source marker: {needle}"))
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 17:55:26 -03:00
|
|
|
#[test]
|
|
|
|
|
fn launcher_default_size_stays_inside_1080p() {
|
|
|
|
|
assert_eq!(const_i32("LAUNCHER_DEFAULT_WIDTH"), 1360);
|
2026-04-23 11:14:58 -03:00
|
|
|
assert_eq!(const_i32("LAUNCHER_DEFAULT_HEIGHT"), 900);
|
2026-04-21 17:55:26 -03:00
|
|
|
assert!(const_i32("LAUNCHER_DEFAULT_WIDTH") <= 1920);
|
2026-04-23 11:14:58 -03:00
|
|
|
assert!(
|
|
|
|
|
const_i32("LAUNCHER_DEFAULT_HEIGHT") <= 900,
|
|
|
|
|
"leave room for desktop panels and window chrome on a 1080p monitor"
|
|
|
|
|
);
|
2026-04-21 17:55:26 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn eye_panes_keep_the_locked_larger_preview_footprint() {
|
2026-04-23 11:14:58 -03:00
|
|
|
assert_eq!(const_i32("EYE_PREVIEW_MIN_WIDTH"), 460);
|
|
|
|
|
assert_eq!(const_i32("EYE_PREVIEW_MIN_HEIGHT"), 300);
|
|
|
|
|
let estimated_min_width = 20
|
|
|
|
|
+ 8
|
|
|
|
|
+ const_i32("OPERATIONS_RAIL_WIDTH")
|
|
|
|
|
+ 8
|
|
|
|
|
+ 2 * (const_i32("EYE_PREVIEW_MIN_WIDTH") + 32);
|
|
|
|
|
assert!(
|
|
|
|
|
estimated_min_width <= const_i32("LAUNCHER_DEFAULT_WIDTH"),
|
|
|
|
|
"the eye panes must not push the operations rail off a 1360px launcher"
|
|
|
|
|
);
|
2026-04-21 17:55:26 -03:00
|
|
|
assert!(
|
2026-04-23 07:00:06 -03:00
|
|
|
UI_LAYOUT_SRC.contains("caption_label.set_halign(gtk::Align::End)")
|
|
|
|
|
|| UI_LAYOUT_SRC.contains("capture_label.set_halign(gtk::Align::End)")
|
2026-04-21 17:55:26 -03:00
|
|
|
);
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(UI_LAYOUT_SRC.contains("capture_label.set_ellipsize(pango::EllipsizeMode::Start);"));
|
|
|
|
|
assert!(!UI_LAYOUT_SRC.contains("root.append(&stream_status);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("stream_status.add_css_class(\"eye-inline-status\");"));
|
2026-04-21 22:15:47 -03:00
|
|
|
assert!(
|
|
|
|
|
source_index("controls_grid.attach(&breakout_row, 0, 1, 1, 1);")
|
|
|
|
|
< source_index("controls_grid.attach(&stream_status, 1, 1, 1, 1);")
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
source_index("controls_grid.attach(&stream_status, 1, 1, 1, 1);")
|
|
|
|
|
< source_index("controls_grid.attach(&action_button, 2, 1, 1, 1);")
|
|
|
|
|
);
|
2026-04-21 17:55:26 -03:00
|
|
|
}
|
|
|
|
|
|
2026-04-23 11:14:58 -03:00
|
|
|
#[test]
|
|
|
|
|
fn eye_and_device_combos_use_compact_labels_so_the_rail_stays_visible() {
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("fn compact_capture_mode_label("));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("format!(\"{size}@{fps}\")"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("format!(\"Source {}\", compact_size_label(option.height))"));
|
|
|
|
|
assert!(
|
|
|
|
|
!UI_LAYOUT_SRC.contains("@ {} fps (Device H.264)"),
|
|
|
|
|
"long capture labels force a huge GTK combo natural width"
|
|
|
|
|
);
|
|
|
|
|
assert!(!UI_LAYOUT_SRC.contains("(Source Size)"));
|
|
|
|
|
assert!(!UI_LAYOUT_SRC.contains("(Display Size)"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("combobox.compact-combo"));
|
|
|
|
|
assert!(
|
|
|
|
|
UI_LAYOUT_SRC
|
|
|
|
|
.matches(".add_css_class(\"compact-combo\")")
|
|
|
|
|
.count()
|
|
|
|
|
>= 9,
|
|
|
|
|
"display, media, and input combos should be allowed to shrink"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 17:55:26 -03:00
|
|
|
#[test]
|
|
|
|
|
fn device_staging_and_testing_bottoms_stay_locked_together() {
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(UI_LAYOUT_SRC.contains("staging_row.set_homogeneous(true);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("staging_row.set_vexpand(false);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("devices_panel.set_valign(gtk::Align::Fill);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("preview_panel.set_valign(gtk::Align::Fill);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains(
|
2026-04-21 17:55:26 -03:00
|
|
|
"let device_body_height_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Vertical);"
|
|
|
|
|
));
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(UI_LAYOUT_SRC.contains("device_body_height_group.add_widget(&devices_body);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("device_body_height_group.add_widget(&testing_row);"));
|
2026-04-21 17:55:26 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn device_testing_keeps_webcam_and_mic_playback_as_equal_bottom_columns() {
|
|
|
|
|
assert_eq!(const_i32("CAMERA_PREVIEW_VIEWPORT_WIDTH"), 280);
|
|
|
|
|
assert_eq!(const_i32("CAMERA_PREVIEW_VIEWPORT_HEIGHT"), 158);
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(UI_LAYOUT_SRC.contains("webcam_group.set_valign(gtk::Align::Fill);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("playback_group.set_valign(gtk::Align::Fill);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("preview_body.set_vexpand(false);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("playback_body.set_valign(gtk::Align::Fill);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("audio_check_meter.set_vexpand(true);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("playback_body.append(&audio_check_meter);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("playback_body.append(µphone_replay_button);"));
|
2026-04-21 17:55:26 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn operations_column_fills_height_and_splits_extra_space_between_logs() {
|
2026-04-21 22:15:47 -03:00
|
|
|
assert_eq!(const_i32("SIDE_LOG_MIN_HEIGHT"), 124);
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(UI_LAYOUT_SRC.contains("operations.set_vexpand(true);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("operations.set_valign(gtk::Align::Fill);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("diagnostics_panel.set_vexpand(true);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("console_panel.set_vexpand(true);"));
|
2026-04-21 17:55:26 -03:00
|
|
|
assert_eq!(
|
2026-04-23 07:00:06 -03:00
|
|
|
UI_LAYOUT_SRC
|
2026-04-21 22:15:47 -03:00
|
|
|
.matches(".min_content_height(SIDE_LOG_MIN_HEIGHT)")
|
2026-04-21 17:55:26 -03:00
|
|
|
.count(),
|
|
|
|
|
2
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
2026-04-23 07:00:06 -03:00
|
|
|
UI_LAYOUT_SRC
|
|
|
|
|
.matches(".max_content_height(SIDE_LOG")
|
|
|
|
|
.count(),
|
2026-04-21 22:15:47 -03:00
|
|
|
0,
|
|
|
|
|
"the docked logs must be allowed to split extra right-rail height"
|
2026-04-21 17:55:26 -03:00
|
|
|
);
|
|
|
|
|
}
|
2026-04-21 20:19:47 -03:00
|
|
|
|
2026-04-23 01:13:29 -03:00
|
|
|
#[test]
|
|
|
|
|
fn session_console_buttons_share_the_remaining_toolbar_width() {
|
|
|
|
|
assert!(
|
2026-04-23 07:00:06 -03:00
|
|
|
UI_LAYOUT_SRC
|
|
|
|
|
.contains("let console_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 8);")
|
2026-04-23 01:13:29 -03:00
|
|
|
);
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(UI_LAYOUT_SRC.contains("console_buttons.set_hexpand(true);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("console_buttons.set_homogeneous(true);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("console_copy_button.set_hexpand(true);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("console_popout_button.set_hexpand(true);"));
|
2026-04-23 01:13:29 -03:00
|
|
|
assert!(
|
|
|
|
|
source_index("console_toolbar.append(&console_level_combo);")
|
|
|
|
|
< source_index("console_toolbar.append(&console_buttons);")
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 21:38:22 -03:00
|
|
|
#[test]
|
2026-04-23 11:14:58 -03:00
|
|
|
fn status_chip_text_is_centered_inside_each_pill() {
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("meta.set_halign(gtk::Align::Center);"));
|
|
|
|
|
assert!(
|
|
|
|
|
UI_LAYOUT_SRC
|
|
|
|
|
.matches("set_halign(gtk::Align::Center);")
|
|
|
|
|
.count()
|
|
|
|
|
>= 5,
|
|
|
|
|
"chip labels and values should be horizontally centered"
|
|
|
|
|
);
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(
|
2026-04-23 11:14:58 -03:00
|
|
|
UI_LAYOUT_SRC.matches("set_xalign(0.5);").count() >= 4,
|
|
|
|
|
"chip text lines should center their own text, not only their widgets"
|
2026-04-23 07:00:06 -03:00
|
|
|
);
|
2026-04-23 11:14:58 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn relay_controls_keep_connect_inline_with_server_entry() {
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("build_panel(\"Relay Controls\")"));
|
|
|
|
|
assert_eq!(const_i32("OPERATIONS_RAIL_WIDTH"), 304);
|
|
|
|
|
assert_eq!(const_i32("RAIL_BUTTON_WIDTH"), 92);
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("let relay_grid = gtk::Grid::new();"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("relay_grid.set_column_homogeneous(true);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("relay_grid.attach(&server_entry, 0, 0, 2, 1);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("let start_button = rail_button(\"Connect\""));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("pub(crate) fn set_rail_button_label("));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("relay_grid.attach(&start_button, 2, 0, 1, 1);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("let clipboard_button = rail_button(\"Send Clipboard\""));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("let probe_button = rail_button(\"Copy Gate Probe\""));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("let usb_recover_button = rail_button(\"Recover USB\""));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("relay_grid.attach(&clipboard_button, 0, 1, 1, 1);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("relay_grid.attach(&probe_button, 1, 1, 1, 1);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("relay_grid.attach(&usb_recover_button, 2, 1, 1, 1);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("text.set_ellipsize(pango::EllipsizeMode::End);"));
|
2026-04-21 21:38:22 -03:00
|
|
|
assert!(
|
2026-04-23 11:14:58 -03:00
|
|
|
source_index("relay_grid.attach(&server_entry, 0, 0, 2, 1);")
|
|
|
|
|
< source_index("relay_grid.attach(&start_button, 2, 0, 1, 1);")
|
2026-04-21 21:38:22 -03:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 20:19:47 -03:00
|
|
|
#[test]
|
2026-04-22 22:10:39 -03:00
|
|
|
fn media_controls_own_stream_toggles_and_inline_gain_controls() {
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(!UI_LAYOUT_SRC.contains("Remote Audio"));
|
|
|
|
|
assert!(
|
|
|
|
|
UI_LAYOUT_SRC.contains("let power_shell = gtk::Box::new(gtk::Orientation::Vertical, 6);")
|
|
|
|
|
);
|
|
|
|
|
assert!(!UI_LAYOUT_SRC.contains("let channel_heading = gtk::Label::new(Some(\"Streams\"));"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("gtk::CheckButton::with_label(\"Camera\")"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("gtk::CheckButton::with_label(\"Mic\")"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("gtk::CheckButton::with_label(\"Speaker\")"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("let camera_quality_combo = gtk::ComboBoxText::new();"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("sync_camera_quality_combo("));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("camera_quality_combo.set_size_request(88, -1);"));
|
2026-04-22 22:10:39 -03:00
|
|
|
assert!(
|
2026-04-23 07:00:06 -03:00
|
|
|
UI_LAYOUT_SRC
|
|
|
|
|
.contains("let camera_selectors = gtk::Box::new(gtk::Orientation::Horizontal, 6);")
|
2026-04-22 22:10:39 -03:00
|
|
|
);
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(UI_LAYOUT_SRC.contains("camera_selectors.append(&camera_combo);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("camera_selectors.append(&camera_quality_combo);"));
|
2026-04-22 22:10:39 -03:00
|
|
|
assert!(
|
|
|
|
|
source_index("camera_selectors.append(&camera_combo);")
|
|
|
|
|
< source_index("camera_selectors.append(&camera_quality_combo);")
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
2026-04-23 07:00:06 -03:00
|
|
|
UI_LAYOUT_SRC
|
|
|
|
|
.contains("let speaker_selectors = gtk::Box::new(gtk::Orientation::Horizontal, 6);")
|
2026-04-22 22:10:39 -03:00
|
|
|
);
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(UI_LAYOUT_SRC.contains("speaker_selectors.append(&speaker_combo);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("speaker_selectors.append(&audio_gain_scale);"));
|
2026-04-22 22:10:39 -03:00
|
|
|
assert!(
|
2026-04-23 07:00:06 -03:00
|
|
|
UI_LAYOUT_SRC
|
2026-04-22 22:10:39 -03:00
|
|
|
.contains("let microphone_selectors = gtk::Box::new(gtk::Orientation::Horizontal, 6);")
|
|
|
|
|
);
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(UI_LAYOUT_SRC.contains("microphone_selectors.append(µphone_combo);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("microphone_selectors.append(&mic_gain_scale);"));
|
2026-04-22 22:10:39 -03:00
|
|
|
assert_eq!(
|
2026-04-23 07:00:06 -03:00
|
|
|
UI_LAYOUT_SRC
|
2026-04-22 22:10:39 -03:00
|
|
|
.matches("attach_device_control_row(\n &media_grid")
|
|
|
|
|
.count(),
|
|
|
|
|
3
|
|
|
|
|
);
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(!UI_LAYOUT_SRC.contains("camera_combo.append(Some(\"auto\")"));
|
|
|
|
|
assert!(!UI_LAYOUT_SRC.contains("speaker_combo.append(Some(\"auto\")"));
|
|
|
|
|
assert!(!UI_LAYOUT_SRC.contains("microphone_combo.append(Some(\"auto\")"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("let power_heading = gtk::Label::new(Some(\"GPIO Power\"));"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("power_row.append(&power_heading);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("power_buttons.set_homogeneous(true);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("let audio_gain_scale ="));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("audio_gain_scale.set_draw_value(false);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("audio_gain_scale.set_size_request(96, -1);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("let mic_gain_scale ="));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("mic_gain_scale.set_draw_value(false);"));
|
|
|
|
|
assert!(UI_LAYOUT_SRC.contains("mic_gain_scale.set_size_request(96, -1);"));
|
|
|
|
|
assert!(!UI_LAYOUT_SRC.contains("audio_gain_row.set_size_request(220, -1);"));
|
|
|
|
|
assert!(!UI_LAYOUT_SRC.contains("mic_gain_row.set_size_request(220, -1);"));
|
|
|
|
|
assert!(!UI_LAYOUT_SRC.contains("power_shell.append(&audio_gain_row);"));
|
|
|
|
|
assert!(!UI_LAYOUT_SRC.contains("power_shell.append(&mic_gain_row);"));
|
2026-04-21 21:38:22 -03:00
|
|
|
assert_eq!(
|
2026-04-23 07:00:06 -03:00
|
|
|
UI_LAYOUT_SRC
|
2026-04-21 21:38:22 -03:00
|
|
|
.matches("connection_body.append(>k::Separator::new(gtk::Orientation::Horizontal));")
|
|
|
|
|
.count(),
|
|
|
|
|
2,
|
|
|
|
|
"the operations rail should not gain extra vertical sections that stretch the lower layout"
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
2026-04-21 22:15:47 -03:00
|
|
|
source_index("let power_heading = gtk::Label::new(Some(\"GPIO Power\"));")
|
2026-04-22 22:10:39 -03:00
|
|
|
< source_index("let routing_heading = gtk::Label::new(Some(\"Inputs\"));")
|
2026-04-22 00:56:03 -03:00
|
|
|
);
|
|
|
|
|
assert!(
|
2026-04-22 22:10:39 -03:00
|
|
|
source_index("power_shell.append(&power_row);")
|
2026-04-21 22:15:47 -03:00
|
|
|
< source_index("let routing_heading = gtk::Label::new(Some(\"Inputs\"));")
|
2026-04-21 21:38:22 -03:00
|
|
|
);
|
2026-04-23 07:00:06 -03:00
|
|
|
assert!(UI_LAYOUT_SRC.contains("routing_buttons.set_homogeneous(true);"));
|
2026-04-21 20:19:47 -03:00
|
|
|
}
|