summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorAchilleas Pipinellis <axilleas@axilleas.me>2017-05-10 18:57:19 +0000
committerAchilleas Pipinellis <axilleas@axilleas.me>2017-05-10 18:57:19 +0000
commit9c7d87ef0ec941b389cb5ef57a07ff0f43909305 (patch)
tree827aaf4dbb628eb31517aaf2960814a521430229 /spec
parent1e0df21a74017deb780023ed83f04dbbeed13899 (diff)
parent180ec7113e358a7f8388e1436dc0670a11ba68df (diff)
downloadgitlab-ce-9c7d87ef0ec941b389cb5ef57a07ff0f43909305.tar.gz
Merge branch 'master' into 'docs/hide-gcp-install'docs/hide-gcp-install
# Conflicts: # doc/install/README.md
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/groups_controller_spec.rb35
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb8
-rw-r--r--spec/controllers/snippets_controller_spec.rb34
-rw-r--r--spec/features/boards/sidebar_spec.rb4
-rw-r--r--spec/features/boards/sub_group_project_spec.rb45
-rw-r--r--spec/features/cycle_analytics_spec.rb10
-rw-r--r--spec/features/dashboard/milestone_filter_spec.rb59
-rw-r--r--spec/features/dashboard/snippets_spec.rb47
-rw-r--r--spec/features/issuables/issuable_list_spec.rb3
-rw-r--r--spec/features/login_spec.rb4
-rw-r--r--spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb10
-rw-r--r--spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb5
-rw-r--r--spec/features/merge_requests/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb2
-rw-r--r--spec/features/merge_requests/widget_spec.rb17
-rw-r--r--spec/features/projects/members/sorting_spec.rb4
-rw-r--r--spec/features/projects/snippets_spec.rb24
-rw-r--r--spec/features/search_spec.rb19
-rw-r--r--spec/features/snippets/explore_spec.rb25
-rw-r--r--spec/features/snippets/internal_snippet_spec.rb23
-rw-r--r--spec/features/users/snippets_spec.rb46
-rw-r--r--spec/finders/groups_finder_spec.rb57
-rw-r--r--spec/finders/snippets_finder_spec.rb125
-rw-r--r--spec/helpers/application_helper_spec.rb25
-rw-r--r--spec/helpers/avatars_helper_spec.rb2
-rw-r--r--spec/helpers/submodule_helper_spec.rb12
-rw-r--r--spec/javascripts/blob/create_branch_dropdown_spec.js1
-rw-r--r--spec/javascripts/blob/target_branch_dropdown_spec.js1
-rw-r--r--spec/javascripts/gl_dropdown_spec.js47
-rw-r--r--spec/javascripts/labels_issue_sidebar_spec.js1
-rw-r--r--spec/javascripts/pipelines/graph/graph_component_spec.js49
-rw-r--r--spec/javascripts/pipelines/graph/mock_data.js232
-rw-r--r--spec/javascripts/project_title_spec.js1
-rw-r--r--spec/javascripts/search_autocomplete_spec.js1
-rw-r--r--spec/javascripts/u2f/mock_u2f_device.js6
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js4
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js184
-rw-r--r--spec/javascripts/vue_shared/ci_action_icons_spec.js5
-rw-r--r--spec/javascripts/vue_shared/components/loading_icon_spec.js53
-rw-r--r--spec/javascripts/vue_shared/components/memory_graph_spec.js143
-rw-r--r--spec/javascripts/vue_shared/components/mock_data.js69
-rw-r--r--spec/javascripts/vue_shared/components/table_pagination_spec.js2
-rw-r--r--spec/lib/banzai/filter/external_link_filter_spec.rb85
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb29
-rw-r--r--spec/lib/gitlab/cycle_analytics/events_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml2
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/other_markup_spec.rb2
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb75
-rw-r--r--spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb33
-rw-r--r--spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb32
-rw-r--r--spec/models/ci/pipeline_spec.rb2
-rw-r--r--spec/models/cycle_analytics/test_spec.rb1
-rw-r--r--spec/models/group_spec.rb16
-rw-r--r--spec/models/merge_request_spec.rb15
-rw-r--r--spec/models/project_spec.rb10
-rw-r--r--spec/models/repository_spec.rb2
-rw-r--r--spec/models/snippet_spec.rb40
-rw-r--r--spec/models/user_spec.rb11
-rw-r--r--spec/policies/project_snippet_policy_spec.rb80
-rw-r--r--spec/requests/api/groups_spec.rb2
-rw-r--r--spec/requests/api/v3/groups_spec.rb2
-rw-r--r--spec/requests/openid_connect_spec.rb2
-rw-r--r--spec/requests/projects/cycle_analytics_events_spec.rb1
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb36
-rw-r--r--spec/services/git_push_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb6
-rw-r--r--spec/services/merge_requests/update_service_spec.rb4
-rw-r--r--spec/services/projects/participants_service_spec.rb5
-rw-r--r--spec/sidekiq/cron/job_gem_dependency_spec.rb18
-rw-r--r--spec/views/projects/imports/new.html.haml_spec.rb22
-rw-r--r--spec/views/projects/tree/show.html.haml_spec.rb2
-rw-r--r--spec/workers/namespaceless_project_destroy_worker_spec.rb79
-rw-r--r--spec/workers/pipeline_metrics_worker_spec.rb2
74 files changed, 1804 insertions, 265 deletions
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 073b87a1cb4..4c8d82a1677 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -26,6 +26,41 @@ describe GroupsController do
end
end
+ describe 'GET #subgroups' do
+ let!(:public_subgroup) { create(:group, :public, parent: group) }
+ let!(:private_subgroup) { create(:group, :private, parent: group) }
+
+ context 'as a user' do
+ before do
+ sign_in(user)
+ end
+
+ it 'shows the public subgroups' do
+ get :subgroups, id: group.to_param
+
+ expect(assigns(:nested_groups)).to contain_exactly(public_subgroup)
+ end
+
+ context 'being member' do
+ it 'shows public and private subgroups the user is member of' do
+ private_subgroup.add_guest(user)
+
+ get :subgroups, id: group.to_param
+
+ expect(assigns(:nested_groups)).to contain_exactly(public_subgroup, private_subgroup)
+ end
+ end
+ end
+
+ context 'as a guest' do
+ it 'shows the public subgroups' do
+ get :subgroups, id: group.to_param
+
+ expect(assigns(:nested_groups)).to contain_exactly(public_subgroup)
+ end
+ end
+ end
+
describe 'GET #issues' do
let(:issue_1) { create(:issue, project: project) }
let(:issue_2) { create(:issue, project: project) }
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 964246b580c..37a253fde9b 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -345,7 +345,8 @@ describe Projects::MergeRequestsController do
end
before do
- create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch)
+ pipeline = create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch)
+ merge_request.update(head_pipeline: pipeline)
end
it 'returns :merge_when_pipeline_succeeds' do
@@ -1159,7 +1160,10 @@ describe Projects::MergeRequestsController do
let(:status) { pipeline.detailed_status(double('user')) }
- before { get_pipeline_status }
+ before do
+ merge_request.update(head_pipeline: pipeline)
+ get_pipeline_status
+ end
it 'return a detailed head_pipeline status in json' do
expect(response).to have_http_status(:ok)
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index 41cd5bdcdd8..930415a4778 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -3,6 +3,34 @@ require 'spec_helper'
describe SnippetsController do
let(:user) { create(:user) }
+ describe 'GET #index' do
+ let(:user) { create(:user) }
+
+ context 'when username parameter is present' do
+ it 'renders snippets of a user when username is present' do
+ get :index, username: user.username
+
+ expect(response).to render_template(:index)
+ end
+ end
+
+ context 'when username parameter is not present' do
+ it 'redirects to explore snippets page when user is not logged in' do
+ get :index
+
+ expect(response).to redirect_to(explore_snippets_path)
+ end
+
+ it 'redirects to snippets dashboard page when user is logged in' do
+ sign_in(user)
+
+ get :index
+
+ expect(response).to redirect_to(dashboard_snippets_path)
+ end
+ end
+ end
+
describe 'GET #new' do
context 'when signed in' do
before do
@@ -132,7 +160,7 @@ describe SnippetsController do
it 'responds with status 404' do
get :show, id: 'doesntexist'
- expect(response).to have_http_status(404)
+ expect(response).to redirect_to(new_user_session_path)
end
end
end
@@ -478,10 +506,10 @@ describe SnippetsController do
end
context 'when not signed in' do
- it 'responds with status 404' do
+ it 'redirects to the sign in path' do
get :raw, id: 'doesntexist'
- expect(response).to have_http_status(404)
+ expect(response).to redirect_to(new_user_session_path)
end
end
end
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 7c53d2b47d9..11ef8e1f61b 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -163,8 +163,8 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.assignee') do
click_link 'Edit'
-
- expect(page).to have_selector('.is-active')
+
+ expect(find('.dropdown-menu')).to have_selector('.is-active')
end
end
end
diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb
new file mode 100644
index 00000000000..6cd7fddd288
--- /dev/null
+++ b/spec/features/boards/sub_group_project_spec.rb
@@ -0,0 +1,45 @@
+require 'rails_helper'
+
+describe 'Sub-group project issue boards', :feature, :js do
+ include WaitForVueResource
+
+ let(:group) { create(:group) }
+ let(:nested_group_1) { create(:group, parent: group) }
+ let(:project) { create(:empty_project, group: nested_group_1) }
+ let(:board) { create(:board, project: project) }
+ let(:label) { create(:label, project: project) }
+ let(:user) { create(:user) }
+ let!(:list1) { create(:list, board: board, label: label, position: 0) }
+ let!(:issue) { create(:labeled_issue, project: project, labels: [label]) }
+
+ before do
+ project.add_master(user)
+
+ login_as(user)
+
+ visit namespace_project_board_path(project.namespace, project, board)
+ wait_for_vue_resource
+ end
+
+ it 'creates new label from sidebar' do
+ find('.card').click
+
+ page.within '.labels' do
+ click_link 'Edit'
+ click_link 'Create new label'
+ end
+
+ page.within '.dropdown-new-label' do
+ fill_in 'new_label_name', with: 'test label'
+ first('.suggest-colors-dropdown a').click
+
+ click_button 'Create'
+
+ wait_for_ajax
+ end
+
+ page.within '.labels' do
+ expect(page).to have_link 'test label'
+ end
+ end
+end
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index df2714f91ff..cbeb73d9cae 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -12,8 +12,10 @@ feature 'Cycle Analytics', feature: true, js: true do
context 'as an allowed user' do
context 'when project is new' do
before do
- project.team << [user, :master]
+ project.add_master(user)
+
login_as(user)
+
visit namespace_project_cycle_analytics_path(project.namespace, project)
wait_for_ajax
end
@@ -30,7 +32,9 @@ feature 'Cycle Analytics', feature: true, js: true do
context "when there's cycle analytics data" do
before do
- project.team << [user, :master]
+ allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
+ mr.update(head_pipeline: pipeline)
+ project.add_master(user)
create_cycle
deploy_master
@@ -84,7 +88,7 @@ feature 'Cycle Analytics', feature: true, js: true do
context "as a guest" do
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
create_cycle
diff --git a/spec/features/dashboard/milestone_filter_spec.rb b/spec/features/dashboard/milestone_filter_spec.rb
new file mode 100644
index 00000000000..628627f70d4
--- /dev/null
+++ b/spec/features/dashboard/milestone_filter_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+describe 'Dashboard > milestone filter', feature: true, js: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, name: 'test', namespace: user.namespace) }
+ let(:milestone) { create(:milestone, title: "v1.0", project: project) }
+ let(:milestone2) { create(:milestone, title: "v2.0", project: project) }
+ let!(:issue) { create :issue, author: user, project: project, milestone: milestone }
+ let!(:issue2) { create :issue, author: user, project: project, milestone: milestone2 }
+
+ before do
+ login_as(user)
+ visit issues_dashboard_path(author_id: user.id)
+ end
+
+ context 'default state' do
+ it 'shows issues with Any Milestone' do
+ page.all('.issue-info').each do |issue_info|
+ expect(issue_info.text).to match(/v\d.0/)
+ end
+ end
+ end
+
+ context 'filtering by milestone' do
+ milestone_select = '.js-milestone-select'
+
+ before do
+ find(milestone_select).click
+
+ page.within('.dropdown-content') do
+ click_link 'v1.0'
+ end
+
+ find(milestone_select).click
+ end
+
+ it 'shows issues with Milestone v1.0' do
+ expect(find('.issues-list')).to have_selector('.issue', count: 1)
+
+ find(milestone_select).click
+
+ expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
+ end
+
+ it 'should not change active Milestone unless clicked' do
+ find(milestone_select).click
+
+ expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
+
+ # open & close dropdown
+ find('.dropdown-menu-close').click
+ expect(find('.milestone-filter')).not_to have_selector('.dropdown.open')
+ find(milestone_select).click
+
+ expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
+ expect(find('.dropdown-content a.is-active')).to have_content('v1.0')
+ end
+ end
+end
diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb
index 62937688c22..c6ba118220a 100644
--- a/spec/features/dashboard/snippets_spec.rb
+++ b/spec/features/dashboard/snippets_spec.rb
@@ -12,4 +12,51 @@ describe 'Dashboard snippets', feature: true do
it_behaves_like 'paginated snippets'
end
+
+ context 'filtering by visibility' do
+ let(:user) { create(:user) }
+ let!(:snippets) do
+ [
+ create(:personal_snippet, :public, author: user),
+ create(:personal_snippet, :internal, author: user),
+ create(:personal_snippet, :private, author: user),
+ create(:personal_snippet, :public)
+ ]
+ end
+
+ before do
+ login_as(user)
+
+ visit dashboard_snippets_path
+ end
+
+ it 'contains all snippets of logged user' do
+ expect(page).to have_selector('.snippet-row', count: 3)
+
+ expect(page).to have_content(snippets[0].title)
+ expect(page).to have_content(snippets[1].title)
+ expect(page).to have_content(snippets[2].title)
+ end
+
+ it 'contains all private snippets of logged user when clicking on private' do
+ click_link('Private')
+
+ expect(page).to have_selector('.snippet-row', count: 1)
+ expect(page).to have_content(snippets[2].title)
+ end
+
+ it 'contains all internal snippets of logged user when clicking on internal' do
+ click_link('Internal')
+
+ expect(page).to have_selector('.snippet-row', count: 1)
+ expect(page).to have_content(snippets[1].title)
+ end
+
+ it 'contains all public snippets of logged user when clicking on public' do
+ click_link('Public')
+
+ expect(page).to have_selector('.snippet-row', count: 1)
+ expect(page).to have_content(snippets[0].title)
+ end
+ end
end
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index f3ec80bb149..414838fa22e 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -52,6 +52,9 @@ describe 'issuable list', feature: true do
create(:issue, project: project, author: user)
else
create(:merge_request, source_project: project, source_branch: generate(:branch))
+ source_branch = FFaker::Name.name
+ pipeline = create(:ci_empty_pipeline, project: project, ref: source_branch, status: %w(running failed success).sample, sha: 'any')
+ create(:merge_request, title: FFaker::Lorem.sentence, source_project: project, source_branch: source_branch, head_pipeline: pipeline)
end
2.times do
diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb
index 11d417c253d..c82e8c03343 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/login_spec.rb
@@ -41,7 +41,7 @@ feature 'Login', feature: true do
expect(page).to have_content('Your account has been blocked.')
end
- it 'does not update Devise trackable attributes' do
+ it 'does not update Devise trackable attributes', :redis do
user = create(:user, :blocked)
expect { login_with(user) }.not_to change { user.reload.sign_in_count }
@@ -55,7 +55,7 @@ feature 'Login', feature: true do
expect(page).to have_content('Invalid Login or password.')
end
- it 'does not update Devise trackable attributes' do
+ it 'does not update Devise trackable attributes', :redis do
expect { login_with(User.ghost) }.not_to change { User.ghost.reload.sign_in_count }
end
end
diff --git a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
index b79667a1a4c..5820784f8e7 100644
--- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
+++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
@@ -4,16 +4,18 @@ feature 'Merge immediately', :feature, :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
- let(:merge_request) do
+ let!(:merge_request) do
create(:merge_request_with_diffs, source_project: project,
author: user,
- title: 'Bug NS-04')
+ title: 'Bug NS-04',
+ head_pipeline: pipeline,
+ source_branch: pipeline.ref)
end
let(:pipeline) do
create(:ci_pipeline, project: project,
- sha: merge_request.diff_head_sha,
- ref: merge_request.source_branch)
+ ref: 'master',
+ sha: project.repository.commit('master').id)
end
before { project.team << [user, :master] }
diff --git a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
index b33d7f90a31..11b6f0c0a64 100644
--- a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
@@ -16,7 +16,10 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
ref: merge_request.source_branch)
end
- before { project.team << [user, :master] }
+ before do
+ project.add_master(user)
+ merge_request.update(head_pipeline_id: pipeline.id)
+ end
context 'when there is active pipeline for merge request' do
background do
diff --git a/spec/features/merge_requests/mini_pipeline_graph_spec.rb b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
index 449a60c1d05..5b2798af32f 100644
--- a/spec/features/merge_requests/mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
feature 'Mini Pipeline Graph', :js, :feature do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
- let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project, head_pipeline: pipeline) }
let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: 'master', status: 'running', sha: project.commit.id) }
let(:build) { create(:ci_build, pipeline: pipeline, stage: 'test', commands: 'test') }
diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
index 187e927dac4..cdda0542c51 100644
--- a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
@@ -31,6 +31,8 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
status: status)
end
+ before { merge_request.update(head_pipeline: pipeline) }
+
context 'when merge requests can only be merged if the pipeline succeeds' do
before do
project.update_attribute(:only_allow_merge_if_pipeline_succeeds, true)
diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb
index 3fcdc9f2c61..ae799584c0f 100644
--- a/spec/features/merge_requests/widget_spec.rb
+++ b/spec/features/merge_requests/widget_spec.rb
@@ -91,6 +91,8 @@ describe 'Merge request', :feature, :js do
statuses: [commit_status])
create(:ci_build, :pending, pipeline: pipeline)
+ merge_request.update(head_pipeline: pipeline)
+
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
@@ -103,10 +105,15 @@ describe 'Merge request', :feature, :js do
context 'when merge request is in the blocked pipeline state' do
before do
- create(:ci_pipeline, project: project,
- sha: merge_request.diff_head_sha,
- ref: merge_request.source_branch,
- status: :manual)
+ pipeline = create(
+ :ci_pipeline,
+ project: project,
+ sha: merge_request.diff_head_sha,
+ ref: merge_request.source_branch,
+ status: :manual
+ )
+
+ merge_request.update(head_pipeline: pipeline)
visit namespace_project_merge_request_path(project.namespace,
project,
@@ -131,6 +138,8 @@ describe 'Merge request', :feature, :js do
statuses: [commit_status])
create(:ci_build, :pending, pipeline: pipeline)
+ merge_request.update(head_pipeline: pipeline)
+
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index c7a32a65e49..b7ae5f0b925 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -68,7 +68,7 @@ feature 'Projects > Members > Sorting', feature: true do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
end
- scenario 'sorts by recent sign in' do
+ scenario 'sorts by recent sign in', :redis do
visit_members_list(sort: :recent_sign_in)
expect(first_member).to include(master.name)
@@ -76,7 +76,7 @@ feature 'Projects > Members > Sorting', feature: true do
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
end
- scenario 'sorts by oldest sign in' do
+ scenario 'sorts by oldest sign in', :redis do
visit_members_list(sort: :oldest_sign_in)
expect(first_member).to include(developer.name)
diff --git a/spec/features/projects/snippets_spec.rb b/spec/features/projects/snippets_spec.rb
index d37e8ed4699..18689c17fe9 100644
--- a/spec/features/projects/snippets_spec.rb
+++ b/spec/features/projects/snippets_spec.rb
@@ -4,11 +4,27 @@ describe 'Project snippets', feature: true do
context 'when the project has snippets' do
let(:project) { create(:empty_project, :public) }
let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) }
- before do
- allow(Snippet).to receive(:default_per_page).and_return(1)
- visit namespace_project_snippets_path(project.namespace, project)
+ let!(:other_snippet) { create(:project_snippet) }
+
+ context 'pagination' do
+ before do
+ allow(Snippet).to receive(:default_per_page).and_return(1)
+
+ visit namespace_project_snippets_path(project.namespace, project)
+ end
+
+ it_behaves_like 'paginated snippets'
end
- it_behaves_like 'paginated snippets'
+ context 'list content' do
+ it 'contains all project snippets' do
+ visit namespace_project_snippets_path(project.namespace, project)
+
+ expect(page).to have_selector('.snippet-row', count: 2)
+
+ expect(page).to have_content(snippets[0].title)
+ expect(page).to have_content(snippets[1].title)
+ end
+ end
end
end
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index 498a4a5cba0..f2866d071dc 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -20,6 +20,7 @@ describe "Search", feature: true do
context 'search filters', js: true do
let(:group) { create(:group) }
+ let!(:group_project) { create(:empty_project, group: group) }
before do
group.add_owner(user)
@@ -36,6 +37,24 @@ describe "Search", feature: true do
expect(find('.js-search-group-dropdown')).to have_content(group.name)
end
+ it 'filters by group projects after filtering by group' do
+ find('.js-search-group-dropdown').click
+ wait_for_ajax
+
+ page.within '.search-holder' do
+ click_link group.name
+ end
+
+ expect(find('.js-search-group-dropdown')).to have_content(group.name)
+
+ page.within('.project-filter') do
+ find('.js-search-project-dropdown').click
+ wait_for_ajax
+
+ expect(page).to have_link(group_project.name_with_namespace)
+ end
+ end
+
it 'shows project name after filtering' do
page.within('.project-filter') do
find('.js-search-project-dropdown').click
diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb
index 10a4597e467..fd097fe2e74 100644
--- a/spec/features/snippets/explore_spec.rb
+++ b/spec/features/snippets/explore_spec.rb
@@ -1,11 +1,11 @@
require 'rails_helper'
feature 'Explore Snippets', feature: true do
- scenario 'User should see snippets that are not private' do
- public_snippet = create(:personal_snippet, :public)
- internal_snippet = create(:personal_snippet, :internal)
- private_snippet = create(:personal_snippet, :private)
+ let!(:public_snippet) { create(:personal_snippet, :public) }
+ let!(:internal_snippet) { create(:personal_snippet, :internal) }
+ let!(:private_snippet) { create(:personal_snippet, :private) }
+ scenario 'User should see snippets that are not private' do
login_as create(:user)
visit explore_snippets_path
@@ -13,4 +13,21 @@ feature 'Explore Snippets', feature: true do
expect(page).to have_content(internal_snippet.title)
expect(page).not_to have_content(private_snippet.title)
end
+
+ scenario 'External user should see only public snippets' do
+ login_as create(:user, :external)
+ visit explore_snippets_path
+
+ expect(page).to have_content(public_snippet.title)
+ expect(page).not_to have_content(internal_snippet.title)
+ expect(page).not_to have_content(private_snippet.title)
+ end
+
+ scenario 'Not authenticated user should see only public snippets' do
+ visit explore_snippets_path
+
+ expect(page).to have_content(public_snippet.title)
+ expect(page).not_to have_content(internal_snippet.title)
+ expect(page).not_to have_content(private_snippet.title)
+ end
end
diff --git a/spec/features/snippets/internal_snippet_spec.rb b/spec/features/snippets/internal_snippet_spec.rb
new file mode 100644
index 00000000000..93382f4c359
--- /dev/null
+++ b/spec/features/snippets/internal_snippet_spec.rb
@@ -0,0 +1,23 @@
+require 'rails_helper'
+
+feature 'Internal Snippets', feature: true, js: true do
+ let(:internal_snippet) { create(:personal_snippet, :internal) }
+
+ describe 'normal user' do
+ before do
+ login_as :user
+ end
+
+ scenario 'sees internal snippets' do
+ visit snippet_path(internal_snippet)
+
+ expect(page).to have_content(internal_snippet.content)
+ end
+
+ scenario 'sees raw internal snippets' do
+ visit raw_snippet_path(internal_snippet)
+
+ expect(page).to have_content(internal_snippet.content)
+ end
+ end
+end
diff --git a/spec/features/users/snippets_spec.rb b/spec/features/users/snippets_spec.rb
index 1546a06b80c..4efbd672322 100644
--- a/spec/features/users/snippets_spec.rb
+++ b/spec/features/users/snippets_spec.rb
@@ -3,14 +3,46 @@ require 'spec_helper'
describe 'Snippets tab on a user profile', feature: true, js: true do
context 'when the user has snippets' do
let(:user) { create(:user) }
- let!(:snippets) { create_list(:snippet, 2, :public, author: user) }
- before do
- allow(Snippet).to receive(:default_per_page).and_return(1)
- visit user_path(user)
- page.within('.user-profile-nav') { click_link 'Snippets' }
- wait_for_ajax
+
+ context 'pagination' do
+ let!(:snippets) { create_list(:snippet, 2, :public, author: user) }
+
+ before do
+ allow(Snippet).to receive(:default_per_page).and_return(1)
+ visit user_path(user)
+ page.within('.user-profile-nav') { click_link 'Snippets' }
+ wait_for_ajax
+ end
+
+ it_behaves_like 'paginated snippets', remote: true
end
- it_behaves_like 'paginated snippets', remote: true
+ context 'list content' do
+ let!(:public_snippet) { create(:snippet, :public, author: user) }
+ let!(:internal_snippet) { create(:snippet, :internal, author: user) }
+ let!(:private_snippet) { create(:snippet, :private, author: user) }
+ let!(:other_snippet) { create(:snippet, :public) }
+
+ it 'contains only internal and public snippets of a user when a user is logged in' do
+ login_as(:user)
+ visit user_path(user)
+ page.within('.user-profile-nav') { click_link 'Snippets' }
+ wait_for_ajax
+
+ expect(page).to have_selector('.snippet-row', count: 2)
+
+ expect(page).to have_content(public_snippet.title)
+ expect(page).to have_content(internal_snippet.title)
+ end
+
+ it 'contains only public snippets of a user when a user is not logged in' do
+ visit user_path(user)
+ page.within('.user-profile-nav') { click_link 'Snippets' }
+ wait_for_ajax
+
+ expect(page).to have_selector('.snippet-row', count: 1)
+ expect(page).to have_content(public_snippet.title)
+ end
+ end
end
end
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
index d5d111e8d15..5b3591550c1 100644
--- a/spec/finders/groups_finder_spec.rb
+++ b/spec/finders/groups_finder_spec.rb
@@ -3,29 +3,64 @@ require 'spec_helper'
describe GroupsFinder do
describe '#execute' do
let(:user) { create(:user) }
- let!(:private_group) { create(:group, :private) }
- let!(:internal_group) { create(:group, :internal) }
- let!(:public_group) { create(:group, :public) }
- let(:finder) { described_class.new }
- describe 'execute' do
- describe 'without a user' do
- subject { finder.execute }
+ context 'root level groups' do
+ let!(:private_group) { create(:group, :private) }
+ let!(:internal_group) { create(:group, :internal) }
+ let!(:public_group) { create(:group, :public) }
+
+ context 'without a user' do
+ subject { described_class.new.execute }
it { is_expected.to eq([public_group]) }
end
- describe 'with a user' do
- subject { finder.execute(user) }
+ context 'with a user' do
+ subject { described_class.new(user).execute }
context 'normal user' do
- it { is_expected.to eq([public_group, internal_group]) }
+ it { is_expected.to contain_exactly(public_group, internal_group) }
end
context 'external user' do
let(:user) { create(:user, external: true) }
- it { is_expected.to eq([public_group]) }
+ it { is_expected.to contain_exactly(public_group) }
+ end
+
+ context 'user is member of the private group' do
+ before do
+ private_group.add_guest(user)
+ end
+
+ it { is_expected.to contain_exactly(public_group, internal_group, private_group) }
+ end
+ end
+ end
+
+ context 'subgroups' do
+ let!(:parent_group) { create(:group, :public) }
+ let!(:public_subgroup) { create(:group, :public, parent: parent_group) }
+ let!(:internal_subgroup) { create(:group, :internal, parent: parent_group) }
+ let!(:private_subgroup) { create(:group, :private, parent: parent_group) }
+
+ context 'without a user' do
+ it 'only returns public subgroups' do
+ expect(described_class.new(nil, parent: parent_group).execute).to contain_exactly(public_subgroup)
+ end
+ end
+
+ context 'with a user' do
+ it 'returns public and internal subgroups' do
+ expect(described_class.new(user, parent: parent_group).execute).to contain_exactly(public_subgroup, internal_subgroup)
+ end
+
+ context 'being member' do
+ it 'returns public subgroups, internal subgroups, and private subgroups user is member of' do
+ private_subgroup.add_guest(user)
+
+ expect(described_class.new(user, parent: parent_group).execute).to contain_exactly(public_subgroup, internal_subgroup, private_subgroup)
+ end
end
end
end
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index cb6c80d1bd0..35f1683eef9 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -8,79 +8,145 @@ describe SnippetsFinder do
let(:project1) { create(:empty_project, :public, group: group) }
let(:project2) { create(:empty_project, :private, group: group) }
- context ':all filter' do
+ context 'all snippets visible to a user' do
let!(:snippet1) { create(:personal_snippet, :private) }
let!(:snippet2) { create(:personal_snippet, :internal) }
let!(:snippet3) { create(:personal_snippet, :public) }
+ let!(:project_snippet1) { create(:project_snippet, :private) }
+ let!(:project_snippet2) { create(:project_snippet, :internal) }
+ let!(:project_snippet3) { create(:project_snippet, :public) }
it "returns all private and internal snippets" do
- snippets = described_class.new.execute(user, filter: :all)
- expect(snippets).to include(snippet2, snippet3)
- expect(snippets).not_to include(snippet1)
+ snippets = described_class.new(user, scope: :all).execute
+ expect(snippets).to include(snippet2, snippet3, project_snippet2, project_snippet3)
+ expect(snippets).not_to include(snippet1, project_snippet1)
end
it "returns all public snippets" do
- snippets = described_class.new.execute(nil, filter: :all)
- expect(snippets).to include(snippet3)
- expect(snippets).not_to include(snippet1, snippet2)
+ snippets = described_class.new(nil, scope: :all).execute
+ expect(snippets).to include(snippet3, project_snippet3)
+ expect(snippets).not_to include(snippet1, snippet2, project_snippet1, project_snippet2)
+ end
+
+ it "returns all public and internal snippets for normal user" do
+ snippets = described_class.new(user).execute
+
+ expect(snippets).to include(snippet2, snippet3, project_snippet2, project_snippet3)
+ expect(snippets).not_to include(snippet1, project_snippet1)
+ end
+
+ it "returns all public snippets for non authorized user" do
+ snippets = described_class.new(nil).execute
+
+ expect(snippets).to include(snippet3, project_snippet3)
+ expect(snippets).not_to include(snippet1, snippet2, project_snippet1, project_snippet2)
+ end
+
+ it "returns all public and authored snippets for external user" do
+ external_user = create(:user, :external)
+ authored_snippet = create(:personal_snippet, :internal, author: external_user)
+
+ snippets = described_class.new(external_user).execute
+
+ expect(snippets).to include(snippet3, project_snippet3, authored_snippet)
+ expect(snippets).not_to include(snippet1, snippet2, project_snippet1, project_snippet2)
end
end
- context ':public filter' do
+ context 'filter by visibility' do
let!(:snippet1) { create(:personal_snippet, :private) }
let!(:snippet2) { create(:personal_snippet, :internal) }
let!(:snippet3) { create(:personal_snippet, :public) }
- it "returns public public snippets" do
- snippets = described_class.new.execute(nil, filter: :public)
+ it "returns public snippets when visibility is PUBLIC" do
+ snippets = described_class.new(nil, visibility: Snippet::PUBLIC).execute
expect(snippets).to include(snippet3)
expect(snippets).not_to include(snippet1, snippet2)
end
end
- context ':by_user filter' do
+ context 'filter by scope' do
+ let!(:snippet1) { create(:personal_snippet, :private, author: user) }
+ let!(:snippet2) { create(:personal_snippet, :internal, author: user) }
+ let!(:snippet3) { create(:personal_snippet, :public, author: user) }
+
+ it "returns all snippets for 'all' scope" do
+ snippets = described_class.new(user, scope: :all).execute
+
+ expect(snippets).to include(snippet1, snippet2, snippet3)
+ end
+
+ it "returns all snippets for 'are_private' scope" do
+ snippets = described_class.new(user, scope: :are_private).execute
+
+ expect(snippets).to include(snippet1)
+ expect(snippets).not_to include(snippet2, snippet3)
+ end
+
+ it "returns all snippets for 'are_interna;' scope" do
+ snippets = described_class.new(user, scope: :are_internal).execute
+
+ expect(snippets).to include(snippet2)
+ expect(snippets).not_to include(snippet1, snippet3)
+ end
+
+ it "returns all snippets for 'are_private' scope" do
+ snippets = described_class.new(user, scope: :are_public).execute
+
+ expect(snippets).to include(snippet3)
+ expect(snippets).not_to include(snippet1, snippet2)
+ end
+ end
+
+ context 'filter by author' do
let!(:snippet1) { create(:personal_snippet, :private, author: user) }
let!(:snippet2) { create(:personal_snippet, :internal, author: user) }
let!(:snippet3) { create(:personal_snippet, :public, author: user) }
it "returns all public and internal snippets" do
- snippets = described_class.new.execute(user1, filter: :by_user, user: user)
+ snippets = described_class.new(user1, author: user).execute
+
expect(snippets).to include(snippet2, snippet3)
expect(snippets).not_to include(snippet1)
end
it "returns internal snippets" do
- snippets = described_class.new.execute(user, filter: :by_user, user: user, scope: "are_internal")
+ snippets = described_class.new(user, author: user, visibility: Snippet::INTERNAL).execute
+
expect(snippets).to include(snippet2)
expect(snippets).not_to include(snippet1, snippet3)
end
it "returns private snippets" do
- snippets = described_class.new.execute(user, filter: :by_user, user: user, scope: "are_private")
+ snippets = described_class.new(user, author: user, visibility: Snippet::PRIVATE).execute
+
expect(snippets).to include(snippet1)
expect(snippets).not_to include(snippet2, snippet3)
end
it "returns public snippets" do
- snippets = described_class.new.execute(user, filter: :by_user, user: user, scope: "are_public")
+ snippets = described_class.new(user, author: user, visibility: Snippet::PUBLIC).execute
+
expect(snippets).to include(snippet3)
expect(snippets).not_to include(snippet1, snippet2)
end
it "returns all snippets" do
- snippets = described_class.new.execute(user, filter: :by_user, user: user)
+ snippets = described_class.new(user, author: user).execute
+
expect(snippets).to include(snippet1, snippet2, snippet3)
end
it "returns only public snippets if unauthenticated user" do
- snippets = described_class.new.execute(nil, filter: :by_user, user: user)
+ snippets = described_class.new(nil, author: user).execute
+
expect(snippets).to include(snippet3)
expect(snippets).not_to include(snippet2, snippet1)
end
end
- context 'by_project filter' do
+ context 'filter by project' do
before do
@snippet1 = create(:project_snippet, :private, project: project1)
@snippet2 = create(:project_snippet, :internal, project: project1)
@@ -88,43 +154,52 @@ describe SnippetsFinder do
end
it "returns public snippets for unauthorized user" do
- snippets = described_class.new.execute(nil, filter: :by_project, project: project1)
+ snippets = described_class.new(nil, project: project1).execute
+
expect(snippets).to include(@snippet3)
expect(snippets).not_to include(@snippet1, @snippet2)
end
it "returns public and internal snippets for non project members" do
- snippets = described_class.new.execute(user, filter: :by_project, project: project1)
+ snippets = described_class.new(user, project: project1).execute
+
expect(snippets).to include(@snippet2, @snippet3)
expect(snippets).not_to include(@snippet1)
end
it "returns public snippets for non project members" do
- snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_public")
+ snippets = described_class.new(user, project: project1, visibility: Snippet::PUBLIC).execute
+
expect(snippets).to include(@snippet3)
expect(snippets).not_to include(@snippet1, @snippet2)
end
it "returns internal snippets for non project members" do
- snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_internal")
+ snippets = described_class.new(user, project: project1, visibility: Snippet::INTERNAL).execute
+
expect(snippets).to include(@snippet2)
expect(snippets).not_to include(@snippet1, @snippet3)
end
it "does not return private snippets for non project members" do
- snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_private")
+ snippets = described_class.new(user, project: project1, visibility: Snippet::PRIVATE).execute
+
expect(snippets).not_to include(@snippet1, @snippet2, @snippet3)
end
it "returns all snippets for project members" do
project1.team << [user, :developer]
- snippets = described_class.new.execute(user, filter: :by_project, project: project1)
+
+ snippets = described_class.new(user, project: project1).execute
+
expect(snippets).to include(@snippet1, @snippet2, @snippet3)
end
it "returns private snippets for project members" do
project1.team << [user, :developer]
- snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_private")
+
+ snippets = described_class.new(user, project: project1, visibility: Snippet::PRIVATE).execute
+
expect(snippets).to include(@snippet1)
end
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 01bdf01ad22..785fb724132 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -3,6 +3,8 @@ require 'spec_helper'
describe ApplicationHelper do
include UploadHelpers
+ let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
+
describe 'current_controller?' do
it 'returns true when controller matches argument' do
stub_controller_name('foo')
@@ -56,8 +58,14 @@ describe ApplicationHelper do
describe 'project_icon' do
it 'returns an url for the avatar' do
project = create(:empty_project, avatar: File.open(uploaded_image_temp_path))
+ avatar_url = "/uploads/project/avatar/#{project.id}/banana_sample.gif"
+
+ expect(helper.project_icon(project.full_path).to_s).
+ to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
+
+ allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
+ avatar_url = "#{gitlab_host}/uploads/project/avatar/#{project.id}/banana_sample.gif"
- avatar_url = "http://#{Gitlab.config.gitlab.host}/uploads/project/avatar/#{project.id}/banana_sample.gif"
expect(helper.project_icon(project.full_path).to_s).
to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
end
@@ -67,9 +75,8 @@ describe ApplicationHelper do
allow_any_instance_of(Project).to receive(:avatar_in_git).and_return(true)
- avatar_url = "http://#{Gitlab.config.gitlab.host}#{namespace_project_avatar_path(project.namespace, project)}"
- expect(helper.project_icon(project.full_path).to_s).to match(
- image_tag(avatar_url))
+ avatar_url = "#{gitlab_host}#{namespace_project_avatar_path(project.namespace, project)}"
+ expect(helper.project_icon(project.full_path).to_s).to match(image_tag(avatar_url))
end
end
@@ -77,8 +84,14 @@ describe ApplicationHelper do
it 'returns an url for the avatar' do
user = create(:user, avatar: File.open(uploaded_image_temp_path))
- expect(helper.avatar_icon(user.email).to_s).
- to match("/uploads/user/avatar/#{user.id}/banana_sample.gif")
+ avatar_url = "/uploads/user/avatar/#{user.id}/banana_sample.gif"
+
+ expect(helper.avatar_icon(user.email).to_s).to match(avatar_url)
+
+ allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
+ avatar_url = "#{gitlab_host}/uploads/user/avatar/#{user.id}/banana_sample.gif"
+
+ expect(helper.avatar_icon(user.email).to_s).to match(avatar_url)
end
it 'returns an url for the avatar with relative url' do
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
index 581726c1d0e..6157abfe339 100644
--- a/spec/helpers/avatars_helper_spec.rb
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -15,7 +15,7 @@ describe AvatarsHelper do
end
it "contains the user's avatar image" do
- is_expected.to include(CGI.escapeHTML(user.avatar_url(16)))
+ is_expected.to include(CGI.escapeHTML(user.avatar_url(size: 16)))
end
end
end
diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb
index 345bc33a67b..9da33792659 100644
--- a/spec/helpers/submodule_helper_spec.rb
+++ b/spec/helpers/submodule_helper_spec.rb
@@ -109,6 +109,18 @@ describe SubmoduleHelper do
end
context 'submodule on unsupported' do
+ it 'sanitizes unsupported protocols' do
+ stub_url('javascript:alert("XSS");')
+
+ expect(helper.submodule_links(submodule_item)).to eq([nil, nil])
+ end
+
+ it 'sanitizes unsupported protocols disguised as a repository URL' do
+ stub_url('javascript:alert("XSS");foo/bar.git')
+
+ expect(helper.submodule_links(submodule_item)).to eq([nil, nil])
+ end
+
it 'returns original' do
stub_url('http://mygitserver.com/gitlab-org/gitlab-ce')
expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil])
diff --git a/spec/javascripts/blob/create_branch_dropdown_spec.js b/spec/javascripts/blob/create_branch_dropdown_spec.js
index c1179e572ae..9f0d373cb81 100644
--- a/spec/javascripts/blob/create_branch_dropdown_spec.js
+++ b/spec/javascripts/blob/create_branch_dropdown_spec.js
@@ -1,5 +1,4 @@
require('~/gl_dropdown');
-require('~/lib/utils/type_utility');
require('~/blob/create_branch_dropdown');
require('~/blob/target_branch_dropdown');
diff --git a/spec/javascripts/blob/target_branch_dropdown_spec.js b/spec/javascripts/blob/target_branch_dropdown_spec.js
index bb436978a0f..76ed3dc1a2d 100644
--- a/spec/javascripts/blob/target_branch_dropdown_spec.js
+++ b/spec/javascripts/blob/target_branch_dropdown_spec.js
@@ -1,5 +1,4 @@
require('~/gl_dropdown');
-require('~/lib/utils/type_utility');
require('~/blob/create_branch_dropdown');
require('~/blob/target_branch_dropdown');
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index c207fb00a47..8f90ed69e64 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -2,7 +2,6 @@
require('~/gl_dropdown');
require('~/lib/utils/common_utils');
-require('~/lib/utils/type_utility');
require('~/lib/utils/url_utility');
(() => {
@@ -44,21 +43,18 @@ require('~/lib/utils/url_utility');
preloadFixtures('static/gl_dropdown.html.raw');
loadJSONFixtures('projects.json');
- function initDropDown(hasRemote, isFilterable) {
- this.dropdownButtonElement = $('#js-project-dropdown', this.dropdownContainerElement).glDropdown({
+ function initDropDown(hasRemote, isFilterable, extraOpts = {}) {
+ const options = Object.assign({
selectable: true,
filterable: isFilterable,
data: hasRemote ? remoteMock.bind({}, this.projectsData) : this.projectsData,
search: {
fields: ['name']
},
- text: (project) => {
- (project.name_with_namespace || project.name);
- },
- id: (project) => {
- project.id;
- }
- });
+ text: project => (project.name_with_namespace || project.name),
+ id: project => project.id,
+ }, extraOpts);
+ this.dropdownButtonElement = $('#js-project-dropdown', this.dropdownContainerElement).glDropdown(options);
}
beforeEach(() => {
@@ -80,6 +76,37 @@ require('~/lib/utils/url_utility');
expect(this.dropdownContainerElement).toHaveClass('open');
});
+ it('escapes HTML as text', () => {
+ this.projectsData[0].name_with_namespace = '<script>alert("testing");</script>';
+
+ initDropDown.call(this, false);
+
+ this.dropdownButtonElement.click();
+
+ expect(
+ $('.dropdown-content li:first-child').text(),
+ ).toBe('<script>alert("testing");</script>');
+ });
+
+ it('should output HTML when highlighting', () => {
+ this.projectsData[0].name_with_namespace = 'testing';
+ $('.dropdown-input .dropdown-input-field').val('test');
+
+ initDropDown.call(this, false, true, {
+ highlight: true,
+ });
+
+ this.dropdownButtonElement.click();
+
+ expect(
+ $('.dropdown-content li:first-child').text(),
+ ).toBe('testing');
+
+ expect(
+ $('.dropdown-content li:first-child a').html(),
+ ).toBe('<b>t</b><b>e</b><b>s</b><b>t</b>ing');
+ });
+
describe('that is open', () => {
beforeEach(() => {
initDropDown.call(this, false, false);
diff --git a/spec/javascripts/labels_issue_sidebar_spec.js b/spec/javascripts/labels_issue_sidebar_spec.js
index 37e038c16da..53aba191b19 100644
--- a/spec/javascripts/labels_issue_sidebar_spec.js
+++ b/spec/javascripts/labels_issue_sidebar_spec.js
@@ -2,7 +2,6 @@
/* global IssuableContext */
/* global LabelsSelect */
-require('~/lib/utils/type_utility');
require('~/gl_dropdown');
require('select2');
require('vendor/jquery.nicescroll');
diff --git a/spec/javascripts/pipelines/graph/graph_component_spec.js b/spec/javascripts/pipelines/graph/graph_component_spec.js
index a756617e65e..6bd0eb86263 100644
--- a/spec/javascripts/pipelines/graph/graph_component_spec.js
+++ b/spec/javascripts/pipelines/graph/graph_component_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import graphComponent from '~/pipelines/components/graph/graph_component.vue';
+import graphJSON from './mock_data';
describe('graph component', () => {
preloadFixtures('static/graph.html.raw');
@@ -20,41 +21,7 @@ describe('graph component', () => {
describe('with a successfull response', () => {
const interceptor = (request, next) => {
- next(request.respondWith(JSON.stringify({
- details: {
- stages: [{
- name: 'test',
- title: 'test: passed',
- status: {
- icon: 'icon_status_success',
- text: 'passed',
- label: 'passed',
- details_path: '/root/ci-mock/pipelines/123#test',
- },
- path: '/root/ci-mock/pipelines/123#test',
- groups: [{
- name: 'test',
- size: 1,
- jobs: [{
- id: 4153,
- name: 'test',
- status: {
- icon: 'icon_status_success',
- text: 'passed',
- label: 'passed',
- details_path: '/root/ci-mock/builds/4153',
- action: {
- icon: 'icon_action_retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4153/retry',
- method: 'post',
- },
- },
- }],
- }],
- }],
- },
- }), {
+ next(request.respondWith(JSON.stringify(graphJSON), {
status: 200,
}));
};
@@ -73,6 +40,18 @@ describe('graph component', () => {
setTimeout(() => {
expect(component.$el.classList.contains('js-pipeline-graph')).toEqual(true);
+ expect(
+ component.$el.querySelector('.stage-column:first-child').classList.contains('no-margin'),
+ ).toEqual(true);
+
+ expect(
+ component.$el.querySelector('.stage-column:nth-child(2)').classList.contains('left-margin'),
+ ).toEqual(true);
+
+ expect(
+ component.$el.querySelector('.stage-column:nth-child(2) .build:nth-child(1)').classList.contains('left-connector'),
+ ).toEqual(true);
+
expect(component.$el.querySelector('loading-icon')).toBe(null);
expect(component.$el.querySelector('.stage-column-list')).toBeDefined();
diff --git a/spec/javascripts/pipelines/graph/mock_data.js b/spec/javascripts/pipelines/graph/mock_data.js
new file mode 100644
index 00000000000..56c522b7f77
--- /dev/null
+++ b/spec/javascripts/pipelines/graph/mock_data.js
@@ -0,0 +1,232 @@
+/* eslint-disable quote-props, quotes, comma-dangle */
+export default {
+ "id": 123,
+ "user": {
+ "name": "Root",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": null,
+ "web_url": "http://localhost:3000/root"
+ },
+ "active": false,
+ "coverage": null,
+ "path": "/root/ci-mock/pipelines/123",
+ "details": {
+ "status": {
+ "icon": "icon_status_success",
+ "text": "passed",
+ "label": "passed",
+ "group": "success",
+ "has_details": true,
+ "details_path": "/root/ci-mock/pipelines/123",
+ "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico"
+ },
+ "duration": 9,
+ "finished_at": "2017-04-19T14:30:27.542Z",
+ "stages": [{
+ "name": "test",
+ "title": "test: passed",
+ "groups": [{
+ "name": "test",
+ "size": 1,
+ "status": {
+ "icon": "icon_status_success",
+ "text": "passed",
+ "label": "passed",
+ "group": "success",
+ "has_details": true,
+ "details_path": "/root/ci-mock/builds/4153",
+ "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
+ "action": {
+ "icon": "icon_action_retry",
+ "title": "Retry",
+ "path": "/root/ci-mock/builds/4153/retry",
+ "method": "post"
+ }
+ },
+ "jobs": [{
+ "id": 4153,
+ "name": "test",
+ "build_path": "/root/ci-mock/builds/4153",
+ "retry_path": "/root/ci-mock/builds/4153/retry",
+ "playable": false,
+ "created_at": "2017-04-13T09:25:18.959Z",
+ "updated_at": "2017-04-13T09:25:23.118Z",
+ "status": {
+ "icon": "icon_status_success",
+ "text": "passed",
+ "label": "passed",
+ "group": "success",
+ "has_details": true,
+ "details_path": "/root/ci-mock/builds/4153",
+ "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
+ "action": {
+ "icon": "icon_action_retry",
+ "title": "Retry",
+ "path": "/root/ci-mock/builds/4153/retry",
+ "method": "post"
+ }
+ }
+ }]
+ }],
+ "status": {
+ "icon": "icon_status_success",
+ "text": "passed",
+ "label": "passed",
+ "group": "success",
+ "has_details": true,
+ "details_path": "/root/ci-mock/pipelines/123#test",
+ "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico"
+ },
+ "path": "/root/ci-mock/pipelines/123#test",
+ "dropdown_path": "/root/ci-mock/pipelines/123/stage.json?stage=test"
+ }, {
+ "name": "deploy",
+ "title": "deploy: passed",
+ "groups": [{
+ "name": "deploy to production",
+ "size": 1,
+ "status": {
+ "icon": "icon_status_success",
+ "text": "passed",
+ "label": "passed",
+ "group": "success",
+ "has_details": true,
+ "details_path": "/root/ci-mock/builds/4166",
+ "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
+ "action": {
+ "icon": "icon_action_retry",
+ "title": "Retry",
+ "path": "/root/ci-mock/builds/4166/retry",
+ "method": "post"
+ }
+ },
+ "jobs": [{
+ "id": 4166,
+ "name": "deploy to production",
+ "build_path": "/root/ci-mock/builds/4166",
+ "retry_path": "/root/ci-mock/builds/4166/retry",
+ "playable": false,
+ "created_at": "2017-04-19T14:29:46.463Z",
+ "updated_at": "2017-04-19T14:30:27.498Z",
+ "status": {
+ "icon": "icon_status_success",
+ "text": "passed",
+ "label": "passed",
+ "group": "success",
+ "has_details": true,
+ "details_path": "/root/ci-mock/builds/4166",
+ "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
+ "action": {
+ "icon": "icon_action_retry",
+ "title": "Retry",
+ "path": "/root/ci-mock/builds/4166/retry",
+ "method": "post"
+ }
+ }
+ }]
+ }, {
+ "name": "deploy to staging",
+ "size": 1,
+ "status": {
+ "icon": "icon_status_success",
+ "text": "passed",
+ "label": "passed",
+ "group": "success",
+ "has_details": true,
+ "details_path": "/root/ci-mock/builds/4159",
+ "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
+ "action": {
+ "icon": "icon_action_retry",
+ "title": "Retry",
+ "path": "/root/ci-mock/builds/4159/retry",
+ "method": "post"
+ }
+ },
+ "jobs": [{
+ "id": 4159,
+ "name": "deploy to staging",
+ "build_path": "/root/ci-mock/builds/4159",
+ "retry_path": "/root/ci-mock/builds/4159/retry",
+ "playable": false,
+ "created_at": "2017-04-18T16:32:08.420Z",
+ "updated_at": "2017-04-18T16:32:12.631Z",
+ "status": {
+ "icon": "icon_status_success",
+ "text": "passed",
+ "label": "passed",
+ "group": "success",
+ "has_details": true,
+ "details_path": "/root/ci-mock/builds/4159",
+ "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
+ "action": {
+ "icon": "icon_action_retry",
+ "title": "Retry",
+ "path": "/root/ci-mock/builds/4159/retry",
+ "method": "post"
+ }
+ }
+ }]
+ }],
+ "status": {
+ "icon": "icon_status_success",
+ "text": "passed",
+ "label": "passed",
+ "group": "success",
+ "has_details": true,
+ "details_path": "/root/ci-mock/pipelines/123#deploy",
+ "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico"
+ },
+ "path": "/root/ci-mock/pipelines/123#deploy",
+ "dropdown_path": "/root/ci-mock/pipelines/123/stage.json?stage=deploy"
+ }],
+ "artifacts": [],
+ "manual_actions": [{
+ "name": "deploy to production",
+ "path": "/root/ci-mock/builds/4166/play",
+ "playable": false
+ }]
+ },
+ "flags": {
+ "latest": true,
+ "triggered": false,
+ "stuck": false,
+ "yaml_errors": false,
+ "retryable": false,
+ "cancelable": false
+ },
+ "ref": {
+ "name": "master",
+ "path": "/root/ci-mock/tree/master",
+ "tag": false,
+ "branch": true
+ },
+ "commit": {
+ "id": "798e5f902592192afaba73f4668ae30e56eae492",
+ "short_id": "798e5f90",
+ "title": "Merge branch 'new-branch' into 'master'\r",
+ "created_at": "2017-04-13T10:25:17.000+01:00",
+ "parent_ids": ["54d483b1ed156fbbf618886ddf7ab023e24f8738", "c8e2d38a6c538822e81c57022a6e3a0cfedebbcc"],
+ "message": "Merge branch 'new-branch' into 'master'\r\n\r\nAdd new file\r\n\r\nSee merge request !1",
+ "author_name": "Root",
+ "author_email": "admin@example.com",
+ "authored_date": "2017-04-13T10:25:17.000+01:00",
+ "committer_name": "Root",
+ "committer_email": "admin@example.com",
+ "committed_date": "2017-04-13T10:25:17.000+01:00",
+ "author": {
+ "name": "Root",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": null,
+ "web_url": "http://localhost:3000/root"
+ },
+ "author_gravatar_url": null,
+ "commit_url": "http://localhost:3000/root/ci-mock/commit/798e5f902592192afaba73f4668ae30e56eae492",
+ "commit_path": "/root/ci-mock/commit/798e5f902592192afaba73f4668ae30e56eae492"
+ },
+ "created_at": "2017-04-13T09:25:18.881Z",
+ "updated_at": "2017-04-19T14:30:27.561Z"
+};
diff --git a/spec/javascripts/project_title_spec.js b/spec/javascripts/project_title_spec.js
index 3a1d4e2440f..5c51e855401 100644
--- a/spec/javascripts/project_title_spec.js
+++ b/spec/javascripts/project_title_spec.js
@@ -2,7 +2,6 @@
/* global Project */
require('select2/select2.js');
-require('~/lib/utils/type_utility');
require('~/gl_dropdown');
require('~/api');
require('~/project_select');
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index aaf058bd755..fa52a8a0dd2 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -3,7 +3,6 @@
require('~/gl_dropdown');
require('~/search_autocomplete');
require('~/lib/utils/common_utils');
-require('~/lib/utils/type_utility');
require('vendor/fuzzaldrin-plus');
(function() {
diff --git a/spec/javascripts/u2f/mock_u2f_device.js b/spec/javascripts/u2f/mock_u2f_device.js
index 6677fe9c1ee..4eb8ad3d9e4 100644
--- a/spec/javascripts/u2f/mock_u2f_device.js
+++ b/spec/javascripts/u2f/mock_u2f_device.js
@@ -1,12 +1,10 @@
/* eslint-disable space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-expressions, no-return-assign, no-param-reassign, max-len */
(function() {
- var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
-
this.MockU2FDevice = (function() {
function MockU2FDevice() {
- this.respondToAuthenticateRequest = bind(this.respondToAuthenticateRequest, this);
- this.respondToRegisterRequest = bind(this.respondToRegisterRequest, this);
+ this.respondToAuthenticateRequest = this.respondToAuthenticateRequest.bind(this);
+ this.respondToRegisterRequest = this.respondToRegisterRequest.bind(this);
window.u2f || (window.u2f = {});
window.u2f.register = (function(_this) {
return function(appId, registerRequests, signRequests, callback) {
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
index 3d5f71babfb..2f971b39d16 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
@@ -9,6 +9,7 @@ const deploymentMockData = [
name: 'review/diplo',
url: '/root/acets-review-apps/environments/15',
stop_url: '/root/acets-review-apps/environments/15/stop',
+ metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
external_url: 'http://diplo.',
external_url_formatted: 'diplo.',
deployed_at: '2017-03-22T22:44:42.258Z',
@@ -156,6 +157,7 @@ describe('MRWidgetDeployment', () => {
expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(deployment.external_url);
expect(el.querySelector('.js-deploy-url').innerText).toContain(deployment.external_url_formatted);
expect(el.querySelector('.js-deploy-time').innerText).toContain(vm.formatDate(deployment.deployed_at));
+ expect(el.querySelector('.js-mr-memory-usage')).toBeDefined();
expect(el.querySelector('button')).toBeDefined();
});
@@ -165,6 +167,7 @@ describe('MRWidgetDeployment', () => {
Vue.nextTick(() => {
expect(el.querySelectorAll('.ci-widget').length).toEqual(3);
+ expect(el.querySelectorAll('.js-mr-memory-usage').length).toEqual(3);
done();
});
});
@@ -176,6 +179,7 @@ describe('MRWidgetDeployment', () => {
expect(el.querySelectorAll('.js-deploy-meta').length).toEqual(0);
expect(el.querySelectorAll('.js-deploy-url').length).toEqual(0);
expect(el.querySelectorAll('.js-deploy-time').length).toEqual(0);
+ expect(el.querySelectorAll('.js-mr-memory-usage').length).toEqual(0);
expect(el.querySelectorAll('.button').length).toEqual(0);
done();
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
new file mode 100644
index 00000000000..da9dff18ada
--- /dev/null
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
@@ -0,0 +1,184 @@
+import Vue from 'vue';
+import memoryUsageComponent from '~/vue_merge_request_widget/components/mr_widget_memory_usage';
+import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
+
+const url = '/root/acets-review-apps/environments/15/deployments/1/metrics';
+
+const metricsMockData = {
+ success: true,
+ metrics: {
+ memory_values: [
+ {
+ metric: {},
+ values: [
+ [1493716685, '4.30859375'],
+ ],
+ },
+ ],
+ },
+ last_update: '2017-05-02T12:34:49.628Z',
+ deployment_time: 1493718485,
+};
+
+const createComponent = () => {
+ const Component = Vue.extend(memoryUsageComponent);
+
+ return new Component({
+ el: document.createElement('div'),
+ propsData: {
+ metricsUrl: url,
+ memoryMetrics: [],
+ deploymentTime: 0,
+ hasMetrics: false,
+ loadFailed: false,
+ loadingMetrics: true,
+ backOffRequestCounter: 0,
+ },
+ });
+};
+
+const messages = {
+ loadingMetrics: 'Loading deployment statistics.',
+ hasMetrics: 'Deployment memory usage:',
+ loadFailed: 'Failed to load deployment statistics.',
+ metricsUnavailable: 'Deployment statistics are not available currently.',
+};
+
+describe('MemoryUsage', () => {
+ let vm;
+ let el;
+
+ beforeEach(() => {
+ vm = createComponent();
+ el = vm.$el;
+ });
+
+ describe('props', () => {
+ it('should have props with defaults', () => {
+ const { metricsUrl } = memoryUsageComponent.props;
+ const MetricsUrlTypeClass = metricsUrl.type;
+
+ Vue.nextTick(() => {
+ expect(new MetricsUrlTypeClass() instanceof String).toBeTruthy();
+ expect(metricsUrl.required).toBeTruthy();
+ });
+ });
+ });
+
+ describe('data', () => {
+ it('should have default data', () => {
+ const data = memoryUsageComponent.data();
+
+ expect(Array.isArray(data.memoryMetrics)).toBeTruthy();
+ expect(data.memoryMetrics.length).toBe(0);
+
+ expect(typeof data.deploymentTime).toBe('number');
+ expect(data.deploymentTime).toBe(0);
+
+ expect(typeof data.hasMetrics).toBe('boolean');
+ expect(data.hasMetrics).toBeFalsy();
+
+ expect(typeof data.loadFailed).toBe('boolean');
+ expect(data.loadFailed).toBeFalsy();
+
+ expect(typeof data.loadingMetrics).toBe('boolean');
+ expect(data.loadingMetrics).toBeTruthy();
+
+ expect(typeof data.backOffRequestCounter).toBe('number');
+ expect(data.backOffRequestCounter).toBe(0);
+ });
+ });
+
+ describe('methods', () => {
+ const { metrics, deployment_time } = metricsMockData;
+
+ describe('computeGraphData', () => {
+ it('should populate sparkline graph', () => {
+ vm.computeGraphData(metrics, deployment_time);
+ const { hasMetrics, memoryMetrics, deploymentTime } = vm;
+
+ expect(hasMetrics).toBeTruthy();
+ expect(memoryMetrics.length > 0).toBeTruthy();
+ expect(deploymentTime).toEqual(deployment_time);
+ });
+ });
+
+ describe('loadMetrics', () => {
+ const returnServicePromise = () => new Promise((resolve) => {
+ resolve({
+ json() {
+ return metricsMockData;
+ },
+ });
+ });
+
+ it('should load metrics data using MRWidgetService', (done) => {
+ spyOn(MRWidgetService, 'fetchMetrics').and.returnValue(returnServicePromise(true));
+ spyOn(vm, 'computeGraphData');
+
+ vm.loadMetrics();
+ setTimeout(() => {
+ expect(MRWidgetService.fetchMetrics).toHaveBeenCalledWith(url);
+ expect(vm.computeGraphData).toHaveBeenCalledWith(metrics, deployment_time);
+ done();
+ }, 333);
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should render template elements correctly', () => {
+ expect(el.classList.contains('mr-memory-usage')).toBeTruthy();
+ expect(el.querySelector('.js-usage-info')).toBeDefined();
+ });
+
+ it('should show loading metrics message while metrics are being loaded', (done) => {
+ vm.loadingMetrics = true;
+ vm.hasMetrics = false;
+ vm.loadFailed = false;
+
+ Vue.nextTick(() => {
+ expect(el.querySelector('.js-usage-info.usage-info-loading')).toBeDefined();
+ expect(el.querySelector('.js-usage-info .usage-info-load-spinner')).toBeDefined();
+ expect(el.querySelector('.js-usage-info').innerText).toContain(messages.loadingMetrics);
+ done();
+ });
+ });
+
+ it('should show deployment memory usage when metrics are loaded', (done) => {
+ vm.loadingMetrics = false;
+ vm.hasMetrics = true;
+ vm.loadFailed = false;
+
+ Vue.nextTick(() => {
+ expect(el.querySelector('.memory-graph-container')).toBeDefined();
+ expect(el.querySelector('.js-usage-info').innerText).toContain(messages.hasMetrics);
+ done();
+ });
+ });
+
+ it('should show failure message when metrics loading failed', (done) => {
+ vm.loadingMetrics = false;
+ vm.hasMetrics = false;
+ vm.loadFailed = true;
+
+ Vue.nextTick(() => {
+ expect(el.querySelector('.js-usage-info.usage-info-failed')).toBeDefined();
+ expect(el.querySelector('.js-usage-info').innerText).toContain(messages.loadFailed);
+ done();
+ });
+ });
+
+ it('should show metrics unavailable message when metrics loading failed', (done) => {
+ vm.loadingMetrics = false;
+ vm.hasMetrics = false;
+ vm.loadFailed = false;
+
+ Vue.nextTick(() => {
+ expect(el.querySelector('.js-usage-info.usage-info-unavailable')).toBeDefined();
+ expect(el.querySelector('.js-usage-info').innerText).toContain(messages.metricsUnavailable);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/ci_action_icons_spec.js b/spec/javascripts/vue_shared/ci_action_icons_spec.js
index 2e89a07e76e..3d53a5ab24d 100644
--- a/spec/javascripts/vue_shared/ci_action_icons_spec.js
+++ b/spec/javascripts/vue_shared/ci_action_icons_spec.js
@@ -2,6 +2,7 @@ import getActionIcon from '~/vue_shared/ci_action_icons';
import cancelSVG from 'icons/_icon_action_cancel.svg';
import retrySVG from 'icons/_icon_action_retry.svg';
import playSVG from 'icons/_icon_action_play.svg';
+import stopSVG from 'icons/_icon_action_stop.svg';
describe('getActionIcon', () => {
it('should return an empty string', () => {
@@ -19,4 +20,8 @@ describe('getActionIcon', () => {
it('should return play svg', () => {
expect(getActionIcon('icon_action_play')).toEqual(playSVG);
});
+
+ it('should render stop svg', () => {
+ expect(getActionIcon('icon_action_stop')).toEqual(stopSVG);
+ });
});
diff --git a/spec/javascripts/vue_shared/components/loading_icon_spec.js b/spec/javascripts/vue_shared/components/loading_icon_spec.js
new file mode 100644
index 00000000000..1baf3537741
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/loading_icon_spec.js
@@ -0,0 +1,53 @@
+import Vue from 'vue';
+import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+
+describe('Loading Icon Component', () => {
+ let LoadingIconComponent;
+
+ beforeEach(() => {
+ LoadingIconComponent = Vue.extend(loadingIcon);
+ });
+
+ it('should render a spinner font awesome icon', () => {
+ const component = new LoadingIconComponent().$mount();
+
+ expect(
+ component.$el.querySelector('i').getAttribute('class'),
+ ).toEqual('fa fa-spin fa-spinner fa-1x');
+
+ expect(component.$el.tagName).toEqual('DIV');
+ expect(component.$el.classList.contains('text-center')).toEqual(true);
+ });
+
+ it('should render accessibility attributes', () => {
+ const component = new LoadingIconComponent().$mount();
+
+ const icon = component.$el.querySelector('i');
+ expect(icon.getAttribute('aria-hidden')).toEqual('true');
+ expect(icon.getAttribute('aria-label')).toEqual('Loading');
+ });
+
+ it('should render the provided label', () => {
+ const component = new LoadingIconComponent({
+ propsData: {
+ label: 'This is a loading icon',
+ },
+ }).$mount();
+
+ expect(
+ component.$el.querySelector('i').getAttribute('aria-label'),
+ ).toEqual('This is a loading icon');
+ });
+
+ it('should render the provided size', () => {
+ const component = new LoadingIconComponent({
+ propsData: {
+ size: '2',
+ },
+ }).$mount();
+
+ expect(
+ component.$el.querySelector('i').classList.contains('fa-2x'),
+ ).toEqual(true);
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/memory_graph_spec.js b/spec/javascripts/vue_shared/components/memory_graph_spec.js
new file mode 100644
index 00000000000..d46a3f2328e
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/memory_graph_spec.js
@@ -0,0 +1,143 @@
+import Vue from 'vue';
+import memoryGraphComponent from '~/vue_shared/components/memory_graph';
+import { mockMetrics, mockMedian, mockMedianIndex } from './mock_data';
+
+const defaultHeight = '25';
+const defaultWidth = '100';
+
+const createComponent = () => {
+ const Component = Vue.extend(memoryGraphComponent);
+
+ return new Component({
+ el: document.createElement('div'),
+ propsData: {
+ metrics: [],
+ deploymentTime: 0,
+ width: '',
+ height: '',
+ pathD: '',
+ pathViewBox: '',
+ dotX: '',
+ dotY: '',
+ },
+ });
+};
+
+describe('MemoryGraph', () => {
+ let vm;
+ let el;
+
+ beforeEach(() => {
+ vm = createComponent();
+ el = vm.$el;
+ });
+
+ describe('props', () => {
+ it('should have props with defaults', (done) => {
+ const { metrics, deploymentTime, width, height } = memoryGraphComponent.props;
+
+ Vue.nextTick(() => {
+ const typeClassMatcher = (propItem, expectedType) => {
+ const PropItemTypeClass = propItem.type;
+ expect(new PropItemTypeClass() instanceof expectedType).toBeTruthy();
+ expect(propItem.required).toBeTruthy();
+ };
+
+ typeClassMatcher(metrics, Array);
+ typeClassMatcher(deploymentTime, Number);
+ typeClassMatcher(width, String);
+ typeClassMatcher(height, String);
+ done();
+ });
+ });
+ });
+
+ describe('data', () => {
+ it('should have default data', () => {
+ const data = memoryGraphComponent.data();
+ const dataValidator = (dataItem, expectedType, defaultVal) => {
+ expect(typeof dataItem).toBe(expectedType);
+ expect(dataItem).toBe(defaultVal);
+ };
+
+ dataValidator(data.pathD, 'string', '');
+ dataValidator(data.pathViewBox, 'string', '');
+ dataValidator(data.dotX, 'string', '');
+ dataValidator(data.dotY, 'string', '');
+ });
+ });
+
+ describe('computed', () => {
+ describe('getFormattedMedian', () => {
+ it('should show human readable median value based on provided median timestamp', () => {
+ vm.deploymentTime = mockMedian;
+ const formattedMedian = vm.getFormattedMedian;
+ expect(formattedMedian.indexOf('Deployed') > -1).toBeTruthy();
+ expect(formattedMedian.indexOf('ago') > -1).toBeTruthy();
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('getMedianMetricIndex', () => {
+ it('should return index of closest metric timestamp to that of median', () => {
+ const matchingIndex = vm.getMedianMetricIndex(mockMedian, mockMetrics);
+ expect(matchingIndex).toBe(mockMedianIndex);
+ });
+ });
+
+ describe('getGraphPlotValues', () => {
+ it('should return Object containing values to plot graph', () => {
+ const plotValues = vm.getGraphPlotValues(mockMedian, mockMetrics);
+ expect(plotValues.pathD).toBeDefined();
+ expect(Array.isArray(plotValues.pathD)).toBeTruthy();
+
+ expect(plotValues.pathViewBox).toBeDefined();
+ expect(typeof plotValues.pathViewBox).toBe('object');
+
+ expect(plotValues.dotX).toBeDefined();
+ expect(typeof plotValues.dotX).toBe('number');
+
+ expect(plotValues.dotY).toBeDefined();
+ expect(typeof plotValues.dotY).toBe('number');
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should render template elements correctly', () => {
+ expect(el.classList.contains('memory-graph-container')).toBeTruthy();
+ expect(el.querySelector('svg')).toBeDefined();
+ });
+
+ it('should render graph when renderGraph is called internally', (done) => {
+ const { pathD, pathViewBox, dotX, dotY } = vm.getGraphPlotValues(mockMedian, mockMetrics);
+ vm.height = defaultHeight;
+ vm.width = defaultWidth;
+ vm.pathD = `M ${pathD}`;
+ vm.pathViewBox = `0 0 ${pathViewBox.lineWidth} ${pathViewBox.diff}`;
+ vm.dotX = dotX;
+ vm.dotY = dotY;
+
+ Vue.nextTick(() => {
+ const svgEl = el.querySelector('svg');
+ expect(svgEl).toBeDefined();
+ expect(svgEl.getAttribute('height')).toBe(defaultHeight);
+ expect(svgEl.getAttribute('width')).toBe(defaultWidth);
+
+ const pathEl = el.querySelector('path');
+ expect(pathEl).toBeDefined();
+ expect(pathEl.getAttribute('d')).toBe(`M ${pathD}`);
+ expect(pathEl.getAttribute('viewBox')).toBe(`0 0 ${pathViewBox.lineWidth} ${pathViewBox.diff}`);
+
+ const circleEl = el.querySelector('circle');
+ expect(circleEl).toBeDefined();
+ expect(circleEl.getAttribute('r')).toBe('1.5');
+ expect(circleEl.getAttribute('tranform')).toBe('translate(0 -1)');
+ expect(circleEl.getAttribute('cx')).toBe(`${dotX}`);
+ expect(circleEl.getAttribute('cy')).toBe(`${dotY}`);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/mock_data.js b/spec/javascripts/vue_shared/components/mock_data.js
new file mode 100644
index 00000000000..0d781bdca74
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/mock_data.js
@@ -0,0 +1,69 @@
+/* eslint-disable */
+
+export const mockMetrics = [
+ [1493716685, '4.30859375'],
+ [1493716745, '4.30859375'],
+ [1493716805, '4.30859375'],
+ [1493716865, '4.30859375'],
+ [1493716925, '4.30859375'],
+ [1493716985, '4.30859375'],
+ [1493717045, '4.30859375'],
+ [1493717105, '4.30859375'],
+ [1493717165, '4.30859375'],
+ [1493717225, '4.30859375'],
+ [1493717285, '4.30859375'],
+ [1493717345, '4.30859375'],
+ [1493717405, '4.30859375'],
+ [1493717465, '4.30859375'],
+ [1493717525, '4.30859375'],
+ [1493717585, '4.30859375'],
+ [1493717645, '4.30859375'],
+ [1493717705, '4.30859375'],
+ [1493717765, '4.30859375'],
+ [1493717825, '4.30859375'],
+ [1493717885, '4.30859375'],
+ [1493717945, '4.30859375'],
+ [1493718005, '4.30859375'],
+ [1493718065, '4.30859375'],
+ [1493718125, '4.30859375'],
+ [1493718185, '4.30859375'],
+ [1493718245, '4.30859375'],
+ [1493718305, '4.234375'],
+ [1493718365, '4.234375'],
+ [1493718425, '4.234375'],
+ [1493718485, '4.234375'],
+ [1493718545, '4.243489583333333'],
+ [1493718605, '4.2109375'],
+ [1493718665, '4.2109375'],
+ [1493718725, '4.2109375'],
+ [1493718785, '4.26171875'],
+ [1493718845, '4.26171875'],
+ [1493718905, '4.26171875'],
+ [1493718965, '4.26171875'],
+ [1493719025, '4.26171875'],
+ [1493719085, '4.26171875'],
+ [1493719145, '4.26171875'],
+ [1493719205, '4.26171875'],
+ [1493719265, '4.26171875'],
+ [1493719325, '4.26171875'],
+ [1493719385, '4.26171875'],
+ [1493719445, '4.26171875'],
+ [1493719505, '4.26171875'],
+ [1493719565, '4.26171875'],
+ [1493719625, '4.26171875'],
+ [1493719685, '4.26171875'],
+ [1493719745, '4.26171875'],
+ [1493719805, '4.26171875'],
+ [1493719865, '4.26171875'],
+ [1493719925, '4.26171875'],
+ [1493719985, '4.26171875'],
+ [1493720045, '4.26171875'],
+ [1493720105, '4.26171875'],
+ [1493720165, '4.26171875'],
+ [1493720225, '4.26171875'],
+ [1493720285, '4.26171875'],
+];
+
+export const mockMedian = 1493718485;
+
+export const mockMedianIndex = 30;
diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js
index 96038718191..895e1c585b4 100644
--- a/spec/javascripts/vue_shared/components/table_pagination_spec.js
+++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import paginationComp from '~/vue_shared/components/table_pagination';
+import paginationComp from '~/vue_shared/components/table_pagination.vue';
import '~/lib/utils/common_utils';
describe('Pagination component', () => {
diff --git a/spec/lib/banzai/filter/external_link_filter_spec.rb b/spec/lib/banzai/filter/external_link_filter_spec.rb
index d9e4525cb28..6f6c215be87 100644
--- a/spec/lib/banzai/filter/external_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_link_filter_spec.rb
@@ -1,5 +1,22 @@
require 'spec_helper'
+shared_examples 'an external link with rel attribute' do
+ it 'adds rel="nofollow" to external links' do
+ expect(doc.at_css('a')).to have_attribute('rel')
+ expect(doc.at_css('a')['rel']).to include 'nofollow'
+ end
+
+ it 'adds rel="noreferrer" to external links' do
+ expect(doc.at_css('a')).to have_attribute('rel')
+ expect(doc.at_css('a')['rel']).to include 'noreferrer'
+ end
+
+ it 'adds rel="noopener" to external links' do
+ expect(doc.at_css('a')).to have_attribute('rel')
+ expect(doc.at_css('a')['rel']).to include 'noopener'
+ end
+end
+
describe Banzai::Filter::ExternalLinkFilter, lib: true do
include FilterSpecHelper
@@ -22,49 +39,51 @@ describe Banzai::Filter::ExternalLinkFilter, lib: true do
context 'for root links on document' do
let(:doc) { filter %q(<a href="https://google.com/">Google</a>) }
- it 'adds rel="nofollow" to external links' do
- expect(doc.at_css('a')).to have_attribute('rel')
- expect(doc.at_css('a')['rel']).to include 'nofollow'
- end
-
- it 'adds rel="noreferrer" to external links' do
- expect(doc.at_css('a')).to have_attribute('rel')
- expect(doc.at_css('a')['rel']).to include 'noreferrer'
- end
+ it_behaves_like 'an external link with rel attribute'
end
context 'for nested links on document' do
let(:doc) { filter %q(<p><a href="https://google.com/">Google</a></p>) }
- it 'adds rel="nofollow" to external links' do
- expect(doc.at_css('a')).to have_attribute('rel')
- expect(doc.at_css('a')['rel']).to include 'nofollow'
+ it_behaves_like 'an external link with rel attribute'
+ end
+
+ context 'for invalid urls' do
+ it 'skips broken hrefs' do
+ doc = filter %q(<p><a href="don't crash on broken urls">Google</a></p>)
+ expected = %q(<p><a href="don't%20crash%20on%20broken%20urls">Google</a></p>)
+
+ expect(doc.to_html).to eq(expected)
end
+ end
+
+ context 'for links with a username' do
+ context 'with a valid username' do
+ let(:doc) { filter %q(<a href="https://user@google.com/">Google</a>) }
- it 'adds rel="noreferrer" to external links' do
- expect(doc.at_css('a')).to have_attribute('rel')
- expect(doc.at_css('a')['rel']).to include 'noreferrer'
+ it_behaves_like 'an external link with rel attribute'
+ end
+
+ context 'with an impersonated username' do
+ let(:internal) { Gitlab.config.gitlab.url }
+
+ let(:doc) { filter %Q(<a href="https://#{internal}@example.com" target="_blank">Reverse Tabnabbing</a>) }
+
+ it_behaves_like 'an external link with rel attribute'
end
end
context 'for non-lowercase scheme links' do
- let(:doc_with_http) { filter %q(<p><a href="httP://google.com/">Google</a></p>) }
- let(:doc_with_https) { filter %q(<p><a href="hTTpS://google.com/">Google</a></p>) }
-
- it 'adds rel="nofollow" to external links' do
- expect(doc_with_http.at_css('a')).to have_attribute('rel')
- expect(doc_with_https.at_css('a')).to have_attribute('rel')
+ context 'with http' do
+ let(:doc) { filter %q(<p><a href="httP://google.com/">Google</a></p>) }
- expect(doc_with_http.at_css('a')['rel']).to include 'nofollow'
- expect(doc_with_https.at_css('a')['rel']).to include 'nofollow'
+ it_behaves_like 'an external link with rel attribute'
end
- it 'adds rel="noreferrer" to external links' do
- expect(doc_with_http.at_css('a')).to have_attribute('rel')
- expect(doc_with_https.at_css('a')).to have_attribute('rel')
+ context 'with https' do
+ let(:doc) { filter %q(<p><a href="hTTpS://google.com/">Google</a></p>) }
- expect(doc_with_http.at_css('a')['rel']).to include 'noreferrer'
- expect(doc_with_https.at_css('a')['rel']).to include 'noreferrer'
+ it_behaves_like 'an external link with rel attribute'
end
it 'skips internal links' do
@@ -84,14 +103,6 @@ describe Banzai::Filter::ExternalLinkFilter, lib: true do
context 'for protocol-relative links' do
let(:doc) { filter %q(<p><a href="//google.com/">Google</a></p>) }
- it 'adds rel="nofollow" to external links' do
- expect(doc.at_css('a')).to have_attribute('rel')
- expect(doc.at_css('a')['rel']).to include 'nofollow'
- end
-
- it 'adds rel="noreferrer" to external links' do
- expect(doc.at_css('a')).to have_attribute('rel')
- expect(doc.at_css('a')['rel']).to include 'noreferrer'
- end
+ it_behaves_like 'an external link with rel attribute'
end
end
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 0f47fb2fbd9..2c7ebb15fd7 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -22,7 +22,22 @@ module Gitlab
expect(Asciidoctor).to receive(:convert)
.with(input, expected_asciidoc_opts).and_return(html)
- expect(render(input)).to eq(html)
+ expect(render(input, context)).to eq(html)
+ end
+
+ context "with asciidoc_opts" do
+ it "merges the options with default ones" do
+ expected_asciidoc_opts = {
+ safe: :secure,
+ backend: :gitlab_html5,
+ attributes: described_class::DEFAULT_ADOC_ATTRS
+ }
+
+ expect(Asciidoctor).to receive(:convert)
+ .with(input, expected_asciidoc_opts).and_return(html)
+
+ render(input, context)
+ end
end
context "XSS" do
@@ -33,7 +48,7 @@ module Gitlab
},
'images' => {
input: 'image:https://localhost.com/image.png[Alt text" onerror="alert(7)]',
- output: "<div>\n<p><span><img src=\"https://localhost.com/image.png\" alt=\"Alt text\"></span></p>\n</div>"
+ output: "<img src=\"https://localhost.com/image.png\" alt=\"Alt text\">"
},
'pre' => {
input: '```mypre"><script>alert(3)</script>',
@@ -43,10 +58,18 @@ module Gitlab
links.each do |name, data|
it "does not convert dangerous #{name} into HTML" do
- expect(render(data[:input])).to eq(data[:output])
+ expect(render(data[:input], context)).to include(data[:output])
end
end
end
+
+ context 'external links' do
+ it 'adds the `rel` attribute to the link' do
+ output = render('link:https://google.com[Google]', context)
+
+ expect(output).to include('rel="nofollow noreferrer noopener"')
+ end
+ end
end
def render(*args)
diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb
index 0d56bdd0ebd..3610a0354e8 100644
--- a/spec/lib/gitlab/cycle_analytics/events_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb
@@ -130,6 +130,8 @@ describe 'cycle analytics events' do
end
before do
+ merge_request.update(head_pipeline: pipeline)
+
create(:ci_build, pipeline: pipeline, status: :success, author: user)
create(:ci_build, pipeline: pipeline, status: :success, author: user)
@@ -226,6 +228,8 @@ describe 'cycle analytics events' do
end
before do
+ merge_request.update(head_pipeline: pipeline)
+
create(:ci_build, pipeline: pipeline, status: :success, author: user)
create(:ci_build, pipeline: pipeline, status: :success, author: user)
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 688e731bf15..34f617e23a5 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -85,6 +85,7 @@ merge_requests:
- merge_requests_closing_issues
- metrics
- timelogs
+- head_pipeline
merge_request_diff:
- merge_request
pipelines:
@@ -102,6 +103,7 @@ pipelines:
- manual_actions
- artifacts
- pipeline_schedule
+- merge_requests
statuses:
- project
- pipeline
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 3af2a172e6d..d2ceb1cf9ae 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -158,6 +158,7 @@ MergeRequest:
- time_estimate
- last_edited_at
- last_edited_by_id
+- head_pipeline_id
MergeRequestDiff:
- id
- state
diff --git a/spec/lib/gitlab/other_markup_spec.rb b/spec/lib/gitlab/other_markup_spec.rb
index d6d53e8586c..c0f5fa9dc1f 100644
--- a/spec/lib/gitlab/other_markup_spec.rb
+++ b/spec/lib/gitlab/other_markup_spec.rb
@@ -13,7 +13,7 @@ describe Gitlab::OtherMarkup, lib: true do
}
links.each do |name, data|
it "does not convert dangerous #{name} into HTML" do
- expect(render(data[:file], data[:input])).to eq(data[:output])
+ expect(render(data[:file], data[:input], context)).to eq(data[:output])
end
end
end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index a7c8e7f1f57..6e0b1192706 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -22,8 +22,37 @@ describe Gitlab::ProjectSearchResults, lib: true do
end
describe 'blob search' do
- let(:project) { create(:project, :repository) }
- let(:results) { described_class.new(user, project, 'files').objects('blobs') }
+ let(:project) { create(:project, :public, :repository) }
+
+ subject(:results) { described_class.new(user, project, 'files').objects('blobs') }
+
+ context 'when repository is disabled' do
+ let(:project) { create(:project, :public, :repository, :repository_disabled) }
+
+ it 'hides blobs from members' do
+ project.add_reporter(user)
+
+ is_expected.to be_empty
+ end
+
+ it 'hides blobs from non-members' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when repository is internal' do
+ let(:project) { create(:project, :public, :repository, :repository_private) }
+
+ it 'finds blobs for members' do
+ project.add_reporter(user)
+
+ is_expected.not_to be_empty
+ end
+
+ it 'hides blobs from non-members' do
+ is_expected.to be_empty
+ end
+ end
it 'finds by name' do
expect(results).to include(["files/images/wm.svg", nil])
@@ -70,6 +99,46 @@ describe Gitlab::ProjectSearchResults, lib: true do
end
end
+ describe 'wiki search' do
+ let(:project) { create(:project, :public) }
+ let(:wiki) { build(:project_wiki, project: project) }
+ let!(:wiki_page) { wiki.create_page('Title', 'Content') }
+
+ subject(:results) { described_class.new(user, project, 'Content').objects('wiki_blobs') }
+
+ context 'when wiki is disabled' do
+ let(:project) { create(:project, :public, :wiki_disabled) }
+
+ it 'hides wiki blobs from members' do
+ project.add_reporter(user)
+
+ is_expected.to be_empty
+ end
+
+ it 'hides wiki blobs from non-members' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when wiki is internal' do
+ let(:project) { create(:project, :public, :wiki_private) }
+
+ it 'finds wiki blobs for members' do
+ project.add_reporter(user)
+
+ is_expected.not_to be_empty
+ end
+
+ it 'hides wiki blobs from non-members' do
+ is_expected.to be_empty
+ end
+ end
+
+ it 'finds by content' do
+ expect(results).to include("master:Title.md:1:Content\n")
+ end
+ end
+
it 'does not list issues on private projects' do
issue = create(:issue, project: project)
@@ -79,7 +148,6 @@ describe Gitlab::ProjectSearchResults, lib: true do
end
describe 'confidential issues' do
- let(:project) { create(:empty_project) }
let(:query) { 'issue' }
let(:author) { create(:user) }
let(:assignee) { create(:user) }
@@ -277,6 +345,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
context 'by commit hash' do
let(:project) { create(:project, :public, :repository) }
let(:commit) { project.repository.commit('0b4bc9a') }
+
commit_hashes = { short: '0b4bc9a', full: '0b4bc9a49b562e85de7cc9e834518ea6828729b9' }
commit_hashes.each do |type, commit_hash|
diff --git a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
new file mode 100644
index 00000000000..bd5f85b901d
--- /dev/null
+++ b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170508170547_add_head_pipeline_for_each_merge_request.rb')
+
+describe AddHeadPipelineForEachMergeRequest do
+ let(:migration) { described_class.new }
+
+ let!(:project) { create(:empty_project) }
+ let!(:forked_project_link) { create(:forked_project_link, forked_from_project: project) }
+ let!(:other_project) { forked_project_link.forked_to_project }
+
+ let!(:pipeline_1) { create(:ci_pipeline, project: project, ref: "branch_1") }
+ let!(:pipeline_2) { create(:ci_pipeline, project: other_project, ref: "branch_1") }
+ let!(:pipeline_3) { create(:ci_pipeline, project: other_project, ref: "branch_1") }
+ let!(:pipeline_4) { create(:ci_pipeline, project: project, ref: "branch_2") }
+
+ let!(:mr_1) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_1", target_branch: "target_1") }
+ let!(:mr_2) { create(:merge_request, source_project: other_project, target_project: project, source_branch: "branch_1", target_branch: "target_2") }
+ let!(:mr_3) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_2", target_branch: "master") }
+ let!(:mr_4) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_3", target_branch: "master") }
+
+ context "#up" do
+ context "when source_project and source_branch of pipeline are the same of merge request" do
+ it "sets head_pipeline_id of given merge requests" do
+ migration.up
+
+ expect(mr_1.reload.head_pipeline_id).to eq(pipeline_1.id)
+ expect(mr_2.reload.head_pipeline_id).to eq(pipeline_3.id)
+ expect(mr_3.reload.head_pipeline_id).to eq(pipeline_4.id)
+ expect(mr_4.reload.head_pipeline_id).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb b/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb
new file mode 100644
index 00000000000..0b8af5010ba
--- /dev/null
+++ b/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170502101023_cleanup_namespaceless_pending_delete_projects.rb')
+
+describe CleanupNamespacelessPendingDeleteProjects do
+ before do
+ # Stub after_save callbacks that will fail when Project has no namespace
+ allow_any_instance_of(Project).to receive(:ensure_dir_exist).and_return(nil)
+ allow_any_instance_of(Project).to receive(:update_project_statistics).and_return(nil)
+ end
+
+ describe '#up' do
+ it 'only cleans up pending delete projects' do
+ create(:empty_project)
+ create(:empty_project, pending_delete: true)
+ project = build(:empty_project, pending_delete: true, namespace_id: nil)
+ project.save(validate: false)
+
+ expect(NamespacelessProjectDestroyWorker).to receive(:bulk_perform_async).with([[project.id.to_s]])
+
+ described_class.new.up
+ end
+
+ it 'does nothing when no pending delete projects without namespace found' do
+ create(:empty_project)
+ create(:empty_project, pending_delete: true)
+
+ expect(NamespacelessProjectDestroyWorker).not_to receive(:bulk_perform_async)
+
+ described_class.new.up
+ end
+ end
+end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 208c8cb1c3d..06e990a0574 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1044,8 +1044,8 @@ describe Ci::Pipeline, models: true do
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: 'a288a022a53a5a944fae87bcec6efc87b7061808') }
it "returns merge requests whose `diff_head_sha` matches the pipeline's SHA" do
- merge_request = create(:merge_request, source_project: project, source_branch: pipeline.ref)
allow_any_instance_of(MergeRequest).to receive(:diff_head_sha) { 'a288a022a53a5a944fae87bcec6efc87b7061808' }
+ merge_request = create(:merge_request, source_project: project, head_pipeline: pipeline, source_branch: pipeline.ref)
expect(pipeline.merge_requests).to eq([merge_request])
end
diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb
index c2ba012a0e6..d0b919efcf9 100644
--- a/spec/models/cycle_analytics/test_spec.rb
+++ b/spec/models/cycle_analytics/test_spec.rb
@@ -14,6 +14,7 @@ describe 'CycleAnalytics#test', feature: true do
issue = context.create(:issue, project: context.project)
merge_request = context.create_merge_request_closing_issue(issue)
pipeline = context.create(:ci_pipeline, ref: merge_request.source_branch, sha: merge_request.diff_head_sha, project: context.project)
+ merge_request.update(head_pipeline: pipeline)
{ pipeline: pipeline, issue: issue }
end,
start_time_conditions: [["pipeline is started", -> (context, data) { data[:pipeline].run! }]],
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 3d60e52f23f..6ca1eb0374d 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -178,16 +178,20 @@ describe Group, models: true do
describe '#avatar_url' do
let!(:group) { create(:group, :access_requestable, :with_avatar) }
let(:user) { create(:user) }
- subject { group.avatar_url }
+ let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
+ let(:avatar_path) { "/uploads/group/avatar/#{group.id}/dk.png" }
context 'when avatar file is uploaded' do
- before do
- group.add_master(user)
- end
+ before { group.add_master(user) }
- let(:avatar_path) { "/uploads/group/avatar/#{group.id}/dk.png" }
+ it 'shows correct avatar url' do
+ expect(group.avatar_url).to eq(avatar_path)
+ expect(group.avatar_url(only_path: false)).to eq([gitlab_host, avatar_path].join)
- it { should eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" }
+ allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
+
+ expect(group.avatar_url).to eq([gitlab_host, avatar_path].join)
+ end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 6cf3dd30ead..ef349530761 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -760,13 +760,8 @@ describe MergeRequest, models: true do
describe '#head_pipeline' do
describe 'when the source project exists' do
it 'returns the latest pipeline' do
- pipeline = double(:ci_pipeline, ref: 'master')
-
- allow(subject).to receive(:diff_head_sha).and_return('123abc')
-
- expect(subject.source_project).to receive(:pipeline_for).
- with('master', '123abc').
- and_return(pipeline)
+ pipeline = create(:ci_empty_pipeline, project: subject.source_project, ref: 'master', status: 'running', sha: "123abc")
+ subject.update(head_pipeline: pipeline)
expect(subject.head_pipeline).to eq(pipeline)
end
@@ -1504,11 +1499,15 @@ describe MergeRequest, models: true do
describe '#mergeable_with_slash_command?' do
def create_pipeline(status)
- create(:ci_pipeline_with_one_job,
+ pipeline = create(:ci_pipeline_with_one_job,
project: project,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha,
status: status)
+
+ merge_request.update(head_pipeline: pipeline)
+
+ pipeline
end
let(:project) { create(:project, :public, :repository, only_allow_merge_if_pipeline_succeeds: true) }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 429b3dd83af..28aa44d8458 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -813,8 +813,16 @@ describe Project, models: true do
context 'when avatar file is uploaded' do
let(:project) { create(:empty_project, :with_avatar) }
let(:avatar_path) { "/uploads/project/avatar/#{project.id}/dk.png" }
+ let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
- it { should eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" }
+ it 'shows correct url' do
+ expect(project.avatar_url).to eq(avatar_path)
+ expect(project.avatar_url(only_path: false)).to eq([gitlab_host, avatar_path].join)
+
+ allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
+
+ expect(project.avatar_url).to eq([gitlab_host, avatar_path].join)
+ end
end
context 'When avatar file in git' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index dd6514b3b50..cea8db7a926 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1634,7 +1634,7 @@ describe Repository, models: true do
context 'with an existing repository' do
it 'returns the README' do
- expect(repository.readme).to be_an_instance_of(Gitlab::Git::Blob)
+ expect(repository.readme).to be_an_instance_of(ReadmeBlob)
end
end
end
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 75b1fc7e216..1e5c96fe593 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -131,46 +131,6 @@ describe Snippet, models: true do
end
end
- describe '.accessible_to' do
- let(:author) { create(:author) }
- let(:project) { create(:empty_project) }
-
- let!(:public_snippet) { create(:snippet, :public) }
- let!(:internal_snippet) { create(:snippet, :internal) }
- let!(:private_snippet) { create(:snippet, :private, author: author) }
-
- let!(:project_public_snippet) { create(:snippet, :public, project: project) }
- let!(:project_internal_snippet) { create(:snippet, :internal, project: project) }
- let!(:project_private_snippet) { create(:snippet, :private, project: project) }
-
- it 'returns only public snippets when user is blank' do
- expect(described_class.accessible_to(nil)).to match_array [public_snippet, project_public_snippet]
- end
-
- it 'returns only public, and internal snippets for regular users' do
- user = create(:user)
-
- expect(described_class.accessible_to(user)).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet]
- end
-
- it 'returns public, internal snippets and project private snippets for project members' do
- member = create(:user)
- project.team << [member, :developer]
-
- expect(described_class.accessible_to(member)).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet, project_private_snippet]
- end
-
- it 'returns private snippets where the user is the author' do
- expect(described_class.accessible_to(author)).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet]
- end
-
- it 'returns all snippets when for admins' do
- admin = create(:admin)
-
- expect(described_class.accessible_to(admin)).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet]
- end
- end
-
describe '#participants' do
let(:project) { create(:empty_project, :public) }
let(:snippet) { create(:snippet, content: 'foo', project: project) }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index c7ddd17872b..b845e85b295 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -964,12 +964,19 @@ describe User, models: true do
describe '#avatar_url' do
let(:user) { create(:user, :with_avatar) }
- subject { user.avatar_url }
context 'when avatar file is uploaded' do
+ let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
let(:avatar_path) { "/uploads/user/avatar/#{user.id}/dk.png" }
- it { should eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" }
+ it 'shows correct avatar url' do
+ expect(user.avatar_url).to eq(avatar_path)
+ expect(user.avatar_url(only_path: false)).to eq([gitlab_host, avatar_path].join)
+
+ allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
+
+ expect(user.avatar_url).to eq([gitlab_host, avatar_path].join)
+ end
end
end
diff --git a/spec/policies/project_snippet_policy_spec.rb b/spec/policies/project_snippet_policy_spec.rb
index d0758af57dd..e1771b636b8 100644
--- a/spec/policies/project_snippet_policy_spec.rb
+++ b/spec/policies/project_snippet_policy_spec.rb
@@ -1,7 +1,9 @@
require 'spec_helper'
describe ProjectSnippetPolicy, models: true do
- let(:current_user) { create(:user) }
+ let(:regular_user) { create(:user) }
+ let(:external_user) { create(:user, :external) }
+ let(:project) { create(:empty_project) }
let(:author_permissions) do
[
@@ -10,13 +12,15 @@ describe ProjectSnippetPolicy, models: true do
]
end
- subject { described_class.abilities(current_user, project_snippet).to_set }
+ def abilities(user, snippet_visibility)
+ snippet = create(:project_snippet, snippet_visibility, project: project)
- context 'public snippet' do
- let(:project_snippet) { create(:project_snippet, :public) }
+ described_class.abilities(user, snippet).to_set
+ end
+ context 'public snippet' do
context 'no user' do
- let(:current_user) { nil }
+ subject { abilities(nil, :public) }
it do
is_expected.to include(:read_project_snippet)
@@ -25,6 +29,17 @@ describe ProjectSnippetPolicy, models: true do
end
context 'regular user' do
+ subject { abilities(regular_user, :public) }
+
+ it do
+ is_expected.to include(:read_project_snippet)
+ is_expected.not_to include(*author_permissions)
+ end
+ end
+
+ context 'external user' do
+ subject { abilities(external_user, :public) }
+
it do
is_expected.to include(:read_project_snippet)
is_expected.not_to include(*author_permissions)
@@ -33,10 +48,8 @@ describe ProjectSnippetPolicy, models: true do
end
context 'internal snippet' do
- let(:project_snippet) { create(:project_snippet, :internal) }
-
context 'no user' do
- let(:current_user) { nil }
+ subject { abilities(nil, :internal) }
it do
is_expected.not_to include(:read_project_snippet)
@@ -45,6 +58,28 @@ describe ProjectSnippetPolicy, models: true do
end
context 'regular user' do
+ subject { abilities(regular_user, :internal) }
+
+ it do
+ is_expected.to include(:read_project_snippet)
+ is_expected.not_to include(*author_permissions)
+ end
+ end
+
+ context 'external user' do
+ subject { abilities(external_user, :internal) }
+
+ it do
+ is_expected.not_to include(:read_project_snippet)
+ is_expected.not_to include(*author_permissions)
+ end
+ end
+
+ context 'project team member external user' do
+ subject { abilities(external_user, :internal) }
+
+ before { project.team << [external_user, :developer] }
+
it do
is_expected.to include(:read_project_snippet)
is_expected.not_to include(*author_permissions)
@@ -53,10 +88,8 @@ describe ProjectSnippetPolicy, models: true do
end
context 'private snippet' do
- let(:project_snippet) { create(:project_snippet, :private) }
-
context 'no user' do
- let(:current_user) { nil }
+ subject { abilities(nil, :private) }
it do
is_expected.not_to include(:read_project_snippet)
@@ -65,6 +98,8 @@ describe ProjectSnippetPolicy, models: true do
end
context 'regular user' do
+ subject { abilities(regular_user, :private) }
+
it do
is_expected.not_to include(:read_project_snippet)
is_expected.not_to include(*author_permissions)
@@ -72,7 +107,9 @@ describe ProjectSnippetPolicy, models: true do
end
context 'snippet author' do
- let(:project_snippet) { create(:project_snippet, :private, author: current_user) }
+ let(:snippet) { create(:project_snippet, :private, author: regular_user) }
+
+ subject { described_class.abilities(regular_user, snippet).to_set }
it do
is_expected.to include(:read_project_snippet)
@@ -80,8 +117,21 @@ describe ProjectSnippetPolicy, models: true do
end
end
- context 'project team member' do
- before { project_snippet.project.team << [current_user, :developer] }
+ context 'project team member normal user' do
+ subject { abilities(regular_user, :private) }
+
+ before { project.team << [regular_user, :developer] }
+
+ it do
+ is_expected.to include(:read_project_snippet)
+ is_expected.not_to include(*author_permissions)
+ end
+ end
+
+ context 'project team member external user' do
+ subject { abilities(external_user, :private) }
+
+ before { project.team << [external_user, :developer] }
it do
is_expected.to include(:read_project_snippet)
@@ -90,7 +140,7 @@ describe ProjectSnippetPolicy, models: true do
end
context 'admin user' do
- let(:current_user) { create(:admin) }
+ subject { abilities(create(:admin), :private) }
it do
is_expected.to include(:read_project_snippet)
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 3e27a3bee77..ed93a8815d3 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -178,7 +178,7 @@ describe API::Groups do
expect(json_response['path']).to eq(group1.path)
expect(json_response['description']).to eq(group1.description)
expect(json_response['visibility']).to eq(Gitlab::VisibilityLevel.string_level(group1.visibility_level))
- expect(json_response['avatar_url']).to eq(group1.avatar_url)
+ expect(json_response['avatar_url']).to eq(group1.avatar_url(only_path: false))
expect(json_response['web_url']).to eq(group1.web_url)
expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
expect(json_response['full_name']).to eq(group1.full_name)
diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb
index 2862580cc70..065cb7ecfe4 100644
--- a/spec/requests/api/v3/groups_spec.rb
+++ b/spec/requests/api/v3/groups_spec.rb
@@ -176,7 +176,7 @@ describe API::V3::Groups do
expect(json_response['path']).to eq(group1.path)
expect(json_response['description']).to eq(group1.description)
expect(json_response['visibility_level']).to eq(group1.visibility_level)
- expect(json_response['avatar_url']).to eq(group1.avatar_url)
+ expect(json_response['avatar_url']).to eq(group1.avatar_url(only_path: false))
expect(json_response['web_url']).to eq(group1.web_url)
expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
expect(json_response['full_name']).to eq(group1.full_name)
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index a4f85c22943..fbb69bc0920 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -98,7 +98,7 @@ describe 'OpenID Connect requests' do
expect(@payload['sub']).to eq hashed_subject
end
- it 'includes the time of the last authentication' do
+ it 'includes the time of the last authentication', :redis do
expect(@payload['auth_time']).to eq user.current_sign_in_at.to_i
end
diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb
index c5a45949841..d92daa345b3 100644
--- a/spec/requests/projects/cycle_analytics_events_spec.rb
+++ b/spec/requests/projects/cycle_analytics_events_spec.rb
@@ -122,6 +122,7 @@ describe 'cycle analytics events', api: true do
mr = create_merge_request_closing_issue(issue, commit_message: "References #{issue.to_reference}")
pipeline = create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha)
+ mr.update(head_pipeline_id: pipeline.id)
pipeline.run
create(:ci_build, pipeline: pipeline, status: :success, author: user)
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index fa5014cee07..1ff1438ba06 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -34,6 +34,42 @@ describe Ci::CreatePipelineService, services: true do
it { expect(pipeline).to have_attributes(status: 'pending') }
it { expect(pipeline.builds.first).to be_kind_of(Ci::Build) }
+ context '#update_merge_requests_head_pipeline' do
+ it 'updates head pipeline of each merge request' do
+ merge_request_1 = create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project)
+ merge_request_2 = create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project)
+
+ head_pipeline = pipeline
+
+ expect(merge_request_1.reload.head_pipeline).to eq(head_pipeline)
+ expect(merge_request_2.reload.head_pipeline).to eq(head_pipeline)
+ end
+
+ context 'when there is no pipeline for source branch' do
+ it "does not update merge request head pipeline" do
+ merge_request = create(:merge_request, source_branch: 'other_branch', target_branch: "branch_1", source_project: project)
+
+ head_pipeline = pipeline
+
+ expect(merge_request.reload.head_pipeline).not_to eq(head_pipeline)
+ end
+ end
+
+ context 'when merge request target project is different from source project' do
+ let!(:target_project) { create(:empty_project) }
+ let!(:forked_project_link) { create(:forked_project_link, forked_to_project: project, forked_from_project: target_project) }
+
+ it 'updates head pipeline for merge request' do
+ merge_request =
+ create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project, target_project: target_project)
+
+ head_pipeline = pipeline
+
+ expect(merge_request.reload.head_pipeline).to eq(head_pipeline)
+ end
+ end
+ end
+
context 'auto-cancel enabled' do
before do
project.update(auto_cancel_pending_pipelines: 'enabled')
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 8c2415b4e07..ab06f45dbb9 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -584,7 +584,7 @@ describe GitPushService, services: true do
commit = double(:commit)
diff = double(:diff, new_path: 'README.md')
- expect(commit).to receive(:raw_diffs).with(deltas_only: true).
+ expect(commit).to receive(:raw_deltas).
and_return([diff])
service.push_commits = [commit]
diff --git a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
index 769b3193275..3ef5135e6a3 100644
--- a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
@@ -82,6 +82,10 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
sha: merge_request_head, status: 'success')
end
+ before do
+ mr_merge_if_green_enabled.update(head_pipeline: triggering_pipeline)
+ end
+
it "merges all merge requests with merge when the pipeline succeeds enabled" do
expect(MergeWorker).to receive(:perform_async)
service.trigger(triggering_pipeline)
@@ -124,6 +128,8 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
sha: mr_conflict.diff_head_sha, status: 'success')
end
+ before { mr_conflict.update(head_pipeline: conflict_pipeline) }
+
it 'does not merge the merge request' do
expect(MergeWorker).not_to receive(:perform_async)
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 31487c0f794..07f5440cc36 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -174,11 +174,13 @@ describe MergeRequests::UpdateService, services: true do
context 'with active pipeline' do
before do
service_mock = double
- create(:ci_pipeline_with_one_job,
+ pipeline = create(:ci_pipeline_with_one_job,
project: project,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha)
+ merge_request.update(head_pipeline: pipeline)
+
expect(MergeRequests::MergeWhenPipelineSucceedsService).to receive(:new).with(project, user).
and_return(service_mock)
expect(service_mock).to receive(:execute).with(merge_request)
diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb
index 063b3bd76eb..0657b7e93fe 100644
--- a/spec/services/projects/participants_service_spec.rb
+++ b/spec/services/projects/participants_service_spec.rb
@@ -6,7 +6,6 @@ describe Projects::ParticipantsService, services: true do
let(:project) { create(:empty_project, :public) }
let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + 'spec/fixtures/dk.png')) }
let(:user) { create(:user) }
- let(:base_url) { Settings.send(:build_base_gitlab_url) }
let!(:group_member) { create(:group_member, group: group, user: user) }
it 'should return an url for the avatar' do
@@ -14,7 +13,7 @@ describe Projects::ParticipantsService, services: true do
groups = participants.groups
expect(groups.size).to eq 1
- expect(groups.first[:avatar_url]).to eq "#{base_url}/uploads/group/avatar/#{group.id}/dk.png"
+ expect(groups.first[:avatar_url]).to eq("/uploads/group/avatar/#{group.id}/dk.png")
end
it 'should return an url for the avatar with relative url' do
@@ -25,7 +24,7 @@ describe Projects::ParticipantsService, services: true do
groups = participants.groups
expect(groups.size).to eq 1
- expect(groups.first[:avatar_url]).to eq "#{base_url}/gitlab/uploads/group/avatar/#{group.id}/dk.png"
+ expect(groups.first[:avatar_url]).to eq("/gitlab/uploads/group/avatar/#{group.id}/dk.png")
end
end
end
diff --git a/spec/sidekiq/cron/job_gem_dependency_spec.rb b/spec/sidekiq/cron/job_gem_dependency_spec.rb
new file mode 100644
index 00000000000..2e30cf025b0
--- /dev/null
+++ b/spec/sidekiq/cron/job_gem_dependency_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe Sidekiq::Cron::Job do
+ describe 'cron jobs' do
+ context 'when rufus-scheduler depends on ZoTime or EoTime' do
+ before do
+ described_class
+ .create(name: 'TestCronWorker',
+ cron: Settings.cron_jobs[:pipeline_schedule_worker]['cron'],
+ class: Settings.cron_jobs[:pipeline_schedule_worker]['job_class'])
+ end
+
+ it 'does not get "Rufus::Scheduler::ZoTime/EtOrbi::EoTime into an exact number"' do
+ expect { described_class.all.first.should_enque?(Time.now) }.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/views/projects/imports/new.html.haml_spec.rb b/spec/views/projects/imports/new.html.haml_spec.rb
new file mode 100644
index 00000000000..9b293065797
--- /dev/null
+++ b/spec/views/projects/imports/new.html.haml_spec.rb
@@ -0,0 +1,22 @@
+require "spec_helper"
+
+describe "projects/imports/new.html.haml" do
+ let(:user) { create(:user) }
+
+ context 'when import fails' do
+ let(:project) { create(:project_empty_repo, import_status: :failed, import_error: '<a href="http://googl.com">Foo</a>', import_type: :gitlab_project, import_source: '/var/opt/gitlab/gitlab-rails/shared/tmp/project_exports/uploads/t.tar.gz', import_url: nil) }
+
+ before do
+ sign_in(user)
+ project.team << [user, :master]
+ end
+
+ it "escapes HTML in import errors" do
+ assign(:project, project)
+
+ render
+
+ expect(rendered).not_to have_link('Foo', href: "http://googl.com")
+ end
+ end
+end
diff --git a/spec/views/projects/tree/show.html.haml_spec.rb b/spec/views/projects/tree/show.html.haml_spec.rb
index 900f8d4732f..835a93e620e 100644
--- a/spec/views/projects/tree/show.html.haml_spec.rb
+++ b/spec/views/projects/tree/show.html.haml_spec.rb
@@ -31,7 +31,7 @@ describe 'projects/tree/show' do
it 'displays correctly' do
render
expect(rendered).to have_css('.js-project-refs-dropdown .dropdown-toggle-text', text: ref)
- expect(rendered).to have_css('.readme-holder .file-content', text: ref)
+ expect(rendered).to have_css('.readme-holder')
end
end
end
diff --git a/spec/workers/namespaceless_project_destroy_worker_spec.rb b/spec/workers/namespaceless_project_destroy_worker_spec.rb
new file mode 100644
index 00000000000..8533b7b85e9
--- /dev/null
+++ b/spec/workers/namespaceless_project_destroy_worker_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe NamespacelessProjectDestroyWorker do
+ subject { described_class.new }
+
+ before do
+ # Stub after_save callbacks that will fail when Project has no namespace
+ allow_any_instance_of(Project).to receive(:ensure_dir_exist).and_return(nil)
+ allow_any_instance_of(Project).to receive(:update_project_statistics).and_return(nil)
+ end
+
+ describe '#perform' do
+ context 'project has namespace' do
+ it 'does not do anything' do
+ project = create(:empty_project)
+
+ subject.perform(project.id)
+
+ expect(Project.unscoped.all).to include(project)
+ end
+ end
+
+ context 'project has no namespace' do
+ let!(:project) do
+ project = build(:empty_project, namespace_id: nil)
+ project.save(validate: false)
+ project
+ end
+
+ context 'project not a fork of another project' do
+ it "truncates the project's team" do
+ expect_any_instance_of(ProjectTeam).to receive(:truncate)
+
+ subject.perform(project.id)
+ end
+
+ it 'deletes the project' do
+ subject.perform(project.id)
+
+ expect(Project.unscoped.all).not_to include(project)
+ end
+
+ it 'does not call unlink_fork' do
+ is_expected.not_to receive(:unlink_fork)
+
+ subject.perform(project.id)
+ end
+
+ it 'does not do anything in Project#remove_pages method' do
+ expect(Gitlab::PagesTransfer).not_to receive(:new)
+
+ subject.perform(project.id)
+ end
+ end
+
+ context 'project forked from another' do
+ let!(:parent_project) { create(:empty_project) }
+
+ before do
+ create(:forked_project_link, forked_to_project: project, forked_from_project: parent_project)
+ end
+
+ it 'closes open merge requests' do
+ merge_request = create(:merge_request, source_project: project, target_project: parent_project)
+
+ subject.perform(project.id)
+
+ expect(merge_request.reload).to be_closed
+ end
+
+ it 'destroys the link' do
+ subject.perform(project.id)
+
+ expect(parent_project.forked_project_links).to be_empty
+ end
+ end
+ end
+ end
+end
diff --git a/spec/workers/pipeline_metrics_worker_spec.rb b/spec/workers/pipeline_metrics_worker_spec.rb
index 5dbc0da95c2..ef71125c0b6 100644
--- a/spec/workers/pipeline_metrics_worker_spec.rb
+++ b/spec/workers/pipeline_metrics_worker_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe PipelineMetricsWorker do
let(:project) { create(:project, :repository) }
- let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) }
+ let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref, head_pipeline: pipeline) }
let(:pipeline) do
create(:ci_empty_pipeline,