caxlsx 3.0.2 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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")