fission-command-ui 0.14.1

Terminal UI application for the fission command
Documentation
mod actions;
mod app;
mod commands;
mod components;
mod density;
mod publish;
mod release_config;
mod routes;
mod screens;
mod state;
mod theme;

use anyhow::Result;
use std::path::PathBuf;

#[cfg(test)]
use theme::UiThemeMode;

pub use app::CliUiApp;
pub use publish::{
    default_publish_options, run_publish_tui, run_publish_window, PublishApp, PublishUiOptions,
    PublishUiState,
};
pub use release_config::{
    run_release_config_tui, ReleaseConfigEditorApp, ReleaseConfigEditorOptions,
    ReleaseConfigEditorState,
};
pub use state::UiState;

#[derive(Clone, Debug)]
pub struct UiOptions {
    pub project_dir: PathBuf,
    pub screenshot: Option<PathBuf>,
    pub exit_after_render: bool,
    pub width: Option<u16>,
    pub height: Option<u16>,
}

pub fn run_ui(options: UiOptions) -> Result<()> {
    let mut publish_options = publish::default_publish_options(options.project_dir);
    publish_options.screenshot = options.screenshot;
    publish_options.exit_after_render = options.exit_after_render;
    publish_options.width = options.width;
    publish_options.height = options.height;
    run_publish_tui(publish_options)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::routes::UiRoute;
    use crate::state::all_targets;
    use fission_command_core::Target;
    use std::path::PathBuf;

    #[test]
    fn cli_ui_renders_every_route_in_terminal_shell() {
        for route in UiRoute::ALL {
            let mut state = UiState {
                project_dir: PathBuf::from("."),
                project_name: "test-app".to_string(),
                app_id: "com.example.test".to_string(),
                project_status: "Project loaded".to_string(),
                targets: all_targets().to_vec(),
                selected_target: Some(Target::Web),
                route,
                host: "127.0.0.1".to_string(),
                port: "8123".to_string(),
                theme_mode: UiThemeMode::Dark,
                ..Default::default()
            };
            state.devices = vec![crate::state::UiDevice {
                id: "chrome".to_string(),
                name: "Chrome/Chromium".to_string(),
                target: Target::Web,
                kind: "browser".to_string(),
                status: "available".to_string(),
                detail: String::new(),
                available: true,
            }];

            let mut app = fission::terminal::TerminalApp::with_state(CliUiApp, state)
                .with_sync_env(|state, env| {
                    env.theme = match state.theme_mode {
                        UiThemeMode::Dark => fission::theme::Theme::dark(),
                        UiThemeMode::Light => fission::theme::Theme::default(),
                    };
                });
            app.render_frame(120, 40).expect("route should render");
        }
    }

    #[test]
    fn cli_ui_renders_confirmation_dialog() {
        let mut state = UiState {
            project_dir: PathBuf::from("."),
            project_name: "test-app".to_string(),
            app_id: "com.example.test".to_string(),
            project_status: "Project loaded".to_string(),
            targets: all_targets().to_vec(),
            selected_target: Some(Target::Web),
            host: "127.0.0.1".to_string(),
            port: "8123".to_string(),
            theme_mode: UiThemeMode::Dark,
            ..Default::default()
        };
        state.request_command_confirmation(crate::commands::UiCommand::RunSelected);

        let mut app = fission::terminal::TerminalApp::with_state(CliUiApp, state).with_sync_env(
            |state, env| {
                env.theme = match state.theme_mode {
                    UiThemeMode::Dark => fission::theme::Theme::dark(),
                    UiThemeMode::Light => fission::theme::Theme::default(),
                };
            },
        );
        let frame = app.render_frame(120, 40).expect("dialog should render");
        assert!(frame
            .as_plain_text()
            .contains("Confirm: run the selected target"));
        assert!(frame.as_plain_text().contains("Cancel"));
    }
}