package excelize import ( "fmt" "math" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestStyleFill(t *testing.T) { cases := []struct { label string format *Style expectFill bool }{{ label: "no_fill", format: &Style{Alignment: &Alignment{WrapText: true}}, expectFill: false, }, { label: "fill", format: &Style{Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"000000"}}}, expectFill: true, }} for _, testCase := range cases { xl := NewFile() styleID, err := xl.NewStyle(testCase.format) assert.NoError(t, err) styles, err := xl.stylesReader() assert.NoError(t, err) style := styles.CellXfs.Xf[styleID] if testCase.expectFill { assert.NotEqual(t, *style.FillID, 0, testCase.label) } else { assert.Equal(t, *style.FillID, 0, testCase.label) } } f := NewFile() styleID1, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"000000"}}}) assert.NoError(t, err) styleID2, err := f.NewStyle(&Style{Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"000000"}}}) assert.NoError(t, err) assert.Equal(t, styleID1, styleID2) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestStyleFill.xlsx"))) } func TestSetConditionalFormat(t *testing.T) { cases := []struct { label string format []ConditionalFormatOptions rules []*xlsxCfRule }{{ label: "3_color_scale", format: []ConditionalFormatOptions{{ Type: "3_color_scale", Criteria: "=", MinType: "num", MidType: "num", MaxType: "num", MinValue: "-10", MidValue: "0", MaxValue: "10", MinColor: "ff0000", MidColor: "00ff00", MaxColor: "0000ff", }}, rules: []*xlsxCfRule{{ Priority: 1, Type: "colorScale", ColorScale: &xlsxColorScale{ Cfvo: []*xlsxCfvo{{ Type: "num", Val: "-10", }, { Type: "num", Val: "0", }, { Type: "num", Val: "10", }}, Color: []*xlsxColor{{ RGB: "FFFF0000", }, { RGB: "FF00FF00", }, { RGB: "FF0000FF", }}, }, }}, }, { label: "3_color_scale default min/mid/max", format: []ConditionalFormatOptions{{ Type: "3_color_scale", Criteria: "=", MinType: "num", MidType: "num", MaxType: "num", MinColor: "ff0000", MidColor: "00ff00", MaxColor: "0000ff", }}, rules: []*xlsxCfRule{{ Priority: 1, Type: "colorScale", ColorScale: &xlsxColorScale{ Cfvo: []*xlsxCfvo{{ Type: "num", Val: "0", }, { Type: "num", Val: "50", }, { Type: "num", Val: "0", }}, Color: []*xlsxColor{{ RGB: "FFFF0000", }, { RGB: "FF00FF00", }, { RGB: "FF0000FF", }}, }, }}, }, { label: "2_color_scale default min/max", format: []ConditionalFormatOptions{{ Type: "2_color_scale", Criteria: "=", MinType: "num", MaxType: "num", MinColor: "ff0000", MaxColor: "0000ff", }}, rules: []*xlsxCfRule{{ Priority: 1, Type: "colorScale", ColorScale: &xlsxColorScale{ Cfvo: []*xlsxCfvo{{ Type: "num", Val: "0", }, { Type: "num", Val: "0", }}, Color: []*xlsxColor{{ RGB: "FFFF0000", }, { RGB: "FF0000FF", }}, }, }}, }} for _, testCase := range cases { f := NewFile() const sheet = "Sheet1" const rangeRef = "A1:A1" assert.NoError(t, f.SetConditionalFormat(sheet, rangeRef, testCase.format)) ws, err := f.workSheetReader(sheet) assert.NoError(t, err) cf := ws.ConditionalFormatting assert.Len(t, cf, 1, testCase.label) assert.Len(t, cf[0].CfRule, 1, testCase.label) assert.Equal(t, rangeRef, cf[0].SQRef, testCase.label) assert.EqualValues(t, testCase.rules, cf[0].CfRule, testCase.label) } // Test creating a conditional format with a solid color data bar style f := NewFile() condFmts := []ConditionalFormatOptions{ {Type: "data_bar", BarColor: "#A9D08E", BarSolid: true, Format: intPtr(0), Criteria: "=", MinType: "min", MaxType: "max"}, } for _, ref := range []string{"A1:A2", "B1:B2"} { assert.NoError(t, f.SetConditionalFormat("Sheet1", ref, condFmts)) } f = NewFile() // Test creating a conditional format without cell reference assert.Equal(t, ErrParameterRequired, f.SetConditionalFormat("Sheet1", "", nil)) // Test creating a conditional format with invalid cell reference assert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat("Sheet1", "A1:A2:A3", nil)) // Test creating a conditional format with existing extension lists ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") assert.True(t, ok) ws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: fmt.Sprintf(``, ExtURISlicerListX14, ExtURISparklineGroups)} assert.NoError(t, f.SetConditionalFormat("Sheet1", "A1:A2", []ConditionalFormatOptions{{Type: "data_bar", Criteria: "=", MinType: "min", MaxType: "max", BarBorderColor: "#0000FF", BarColor: "#638EC6", BarSolid: true}})) f = NewFile() // Test creating a conditional format with invalid extension list characters ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml") assert.True(t, ok) ws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: ""} assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", condFmts), "XML syntax error on line 1: element closed by ") // Test creating a conditional format with invalid icon set style assert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat("Sheet1", "A1:A2", []ConditionalFormatOptions{{Type: "icon_set", IconStyle: "unknown"}})) // Test unsupported conditional formatting rule types assert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat("Sheet1", "A1", []ConditionalFormatOptions{{Type: "unsupported"}})) t.Run("multi_conditional_formatting_rules_priority", func(t *testing.T) { f := NewFile() var condFmts []ConditionalFormatOptions for _, color := range []string{ "#264B96", // Blue "#F9A73E", // Yellow "#006F3C", // Green } { condFmts = append(condFmts, ConditionalFormatOptions{ Type: "data_bar", Criteria: "=", MinType: "num", MaxType: "num", MinValue: "0", MaxValue: "5", BarColor: color, BarSolid: true, }) } assert.NoError(t, f.SetConditionalFormat("Sheet1", "A1:A5", condFmts)) assert.NoError(t, f.SetConditionalFormat("Sheet1", "B1:B5", condFmts)) for r := 1; r <= 20; r++ { cell, err := CoordinatesToCellName(1, r) assert.NoError(t, err) assert.NoError(t, f.SetCellValue("Sheet1", cell, r)) cell, err = CoordinatesToCellName(2, r) assert.NoError(t, err) assert.NoError(t, f.SetCellValue("Sheet1", cell, r)) } ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") assert.True(t, ok) var priorities []int expected := []int{1, 2, 3, 4, 5, 6} for _, condFmt := range ws.(*xlsxWorksheet).ConditionalFormatting { for _, rule := range condFmt.CfRule { priorities = append(priorities, rule.Priority) } } assert.Equal(t, expected, priorities) assert.NoError(t, f.Close()) }) } func TestGetConditionalFormats(t *testing.T) { for _, format := range [][]ConditionalFormatOptions{ {{Type: "cell", Format: intPtr(1), Criteria: "greater than", Value: "6"}}, {{Type: "cell", Format: intPtr(1), Criteria: "between", MinValue: "6", MaxValue: "8"}}, {{Type: "time_period", Format: intPtr(1), Criteria: "yesterday"}}, {{Type: "time_period", Format: intPtr(1), Criteria: "today"}}, {{Type: "time_period", Format: intPtr(1), Criteria: "tomorrow"}}, {{Type: "time_period", Format: intPtr(1), Criteria: "last 7 days"}}, {{Type: "time_period", Format: intPtr(1), Criteria: "last week"}}, {{Type: "time_period", Format: intPtr(1), Criteria: "this week"}}, {{Type: "time_period", Format: intPtr(1), Criteria: "continue week"}}, {{Type: "time_period", Format: intPtr(1), Criteria: "last month"}}, {{Type: "time_period", Format: intPtr(1), Criteria: "this month"}}, {{Type: "time_period", Format: intPtr(1), Criteria: "continue month"}}, {{Type: "text", Format: intPtr(1), Criteria: "containing", Value: "~!@#$%^&*()_+{}|:<>?\"';"}}, {{Type: "text", Format: intPtr(1), Criteria: "not containing", Value: "text"}}, {{Type: "text", Format: intPtr(1), Criteria: "begins with", Value: "prefix"}}, {{Type: "text", Format: intPtr(1), Criteria: "ends with", Value: "suffix"}}, {{Type: "top", Format: intPtr(1), Criteria: "=", Value: "6"}}, {{Type: "bottom", Format: intPtr(1), Criteria: "=", Value: "6"}}, {{Type: "average", AboveAverage: true, Format: intPtr(1), Criteria: "="}}, {{Type: "duplicate", Format: intPtr(1), Criteria: "="}}, {{Type: "unique", Format: intPtr(1), Criteria: "="}}, {{Type: "3_color_scale", Criteria: "=", MinType: "num", MidType: "num", MaxType: "num", MinValue: "-10", MidValue: "50", MaxValue: "10", MinColor: "#FF0000", MidColor: "#00FF00", MaxColor: "#0000FF"}}, {{Type: "2_color_scale", Criteria: "=", MinType: "num", MaxType: "num", MinColor: "#FF0000", MaxColor: "#0000FF"}}, {{Type: "data_bar", Criteria: "=", MinType: "num", MaxType: "num", MinValue: "-10", MaxValue: "10", BarBorderColor: "#0000FF", BarColor: "#638EC6", BarOnly: true, BarSolid: true, StopIfTrue: true}}, {{Type: "data_bar", Criteria: "=", MinType: "min", MaxType: "max", BarBorderColor: "#0000FF", BarColor: "#638EC6", BarDirection: "rightToLeft", BarOnly: true, BarSolid: true, StopIfTrue: true}}, {{Type: "formula", Format: intPtr(1), Criteria: "="}}, {{Type: "blanks", Format: intPtr(1)}}, {{Type: "no_blanks", Format: intPtr(1)}}, {{Type: "errors", Format: intPtr(1)}}, {{Type: "no_errors", Format: intPtr(1)}}, {{Type: "icon_set", IconStyle: "3Arrows", ReverseIcons: true, IconsOnly: true}}, } { f := NewFile() err := f.SetConditionalFormat("Sheet1", "A2:A1,B:B,2:2", format) assert.NoError(t, err) opts, err := f.GetConditionalFormats("Sheet1") assert.NoError(t, err) assert.Equal(t, format, opts["A2:A1 B1:B1048576 A2:XFD2"]) } // Test get multiple conditional formats f := NewFile() expected := []ConditionalFormatOptions{ {Type: "data_bar", Criteria: "=", MinType: "num", MaxType: "num", MinValue: "-10", MaxValue: "10", BarBorderColor: "#0000FF", BarColor: "#638EC6", BarOnly: true, BarSolid: true, StopIfTrue: true}, {Type: "data_bar", Criteria: "=", MinType: "min", MaxType: "max", BarBorderColor: "#0000FF", BarColor: "#638EC6", BarDirection: "rightToLeft", BarOnly: true, BarSolid: false, StopIfTrue: true}, } err := f.SetConditionalFormat("Sheet1", "A1:A2", expected) assert.NoError(t, err) opts, err := f.GetConditionalFormats("Sheet1") assert.NoError(t, err) assert.Equal(t, expected, opts["A1:A2"]) // Test get conditional formats on no exists worksheet f = NewFile() _, err = f.GetConditionalFormats("SheetN") assert.EqualError(t, err, "sheet SheetN does not exist") // Test get conditional formats with invalid sheet name _, err = f.GetConditionalFormats("Sheet:1") assert.Equal(t, ErrSheetNameInvalid, err) } func TestUnsetConditionalFormat(t *testing.T) { f := NewFile() assert.NoError(t, f.SetCellValue("Sheet1", "A1", 7)) assert.NoError(t, f.UnsetConditionalFormat("Sheet1", "A1:A10")) format, err := f.NewConditionalStyle(&Style{Font: &Font{Color: "9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"FEC7CE"}, Pattern: 1}}) assert.NoError(t, err) assert.NoError(t, f.SetConditionalFormat("Sheet1", "A1:A10", []ConditionalFormatOptions{{Type: "cell", Criteria: ">", Format: &format, Value: "6"}})) assert.NoError(t, f.UnsetConditionalFormat("Sheet1", "A1:A10")) // Test unset conditional format on not exists worksheet assert.EqualError(t, f.UnsetConditionalFormat("SheetN", "A1:A10"), "sheet SheetN does not exist") // Test unset conditional format with invalid sheet name assert.Equal(t, ErrSheetNameInvalid, f.UnsetConditionalFormat("Sheet:1", "A1:A10")) // Save spreadsheet by the given path assert.NoError(t, f.SaveAs(filepath.Join("test", "TestUnsetConditionalFormat.xlsx"))) } func TestNewStyle(t *testing.T) { f := NewFile() for i := 0; i < 18; i++ { _, err := f.NewStyle(&Style{ Fill: Fill{Type: "gradient", Color: []string{"FFFFFF", "4E71BE"}, Shading: i}, }) assert.NoError(t, err) } f = NewFile() styleID, err := f.NewStyle(&Style{Font: &Font{Bold: true, Italic: true, Family: "Times New Roman", Size: 36, Color: "777777"}}) assert.NoError(t, err) styles, err := f.stylesReader() assert.NoError(t, err) fontID := styles.CellXfs.Xf[styleID].FontID font := styles.Fonts.Font[*fontID] assert.Contains(t, *font.Name.Val, "Times New Roman", "Stored font should contain font name") assert.Equal(t, 2, styles.CellXfs.Count, "Should have 2 styles") _, err = f.NewStyle(&Style{}) assert.NoError(t, err) _, err = f.NewStyle(nil) assert.NoError(t, err) // Test gradient fills f = NewFile() styleID1, err := f.NewStyle(&Style{Fill: Fill{Type: "gradient", Color: []string{"FFFFFF", "4E71BE"}, Shading: 1, Pattern: 1}}) assert.NoError(t, err) styleID2, err := f.NewStyle(&Style{Fill: Fill{Type: "gradient", Color: []string{"FF0000", "4E71BE"}, Shading: 1, Pattern: 1}}) assert.NoError(t, err) assert.NotEqual(t, styleID1, styleID2) var exp string f = NewFile() _, err = f.NewStyle(&Style{CustomNumFmt: &exp}) assert.Equal(t, ErrCustomNumFmt, err) _, err = f.NewStyle(&Style{Font: &Font{Family: strings.Repeat("s", MaxFontFamilyLength+1)}}) assert.Equal(t, ErrFontLength, err) _, err = f.NewStyle(&Style{Font: &Font{Size: MaxFontSize + 1}}) assert.Equal(t, ErrFontSize, err) // Test create numeric custom style numFmt := "####;####" f.Styles.NumFmts = nil styleID, err = f.NewStyle(&Style{ CustomNumFmt: &numFmt, }) assert.NoError(t, err) assert.Equal(t, 1, styleID) assert.NotNil(t, f.Styles) assert.NotNil(t, f.Styles.CellXfs) assert.NotNil(t, f.Styles.CellXfs.Xf) nf := f.Styles.CellXfs.Xf[styleID] assert.Equal(t, 164, *nf.NumFmtID) // Test create currency custom style f.Styles.NumFmts = nil styleID, err = f.NewStyle(&Style{ NumFmt: 32, // must not be in currencyNumFmt }) assert.NoError(t, err) assert.Equal(t, 2, styleID) assert.NotNil(t, f.Styles) assert.NotNil(t, f.Styles.CellXfs) assert.NotNil(t, f.Styles.CellXfs.Xf) nf = f.Styles.CellXfs.Xf[styleID] assert.Equal(t, 32, *nf.NumFmtID) // Test set build-in scientific number format styleID, err = f.NewStyle(&Style{NumFmt: 11}) assert.NoError(t, err) assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "B1", styleID)) assert.NoError(t, f.SetSheetRow("Sheet1", "A1", &[]float64{1.23, 1.234})) rows, err := f.GetRows("Sheet1") assert.NoError(t, err) assert.Equal(t, [][]string{{"1.23E+00", "1.23E+00"}}, rows) f = NewFile() // Test currency number format customNumFmt := "[$$-409]#,##0.00" style1, err := f.NewStyle(&Style{CustomNumFmt: &customNumFmt}) assert.NoError(t, err) style2, err := f.NewStyle(&Style{NumFmt: 165}) assert.NoError(t, err) assert.Equal(t, style1, style2) style3, err := f.NewStyle(&Style{NumFmt: 166}) assert.NoError(t, err) assert.Equal(t, 2, style3) f = NewFile() f.Styles.NumFmts = nil f.Styles.CellXfs.Xf = nil style4, err := f.NewStyle(&Style{NumFmt: 160}) assert.NoError(t, err) assert.Equal(t, 0, style4) f = NewFile() f.Styles.NumFmts = nil f.Styles.CellXfs.Xf = nil style5, err := f.NewStyle(&Style{NumFmt: 160}) assert.NoError(t, err) assert.Equal(t, 0, style5) // Test create style with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) _, err = f.NewStyle(&Style{NumFmt: 165}) assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") // Test create cell styles reach maximum f = NewFile() f.Styles.CellXfs.Xf = make([]xlsxXf, MaxCellStyles) f.Styles.CellXfs.Count = MaxCellStyles _, err = f.NewStyle(&Style{NumFmt: 0}) assert.Equal(t, ErrCellStyles, err) } func TestConditionalStyle(t *testing.T) { f := NewFile() expected := &Style{Protection: &Protection{Hidden: true, Locked: true}} idx, err := f.NewConditionalStyle(expected) assert.NoError(t, err) style, err := f.GetConditionalStyle(idx) assert.NoError(t, err) assert.Equal(t, expected, style) _, err = f.NewConditionalStyle(&Style{DecimalPlaces: intPtr(4), NumFmt: 165, NegRed: true}) assert.NoError(t, err) _, err = f.NewConditionalStyle(&Style{DecimalPlaces: intPtr(-1)}) assert.NoError(t, err) expected = &Style{NumFmt: 1} idx, err = f.NewConditionalStyle(expected) assert.NoError(t, err) style, err = f.GetConditionalStyle(idx) assert.NoError(t, err) assert.Equal(t, expected.NumFmt, style.NumFmt) assert.Zero(t, *style.DecimalPlaces) _, err = f.NewConditionalStyle(&Style{NumFmt: 27}) assert.NoError(t, err) numFmt := "general" _, err = f.NewConditionalStyle(&Style{CustomNumFmt: &numFmt}) assert.NoError(t, err) numFmt1 := "0.00" _, err = f.NewConditionalStyle(&Style{CustomNumFmt: &numFmt1}) assert.NoError(t, err) // Test create conditional style with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) _, err = f.NewConditionalStyle(&Style{Font: &Font{Color: "9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"FEC7CE"}, Pattern: 1}}) assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") // Test get conditional style with invalid style index _, err = f.GetConditionalStyle(1) assert.Equal(t, newInvalidStyleID(1), err) // Test get conditional style with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) _, err = f.GetConditionalStyle(1) assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") f = NewFile() // Test get conditional style with background color and empty pattern type idx, err = f.NewConditionalStyle(&Style{Fill: Fill{Type: "pattern", Color: []string{"FEC7CE"}, Pattern: 1}}) assert.NoError(t, err) f.Styles.Dxfs.Dxfs[0].Fill.PatternFill.PatternType = "" f.Styles.Dxfs.Dxfs[0].Fill.PatternFill.FgColor = nil f.Styles.Dxfs.Dxfs[0].Fill.PatternFill.BgColor = &xlsxColor{Theme: intPtr(6)} style, err = f.GetConditionalStyle(idx) assert.NoError(t, err) assert.Equal(t, "pattern", style.Fill.Type) assert.Equal(t, []string{"A5A5A5"}, style.Fill.Color) } func TestGetDefaultFont(t *testing.T) { f := NewFile() s, err := f.GetDefaultFont() assert.NoError(t, err) assert.Equal(t, s, "Calibri", "Default font should be Calibri") // Test get default font with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) _, err = f.GetDefaultFont() assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") } func TestSetDefaultFont(t *testing.T) { f := NewFile() assert.NoError(t, f.SetDefaultFont("Arial")) styles, err := f.stylesReader() assert.NoError(t, err) s, err := f.GetDefaultFont() assert.NoError(t, err) assert.Equal(t, s, "Arial", "Default font should change to Arial") assert.Equal(t, *styles.CellStyles.CellStyle[0].CustomBuiltIn, true) // Test set default font with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) assert.EqualError(t, f.SetDefaultFont("Arial"), "XML syntax error on line 1: invalid UTF-8") } func TestStylesReader(t *testing.T) { f := NewFile() // Test read styles with unsupported charset f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) styles, err := f.stylesReader() assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") assert.EqualValues(t, new(xlsxStyleSheet), styles) } func TestThemeReader(t *testing.T) { f := NewFile() // Test read theme with unsupported charset f.Pkg.Store(defaultXMLPathTheme, MacintoshCyrillicCharset) theme, err := f.themeReader() assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") assert.EqualValues(t, &decodeTheme{}, theme) } func TestSetCellStyle(t *testing.T) { f := NewFile() // Test set cell style on not exists worksheet assert.EqualError(t, f.SetCellStyle("SheetN", "A1", "A2", 1), "sheet SheetN does not exist") // Test set cell style with invalid style ID assert.Equal(t, newInvalidStyleID(-1), f.SetCellStyle("Sheet1", "A1", "A2", -1)) // Test set cell style with not exists style ID assert.Equal(t, newInvalidStyleID(10), f.SetCellStyle("Sheet1", "A1", "A2", 10)) // Test set cell style with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) assert.EqualError(t, f.SetCellStyle("Sheet1", "A1", "A2", 1), "XML syntax error on line 1: invalid UTF-8") } func TestGetStyleID(t *testing.T) { f := NewFile() styleID, err := f.getStyleID(&xlsxStyleSheet{}, nil) assert.NoError(t, err) assert.Equal(t, -1, styleID) // Test get style ID with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) _, err = f.getStyleID(&xlsxStyleSheet{ CellXfs: &xlsxCellXfs{}, Fonts: &xlsxFonts{ Font: []*xlsxFont{{}}, }, }, &Style{NumFmt: 0, Font: &Font{}}) assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") } func TestGetFillID(t *testing.T) { styles, err := NewFile().stylesReader() assert.NoError(t, err) assert.Equal(t, -1, getFillID(styles, &Style{Fill: Fill{Type: "unknown"}})) } func TestThemeColor(t *testing.T) { for _, clr := range [][]string{ {"FF000000", ThemeColor("000000", -0.1)}, {"FF000000", ThemeColor("000000", 0)}, {"FF33FF33", ThemeColor("00FF00", 0.2)}, {"FFFFFFFF", ThemeColor("000000", 1)}, {"FFFFFFFF", ThemeColor(strings.Repeat(string(rune(math.MaxUint8+1)), 6), 1)}, {"FFFFFFFF", ThemeColor(strings.Repeat(string(rune(-1)), 6), 1)}, } { assert.Equal(t, clr[0], clr[1]) } } func TestGetNumFmtID(t *testing.T) { f := NewFile() fs1, err := parseFormatStyleSet(&Style{Protection: &Protection{Hidden: false, Locked: false}, NumFmt: 10}) assert.NoError(t, err) id1 := getNumFmtID(&xlsxStyleSheet{}, fs1) fs2, err := parseFormatStyleSet(&Style{Protection: &Protection{Hidden: false, Locked: false}, NumFmt: 0}) assert.NoError(t, err) id2 := getNumFmtID(&xlsxStyleSheet{}, fs2) assert.NotEqual(t, id1, id2) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestStyleNumFmt.xlsx"))) } func TestGetThemeColor(t *testing.T) { assert.Empty(t, (&File{}).getThemeColor(&xlsxColor{})) f := NewFile() assert.Empty(t, f.getThemeColor(nil)) var theme int assert.Equal(t, "FFFFFF", f.getThemeColor(&xlsxColor{Theme: &theme})) assert.Equal(t, "FFFFFF", f.getThemeColor(&xlsxColor{RGB: "FFFFFF"})) assert.Equal(t, "FF8080", f.getThemeColor(&xlsxColor{Indexed: 2, Tint: 0.5})) assert.Empty(t, f.getThemeColor(&xlsxColor{Indexed: len(IndexedColorMapping), Tint: 0.5})) clr := &decodeCTColor{} assert.Nil(t, clr.colorChoice()) } func TestGetStyle(t *testing.T) { f := NewFile() expected := &Style{ Border: []Border{ {Type: "left", Color: "0000FF", Style: 3}, {Type: "right", Color: "FF0000", Style: 6}, {Type: "top", Color: "00FF00", Style: 4}, {Type: "bottom", Color: "FFFF00", Style: 5}, {Type: "diagonalUp", Color: "A020F0", Style: 7}, {Type: "diagonalDown", Color: "A020F0", Style: 7}, }, Fill: Fill{Type: "gradient", Shading: 16, Color: []string{"0000FF", "00FF00"}}, Font: &Font{ Bold: true, Italic: true, Underline: "single", Family: "Arial", Size: 8.5, Strike: true, Color: "777777", ColorIndexed: 1, ColorTint: 0.1, }, Alignment: &Alignment{ Horizontal: "center", Indent: 1, JustifyLastLine: true, ReadingOrder: 1, RelativeIndent: 1, ShrinkToFit: true, TextRotation: 180, Vertical: "center", WrapText: true, }, Protection: &Protection{Hidden: true, Locked: true}, NumFmt: 49, } styleID, err := f.NewStyle(expected) assert.NoError(t, err) style, err := f.GetStyle(styleID) assert.NoError(t, err) assert.Equal(t, expected.Border, style.Border) assert.Equal(t, expected.Fill, style.Fill) assert.Equal(t, expected.Font, style.Font) assert.Equal(t, expected.Alignment, style.Alignment) assert.Equal(t, expected.Protection, style.Protection) assert.Equal(t, expected.NumFmt, style.NumFmt) assert.Nil(t, style.DecimalPlaces) expected = &Style{ Fill: Fill{Type: "pattern", Pattern: 1, Color: []string{"0000FF"}}, } styleID, err = f.NewStyle(expected) assert.NoError(t, err) style, err = f.GetStyle(styleID) assert.NoError(t, err) assert.Equal(t, expected.Fill, style.Fill) assert.Nil(t, style.DecimalPlaces) expected = &Style{NumFmt: 2} styleID, err = f.NewStyle(expected) assert.NoError(t, err) style, err = f.GetStyle(styleID) assert.NoError(t, err) assert.Equal(t, expected.NumFmt, style.NumFmt) assert.Equal(t, 2, *style.DecimalPlaces) expected = &Style{NumFmt: 27} styleID, err = f.NewStyle(expected) assert.NoError(t, err) style, err = f.GetStyle(styleID) assert.NoError(t, err) assert.Equal(t, expected.NumFmt, style.NumFmt) assert.Nil(t, style.DecimalPlaces) expected = &Style{NumFmt: 165} styleID, err = f.NewStyle(expected) assert.NoError(t, err) style, err = f.GetStyle(styleID) assert.NoError(t, err) assert.Equal(t, expected.NumFmt, style.NumFmt) assert.Equal(t, 2, *style.DecimalPlaces) decimal := 4 expected = &Style{NumFmt: 165, DecimalPlaces: &decimal, NegRed: true} styleID, err = f.NewStyle(expected) assert.NoError(t, err) style, err = f.GetStyle(styleID) assert.NoError(t, err) assert.Equal(t, 0, style.NumFmt) assert.Equal(t, *expected.DecimalPlaces, *style.DecimalPlaces) assert.Equal(t, "[$$-409]#,##0.0000;[Red][$$-409]#,##0.0000", *style.CustomNumFmt) for _, val := range [][]interface{}{ {"$#,##0", 0}, {"$#,##0.0", 1}, {"_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(@_)", 0}, {"_($* #,##000_);_($* (#,##000);_($* \"-\"_);_(@_)", 0}, {"_($* #,##0.0000_);_($* (#,##0.0000);_($* \"-\"????_);_(@_)", 4}, } { numFmtCode := val[0].(string) expected = &Style{CustomNumFmt: &numFmtCode} styleID, err = f.NewStyle(expected) assert.NoError(t, err) style, err = f.GetStyle(styleID) assert.NoError(t, err) assert.Equal(t, val[1].(int), *style.DecimalPlaces, numFmtCode) } for _, val := range []string{ ";$#,##0", ";$#,##0;", ";$#,##0.0", ";$#,##0.0;", "$#,##0;0.0", "_($* #,##0_);;_($* \"-\"_);_(@_)", "_($* #,##0.0_);_($* (#,##0.00);_($* \"-\"_);_(@_)", } { expected = &Style{CustomNumFmt: &val} styleID, err = f.NewStyle(expected) assert.NoError(t, err) style, err = f.GetStyle(styleID) assert.NoError(t, err) assert.Nil(t, style.DecimalPlaces) } // Test get style with custom color index f.Styles.Colors = &xlsxStyleColors{ IndexedColors: &xlsxIndexedColors{ RgbColor: []xlsxColor{{RGB: "FF012345"}}, }, } assert.Equal(t, "012345", f.getThemeColor(&xlsxColor{Indexed: 0})) f.Styles.Fonts.Font[0].U = &attrValString{} f.Styles.CellXfs.Xf[0].FontID = intPtr(0) style, err = f.GetStyle(styleID) assert.NoError(t, err) assert.Equal(t, "single", style.Font.Underline) // Test get style with invalid style index style, err = f.GetStyle(-1) assert.Nil(t, style) assert.Equal(t, err, newInvalidStyleID(-1)) // Test get style with unsupported charset style sheet f.Styles = nil f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset) style, err = f.GetStyle(1) assert.Nil(t, style) assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") }