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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +31 -8
- data/README.md +4 -0
- data/examples/{image1.jpeg → assets/image1.jpeg} +0 -0
- data/examples/generate.rb +15 -0
- data/lib/axlsx/drawing/bar_3D_chart.rb +5 -8
- data/lib/axlsx/drawing/bar_chart.rb +13 -18
- data/lib/axlsx/drawing/bar_series.rb +18 -1
- data/lib/axlsx/drawing/pie_series.rb +1 -1
- data/lib/axlsx/package.rb +44 -6
- data/lib/axlsx/util/constants.rb +2 -1
- data/lib/axlsx/util/mime_type_utils.rb +1 -1
- data/lib/axlsx/util/validators.rb +1 -1
- data/lib/axlsx/util/zip_command.rb +73 -0
- data/lib/axlsx/version.rb +1 -1
- data/lib/axlsx/workbook/worksheet/cell.rb +9 -3
- data/lib/axlsx/workbook/worksheet/data_validation.rb +4 -4
- data/lib/axlsx/workbook/worksheet/pivot_table.rb +7 -2
- data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +1 -1
- data/lib/axlsx/workbook/worksheet/row.rb +6 -4
- data/lib/axlsx/workbook/worksheet/table.rb +1 -1
- data/lib/axlsx/workbook/worksheet/worksheet.rb +7 -1
- data/lib/axlsx.rb +7 -5
- data/test/drawing/tc_bar_3D_chart.rb +26 -11
- data/test/drawing/tc_bar_chart.rb +26 -11
- data/test/drawing/tc_bar_series.rb +10 -1
- data/test/drawing/tc_drawing.rb +2 -2
- data/test/drawing/tc_hyperlink.rb +1 -1
- data/test/drawing/tc_one_cell_anchor.rb +1 -1
- data/test/drawing/tc_pic.rb +4 -4
- data/test/drawing/tc_pie_series.rb +2 -1
- data/test/fixtures/image1.gif +0 -0
- data/test/fixtures/image1.jpeg +0 -0
- data/test/fixtures/image1.jpg +0 -0
- data/test/fixtures/image1.png +0 -0
- data/test/fixtures/image1_fake.jpg +0 -0
- data/test/tc_helper.rb +0 -2
- data/test/tc_package.rb +80 -13
- data/test/util/tc_mime_type_utils.rb +1 -1
- data/test/util/tc_validators.rb +1 -1
- data/test/workbook/worksheet/tc_cell.rb +38 -0
- data/test/workbook/worksheet/tc_pivot_table.rb +8 -0
- data/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +8 -0
- data/test/workbook/worksheet/tc_row.rb +21 -0
- data/test/workbook/worksheet/tc_table.rb +10 -0
- data/test/workbook/worksheet/tc_worksheet.rb +16 -18
- metadata +116 -137
- data/examples/2010_comments.rb +0 -17
- data/examples/anchor_swapping.rb +0 -28
- data/examples/auto_filter.rb +0 -25
- data/examples/basic_charts.rb +0 -58
- data/examples/chart_colors.rb +0 -88
- data/examples/colored_links.rb +0 -45
- data/examples/conditional_formatting/example_conditional_formatting.rb +0 -89
- data/examples/conditional_formatting/getting_barred.rb +0 -37
- data/examples/conditional_formatting/hitting_the_high_notes.rb +0 -37
- data/examples/conditional_formatting/scaled_colors.rb +0 -39
- data/examples/conditional_formatting/stop_and_go.rb +0 -37
- data/examples/data_validation.rb +0 -67
- data/examples/example.rb +0 -900
- data/examples/extractive.rb +0 -45
- data/examples/ios_preview.rb +0 -14
- data/examples/merge_cells.rb +0 -17
- data/examples/no_grid_with_borders.rb +0 -18
- data/examples/page_setup.rb +0 -11
- data/examples/pivot_table.rb +0 -39
- data/examples/pivot_test.rb +0 -63
- data/examples/sheet_protection.rb +0 -10
- data/examples/skydrive/real_example.rb +0 -63
- data/examples/split.rb +0 -16
- data/examples/styles.rb +0 -66
- data/examples/underline.rb +0 -13
- 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.
|
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
|
-
|
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 '
|
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
|
-
|
54
|
-
|
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 =
|
58
|
+
escaped_name = first_cell.row.worksheet.name.gsub ''', "''"
|
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.
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
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
|
data/test/drawing/tc_drawing.rb
CHANGED
@@ -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__) + "
|
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__) + "
|
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__) + "
|
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__) + "
|
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
|
data/test/drawing/tc_pic.rb
CHANGED
@@ -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__) + "
|
9
|
-
@test_img_png = File.dirname(__FILE__) + "
|
10
|
-
@test_img_gif = File.dirname(__FILE__) + "
|
11
|
-
@test_img_fake = File.dirname(__FILE__) + "
|
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
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('
|
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('
|
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('
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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(
|
231
|
+
assert_equal(Marcel::MimeType.for(@package.to_stream),
|
165
232
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
166
233
|
end
|
167
234
|
|
data/test/util/tc_validators.rb
CHANGED
@@ -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
|
-
|
39
|
-
|
40
|
-
|
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")
|