package excelize import ( "fmt" "math/rand" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestAddPivotTable(t *testing.T) { f := NewFile() // Create some data in a sheet month := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} year := []int{2017, 2018, 2019} types := []string{"Meat", "Dairy", "Beverages", "Produce"} region := []string{"East", "West", "North", "South"} assert.NoError(t, f.SetSheetRow("Sheet1", "A1", &[]string{"Month", "Year", "Type", "Sales", "Region"})) for row := 2; row < 32; row++ { assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("A%d", row), month[rand.Intn(12)])) assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("B%d", row), year[rand.Intn(3)])) assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("C%d", row), types[rand.Intn(4)])) assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("D%d", row), rand.Intn(5000))) assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("E%d", row), region[rand.Intn(4)])) } assert.NoError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "Sheet1!$G$2:$M$34", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Filter: []PivotTableField{{Data: "Region"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "Sum", Name: "Summarize by Sum"}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, ShowRowHeaders: true, ShowColHeaders: true, ShowLastColumn: true, ShowError: true, })) // Use different order of coordinate tests assert.NoError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "Sheet1!$U$34:$O$2", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "Average", Name: "Summarize by Average"}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, ShowRowHeaders: true, ShowColHeaders: true, ShowLastColumn: true, })) assert.NoError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "Sheet1!$W$2:$AC$34", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Region"}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "Count", Name: "Summarize by Count"}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, ShowRowHeaders: true, ShowColHeaders: true, ShowLastColumn: true, })) assert.NoError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "Sheet1!$G$37:$W$50", Rows: []PivotTableField{{Data: "Month"}}, Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Year"}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "CountNums", Name: "Summarize by CountNums"}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, ShowRowHeaders: true, ShowColHeaders: true, ShowLastColumn: true, })) assert.NoError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "Sheet1!$AE$2:$AG$33", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "Max", Name: "Summarize by Max"}, {Data: "Sales", Subtotal: "Average", Name: "Average of Sales"}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, ShowRowHeaders: true, ShowColHeaders: true, ShowLastColumn: true, })) // Create pivot table with empty subtotal field name and specified style assert.NoError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "Sheet1!$AJ$2:$AP1$35", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Filter: []PivotTableField{{Data: "Region"}}, Columns: []PivotTableField{}, Data: []PivotTableField{{Subtotal: "Sum", Name: "Summarize by Sum"}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, ShowRowHeaders: true, ShowColHeaders: true, ShowLastColumn: true, PivotTableStyleName: "PivotStyleLight19", })) f.NewSheet("Sheet2") assert.NoError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "Sheet2!$A$1:$AR$15", Rows: []PivotTableField{{Data: "Month"}}, Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Type", DefaultSubtotal: true}, {Data: "Year"}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "Min", Name: "Summarize by Min"}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, ShowRowHeaders: true, ShowColHeaders: true, ShowLastColumn: true, })) assert.NoError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "Sheet2!$A$18:$AR$54", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Type"}}, Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Year"}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "Product", Name: "Summarize by Product"}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, ShowRowHeaders: true, ShowColHeaders: true, ShowLastColumn: true, })) // Create pivot table with many data, many rows, many cols and defined name assert.NoError(t, f.SetDefinedName(&DefinedName{ Name: "dataRange", RefersTo: "Sheet1!$A$1:$E$31", Comment: "Pivot Table Data Range", Scope: "Sheet2", })) assert.NoError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "dataRange", PivotTableRange: "Sheet2!$A$57:$AJ$91", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Type"}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "Sum", Name: "Sum of Sales"}, {Data: "Sales", Subtotal: "Average", Name: "Average of Sales"}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, ShowRowHeaders: true, ShowColHeaders: true, ShowLastColumn: true, })) // Test empty pivot table options assert.EqualError(t, f.AddPivotTable(nil), ErrParameterRequired.Error()) // Test invalid data range assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$A$1", PivotTableRange: "Sheet1!$U$34:$O$2", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales"}}, }), `parameter 'DataRange' parsing error: parameter is invalid`) // Test the data range of the worksheet that is not declared assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "$A$1:$E$31", PivotTableRange: "Sheet1!$U$34:$O$2", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales"}}, }), `parameter 'DataRange' parsing error: parameter is invalid`) // Test the worksheet declared in the data range does not exist assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "SheetN!$A$1:$E$31", PivotTableRange: "Sheet1!$U$34:$O$2", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales"}}, }), "sheet SheetN is not exist") // Test the pivot table range of the worksheet that is not declared assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "$U$34:$O$2", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales"}}, }), `parameter 'PivotTableRange' parsing error: parameter is invalid`) // Test the worksheet declared in the pivot table range does not exist assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "SheetN!$U$34:$O$2", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales"}}, }), "sheet SheetN is not exist") // Test not exists worksheet in data range assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "SheetN!$A$1:$E$31", PivotTableRange: "Sheet1!$U$34:$O$2", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales"}}, }), "sheet SheetN is not exist") // Test invalid row number in data range assert.EqualError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$0:$E$31", PivotTableRange: "Sheet1!$U$34:$O$2", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales"}}, }), `parameter 'DataRange' parsing error: cannot convert cell "A0" to coordinates: invalid cell name "A0"`) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPivotTable1.xlsx"))) // Test with field names that exceed the length limit and invalid subtotal assert.NoError(t, f.AddPivotTable(&PivotTableOption{ DataRange: "Sheet1!$A$1:$E$31", PivotTableRange: "Sheet1!$G$2:$M$34", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales", Subtotal: "-", Name: strings.Repeat("s", MaxFieldLength+1)}}, })) // Test adjust range with invalid range _, _, err := f.adjustRange("") assert.EqualError(t, err, ErrParameterRequired.Error()) // Test adjust range with incorrect range _, _, err = f.adjustRange("sheet1!") assert.EqualError(t, err, "parameter is invalid") // Test get pivot fields order with empty data range _, err = f.getPivotFieldsOrder(&PivotTableOption{}) assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`) // Test add pivot cache with empty data range assert.EqualError(t, f.addPivotCache("", &PivotTableOption{}), "parameter 'DataRange' parsing error: parameter is required") // Test add pivot cache with invalid data range assert.EqualError(t, f.addPivotCache("", &PivotTableOption{ DataRange: "$A$1:$E$31", PivotTableRange: "Sheet1!$U$34:$O$2", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales"}}, }), "parameter 'DataRange' parsing error: parameter is invalid") // Test add pivot table with empty options assert.EqualError(t, f.addPivotTable(0, 0, "", &PivotTableOption{}), "parameter 'PivotTableRange' parsing error: parameter is required") // Test add pivot table with invalid data range assert.EqualError(t, f.addPivotTable(0, 0, "", &PivotTableOption{}), "parameter 'PivotTableRange' parsing error: parameter is required") // Test add pivot fields with empty data range assert.EqualError(t, f.addPivotFields(nil, &PivotTableOption{ DataRange: "$A$1:$E$31", PivotTableRange: "Sheet1!$U$34:$O$2", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, Data: []PivotTableField{{Data: "Sales"}}, }), `parameter 'DataRange' parsing error: parameter is invalid`) // Test get pivot fields index with empty data range _, err = f.getPivotFieldsIndex([]PivotTableField{}, &PivotTableOption{}) assert.EqualError(t, err, `parameter 'DataRange' parsing error: parameter is required`) } func TestAddPivotRowFields(t *testing.T) { f := NewFile() // Test invalid data range assert.EqualError(t, f.addPivotRowFields(&xlsxPivotTableDefinition{}, &PivotTableOption{ DataRange: "Sheet1!$A$1:$A$1", }), `parameter 'DataRange' parsing error: parameter is invalid`) } func TestAddPivotPageFields(t *testing.T) { f := NewFile() // Test invalid data range assert.EqualError(t, f.addPivotPageFields(&xlsxPivotTableDefinition{}, &PivotTableOption{ DataRange: "Sheet1!$A$1:$A$1", }), `parameter 'DataRange' parsing error: parameter is invalid`) } func TestAddPivotDataFields(t *testing.T) { f := NewFile() // Test invalid data range assert.EqualError(t, f.addPivotDataFields(&xlsxPivotTableDefinition{}, &PivotTableOption{ DataRange: "Sheet1!$A$1:$A$1", }), `parameter 'DataRange' parsing error: parameter is invalid`) } func TestAddPivotColFields(t *testing.T) { f := NewFile() // Test invalid data range assert.EqualError(t, f.addPivotColFields(&xlsxPivotTableDefinition{}, &PivotTableOption{ DataRange: "Sheet1!$A$1:$A$1", Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, }), `parameter 'DataRange' parsing error: parameter is invalid`) } func TestGetPivotFieldsOrder(t *testing.T) { f := NewFile() // Test get pivot fields order with not exist worksheet _, err := f.getPivotFieldsOrder(&PivotTableOption{DataRange: "SheetN!$A$1:$E$31"}) assert.EqualError(t, err, "sheet SheetN is not exist") } func TestGetPivotTableFieldName(t *testing.T) { f := NewFile() f.getPivotTableFieldName("-", []PivotTableField{}) }