mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-24 10:46:50 -06:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ee8c0d15f | ||
|
|
bc0cd89225 | ||
|
|
524ce912e5 | ||
|
|
ebc986fcc9 |
@@ -108,6 +108,54 @@ fn save_actions_include_the_cell_draft_and_close_keeps_the_unsaved_prompt() {
|
||||
assert_eq!(h.state().session.active().unwrap().wb.active().unwrap().value(CellRef::new(0, 0)), Value::Number(123.0));
|
||||
}
|
||||
|
||||
/// Where the title bar's icon button for `name` is. Its icon has no text, so it is found by
|
||||
/// hovering along the title bar until its tooltip ("Save (⌘S)") shows.
|
||||
fn title_bar_button(h: &mut egui_kittest::Harness<'static, SheetApp>, name: &str) -> egui::Pos2 {
|
||||
let autosave = h.get_by_label("AutoSave").rect();
|
||||
let tip = format!("{name} (");
|
||||
let mut x = autosave.right();
|
||||
while x < autosave.right() + 300.0 {
|
||||
let at = egui::pos2(x, autosave.center().y);
|
||||
h.hover_at(at);
|
||||
h.run_steps(3);
|
||||
if h.query_by_label_contains(&tip).is_some() {
|
||||
return at;
|
||||
}
|
||||
x += 6.0;
|
||||
}
|
||||
panic!("no title bar button shows a {tip:?} tooltip");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_title_bar_save_button_saves_the_cell_being_typed() {
|
||||
// Regression test for #223: the title bar's Save button saved the file without the cell the
|
||||
// user was still typing in.
|
||||
let dir = std::env::temp_dir().join(format!("gridcraft-title-bar-save-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("Book.xlsx").to_string_lossy().into_owned();
|
||||
let mut h = harness(Services::default());
|
||||
h.state_mut().run("file.save", serde_json::json!({"path": &path})).unwrap();
|
||||
h.run_steps(2);
|
||||
h.input_mut().events.push(egui::Event::Text("123".into()));
|
||||
h.run_steps(2);
|
||||
assert!(h.state().editor.is_some());
|
||||
let save = title_bar_button(&mut h, "Save");
|
||||
for pressed in [true, false] {
|
||||
h.input_mut().events.push(egui::Event::PointerButton {
|
||||
pos: save,
|
||||
button: egui::PointerButton::Primary,
|
||||
pressed,
|
||||
modifiers: egui::Modifiers::NONE,
|
||||
});
|
||||
h.run_steps(1);
|
||||
}
|
||||
h.run_steps(2);
|
||||
assert!(h.state().editor.is_none(), "Save commits the cell draft");
|
||||
let (wb, _) = gridcraft_engine::io::open_bytes("Book.xlsx", &std::fs::read(&path).unwrap()).unwrap();
|
||||
assert_eq!(wb.active().unwrap().value(CellRef::new(0, 0)), Value::Number(123.0), "the saved file has the typed value");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_options_opens_the_options_page_and_pages_are_reachable_by_command() {
|
||||
let mut h = harness(Services::default());
|
||||
|
||||
@@ -26,6 +26,30 @@ fn blank() -> Session {
|
||||
s
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_formulas_ribbon_keeps_watch_window_inside_the_ribbon() {
|
||||
// Regression test for #360 (#342): Watch Window was a fourth small button in a column of
|
||||
// three, so it hung below the ribbon and was cut off.
|
||||
fn texts(shape: &egui::Shape, out: &mut Vec<(String, egui::Rect)>) {
|
||||
match shape {
|
||||
egui::Shape::Vec(shapes) => shapes.iter().for_each(|s| texts(s, out)),
|
||||
egui::Shape::Text(t) => out.push((t.galley.text().to_string(), t.galley.rect.translate(t.pos.to_vec2()))),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let mut h = harness(blank());
|
||||
h.state_mut().ui.ribbon_tab = "Formulas".into();
|
||||
h.run_steps(3);
|
||||
// Ribbon button labels are painted text: find them among the frame's shapes.
|
||||
let mut painted = Vec::new();
|
||||
h.output().shapes.iter().for_each(|c| texts(&c.shape, &mut painted));
|
||||
let bottom_of = |word: &str| painted.iter().filter(|(s, _)| s.contains(word)).map(|(_, r)| r.bottom()).fold(f32::NEG_INFINITY, f32::max);
|
||||
// "Calculation Options" is a big button whose second line sits at the foot of the ribbon.
|
||||
let (watch, foot) = (bottom_of("Window"), bottom_of("Options"));
|
||||
assert!(watch.is_finite() && foot.is_finite(), "the Formulas ribbon paints Watch Window and Calculation Options");
|
||||
assert!(watch <= foot + 0.5, "Watch Window's label ends at y={watch}, below the ribbon's last label row (y={foot})");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vertical_scrollbar_pages_and_drags_without_changing_selection() {
|
||||
let mut h = harness(blank());
|
||||
|
||||
@@ -56,3 +56,47 @@ fn pivot_field_button_surfaces_follow_the_active_theme() {
|
||||
assert_eq!(field_button_fill(&shapes, "Region ▾"), expected, "dark mode: {dark}");
|
||||
}
|
||||
}
|
||||
|
||||
fn flatten(shape: Shape, out: &mut Vec<Shape>) {
|
||||
match shape {
|
||||
Shape::Vec(shapes) => shapes.into_iter().for_each(|s| flatten(s, out)),
|
||||
s => out.push(s),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_field_list_scrollbar_sits_at_the_pane_edge() {
|
||||
// Regression test for #1485: with enough source fields to scroll, the field list's scrollbar
|
||||
// sat just right of the longest (short) field name instead of at the pane's right edge.
|
||||
use egui_kittest::kittest::Queryable as _;
|
||||
let names: Vec<String> = (1..=14).map(|i| format!("F{i}")).collect();
|
||||
let mut session = Session::new();
|
||||
session.new_workbook();
|
||||
session.execute("range.setValues", json!({"range": "A1", "values": [names.clone(), (1..=14).collect::<Vec<i32>>()]})).unwrap();
|
||||
session.execute("insert.pivotTable", json!({"source": "Sheet1!A1:N2", "destination": "Sheet1!A5", "rows": ["F1"], "values": ["F2"]})).unwrap();
|
||||
session.execute("selection.set", json!({"cell": "A5"})).unwrap();
|
||||
let mut h = egui_kittest::Harness::builder().with_size(vec2(1200.0, 800.0)).build_ui_state(
|
||||
|ui, app: &mut SheetApp| {
|
||||
let ctx = ui.ctx().clone();
|
||||
app.logic(&ctx);
|
||||
app.ui(ui);
|
||||
},
|
||||
SheetApp::new(session, Default::default()),
|
||||
);
|
||||
h.run_steps(4);
|
||||
let first = h.get_by_label("F1").rect();
|
||||
// A floating scrollbar is drawn while the pointer is over its area.
|
||||
h.get_by_label("F1").hover();
|
||||
h.run_steps(3);
|
||||
let mut shapes = Vec::new();
|
||||
h.output().shapes.iter().for_each(|c| flatten(c.shape.clone(), &mut shapes));
|
||||
let bar = shapes
|
||||
.iter()
|
||||
.filter_map(|s| match s {
|
||||
Shape::Rect(r) if r.fill.a() > 0 && r.rect.width() <= 12.0 && r.rect.height() >= 20.0 && r.rect.left() > first.left() => Some(r.rect),
|
||||
_ => None,
|
||||
})
|
||||
.max_by(|a, b| a.right().total_cmp(&b.right()))
|
||||
.expect("the field list draws a scrollbar");
|
||||
assert!(bar.right() > 1200.0 - 20.0, "the scrollbar {bar:?} sits at the pane's right edge, not beside the field names ({first:?})");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user