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(%{
Name | Class |
---|
Luke Skywalker | Jedi Knight |
Emporer Palpatine | Sith Lord |
Mithrander | Wizard |
Aragorn | Ranger |
})
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(%{Name | Class |
---|
Luke Skywalker | Jedi Knight |
Emporer Palpatine | Sith Lord |
Mithrander | Wizard |
Aragorn | Ranger |
})
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(%{ |
---|
50 |
95 |
Star Wars |
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(%{})
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(%{})
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(%{})
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 | |
---|
50 | |
51 | |
50.5 | |
52 | |
53 | |
54 | |
53.0 | |
55 | |
55.0 | |
52.5 | |
)
expect(html).to eq(expected_html)
end
end
end