Support 5 new kinds of conditional formatting types
- New conditional formatting types: text, blanks, no blanks, errors, and no errors - Support calculate formula with multiple dash arithmetic symbol - Fix empty calculate result with numeric arguments in LEN, LOWER, PROPER, REPT, UPPER, and IF formula functions - Uniform double quote in calculation unit tests - Update unit tests
This commit is contained in:
parent
c7acf4fafe
commit
2499bf6b5b
45
calc.go
45
calc.go
|
@ -844,7 +844,7 @@ func (f *File) calcCellValue(ctx *calcContext, sheet, cell string) (result formu
|
|||
ps := efp.ExcelParser()
|
||||
tokens := ps.Parse(formula)
|
||||
if tokens == nil {
|
||||
return
|
||||
return f.cellResolver(ctx, sheet, cell)
|
||||
}
|
||||
result, err = f.evalInfixExp(ctx, sheet, cell, tokens)
|
||||
return
|
||||
|
@ -1225,6 +1225,12 @@ func calcAdd(rOpd, lOpd formulaArg, opdStack *Stack) error {
|
|||
|
||||
// calcSubtract evaluate subtraction arithmetic operations.
|
||||
func calcSubtract(rOpd, lOpd formulaArg, opdStack *Stack) error {
|
||||
if rOpd.Value() == "" {
|
||||
rOpd = newNumberFormulaArg(0)
|
||||
}
|
||||
if lOpd.Value() == "" {
|
||||
lOpd = newNumberFormulaArg(0)
|
||||
}
|
||||
lOpdVal := lOpd.ToNumber()
|
||||
if lOpdVal.Type != ArgNumber {
|
||||
return errors.New(lOpdVal.Value())
|
||||
|
@ -1300,22 +1306,27 @@ func calculate(opdStack *Stack, opt efp.Token) error {
|
|||
">=": calcGe,
|
||||
"&": calcSplice,
|
||||
}
|
||||
fn, ok := tokenCalcFunc[opt.TValue]
|
||||
if ok {
|
||||
if fn, ok := tokenCalcFunc[opt.TValue]; ok {
|
||||
if opdStack.Len() < 2 {
|
||||
return ErrInvalidFormula
|
||||
}
|
||||
rOpd := opdStack.Pop().(formulaArg)
|
||||
lOpd := opdStack.Pop().(formulaArg)
|
||||
if opt.TValue != "&" {
|
||||
if rOpd.Value() == "" {
|
||||
rOpd = newNumberFormulaArg(0)
|
||||
}
|
||||
if lOpd.Value() == "" {
|
||||
lOpd = newNumberFormulaArg(0)
|
||||
}
|
||||
}
|
||||
if rOpd.Type == ArgError {
|
||||
return errors.New(rOpd.Value())
|
||||
}
|
||||
if lOpd.Type == ArgError {
|
||||
return errors.New(lOpd.Value())
|
||||
}
|
||||
if err := fn(rOpd, lOpd, opdStack); err != nil {
|
||||
return err
|
||||
}
|
||||
return fn(rOpd, lOpd, opdStack)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1329,6 +1340,10 @@ func (f *File) parseOperatorPrefixToken(optStack, opdStack *Stack, token efp.Tok
|
|||
tokenPriority := getPriority(token)
|
||||
topOpt := optStack.Peek().(efp.Token)
|
||||
topOptPriority := getPriority(topOpt)
|
||||
if topOpt.TValue == "-" && topOpt.TType == efp.TokenTypeOperatorPrefix && token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix {
|
||||
optStack.Pop()
|
||||
return
|
||||
}
|
||||
if tokenPriority > topOptPriority {
|
||||
optStack.Push(token)
|
||||
return
|
||||
|
@ -13757,7 +13772,7 @@ func (fn *formulaFuncs) LEN(argsList *list.List) formulaArg {
|
|||
if argsList.Len() != 1 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "LEN requires 1 string argument")
|
||||
}
|
||||
return newNumberFormulaArg(float64(utf8.RuneCountInString(argsList.Front().Value.(formulaArg).String)))
|
||||
return newNumberFormulaArg(float64(utf8.RuneCountInString(argsList.Front().Value.(formulaArg).Value())))
|
||||
}
|
||||
|
||||
// LENB returns the number of bytes used to represent the characters in a text
|
||||
|
@ -13790,7 +13805,7 @@ func (fn *formulaFuncs) LOWER(argsList *list.List) formulaArg {
|
|||
if argsList.Len() != 1 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "LOWER requires 1 argument")
|
||||
}
|
||||
return newStringFormulaArg(strings.ToLower(argsList.Front().Value.(formulaArg).String))
|
||||
return newStringFormulaArg(strings.ToLower(argsList.Front().Value.(formulaArg).Value()))
|
||||
}
|
||||
|
||||
// MID function returns a specified number of characters from the middle of a
|
||||
|
@ -13881,7 +13896,7 @@ func (fn *formulaFuncs) PROPER(argsList *list.List) formulaArg {
|
|||
}
|
||||
buf := bytes.Buffer{}
|
||||
isLetter := false
|
||||
for _, char := range argsList.Front().Value.(formulaArg).String {
|
||||
for _, char := range argsList.Front().Value.(formulaArg).Value() {
|
||||
if !isLetter && unicode.IsLetter(char) {
|
||||
buf.WriteRune(unicode.ToUpper(char))
|
||||
} else {
|
||||
|
@ -13962,7 +13977,7 @@ func (fn *formulaFuncs) REPT(argsList *list.List) formulaArg {
|
|||
}
|
||||
buf := bytes.Buffer{}
|
||||
for i := 0; i < int(times.Number); i++ {
|
||||
buf.WriteString(text.String)
|
||||
buf.WriteString(text.Value())
|
||||
}
|
||||
return newStringFormulaArg(buf.String())
|
||||
}
|
||||
|
@ -14327,7 +14342,7 @@ func (fn *formulaFuncs) UPPER(argsList *list.List) formulaArg {
|
|||
if argsList.Len() != 1 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "UPPER requires 1 argument")
|
||||
}
|
||||
return newStringFormulaArg(strings.ToUpper(argsList.Front().Value.(formulaArg).String))
|
||||
return newStringFormulaArg(strings.ToUpper(argsList.Front().Value.(formulaArg).Value()))
|
||||
}
|
||||
|
||||
// VALUE function converts a text string into a numeric value. The syntax of
|
||||
|
@ -14405,7 +14420,7 @@ func (fn *formulaFuncs) IF(argsList *list.List) formulaArg {
|
|||
)
|
||||
switch token.Type {
|
||||
case ArgString:
|
||||
if cond, err = strconv.ParseBool(token.String); err != nil {
|
||||
if cond, err = strconv.ParseBool(token.Value()); err != nil {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, err.Error())
|
||||
}
|
||||
case ArgNumber:
|
||||
|
@ -14421,7 +14436,7 @@ func (fn *formulaFuncs) IF(argsList *list.List) formulaArg {
|
|||
case ArgNumber:
|
||||
result = value.ToNumber()
|
||||
default:
|
||||
result = newStringFormulaArg(value.String)
|
||||
result = newStringFormulaArg(value.Value())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -14431,7 +14446,7 @@ func (fn *formulaFuncs) IF(argsList *list.List) formulaArg {
|
|||
case ArgNumber:
|
||||
result = value.ToNumber()
|
||||
default:
|
||||
result = newStringFormulaArg(value.String)
|
||||
result = newStringFormulaArg(value.Value())
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -14582,7 +14597,7 @@ func compareFormulaArg(lhs, rhs, matchMode formulaArg, caseSensitive bool) byte
|
|||
}
|
||||
return criteriaG
|
||||
case ArgString:
|
||||
ls, rs := lhs.String, rhs.String
|
||||
ls, rs := lhs.Value(), rhs.Value()
|
||||
if !caseSensitive {
|
||||
ls, rs = strings.ToLower(ls), strings.ToLower(rs)
|
||||
}
|
||||
|
|
182
calc_test.go
182
calc_test.go
|
@ -58,12 +58,22 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=1>=\"-1\"": "FALSE",
|
||||
"=\"-1\">=-1": "TRUE",
|
||||
"=\"-1\">=\"-2\"": "FALSE",
|
||||
"=-----1+1": "0",
|
||||
"=------1+1": "2",
|
||||
"=---1---1": "-2",
|
||||
"=---1----1": "0",
|
||||
"=1&2": "12",
|
||||
"=15%": "0.15",
|
||||
"=1+20%": "1.2",
|
||||
"={1}+2": "3",
|
||||
"=1+{2}": "3",
|
||||
"={1}+{2}": "3",
|
||||
"=A1+(B1-C1)": "5",
|
||||
"=A1+(C1-B1)": "-3",
|
||||
"=A1&B1&C1": "14",
|
||||
"=B1+C1": "4",
|
||||
"=C1+B1": "4",
|
||||
"=C1+C1": "0",
|
||||
"=\"A\"=\"A\"": "TRUE",
|
||||
"=\"A\"<>\"A\"": "FALSE",
|
||||
"=TRUE()&FALSE()": "TRUEFALSE",
|
||||
|
@ -1764,6 +1774,8 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=LEN(D1)": "5",
|
||||
"=LEN(\"テキスト\")": "4",
|
||||
"=LEN(\"オリジナルテキスト\")": "9",
|
||||
"=LEN(7+LEN(A1&B1&C1))": "1",
|
||||
"=LEN(8+LEN(A1+(C1-B1)))": "2",
|
||||
// LENB
|
||||
"=LENB(\"\")": "0",
|
||||
"=LENB(D1)": "5",
|
||||
|
@ -2547,142 +2559,142 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=_xlfn.ARABIC(\"" + strings.Repeat("I", 256) + "\")": {"#VALUE!", "#VALUE!"},
|
||||
// ASIN
|
||||
"=ASIN()": {"#VALUE!", "ASIN requires 1 numeric argument"},
|
||||
`=ASIN("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ASIN(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// ASINH
|
||||
"=ASINH()": {"#VALUE!", "ASINH requires 1 numeric argument"},
|
||||
`=ASINH("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ASINH(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// ATAN
|
||||
"=ATAN()": {"#VALUE!", "ATAN requires 1 numeric argument"},
|
||||
`=ATAN("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ATAN(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// ATANH
|
||||
"=ATANH()": {"#VALUE!", "ATANH requires 1 numeric argument"},
|
||||
`=ATANH("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ATANH(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// ATAN2
|
||||
"=ATAN2()": {"#VALUE!", "ATAN2 requires 2 numeric arguments"},
|
||||
`=ATAN2("X",0)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=ATAN2(0,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ATAN2(\"X\",0)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ATAN2(0,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// BASE
|
||||
"=BASE()": {"#VALUE!", "BASE requires at least 2 arguments"},
|
||||
"=BASE(1,2,3,4)": {"#VALUE!", "BASE allows at most 3 arguments"},
|
||||
"=BASE(1,1)": {"#VALUE!", "radix must be an integer >= 2 and <= 36"},
|
||||
`=BASE("X",2)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=BASE(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=BASE(1,2,"X")`: {"#VALUE!", "strconv.Atoi: parsing \"X\": invalid syntax"},
|
||||
"=BASE(\"X\",2)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=BASE(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=BASE(1,2,\"X\")": {"#VALUE!", "strconv.Atoi: parsing \"X\": invalid syntax"},
|
||||
// CEILING
|
||||
"=CEILING()": {"#VALUE!", "CEILING requires at least 1 argument"},
|
||||
"=CEILING(1,2,3)": {"#VALUE!", "CEILING allows at most 2 arguments"},
|
||||
"=CEILING(1,-1)": {"#VALUE!", "negative sig to CEILING invalid"},
|
||||
`=CEILING("X",0)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=CEILING(0,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=CEILING(\"X\",0)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=CEILING(0,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// _xlfn.CEILING.MATH
|
||||
"=_xlfn.CEILING.MATH()": {"#VALUE!", "CEILING.MATH requires at least 1 argument"},
|
||||
"=_xlfn.CEILING.MATH(1,2,3,4)": {"#VALUE!", "CEILING.MATH allows at most 3 arguments"},
|
||||
`=_xlfn.CEILING.MATH("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=_xlfn.CEILING.MATH(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=_xlfn.CEILING.MATH(1,2,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.CEILING.MATH(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.CEILING.MATH(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.CEILING.MATH(1,2,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// _xlfn.CEILING.PRECISE
|
||||
"=_xlfn.CEILING.PRECISE()": {"#VALUE!", "CEILING.PRECISE requires at least 1 argument"},
|
||||
"=_xlfn.CEILING.PRECISE(1,2,3)": {"#VALUE!", "CEILING.PRECISE allows at most 2 arguments"},
|
||||
`=_xlfn.CEILING.PRECISE("X",2)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=_xlfn.CEILING.PRECISE(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.CEILING.PRECISE(\"X\",2)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.CEILING.PRECISE(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// COMBIN
|
||||
"=COMBIN()": {"#VALUE!", "COMBIN requires 2 argument"},
|
||||
"=COMBIN(-1,1)": {"#VALUE!", "COMBIN requires number >= number_chosen"},
|
||||
`=COMBIN("X",1)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=COMBIN(-1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=COMBIN(\"X\",1)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=COMBIN(-1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// _xlfn.COMBINA
|
||||
"=_xlfn.COMBINA()": {"#VALUE!", "COMBINA requires 2 argument"},
|
||||
"=_xlfn.COMBINA(-1,1)": {"#VALUE!", "COMBINA requires number > number_chosen"},
|
||||
"=_xlfn.COMBINA(-1,-1)": {"#VALUE!", "COMBIN requires number >= number_chosen"},
|
||||
`=_xlfn.COMBINA("X",1)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=_xlfn.COMBINA(-1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.COMBINA(\"X\",1)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.COMBINA(-1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// COS
|
||||
"=COS()": {"#VALUE!", "COS requires 1 numeric argument"},
|
||||
`=COS("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=COS(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// COSH
|
||||
"=COSH()": {"#VALUE!", "COSH requires 1 numeric argument"},
|
||||
`=COSH("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=COSH(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// _xlfn.COT
|
||||
"=COT()": {"#VALUE!", "COT requires 1 numeric argument"},
|
||||
`=COT("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=COT(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=COT(0)": {"#DIV/0!", "#DIV/0!"},
|
||||
// _xlfn.COTH
|
||||
"=COTH()": {"#VALUE!", "COTH requires 1 numeric argument"},
|
||||
`=COTH("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=COTH(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=COTH(0)": {"#DIV/0!", "#DIV/0!"},
|
||||
// _xlfn.CSC
|
||||
"=_xlfn.CSC()": {"#VALUE!", "CSC requires 1 numeric argument"},
|
||||
`=_xlfn.CSC("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.CSC(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.CSC(0)": {"#DIV/0!", "#DIV/0!"},
|
||||
// _xlfn.CSCH
|
||||
"=_xlfn.CSCH()": {"#VALUE!", "CSCH requires 1 numeric argument"},
|
||||
`=_xlfn.CSCH("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.CSCH(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.CSCH(0)": {"#DIV/0!", "#DIV/0!"},
|
||||
// _xlfn.DECIMAL
|
||||
"=_xlfn.DECIMAL()": {"#VALUE!", "DECIMAL requires 2 numeric arguments"},
|
||||
`=_xlfn.DECIMAL("X",2)`: {"#VALUE!", "strconv.ParseInt: parsing \"X\": invalid syntax"},
|
||||
`=_xlfn.DECIMAL(2000,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.DECIMAL(\"X\",2)": {"#VALUE!", "strconv.ParseInt: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.DECIMAL(2000,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// DEGREES
|
||||
"=DEGREES()": {"#VALUE!", "DEGREES requires 1 numeric argument"},
|
||||
`=DEGREES("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=DEGREES(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=DEGREES(0)": {"#DIV/0!", "#DIV/0!"},
|
||||
// EVEN
|
||||
"=EVEN()": {"#VALUE!", "EVEN requires 1 numeric argument"},
|
||||
`=EVEN("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=EVEN(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// EXP
|
||||
"=EXP()": {"#VALUE!", "EXP requires 1 numeric argument"},
|
||||
`=EXP("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=EXP(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// FACT
|
||||
"=FACT()": {"#VALUE!", "FACT requires 1 numeric argument"},
|
||||
`=FACT("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=FACT(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=FACT(-1)": {"#NUM!", "#NUM!"},
|
||||
// FACTDOUBLE
|
||||
"=FACTDOUBLE()": {"#VALUE!", "FACTDOUBLE requires 1 numeric argument"},
|
||||
`=FACTDOUBLE("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=FACTDOUBLE(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=FACTDOUBLE(-1)": {"#NUM!", "#NUM!"},
|
||||
// FLOOR
|
||||
"=FLOOR()": {"#VALUE!", "FLOOR requires 2 numeric arguments"},
|
||||
`=FLOOR("X",-1)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=FLOOR(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=FLOOR(\"X\",-1)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=FLOOR(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=FLOOR(1,-1)": {"#NUM!", "invalid arguments to FLOOR"},
|
||||
// _xlfn.FLOOR.MATH
|
||||
"=_xlfn.FLOOR.MATH()": {"#VALUE!", "FLOOR.MATH requires at least 1 argument"},
|
||||
"=_xlfn.FLOOR.MATH(1,2,3,4)": {"#VALUE!", "FLOOR.MATH allows at most 3 arguments"},
|
||||
`=_xlfn.FLOOR.MATH("X",2,3)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=_xlfn.FLOOR.MATH(1,"X",3)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=_xlfn.FLOOR.MATH(1,2,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.FLOOR.MATH(\"X\",2,3)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.FLOOR.MATH(1,\"X\",3)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.FLOOR.MATH(1,2,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// _xlfn.FLOOR.PRECISE
|
||||
"=_xlfn.FLOOR.PRECISE()": {"#VALUE!", "FLOOR.PRECISE requires at least 1 argument"},
|
||||
"=_xlfn.FLOOR.PRECISE(1,2,3)": {"#VALUE!", "FLOOR.PRECISE allows at most 2 arguments"},
|
||||
`=_xlfn.FLOOR.PRECISE("X",2)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=_xlfn.FLOOR.PRECISE(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.FLOOR.PRECISE(\"X\",2)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.FLOOR.PRECISE(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// GCD
|
||||
"=GCD()": {"#VALUE!", "GCD requires at least 1 argument"},
|
||||
"=GCD(\"\")": {"#VALUE!", "strconv.ParseFloat: parsing \"\": invalid syntax"},
|
||||
"=GCD(-1)": {"#VALUE!", "GCD only accepts positive arguments"},
|
||||
"=GCD(1,-1)": {"#VALUE!", "GCD only accepts positive arguments"},
|
||||
`=GCD("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=GCD(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// INT
|
||||
"=INT()": {"#VALUE!", "INT requires 1 numeric argument"},
|
||||
`=INT("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=INT(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// ISO.CEILING
|
||||
"=ISO.CEILING()": {"#VALUE!", "ISO.CEILING requires at least 1 argument"},
|
||||
"=ISO.CEILING(1,2,3)": {"#VALUE!", "ISO.CEILING allows at most 2 arguments"},
|
||||
`=ISO.CEILING("X",2)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=ISO.CEILING(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ISO.CEILING(\"X\",2)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ISO.CEILING(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// LCM
|
||||
"=LCM()": {"#VALUE!", "LCM requires at least 1 argument"},
|
||||
"=LCM(-1)": {"#VALUE!", "LCM only accepts positive arguments"},
|
||||
"=LCM(1,-1)": {"#VALUE!", "LCM only accepts positive arguments"},
|
||||
`=LCM("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=LCM(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// LN
|
||||
"=LN()": {"#VALUE!", "LN requires 1 numeric argument"},
|
||||
"=LN(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// LOG
|
||||
"=LOG()": {"#VALUE!", "LOG requires at least 1 argument"},
|
||||
"=LOG(1,2,3)": {"#VALUE!", "LOG allows at most 2 arguments"},
|
||||
`=LOG("X",1)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=LOG(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=LOG(\"X\",1)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=LOG(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=LOG(0,0)": {"#NUM!", "#DIV/0!"},
|
||||
"=LOG(1,0)": {"#NUM!", "#DIV/0!"},
|
||||
"=LOG(1,1)": {"#DIV/0!", "#DIV/0!"},
|
||||
|
@ -2704,28 +2716,28 @@ func TestCalcCellValue(t *testing.T) {
|
|||
// MOD
|
||||
"=MOD()": {"#VALUE!", "MOD requires 2 numeric arguments"},
|
||||
"=MOD(6,0)": {"#DIV/0!", "MOD divide by zero"},
|
||||
`=MOD("X",0)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=MOD(6,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=MOD(\"X\",0)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=MOD(6,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// MROUND
|
||||
"=MROUND()": {"#VALUE!", "MROUND requires 2 numeric arguments"},
|
||||
"=MROUND(1,0)": {"#NUM!", "#NUM!"},
|
||||
"=MROUND(1,-1)": {"#NUM!", "#NUM!"},
|
||||
`=MROUND("X",0)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=MROUND(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=MROUND(\"X\",0)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=MROUND(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// MULTINOMIAL
|
||||
`=MULTINOMIAL("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=MULTINOMIAL(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// _xlfn.MUNIT
|
||||
"=_xlfn.MUNIT()": {"#VALUE!", "MUNIT requires 1 numeric argument"},
|
||||
`=_xlfn.MUNIT("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.MUNIT(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.MUNIT(-1)": {"#VALUE!", ""},
|
||||
// ODD
|
||||
"=ODD()": {"#VALUE!", "ODD requires 1 numeric argument"},
|
||||
`=ODD("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ODD(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// PI
|
||||
"=PI(1)": {"#VALUE!", "PI accepts no arguments"},
|
||||
// POWER
|
||||
`=POWER("X",1)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=POWER(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=POWER(\"X\",1)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=POWER(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=POWER(0,0)": {"#NUM!", "#NUM!"},
|
||||
"=POWER(0,-1)": {"#DIV/0!", "#DIV/0!"},
|
||||
"=POWER(1)": {"#VALUE!", "POWER requires 2 numeric arguments"},
|
||||
|
@ -2733,18 +2745,18 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=PRODUCT(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=PRODUCT(\"\",3,6)": {"#VALUE!", "strconv.ParseFloat: parsing \"\": invalid syntax"},
|
||||
// QUOTIENT
|
||||
`=QUOTIENT("X",1)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=QUOTIENT(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=QUOTIENT(\"X\",1)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=QUOTIENT(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=QUOTIENT(1,0)": {"#DIV/0!", "#DIV/0!"},
|
||||
"=QUOTIENT(1)": {"#VALUE!", "QUOTIENT requires 2 numeric arguments"},
|
||||
// RADIANS
|
||||
`=RADIANS("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=RADIANS(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=RADIANS()": {"#VALUE!", "RADIANS requires 1 numeric argument"},
|
||||
// RAND
|
||||
"=RAND(1)": {"#VALUE!", "RAND accepts no arguments"},
|
||||
// RANDBETWEEN
|
||||
`=RANDBETWEEN("X",1)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=RANDBETWEEN(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=RANDBETWEEN(\"X\",1)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=RANDBETWEEN(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=RANDBETWEEN()": {"#VALUE!", "RANDBETWEEN requires 2 numeric arguments"},
|
||||
"=RANDBETWEEN(2,1)": {"#NUM!", "#NUM!"},
|
||||
// ROMAN
|
||||
|
@ -2755,16 +2767,16 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=ROMAN(\"\",1)": {"#VALUE!", "strconv.ParseFloat: parsing \"\": invalid syntax"},
|
||||
// ROUND
|
||||
"=ROUND()": {"#VALUE!", "ROUND requires 2 numeric arguments"},
|
||||
`=ROUND("X",1)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=ROUND(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ROUND(\"X\",1)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ROUND(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// ROUNDDOWN
|
||||
"=ROUNDDOWN()": {"#VALUE!", "ROUNDDOWN requires 2 numeric arguments"},
|
||||
`=ROUNDDOWN("X",1)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=ROUNDDOWN(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ROUNDDOWN(\"X\",1)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ROUNDDOWN(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// ROUNDUP
|
||||
"=ROUNDUP()": {"#VALUE!", "ROUNDUP requires 2 numeric arguments"},
|
||||
`=ROUNDUP("X",1)`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
`=ROUNDUP(1,"X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ROUNDUP(\"X\",1)": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=ROUNDUP(1,\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// SEARCH
|
||||
"=SEARCH()": {"#VALUE!", "SEARCH requires at least 2 arguments"},
|
||||
"=SEARCH(1,A1,1,1)": {"#VALUE!", "SEARCH allows at most 3 arguments"},
|
||||
|
@ -2778,10 +2790,10 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=SEARCHB(1,A1,\"\")": {"#VALUE!", "strconv.ParseFloat: parsing \"\": invalid syntax"},
|
||||
// SEC
|
||||
"=_xlfn.SEC()": {"#VALUE!", "SEC requires 1 numeric argument"},
|
||||
`=_xlfn.SEC("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.SEC(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// _xlfn.SECH
|
||||
"=_xlfn.SECH()": {"#VALUE!", "SECH requires 1 numeric argument"},
|
||||
`=_xlfn.SECH("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=_xlfn.SECH(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// SERIESSUM
|
||||
"=SERIESSUM()": {"#VALUE!", "SERIESSUM requires 4 arguments"},
|
||||
"=SERIESSUM(\"\",2,3,A1:A4)": {"#VALUE!", "strconv.ParseFloat: parsing \"\": invalid syntax"},
|
||||
|
@ -2790,21 +2802,21 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=SERIESSUM(1,2,3,A1:D1)": {"#VALUE!", "strconv.ParseFloat: parsing \"Month\": invalid syntax"},
|
||||
// SIGN
|
||||
"=SIGN()": {"#VALUE!", "SIGN requires 1 numeric argument"},
|
||||
`=SIGN("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=SIGN(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// SIN
|
||||
"=SIN()": {"#VALUE!", "SIN requires 1 numeric argument"},
|
||||
`=SIN("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=SIN(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// SINH
|
||||
"=SINH()": {"#VALUE!", "SINH requires 1 numeric argument"},
|
||||
`=SINH("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=SINH(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// SQRT
|
||||
"=SQRT()": {"#VALUE!", "SQRT requires 1 numeric argument"},
|
||||
`=SQRT("")`: {"#VALUE!", "strconv.ParseFloat: parsing \"\": invalid syntax"},
|
||||
`=SQRT("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=SQRT(\"\")": {"#VALUE!", "strconv.ParseFloat: parsing \"\": invalid syntax"},
|
||||
"=SQRT(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=SQRT(-1)": {"#NUM!", "#NUM!"},
|
||||
// SQRTPI
|
||||
"=SQRTPI()": {"#VALUE!", "SQRTPI requires 1 numeric argument"},
|
||||
`=SQRTPI("X")`: {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
"=SQRTPI(\"X\")": {"#VALUE!", "strconv.ParseFloat: parsing \"X\": invalid syntax"},
|
||||
// STDEV
|
||||
"=STDEV()": {"#VALUE!", "STDEV requires at least 1 argument"},
|
||||
"=STDEV(E2:E9)": {"#DIV/0!", "#DIV/0!"},
|
||||
|
@ -4666,7 +4678,7 @@ func TestCalcCellValue(t *testing.T) {
|
|||
f := prepareCalcData(cellData)
|
||||
result, err := f.CalcCellValue("Sheet1", "A1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", result)
|
||||
assert.Equal(t, "1", result)
|
||||
// Test get calculated cell value on not exists worksheet
|
||||
f = prepareCalcData(cellData)
|
||||
_, err = f.CalcCellValue("SheetN", "A1")
|
||||
|
@ -4718,7 +4730,7 @@ func TestCalcWithDefinedName(t *testing.T) {
|
|||
assert.NoError(t, f.SetCellFormula("Sheet1", "D1", "=IF(\"B1_as_string\"=defined_name1,\"YES\",\"NO\")"))
|
||||
result, err = f.CalcCellValue("Sheet1", "D1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "YES", result, `=IF("B1_as_string"=defined_name1,"YES","NO")`)
|
||||
assert.Equal(t, "YES", result, "=IF(\"B1_as_string\"=defined_name1,\"YES\",\"NO\")")
|
||||
}
|
||||
|
||||
func TestCalcISBLANK(t *testing.T) {
|
||||
|
@ -6140,14 +6152,14 @@ func TestCalcBetainvProbIterator(t *testing.T) {
|
|||
func TestNestedFunctionsWithOperators(t *testing.T) {
|
||||
f := NewFile()
|
||||
formulaList := map[string]string{
|
||||
`=LEN("KEEP")`: "4",
|
||||
`=LEN("REMOVEKEEP") - LEN("REMOVE")`: "4",
|
||||
`=RIGHT("REMOVEKEEP", 4)`: "KEEP",
|
||||
`=RIGHT("REMOVEKEEP", 10 - 6))`: "KEEP",
|
||||
`=RIGHT("REMOVEKEEP", LEN("REMOVEKEEP") - 6)`: "KEEP",
|
||||
`=RIGHT("REMOVEKEEP", LEN("REMOVEKEEP") - LEN("REMOV") - 1)`: "KEEP",
|
||||
`=RIGHT("REMOVEKEEP", 10 - LEN("REMOVE"))`: "KEEP",
|
||||
`=RIGHT("REMOVEKEEP", LEN("REMOVEKEEP") - LEN("REMOVE"))`: "KEEP",
|
||||
"=LEN(\"KEEP\")": "4",
|
||||
"=LEN(\"REMOVEKEEP\") - LEN(\"REMOVE\")": "4",
|
||||
"=RIGHT(\"REMOVEKEEP\", 4)": "KEEP",
|
||||
"=RIGHT(\"REMOVEKEEP\", 10 - 6))": "KEEP",
|
||||
"=RIGHT(\"REMOVEKEEP\", LEN(\"REMOVEKEEP\") - 6)": "KEEP",
|
||||
"=RIGHT(\"REMOVEKEEP\", LEN(\"REMOVEKEEP\") - LEN(\"REMOV\") - 1)": "KEEP",
|
||||
"=RIGHT(\"REMOVEKEEP\", 10 - LEN(\"REMOVE\"))": "KEEP",
|
||||
"=RIGHT(\"REMOVEKEEP\", LEN(\"REMOVEKEEP\") - LEN(\"REMOVE\"))": "KEEP",
|
||||
}
|
||||
for formula, expected := range formulaList {
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "E1", formula))
|
||||
|
|
|
@ -1202,16 +1202,18 @@ func TestConditionalFormat(t *testing.T) {
|
|||
},
|
||||
},
|
||||
))
|
||||
// Test set conditional format with invalid cell reference
|
||||
assert.Equal(t, newCellNameToCoordinatesError("-", newInvalidCellNameError("-")), f.SetConditionalFormat("Sheet1", "A1:-", nil))
|
||||
// Test set conditional format on not exists worksheet
|
||||
assert.EqualError(t, f.SetConditionalFormat("SheetN", "L1:L10", nil), "sheet SheetN does not exist")
|
||||
// Test set conditional format with invalid sheet name
|
||||
assert.EqualError(t, f.SetConditionalFormat("Sheet:1", "L1:L10", nil), ErrSheetNameInvalid.Error())
|
||||
assert.Equal(t, ErrSheetNameInvalid, f.SetConditionalFormat("Sheet:1", "L1:L10", nil))
|
||||
|
||||
err = f.SaveAs(filepath.Join("test", "TestConditionalFormat.xlsx"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Set conditional format with illegal valid type
|
||||
assert.NoError(t, f.SetConditionalFormat(sheet1, "K1:K10",
|
||||
assert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat(sheet1, "K1:K10",
|
||||
[]ConditionalFormatOptions{
|
||||
{
|
||||
Type: "",
|
||||
|
|
194
styles.go
194
styles.go
|
@ -33,12 +33,12 @@ var validType = map[string]string{
|
|||
"unique": "uniqueValues",
|
||||
"top": "top10",
|
||||
"bottom": "top10",
|
||||
"text": "text", // Doesn't support currently
|
||||
"text": "text",
|
||||
"time_period": "timePeriod", // Doesn't support currently
|
||||
"blanks": "containsBlanks", // Doesn't support currently
|
||||
"no_blanks": "notContainsBlanks", // Doesn't support currently
|
||||
"errors": "containsErrors", // Doesn't support currently
|
||||
"no_errors": "notContainsErrors", // Doesn't support currently
|
||||
"blanks": "containsBlanks",
|
||||
"no_blanks": "notContainsBlanks",
|
||||
"errors": "containsErrors",
|
||||
"no_errors": "notContainsErrors",
|
||||
"2_color_scale": "2_color_scale",
|
||||
"3_color_scale": "3_color_scale",
|
||||
"data_bar": "dataBar",
|
||||
|
@ -1223,12 +1223,17 @@ var (
|
|||
},
|
||||
}
|
||||
// drawContFmtFunc defines functions to create conditional formats.
|
||||
drawContFmtFunc = map[string]func(p int, ct, GUID string, fmtCond *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule){
|
||||
drawContFmtFunc = map[string]func(p int, ct, ref, GUID string, fmtCond *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule){
|
||||
"cellIs": drawCondFmtCellIs,
|
||||
"text": drawCondFmtText,
|
||||
"top10": drawCondFmtTop10,
|
||||
"aboveAverage": drawCondFmtAboveAverage,
|
||||
"duplicateValues": drawCondFmtDuplicateUniqueValues,
|
||||
"uniqueValues": drawCondFmtDuplicateUniqueValues,
|
||||
"containsBlanks": drawCondFmtBlanks,
|
||||
"notContainsBlanks": drawCondFmtNoBlanks,
|
||||
"containsErrors": drawCondFmtErrors,
|
||||
"notContainsErrors": drawCondFmtNoErrors,
|
||||
"2_color_scale": drawCondFmtColorScale,
|
||||
"3_color_scale": drawCondFmtColorScale,
|
||||
"dataBar": drawCondFmtDataBar,
|
||||
|
@ -1238,10 +1243,18 @@ var (
|
|||
// extractContFmtFunc defines functions to get conditional formats.
|
||||
extractContFmtFunc = map[string]func(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions{
|
||||
"cellIs": extractCondFmtCellIs,
|
||||
"containsText": extractCondFmtText,
|
||||
"notContainsText": extractCondFmtText,
|
||||
"beginsWith": extractCondFmtText,
|
||||
"endsWith": extractCondFmtText,
|
||||
"top10": extractCondFmtTop10,
|
||||
"aboveAverage": extractCondFmtAboveAverage,
|
||||
"duplicateValues": extractCondFmtDuplicateUniqueValues,
|
||||
"uniqueValues": extractCondFmtDuplicateUniqueValues,
|
||||
"containsBlanks": extractCondFmtBlanks,
|
||||
"notContainsBlanks": extractCondFmtNoBlanks,
|
||||
"containsErrors": extractCondFmtErrors,
|
||||
"notContainsErrors": extractCondFmtNoErrors,
|
||||
"colorScale": extractCondFmtColorScale,
|
||||
"dataBar": extractCondFmtDataBar,
|
||||
"expression": extractCondFmtExp,
|
||||
|
@ -2635,13 +2648,31 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(rangeRef, ":") {
|
||||
rect, err := rangeRefToCoordinates(rangeRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = sortCoordinates(rect)
|
||||
rangeRef, _ = f.coordinatesToRangeRef(rect, strings.Contains(rangeRef, "$"))
|
||||
}
|
||||
// Create a pseudo GUID for each unique rule.
|
||||
var rules int
|
||||
for _, cf := range ws.ConditionalFormatting {
|
||||
rules += len(cf.CfRule)
|
||||
}
|
||||
GUID := fmt.Sprintf("{00000000-0000-0000-%04X-%012X}", f.getSheetID(sheet), rules)
|
||||
var cfRule []*xlsxCfRule
|
||||
var (
|
||||
GUID = fmt.Sprintf("{00000000-0000-0000-%04X-%012X}", f.getSheetID(sheet), rules)
|
||||
cfRule []*xlsxCfRule
|
||||
noCriteriaTypes = []string{
|
||||
"containsBlanks",
|
||||
"notContainsBlanks",
|
||||
"containsErrors",
|
||||
"notContainsErrors",
|
||||
"expression",
|
||||
"iconSet",
|
||||
}
|
||||
)
|
||||
for p, v := range opts {
|
||||
var vt, ct string
|
||||
var ok bool
|
||||
|
@ -2650,10 +2681,10 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
|
|||
if ok {
|
||||
// Check for valid criteria types.
|
||||
ct, ok = criteriaType[v.Criteria]
|
||||
if ok || vt == "expression" || vt == "iconSet" {
|
||||
if ok || inStrSlice(noCriteriaTypes, vt, true) != -1 {
|
||||
drawFunc, ok := drawContFmtFunc[vt]
|
||||
if ok {
|
||||
rule, x14rule := drawFunc(p, ct, GUID, &v)
|
||||
rule, x14rule := drawFunc(p, ct, strings.Split(rangeRef, ":")[0], GUID, &v)
|
||||
if rule == nil {
|
||||
return ErrParameterInvalid
|
||||
}
|
||||
|
@ -2669,6 +2700,7 @@ func (f *File) SetConditionalFormat(sheet, rangeRef string, opts []ConditionalFo
|
|||
}
|
||||
return ErrParameterInvalid
|
||||
}
|
||||
return ErrParameterInvalid
|
||||
}
|
||||
|
||||
ws.ConditionalFormatting = append(ws.ConditionalFormatting, &xlsxConditionalFormatting{
|
||||
|
@ -2740,6 +2772,12 @@ func extractCondFmtCellIs(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOp
|
|||
return format
|
||||
}
|
||||
|
||||
// extractCondFmtText provides a function to extract conditional format
|
||||
// settings for text cell values by given conditional formatting rule.
|
||||
func extractCondFmtText(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
|
||||
return ConditionalFormatOptions{StopIfTrue: c.StopIfTrue, Type: "text", Criteria: operatorType[c.Operator], Format: *c.DxfID, Value: c.Text}
|
||||
}
|
||||
|
||||
// extractCondFmtTop10 provides a function to extract conditional format
|
||||
// settings for top N (default is top 10) by given conditional formatting
|
||||
// rule.
|
||||
|
@ -2786,6 +2824,46 @@ func extractCondFmtDuplicateUniqueValues(c *xlsxCfRule, extLst *xlsxExtLst) Cond
|
|||
}
|
||||
}
|
||||
|
||||
// extractCondFmtBlanks provides a function to extract conditional format
|
||||
// settings for blank cells by given conditional formatting rule.
|
||||
func extractCondFmtBlanks(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
|
||||
return ConditionalFormatOptions{
|
||||
StopIfTrue: c.StopIfTrue,
|
||||
Type: "blanks",
|
||||
Format: *c.DxfID,
|
||||
}
|
||||
}
|
||||
|
||||
// extractCondFmtNoBlanks provides a function to extract conditional format
|
||||
// settings for no blank cells by given conditional formatting rule.
|
||||
func extractCondFmtNoBlanks(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
|
||||
return ConditionalFormatOptions{
|
||||
StopIfTrue: c.StopIfTrue,
|
||||
Type: "no_blanks",
|
||||
Format: *c.DxfID,
|
||||
}
|
||||
}
|
||||
|
||||
// extractCondFmtErrors provides a function to extract conditional format
|
||||
// settings for cells with errors by given conditional formatting rule.
|
||||
func extractCondFmtErrors(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
|
||||
return ConditionalFormatOptions{
|
||||
StopIfTrue: c.StopIfTrue,
|
||||
Type: "errors",
|
||||
Format: *c.DxfID,
|
||||
}
|
||||
}
|
||||
|
||||
// extractCondFmtNoErrors provides a function to extract conditional format
|
||||
// settings for cells without errors by given conditional formatting rule.
|
||||
func extractCondFmtNoErrors(c *xlsxCfRule, extLst *xlsxExtLst) ConditionalFormatOptions {
|
||||
return ConditionalFormatOptions{
|
||||
StopIfTrue: c.StopIfTrue,
|
||||
Type: "no_errors",
|
||||
Format: *c.DxfID,
|
||||
}
|
||||
}
|
||||
|
||||
// extractCondFmtColorScale provides a function to extract conditional format
|
||||
// settings for color scale (include 2 color scale and 3 color scale) by given
|
||||
// conditional formatting rule.
|
||||
|
@ -2938,7 +3016,7 @@ func (f *File) UnsetConditionalFormat(sheet, rangeRef string) error {
|
|||
// drawCondFmtCellIs provides a function to create conditional formatting rule
|
||||
// for cell value (include between, not between, equal, not equal, greater
|
||||
// than and less than) by given priority, criteria type and format settings.
|
||||
func drawCondFmtCellIs(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
func drawCondFmtCellIs(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
c := &xlsxCfRule{
|
||||
Priority: p + 1,
|
||||
StopIfTrue: format.StopIfTrue,
|
||||
|
@ -2950,16 +3028,46 @@ func drawCondFmtCellIs(p int, ct, GUID string, format *ConditionalFormatOptions)
|
|||
if ct == "between" || ct == "notBetween" {
|
||||
c.Formula = append(c.Formula, []string{format.MinValue, format.MaxValue}...)
|
||||
}
|
||||
if idx := inStrSlice([]string{"equal", "notEqual", "greaterThan", "lessThan", "greaterThanOrEqual", "lessThanOrEqual", "containsText", "notContains", "beginsWith", "endsWith"}, ct, true); idx != -1 {
|
||||
if inStrSlice([]string{"equal", "notEqual", "greaterThan", "lessThan", "greaterThanOrEqual", "lessThanOrEqual", "containsText", "notContains", "beginsWith", "endsWith"}, ct, true) != -1 {
|
||||
c.Formula = append(c.Formula, format.Value)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// drawCondFmtText provides a function to create conditional formatting rule for
|
||||
// text cell values by given priority, criteria type and format settings.
|
||||
func drawCondFmtText(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
return &xlsxCfRule{
|
||||
Priority: p + 1,
|
||||
StopIfTrue: format.StopIfTrue,
|
||||
Type: map[string]string{
|
||||
"containsText": "containsText",
|
||||
"notContains": "notContainsText",
|
||||
"beginsWith": "beginsWith",
|
||||
"endsWith": "endsWith",
|
||||
}[ct],
|
||||
Text: format.Value,
|
||||
Operator: ct,
|
||||
Formula: []string{
|
||||
map[string]string{
|
||||
"containsText": fmt.Sprintf("NOT(ISERROR(SEARCH(\"%s\",%s)))",
|
||||
strings.NewReplacer(`"`, `""`).Replace(format.Value), ref),
|
||||
"notContains": fmt.Sprintf("ISERROR(SEARCH(\"%s\",%s))",
|
||||
strings.NewReplacer(`"`, `""`).Replace(format.Value), ref),
|
||||
"beginsWith": fmt.Sprintf("LEFT(%[2]s,LEN(\"%[1]s\"))=\"%[1]s\"",
|
||||
strings.NewReplacer(`"`, `""`).Replace(format.Value), ref),
|
||||
"endsWith": fmt.Sprintf("RIGHT(%[2]s,LEN(\"%[1]s\"))=\"%[1]s\"",
|
||||
strings.NewReplacer(`"`, `""`).Replace(format.Value), ref),
|
||||
}[ct],
|
||||
},
|
||||
DxfID: intPtr(format.Format),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// drawCondFmtTop10 provides a function to create conditional formatting rule
|
||||
// for top N (default is top 10) by given priority, criteria type and format
|
||||
// settings.
|
||||
func drawCondFmtTop10(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
func drawCondFmtTop10(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
c := &xlsxCfRule{
|
||||
Priority: p + 1,
|
||||
StopIfTrue: format.StopIfTrue,
|
||||
|
@ -2978,7 +3086,7 @@ func drawCondFmtTop10(p int, ct, GUID string, format *ConditionalFormatOptions)
|
|||
// drawCondFmtAboveAverage provides a function to create conditional
|
||||
// formatting rule for above average and below average by given priority,
|
||||
// criteria type and format settings.
|
||||
func drawCondFmtAboveAverage(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
func drawCondFmtAboveAverage(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
return &xlsxCfRule{
|
||||
Priority: p + 1,
|
||||
StopIfTrue: format.StopIfTrue,
|
||||
|
@ -2991,7 +3099,7 @@ func drawCondFmtAboveAverage(p int, ct, GUID string, format *ConditionalFormatOp
|
|||
// drawCondFmtDuplicateUniqueValues provides a function to create conditional
|
||||
// formatting rule for duplicate and unique values by given priority, criteria
|
||||
// type and format settings.
|
||||
func drawCondFmtDuplicateUniqueValues(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
func drawCondFmtDuplicateUniqueValues(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
return &xlsxCfRule{
|
||||
Priority: p + 1,
|
||||
StopIfTrue: format.StopIfTrue,
|
||||
|
@ -3003,7 +3111,7 @@ func drawCondFmtDuplicateUniqueValues(p int, ct, GUID string, format *Conditiona
|
|||
// drawCondFmtColorScale provides a function to create conditional formatting
|
||||
// rule for color scale (include 2 color scale and 3 color scale) by given
|
||||
// priority, criteria type and format settings.
|
||||
func drawCondFmtColorScale(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
func drawCondFmtColorScale(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
minValue := format.MinValue
|
||||
if minValue == "" {
|
||||
minValue = "0"
|
||||
|
@ -3041,7 +3149,7 @@ func drawCondFmtColorScale(p int, ct, GUID string, format *ConditionalFormatOpti
|
|||
|
||||
// drawCondFmtDataBar provides a function to create conditional formatting
|
||||
// rule for data bar by given priority, criteria type and format settings.
|
||||
func drawCondFmtDataBar(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
func drawCondFmtDataBar(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
var x14CfRule *xlsxX14CfRule
|
||||
var extLst *xlsxExtLst
|
||||
if format.BarSolid || format.BarDirection == "leftToRight" || format.BarDirection == "rightToLeft" || format.BarBorderColor != "" {
|
||||
|
@ -3078,7 +3186,7 @@ func drawCondFmtDataBar(p int, ct, GUID string, format *ConditionalFormatOptions
|
|||
|
||||
// drawCondFmtExp provides a function to create conditional formatting rule
|
||||
// for expression by given priority, criteria type and format settings.
|
||||
func drawCondFmtExp(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
func drawCondFmtExp(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
return &xlsxCfRule{
|
||||
Priority: p + 1,
|
||||
StopIfTrue: format.StopIfTrue,
|
||||
|
@ -3088,9 +3196,57 @@ func drawCondFmtExp(p int, ct, GUID string, format *ConditionalFormatOptions) (*
|
|||
}, nil
|
||||
}
|
||||
|
||||
// drawCondFmtErrors provides a function to create conditional formatting rule
|
||||
// for cells with errors by given priority, criteria type and format settings.
|
||||
func drawCondFmtErrors(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
return &xlsxCfRule{
|
||||
Priority: p + 1,
|
||||
StopIfTrue: format.StopIfTrue,
|
||||
Type: validType[format.Type],
|
||||
Formula: []string{fmt.Sprintf("ISERROR(%s)", ref)},
|
||||
DxfID: intPtr(format.Format),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// drawCondFmtErrors provides a function to create conditional formatting rule
|
||||
// for cells without errors by given priority, criteria type and format settings.
|
||||
func drawCondFmtNoErrors(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
return &xlsxCfRule{
|
||||
Priority: p + 1,
|
||||
StopIfTrue: format.StopIfTrue,
|
||||
Type: validType[format.Type],
|
||||
Formula: []string{fmt.Sprintf("NOT(ISERROR(%s))", ref)},
|
||||
DxfID: intPtr(format.Format),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// drawCondFmtErrors provides a function to create conditional formatting rule
|
||||
// for blank cells by given priority, criteria type and format settings.
|
||||
func drawCondFmtBlanks(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
return &xlsxCfRule{
|
||||
Priority: p + 1,
|
||||
StopIfTrue: format.StopIfTrue,
|
||||
Type: validType[format.Type],
|
||||
Formula: []string{fmt.Sprintf("LEN(TRIM(%s))=0", ref)},
|
||||
DxfID: intPtr(format.Format),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// drawCondFmtErrors provides a function to create conditional formatting rule
|
||||
// for no blanks cells by given priority, criteria type and format settings.
|
||||
func drawCondFmtNoBlanks(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
return &xlsxCfRule{
|
||||
Priority: p + 1,
|
||||
StopIfTrue: format.StopIfTrue,
|
||||
Type: validType[format.Type],
|
||||
Formula: []string{fmt.Sprintf("LEN(TRIM(%s))>0", ref)},
|
||||
DxfID: intPtr(format.Format),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// drawCondFmtIconSet provides a function to create conditional formatting rule
|
||||
// for icon set by given priority, criteria type and format settings.
|
||||
func drawCondFmtIconSet(p int, ct, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
func drawCondFmtIconSet(p int, ct, ref, GUID string, format *ConditionalFormatOptions) (*xlsxCfRule, *xlsxX14CfRule) {
|
||||
cfvo3 := &xlsxCfRule{IconSet: &xlsxIconSet{Cfvo: []*xlsxCfvo{
|
||||
{Type: "percent", Val: "0"},
|
||||
{Type: "percent", Val: "33"},
|
||||
|
|
|
@ -195,12 +195,7 @@ func TestSetConditionalFormat(t *testing.T) {
|
|||
for _, val := range []string{
|
||||
"date",
|
||||
"time",
|
||||
"text",
|
||||
"time_period",
|
||||
"blanks",
|
||||
"no_blanks",
|
||||
"errors",
|
||||
"no_errors",
|
||||
} {
|
||||
assert.Equal(t, ErrParameterInvalid, f.SetConditionalFormat("Sheet1", "A1", []ConditionalFormatOptions{{Type: val}}))
|
||||
}
|
||||
|
@ -210,6 +205,10 @@ func TestGetConditionalFormats(t *testing.T) {
|
|||
for _, format := range [][]ConditionalFormatOptions{
|
||||
{{Type: "cell", Format: 1, Criteria: "greater than", Value: "6"}},
|
||||
{{Type: "cell", Format: 1, Criteria: "between", MinValue: "6", MaxValue: "8"}},
|
||||
{{Type: "text", Format: 1, Criteria: "containing", Value: "~!@#$%^&*()_+{}|:<>?\"';"}},
|
||||
{{Type: "text", Format: 1, Criteria: "not containing", Value: "text"}},
|
||||
{{Type: "text", Format: 1, Criteria: "begins with", Value: "prefix"}},
|
||||
{{Type: "text", Format: 1, Criteria: "ends with", Value: "suffix"}},
|
||||
{{Type: "top", Format: 1, Criteria: "=", Value: "6"}},
|
||||
{{Type: "bottom", Format: 1, Criteria: "=", Value: "6"}},
|
||||
{{Type: "average", AboveAverage: true, Format: 1, Criteria: "="}},
|
||||
|
@ -220,10 +219,14 @@ func TestGetConditionalFormats(t *testing.T) {
|
|||
{{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: 1, Criteria: "="}},
|
||||
{{Type: "blanks", Format: 1}},
|
||||
{{Type: "no_blanks", Format: 1}},
|
||||
{{Type: "errors", Format: 1}},
|
||||
{{Type: "no_errors", Format: 1}},
|
||||
{{Type: "icon_set", IconStyle: "3Arrows", ReverseIcons: true, IconsOnly: true}},
|
||||
} {
|
||||
f := NewFile()
|
||||
err := f.SetConditionalFormat("Sheet1", "A1:A2", format)
|
||||
err := f.SetConditionalFormat("Sheet1", "A2:A1", format)
|
||||
assert.NoError(t, err)
|
||||
opts, err := f.GetConditionalFormats("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
|
|
Loading…
Reference in New Issue