caxlsx 3.0.2 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +31 -8
  3. data/README.md +4 -0
  4. data/examples/{image1.jpeg → assets/image1.jpeg} +0 -0
  5. data/examples/generate.rb +15 -0
  6. data/lib/axlsx/drawing/bar_3D_chart.rb +5 -8
  7. data/lib/axlsx/drawing/bar_chart.rb +13 -18
  8. data/lib/axlsx/drawing/bar_series.rb +18 -1
  9. data/lib/axlsx/drawing/pie_series.rb +1 -1
  10. data/lib/axlsx/package.rb +44 -6
  11. data/lib/axlsx/util/constants.rb +2 -1
  12. data/lib/axlsx/util/mime_type_utils.rb +1 -1
  13. data/lib/axlsx/util/validators.rb +1 -1
  14. data/lib/axlsx/util/zip_command.rb +73 -0
  15. data/lib/axlsx/version.rb +1 -1
  16. data/lib/axlsx/workbook/worksheet/cell.rb +9 -3
  17. data/lib/axlsx/workbook/worksheet/data_validation.rb +4 -4
  18. data/lib/axlsx/workbook/worksheet/pivot_table.rb +7 -2
  19. data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +1 -1
  20. data/lib/axlsx/workbook/worksheet/row.rb +6 -4
  21. data/lib/axlsx/workbook/worksheet/table.rb +1 -1
  22. data/lib/axlsx/workbook/worksheet/worksheet.rb +7 -1
  23. data/lib/axlsx.rb +7 -5
  24. data/test/drawing/tc_bar_3D_chart.rb +26 -11
  25. data/test/drawing/tc_bar_chart.rb +26 -11
  26. data/test/drawing/tc_bar_series.rb +10 -1
  27. data/test/drawing/tc_drawing.rb +2 -2
  28. data/test/drawing/tc_hyperlink.rb +1 -1
  29. data/test/drawing/tc_one_cell_anchor.rb +1 -1
  30. data/test/drawing/tc_pic.rb +4 -4
  31. data/test/drawing/tc_pie_series.rb +2 -1
  32. data/test/fixtures/image1.gif +0 -0
  33. data/test/fixtures/image1.jpeg +0 -0
  34. data/test/fixtures/image1.jpg +0 -0
  35. data/test/fixtures/image1.png +0 -0
  36. data/test/fixtures/image1_fake.jpg +0 -0
  37. data/test/tc_helper.rb +0 -2
  38. data/test/tc_package.rb +80 -13
  39. data/test/util/tc_mime_type_utils.rb +1 -1
  40. data/test/util/tc_validators.rb +1 -1
  41. data/test/workbook/worksheet/tc_cell.rb +38 -0
  42. data/test/workbook/worksheet/tc_pivot_table.rb +8 -0
  43. data/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +8 -0
  44. data/test/workbook/worksheet/tc_row.rb +21 -0
  45. data/test/workbook/worksheet/tc_table.rb +10 -0
  46. data/test/workbook/worksheet/tc_worksheet.rb +16 -18
  47. metadata +116 -137
  48. data/examples/2010_comments.rb +0 -17
  49. data/examples/anchor_swapping.rb +0 -28
  50. data/examples/auto_filter.rb +0 -25
  51. data/examples/basic_charts.rb +0 -58
  52. data/examples/chart_colors.rb +0 -88
  53. data/examples/colored_links.rb +0 -45
  54. data/examples/conditional_formatting/example_conditional_formatting.rb +0 -89
  55. data/examples/conditional_formatting/getting_barred.rb +0 -37
  56. data/examples/conditional_formatting/hitting_the_high_notes.rb +0 -37
  57. data/examples/conditional_formatting/scaled_colors.rb +0 -39
  58. data/examples/conditional_formatting/stop_and_go.rb +0 -37
  59. data/examples/data_validation.rb +0 -67
  60. data/examples/example.rb +0 -900
  61. data/examples/extractive.rb +0 -45
  62. data/examples/ios_preview.rb +0 -14
  63. data/examples/merge_cells.rb +0 -17
  64. data/examples/no_grid_with_borders.rb +0 -18
  65. data/examples/page_setup.rb +0 -11
  66. data/examples/pivot_table.rb +0 -39
  67. data/examples/pivot_test.rb +0 -63
  68. data/examples/sheet_protection.rb +0 -10
  69. data/examples/skydrive/real_example.rb +0 -63
  70. data/examples/split.rb +0 -16
  71. data/examples/styles.rb +0 -66
  72. data/examples/underline.rb +0 -13
  73. data/examples/wrap_text.rb +0 -21
@@ -80,7 +80,7 @@ module Axlsx
80
80
  str << ('<autoFilter ref="' << @ref << '"/>')
81
81
  str << ('<tableColumns count="' << header_cells.length.to_s << '">')
82
82
  header_cells.each_with_index do |cell,index|
83
- str << ('<tableColumn id ="' << (index+1).to_s << '" name="' << cell.value << '"/>')
83
+ str << ('<tableColumn id ="' << (index+1).to_s << '" name="' << cell.clean_value << '"/>')
84
84
  end
85
85
  str << '</tableColumns>'
86
86
  table_style_info.to_xml_string(str)
@@ -393,6 +393,9 @@ module Axlsx
393
393
  # @example - specify whether a certain cells in a row should escape formulas or not
394
394
  # ws.add_row ['=IF(2+2=4,4,5)', '=IF(13+13=4,4,5)'], :escape_formulas=>[true, false]
395
395
  #
396
+ # @example - add a column offset when adding a row (inserts 'n' blank, unstyled columns before data)
397
+ # ws.add_row ['I wish', 'for a fish', 'on my fish wish dish'], offset: 3
398
+ #
396
399
  # @see Worksheet#column_widths
397
400
  # @return [Row]
398
401
  # @option options [Array] values
@@ -400,6 +403,7 @@ module Axlsx
400
403
  # @option options [Array, Integer] style
401
404
  # @option options [Array] widths each member of the widths array will affect how auto_fit behavies.
402
405
  # @option options [Float] height the row's height (in points)
406
+ # @option options [Integer] offset - add empty columns before values
403
407
  # @option options [Array, Boolean] escape_formulas - Whether to treat a value starting with an equal
404
408
  # sign as formula (default) or as simple string.
405
409
  # Allowing user generated data to be interpreted as formulas can be dangerous
@@ -663,7 +667,9 @@ module Axlsx
663
667
 
664
668
  def validate_sheet_name(name)
665
669
  DataTypeValidator.validate :worksheet_name, String, name
666
- raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % name) if name.bytesize > 31
670
+ # ignore first character (BOM) after encoding to utf16 because Excel does so, too.
671
+ character_length = name.encode("utf-16")[1..-1].encode("utf-16").bytesize / 2
672
+ raise ArgumentError, (ERR_SHEET_NAME_TOO_LONG % name) if character_length > 31
667
673
  raise ArgumentError, (ERR_SHEET_NAME_CHARACTER_FORBIDDEN % name) if '[]*/\?:'.chars.any? { |char| name.include? char }
668
674
  name = Axlsx::coder.encode(name)
669
675
  sheet_names = @workbook.worksheets.reject { |s| s == self }.map { |s| s.name }
data/lib/axlsx.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # encoding: UTF-8
2
2
  require 'htmlentities'
3
3
  require 'axlsx/version.rb'
4
- require 'mimemagic'
4
+ require 'marcel'
5
5
 
6
6
  require 'axlsx/util/simple_typed_list.rb'
7
7
  require 'axlsx/util/constants.rb'
@@ -10,6 +10,7 @@ require 'axlsx/util/accessors.rb'
10
10
  require 'axlsx/util/serialized_attributes'
11
11
  require 'axlsx/util/options_parser'
12
12
  require 'axlsx/util/mime_type_utils'
13
+ require 'axlsx/util/zip_command'
13
14
 
14
15
  require 'axlsx/stylesheet/styles.rb'
15
16
 
@@ -50,10 +51,11 @@ module Axlsx
50
51
  # determines the cell range for the items provided
51
52
  def self.cell_range(cells, absolute=true)
52
53
  return "" unless cells.first.is_a? Cell
53
- cells = sort_cells(cells)
54
- reference = "#{cells.first.reference(absolute)}:#{cells.last.reference(absolute)}"
54
+
55
+ first_cell, last_cell = cells.minmax_by(&:pos)
56
+ reference = "#{first_cell.reference(absolute)}:#{last_cell.reference(absolute)}"
55
57
  if absolute
56
- escaped_name = cells.first.row.worksheet.name.gsub '&apos;', "''"
58
+ escaped_name = first_cell.row.worksheet.name.gsub '&apos;', "''"
57
59
  "'#{escaped_name}'!#{reference}"
58
60
  else
59
61
  reference
@@ -65,7 +67,7 @@ module Axlsx
65
67
  # @param [Array] cells
66
68
  # @return [Array]
67
69
  def self.sort_cells(cells)
68
- cells.sort { |x, y| [x.index, x.row.row_index] <=> [y.index, y.row.row_index] }
70
+ cells.sort_by(&:pos)
69
71
  end
70
72
 
71
73
  #global reference html entity encoding
@@ -32,18 +32,19 @@ class TestBar3DChart < Test::Unit::TestCase
32
32
  assert(@chart.grouping == :standard)
33
33
  end
34
34
 
35
+ def test_gap_width
36
+ assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = -1 }
37
+ assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = 501 }
38
+ assert_nothing_raised("allow valid gapWidth") { @chart.gap_width = 200 }
39
+ assert_equal(@chart.gap_width, 200, 'gap width is incorrect')
40
+ end
35
41
 
36
- def test_gapWidth
37
- assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = 200 }
38
- assert_nothing_raised("allow valid gapWidth") { @chart.gap_width = "200%" }
39
- assert(@chart.gap_width == "200%")
40
- end
41
-
42
- def test_gapDepth
43
- assert_raise(ArgumentError, "require valid gap_depth") { @chart.gap_depth = 200 }
44
- assert_nothing_raised("allow valid gap_depth") { @chart.gap_depth = "200%" }
45
- assert(@chart.gap_depth == "200%")
46
- end
42
+ def test_gap_depth
43
+ assert_raise(ArgumentError, "require valid gap_depth") { @chart.gap_depth = -1 }
44
+ assert_raise(ArgumentError, "require valid gap_depth") { @chart.gap_depth = 501 }
45
+ assert_nothing_raised("allow valid gap_depth") { @chart.gap_depth = 200 }
46
+ assert_equal(@chart.gap_depth, 200, 'gap depth is incorrect')
47
+ end
47
48
 
48
49
  def test_shape
49
50
  assert_raise(ArgumentError, "require valid shape") { @chart.shape = :star }
@@ -68,4 +69,18 @@ class TestBar3DChart < Test::Unit::TestCase
68
69
  val_axis_position = str.index(@chart.axes[:val_axis].id.to_s)
69
70
  assert(cat_axis_position < val_axis_position, "cat_axis must occur earlier than val_axis in the XML")
70
71
  end
72
+
73
+ def test_to_xml_string_has_gap_depth
74
+ gap_depth_value = rand(0..500)
75
+ @chart.gap_depth = gap_depth_value
76
+ doc = Nokogiri::XML(@chart.to_xml_string)
77
+ assert_equal(doc.xpath("//c:bar3DChart/c:gapDepth").first.attribute('val').value, gap_depth_value.to_s)
78
+ end
79
+
80
+ def test_to_xml_string_has_gap_width
81
+ gap_width_value = rand(0..500)
82
+ @chart.gap_width = gap_width_value
83
+ doc = Nokogiri::XML(@chart.to_xml_string)
84
+ assert_equal(doc.xpath("//c:bar3DChart/c:gapWidth").first.attribute('val').value, gap_width_value.to_s)
85
+ end
71
86
  end
@@ -32,18 +32,19 @@ class TestBarChart < Test::Unit::TestCase
32
32
  assert(@chart.grouping == :standard)
33
33
  end
34
34
 
35
+ def test_gap_width
36
+ assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = -1 }
37
+ assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = 501 }
38
+ assert_nothing_raised("allow valid gap width") { @chart.gap_width = 200 }
39
+ assert_equal(@chart.gap_width, 200, 'gap width is incorrect')
40
+ end
35
41
 
36
- def test_gapWidth
37
- assert_raise(ArgumentError, "require valid gap width") { @chart.gap_width = 200 }
38
- assert_nothing_raised("allow valid gapWidth") { @chart.gap_width = "200%" }
39
- assert(@chart.gap_width == "200%")
40
- end
41
-
42
- def test_gapDepth
43
- assert_raise(ArgumentError, "require valid gap_depth") { @chart.gap_depth = 200 }
44
- assert_nothing_raised("allow valid gap_depth") { @chart.gap_depth = "200%" }
45
- assert(@chart.gap_depth == "200%")
46
- end
42
+ def test_overlap
43
+ assert_raise(ArgumentError, "require valid overlap") { @chart.overlap = -101 }
44
+ assert_raise(ArgumentError, "require valid overlap") { @chart.overlap = 101 }
45
+ assert_nothing_raised("allow valid overlap") { @chart.overlap = 100 }
46
+ assert_equal(@chart.overlap, 100, 'overlap is incorrect')
47
+ end
47
48
 
48
49
  def test_shape
49
50
  assert_raise(ArgumentError, "require valid shape") { @chart.shape = :star }
@@ -68,4 +69,18 @@ class TestBarChart < Test::Unit::TestCase
68
69
  val_axis_position = str.index(@chart.axes[:val_axis].id.to_s)
69
70
  assert(cat_axis_position < val_axis_position, "cat_axis must occur earlier than val_axis in the XML")
70
71
  end
72
+
73
+ def test_to_xml_string_has_gap_width
74
+ gap_width_value = rand(0..500)
75
+ @chart.gap_width = gap_width_value
76
+ doc = Nokogiri::XML(@chart.to_xml_string)
77
+ assert_equal(doc.xpath("//c:barChart/c:gapWidth").first.attribute('val').value, gap_width_value.to_s)
78
+ end
79
+
80
+ def test_to_xml_string_has_overlap
81
+ overlap_value = rand(-100..100)
82
+ @chart.overlap = overlap_value
83
+ doc = Nokogiri::XML(@chart.to_xml_string)
84
+ assert_equal(doc.xpath("//c:barChart/c:overlap").first.attribute('val').value, overlap_value.to_s)
85
+ end
71
86
  end
@@ -6,13 +6,21 @@ class TestBarSeries < Test::Unit::TestCase
6
6
  p = Axlsx::Package.new
7
7
  @ws = p.workbook.add_worksheet :name=>"hmmm"
8
8
  @chart = @ws.add_chart Axlsx::Bar3DChart, :title => "fishery"
9
- @series = @chart.add_series :data=>[0,1,2], :labels=>["zero", "one", "two"], :title=>"bob", :colors => ['FF0000', '00FF00', '0000FF'], :shape => :cone
9
+ @series = @chart.add_series(
10
+ data: [0, 1, 2],
11
+ labels: ['zero', 'one', 'two'],
12
+ title: 'bob',
13
+ colors: ['FF0000', '00FF00', '0000FF'],
14
+ shape: :cone,
15
+ series_color: '5A5A5A'
16
+ )
10
17
  end
11
18
 
12
19
  def test_initialize
13
20
  assert_equal(@series.title.text, "bob", "series title has been applied")
14
21
  assert_equal(@series.data.class, Axlsx::NumDataSource, "data option applied")
15
22
  assert_equal(@series.shape, :cone, "series shape has been applied")
23
+ assert_equal(@series.series_color, '5A5A5A', 'series color has been applied')
16
24
  assert(@series.data.is_a?(Axlsx::NumDataSource))
17
25
  assert(@series.labels.is_a?(Axlsx::AxDataSource))
18
26
  end
@@ -33,5 +41,6 @@ class TestBarSeries < Test::Unit::TestCase
33
41
  assert_equal(doc.xpath("//c:dPt/c:idx[@val='#{index}']").size,1)
34
42
  assert_equal(doc.xpath("//c:dPt/c:spPr/a:solidFill/a:srgbClr[@val='#{@series.colors[index]}']").size,1)
35
43
  end
44
+ assert_equal(doc.xpath('//c:spPr[not(ancestor::c:dPt)]/a:solidFill/a:srgbClr').first.get_attribute('val'), '5A5A5A', 'series color has been applied')
36
45
  end
37
46
  end
@@ -23,7 +23,7 @@ class TestDrawing < Test::Unit::TestCase
23
23
  end
24
24
 
25
25
  def test_add_image
26
- src = File.dirname(__FILE__) + "/../../examples/image1.jpeg"
26
+ src = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
27
27
  image = @ws.add_image(:image_src => src, :start_at=>[0,0], :width=>600, :height=>400)
28
28
  assert(@ws.drawing.anchors.last.is_a?(Axlsx::OneCellAnchor))
29
29
  assert(image.is_a?(Axlsx::Pic))
@@ -31,7 +31,7 @@ class TestDrawing < Test::Unit::TestCase
31
31
  assert_equal(400, image.height)
32
32
  end
33
33
  def test_add_two_cell_anchor_image
34
- src = File.dirname(__FILE__) + "/../../examples/image1.jpeg"
34
+ src = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
35
35
  image = @ws.add_image(:image_src => src, :start_at=>[0,0], :end_at => [15,0])
36
36
  assert(@ws.drawing.anchors.last.is_a?(Axlsx::TwoCellAnchor))
37
37
  assert(image.is_a?(Axlsx::Pic))
@@ -5,7 +5,7 @@ class TestHyperlink < Test::Unit::TestCase
5
5
  def setup
6
6
  @p = Axlsx::Package.new
7
7
  ws = @p.workbook.add_worksheet
8
- @test_img = File.dirname(__FILE__) + "/../../examples/image1.jpeg"
8
+ @test_img = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
9
9
  @image = ws.add_image :image_src => @test_img, :hyperlink => "http://axlsx.blogspot.com"
10
10
  @hyperlink = @image.hyperlink
11
11
  end
@@ -5,7 +5,7 @@ class TestOneCellAnchor < Test::Unit::TestCase
5
5
  def setup
6
6
  @p = Axlsx::Package.new
7
7
  @ws = @p.workbook.add_worksheet
8
- @test_img = File.dirname(__FILE__) + "/../../examples/image1.jpeg"
8
+ @test_img = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
9
9
  @image = @ws.add_image :image_src => @test_img
10
10
  @anchor = @image.anchor
11
11
  end
@@ -5,10 +5,10 @@ class TestPic < Test::Unit::TestCase
5
5
  def setup
6
6
  @p = Axlsx::Package.new
7
7
  ws = @p.workbook.add_worksheet
8
- @test_img = @test_img_jpg = File.dirname(__FILE__) + "/../../examples/image1.jpeg"
9
- @test_img_png = File.dirname(__FILE__) + "/../../examples/image1.png"
10
- @test_img_gif = File.dirname(__FILE__) + "/../../examples/image1.gif"
11
- @test_img_fake = File.dirname(__FILE__) + "/../../examples/image1_fake.jpg"
8
+ @test_img = @test_img_jpg = File.dirname(__FILE__) + "/../fixtures/image1.jpeg"
9
+ @test_img_png = File.dirname(__FILE__) + "/../fixtures/image1.png"
10
+ @test_img_gif = File.dirname(__FILE__) + "/../fixtures/image1.gif"
11
+ @test_img_fake = File.dirname(__FILE__) + "/../fixtures/image1_fake.jpg"
12
12
  @image = ws.add_image :image_src => @test_img, :hyperlink => 'https://github.com/randym', :tooltip => "What's up doc?", :opacity => 5
13
13
  end
14
14
 
@@ -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
 
Binary file
Binary file
Binary file
Binary file
File without changes
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
 
@@ -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 }
@@ -71,6 +71,13 @@ class TestCell < Test::Unit::TestCase
71
71
  assert_equal(@c.value, now.to_time)
72
72
  end
73
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
+
74
81
  def test_style
75
82
  assert_raise(ArgumentError, "must reject invalid style indexes") { @c.style=@c.row.worksheet.workbook.styles.cellXfs.size }
76
83
  assert_nothing_raised("must allow valid style index changes") {@c.style=1}
@@ -100,6 +107,12 @@ class TestCell < Test::Unit::TestCase
100
107
 
101
108
  def test_cell_type_from_value
102
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)
103
116
  assert_equal(@c.send(:cell_type_from_value, 1), :integer)
104
117
  assert_equal(@c.send(:cell_type_from_value, Date.today), :date)
105
118
  assert_equal(@c.send(:cell_type_from_value, Time.now), :time)
@@ -114,6 +127,31 @@ class TestCell < Test::Unit::TestCase
114
127
  assert_equal(:iso_8601, @c.send(:cell_type_from_value, '2008-08-30T01:45:36.123+09:00'))
115
128
  end
116
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
+
117
155
  def test_cast_value
118
156
  @c.type = :string
119
157
  assert_equal(@c.send(:cast_value, 1.0), "1.0")
@@ -132,4 +132,12 @@ class TestPivotTable < Test::Unit::TestCase
132
132
  end
133
133
  shared_test_pivot_table_xml_validity(pivot_table)
134
134
  end
135
+
136
+ def test_add_pivot_table_with_format_options_on_data_field
137
+ pivot_table = @ws.add_pivot_table('G5:G6', 'A1:E5') do |pt|
138
+ pt.data = [{:ref=>"Sales", :subtotal => 'sum', num_fmt: 4}]
139
+ end
140
+ doc = Nokogiri::XML(pivot_table.to_xml_string)
141
+ assert_equal('4', doc.at_css('dataFields dataField')['numFmtId'], 'adding format options to pivot_table')
142
+ end
135
143
  end
@@ -51,4 +51,12 @@ class TestPivotTableCacheDefinition < Test::Unit::TestCase
51
51
  assert(errors.empty?, "error free validation")
52
52
  end
53
53
 
54
+ def test_to_xml_string_for_special_characters
55
+ cell = @ws.rows.first.cells.first
56
+ cell.value = "&><'\""
57
+
58
+ doc = Nokogiri::XML(@cache_definition.to_xml_string)
59
+ errors = doc.errors
60
+ assert(errors.empty?, "invalid xml: #{errors.map(&:to_s).join(', ')}")
61
+ end
54
62
  end
@@ -136,4 +136,25 @@ class TestRow < Test::Unit::TestCase
136
136
  assert_equal(r_s_xml.xpath(".//row[@r=1][@ht=20][@customHeight=1]").size, 1)
137
137
  end
138
138
 
139
+ def test_offsets
140
+ offset = 3
141
+ values = [1,2,3,4,5]
142
+ r = @ws.add_row(values, offset: offset, style: 1)
143
+ r.cells.each_with_index do |c, index|
144
+ assert_equal(c.style, index < offset ? 0 : 1)
145
+ assert_equal(c.value, index < offset ? nil : values[index - offset])
146
+ end
147
+ end
148
+
149
+ def test_offsets_with_styles
150
+ offset = 3
151
+ values = [1,2,3,4,5]
152
+ styles = (1..5).map{ @ws.workbook.styles.add_style }
153
+ r = @ws.add_row(values, offset: offset, style: styles)
154
+ r.cells.each_with_index do |c, index|
155
+ assert_equal(c.style, index < offset ? 0 : styles[index-offset])
156
+ assert_equal(c.value, index < offset ? nil : values[index - offset])
157
+ end
158
+ end
159
+
139
160
  end
@@ -64,4 +64,14 @@ class TestTable < Test::Unit::TestCase
64
64
  end
65
65
  assert(errors.empty?, "error free validation")
66
66
  end
67
+
68
+ def test_to_xml_string_for_special_characters
69
+ cell = @ws.rows.first.cells.first
70
+ cell.value = "&><'\""
71
+
72
+ table = @ws.add_table("A1:D5")
73
+ doc = Nokogiri::XML(table.to_xml_string)
74
+ errors = doc.errors
75
+ assert(errors.empty?, "invalid xml: #{errors.map(&:to_s).join(', ')}")
76
+ end
67
77
  end
@@ -29,15 +29,28 @@ class TestWorksheet < Test::Unit::TestCase
29
29
  assert_raises(ArgumentError) { @ws.name = 'foo?bar' }
30
30
  end
31
31
 
32
+ def test_name_unique
33
+ assert_raise(ArgumentError, "worksheet name must be unique") { n = @ws.name; @ws.workbook.add_worksheet(:name=> n) }
34
+ end
35
+
36
+ def test_name_unique_only_checks_other_worksheet_names
37
+ assert_nothing_raised { @ws.name = @ws.name }
38
+ assert_nothing_raised { Axlsx::Package.new.workbook.add_worksheet :name => 'Sheet1' }
39
+ end
40
+
32
41
  def test_exception_if_name_too_long
33
42
  assert_nothing_raised { @ws.name = 'x' * 31 }
34
43
  assert_raises(ArgumentError) { @ws.name = 'x' * 32 }
35
44
  end
36
45
 
37
46
  def test_exception_if_name_too_long_because_of_multibyte_characters
38
- three_byte_character = ""
39
- assert_nothing_raised { @ws.name = 'x' * 28 + three_byte_character}
40
- assert_raises(ArgumentError) { @ws.name = 'x' * 29 + three_byte_character }
47
+ four_characters_for_excel = "\u{1F1EB 1F1F7}" # french flag emoji
48
+ assert_raises(ArgumentError, "name too long!") do
49
+ @ws.name = four_characters_for_excel + "x" * 28
50
+ end
51
+ assert_nothing_raised { @ws.name = "#{four_characters_for_excel}123456789012345678901234567" }
52
+ assert_nothing_raised { @ws.name = "123456789012345678901234567890…" }
53
+ assert_nothing_raised { @ws.name = "123456789012345678901234567890✔" }
41
54
  end
42
55
 
43
56
  def test_page_margins
@@ -460,21 +473,6 @@ class TestWorksheet < Test::Unit::TestCase
460
473
  assert_equal(@ws.relationships.size, 4, "adding a pivot table adds 1 relationship")
461
474
  end
462
475
 
463
-
464
- def test_name_unique
465
- assert_raise(ArgumentError, "worksheet name must be unique") { n = @ws.name; @ws.workbook.add_worksheet(:name=> n) }
466
- end
467
-
468
- def test_name_unique_only_checks_other_worksheet_names
469
- assert_nothing_raised { @ws.name = @ws.name }
470
- assert_nothing_raised { Axlsx::Package.new.workbook.add_worksheet :name => 'Sheet1' }
471
- end
472
-
473
- def test_name_size
474
- assert_raise(ArgumentError, "name too long!") { @ws.name = Array.new(32, "A").join() }
475
- assert_nothing_raised { @ws.name = Array.new(31, "A").join() }
476
- end
477
-
478
476
  def test_set_fixed_width_column
479
477
  @ws.add_row ["mule", "donkey", "horse"], :widths => [20, :ignore, nil]
480
478
  assert(@ws.column_info.size == 3, "a data item for each column")