cell_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. package excelize
  2. import (
  3. "fmt"
  4. "path/filepath"
  5. "reflect"
  6. "strconv"
  7. "strings"
  8. "sync"
  9. "testing"
  10. "time"
  11. _ "image/jpeg"
  12. "github.com/stretchr/testify/assert"
  13. )
  14. func TestConcurrency(t *testing.T) {
  15. f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
  16. assert.NoError(t, err)
  17. wg := new(sync.WaitGroup)
  18. for i := 1; i <= 5; i++ {
  19. wg.Add(1)
  20. go func(val int, t *testing.T) {
  21. // Concurrency set cell value
  22. assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("A%d", val), val))
  23. assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("B%d", val), strconv.Itoa(val)))
  24. // Concurrency get cell value
  25. _, err := f.GetCellValue("Sheet1", fmt.Sprintf("A%d", val))
  26. assert.NoError(t, err)
  27. // Concurrency set rows
  28. assert.NoError(t, f.SetSheetRow("Sheet1", "B6", &[]interface{}{" Hello",
  29. []byte("World"), 42, int8(1<<8/2 - 1), int16(1<<16/2 - 1), int32(1<<32/2 - 1),
  30. int64(1<<32/2 - 1), float32(42.65418), float64(-42.65418), float32(42), float64(42),
  31. uint(1<<32 - 1), uint8(1<<8 - 1), uint16(1<<16 - 1), uint32(1<<32 - 1),
  32. uint64(1<<32 - 1), true, complex64(5 + 10i)}))
  33. // Concurrency create style
  34. style, err := f.NewStyle(`{"font":{"color":"#1265BE","underline":"single"}}`)
  35. assert.NoError(t, err)
  36. // Concurrency set cell style
  37. assert.NoError(t, f.SetCellStyle("Sheet1", "A3", "A3", style))
  38. // Concurrency add picture
  39. assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"),
  40. `{"x_offset": 10, "y_offset": 10, "hyperlink": "https://github.com/xuri/excelize", "hyperlink_type": "External", "positioning": "oneCell"}`))
  41. // Concurrency get cell picture
  42. name, raw, err := f.GetPicture("Sheet1", "A1")
  43. assert.Equal(t, "", name)
  44. assert.Nil(t, raw)
  45. assert.NoError(t, err)
  46. // Concurrency iterate rows
  47. rows, err := f.Rows("Sheet1")
  48. assert.NoError(t, err)
  49. for rows.Next() {
  50. _, err := rows.Columns()
  51. assert.NoError(t, err)
  52. }
  53. // Concurrency iterate columns
  54. cols, err := f.Cols("Sheet1")
  55. assert.NoError(t, err)
  56. for rows.Next() {
  57. _, err := cols.Rows()
  58. assert.NoError(t, err)
  59. }
  60. wg.Done()
  61. }(i, t)
  62. }
  63. wg.Wait()
  64. val, err := f.GetCellValue("Sheet1", "A1")
  65. if err != nil {
  66. t.Error(err)
  67. }
  68. assert.Equal(t, "1", val)
  69. assert.NoError(t, f.SaveAs(filepath.Join("test", "TestConcurrency.xlsx")))
  70. }
  71. func TestCheckCellInArea(t *testing.T) {
  72. f := NewFile()
  73. expectedTrueCellInAreaList := [][2]string{
  74. {"c2", "A1:AAZ32"},
  75. {"B9", "A1:B9"},
  76. {"C2", "C2:C2"},
  77. }
  78. for _, expectedTrueCellInArea := range expectedTrueCellInAreaList {
  79. cell := expectedTrueCellInArea[0]
  80. area := expectedTrueCellInArea[1]
  81. ok, err := f.checkCellInArea(cell, area)
  82. assert.NoError(t, err)
  83. assert.Truef(t, ok,
  84. "Expected cell %v to be in area %v, got false\n", cell, area)
  85. }
  86. expectedFalseCellInAreaList := [][2]string{
  87. {"c2", "A4:AAZ32"},
  88. {"C4", "D6:A1"}, // weird case, but you never know
  89. {"AEF42", "BZ40:AEF41"},
  90. }
  91. for _, expectedFalseCellInArea := range expectedFalseCellInAreaList {
  92. cell := expectedFalseCellInArea[0]
  93. area := expectedFalseCellInArea[1]
  94. ok, err := f.checkCellInArea(cell, area)
  95. assert.NoError(t, err)
  96. assert.Falsef(t, ok,
  97. "Expected cell %v not to be inside of area %v, but got true\n", cell, area)
  98. }
  99. ok, err := f.checkCellInArea("A1", "A:B")
  100. assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`)
  101. assert.False(t, ok)
  102. ok, err = f.checkCellInArea("AA0", "Z0:AB1")
  103. assert.EqualError(t, err, `cannot convert cell "AA0" to coordinates: invalid cell name "AA0"`)
  104. assert.False(t, ok)
  105. }
  106. func TestSetCellFloat(t *testing.T) {
  107. sheet := "Sheet1"
  108. t.Run("with no decimal", func(t *testing.T) {
  109. f := NewFile()
  110. assert.NoError(t, f.SetCellFloat(sheet, "A1", 123.0, -1, 64))
  111. assert.NoError(t, f.SetCellFloat(sheet, "A2", 123.0, 1, 64))
  112. val, err := f.GetCellValue(sheet, "A1")
  113. assert.NoError(t, err)
  114. assert.Equal(t, "123", val, "A1 should be 123")
  115. val, err = f.GetCellValue(sheet, "A2")
  116. assert.NoError(t, err)
  117. assert.Equal(t, "123.0", val, "A2 should be 123.0")
  118. })
  119. t.Run("with a decimal and precision limit", func(t *testing.T) {
  120. f := NewFile()
  121. assert.NoError(t, f.SetCellFloat(sheet, "A1", 123.42, 1, 64))
  122. val, err := f.GetCellValue(sheet, "A1")
  123. assert.NoError(t, err)
  124. assert.Equal(t, "123.4", val, "A1 should be 123.4")
  125. })
  126. t.Run("with a decimal and no limit", func(t *testing.T) {
  127. f := NewFile()
  128. assert.NoError(t, f.SetCellFloat(sheet, "A1", 123.42, -1, 64))
  129. val, err := f.GetCellValue(sheet, "A1")
  130. assert.NoError(t, err)
  131. assert.Equal(t, "123.42", val, "A1 should be 123.42")
  132. })
  133. f := NewFile()
  134. assert.EqualError(t, f.SetCellFloat(sheet, "A", 123.42, -1, 64), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
  135. }
  136. func TestSetCellValue(t *testing.T) {
  137. f := NewFile()
  138. assert.EqualError(t, f.SetCellValue("Sheet1", "A", time.Now().UTC()), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
  139. assert.EqualError(t, f.SetCellValue("Sheet1", "A", time.Duration(1e13)), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
  140. }
  141. func TestSetCellValues(t *testing.T) {
  142. f := NewFile()
  143. err := f.SetCellValue("Sheet1", "A1", time.Date(2010, time.December, 31, 0, 0, 0, 0, time.UTC))
  144. assert.NoError(t, err)
  145. v, err := f.GetCellValue("Sheet1", "A1")
  146. assert.NoError(t, err)
  147. assert.Equal(t, v, "12/31/10 00:00")
  148. // test date value lower than min date supported by Excel
  149. err = f.SetCellValue("Sheet1", "A1", time.Date(1600, time.December, 31, 0, 0, 0, 0, time.UTC))
  150. assert.NoError(t, err)
  151. v, err = f.GetCellValue("Sheet1", "A1")
  152. assert.NoError(t, err)
  153. assert.Equal(t, v, "1600-12-31T00:00:00Z")
  154. }
  155. func TestSetCellBool(t *testing.T) {
  156. f := NewFile()
  157. assert.EqualError(t, f.SetCellBool("Sheet1", "A", true), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
  158. }
  159. func TestGetCellValue(t *testing.T) {
  160. // Test get cell value without r attribute of the row.
  161. f := NewFile()
  162. sheetData := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>%s</sheetData></worksheet>`
  163. f.Sheet.Delete("xl/worksheets/sheet1.xml")
  164. f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="3"><c t="str"><v>A3</v></c></row><row><c t="str"><v>A4</v></c><c t="str"><v>B4</v></c></row><row r="7"><c t="str"><v>A7</v></c><c t="str"><v>B7</v></c></row><row><c t="str"><v>A8</v></c><c t="str"><v>B8</v></c></row>`)))
  165. f.checked = nil
  166. cells := []string{"A3", "A4", "B4", "A7", "B7"}
  167. rows, err := f.GetRows("Sheet1")
  168. assert.Equal(t, [][]string{nil, nil, {"A3"}, {"A4", "B4"}, nil, nil, {"A7", "B7"}, {"A8", "B8"}}, rows)
  169. assert.NoError(t, err)
  170. for _, cell := range cells {
  171. value, err := f.GetCellValue("Sheet1", cell)
  172. assert.Equal(t, cell, value)
  173. assert.NoError(t, err)
  174. }
  175. cols, err := f.GetCols("Sheet1")
  176. assert.Equal(t, [][]string{{"", "", "A3", "A4", "", "", "A7", "A8"}, {"", "", "", "B4", "", "", "B7", "B8"}}, cols)
  177. assert.NoError(t, err)
  178. f.Sheet.Delete("xl/worksheets/sheet1.xml")
  179. f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="2"><c r="A2" t="str"><v>A2</v></c></row><row r="2"><c r="B2" t="str"><v>B2</v></c></row>`)))
  180. f.checked = nil
  181. cell, err := f.GetCellValue("Sheet1", "A2")
  182. assert.Equal(t, "A2", cell)
  183. assert.NoError(t, err)
  184. f.Sheet.Delete("xl/worksheets/sheet1.xml")
  185. f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="2"><c r="A2" t="str"><v>A2</v></c></row><row r="2"><c r="B2" t="str"><v>B2</v></c></row>`)))
  186. f.checked = nil
  187. rows, err = f.GetRows("Sheet1")
  188. assert.Equal(t, [][]string{nil, {"A2", "B2"}}, rows)
  189. assert.NoError(t, err)
  190. f.Sheet.Delete("xl/worksheets/sheet1.xml")
  191. f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="1"><c r="A1" t="str"><v>A1</v></c></row><row r="1"><c r="B1" t="str"><v>B1</v></c></row>`)))
  192. f.checked = nil
  193. rows, err = f.GetRows("Sheet1")
  194. assert.Equal(t, [][]string{{"A1", "B1"}}, rows)
  195. assert.NoError(t, err)
  196. }
  197. func TestGetCellFormula(t *testing.T) {
  198. // Test get cell formula on not exist worksheet.
  199. f := NewFile()
  200. _, err := f.GetCellFormula("SheetN", "A1")
  201. assert.EqualError(t, err, "sheet SheetN is not exist")
  202. // Test get cell formula on no formula cell.
  203. assert.NoError(t, f.SetCellValue("Sheet1", "A1", true))
  204. _, err = f.GetCellFormula("Sheet1", "A1")
  205. assert.NoError(t, err)
  206. }
  207. func ExampleFile_SetCellFloat() {
  208. f := NewFile()
  209. var x = 3.14159265
  210. if err := f.SetCellFloat("Sheet1", "A1", x, 2, 64); err != nil {
  211. fmt.Println(err)
  212. }
  213. val, _ := f.GetCellValue("Sheet1", "A1")
  214. fmt.Println(val)
  215. // Output: 3.14
  216. }
  217. func BenchmarkSetCellValue(b *testing.B) {
  218. values := []string{"First", "Second", "Third", "Fourth", "Fifth", "Sixth"}
  219. cols := []string{"A", "B", "C", "D", "E", "F"}
  220. f := NewFile()
  221. b.ResetTimer()
  222. for i := 1; i <= b.N; i++ {
  223. for j := 0; j < len(values); j++ {
  224. if err := f.SetCellValue("Sheet1", cols[j]+strconv.Itoa(i), values[j]); err != nil {
  225. b.Error(err)
  226. }
  227. }
  228. }
  229. }
  230. func TestOverflowNumericCell(t *testing.T) {
  231. f, err := OpenFile(filepath.Join("test", "OverflowNumericCell.xlsx"))
  232. if !assert.NoError(t, err) {
  233. t.FailNow()
  234. }
  235. val, err := f.GetCellValue("Sheet1", "A1")
  236. assert.NoError(t, err)
  237. // GOARCH=amd64 - all ok; GOARCH=386 - actual: "-2147483648"
  238. assert.Equal(t, "8595602512225", val, "A1 should be 8595602512225")
  239. }
  240. func TestGetCellRichText(t *testing.T) {
  241. f := NewFile()
  242. runsSource := []RichTextRun{
  243. {
  244. Text: "a\n",
  245. },
  246. {
  247. Text: "b",
  248. Font: &Font{
  249. Underline: "single",
  250. Color: "ff0000",
  251. Bold: true,
  252. Italic: true,
  253. Family: "Times New Roman",
  254. Size: 100,
  255. Strike: true,
  256. },
  257. },
  258. }
  259. assert.NoError(t, f.SetCellRichText("Sheet1", "A1", runsSource))
  260. runs, err := f.GetCellRichText("Sheet1", "A1")
  261. assert.NoError(t, err)
  262. assert.Equal(t, runsSource[0].Text, runs[0].Text)
  263. assert.Nil(t, runs[0].Font)
  264. assert.NotNil(t, runs[1].Font)
  265. runsSource[1].Font.Color = strings.ToUpper(runsSource[1].Font.Color)
  266. assert.True(t, reflect.DeepEqual(runsSource[1].Font, runs[1].Font), "should get the same font")
  267. // Test get cell rich text when string item index overflow
  268. ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
  269. assert.True(t, ok)
  270. ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "2"
  271. runs, err = f.GetCellRichText("Sheet1", "A1")
  272. assert.NoError(t, err)
  273. assert.Equal(t, 0, len(runs))
  274. // Test get cell rich text when string item index is negative
  275. ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
  276. assert.True(t, ok)
  277. ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "-1"
  278. runs, err = f.GetCellRichText("Sheet1", "A1")
  279. assert.NoError(t, err)
  280. assert.Equal(t, 0, len(runs))
  281. // Test get cell rich text on invalid string item index
  282. ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
  283. assert.True(t, ok)
  284. ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "x"
  285. _, err = f.GetCellRichText("Sheet1", "A1")
  286. assert.EqualError(t, err, "strconv.Atoi: parsing \"x\": invalid syntax")
  287. // Test set cell rich text on not exists worksheet
  288. _, err = f.GetCellRichText("SheetN", "A1")
  289. assert.EqualError(t, err, "sheet SheetN is not exist")
  290. // Test set cell rich text with illegal cell coordinates
  291. _, err = f.GetCellRichText("Sheet1", "A")
  292. assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`)
  293. }
  294. func TestSetCellRichText(t *testing.T) {
  295. f := NewFile()
  296. assert.NoError(t, f.SetRowHeight("Sheet1", 1, 35))
  297. assert.NoError(t, f.SetColWidth("Sheet1", "A", "A", 44))
  298. richTextRun := []RichTextRun{
  299. {
  300. Text: "bold",
  301. Font: &Font{
  302. Bold: true,
  303. Color: "2354e8",
  304. Family: "Times New Roman",
  305. },
  306. },
  307. {
  308. Text: " and ",
  309. Font: &Font{
  310. Family: "Times New Roman",
  311. },
  312. },
  313. {
  314. Text: "italic ",
  315. Font: &Font{
  316. Bold: true,
  317. Color: "e83723",
  318. Italic: true,
  319. Family: "Times New Roman",
  320. },
  321. },
  322. {
  323. Text: "text with color and font-family,",
  324. Font: &Font{
  325. Bold: true,
  326. Color: "2354e8",
  327. Family: "Times New Roman",
  328. },
  329. },
  330. {
  331. Text: "\r\nlarge text with ",
  332. Font: &Font{
  333. Size: 14,
  334. Color: "ad23e8",
  335. },
  336. },
  337. {
  338. Text: "strike",
  339. Font: &Font{
  340. Color: "e89923",
  341. Strike: true,
  342. },
  343. },
  344. {
  345. Text: " and ",
  346. Font: &Font{
  347. Size: 14,
  348. Color: "ad23e8",
  349. },
  350. },
  351. {
  352. Text: "underline.",
  353. Font: &Font{
  354. Color: "23e833",
  355. Underline: "single",
  356. },
  357. },
  358. }
  359. assert.NoError(t, f.SetCellRichText("Sheet1", "A1", richTextRun))
  360. assert.NoError(t, f.SetCellRichText("Sheet1", "A2", richTextRun))
  361. style, err := f.NewStyle(&Style{
  362. Alignment: &Alignment{
  363. WrapText: true,
  364. },
  365. })
  366. assert.NoError(t, err)
  367. assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style))
  368. assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellRichText.xlsx")))
  369. // Test set cell rich text on not exists worksheet
  370. assert.EqualError(t, f.SetCellRichText("SheetN", "A1", richTextRun), "sheet SheetN is not exist")
  371. // Test set cell rich text with illegal cell coordinates
  372. assert.EqualError(t, f.SetCellRichText("Sheet1", "A", richTextRun), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
  373. richTextRun = []RichTextRun{{Text: strings.Repeat("s", TotalCellChars+1)}}
  374. // Test set cell rich text with characters over the maximum limit
  375. assert.EqualError(t, f.SetCellRichText("Sheet1", "A1", richTextRun), ErrCellCharsLength.Error())
  376. }
  377. func TestFormattedValue2(t *testing.T) {
  378. f := NewFile()
  379. v := f.formattedValue(0, "43528")
  380. assert.Equal(t, "43528", v)
  381. v = f.formattedValue(15, "43528")
  382. assert.Equal(t, "43528", v)
  383. v = f.formattedValue(1, "43528")
  384. assert.Equal(t, "43528", v)
  385. customNumFmt := "[$-409]MM/DD/YYYY"
  386. _, err := f.NewStyle(&Style{
  387. CustomNumFmt: &customNumFmt,
  388. })
  389. assert.NoError(t, err)
  390. v = f.formattedValue(1, "43528")
  391. assert.Equal(t, "03/04/2019", v)
  392. // formatted value with no built-in number format ID
  393. numFmtID := 5
  394. f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{
  395. NumFmtID: &numFmtID,
  396. })
  397. v = f.formattedValue(2, "43528")
  398. assert.Equal(t, "43528", v)
  399. // formatted value with invalid number format ID
  400. f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{
  401. NumFmtID: nil,
  402. })
  403. _ = f.formattedValue(3, "43528")
  404. // formatted value with empty number format
  405. f.Styles.NumFmts = nil
  406. f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{
  407. NumFmtID: &numFmtID,
  408. })
  409. v = f.formattedValue(1, "43528")
  410. assert.Equal(t, "43528", v)
  411. }