diff --git a/pandas/tests/io/excel/test_openpyxl.py b/pandas/tests/io/excel/test_openpyxl.py index 5b4bbb9e686d3..ef793ad08077b 100644 --- a/pandas/tests/io/excel/test_openpyxl.py +++ b/pandas/tests/io/excel/test_openpyxl.py @@ -18,6 +18,12 @@ openpyxl = pytest.importorskip("openpyxl") +# xfail marker for pending autofilter feature; see #62994 +xfail_autofilter = pytest.mark.xfail( + reason="Excel header autofilter not yet implemented on main; see #62994", + strict=False, +) + @pytest.fixture def ext(): @@ -155,6 +161,90 @@ def test_engine_kwargs_append_data_only(tmp_excel, data_only, expected): ) +@xfail_autofilter +def test_to_excel_autofilter_openpyxl(tmp_excel): + # Ensure that writing with autofilter=True sets auto_filter.ref + df = DataFrame({"A": [1, 2], "B": [3, 4]}) + df.to_excel(tmp_excel, engine="openpyxl", index=False, autofilter=True) + + with contextlib.closing(openpyxl.load_workbook(tmp_excel)) as wb: + ws = wb[wb.sheetnames[0]] + # Expect filter over the full range, e.g. A1:B3 (header + 2 rows) + assert ws.auto_filter is not None + assert ws.auto_filter.ref is not None + # Verify filter covers all columns (A and B) + assert "A" in ws.auto_filter.ref + assert "B" in ws.auto_filter.ref + + +@xfail_autofilter +def test_to_excel_autofilter_startrow_startcol_openpyxl(tmp_excel): + # Test autofilter with nonzero startrow and startcol + df = DataFrame({"A": [1, 2], "B": [3, 4]}) + df.to_excel( + tmp_excel, + engine="openpyxl", + index=False, + autofilter=True, + startrow=2, + startcol=1, + ) + + with contextlib.closing(openpyxl.load_workbook(tmp_excel)) as wb: + ws = wb[wb.sheetnames[0]] + assert ws.auto_filter is not None + assert ws.auto_filter.ref is not None + # Filter should be offset by startrow=2 and startcol=1 (B3:D5) + assert ws.auto_filter.ref.startswith("B") + assert "3" in ws.auto_filter.ref + + +@xfail_autofilter +def test_to_excel_autofilter_multiindex_merge_cells_openpyxl(tmp_excel): + # Test autofilter with MultiIndex columns and merge_cells=True + df = DataFrame( + [[1, 2, 3, 4], [5, 6, 7, 8]], + columns=pd.MultiIndex.from_tuples( + [("A", "a"), ("A", "b"), ("B", "a"), ("B", "b")] + ), + ) + df.to_excel( + tmp_excel, + engine="openpyxl", + index=False, + autofilter=True, + merge_cells=True, + ) + + with contextlib.closing(openpyxl.load_workbook(tmp_excel)) as wb: + ws = wb[wb.sheetnames[0]] + assert ws.auto_filter is not None + assert ws.auto_filter.ref is not None + + +@xfail_autofilter +def test_to_excel_autofilter_multiindex_no_merge_openpyxl(tmp_excel): + # Test autofilter with MultiIndex columns and merge_cells=False + df = DataFrame( + [[1, 2, 3, 4], [5, 6, 7, 8]], + columns=pd.MultiIndex.from_tuples( + [("A", "a"), ("A", "b"), ("B", "a"), ("B", "b")] + ), + ) + df.to_excel( + tmp_excel, + engine="openpyxl", + index=False, + autofilter=True, + merge_cells=False, + ) + + with contextlib.closing(openpyxl.load_workbook(tmp_excel)) as wb: + ws = wb[wb.sheetnames[0]] + assert ws.auto_filter is not None + assert ws.auto_filter.ref is not None + + @pytest.mark.parametrize("kwarg_name", ["read_only", "data_only"]) @pytest.mark.parametrize("kwarg_value", [True, False]) def test_engine_kwargs_append_reader(datapath, ext, kwarg_name, kwarg_value): diff --git a/pandas/tests/io/excel/test_xlsxwriter.py b/pandas/tests/io/excel/test_xlsxwriter.py index b2e6c845e5019..510c604ce4eaf 100644 --- a/pandas/tests/io/excel/test_xlsxwriter.py +++ b/pandas/tests/io/excel/test_xlsxwriter.py @@ -3,12 +3,19 @@ import pytest +import pandas as pd from pandas import DataFrame from pandas.io.excel import ExcelWriter xlsxwriter = pytest.importorskip("xlsxwriter") +# xfail marker for pending autofilter feature; see #62994 +xfail_autofilter = pytest.mark.xfail( + reason="Excel header autofilter not yet implemented on main; see #62994", + strict=False, +) + @pytest.fixture def ext(): @@ -84,3 +91,103 @@ def test_book_and_sheets_consistent(tmp_excel): assert writer.sheets == {} sheet = writer.book.add_worksheet("test_name") assert writer.sheets == {"test_name": sheet} + + +@xfail_autofilter +def test_to_excel_autofilter_xlsxwriter(tmp_excel): + openpyxl = pytest.importorskip("openpyxl") + + df = DataFrame({"A": [1, 2], "B": [3, 4]}) + # Write with xlsxwriter, verify via openpyxl that an autofilter exists + df.to_excel(tmp_excel, engine="xlsxwriter", index=False, autofilter=True) + + wb = openpyxl.load_workbook(tmp_excel) + try: + ws = wb[wb.sheetnames[0]] + assert ws.auto_filter is not None + assert ws.auto_filter.ref is not None + # Verify filter covers all columns (A and B) + assert "A" in ws.auto_filter.ref + assert "B" in ws.auto_filter.ref + finally: + wb.close() + + +@xfail_autofilter +def test_to_excel_autofilter_startrow_startcol_xlsxwriter(tmp_excel): + openpyxl = pytest.importorskip("openpyxl") + + df = DataFrame({"A": [1, 2], "B": [3, 4]}) + df.to_excel( + tmp_excel, + engine="xlsxwriter", + index=False, + autofilter=True, + startrow=2, + startcol=1, + ) + + wb = openpyxl.load_workbook(tmp_excel) + try: + ws = wb[wb.sheetnames[0]] + assert ws.auto_filter is not None + assert ws.auto_filter.ref is not None + # Filter should be offset by startrow=2 and startcol=1 (B3:D5) + assert ws.auto_filter.ref.startswith("B") + assert "3" in ws.auto_filter.ref + finally: + wb.close() + + +@xfail_autofilter +def test_to_excel_autofilter_multiindex_merge_cells_xlsxwriter(tmp_excel): + openpyxl = pytest.importorskip("openpyxl") + + df = DataFrame( + [[1, 2, 3, 4], [5, 6, 7, 8]], + columns=pd.MultiIndex.from_tuples( + [("A", "a"), ("A", "b"), ("B", "a"), ("B", "b")] + ), + ) + df.to_excel( + tmp_excel, + engine="xlsxwriter", + index=False, + autofilter=True, + merge_cells=True, + ) + + wb = openpyxl.load_workbook(tmp_excel) + try: + ws = wb[wb.sheetnames[0]] + assert ws.auto_filter is not None + assert ws.auto_filter.ref is not None + finally: + wb.close() + + +@xfail_autofilter +def test_to_excel_autofilter_multiindex_no_merge_xlsxwriter(tmp_excel): + openpyxl = pytest.importorskip("openpyxl") + + df = DataFrame( + [[1, 2, 3, 4], [5, 6, 7, 8]], + columns=pd.MultiIndex.from_tuples( + [("A", "a"), ("A", "b"), ("B", "a"), ("B", "b")] + ), + ) + df.to_excel( + tmp_excel, + engine="xlsxwriter", + index=False, + autofilter=True, + merge_cells=False, + ) + + wb = openpyxl.load_workbook(tmp_excel) + try: + ws = wb[wb.sheetnames[0]] + assert ws.auto_filter is not None + assert ws.auto_filter.ref is not None + finally: + wb.close()