caxlsx 3.0.2 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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")
|