diff --git a/frontend/src/app/features/reporting/reporting-page/functionality/reporting_engine/group_bys.js b/frontend/src/app/features/reporting/reporting-page/functionality/reporting_engine/group_bys.js index 35c9efb718..313597337c 100644 --- a/frontend/src/app/features/reporting/reporting-page/functionality/reporting_engine/group_bys.js +++ b/frontend/src/app/features/reporting/reporting-page/functionality/reporting_engine/group_bys.js @@ -27,7 +27,7 @@ //++ /*jslint white: false, nomen: true, devel: true, on: true, debug: false, evil: true, onevar: false, browser: true, white: false, indent: 2 */ -/*global window, $, $$, Reporting, Effect, Ajax, selectAllOptions, moveOptions, moveOptionUp, moveOptionDown */ +/*global _, dragula, I18n, jQuery, Reporting*/ Reporting.GroupBys = (function($){ var group_by_container_ids = function() { @@ -59,6 +59,7 @@ Reporting.GroupBys = (function($){ .attr('class', 'in_row group-by--label') .attr('for', group_by.attr('id')) .attr('id', group_by.attr('id') + '_label') + .attr('title', text) .html(text); }; @@ -106,7 +107,7 @@ Reporting.GroupBys = (function($){ }; var create_group_by = function(field, caption) { - var group_by, label, right_arrow, left_arrow, remove_button; + var group_by, label, remove_button; group_by = $(''); group_by.attr('class', 'group-by--selected-element'); group_by.attr('data-group-by', field); @@ -146,8 +147,7 @@ Reporting.GroupBys = (function($){ }; var add_group_by = function(field, caption, container) { - var group_by, add_groups_select_box, added_container; - add_groups_select_box = container.find('select').first(); + var group_by, added_container; group_by = Reporting.GroupBys.create_group_by(field, caption); added_container = container.find('.group-by--selected-elements'); added_container.append(group_by); diff --git a/frontend/src/app/features/reporting/reporting-page/styles/_reporting_group_by.sass b/frontend/src/app/features/reporting/reporting-page/styles/_reporting_group_by.sass index 2215ad7231..613afeea89 100644 --- a/frontend/src/app/features/reporting/reporting-page/styles/_reporting_group_by.sass +++ b/frontend/src/app/features/reporting/reporting-page/styles/_reporting_group_by.sass @@ -21,6 +21,8 @@ padding-left: 14px margin-left: 18px min-width: 145px + display: flex + align-items: center fieldset.collapsible.header_collapsible legend.in_row width: inherit @@ -31,8 +33,8 @@ fieldset.collapsible.header_collapsible legend.in_row .group-by--label margin: 0px - padding: 0px 18px 0 0 min-width: 60px + max-width: 110px text-align: center white-space: nowrap font-weight: bold @@ -40,6 +42,8 @@ fieldset.collapsible.header_collapsible legend.in_row height: 36px line-height: 36px cursor: move + overflow: hidden + text-overflow: ellipsis .group-by--selected-element:after, .group-by--selected-element:before border: solid transparent @@ -77,6 +81,7 @@ fieldset.collapsible.header_collapsible legend.in_row line-height: normal cursor: pointer color: #FFFFFF + padding: 0 0 0 5px .group-by--remove:hover background-color: #3493B3 !important @@ -86,6 +91,7 @@ fieldset.collapsible.header_collapsible legend.in_row .group-by--control margin: 0 padding: 0 + max-width: 200px .group-by--selected-elements background-color: #EEE diff --git a/modules/reporting/app/models/cost_query/group_by/author_id.rb b/modules/reporting/app/models/cost_query/group_by/author_id.rb index 7b0b988771..87c24a2ac9 100644 --- a/modules/reporting/app/models/cost_query/group_by/author_id.rb +++ b/modules/reporting/app/models/cost_query/group_by/author_id.rb @@ -27,6 +27,8 @@ #++ class CostQuery::GroupBy::AuthorId < Report::GroupBy::Base + join_table WorkPackage + def self.label WorkPackage.human_attribute_name(:author) end diff --git a/modules/reporting/lib/widget/group_bys.rb b/modules/reporting/lib/widget/group_bys.rb index 67d266622d..62a6db4e39 100644 --- a/modules/reporting/lib/widget/group_bys.rb +++ b/modules/reporting/lib/widget/group_bys.rb @@ -30,9 +30,11 @@ class Widget::GroupBys < Widget::Base def render_options(group_by_ary) group_by_ary.sort_by(&:label).map do |group_by| next unless group_by.selectable? - - content_tag :option, value: group_by.underscore_name, 'data-label': CGI::escapeHTML(h(group_by.label)).to_s do - h(group_by.label) + label_text = CGI::escapeHTML(h(group_by.label)).to_s + option_tags = { value: group_by.underscore_name, 'data-label': label_text } + option_tags[:title] = label_text if group_by.label.length > 40 + content_tag :option, option_tags do + h(truncate_single_line(group_by.label, length: 40)) end end.join.html_safe end @@ -65,7 +67,7 @@ class Widget::GroupBys < Widget::Base class: 'hidden-for-sighted' label += content_tag :select, id: "group-by--add-#{type}", class: 'advanced-filters--select' do - content = content_tag :option, I18n.t(:label_group_by_add), value: '' + content = content_tag :option, I18n.t(:label_group_by_add), value: '', disabled: true, selected: true content += engine::GroupBy.all_grouped.sort_by do |label, _group_by_ary| I18n.t(label) diff --git a/modules/reporting/spec/models/cost_query/chaining_spec.rb b/modules/reporting/spec/models/cost_query/chaining_spec.rb index ba52163e63..bfc9c5d7cc 100644 --- a/modules/reporting/spec/models/cost_query/chaining_spec.rb +++ b/modules/reporting/spec/models/cost_query/chaining_spec.rb @@ -26,7 +26,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper") describe CostQuery, type: :model, reporting_query_helper: true do let(:project) { create(:project) } @@ -37,72 +37,72 @@ describe CostQuery, type: :model, reporting_query_helper: true do before do # FIXME: is there a better way to load all filter and groups? CostQuery::Filter.all && CostQuery::GroupBy.all - CostQuery.chain_initializer.clear + described_class.chain_initializer.clear end after(:all) do - CostQuery.chain_initializer.clear + described_class.chain_initializer.clear end it "contains NoFilter" do - expect(@query.chain).to be_a(CostQuery::Filter::NoFilter) + expect(query.chain).to be_a(CostQuery::Filter::NoFilter) end it "keeps NoFilter at bottom" do - @query.filter :project_id - expect(@query.chain.bottom).to be_a(CostQuery::Filter::NoFilter) - expect(@query.chain.top).not_to be_a(CostQuery::Filter::NoFilter) + query.filter :project_id + expect(query.chain.bottom).to be_a(CostQuery::Filter::NoFilter) + expect(query.chain.top).not_to be_a(CostQuery::Filter::NoFilter) end it "remembers it's correct parent" do - @query.group_by :project_id - @query.filter :project_id - expect(@query.chain.top.child.child.parent).to eq(@query.chain.top.child) + query.group_by :project_id + query.filter :project_id + expect(query.chain.top.child.child.parent).to eq(query.chain.top.child) end it "places filter after a group_by" do - @query.group_by :project_id - expect(@query.chain.bottom.parent).to be_a(CostQuery::GroupBy::ProjectId) - expect(@query.chain.top).to be_a(CostQuery::GroupBy::ProjectId) + query.group_by :project_id + expect(query.chain.bottom.parent).to be_a(CostQuery::GroupBy::ProjectId) + expect(query.chain.top).to be_a(CostQuery::GroupBy::ProjectId) - @query.filter :project_id - expect(@query.chain.bottom.parent).to be_a(CostQuery::Filter::ProjectId) - expect(@query.chain.top).to be_a(CostQuery::GroupBy::ProjectId) + query.filter :project_id + expect(query.chain.bottom.parent).to be_a(CostQuery::Filter::ProjectId) + expect(query.chain.top).to be_a(CostQuery::GroupBy::ProjectId) end it "places rows in front of columns when adding a column first" do - @query.column :project_id - expect(@query.chain.bottom.parent.type).to eq(:column) - expect(@query.chain.top.type).to eq(:column) + query.column :project_id + expect(query.chain.bottom.parent.type).to eq(:column) + expect(query.chain.top.type).to eq(:column) - @query.row :project_id - expect(@query.chain.bottom.parent.type).to eq(:column) - expect(@query.chain.top.type).to eq(:row) + query.row :project_id + expect(query.chain.bottom.parent.type).to eq(:column) + expect(query.chain.top.type).to eq(:row) end it "places rows in front of filters" do - @query.row :project_id - expect(@query.chain.bottom.parent.type).to eq(:row) - expect(@query.chain.top.type).to eq(:row) - - @query.filter :project_id - expect(@query.chain.bottom.parent).to be_a(CostQuery::Filter::ProjectId) - expect(@query.chain.top).to be_a(CostQuery::GroupBy::ProjectId) - expect(@query.chain.top.type).to eq(:row) + query.row :project_id + expect(query.chain.bottom.parent.type).to eq(:row) + expect(query.chain.top.type).to eq(:row) + + query.filter :project_id + expect(query.chain.bottom.parent).to be_a(CostQuery::Filter::ProjectId) + expect(query.chain.top).to be_a(CostQuery::GroupBy::ProjectId) + expect(query.chain.top.type).to eq(:row) end it "returns all filters, including the NoFilter" do - @query.filter :project_id - @query.group_by :project_id - expect(@query.filters.size).to eq(2) - expect(@query.filters.map { |f| f.class.underscore_name }).to include "project_id" + query.filter :project_id + query.group_by :project_id + expect(query.filters.size).to eq(2) + expect(query.filters.map { |f| f.class.underscore_name }).to include "project_id" end it "returns all group_bys" do - @query.filter :project_id - @query.group_by :project_id - expect(@query.group_bys.size).to eq(1) - expect(@query.group_bys.map { |g| g.class.underscore_name }).to include "project_id" + query.filter :project_id + query.group_by :project_id + expect(query.group_bys.size).to eq(1) + expect(query.group_bys.map { |g| g.class.underscore_name }).to include "project_id" end it "initializes the chain through a block" do @@ -112,38 +112,39 @@ describe CostQuery, type: :model, reporting_query_helper: true do end end TestFilter.send(:initialize_query_with) { |query| query.filter(:project_id, value: project.id) } - @query.build_new_chain - expect(@query.filters.map { |f| f.class.underscore_name }).to include "project_id" - expect(@query.filters.detect { |f| f.class.underscore_name == "project_id" }.values).to eq(Array(project.id)) + query.build_new_chain + expect(query.filters.map { |f| f.class.underscore_name }).to include "project_id" + expect(query.filters.detect { |f| f.class.underscore_name == "project_id" }.values).to eq(Array(project.id)) end context "store and load" do + let(:new_query) { described_class.deserialize(query.serialize) } + before do - @query.filter :project_id, value: project.id - @query.filter :cost_type_id, value: CostQuery::Filter::CostTypeId.available_values.first - @query.filter :category_id, value: CostQuery::Filter::CategoryId.available_values.first - @query.group_by :activity_id - @query.group_by :budget_id - @query.group_by :cost_type_id - @new_query = CostQuery.deserialize(@query.serialize) + query.filter :project_id, value: project.id + query.filter :cost_type_id, value: CostQuery::Filter::CostTypeId.available_values.first + query.filter :category_id, value: CostQuery::Filter::CategoryId.available_values.first + query.group_by :activity_id + query.group_by :budget_id + query.group_by :cost_type_id end it "serializes the chain correctly" do %i[filters group_bys].each do |type| - @query.send(type).each do |chainable| - expect(@query.serialize[type].collect { |c| c[0] }).to include chainable.class.name.demodulize + query.send(type).each do |chainable| + expect(query.serialize[type].collect { |c| c[0] }).to include chainable.class.name.demodulize end end end it "deserializes a serialized query correctly" do - expect(@new_query.serialize).to eq(@query.serialize) + expect(new_query.serialize).to eq(query.serialize) end it "keeps the order of group bys" do - @query.group_bys.each_with_index do |group_by, index| + query.group_bys.each_with_index do |group_by, index| # check for order - @new_query.group_bys.each_with_index do |g, ix| + new_query.group_bys.each_with_index do |g, ix| if g.instance_of?(group_by.class) expect(ix).to eq(index) end @@ -152,11 +153,11 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "keeps the right filter values" do - @query.filters.each_with_index do |filter, _index| + query.filters.each_with_index do |filter, _index| # check for presence - expect(@new_query.filters.any? do |f| + expect(new_query.filters).to be_any do |f| f.instance_of?(filter.class) && (filter.respond_to?(:values) ? f.values == filter.values : true) - end).to be_truthy + end end end end @@ -164,33 +165,33 @@ describe CostQuery, type: :model, reporting_query_helper: true do describe Report::Chainable do describe '#top' do - before { @chain = Report::Chainable.new } + let(:chain) { described_class.new } it "returns for an one element long chain that chain as top" do - expect(@chain.top).to eq(@chain) - expect(@chain).to be_top + expect(chain.top).to eq(chain) + expect(chain).to be_top end it "does not keep the old top when prepending elements" do - Report::Chainable.new @chain - expect(@chain.top).not_to eq(@chain) - expect(@chain).not_to be_top + described_class.new chain + expect(chain.top).not_to eq(chain) + expect(chain).not_to be_top end it "sets new top when prepending elements" do - current = @chain + current = chain 10.times do old = current - current = Report::Chainable.new(current) + current = described_class.new(current) expect(old.top).to eq(current) - expect(@chain.top).to eq(current) + expect(chain.top).to eq(current) end end end describe '#inherited_attribute' do before do - @a = Class.new Report::Chainable + @a = Class.new described_class @a.inherited_attribute :foo, default: 42 @b = Class.new @a @c = Class.new @a diff --git a/modules/reporting/spec/models/cost_query/filter_spec.rb b/modules/reporting/spec/models/cost_query/filter_spec.rb index 913f59f233..60d138ec52 100644 --- a/modules/reporting/spec/models/cost_query/filter_spec.rb +++ b/modules/reporting/spec/models/cost_query/filter_spec.rb @@ -26,7 +26,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper") require File.join(File.dirname(__FILE__), '..', '..', 'support', 'custom_field_filter') describe CostQuery, type: :model, reporting_query_helper: true do @@ -51,17 +51,17 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "shows all entries when no filter is applied" do - expect(@query.result.count).to eq(Entry.count) + expect(query.result.count).to eq(Entry.count) end it "always sets cost_type" do - @query.result.each do |result| + query.result.each do |result| expect(result["cost_type"]).not_to be_nil end end it "sets activity_id to -1 for cost entries" do - @query.result.each do |result| + query.result.each do |result| expect(result["activity_id"].to_i).to eq(-1) if result["type"] != "TimeEntry" end end @@ -102,29 +102,29 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "onlies return entries from the given #{filter}" do - @query.filter field, value: object.id - @query.result.each do |result| + query.filter field, value: object.id + query.result.each do |result| expect(result[field].to_s).to eq(object.id.to_s) end end it "allows chaining the same filter" do - @query.filter field, value: object.id - @query.filter field, value: object.id - @query.result.each do |result| + query.filter field, value: object.id + query.filter field, value: object.id + query.result.each do |result| expect(result[field].to_s).to eq(object.id.to_s) end end it "returns no results for excluding filters" do - @query.filter field, value: object.id - @query.filter field, value: object.id + 1 - expect(@query.result.count).to eq(0) + query.filter field, value: object.id + query.filter field, value: object.id + 1 + expect(query.result.count).to eq(0) end it "computes the correct number of results" do - @query.filter field, value: object.id - expect(@query.result.count).to eq(expected_count) + query.filter field, value: object.id + expect(query.result.count).to eq(expected_count) end end end @@ -157,49 +157,49 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "onlies return entries from the given CostQuery::Filter::AuthorId" do - @query.filter 'author_id', value: author.id - @query.result.each do |result| + query.filter 'author_id', value: author.id + query.result.each do |result| work_package_id = result["work_package_id"] expect(WorkPackage.find(work_package_id).author.id).to eq(author.id) end end it "allows chaining the same filter" do - @query.filter 'author_id', value: author.id - @query.filter 'author_id', value: author.id - @query.result.each do |result| + query.filter 'author_id', value: author.id + query.filter 'author_id', value: author.id + query.result.each do |result| work_package_id = result["work_package_id"] expect(WorkPackage.find(work_package_id).author.id).to eq(author.id) end end it "returns no results for excluding filters" do - @query.filter 'author_id', value: author.id - @query.filter 'author_id', value: author.id + 1 - expect(@query.result.count).to eq(0) + query.filter 'author_id', value: author.id + query.filter 'author_id', value: author.id + 1 + expect(query.result.count).to eq(0) end it "computes the correct number of results" do - @query.filter 'author_id', value: author.id - expect(@query.result.count).to eq(2) + query.filter 'author_id', value: author.id + expect(query.result.count).to eq(2) end end it "filters spent_on" do - @query.filter :spent_on, operator: 'w' - expect(@query.result.count).to eq(Entry.all.select { |e| e.spent_on.cweek == TimeEntry.all.first.spent_on.cweek }.count) + query.filter :spent_on, operator: 'w' + expect(query.result.count).to eq(Entry.all.select { |e| e.spent_on.cweek == TimeEntry.all.first.spent_on.cweek }.count) end it "filters created_at" do - @query.filter :created_on, operator: 't' + query.filter :created_on, operator: 't' # we assume that some of our fixtures set created_at to Time.now - expect(@query.result.count).to eq(Entry.all.select { |e| e.created_at.to_date == Date.today }.count) + expect(query.result.count).to eq(Entry.all.select { |e| e.created_at.to_date == Time.zone.today }.count) end it "filters updated_at" do - @query.filter :updated_on, value: Date.today.years_ago(20), operator: '>d' + query.filter :updated_on, value: Time.zone.today.years_ago(20), operator: '>d' # we assume that our were updated in the last 20 years - expect(@query.result.count).to eq(Entry.all.select { |e| e.updated_at.to_date > Date.today.years_ago(20) }.count) + expect(query.result.count).to eq(Entry.all.select { |e| e.updated_at.to_date > Time.zone.today.years_ago(20) }.count) end it "filters user_id" do @@ -209,8 +209,8 @@ describe CostQuery, type: :model, reporting_query_helper: true do create_work_package_with_time_entry({}, { user: anonymous }) # create matching entry create_work_package_with_time_entry - @query.filter :user_id, value: user.id, operator: '=' - expect(@query.result.count).to eq(1) + query.filter :user_id, value: user.id, operator: '=' + expect(query.result.count).to eq(1) end describe "work_package-based filters" do @@ -227,79 +227,79 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "filters overridden_costs" do - @query.filter :overridden_costs, operator: 'y' - expect(@query.result.count).to eq(Entry.all.select { |e| not e.overridden_costs.nil? }.count) + query.filter :overridden_costs, operator: 'y' + expect(query.result.count).to eq(Entry.all.reject { |e| e.overridden_costs.nil? }.count) end it "filters status" do matching_status = create(:status, is_closed: true) create_work_packages_and_time_entries(3, status: matching_status) - @query.filter :status_id, operator: 'c' - expect(@query.result.count).to eq(3) + query.filter :status_id, operator: 'c' + expect(query.result.count).to eq(3) end it "filters types" do matching_type = project.types.first create_work_packages_and_time_entries(3, type: matching_type) - @query.filter :type_id, operator: '=', value: matching_type.id - expect(@query.result.count).to eq(3) + query.filter :type_id, operator: '=', value: matching_type.id + expect(query.result.count).to eq(3) end it "filters work_package authors" do matching_author = create_matching_object_with_time_entries(:user, :author, 3) - @query.filter :author_id, operator: '=', value: matching_author.id - expect(@query.result.count).to eq(3) + query.filter :author_id, operator: '=', value: matching_author.id + expect(query.result.count).to eq(3) end it "filters priority" do matching_priority = create_matching_object_with_time_entries(:priority, :priority, 3) - @query.filter :priority_id, operator: '=', value: matching_priority.id - expect(@query.result.count).to eq(3) + query.filter :priority_id, operator: '=', value: matching_priority.id + expect(query.result.count).to eq(3) end it "filters assigned to" do matching_user = create_matching_object_with_time_entries(:user, :assigned_to, 3) - @query.filter :assigned_to_id, operator: '=', value: matching_user.id - expect(@query.result.count).to eq(3) + query.filter :assigned_to_id, operator: '=', value: matching_user.id + expect(query.result.count).to eq(3) end it "filters category" do category = create(:category, project:) create_work_packages_and_time_entries(3, category:) - @query.filter :category_id, operator: '=', value: category.id - expect(@query.result.count).to eq(3) + query.filter :category_id, operator: '=', value: category.id + expect(query.result.count).to eq(3) end it "filters target version" do matching_version = create(:version, project:) create_work_packages_and_time_entries(3, version: matching_version) - @query.filter :version_id, operator: '=', value: matching_version.id - expect(@query.result.count).to eq(3) + query.filter :version_id, operator: '=', value: matching_version.id + expect(query.result.count).to eq(3) end it "filters subject" do matching_work_package = create_work_package_with_time_entry(subject: 'matching subject') - @query.filter :subject, operator: '=', value: 'matching subject' - expect(@query.result.count).to eq(1) + query.filter :subject, operator: '=', value: 'matching subject' + expect(query.result.count).to eq(1) end it "filters start" do start_date = Date.new(2013, 1, 1) matching_work_package = create_work_package_with_time_entry(start_date:) - @query.filter :start_date, operator: '=d', value: start_date - expect(@query.result.count).to eq(1) + query.filter :start_date, operator: '=d', value: start_date + expect(query.result.count).to eq(1) end it "filters due date" do due_date = Date.new(2013, 1, 1) matching_work_package = create_work_package_with_time_entry(due_date:) - @query.filter :due_date, operator: '=d', value: due_date - expect(@query.result.count).to eq(1) + query.filter :due_date, operator: '=d', value: due_date + expect(query.result.count).to eq(1) end it "raises an error if operator is not supported" do - expect { @query.filter :spent_on, operator: 'c' }.to raise_error(ArgumentError) + expect { query.filter :spent_on, operator: 'c' }.to raise_error(ArgumentError) end end @@ -426,7 +426,7 @@ describe CostQuery, type: :model, reporting_query_helper: true do it "includes custom fields classes in CustomFieldEntries.all" do custom_field - expect(CostQuery::Filter::CustomFieldEntries.all) + expect(described_class.all) .to include(filter_class_name_string(custom_field).constantize) end @@ -457,15 +457,15 @@ describe CostQuery, type: :model, reporting_query_helper: true do it "is usable as filter" do create_searchable_fields_and_values id = WorkPackageCustomField.find_by(name: "Searchable Field").id - @query.filter "custom_field_#{id}".to_sym, operator: '=', value: "125" - expect(@query.result.count).to eq(2) + query.filter "custom_field_#{id}".to_sym, operator: '=', value: "125" + expect(query.result.count).to eq(2) end it "is usable as filter #2" do create_searchable_fields_and_values id = WorkPackageCustomField.find_by(name: "Searchable Field").id - @query.filter "custom_field_#{id}".to_sym, operator: '=', value: "finnlabs" - expect(@query.result.count).to eq(0) + query.filter "custom_field_#{id}".to_sym, operator: '=', value: "finnlabs" + expect(query.result.count).to eq(0) end end end diff --git a/modules/reporting/spec/models/cost_query/group_by_spec.rb b/modules/reporting/spec/models/cost_query/group_by_spec.rb index 041f2d4599..ad3914faa4 100644 --- a/modules/reporting/spec/models/cost_query/group_by_spec.rb +++ b/modules/reporting/spec/models/cost_query/group_by_spec.rb @@ -26,7 +26,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper") require File.join(File.dirname(__FILE__), '..', '..', 'support', 'custom_field_filter') describe CostQuery, type: :model, reporting_query_helper: true do @@ -75,75 +75,80 @@ describe CostQuery, type: :model, reporting_query_helper: true do describe CostQuery::GroupBy do it "computes group_by on projects" do - @query.group_by :project_id - expect(@query.result.size).to eq(2) + query.group_by :project_id + expect(query.result.size).to eq(2) end it "keeps own and all parents' group fields in all_group_fields" do - @query.group_by :project_id - @query.group_by :work_package_id - @query.group_by :cost_type_id - expect(@query.all_group_fields).to eq(%w[entries.cost_type_id]) - expect(@query.child.all_group_fields).to eq(%w[entries.cost_type_id entries.work_package_id]) - expect(@query.child.child.all_group_fields).to eq(%w[entries.cost_type_id entries.work_package_id entries.project_id]) + query.group_by :project_id + query.group_by :work_package_id + query.group_by :cost_type_id + expect(query.all_group_fields).to eq(%w[entries.cost_type_id]) + expect(query.child.all_group_fields).to eq(%w[entries.cost_type_id entries.work_package_id]) + expect(query.child.child.all_group_fields).to eq(%w[entries.cost_type_id entries.work_package_id entries.project_id]) end it "computes group_by WorkPackage" do - @query.group_by :work_package_id - expect(@query.result.size).to eq(2) + query.group_by :work_package_id + expect(query.result.size).to eq(2) end it "computes group_by CostType" do - @query.group_by :cost_type_id + query.group_by :cost_type_id # type 'Labor' for time entries, 2 different cost types - expect(@query.result.size).to eq(3) + expect(query.result.size).to eq(3) end it "computes group_by Activity" do - @query.group_by :activity_id + query.group_by :activity_id # "-1" for time entries, 2 different cost activities - expect(@query.result.size).to eq(3) + expect(query.result.size).to eq(3) end it "computes group_by Date (day)" do - @query.group_by :spent_on - expect(@query.result.size).to eq(2) + query.group_by :spent_on + expect(query.result.size).to eq(2) end it "computes group_by Date (week)" do - @query.group_by :tweek - expect(@query.result.size).to eq(2) + query.group_by :tweek + expect(query.result.size).to eq(2) end it "computes group_by Date (month)" do - @query.group_by :tmonth - expect(@query.result.size).to eq(2) + query.group_by :tmonth + expect(query.result.size).to eq(2) end it "computes group_by Date (year)" do - @query.group_by :tyear - expect(@query.result.size).to eq(2) + query.group_by :tyear + expect(query.result.size).to eq(2) end it "computes group_by User" do - @query.group_by :user_id - expect(@query.result.size).to eq(4) + query.group_by :user_id + expect(query.result.size).to eq(4) + end + + it "computes group_by Author" do + query.group_by :author_id + expect(query.result.size).to eq(2) end it "computes group_by Type" do - @query.group_by :type_id - expect(@query.result.size).to eq(1) + query.group_by :type_id + expect(query.result.size).to eq(1) end it "computes group_by Budget" do - @query.group_by :budget_id - expect(@query.result.size).to eq(1) + query.group_by :budget_id + expect(query.result.size).to eq(1) end it "computes multiple group_by" do - @query.group_by :project_id - @query.group_by :user_id - sql_result = @query.result + query.group_by :project_id + query.group_by :user_id + sql_result = query.result expect(sql_result.size).to eq(4) # for each user the number of projects should be correct @@ -159,9 +164,9 @@ describe CostQuery, type: :model, reporting_query_helper: true do # TODO: ? it "computes multiple group_by with joins" do - @query.group_by :project_id - @query.group_by :type_id - sql_result = @query.result + query.group_by :project_id + query.group_by :type_id + sql_result = query.result expect(sql_result.size).to eq(1) # for each type the number of projects should be correct sql_sizes = [] @@ -175,39 +180,39 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "compute count correct with lots of group_by" do - @query.group_by :project_id - @query.group_by :work_package_id - @query.group_by :cost_type_id - @query.group_by :activity_id - @query.group_by :spent_on - @query.group_by :tweek - @query.group_by :type_id - @query.group_by :tmonth - @query.group_by :tyear - - expect(@query.result.count).to eq(8) + query.group_by :project_id + query.group_by :work_package_id + query.group_by :cost_type_id + query.group_by :activity_id + query.group_by :spent_on + query.group_by :tweek + query.group_by :type_id + query.group_by :tmonth + query.group_by :tyear + + expect(query.result.count).to eq(8) end it "accepts row as a specialised group_by" do - @query.row :project_id - expect(@query.chain.type).to eq(:row) + query.row :project_id + expect(query.chain.type).to eq(:row) end it "accepts column as a specialised group_by" do - @query.column :project_id - expect(@query.chain.type).to eq(:column) + query.column :project_id + expect(query.chain.type).to eq(:column) end it "has type :column as a default" do - @query.group_by :project_id - expect(@query.chain.type).to eq(:column) + query.group_by :project_id + expect(query.chain.type).to eq(:column) end it "aggregates a third group_by which owns at least 2 sub results" do - @query.group_by :tweek - @query.group_by :project_id - @query.group_by :user_id - sql_result = @query.result + query.group_by :tweek + query.group_by :project_id + query.group_by :user_id + sql_result = query.result expect(sql_result.size).to eq(4) # for each user the number of projects should be correct @@ -243,7 +248,7 @@ describe CostQuery, type: :model, reporting_query_helper: true do before do check_cache - CostQuery::GroupBy.all.merge CostQuery::GroupBy::CustomFieldEntries.all + CostQuery::GroupBy.all.merge described_class.all end def check_cache @@ -287,7 +292,7 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "includes custom fields classes in CustomFieldEntries.all" do - expect(CostQuery::GroupBy::CustomFieldEntries.all) + expect(described_class.all) .to include(group_by_class_name_string(custom_field).constantize) end @@ -301,8 +306,8 @@ describe CostQuery, type: :model, reporting_query_helper: true do check_cache - @query.group_by "custom_field_#{custom_field2.id}".to_sym - footprint = @query.result.each_direct_result.map { |c| [c.count, c.units.to_i] }.sort + query.group_by "custom_field_#{custom_field2.id}".to_sym + footprint = query.result.each_direct_result.map { |c| [c.count, c.units.to_i] }.sort expect(footprint).to eq([[8, 8]]) end end diff --git a/modules/reporting/spec/models/cost_query/integration_spec.rb b/modules/reporting/spec/models/cost_query/integration_spec.rb index eca5cc5121..99f89e4287 100644 --- a/modules/reporting/spec/models/cost_query/integration_spec.rb +++ b/modules/reporting/spec/models/cost_query/integration_spec.rb @@ -26,7 +26,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper") describe CostQuery, type: :model, reporting_query_helper: true do minimal_query @@ -47,9 +47,9 @@ describe CostQuery, type: :model, reporting_query_helper: true do describe "the reporting system" do it "computes group_by and a filter" do - @query.group_by :project_id - @query.filter :status_id, operator: 'o' - sql_result = @query.result + query.group_by :project_id + query.filter :status_id, operator: 'o' + sql_result = query.result expect(sql_result.size).to eq(2) # for each project the number of entries should be correct @@ -63,11 +63,11 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "applies two filter and a group_by correctly" do - @query.filter :project_id, operator: '=', value: [project1.id] - @query.group_by :user_id - @query.filter :overridden_costs, operator: 'n' + query.filter :project_id, operator: '=', value: [project1.id] + query.group_by :user_id + query.filter :overridden_costs, operator: 'n' - sql_result = @query.result + sql_result = query.result expect(sql_result.size).to eq(2) # for each user the number of entries should be correct sql_count = [] @@ -80,10 +80,10 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "applies two different filters on the same field" do - @query.filter :project_id, operator: '=', value: [project1.id, project2.id] - @query.filter :project_id, operator: '!', value: [project2.id] + query.filter :project_id, operator: '=', value: [project1.id, project2.id] + query.filter :project_id, operator: '!', value: [project2.id] - sql_result = @query.result + sql_result = query.result expect(sql_result.count).to eq(2) end @@ -98,14 +98,14 @@ describe CostQuery, type: :model, reporting_query_helper: true do end # create a random query - @query.group_by :work_package_id - @query.column :tweek - @query.row :project_id - @query.row :user_id + query.group_by :work_package_id + query.column :tweek + query.row :project_id + query.row :user_id # count how often a sql query was created number_of_sql_queries = 0 # do some random things on it - walker = @query.transformer + walker = query.transformer walker.row_first walker.column_first # TODO - to do something diff --git a/modules/reporting/spec/models/cost_query/operator_spec.rb b/modules/reporting/spec/models/cost_query/operator_spec.rb index 6233da23b1..50547e319b 100644 --- a/modules/reporting/spec/models/cost_query/operator_spec.rb +++ b/modules/reporting/spec/models/cost_query/operator_spec.rb @@ -26,7 +26,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper") describe CostQuery, type: :model, reporting_query_helper: true do minimal_query @@ -35,7 +35,7 @@ describe CostQuery, type: :model, reporting_query_helper: true do let!(:project2) { create(:project, name: "project2", created_at: 6.minutes.ago) } describe CostQuery::Operator do - def query(table, field, operator, *values) + def cost_query(table, field, operator, *values) sql = CostQuery::SqlStatement.new table yield sql if block_given? operator.to_operator.modify sql, field, *values @@ -53,11 +53,11 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "does =" do - expect(query('projects', 'id', '=', project1.id).size).to eq(1) + expect(cost_query('projects', 'id', '=', project1.id).size).to eq(1) end it "does = for multiple values" do - expect(query('projects', 'id', '=', project1.id, project2.id).size).to eq(2) + expect(cost_query('projects', 'id', '=', project1.id, project2.id).size).to eq(2) end it "does = for no values" do @@ -68,172 +68,172 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "does = for nil" do - expect(query('projects', 'id', '=', nil).size).to eq(0) + expect(cost_query('projects', 'id', '=', nil).size).to eq(0) end it "does = for empty string" do - expect(query('projects', 'id', '=', '').size).to eq(0) + expect(cost_query('projects', 'id', '=', '').size).to eq(0) end it "does <=" do - expect(query('projects', 'id', '<=', project2.id - 1).size).to eq(1) + expect(cost_query('projects', 'id', '<=', project2.id - 1).size).to eq(1) end it "does >=" do - expect(query('projects', 'id', '>=', project1.id + 1).size).to eq(1) + expect(cost_query('projects', 'id', '>=', project1.id + 1).size).to eq(1) end it "does !" do - expect(query('projects', 'id', '!', project1.id).size).to eq(1) + expect(cost_query('projects', 'id', '!', project1.id).size).to eq(1) end it "does ! for empty string" do - expect(query('projects', 'id', '!', '').size).to eq(0) + expect(cost_query('projects', 'id', '!', '').size).to eq(0) end it "does ! for multiple values" do - expect(query('projects', 'id', '!', project1.id, project2.id).size).to eq(0) + expect(cost_query('projects', 'id', '!', project1.id, project2.id).size).to eq(0) end it "does !*" do - expect(query('cost_entries', 'project_id', '!*', []).size).to eq(0) + expect(cost_query('cost_entries', 'project_id', '!*', []).size).to eq(0) end it "does ~ (contains)" do - expect(query('projects', 'name', '~', 'o').size).to eq(Project.all.select { |p| p.name =~ /o/ }.count) - expect(query('projects', 'name', '~', 'test').size).to eq(Project.all.select { |p| p.name =~ /test/ }.count) - expect(query('projects', 'name', '~', 'child').size).to eq(Project.all.select { |p| p.name =~ /child/ }.count) + expect(cost_query('projects', 'name', '~', 'o').size).to eq(Project.all.select { |p| p.name =~ /o/ }.count) + expect(cost_query('projects', 'name', '~', 'test').size).to eq(Project.all.select { |p| p.name =~ /test/ }.count) + expect(cost_query('projects', 'name', '~', 'child').size).to eq(Project.all.select { |p| p.name =~ /child/ }.count) end it "does !~ (not contains)" do - expect(query('projects', 'name', '!~', 'o').size).to eq(Project.all.select { |p| p.name !~ /o/ }.count) - expect(query('projects', 'name', '!~', 'test').size).to eq(Project.all.select { |p| p.name !~ /test/ }.count) - expect(query('projects', 'name', '!~', 'child').size).to eq(Project.all.select { |p| p.name !~ /child/ }.count) + expect(cost_query('projects', 'name', '!~', 'o').size).to eq(Project.all.reject { |p| p.name =~ /o/ }.count) + expect(cost_query('projects', 'name', '!~', 'test').size).to eq(Project.all.reject { |p| p.name =~ /test/ }.count) + expect(cost_query('projects', 'name', '!~', 'child').size).to eq(Project.all.reject { |p| p.name =~ /child/ }.count) end it "does c (closed work_package)" do - expect(query('work_packages', 'status_id', 'c') { |s| s.join Status => [WorkPackage, :status] }.size).to be >= 0 + expect(cost_query('work_packages', 'status_id', 'c') { |s| s.join Status => [WorkPackage, :status] }.size).to be >= 0 end it "does o (open work_package)" do - expect(query('work_packages', 'status_id', 'o') { |s| s.join Status => [WorkPackage, :status] }.size).to be >= 0 + expect(cost_query('work_packages', 'status_id', 'o') { |s| s.join Status => [WorkPackage, :status] }.size).to be >= 0 end it "does give the correct number of results when counting closed and open work_packages" do - a = query('work_packages', 'status_id', 'o') { |s| s.join Status => [WorkPackage, :status] }.size - b = query('work_packages', 'status_id', 'c') { |s| s.join Status => [WorkPackage, :status] }.size + a = cost_query('work_packages', 'status_id', 'o') { |s| s.join Status => [WorkPackage, :status] }.size + b = cost_query('work_packages', 'status_id', 'c') { |s| s.join Status => [WorkPackage, :status] }.size expect(WorkPackage.count).to eq(a + b) end it "does w (this week)" do # somehow this test doesn't work on sundays - n = query('projects', 'created_at', 'w').size - day_in_this_week = Time.now.at_beginning_of_week + 1.day + n = cost_query('projects', 'created_at', 'w').size + day_in_this_week = Time.zone.now.at_beginning_of_week + 1.day create(:project, created_at: day_in_this_week) - expect(query('projects', 'created_at', 'w').size).to eq(n + 1) + expect(cost_query('projects', 'created_at', 'w').size).to eq(n + 1) create(:project, created_at: day_in_this_week + 7.days) create(:project, created_at: day_in_this_week - 7.days) - expect(query('projects', 'created_at', 'w').size).to eq(n + 1) + expect(cost_query('projects', 'created_at', 'w').size).to eq(n + 1) end it "does t (today)" do - s = query('projects', 'created_at', 't').size + s = cost_query('projects', 'created_at', 't').size create(:project, created_at: Date.yesterday) - expect(query('projects', 'created_at', 't').size).to eq(s) - create(:project, created_at: Time.now) - expect(query('projects', 'created_at', 't').size).to eq(s + 1) + expect(cost_query('projects', 'created_at', 't').size).to eq(s) + create(:project, created_at: Time.zone.now) + expect(cost_query('projects', 'created_at', 't').size).to eq(s + 1) end it "does t+ (after the day which is n days in the future)" do - n = query('projects', 'created_at', '>t+', 1).size - create(:project, created_at: Time.now) - expect(query('projects', 'created_at', '>t+', 1).size).to eq(n) + n = cost_query('projects', 'created_at', '>t+', 1).size + create(:project, created_at: Time.zone.now) + expect(cost_query('projects', 'created_at', '>t+', 1).size).to eq(n) create(:project, created_at: Date.tomorrow + 1) - expect(query('projects', 'created_at', '>t+', 1).size).to eq(n + 1) + expect(cost_query('projects', 'created_at', '>t+', 1).size).to eq(n + 1) end it "does >t- (after the day which is n days ago)" do - n = query('projects', 'created_at', '>t-', 1).size + n = cost_query('projects', 'created_at', '>t-', 1).size create(:project, created_at: Date.today) - expect(query('projects', 'created_at', '>t-', 1).size).to eq(n + 1) + expect(cost_query('projects', 'created_at', '>t-', 1).size).to eq(n + 1) create(:project, created_at: Date.yesterday - 1) - expect(query('projects', 'created_at', '>t-', 1).size).to eq(n + 1) + expect(cost_query('projects', 'created_at', '>t-', 1).size).to eq(n + 1) end it "does t- (n days ago)" do - n = query('projects', 'created_at', 't-', 1).size + n = cost_query('projects', 'created_at', 't-', 1).size create(:project, created_at: Date.yesterday) - expect(query('projects', 'created_at', 't-', 1).size).to eq(n + 1) + expect(cost_query('projects', 'created_at', 't-', 1).size).to eq(n + 1) create(:project, created_at: Date.yesterday - 2) - expect(query('projects', 'created_at', 't-', 1).size).to eq(n + 1) + expect(cost_query('projects', 'created_at', 't-', 1).size).to eq(n + 1) end it "does d" do - expect(query('projects', 'created_at', '<>d', Time.now, 5.minutes.from_now).size).to eq(0) + expect(cost_query('projects', 'created_at', '<>d', Time.zone.now, 5.minutes.from_now).size).to eq(0) end it "does >d" do # assuming that all projects were created in the past - expect(query('projects', 'created_at', '>d', Time.now).size).to eq(0) + expect(cost_query('projects', 'created_at', '>d', Time.zone.now).size).to eq(0) end describe 'arity' do diff --git a/modules/reporting/spec/models/cost_query/result_spec.rb b/modules/reporting/spec/models/cost_query/result_spec.rb index 0e7af8e25f..fe73885b4a 100644 --- a/modules/reporting/spec/models/cost_query/result_spec.rb +++ b/modules/reporting/spec/models/cost_query/result_spec.rb @@ -26,7 +26,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper") describe CostQuery, type: :model, reporting_query_helper: true do before do @@ -105,23 +105,23 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "computes count correctly" do - expect(@query.result.count).to eq(Entry.count) + expect(query.result.count).to eq(Entry.count) end it "computes units correctly" do - expect(@query.result.units).to eq(Entry.all.map { |e| e.units }.sum) + expect(query.result.units).to eq(Entry.all.map(&:units).sum) end it "computes real_costs correctly" do - expect(@query.result.real_costs).to eq(Entry.all.map { |e| e.overridden_costs || e.costs }.sum) + expect(query.result.real_costs).to eq(Entry.all.map { |e| e.overridden_costs || e.costs }.sum) end it "computes count for DirectResults" do - expect(@query.result.values[0].count).to eq(1) + expect(query.result.values[0].count).to eq(1) end it "computes units for DirectResults" do - id_sorted = @query.result.values.sort_by { |r| r[:id] } + id_sorted = query.result.values.sort_by { |r| r[:id] } te_result = id_sorted.select { |r| r[:type] == TimeEntry.to_s }.first ce_result = id_sorted.select { |r| r[:type] == CostEntry.to_s }.first expect(te_result.units.to_s).to eq("1.0") @@ -129,7 +129,7 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "computes real_costs for DirectResults" do - id_sorted = @query.result.values.sort_by { |r| r[:id] } + id_sorted = query.result.values.sort_by { |r| r[:id] } [CostEntry].each do |type| result = id_sorted.select { |r| r[:type] == type.to_s }.first first = type.all.first @@ -138,18 +138,18 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "is a column if created with CostQuery.column" do - @query.column :project_id - expect(@query.result.type).to eq(:column) + query.column :project_id + expect(query.result.type).to eq(:column) end it "is a row if created with CostQuery.row" do - @query.row :project_id - expect(@query.result.type).to eq(:row) + query.row :project_id + expect(query.result.type).to eq(:row) end it "shows the type :direct for its direct results" do - @query.column :project_id - expect(@query.result.first.first.type).to eq(:direct) + query.column :project_id + expect(query.result.first.first.type).to eq(:direct) end end end diff --git a/modules/reporting/spec/models/cost_query/walker_spec.rb b/modules/reporting/spec/models/cost_query/walker_spec.rb index c17267f71c..1775eadd65 100644 --- a/modules/reporting/spec/models/cost_query/walker_spec.rb +++ b/modules/reporting/spec/models/cost_query/walker_spec.rb @@ -26,7 +26,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper") describe CostQuery, type: :model, reporting_query_helper: true do minimal_query @@ -40,12 +40,12 @@ describe CostQuery, type: :model, reporting_query_helper: true do describe Report::Transformer do it "walks down row_first" do - @query.group_by :work_package_id - @query.column :tweek - @query.row :project_id - @query.row :user_id + query.group_by :work_package_id + query.column :tweek + query.row :project_id + query.row :user_id - result = @query.transformer.row_first.values.first + result = query.transformer.row_first.values.first %i[user_id project_id tweek].each do |field| expect(result.fields).to include(field) result = result.values.first @@ -53,12 +53,12 @@ describe CostQuery, type: :model, reporting_query_helper: true do end it "walks down column_first" do - @query.group_by :work_package_id - @query.column :tweek - @query.row :project_id - @query.row :user_id + query.group_by :work_package_id + query.column :tweek + query.row :project_id + query.row :user_id - result = @query.transformer.column_first.values.first + result = query.transformer.column_first.values.first %i[tweek work_package_id].each do |field| expect(result.fields).to include(field) result = result.values.first diff --git a/modules/reporting/spec/support/query_helper.rb b/modules/reporting/spec/support/query_helper.rb index adf0ca804e..30ead1918e 100644 --- a/modules/reporting/spec/support/query_helper.rb +++ b/modules/reporting/spec/support/query_helper.rb @@ -32,9 +32,10 @@ require 'cost_query/operator' module OpenProject::Reporting module QueryHelper def minimal_query + let(:query) { CostQuery.new } + before do - @query = CostQuery.new - @query.send(:minimal_chain!) + query.send(:minimal_chain!) end end end