Fix-up: repair test files after merging the conflicting PRs

- restore closing braces the line-level merges of side-by-side tests dropped
- new ChartData literals carry the palette field
- the filter-menu shortcut passes the row with the column
- #688's tab tests account for #851's All Sheets button
- Serbian margins for the ruler-margin labels

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-13 00:34:01 -04:00
co-authored by Claude Opus 5.5
parent 7e6aa698fa
commit 6405b00cf7
13 changed files with 64 additions and 23 deletions
+7
View File
@@ -571,6 +571,9 @@ fn functions_given_by_name() {
// A LET variable of that name is still the variable.
t.set("J1", "=LET(ABS,LAMBDA(x,x*10),SUM(MAP(B1:B3,ABS)))");
assert_eq!(t.num("J1"), 20.0);
}
#[test]
fn lambda_argument_count() {
let mut t = T::new();
t.wb.names.push(gridcraft_model::DefinedName {
@@ -1134,6 +1137,8 @@ fn cell_width_is_the_column_width_dialog_value_rounded() {
t.set(at, &format!("=CELL(\"width\",{col}1)"));
}
assert_eq!([t.num("H1"), t.num("H2"), t.num("H3"), t.num("H4"), t.num("H5")], [8.0, 11.0, 12.0, 11.0, 3.0]);
}
/// Expected results are Excel's, from Apache POI's `DGet.xls` (rows 46-49): a criteria cell holding
/// a formula under a label that names no field is evaluated for every record.
#[test]
@@ -1184,6 +1189,8 @@ fn database_functions_with_computed_criteria() {
assert_eq!(t.num("B51"), 1.0);
assert_eq!(t.num("B52"), 7.0);
assert_eq!(t.num("B53"), 4.0);
}
#[test]
fn offset_extreme_arguments_return_ref() {
let wb = Workbook::new();
+15 -6
View File
@@ -456,11 +456,8 @@ fn pie_starts_at_twelve_clockwise() {
#[test]
fn negative_values_put_axis_below_zero() {
let c = chart(ChartKind::ColumnClustered);
let d = ChartData {
palette: vec![],
categories: vec![],
series: vec![series("N", &[-5.0, 3.0, -2.0], [1, 2, 3, 255], ChartKind::ColumnClustered)],
};
let d =
ChartData { palette: vec![], categories: vec![], series: vec![series("N", &[-5.0, 3.0, -2.0], [1, 2, 3, 255], ChartKind::ColumnClustered)] };
let prims = render(&c, &d, 400.0, 300.0, &ApproxMeasure);
let t = texts(&prims);
assert!(t.iter().any(|s| s.starts_with('-') || s.starts_with("($") || s.starts_with("-$")), "{t:?}");
@@ -901,7 +898,7 @@ fn treemap_and_histogram_shapes() {
#[test]
fn histogram_boundary_values_match_interval_labels() {
let heights = |vals: &[f64]| -> Vec<f32> {
let d = ChartData { categories: vec![], series: vec![series("h", vals, [10, 20, 30, 255], ChartKind::Histogram)] };
let d = ChartData { palette: vec![], categories: vec![], series: vec![series("h", vals, [10, 20, 30, 255], ChartKind::Histogram)] };
let prims = render(&chart(ChartKind::Histogram), &d, 600.0, 400.0, &ApproxMeasure);
prims.iter().filter_map(|p| if let Prim::Rect { h, fill: Some([10, 20, 30, 255]), .. } = p { Some(*h) } else { None }).collect()
};
@@ -964,8 +961,13 @@ fn box_and_stock_charts_honour_the_gridlines_flag() {
let off = render(&c, &d, 500.0, 350.0, &ApproxMeasure);
assert!(grids(&on) > 0, "{kind:?}: gridlines on must draw lines");
assert_eq!(grids(&off), 0, "{kind:?}: gridlines off must hide them");
}
}
#[test]
fn funnel_value_labels_follow_the_data_labels_flag() {
let data = ChartData {
palette: vec![],
categories: vec!["a".into(), "b".into(), "c".into()],
series: vec![series("S", &[11111.0, 22222.0, 33333.0], [20, 96, 130, 255], ChartKind::Funnel)],
};
@@ -982,6 +984,10 @@ fn funnel_value_labels_follow_the_data_labels_flag() {
for cat in ["a", "b", "c"] {
assert!(texts(&off).contains(&cat));
assert!(texts(&on).contains(&cat));
}
}
#[test]
fn pie_points_follow_workbook_theme() {
let mut wb = Workbook::new();
wb.theme.colors[4] = 0x4F6D8A;
@@ -1024,6 +1030,9 @@ fn pie_points_follow_workbook_theme() {
assert_eq!(legend.len(), 40);
assert_eq!(legend.get(37), Some(&series_color(&wb.theme, 37)));
assert_ne!(series_color(&wb.theme, 37), series_color(&gridcraft_model::Theme::default(), 37));
}
#[test]
fn area_charts_draw_source_value_labels_only_when_enabled() {
for kind in [ChartKind::Area, ChartKind::AreaStacked] {
let d = sample(kind);
+6
View File
@@ -1086,6 +1086,9 @@ fn structured_refs_to_columns_with_edge_spaces() {
s.execute("file.open", json!({"name": "x.xlsx", "base64": b64})).unwrap();
assert_eq!(s.execute("cell.get", json!({"cell": "F3"})).unwrap()["formula"], "=SUBTOTAL(109,Table1[[Qty ]])");
assert_eq!([v(&s, "F1"), v(&s, "F2"), v(&s, "F3")], [n(5.0), n(5.0), n(5.0)]);
}
#[test]
fn table_rename_keeps_a_defined_name_with_the_same_name() {
let mut s = s();
s.execute("range.setValues", json!({"range": "A1", "values": [["Qty"], [7]]})).unwrap();
@@ -3054,6 +3057,9 @@ fn subtotal_109_recalculates_when_row_height_or_autofit_reveals_rows() {
assert_eq!(v(&s, "C1"), Value::Number(9.0));
s.execute("home.autofitRowHeight", json!({"rows": "2:3"})).unwrap();
assert_eq!(v(&s, "C1"), Value::Number(15.0));
}
#[test]
fn paste_special_validation_copies_the_rule() {
let mut s = s();
let ok = |s: &mut Session, cell: &str, input: &str| s.execute("data.validate", json!({"cell": cell, "input": input})).unwrap()["ok"] == true;
+3
View File
@@ -10,6 +10,9 @@ fn quoted_sheet_qualified_name_keeps_quotes() {
assert_eq!(rt("'My Sheet'!Total"), "'My Sheet'!Total");
assert_eq!(rt("'Data'!Total"), "Data!Total");
assert_eq!(rt("'It''s'!Total+1"), "'It''s'!Total+1");
}
#[test]
fn intersection_with_deleted_operand_reparses() {
// Deleting the columns of an intersection operand prints `A1:B2 #REF!`; that text must parse back.
assert_eq!(rt("A1:B2 #REF!"), "A1:B2 #REF!");
+2
View File
@@ -236,6 +236,8 @@ fn every_draw_method_survives_hostile_numbers() {
assert!(!c.contains("NaN") && !c.contains("inf"));
let pdf = d.finish();
check_xref(&pdf);
}
/// Decodes the `<FEFF…>` hex string following `key` in the file bytes.
fn utf16_entry(pdf: &[u8], key: &str) -> String {
let at = find(pdf, key.as_bytes()).unwrap() + key.len();
+6
View File
@@ -391,6 +391,12 @@ pub(super) fn translate(text: &str) -> Option<&'static str> {
"Workbook calculation" => "Izračunavanje radne sveske",
"Enable iterative calculation" => "Omogući iterativno izračunavanje",
"Calculation settings are saved with the workbook." => "Podešavanja izračunavanja se čuvaju sa radnom sveskom.",
"Left Margin" => "Leva margina",
"Right Margin" => "Desna margina",
"Top Margin" => "Gornja margina",
"Bottom Margin" => "Donja margina",
"Header Margin" => "Margina zaglavlja",
"Footer Margin" => "Margina podnožja",
_ => return None,
})
}
+1 -1
View File
@@ -223,7 +223,7 @@ pub(crate) fn dispatch(app: &mut SheetApp, ctx: &egui::Context, key: Key, physic
app.grid.list_picker = Some(active);
} else if sh.has_filter_button(active) {
let at = geo.mirror_pos(crate::grid::filter_button_rect(geo, sh, active).center());
app.grid.filter_menu = Some((active.col, at));
app.grid.filter_menu = Some((active.col, active.row, at));
}
}
Shortcut::Zoom(step) => {
+3
View File
@@ -139,6 +139,9 @@ fn enter_pastes_the_copied_cells_and_ends_copy_mode() {
h.key_press(egui::Key::Enter);
h.run_steps(2);
assert_eq!(h.state().session.active().map(|d| d.selection.active.a1()), Some("D6".to_string()));
}
#[test]
fn clicking_the_ribbon_paste_button_or_its_menu_does_not_freeze() {
// #273: a click on Paste deadlocked the UI thread (a click-state query inside the egui context
// lock), so the frame never returned. Run the clicks on a worker and fail on a timeout instead of
+2 -2
View File
@@ -160,7 +160,7 @@ fn alt_down_opens_autofilter_headers_without_changing_the_workbook() {
let mut h = harness(session);
let before = h.state().session.active().unwrap().wb.clone();
key(&mut h, Key::ArrowDown, Modifiers::ALT);
let (column, at) = h.state().grid.filter_menu.expect("Alt+Down must open the filter header menu");
let (column, _row, at) = h.state().grid.filter_menu.expect("Alt+Down must open the filter header menu");
let d = h.state().session.active().unwrap();
let active = CellRef::parse(cell).unwrap();
assert_eq!(column, active.col);
@@ -210,7 +210,7 @@ fn alt_down_only_opens_enabled_table_filter_buttons() {
session.execute("selection.set", json!({"cell": "B1"})).unwrap();
let mut h = harness(session);
key(&mut h, Key::ArrowDown, Modifiers::ALT);
assert_eq!(h.state().grid.filter_menu.map(|(column, _)| column), enabled.then_some(1));
assert_eq!(h.state().grid.filter_menu.map(|(column, _, _)| column), enabled.then_some(1));
assert_eq!(h.state().session.active().unwrap().selection.active.a1(), "B1");
}
}
+4 -2
View File
@@ -71,7 +71,8 @@ fn opening_and_activating_late_sheets_reveals_their_tabs() {
fn navigation_arrows_reveal_the_selected_tab() {
let mut h = harness(700.0, 0);
let (_, clip) = tab(&h, "Tab00").unwrap();
let next = pos2(clip.left() - 20.0, clip.center().y);
// The Next arrow sits left of the All Sheets button (24 px) and the 8 px gap before the tabs.
let next = pos2(clip.left() - 44.0, clip.center().y);
for _ in 0..29 {
click(&mut h, next);
}
@@ -129,7 +130,8 @@ fn hidden_sheets_long_names_and_window_resize_keep_active_tab_visible() {
h.state_mut().run("sheet.activate", json!({"sheet":29})).unwrap();
h.run_steps(20);
assert_visible(&h, name);
h.set_size(vec2(500.0, 120.0));
// Narrow, but still wide enough for the long tab next to the arrows and the All Sheets button.
h.set_size(vec2(560.0, 120.0));
h.run_steps(20);
assert_visible(&h, name);
h.set_size(vec2(1000.0, 120.0));
+2 -5
View File
@@ -598,11 +598,8 @@ fn read_cf(cx: &mut Ctx<'_>, e: &El, sheet: &mut Sheet) {
let f0 = formulas.first().cloned().unwrap_or_default();
let text = r.attr("text").unwrap_or("").to_string();
// The x14 id that links a data bar to its Excel 2010 extension (solid fill lives there).
let ext_id = r
.child("extLst")
.and_then(|l| l.kids("ext").find_map(|x| x.child("id")))
.map(|i| i.text.trim().to_string())
.filter(|i| !i.is_empty());
let ext_id =
r.child("extLst").and_then(|l| l.kids("ext").find_map(|x| x.child("id"))).map(|i| i.text.trim().to_string()).filter(|i| !i.is_empty());
let rule = match r.attr("type").unwrap_or("") {
"cellIs" => CfRule::CellIs { op: cf_op(r.attr("operator")), a: f0, b: formulas.get(1).cloned(), style },
"expression" => CfRule::Expression { formula: f0, style },
+7 -7
View File
@@ -1096,18 +1096,15 @@ fn default_font_survives_a_round_trip() {
);
let saved = write_xlsx(&read_xlsx(&file).unwrap().0).unwrap();
assert_eq!(font0(&saved), (Some("12".into()), Some("Calibri".into()), Some("2".into()), Some("minor".into())));
}
#[test]
fn solid_data_bar_survives_with_sparklines_and_gradient_neighbours() {
let mut wb = Workbook::new();
let s = wb.sheet_mut(0).unwrap();
s.set_value(at("A1"), Value::Number(1.0));
let bar = |gradient: bool, max: CfValueKind| CfRule::DataBar {
min: CfValueKind::Min,
max,
color: Color::Rgb(0x638EC6),
gradient,
show_value: true,
};
let bar =
|gradient: bool, max: CfValueKind| CfRule::DataBar { min: CfValueKind::Min, max, color: Color::Rgb(0x638EC6), gradient, show_value: true };
let rules = [(true, "A1", CfValueKind::Max), (false, "B1", CfValueKind::Formula("$C$1".into())), (false, "D1:D3", CfValueKind::Number(9.0))];
for (i, (gradient, range, max)) in rules.into_iter().enumerate() {
s.cond_formats.push(CondFormat { ranges: vec![rr(range)], rule: bar(gradient, max), priority: i as u32 + 1, stop_if_true: false });
@@ -1122,6 +1119,9 @@ fn solid_data_bar_survives_with_sparklines_and_gradient_neighbours() {
let b = &back.sheets[0];
assert_eq!(b.cond_formats, wb.sheets[0].cond_formats);
assert_eq!(b.sparklines.len(), 1);
}
#[test]
fn doc_props_keep_boundary_whitespace() {
let mut wb = Workbook::new();
wb.props = DocProps {
+6
View File
@@ -535,6 +535,9 @@ fn formulas_using_a_skipped_name_keep_their_saved_results() {
assert_eq!(v(&wb, 0, "B6"), Value::Number(60.0));
assert_eq!(ftext(&wb, "B7").as_deref(), Some("Local*2"));
assert!(report.warnings.iter().any(|w| w.contains("defined names") && w.contains("saved results")), "{:?}", report.warnings);
}
#[test]
fn reads_iterative_calculation_settings() {
// CalcMode, CalcCount, CalcIter and CalcDelta repeat in every worksheet substream; here only
// the first sheet carries them, so the sheet after it must not reset them to the defaults.
@@ -554,6 +557,9 @@ fn reads_iterative_calculation_settings() {
// Without the records: Excel's defaults, iteration off.
let (wb, _) = read_xls(&compound("Workbook", &sample(false))).unwrap();
assert_eq!(wb.calc, gridcraft_model::CalcSettings::default());
}
#[test]
fn a_format_shorter_than_its_count_keeps_its_characters() {
// A FORMAT whose cch is one more than the characters in the record (seen in real files):
// Excel opens them, the record's characters are the format code.