Commit 55545ce9 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents d5fb657a 0cec0605
...@@ -117,7 +117,7 @@ export const createCommitPayload = ({ ...@@ -117,7 +117,7 @@ export const createCommitPayload = ({
action: commitActionForFile(f), action: commitActionForFile(f),
file_path: f.path, file_path: f.path,
previous_path: f.prevPath || undefined, previous_path: f.prevPath || undefined,
content: f.prevPath && !f.changed ? null : content || undefined, content: content || undefined,
encoding: isBlob ? 'base64' : 'text', encoding: isBlob ? 'base64' : 'text',
last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha, last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha,
}; };
......
...@@ -24,7 +24,10 @@ class Projects::BadgesController < Projects::ApplicationController ...@@ -24,7 +24,10 @@ class Projects::BadgesController < Projects::ApplicationController
.new(project, params[:ref], opts: { .new(project, params[:ref], opts: {
job: params[:job], job: params[:job],
key_text: params[:key_text], key_text: params[:key_text],
key_width: params[:key_width] key_width: params[:key_width],
min_good: params[:min_good],
min_acceptable: params[:min_acceptable],
min_medium: params[:min_medium]
}) })
render_badge coverage_report render_badge coverage_report
......
...@@ -107,8 +107,6 @@ class Issue < ApplicationRecord ...@@ -107,8 +107,6 @@ class Issue < ApplicationRecord
scope :order_due_date_asc, -> { reorder(::Gitlab::Database.nulls_last_order('due_date', 'ASC')) } scope :order_due_date_asc, -> { reorder(::Gitlab::Database.nulls_last_order('due_date', 'ASC')) }
scope :order_due_date_desc, -> { reorder(::Gitlab::Database.nulls_last_order('due_date', 'DESC')) } scope :order_due_date_desc, -> { reorder(::Gitlab::Database.nulls_last_order('due_date', 'DESC')) }
scope :order_closest_future_date, -> { reorder(Arel.sql('CASE WHEN issues.due_date >= CURRENT_DATE THEN 0 ELSE 1 END ASC, ABS(CURRENT_DATE - issues.due_date) ASC')) } scope :order_closest_future_date, -> { reorder(Arel.sql('CASE WHEN issues.due_date >= CURRENT_DATE THEN 0 ELSE 1 END ASC, ABS(CURRENT_DATE - issues.due_date) ASC')) }
scope :order_relative_position_asc, -> { reorder(::Gitlab::Database.nulls_last_order('relative_position', 'ASC')) }
scope :order_relative_position_desc, -> { reorder(::Gitlab::Database.nulls_first_order('relative_position', 'DESC')) }
scope :order_closed_date_desc, -> { reorder(closed_at: :desc) } scope :order_closed_date_desc, -> { reorder(closed_at: :desc) }
scope :order_created_at_desc, -> { reorder(created_at: :desc) } scope :order_created_at_desc, -> { reorder(created_at: :desc) }
scope :order_severity_asc, -> { includes(:issuable_severity).order('issuable_severities.severity ASC NULLS FIRST') } scope :order_severity_asc, -> { includes(:issuable_severity).order('issuable_severities.severity ASC NULLS FIRST') }
...@@ -267,8 +265,8 @@ class Issue < ApplicationRecord ...@@ -267,8 +265,8 @@ class Issue < ApplicationRecord
'due_date' => -> { order_due_date_asc.with_order_id_desc }, 'due_date' => -> { order_due_date_asc.with_order_id_desc },
'due_date_asc' => -> { order_due_date_asc.with_order_id_desc }, 'due_date_asc' => -> { order_due_date_asc.with_order_id_desc },
'due_date_desc' => -> { order_due_date_desc.with_order_id_desc }, 'due_date_desc' => -> { order_due_date_desc.with_order_id_desc },
'relative_position' => -> { order_relative_position_asc.with_order_id_desc }, 'relative_position' => -> { order_by_relative_position },
'relative_position_asc' => -> { order_relative_position_asc.with_order_id_desc } 'relative_position_asc' => -> { order_by_relative_position }
} }
) )
end end
...@@ -278,7 +276,7 @@ class Issue < ApplicationRecord ...@@ -278,7 +276,7 @@ class Issue < ApplicationRecord
when 'closest_future_date', 'closest_future_date_asc' then order_closest_future_date when 'closest_future_date', 'closest_future_date_asc' then order_closest_future_date
when 'due_date', 'due_date_asc' then order_due_date_asc.with_order_id_desc when 'due_date', 'due_date_asc' then order_due_date_asc.with_order_id_desc
when 'due_date_desc' then order_due_date_desc.with_order_id_desc when 'due_date_desc' then order_due_date_desc.with_order_id_desc
when 'relative_position', 'relative_position_asc' then order_relative_position_asc.with_order_id_desc when 'relative_position', 'relative_position_asc' then order_by_relative_position
when 'severity_asc' then order_severity_asc.with_order_id_desc when 'severity_asc' then order_severity_asc.with_order_id_desc
when 'severity_desc' then order_severity_desc.with_order_id_desc when 'severity_desc' then order_severity_desc.with_order_id_desc
else else
...@@ -286,13 +284,8 @@ class Issue < ApplicationRecord ...@@ -286,13 +284,8 @@ class Issue < ApplicationRecord
end end
end end
# `with_cte` argument allows sorting when using CTE queries and prevents def self.order_by_relative_position
# errors in postgres when using CTE search optimisation reorder(Gitlab::Pagination::Keyset::Order.build([column_order_relative_position, column_order_id_asc]))
def self.order_by_position_and_priority(with_cte: false)
order = Gitlab::Pagination::Keyset::Order.build([column_order_relative_position, column_order_highest_priority, column_order_id_desc])
order_labels_priority(with_cte: with_cte)
.reorder(order)
end end
def self.column_order_relative_position def self.column_order_relative_position
...@@ -307,25 +300,6 @@ class Issue < ApplicationRecord ...@@ -307,25 +300,6 @@ class Issue < ApplicationRecord
) )
end end
def self.column_order_highest_priority
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'highest_priority',
column_expression: Arel.sql('highest_priorities.label_priority'),
order_expression: Gitlab::Database.nulls_last_order('highest_priorities.label_priority', 'ASC'),
reversed_order_expression: Gitlab::Database.nulls_last_order('highest_priorities.label_priority', 'DESC'),
order_direction: :asc,
nullable: :nulls_last,
distinct: false
)
end
def self.column_order_id_desc
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: arel_table[:id].desc
)
end
def self.column_order_id_asc def self.column_order_id_asc
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id', attribute_name: 'id',
......
...@@ -22,7 +22,7 @@ module Boards ...@@ -22,7 +22,7 @@ module Boards
def order(items) def order(items)
return items.order_closed_date_desc if list&.closed? return items.order_closed_date_desc if list&.closed?
items.order_by_position_and_priority(with_cte: params[:search].present?) items.order_by_relative_position
end end
def finder def finder
......
...@@ -82,7 +82,7 @@ module Issues ...@@ -82,7 +82,7 @@ module Issues
collection.each do |project| collection.each do |project|
caching.cache_current_project_id(project.id) caching.cache_current_project_id(project.id)
index += 1 index += 1
scope = Issue.in_projects(project).reorder(custom_reorder).select(:id, :relative_position) scope = Issue.in_projects(project).order_by_relative_position.select(:id, :relative_position)
with_retry(PREFETCH_ISSUES_BATCH_SIZE, 100) do |batch_size| with_retry(PREFETCH_ISSUES_BATCH_SIZE, 100) do |batch_size|
Gitlab::Pagination::Keyset::Iterator.new(scope: scope).each_batch(of: batch_size) do |batch| Gitlab::Pagination::Keyset::Iterator.new(scope: scope).each_batch(of: batch_size) do |batch|
...@@ -166,10 +166,6 @@ module Issues ...@@ -166,10 +166,6 @@ module Issues
@start_position ||= (RelativePositioning::START_POSITION - (gaps / 2) * gap_size).to_i @start_position ||= (RelativePositioning::START_POSITION - (gaps / 2) * gap_size).to_i
end end
def custom_reorder
::Gitlab::Pagination::Keyset::Order.build([Issue.column_order_relative_position, Issue.column_order_id_asc])
end
def with_retry(initial_batch_size, exit_batch_size) def with_retry(initial_batch_size, exit_batch_size)
retries = 0 retries = 0
batch_size = initial_batch_size batch_size = initial_batch_size
......
# frozen_string_literal: true
class UpdateIssuesRelativePositionIndexes < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
RELATIVE_POSITION_INDEX_NAME = 'idx_issues_on_project_id_and_rel_asc_and_id'
RELATIVE_POSITION_STATE_INDEX_NAME = 'idx_issues_on_project_id_and_rel_position_and_state_id_and_id'
NEW_RELATIVE_POSITION_STATE_INDEX_NAME = 'idx_issues_on_project_id_and_rel_position_and_id_and_state_id'
def up
add_concurrent_index :issues, [:project_id, :relative_position, :id, :state_id], name: NEW_RELATIVE_POSITION_STATE_INDEX_NAME
remove_concurrent_index_by_name :issues, RELATIVE_POSITION_INDEX_NAME
remove_concurrent_index_by_name :issues, RELATIVE_POSITION_STATE_INDEX_NAME
end
def down
add_concurrent_index :issues, [:project_id, :relative_position, :state_id, :id], order: { id: :desc }, name: RELATIVE_POSITION_STATE_INDEX_NAME
add_concurrent_index :issues, [:project_id, :relative_position, :id], name: RELATIVE_POSITION_INDEX_NAME
remove_concurrent_index_by_name :issues, NEW_RELATIVE_POSITION_STATE_INDEX_NAME
end
end
8e54f43a955023e422bf40476f468fbdf04f06e806b08fddf35208c65607fec3
\ No newline at end of file
...@@ -24045,9 +24045,7 @@ CREATE INDEX idx_issues_on_project_id_and_created_at_and_id_and_state_id ON issu ...@@ -24045,9 +24045,7 @@ CREATE INDEX idx_issues_on_project_id_and_created_at_and_id_and_state_id ON issu
CREATE INDEX idx_issues_on_project_id_and_due_date_and_id_and_state_id ON issues USING btree (project_id, due_date, id, state_id) WHERE (due_date IS NOT NULL); CREATE INDEX idx_issues_on_project_id_and_due_date_and_id_and_state_id ON issues USING btree (project_id, due_date, id, state_id) WHERE (due_date IS NOT NULL);
CREATE INDEX idx_issues_on_project_id_and_rel_asc_and_id ON issues USING btree (project_id, relative_position, id); CREATE INDEX idx_issues_on_project_id_and_rel_position_and_id_and_state_id ON issues USING btree (project_id, relative_position, id, state_id);
CREATE INDEX idx_issues_on_project_id_and_rel_position_and_state_id_and_id ON issues USING btree (project_id, relative_position, state_id, id DESC);
CREATE INDEX idx_issues_on_project_id_and_updated_at_and_id_and_state_id ON issues USING btree (project_id, updated_at, id, state_id); CREATE INDEX idx_issues_on_project_id_and_updated_at_and_id_and_state_id ON issues USING btree (project_id, updated_at, id, state_id);
...@@ -358,6 +358,29 @@ in your `README.md`: ...@@ -358,6 +358,29 @@ in your `README.md`:
![coverage](https://gitlab.com/gitlab-org/gitlab/badges/main/coverage.svg?job=coverage) ![coverage](https://gitlab.com/gitlab-org/gitlab/badges/main/coverage.svg?job=coverage)
``` ```
#### Test coverage report badge colors and limits
The default colors and limits for the badge are as follows:
- 95 up to and including 100% - good (`#4c1`)
- 90 up to 95% - acceptable (`#a3c51c`)
- 75 up to 90% - medium (`#dfb317`)
- 0 up to 75% - low (`#e05d44`)
- no coverage - unknown (`#9f9f9f`)
NOTE:
*Up to* means up to, but not including, the upper bound.
You can overwrite the limits by using the following additional parameters ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/28317) in GitLab 14.4):
- `min_good` (default 95, can use any value between 3 and 100)
- `min_acceptable` (default 90, can use any value between 2 and min_good-1)
- `min_medium` (default 75, can use any value between 1 and min_acceptable-1)
If an invalid boundary is set, GitLab automatically adjusts it to be valid. For example,
if `min_good` is set `80`, and `min_acceptable` is set to `85` (too high), GitLab automatically
sets `min_acceptable` to `79` (`min_good` - `1`).
### Badge styles ### Badge styles
Pipeline badges can be rendered in different styles by adding the `style=style_name` parameter to the URL. Two styles are available: Pipeline badges can be rendered in different styles by adding the `style=style_name` parameter to the URL. Two styles are available:
......
...@@ -203,17 +203,13 @@ When visiting a board, issues appear ordered in any list. You're able to change ...@@ -203,17 +203,13 @@ When visiting a board, issues appear ordered in any list. You're able to change
that order by dragging the issues. The changed order is saved, so that anybody who visits the same that order by dragging the issues. The changed order is saved, so that anybody who visits the same
board later sees the reordering, with some exceptions. board later sees the reordering, with some exceptions.
The first time an issue appears in any board (that is, the first time a user When an issue is created, the system assigns a relative order value that is greater than the maximum value
loads a board containing that issue), it is ordered in relation to other issues in that list. of that issue's project or root group. This means the issue will be at the bottom of any issue list that
The order is done according to [label priority](labels.md#label-priority). it appears in.
At this point, that issue is assigned a relative order value by the system, Any time you drag and reorder the issue, its relative order value changes accordingly.
with respect to the other issues in the list. Any time Then, any time that issue appears in any board, the ordering is done according to
you drag and reorder the issue, its relative order value changes accordingly. the updated relative order value. If a user in your GitLab instance
Also, any time that issue appears in any board, the ordering is done according to
the updated relative order value. It's only the first
time an issue appears that it takes from the priority order mentioned above. If a user in your GitLab instance
drags issue `A` above issue `B`, the ordering is maintained when these two issues are subsequently drags issue `A` above issue `B`, the ordering is maintained when these two issues are subsequently
loaded in any board in the same instance. This could be a different project board or a different group loaded in any board in the same instance. This could be a different project board or a different group
board, for example. board, for example.
......
...@@ -81,8 +81,8 @@ RSpec.describe 'Project issue boards', :js do ...@@ -81,8 +81,8 @@ RSpec.describe 'Project issue boards', :js do
context 'total weight' do context 'total weight' do
let!(:label) { create(:label, project: project, name: 'Label 1') } let!(:label) { create(:label, project: project, name: 'Label 1') }
let!(:list) { create(:list, board: board, label: label, position: 0) } let!(:list) { create(:list, board: board, label: label, position: 0) }
let!(:issue) { create(:issue, project: project, weight: 3) } let!(:issue) { create(:issue, project: project, weight: 3, relative_position: 2) }
let!(:issue_2) { create(:issue, project: project, weight: 2) } let!(:issue_2) { create(:issue, project: project, weight: 2, relative_position: 1) }
before do before do
project.add_developer(user) project.add_developer(user)
......
...@@ -22,7 +22,7 @@ RSpec.describe IssuablesAnalytics do ...@@ -22,7 +22,7 @@ RSpec.describe IssuablesAnalytics do
context 'when issuable relation is ordered by priority' do context 'when issuable relation is ordered by priority' do
it 'generates chart data correctly' do it 'generates chart data correctly' do
issues = project.issues.order_by_position_and_priority issues = project.issues.order_labels_priority
data = described_class.new(issuables: issues).data data = described_class.new(issuables: issues).data
seed.each_pair do |months_back, issues_count| seed.each_pair do |months_back, issues_count|
......
...@@ -15,7 +15,10 @@ module Gitlab::Ci ...@@ -15,7 +15,10 @@ module Gitlab::Ci
@job = opts[:job] @job = opts[:job]
@customization = { @customization = {
key_width: opts[:key_width].to_i, key_width: opts[:key_width].to_i,
key_text: opts[:key_text] key_text: opts[:key_text],
min_good: opts[:min_good].to_i,
min_acceptable: opts[:min_acceptable].to_i,
min_medium: opts[:min_medium].to_i
} }
end end
......
...@@ -16,12 +16,20 @@ module Gitlab::Ci ...@@ -16,12 +16,20 @@ module Gitlab::Ci
low: '#e05d44', low: '#e05d44',
unknown: '#9f9f9f' unknown: '#9f9f9f'
}.freeze }.freeze
COVERAGE_MAX = 100
COVERAGE_MIN = 0
MIN_GOOD_DEFAULT = 95
MIN_ACCEPTABLE_DEFAULT = 90
MIN_MEDIUM_DEFAULT = 75
def initialize(badge) def initialize(badge)
@entity = badge.entity @entity = badge.entity
@status = badge.status @status = badge.status
@key_text = badge.customization.dig(:key_text) @key_text = badge.customization.dig(:key_text)
@key_width = badge.customization.dig(:key_width) @key_width = badge.customization.dig(:key_width)
@min_good = badge.customization.dig(:min_good)
@min_acceptable = badge.customization.dig(:min_acceptable)
@min_medium = badge.customization.dig(:min_medium)
end end
def value_text def value_text
...@@ -32,12 +40,36 @@ module Gitlab::Ci ...@@ -32,12 +40,36 @@ module Gitlab::Ci
@status ? 54 : 58 @status ? 54 : 58
end end
def min_good_value
if @min_good && @min_good.between?(3, COVERAGE_MAX)
@min_good
else
MIN_GOOD_DEFAULT
end
end
def min_acceptable_value
if @min_acceptable && @min_acceptable.between?(2, min_good_value - 1)
@min_acceptable
else
[MIN_ACCEPTABLE_DEFAULT, (min_good_value - 1)].min
end
end
def min_medium_value
if @min_medium && @min_medium.between?(1, min_acceptable_value - 1)
@min_medium
else
[MIN_MEDIUM_DEFAULT, (min_acceptable_value - 1)].min
end
end
def value_color def value_color
case @status case @status
when 95..100 then STATUS_COLOR[:good] when min_good_value..COVERAGE_MAX then STATUS_COLOR[:good]
when 90..95 then STATUS_COLOR[:acceptable] when min_acceptable_value..min_good_value then STATUS_COLOR[:acceptable]
when 75..90 then STATUS_COLOR[:medium] when min_medium_value..min_acceptable_value then STATUS_COLOR[:medium]
when 0..75 then STATUS_COLOR[:low] when COVERAGE_MIN..min_medium_value then STATUS_COLOR[:low]
else else
STATUS_COLOR[:unknown] STATUS_COLOR[:unknown]
end end
......
...@@ -116,7 +116,7 @@ RSpec.describe Boards::IssuesController do ...@@ -116,7 +116,7 @@ RSpec.describe Boards::IssuesController do
it 'does not query issues table more than once' do it 'does not query issues table more than once' do
recorder = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: board, list: list1) } recorder = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: board, list: list1) }
query_count = recorder.occurrences.select { |query,| query.start_with?('SELECT issues.*') }.each_value.first query_count = recorder.occurrences.select { |query,| query.match?(/FROM "?issues"?/) }.each_value.first
expect(query_count).to eq(1) expect(query_count).to eq(1)
end end
......
...@@ -12,6 +12,120 @@ RSpec.describe 'test coverage badge' do ...@@ -12,6 +12,120 @@ RSpec.describe 'test coverage badge' do
sign_in(user) sign_in(user)
end end
it 'user requests coverage badge image for pipeline with custom limits - 80% good' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 80, name: 'test:1')
end
show_test_coverage_badge(min_good: 75, min_acceptable: 50, min_medium: 25)
expect_coverage_badge_color(:good)
expect_coverage_badge('80.00%')
end
it 'user requests coverage badge image for pipeline with custom limits - 74% - bad config' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 74, name: 'test:1')
end
# User sets a minimum good value that is lower than min acceptable and min medium,
# in which case we force the min acceptable value to be min good -1 and min medium value to be min acceptable -1
show_test_coverage_badge(min_good: 75, min_acceptable: 76, min_medium: 77)
expect_coverage_badge_color(:acceptable)
expect_coverage_badge('74.00%')
end
it 'user requests coverage badge image for pipeline with custom limits - 73% - bad config' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 73, name: 'test:1')
end
# User sets a minimum good value that is lower than min acceptable and min medium,
# in which case we force the min acceptable value to be min good -1 and min medium value to be min acceptable -1
show_test_coverage_badge(min_good: 75, min_acceptable: 76, min_medium: 77)
expect_coverage_badge_color(:medium)
expect_coverage_badge('73.00%')
end
it 'user requests coverage badge image for pipeline with custom limits - 72% - partial config - low' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 72, name: 'test:1')
end
# User only sets good to 75 and leaves the others on the default settings,
# in which case we force the min acceptable value to be min good -1 and min medium value to be min acceptable -1
show_test_coverage_badge(min_good: 75)
expect_coverage_badge_color(:low)
expect_coverage_badge('72.00%')
end
it 'user requests coverage badge image for pipeline with custom limits - 72% - partial config - medium' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 72, name: 'test:1')
end
# User only sets good to 74 and leaves the others on the default settings,
# in which case we force the min acceptable value to be min good -1 and min medium value to be min acceptable -1
show_test_coverage_badge(min_good: 74)
expect_coverage_badge_color(:medium)
expect_coverage_badge('72.00%')
end
it 'user requests coverage badge image for pipeline with custom limits - 72% - partial config - medium v2' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 72, name: 'test:1')
end
# User only sets medium to 72 and leaves the others on the defaults good as 95 and acceptable as 90
show_test_coverage_badge(min_medium: 72)
expect_coverage_badge_color(:medium)
expect_coverage_badge('72.00%')
end
it 'user requests coverage badge image for pipeline with custom limits - 70% acceptable' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 70, name: 'test:1')
end
show_test_coverage_badge(min_good: 75, min_acceptable: 50, min_medium: 25)
expect_coverage_badge_color(:acceptable)
expect_coverage_badge('70.00%')
end
it 'user requests coverage badge image for pipeline with custom limits - 30% medium' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 30, name: 'test:1')
end
show_test_coverage_badge(min_good: 75, min_acceptable: 50, min_medium: 25)
expect_coverage_badge_color(:medium)
expect_coverage_badge('30.00%')
end
it 'user requests coverage badge image for pipeline with custom limits - 20% low' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 20, name: 'test:1')
end
show_test_coverage_badge(min_good: 75, min_acceptable: 50, min_medium: 25)
expect_coverage_badge_color(:low)
expect_coverage_badge('20.00%')
end
it 'user requests coverage badge image for pipeline with custom limits - nonsense values which use the defaults' do
create_pipeline do |pipeline|
create_build(pipeline, coverage: 92, name: 'test:1')
end
show_test_coverage_badge(min_good: "nonsense", min_acceptable: "rubbish", min_medium: "NaN")
expect_coverage_badge_color(:acceptable)
expect_coverage_badge('92.00%')
end
it 'user requests coverage badge image for pipeline' do it 'user requests coverage badge image for pipeline' do
create_pipeline do |pipeline| create_pipeline do |pipeline|
create_build(pipeline, coverage: 100, name: 'test:1') create_build(pipeline, coverage: 100, name: 'test:1')
...@@ -20,6 +134,7 @@ RSpec.describe 'test coverage badge' do ...@@ -20,6 +134,7 @@ RSpec.describe 'test coverage badge' do
show_test_coverage_badge show_test_coverage_badge
expect_coverage_badge_color(:good)
expect_coverage_badge('95.00%') expect_coverage_badge('95.00%')
end end
...@@ -32,6 +147,7 @@ RSpec.describe 'test coverage badge' do ...@@ -32,6 +147,7 @@ RSpec.describe 'test coverage badge' do
show_test_coverage_badge(job: 'coverage') show_test_coverage_badge(job: 'coverage')
expect_coverage_badge_color(:medium)
expect_coverage_badge('85.00%') expect_coverage_badge('85.00%')
end end
...@@ -73,8 +189,9 @@ RSpec.describe 'test coverage badge' do ...@@ -73,8 +189,9 @@ RSpec.describe 'test coverage badge' do
create(:ci_build, :success, opts) create(:ci_build, :success, opts)
end end
def show_test_coverage_badge(job: nil) def show_test_coverage_badge(job: nil, min_good: nil, min_acceptable: nil, min_medium: nil)
visit coverage_project_badges_path(project, ref: :master, job: job, format: :svg) visit coverage_project_badges_path(project, ref: :master, job: job, min_good: min_good,
min_acceptable: min_acceptable, min_medium: min_medium, format: :svg)
end end
def expect_coverage_badge(coverage) def expect_coverage_badge(coverage)
...@@ -82,4 +199,12 @@ RSpec.describe 'test coverage badge' do ...@@ -82,4 +199,12 @@ RSpec.describe 'test coverage badge' do
expect(page.response_headers['Content-Type']).to include('image/svg+xml') expect(page.response_headers['Content-Type']).to include('image/svg+xml')
expect(svg.at(%Q{text:contains("#{coverage}")})).to be_truthy expect(svg.at(%Q{text:contains("#{coverage}")})).to be_truthy
end end
def expect_coverage_badge_color(color)
svg = Nokogiri::HTML(page.body)
expect(page.response_headers['Content-Type']).to include('image/svg+xml')
badge_color = svg.xpath("//path[starts-with(@d, 'M62')]")[0].attributes['fill'].to_s
expected_badge_color = Gitlab::Ci::Badge::Coverage::Template::STATUS_COLOR[color]
expect(badge_color).to eq(expected_badge_color)
end
end end
...@@ -94,7 +94,7 @@ describe('Multi-file store utils', () => { ...@@ -94,7 +94,7 @@ describe('Multi-file store utils', () => {
{ {
action: commitActionTypes.move, action: commitActionTypes.move,
file_path: 'renamedFile', file_path: 'renamedFile',
content: null, content: undefined,
encoding: 'text', encoding: 'text',
last_commit_id: undefined, last_commit_id: undefined,
previous_path: 'prevPath', previous_path: 'prevPath',
......
...@@ -35,7 +35,7 @@ RSpec.describe Resolvers::BoardListIssuesResolver do ...@@ -35,7 +35,7 @@ RSpec.describe Resolvers::BoardListIssuesResolver do
# by relative_position and then ID # by relative_position and then ID
result = resolve_board_list_issues result = resolve_board_list_issues
expect(result.map(&:id)).to eq [issue3.id, issue1.id, issue2.id, issue4.id] expect(result.map(&:id)).to eq [issue1.id, issue3.id, issue2.id, issue4.id]
expect(result.map(&:relative_position)).not_to include(nil) expect(result.map(&:relative_position)).not_to include(nil)
end end
......
...@@ -338,7 +338,7 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -338,7 +338,7 @@ RSpec.describe Resolvers::IssuesResolver do
let_it_be(:relative_issue4) { create(:issue, project: project, relative_position: nil) } let_it_be(:relative_issue4) { create(:issue, project: project, relative_position: nil) }
it 'sorts issues ascending' do it 'sorts issues ascending' do
expect(resolve_issues(sort: :relative_position_asc).to_a).to eq [relative_issue3, relative_issue1, relative_issue4, relative_issue2] expect(resolve_issues(sort: :relative_position_asc).to_a).to eq [relative_issue3, relative_issue1, relative_issue2, relative_issue4]
end end
end end
......
...@@ -115,7 +115,7 @@ RSpec.describe Gitlab::ImportExport::Json::StreamingSerializer do ...@@ -115,7 +115,7 @@ RSpec.describe Gitlab::ImportExport::Json::StreamingSerializer do
end end
it 'orders exported issues by custom column(relative_position)' do it 'orders exported issues by custom column(relative_position)' do
expected_issues = exportable.issues.order_relative_position_desc.order(id: :desc).map(&:to_json) expected_issues = exportable.issues.reorder(::Gitlab::Database.nulls_first_order('relative_position', 'DESC')).order(id: :desc).map(&:to_json)
expect(json_writer).to receive(:write_relation_array).with(exportable_path, :issues, expected_issues) expect(json_writer).to receive(:write_relation_array).with(exportable_path, :issues, expected_issues)
......
...@@ -73,7 +73,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Iterator do ...@@ -73,7 +73,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Iterator do
iterator.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) } iterator.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) }
expect(positions).to eq(project.issues.order_relative_position_asc.order(id: :asc).pluck(:relative_position, :id)) expect(positions).to eq(project.issues.reorder(::Gitlab::Database.nulls_last_order('relative_position', 'ASC')).order(id: :asc).pluck(:relative_position, :id))
end end
end end
...@@ -85,7 +85,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Iterator do ...@@ -85,7 +85,7 @@ RSpec.describe Gitlab::Pagination::Keyset::Iterator do
iterator.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) } iterator.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) }
expect(positions).to eq(project.issues.order_relative_position_desc.order(id: :desc).pluck(:relative_position, :id)) expect(positions).to eq(project.issues.reorder(::Gitlab::Database.nulls_first_order('relative_position', 'DESC')).order(id: :desc).pluck(:relative_position, :id))
end end
end end
......
...@@ -222,17 +222,15 @@ RSpec.describe Issue do ...@@ -222,17 +222,15 @@ RSpec.describe Issue do
end end
end end
describe '#order_by_position_and_priority' do describe '#order_by_relative_position' do
let(:project) { reusable_project } let(:project) { reusable_project }
let(:p1) { create(:label, title: 'P1', project: project, priority: 1) } let!(:issue1) { create(:issue, project: project) }
let(:p2) { create(:label, title: 'P2', project: project, priority: 2) } let!(:issue2) { create(:issue, project: project) }
let!(:issue1) { create(:labeled_issue, project: project, labels: [p1]) }
let!(:issue2) { create(:labeled_issue, project: project, labels: [p2]) }
let!(:issue3) { create(:issue, project: project, relative_position: -200) } let!(:issue3) { create(:issue, project: project, relative_position: -200) }
let!(:issue4) { create(:issue, project: project, relative_position: -100) } let!(:issue4) { create(:issue, project: project, relative_position: -100) }
it 'returns ordered list' do it 'returns ordered list' do
expect(project.issues.order_by_position_and_priority) expect(project.issues.order_by_relative_position)
.to match [issue3, issue4, issue1, issue2] .to match [issue3, issue4, issue1, issue2]
end end
end end
......
...@@ -233,7 +233,7 @@ RSpec.describe 'getting an issue list for a project' do ...@@ -233,7 +233,7 @@ RSpec.describe 'getting an issue list for a project' do
let(:all_records) do let(:all_records) do
[ [
relative_issue5.iid, relative_issue3.iid, relative_issue1.iid, relative_issue5.iid, relative_issue3.iid, relative_issue1.iid,
relative_issue4.iid, relative_issue2.iid relative_issue2.iid, relative_issue4.iid
] ]
end end
end end
......
...@@ -27,7 +27,7 @@ RSpec.describe IssuePlacementWorker do ...@@ -27,7 +27,7 @@ RSpec.describe IssuePlacementWorker do
it 'places all issues created at most 5 minutes before this one at the end, most recent last' do it 'places all issues created at most 5 minutes before this one at the end, most recent last' do
expect { run_worker }.not_to change { irrelevant.reset.relative_position } expect { run_worker }.not_to change { irrelevant.reset.relative_position }
expect(project.issues.order_relative_position_asc) expect(project.issues.order_by_relative_position)
.to eq([issue_e, issue_b, issue_a, issue, issue_c, issue_f, issue_d]) .to eq([issue_e, issue_b, issue_a, issue, issue_c, issue_f, issue_d])
expect(project.issues.where(relative_position: nil)).not_to exist expect(project.issues.where(relative_position: nil)).not_to exist
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment