Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--app/controllers/projects/badges_controller.rb17
-rw-r--r--app/controllers/projects/blob_controller.rb14
-rw-r--r--app/controllers/projects/pipelines_settings_controller.rb8
-rw-r--r--app/helpers/sorting_helper.rb8
-rw-r--r--app/services/files/base_service.rb1
-rw-r--r--app/services/files/update_service.rb23
-rw-r--r--app/views/projects/blob/edit.html.haml9
-rw-r--r--app/views/projects/pipelines_settings/_badge.html.haml27
-rw-r--r--app/views/projects/pipelines_settings/show.html.haml25
-rw-r--r--config/routes.rb5
-rw-r--r--db/fixtures/development/14_builds.rb59
-rw-r--r--doc/ci/pipelines.md35
-rw-r--r--doc/ci/quick_start/README.md12
-rw-r--r--features/project/merge_requests.feature10
-rw-r--r--lib/gitlab/badge/base.rb21
-rw-r--r--lib/gitlab/badge/build.rb30
-rw-r--r--lib/gitlab/badge/build/metadata.rb22
-rw-r--r--lib/gitlab/badge/build/status.rb37
-rw-r--r--lib/gitlab/badge/build/template.rb32
-rw-r--r--lib/gitlab/badge/coverage/metadata.rb30
-rw-r--r--lib/gitlab/badge/coverage/report.rb56
-rw-r--r--lib/gitlab/badge/coverage/template.rb52
-rw-r--r--lib/gitlab/badge/metadata.rb36
-rw-r--r--lib/gitlab/badge/template.rb49
-rw-r--r--spec/controllers/admin/groups_controller_spec.rb5
-rw-r--r--spec/controllers/groups_controller_spec.rb5
-rw-r--r--spec/features/merge_requests/diff_notes_spec.rb159
-rw-r--r--spec/features/projects/badges/coverage_spec.rb73
-rw-r--r--spec/features/projects/badges/list_spec.rb46
-rw-r--r--spec/features/projects/files/editing_a_file_spec.rb34
-rw-r--r--spec/lib/gitlab/badge/build/metadata_spec.rb36
-rw-r--r--spec/lib/gitlab/badge/build/status_spec.rb (renamed from spec/lib/gitlab/badge/build_spec.rb)38
-rw-r--r--spec/lib/gitlab/badge/build/template_spec.rb22
-rw-r--r--spec/lib/gitlab/badge/coverage/metadata_spec.rb30
-rw-r--r--spec/lib/gitlab/badge/coverage/report_spec.rb93
-rw-r--r--spec/lib/gitlab/badge/coverage/template_spec.rb130
-rw-r--r--spec/lib/gitlab/badge/shared/metadata.rb21
-rw-r--r--spec/models/project_services/irker_service_spec.rb7
-rw-r--r--spec/services/files/update_service_spec.rb84
40 files changed, 1174 insertions, 229 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 4aad4545509..fc9291eefd5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.11.0 (unreleased)
+ - Add test coverage report badge. !5708
- Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar)
- Ability to specify branches for Pivotal Tracker integration (Egor Lynko)
- Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres)
@@ -114,6 +115,7 @@ v 8.11.0 (unreleased)
- Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska)
- Fix a memory leak caused by Banzai::Filter::SanitizationFilter
- Speed up todos queries by limiting the projects set we join with
+ - Ensure file editing in UI does not overwrite commited changes without warning user
v 8.10.5
- Add a data migration to fix some missing timestamps in the members table. !5670
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index d0f5071d2cc..6c25cd83a24 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -4,11 +4,24 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :no_cache_headers, except: [:index]
def build
- badge = Gitlab::Badge::Build.new(project, params[:ref])
+ build_status = Gitlab::Badge::Build::Status
+ .new(project, params[:ref])
+ render_badge build_status
+ end
+
+ def coverage
+ coverage_report = Gitlab::Badge::Coverage::Report
+ .new(project, params[:ref], params[:job])
+
+ render_badge coverage_report
+ end
+
+ private
+
+ def render_badge(badge)
respond_to do |format|
format.html { render_404 }
-
format.svg do
render 'badge', locals: { badge: badge.template }
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 19d051720e9..cdf9a04bacf 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -17,6 +17,7 @@ class Projects::BlobController < Projects::ApplicationController
before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff]
before_action :validate_diff_params, only: :diff
+ before_action :set_last_commit_sha, only: [:edit, :update]
def new
commit unless @repository.empty?
@@ -33,7 +34,6 @@ class Projects::BlobController < Projects::ApplicationController
end
def edit
- @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
blob.load_all_data!(@repository)
end
@@ -55,6 +55,10 @@ class Projects::BlobController < Projects::ApplicationController
create_commit(Files::UpdateService, success_path: after_edit_path,
failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
+
+ rescue Files::UpdateService::FileChangedError
+ @conflict = true
+ render :edit
end
def preview
@@ -152,7 +156,8 @@ class Projects::BlobController < Projects::ApplicationController
file_path: @file_path,
commit_message: params[:commit_message],
file_content: params[:content],
- file_content_encoding: params[:encoding]
+ file_content_encoding: params[:encoding],
+ last_commit_sha: params[:last_commit_sha]
}
end
@@ -161,4 +166,9 @@ class Projects::BlobController < Projects::ApplicationController
render nothing: true
end
end
+
+ def set_last_commit_sha
+ @last_commit_sha = Gitlab::Git::Commit.
+ last_for_path(@repository, @ref, @path).sha
+ end
end
diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb
index 75dd3648e45..9136633b87a 100644
--- a/app/controllers/projects/pipelines_settings_controller.rb
+++ b/app/controllers/projects/pipelines_settings_controller.rb
@@ -3,7 +3,13 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
def show
@ref = params[:ref] || @project.default_branch || 'master'
- @build_badge = Gitlab::Badge::Build.new(@project, @ref).metadata
+
+ @badges = [Gitlab::Badge::Build::Status,
+ Gitlab::Badge::Coverage::Report]
+
+ @badges.map! do |badge|
+ badge.new(@project, @ref).metadata
+ end
end
def update
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index e1c0b497550..8b138a8e69f 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -20,13 +20,19 @@ module SortingHelper
end
def projects_sort_options_hash
- {
+ options = {
sort_value_name => sort_title_name,
sort_value_recently_updated => sort_title_recently_updated,
sort_value_oldest_updated => sort_title_oldest_updated,
sort_value_recently_created => sort_title_recently_created,
sort_value_oldest_created => sort_title_oldest_created,
}
+
+ if current_controller?('admin/projects')
+ options.merge!(sort_value_largest_repo => sort_title_largest_repo)
+ end
+
+ options
end
def sort_title_priority
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index c4a206f785e..ea94818713b 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -15,6 +15,7 @@ module Files
else
params[:file_content]
end
+ @last_commit_sha = params[:last_commit_sha]
# Validate parameters
validate
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index 8d2b5083179..4fc3b640799 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -2,11 +2,34 @@ require_relative "base_service"
module Files
class UpdateService < Files::BaseService
+ class FileChangedError < StandardError; end
+
def commit
repository.update_file(current_user, @file_path, @file_content,
branch: @target_branch,
previous_path: @previous_path,
message: @commit_message)
end
+
+ private
+
+ def validate
+ super
+
+ if file_has_changed?
+ raise FileChangedError.new("You are attempting to update a file that has changed since you started editing it.")
+ end
+ end
+
+ def file_has_changed?
+ return false unless @last_commit_sha && last_commit
+
+ @last_commit_sha != last_commit.sha
+ end
+
+ def last_commit
+ @last_commit ||= Gitlab::Git::Commit.
+ last_for_path(@source_project.repository, @source_branch, @file_path)
+ end
end
end
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index b1c9895f43e..7b0621f9401 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -1,5 +1,11 @@
- page_title "Edit", @blob.path, @ref
+- if @conflict
+ .alert.alert-danger
+ Someone edited the file the same time you did. Please check out
+ = link_to "the file", namespace_project_blob_path(@project.namespace, @project, tree_join(@target_branch, @file_path)), target: "_blank"
+ and make sure your changes will not unintentionally remove theirs.
+
.file-editor
%ul.nav-links.no-bottom.js-edit-mode
%li.active
@@ -13,8 +19,7 @@
= form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-quick-submit js-requires-input js-edit-blob-form') do
= render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
= render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
-
- = hidden_field_tag 'last_commit', @last_commit
+ = hidden_field_tag 'last_commit_sha', @last_commit_sha
= hidden_field_tag 'content', '', id: "file-content"
= hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
= render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id)
diff --git a/app/views/projects/pipelines_settings/_badge.html.haml b/app/views/projects/pipelines_settings/_badge.html.haml
new file mode 100644
index 00000000000..7b7fa56d993
--- /dev/null
+++ b/app/views/projects/pipelines_settings/_badge.html.haml
@@ -0,0 +1,27 @@
+.row{ class: badge.title.gsub(' ', '-') }
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ = badge.title.capitalize
+ .col-lg-9
+ .prepend-top-10
+ .panel.panel-default
+ .panel-heading
+ %b
+ = badge.title.capitalize
+ &middot;
+ = badge.to_html
+ .pull-right
+ = render 'shared/ref_switcher', destination: 'badges', align_right: true
+ .panel-body
+ .row
+ .col-md-2.text-center
+ Markdown
+ .col-md-10.code.js-syntax-highlight
+ = highlight('.md', badge.to_markdown)
+ .row
+ %hr
+ .row
+ .col-md-2.text-center
+ HTML
+ .col-md-10.code.js-syntax-highlight
+ = highlight('.html', badge.to_html)
diff --git a/app/views/projects/pipelines_settings/show.html.haml b/app/views/projects/pipelines_settings/show.html.haml
index 228bad36ebd..8c7222bfe3d 100644
--- a/app/views/projects/pipelines_settings/show.html.haml
+++ b/app/views/projects/pipelines_settings/show.html.haml
@@ -77,27 +77,4 @@
%hr
.row.prepend-top-default
- .col-lg-3.profile-settings-sidebar
- %h4.prepend-top-0
- Builds Badge
- .col-lg-9
- .prepend-top-10
- .panel.panel-default
- .panel-heading
- %b Builds badge &middot;
- = @build_badge.to_html
- .pull-right
- = render 'shared/ref_switcher', destination: 'badges', align_right: true
- .panel-body
- .row
- .col-md-2.text-center
- Markdown
- .col-md-10.code.js-syntax-highlight
- = highlight('.md', @build_badge.to_markdown)
- .row
- %hr
- .row
- .col-md-2.text-center
- HTML
- .col-md-10.code.js-syntax-highlight
- = highlight('.html', @build_badge.to_html)
+ = render partial: 'badge', collection: @badges
diff --git a/config/routes.rb b/config/routes.rb
index 2cf2d111920..1d2db91344f 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -874,7 +874,10 @@ Rails.application.routes.draw do
resources :badges, only: [:index] do
collection do
scope '*ref', constraints: { ref: Gitlab::Regex.git_reference_regex } do
- get :build, constraints: { format: /svg/ }
+ constraints format: /svg/ do
+ get :build
+ get :coverage
+ end
end
end
end
diff --git a/db/fixtures/development/14_builds.rb b/db/fixtures/development/14_builds.rb
index e65abe4ef77..6441a036e75 100644
--- a/db/fixtures/development/14_builds.rb
+++ b/db/fixtures/development/14_builds.rb
@@ -1,5 +1,21 @@
class Gitlab::Seeder::Builds
STAGES = %w[build notify_build test notify_test deploy notify_deploy]
+ BUILDS = [
+ { name: 'build:linux', stage: 'build', status: :success },
+ { name: 'build:osx', stage: 'build', status: :success },
+ { name: 'slack post build', stage: 'notify_build', status: :success },
+ { name: 'rspec:linux', stage: 'test', status: :success },
+ { name: 'rspec:windows', stage: 'test', status: :success },
+ { name: 'rspec:windows', stage: 'test', status: :success },
+ { name: 'rspec:osx', stage: 'test', status_event: :success },
+ { name: 'spinach:linux', stage: 'test', status: :pending },
+ { name: 'spinach:osx', stage: 'test', status: :canceled },
+ { name: 'cucumber:linux', stage: 'test', status: :running },
+ { name: 'cucumber:osx', stage: 'test', status: :failed },
+ { name: 'slack post test', stage: 'notify_test', status: :success },
+ { name: 'staging', stage: 'deploy', environment: 'staging', status: :success },
+ { name: 'production', stage: 'deploy', environment: 'production', when: 'manual', status: :success },
+ ]
def initialize(project)
@project = project
@@ -8,25 +24,7 @@ class Gitlab::Seeder::Builds
def seed!
pipelines.each do |pipeline|
begin
- build_create!(pipeline, name: 'build:linux', stage: 'build', status_event: :success)
- build_create!(pipeline, name: 'build:osx', stage: 'build', status_event: :success)
-
- build_create!(pipeline, name: 'slack post build', stage: 'notify_build', status_event: :success)
-
- build_create!(pipeline, name: 'rspec:linux', stage: 'test', status_event: :success)
- build_create!(pipeline, name: 'rspec:windows', stage: 'test', status_event: :success)
- build_create!(pipeline, name: 'rspec:windows', stage: 'test', status_event: :success)
- build_create!(pipeline, name: 'rspec:osx', stage: 'test', status_event: :success)
- build_create!(pipeline, name: 'spinach:linux', stage: 'test', status: :pending)
- build_create!(pipeline, name: 'spinach:osx', stage: 'test', status_event: :cancel)
- build_create!(pipeline, name: 'cucumber:linux', stage: 'test', status_event: :run)
- build_create!(pipeline, name: 'cucumber:osx', stage: 'test', status_event: :drop)
-
- build_create!(pipeline, name: 'slack post test', stage: 'notify_test', status_event: :success)
-
- build_create!(pipeline, name: 'staging', stage: 'deploy', environment: 'staging', status_event: :success)
- build_create!(pipeline, name: 'production', stage: 'deploy', environment: 'production', when: 'manual', status: :success)
-
+ BUILDS.each { |opts| build_create!(pipeline, opts) }
commit_status_create!(pipeline, name: 'jenkins', status: :success)
print '.'
@@ -48,21 +46,22 @@ class Gitlab::Seeder::Builds
def build_create!(pipeline, opts = {})
attributes = build_attributes_for(pipeline, opts)
- build = Ci::Build.create!(attributes)
- if opts[:name].start_with?('build')
- artifacts_cache_file(artifacts_archive_path) do |file|
- build.artifacts_file = file
- end
+ Ci::Build.create!(attributes) do |build|
+ if opts[:name].start_with?('build')
+ artifacts_cache_file(artifacts_archive_path) do |file|
+ build.artifacts_file = file
+ end
- artifacts_cache_file(artifacts_metadata_path) do |file|
- build.artifacts_metadata = file
+ artifacts_cache_file(artifacts_metadata_path) do |file|
+ build.artifacts_metadata = file
+ end
end
- end
- if %w(running success failed).include?(build.status)
- # We need to set build trace after saving a build (id required)
- build.trace = FFaker::Lorem.paragraphs(6).join("\n\n")
+ if %w(running success failed).include?(build.status)
+ # We need to set build trace after saving a build (id required)
+ build.trace = FFaker::Lorem.paragraphs(6).join("\n\n")
+ end
end
end
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
index 48a9f994759..d90d7aca4fd 100644
--- a/doc/ci/pipelines.md
+++ b/doc/ci/pipelines.md
@@ -32,6 +32,41 @@ project.
Clicking on a pipeline will show the builds that were run for that pipeline.
+## Badges
+
+There are build status and test coverage report badges available.
+
+Go to pipeline settings to see available badges and code you can use to embed
+badges in the `README.md` or your website.
+
+### Build status badge
+
+You can access a build status badge image using following link:
+
+```
+http://example.gitlab.com/namespace/project/badges/branch/build.svg
+```
+
+### Test coverage report badge
+
+GitLab makes it possible to define the regular expression for coverage report,
+that each build log will be matched against. This means that each build in the
+pipeline can have the test coverage percentage value defined.
+
+You can access test coverage badge using following link:
+
+```
+http://example.gitlab.com/namespace/project/badges/branch/coverage.svg
+```
+
+If you would like to get the coverage report from the specific job, you can add
+a `job=coverage_job_name` parameter to the URL. For example, it is possible to
+use following Markdown code to embed the est coverage report into `README.md`:
+
+```markdown
+![coverage](http://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)
+```
+
[builds]: #builds
[jobs]: yaml/README.md#jobs
[stages]: yaml/README.md#stages
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index 6a3c416d995..c835ebc2d44 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -218,21 +218,13 @@ project's settings.
For more information read the
[Builds emails service documentation](../../project_services/builds_emails.md).
-## Builds badge
-
-You can access a builds badge image using following link:
-
-```
-http://example.gitlab.com/namespace/project/badges/branch/build.svg
-```
-
-Awesome! You started using CI in GitLab!
-
## Examples
Visit the [examples README][examples] to see a list of examples using GitLab
CI with various languages.
+Awesome! You started using CI in GitLab!
+
[runner-install]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/tree/master#install-gitlab-runner
[blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/
[examples]: ../examples/README.md
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 6bac6011467..1b8e4262e40 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -107,16 +107,6 @@ Feature: Project Merge Requests
Then The list should be sorted by "Least popular"
@javascript
- Scenario: I comment on a merge request diff
- Given project "Shop" have "Bug NS-05" open merge request with diffs inside
- And I visit merge request page "Bug NS-05"
- And I click on the Changes tab
- And I leave a comment like "Line is wrong" on diff
- And I switch to the merge request's comments tab
- Then I should see a discussion has started on diff
- And I should see a badge of "1" next to the discussion link
-
- @javascript
Scenario: I see a new comment on merge request diff from another user in the discussion tab
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
And I visit merge request page "Bug NS-05"
diff --git a/lib/gitlab/badge/base.rb b/lib/gitlab/badge/base.rb
new file mode 100644
index 00000000000..909fa24fa90
--- /dev/null
+++ b/lib/gitlab/badge/base.rb
@@ -0,0 +1,21 @@
+module Gitlab
+ module Badge
+ class Base
+ def entity
+ raise NotImplementedError
+ end
+
+ def status
+ raise NotImplementedError
+ end
+
+ def metadata
+ raise NotImplementedError
+ end
+
+ def template
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/badge/build.rb b/lib/gitlab/badge/build.rb
deleted file mode 100644
index 1de721a2269..00000000000
--- a/lib/gitlab/badge/build.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-module Gitlab
- module Badge
- ##
- # Build badge
- #
- class Build
- delegate :key_text, :value_text, to: :template
-
- def initialize(project, ref)
- @project = project
- @ref = ref
- @sha = @project.commit(@ref).try(:sha)
- end
-
- def status
- @project.pipelines
- .where(sha: @sha, ref: @ref)
- .status || 'unknown'
- end
-
- def metadata
- @metadata ||= Build::Metadata.new(@project, @ref)
- end
-
- def template
- @template ||= Build::Template.new(status)
- end
- end
- end
-end
diff --git a/lib/gitlab/badge/build/metadata.rb b/lib/gitlab/badge/build/metadata.rb
index 553ef8d7b16..f87a7b7942e 100644
--- a/lib/gitlab/badge/build/metadata.rb
+++ b/lib/gitlab/badge/build/metadata.rb
@@ -1,25 +1,17 @@
module Gitlab
module Badge
- class Build
+ module Build
##
# Class that describes build badge metadata
#
- class Metadata
- include Gitlab::Application.routes.url_helpers
- include ActionView::Helpers::AssetTagHelper
- include ActionView::Helpers::UrlHelper
-
- def initialize(project, ref)
- @project = project
- @ref = ref
- end
-
- def to_html
- link_to(image_tag(image_url, alt: 'build status'), link_url)
+ class Metadata < Badge::Metadata
+ def initialize(badge)
+ @project = badge.project
+ @ref = badge.ref
end
- def to_markdown
- "[![build status](#{image_url})](#{link_url})"
+ def title
+ 'build status'
end
def image_url
diff --git a/lib/gitlab/badge/build/status.rb b/lib/gitlab/badge/build/status.rb
new file mode 100644
index 00000000000..50aa45e5406
--- /dev/null
+++ b/lib/gitlab/badge/build/status.rb
@@ -0,0 +1,37 @@
+module Gitlab
+ module Badge
+ module Build
+ ##
+ # Build status badge
+ #
+ class Status < Badge::Base
+ attr_reader :project, :ref
+
+ def initialize(project, ref)
+ @project = project
+ @ref = ref
+
+ @sha = @project.commit(@ref).try(:sha)
+ end
+
+ def entity
+ 'build'
+ end
+
+ def status
+ @project.pipelines
+ .where(sha: @sha, ref: @ref)
+ .status || 'unknown'
+ end
+
+ def metadata
+ @metadata ||= Build::Metadata.new(self)
+ end
+
+ def template
+ @template ||= Build::Template.new(self)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/badge/build/template.rb b/lib/gitlab/badge/build/template.rb
index deba3b669b3..2b95ddfcb53 100644
--- a/lib/gitlab/badge/build/template.rb
+++ b/lib/gitlab/badge/build/template.rb
@@ -1,12 +1,12 @@
module Gitlab
module Badge
- class Build
+ module Build
##
# Class that represents a build badge template.
#
# Template object will be passed to badge.svg.erb template.
#
- class Template
+ class Template < Badge::Template
STATUS_COLOR = {
success: '#4c1',
failed: '#e05d44',
@@ -17,16 +17,17 @@ module Gitlab
unknown: '#9f9f9f'
}
- def initialize(status)
- @status = status
+ def initialize(badge)
+ @entity = badge.entity
+ @status = badge.status
end
def key_text
- 'build'
+ @entity.to_s
end
def value_text
- @status
+ @status.to_s
end
def key_width
@@ -37,25 +38,8 @@ module Gitlab
54
end
- def key_color
- '#555'
- end
-
def value_color
- STATUS_COLOR[@status.to_sym] ||
- STATUS_COLOR[:unknown]
- end
-
- def key_text_anchor
- key_width / 2
- end
-
- def value_text_anchor
- key_width + (value_width / 2)
- end
-
- def width
- key_width + value_width
+ STATUS_COLOR[@status.to_sym] || STATUS_COLOR[:unknown]
end
end
end
diff --git a/lib/gitlab/badge/coverage/metadata.rb b/lib/gitlab/badge/coverage/metadata.rb
new file mode 100644
index 00000000000..53588185622
--- /dev/null
+++ b/lib/gitlab/badge/coverage/metadata.rb
@@ -0,0 +1,30 @@
+module Gitlab
+ module Badge
+ module Coverage
+ ##
+ # Class that describes coverage badge metadata
+ #
+ class Metadata < Badge::Metadata
+ def initialize(badge)
+ @project = badge.project
+ @ref = badge.ref
+ @job = badge.job
+ end
+
+ def title
+ 'coverage report'
+ end
+
+ def image_url
+ coverage_namespace_project_badges_url(@project.namespace,
+ @project, @ref,
+ format: :svg)
+ end
+
+ def link_url
+ namespace_project_commits_url(@project.namespace, @project, id: @ref)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/badge/coverage/report.rb b/lib/gitlab/badge/coverage/report.rb
new file mode 100644
index 00000000000..3d56ea3e47a
--- /dev/null
+++ b/lib/gitlab/badge/coverage/report.rb
@@ -0,0 +1,56 @@
+module Gitlab
+ module Badge
+ module Coverage
+ ##
+ # Test coverage report badge
+ #
+ class Report < Badge::Base
+ attr_reader :project, :ref, :job
+
+ def initialize(project, ref, job = nil)
+ @project = project
+ @ref = ref
+ @job = job
+
+ @pipeline = @project.pipelines
+ .where(ref: @ref)
+ .where(sha: @project.commit(@ref).try(:sha))
+ .first
+ end
+
+ def entity
+ 'coverage'
+ end
+
+ def status
+ @coverage ||= raw_coverage
+ return unless @coverage
+
+ @coverage.to_i
+ end
+
+ def metadata
+ @metadata ||= Coverage::Metadata.new(self)
+ end
+
+ def template
+ @template ||= Coverage::Template.new(self)
+ end
+
+ private
+
+ def raw_coverage
+ return unless @pipeline
+
+ if @job.blank?
+ @pipeline.coverage
+ else
+ @pipeline.builds
+ .find_by(name: @job)
+ .try(:coverage)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/badge/coverage/template.rb b/lib/gitlab/badge/coverage/template.rb
new file mode 100644
index 00000000000..06e0d084e9f
--- /dev/null
+++ b/lib/gitlab/badge/coverage/template.rb
@@ -0,0 +1,52 @@
+module Gitlab
+ module Badge
+ module Coverage
+ ##
+ # Class that represents a coverage badge template.
+ #
+ # Template object will be passed to badge.svg.erb template.
+ #
+ class Template < Badge::Template
+ STATUS_COLOR = {
+ good: '#4c1',
+ acceptable: '#a3c51c',
+ medium: '#dfb317',
+ low: '#e05d44',
+ unknown: '#9f9f9f'
+ }
+
+ def initialize(badge)
+ @entity = badge.entity
+ @status = badge.status
+ end
+
+ def key_text
+ @entity.to_s
+ end
+
+ def value_text
+ @status ? "#{@status}%" : 'unknown'
+ end
+
+ def key_width
+ 62
+ end
+
+ def value_width
+ @status ? 36 : 58
+ end
+
+ def value_color
+ case @status
+ when 95..100 then STATUS_COLOR[:good]
+ when 90..95 then STATUS_COLOR[:acceptable]
+ when 75..90 then STATUS_COLOR[:medium]
+ when 0..75 then STATUS_COLOR[:low]
+ else
+ STATUS_COLOR[:unknown]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/badge/metadata.rb b/lib/gitlab/badge/metadata.rb
new file mode 100644
index 00000000000..548f85b78bb
--- /dev/null
+++ b/lib/gitlab/badge/metadata.rb
@@ -0,0 +1,36 @@
+module Gitlab
+ module Badge
+ ##
+ # Abstract class for badge metadata
+ #
+ class Metadata
+ include Gitlab::Application.routes.url_helpers
+ include ActionView::Helpers::AssetTagHelper
+ include ActionView::Helpers::UrlHelper
+
+ def initialize(badge)
+ @badge = badge
+ end
+
+ def to_html
+ link_to(image_tag(image_url, alt: title), link_url)
+ end
+
+ def to_markdown
+ "[![#{title}](#{image_url})](#{link_url})"
+ end
+
+ def title
+ raise NotImplementedError
+ end
+
+ def image_url
+ raise NotImplementedError
+ end
+
+ def link_url
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/badge/template.rb b/lib/gitlab/badge/template.rb
new file mode 100644
index 00000000000..bfeb0052642
--- /dev/null
+++ b/lib/gitlab/badge/template.rb
@@ -0,0 +1,49 @@
+module Gitlab
+ module Badge
+ ##
+ # Abstract template class for badges
+ #
+ class Template
+ def initialize(badge)
+ @entity = badge.entity
+ @status = badge.status
+ end
+
+ def key_text
+ raise NotImplementedError
+ end
+
+ def value_text
+ raise NotImplementedError
+ end
+
+ def key_width
+ raise NotImplementedError
+ end
+
+ def value_width
+ raise NotImplementedError
+ end
+
+ def value_color
+ raise NotImplementedError
+ end
+
+ def key_color
+ '#555'
+ end
+
+ def key_text_anchor
+ key_width / 2
+ end
+
+ def value_text_anchor
+ key_width + (value_width / 2)
+ end
+
+ def width
+ key_width + value_width
+ end
+ end
+ end
+end
diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb
index 0239aea47fb..602de72d23f 100644
--- a/spec/controllers/admin/groups_controller_spec.rb
+++ b/spec/controllers/admin/groups_controller_spec.rb
@@ -7,12 +7,13 @@ describe Admin::GroupsController do
before do
sign_in(admin)
- Sidekiq::Testing.fake!
end
describe 'DELETE #destroy' do
it 'schedules a group destroy' do
- expect { delete :destroy, id: project.group.path }.to change(GroupDestroyWorker.jobs, :size).by(1)
+ Sidekiq::Testing.fake! do
+ expect { delete :destroy, id: project.group.path }.to change(GroupDestroyWorker.jobs, :size).by(1)
+ end
end
it 'redirects to the admin group path' do
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 4ae6364207b..a763e2c5ba8 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -89,12 +89,13 @@ describe GroupsController do
context 'as the group owner' do
before do
- Sidekiq::Testing.fake!
sign_in(user)
end
it 'schedules a group destroy' do
- expect { delete :destroy, id: group.path }.to change(GroupDestroyWorker.jobs, :size).by(1)
+ Sidekiq::Testing.fake! do
+ expect { delete :destroy, id: group.path }.to change(GroupDestroyWorker.jobs, :size).by(1)
+ end
end
it 'redirects to the root path' do
diff --git a/spec/features/merge_requests/diff_notes_spec.rb b/spec/features/merge_requests/diff_notes_spec.rb
new file mode 100644
index 00000000000..12e89742b79
--- /dev/null
+++ b/spec/features/merge_requests/diff_notes_spec.rb
@@ -0,0 +1,159 @@
+require 'spec_helper'
+
+feature 'Diff notes', js: true, feature: true do
+ include WaitForAjax
+
+ before do
+ login_as :admin
+ @merge_request = create(:merge_request)
+ @project = @merge_request.source_project
+ end
+
+ context 'merge request diffs' do
+ let(:comment_button_class) { '.add-diff-note' }
+ let(:notes_holder_input_class) { 'js-temp-notes-holder' }
+ let(:notes_holder_input_xpath) { './following-sibling::*[contains(concat(" ", @class, " "), " notes_holder ")]' }
+ let(:test_note_comment) { 'this is a test note!' }
+
+ context 'when hovering over the parallel view diff file' do
+ before(:each) do
+ visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
+ click_link 'Side-by-side'
+ end
+
+ context 'with an old line on the left and no line on the right' do
+ it 'should allow commenting on the left side' do
+ should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]').find(:xpath, '..'), 'left')
+ end
+
+ it 'should not allow commenting on the right side' do
+ should_not_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]').find(:xpath, '..'), 'right')
+ end
+ end
+
+ context 'with no line on the left and a new line on the right' do
+ it 'should not allow commenting on the left side' do
+ should_not_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"]').find(:xpath, '..'), 'left')
+ end
+
+ it 'should allow commenting on the right side' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"]').find(:xpath, '..'), 'right')
+ end
+ end
+
+ context 'with an old line on the left and a new line on the right' do
+ it 'should allow commenting on the left side' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'left')
+ end
+
+ it 'should allow commenting on the right side' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"]').find(:xpath, '..'), 'right')
+ end
+ end
+
+ context 'with an unchanged line on the left and an unchanged line on the right' do
+ it 'should allow commenting on the left side' do
+ should_allow_commenting(first('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]').find(:xpath, '..'), 'left')
+ end
+
+ it 'should allow commenting on the right side' do
+ should_allow_commenting(first('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]').find(:xpath, '..'), 'right')
+ end
+ end
+
+ context 'with a match line' do
+ it 'should not allow commenting on the left side' do
+ should_not_allow_commenting(first('.match').find(:xpath, '..'), 'left')
+ end
+
+ it 'should not allow commenting on the right side' do
+ should_not_allow_commenting(first('.match').find(:xpath, '..'), 'right')
+ end
+ end
+ end
+
+ context 'when hovering over the inline view diff file' do
+ before do
+ visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
+ click_link 'Inline'
+ end
+
+ context 'with a new line' do
+ it 'should allow commenting' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
+ end
+ end
+
+ context 'with an old line' do
+ it 'should allow commenting' do
+ should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'))
+ end
+ end
+
+ context 'with an unchanged line' do
+ it 'should allow commenting' do
+ should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]'))
+ end
+ end
+
+ context 'with a match line' do
+ it 'should not allow commenting' do
+ should_not_allow_commenting(first('.match'))
+ end
+ end
+ end
+
+ def should_allow_commenting(line_holder, diff_side = nil)
+ line = get_line_components(line_holder, diff_side)
+ line[:content].hover
+ expect(line[:num]).to have_css comment_button_class
+
+ comment_on_line(line_holder, line)
+ wait_for_ajax
+
+ assert_comment_persistence(line_holder)
+ end
+
+ def should_not_allow_commenting(line_holder, diff_side = nil)
+ line = get_line_components(line_holder, diff_side)
+ line[:content].hover
+ expect(line[:num]).not_to have_css comment_button_class
+ end
+
+ def get_line_components(line_holder, diff_side = nil)
+ if diff_side.nil?
+ get_inline_line_components(line_holder)
+ else
+ get_parallel_line_components(line_holder, diff_side)
+ end
+ end
+
+ def get_inline_line_components(line_holder)
+ { content: line_holder.first('.line_content'), num: line_holder.first('.diff-line-num') }
+ end
+
+ def get_parallel_line_components(line_holder, diff_side = nil)
+ side_index = diff_side == 'left' ? 0 : 1
+ { content: line_holder.all('.line_content')[side_index], num: line_holder.all('.diff-line-num')[side_index] }
+ end
+
+ def comment_on_line(line_holder, line)
+ line[:num].find(comment_button_class).trigger 'click'
+ expect(line_holder).to have_xpath notes_holder_input_xpath
+
+ notes_holder_input = line_holder.find(:xpath, notes_holder_input_xpath)
+ expect(notes_holder_input[:class]).to include(notes_holder_input_class)
+
+ notes_holder_input.fill_in 'note[note]', with: test_note_comment
+ click_button 'Comment'
+ end
+
+ def assert_comment_persistence(line_holder)
+ expect(line_holder).to have_xpath notes_holder_input_xpath
+
+ notes_holder_saved = line_holder.find(:xpath, notes_holder_input_xpath)
+ expect(notes_holder_saved[:class]).not_to include(notes_holder_input_class)
+ expect(notes_holder_saved).to have_content test_note_comment
+ end
+ end
+end
diff --git a/spec/features/projects/badges/coverage_spec.rb b/spec/features/projects/badges/coverage_spec.rb
new file mode 100644
index 00000000000..af86d3c338a
--- /dev/null
+++ b/spec/features/projects/badges/coverage_spec.rb
@@ -0,0 +1,73 @@
+require 'spec_helper'
+
+feature 'test coverage badge' do
+ given!(:user) { create(:user) }
+ given!(:project) { create(:project, :private) }
+
+ given!(:pipeline) do
+ create(:ci_pipeline, project: project,
+ ref: 'master',
+ sha: project.commit.id)
+ end
+
+ context 'when user has access to view badge' do
+ background do
+ project.team << [user, :developer]
+ login_as(user)
+ end
+
+ scenario 'user requests coverage badge image for pipeline' do
+ create_job(coverage: 100, name: 'test:1')
+ create_job(coverage: 90, name: 'test:2')
+
+ show_test_coverage_badge
+
+ expect_coverage_badge('95%')
+ end
+
+ scenario 'user requests coverage badge for specific job' do
+ create_job(coverage: 50, name: 'test:1')
+ create_job(coverage: 50, name: 'test:2')
+ create_job(coverage: 85, name: 'coverage')
+
+ show_test_coverage_badge(job: 'coverage')
+
+ expect_coverage_badge('85%')
+ end
+
+ scenario 'user requests coverage badge for pipeline without coverage' do
+ create_job(coverage: nil, name: 'test')
+
+ show_test_coverage_badge
+
+ expect_coverage_badge('unknown')
+ end
+ end
+
+ context 'when user does not have access to view badge' do
+ background { login_as(user) }
+
+ scenario 'user requests test coverage badge image' do
+ show_test_coverage_badge
+
+ expect(page).to have_http_status(404)
+ end
+ end
+
+ def create_job(coverage:, name:)
+ create(:ci_build, name: name,
+ coverage: coverage,
+ pipeline: pipeline)
+ end
+
+ def show_test_coverage_badge(job: nil)
+ visit coverage_namespace_project_badges_path(
+ project.namespace, project, ref: :master, job: job, format: :svg)
+ end
+
+ def expect_coverage_badge(coverage)
+ svg = Nokogiri::XML.parse(page.body)
+ expect(page.response_headers['Content-Type']).to include('image/svg+xml')
+ expect(svg.at(%Q{text:contains("#{coverage}")})).to be_truthy
+ end
+end
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index 75166bca119..67a4a5d1ab1 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -9,25 +9,43 @@ feature 'list of badges' do
visit namespace_project_pipelines_settings_path(project.namespace, project)
end
- scenario 'user displays list of badges' do
- expect(page).to have_content 'build status'
- expect(page).to have_content 'Markdown'
- expect(page).to have_content 'HTML'
- expect(page).to have_css('.highlight', count: 2)
- expect(page).to have_xpath("//img[@alt='build status']")
-
- page.within('.highlight', match: :first) do
- expect(page).to have_content 'badges/master/build.svg'
+ scenario 'user wants to see build status badge' do
+ page.within('.build-status') do
+ expect(page).to have_content 'build status'
+ expect(page).to have_content 'Markdown'
+ expect(page).to have_content 'HTML'
+ expect(page).to have_css('.highlight', count: 2)
+ expect(page).to have_xpath("//img[@alt='build status']")
+
+ page.within('.highlight', match: :first) do
+ expect(page).to have_content 'badges/master/build.svg'
+ end
end
end
- scenario 'user changes current ref on badges list page', js: true do
- first('.js-project-refs-dropdown').click
+ scenario 'user wants to see coverage report badge' do
+ page.within('.coverage-report') do
+ expect(page).to have_content 'coverage report'
+ expect(page).to have_content 'Markdown'
+ expect(page).to have_content 'HTML'
+ expect(page).to have_css('.highlight', count: 2)
+ expect(page).to have_xpath("//img[@alt='coverage report']")
- page.within '.project-refs-form' do
- click_link 'improve/awesome'
+ page.within('.highlight', match: :first) do
+ expect(page).to have_content 'badges/master/coverage.svg'
+ end
end
+ end
+
+ scenario 'user changes current ref of build status badge', js: true do
+ page.within('.build-status') do
+ first('.js-project-refs-dropdown').click
- expect(page).to have_content 'badges/improve/awesome/build.svg'
+ page.within '.project-refs-form' do
+ click_link 'improve/awesome'
+ end
+
+ expect(page).to have_content 'badges/improve/awesome/build.svg'
+ end
end
end
diff --git a/spec/features/projects/files/editing_a_file_spec.rb b/spec/features/projects/files/editing_a_file_spec.rb
new file mode 100644
index 00000000000..fe047e00409
--- /dev/null
+++ b/spec/features/projects/files/editing_a_file_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+
+feature 'User wants to edit a file', feature: true do
+ include WaitForAjax
+
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:commit_params) do
+ {
+ source_branch: project.default_branch,
+ target_branch: project.default_branch,
+ commit_message: "Committing First Update",
+ file_path: ".gitignore",
+ file_content: "First Update",
+ last_commit_sha: Gitlab::Git::Commit.last_for_path(project.repository, project.default_branch,
+ ".gitignore").sha
+ }
+ end
+
+ background do
+ project.team << [user, :master]
+ login_as user
+ visit namespace_project_edit_blob_path(project.namespace, project,
+ File.join(project.default_branch, '.gitignore'))
+ end
+
+ scenario 'file has been updated since the user opened the edit page' do
+ Files::UpdateService.new(project, user, commit_params).execute
+
+ click_button 'Commit Changes'
+
+ expect(page).to have_content 'Someone edited the file the same time you did.'
+ end
+end
diff --git a/spec/lib/gitlab/badge/build/metadata_spec.rb b/spec/lib/gitlab/badge/build/metadata_spec.rb
index ad5388215c2..d678e522721 100644
--- a/spec/lib/gitlab/badge/build/metadata_spec.rb
+++ b/spec/lib/gitlab/badge/build/metadata_spec.rb
@@ -1,37 +1,27 @@
require 'spec_helper'
+require 'lib/gitlab/badge/shared/metadata'
describe Gitlab::Badge::Build::Metadata do
- let(:project) { create(:project) }
- let(:branch) { 'master' }
- let(:badge) { described_class.new(project, branch) }
+ let(:badge) { double(project: create(:project), ref: 'feature') }
+ let(:metadata) { described_class.new(badge) }
- describe '#to_html' do
- let(:html) { Nokogiri::HTML.parse(badge.to_html) }
- let(:a_href) { html.at('a') }
+ it_behaves_like 'badge metadata'
- it 'points to link' do
- expect(a_href[:href]).to eq badge.link_url
- end
-
- it 'contains clickable image' do
- expect(a_href.children.first.name).to eq 'img'
+ describe '#title' do
+ it 'returns build status title' do
+ expect(metadata.title).to eq 'build status'
end
end
- describe '#to_markdown' do
- subject { badge.to_markdown }
-
- it { is_expected.to include badge.image_url }
- it { is_expected.to include badge.link_url }
- end
-
describe '#image_url' do
- subject { badge.image_url }
- it { is_expected.to include "badges/#{branch}/build.svg" }
+ it 'returns valid url' do
+ expect(metadata.image_url).to include 'badges/feature/build.svg'
+ end
end
describe '#link_url' do
- subject { badge.link_url }
- it { is_expected.to include "commits/#{branch}" }
+ it 'returns valid link' do
+ expect(metadata.link_url).to include 'commits/feature'
+ end
end
end
diff --git a/spec/lib/gitlab/badge/build_spec.rb b/spec/lib/gitlab/badge/build/status_spec.rb
index bb8144d5122..38eebb2a176 100644
--- a/spec/lib/gitlab/badge/build_spec.rb
+++ b/spec/lib/gitlab/badge/build/status_spec.rb
@@ -1,11 +1,23 @@
require 'spec_helper'
-describe Gitlab::Badge::Build do
+describe Gitlab::Badge::Build::Status do
let(:project) { create(:project) }
let(:sha) { project.commit.sha }
let(:branch) { 'master' }
let(:badge) { described_class.new(project, branch) }
+ describe '#entity' do
+ it 'always says build' do
+ expect(badge.entity).to eq 'build'
+ end
+ end
+
+ describe '#template' do
+ it 'returns badge template' do
+ expect(badge.template.key_text).to eq 'build'
+ end
+ end
+
describe '#metadata' do
it 'returns badge metadata' do
expect(badge.metadata.image_url)
@@ -13,12 +25,6 @@ describe Gitlab::Badge::Build do
end
end
- describe '#key_text' do
- it 'always says build' do
- expect(badge.key_text).to eq 'build'
- end
- end
-
context 'build exists' do
let!(:build) { create_build(project, sha, branch) }
@@ -30,12 +36,6 @@ describe Gitlab::Badge::Build do
expect(badge.status).to eq 'success'
end
end
-
- describe '#value_text' do
- it 'returns correct value text' do
- expect(badge.value_text).to eq 'success'
- end
- end
end
context 'build failed' do
@@ -46,12 +46,6 @@ describe Gitlab::Badge::Build do
expect(badge.status).to eq 'failed'
end
end
-
- describe '#value_text' do
- it 'has correct value text' do
- expect(badge.value_text).to eq 'failed'
- end
- end
end
context 'when outdated pipeline for given ref exists' do
@@ -87,12 +81,6 @@ describe Gitlab::Badge::Build do
expect(badge.status).to eq 'unknown'
end
end
-
- describe '#value_text' do
- it 'has correct value text' do
- expect(badge.value_text).to eq 'unknown'
- end
- end
end
def create_build(project, sha, branch)
diff --git a/spec/lib/gitlab/badge/build/template_spec.rb b/spec/lib/gitlab/badge/build/template_spec.rb
index 86dead3c54e..a7e21fb8bb1 100644
--- a/spec/lib/gitlab/badge/build/template_spec.rb
+++ b/spec/lib/gitlab/badge/build/template_spec.rb
@@ -1,8 +1,8 @@
require 'spec_helper'
describe Gitlab::Badge::Build::Template do
- let(:status) { 'success' }
- let(:template) { described_class.new(status) }
+ let(:badge) { double(entity: 'build', status: 'success') }
+ let(:template) { described_class.new(badge) }
describe '#key_text' do
it 'is always says build' do
@@ -34,15 +34,15 @@ describe Gitlab::Badge::Build::Template do
describe '#value_color' do
context 'when status is success' do
- let(:status) { 'success' }
-
it 'has expected color' do
expect(template.value_color).to eq '#4c1'
end
end
context 'when status is failed' do
- let(:status) { 'failed' }
+ before do
+ allow(badge).to receive(:status).and_return('failed')
+ end
it 'has expected color' do
expect(template.value_color).to eq '#e05d44'
@@ -50,7 +50,9 @@ describe Gitlab::Badge::Build::Template do
end
context 'when status is running' do
- let(:status) { 'running' }
+ before do
+ allow(badge).to receive(:status).and_return('running')
+ end
it 'has expected color' do
expect(template.value_color).to eq '#dfb317'
@@ -58,7 +60,9 @@ describe Gitlab::Badge::Build::Template do
end
context 'when status is unknown' do
- let(:status) { 'unknown' }
+ before do
+ allow(badge).to receive(:status).and_return('unknown')
+ end
it 'has expected color' do
expect(template.value_color).to eq '#9f9f9f'
@@ -66,7 +70,9 @@ describe Gitlab::Badge::Build::Template do
end
context 'when status does not match any known statuses' do
- let(:status) { 'invalid status' }
+ before do
+ allow(badge).to receive(:status).and_return('invalid')
+ end
it 'has expected color' do
expect(template.value_color).to eq '#9f9f9f'
diff --git a/spec/lib/gitlab/badge/coverage/metadata_spec.rb b/spec/lib/gitlab/badge/coverage/metadata_spec.rb
new file mode 100644
index 00000000000..74eaf7eaf8b
--- /dev/null
+++ b/spec/lib/gitlab/badge/coverage/metadata_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+require 'lib/gitlab/badge/shared/metadata'
+
+describe Gitlab::Badge::Coverage::Metadata do
+ let(:badge) do
+ double(project: create(:project), ref: 'feature', job: 'test')
+ end
+
+ let(:metadata) { described_class.new(badge) }
+
+ it_behaves_like 'badge metadata'
+
+ describe '#title' do
+ it 'returns coverage report title' do
+ expect(metadata.title).to eq 'coverage report'
+ end
+ end
+
+ describe '#image_url' do
+ it 'returns valid url' do
+ expect(metadata.image_url).to include 'badges/feature/coverage.svg'
+ end
+ end
+
+ describe '#link_url' do
+ it 'returns valid link' do
+ expect(metadata.link_url).to include 'commits/feature'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/badge/coverage/report_spec.rb b/spec/lib/gitlab/badge/coverage/report_spec.rb
new file mode 100644
index 00000000000..1ff49602486
--- /dev/null
+++ b/spec/lib/gitlab/badge/coverage/report_spec.rb
@@ -0,0 +1,93 @@
+require 'spec_helper'
+
+describe Gitlab::Badge::Coverage::Report do
+ let(:project) { create(:project) }
+ let(:job_name) { nil }
+
+ let(:badge) do
+ described_class.new(project, 'master', job_name)
+ end
+
+ describe '#entity' do
+ it 'describes a coverage' do
+ expect(badge.entity).to eq 'coverage'
+ end
+ end
+
+ describe '#metadata' do
+ it 'returns correct metadata' do
+ expect(badge.metadata.image_url).to include 'coverage.svg'
+ end
+ end
+
+ describe '#template' do
+ it 'returns correct template' do
+ expect(badge.template.key_text).to eq 'coverage'
+ end
+ end
+
+ shared_examples 'unknown coverage report' do
+ context 'particular job specified' do
+ let(:job_name) { '' }
+
+ it 'returns nil' do
+ expect(badge.status).to be_nil
+ end
+ end
+
+ context 'particular job not specified' do
+ let(:job_name) { nil }
+
+ it 'returns nil' do
+ expect(badge.status).to be_nil
+ end
+ end
+ end
+
+ context 'pipeline exists' do
+ let!(:pipeline) do
+ create(:ci_pipeline, project: project,
+ sha: project.commit.id,
+ ref: 'master')
+ end
+
+ context 'builds exist' do
+ before do
+ create(:ci_build, name: 'first', pipeline: pipeline, coverage: 40)
+ create(:ci_build, pipeline: pipeline, coverage: 60)
+ end
+
+ context 'particular job specified' do
+ let(:job_name) { 'first' }
+
+ it 'returns coverage for the particular job' do
+ expect(badge.status).to eq 40
+ end
+ end
+
+ context 'particular job not specified' do
+ let(:job_name) { '' }
+
+ it 'returns arithemetic mean for the pipeline' do
+ expect(badge.status).to eq 50
+ end
+ end
+ end
+
+ context 'builds do not exist' do
+ it_behaves_like 'unknown coverage report'
+
+ context 'particular job specified' do
+ let(:job_name) { 'nonexistent' }
+
+ it 'retruns nil' do
+ expect(badge.status).to be_nil
+ end
+ end
+ end
+ end
+
+ context 'pipeline does not exist' do
+ it_behaves_like 'unknown coverage report'
+ end
+end
diff --git a/spec/lib/gitlab/badge/coverage/template_spec.rb b/spec/lib/gitlab/badge/coverage/template_spec.rb
new file mode 100644
index 00000000000..383bae6e087
--- /dev/null
+++ b/spec/lib/gitlab/badge/coverage/template_spec.rb
@@ -0,0 +1,130 @@
+require 'spec_helper'
+
+describe Gitlab::Badge::Coverage::Template do
+ let(:badge) { double(entity: 'coverage', status: 90) }
+ let(:template) { described_class.new(badge) }
+
+ describe '#key_text' do
+ it 'is always says coverage' do
+ expect(template.key_text).to eq 'coverage'
+ end
+ end
+
+ describe '#value_text' do
+ context 'when coverage is known' do
+ it 'returns coverage percentage' do
+ expect(template.value_text).to eq '90%'
+ end
+ end
+
+ context 'when coverage is unknown' do
+ before do
+ allow(badge).to receive(:status).and_return(nil)
+ end
+
+ it 'returns string that says coverage is unknown' do
+ expect(template.value_text).to eq 'unknown'
+ end
+ end
+ end
+
+ describe '#key_width' do
+ it 'has a fixed key width' do
+ expect(template.key_width).to eq 62
+ end
+ end
+
+ describe '#value_width' do
+ context 'when coverage is known' do
+ it 'is narrower when coverage is known' do
+ expect(template.value_width).to eq 36
+ end
+ end
+
+ context 'when coverage is unknown' do
+ before do
+ allow(badge).to receive(:status).and_return(nil)
+ end
+
+ it 'is wider when coverage is unknown to fit text' do
+ expect(template.value_width).to eq 58
+ end
+ end
+ end
+
+ describe '#key_color' do
+ it 'always has the same color' do
+ expect(template.key_color).to eq '#555'
+ end
+ end
+
+ describe '#value_color' do
+ context 'when coverage is good' do
+ before do
+ allow(badge).to receive(:status).and_return(98)
+ end
+
+ it 'is green' do
+ expect(template.value_color).to eq '#4c1'
+ end
+ end
+
+ context 'when coverage is acceptable' do
+ before do
+ allow(badge).to receive(:status).and_return(90)
+ end
+
+ it 'is green-orange' do
+ expect(template.value_color).to eq '#a3c51c'
+ end
+ end
+
+ context 'when coverage is medium' do
+ before do
+ allow(badge).to receive(:status).and_return(75)
+ end
+
+ it 'is orange-yellow' do
+ expect(template.value_color).to eq '#dfb317'
+ end
+ end
+
+ context 'when coverage is low' do
+ before do
+ allow(badge).to receive(:status).and_return(50)
+ end
+
+ it 'is red' do
+ expect(template.value_color).to eq '#e05d44'
+ end
+ end
+
+ context 'when coverage is unknown' do
+ before do
+ allow(badge).to receive(:status).and_return(nil)
+ end
+
+ it 'is grey' do
+ expect(template.value_color).to eq '#9f9f9f'
+ end
+ end
+ end
+
+ describe '#width' do
+ context 'when coverage is known' do
+ it 'returns the key width plus value width' do
+ expect(template.width).to eq 98
+ end
+ end
+
+ context 'when coverage is unknown' do
+ before do
+ allow(badge).to receive(:status).and_return(nil)
+ end
+
+ it 'returns key width plus wider value width' do
+ expect(template.width).to eq 120
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/badge/shared/metadata.rb b/spec/lib/gitlab/badge/shared/metadata.rb
new file mode 100644
index 00000000000..0cf18514251
--- /dev/null
+++ b/spec/lib/gitlab/badge/shared/metadata.rb
@@ -0,0 +1,21 @@
+shared_examples 'badge metadata' do
+ describe '#to_html' do
+ let(:html) { Nokogiri::HTML.parse(metadata.to_html) }
+ let(:a_href) { html.at('a') }
+
+ it 'points to link' do
+ expect(a_href[:href]).to eq metadata.link_url
+ end
+
+ it 'contains clickable image' do
+ expect(a_href.children.first.name).to eq 'img'
+ end
+ end
+
+ describe '#to_markdown' do
+ subject { metadata.to_markdown }
+
+ it { is_expected.to include metadata.image_url }
+ it { is_expected.to include metadata.link_url }
+ end
+end
diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb
index b528baaf15c..ea718232255 100644
--- a/spec/models/project_services/irker_service_spec.rb
+++ b/spec/models/project_services/irker_service_spec.rb
@@ -52,19 +52,20 @@ describe IrkerService, models: true do
let(:colorize_messages) { '1' }
before do
+ @irker_server = TCPServer.new 'localhost', 0
+
allow(irker).to receive_messages(
active: true,
project: project,
project_id: project.id,
service_hook: true,
- server_host: 'localhost',
- server_port: 6659,
+ server_host: @irker_server.addr[2],
+ server_port: @irker_server.addr[1],
default_irc_uri: 'irc://chat.freenode.net/',
recipients: recipients,
colorize_messages: colorize_messages)
irker.valid?
- @irker_server = TCPServer.new 'localhost', 6659
end
after do
diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb
new file mode 100644
index 00000000000..d019e50649f
--- /dev/null
+++ b/spec/services/files/update_service_spec.rb
@@ -0,0 +1,84 @@
+require "spec_helper"
+
+describe Files::UpdateService do
+ subject { described_class.new(project, user, commit_params) }
+
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:file_path) { 'files/ruby/popen.rb' }
+ let(:new_contents) { "New Content" }
+ let(:commit_params) do
+ {
+ file_path: file_path,
+ commit_message: "Update File",
+ file_content: new_contents,
+ file_content_encoding: "text",
+ last_commit_sha: last_commit_sha,
+ source_project: project,
+ source_branch: project.default_branch,
+ target_branch: project.default_branch,
+ }
+ end
+
+ before do
+ project.team << [user, :master]
+ end
+
+ describe "#execute" do
+ context "when the file's last commit sha does not match the supplied last_commit_sha" do
+ let(:last_commit_sha) { "foo" }
+
+ it "returns a hash with the correct error message and a :error status " do
+ expect { subject.execute }.
+ to raise_error(Files::UpdateService::FileChangedError,
+ "You are attempting to update a file that has changed since you started editing it.")
+ end
+ end
+
+ context "when the file's last commit sha does match the supplied last_commit_sha" do
+ let(:last_commit_sha) { Gitlab::Git::Commit.last_for_path(project.repository, project.default_branch, file_path).sha }
+
+ it "returns a hash with the :success status " do
+ results = subject.execute
+
+ expect(results).to match({ status: :success })
+ end
+
+ it "updates the file with the new contents" do
+ subject.execute
+
+ results = project.repository.blob_at_branch(project.default_branch, file_path)
+
+ expect(results.data).to eq(new_contents)
+ end
+ end
+
+ context "when the last_commit_sha is not supplied" do
+ let(:commit_params) do
+ {
+ file_path: file_path,
+ commit_message: "Update File",
+ file_content: new_contents,
+ file_content_encoding: "text",
+ source_project: project,
+ source_branch: project.default_branch,
+ target_branch: project.default_branch,
+ }
+ end
+
+ it "returns a hash with the :success status " do
+ results = subject.execute
+
+ expect(results).to match({ status: :success })
+ end
+
+ it "updates the file with the new contents" do
+ subject.execute
+
+ results = project.repository.blob_at_branch(project.default_branch, file_path)
+
+ expect(results.data).to eq(new_contents)
+ end
+ end
+ end
+end