Files
vectorcraft/crates/ui-egui/src/tests_labels.rs
T
bflatasticandClaude Opus 5.5 e7f6328188 M3.56: swatch library panel with code-generated swatch libraries
Window > Swatch Libraries and the Swatches panel's library button open a
read-only, floating library panel (thumbnail and list views, find field,
libraries menu, previous/next arrows). Clicking a swatch adds it to the
document and applies it as one undo step; Shift/Cmd-clicks select swatches
and groups for Add to Swatches. Default Swatches restores missing defaults.

Libraries are computed in code (OKLCH, rounded to 8-bit sRGB): Web Safe 216,
Grays and Neutrals, Earth Tones, Skin Tone Ramps, Pastels, Brights,
Metallic Gradients, Perceptual Scales, Harmony Sets.

Commands: swatch.library.list, swatch.library.get, swatch.library.add
{library, names?, apply?, focus?}, swatch.resetDefaults {replace?};
UI command window.swatchLibrary {library}.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:59:59 +07:00

111 lines
4.6 KiB
Rust

//! Neutral wording in menus and panels: what users read names VectorCraft's own features.
use egui::epaint::Shape;
use serde_json::json;
use vectorcraft_engine::Session;
use crate::VectorcraftApp;
use crate::menus::{self, Item};
fn app() -> VectorcraftApp {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 100, "height": 100})).unwrap();
app
}
/// Every string painted by `f` in a headless frame (app fonts installed), one per line. The second
/// of two frames: windows size themselves invisibly in their first.
pub(crate) fn painted_text(app: &mut VectorcraftApp, mut f: impl FnMut(&mut VectorcraftApp, &mut egui::Ui)) -> String {
fn collect(s: &Shape, out: &mut String) {
match s {
Shape::Text(t) => {
out.push_str(t.galley.text());
out.push('\n');
}
Shape::Vec(v) => v.iter().for_each(|s| collect(s, out)),
_ => {}
}
}
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
ctx.run_ui(egui::RawInput::default(), |ui| f(app, ui)).textures_delta.clear();
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| f(app, ui));
out.textures_delta.clear();
let mut text = String::new();
for c in &out.shapes {
collect(&c.shape, &mut text);
}
text
}
#[test]
fn effect_menu_has_a_vector_effects_section() {
let tree = menus::menu_tree();
let (_, effect) = tree.iter().find(|(t, _)| *t == "Effect").expect("Effect menu");
assert!(effect.iter().any(|i| matches!(i, Item::Header("Vector Effects"))));
}
#[test]
fn library_submenus_are_disabled_placeholders() {
let app = app();
let entries = menus::menu_entries(&app);
for lib in ["Brush Libraries", "Graphic Style Libraries", "Symbol Libraries"] {
let items: Vec<_> = entries.iter().filter(|e| e.path == ["Window", lib]).collect();
let labels: Vec<&str> = items.iter().map(|e| e.label.as_str()).collect();
assert_eq!(labels, ["Built-in Libraries", "User Defined", "Other Library…"], "{lib}");
assert!(items.iter().all(|e| !e.enabled && e.command.is_none()), "{lib}");
}
}
#[test]
fn stroke_profiles_have_descriptive_names() {
let labels: Vec<&str> = vectorcraft_doc::WidthProfile::PRESETS.iter().map(|p| p.label).collect();
assert_eq!(labels, ["Uniform", "Lens", "Taper Start", "Taper End"]);
}
#[test]
fn swatches_menu_offers_save_swatch_library() {
let mut app = app();
let text = painted_text(&mut app, crate::panels::swatches::menu);
assert!(text.contains("Save Swatch Library…"), "{text}");
assert!(!text.contains("ASE"), "{text}");
}
#[test]
fn edit_menu_lists_pdf_presets() {
let app = app();
assert!(menus::menu_entries(&app).iter().any(|e| e.path == ["Edit"] && e.label == "PDF Presets…" && !e.enabled));
}
#[test]
fn clipboard_preferences_name_the_legacy_format_neutrally() {
let label = |key: &str| vectorcraft_engine::cmd::prefscmds::PREF_SPECS.iter().find(|p| p.key == key).unwrap().label;
assert_eq!(label("copyAicb"), "Legacy vector clipboard (no transparency)");
assert_eq!(label("aicbMode"), "Legacy vector clipboard");
}
#[test]
fn shortcut_set_old_name_is_accepted() {
use crate::shortcut_editor::{PRESETS, import_json};
let (old, _) = crate::shortcut_editor::LEGACY_PRESETS[0];
assert_eq!(PRESETS, ["VectorCraft Defaults", "Classic Defaults"]);
// UI preferences saved by an earlier version load (and save again) under the current name.
let ui: crate::state::UiState = serde_json::from_value(json!({ "shortcut_set": old })).unwrap();
assert_eq!(ui.shortcut_set, "Classic Defaults");
let saved = serde_json::to_value(&ui).unwrap();
assert_eq!(saved["shortcut_set"], "Classic Defaults");
let back: crate::state::UiState = serde_json::from_value(saved).unwrap();
assert_eq!(back.shortcut_set, "Classic Defaults");
let default: crate::state::UiState = serde_json::from_value(json!({})).unwrap();
assert_eq!(default.shortcut_set, PRESETS[0]);
// The command and imported sets take the old name too.
let mut app = app();
app.run("shortcuts.preset", json!({ "name": old })).unwrap();
assert_eq!(app.ui.shortcut_set, "Classic Defaults");
app.run("shortcuts.preset", json!({ "name": "VectorCraft Defaults" })).unwrap();
assert_eq!(app.ui.shortcut_set, "VectorCraft Defaults");
assert!(app.run("shortcuts.preset", json!({ "name": "Nope" })).is_err());
let (set, _) = import_json(&json!({ "set": old, "overrides": {} })).unwrap();
assert_eq!(set, "Classic Defaults");
}