"""Sheet-name translation must not break references. openpyxl does not rewrite references on rename — cell formulas, defined names and validations pointing at a translated sheet would break (#REF!). These tests cover the full rename + rewrite path. """ from openpyxl import Workbook, load_workbook from openpyxl.workbook.defined_name import DefinedName from translators.excel_translator import ExcelTranslator class _FrToEn: """Minimal legacy-style provider: French sheet name → English.""" def translate_batch(self, texts, target_language, source_language="auto"): mapping = { "Ventes": "Sales", "Données": "Data", "Rapport des ventes": "Sales report", "Total": "Total", } return [mapping.get(t, t) for t in texts] def _make_workbook(path): wb = Workbook() ws = wb.active ws.title = "Ventes" ws["A1"] = "Rapport des ventes" ws["A2"] = "Total" ws["A3"] = 10 ws["A4"] = 20 # Cross-sheet formula (unquoted name) ws2 = wb.create_sheet("Données") ws2["A1"] = "=SUM(Ventes!A3:A4)" # Quoted name (name would need quotes if it had spaces — keep simple here) ws2["A2"] = "=Ventes!A3+Ventes!A4" # Defined name pointing at the renamed sheet wb.defined_names["TotalVentes"] = DefinedName( "TotalVentes", attr_text="'Ventes'!$A$3" ) wb.save(path) return wb class TestSheetRenameReferences: def test_cell_formulas_and_defined_names_rewritten(self, tmp_path): from pathlib import Path src = tmp_path / "in.xlsx" out = tmp_path / "out.xlsx" _make_workbook(src) translator = ExcelTranslator(provider=_FrToEn()) translator.translate_file(Path(src), Path(out), "en", "fr") wb = load_workbook(out) data = wb["Data"] assert data["A1"].value == "=SUM(Sales!A3:A4)", data["A1"].value assert data["A2"].value == "=Sales!A3+Sales!A4", data["A2"].value # Defined name follows the rename dn = wb.defined_names["TotalVentes"] assert "Sales" in (dn.attr_text or ""), dn.attr_text assert "Ventes" not in (dn.attr_text or "") # The renamed sheet actually exists under its new name assert "Sales" in wb.sheetnames assert "Ventes" not in wb.sheetnames def test_3d_and_multiple_refs(self, tmp_path): mapping = {"Sheet1": "Feuille1", "Sheet2": "Feuille2"} formula = "=SUM(Sheet1!A1:Sheet2!B2)+Sheet1!C3" out = ExcelTranslator._rewrite_sheet_refs_in_formula(formula, mapping) assert out == "=SUM(Feuille1!A1:Feuille2!B2)+Feuille1!C3" def test_quoted_refs_and_prefix_safety(self, tmp_path): mapping = {"Ventes": "Sales", "Ventes 2026": "Sales 2026"} out = ExcelTranslator._rewrite_sheet_refs_in_formula( "=SUM('Ventes 2026'!A1:A2)+'Ventes'!B1", mapping ) # "Ventes 2026" (longest first) keeps its quotes (name with space); # "Sales" needs no quotes so the canonical unquoted form is emitted. assert out == "=SUM('Sales 2026'!A1:A2)+Sales!B1" def test_non_sheet_bang_not_touched(self, tmp_path): mapping = {"Ventes": "Sales"} # "Total!A1" is not a renamed sheet — must stay untouched out = ExcelTranslator._rewrite_sheet_refs_in_formula("=Total!A1", mapping) assert out == "=Total!A1"