diff options
| author | 2017-06-12 03:37:11 -0400 | |
|---|---|---|
| committer | 2017-06-12 03:37:11 -0400 | |
| commit | 8280a21a23d44aa90177e2bc041d0b8dc8556f4b (patch) | |
| tree | dadef7ee085c0e990a5070bd41b6a5b98c97f4fd /spec/lib/roo | |
Import Upstream version 2.7.1upstream/2.7.1
Diffstat (limited to 'spec/lib/roo')
| -rw-r--r-- | spec/lib/roo/base_spec.rb | 233 | ||||
| -rw-r--r-- | spec/lib/roo/csv_spec.rb | 81 | ||||
| -rw-r--r-- | spec/lib/roo/excelx/format_spec.rb | 51 | ||||
| -rwxr-xr-x | spec/lib/roo/excelx_spec.rb | 537 | ||||
| -rw-r--r-- | spec/lib/roo/libreoffice_spec.rb | 29 | ||||
| -rw-r--r-- | spec/lib/roo/openoffice_spec.rb | 43 | ||||
| -rw-r--r-- | spec/lib/roo/spreadsheet_spec.rb | 108 | ||||
| -rw-r--r-- | spec/lib/roo/utils_spec.rb | 106 |
8 files changed, 1188 insertions, 0 deletions
diff --git a/spec/lib/roo/base_spec.rb b/spec/lib/roo/base_spec.rb new file mode 100644 index 0000000..d00025d --- /dev/null +++ b/spec/lib/roo/base_spec.rb @@ -0,0 +1,233 @@ +require 'spec_helper' + +describe Roo::Base do + let(:klass) do + Class.new(Roo::Base) do + def initialize(filename, data = {}) + super(filename) + @data ||= data + end + + def read_cells(sheet = default_sheet) + return if @cells_read[sheet] + type_map = { String => :string, Date => :date, Numeric => :float } + + @cell[sheet] = @data + @cell_type[sheet] = Hash[@data.map { |k, v| [k, type_map.find {|type,_| v.is_a?(type) }.last ] }] + @first_row[sheet] = @data.map { |k, _| k[0] }.min + @last_row[sheet] = @data.map { |k, _| k[0] }.max + @first_column[sheet] = @data.map { |k, _| k[1] }.min + @last_column[sheet] = @data.map { |k, _| k[1] }.max + @cells_read[sheet] = true + end + + def cell(row, col, sheet = nil) + sheet ||= default_sheet + read_cells(sheet) + @cell[sheet][[row, col]] + end + + def celltype(row, col, sheet = nil) + sheet ||= default_sheet + read_cells(sheet) + @cell_type[sheet][[row, col]] + end + + def sheets + ['my_sheet', 'blank sheet'] + end + end + end + + let(:spreadsheet_data) do + { + [3, 1] => 'Header', + + [5, 1] => Date.civil(1961, 11, 21), + + [8, 3] => 'thisisc8', + [8, 7] => 'thisisg8', + + [12, 1] => 41.0, + [12, 2] => 42.0, + [12, 3] => 43.0, + [12, 4] => 44.0, + [12, 5] => 45.0, + + [15, 3] => 43.0, + [15, 4] => 44.0, + [15, 5] => 45.0, + + [16, 2] => '"Hello world!"', + [16, 3] => 'forty-three', + [16, 4] => 'forty-four', + [16, 5] => 'forty-five' + } + end + + let(:spreadsheet) { klass.new('some_file', spreadsheet_data) } + + describe '#uri?' do + it 'should return true when passed a filename starting with http(s)://' do + expect(spreadsheet.send(:uri?, 'http://example.com/')).to be_truthy + expect(spreadsheet.send(:uri?, 'https://example.com/')).to be_truthy + end + + it 'should return false when passed a filename which does not start with http(s)://' do + expect(spreadsheet.send(:uri?, 'example.com')).to be_falsy + end + + it 'should return false when passed non-String object such as Tempfile' do + expect(spreadsheet.send(:uri?, Tempfile.new('test'))).to be_falsy + end + end + + describe '#set' do + it 'should not update cell when setting an invalid type' do + spreadsheet.set(1, 1, 1) + expect { spreadsheet.set(1, 1, :invalid_type) }.to raise_error(ArgumentError) + expect(spreadsheet.cell(1, 1)).to eq(1) + expect(spreadsheet.celltype(1, 1)).to eq(:float) + end + end + + describe '#first_row' do + it 'should return the first row' do + expect(spreadsheet.first_row).to eq(3) + end + end + + describe '#last_row' do + it 'should return the last row' do + expect(spreadsheet.last_row).to eq(16) + end + end + + describe '#first_column' do + it 'should return the first column' do + expect(spreadsheet.first_column).to eq(1) + end + end + + describe '#first_column_as_letter' do + it 'should return the first column as a letter' do + expect(spreadsheet.first_column_as_letter).to eq('A') + end + end + + describe '#last_column' do + it 'should return the last column' do + expect(spreadsheet.last_column).to eq(7) + end + end + + describe '#last_column_as_letter' do + it 'should return the last column as a letter' do + expect(spreadsheet.last_column_as_letter).to eq('G') + end + end + + describe '#row' do + it 'should return the specified row' do + expect(spreadsheet.row(12)).to eq([41.0, 42.0, 43.0, 44.0, 45.0, nil, nil]) + expect(spreadsheet.row(16)).to eq([nil, '"Hello world!"', 'forty-three', 'forty-four', 'forty-five', nil, nil]) + end + end + + describe '#row_with' do + context 'with a matching header row' do + it 'returns the row number' do + expect(spreadsheet.row_with([/Header/])). to eq 3 + end + end + + context 'without a matching header row' do + it 'raises an error' do + expect { spreadsheet.row_with([/Missing Header/]) }.to \ + raise_error(Roo::HeaderRowNotFoundError) + end + end + end + + describe '#empty?' do + it 'should return true when empty' do + expect(spreadsheet.empty?(1, 1)).to be_truthy + expect(spreadsheet.empty?(8, 3)).to be_falsy + expect(spreadsheet.empty?('A', 11)).to be_truthy + expect(spreadsheet.empty?('A', 12)).to be_falsy + end + end + + describe '#reload' do + it 'should return reinitialize the spreadsheet' do + spreadsheet.reload + expect(spreadsheet.instance_variable_get(:@cell).empty?).to be_truthy + end + end + + describe '#each' do + it 'should return an enumerator with all the rows' do + each = spreadsheet.each + expect(each).to be_a(Enumerator) + expect(each.to_a.last).to eq([nil, '"Hello world!"', 'forty-three', 'forty-four', 'forty-five', nil, nil]) + end + end + + describe '#to_yaml' do + it 'should convert the spreadsheet to yaml' do + expect(spreadsheet.to_yaml({}, 5, 1, 5, 1)).to eq("--- \n" + yaml_entry(5, 1, 'date', '1961-11-21')) + expect(spreadsheet.to_yaml({}, 8, 3, 8, 3)).to eq("--- \n" + yaml_entry(8, 3, 'string', 'thisisc8')) + + expect(spreadsheet.to_yaml({}, 12, 3, 12, 3)).to eq("--- \n" + yaml_entry(12, 3, 'float', 43.0)) + + expect(spreadsheet.to_yaml({}, 12, 3, 12)).to eq( + "--- \n" + yaml_entry(12, 3, 'float', 43.0) + + yaml_entry(12, 4, 'float', 44.0) + + yaml_entry(12, 5, 'float', 45.0)) + + expect(spreadsheet.to_yaml({}, 12, 3)).to eq( + "--- \n" + yaml_entry(12, 3, 'float', 43.0) + + yaml_entry(12, 4, 'float', 44.0) + + yaml_entry(12, 5, 'float', 45.0) + + yaml_entry(15, 3, 'float', 43.0) + + yaml_entry(15, 4, 'float', 44.0) + + yaml_entry(15, 5, 'float', 45.0) + + yaml_entry(16, 3, 'string', 'forty-three') + + yaml_entry(16, 4, 'string', 'forty-four') + + yaml_entry(16, 5, 'string', 'forty-five')) + end + end + + let(:expected_csv) do + <<EOS +,,,,,, +,,,,,, +"Header",,,,,, +,,,,,, +1961-11-21,,,,,, +,,,,,, +,,,,,, +,,"thisisc8",,,,"thisisg8" +,,,,,, +,,,,,, +,,,,,, +41,42,43,44,45,, +,,,,,, +,,,,,, +,,43,44,45,, +,"""Hello world!""","forty-three","forty-four","forty-five",, +EOS + end + + let(:expected_csv_with_semicolons) { expected_csv.gsub(/\,/, ';') } + + describe '#to_csv' do + it 'should convert the spreadsheet to csv' do + expect(spreadsheet.to_csv).to eq(expected_csv) + end + + it 'should convert the spreadsheet to csv using the separator when is passed on the parameter' do + expect(spreadsheet.to_csv(nil, ';')).to eq(expected_csv_with_semicolons) + end + end +end diff --git a/spec/lib/roo/csv_spec.rb b/spec/lib/roo/csv_spec.rb new file mode 100644 index 0000000..2ea1b93 --- /dev/null +++ b/spec/lib/roo/csv_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' + +describe Roo::CSV do + let(:path) { 'test/files/csvtypes.csv' } + let(:csv) { Roo::CSV.new(path) } + + describe '.new' do + it 'creates an instance' do + expect(csv).to be_a(Roo::CSV) + end + end + + describe '.new with stream' do + let(:csv) { Roo::CSV.new(File.read(path)) } + it 'creates an instance' do + expect(csv).to be_a(Roo::CSV) + end + end + + describe '#parse' do + subject do + csv.parse(options) + end + context 'with headers: true' do + let(:options) { { headers: true } } + + it "doesn't blow up" do + expect { subject }.to_not raise_error + end + end + end + + describe '#parse_with_clean_option' do + subject do + csv.parse(options) + end + context 'with clean: true' do + let(:options) { {clean: true} } + let(:path) { 'test/files/parse_with_clean_option.csv' } + + it "doesn't blow up" do + expect { subject }.to_not raise_error + end + end + end + + describe '#csv_options' do + context 'when created with the csv_options option' do + let(:options) do + { + col_sep: '\t', + quote_char: "'" + } + end + + it 'returns the csv options' do + csv = Roo::CSV.new(path, csv_options: options) + expect(csv.csv_options).to eq(options) + end + end + + context 'when created without the csv_options option' do + it 'returns a hash' do + csv = Roo::CSV.new(path) + expect(csv.csv_options).to eq({}) + end + end + end + + describe '#set_value' do + it 'returns the cell value' do + expect(csv.set_value('A', 1, 'some-value', nil)).to eq('some-value') + end + end + + describe '#set_type' do + it 'returns the cell type' do + expect(csv.set_type('A', 1, 'some-type', nil)).to eq('some-type') + end + end +end diff --git a/spec/lib/roo/excelx/format_spec.rb b/spec/lib/roo/excelx/format_spec.rb new file mode 100644 index 0000000..a2819a5 --- /dev/null +++ b/spec/lib/roo/excelx/format_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe Roo::Excelx::Format do + describe '#to_type' do + FORMATS = { + 'General' => :float, + '0' => :float, + '0.00' => :float, + '#,##0' => :float, + '#,##0.00' => :float, + '0%' => :percentage, + '0.00%' => :percentage, + '0.00E+00' => :float, + '# ?/?' => :float, # ??? TODO: + '# ??/??' => :float, # ??? TODO: + 'mm-dd-yy' => :date, + 'd-mmm-yy' => :date, + 'd-mmm' => :date, + 'mmm-yy' => :date, + 'h:mm AM/PM' => :date, + 'h:mm:ss AM/PM' => :date, + 'h:mm' => :time, + 'h:mm:ss' => :time, + 'm/d/yy h:mm' => :datetime, + '#,##0 ;(#,##0)' => :float, + '#,##0 ;[Red](#,##0)' => :float, + '#,##0.00;(#,##0.00)' => :float, + '#,##0.00;[Red](#,##0.00)' => :float, + '#,##0_);[Red](#,##0)' => :float, + 'mm:ss' => :time, + '[h]:mm:ss' => :time, + 'mmss.0' => :time, + '##0.0E+0' => :float, + '@' => :float, + #-- zusaetzliche Formate, die nicht standardmaessig definiert sind: + 'yyyy\\-mm\\-dd' => :date, + 'dd/mm/yy' => :date, + 'hh:mm:ss' => :time, + 'dd/mm/yy\\ hh:mm' => :datetime, + 'dd/mmm/yy\\ hh:mm' => :datetime, + 'dd/mmm/yy' => :date, # 2011-05-21 + 'yyyy-mm-dd' => :date, # 2011-09-16 + 'yyyy-mm-dd;@' => :date, + '#0_);[Red]\(0\)' => :float + }.each do |format, type| + it "translates #{format} to #{type}" do + expect(Roo::Excelx::Format.to_type(format)).to eq(type) + end + end + end +end diff --git a/spec/lib/roo/excelx_spec.rb b/spec/lib/roo/excelx_spec.rb new file mode 100755 index 0000000..0ef0f19 --- /dev/null +++ b/spec/lib/roo/excelx_spec.rb @@ -0,0 +1,537 @@ +# encoding: utf-8 +require 'spec_helper' + +describe Roo::Excelx do + subject(:xlsx) do + Roo::Excelx.new(path) + end + + describe 'Constants' do + describe 'ERROR_VALUES' do + it 'returns all possible errorr values' do + expect(described_class::ERROR_VALUES).to eq(%w(#N/A #REF! #NAME? #DIV/0! #NULL! #VALUE! #NUM!).to_set) + end + + it 'is a set' do + expect(described_class::ERROR_VALUES).to be_an_instance_of(Set) + end + end + end + + describe '.new' do + let(:path) { 'test/files/numeric-link.xlsx' } + + it 'creates an instance' do + expect(subject).to be_a(Roo::Excelx) + end + + context 'given a file with missing rels' do + let(:path) { 'test/files/file_item_error.xlsx' } + + it 'creates an instance' do + expect(subject).to be_a(Roo::Excelx) + end + end + + context 'with more cells than specified max' do + let(:path) { 'test/files/only_one_sheet.xlsx' } + + it 'raises an appropriate error' do + expect { Roo::Excelx.new(path, cell_max: 1) }.to raise_error(Roo::Excelx::ExceedsMaxError) + end + end + + context 'with fewer cells than specified max' do + let(:path) { 'test/files/only_one_sheet.xlsx' } + + it 'creates an instance' do + expect(Roo::Excelx.new(path, cell_max: 100)).to be_a(Roo::Excelx) + end + end + + context 'file path is a Pathname' do + let(:path) { Pathname.new('test/files/file_item_error.xlsx') } + + it 'creates an instance' do + expect(subject).to be_a(Roo::Excelx) + end + end + end + + describe '#cell' do + context 'for a link cell' do + context 'with numeric contents' do + let(:path) { 'test/files/numeric-link.xlsx' } + + subject do + xlsx.cell('A', 1) + end + + it 'returns a link with the number as a string value' do + expect(subject).to be_a(Roo::Link) + # FIXME: Because Link inherits from String, it is a String, + # But in theory, it shouldn't have to be a String. + # NOTE: This test is broken becase Cell::Numeric formats numbers + # more intelligently. + # expect(subject).to eq('8675309.0') + end + end + end + + context 'for a non-existent cell' do + let(:path) { 'test/files/numeric-link.xlsx' } + it 'return nil' do + expect(xlsx.cell('AAA', 999)).to eq nil + end + end + end + + describe '#parse' do + let(:path) { 'test/files/numeric-link.xlsx' } + + context 'with a columns hash' do + context 'when not present in the sheet' do + it 'does not raise' do + expect do + xlsx.sheet(0).parse( + this: 'This', + that: 'That' + ) + end.to raise_error(Roo::HeaderRowNotFoundError) + end + end + end + end + + describe '#parse_with_clean_option' do + let(:path) { 'test/files/parse_with_clean_option.xlsx' } + let(:options) { {clean: true} } + + context 'with clean: true' do + + it 'does not raise' do + expect do + xlsx.parse(options) + end.not_to raise_error + end + end + end + + describe '#parse_unicode_with_clean_option' do + let(:path) { 'test/files/parse_clean_with_unicode.xlsx' } + let(:options) { {clean: true, name: 'Name'} } + + context 'with clean: true' do + it 'returns a non empty string' do + expect(xlsx.parse(options).last[:name]).to eql('凯') + end + end + end + + describe '#sheets' do + let(:path) { 'test/files/numbers1.xlsx' } + + it 'returns the expected result' do + expect(subject.sheets).to eq ["Tabelle1", "Name of Sheet 2", "Sheet3", "Sheet4", "Sheet5"] + end + + describe 'only showing visible sheets' do + let(:path) { 'test/files/hidden_sheets.xlsx' } + + it 'returns the expected result' do + expect(Roo::Excelx.new(path, only_visible_sheets: true).sheets).to eq ["VisibleSheet1"] + end + end + end + + describe '#sheet_for' do + let(:path) { 'test/files/numbers1.xlsx' } + + # This is kinda gross + it 'returns the expected result' do + expect(subject.sheet_for("Tabelle1").instance_variable_get("@name")).to eq "Tabelle1" + end + end + + describe '#row' do + let(:path) { 'test/files/numbers1.xlsx' } + + it 'returns the expected result' do + expect(subject.row(1, "Sheet5")).to eq [1.0, 5.0, 5.0, nil, nil] + end + end + + describe '#column' do + let(:path) { 'test/files/numbers1.xlsx' } + + it 'returns the expected result' do + expect(subject.column(1, "Sheet5")).to eq [1.0, 2.0, 3.0, Date.new(2007,11,21), 42.0, "ABC"] + end + end + + describe '#first_row' do + let(:path) { 'test/files/numbers1.xlsx' } + + it 'returns the expected result' do + expect(subject.first_row("Sheet5")).to eq 1 + end + end + + describe '#last_row' do + let(:path) { 'test/files/numbers1.xlsx' } + + it 'returns the expected result' do + expect(subject.last_row("Sheet5")).to eq 6 + end + end + + describe '#first_column' do + let(:path) { 'test/files/numbers1.xlsx' } + + it 'returns the expected result' do + expect(subject.first_column("Sheet5")).to eq 1 + end + end + + describe '#last_column' do + let(:path) { 'test/files/numbers1.xlsx' } + + it 'returns the expected result' do + expect(subject.last_column("Sheet5")).to eq 5 + end + end + + describe '#set' do + before do + subject.set(1, 2, "Foo", "Sheet5") + end + + let(:path) { 'test/files/numbers1.xlsx' } + let(:cell) { subject.cell(1, 2, "Sheet5") } + + it 'returns the expected result' do + expect(cell).to eq "Foo" + end + end + + describe '#formula' do + let(:path) { 'test/files/formula.xlsx' } + + it 'returns the expected result' do + expect(subject.formula(1, 1, "Sheet1")).to eq nil + expect(subject.formula(7, 2, "Sheet1")).to eq "SUM($A$1:B6)" + expect(subject.formula(1000, 2000, "Sheet1")).to eq nil + end + end + + describe '#formula?' do + let(:path) { 'test/files/formula.xlsx' } + + it 'returns the expected result' do + expect(subject.formula?(1, 1, "Sheet1")).to eq false + expect(subject.formula?(7, 2, "Sheet1")).to eq true + expect(subject.formula?(1000, 2000, "Sheet1")).to eq false + end + end + + describe '#formulas' do + let(:path) { 'test/files/formula.xlsx' } + + it 'returns the expected result' do + expect(subject.formulas("Sheet1")).to eq [[7, 1, "SUM(A1:A6)"], [7, 2, "SUM($A$1:B6)"]] + end + end + + describe '#font' do + let(:path) { 'test/files/style.xlsx' } + + it 'returns the expected result' do + expect(subject.font(1, 1).bold?).to eq true + expect(subject.font(1, 1).italic?).to eq false + expect(subject.font(1, 1).underline?).to eq false + + expect(subject.font(7, 1).bold?).to eq false + expect(subject.font(7, 1).italic?).to eq true + expect(subject.font(7, 1).underline?).to eq true + expect(subject.font(1000, 2000)).to eq nil + end + end + + describe '#celltype' do + let(:path) { 'test/files/numbers1.xlsx' } + + it 'returns the expected result' do + expect(subject.celltype(1, 1, "Sheet4")).to eq :date + expect(subject.celltype(1, 2, "Sheet4")).to eq :float + expect(subject.celltype(6, 2, "Sheet5")).to eq :string + expect(subject.celltype(1000, 2000, "Sheet5")).to eq nil + end + end + + describe '#excelx_type' do + let(:path) { 'test/files/numbers1.xlsx' } + + it 'returns the expected result' do + expect(subject.excelx_type(1, 1, "Sheet5")).to eq [:numeric_or_formula, "General"] + expect(subject.excelx_type(6, 2, "Sheet5")).to eq :string + expect(subject.excelx_type(1000, 2000, "Sheet5")).to eq nil + end + end + + # FIXME: IMO, these tests don't provide much value. Under what circumstances + # will a user require the "index" value for the shared strings table? + # Excel value should be the raw unformatted value for the cell. + describe '#excelx_value' do + let(:path) { 'test/files/numbers1.xlsx' } + + it 'returns the expected result' do + # These values are the index in the shared strings table, might be a better + # way to get these rather than hardcoding. + + # expect(subject.excelx_value(1, 1, "Sheet5")).to eq "1" # passes by accident + # expect(subject.excelx_value(6, 2, "Sheet5")).to eq "16" + # expect(subject.excelx_value(6000, 2000, "Sheet5")).to eq nil + end + end + + describe '#formatted_value' do + context 'contains zero-padded numbers' do + let(:path) { 'test/files/zero-padded-number.xlsx' } + + it 'returns a zero-padded number' do + expect(subject.formatted_value(4, 1)).to eq '05010' + end + end + end + + describe '#excelx_format' do + let(:path) { 'test/files/style.xlsx' } + + it 'returns the expected result' do + # These are the index of the style for a given document + # might be more reliable way to get this info. + expect(subject.excelx_format(1, 1)).to eq "General" + expect(subject.excelx_format(2, 2)).to eq "0.00" + expect(subject.excelx_format(5000, 1000)).to eq nil + end + end + + describe '#empty?' do + let(:path) { 'test/files/style.xlsx' } + + it 'returns the expected result' do + # These are the index of the style for a given document + # might be more reliable way to get this info. + expect(subject.empty?(1, 1)).to eq false + expect(subject.empty?(13, 1)).to eq true + end + end + + describe '#label' do + let(:path) { 'test/files/named_cells.xlsx' } + + it 'returns the expected result' do + expect(subject.label("berta")).to eq [4, 2, "Sheet1"] + expect(subject.label("dave")).to eq [nil, nil, nil] + end + end + + describe '#labels' do + let(:path) { 'test/files/named_cells.xlsx' } + + it 'returns the expected result' do + expect(subject.labels).to eq [["anton", [5, 3, "Sheet1"]], ["berta", [4, 2, "Sheet1"]], ["caesar", [7, 2, "Sheet1"]]] + end + end + + describe '#hyperlink?' do + let(:path) { 'test/files/link.xlsx' } + + it 'returns the expected result' do + expect(subject.hyperlink?(1, 1)).to eq true + expect(subject.hyperlink?(1, 2)).to eq false + end + end + + describe '#hyperlink' do + let(:path) { 'test/files/link.xlsx' } + + it 'returns the expected result' do + expect(subject.hyperlink(1, 1)).to eq "http://www.google.com" + expect(subject.hyperlink(1, 2)).to eq nil + end + end + + describe '#comment' do + let(:path) { 'test/files/comments.xlsx' } + + it 'returns the expected result' do + expect(subject.comment(4, 2)).to eq "Kommentar fuer B4" + expect(subject.comment(1, 2)).to eq nil + end + end + + describe '#comment?' do + let(:path) { 'test/files/comments.xlsx' } + + it 'returns the expected result' do + expect(subject.comment?(4, 2)).to eq true + expect(subject.comment?(1, 2)).to eq false + end + end + + describe '#comments' do + let(:path) { 'test/files/comments.xlsx' } + + it 'returns the expected result' do + expect(subject.comments).to eq [[4, 2, "Kommentar fuer B4"], [5, 2, "Kommentar fuer B5"]] + end + end + + # nil, nil, nil, nil, nil + # nil, nil, nil, nil, nil + # Date Start time End time Pause Sum Comment + # 2007-05-07 9.25 10.25 0 1 Task 1 + # 2007-05-07 10.75 12.50 0 1.75 Task 1 + # 2007-05-07 18.00 19.00 0 1 Task 2 + # 2007-05-08 9.25 10.25 0 1 Task 2 + # 2007-05-08 14.50 15.50 0 1 Task 3 + # 2007-05-08 8.75 9.25 0 0.5 Task 3 + # 2007-05-14 21.75 22.25 0 0.5 Task 3 + # 2007-05-14 22.50 23.00 0 0.5 Task 3 + # 2007-05-15 11.75 12.75 0 1 Task 3 + # 2007-05-07 10.75 10.75 0 0 Task 1 + # nil + describe '#each_row_streaming' do + let(:path) { 'test/files/simple_spreadsheet.xlsx' } + + let(:expected_rows) do + [ + [nil, nil, nil, nil, nil], + [nil, nil, nil, nil, nil], + ["Date", "Start time", "End time", "Pause", "Sum", "Comment", nil, nil], + [Date.new(2007, 5, 7), 9.25, 10.25, 0.0, 1.0, "Task 1"], + [Date.new(2007, 5, 7), 10.75, 12.50, 0.0, 1.75, "Task 1"], + [Date.new(2007, 5, 7), 18.0, 19.0, 0.0, 1.0, "Task 2"], + [Date.new(2007, 5, 8), 9.25, 10.25, 0.0, 1.0, "Task 2"], + [Date.new(2007, 5, 8), 14.5, 15.5, 0.0, 1.0, "Task 3"], + [Date.new(2007, 5, 8), 8.75, 9.25, 0.0, 0.5, "Task 3"], + [Date.new(2007, 5, 14), 21.75, 22.25, 0.0, 0.5, "Task 3"], + [Date.new(2007, 5, 14), 22.5, 23.0, 0.0, 0.5, "Task 3"], + [Date.new(2007, 5, 15), 11.75, 12.75, 0.0, 1.0, "Task 3"], + [Date.new(2007, 5, 7), 10.75, 10.75, 0.0, 0.0, "Task 1"], + [nil] + ] + end + + it 'returns the expected result' do + index = 0 + subject.each_row_streaming do |row| + expect(row.map(&:value)).to eq expected_rows[index] + index += 1 + end + end + + context 'with max_rows options' do + it 'returns the expected result' do + index = 0 + subject.each_row_streaming(max_rows: 3) do |row| + expect(row.map(&:value)).to eq expected_rows[index] + index += 1 + end + # Expect this to get incremented one time more than max (because of the increment at the end of the block) + # but it should not be near expected_rows.size + expect(index).to eq 4 + end + end + + context 'with offset option' do + let(:offset) { 3 } + + it 'returns the expected result' do + index = 0 + subject.each_row_streaming(offset: offset) do |row| + expect(row.map(&:value)).to eq expected_rows[index + offset] + index += 1 + end + expect(index).to eq 11 + end + end + + context 'with offset and max_rows options' do + let(:offset) { 3 } + let(:max_rows) { 3 } + + it 'returns the expected result' do + index = 0 + subject.each_row_streaming(offset: offset, max_rows: max_rows) do |row| + expect(row.map(&:value)).to eq expected_rows[index + offset] + index += 1 + end + expect(index).to eq 4 + end + end + + context 'without block passed' do + it 'returns an enumerator' do + expect(subject.each_row_streaming).to be_a(Enumerator) + end + end + end + + describe '#html_strings' do + let(:path) { 'test/files/html_strings_formatting.xlsx' } + + it 'returns the expected result' do + expect(subject.excelx_value(1, 1, "Sheet1")).to eq "This has no formatting." + expect(subject.excelx_value(2, 1, "Sheet1")).to eq "<html>This has<b> bold </b>formatting.</html>" + expect(subject.excelx_value(2, 2, "Sheet1")).to eq "<html>This has <i>italics</i> formatting.</html>" + expect(subject.excelx_value(2, 3, "Sheet1")).to eq "<html>This has <u>underline</u> format.</html>" + expect(subject.excelx_value(2, 4, "Sheet1")).to eq "<html>Superscript. x<sup>123</sup></html>" + expect(subject.excelx_value(2, 5, "Sheet1")).to eq "<html>SubScript. T<sub>j</sub></html>" + + expect(subject.excelx_value(3, 1, "Sheet1")).to eq "<html>Bold, italics <b><i>together</i></b>.</html>" + expect(subject.excelx_value(3, 2, "Sheet1")).to eq "<html>Bold, Underline <b><u>together</u></b>.</html>" + expect(subject.excelx_value(3, 3, "Sheet1")).to eq "<html>Bold, Superscript. <b>x</b><sup><b>N</b></sup></html>" + expect(subject.excelx_value(3, 4, "Sheet1")).to eq "<html>Bold, Subscript. <b>T</b><sub><b>abc</b></sub></html>" + expect(subject.excelx_value(3, 5, "Sheet1")).to eq "<html>Italics, Underline <i><u>together</u></i>.</html>" + expect(subject.excelx_value(3, 6, "Sheet1")).to eq "<html>Italics, Superscript. <i>X</i><sup><i>abc</i></sup></html>" + expect(subject.excelx_value(3, 7, "Sheet1")).to eq "<html>Italics, Subscript. <i>B</i><sub><i>efg</i></sub></html>" + expect(subject.excelx_value(4, 1, "Sheet1")).to eq "<html>Bold, italics underline,<b><i><u> together</u></i></b>.</html>" + expect(subject.excelx_value(4, 2, "Sheet1")).to eq "<html>Bold, italics, superscript. <b>X</b><sup><b><i>abc</i></b></sup><b><i>123</i></b></html>" + expect(subject.excelx_value(4, 3, "Sheet1")).to eq "<html>Bold, Italics, subscript. <b><i>Mg</i></b><sub><b><i>ha</i></b></sub><b><i>2</i></b></html>" + expect(subject.excelx_value(4, 4, "Sheet1")).to eq "<html>Bold, Underline, superscript. <b><u>AB</u></b><sup><b><u>C12</u></b></sup><b><u>3</u></b></html>" + expect(subject.excelx_value(4, 5, "Sheet1")).to eq "<html>Bold, Underline, subscript. <b><u>Good</u></b><sub><b><u>XYZ</u></b></sub></html>" + expect(subject.excelx_value(4, 6, "Sheet1")).to eq "<html>Italics, Underline, superscript. <i><u>Up</u></i><sup><i><u>swing</u></i></sup></html>" + expect(subject.excelx_value(4, 7, "Sheet1")).to eq "<html>Italics, Underline, subscript. <i><u>T</u></i><sub><i><u>swing</u></i></sub></html>" + expect(subject.excelx_value(5, 1, "Sheet1")).to eq "<html>Bold, italics, underline, superscript. <b><i><u>GHJK</u></i></b><sup><b><i><u>190</u></i></b></sup><b><i><u>4</u></i></b></html>" + expect(subject.excelx_value(5, 2, "Sheet1")).to eq "<html>Bold, italics, underline, subscript. <b><i><u>Mike</u></i></b><sub><b><i><u>drop</u></i></b></sub></html>" + expect(subject.excelx_value(6, 1, "Sheet1")).to eq "See that regular html tags do not create html tags.\n<ol>\n <li> Denver Broncos </li>\n <li> Carolina Panthers </li>\n <li> New England Patriots</li>\n <li>Arizona Panthers</li>\n</ol>" + expect(subject.excelx_value(7, 1, "Sheet1")).to eq "<html>Does create html tags when formatting is used..\n<ol>\n <li> <b>Denver Broncos</b> </li>\n <li> <i>Carolina Panthers </i></li>\n <li> <u>New England Patriots</u></li>\n <li>Arizona Panthers</li>\n</ol></html>" + end + end + + describe '_x000D_' do + let(:path) { 'test/files/x000D.xlsx' } + it 'does not contain _x000D_' do + expect(subject.cell(2, 9)).not_to include('_x000D_') + end + end + + describe 'opening a file with a chart sheet' do + let(:path) { 'test/files/chart_sheet.xlsx' } + it 'should not raise' do + expect{ subject }.to_not raise_error + end + end + + describe 'opening a file with white space in the styles.xml' do + let(:path) { 'test/files/style_nodes_with_white_spaces.xlsx' } + subject(:xlsx) do + Roo::Spreadsheet.open(path, expand_merged_ranges: true, extension: :xlsx) + end + it 'should properly recognize formats' do + expect(subject.sheet(0).excelx_format(2,1)).to eq 'm/d/yyyy" "h:mm:ss" "AM/PM' + end + end +end diff --git a/spec/lib/roo/libreoffice_spec.rb b/spec/lib/roo/libreoffice_spec.rb new file mode 100644 index 0000000..3a64744 --- /dev/null +++ b/spec/lib/roo/libreoffice_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe Roo::LibreOffice do + describe '.new' do + subject do + Roo::LibreOffice.new('test/files/numbers1.ods') + end + + it 'creates an instance' do + expect(subject).to be_a(Roo::LibreOffice) + end + end + + describe '#sheets' do + let(:path) { 'test/files/hidden_sheets.ods' } + + describe 'showing all sheets' do + it 'returns the expected result' do + expect(Roo::LibreOffice.new(path).sheets).to eq ["HiddenSheet1", "VisibleSheet1", "HiddenSheet2"] + end + end + + describe 'only showing visible sheets' do + it 'returns the expected result' do + expect(Roo::LibreOffice.new(path, only_visible_sheets: true).sheets).to eq ["VisibleSheet1"] + end + end + end +end diff --git a/spec/lib/roo/openoffice_spec.rb b/spec/lib/roo/openoffice_spec.rb new file mode 100644 index 0000000..9810f45 --- /dev/null +++ b/spec/lib/roo/openoffice_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe Roo::OpenOffice do + describe '.new' do + subject do + Roo::OpenOffice.new('test/files/numbers1.ods') + end + + it 'creates an instance' do + expect(subject).to be_a(Roo::OpenOffice) + end + + context 'for float/integer values' do + context 'integer without point' do + it { expect(subject.cell(3,"A","Sheet4")).to eq(1234) } + it { expect(subject.cell(3,"A","Sheet4")).to be_a(Integer) } + end + + context 'float with point' do + it { expect(subject.cell(3,"B","Sheet4")).to eq(1234.00) } + it { expect(subject.cell(3,"B","Sheet4")).to be_a(Float) } + end + + context 'float with point' do + it { expect(subject.cell(3,"C","Sheet4")).to eq(1234.12) } + it { expect(subject.cell(3,"C","Sheet4")).to be_a(Float) } + end + end + + context 'file path is a Pathname' do + subject do + Roo::OpenOffice.new(Pathname.new('test/files/numbers1.ods')) + end + + it 'creates an instance' do + expect(subject).to be_a(Roo::OpenOffice) + end + end + + end + + # OpenOffice is an alias of LibreOffice. See libreoffice_spec. +end diff --git a/spec/lib/roo/spreadsheet_spec.rb b/spec/lib/roo/spreadsheet_spec.rb new file mode 100644 index 0000000..19fb8a7 --- /dev/null +++ b/spec/lib/roo/spreadsheet_spec.rb @@ -0,0 +1,108 @@ +require 'spec_helper' + +describe Roo::Spreadsheet do + describe '.open' do + context 'when the file name includes a space' do + let(:filename) { 'great scott.xlsx' } + + it 'loads the proper type' do + expect(Roo::Excelx).to receive(:new).with(filename, {}) + Roo::Spreadsheet.open(filename) + end + end + + context 'when the file extension is uppercase' do + let(:filename) { 'file.XLSX' } + + it 'loads the proper type' do + expect(Roo::Excelx).to receive(:new).with(filename, {}) + Roo::Spreadsheet.open(filename) + end + end + + context 'for a tempfile' do + let(:tempfile) { Tempfile.new('foo.csv') } + let(:filename) { tempfile.path } + + it 'loads the proper type' do + expect(Roo::CSV).to receive(:new).with(filename, file_warning: :ignore).and_call_original + expect(Roo::Spreadsheet.open(tempfile, extension: :csv)).to be_a(Roo::CSV) + end + end + + context 'for a url' do + context 'that is csv' do + let(:filename) { 'http://example.com/file.csv?with=params#and=anchor' } + + it 'treats the url as CSV' do + expect(Roo::CSV).to receive(:new).with(filename, {}) + Roo::Spreadsheet.open(filename) + end + end + end + + context 'for a windows path' do + context 'that is xlsx' do + let(:filename) { 'c:\Users\Joe\Desktop\myfile.xlsx' } + + it 'loads the proper type' do + expect(Roo::Excelx).to receive(:new).with(filename, {}) + Roo::Spreadsheet.open(filename) + end + end + end + + context 'for a xlsm file' do + let(:filename) { 'macros spreadsheet.xlsm' } + + it 'loads the proper type' do + expect(Roo::Excelx).to receive(:new).with(filename, {}) + Roo::Spreadsheet.open(filename) + end + end + + context 'for a csv file' do + let(:filename) { 'file.csv' } + let(:options) { { csv_options: { col_sep: '"' } } } + + context 'with csv_options' do + it 'passes the csv_options through' do + expect(Roo::CSV).to receive(:new).with(filename, options) + Roo::Spreadsheet.open(filename, options) + end + end + end + + context 'with a file extension option' do + let(:filename) { 'file.xls' } + + context ':xlsx' do + let(:options) { { extension: :xlsx } } + + it 'loads with xls extension options' do + expect(Roo::Excelx).to receive(:new).with(filename, options) + Roo::Spreadsheet.open(filename, options) + end + end + + context 'xlsx' do + let(:options) { { extension: 'xlsx' } } + + it 'loads with xls extension options' do + expect(Roo::Excelx).to receive(:new).with(filename, options) + Roo::Spreadsheet.open(filename, options) + end + end + + context '.xlsx' do + let(:options) { { extension: '.xlsx' } } + + it 'loads with .xls extension options' do + expect(Roo::Excelx).to receive(:new).with(filename, options) + Roo::Spreadsheet.open(filename, options) + end + end + + end + end +end diff --git a/spec/lib/roo/utils_spec.rb b/spec/lib/roo/utils_spec.rb new file mode 100644 index 0000000..ffe93d4 --- /dev/null +++ b/spec/lib/roo/utils_spec.rb @@ -0,0 +1,106 @@ +require 'spec_helper' + +RSpec.describe ::Roo::Utils do + subject { described_class } + + context '#number_to_letter' do + described_class::LETTERS.each_with_index do |letter, index| + it "should return '#{ letter }' when passed #{ index + 1 }" do + expect(described_class.number_to_letter(index + 1)).to eq(letter) + end + end + + { + 27 => 'AA', 26*2 => 'AZ', 26*3 => 'BZ', 26**2 + 26 => 'ZZ', 26**2 + 27 => 'AAA', + 26**3 + 26**2 + 26 => 'ZZZ', 1.0 => 'A', 676 => 'YZ', 677 => 'ZA' + }.each do |key, value| + it "should return '#{value}' when passed #{key}" do + expect(described_class.number_to_letter(key)).to eq(value) + end + end + end + + context '#letter_to_number' do + it "should give 1 for 'A' and 'a'" do + expect(described_class.letter_to_number('A')).to eq(1) + expect(described_class.letter_to_number('a')).to eq(1) + end + + it "should give the correct value for 'Z'" do + expect(described_class.letter_to_number('Z')).to eq(26) + end + + it "should give the correct value for 'AA' regardless of case mixing" do + %w(AA aA Aa aa).each do |key| + expect(described_class.letter_to_number(key)).to eq(27) + end + end + + { 'AB' => 28, 'AZ' => 26*2, 'BZ' => 26*3, 'ZZ' => 26**2 + 26 }.each do |key, value| + it "should give the correct value for '#{key}'" do + expect(described_class.letter_to_number(key)).to eq(value) + end + end + end + + context '.split_coordinate' do + it "returns the expected result" do + expect(described_class.split_coordinate('A1')).to eq [1, 1] + expect(described_class.split_coordinate('B2')).to eq [2, 2] + expect(described_class.split_coordinate('R2')).to eq [2, 18] + expect(described_class.split_coordinate('AR31')).to eq [31, 18 + 26] + end + end + + context '.split_coord' do + it "returns the expected result" do + expect(described_class.split_coord('A1')).to eq ["A", 1] + expect(described_class.split_coord('B2')).to eq ["B", 2] + expect(described_class.split_coord('R2')).to eq ["R", 2] + expect(described_class.split_coord('AR31')).to eq ["AR", 31] + end + + it "raises an error when appropriate" do + expect { described_class.split_coord('A') }.to raise_error(ArgumentError) + expect { described_class.split_coord('2') }.to raise_error(ArgumentError) + end + end + + + context '.num_cells_in_range' do + it "returns the expected result" do + expect(described_class.num_cells_in_range('A1:B2')).to eq 4 + expect(described_class.num_cells_in_range('B2:E3')).to eq 8 + expect(described_class.num_cells_in_range('R2:Z10')).to eq 81 + expect(described_class.num_cells_in_range('AR31:AR32')).to eq 2 + expect(described_class.num_cells_in_range('A1')).to eq 1 + end + + it "raises an error when appropriate" do + expect { described_class.num_cells_in_range('A1:B1:B2') }.to raise_error(ArgumentError) + end + end + + context '.load_xml' do + it 'returns the expected result' do + expect(described_class.load_xml('test/files/sheet1.xml')).to be_a(Nokogiri::XML::Document) + expect(described_class.load_xml('test/files/sheet1.xml'). + remove_namespaces!.xpath("/worksheet/dimension").map do |dim| + dim.attributes["ref"].value end.first).to eq "A1:B11" + end + end + + context '.each_element' do + it 'returns the expected result' do + described_class.each_element('test/files/sheet1.xml', 'dimension') do |dim| + expect(dim.attributes["ref"].value).to eq "A1:B11" + end + rows = [] + described_class.each_element('test/files/sheet1.xml', 'row') do |row| + rows << row + end + expect(rows.size).to eq 11 + expect(rows[2].attributes["r"].value).to eq "3" + end + end +end |
