147 lines
6.3 KiB
Rust
147 lines
6.3 KiB
Rust
use gridcraft_core::{CellRef, Value};
|
|
use gridcraft_engine::Session;
|
|
use serde_json::{Value as Json, json};
|
|
|
|
fn workbook() -> Session {
|
|
let mut s = Session::new();
|
|
s.new_workbook();
|
|
s
|
|
}
|
|
|
|
fn run(s: &mut Session, command: &str, params: Json) {
|
|
s.execute(command, params).unwrap();
|
|
}
|
|
|
|
fn set(s: &mut Session, cell: &str, input: &str) {
|
|
run(s, "cell.set", json!({"cell": cell, "input": input}));
|
|
}
|
|
|
|
fn activate(s: &mut Session, sheet: &str) {
|
|
run(s, "sheet.activate", json!({"sheet": sheet}));
|
|
}
|
|
|
|
fn value(s: &Session, sheet: &str, cell: &str) -> Value {
|
|
let wb = &s.doc().unwrap().wb;
|
|
wb.sheet(wb.sheet_index(sheet).unwrap()).unwrap().value(CellRef::parse(cell).unwrap())
|
|
}
|
|
|
|
fn formula(s: &Session, sheet: &str, cell: &str) -> String {
|
|
let wb = &s.doc().unwrap().wb;
|
|
wb.sheet(wb.sheet_index(sheet).unwrap()).unwrap().cell(CellRef::parse(cell).unwrap()).unwrap().formula.as_ref().unwrap().text.clone()
|
|
}
|
|
|
|
fn cut_to(s: &mut Session, source: &str, range: &str, destination: &str, at: &str) {
|
|
activate(s, source);
|
|
run(s, "edit.cut", json!({"range": range}));
|
|
activate(s, destination);
|
|
run(s, "edit.paste", json!({"at": at}));
|
|
}
|
|
|
|
#[test]
|
|
fn cross_sheet_cut_preserves_total_through_undo_redo_and_xlsx() {
|
|
let mut s = workbook();
|
|
run(&mut s, "range.setValues", json!({"range": "A1", "values": [[10], [20]]}));
|
|
set(&mut s, "B1", "=SUM(A1:A2)");
|
|
run(&mut s, "home.insertSheet", json!({"name": "Sheet2"}));
|
|
cut_to(&mut s, "Sheet1", "A1:A2", "Sheet2", "C3");
|
|
|
|
let assert_moved = |s: &Session| {
|
|
assert_eq!(value(s, "Sheet1", "A1"), Value::Empty);
|
|
assert_eq!(value(s, "Sheet1", "A2"), Value::Empty);
|
|
assert_eq!(value(s, "Sheet2", "C3"), Value::Number(10.0));
|
|
assert_eq!(value(s, "Sheet2", "C4"), Value::Number(20.0));
|
|
assert_eq!(value(s, "Sheet1", "B1"), Value::Number(30.0));
|
|
assert_eq!(formula(s, "Sheet1", "B1"), "SUM(Sheet2!C3:C4)");
|
|
};
|
|
assert_moved(&s);
|
|
run(&mut s, "edit.undo", json!({}));
|
|
assert_eq!(value(&s, "Sheet1", "A1"), Value::Number(10.0));
|
|
assert_eq!(value(&s, "Sheet1", "A2"), Value::Number(20.0));
|
|
assert_eq!(value(&s, "Sheet2", "C3"), Value::Empty);
|
|
assert_eq!(value(&s, "Sheet2", "C4"), Value::Empty);
|
|
assert_eq!(value(&s, "Sheet1", "B1"), Value::Number(30.0));
|
|
assert_eq!(formula(&s, "Sheet1", "B1"), "SUM(A1:A2)");
|
|
run(&mut s, "edit.redo", json!({}));
|
|
assert_moved(&s);
|
|
|
|
let saved = s.execute("file.saveBytes", json!({"format": "xlsx"})).unwrap();
|
|
run(&mut s, "file.open", json!({"name": "moved.xlsx", "base64": saved["base64"]}));
|
|
assert_moved(&s);
|
|
}
|
|
|
|
#[test]
|
|
fn references_on_all_sheets_follow_cut_but_destination_local_refs_do_not() {
|
|
let mut s = workbook();
|
|
run(&mut s, "sheet.rename", json!({"name": "Source Data"}));
|
|
run(&mut s, "range.setValues", json!({"range": "A1", "values": [[10], [20]]}));
|
|
set(&mut s, "B1", "=SUM($A$1:$A$2)");
|
|
run(&mut s, "formulas.defineName", json!({"name": "MovedData", "refersTo": "='Source Data'!$A$1:$A$2"}));
|
|
run(&mut s, "home.insertSheet", json!({"name": "Target's Data"}));
|
|
run(&mut s, "range.setValues", json!({"range": "A1", "values": [[100], [200]]}));
|
|
set(&mut s, "B1", "=SUM('Source Data'!$A$1:$A$2)");
|
|
set(&mut s, "B2", "=SUM(A1:A2)");
|
|
run(&mut s, "home.insertSheet", json!({"name": "Summary"}));
|
|
set(&mut s, "A1", "='Source Data'!$A$1+'Source Data'!A$2");
|
|
set(&mut s, "A2", "=SUM(MovedData)");
|
|
cut_to(&mut s, "Source Data", "A1:A2", "Target's Data", "C3");
|
|
|
|
for (sheet, cell) in [("Source Data", "B1"), ("Target's Data", "B1"), ("Summary", "A1"), ("Summary", "A2")] {
|
|
assert_eq!(value(&s, sheet, cell), Value::Number(30.0), "{sheet}!{cell}");
|
|
}
|
|
assert_eq!(formula(&s, "Source Data", "B1"), "SUM('Target''s Data'!$C$3:$C$4)");
|
|
assert_eq!(formula(&s, "Summary", "A1"), "'Target''s Data'!$C$3+'Target''s Data'!C$4");
|
|
assert_eq!(value(&s, "Target's Data", "B2"), Value::Number(300.0));
|
|
assert_eq!(formula(&s, "Target's Data", "B2"), "SUM(A1:A2)");
|
|
|
|
// Recalculation must keep tracking the destination after the move, not just retain a cache.
|
|
activate(&mut s, "Target's Data");
|
|
set(&mut s, "C3", "40");
|
|
for (sheet, cell) in [("Source Data", "B1"), ("Target's Data", "B1"), ("Summary", "A1"), ("Summary", "A2")] {
|
|
assert_eq!(value(&s, sheet, cell), Value::Number(60.0), "{sheet}!{cell}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn moved_formulas_keep_external_source_refs_and_move_internal_refs_once() {
|
|
let mut s = workbook();
|
|
run(&mut s, "sheet.rename", json!({"name": "Source Data"}));
|
|
set(&mut s, "A1", "10");
|
|
set(&mut s, "D1", "7");
|
|
set(&mut s, "A2", "=$A$1+$D$1+'Source Data'!A1");
|
|
set(&mut s, "A3", "=A2*2");
|
|
run(&mut s, "home.insertSheet", json!({"name": "Destination"}));
|
|
set(&mut s, "D1", "1000");
|
|
// Overlapping coordinates on different sheets catch accidental double adjustment.
|
|
cut_to(&mut s, "Source Data", "A1:A3", "Destination", "A2");
|
|
|
|
assert_eq!(value(&s, "Destination", "A2"), Value::Number(10.0));
|
|
assert_eq!(value(&s, "Destination", "A3"), Value::Number(27.0));
|
|
assert_eq!(value(&s, "Destination", "A4"), Value::Number(54.0));
|
|
assert_eq!(value(&s, "Source Data", "D1"), Value::Number(7.0));
|
|
assert!(formula(&s, "Destination", "A3").contains("'Source Data'!$D$1"));
|
|
for cell in ["A1", "A2", "A3"] {
|
|
assert_eq!(value(&s, "Source Data", cell), Value::Empty);
|
|
}
|
|
activate(&mut s, "Source Data");
|
|
set(&mut s, "D1", "8");
|
|
assert_eq!(value(&s, "Destination", "A3"), Value::Number(28.0));
|
|
assert_eq!(value(&s, "Destination", "A4"), Value::Number(56.0));
|
|
}
|
|
|
|
#[test]
|
|
fn same_sheet_overlapping_cut_still_updates_references_once() {
|
|
let mut s = workbook();
|
|
run(&mut s, "range.setValues", json!({"range": "A1", "values": [[10], [20]]}));
|
|
set(&mut s, "B1", "=SUM(A1:A2)");
|
|
set(&mut s, "A3", "=A1+A2");
|
|
run(&mut s, "edit.cut", json!({"range": "A1:A3"}));
|
|
run(&mut s, "edit.paste", json!({"at": "A2"}));
|
|
assert_eq!(value(&s, "Sheet1", "A1"), Value::Empty);
|
|
assert_eq!(value(&s, "Sheet1", "A2"), Value::Number(10.0));
|
|
assert_eq!(value(&s, "Sheet1", "A3"), Value::Number(20.0));
|
|
assert_eq!(value(&s, "Sheet1", "A4"), Value::Number(30.0));
|
|
assert_eq!(value(&s, "Sheet1", "B1"), Value::Number(30.0));
|
|
assert_eq!(formula(&s, "Sheet1", "A4"), "A2+A3");
|
|
assert_eq!(formula(&s, "Sheet1", "B1"), "SUM(A2:A3)");
|
|
}
|