use crate::config::{Config, KeyBindingOverrides, State, Theme};
use crate::document::Buffer;
use crate::terminal::Capabilities;
use super::App;
pub(crate) fn make_app() -> App {
let caps = Capabilities {
color_depth: crate::terminal::ColorDepth::TrueColor,
..Capabilities::default()
};
let theme_file = (&Theme::default()).into();
App::new(
Config::default(),
State::default(),
KeyBindingOverrides::default(),
theme_file,
None,
caps,
Vec::new(),
)
.expect("build app")
}
pub(crate) fn close_startup_modals(app: &mut App) {
while app.modal_stack.pop().is_some() {}
}
pub(crate) fn app_with_buffer(text: &str, cursor_byte: usize) -> App {
let mut app = make_app();
app.editor.buffer = Buffer::from_str(text);
app.editor.refresh_parsed();
let total = app.editor.buffer.len_chars();
let char_off = app
.editor
.buffer
.rope()
.byte_to_char(cursor_byte.min(app.editor.buffer.contents().len()));
app.editor.cursor.offset = char_off.min(total);
app.editor.update_cursor_block();
app
}
#[cfg(test)]
mod theme_downgrade_tests {
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crate::app::modal::types::{Modal, ModalOutcome};
use crate::app::modal::{TerminalCapabilitiesModal, ThemeDowngradeModal, WelcomeModal};
use crate::config::{Config, FiguresEnabled, ImagesEnabled, KeyBindingOverrides, State, Theme};
use crate::terminal::{Capabilities, ColorDepth};
use super::App;
fn app_with(color_depth: ColorDepth, theme: &str) -> App {
app_with_welcome(color_depth, theme, true)
}
fn app_with_welcome(color_depth: ColorDepth, theme: &str, show_welcome: bool) -> App {
let caps = Capabilities {
color_depth,
..Capabilities::minimal()
};
let mut config = Config {
theme: theme.to_owned(),
..Config::default()
};
config.editor.show_welcome = show_welcome;
let state = State {
last_version_seen: crate::app::update_check::INSTALLED_VERSION.to_owned(),
..State::default()
};
App::new(
config,
state,
KeyBindingOverrides::default(),
(&Theme::default()).into(),
None,
caps,
Vec::new(),
)
.expect("build app")
}
#[test]
fn indexed_terminal_substitutes_the_theme_and_notifies() {
let app = app_with(ColorDepth::Ansi256, "Dracula");
assert_eq!(app.config.theme, "256 Dark");
assert_eq!(app.config.theme_downgraded_from.as_deref(), Some("Dracula"));
assert!(app.modal_stack.contains::<ThemeDowngradeModal>());
}
#[test]
fn truecolor_terminal_leaves_the_theme_alone() {
let app = app_with(ColorDepth::TrueColor, "Dracula");
assert_eq!(app.config.theme, "Dracula");
assert!(app.config.theme_downgraded_from.is_none());
assert!(!app.modal_stack.contains::<ThemeDowngradeModal>());
}
#[test]
fn an_indexed_terminal_disables_media_without_touching_config() {
let caps = Capabilities {
color_depth: ColorDepth::Ansi256,
..Capabilities::minimal()
};
let mut config = Config {
theme: "Dracula".into(),
..Config::default()
};
config.images.enabled = ImagesEnabled::Always;
config.figures.enabled = FiguresEnabled::Always;
let app = App::new(
config,
State::default(),
KeyBindingOverrides::default(),
(&Theme::default()).into(),
None,
caps,
Vec::new(),
)
.expect("build app");
assert!(!app.effective_images_enabled());
assert!(!app.effective_diagrams_enabled());
assert!(!app.images_layout_enabled());
assert!(!app.diagrams_layout_enabled());
assert!(!app.editor.images_enabled);
assert!(!app.editor.diagrams_enabled);
assert_eq!(app.config.images.enabled, ImagesEnabled::Always);
assert_eq!(app.config.figures.enabled, FiguresEnabled::Always);
}
#[test]
fn a_truecolor_terminal_still_renders_media() {
let caps = Capabilities {
color_depth: ColorDepth::TrueColor,
..Capabilities::minimal()
};
let mut config = Config::default();
config.images.enabled = ImagesEnabled::Always;
config.figures.enabled = FiguresEnabled::Always;
let app = App::new(
config,
State::default(),
KeyBindingOverrides::default(),
(&Theme::default()).into(),
None,
caps,
Vec::new(),
)
.expect("build app");
assert!(app.effective_images_enabled());
assert!(app.effective_diagrams_enabled());
}
#[test]
fn a_new_terminal_gets_one_notice_not_two() {
let app = app_with_welcome(ColorDepth::Ansi256, "Dracula", false);
assert_eq!(app.config.theme, "256 Dark");
assert!(app.modal_stack.contains::<TerminalCapabilitiesModal>());
assert!(!app.modal_stack.contains::<ThemeDowngradeModal>());
}
#[test]
fn an_on_demand_welcome_is_escapable_but_a_first_run_one_is_not() {
let mut app = app_with_welcome(ColorDepth::Ansi256, "Dracula", false);
app.open_welcome_modal();
let top = app.modal_stack.top_mut().expect("welcome is on top");
assert!(top.as_any().is::<WelcomeModal>());
assert!(top.dismissable());
let caps = Capabilities {
color_depth: ColorDepth::Ansi256,
..Capabilities::minimal()
};
let first_run = WelcomeModal::from_state(&caps, &Config::default())
.expect("show_welcome defaults to true");
assert!(!first_run.dismissable());
}
#[test]
fn saving_the_welcome_below_truecolor_leaves_persisted_media_alone() {
let _iso = crate::test_env::config_isolation();
let mut app = app_with_welcome(ColorDepth::Ansi256, "Dracula", false);
app.config.images.enabled = ImagesEnabled::Always;
app.config.figures.enabled = FiguresEnabled::Always;
let mut modal = WelcomeModal::new(
&Capabilities {
color_depth: ColorDepth::Ansi256,
..Capabilities::minimal()
},
&app.config,
);
modal.focus_save_for_test();
let outcome = modal.handle_key(
KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE),
&mut app,
24,
80,
);
match outcome {
ModalOutcome::CloseAnd(f) => f(&mut app),
_ => panic!("Save should close and persist"),
}
assert_eq!(app.config.images.enabled, ImagesEnabled::Always);
assert_eq!(app.config.figures.enabled, FiguresEnabled::Always);
assert!(!app.effective_images_enabled());
assert!(!app.effective_diagrams_enabled());
}
#[test]
fn a_colorless_terminal_is_not_downgraded() {
let app = app_with(ColorDepth::NoColor, "Dracula");
assert_eq!(app.config.theme, "Dracula");
assert!(app.config.theme_downgraded_from.is_none());
assert!(!app.modal_stack.contains::<ThemeDowngradeModal>());
}
#[test]
fn a_monochrome_theme_is_not_substituted() {
let app = app_with(ColorDepth::Ansi16, "Monochrome Dark");
assert_eq!(app.config.theme, "Monochrome Dark");
assert!(app.config.theme_downgraded_from.is_none());
assert!(!app.modal_stack.contains::<ThemeDowngradeModal>());
}
#[test]
fn an_already_indexed_theme_is_not_substituted() {
let app = app_with(ColorDepth::Ansi16, "256 Light");
assert_eq!(app.config.theme, "256 Light");
assert!(app.config.theme_downgraded_from.is_none());
assert!(!app.modal_stack.contains::<ThemeDowngradeModal>());
}
}