test(tui): expand UX regression snapshots
This commit is contained in:
@@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Inline guidance overlay adds a three-step onboarding tour, keymap-aware cheat sheets (F1 / `?`), and persists completion state via `ui.guidance`.
|
||||
- Status surface renders a layered HUD with streaming/tool indicators, contextual gauges, and redesigned toast cards featuring icons, countdown timers, and a compact history log.
|
||||
- Published a TUI UX & keybinding playbook documenting modal ergonomics, command metadata, theming tokens, and animation policy.
|
||||
- Automated TUI regression snapshots now cover mode transitions, keymap variants, accessibility presets, and multiple terminal breakpoints.
|
||||
- Cloud usage tracker persists hourly/weekly token totals, adds a `:limits` command, shows live header badges, and raises toast warnings at 80 %/95 % of the configured quotas.
|
||||
- Message rendering caches wrapped lines and throttles streaming redraws to keep the TUI responsive on long sessions.
|
||||
- Model picker badges now inspect provider capabilities so vision/audio/thinking models surface the correct icons even when descriptions are sparse.
|
||||
|
||||
@@ -10,6 +10,18 @@ use ratatui::{Terminal, backend::TestBackend};
|
||||
|
||||
use common::build_chat_app;
|
||||
|
||||
async fn send_key(app: &mut ChatApp, code: KeyCode, modifiers: KeyModifiers) {
|
||||
app.handle_event(Event::Key(KeyEvent::new(code, modifiers)))
|
||||
.await
|
||||
.expect("send key event");
|
||||
}
|
||||
|
||||
async fn type_text(app: &mut ChatApp, text: &str) {
|
||||
for ch in text.chars() {
|
||||
send_key(app, KeyCode::Char(ch), KeyModifiers::NONE).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer_to_string(buffer: &ratatui::buffer::Buffer) -> String {
|
||||
let mut output = String::new();
|
||||
|
||||
@@ -213,3 +225,98 @@ async fn render_guidance_cheatsheet_snapshot() {
|
||||
assert_snapshot!("guidance_cheatsheet", snapshot);
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn render_mode_states_snapshots() {
|
||||
let mut normal = build_chat_app(|_| {}, |_| {}).await;
|
||||
with_settings!({ snapshot_suffix => "normal-90x28" }, {
|
||||
let snapshot = render_snapshot(&mut normal, 90, 28);
|
||||
assert_snapshot!("mode_states", snapshot);
|
||||
});
|
||||
|
||||
let mut editing = build_chat_app(|_| {}, |_| {}).await;
|
||||
send_key(&mut editing, KeyCode::Char('i'), KeyModifiers::NONE).await;
|
||||
type_text(&mut editing, "Typing in editing mode").await;
|
||||
with_settings!({ snapshot_suffix => "editing-90x28" }, {
|
||||
let snapshot = render_snapshot(&mut editing, 90, 28);
|
||||
assert_snapshot!("mode_states", snapshot);
|
||||
});
|
||||
|
||||
let mut visual = build_chat_app(
|
||||
|_| {},
|
||||
|session| {
|
||||
let conversation = session.conversation_mut();
|
||||
conversation.push_user_message("Render visual selection across multiple lines.");
|
||||
conversation.push_message(Message::assistant(
|
||||
"Assistant reply for visual mode highlighting.".into(),
|
||||
));
|
||||
},
|
||||
)
|
||||
.await;
|
||||
send_key(&mut visual, KeyCode::Char('v'), KeyModifiers::NONE).await;
|
||||
send_key(&mut visual, KeyCode::Char('j'), KeyModifiers::NONE).await;
|
||||
send_key(&mut visual, KeyCode::Char('j'), KeyModifiers::NONE).await;
|
||||
with_settings!({ snapshot_suffix => "visual-90x28" }, {
|
||||
let snapshot = render_snapshot(&mut visual, 90, 28);
|
||||
assert_snapshot!("mode_states", snapshot);
|
||||
});
|
||||
|
||||
let mut command = build_chat_app(|_| {}, |_| {}).await;
|
||||
send_key(&mut command, KeyCode::Char(':'), KeyModifiers::NONE).await;
|
||||
type_text(&mut command, "session save").await;
|
||||
with_settings!({ snapshot_suffix => "command-90x28" }, {
|
||||
let snapshot = render_snapshot(&mut command, 90, 28);
|
||||
assert_snapshot!("mode_states", snapshot);
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn render_emacs_profile_snapshot() {
|
||||
let mut app = build_chat_app(
|
||||
|cfg| {
|
||||
cfg.ui.keymap_profile = Some("emacs".into());
|
||||
cfg.ui.guidance.coach_marks_complete = true;
|
||||
},
|
||||
|_| {},
|
||||
)
|
||||
.await;
|
||||
|
||||
send_key(&mut app, KeyCode::Char(':'), KeyModifiers::NONE).await;
|
||||
type_text(&mut app, "help").await;
|
||||
|
||||
with_settings!({ snapshot_suffix => "emacs-110x30" }, {
|
||||
let snapshot = render_snapshot(&mut app, 110, 30);
|
||||
assert_snapshot!("emacs_profile", snapshot);
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn render_accessibility_snapshots() {
|
||||
let mut high_contrast = build_chat_app(
|
||||
|cfg| {
|
||||
cfg.ui.accessibility.high_contrast = true;
|
||||
cfg.ui.guidance.coach_marks_complete = true;
|
||||
},
|
||||
|_| {},
|
||||
)
|
||||
.await;
|
||||
|
||||
with_settings!({ snapshot_suffix => "high-contrast-100x32" }, {
|
||||
let snapshot = render_snapshot(&mut high_contrast, 100, 32);
|
||||
assert_snapshot!("accessibility_modes", snapshot);
|
||||
});
|
||||
|
||||
let mut reduced_chrome = build_chat_app(
|
||||
|cfg| {
|
||||
cfg.ui.accessibility.reduced_chrome = true;
|
||||
cfg.ui.guidance.coach_marks_complete = true;
|
||||
},
|
||||
|_| {},
|
||||
)
|
||||
.await;
|
||||
|
||||
with_settings!({ snapshot_suffix => "reduced-chrome-100x32" }, {
|
||||
let snapshot = render_snapshot(&mut reduced_chrome, 100, 32);
|
||||
assert_snapshot!("accessibility_modes", snapshot);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
source: crates/owlen-tui/tests/chat_snapshots.rs
|
||||
expression: snapshot
|
||||
---
|
||||
" "
|
||||
" 🦉 OWLEN v0.2.0 · Mode Chat · Focus Input ollama_local · stub-model · Accessibil "
|
||||
" "
|
||||
" Context metrics not available Cloud usage pending "
|
||||
" "
|
||||
" "
|
||||
" ▌ Chat · stub-model PgUp/PgDn scroll · g/G jump · s save · Ctrl+2 focus "
|
||||
" "
|
||||
" No messages yet. Press 'i' to start typing. "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" Input Press i to start typing · Ctrl+5 focus "
|
||||
" "
|
||||
" "
|
||||
" System/Status "
|
||||
" "
|
||||
" "
|
||||
" NORMAL CHAT Focus INPUT · ⓘ Normal mode • Press F1 for help owlen-tui "
|
||||
" Ctrl+5 Icons: Nerd (auto) 1:1 · Plain Text · UTF-8 "
|
||||
" F1 Help ? Guidance F12 DebugUsage metrics pending · run :limits "
|
||||
" "
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
source: crates/owlen-tui/tests/chat_snapshots.rs
|
||||
expression: snapshot
|
||||
---
|
||||
" 🦉 OWLEN v0.2.0 · Mode Chat · Focus Input ollama_local · stub-model · Accessibili "
|
||||
" "
|
||||
" Context metrics not available Cloud usage pending "
|
||||
" "
|
||||
" Legend · Normal <60% · Warning 60–85% · Critical >85% · Modes RC "
|
||||
" ▌ Chat · stub-model PgUp/PgDn scroll · g/G jump · s save · Ctrl+2 focus "
|
||||
" No messages yet. Press 'i' to start typing. "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" Input Press i to start typing · Ctrl+5 focus "
|
||||
" Press 'i' to start typing "
|
||||
" "
|
||||
" System/Status "
|
||||
" Icons: Nerd (auto) "
|
||||
" "
|
||||
" NORMAL CHAT Focus INPUT · ⓘ Normal mode • Press F1 for help owlen-tui "
|
||||
" Ctrl+5 Icons: Nerd (auto) 1:1 · Plain Text · UTF-8 "
|
||||
" F1 Help ? Guidance F12 DebugUsage metrics pending · run :limits "
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
source: crates/owlen-tui/tests/chat_snapshots.rs
|
||||
expression: snapshot
|
||||
---
|
||||
" "
|
||||
" 🦉 OWLEN v0.2.0 · Mode Chat · Focus Input ollama_local · stub-model "
|
||||
" "
|
||||
" Context metri Command Palette Ctrl+P "
|
||||
" "
|
||||
" :help "
|
||||
" ▌ Chat · stub "
|
||||
" "
|
||||
" No messages Commands "
|
||||
" › help F1 / ? "
|
||||
" Open the help overlay "
|
||||
" Support · Modes: Normal, Command · #help #docs #support "
|
||||
" tutorial "
|
||||
" Show keybinding tutorial "
|
||||
" Support · Modes: Command · #help #tutorial #onboarding "
|
||||
" h F1 / ? "
|
||||
" Open the help overlay "
|
||||
" Support · Modes: Normal, Command · #help #docs #support "
|
||||
" "
|
||||
" "
|
||||
" ▌ Command En Enter: run · Tab: autocomplete · /tag filter · Esc: cancel "
|
||||
" "
|
||||
" "
|
||||
" System/Status "
|
||||
" "
|
||||
" "
|
||||
" COMMAND CHAT Focus INPUT ·ⓘ :help owlen-tui "
|
||||
" Ctrl+5 Icons: Nerd (auto) 1:1 · Plain Text · UTF-8 "
|
||||
" F1 Help ? Guidance F12 DebugUsage metrics pending · run :limits "
|
||||
" "
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: crates/owlen-tui/tests/chat_snapshots.rs
|
||||
expression: snapshot
|
||||
---
|
||||
" "
|
||||
" 🦉 OWLEN v0.2.0 · Mode Chat · Focus Input ollama_local · stub-model "
|
||||
" "
|
||||
" Con Command Palette Ctrl+P "
|
||||
" "
|
||||
" :session save "
|
||||
" ▌ C "
|
||||
" "
|
||||
" N Commands "
|
||||
" › session save "
|
||||
" Save the current conversation "
|
||||
" Sessions · Modes: Command · #session #save #history "
|
||||
" sessions "
|
||||
" List saved sessions "
|
||||
" Sessions · Modes: Command · #session #history #browse "
|
||||
" load "
|
||||
" Load a saved conversation "
|
||||
" Sessions · Modes: Command · #session #restore #history "
|
||||
" ▌ C Enter: run · Tab: autocomplete · /tag filter · Esc: cancel "
|
||||
" "
|
||||
" "
|
||||
" System/Status "
|
||||
" "
|
||||
" "
|
||||
" COMMAND CHAT Focus INPUT ·ⓘ :session save owlen-tui "
|
||||
" Ctrl+5 Icons: Nerd (auto) 1:1 · Plain Text "
|
||||
" F1 Help ? Guidance F12 DebugUsage metrics pending · run :limits · UTF-8 "
|
||||
" "
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: crates/owlen-tui/tests/chat_snapshots.rs
|
||||
expression: snapshot
|
||||
---
|
||||
" "
|
||||
" 🦉 OWLEN v0.2.0 · Mode Chat · Focus Input ollama_local · stub-model "
|
||||
" "
|
||||
" Context metrics not available Cloud usage pending "
|
||||
" "
|
||||
" "
|
||||
" ▌ Chat · stub-model PgUp/PgDn scroll · g/G jump · s save · Ctrl+2 focus "
|
||||
" "
|
||||
" No messages yet. Press 'i' to start typing. "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" ▌ Input Enter send · Shift+Enter newline · Esc normal · Ctrl+5 focus "
|
||||
" "
|
||||
" "
|
||||
" System/Status "
|
||||
" "
|
||||
" "
|
||||
" INSERT CHAT Focus INPUT · ⓘ Normal mode • Press F1 for help owlen-tui "
|
||||
" Ctrl+5 Icons: Nerd (auto) 1:20 · Plain "
|
||||
" F1 Help ? Guidance F12 DebugUsage metrics pending · run :limits Text · UTF-8 "
|
||||
" "
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: crates/owlen-tui/tests/chat_snapshots.rs
|
||||
expression: snapshot
|
||||
---
|
||||
" "
|
||||
" 🦉 OWLEN v0.2.0 · Mode Chat · Focus Input ollama_local · stub-model "
|
||||
" "
|
||||
" Context metrics not available Cloud usage pending "
|
||||
" "
|
||||
" "
|
||||
" ▌ Chat · stub-model PgUp/PgDn scroll · g/G jump · s save · Ctrl+2 focus "
|
||||
" "
|
||||
" No messages yet. Press 'i' to start typing. "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" Input Press i to start typing · Ctrl+5 focus "
|
||||
" "
|
||||
" "
|
||||
" System/Status "
|
||||
" "
|
||||
" "
|
||||
" NORMAL CHAT Focus INPUT · ⓘ Normal mode • Press F1 for help owlen-tui "
|
||||
" Ctrl+5 Icons: Nerd (auto) 1:1 · Plain Text "
|
||||
" F1 Help ? Guidance F12 DebugUsage metrics pending · run :limits · UTF-8 "
|
||||
" "
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: crates/owlen-tui/tests/chat_snapshots.rs
|
||||
expression: snapshot
|
||||
---
|
||||
" "
|
||||
" 🦉 OWLEN v0.2.0 · Mode Chat · Focus Input ollama_local · stub-model "
|
||||
" "
|
||||
" Context metrics not available Cloud usage pending "
|
||||
" "
|
||||
" "
|
||||
" ▌ Chat · stub-model PgUp/PgDn scroll · g/G jump · s save · Ctrl+2 focus "
|
||||
" "
|
||||
" ┌ 👤 You ────────────────────────────────────────────────────────────────────────┐ "
|
||||
" │ Render visual selection across multiple lines. │ "
|
||||
" └────────────────────────────────────────────────────────────────────────────────┘ "
|
||||
" ┌ 🤖 Assistant ──────────────────────────────────────────────────────────────────┐ "
|
||||
" │ Assistant reply for visual mode highlighting. │ "
|
||||
" └────────────────────────────────────────────────────────────────────────────────┘ "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" ▌ Visual Select y yank · d cut · Esc cancel · Ctrl+5 focus "
|
||||
" "
|
||||
" "
|
||||
" System/Status "
|
||||
" "
|
||||
" "
|
||||
" VISUAL CHAT Focus INPUT · ⓘ -- VISUAL -- (move with j/k, yankowlen-tui "
|
||||
" Ctrl+5 Icons: Nerd (auto) 1:1 · Plain Text "
|
||||
" F1 Help ? Guidance F12 DebugUsage metrics pending · run :limits · UTF-8 "
|
||||
" "
|
||||
Reference in New Issue
Block a user