caxlsx 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +126 -31
  3. data/README.md +78 -170
  4. data/examples/{image1.jpeg → assets/image1.jpeg} +0 -0
  5. data/examples/generate.rb +15 -0
  6. data/lib/axlsx.rb +2 -3
  7. data/lib/axlsx/drawing/bar_chart.rb +3 -3
  8. data/lib/axlsx/drawing/bar_series.rb +3 -5
  9. data/lib/axlsx/drawing/d_lbls.rb +1 -1
  10. data/lib/axlsx/drawing/pie_series.rb +1 -1
  11. data/lib/axlsx/drawing/series_title.rb +3 -1
  12. data/lib/axlsx/drawing/title.rb +3 -2
  13. data/lib/axlsx/package.rb +53 -19
  14. data/lib/axlsx/rels/relationship.rb +26 -25
  15. data/lib/axlsx/stylesheet/font.rb +10 -2
  16. data/lib/axlsx/util/constants.rb +2 -1
  17. data/lib/axlsx/util/mime_type_utils.rb +1 -1
  18. data/lib/axlsx/util/validators.rb +2 -2
  19. data/lib/axlsx/util/zip_command.rb +73 -0
  20. data/lib/axlsx/version.rb +1 -1
  21. data/lib/axlsx/workbook/workbook.rb +0 -9
  22. data/lib/axlsx/workbook/worksheet/cell.rb +32 -5
  23. data/lib/axlsx/workbook/worksheet/col.rb +8 -4
  24. data/lib/axlsx/workbook/worksheet/data_validation.rb +4 -4
  25. data/lib/axlsx/workbook/worksheet/pivot_table.rb +7 -2
  26. data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +1 -1
  27. data/lib/axlsx/workbook/worksheet/rich_text_run.rb +1 -1
  28. data/lib/axlsx/workbook/worksheet/row.rb +6 -3
  29. data/lib/axlsx/workbook/worksheet/table.rb +1 -1
  30. data/lib/axlsx/workbook/worksheet/worksheet.rb +17 -4
  31. data/lib/caxlsx.rb +2 -0
  32. data/test/drawing/tc_drawing.rb +2 -2
  33. data/test/drawing/tc_hyperlink.rb +1 -1
  34. data/test/drawing/tc_one_cell_anchor.rb +1 -1
  35. data/test/drawing/tc_pic.rb +4 -4
  36. data/test/drawing/tc_pie_series.rb +2 -1
  37. data/test/drawing/tc_series_title.rb +21 -0
  38. data/test/drawing/tc_title.rb +16 -0
  39. data/test/fixtures/image1.gif +0 -0
  40. data/test/fixtures/image1.jpeg +0 -0
  41. data/test/fixtures/image1.jpg +0 -0
  42. data/test/fixtures/image1.png +0 -0
  43. data/test/fixtures/image1_fake.jpg +0 -0
  44. data/test/rels/tc_relationship.rb +8 -0
  45. data/test/stylesheet/tc_font.rb +14 -2
  46. data/test/stylesheet/tc_styles.rb +27 -1
  47. data/test/tc_axlsx.rb +6 -0
  48. data/test/tc_helper.rb +0 -2
  49. data/test/tc_package.rb +82 -13
  50. data/test/util/tc_mime_type_utils.rb +1 -1
  51. data/test/util/tc_validators.rb +1 -1
  52. data/test/workbook/worksheet/tc_cell.rb +68 -2
  53. data/test/workbook/worksheet/tc_col.rb +16 -1
  54. data/test/workbook/worksheet/tc_pivot_table.rb +8 -0
  55. data/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +8 -0
  56. data/test/workbook/worksheet/tc_rich_text_run.rb +3 -2
  57. data/test/workbook/worksheet/tc_row.rb +38 -0
  58. data/test/workbook/worksheet/tc_table.rb +10 -0
  59. data/test/workbook/worksheet/tc_worksheet.rb +24 -15
  60. metadata +130 -151
  61. data/examples/2010_comments.rb +0 -17
  62. data/examples/anchor_swapping.rb +0 -28
  63. data/examples/auto_filter.rb +0 -25
  64. data/examples/basic_charts.rb +0 -58
  65. data/examples/chart_colors.rb +0 -88
  66. data/examples/colored_links.rb +0 -59
  67. data/examples/conditional_formatting/example_conditional_formatting.rb +0 -89
  68. data/examples/conditional_formatting/getting_barred.rb +0 -37
  69. data/examples/conditional_formatting/hitting_the_high_notes.rb +0 -37
  70. data/examples/conditional_formatting/scaled_colors.rb +0 -39
  71. data/examples/conditional_formatting/stop_and_go.rb +0 -37
  72. data/examples/data_validation.rb +0 -67
  73. data/examples/example.rb +0 -885
  74. data/examples/extractive.rb +0 -45
  75. data/examples/ios_preview.rb +0 -14
  76. data/examples/merge_cells.rb +0 -17
  77. data/examples/no_grid_with_borders.rb +0 -18
  78. data/examples/page_setup.rb +0 -11
  79. data/examples/pivot_table.rb +0 -39
  80. data/examples/pivot_test.rb +0 -63
  81. data/examples/sheet_protection.rb +0 -10
  82. data/examples/skydrive/real_example.rb +0 -63
  83. data/examples/split.rb +0 -16
  84. data/examples/styles.rb +0 -66
  85. data/examples/underline.rb +0 -13
  86. data/examples/wrap_text.rb +0 -21
  87. data/lib/axlsx/util/parser.rb +0 -44
@@ -20,12 +20,13 @@ class TestPieSeries < Test::Unit::TestCase
20
20
  assert_raise(ArgumentError, "require valid explosion") { @series.explosion = :lots }
21
21
  assert_nothing_raised("allow valid explosion") { @series.explosion = 20 }
22
22
  assert(@series.explosion == 20)
23
+ # issue 58 - explosion caused to_xml_string to fail - now tested
24
+ assert_nothing_raised("allow to_xml_string") { @series.to_xml_string }
23
25
  end
24
26
 
25
27
  def test_to_xml_string
26
28
  doc = Nokogiri::XML(@series.to_xml_string)
27
29
  assert(doc.xpath("//srgbClr[@val='#{@series.colors[0]}']"))
28
-
29
30
  end
30
31
  #TODO test unique serialization parts
31
32
 
@@ -30,4 +30,25 @@ class TestSeriesTitle < Test::Unit::TestCase
30
30
  assert(@title.text == "one")
31
31
  end
32
32
 
33
+ def test_to_xml_string_for_special_characters
34
+ @chart.add_series(title: @title, data: [3, 7], labels: ['A', 'B'])
35
+
36
+ @title.text = "&><'\""
37
+
38
+ doc = Nokogiri::XML(@chart.to_xml_string)
39
+ errors = doc.errors
40
+ assert(errors.empty?, "invalid xml: #{errors.map(&:to_s).join(', ')}")
41
+ end
42
+
43
+ def test_to_xml_string_for_special_characters_in_cell
44
+ @chart.add_series(title: @title, data: [3, 7], labels: ['A', 'B'])
45
+
46
+ cell = @row.cells.first
47
+ cell.value = "&><'\""
48
+ @title.cell = cell
49
+
50
+ doc = Nokogiri::XML(@chart.to_xml_string)
51
+ errors = doc.errors
52
+ assert(errors.empty?, "invalid xml: #{errors.map(&:to_s).join(', ')}")
53
+ end
33
54
  end
@@ -51,4 +51,20 @@ class TestTitle < Test::Unit::TestCase
51
51
  assert_equal(1, doc.xpath('//c:v[text()="one"]').size)
52
52
  end
53
53
 
54
+ def test_to_xml_string_for_special_characters
55
+ @chart.title.text = "&><'\""
56
+ doc = Nokogiri::XML(@chart.to_xml_string)
57
+ errors = doc.errors
58
+ assert(errors.empty?, "invalid xml: #{errors.map(&:to_s).join(', ')}")
59
+ end
60
+
61
+ def test_to_xml_string_for_special_characters_in_cell
62
+ cell = @row.cells.first
63
+ cell.value = "&><'\""
64
+
65
+ @chart.title.cell = cell
66
+ doc = Nokogiri::XML(@chart.to_xml_string)
67
+ errors = doc.errors
68
+ assert(errors.empty?, "invalid xml: #{errors.map(&:to_s).join(', ')}")
69
+ end
54
70
  end
Binary file
Binary file
Binary file
Binary file
File without changes
@@ -13,6 +13,14 @@ class TestRelationships < Test::Unit::TestCase
13
13
  instance = Axlsx::Relationship.new(source_obj, Axlsx::WORKSHEET_R, 'target')
14
14
  assert_equal instance.Id, Axlsx::Relationship.new(source_obj, Axlsx::WORKSHEET_R, 'target').Id
15
15
  end
16
+
17
+ def test_ids_cache_is_thread_safe
18
+ cache1, cache2 = nil
19
+ t1 = Thread.new { cache1 = Axlsx::Relationship.ids_cache }
20
+ t2 = Thread.new { cache2 = Axlsx::Relationship.ids_cache }
21
+ [t1, t2].each(&:join)
22
+ assert_not_same(cache1, cache2)
23
+ end
16
24
 
17
25
  def test_target_is_only_considered_for_same_attributes_check_if_target_mode_is_external
18
26
  source_obj = Object.new
@@ -62,11 +62,23 @@ class TestFont < Test::Unit::TestCase
62
62
  assert_equal(@item.i, true)
63
63
  end
64
64
 
65
- # def u=(v) Axlsx::validate_boolean v; @u = v end
65
+ # def u=(v) Axlsx::validate_cell_u v; @u = v end
66
66
  def test_u
67
67
  assert_raise(ArgumentError) { @item.u = -7 }
68
+ assert_nothing_raised { @item.u = :single }
69
+ assert_equal(@item.u, :single)
70
+ doc = Nokogiri::XML(@item.to_xml_string)
71
+ assert(doc.xpath('//u[@val="single"]'))
72
+ end
73
+
74
+ def test_u_backward_compatibility
75
+ # backward compatibility for true
68
76
  assert_nothing_raised { @item.u = true }
69
- assert_equal(@item.u, true)
77
+ assert_equal(@item.u, :single)
78
+
79
+ # backward compatibility for false
80
+ assert_nothing_raised { @item.u = false }
81
+ assert_equal(@item.u, :none)
70
82
  end
71
83
 
72
84
  # def strike=(v) Axlsx::validate_boolean v; @strike = v end
@@ -124,7 +124,7 @@ class TestStyles < Test::Unit::TestCase
124
124
  :sz => 20,
125
125
  :b => 1,
126
126
  :i => 1,
127
- :u => 1,
127
+ :u => :single,
128
128
  :strike => 1,
129
129
  :outline => 1,
130
130
  :shadow => 1,
@@ -232,4 +232,30 @@ class TestStyles < Test::Unit::TestCase
232
232
  style = @styles.add_style :bg_color=>"FF000000", :fg_color=>"FFFFFFFF", :sz=>13, :alignment=>{:horizontal=>:left}, :border=>{:style => :thin, :color => "FFFF0000"}, :hidden=>true, :locked=>true, :type => :dxf
233
233
  assert_equal(1, style, "returns the second dxfId")
234
234
  end
235
+
236
+ def test_valid_document_with_font_options
237
+ font_options = {
238
+ :fg_color => "FF050505",
239
+ :sz => 20,
240
+ :b => 1,
241
+ :i => 1,
242
+ :u => :single,
243
+ :strike => 1,
244
+ :outline => 1,
245
+ :shadow => 1,
246
+ :charset => 9,
247
+ :family => 1,
248
+ :font_name => "woot font"
249
+ }
250
+ @styles.add_style font_options
251
+
252
+ schema = Nokogiri::XML::Schema(File.open(Axlsx::SML_XSD))
253
+ doc = Nokogiri::XML(@styles.to_xml_string)
254
+ errors = []
255
+ schema.validate(doc).each do |error|
256
+ errors.push error
257
+ puts error.message
258
+ end
259
+ assert(errors.size == 0)
260
+ end
235
261
  end
data/test/tc_axlsx.rb CHANGED
@@ -24,8 +24,14 @@ class TestAxlsx < Test::Unit::TestCase
24
24
 
25
25
 
26
26
  def test_trust_input_can_be_set_to_true
27
+ # Class variables like this are not reset between test runs, so we have
28
+ # to save and restore the original value manually.
29
+ old = Axlsx.trust_input
30
+
27
31
  Axlsx.trust_input = true
28
32
  assert_equal true, Axlsx.trust_input
33
+
34
+ Axlsx.trust_input = old
29
35
  end
30
36
  def test_cell_range_relative
31
37
  p = Axlsx::Package.new
data/test/tc_helper.rb CHANGED
@@ -8,5 +8,3 @@ end
8
8
  require 'test/unit'
9
9
  require "timecop"
10
10
  require "axlsx.rb"
11
- # MIME detection for Microsoft Office 2007+ formats
12
- require 'mimemagic/overlay'
data/test/tc_package.rb CHANGED
@@ -65,7 +65,7 @@ class TestPackage < Test::Unit::TestCase
65
65
  end
66
66
 
67
67
  @fname = 'axlsx_test_serialization.xlsx'
68
- img = File.expand_path('../../examples/image1.jpeg', __FILE__)
68
+ img = File.expand_path('../fixtures/image1.jpeg', __FILE__)
69
69
  ws.add_image(:image_src => img, :noSelect => true, :noMove => true, :hyperlink=>"http://axlsx.blogspot.com") do |image|
70
70
  image.width=720
71
71
  image.height=666
@@ -73,12 +73,12 @@ class TestPackage < Test::Unit::TestCase
73
73
  image.start_at 5, 5
74
74
  image.end_at 10, 10
75
75
  end
76
- ws.add_image :image_src => File.expand_path('../../examples/image1.gif', __FILE__) do |image|
76
+ ws.add_image :image_src => File.expand_path('../fixtures/image1.gif', __FILE__) do |image|
77
77
  image.start_at 0, 20
78
78
  image.width=360
79
79
  image.height=333
80
80
  end
81
- ws.add_image :image_src => File.expand_path('../../examples/image1.png', __FILE__) do |image|
81
+ ws.add_image :image_src => File.expand_path('../fixtures/image1.png', __FILE__) do |image|
82
82
  image.start_at 9, 20
83
83
  image.width = 180
84
84
  image.height = 167
@@ -127,18 +127,85 @@ class TestPackage < Test::Unit::TestCase
127
127
  end
128
128
 
129
129
  def test_serialization
130
- assert_nothing_raised do
131
- begin
132
- @package.serialize(@fname)
133
- zf = Zip::File.open(@fname)
134
- @package.send(:parts).each{ |part| zf.get_entry(part[:entry]) }
135
- File.delete(@fname)
136
- rescue Errno::EACCES
137
- puts "WARNING:: test_serialization requires write access."
138
- end
130
+ @package.serialize(@fname)
131
+ assert_zip_file_matches_package(@fname, @package)
132
+ assert_created_with_rubyzip(@fname, @package)
133
+ File.delete(@fname)
134
+ end
135
+
136
+ def test_serialization_with_zip_command
137
+ @package.serialize(@fname, zip_command: "zip")
138
+ assert_zip_file_matches_package(@fname, @package)
139
+ assert_created_with_zip_command(@fname, @package)
140
+ File.delete(@fname)
141
+ end
142
+
143
+ def test_serialization_with_zip_command_and_absolute_path
144
+ fname = "#{Dir.tmpdir}/#{@fname}"
145
+ @package.serialize(fname, zip_command: "zip")
146
+ assert_zip_file_matches_package(fname, @package)
147
+ assert_created_with_zip_command(fname, @package)
148
+ File.delete(fname)
149
+ end
150
+
151
+ def test_serialization_with_invalid_zip_command
152
+ assert_raises Axlsx::ZipCommand::ZipError do
153
+ @package.serialize(@fname, zip_command: "invalid_zip")
139
154
  end
140
155
  end
141
156
 
157
+ def assert_zip_file_matches_package(fname, package)
158
+ zf = Zip::File.open(fname)
159
+ package.send(:parts).each{ |part| zf.get_entry(part[:entry]) }
160
+ end
161
+
162
+ def assert_created_with_rubyzip(fname, package)
163
+ assert_equal 2098, get_mtime(fname, package).year, "XLSX files created with RubyZip have 2098 as the file mtime"
164
+ end
165
+
166
+ def assert_created_with_zip_command(fname, package)
167
+ assert_equal Time.now.utc.year, get_mtime(fname, package).year, "XLSX files created with a zip command have the current year as the file mtime"
168
+ end
169
+
170
+ def get_mtime(fname, package)
171
+ zf = Zip::File.open(fname)
172
+ part = package.send(:parts).first
173
+ entry = zf.get_entry(part[:entry])
174
+ entry.mtime.utc
175
+ end
176
+
177
+ def test_serialization_with_deprecated_argument
178
+ warnings = capture_warnings do
179
+ @package.serialize(@fname, false)
180
+ end
181
+ assert_equal 1, warnings.size
182
+ assert_includes warnings.first, "confirm_valid as a boolean is deprecated"
183
+ File.delete(@fname)
184
+ end
185
+
186
+ def test_serialization_with_deprecated_three_arguments
187
+ warnings = capture_warnings do
188
+ @package.serialize(@fname, true, zip_command: "zip")
189
+ end
190
+ assert_zip_file_matches_package(@fname, @package)
191
+ assert_created_with_zip_command(@fname, @package)
192
+ assert_equal 2, warnings.size
193
+ assert_includes warnings.first, "with 3 arguments is deprecated"
194
+ File.delete(@fname)
195
+ end
196
+
197
+ def capture_warnings(&block)
198
+ original_warn = Kernel.instance_method(:warn)
199
+ warnings = []
200
+ Kernel.send(:define_method, :warn) { |string| warnings << string }
201
+ block.call
202
+ original_verbose = $VERBOSE
203
+ $VERBOSE = nil
204
+ Kernel.send(:define_method, :warn, original_warn)
205
+ $VERBOSE = original_verbose
206
+ warnings
207
+ end
208
+
142
209
  # See comment for Package#zip_entry_for_part
143
210
  def test_serialization_creates_identical_files_at_any_time_if_created_at_is_set
144
211
  @package.core.created = Time.now
@@ -161,7 +228,7 @@ class TestPackage < Test::Unit::TestCase
161
228
  end
162
229
 
163
230
  def test_serialization_creates_files_with_excel_mime_type
164
- assert_equal(MimeMagic.by_magic(@package.to_stream).type,
231
+ assert_equal(Marcel::MimeType.for(@package.to_stream),
165
232
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
166
233
  end
167
234
 
@@ -236,6 +303,8 @@ class TestPackage < Test::Unit::TestCase
236
303
  # this is just a roundabout guess for a package as it is build now
237
304
  # in testing.
238
305
  assert(stream.size > 80000)
306
+ # Cached ids should be cleared
307
+ assert(Axlsx::Relationship.ids_cache.empty?)
239
308
  end
240
309
 
241
310
  def test_encrypt
@@ -1,7 +1,7 @@
1
1
  require 'tc_helper.rb'
2
2
  class TestMimeTypeUtils < Test::Unit::TestCase
3
3
  def setup
4
- @test_img = File.dirname(__FILE__) + "/../../examples/image1.jpeg"
4
+ @test_img = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
5
5
  end
6
6
 
7
7
  def teardown
@@ -127,7 +127,7 @@ class TestValidators < Test::Unit::TestCase
127
127
  assert_raise(ArgumentError) { Axlsx.validate_data_validation_error_style 0 }
128
128
 
129
129
  #data_validation_type
130
- [:custom, :data, :decimal, :list, :none, :textLength, :time, :whole].each do |sym|
130
+ [:custom, :data, :decimal, :list, :none, :textLength, :date, :time, :whole].each do |sym|
131
131
  assert_nothing_raised { Axlsx.validate_data_validation_type sym }
132
132
  end
133
133
  assert_raise(ArgumentError) { Axlsx.validate_data_validation_error_style :other_symbol }
@@ -8,7 +8,7 @@ class TestCell < Test::Unit::TestCase
8
8
  @ws = p.workbook.add_worksheet :name=>"hmmm"
9
9
  p.workbook.styles.add_style :sz=>20
10
10
  @row = @ws.add_row
11
- @c = @row.add_cell 1, :type=>:float, :style=>1
11
+ @c = @row.add_cell 1, :type=>:float, :style=>1, :escape_formulas=>true
12
12
  data = (0..26).map { |index| index }
13
13
  @ws.add_row data
14
14
  @cAA = @ws["AA2"]
@@ -19,6 +19,7 @@ class TestCell < Test::Unit::TestCase
19
19
  assert_equal(@c.type, :float, "type option is applied")
20
20
  assert_equal(@c.style, 1, "style option is applied")
21
21
  assert_equal(@c.value, 1.0, "type option is applied and value is casted")
22
+ assert_equal(@c.escape_formulas, true, "escape formulas option is applied")
22
23
  end
23
24
 
24
25
  def test_style_date_data
@@ -60,7 +61,7 @@ class TestCell < Test::Unit::TestCase
60
61
  def test_autowidth
61
62
  style = @c.row.worksheet.workbook.styles.add_style({:alignment => {:horizontal => :center, :vertical => :center, :wrap_text => true}} )
62
63
  @c.style = style
63
- assert_equal(@c.autowidth, 5.5)
64
+ assert_in_delta(6.6, @c.autowidth, 0.01)
64
65
  end
65
66
 
66
67
  def test_time
@@ -70,6 +71,13 @@ class TestCell < Test::Unit::TestCase
70
71
  assert_equal(@c.value, now.to_time)
71
72
  end
72
73
 
74
+ def test_date
75
+ @c.type = :date
76
+ now = Time.now
77
+ @c.value = now
78
+ assert_equal(@c.value, now.to_date)
79
+ end
80
+
73
81
  def test_style
74
82
  assert_raise(ArgumentError, "must reject invalid style indexes") { @c.style=@c.row.worksheet.workbook.styles.cellXfs.size }
75
83
  assert_nothing_raised("must allow valid style index changes") {@c.style=1}
@@ -99,6 +107,12 @@ class TestCell < Test::Unit::TestCase
99
107
 
100
108
  def test_cell_type_from_value
101
109
  assert_equal(@c.send(:cell_type_from_value, 1.0), :float)
110
+ assert_equal(@c.send(:cell_type_from_value, "1e1"), :float)
111
+ assert_equal(@c.send(:cell_type_from_value, "1e#{Float::MAX_10_EXP}"), :float)
112
+ assert_equal(@c.send(:cell_type_from_value, "1e#{Float::MAX_10_EXP + 1}"), :string)
113
+ assert_equal(@c.send(:cell_type_from_value, "1e-1"), :float)
114
+ assert_equal(@c.send(:cell_type_from_value, "1e#{Float::MIN_10_EXP}"), :float)
115
+ assert_equal(@c.send(:cell_type_from_value, "1e#{Float::MIN_10_EXP - 1}"), :string)
102
116
  assert_equal(@c.send(:cell_type_from_value, 1), :integer)
103
117
  assert_equal(@c.send(:cell_type_from_value, Date.today), :date)
104
118
  assert_equal(@c.send(:cell_type_from_value, Time.now), :time)
@@ -113,6 +127,31 @@ class TestCell < Test::Unit::TestCase
113
127
  assert_equal(:iso_8601, @c.send(:cell_type_from_value, '2008-08-30T01:45:36.123+09:00'))
114
128
  end
115
129
 
130
+ def test_cell_type_from_value_looks_like_number_but_is_not
131
+ mimic_number = Class.new do
132
+ def initialize(to_s_value)
133
+ @to_s_value = to_s_value
134
+ end
135
+
136
+ def to_s
137
+ @to_s_value
138
+ end
139
+ end
140
+
141
+ number_strings = [
142
+ '1',
143
+ '1234567890',
144
+ '1.0',
145
+ '1e1',
146
+ '0',
147
+ "1e#{Float::MIN_10_EXP}"
148
+ ]
149
+
150
+ number_strings.each do |number_string|
151
+ assert_equal(@c.send(:cell_type_from_value, mimic_number.new(number_string)), :string)
152
+ end
153
+ end
154
+
116
155
  def test_cast_value
117
156
  @c.type = :string
118
157
  assert_equal(@c.send(:cast_value, 1.0), "1.0")
@@ -321,6 +360,33 @@ class TestCell < Test::Unit::TestCase
321
360
  assert(doc.xpath("//f[text()='IF(2+2=4,4,5)']").any?)
322
361
  end
323
362
 
363
+ def test_to_xml_string_formula_escaped
364
+ p = Axlsx::Package.new
365
+ ws = p.workbook.add_worksheet do |sheet|
366
+ sheet.add_row ["=IF(2+2=4,4,5)"], escape_formulas: true
367
+ end
368
+ doc = Nokogiri::XML(ws.to_xml_string)
369
+ doc.remove_namespaces!
370
+ assert(doc.xpath("//t[text()='=IF(2+2=4,4,5)']").any?)
371
+ end
372
+
373
+ def test_to_xml_string_formula_escape_array_parameter
374
+ p = Axlsx::Package.new
375
+ ws = p.workbook.add_worksheet do |sheet|
376
+ sheet.add_row [
377
+ "=IF(2+2=4,4,5)",
378
+ "=IF(13+13=4,4,5)",
379
+ "=IF(99+99=4,4,5)"
380
+ ], escape_formulas: [true, false, true]
381
+ end
382
+ doc = Nokogiri::XML(ws.to_xml_string)
383
+ doc.remove_namespaces!
384
+
385
+ assert(doc.xpath("//t[text()='=IF(2+2=4,4,5)']").any?)
386
+ assert(doc.xpath("//f[text()='IF(13+13=4,4,5)']").any?)
387
+ assert(doc.xpath("//t[text()='=IF(99+99=4,4,5)']").any?)
388
+ end
389
+
324
390
  def test_to_xml_string_array_formula
325
391
  p = Axlsx::Package.new
326
392
  ws = p.workbook.add_worksheet do |sheet|