From 1cce634d24867d74e3842fef0f57c1cb892ca162 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 15 May 2025 12:56:28 +0800 Subject: [PATCH] feat: Add row reading and dimension retrieval functionalities for Excel sheets - Implemented `excel.sheet.rows` to read specified rows from a sheet with pagination support. - Added `excel.sheet.dimension` to retrieve the number of rows and columns in a sheet. - Enhanced the Excel processing logic to handle new functionalities and updated related tests. - Updated README with examples for the new features. --- excel/README.md | 31 ++++ excel/process.go | 58 ++++++- excel/process_test.go | 356 +++++++++++++++++++++++++++++++++++++++--- excel/sheet.go | 145 +++++++++++++++++ excel/sheet_test.go | 165 ++++++++++++++++++++ 5 files changed, 730 insertions(+), 25 deletions(-) diff --git a/excel/README.md b/excel/README.md index e31122db..d42cdf7d 100644 --- a/excel/README.md +++ b/excel/README.md @@ -142,6 +142,37 @@ Process("excel.sheet.delete", h, "Sheet1Copy"); const exists: boolean = Process("excel.sheet.exists", h, "Sheet1"); ``` +#### Read sheet rows with pagination + +```typescript +/** + * Reads rows from a sheet with pagination support + * @param handle - Handle ID from excel.open + * @param name - Sheet name + * @param start - Starting row index (0-based) + * @param size - Number of rows to read + * @returns string[][] - Two-dimensional array of cell values + */ +const rows: string[][] = Process("excel.sheet.rows", h, "Sheet1", 0, 10); // Read first 10 rows +``` + +#### Get sheet dimensions + +```typescript +/** + * Gets the dimensions (number of rows and columns) of a sheet + * @param handle - Handle ID from excel.open + * @param name - Sheet name + * @returns {rows: number, cols: number} - Object containing row and column counts + */ +const dim: { rows: number; cols: number } = Process( + "excel.sheet.dimension", + h, + "Sheet1" +); +console.log(`Sheet has ${dim.rows} rows and ${dim.cols} columns`); +``` + ### Example: Sheet Operations Workflow ```typescript diff --git a/excel/process.go b/excel/process.go index f827dd86..bba40fa6 100644 --- a/excel/process.go +++ b/excel/process.go @@ -13,13 +13,15 @@ func init() { "save": processSave, "sheets": processSheets, - "sheet.create": processCreateSheet, - "sheet.read": processReadSheet, - "sheet.update": processUpdateSheet, - "sheet.delete": processDeleteSheet, - "sheet.copy": processCopySheet, - "sheet.list": processListSheets, - "sheet.exists": processSheetExists, + "sheet.create": processCreateSheet, + "sheet.read": processReadSheet, + "sheet.update": processUpdateSheet, + "sheet.delete": processDeleteSheet, + "sheet.copy": processCopySheet, + "sheet.list": processListSheets, + "sheet.exists": processSheetExists, + "sheet.rows": processReadSheetRows, + "sheet.dimension": processGetSheetDimension, "read.cell": processReadCell, "read.row": processReadRow, @@ -736,3 +738,45 @@ func processSheetExists(process *process.Process) interface{} { return xls.SheetExists(name) } + +// processReadSheetRows process the excel.sheet.rows +func processReadSheetRows(process *process.Process) interface{} { + process.ValidateArgNums(4) + handle := process.ArgsString(0) + name := process.ArgsString(1) + start := process.ArgsInt(2) + size := process.ArgsInt(3) + + xls, err := Get(handle) + if err != nil { + exception.New("excel.sheet.rows %s error: %s", 500, handle, err.Error()).Throw() + } + + data, err := xls.ReadSheetRows(name, start, size) + if err != nil { + exception.New("excel.sheet.rows %s:%s error: %s", 500, handle, name, err.Error()).Throw() + } + return data +} + +// processGetSheetDimension process the excel.sheet.dimension +func processGetSheetDimension(process *process.Process) interface{} { + process.ValidateArgNums(2) + handle := process.ArgsString(0) + name := process.ArgsString(1) + + xls, err := Get(handle) + if err != nil { + exception.New("excel.sheet.dimension %s error: %s", 500, handle, err.Error()).Throw() + } + + rows, cols, err := xls.GetSheetDimension(name) + if err != nil { + exception.New("excel.sheet.dimension %s:%s error: %s", 500, handle, name, err.Error()).Throw() + } + + return map[string]int{ + "rows": rows, + "cols": cols, + } +} diff --git a/excel/process_test.go b/excel/process_test.go index 479827dc..5d1e201c 100644 --- a/excel/process_test.go +++ b/excel/process_test.go @@ -1,6 +1,7 @@ package excel import ( + "fmt" "os" "path/filepath" "testing" @@ -531,36 +532,38 @@ func TestProcessWriteOperations(t *testing.T) { t.Fatal(err) } - // Test write row - p, err = process.Of("excel.write.row", handle, "供销存管理表格", "A1", []interface{}{"Test1", "Test2", "Test3"}) + // Create test data + testData := make([]interface{}, 0) + testData = append(testData, []interface{}{"Header1", "Header2", "Header3"}) + testData = append(testData, []interface{}{1, "Row1", true}) + testData = append(testData, []interface{}{2, "Row2", false}) + testData = append(testData, []interface{}{3, "Row3", true}) + testData = append(testData, []interface{}{4, "Row4", false}) + testData = append(testData, []interface{}{5, "Row5", true}) + testData = append(testData, []interface{}{6, "Row6", false}) + testData = append(testData, []interface{}{7, "Row7", true}) + testData = append(testData, []interface{}{8, "Row8", false}) + testData = append(testData, []interface{}{9, "Row9", true}) + + // Create a new sheet and write test data + p, err = process.Of("excel.sheet.create", handle, "RowTestSheet") + if err != nil { + t.Fatal(err) + } + _, err = p.Exec() if err != nil { t.Fatal(err) } - _, err = p.Exec() - assert.NoError(t, err) - - // Test write column - p, err = process.Of("excel.write.column", handle, "供销存管理表格", "B1", []interface{}{"Col1", "Col2", "Col3"}) + p, err = process.Of("excel.write.all", handle, "RowTestSheet", "A1", testData) if err != nil { t.Fatal(err) } - _, err = p.Exec() - assert.NoError(t, err) - - // Test write all - p, err = process.Of("excel.write.all", handle, "供销存管理表格", "C1", [][]interface{}{ - {"All1", "All2", "All3"}, - {"All4", "All5", "All6"}, - }) if err != nil { t.Fatal(err) } - _, err = p.Exec() - assert.NoError(t, err) - // Save and close p, err = process.Of("excel.save", handle) if err != nil { @@ -916,3 +919,320 @@ func TestProcessSheetOperations(t *testing.T) { _, err = p.Exec() assert.NoError(t, err) } + +func TestProcessReadSheetRows(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + + files := testFiles(t) + + // Create a new test file path + dataRoot := config.Conf.DataRoot + newFile := filepath.Join(filepath.Dir(files["test-01"]), "test-read-rows.xlsx") + + // Copy test-01.xlsx to new file + content, err := os.ReadFile(filepath.Join(dataRoot, files["test-01"])) + if err != nil { + t.Fatal(err) + } + err = os.WriteFile(filepath.Join(dataRoot, newFile), content, 0644) + if err != nil { + t.Fatal(err) + } + defer os.Remove(filepath.Join(dataRoot, newFile)) // Clean up after test + + // Open file in write mode + p, err := process.Of("excel.open", newFile, false) + if err != nil { + t.Fatal(err) + } + + handle, err := p.Exec() + if err != nil { + t.Fatal(err) + } + + // Create test data + testData := make([]interface{}, 0) + testData = append(testData, []interface{}{"Header1", "Header2", "Header3"}) + testData = append(testData, []interface{}{1, "Row1", true}) + testData = append(testData, []interface{}{2, "Row2", false}) + testData = append(testData, []interface{}{3, "Row3", true}) + testData = append(testData, []interface{}{4, "Row4", false}) + testData = append(testData, []interface{}{5, "Row5", true}) + testData = append(testData, []interface{}{6, "Row6", false}) + testData = append(testData, []interface{}{7, "Row7", true}) + testData = append(testData, []interface{}{8, "Row8", false}) + testData = append(testData, []interface{}{9, "Row9", true}) + + // Create a new sheet and write test data + p, err = process.Of("excel.sheet.create", handle, "RowTestSheet") + if err != nil { + t.Fatal(err) + } + _, err = p.Exec() + if err != nil { + t.Fatal(err) + } + + p, err = process.Of("excel.write.all", handle, "RowTestSheet", "A1", testData) + if err != nil { + t.Fatal(err) + } + _, err = p.Exec() + if err != nil { + t.Fatal(err) + } + + // Save and close the file + p, err = process.Of("excel.save", handle) + if err != nil { + t.Fatal(err) + } + _, err = p.Exec() + if err != nil { + t.Fatal(err) + } + + p, err = process.Of("excel.close", handle) + if err != nil { + t.Fatal(err) + } + _, err = p.Exec() + if err != nil { + t.Fatal(err) + } + + // Reopen the file for reading + p, err = process.Of("excel.open", newFile, true) + if err != nil { + t.Fatal(err) + } + handle, err = p.Exec() + if err != nil { + t.Fatal(err) + } + + // Test cases for reading rows + t.Run("ReadFromMiddle", func(t *testing.T) { + p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", 2, 4) + assert.NoError(t, err) + data, err := p.Exec() + assert.NoError(t, err) + rows := data.([][]string) + assert.Equal(t, 4, len(rows)) + assert.Equal(t, "2", rows[0][0]) // First row should be row 2 + }) + + t.Run("ReadFromBeginning", func(t *testing.T) { + p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", 0, 3) + assert.NoError(t, err) + data, err := p.Exec() + assert.NoError(t, err) + rows := data.([][]string) + assert.Equal(t, 3, len(rows)) + assert.Equal(t, "Header1", rows[0][0]) // First row should be header + }) + + t.Run("ReadBeyondAvailable", func(t *testing.T) { + p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", 8, 5) + assert.NoError(t, err) + data, err := p.Exec() + assert.NoError(t, err) + rows := data.([][]string) + assert.Equal(t, 2, len(rows)) // Only 2 rows remain + }) + + t.Run("ReadNonExistentSheet", func(t *testing.T) { + p, err := process.Of("excel.sheet.rows", handle, "NonExistentSheet", 0, 5) + assert.NoError(t, err) + _, err = p.Exec() + assert.Error(t, err) + }) + + t.Run("ReadWithSizeZero", func(t *testing.T) { + p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", 0, 0) + assert.NoError(t, err) + data, err := p.Exec() + assert.NoError(t, err) + rows := data.([][]string) + assert.Equal(t, 0, len(rows)) + }) + + t.Run("ReadWithNegativeStart", func(t *testing.T) { + p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", -1, 5) + assert.NoError(t, err) + _, err = p.Exec() + assert.Error(t, err) + }) + + t.Run("ReadWithNegativeSize", func(t *testing.T) { + p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", 0, -1) + assert.NoError(t, err) + _, err = p.Exec() + assert.Error(t, err) + }) + + // Save and close + p, err = process.Of("excel.save", handle) + if err != nil { + t.Fatal(err) + } + _, err = p.Exec() + assert.NoError(t, err) + + p, err = process.Of("excel.close", handle) + if err != nil { + t.Fatal(err) + } + _, err = p.Exec() + assert.NoError(t, err) +} + +func TestProcessGetSheetDimension(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + + files := testFiles(t) + + // Create a new test file path + dataRoot := config.Conf.DataRoot + newFile := filepath.Join(filepath.Dir(files["test-01"]), "test-dimension.xlsx") + + // Copy test-01.xlsx to new file + content, err := os.ReadFile(filepath.Join(dataRoot, files["test-01"])) + if err != nil { + t.Fatal(err) + } + err = os.WriteFile(filepath.Join(dataRoot, newFile), content, 0644) + if err != nil { + t.Fatal(err) + } + defer os.Remove(filepath.Join(dataRoot, newFile)) // Clean up after test + + // Open file in write mode + p, err := process.Of("excel.open", newFile, false) + if err != nil { + t.Fatal(err) + } + + handle, err := p.Exec() + if err != nil { + t.Fatal(err) + } + + // Test 1: Empty sheet + t.Run("EmptySheet", func(t *testing.T) { + // Create empty sheet + p, err := process.Of("excel.sheet.create", handle, "EmptySheet") + assert.NoError(t, err) + _, err = p.Exec() + assert.NoError(t, err) + + // Get dimensions + p, err = process.Of("excel.sheet.dimension", handle, "EmptySheet") + assert.NoError(t, err) + dim, err := p.Exec() + assert.NoError(t, err) + + // Verify dimensions + dimMap := dim.(map[string]int) + assert.Equal(t, 0, dimMap["rows"]) + assert.Equal(t, 0, dimMap["cols"]) + }) + + // Test 2: Sheet with data + t.Run("SheetWithData", func(t *testing.T) { + + // Create test data + testData := make([]interface{}, 0) + testData = append(testData, []interface{}{"A1", "B1", "C1"}) + testData = append(testData, []interface{}{"A2", "B2", "C2"}) + testData = append(testData, []interface{}{"A3", "B3", "C3"}) + + // Create and write to sheet + p, err := process.Of("excel.sheet.create", handle, "DataSheet") + assert.NoError(t, err) + _, err = p.Exec() + assert.NoError(t, err) + + p, err = process.Of("excel.write.all", handle, "DataSheet", "A1", testData) + assert.NoError(t, err) + _, err = p.Exec() + assert.NoError(t, err) + + // Save to ensure dimensions are updated + p, err = process.Of("excel.save", handle) + assert.NoError(t, err) + _, err = p.Exec() + assert.NoError(t, err) + + // Get dimensions + p, err = process.Of("excel.sheet.dimension", handle, "DataSheet") + assert.NoError(t, err) + dim, err := p.Exec() + assert.NoError(t, err) + + // Verify dimensions + dimMap := dim.(map[string]int) + assert.Equal(t, 3, dimMap["rows"]) + assert.Equal(t, 3, dimMap["cols"]) + }) + + // Test 3: Non-existent sheet + t.Run("NonExistentSheet", func(t *testing.T) { + p, err := process.Of("excel.sheet.dimension", handle, "NonExistentSheet") + assert.NoError(t, err) + _, err = p.Exec() + assert.Error(t, err) + }) + + // Test 4: Large sheet + t.Run("LargeSheet", func(t *testing.T) { + // Create large test data (100x50) + largeData := make([]interface{}, 0) + for i := 0; i < 100; i++ { + row := make([]interface{}, 50) + for j := 0; j < 50; j++ { + row[j] = fmt.Sprintf("Cell_%d_%d", i, j) + } + largeData = append(largeData, row) + } + + // Create and write to sheet + p, err := process.Of("excel.sheet.create", handle, "LargeSheet") + assert.NoError(t, err) + _, err = p.Exec() + assert.NoError(t, err) + + p, err = process.Of("excel.write.all", handle, "LargeSheet", "A1", largeData) + assert.NoError(t, err) + _, err = p.Exec() + assert.NoError(t, err) + + // Save to ensure dimensions are updated + p, err = process.Of("excel.save", handle) + assert.NoError(t, err) + _, err = p.Exec() + assert.NoError(t, err) + + // Get dimensions + p, err = process.Of("excel.sheet.dimension", handle, "LargeSheet") + assert.NoError(t, err) + dim, err := p.Exec() + assert.NoError(t, err) + + // Verify dimensions + dimMap := dim.(map[string]int) + assert.Equal(t, 100, dimMap["rows"]) + assert.Equal(t, 50, dimMap["cols"]) + }) + + // Clean up + p, err = process.Of("excel.close", handle) + if err != nil { + t.Fatal(err) + } + _, err = p.Exec() + assert.NoError(t, err) +} diff --git a/excel/sheet.go b/excel/sheet.go index e78595ad..e0929dca 100644 --- a/excel/sheet.go +++ b/excel/sheet.go @@ -76,6 +76,151 @@ func (excel *Excel) ReadSheet(name string) ([][]interface{}, error) { return result, nil } +// GetSheetDimension returns the number of rows and columns in a sheet +func (excel *Excel) GetSheetDimension(name string) (rows int, cols int, err error) { + // Check if sheet exists + if idx, _ := excel.GetSheetIndex(name); idx == -1 { + return 0, 0, fmt.Errorf("sheet %s does not exist", name) + } + rows = 0 + cols = 0 + ri, err := excel.File.Rows(name) + if err != nil { + return 0, 0, err + } + defer ri.Close() + for ri.Next() { + rows++ + row, err := ri.Columns() + if err != nil { + return 0, 0, err + } + if len(row) > cols { + cols = max(cols, len(row)) + } + } + return rows, cols, nil + + // // Get dimension directly using excelize API + // dimension, err := excel.File.GetSheetDimension(name) + // if err != nil { + // return 0, 0, err + // } + + // // If sheet is not set, use row and column count + // if dimension == "A1" { + // rows = 0 + // cols = 0 + // ri, err := excel.File.Rows(name) + // if err != nil { + // return 0, 0, err + // } + // defer ri.Close() + // for ri.Next() { + // rows++ + // row, err := ri.Columns() + // if err != nil { + // return 0, 0, err + // } + // if len(row) > cols { + // cols = max(cols, len(row)) + // } + // } + // return rows, cols, nil + // } + + // // If sheet is empty + // if dimension == "" || dimension == "A1" { + // return 0, 0, nil + // } + + // // For single cell case, add the range suffix + // if !strings.Contains(dimension, ":") { + // dimension = fmt.Sprintf("%s:%s", dimension, dimension) + // } + + // // Split dimension into start and end coordinates + // parts := strings.Split(dimension, ":") + // if len(parts) != 2 { + // return 0, 0, fmt.Errorf("invalid dimension format: %s", dimension) + // } + + // // Convert end coordinate to row and column numbers + // endCol, endRow, err := excelize.CellNameToCoordinates(parts[1]) + // if err != nil { + // return 0, 0, err + // } + + // // Convert start coordinate to row and column numbers + // startCol, startRow, err := excelize.CellNameToCoordinates(parts[0]) + // if err != nil { + // return 0, 0, err + // } + + // // Calculate actual dimensions + // rows = endRow - startRow + 1 + // cols = endCol - startCol + 1 + + // return rows, cols, nil +} + +// ReadSheetRows reads all data from a sheet by rows +func (excel *Excel) ReadSheetRows(name string, start int, size int) ([][]string, error) { + // Validate parameters + if start < 0 { + return nil, fmt.Errorf("start position cannot be negative") + } + if size < 0 { + return nil, fmt.Errorf("size cannot be negative") + } + + // Check if sheet exists + if idx, _ := excel.GetSheetIndex(name); idx == -1 { + return nil, fmt.Errorf("sheet %s does not exist", name) + } + + // If size is 0, return empty slice + if size == 0 { + return [][]string{}, nil + } + + // Get rows iterator + rows, err := excel.File.Rows(name) + if err != nil { + return nil, err + } + defer rows.Close() + + // Skip to start position + currentRow := -1 + for rows.Next() { + currentRow++ + if currentRow >= start { + break + } + } + + // Read requested number of rows + result := make([][]string, 0, size) + if currentRow == start { + row, err := rows.Columns() + if err != nil { + return nil, err + } + result = append(result, row) + } + + for i := 1; i < size && rows.Next(); i++ { + row, err := rows.Columns() + if err != nil { + return nil, err + } + result = append(result, row) + } + + return result, nil +} + // UpdateSheet updates an existing sheet with new data // If the sheet doesn't exist, it will be created func (excel *Excel) UpdateSheet(name string, data [][]interface{}) error { diff --git a/excel/sheet_test.go b/excel/sheet_test.go index a6b1d5cc..e6cdccab 100644 --- a/excel/sheet_test.go +++ b/excel/sheet_test.go @@ -1,6 +1,8 @@ package excel import ( + "fmt" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -131,4 +133,167 @@ func TestSheetOperations(t *testing.T) { err = excel.DeleteSheet("NonExistentSheet") assert.Error(t, err) }) + + // Test ReadSheetRows + t.Run("ReadSheetRows", func(t *testing.T) { + // Create test data with 10 rows + testData := [][]interface{}{ + {"Header1", "Header2", "Header3"}, + {1, "Row1", true}, + {2, "Row2", false}, + {3, "Row3", true}, + {4, "Row4", false}, + {5, "Row5", true}, + {6, "Row6", false}, + {7, "Row7", true}, + {8, "Row8", false}, + {9, "Row9", true}, + } + + // Expected string data + expectedData := [][]string{ + {"Header1", "Header2", "Header3"}, + {"1", "Row1", "TRUE"}, + {"2", "Row2", "FALSE"}, + {"3", "Row3", "TRUE"}, + {"4", "Row4", "FALSE"}, + {"5", "Row5", "TRUE"}, + {"6", "Row6", "FALSE"}, + {"7", "Row7", "TRUE"}, + {"8", "Row8", "FALSE"}, + {"9", "Row9", "TRUE"}, + } + + // Create a new sheet for testing + _, err := excel.CreateSheet("RowTestSheet") + assert.NoError(t, err) + + // Write test data + err = excel.WriteAll("RowTestSheet", "A1", testData) + assert.NoError(t, err) + + // Test 1: Read from middle (start at row 2, read 4 rows) + data, err := excel.ReadSheetRows("RowTestSheet", 2, 4) + assert.NoError(t, err) + assert.Equal(t, 4, len(data)) + assert.Equal(t, expectedData[2:6], data) + + // Test 2: Read from beginning (start at row 0, read 3 rows) + data, err = excel.ReadSheetRows("RowTestSheet", 0, 3) + assert.NoError(t, err) + assert.Equal(t, 3, len(data)) + assert.Equal(t, expectedData[0:3], data) + + // Test 3: Read beyond available rows (should return remaining rows) + data, err = excel.ReadSheetRows("RowTestSheet", 8, 5) + assert.NoError(t, err) + assert.Equal(t, 2, len(data)) // Only 2 rows remain + assert.Equal(t, expectedData[8:], data) + + // Test 4: Read from non-existent sheet + _, err = excel.ReadSheetRows("NonExistentSheet", 0, 5) + assert.Error(t, err) + + // Test 5: Read with size 0 (should return empty slice) + data, err = excel.ReadSheetRows("RowTestSheet", 0, 0) + assert.NoError(t, err) + assert.Equal(t, 0, len(data)) + + // Test 6: Read with negative start (should return error) + _, err = excel.ReadSheetRows("RowTestSheet", -1, 5) + assert.Error(t, err) + + // Test 7: Read with negative size (should return error) + _, err = excel.ReadSheetRows("RowTestSheet", 0, -1) + assert.Error(t, err) + }) + + // Test GetSheetDimension + t.Run("GetSheetDimension", TestGetSheetDimension) +} + +// TestGetSheetDimension tests the GetSheetDimension function +func TestGetSheetDimension(t *testing.T) { + // Get test files and open the test file + files := testFiles(t) + filename := filepath.Dir(files["test-01"]) + "/test-dimension.xlsx" + handler, err := Open(filename, true) // Open in writable mode + if err != nil { + t.Fatal(err) + } + defer Close(handler) + + excel, err := Get(handler) + if err != nil { + t.Fatal(err) + } + + // Clean up existing sheets + sheets := excel.ListSheets() + for _, sheet := range sheets { + if sheet != "Sheet1" { // Keep the default sheet + err = excel.DeleteSheet(sheet) + assert.NoError(t, err) + } + } + + // Test 1: Create a large sheet (100x100) + _, err = excel.CreateSheet("LargeSheet") + assert.NoError(t, err) + + // Create test data (100x100) + largeData := make([][]interface{}, 100) + for i := 0; i < 100; i++ { + largeData[i] = make([]interface{}, 100) + for j := 0; j < 100; j++ { + largeData[i][j] = fmt.Sprintf("Cell_%d_%d", i, j) + } + } + err = excel.WriteAll("LargeSheet", "A1", largeData) + assert.NoError(t, err) + + // Save file to ensure dimensions are updated + err = excel.Save() + assert.NoError(t, err) + + // Test large sheet dimensions + rows, cols, err := excel.GetSheetDimension("LargeSheet") + assert.NoError(t, err) + assert.Equal(t, 100, rows) + assert.Equal(t, 100, cols) + + // Test 2: Empty sheet + if excel.SheetExists("EmptySheet") { + excel.DeleteSheet("EmptySheet") + } + _, err = excel.CreateSheet("EmptySheet") + assert.NoError(t, err) + rows, cols, err = excel.GetSheetDimension("EmptySheet") + assert.NoError(t, err) + assert.Equal(t, 0, rows) + assert.Equal(t, 0, cols) + + // Test 3: Regular sheet with data + testData := [][]interface{}{ + {"A1", "B1", "C1"}, + {"A2", "B2", "C2"}, + {"A3", "B3", "C3"}, + } + err = excel.WriteAll("RegularSheet", "A1", testData) + assert.NoError(t, err) + + // Save file to ensure dimensions are updated + err = excel.Save() + assert.NoError(t, err) + + rows, cols, err = excel.GetSheetDimension("RegularSheet") + assert.NoError(t, err) + assert.Equal(t, 3, rows) + assert.Equal(t, 3, cols) + + // Test 4: Non-existent sheet + rows, cols, err = excel.GetSheetDimension("NonExistentSheet") + assert.Error(t, err) + assert.Equal(t, 0, rows) + assert.Equal(t, 0, cols) }