require 'spec_helper' describe DataTable::Table do context "with a non-empty collection of hashes" do let(:collection) { [ {:name => 'Luke Skywalker', :class => 'Jedi Knight', :world => 'Star Wars', :power_level => 50}, {:name => 'Emporer Palpatine', :class => 'Sith Lord', :world => 'Star Wars', :power_level => 95}, {:name => 'Mithrander', :class => 'Wizard', :world => 'Middle Earth', :power_level => 9001}, {:name => 'Aragorn', :class => 'Ranger', :world => 'Middle Earth', :power_level => 80} ] } let(:data_table) {DataTable::Table.new(collection)} it "should add a column do @columns" do data_table.column(:name, 'Name') expect(data_table.columns).to_not be_empty expect(data_table.columns.first.class).to be(DataTable::Column) end it "should render the collection" do data_table.column(:name, 'Name') data_table.column(:class, 'Class') expect(data_table.render).to \ eq(%{
NameClass
Luke SkywalkerJedi Knight
Emporer PalpatineSith Lord
MithranderWizard
AragornRanger
}) end it "should group the records" do grouping_column = :world data_table.group_by grouping_column, level: 0 data_table.column(:name, 'Name') data_table.column(:class, 'Class') expect(data_table.grouped_data).to be true data_table.prepare_data expect(data_table.collection).to eq(collection.group_by {|g| g[grouping_column]}) expect(data_table.render).to eq(%{
NameClass
Star Wars
Luke SkywalkerJedi Knight
Emporer PalpatineSith Lord
Middle Earth
MithranderWizard
AragornRanger
}) end it "should do totaling" do data_table.column :power_level data_table.total :power_level, :sum, 0 data_table.calculate_totals! expect(data_table.total_calculations).to eq([{:power_level=>9226.0}]) end it "should do custom formatting for the total" do data_table.column :power_level data_table.total :power_level, :avg, 0 do |average| "#{average / 100.0}%" end data_table.calculate_totals! expect(data_table.total_calculations).to eq([{:power_level=>"23.065%"}]) end it "should do custom totalling" do data_table.column :power_level data_table.total :power_level do |collection| collection.inject(0) { |sum, c| sum + c[:power_level] } end data_table.calculate_totals! expect(data_table.total_calculations).to eq([{:power_level=>9226}]) end it "should do sub-totaling" do data_table.group_by :world, level: 0 data_table.column :power_level data_table.subtotal :power_level, :sum, 0 data_table.prepare_data expect(data_table.subtotal_calculations).to eq({["Star Wars"]=>[{:power_level=>{:sum=>145.0}}], ["Middle Earth"]=>[{:power_level=>{:sum=>9081.0}}]}) end it "should do sub-totaling starting with indexes > 0" do data_table.group_by :world, level: 0 data_table.column :power_level data_table.subtotal :power_level, :sum, 1 data_table.prepare_data expect(data_table.subtotal_calculations).to eq({ ["Star Wars"] => [{}, {:power_level => {:sum => 145.0}}], ["Middle Earth"] => [{}, {:power_level => {:sum => 9081.0}}] }) end it "should not render empty sub-total aggregate rows" do data_table.group_by :world, level: 0 data_table.column :power_level data_table.subtotal :power_level, nil, 1 do |_records, _column, path| path end data_table.prepare_data subtotal_calculations = data_table.subtotal_calculations # this is convoluted because it's hard to assert a nested structure that includes procs # [ # ["Middle Earth"] => [{}, {:power_level=>{#=>"Middle Earth"}}], # ["Star Wars"] => [{}, {:power_level=>{#=>"Star Wars"}}] # ] expect(subtotal_calculations.keys).to eq([["Star Wars"], ["Middle Earth"]]) expect(subtotal_calculations.values.flatten.map(&:keys)).to eq([[], [:power_level], [], [:power_level]]) subtotal_calculations.values.flatten.map(&:values).flatten.map(&:keys).each do |k| expect(k).to be_a(Array) expect(k.length).to eq(1) expect(k[0]).to be_a(Proc) end expect(subtotal_calculations.values.flatten.map(&:values).flatten.map(&:values)).to eq([["Star Wars"], ["Middle Earth"]]) # note how the rows are index_1, and there is no index_0 row expect(data_table.render).to \ eq(%{
Star Wars
50
95
Star Wars
Middle Earth
9001
80
Middle Earth
}) end it "should render a custom header" do data_table.custom_header do th 'Two Columns', :colspan => 2 th 'One Column', :colspan => 1 end expect(data_table.render_custom_table_header).to eq(%{Two ColumnsOne Column}) end end context "with an empty collection" do let(:collection) {Array.new} let(:data_table) {DataTable::Table.new(collection)} it "should render a table with the 'no records' message" do expect(data_table.render).to \ eq(%{
No records found
}) end it "should render a custom empty text notice" do text = "Nothing to see here" data_table.empty_text = text expect(data_table.render).to \ eq(%{
#{text}
}) end it "avoids dividing by zero" do data_table.column :power_level data_table.total :power_level, :avg, 0 data_table.calculate_totals! expect(data_table.total_calculations).to eq([{:power_level=>0}]) end end context 'with a more complicated setup' do it 'renders okay' do raw_results = [ { 'class' => 'Basketball', 'grade_level' => '9', 'points' => 50 }, { 'class' => 'Basketball', 'grade_level' => '9', 'points' => 51 }, { 'class' => 'Basketball', 'grade_level' => '10', 'points' => 52 }, { 'class' => 'Basketball', 'grade_level' => '10', 'points' => 53 }, { 'class' => 'Basketball', 'grade_level' => '10', 'points' => 54 }, { 'class' => 'Basketball', 'grade_level' => '12', 'points' => 55 } ] fields = [{ field_name: 'class', display_description: 'Class', column_width: 1.23, data_type: 2 }, { field_name: 'grade_level', display_description: 'Grade Level', column_width: 2.34, data_type: 2 }, { field_name: 'points', display_description: 'Points', column_width: 3.45, data_type: 4 }] column_groups = {} subtotal_headers = [ { field_name: 'class' }, { field_name: 'grade_level' } ] subtotal_aggregates = { sum: [], avg: [{ field_name: 'points', data_type: 4 }], min: [], max: [] } total_aggregates = { sum: [], avg: [], min: [], max: [] } has_aggregates = true raw_results.each_with_index do |record, index| record[:___data_table_index___] = index end html = DataTable.render(raw_results) do |t| if has_aggregates t.column :__subtotal_header__, ' ', width: '30px' do |_v| ' ' end end # COLUMN GROUPS if column_groups.any? t.custom_header do th '', colspan: 1, css: 'column-group', style: 'width: 30px;' unless subtotal_headers.empty? column_groups.each do |_column_group_index, column_group| th column_group[:description], colspan: column_group[:column_count], css: 'column-group', style: "width: #{column_group_width}in;" end # spacer column th '', colspan: 1, css: 'column-group' end end # COLUMNS fields.each do |field| t.column field[:field_name], field[:display_description], css_class: "data-type-#{field[:data_type]}", width: field[:column_width] do |_v, record| record[field[:field_name]] end end # SUBTOTAL HEADERS subtotal_headers.each_with_index do |subtotal_header, index| t.group_by subtotal_header[:field_name], level: index end # SUBTOTAL AGGREGATES unless subtotal_headers.empty? subtotal_aggregates.each_with_index do |(aggregate_function, columns), index| next if columns.empty? t.subtotal :__subtotal_header__, nil, index do |_records, _column, path| "#{path}: #{aggregate_function.to_s.upcase}" end columns.each do |column| t.subtotal column[:field_name], aggregate_function, index do |value| value end end end end # TOTAL AGGREGATES total_aggregates.each_with_index do |(aggregate_function, columns), index| next if columns.empty? t.total :__subtotal_header__, nil, index do |_records| aggregate_function.to_s.upcase end columns.each do |column| t.total column[:field_name], aggregate_function, index do |value| value end end end # spacer column t.column :_empty_space, '' end expected_html = %(
 Points
Basketball
9
 50
 51
9: AVG50.5
10
 52
 53
 54
10: AVG53.0
12
 55
12: AVG55.0
Basketball: AVG52.5
) expect(html).to eq(expected_html) end end end