Support update data validations on inserting/deleting columns/rows
This commit is contained in:
parent
e014a8bb23
commit
c7acf4fafe
66
adjust.go
66
adjust.go
|
@ -30,10 +30,13 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// adjustHelperFunc defines functions to adjust helper.
|
// adjustHelperFunc defines functions to adjust helper.
|
||||||
var adjustHelperFunc = [8]func(*File, *xlsxWorksheet, string, adjustDirection, int, int, int) error{
|
var adjustHelperFunc = [9]func(*File, *xlsxWorksheet, string, adjustDirection, int, int, int) error{
|
||||||
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||||
return f.adjustConditionalFormats(ws, sheet, dir, num, offset, sheetID)
|
return f.adjustConditionalFormats(ws, sheet, dir, num, offset, sheetID)
|
||||||
},
|
},
|
||||||
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||||
|
return f.adjustDataValidations(ws, sheet, dir, num, offset, sheetID)
|
||||||
|
},
|
||||||
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
func(f *File, ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||||
return f.adjustDefinedNames(ws, sheet, dir, num, offset, sheetID)
|
return f.adjustDefinedNames(ws, sheet, dir, num, offset, sheetID)
|
||||||
},
|
},
|
||||||
|
@ -66,7 +69,7 @@ var adjustHelperFunc = [8]func(*File, *xlsxWorksheet, string, adjustDirection, i
|
||||||
// row: Index number of the row we're inserting/deleting before
|
// row: Index number of the row we're inserting/deleting before
|
||||||
// offset: Number of rows/column to insert/delete negative values indicate deletion
|
// offset: Number of rows/column to insert/delete negative values indicate deletion
|
||||||
//
|
//
|
||||||
// TODO: adjustComments, adjustDataValidations, adjustPageBreaks, adjustProtectedCells
|
// TODO: adjustComments, adjustPageBreaks, adjustProtectedCells
|
||||||
func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) error {
|
func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) error {
|
||||||
ws, err := f.workSheetReader(sheet)
|
ws, err := f.workSheetReader(sheet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -369,7 +372,10 @@ func (f *File) adjustFormulaOperand(sheet, sheetN string, keepRelative bool, tok
|
||||||
sheetName, cell = tokens[0], tokens[1]
|
sheetName, cell = tokens[0], tokens[1]
|
||||||
operand = escapeSheetName(sheetName) + "!"
|
operand = escapeSheetName(sheetName) + "!"
|
||||||
}
|
}
|
||||||
if sheet != sheetN && sheet != sheetName {
|
if sheetName == "" {
|
||||||
|
sheetName = sheetN
|
||||||
|
}
|
||||||
|
if sheet != sheetName {
|
||||||
return operand + cell, err
|
return operand + cell, err
|
||||||
}
|
}
|
||||||
for _, r := range cell {
|
for _, r := range cell {
|
||||||
|
@ -804,6 +810,60 @@ func (f *File) adjustConditionalFormats(ws *xlsxWorksheet, sheet string, dir adj
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adjustDataValidations updates the range of data validations for the worksheet
|
||||||
|
// when inserting or deleting rows or columns.
|
||||||
|
func (f *File) adjustDataValidations(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset, sheetID int) error {
|
||||||
|
for _, sheetN := range f.GetSheetList() {
|
||||||
|
worksheet, err := f.workSheetReader(sheetN)
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == newNotWorksheetError(sheetN).Error() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if worksheet.DataValidations == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for i := 0; i < len(worksheet.DataValidations.DataValidation); i++ {
|
||||||
|
dv := worksheet.DataValidations.DataValidation[i]
|
||||||
|
if dv == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if sheet == sheetN {
|
||||||
|
ref, del, err := f.adjustCellRef(dv.Sqref, dir, num, offset)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if del {
|
||||||
|
worksheet.DataValidations.DataValidation = append(worksheet.DataValidations.DataValidation[:i],
|
||||||
|
worksheet.DataValidations.DataValidation[i+1:]...)
|
||||||
|
i--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
worksheet.DataValidations.DataValidation[i].Sqref = ref
|
||||||
|
}
|
||||||
|
if worksheet.DataValidations.DataValidation[i].Formula1 != nil {
|
||||||
|
formula := unescapeDataValidationFormula(worksheet.DataValidations.DataValidation[i].Formula1.Content)
|
||||||
|
if formula, err = f.adjustFormulaRef(sheet, sheetN, formula, false, dir, num, offset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
worksheet.DataValidations.DataValidation[i].Formula1 = &xlsxInnerXML{Content: formulaEscaper.Replace(formula)}
|
||||||
|
}
|
||||||
|
if worksheet.DataValidations.DataValidation[i].Formula2 != nil {
|
||||||
|
formula := unescapeDataValidationFormula(worksheet.DataValidations.DataValidation[i].Formula2.Content)
|
||||||
|
if formula, err = f.adjustFormulaRef(sheet, sheetN, formula, false, dir, num, offset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
worksheet.DataValidations.DataValidation[i].Formula2 = &xlsxInnerXML{Content: formulaEscaper.Replace(formula)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if worksheet.DataValidations.Count = len(worksheet.DataValidations.DataValidation); worksheet.DataValidations.Count == 0 {
|
||||||
|
worksheet.DataValidations = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// adjustDrawings updates the starting anchor of the two cell anchor pictures
|
// adjustDrawings updates the starting anchor of the two cell anchor pictures
|
||||||
// and charts object when inserting or deleting rows or columns.
|
// and charts object when inserting or deleting rows or columns.
|
||||||
func (from *xlsxFrom) adjustDrawings(dir adjustDirection, num, offset int, editAs string) (bool, error) {
|
func (from *xlsxFrom) adjustDrawings(dir adjustDirection, num, offset int, editAs string) (bool, error) {
|
||||||
|
|
|
@ -743,7 +743,7 @@ func TestAdjustFormula(t *testing.T) {
|
||||||
assert.NoError(t, f.InsertRows("Sheet1", 2, 1))
|
assert.NoError(t, f.InsertRows("Sheet1", 2, 1))
|
||||||
formula, err = f.GetCellFormula("Sheet1", "B1")
|
formula, err = f.GetCellFormula("Sheet1", "B1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "SUM('Sheet 1'!A3,A5)", formula)
|
assert.Equal(t, "SUM('Sheet 1'!A2,A5)", formula)
|
||||||
|
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
// Test adjust formula on insert col in the middle of the range
|
// Test adjust formula on insert col in the middle of the range
|
||||||
|
@ -993,6 +993,76 @@ func TestAdjustConditionalFormats(t *testing.T) {
|
||||||
assert.NoError(t, f.RemoveCol("Sheet1", "B"))
|
assert.NoError(t, f.RemoveCol("Sheet1", "B"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAdjustDataValidations(t *testing.T) {
|
||||||
|
f := NewFile()
|
||||||
|
dv := NewDataValidation(true)
|
||||||
|
dv.Sqref = "B1"
|
||||||
|
assert.NoError(t, dv.SetDropList([]string{"1", "2", "3"}))
|
||||||
|
assert.NoError(t, f.AddDataValidation("Sheet1", dv))
|
||||||
|
assert.NoError(t, f.RemoveCol("Sheet1", "B"))
|
||||||
|
dvs, err := f.GetDataValidations("Sheet1")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, dvs, 0)
|
||||||
|
|
||||||
|
assert.NoError(t, f.SetCellValue("Sheet1", "F2", 1))
|
||||||
|
assert.NoError(t, f.SetCellValue("Sheet1", "F3", 2))
|
||||||
|
dv = NewDataValidation(true)
|
||||||
|
dv.Sqref = "C2:D3"
|
||||||
|
dv.SetSqrefDropList("$F$2:$F$3")
|
||||||
|
assert.NoError(t, f.AddDataValidation("Sheet1", dv))
|
||||||
|
|
||||||
|
assert.NoError(t, f.AddChartSheet("Chart1", &Chart{Type: Line}))
|
||||||
|
_, err = f.NewSheet("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, f.SetSheetRow("Sheet2", "C1", &[]interface{}{1, 10}))
|
||||||
|
dv = NewDataValidation(true)
|
||||||
|
dv.Sqref = "C5:D6"
|
||||||
|
assert.NoError(t, dv.SetRange("Sheet2!C1", "Sheet2!D1", DataValidationTypeWhole, DataValidationOperatorBetween))
|
||||||
|
dv.SetError(DataValidationErrorStyleStop, "error title", "error body")
|
||||||
|
assert.NoError(t, f.AddDataValidation("Sheet1", dv))
|
||||||
|
assert.NoError(t, f.RemoveCol("Sheet1", "B"))
|
||||||
|
assert.NoError(t, f.RemoveCol("Sheet2", "B"))
|
||||||
|
dvs, err = f.GetDataValidations("Sheet1")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "B2:C3", dvs[0].Sqref)
|
||||||
|
assert.Equal(t, "$E$2:$E$3", dvs[0].Formula1)
|
||||||
|
assert.Equal(t, "B5:C6", dvs[1].Sqref)
|
||||||
|
assert.Equal(t, "Sheet2!B1", dvs[1].Formula1)
|
||||||
|
assert.Equal(t, "Sheet2!C1", dvs[1].Formula2)
|
||||||
|
|
||||||
|
dv = NewDataValidation(true)
|
||||||
|
dv.Sqref = "C8:D10"
|
||||||
|
assert.NoError(t, dv.SetDropList([]string{`A<`, `B>`, `C"`, "D\t", `E'`, `F`}))
|
||||||
|
assert.NoError(t, f.AddDataValidation("Sheet1", dv))
|
||||||
|
assert.NoError(t, f.RemoveCol("Sheet1", "B"))
|
||||||
|
dvs, err = f.GetDataValidations("Sheet1")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "\"A<,B>,C\",D\t,E',F\"", dvs[2].Formula1)
|
||||||
|
|
||||||
|
dv = NewDataValidation(true)
|
||||||
|
dv.Sqref = "C5:D6"
|
||||||
|
assert.NoError(t, dv.SetRange("Sheet1!A1048576", "Sheet1!XFD1", DataValidationTypeWhole, DataValidationOperatorBetween))
|
||||||
|
dv.SetError(DataValidationErrorStyleStop, "error title", "error body")
|
||||||
|
assert.NoError(t, f.AddDataValidation("Sheet1", dv))
|
||||||
|
assert.Equal(t, ErrColumnNumber, f.InsertCols("Sheet1", "A", 1))
|
||||||
|
assert.Equal(t, ErrMaxRows, f.InsertRows("Sheet1", 1, 1))
|
||||||
|
|
||||||
|
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
|
||||||
|
assert.True(t, ok)
|
||||||
|
ws.(*xlsxWorksheet).DataValidations.DataValidation[0].Sqref = "-"
|
||||||
|
assert.Equal(t, newCellNameToCoordinatesError("-", newInvalidCellNameError("-")), f.RemoveCol("Sheet1", "B"))
|
||||||
|
|
||||||
|
ws.(*xlsxWorksheet).DataValidations.DataValidation[0] = nil
|
||||||
|
assert.NoError(t, f.RemoveCol("Sheet1", "B"))
|
||||||
|
|
||||||
|
ws.(*xlsxWorksheet).DataValidations = nil
|
||||||
|
assert.NoError(t, f.RemoveCol("Sheet1", "B"))
|
||||||
|
|
||||||
|
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
||||||
|
f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.adjustDataValidations(nil, "Sheet1", columns, 0, 0, 1), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
}
|
||||||
|
|
||||||
func TestAdjustDrawings(t *testing.T) {
|
func TestAdjustDrawings(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
// Test add pictures to sheet with positioning
|
// Test add pictures to sheet with positioning
|
||||||
|
|
|
@ -75,7 +75,11 @@ var (
|
||||||
`&`, `&`,
|
`&`, `&`,
|
||||||
`<`, `<`,
|
`<`, `<`,
|
||||||
`>`, `>`,
|
`>`, `>`,
|
||||||
`"`, `""`,
|
)
|
||||||
|
formulaUnescaper = strings.NewReplacer(
|
||||||
|
`&`, `&`,
|
||||||
|
`<`, `<`,
|
||||||
|
`>`, `>`,
|
||||||
)
|
)
|
||||||
// dataValidationTypeMap defined supported data validation types.
|
// dataValidationTypeMap defined supported data validation types.
|
||||||
dataValidationTypeMap = map[DataValidationType]string{
|
dataValidationTypeMap = map[DataValidationType]string{
|
||||||
|
@ -101,11 +105,6 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
formula1Name = "formula1"
|
|
||||||
formula2Name = "formula2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewDataValidation return data validation struct.
|
// NewDataValidation return data validation struct.
|
||||||
func NewDataValidation(allowBlank bool) *DataValidation {
|
func NewDataValidation(allowBlank bool) *DataValidation {
|
||||||
return &DataValidation{
|
return &DataValidation{
|
||||||
|
@ -151,36 +150,40 @@ func (dv *DataValidation) SetDropList(keys []string) error {
|
||||||
if MaxFieldLength < len(utf16.Encode([]rune(formula))) {
|
if MaxFieldLength < len(utf16.Encode([]rune(formula))) {
|
||||||
return ErrDataValidationFormulaLength
|
return ErrDataValidationFormulaLength
|
||||||
}
|
}
|
||||||
dv.Formula1 = fmt.Sprintf(`<%[2]s>"%[1]s"</%[2]s>`, formulaEscaper.Replace(formula), formula1Name)
|
|
||||||
dv.Type = dataValidationTypeMap[DataValidationTypeList]
|
dv.Type = dataValidationTypeMap[DataValidationTypeList]
|
||||||
|
if strings.HasPrefix(formula, "=") {
|
||||||
|
dv.Formula1 = formulaEscaper.Replace(formula)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
dv.Formula1 = fmt.Sprintf(`"%s"`, strings.NewReplacer(`"`, `""`).Replace(formulaEscaper.Replace(formula)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRange provides function to set data validation range in drop list, only
|
// SetRange provides function to set data validation range in drop list, only
|
||||||
// accepts int, float64, string or []string data type formula argument.
|
// accepts int, float64, string or []string data type formula argument.
|
||||||
func (dv *DataValidation) SetRange(f1, f2 interface{}, t DataValidationType, o DataValidationOperator) error {
|
func (dv *DataValidation) SetRange(f1, f2 interface{}, t DataValidationType, o DataValidationOperator) error {
|
||||||
genFormula := func(name string, val interface{}) (string, error) {
|
genFormula := func(val interface{}) (string, error) {
|
||||||
var formula string
|
var formula string
|
||||||
switch v := val.(type) {
|
switch v := val.(type) {
|
||||||
case int:
|
case int:
|
||||||
formula = fmt.Sprintf("<%s>%d</%s>", name, v, name)
|
formula = fmt.Sprintf("%d", v)
|
||||||
case float64:
|
case float64:
|
||||||
if math.Abs(v) > math.MaxFloat32 {
|
if math.Abs(v) > math.MaxFloat32 {
|
||||||
return formula, ErrDataValidationRange
|
return formula, ErrDataValidationRange
|
||||||
}
|
}
|
||||||
formula = fmt.Sprintf("<%s>%.17g</%s>", name, v, name)
|
formula = fmt.Sprintf("%.17g", v)
|
||||||
case string:
|
case string:
|
||||||
formula = fmt.Sprintf("<%s>%s</%s>", name, v, name)
|
formula = v
|
||||||
default:
|
default:
|
||||||
return formula, ErrParameterInvalid
|
return formula, ErrParameterInvalid
|
||||||
}
|
}
|
||||||
return formula, nil
|
return formula, nil
|
||||||
}
|
}
|
||||||
formula1, err := genFormula(formula1Name, f1)
|
formula1, err := genFormula(f1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
formula2, err := genFormula(formula2Name, f2)
|
formula2, err := genFormula(f2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -205,7 +208,7 @@ func (dv *DataValidation) SetRange(f1, f2 interface{}, t DataValidationType, o D
|
||||||
// dv.SetSqrefDropList("$E$1:$E$3")
|
// dv.SetSqrefDropList("$E$1:$E$3")
|
||||||
// err := f.AddDataValidation("Sheet1", dv)
|
// err := f.AddDataValidation("Sheet1", dv)
|
||||||
func (dv *DataValidation) SetSqrefDropList(sqref string) {
|
func (dv *DataValidation) SetSqrefDropList(sqref string) {
|
||||||
dv.Formula1 = fmt.Sprintf("<formula1>%s</formula1>", sqref)
|
dv.Formula1 = sqref
|
||||||
dv.Type = dataValidationTypeMap[DataValidationTypeList]
|
dv.Type = dataValidationTypeMap[DataValidationTypeList]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +259,27 @@ func (f *File) AddDataValidation(sheet string, dv *DataValidation) error {
|
||||||
if nil == ws.DataValidations {
|
if nil == ws.DataValidations {
|
||||||
ws.DataValidations = new(xlsxDataValidations)
|
ws.DataValidations = new(xlsxDataValidations)
|
||||||
}
|
}
|
||||||
ws.DataValidations.DataValidation = append(ws.DataValidations.DataValidation, dv)
|
dataValidation := &xlsxDataValidation{
|
||||||
|
AllowBlank: dv.AllowBlank,
|
||||||
|
Error: dv.Error,
|
||||||
|
ErrorStyle: dv.ErrorStyle,
|
||||||
|
ErrorTitle: dv.ErrorTitle,
|
||||||
|
Operator: dv.Operator,
|
||||||
|
Prompt: dv.Prompt,
|
||||||
|
PromptTitle: dv.PromptTitle,
|
||||||
|
ShowDropDown: dv.ShowDropDown,
|
||||||
|
ShowErrorMessage: dv.ShowErrorMessage,
|
||||||
|
ShowInputMessage: dv.ShowInputMessage,
|
||||||
|
Sqref: dv.Sqref,
|
||||||
|
Type: dv.Type,
|
||||||
|
}
|
||||||
|
if dv.Formula1 != "" {
|
||||||
|
dataValidation.Formula1 = &xlsxInnerXML{Content: dv.Formula1}
|
||||||
|
}
|
||||||
|
if dv.Formula2 != "" {
|
||||||
|
dataValidation.Formula2 = &xlsxInnerXML{Content: dv.Formula2}
|
||||||
|
}
|
||||||
|
ws.DataValidations.DataValidation = append(ws.DataValidations.DataValidation, dataValidation)
|
||||||
ws.DataValidations.Count = len(ws.DataValidations.DataValidation)
|
ws.DataValidations.Count = len(ws.DataValidations.DataValidation)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -270,7 +293,33 @@ func (f *File) GetDataValidations(sheet string) ([]*DataValidation, error) {
|
||||||
if ws.DataValidations == nil || len(ws.DataValidations.DataValidation) == 0 {
|
if ws.DataValidations == nil || len(ws.DataValidations.DataValidation) == 0 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return ws.DataValidations.DataValidation, err
|
var dvs []*DataValidation
|
||||||
|
for _, dv := range ws.DataValidations.DataValidation {
|
||||||
|
if dv != nil {
|
||||||
|
dataValidation := &DataValidation{
|
||||||
|
AllowBlank: dv.AllowBlank,
|
||||||
|
Error: dv.Error,
|
||||||
|
ErrorStyle: dv.ErrorStyle,
|
||||||
|
ErrorTitle: dv.ErrorTitle,
|
||||||
|
Operator: dv.Operator,
|
||||||
|
Prompt: dv.Prompt,
|
||||||
|
PromptTitle: dv.PromptTitle,
|
||||||
|
ShowDropDown: dv.ShowDropDown,
|
||||||
|
ShowErrorMessage: dv.ShowErrorMessage,
|
||||||
|
ShowInputMessage: dv.ShowInputMessage,
|
||||||
|
Sqref: dv.Sqref,
|
||||||
|
Type: dv.Type,
|
||||||
|
}
|
||||||
|
if dv.Formula1 != nil {
|
||||||
|
dataValidation.Formula1 = unescapeDataValidationFormula(dv.Formula1.Content)
|
||||||
|
}
|
||||||
|
if dv.Formula2 != nil {
|
||||||
|
dataValidation.Formula2 = unescapeDataValidationFormula(dv.Formula2.Content)
|
||||||
|
}
|
||||||
|
dvs = append(dvs, dataValidation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dvs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteDataValidation delete data validation by given worksheet name and
|
// DeleteDataValidation delete data validation by given worksheet name and
|
||||||
|
@ -351,3 +400,11 @@ func (f *File) squashSqref(cells [][]int) []string {
|
||||||
}
|
}
|
||||||
return append(refs, ref)
|
return append(refs, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unescapeDataValidationFormula returns unescaped data validation formula.
|
||||||
|
func unescapeDataValidationFormula(val string) string {
|
||||||
|
if strings.HasPrefix(val, "\"") { // Text detection
|
||||||
|
return strings.NewReplacer(`""`, `"`).Replace(formulaUnescaper.Replace(val))
|
||||||
|
}
|
||||||
|
return formulaUnescaper.Replace(val)
|
||||||
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ func TestDataValidation(t *testing.T) {
|
||||||
dv.Sqref = "A5:B6"
|
dv.Sqref = "A5:B6"
|
||||||
for _, listValid := range [][]string{
|
for _, listValid := range [][]string{
|
||||||
{"1", "2", "3"},
|
{"1", "2", "3"},
|
||||||
|
{"=A1"},
|
||||||
{strings.Repeat("&", MaxFieldLength)},
|
{strings.Repeat("&", MaxFieldLength)},
|
||||||
{strings.Repeat("\u4E00", MaxFieldLength)},
|
{strings.Repeat("\u4E00", MaxFieldLength)},
|
||||||
{strings.Repeat("\U0001F600", 100), strings.Repeat("\u4E01", 50), "<&>"},
|
{strings.Repeat("\U0001F600", 100), strings.Repeat("\u4E01", 50), "<&>"},
|
||||||
|
@ -82,7 +83,7 @@ func TestDataValidation(t *testing.T) {
|
||||||
assert.NotEqual(t, "", dv.Formula1,
|
assert.NotEqual(t, "", dv.Formula1,
|
||||||
"Formula1 should not be empty for valid input %v", listValid)
|
"Formula1 should not be empty for valid input %v", listValid)
|
||||||
}
|
}
|
||||||
assert.Equal(t, `<formula1>"A<,B>,C"",D ,E',F"</formula1>`, dv.Formula1)
|
assert.Equal(t, `"A<,B>,C"",D ,E',F"`, dv.Formula1)
|
||||||
assert.NoError(t, f.AddDataValidation("Sheet1", dv))
|
assert.NoError(t, f.AddDataValidation("Sheet1", dv))
|
||||||
|
|
||||||
dataValidations, err = f.GetDataValidations("Sheet1")
|
dataValidations, err = f.GetDataValidations("Sheet1")
|
||||||
|
|
|
@ -294,9 +294,9 @@ func TestDeletePicture(t *testing.T) {
|
||||||
// Test delete picture on not exists worksheet
|
// Test delete picture on not exists worksheet
|
||||||
assert.EqualError(t, f.DeletePicture("SheetN", "A1"), "sheet SheetN does not exist")
|
assert.EqualError(t, f.DeletePicture("SheetN", "A1"), "sheet SheetN does not exist")
|
||||||
// Test delete picture with invalid sheet name
|
// Test delete picture with invalid sheet name
|
||||||
assert.EqualError(t, f.DeletePicture("Sheet:1", "A1"), ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, f.DeletePicture("Sheet:1", "A1"))
|
||||||
// Test delete picture with invalid coordinates
|
// Test delete picture with invalid coordinates
|
||||||
assert.EqualError(t, f.DeletePicture("Sheet1", ""), newCellNameToCoordinatesError("", newInvalidCellNameError("")).Error())
|
assert.Equal(t, newCellNameToCoordinatesError("", newInvalidCellNameError("")), f.DeletePicture("Sheet1", ""))
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
// Test delete picture on no chart worksheet
|
// Test delete picture on no chart worksheet
|
||||||
assert.NoError(t, NewFile().DeletePicture("Sheet1", "A1"))
|
assert.NoError(t, NewFile().DeletePicture("Sheet1", "A1"))
|
||||||
|
|
|
@ -174,7 +174,7 @@ func TestPivotTable(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Test empty pivot table options
|
// Test empty pivot table options
|
||||||
assert.EqualError(t, f.AddPivotTable(nil), ErrParameterRequired.Error())
|
assert.Equal(t, ErrParameterRequired, f.AddPivotTable(nil))
|
||||||
// Test add pivot table with custom name which exceeds the max characters limit
|
// Test add pivot table with custom name which exceeds the max characters limit
|
||||||
assert.Equal(t, ErrNameLength, f.AddPivotTable(&PivotTableOptions{
|
assert.Equal(t, ErrNameLength, f.AddPivotTable(&PivotTableOptions{
|
||||||
DataRange: "dataRange",
|
DataRange: "dataRange",
|
||||||
|
|
|
@ -42,16 +42,16 @@ func TestAddShape(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
), "sheet Sheet3 does not exist")
|
), "sheet Sheet3 does not exist")
|
||||||
assert.EqualError(t, f.AddShape("Sheet3", nil), ErrParameterInvalid.Error())
|
assert.Equal(t, ErrParameterInvalid, f.AddShape("Sheet3", nil))
|
||||||
assert.EqualError(t, f.AddShape("Sheet1", &Shape{Cell: "A1"}), ErrParameterInvalid.Error())
|
assert.Equal(t, ErrParameterInvalid, f.AddShape("Sheet1", &Shape{Cell: "A1"}))
|
||||||
assert.EqualError(t, f.AddShape("Sheet1", &Shape{
|
assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), f.AddShape("Sheet1", &Shape{
|
||||||
Cell: "A",
|
Cell: "A",
|
||||||
Type: "rect",
|
Type: "rect",
|
||||||
Paragraph: []RichTextRun{
|
Paragraph: []RichTextRun{
|
||||||
{Text: "Rectangle", Font: &Font{Color: "CD5C5C"}},
|
{Text: "Rectangle", Font: &Font{Color: "CD5C5C"}},
|
||||||
{Text: "Shape", Font: &Font{Bold: true, Color: "2980B9"}},
|
{Text: "Shape", Font: &Font{Bold: true, Color: "2980B9"}},
|
||||||
},
|
},
|
||||||
}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
}))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape1.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape1.xlsx")))
|
||||||
|
|
||||||
// Test add first shape for given sheet
|
// Test add first shape for given sheet
|
||||||
|
@ -79,14 +79,14 @@ func TestAddShape(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape2.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddShape2.xlsx")))
|
||||||
// Test add shape with invalid sheet name
|
// Test add shape with invalid sheet name
|
||||||
assert.EqualError(t, f.AddShape("Sheet:1", &Shape{
|
assert.Equal(t, ErrSheetNameInvalid, f.AddShape("Sheet:1", &Shape{
|
||||||
Cell: "A30",
|
Cell: "A30",
|
||||||
Type: "rect",
|
Type: "rect",
|
||||||
Paragraph: []RichTextRun{
|
Paragraph: []RichTextRun{
|
||||||
{Text: "Rectangle", Font: &Font{Color: "CD5C5C"}},
|
{Text: "Rectangle", Font: &Font{Color: "CD5C5C"}},
|
||||||
{Text: "Shape", Font: &Font{Bold: true, Color: "2980B9"}},
|
{Text: "Shape", Font: &Font{Bold: true, Color: "2980B9"}},
|
||||||
},
|
},
|
||||||
}), ErrSheetNameInvalid.Error())
|
}))
|
||||||
// Test add shape with unsupported charset style sheet
|
// Test add shape with unsupported charset style sheet
|
||||||
f.Styles = nil
|
f.Styles = nil
|
||||||
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
||||||
|
|
|
@ -30,7 +30,7 @@ func TestSetPageMargins(t *testing.T) {
|
||||||
// Test set page margins on not exists worksheet
|
// Test set page margins on not exists worksheet
|
||||||
assert.EqualError(t, f.SetPageMargins("SheetN", nil), "sheet SheetN does not exist")
|
assert.EqualError(t, f.SetPageMargins("SheetN", nil), "sheet SheetN does not exist")
|
||||||
// Test set page margins with invalid sheet name
|
// Test set page margins with invalid sheet name
|
||||||
assert.EqualError(t, f.SetPageMargins("Sheet:1", nil), ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, f.SetPageMargins("Sheet:1", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetPageMargins(t *testing.T) {
|
func TestGetPageMargins(t *testing.T) {
|
||||||
|
@ -40,7 +40,7 @@ func TestGetPageMargins(t *testing.T) {
|
||||||
assert.EqualError(t, err, "sheet SheetN does not exist")
|
assert.EqualError(t, err, "sheet SheetN does not exist")
|
||||||
// Test get page margins with invalid sheet name
|
// Test get page margins with invalid sheet name
|
||||||
_, err = f.GetPageMargins("Sheet:1")
|
_, err = f.GetPageMargins("Sheet:1")
|
||||||
assert.EqualError(t, err, ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetSheetProps(t *testing.T) {
|
func TestSetSheetProps(t *testing.T) {
|
||||||
|
@ -88,7 +88,7 @@ func TestSetSheetProps(t *testing.T) {
|
||||||
// Test set worksheet properties on not exists worksheet
|
// Test set worksheet properties on not exists worksheet
|
||||||
assert.EqualError(t, f.SetSheetProps("SheetN", nil), "sheet SheetN does not exist")
|
assert.EqualError(t, f.SetSheetProps("SheetN", nil), "sheet SheetN does not exist")
|
||||||
// Test set worksheet properties with invalid sheet name
|
// Test set worksheet properties with invalid sheet name
|
||||||
assert.EqualError(t, f.SetSheetProps("Sheet:1", nil), ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, f.SetSheetProps("Sheet:1", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSheetProps(t *testing.T) {
|
func TestGetSheetProps(t *testing.T) {
|
||||||
|
@ -98,5 +98,5 @@ func TestGetSheetProps(t *testing.T) {
|
||||||
assert.EqualError(t, err, "sheet SheetN does not exist")
|
assert.EqualError(t, err, "sheet SheetN does not exist")
|
||||||
// Test get worksheet properties with invalid sheet name
|
// Test get worksheet properties with invalid sheet name
|
||||||
_, err = f.GetSheetProps("Sheet:1")
|
_, err = f.GetSheetProps("Sheet:1")
|
||||||
assert.EqualError(t, err, ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,46 +224,46 @@ func TestAddSparkline(t *testing.T) {
|
||||||
Range: []string{"Sheet2!A3:E3"},
|
Range: []string{"Sheet2!A3:E3"},
|
||||||
}), "sheet SheetN does not exist")
|
}), "sheet SheetN does not exist")
|
||||||
|
|
||||||
assert.EqualError(t, f.AddSparkline("Sheet1", nil), ErrParameterRequired.Error())
|
assert.Equal(t, ErrParameterRequired, f.AddSparkline("Sheet1", nil))
|
||||||
|
|
||||||
// Test add sparkline with invalid sheet name
|
// Test add sparkline with invalid sheet name
|
||||||
assert.EqualError(t, f.AddSparkline("Sheet:1", &SparklineOptions{
|
assert.Equal(t, ErrSheetNameInvalid, f.AddSparkline("Sheet:1", &SparklineOptions{
|
||||||
Location: []string{"F3"},
|
Location: []string{"F3"},
|
||||||
Range: []string{"Sheet2!A3:E3"},
|
Range: []string{"Sheet2!A3:E3"},
|
||||||
Type: "win_loss",
|
Type: "win_loss",
|
||||||
Negative: true,
|
Negative: true,
|
||||||
}), ErrSheetNameInvalid.Error())
|
}))
|
||||||
|
|
||||||
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
|
assert.Equal(t, ErrSparklineLocation, f.AddSparkline("Sheet1", &SparklineOptions{
|
||||||
Range: []string{"Sheet2!A3:E3"},
|
Range: []string{"Sheet2!A3:E3"},
|
||||||
}), ErrSparklineLocation.Error())
|
}))
|
||||||
|
|
||||||
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
|
assert.Equal(t, ErrSparklineRange, f.AddSparkline("Sheet1", &SparklineOptions{
|
||||||
Location: []string{"F3"},
|
Location: []string{"F3"},
|
||||||
}), ErrSparklineRange.Error())
|
}))
|
||||||
|
|
||||||
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
|
assert.Equal(t, ErrSparkline, f.AddSparkline("Sheet1", &SparklineOptions{
|
||||||
Location: []string{"F2", "F3"},
|
Location: []string{"F2", "F3"},
|
||||||
Range: []string{"Sheet2!A3:E3"},
|
Range: []string{"Sheet2!A3:E3"},
|
||||||
}), ErrSparkline.Error())
|
}))
|
||||||
|
|
||||||
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
|
assert.Equal(t, ErrSparklineType, f.AddSparkline("Sheet1", &SparklineOptions{
|
||||||
Location: []string{"F3"},
|
Location: []string{"F3"},
|
||||||
Range: []string{"Sheet2!A3:E3"},
|
Range: []string{"Sheet2!A3:E3"},
|
||||||
Type: "unknown_type",
|
Type: "unknown_type",
|
||||||
}), ErrSparklineType.Error())
|
}))
|
||||||
|
|
||||||
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
|
assert.Equal(t, ErrSparklineStyle, f.AddSparkline("Sheet1", &SparklineOptions{
|
||||||
Location: []string{"F3"},
|
Location: []string{"F3"},
|
||||||
Range: []string{"Sheet2!A3:E3"},
|
Range: []string{"Sheet2!A3:E3"},
|
||||||
Style: -1,
|
Style: -1,
|
||||||
}), ErrSparklineStyle.Error())
|
}))
|
||||||
|
|
||||||
assert.EqualError(t, f.AddSparkline("Sheet1", &SparklineOptions{
|
assert.Equal(t, ErrSparklineStyle, f.AddSparkline("Sheet1", &SparklineOptions{
|
||||||
Location: []string{"F3"},
|
Location: []string{"F3"},
|
||||||
Range: []string{"Sheet2!A3:E3"},
|
Range: []string{"Sheet2!A3:E3"},
|
||||||
Style: -1,
|
Style: -1,
|
||||||
}), ErrSparklineStyle.Error())
|
}))
|
||||||
// Test creating a conditional format with existing extension lists
|
// Test creating a conditional format with existing extension lists
|
||||||
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
|
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
|
|
@ -73,7 +73,7 @@ func TestStreamWriter(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
assert.NoError(t, streamWriter.SetRow("A6", []interface{}{time.Now()}))
|
assert.NoError(t, streamWriter.SetRow("A6", []interface{}{time.Now()}))
|
||||||
assert.NoError(t, streamWriter.SetRow("A7", nil, RowOpts{Height: 20, Hidden: true, StyleID: styleID}))
|
assert.NoError(t, streamWriter.SetRow("A7", nil, RowOpts{Height: 20, Hidden: true, StyleID: styleID}))
|
||||||
assert.EqualError(t, streamWriter.SetRow("A8", nil, RowOpts{Height: MaxRowHeight + 1}), ErrMaxRowHeight.Error())
|
assert.Equal(t, ErrMaxRowHeight, streamWriter.SetRow("A8", nil, RowOpts{Height: MaxRowHeight + 1}))
|
||||||
|
|
||||||
for rowID := 10; rowID <= 51200; rowID++ {
|
for rowID := 10; rowID <= 51200; rowID++ {
|
||||||
row := make([]interface{}, 50)
|
row := make([]interface{}, 50)
|
||||||
|
@ -158,11 +158,11 @@ func TestStreamSetColWidth(t *testing.T) {
|
||||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, streamWriter.SetColWidth(3, 2, 20))
|
assert.NoError(t, streamWriter.SetColWidth(3, 2, 20))
|
||||||
assert.ErrorIs(t, streamWriter.SetColWidth(0, 3, 20), ErrColumnNumber)
|
assert.Equal(t, ErrColumnNumber, streamWriter.SetColWidth(0, 3, 20))
|
||||||
assert.ErrorIs(t, streamWriter.SetColWidth(MaxColumns+1, 3, 20), ErrColumnNumber)
|
assert.Equal(t, ErrColumnNumber, streamWriter.SetColWidth(MaxColumns+1, 3, 20))
|
||||||
assert.EqualError(t, streamWriter.SetColWidth(1, 3, MaxColumnWidth+1), ErrColumnWidth.Error())
|
assert.Equal(t, ErrColumnWidth, streamWriter.SetColWidth(1, 3, MaxColumnWidth+1))
|
||||||
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
|
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
|
||||||
assert.ErrorIs(t, streamWriter.SetColWidth(2, 3, 20), ErrStreamSetColWidth)
|
assert.Equal(t, ErrStreamSetColWidth, streamWriter.SetColWidth(2, 3, 20))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStreamSetPanes(t *testing.T) {
|
func TestStreamSetPanes(t *testing.T) {
|
||||||
|
@ -183,9 +183,9 @@ func TestStreamSetPanes(t *testing.T) {
|
||||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, streamWriter.SetPanes(paneOpts))
|
assert.NoError(t, streamWriter.SetPanes(paneOpts))
|
||||||
assert.EqualError(t, streamWriter.SetPanes(nil), ErrParameterInvalid.Error())
|
assert.Equal(t, ErrParameterInvalid, streamWriter.SetPanes(nil))
|
||||||
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
|
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
|
||||||
assert.ErrorIs(t, streamWriter.SetPanes(paneOpts), ErrStreamSetPanes)
|
assert.Equal(t, ErrStreamSetPanes, streamWriter.SetPanes(paneOpts))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStreamTable(t *testing.T) {
|
func TestStreamTable(t *testing.T) {
|
||||||
|
@ -220,10 +220,10 @@ func TestStreamTable(t *testing.T) {
|
||||||
assert.NoError(t, streamWriter.AddTable(&Table{Range: "A1:C1"}))
|
assert.NoError(t, streamWriter.AddTable(&Table{Range: "A1:C1"}))
|
||||||
|
|
||||||
// Test add table with illegal cell reference
|
// Test add table with illegal cell reference
|
||||||
assert.EqualError(t, streamWriter.AddTable(&Table{Range: "A:B1"}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), streamWriter.AddTable(&Table{Range: "A:B1"}))
|
||||||
assert.EqualError(t, streamWriter.AddTable(&Table{Range: "A1:B"}), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
assert.Equal(t, newCellNameToCoordinatesError("B", newInvalidCellNameError("B")), streamWriter.AddTable(&Table{Range: "A1:B"}))
|
||||||
// Test add table with invalid table name
|
// Test add table with invalid table name
|
||||||
assert.EqualError(t, streamWriter.AddTable(&Table{Range: "A:B1", Name: "1Table"}), newInvalidNameError("1Table").Error())
|
assert.Equal(t, newInvalidNameError("1Table"), streamWriter.AddTable(&Table{Range: "A:B1", Name: "1Table"}))
|
||||||
// Test add table with unsupported charset content types
|
// Test add table with unsupported charset content types
|
||||||
file.ContentTypes = nil
|
file.ContentTypes = nil
|
||||||
file.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
file.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
|
||||||
|
@ -239,7 +239,7 @@ func TestStreamMergeCells(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, streamWriter.MergeCell("A1", "D1"))
|
assert.NoError(t, streamWriter.MergeCell("A1", "D1"))
|
||||||
// Test merge cells with illegal cell reference
|
// Test merge cells with illegal cell reference
|
||||||
assert.EqualError(t, streamWriter.MergeCell("A", "D1"), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), streamWriter.MergeCell("A", "D1"))
|
||||||
assert.NoError(t, streamWriter.Flush())
|
assert.NoError(t, streamWriter.Flush())
|
||||||
// Save spreadsheet by the given path
|
// Save spreadsheet by the given path
|
||||||
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamMergeCells.xlsx")))
|
assert.NoError(t, file.SaveAs(filepath.Join("test", "TestStreamMergeCells.xlsx")))
|
||||||
|
@ -270,7 +270,7 @@ func TestNewStreamWriter(t *testing.T) {
|
||||||
assert.EqualError(t, err, "sheet SheetN does not exist")
|
assert.EqualError(t, err, "sheet SheetN does not exist")
|
||||||
// Test new stream write with invalid sheet name
|
// Test new stream write with invalid sheet name
|
||||||
_, err = file.NewStreamWriter("Sheet:1")
|
_, err = file.NewStreamWriter("Sheet:1")
|
||||||
assert.EqualError(t, err, ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStreamMarshalAttrs(t *testing.T) {
|
func TestStreamMarshalAttrs(t *testing.T) {
|
||||||
|
@ -288,10 +288,10 @@ func TestStreamSetRow(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
streamWriter, err := file.NewStreamWriter("Sheet1")
|
streamWriter, err := file.NewStreamWriter("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualError(t, streamWriter.SetRow("A", []interface{}{}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), streamWriter.SetRow("A", []interface{}{}))
|
||||||
// Test set row with non-ascending row number
|
// Test set row with non-ascending row number
|
||||||
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{}))
|
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{}))
|
||||||
assert.EqualError(t, streamWriter.SetRow("A1", []interface{}{}), newStreamSetRowError(1).Error())
|
assert.Equal(t, newStreamSetRowError(1), streamWriter.SetRow("A1", []interface{}{}))
|
||||||
// Test set row with unsupported charset workbook
|
// Test set row with unsupported charset workbook
|
||||||
file.WorkBook = nil
|
file.WorkBook = nil
|
||||||
file.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
file.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
|
|
@ -190,7 +190,7 @@ func TestSetConditionalFormat(t *testing.T) {
|
||||||
ws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: "<ext><x14:conditionalFormattings></x14:conditionalFormatting></x14:conditionalFormattings></ext>"}
|
ws.(*xlsxWorksheet).ExtLst = &xlsxExtLst{Ext: "<ext><x14:conditionalFormattings></x14:conditionalFormatting></x14:conditionalFormattings></ext>"}
|
||||||
assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", condFmts), "XML syntax error on line 1: element <conditionalFormattings> closed by </conditionalFormatting>")
|
assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", condFmts), "XML syntax error on line 1: element <conditionalFormattings> closed by </conditionalFormatting>")
|
||||||
// Test creating a conditional format with invalid icon set style
|
// Test creating a conditional format with invalid icon set style
|
||||||
assert.EqualError(t, f.SetConditionalFormat("Sheet1", "A1:A2", []ConditionalFormatOptions{{Type: "icon_set", IconStyle: "unknown"}}), ErrParameterInvalid.Error())
|
assert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat("Sheet1", "A1:A2", []ConditionalFormatOptions{{Type: "icon_set", IconStyle: "unknown"}}))
|
||||||
// Test unsupported conditional formatting rule types
|
// Test unsupported conditional formatting rule types
|
||||||
for _, val := range []string{
|
for _, val := range []string{
|
||||||
"date",
|
"date",
|
||||||
|
@ -235,7 +235,7 @@ func TestGetConditionalFormats(t *testing.T) {
|
||||||
assert.EqualError(t, err, "sheet SheetN does not exist")
|
assert.EqualError(t, err, "sheet SheetN does not exist")
|
||||||
// Test get conditional formats with invalid sheet name
|
// Test get conditional formats with invalid sheet name
|
||||||
_, err = f.GetConditionalFormats("Sheet:1")
|
_, err = f.GetConditionalFormats("Sheet:1")
|
||||||
assert.EqualError(t, err, ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnsetConditionalFormat(t *testing.T) {
|
func TestUnsetConditionalFormat(t *testing.T) {
|
||||||
|
@ -249,7 +249,7 @@ func TestUnsetConditionalFormat(t *testing.T) {
|
||||||
// Test unset conditional format on not exists worksheet
|
// Test unset conditional format on not exists worksheet
|
||||||
assert.EqualError(t, f.UnsetConditionalFormat("SheetN", "A1:A10"), "sheet SheetN does not exist")
|
assert.EqualError(t, f.UnsetConditionalFormat("SheetN", "A1:A10"), "sheet SheetN does not exist")
|
||||||
// Test unset conditional format with invalid sheet name
|
// Test unset conditional format with invalid sheet name
|
||||||
assert.EqualError(t, f.UnsetConditionalFormat("Sheet:1", "A1:A10"), ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, f.UnsetConditionalFormat("Sheet:1", "A1:A10"))
|
||||||
// Save spreadsheet by the given path
|
// Save spreadsheet by the given path
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestUnsetConditionalFormat.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestUnsetConditionalFormat.xlsx")))
|
||||||
}
|
}
|
||||||
|
@ -469,9 +469,9 @@ func TestSetCellStyle(t *testing.T) {
|
||||||
// Test set cell style on not exists worksheet
|
// Test set cell style on not exists worksheet
|
||||||
assert.EqualError(t, f.SetCellStyle("SheetN", "A1", "A2", 1), "sheet SheetN does not exist")
|
assert.EqualError(t, f.SetCellStyle("SheetN", "A1", "A2", 1), "sheet SheetN does not exist")
|
||||||
// Test set cell style with invalid style ID
|
// Test set cell style with invalid style ID
|
||||||
assert.EqualError(t, f.SetCellStyle("Sheet1", "A1", "A2", -1), newInvalidStyleID(-1).Error())
|
assert.Equal(t, newInvalidStyleID(-1), f.SetCellStyle("Sheet1", "A1", "A2", -1))
|
||||||
// Test set cell style with not exists style ID
|
// Test set cell style with not exists style ID
|
||||||
assert.EqualError(t, f.SetCellStyle("Sheet1", "A1", "A2", 10), newInvalidStyleID(10).Error())
|
assert.Equal(t, newInvalidStyleID(10), f.SetCellStyle("Sheet1", "A1", "A2", 10))
|
||||||
// Test set cell style with unsupported charset style sheet
|
// Test set cell style with unsupported charset style sheet
|
||||||
f.Styles = nil
|
f.Styles = nil
|
||||||
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
|
||||||
|
|
|
@ -45,7 +45,7 @@ func TestAddTable(t *testing.T) {
|
||||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddTable.xlsx")))
|
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddTable.xlsx")))
|
||||||
|
|
||||||
// Test add table with invalid sheet name
|
// Test add table with invalid sheet name
|
||||||
assert.EqualError(t, f.AddTable("Sheet:1", &Table{Range: "B26:A21"}), ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, f.AddTable("Sheet:1", &Table{Range: "B26:A21"}))
|
||||||
// Test addTable with illegal cell reference
|
// Test addTable with illegal cell reference
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
assert.Equal(t, newCoordinatesToCellNameError(0, 0), f.addTable("sheet1", "", 0, 0, 0, 0, 0, nil))
|
assert.Equal(t, newCoordinatesToCellNameError(0, 0), f.addTable("sheet1", "", 0, 0, 0, 0, 0, nil))
|
||||||
|
@ -64,13 +64,13 @@ func TestAddTable(t *testing.T) {
|
||||||
{name: "\u0f5f\u0fb3\u0f0b\u0f21", err: newInvalidNameError("\u0f5f\u0fb3\u0f0b\u0f21")},
|
{name: "\u0f5f\u0fb3\u0f0b\u0f21", err: newInvalidNameError("\u0f5f\u0fb3\u0f0b\u0f21")},
|
||||||
{name: strings.Repeat("c", MaxFieldLength+1), err: ErrNameLength},
|
{name: strings.Repeat("c", MaxFieldLength+1), err: ErrNameLength},
|
||||||
} {
|
} {
|
||||||
assert.EqualError(t, f.AddTable("Sheet1", &Table{
|
assert.Equal(t, cases.err, f.AddTable("Sheet1", &Table{
|
||||||
Range: "A1:B2",
|
Range: "A1:B2",
|
||||||
Name: cases.name,
|
Name: cases.name,
|
||||||
}), cases.err.Error())
|
}))
|
||||||
assert.EqualError(t, f.SetDefinedName(&DefinedName{
|
assert.Equal(t, cases.err, f.SetDefinedName(&DefinedName{
|
||||||
Name: cases.name, RefersTo: "Sheet1!$A$2:$D$5",
|
Name: cases.name, RefersTo: "Sheet1!$A$2:$D$5",
|
||||||
}), cases.err.Error())
|
}))
|
||||||
}
|
}
|
||||||
// Test check duplicate table name with unsupported charset table parts
|
// Test check duplicate table name with unsupported charset table parts
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
|
@ -115,9 +115,9 @@ func TestDeleteTable(t *testing.T) {
|
||||||
assert.NoError(t, f.DeleteTable("Table2"))
|
assert.NoError(t, f.DeleteTable("Table2"))
|
||||||
assert.NoError(t, f.DeleteTable("Table1"))
|
assert.NoError(t, f.DeleteTable("Table1"))
|
||||||
// Test delete table with invalid table name
|
// Test delete table with invalid table name
|
||||||
assert.EqualError(t, f.DeleteTable("Table 1"), newInvalidNameError("Table 1").Error())
|
assert.Equal(t, newInvalidNameError("Table 1"), f.DeleteTable("Table 1"))
|
||||||
// Test delete table with no exist table name
|
// Test delete table with no exist table name
|
||||||
assert.EqualError(t, f.DeleteTable("Table"), newNoExistTableError("Table").Error())
|
assert.Equal(t, newNoExistTableError("Table"), f.DeleteTable("Table"))
|
||||||
// Test delete table with unsupported charset
|
// Test delete table with unsupported charset
|
||||||
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
||||||
f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
|
f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
|
||||||
|
@ -164,10 +164,10 @@ func TestAutoFilter(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test add auto filter with invalid sheet name
|
// Test add auto filter with invalid sheet name
|
||||||
assert.EqualError(t, f.AutoFilter("Sheet:1", "A1:B1", nil), ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, f.AutoFilter("Sheet:1", "A1:B1", nil))
|
||||||
// Test add auto filter with illegal cell reference
|
// Test add auto filter with illegal cell reference
|
||||||
assert.EqualError(t, f.AutoFilter("Sheet1", "A:B1", nil), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), f.AutoFilter("Sheet1", "A:B1", nil))
|
||||||
assert.EqualError(t, f.AutoFilter("Sheet1", "A1:B", nil), newCellNameToCoordinatesError("B", newInvalidCellNameError("B")).Error())
|
assert.Equal(t, newCellNameToCoordinatesError("B", newInvalidCellNameError("B")), f.AutoFilter("Sheet1", "A1:B", nil))
|
||||||
// Test add auto filter with unsupported charset workbook
|
// Test add auto filter with unsupported charset workbook
|
||||||
f.WorkBook = nil
|
f.WorkBook = nil
|
||||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
|
|
@ -35,7 +35,7 @@ func TestAddComment(t *testing.T) {
|
||||||
// Test add comment on not exists worksheet
|
// Test add comment on not exists worksheet
|
||||||
assert.EqualError(t, f.AddComment("SheetN", Comment{Cell: "B7", Author: "Excelize", Paragraph: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}), "sheet SheetN does not exist")
|
assert.EqualError(t, f.AddComment("SheetN", Comment{Cell: "B7", Author: "Excelize", Paragraph: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}), "sheet SheetN does not exist")
|
||||||
// Test add comment on with illegal cell reference
|
// Test add comment on with illegal cell reference
|
||||||
assert.EqualError(t, f.AddComment("Sheet1", Comment{Cell: "A", Author: "Excelize", Paragraph: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
|
assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), f.AddComment("Sheet1", Comment{Cell: "A", Author: "Excelize", Paragraph: []RichTextRun{{Text: "Excelize: ", Font: &Font{Bold: true}}, {Text: "This is a comment."}}}))
|
||||||
comments, err := f.GetComments("Sheet1")
|
comments, err := f.GetComments("Sheet1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, comments, 2)
|
assert.Len(t, comments, 2)
|
||||||
|
@ -57,7 +57,7 @@ func TestAddComment(t *testing.T) {
|
||||||
assert.Len(t, comments, 0)
|
assert.Len(t, comments, 0)
|
||||||
|
|
||||||
// Test add comments with invalid sheet name
|
// Test add comments with invalid sheet name
|
||||||
assert.EqualError(t, f.AddComment("Sheet:1", Comment{Cell: "A1", Author: "Excelize", Text: "This is a comment."}), ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, f.AddComment("Sheet:1", Comment{Cell: "A1", Author: "Excelize", Text: "This is a comment."}))
|
||||||
|
|
||||||
// Test add comments with unsupported charset
|
// Test add comments with unsupported charset
|
||||||
f.Comments["xl/comments2.xml"] = nil
|
f.Comments["xl/comments2.xml"] = nil
|
||||||
|
@ -105,7 +105,7 @@ func TestDeleteComment(t *testing.T) {
|
||||||
assert.Len(t, comments, 0)
|
assert.Len(t, comments, 0)
|
||||||
|
|
||||||
// Test delete comment with invalid sheet name
|
// Test delete comment with invalid sheet name
|
||||||
assert.EqualError(t, f.DeleteComment("Sheet:1", "A1"), ErrSheetNameInvalid.Error())
|
assert.Equal(t, ErrSheetNameInvalid, f.DeleteComment("Sheet:1", "A1"))
|
||||||
// Test delete all comments in a worksheet
|
// Test delete all comments in a worksheet
|
||||||
assert.NoError(t, f.DeleteComment("Sheet2", "A41"))
|
assert.NoError(t, f.DeleteComment("Sheet2", "A41"))
|
||||||
assert.NoError(t, f.DeleteComment("Sheet2", "C41"))
|
assert.NoError(t, f.DeleteComment("Sheet2", "C41"))
|
||||||
|
|
|
@ -419,31 +419,31 @@ type xlsxMergeCells struct {
|
||||||
// xlsxDataValidations expresses all data validation information for cells in a
|
// xlsxDataValidations expresses all data validation information for cells in a
|
||||||
// sheet which have data validation features applied.
|
// sheet which have data validation features applied.
|
||||||
type xlsxDataValidations struct {
|
type xlsxDataValidations struct {
|
||||||
XMLName xml.Name `xml:"dataValidations"`
|
XMLName xml.Name `xml:"dataValidations"`
|
||||||
Count int `xml:"count,attr,omitempty"`
|
Count int `xml:"count,attr,omitempty"`
|
||||||
DisablePrompts bool `xml:"disablePrompts,attr,omitempty"`
|
DisablePrompts bool `xml:"disablePrompts,attr,omitempty"`
|
||||||
XWindow int `xml:"xWindow,attr,omitempty"`
|
XWindow int `xml:"xWindow,attr,omitempty"`
|
||||||
YWindow int `xml:"yWindow,attr,omitempty"`
|
YWindow int `xml:"yWindow,attr,omitempty"`
|
||||||
DataValidation []*DataValidation `xml:"dataValidation"`
|
DataValidation []*xlsxDataValidation `xml:"dataValidation"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DataValidation directly maps the single item of data validation defined
|
// DataValidation directly maps the single item of data validation defined
|
||||||
// on a range of the worksheet.
|
// on a range of the worksheet.
|
||||||
type DataValidation struct {
|
type xlsxDataValidation struct {
|
||||||
AllowBlank bool `xml:"allowBlank,attr"`
|
AllowBlank bool `xml:"allowBlank,attr"`
|
||||||
Error *string `xml:"error,attr"`
|
Error *string `xml:"error,attr"`
|
||||||
ErrorStyle *string `xml:"errorStyle,attr"`
|
ErrorStyle *string `xml:"errorStyle,attr"`
|
||||||
ErrorTitle *string `xml:"errorTitle,attr"`
|
ErrorTitle *string `xml:"errorTitle,attr"`
|
||||||
Operator string `xml:"operator,attr,omitempty"`
|
Operator string `xml:"operator,attr,omitempty"`
|
||||||
Prompt *string `xml:"prompt,attr"`
|
Prompt *string `xml:"prompt,attr"`
|
||||||
PromptTitle *string `xml:"promptTitle,attr"`
|
PromptTitle *string `xml:"promptTitle,attr"`
|
||||||
ShowDropDown bool `xml:"showDropDown,attr,omitempty"`
|
ShowDropDown bool `xml:"showDropDown,attr,omitempty"`
|
||||||
ShowErrorMessage bool `xml:"showErrorMessage,attr,omitempty"`
|
ShowErrorMessage bool `xml:"showErrorMessage,attr,omitempty"`
|
||||||
ShowInputMessage bool `xml:"showInputMessage,attr,omitempty"`
|
ShowInputMessage bool `xml:"showInputMessage,attr,omitempty"`
|
||||||
Sqref string `xml:"sqref,attr"`
|
Sqref string `xml:"sqref,attr"`
|
||||||
Type string `xml:"type,attr,omitempty"`
|
Type string `xml:"type,attr,omitempty"`
|
||||||
Formula1 string `xml:",innerxml"`
|
Formula1 *xlsxInnerXML `xml:"formula1"`
|
||||||
Formula2 string `xml:",innerxml"`
|
Formula2 *xlsxInnerXML `xml:"formula2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// xlsxC collection represents a cell in the worksheet. Information about the
|
// xlsxC collection represents a cell in the worksheet. Information about the
|
||||||
|
@ -835,6 +835,24 @@ type xlsxX14Sparkline struct {
|
||||||
Sqref string `xml:"xm:sqref"`
|
Sqref string `xml:"xm:sqref"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DataValidation directly maps the settings of the data validation rule.
|
||||||
|
type DataValidation struct {
|
||||||
|
AllowBlank bool
|
||||||
|
Error *string
|
||||||
|
ErrorStyle *string
|
||||||
|
ErrorTitle *string
|
||||||
|
Operator string
|
||||||
|
Prompt *string
|
||||||
|
PromptTitle *string
|
||||||
|
ShowDropDown bool
|
||||||
|
ShowErrorMessage bool
|
||||||
|
ShowInputMessage bool
|
||||||
|
Sqref string
|
||||||
|
Type string
|
||||||
|
Formula1 string
|
||||||
|
Formula2 string
|
||||||
|
}
|
||||||
|
|
||||||
// SparklineOptions directly maps the settings of the sparkline.
|
// SparklineOptions directly maps the settings of the sparkline.
|
||||||
type SparklineOptions struct {
|
type SparklineOptions struct {
|
||||||
Location []string
|
Location []string
|
||||||
|
|
Loading…
Reference in New Issue