summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2017-03-27 12:35:10 +0200
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2017-03-27 12:35:10 +0200
commit1a4c60ef57d047dab6aa823f7cc50548897b74f6 (patch)
tree92bbccd1e1850837da5c7b10344ff77f0be0e7be /spec
parent7ada193e0fd28b4a6eca1fda7dda6f0ebe6b2d72 (diff)
parent7324d6713262d7f9c563d48b82934c4a8eb72a52 (diff)
downloadgitlab-ce-1a4c60ef57d047dab6aa823f7cc50548897b74f6.tar.gz
Merge branch 'master' into feature/multi-level-container-registry-images
* master: (192 commits) Implement new service for creating user Update sentry-raven 2.0.2 -> 2.4.0 Update webmock 1.21.0 -> 1.24.6 Update spring 1.7.2 -> 2.0.1 Update simplecov 0.12.0 -> 0.14.1 Update pry-rails 0.3.4 -> 0.3.5 Update pry-byebug 3.4.1 -> 3.4.2 Update flay 2.6.1 -> 2.8.1 Remove Tags filter from Projects Explore dropdown Update capybara-screenshot 1.0.11 -> 1.0.14 Update bullet 5.2.0 -> 5.5.1 Update brakeman 3.4.1 -> 3.6.1 Remove web-console gem Update better_errors 1.0.1 -> 2.1.1 Display flash message to unauthenticated user when creating new issue Fix up emoji tests that should have failed :/ Fix RSpec/DescribeSymbol cop violations Add event limit warning all tabs Cycle Analytics Adding non_archived scope for counting projects Resolve "Gitlab administrator cannot create projects in every group" ... Conflicts: db/schema.rb
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb11
-rw-r--r--spec/controllers/projects/builds_controller_specs.rb47
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb24
-rw-r--r--spec/controllers/registrations_controller_spec.rb9
-rw-r--r--spec/factories/ci/builds.rb2
-rw-r--r--spec/factories/merge_requests.rb1
-rw-r--r--spec/features/admin/admin_broadcast_messages_spec.rb2
-rw-r--r--spec/features/admin/admin_settings_spec.rb1
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb14
-rw-r--r--spec/features/commits_spec.rb13
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb5
-rw-r--r--spec/features/explore/groups_list_spec.rb20
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb33
-rw-r--r--spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb2
-rw-r--r--spec/features/participants_autocomplete_spec.rb95
-rw-r--r--spec/features/projects/blobs/user_create_spec.rb18
-rw-r--r--spec/features/projects/compare_spec.rb3
-rw-r--r--spec/features/projects/deploy_keys_spec.rb26
-rw-r--r--spec/features/projects/group_links_spec.rb24
-rw-r--r--spec/features/projects/members/user_requests_access_spec.rb2
-rw-r--r--spec/features/projects/milestones/milestones_sorting_spec.rb52
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb2
-rw-r--r--spec/features/projects/user_create_dir_spec.rb72
-rw-r--r--spec/features/user_callout_spec.rb8
-rw-r--r--spec/features/users/projects_spec.rb31
-rw-r--r--spec/helpers/avatars_helper_spec.rb21
-rw-r--r--spec/helpers/namespaces_helper_spec.rb33
-rw-r--r--spec/helpers/todos_helper_spec.rb34
-rw-r--r--spec/helpers/users_helper_spec.rb17
-rw-r--r--spec/javascripts/boards/board_card_spec.js3
-rw-r--r--spec/javascripts/boards/boards_store_spec.js5
-rw-r--r--spec/javascripts/boards/issue_card_spec.js3
-rw-r--r--spec/javascripts/boards/list_spec.js3
-rw-r--r--spec/javascripts/boards/modal_store_spec.js1
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js7
-rw-r--r--spec/javascripts/cycle_analytics/limit_warning_component_spec.js39
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js2
-rw-r--r--spec/javascripts/fixtures/pipelines.html.haml14
-rw-r--r--spec/javascripts/fixtures/pipelines_table.html.haml3
-rw-r--r--spec/javascripts/issuable_time_tracker_spec.js6
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js67
-rw-r--r--spec/javascripts/lib/utils/poll_spec.js163
-rw-r--r--spec/javascripts/test_bundle.js6
-rw-r--r--spec/javascripts/user_callout_spec.js18
-rw-r--r--spec/javascripts/vue_pipelines_index/empty_state_spec.js38
-rw-r--r--spec/javascripts/vue_pipelines_index/error_state_spec.js23
-rw-r--r--spec/javascripts/vue_pipelines_index/mock_data.js107
-rw-r--r--spec/javascripts/vue_pipelines_index/nav_controls_spec.js93
-rw-r--r--spec/javascripts/vue_pipelines_index/pipelines_spec.js114
-rw-r--r--spec/lib/gitlab/ci/build/step_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_snippet_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb8
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb28
-rw-r--r--spec/lib/gitlab/git/compare_spec.rb10
-rw-r--r--spec/lib/gitlab/git/diff_collection_spec.rb4
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb10
-rw-r--r--spec/lib/gitlab/git/util_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb2
-rw-r--r--spec/mailers/emails/profile_spec.rb10
-rw-r--r--spec/mailers/notify_spec.rb380
-rw-r--r--spec/models/concerns/issuable_spec.rb28
-rw-r--r--spec/models/hooks/system_hook_spec.rb5
-rw-r--r--spec/models/issue_spec.rb37
-rw-r--r--spec/models/milestone_spec.rb2
-rw-r--r--spec/models/namespace_spec.rb6
-rw-r--r--spec/models/pages_domain_spec.rb14
-rw-r--r--spec/models/project_spec.rb7
-rw-r--r--spec/models/repository_spec.rb2
-rw-r--r--spec/models/route_spec.rb20
-rw-r--r--spec/models/snippet_spec.rb43
-rw-r--r--spec/models/user_spec.rb13
-rw-r--r--spec/requests/api/projects_spec.rb2
-rw-r--r--spec/requests/api/v3/projects_spec.rb2
-rw-r--r--spec/requests/api/v3/users_spec.rb14
-rw-r--r--spec/services/boards/issues/list_service_spec.rb3
-rw-r--r--spec/services/create_branch_service_spec.rb24
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb2
-rw-r--r--spec/services/milestones/close_service_spec.rb2
-rw-r--r--spec/services/projects/fork_service_spec.rb4
-rw-r--r--spec/services/spam_service_spec.rb71
-rw-r--r--spec/services/users/create_service_spec.rb182
-rw-r--r--spec/spec_helper.rb3
-rw-r--r--spec/support/notify_shared_examples.rb140
-rw-r--r--spec/support/prometheus_helpers.rb4
-rw-r--r--spec/support/slack_mattermost_notifications_shared_examples.rb19
-rw-r--r--spec/support/target_branch_helpers.rb16
-rw-r--r--spec/support/test_env.rb5
-rw-r--r--spec/support/wait_for_requests.rb32
-rw-r--r--spec/support/wait_for_vue_resource.rb2
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb78
-rw-r--r--spec/tasks/gitlab/workhorse_rake_spec.rb3
-rw-r--r--spec/tasks/tokens_spec.rb21
-rw-r--r--spec/views/projects/pipelines/show.html.haml_spec.rb10
94 files changed, 2043 insertions, 570 deletions
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index fa4cc0ebbe0..51f23e4eeb9 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -112,6 +112,17 @@ describe Import::BitbucketController do
post :create, format: :js
end
end
+
+ context 'when the Bitbucket user is unauthorized' do
+ render_views
+
+ it 'returns unauthorized' do
+ allow(controller).to receive(:current_user).and_return(user)
+ allow(user).to receive(:can?).and_return(false)
+
+ post :create, format: :js
+ end
+ end
end
context "when the repository owner is not the Bitbucket user" do
diff --git a/spec/controllers/projects/builds_controller_specs.rb b/spec/controllers/projects/builds_controller_specs.rb
new file mode 100644
index 00000000000..d501f7b3155
--- /dev/null
+++ b/spec/controllers/projects/builds_controller_specs.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe Projects::BuildsController do
+ include ApiHelpers
+
+ let(:project) { create(:empty_project, :public) }
+
+ describe 'GET trace.json' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:user) { create(:user) }
+
+ context 'when user is logged in as developer' do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ get_trace
+ end
+
+ it 'traces build log' do
+ expect(response).to have_http_status(:ok)
+ expect(json_response['id']).to eq build.id
+ expect(json_response['status']).to eq build.status
+ end
+ end
+
+ context 'when user is logged in as non member' do
+ before do
+ sign_in(user)
+ get_trace
+ end
+
+ it 'traces build log' do
+ expect(response).to have_http_status(:ok)
+ expect(json_response['id']).to eq build.id
+ expect(json_response['status']).to eq build.status
+ end
+ end
+
+ def get_trace
+ get :trace, namespace_id: project.namespace,
+ project_id: project,
+ id: build.id,
+ format: :json
+ end
+ end
+end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 57a921e3676..734966d50b2 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -90,6 +90,7 @@ describe Projects::IssuesController do
it 'redirects to signin if not logged in' do
get :new, namespace_id: project.namespace, project_id: project
+ expect(flash[:notice]).to eq 'Please sign in to create the new issue.'
expect(response).to redirect_to(new_user_session_path)
end
@@ -241,10 +242,27 @@ describe Projects::IssuesController do
expect(spam_logs.first.recaptcha_verified).to be_falsey
end
- it 'renders verify template' do
- update_spam_issue
+ context 'as HTML' do
+ it 'renders verify template' do
+ update_spam_issue
+
+ expect(response).to render_template(:verify)
+ end
+ end
+
+ context 'as JSON' do
+ before do
+ update_issue({ title: 'Spam Title', description: 'Spam lives here' }, format: :json)
+ end
+
+ it 'renders json errors' do
+ expect(json_response)
+ .to eql("errors" => ["Your issue has been recognized as spam. Please, change the content or solve the reCAPTCHA to proceed."])
+ end
- expect(response).to render_template(:verify)
+ it 'returns 422 status' do
+ expect(response).to have_http_status(422)
+ end
end
end
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 8cc216445eb..902911071c4 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -30,6 +30,15 @@ describe RegistrationsController do
expect(subject.current_user).to be_nil
end
end
+
+ context 'when signup_enabled? is false' do
+ it 'redirects to sign_in' do
+ allow_any_instance_of(ApplicationSetting).to receive(:signup_enabled?).and_return(false)
+
+ expect { post(:create, user_params) }.not_to change(User, :count)
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
end
context 'when reCAPTCHA is enabled' do
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 6b0d084614b..f78086211f7 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -172,7 +172,7 @@ FactoryGirl.define do
{
image: 'ruby:2.1',
services: ['postgres'],
- after_script: "ls\ndate",
+ after_script: %w(ls date),
artifacts: {
name: 'artifacts_file',
untracked: false,
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 21487541507..ae0bbbd6aeb 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -4,7 +4,6 @@ FactoryGirl.define do
author
association :source_project, :repository, factory: :project
target_project { source_project }
- project { target_project }
# $ git log --pretty=oneline feature..master
# 5937ac0a7beb003549fc5fd26fc247adbce4a52e Add submodule from gitlab.com
diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb
index bc957ec72e1..d6c63f66a9b 100644
--- a/spec/features/admin/admin_broadcast_messages_spec.rb
+++ b/spec/features/admin/admin_broadcast_messages_spec.rb
@@ -45,7 +45,7 @@ feature 'Admin Broadcast Messages', feature: true do
page.within('.broadcast-message-preview') do
expect(page).to have_selector('strong', text: 'Markdown')
- expect(page).to have_selector('img.emoji')
+ expect(page).to have_selector('gl-emoji[data-name="tada"]')
end
end
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 03daab12c8f..5099441dce2 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -34,6 +34,7 @@ feature 'Admin updates settings', feature: true do
fill_in 'Username', with: 'test_user'
fill_in 'service_push_channel', with: '#test_channel'
page.check('Notify only broken pipelines')
+ page.check('Notify only default branch')
check_all_events
click_on 'Save'
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index d17a418b8c3..1c0f97d8a1c 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -23,6 +23,20 @@ describe 'Issue Boards add issue modal', :feature, :js do
wait_for_vue_resource
end
+ it 'resets filtered search state' do
+ visit namespace_project_board_path(project.namespace, project, board, search: 'testing')
+
+ wait_for_vue_resource
+
+ click_button('Add issues')
+
+ page.within('.add-issues-modal') do
+ expect(find('.form-control').value).to eq('')
+ expect(page).to have_selector('.clear-search', visible: false)
+ expect(find('.form-control')[:placeholder]).to eq('Search or filter results...')
+ end
+ end
+
context 'modal interaction' do
it 'opens modal' do
click_button('Add issues')
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 0e305c52358..881f1fca4d1 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -11,12 +11,16 @@ describe 'Commits' do
stub_ci_pipeline_to_return_yaml_file
end
+ let(:creator) { create(:user) }
+
let!(:pipeline) do
create(:ci_pipeline,
project: project,
+ user: creator,
ref: project.default_branch,
sha: project.commit.sha,
- status: :success)
+ status: :success,
+ created_at: 5.months.ago)
end
context 'commit status is Generic Commit Status' do
@@ -80,7 +84,8 @@ describe 'Commits' do
it 'shows pipeline`s data' do
expect(page).to have_content pipeline.sha[0..7]
expect(page).to have_content pipeline.git_commit_message
- expect(page).to have_content pipeline.git_author_name
+ expect(page).to have_content pipeline.user.name
+ expect(page).to have_content pipeline.created_at.strftime('%b %d, %Y')
end
end
@@ -150,7 +155,7 @@ describe 'Commits' do
it do
expect(page).to have_content pipeline.sha[0..7]
expect(page).to have_content pipeline.git_commit_message
- expect(page).to have_content pipeline.git_author_name
+ expect(page).to have_content pipeline.user.name
expect(page).to have_link('Download artifacts')
expect(page).not_to have_link('Cancel running')
expect(page).not_to have_link('Retry')
@@ -169,7 +174,7 @@ describe 'Commits' do
it do
expect(page).to have_content pipeline.sha[0..7]
expect(page).to have_content pipeline.git_commit_message
- expect(page).to have_content pipeline.git_author_name
+ expect(page).to have_content pipeline.user.name
expect(page).not_to have_link('Download artifacts')
expect(page).not_to have_link('Cancel running')
expect(page).not_to have_link('Retry')
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index 62a2c54c94c..3642c0bfb5b 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -21,6 +21,11 @@ feature 'Dashboard shortcuts', feature: true, js: true do
find('body').native.send_key('m')
check_page_title('Merge Requests')
+
+ find('body').native.send_key('g')
+ find('body').native.send_key('t')
+
+ check_page_title('Todos')
end
def check_page_title(title)
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
index 773ae4b38bc..9daaaa8e555 100644
--- a/spec/features/explore/groups_list_spec.rb
+++ b/spec/features/explore/groups_list_spec.rb
@@ -7,6 +7,7 @@ describe 'Explore Groups page', js: true, feature: true do
let!(:group) { create(:group) }
let!(:public_group) { create(:group, :public) }
let!(:private_group) { create(:group, :private) }
+ let!(:empty_project) { create(:empty_project, group: public_group) }
before do
group.add_owner(user)
@@ -43,4 +44,23 @@ describe 'Explore Groups page', js: true, feature: true do
expect(page).not_to have_content(private_group.full_name)
expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
end
+
+ it 'shows non-archived projects count' do
+ # Initially project is not archived
+ expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("1")
+
+ # Archive project
+ empty_project.archive!
+ visit explore_groups_path
+
+ # Check project count
+ expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("0")
+
+ # Unarchive project
+ empty_project.unarchive!
+ visit explore_groups_path
+
+ # Check project count
+ expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("1")
+ end
end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index 0832a3656a8..f1ad4a55246 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -12,6 +12,33 @@ feature 'Create New Merge Request', feature: true, js: true do
login_as user
end
+ it 'selects the source branch sha when a tag with the same name exists' do
+ visit namespace_project_merge_requests_path(project.namespace, project)
+
+ click_link 'New Merge Request'
+ expect(page).to have_content('Source branch')
+ expect(page).to have_content('Target branch')
+
+ first('.js-source-branch').click
+ first('.dropdown-source-branch .dropdown-content a', text: 'v1.1.0').click
+
+ expect(page).to have_content "b83d6e3"
+ end
+
+ it 'selects the target branch sha when a tag with the same name exists' do
+ visit namespace_project_merge_requests_path(project.namespace, project)
+
+ click_link 'New Merge Request'
+
+ expect(page).to have_content('Source branch')
+ expect(page).to have_content('Target branch')
+
+ first('.js-target-branch').click
+ first('.dropdown-target-branch .dropdown-content a', text: 'v1.1.0').click
+
+ expect(page).to have_content "b83d6e3"
+ end
+
it 'generates a diff for an orphaned branch' do
visit namespace_project_merge_requests_path(project.namespace, project)
@@ -46,6 +73,12 @@ feature 'Create New Merge Request', feature: true, js: true do
end
end
+ it 'populates source branch button' do
+ visit new_namespace_project_merge_request_path(project.namespace, project, change_branches: true, merge_request: { target_branch: 'master', source_branch: 'fix' })
+
+ expect(find('.js-source-branch')).to have_content('fix')
+ end
+
it 'allows to change the diff view' do
visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_branch: 'master', source_branch: 'fix' })
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 0ceaf7bc830..79105b1ee46 100644
--- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
+++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
@@ -35,6 +35,8 @@ feature 'Merge immediately', :feature, :js do
click_link 'Merge Immediately'
expect(find('.js-merge-when-pipeline-succeeds-button')).to have_content('Merge in progress')
+
+ wait_for_ajax
end
end
end
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
index c2545b0c259..decad589c23 100644
--- a/spec/features/participants_autocomplete_spec.rb
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -1,101 +1,62 @@
require 'spec_helper'
-feature 'Member autocomplete', feature: true do
- include WaitForAjax
-
+feature 'Member autocomplete', :js do
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
- let(:participant) { create(:user) }
let(:author) { create(:user) }
+ let(:note) { create(:note, noteable: noteable, project: noteable.project) }
before do
- allow_any_instance_of(Commit).to receive(:author).and_return(author)
- login_as user
+ note # actually create the note
+ login_as(user)
end
- shared_examples "open suggestions" do
- it 'displays suggestions' do
- expect(page).to have_selector('.atwho-view', visible: true)
- end
-
- it 'suggests author' do
- page.within('.atwho-view', visible: true) do
- expect(page).to have_content(author.username)
+ shared_examples "open suggestions when typing @" do
+ before do
+ page.within('.new-note') do
+ find('#note_note').send_keys('@')
end
end
- it 'suggests participant' do
+ it 'suggests noteable author and note author' do
page.within('.atwho-view', visible: true) do
- expect(page).to have_content(participant.username)
+ expect(page).to have_content(author.username)
+ expect(page).to have_content(note.author.username)
end
end
end
- context 'adding a new note on a Issue', js: true do
+ context 'adding a new note on a Issue' do
+ let(:noteable) { create(:issue, author: author, project: project) }
before do
- issue = create(:issue, author: author, project: project)
- create(:note, note: 'Ultralight Beam', noteable: issue,
- project: project, author: participant)
- visit_issue(project, issue)
+ visit namespace_project_issue_path(project.namespace, project, noteable)
end
- context 'when typing @' do
- include_examples "open suggestions"
- before do
- open_member_suggestions
- end
- end
+ include_examples "open suggestions when typing @"
end
- context 'adding a new note on a Merge Request ', js: true do
+ context 'adding a new note on a Merge Request' do
+ let(:noteable) do
+ create(:merge_request, source_project: project,
+ target_project: project, author: author)
+ end
before do
- merge = create(:merge_request, source_project: project, target_project: project, author: author)
- create(:note, note: 'Ultralight Beam', noteable: merge,
- project: project, author: participant)
- visit_merge_request(project, merge)
+ visit namespace_project_merge_request_path(project.namespace, project, noteable)
end
- context 'when typing @' do
- include_examples "open suggestions"
- before do
- open_member_suggestions
- end
- end
+ include_examples "open suggestions when typing @"
end
- context 'adding a new note on a Commit ', js: true do
- let(:commit) { project.commit }
+ context 'adding a new note on a Commit' do
+ let(:noteable) { project.commit }
+ let(:note) { create(:note_on_commit, project: project, commit_id: project.commit.id) }
before do
- allow(commit).to receive(:author).and_return(author)
- create(:note_on_commit, author: participant, project: project, commit_id: project.repository.commit.id, note: 'No More Parties in LA')
- visit_commit(project, commit)
- end
-
- context 'when typing @' do
- include_examples "open suggestions"
- before do
- open_member_suggestions
- end
- end
- end
+ allow_any_instance_of(Commit).to receive(:author).and_return(author)
- def open_member_suggestions
- page.within('.new-note') do
- find('#note_note').send_keys('@')
+ visit namespace_project_commit_path(project.namespace, project, noteable)
end
- wait_for_ajax
- end
-
- def visit_issue(project, issue)
- visit namespace_project_issue_path(project.namespace, project, issue)
- end
-
- def visit_merge_request(project, merge)
- visit namespace_project_merge_request_path(project.namespace, project, merge)
- end
- def visit_commit(project, commit)
- visit namespace_project_commit_path(project.namespace, project, commit)
+ include_examples "open suggestions when typing @"
end
end
diff --git a/spec/features/projects/blobs/user_create_spec.rb b/spec/features/projects/blobs/user_create_spec.rb
index 03d08c12612..5686868a0c4 100644
--- a/spec/features/projects/blobs/user_create_spec.rb
+++ b/spec/features/projects/blobs/user_create_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
feature 'New blob creation', feature: true, js: true do
include WaitForAjax
+ include TargetBranchHelpers
given(:user) { create(:user) }
given(:role) { :developer }
@@ -20,19 +21,6 @@ feature 'New blob creation', feature: true, js: true do
execute_script("ace.edit('editor').setValue('#{content}')")
end
- def select_branch_index(index)
- first('button.js-target-branch').click
- wait_for_ajax
- all('a[data-group="Branches"]')[index].click
- end
-
- def create_new_branch(name)
- first('button.js-target-branch').click
- click_link 'Create new branch'
- fill_in 'new_branch_name', with: name
- click_button 'Create'
- end
-
def commit_file
click_button 'Commit Changes'
end
@@ -53,12 +41,12 @@ feature 'New blob creation', feature: true, js: true do
context 'with different target branch' do
background do
edit_file
- select_branch_index(0)
+ select_branch('feature')
commit_file
end
scenario 'creates the blob in the different branch' do
- expect(page).to have_content 'test'
+ expect(page).to have_content 'feature'
expect(page).to have_content 'successfully created'
end
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index 030043d14aa..b2a3b111c9e 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -53,6 +53,7 @@ describe "Compare", js: true do
dropdown = find(".js-compare-#{dropdown_type}-dropdown")
dropdown.find(".compare-dropdown-toggle").click
dropdown.fill_in("Filter by Git revision", with: selection)
- find_link(selection, visible: true).click
+ wait_for_ajax
+ dropdown.find_all("a[data-ref=\"#{selection}\"]", visible: true).last.click
end
end
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
new file mode 100644
index 00000000000..0b997f130ea
--- /dev/null
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe 'Project deploy keys', feature: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project_empty_repo) }
+
+ before do
+ project.team << [user, :master]
+ login_as(user)
+ end
+
+ describe 'removing key' do
+ before do
+ create(:deploy_keys_project, project: project)
+ end
+
+ it 'removes association between project and deploy key' do
+ visit namespace_project_settings_repository_path(project.namespace, project)
+
+ page.within '.deploy-keys' do
+ expect { click_on 'Remove' }
+ .to change { project.deploy_keys.count }.by(-1)
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/group_links_spec.rb b/spec/features/projects/group_links_spec.rb
index 8b302a6aa23..4c28205da9b 100644
--- a/spec/features/projects/group_links_spec.rb
+++ b/spec/features/projects/group_links_spec.rb
@@ -8,7 +8,7 @@ feature 'Project group links', feature: true, js: true do
let!(:group) { create(:group) }
background do
- project.team << [master, :master]
+ project.add_master(master)
login_as(master)
end
@@ -29,4 +29,26 @@ feature 'Project group links', feature: true, js: true do
end
end
end
+
+ context 'nested group project' do
+ let!(:nested_group) { create(:group, parent: group) }
+ let!(:another_group) { create(:group) }
+ let!(:project) { create(:project, namespace: nested_group) }
+
+ background do
+ group.add_master(master)
+ another_group.add_master(master)
+ end
+
+ it 'does not show ancestors' do
+ visit namespace_project_settings_members_path(project.namespace, project)
+
+ click_link 'Search for a group'
+
+ page.within '.select2-drop' do
+ expect(page).to have_content(another_group.name)
+ expect(page).not_to have_content(group.name)
+ end
+ end
+ end
end
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
index b64c15e0adc..de25d45f447 100644
--- a/spec/features/projects/members/user_requests_access_spec.rb
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -61,7 +61,7 @@ feature 'Projects > Members > User requests access', feature: true do
click_link('Settings')
end
- page.within('.page-with-layout-nav .sub-nav') do
+ page.within('.sub-nav') do
click_link('Members')
end
end
diff --git a/spec/features/projects/milestones/milestones_sorting_spec.rb b/spec/features/projects/milestones/milestones_sorting_spec.rb
new file mode 100644
index 00000000000..da3eaed707a
--- /dev/null
+++ b/spec/features/projects/milestones/milestones_sorting_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+feature 'Milestones sorting', :feature, :js do
+ include SortingHelper
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) }
+
+ before do
+ # Milestones
+ create(:milestone,
+ due_date: 10.days.from_now,
+ created_at: 2.hours.ago,
+ title: "aaa", project: project)
+ create(:milestone,
+ due_date: 11.days.from_now,
+ created_at: 1.hour.ago,
+ title: "bbb", project: project)
+ login_as(user)
+ end
+
+ scenario 'visit project milestones and sort by due_date_asc' do
+ visit namespace_project_milestones_path(project.namespace, project)
+
+ expect(page).to have_button('Due soon')
+
+ # assert default sorting
+ within '.milestones' do
+ expect(page.all('ul.content-list > li').first.text).to include('aaa')
+ expect(page.all('ul.content-list > li').last.text).to include('bbb')
+ end
+
+ click_button 'Due soon'
+
+ sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text)
+
+ expect(sort_options[0]).to eq('Due soon')
+ expect(sort_options[1]).to eq('Due later')
+ expect(sort_options[2]).to eq('Start soon')
+ expect(sort_options[3]).to eq('Start later')
+ expect(sort_options[4]).to eq('Name, ascending')
+ expect(sort_options[5]).to eq('Name, descending')
+
+ click_link 'Due later'
+
+ expect(page).to have_button('Due later')
+
+ within '.milestones' do
+ expect(page.all('ul.content-list > li').first.text).to include('bbb')
+ expect(page.all('ul.content-list > li').last.text).to include('aaa')
+ end
+ end
+end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 9f06e52ab55..5a53e48f5f8 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -45,7 +45,7 @@ describe 'Pipeline', :feature, :js do
include_context 'pipeline builds'
let(:project) { create(:project) }
- let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) }
before { visit namespace_project_pipeline_path(project.namespace, project, pipeline) }
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 162056671e0..2272b19bc8f 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -442,7 +442,7 @@ describe 'Pipelines', :feature, :js do
context 'when project is public' do
let(:project) { create(:project, :public) }
- it { expect(page).to have_content 'No pipelines to show' }
+ it { expect(page).to have_content 'Build with confidence' }
it { expect(page).to have_http_status(:success) }
end
diff --git a/spec/features/projects/user_create_dir_spec.rb b/spec/features/projects/user_create_dir_spec.rb
new file mode 100644
index 00000000000..2065abfb248
--- /dev/null
+++ b/spec/features/projects/user_create_dir_spec.rb
@@ -0,0 +1,72 @@
+require 'spec_helper'
+
+feature 'New directory creation', feature: true, js: true do
+ include WaitForAjax
+ include TargetBranchHelpers
+
+ given(:user) { create(:user) }
+ given(:role) { :developer }
+ given(:project) { create(:project) }
+
+ background do
+ login_as(user)
+ project.team << [user, role]
+ visit namespace_project_tree_path(project.namespace, project, 'master')
+ open_new_directory_modal
+ fill_in 'dir_name', with: 'new_directory'
+ end
+
+ def open_new_directory_modal
+ first('.add-to-tree').click
+ click_link 'New directory'
+ end
+
+ def create_directory
+ click_button 'Create directory'
+ end
+
+ context 'with default target branch' do
+ background do
+ create_directory
+ end
+
+ scenario 'creates the directory in the default branch' do
+ expect(page).to have_content 'master'
+ expect(page).to have_content 'The directory has been successfully created'
+ expect(page).to have_content 'new_directory'
+ end
+ end
+
+ context 'with different target branch' do
+ background do
+ select_branch('feature')
+ create_directory
+ end
+
+ scenario 'creates the directory in the different branch' do
+ expect(page).to have_content 'feature'
+ expect(page).to have_content 'The directory has been successfully created'
+ end
+ end
+
+ context 'with a new target branch' do
+ given(:new_branch_name) { 'new-feature' }
+
+ background do
+ create_new_branch(new_branch_name)
+ create_directory
+ end
+
+ scenario 'creates the directory in the new branch' do
+ expect(page).to have_content new_branch_name
+ expect(page).to have_content 'The directory has been successfully created'
+ end
+
+ scenario 'redirects to the merge request' do
+ expect(page).to have_content 'New Merge Request'
+ expect(page).to have_content "From #{new_branch_name} into master"
+ expect(page).to have_content 'Add new directory'
+ expect(current_path).to eq(new_namespace_project_merge_request_path(project.namespace, project))
+ end
+ end
+end
diff --git a/spec/features/user_callout_spec.rb b/spec/features/user_callout_spec.rb
index 336c4092c98..659cd7c7af7 100644
--- a/spec/features/user_callout_spec.rb
+++ b/spec/features/user_callout_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe 'User Callouts', js: true do
let(:user) { create(:user) }
+ let(:another_user) { create(:user) }
let(:project) { create(:empty_project, path: 'gitlab', name: 'sample') }
before do
@@ -32,6 +33,11 @@ describe 'User Callouts', js: true do
within('.user-callout') do
find('.close-user-callout').click
end
- expect(page).not_to have_selector('#user-callout')
+ expect(page).not_to have_selector('.user-callout')
+ end
+
+ it 'does not show callout on another users profile' do
+ visit user_path(another_user)
+ expect(page).not_to have_selector('.user-callout')
end
end
diff --git a/spec/features/users/projects_spec.rb b/spec/features/users/projects_spec.rb
new file mode 100644
index 00000000000..1d75fe434b0
--- /dev/null
+++ b/spec/features/users/projects_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe 'Projects tab on a user profile', :feature, :js do
+ include WaitForAjax
+
+ let(:user) { create(:user) }
+ let!(:project) { create(:empty_project, namespace: user.namespace) }
+ let!(:project2) { create(:empty_project, namespace: user.namespace) }
+
+ before do
+ allow(Project).to receive(:default_per_page).and_return(1)
+
+ login_as(user)
+
+ visit user_path(user)
+
+ page.within('.user-profile-nav') do
+ click_link('Personal projects')
+ end
+
+ wait_for_ajax
+ end
+
+ it 'paginates results' do
+ expect(page).to have_content(project2.name)
+
+ click_link('Next')
+
+ expect(page).to have_content(project.name)
+ end
+end
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
new file mode 100644
index 00000000000..581726c1d0e
--- /dev/null
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -0,0 +1,21 @@
+require 'rails_helper'
+
+describe AvatarsHelper do
+ let(:user) { create(:user) }
+
+ describe '#user_avatar' do
+ subject { helper.user_avatar(user: user) }
+
+ it "links to the user's profile" do
+ is_expected.to include("href=\"#{user_path(user)}\"")
+ end
+
+ it "has the user's name as title" do
+ is_expected.to include("title=\"#{user.name}\"")
+ end
+
+ it "contains the user's avatar image" do
+ is_expected.to include(CGI.escapeHTML(user.avatar_url(16)))
+ end
+ end
+end
diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb
new file mode 100644
index 00000000000..e5143a0263d
--- /dev/null
+++ b/spec/helpers/namespaces_helper_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe NamespacesHelper, type: :helper do
+ let!(:admin) { create(:admin) }
+ let!(:admin_group) { create(:group, :private) }
+ let!(:user) { create(:user) }
+ let!(:user_group) { create(:group, :private) }
+
+ before do
+ admin_group.add_owner(admin)
+ user_group.add_owner(user)
+ end
+
+ describe '#namespaces_options' do
+ it 'returns groups without being a member for admin' do
+ allow(helper).to receive(:current_user).and_return(admin)
+
+ options = helper.namespaces_options(user_group.id, display_path: true, extra_group: user_group.id)
+
+ expect(options).to include(admin_group.name)
+ expect(options).to include(user_group.name)
+ end
+
+ it 'returns only allowed namespaces for user' do
+ allow(helper).to receive(:current_user).and_return(user)
+
+ options = helper.namespaces_options
+
+ expect(options).not_to include(admin_group.name)
+ expect(options).to include(user_group.name)
+ end
+ end
+end
diff --git a/spec/helpers/todos_helper_spec.rb b/spec/helpers/todos_helper_spec.rb
index 21e0e74e008..50060a0925d 100644
--- a/spec/helpers/todos_helper_spec.rb
+++ b/spec/helpers/todos_helper_spec.rb
@@ -1,40 +1,6 @@
require "spec_helper"
describe TodosHelper do
- include GitlabRoutingHelper
-
- describe '#todo_target_path' do
- let(:project) { create(:project) }
- let(:merge_request) { create(:merge_request, target_project: project, source_project: project) }
- let(:issue) { create(:issue, project: project) }
- let(:note) { create(:note_on_issue, noteable: issue, project: project) }
-
- let(:mr_todo) { build(:todo, project: project, target: merge_request) }
- let(:issue_todo) { build(:todo, project: project, target: issue) }
- let(:note_todo) { build(:todo, project: project, target: issue, note: note) }
- let(:build_failed_todo) { build(:todo, :build_failed, project: project, target: merge_request) }
-
- it 'returns correct path to the todo MR' do
- expect(todo_target_path(mr_todo)).
- to eq("/#{project.full_path}/merge_requests/#{merge_request.iid}")
- end
-
- it 'returns correct path to the todo issue' do
- expect(todo_target_path(issue_todo)).
- to eq("/#{project.full_path}/issues/#{issue.iid}")
- end
-
- it 'returns correct path to the todo note' do
- expect(todo_target_path(note_todo)).
- to eq("/#{project.full_path}/issues/#{issue.iid}#note_#{note.id}")
- end
-
- it 'returns correct path to build_todo MR when pipeline failed' do
- expect(todo_target_path(build_failed_todo)).
- to eq("/#{project.full_path}/merge_requests/#{merge_request.iid}/pipelines")
- end
- end
-
describe '#todo_projects_options' do
let(:projects) { create_list(:empty_project, 3) }
let(:user) { create(:user) }
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
new file mode 100644
index 00000000000..03f78de8e91
--- /dev/null
+++ b/spec/helpers/users_helper_spec.rb
@@ -0,0 +1,17 @@
+require 'rails_helper'
+
+describe UsersHelper do
+ let(:user) { create(:user) }
+
+ describe '#user_link' do
+ subject { helper.user_link(user) }
+
+ it "links to the user's profile" do
+ is_expected.to include("href=\"#{user_path(user)}\"")
+ end
+
+ it "has the user's email as title" do
+ is_expected.to include("title=\"#{user.email}\"")
+ end
+ end
+end
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index be31f644e20..73d18458366 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -1,10 +1,11 @@
-/* global Vue */
/* global List */
/* global ListLabel */
/* global listObj */
/* global boardsMockInterceptor */
/* global BoardService */
+import Vue from 'vue';
+
require('~/boards/models/list');
require('~/boards/models/label');
require('~/boards/stores/boards_store');
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 1d1069600fc..e21f4ca2bc0 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -1,12 +1,13 @@
/* eslint-disable comma-dangle, one-var, no-unused-vars */
-/* global Vue */
/* global BoardService */
/* global boardsMockInterceptor */
-/* global Cookies */
/* global listObj */
/* global listObjDuplicate */
/* global ListIssue */
+import Vue from 'vue';
+import Cookies from 'js-cookie';
+
require('~/lib/utils/url_utility');
require('~/boards/models/issue');
require('~/boards/models/label');
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index 4340a571017..1a5e9e9fd07 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -1,9 +1,10 @@
-/* global Vue */
/* global ListUser */
/* global ListLabel */
/* global listObj */
/* global ListIssue */
+import Vue from 'vue';
+
require('~/boards/models/issue');
require('~/boards/models/label');
require('~/boards/models/list');
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index d49d3af33d9..66fc01fa1e5 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -1,5 +1,4 @@
/* eslint-disable comma-dangle */
-/* global Vue */
/* global boardsMockInterceptor */
/* global BoardService */
/* global List */
@@ -7,6 +6,8 @@
/* global listObj */
/* global listObjDuplicate */
+import Vue from 'vue';
+
require('~/lib/utils/url_utility');
require('~/boards/models/issue');
require('~/boards/models/label');
diff --git a/spec/javascripts/boards/modal_store_spec.js b/spec/javascripts/boards/modal_store_spec.js
index 1815847f3fa..80db816aff8 100644
--- a/spec/javascripts/boards/modal_store_spec.js
+++ b/spec/javascripts/boards/modal_store_spec.js
@@ -1,4 +1,3 @@
-/* global Vue */
/* global ListIssue */
require('~/boards/models/issue');
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index 75efcc06585..bc2e092db65 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -33,7 +33,8 @@ describe('Pipelines table in Commits and Merge requests', () => {
});
setTimeout(() => {
- expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain('No pipelines to show');
+ expect(component.$el.querySelector('.empty-state')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
done();
}, 1);
});
@@ -63,6 +64,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
setTimeout(() => {
expect(component.$el.querySelectorAll('table > tbody > tr').length).toEqual(1);
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
done();
}, 0);
});
@@ -92,7 +94,8 @@ describe('Pipelines table in Commits and Merge requests', () => {
});
setTimeout(() => {
- expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain('No pipelines to show');
+ expect(component.$el.querySelector('.js-pipelines-error-state')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
done();
}, 0);
});
diff --git a/spec/javascripts/cycle_analytics/limit_warning_component_spec.js b/spec/javascripts/cycle_analytics/limit_warning_component_spec.js
new file mode 100644
index 00000000000..50000c5a5f5
--- /dev/null
+++ b/spec/javascripts/cycle_analytics/limit_warning_component_spec.js
@@ -0,0 +1,39 @@
+import Vue from 'vue';
+import limitWarningComp from '~/cycle_analytics/components/limit_warning_component';
+
+describe('Limit warning component', () => {
+ let component;
+ let LimitWarningComponent;
+
+ beforeEach(() => {
+ LimitWarningComponent = Vue.extend(limitWarningComp);
+ });
+
+ it('should not render if count is not exactly than 50', () => {
+ component = new LimitWarningComponent({
+ propsData: {
+ count: 5,
+ },
+ }).$mount();
+
+ expect(component.$el.textContent.trim()).toBe('');
+
+ component = new LimitWarningComponent({
+ propsData: {
+ count: 55,
+ },
+ }).$mount();
+
+ expect(component.$el.textContent.trim()).toBe('');
+ });
+
+ it('should render if count is exactly 50', () => {
+ component = new LimitWarningComponent({
+ propsData: {
+ count: 50,
+ },
+ }).$mount();
+
+ expect(component.$el.textContent.trim()).toBe('Showing 50 events');
+ });
+});
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 113161c21c6..848c7656a8d 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -58,7 +58,7 @@ const FilteredSearchSpecHelper = require('../helpers/filtered_search_spec_helper
});
describe('search', () => {
- const defaultParams = '?scope=all&utf8=✓&state=opened';
+ const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
it('should search with a single word', (done) => {
input.value = 'searchTerm';
diff --git a/spec/javascripts/fixtures/pipelines.html.haml b/spec/javascripts/fixtures/pipelines.html.haml
new file mode 100644
index 00000000000..418a38a0e2e
--- /dev/null
+++ b/spec/javascripts/fixtures/pipelines.html.haml
@@ -0,0 +1,14 @@
+%div
+ #pipelines-list-vue{ data: { endpoint: 'foo',
+ "css-class" => 'foo',
+ "help-page-path" => 'foo',
+ "new-pipeline-path" => 'foo',
+ "can-create-pipeline" => 'true',
+ "all-path" => 'foo',
+ "pending-path" => 'foo',
+ "running-path" => 'foo',
+ "finished-path" => 'foo',
+ "branches-path" => 'foo',
+ "tags-path" => 'foo',
+ "has-ci" => 'foo',
+ "ci-lint-path" => 'foo' } }
diff --git a/spec/javascripts/fixtures/pipelines_table.html.haml b/spec/javascripts/fixtures/pipelines_table.html.haml
index fbe4a434f76..ad1682704bb 100644
--- a/spec/javascripts/fixtures/pipelines_table.html.haml
+++ b/spec/javascripts/fixtures/pipelines_table.html.haml
@@ -1,2 +1 @@
-#commit-pipeline-table-view{ data: { endpoint: "endpoint" } }
-.pipeline-svgs{ data: { "commit_icon_svg": "svg"} }
+#commit-pipeline-table-view{ data: { endpoint: "endpoint", "help-page-path": "foo" } }
diff --git a/spec/javascripts/issuable_time_tracker_spec.js b/spec/javascripts/issuable_time_tracker_spec.js
index cb068a4f879..0a830f25e29 100644
--- a/spec/javascripts/issuable_time_tracker_spec.js
+++ b/spec/javascripts/issuable_time_tracker_spec.js
@@ -1,7 +1,7 @@
-/* eslint-disable */
+/* eslint-disable no-unused-vars, space-before-function-paren, func-call-spacing, no-spaced-func, semi, max-len, quotes, space-infix-ops, padded-blocks */
+
+import Vue from 'vue';
-require('jquery');
-require('vue');
require('~/issuable/time_tracking/components/time_tracker');
function initTimeTrackingComponent(opts) {
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index f4d3e77e515..d2e24eb7eb2 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -163,5 +163,72 @@ require('~/lib/utils/common_utils');
expect(gl.utils.isMetaClick(e)).toBe(true);
});
});
+
+ describe('gl.utils.backOff', () => {
+ it('solves the promise from the callback', (done) => {
+ const expectedResponseValue = 'Success!';
+ gl.utils.backOff((next, stop) => (
+ new Promise((resolve) => {
+ resolve(expectedResponseValue);
+ }).then((resp) => {
+ stop(resp);
+ })
+ )).then((respBackoff) => {
+ expect(respBackoff).toBe(expectedResponseValue);
+ done();
+ });
+ });
+
+ it('catches the rejected promise from the callback ', (done) => {
+ const errorMessage = 'Mistakes were made!';
+ gl.utils.backOff((next, stop) => {
+ new Promise((resolve, reject) => {
+ reject(new Error(errorMessage));
+ }).then((resp) => {
+ stop(resp);
+ }).catch(err => stop(err));
+ }).catch((errBackoffResp) => {
+ expect(errBackoffResp instanceof Error).toBe(true);
+ expect(errBackoffResp.message).toBe(errorMessage);
+ done();
+ });
+ });
+
+ it('solves the promise correctly after retrying a third time', (done) => {
+ let numberOfCalls = 1;
+ const expectedResponseValue = 'Success!';
+ gl.utils.backOff((next, stop) => (
+ new Promise((resolve) => {
+ resolve(expectedResponseValue);
+ }).then((resp) => {
+ if (numberOfCalls < 3) {
+ numberOfCalls += 1;
+ next();
+ } else {
+ stop(resp);
+ }
+ })
+ )).then((respBackoff) => {
+ expect(respBackoff).toBe(expectedResponseValue);
+ expect(numberOfCalls).toBe(3);
+ done();
+ });
+ }, 10000);
+
+ it('rejects the backOff promise after timing out', (done) => {
+ const expectedResponseValue = 'Success!';
+ gl.utils.backOff(next => (
+ new Promise((resolve) => {
+ resolve(expectedResponseValue);
+ }).then(() => {
+ setTimeout(next(), 5000); // it will time out
+ })
+ ), 3000).catch((errBackoffResp) => {
+ expect(errBackoffResp instanceof Error).toBe(true);
+ expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT');
+ done();
+ });
+ }, 10000);
+ });
});
})();
diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js
new file mode 100644
index 00000000000..c794a632417
--- /dev/null
+++ b/spec/javascripts/lib/utils/poll_spec.js
@@ -0,0 +1,163 @@
+import Vue from 'vue';
+import VueResource from 'vue-resource';
+import Poll from '~/lib/utils/poll';
+
+Vue.use(VueResource);
+
+class ServiceMock {
+ constructor(endpoint) {
+ this.service = Vue.resource(endpoint);
+ }
+
+ fetch() {
+ return this.service.get();
+ }
+}
+
+describe('Poll', () => {
+ let callbacks;
+
+ beforeEach(() => {
+ callbacks = {
+ success: () => {},
+ error: () => {},
+ };
+
+ spyOn(callbacks, 'success');
+ spyOn(callbacks, 'error');
+ });
+
+ it('calls the success callback when no header for interval is provided', (done) => {
+ const successInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200 }));
+ };
+
+ Vue.http.interceptors.push(successInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, successInterceptor);
+ });
+
+ it('calls the error callback whe the http request returns an error', (done) => {
+ const errorInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 500 }));
+ };
+
+ Vue.http.interceptors.push(errorInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).not.toHaveBeenCalled();
+ expect(callbacks.error).toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, errorInterceptor);
+ });
+
+ it('should call the success callback when the interval header is -1', (done) => {
+ const intervalInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': -1 } }));
+ };
+
+ Vue.http.interceptors.push(intervalInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, intervalInterceptor);
+ });
+
+ it('starts polling when http status is 200 and interval header is provided', (done) => {
+ const pollInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': 2 } }));
+ };
+
+ Vue.http.interceptors.push(pollInterceptor);
+
+ const service = new ServiceMock('endpoint');
+ spyOn(service, 'fetch').and.callThrough();
+
+ new Poll({
+ resource: service,
+ method: 'fetch',
+ data: { page: 1 },
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(service.fetch.calls.count()).toEqual(2);
+ expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 5);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+ });
+
+ describe('stop', () => {
+ it('stops polling when method is called', (done) => {
+ const pollInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': 2 } }));
+ };
+
+ Vue.http.interceptors.push(pollInterceptor);
+
+ const service = new ServiceMock('endpoint');
+ spyOn(service, 'fetch').and.callThrough();
+
+ const Polling = new Poll({
+ resource: service,
+ method: 'fetch',
+ data: { page: 1 },
+ successCallback: () => {
+ Polling.stop();
+ },
+ errorCallback: callbacks.error,
+ });
+
+ spyOn(Polling, 'stop').and.callThrough();
+
+ Polling.makeRequest();
+
+ setTimeout(() => {
+ expect(service.fetch.calls.count()).toEqual(1);
+ expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
+ expect(Polling.stop).toHaveBeenCalled();
+ done();
+ }, 100);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+ });
+ });
+});
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 5cdb6473eda..d658f680f97 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -8,9 +8,6 @@ jasmine.getJSONFixtures().fixturesPath = 'base/spec/javascripts/fixtures';
require('~/commons/index.js');
window.$ = window.jQuery = require('jquery');
window._ = require('underscore');
-window.Cookies = require('js-cookie');
-window.Vue = require('vue');
-window.Vue.use(require('vue-resource'));
// stub expected globals
window.gl = window.gl || {};
@@ -38,7 +35,8 @@ testsContext.keys().forEach(function (path) {
if (process.env.BABEL_ENV === 'coverage') {
// exempt these files from the coverage report
const troubleMakers = [
- './blob_edit/blob_edit_bundle.js',
+ './blob_edit/blob_bundle.js',
+ './boards/boards_bundle.js',
'./cycle_analytics/components/stage_plan_component.js',
'./cycle_analytics/components/stage_staging_component.js',
'./cycle_analytics/components/stage_test_component.js',
diff --git a/spec/javascripts/user_callout_spec.js b/spec/javascripts/user_callout_spec.js
index 205e72af600..2398149d3ad 100644
--- a/spec/javascripts/user_callout_spec.js
+++ b/spec/javascripts/user_callout_spec.js
@@ -1,7 +1,7 @@
-const UserCallout = require('~/user_callout');
+import Cookies from 'js-cookie';
+import UserCallout from '~/user_callout';
const USER_CALLOUT_COOKIE = 'user_callout_dismissed';
-const Cookie = window.Cookies;
describe('UserCallout', function () {
const fixtureName = 'static/user_callout.html.raw';
@@ -9,7 +9,7 @@ describe('UserCallout', function () {
beforeEach(() => {
loadFixtures(fixtureName);
- Cookie.remove(USER_CALLOUT_COOKIE);
+ Cookies.remove(USER_CALLOUT_COOKIE);
this.userCallout = new UserCallout();
this.closeButton = $('.close-user-callout');
@@ -18,25 +18,25 @@ describe('UserCallout', function () {
});
it('does not show when cookie is set not defined', () => {
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeUndefined();
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBeUndefined();
expect(this.userCalloutContainer.is(':visible')).toBe(true);
});
it('shows when cookie is set to false', () => {
- Cookie.set(USER_CALLOUT_COOKIE, 'false');
+ Cookies.set(USER_CALLOUT_COOKIE, 'false');
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeDefined();
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBeDefined();
expect(this.userCalloutContainer.is(':visible')).toBe(true);
});
it('hides when user clicks on the dismiss-icon', () => {
this.closeButton.click();
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBe('true');
});
it('hides when user clicks on the "check it out" button', () => {
this.userCalloutBtn.click();
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBe('true');
});
});
@@ -46,7 +46,7 @@ describe('UserCallout when cookie is present', function () {
beforeEach(() => {
loadFixtures(fixtureName);
- Cookie.set(USER_CALLOUT_COOKIE, 'true');
+ Cookies.set(USER_CALLOUT_COOKIE, 'true');
this.userCallout = new UserCallout();
this.userCalloutContainer = $('.user-callout');
});
diff --git a/spec/javascripts/vue_pipelines_index/empty_state_spec.js b/spec/javascripts/vue_pipelines_index/empty_state_spec.js
new file mode 100644
index 00000000000..733337168dc
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/empty_state_spec.js
@@ -0,0 +1,38 @@
+import Vue from 'vue';
+import emptyStateComp from '~/vue_pipelines_index/components/empty_state';
+
+describe('Pipelines Empty State', () => {
+ let component;
+ let EmptyStateComponent;
+
+ beforeEach(() => {
+ EmptyStateComponent = Vue.extend(emptyStateComp);
+
+ component = new EmptyStateComponent({
+ propsData: {
+ helpPagePath: 'foo',
+ },
+ }).$mount();
+ });
+
+ it('should render empty state SVG', () => {
+ expect(component.$el.querySelector('.svg-content svg')).toBeDefined();
+ });
+
+ it('should render emtpy state information', () => {
+ expect(component.$el.querySelector('h4').textContent).toContain('Build with confidence');
+
+ expect(
+ component.$el.querySelector('p').textContent,
+ ).toContain('Continous Integration can help catch bugs by running your tests automatically');
+
+ expect(
+ component.$el.querySelector('p').textContent,
+ ).toContain('Continuous Deployment can help you deliver code to your product environment');
+ });
+
+ it('should render a link with provided help path', () => {
+ expect(component.$el.querySelector('.btn-info').getAttribute('href')).toEqual('foo');
+ expect(component.$el.querySelector('.btn-info').textContent).toContain('Get started with Pipelines');
+ });
+});
diff --git a/spec/javascripts/vue_pipelines_index/error_state_spec.js b/spec/javascripts/vue_pipelines_index/error_state_spec.js
new file mode 100644
index 00000000000..524e018b1fa
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/error_state_spec.js
@@ -0,0 +1,23 @@
+import Vue from 'vue';
+import errorStateComp from '~/vue_pipelines_index/components/error_state';
+
+describe('Pipelines Error State', () => {
+ let component;
+ let ErrorStateComponent;
+
+ beforeEach(() => {
+ ErrorStateComponent = Vue.extend(errorStateComp);
+
+ component = new ErrorStateComponent().$mount();
+ });
+
+ it('should render error state SVG', () => {
+ expect(component.$el.querySelector('.svg-content svg')).toBeDefined();
+ });
+
+ it('should render emtpy state information', () => {
+ expect(
+ component.$el.querySelector('h4').textContent,
+ ).toContain('The API failed to fetch the pipelines');
+ });
+});
diff --git a/spec/javascripts/vue_pipelines_index/mock_data.js b/spec/javascripts/vue_pipelines_index/mock_data.js
new file mode 100644
index 00000000000..2365a662b9f
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/mock_data.js
@@ -0,0 +1,107 @@
+export default {
+ pipelines: [{
+ id: 115,
+ user: {
+ name: 'Root',
+ username: 'root',
+ id: 1,
+ state: 'active',
+ avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3000/root',
+ },
+ path: '/root/review-app/pipelines/115',
+ details: {
+ status: {
+ icon: 'icon_status_failed',
+ text: 'failed',
+ label: 'failed',
+ group: 'failed',
+ has_details: true,
+ details_path: '/root/review-app/pipelines/115',
+ },
+ duration: null,
+ finished_at: '2017-03-17T19:00:15.996Z',
+ stages: [{
+ name: 'build',
+ title: 'build: failed',
+ status: {
+ icon: 'icon_status_failed',
+ text: 'failed',
+ label: 'failed',
+ group: 'failed',
+ has_details: true,
+ details_path: '/root/review-app/pipelines/115#build',
+ },
+ path: '/root/review-app/pipelines/115#build',
+ dropdown_path: '/root/review-app/pipelines/115/stage.json?stage=build',
+ },
+ {
+ name: 'review',
+ title: 'review: skipped',
+ status: {
+ icon: 'icon_status_skipped',
+ text: 'skipped',
+ label: 'skipped',
+ group: 'skipped',
+ has_details: true,
+ details_path: '/root/review-app/pipelines/115#review',
+ },
+ path: '/root/review-app/pipelines/115#review',
+ dropdown_path: '/root/review-app/pipelines/115/stage.json?stage=review',
+ }],
+ artifacts: [],
+ manual_actions: [{
+ name: 'stop_review',
+ path: '/root/review-app/builds/3766/play',
+ }],
+ },
+ flags: {
+ latest: true,
+ triggered: false,
+ stuck: false,
+ yaml_errors: false,
+ retryable: true,
+ cancelable: false,
+ },
+ ref: {
+ name: 'thisisabranch',
+ path: '/root/review-app/tree/thisisabranch',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: '9e87f87625b26c42c59a2ee0398f81d20cdfe600',
+ short_id: '9e87f876',
+ title: 'Update README.md',
+ created_at: '2017-03-15T22:58:28.000+00:00',
+ parent_ids: ['3744f9226e699faec2662a8b267e5d3fd0bfff0e'],
+ message: 'Update README.md',
+ author_name: 'Root',
+ author_email: 'admin@example.com',
+ authored_date: '2017-03-15T22:58:28.000+00:00',
+ committer_name: 'Root',
+ committer_email: 'admin@example.com',
+ committed_date: '2017-03-15T22:58:28.000+00:00',
+ author: {
+ name: 'Root',
+ username: 'root',
+ id: 1,
+ state: 'active',
+ avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3000/root',
+ },
+ author_gravatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ commit_url: 'http://localhost:3000/root/review-app/commit/9e87f87625b26c42c59a2ee0398f81d20cdfe600',
+ commit_path: '/root/review-app/commit/9e87f87625b26c42c59a2ee0398f81d20cdfe600',
+ },
+ retry_path: '/root/review-app/pipelines/115/retry',
+ created_at: '2017-03-15T22:58:33.436Z',
+ updated_at: '2017-03-17T19:00:15.997Z',
+ }],
+ count: {
+ all: 52,
+ running: 0,
+ pending: 0,
+ finished: 52,
+ },
+};
diff --git a/spec/javascripts/vue_pipelines_index/nav_controls_spec.js b/spec/javascripts/vue_pipelines_index/nav_controls_spec.js
new file mode 100644
index 00000000000..659c4854a56
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/nav_controls_spec.js
@@ -0,0 +1,93 @@
+import Vue from 'vue';
+import navControlsComp from '~/vue_pipelines_index/components/nav_controls';
+
+describe('Pipelines Nav Controls', () => {
+ let NavControlsComponent;
+
+ beforeEach(() => {
+ NavControlsComponent = Vue.extend(navControlsComp);
+ });
+
+ it('should render link to create a new pipeline', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: true,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ canCreatePipeline: true,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelector('.btn-create').textContent).toContain('Run Pipeline');
+ expect(component.$el.querySelector('.btn-create').getAttribute('href')).toEqual(mockData.newPipelinePath);
+ });
+
+ it('should not render link to create pipeline if no permission is provided', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: true,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ canCreatePipeline: false,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelector('.btn-create')).toEqual(null);
+ });
+
+ it('should render link for CI lint', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: true,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ canCreatePipeline: true,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelector('.btn-default').textContent).toContain('CI Lint');
+ expect(component.$el.querySelector('.btn-default').getAttribute('href')).toEqual(mockData.ciLintPath);
+ });
+
+ it('should render link to help page when CI is not enabled', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: false,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ canCreatePipeline: true,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelector('.btn-info').textContent).toContain('Get started with Pipelines');
+ expect(component.$el.querySelector('.btn-info').getAttribute('href')).toEqual(mockData.helpPagePath);
+ });
+
+ it('should not render link to help page when CI is enabled', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: true,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ canCreatePipeline: true,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelector('.btn-info')).toEqual(null);
+ });
+});
diff --git a/spec/javascripts/vue_pipelines_index/pipelines_spec.js b/spec/javascripts/vue_pipelines_index/pipelines_spec.js
new file mode 100644
index 00000000000..725f6cb2d7a
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/pipelines_spec.js
@@ -0,0 +1,114 @@
+import Vue from 'vue';
+import pipelinesComp from '~/vue_pipelines_index/pipelines';
+import Store from '~/vue_pipelines_index/stores/pipelines_store';
+import pipelinesData from './mock_data';
+
+describe('Pipelines', () => {
+ preloadFixtures('static/pipelines.html.raw');
+
+ let PipelinesComponent;
+
+ beforeEach(() => {
+ loadFixtures('static/pipelines.html.raw');
+
+ PipelinesComponent = Vue.extend(pipelinesComp);
+ });
+
+ describe('successfull request', () => {
+ describe('with pipelines', () => {
+ const pipelinesInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify(pipelinesData), {
+ status: 200,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(pipelinesInterceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors, pipelinesInterceptor,
+ );
+ });
+
+ it('should render table', (done) => {
+ const component = new PipelinesComponent({
+ propsData: {
+ store: new Store(),
+ },
+ }).$mount();
+
+ setTimeout(() => {
+ expect(component.$el.querySelector('.table-holder')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
+ done();
+ });
+ });
+ });
+
+ describe('without pipelines', () => {
+ const emptyInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), {
+ status: 200,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(emptyInterceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors, emptyInterceptor,
+ );
+ });
+
+ it('should render empty state', (done) => {
+ const component = new PipelinesComponent({
+ propsData: {
+ store: new Store(),
+ },
+ }).$mount();
+
+ setTimeout(() => {
+ expect(component.$el.querySelector('.empty-state')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('unsuccessfull request', () => {
+ const errorInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), {
+ status: 500,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(errorInterceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors, errorInterceptor,
+ );
+ });
+
+ it('should render error state', (done) => {
+ const component = new PipelinesComponent({
+ propsData: {
+ store: new Store(),
+ },
+ }).$mount();
+
+ setTimeout(() => {
+ expect(component.$el.querySelector('.js-pipelines-error-state')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/lib/gitlab/ci/build/step_spec.rb b/spec/lib/gitlab/ci/build/step_spec.rb
index 2a314a744ca..49457b129e3 100644
--- a/spec/lib/gitlab/ci/build/step_spec.rb
+++ b/spec/lib/gitlab/ci/build/step_spec.rb
@@ -25,7 +25,7 @@ describe Gitlab::Ci::Build::Step do
end
context 'when after_script is not empty' do
- let(:job) { create(:ci_build, options: { after_script: "ls -la\ndate" }) }
+ let(:job) { create(:ci_build, options: { after_script: ['ls -la', 'date'] }) }
it 'fabricates an object' do
expect(subject.name).to eq(:after_script)
diff --git a/spec/lib/gitlab/git/blob_snippet_spec.rb b/spec/lib/gitlab/git/blob_snippet_spec.rb
index 17d6be470ac..d6d365f6492 100644
--- a/spec/lib/gitlab/git/blob_snippet_spec.rb
+++ b/spec/lib/gitlab/git/blob_snippet_spec.rb
@@ -3,7 +3,7 @@
require "spec_helper"
describe Gitlab::Git::BlobSnippet, seed_helper: true do
- describe :data do
+ describe '#data' do
context 'empty lines' do
let(:snippet) { Gitlab::Git::BlobSnippet.new('master', nil, nil, nil) }
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 8049e2c120d..b883526151e 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -5,7 +5,7 @@ require "spec_helper"
describe Gitlab::Git::Blob, seed_helper: true do
let(:repository) { Gitlab::Git::Repository.new(TEST_REPO_PATH) }
- describe :initialize do
+ describe 'initialize' do
let(:blob) { Gitlab::Git::Blob.new(name: 'test') }
it 'handles nil data' do
@@ -15,7 +15,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
end
- describe :find do
+ describe '.find' do
context 'file in subdir' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") }
@@ -101,7 +101,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
end
- describe :raw do
+ describe '.raw' do
let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) }
it { expect(raw_blob.id).to eq(SeedRepo::RubyBlob::ID) }
it { expect(raw_blob.data[0..10]).to eq("require \'fi") }
@@ -222,7 +222,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
end
- describe :lfs_pointers do
+ describe 'lfs_pointers' do
context 'file a valid lfs pointer' do
let(:blob) do
Gitlab::Git::Blob.find(
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index e1be6784c20..5cf4631fbfc 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -65,7 +65,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
context 'Class methods' do
- describe :find do
+ describe '.find' do
it "should return first head commit if without params" do
expect(Gitlab::Git::Commit.last(repository).id).to eq(
repository.raw.head.target.oid
@@ -103,7 +103,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :last_for_path do
+ describe '.last_for_path' do
context 'no path' do
subject { Gitlab::Git::Commit.last_for_path(repository, 'master') }
@@ -132,7 +132,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe "where" do
+ describe '.where' do
context 'path is empty string' do
subject do
commits = Gitlab::Git::Commit.where(
@@ -230,7 +230,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :between do
+ describe '.between' do
subject do
commits = Gitlab::Git::Commit.between(repository, SeedRepo::Commit::PARENT_ID, SeedRepo::Commit::ID)
commits.map { |c| c.id }
@@ -243,7 +243,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
it { is_expected.not_to include(SeedRepo::FirstCommit::ID) }
end
- describe :find_all do
+ describe '.find_all' do
context 'max_count' do
subject do
commits = Gitlab::Git::Commit.find_all(
@@ -304,7 +304,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :init_from_rugged do
+ describe '#init_from_rugged' do
let(:gitlab_commit) { Gitlab::Git::Commit.new(rugged_commit) }
subject { gitlab_commit }
@@ -314,7 +314,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :init_from_hash do
+ describe '#init_from_hash' do
let(:commit) { Gitlab::Git::Commit.new(sample_commit_hash) }
subject { commit }
@@ -329,7 +329,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :stats do
+ describe '#stats' do
subject { commit.stats }
describe '#additions' do
@@ -343,25 +343,25 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :to_diff do
+ describe '#to_diff' do
subject { commit.to_diff }
it { is_expected.not_to include "From #{SeedRepo::Commit::ID}" }
it { is_expected.to include 'diff --git a/files/ruby/popen.rb b/files/ruby/popen.rb'}
end
- describe :has_zero_stats? do
+ describe '#has_zero_stats?' do
it { expect(commit.has_zero_stats?).to eq(false) }
end
- describe :to_patch do
+ describe '#to_patch' do
subject { commit.to_patch }
it { is_expected.to include "From #{SeedRepo::Commit::ID}" }
it { is_expected.to include 'diff --git a/files/ruby/popen.rb b/files/ruby/popen.rb'}
end
- describe :to_hash do
+ describe '#to_hash' do
let(:hash) { commit.to_hash }
subject { hash }
@@ -373,7 +373,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :diffs do
+ describe '#diffs' do
subject { commit.diffs }
it { is_expected.to be_kind_of Gitlab::Git::DiffCollection }
@@ -381,7 +381,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
it { expect(subject.first).to be_kind_of Gitlab::Git::Diff }
end
- describe :ref_names do
+ describe '#ref_names' do
let(:commit) { Gitlab::Git::Commit.find(repository, 'master') }
subject { commit.ref_names(repository) }
diff --git a/spec/lib/gitlab/git/compare_spec.rb b/spec/lib/gitlab/git/compare_spec.rb
index f66b68e4218..e28debe1494 100644
--- a/spec/lib/gitlab/git/compare_spec.rb
+++ b/spec/lib/gitlab/git/compare_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::Git::Compare, seed_helper: true do
let(:compare) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, false) }
let(:compare_straight) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, true) }
- describe :commits do
+ describe '#commits' do
subject do
compare.commits.map(&:id)
end
@@ -42,7 +42,7 @@ describe Gitlab::Git::Compare, seed_helper: true do
end
end
- describe :diffs do
+ describe '#diffs' do
subject do
compare.diffs.map(&:new_path)
end
@@ -67,7 +67,7 @@ describe Gitlab::Git::Compare, seed_helper: true do
end
end
- describe :same do
+ describe '#same' do
subject do
compare.same
end
@@ -81,7 +81,7 @@ describe Gitlab::Git::Compare, seed_helper: true do
end
end
- describe :commits_straight do
+ describe '#commits', 'straight compare' do
subject do
compare_straight.commits.map(&:id)
end
@@ -94,7 +94,7 @@ describe Gitlab::Git::Compare, seed_helper: true do
it { is_expected.not_to include(SeedRepo::BigCommit::PARENT_ID) }
end
- describe :diffs_straight do
+ describe '#diffs', 'straight compare' do
subject do
compare_straight.diffs.map(&:new_path)
end
diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb
index 47bdd7310d5..122c93dcd69 100644
--- a/spec/lib/gitlab/git/diff_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_collection_spec.rb
@@ -24,7 +24,7 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
it { is_expected.to be_kind_of ::Array }
end
- describe :decorate! do
+ describe '#decorate!' do
let(:file_count) { 3 }
it 'modifies the array in place' do
@@ -302,7 +302,7 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
end
end
- describe :each do
+ describe '#each' do
context 'when diff are too large' do
let(:collection) do
Gitlab::Git::DiffCollection.new([{ diff: 'a' * 204800 }])
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 688e2a75373..83d2ff8f9b3 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -11,7 +11,7 @@ describe Gitlab::Git::Tree, seed_helper: true do
it { expect(tree.select(&:file?).size).to eq(10) }
it { expect(tree.select(&:submodule?).size).to eq(2) }
- describe :dir do
+ describe '#dir?' do
let(:dir) { tree.select(&:dir?).first }
it { expect(dir).to be_kind_of Gitlab::Git::Tree }
@@ -41,7 +41,7 @@ describe Gitlab::Git::Tree, seed_helper: true do
end
end
- describe :file do
+ describe '#file?' do
let(:file) { tree.select(&:file?).first }
it { expect(file).to be_kind_of Gitlab::Git::Tree }
@@ -50,21 +50,21 @@ describe Gitlab::Git::Tree, seed_helper: true do
it { expect(file.name).to eq('.gitignore') }
end
- describe :readme do
+ describe '#readme?' do
let(:file) { tree.select(&:readme?).first }
it { expect(file).to be_kind_of Gitlab::Git::Tree }
it { expect(file.name).to eq('README.md') }
end
- describe :contributing do
+ describe '#contributing?' do
let(:file) { tree.select(&:contributing?).first }
it { expect(file).to be_kind_of Gitlab::Git::Tree }
it { expect(file.name).to eq('CONTRIBUTING.md') }
end
- describe :submodule do
+ describe '#submodule?' do
let(:submodule) { tree.select(&:submodule?).first }
it { expect(submodule).to be_kind_of Gitlab::Git::Tree }
diff --git a/spec/lib/gitlab/git/util_spec.rb b/spec/lib/gitlab/git/util_spec.rb
index 8d43b570e98..bcca4d4c746 100644
--- a/spec/lib/gitlab/git/util_spec.rb
+++ b/spec/lib/gitlab/git/util_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Git::Util do
- describe :count_lines do
+ describe '#count_lines' do
[
["", 0],
["foo", 1],
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 2f3bd4393b7..346cf0d117c 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -57,7 +57,7 @@ describe Gitlab::LDAP::User, lib: true do
end
end
- describe :find_or_create do
+ describe 'find or create' do
it "finds the user if already existing" do
create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb
index e1877d5fde0..5ca936f28f0 100644
--- a/spec/mailers/emails/profile_spec.rb
+++ b/spec/mailers/emails/profile_spec.rb
@@ -5,6 +5,16 @@ describe Notify do
include EmailSpec::Matchers
include_context 'gitlab email notification'
+ shared_examples 'a new user email' do
+ it 'is sent to the new user with the correct subject and body' do
+ aggregate_failures do
+ is_expected.to deliver_to new_user_address
+ is_expected.to have_subject(/^Account was created for you$/i)
+ is_expected.to have_body_text(new_user_address)
+ end
+ end
+ end
+
describe 'profile notifications' do
describe 'for new users, the email' do
let(:example_site_path) { root_path }
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 6ee91576676..4b72eb2eaa3 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -24,14 +24,14 @@ describe Notify do
let(:previous_assignee) { create(:user, name: 'Previous Assignee') }
shared_examples 'an assignee email' do
- it 'is sent as the author' do
- sender = subject.header[:from].addrs[0]
- expect(sender.display_name).to eq(current_user.name)
- expect(sender.address).to eq(gitlab_sender)
- end
+ it 'is sent to the assignee as the author' do
+ sender = subject.header[:from].addrs.first
- it 'is sent to the assignee' do
- is_expected.to deliver_to assignee.email
+ aggregate_failures do
+ expect(sender.display_name).to eq(current_user.name)
+ expect(sender.address).to eq(gitlab_sender)
+ expect(subject).to deliver_to(assignee.email)
+ end
end
end
@@ -49,12 +49,11 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue)
- end
-
- it 'contains a link to the new issue' do
- is_expected.to have_body_text namespace_project_issue_path(project.namespace, project, issue)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue)
+ is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
+ end
end
context 'when enabled email_author_in_body' do
@@ -63,7 +62,7 @@ describe Notify do
end
it 'contains a link to note author' do
- is_expected.to have_html_escaped_body_text issue.author_name
+ is_expected.to have_html_escaped_body_text(issue.author_name)
is_expected.to have_body_text 'wrote:'
end
end
@@ -95,20 +94,13 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue, reply: true)
- end
-
- it 'contains the name of the previous assignee' do
- is_expected.to have_html_escaped_body_text previous_assignee.name
- end
-
- it 'contains the name of the new assignee' do
- is_expected.to have_html_escaped_body_text assignee.name
- end
-
- it 'contains a link to the issue' do
- is_expected.to have_body_text namespace_project_issue_path(project.namespace, project, issue)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_html_escaped_body_text(previous_assignee.name)
+ is_expected.to have_html_escaped_body_text(assignee.name)
+ is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
+ end
end
end
@@ -129,16 +121,12 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue, reply: true)
- end
-
- it 'contains the names of the added labels' do
- is_expected.to have_body_text 'foo, bar, and baz'
- end
-
- it 'contains a link to the issue' do
- is_expected.to have_body_text namespace_project_issue_path(project.namespace, project, issue)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_body_text('foo, bar, and baz')
+ is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
+ end
end
end
@@ -158,20 +146,13 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue, reply: true)
- end
-
- it 'contains the new status' do
- is_expected.to have_body_text status
- end
-
- it 'contains the user name' do
- is_expected.to have_html_escaped_body_text current_user.name
- end
-
- it 'contains a link to the issue' do
- is_expected.to have_body_text(namespace_project_issue_path project.namespace, project, issue)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_body_text(status)
+ is_expected.to have_html_escaped_body_text(current_user.name)
+ is_expected.to have_body_text(namespace_project_issue_path project.namespace, project, issue)
+ end
end
end
@@ -189,18 +170,15 @@ describe Notify do
is_expected.to have_body_text 'Issue was moved to another project'
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue, reply: true)
- end
-
- it 'contains link to new issue' do
+ it 'has the correct subject and body' do
new_issue_url = namespace_project_issue_path(new_issue.project.namespace,
new_issue.project, new_issue)
- is_expected.to have_body_text new_issue_url
- end
- it 'contains a link to the original issue' do
- is_expected.to have_body_text namespace_project_issue_path(project.namespace, project, issue)
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_body_text(new_issue_url)
+ is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
+ end
end
end
end
@@ -220,20 +198,13 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
- it 'has the correct subject' do
- is_expected.to have_referable_subject(merge_request)
- end
-
- it 'contains a link to the new merge request' do
- is_expected.to have_body_text namespace_project_merge_request_path(project.namespace, project, merge_request)
- end
-
- it 'contains the source branch for the merge request' do
- is_expected.to have_body_text merge_request.source_branch
- end
-
- it 'contains the target branch for the merge request' do
- is_expected.to have_body_text merge_request.target_branch
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request)
+ is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
+ is_expected.to have_body_text(merge_request.source_branch)
+ is_expected.to have_body_text(merge_request.target_branch)
+ end
end
context 'when enabled email_author_in_body' do
@@ -275,20 +246,13 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(merge_request, reply: true)
- end
-
- it 'contains the name of the previous assignee' do
- is_expected.to have_html_escaped_body_text previous_assignee.name
- end
-
- it 'contains the name of the new assignee' do
- is_expected.to have_html_escaped_body_text assignee.name
- end
-
- it 'contains a link to the merge request' do
- is_expected.to have_body_text namespace_project_merge_request_path(project.namespace, project, merge_request)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request, reply: true)
+ is_expected.to have_html_escaped_body_text(previous_assignee.name)
+ is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
+ is_expected.to have_html_escaped_body_text(assignee.name)
+ end
end
end
@@ -309,16 +273,10 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
+ it 'has the correct subject and body' do
is_expected.to have_referable_subject(merge_request, reply: true)
- end
-
- it 'contains the names of the added labels' do
- is_expected.to have_body_text 'foo, bar, and baz'
- end
-
- it 'contains a link to the merge request' do
- is_expected.to have_body_text namespace_project_merge_request_path(project.namespace, project, merge_request)
+ is_expected.to have_body_text('foo, bar, and baz')
+ is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
end
end
@@ -338,20 +296,13 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(merge_request, reply: true)
- end
-
- it 'contains the new status' do
- is_expected.to have_body_text status
- end
-
- it 'contains the user name' do
- is_expected.to have_html_escaped_body_text current_user.name
- end
-
- it 'contains a link to the merge request' do
- is_expected.to have_body_text namespace_project_merge_request_path(project.namespace, project, merge_request)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request, reply: true)
+ is_expected.to have_body_text(status)
+ is_expected.to have_html_escaped_body_text(current_user.name)
+ is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
+ end
end
end
@@ -371,16 +322,12 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(merge_request, reply: true)
- end
-
- it 'contains the new status' do
- is_expected.to have_body_text 'merged'
- end
-
- it 'contains a link to the merge request' do
- is_expected.to have_body_text namespace_project_merge_request_path(project.namespace, project, merge_request)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request, reply: true)
+ is_expected.to have_body_text('merged')
+ is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
+ end
end
end
end
@@ -395,16 +342,10 @@ describe Notify do
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
- it 'has the correct subject' do
- is_expected.to have_subject "#{project.name} | Project was moved"
- end
-
- it 'contains name of project' do
+ it 'has the correct subject and body' do
+ is_expected.to have_subject("#{project.name} | Project was moved")
is_expected.to have_html_escaped_body_text project.name_with_namespace
- end
-
- it 'contains new user role' do
- is_expected.to have_body_text project.ssh_url_to_repo
+ is_expected.to have_body_text(project.ssh_url_to_repo)
end
end
@@ -597,14 +538,14 @@ describe Notify do
shared_examples 'a note email' do
it_behaves_like 'it should have Gmail Actions links'
- it 'is sent as the author' do
+ it 'is sent to the given recipient as the author' do
sender = subject.header[:from].addrs[0]
- expect(sender.display_name).to eq(note_author.name)
- expect(sender.address).to eq(gitlab_sender)
- end
- it 'is sent to the given recipient' do
- is_expected.to deliver_to recipient.notification_email
+ aggregate_failures do
+ expect(sender.display_name).to eq(note_author.name)
+ expect(sender.address).to eq(gitlab_sender)
+ expect(subject).to deliver_to(recipient.notification_email)
+ end
end
it 'contains the message from the note' do
@@ -641,12 +582,11 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Commit link'
it_behaves_like 'a user cannot unsubscribe through footer link'
- it 'has the correct subject' do
- is_expected.to have_subject "Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})"
- end
-
- it 'contains a link to the commit' do
- is_expected.to have_body_text commit.short_id
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject("Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})")
+ is_expected.to have_body_text(commit.short_id)
+ end
end
end
@@ -664,12 +604,11 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
- it 'has the correct subject' do
- is_expected.to have_referable_subject(merge_request, reply: true)
- end
-
- it 'contains a link to the merge request note' do
- is_expected.to have_body_text note_on_merge_request_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request, reply: true)
+ is_expected.to have_body_text note_on_merge_request_path
+ end
end
end
@@ -687,12 +626,11 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue, reply: true)
- end
-
- it 'contains a link to the issue note' do
- is_expected.to have_body_text note_on_issue_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_body_text(note_on_issue_path)
+ end
end
end
end
@@ -717,14 +655,14 @@ describe Notify do
it_behaves_like 'it should have Gmail Actions links'
- it 'is sent as the author' do
+ it 'is sent to the given recipient as the author' do
sender = subject.header[:from].addrs[0]
- expect(sender.display_name).to eq(note_author.name)
- expect(sender.address).to eq(gitlab_sender)
- end
- it 'is sent to the given recipient' do
- is_expected.to deliver_to recipient.notification_email
+ aggregate_failures do
+ expect(sender.display_name).to eq(note_author.name)
+ expect(sender.address).to eq(gitlab_sender)
+ expect(subject).to deliver_to(recipient.notification_email)
+ end
end
it 'contains the message from the note' do
@@ -934,21 +872,20 @@ describe Notify do
is_expected.to deliver_to 'new-email@mail.com'
end
- it 'has the correct subject' do
- is_expected.to have_subject 'Confirmation instructions | A Nice Suffix'
- end
-
- it 'includes a link to the site' do
- is_expected.to have_body_text example_site_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject('Confirmation instructions | A Nice Suffix')
+ is_expected.to have_body_text(example_site_path)
+ end
end
end
describe 'email on push for a created branch' do
let(:example_site_path) { root_path }
let(:user) { create(:user) }
- let(:tree_path) { namespace_project_tree_path(project.namespace, project, "master") }
+ let(:tree_path) { namespace_project_tree_path(project.namespace, project, "empty-branch") }
- subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :create) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/empty-branch', action: :create) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link'
@@ -961,12 +898,11 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_subject "[Git][#{project.full_path}] Pushed new branch master"
- end
-
- it 'contains a link to the branch' do
- is_expected.to have_body_text tree_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject("[Git][#{project.full_path}] Pushed new branch empty-branch")
+ is_expected.to have_body_text(tree_path)
+ end
end
end
@@ -988,12 +924,11 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_subject "[Git][#{project.full_path}] Pushed new tag v1.0"
- end
-
- it 'contains a link to the tag' do
- is_expected.to have_body_text tree_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject("[Git][#{project.full_path}] Pushed new tag v1.0")
+ is_expected.to have_body_text(tree_path)
+ end
end
end
@@ -1064,24 +999,14 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_subject "[Git][#{project.full_path}][master] #{commits.length} commits: Ruby files modified"
- end
-
- it 'includes commits list' do
- is_expected.to have_body_text 'Change some files'
- end
-
- it 'includes diffs with character-level highlighting' do
- is_expected.to have_body_text 'def</span> <span class="nf">archive_formats_regex'
- end
-
- it 'contains a link to the diff' do
- is_expected.to have_body_text diff_path
- end
-
- it 'does not contain the misleading footer' do
- is_expected.not_to have_body_text 'you are a member of'
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject("[Git][#{project.full_path}][master] #{commits.length} commits: Ruby files modified")
+ is_expected.to have_body_text('Change some files')
+ is_expected.to have_body_text('def</span> <span class="nf">archive_formats_regex')
+ is_expected.to have_body_text(diff_path)
+ is_expected.not_to have_body_text('you are a member of')
+ end
end
context "when set to send from committer email if domain matches" do
@@ -1098,13 +1023,13 @@ describe Notify do
end
it "is sent from the committer email" do
- sender = subject.header[:from].addrs[0]
- expect(sender.address).to eq(user.email)
- end
+ from = subject.header[:from].addrs.first
+ reply = subject.header[:reply_to].addrs.first
- it "is set to reply to the committer email" do
- sender = subject.header[:reply_to].addrs[0]
- expect(sender.address).to eq(user.email)
+ aggregate_failures do
+ expect(from.address).to eq(user.email)
+ expect(reply.address).to eq(user.email)
+ end
end
end
@@ -1115,13 +1040,13 @@ describe Notify do
end
it "is sent from the default email" do
- sender = subject.header[:from].addrs[0]
- expect(sender.address).to eq(gitlab_sender)
- end
+ from = subject.header[:from].addrs.first
+ reply = subject.header[:reply_to].addrs.first
- it "is set to reply to the default email" do
- sender = subject.header[:reply_to].addrs[0]
- expect(sender.address).to eq(gitlab_sender_reply_to)
+ aggregate_failures do
+ expect(from.address).to eq(gitlab_sender)
+ expect(reply.address).to eq(gitlab_sender_reply_to)
+ end
end
end
@@ -1132,13 +1057,13 @@ describe Notify do
end
it "is sent from the default email" do
- sender = subject.header[:from].addrs[0]
- expect(sender.address).to eq(gitlab_sender)
- end
+ from = subject.header[:from].addrs.first
+ reply = subject.header[:reply_to].addrs.first
- it "is set to reply to the default email" do
- sender = subject.header[:reply_to].addrs[0]
- expect(sender.address).to eq(gitlab_sender_reply_to)
+ aggregate_failures do
+ expect(from.address).to eq(gitlab_sender)
+ expect(reply.address).to eq(gitlab_sender_reply_to)
+ end
end
end
end
@@ -1166,20 +1091,13 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_subject "[Git][#{project.full_path}][master] #{commits.first.title}"
- end
-
- it 'includes commits list' do
- is_expected.to have_body_text 'Change some files'
- end
-
- it 'includes diffs with character-level highlighting' do
- is_expected.to have_body_text 'def</span> <span class="nf">archive_formats_regex'
- end
-
- it 'contains a link to the diff' do
- is_expected.to have_body_text diff_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject("[Git][#{project.full_path}][master] #{commits.first.title}")
+ is_expected.to have_body_text('Change some files')
+ is_expected.to have_body_text('def</span> <span class="nf">archive_formats_regex')
+ is_expected.to have_body_text(diff_path)
+ end
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 9574796a945..4522206fab1 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -44,6 +44,34 @@ describe Issue, "Issuable" do
it { expect(described_class).to respond_to(:assigned) }
end
+ describe 'author_name' do
+ it 'is delegated to author' do
+ expect(issue.author_name).to eq issue.author.name
+ end
+
+ it 'returns nil when author is nil' do
+ issue.author_id = nil
+ issue.save(validate: false)
+
+ expect(issue.author_name).to eq nil
+ end
+ end
+
+ describe 'assignee_name' do
+ it 'is delegated to assignee' do
+ issue.update!(assignee: create(:user))
+
+ expect(issue.assignee_name).to eq issue.assignee.name
+ end
+
+ it 'returns nil when assignee is nil' do
+ issue.assignee_id = nil
+ issue.save(validate: false)
+
+ expect(issue.assignee_name).to eq nil
+ end
+ end
+
describe "before_save" do
describe "#update_cache_counts" do
context "when previous assignee exists" do
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index e8caad00c44..8acec805584 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -6,6 +6,9 @@ describe SystemHook, models: true do
let(:user) { create(:user) }
let(:project) { create(:empty_project, namespace: user.namespace) }
let(:group) { create(:group) }
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jg@example.com', password: 'mydummypass' }
+ end
before do
WebMock.stub_request(:post, system_hook.url)
@@ -29,7 +32,7 @@ describe SystemHook, models: true do
end
it "user_create hook" do
- create(:user)
+ Users::CreateService.new(nil, params).execute
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_create/,
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 73977d031f9..b8584301baa 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -670,4 +670,41 @@ describe Issue, models: true do
expect(attrs_hash).to include('time_estimate')
end
end
+
+ describe '#check_for_spam' do
+ let(:project) { create :project, visibility_level: visibility_level }
+ let(:issue) { create :issue, project: project }
+
+ subject do
+ issue.assign_attributes(description: description)
+ issue.check_for_spam?
+ end
+
+ context 'when project is public and spammable attributes changed' do
+ let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
+ let(:description) { 'woo' }
+
+ it 'returns true' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'when project is private' do
+ let(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
+ let(:description) { issue.description }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+
+ context 'when spammable attributes have not changed' do
+ let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
+ let(:description) { issue.description }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 3cee2b7714f..f3f48f951a8 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -109,7 +109,7 @@ describe Milestone, models: true do
it { expect(milestone.percent_complete(user)).to eq(75) }
end
- describe :items_count do
+ describe '#is_empty?' do
before do
milestone.issues << create(:issue, project: project)
milestone.issues << create(:closed_issue, project: project)
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index bc70c6f4aa2..95f4785060c 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -165,7 +165,7 @@ describe Namespace, models: true do
end
end
- describe :rm_dir do
+ describe '#rm_dir', 'callback' do
let!(:project) { create(:empty_project, namespace: namespace) }
let!(:path) { File.join(Gitlab.config.repositories.storages.default['path'], namespace.full_path) }
@@ -217,10 +217,12 @@ describe Namespace, models: true do
end
describe '#descendants' do
- let!(:group) { create(:group) }
+ let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
let!(:deep_nested_group) { create(:group, parent: nested_group) }
let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
+ let!(:another_group) { create(:group, path: 'gitllab') }
+ let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) }
it 'returns the correct descendants' do
expect(very_deep_nested_group.descendants.to_a).to eq([])
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index e6a4583a8fb..c6c45d78990 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -5,7 +5,7 @@ describe PagesDomain, models: true do
it { is_expected.to belong_to(:project) }
end
- describe :validate_domain do
+ describe 'validate domain' do
subject { build(:pages_domain, domain: domain) }
context 'is unique' do
@@ -75,7 +75,7 @@ describe PagesDomain, models: true do
end
end
- describe :url do
+ describe '#url' do
subject { domain.url }
context 'without the certificate' do
@@ -91,7 +91,7 @@ describe PagesDomain, models: true do
end
end
- describe :has_matching_key? do
+ describe '#has_matching_key?' do
subject { domain.has_matching_key? }
context 'for matching key' do
@@ -107,7 +107,7 @@ describe PagesDomain, models: true do
end
end
- describe :has_intermediates? do
+ describe '#has_intermediates?' do
subject { domain.has_intermediates? }
context 'for self signed' do
@@ -133,7 +133,7 @@ describe PagesDomain, models: true do
end
end
- describe :expired? do
+ describe '#expired?' do
subject { domain.expired? }
context 'for valid' do
@@ -149,7 +149,7 @@ describe PagesDomain, models: true do
end
end
- describe :subject do
+ describe '#subject' do
let(:domain) { build(:pages_domain, :with_certificate) }
subject { domain.subject }
@@ -157,7 +157,7 @@ describe PagesDomain, models: true do
it { is_expected.to eq('/CN=test-certificate') }
end
- describe :certificate_text do
+ describe '#certificate_text' do
let(:domain) { build(:pages_domain, :with_certificate) }
subject { domain.certificate_text }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e4e75cc6a09..841c7d4cb5b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1714,11 +1714,14 @@ describe Project, models: true do
end
describe 'inside_path' do
- let!(:project1) { create(:empty_project) }
+ let!(:project1) { create(:empty_project, namespace: create(:namespace, path: 'name_pace')) }
let!(:project2) { create(:empty_project) }
+ let!(:project3) { create(:empty_project, namespace: create(:namespace, path: 'namespace')) }
let!(:path) { project1.namespace.full_path }
- it { expect(Project.inside_path(path)).to eq([project1]) }
+ it 'returns correct project' do
+ expect(Project.inside_path(path)).to eq([project1])
+ end
end
describe '#route_map_for' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 274e4f00a0a..585b87b828d 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1083,7 +1083,7 @@ describe Repository, models: true do
end
end
- describe :skip_merged_commit do
+ describe 'skip_merges option' do
subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", limit: 100, skip_merges: true).map{ |k| k.id } }
it { is_expected.not_to include('e56497bb5f03a90a51293fc6d516788730953899') }
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index bc8ae4ae5a8..171a51fcc5b 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Route, models: true do
- let!(:group) { create(:group, path: 'gitlab', name: 'gitlab') }
+ let!(:group) { create(:group, path: 'git_lab', name: 'git_lab') }
let!(:route) { group.route }
describe 'relationships' do
@@ -14,10 +14,24 @@ describe Route, models: true do
it { is_expected.to validate_uniqueness_of(:path) }
end
+ describe '.inside_path' do
+ let!(:nested_group) { create(:group, path: 'test', name: 'test', parent: group) }
+ let!(:deep_nested_group) { create(:group, path: 'foo', name: 'foo', parent: nested_group) }
+ let!(:another_group) { create(:group, path: 'other') }
+ let!(:similar_group) { create(:group, path: 'gitllab') }
+ let!(:another_group_nested) { create(:group, path: 'another', name: 'another', parent: similar_group) }
+
+ it 'returns correct routes' do
+ expect(Route.inside_path('git_lab')).to match_array([nested_group.route, deep_nested_group.route])
+ end
+ end
+
describe '#rename_descendants' do
let!(:nested_group) { create(:group, path: 'test', name: 'test', parent: group) }
let!(:deep_nested_group) { create(:group, path: 'foo', name: 'foo', parent: nested_group) }
let!(:similar_group) { create(:group, path: 'gitlab-org', name: 'gitlab-org') }
+ let!(:another_group) { create(:group, path: 'gittlab', name: 'gitllab') }
+ let!(:another_group_nested) { create(:group, path: 'git_lab', name: 'git_lab', parent: another_group) }
context 'path update' do
context 'when route name is set' do
@@ -28,6 +42,8 @@ describe Route, models: true do
expect(described_class.exists?(path: 'bar/test')).to be_truthy
expect(described_class.exists?(path: 'bar/test/foo')).to be_truthy
expect(described_class.exists?(path: 'gitlab-org')).to be_truthy
+ expect(described_class.exists?(path: 'gittlab')).to be_truthy
+ expect(described_class.exists?(path: 'gittlab/git_lab')).to be_truthy
end
end
@@ -44,7 +60,7 @@ describe Route, models: true do
context 'name update' do
it "updates children routes with new path" do
- route.update_attributes(name: 'bar')
+ route.update_attributes(name: 'bar')
expect(described_class.exists?(name: 'bar')).to be_truthy
expect(described_class.exists?(name: 'bar / test')).to be_truthy
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 219ab1989ea..8095d01b69e 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -198,4 +198,47 @@ describe Snippet, models: true do
expect(snippet.participants).to include(note1.author, note2.author)
end
end
+
+ describe '#check_for_spam' do
+ let(:snippet) { create :snippet, visibility_level: visibility_level }
+
+ subject do
+ snippet.assign_attributes(title: title)
+ snippet.check_for_spam?
+ end
+
+ context 'when public and spammable attributes changed' do
+ let(:visibility_level) { Snippet::PUBLIC }
+ let(:title) { 'woo' }
+
+ it 'returns true' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'when private' do
+ let(:visibility_level) { Snippet::PRIVATE }
+ let(:title) { snippet.title }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+
+ it 'returns true when switching to public' do
+ snippet.save!
+ snippet.visibility_level = Snippet::PUBLIC
+
+ expect(snippet.check_for_spam?).to be_truthy
+ end
+ end
+
+ context 'when spammable attributes have not changed' do
+ let(:visibility_level) { Snippet::PUBLIC }
+ let(:title) { snippet.title }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 90378179e32..a9e37be1157 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -81,6 +81,7 @@ describe User, models: true do
it { is_expected.to validate_numericality_of(:projects_limit) }
it { is_expected.to allow_value(0).for(:projects_limit) }
it { is_expected.not_to allow_value(-1).for(:projects_limit) }
+ it { is_expected.not_to allow_value(Gitlab::Database::MAX_INT_VALUE + 1).for(:projects_limit) }
it { is_expected.to validate_length_of(:bio).is_at_most(255) }
@@ -360,22 +361,10 @@ describe User, models: true do
end
describe '#generate_password' do
- it "executes callback when force_random_password specified" do
- user = build(:user, force_random_password: true)
- expect(user).to receive(:generate_password)
- user.save
- end
-
it "does not generate password by default" do
user = create(:user, password: 'abcdefghe')
expect(user.password).to eq('abcdefghe')
end
-
- it "generates password when forcing random password" do
- allow(Devise).to receive(:friendly_token).and_return('123456789')
- user = create(:user, password: 'abcdefg', force_random_password: true)
- expect(user.password).to eq('12345678')
- end
end
describe 'authentication token' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index c481b7e72b1..a3de4702ad0 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -902,7 +902,7 @@ describe API::Projects, :api do
end
end
- describe :fork_admin do
+ describe 'fork management' do
let(:project_fork_target) { create(:empty_project) }
let(:project_fork_source) { create(:empty_project, :public) }
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
index d8bb562587d..b1aa793ec00 100644
--- a/spec/requests/api/v3/projects_spec.rb
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -949,7 +949,7 @@ describe API::V3::Projects, api: true do
end
end
- describe :fork_admin do
+ describe 'fork management' do
let(:project_fork_target) { create(:empty_project) }
let(:project_fork_source) { create(:empty_project, :public) }
diff --git a/spec/requests/api/v3/users_spec.rb b/spec/requests/api/v3/users_spec.rb
index 17bbb0b53c1..b38cbe74b85 100644
--- a/spec/requests/api/v3/users_spec.rb
+++ b/spec/requests/api/v3/users_spec.rb
@@ -263,4 +263,18 @@ describe API::V3::Users, api: true do
expect(json_response['message']).to eq('404 User Not Found')
end
end
+
+ describe 'POST /users' do
+ it 'creates confirmed user when confirm parameter is false' do
+ optional_attributes = { confirm: false }
+ attributes = attributes_for(:user).merge(optional_attributes)
+
+ post v3_api('/users', admin), attributes
+
+ user_id = json_response['id']
+ new_user = User.find(user_id)
+
+ expect(new_user).to be_confirmed
+ end
+ end
end
diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb
index 22115c6566d..d841bdaa292 100644
--- a/spec/services/boards/issues/list_service_spec.rb
+++ b/spec/services/boards/issues/list_service_spec.rb
@@ -30,6 +30,7 @@ describe Boards::Issues::ListService, services: true do
let!(:closed_issue2) { create(:labeled_issue, :closed, project: project, labels: [p3]) }
let!(:closed_issue3) { create(:issue, :closed, project: project) }
let!(:closed_issue4) { create(:labeled_issue, :closed, project: project, labels: [p1]) }
+ let!(:closed_issue5) { create(:labeled_issue, :closed, project: project, labels: [development]) }
before do
project.team << [user, :developer]
@@ -57,7 +58,7 @@ describe Boards::Issues::ListService, services: true do
issues = described_class.new(project, user, params).execute
- expect(issues).to eq [closed_issue4, closed_issue2, closed_issue3, closed_issue1]
+ expect(issues).to eq [closed_issue4, closed_issue2, closed_issue5, closed_issue3, closed_issue1]
end
it 'returns opened issues that have label list applied when listing issues from a label list' do
diff --git a/spec/services/create_branch_service_spec.rb b/spec/services/create_branch_service_spec.rb
new file mode 100644
index 00000000000..3f548688c20
--- /dev/null
+++ b/spec/services/create_branch_service_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe CreateBranchService, services: true do
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(project, user) }
+
+ describe '#execute' do
+ context 'when repository is empty' do
+ let(:project) { create(:project_empty_repo) }
+
+ it 'creates master branch' do
+ service.execute('my-feature', 'master')
+
+ expect(project.repository.branch_exists?('master')).to be_truthy
+ end
+
+ it 'creates my-feature branch' do
+ service.execute('my-feature', 'master')
+
+ expect(project.repository.branch_exists?('my-feature')).to be_truthy
+ end
+ end
+ end
+end
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index 0475f38fe5e..7a1ac027310 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -138,7 +138,7 @@ describe Issuable::BulkUpdateService, services: true do
let(:labels) { [bug, regression] }
it 'updates the labels of all issues passed to the labels passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(eq(labels.map(&:id)))
+ expect(issues.map(&:reload).map(&:label_ids)).to all(match_array(labels.map(&:id)))
end
it 'does not update issues not passed in' do
diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
index 92b84308f73..fe6a19e97ea 100644
--- a/spec/services/milestones/close_service_spec.rb
+++ b/spec/services/milestones/close_service_spec.rb
@@ -17,7 +17,7 @@ describe Milestones::CloseService, services: true do
it { expect(milestone).to be_valid }
it { expect(milestone).to be_closed }
- describe :event do
+ describe 'event' do
let(:event) { Event.recent.first }
it { expect(event.milestone).to be_truthy }
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 8e614211116..e3be1989c93 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::ForkService, services: true do
- describe :fork_by_user do
+ describe 'fork by user' do
before do
@from_namespace = create(:namespace)
@from_user = create(:user, namespace: @from_namespace )
@@ -100,7 +100,7 @@ describe Projects::ForkService, services: true do
end
end
- describe :fork_to_namespace do
+ describe 'fork to namespace' do
before do
@group_owner = create(:user)
@developer = create(:user)
diff --git a/spec/services/spam_service_spec.rb b/spec/services/spam_service_spec.rb
index 4ce3b95aa87..e09c05ccf32 100644
--- a/spec/services/spam_service_spec.rb
+++ b/spec/services/spam_service_spec.rb
@@ -19,42 +19,67 @@ describe SpamService, services: true do
let(:issue) { create(:issue, project: project) }
let(:request) { double(:request, env: {}) }
- context 'when indicated as spam by akismet' do
- before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: true)) }
+ context 'when spammable attributes have not changed' do
+ before do
+ issue.closed_at = Time.zone.now
- it 'doesnt check as spam when request is missing' do
- check_spam(issue, nil, false)
-
- expect(issue.spam).to be_falsey
+ allow(AkismetService).to receive(:new).and_return(double(is_spam?: true))
end
- it 'checks as spam' do
- check_spam(issue, request, false)
-
- expect(issue.spam).to be_truthy
+ it 'returns false' do
+ expect(check_spam(issue, request, false)).to be_falsey
end
- it 'creates a spam log' do
+ it 'does not create a spam log' do
expect { check_spam(issue, request, false) }
- .to change { SpamLog.count }.from(0).to(1)
+ .not_to change { SpamLog.count }
end
+ end
- it 'doesnt yield block' do
- expect(check_spam(issue, request, false))
- .to eql(SpamLog.last)
+ context 'when spammable attributes have changed' do
+ before do
+ issue.description = 'SPAM!'
end
- end
- context 'when not indicated as spam by akismet' do
- before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) }
+ context 'when indicated as spam by akismet' do
+ before do
+ allow(AkismetService).to receive(:new).and_return(double(is_spam?: true))
+ end
- it 'returns false' do
- expect(check_spam(issue, request, false)).to be_falsey
+ it 'doesnt check as spam when request is missing' do
+ check_spam(issue, nil, false)
+
+ expect(issue.spam).to be_falsey
+ end
+
+ it 'checks as spam' do
+ check_spam(issue, request, false)
+
+ expect(issue.spam).to be_truthy
+ end
+
+ it 'creates a spam log' do
+ expect { check_spam(issue, request, false) }
+ .to change { SpamLog.count }.from(0).to(1)
+ end
+
+ it 'doesnt yield block' do
+ expect(check_spam(issue, request, false))
+ .to eql(SpamLog.last)
+ end
end
- it 'does not create a spam log' do
- expect { check_spam(issue, request, false) }
- .not_to change { SpamLog.count }
+ context 'when not indicated as spam by akismet' do
+ before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) }
+
+ it 'returns false' do
+ expect(check_spam(issue, request, false)).to be_falsey
+ end
+
+ it 'does not create a spam log' do
+ expect { check_spam(issue, request, false) }
+ .not_to change { SpamLog.count }
+ end
end
end
end
diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb
new file mode 100644
index 00000000000..5f79203701a
--- /dev/null
+++ b/spec/services/users/create_service_spec.rb
@@ -0,0 +1,182 @@
+require 'spec_helper'
+
+describe Users::CreateService, services: true do
+ describe '#build' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass' }
+ end
+
+ context 'with an admin user' do
+ let(:admin_user) { create(:admin) }
+ let(:service) { described_class.new(admin_user, params) }
+
+ it 'returns a valid user' do
+ expect(service.build).to be_valid
+ end
+ end
+
+ context 'with non admin user' do
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(user, params) }
+
+ it 'raises AccessDeniedError exception' do
+ expect { service.build }.to raise_error Gitlab::Access::AccessDeniedError
+ end
+ end
+
+ context 'with nil user' do
+ let(:service) { described_class.new(nil, params) }
+
+ it 'returns a valid user' do
+ expect(service.build).to be_valid
+ end
+ end
+ end
+
+ describe '#execute' do
+ let(:admin_user) { create(:admin) }
+
+ context 'with an admin user' do
+ let(:service) { described_class.new(admin_user, params) }
+
+ context 'when required parameters are provided' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass' }
+ end
+
+ it 'returns a persisted user' do
+ expect(service.execute).to be_persisted
+ end
+
+ it 'persists the given attributes' do
+ user = service.execute
+ user.reload
+
+ expect(user).to have_attributes(
+ name: params[:name],
+ username: params[:username],
+ email: params[:email],
+ password: params[:password],
+ created_by_id: admin_user.id
+ )
+ end
+
+ it 'user is not confirmed if skip_confirmation param is not present' do
+ expect(service.execute).not_to be_confirmed
+ end
+
+ it 'logs the user creation' do
+ expect(service).to receive(:log_info).with("User \"John Doe\" (jd@example.com) was created")
+
+ service.execute
+ end
+
+ it 'executes system hooks ' do
+ system_hook_service = spy(:system_hook_service)
+
+ expect(service).to receive(:system_hook_service).and_return(system_hook_service)
+
+ user = service.execute
+
+ expect(system_hook_service).to have_received(:execute_hooks_for).with(user, :create)
+ end
+
+ it 'does not send a notification email' do
+ notification_service = spy(:notification_service)
+
+ expect(service).not_to receive(:notification_service)
+
+ service.execute
+
+ expect(notification_service).not_to have_received(:new_user)
+ end
+ end
+
+ context 'when force_random_password parameter is true' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass', force_random_password: true }
+ end
+
+ it 'generates random password' do
+ user = service.execute
+
+ expect(user.password).not_to eq 'mydummypass'
+ expect(user.password).to be_present
+ end
+ end
+
+ context 'when skip_confirmation parameter is true' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass', skip_confirmation: true }
+ end
+
+ it 'confirms the user' do
+ expect(service.execute).to be_confirmed
+ end
+ end
+
+ context 'when reset_password parameter is true' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass', reset_password: true }
+ end
+
+ it 'resets password even if a password parameter is given' do
+ expect(service.execute).to be_recently_sent_password_reset
+ end
+
+ it 'sends a notification email' do
+ notification_service = spy(:notification_service)
+
+ expect(service).to receive(:notification_service).and_return(notification_service)
+
+ user = service.execute
+
+ expect(notification_service).to have_received(:new_user).with(user, an_instance_of(String))
+ end
+ end
+ end
+
+ context 'with nil user' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass', skip_confirmation: true }
+ end
+ let(:service) { described_class.new(nil, params) }
+
+ context 'when "send_user_confirmation_email" application setting is true' do
+ before do
+ current_application_settings = double(:current_application_settings, send_user_confirmation_email: true, signup_enabled?: true)
+ allow(service).to receive(:current_application_settings).and_return(current_application_settings)
+ end
+
+ it 'does not confirm the user' do
+ expect(service.execute).not_to be_confirmed
+ end
+ end
+
+ context 'when "send_user_confirmation_email" application setting is false' do
+ before do
+ current_application_settings = double(:current_application_settings, send_user_confirmation_email: false, signup_enabled?: true)
+ allow(service).to receive(:current_application_settings).and_return(current_application_settings)
+ end
+
+ it 'confirms the user' do
+ expect(service.execute).to be_confirmed
+ end
+
+ it 'persists the given attributes' do
+ user = service.execute
+ user.reload
+
+ expect(user).to have_attributes(
+ name: params[:name],
+ username: params[:username],
+ email: params[:email],
+ password: params[:password],
+ created_by_id: nil,
+ admin: false
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index ceb3209331f..5ab8f0d981a 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -35,7 +35,8 @@ RSpec.configure do |config|
config.include Warden::Test::Helpers, type: :request
config.include LoginHelpers, type: :feature
config.include SearchHelpers, type: :feature
- config.include WaitForAjax, type: :feature
+ config.include WaitForRequests, :js
+ config.include WaitForAjax, :js
config.include StubConfiguration
config.include EmailHelpers, type: :mailer
config.include TestEnv
diff --git a/spec/support/notify_shared_examples.rb b/spec/support/notify_shared_examples.rb
index a3724b801b3..16a425f2ca2 100644
--- a/spec/support/notify_shared_examples.rb
+++ b/spec/support/notify_shared_examples.rb
@@ -27,24 +27,14 @@ shared_examples 'a multiple recipients email' do
end
shared_examples 'an email sent from GitLab' do
- it 'is sent from GitLab' do
+ it 'has the characteristics of an email sent from GitLab' do
sender = subject.header[:from].addrs[0]
- expect(sender.display_name).to eq(gitlab_sender_display_name)
- expect(sender.address).to eq(gitlab_sender)
- end
-
- it 'has a Reply-To address' do
reply_to = subject.header[:reply_to].addresses
- expect(reply_to).to eq([gitlab_sender_reply_to])
- end
-
- context 'when custom suffix for email subject is set' do
- before do
- stub_config_setting(email_subject_suffix: 'A Nice Suffix')
- end
- it 'ends the subject with the suffix' do
- is_expected.to have_subject /\ \| A Nice Suffix$/
+ aggregate_failures do
+ expect(sender.display_name).to eq(gitlab_sender_display_name)
+ expect(sender.address).to eq(gitlab_sender)
+ expect(reply_to).to eq([gitlab_sender_reply_to])
end
end
end
@@ -56,43 +46,40 @@ shared_examples 'an email that contains a header with author username' do
end
shared_examples 'an email with X-GitLab headers containing project details' do
- it 'has X-GitLab-Project* headers' do
- is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
- is_expected.to have_header 'X-GitLab-Project-Id', /#{project.id}/
- is_expected.to have_header 'X-GitLab-Project-Path', /#{project.path_with_namespace}/
+ it 'has X-GitLab-Project headers' do
+ aggregate_failures do
+ is_expected.to have_header('X-GitLab-Project', /#{project.name}/)
+ is_expected.to have_header('X-GitLab-Project-Id', /#{project.id}/)
+ is_expected.to have_header('X-GitLab-Project-Path', /#{project.path_with_namespace}/)
+ end
end
end
shared_examples 'a new thread email with reply-by-email enabled' do
- let(:regex) { /\A<reply\-(.*)@#{Gitlab.config.gitlab.host}>\Z/ }
-
- it 'has a Message-ID header' do
- is_expected.to have_header 'Message-ID', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>"
- end
+ it 'has the characteristics of a threaded email' do
+ host = Gitlab.config.gitlab.host
+ route_key = "#{model.class.model_name.singular_route_key}_#{model.id}"
- it 'has a References header' do
- is_expected.to have_header 'References', regex
+ aggregate_failures do
+ is_expected.to have_header('Message-ID', "<#{route_key}@#{host}>")
+ is_expected.to have_header('References', /\A<reply\-.*@#{host}>\Z/ )
+ end
end
end
shared_examples 'a thread answer email with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details'
- let(:regex) { /\A<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}> <reply\-(.*)@#{Gitlab.config.gitlab.host}>\Z/ }
-
- it 'has a Message-ID header' do
- is_expected.to have_header 'Message-ID', /\A<(.*)@#{Gitlab.config.gitlab.host}>\Z/
- end
-
- it 'has a In-Reply-To header' do
- is_expected.to have_header 'In-Reply-To', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>"
- end
- it 'has a References header' do
- is_expected.to have_header 'References', regex
- end
+ it 'has the characteristics of a threaded reply' do
+ host = Gitlab.config.gitlab.host
+ route_key = "#{model.class.model_name.singular_route_key}_#{model.id}"
- it 'has a subject that begins with Re: ' do
- is_expected.to have_subject /^Re: /
+ aggregate_failures do
+ is_expected.to have_header('Message-ID', /\A<.*@#{host}>\Z/)
+ is_expected.to have_header('In-Reply-To', "<#{route_key}@#{host}>")
+ is_expected.to have_header('References', /\A<#{route_key}@#{host}> <reply\-.*@#{host}>\Z/ )
+ is_expected.to have_subject(/^Re: /)
+ end
end
end
@@ -136,80 +123,77 @@ shared_examples 'an answer to an existing thread with reply-by-email enabled' do
end
end
-shared_examples 'a new user email' do
- it 'is sent to the new user' do
- is_expected.to deliver_to new_user_address
- end
-
- it 'has the correct subject' do
- is_expected.to have_subject /^Account was created for you$/i
- end
-
- it 'contains the new user\'s login name' do
- is_expected.to have_body_text /#{new_user_address}/
- end
-end
-
shared_examples 'it should have Gmail Actions links' do
- it { is_expected.to have_body_text '<script type="application/ld+json">' }
- it { is_expected.to have_body_text /ViewAction/ }
+ it do
+ aggregate_failures do
+ is_expected.to have_body_text('<script type="application/ld+json">')
+ is_expected.to have_body_text('ViewAction')
+ end
+ end
end
shared_examples 'it should not have Gmail Actions links' do
- it { is_expected.not_to have_body_text '<script type="application/ld+json">' }
- it { is_expected.not_to have_body_text /ViewAction/ }
+ it do
+ aggregate_failures do
+ is_expected.not_to have_body_text('<script type="application/ld+json">')
+ is_expected.not_to have_body_text('ViewAction')
+ end
+ end
end
shared_examples 'it should show Gmail Actions View Issue link' do
it_behaves_like 'it should have Gmail Actions links'
- it { is_expected.to have_body_text /View Issue/ }
+ it { is_expected.to have_body_text('View Issue') }
end
shared_examples 'it should show Gmail Actions View Merge request link' do
it_behaves_like 'it should have Gmail Actions links'
- it { is_expected.to have_body_text /View Merge request/ }
+ it { is_expected.to have_body_text('View Merge request') }
end
shared_examples 'it should show Gmail Actions View Commit link' do
it_behaves_like 'it should have Gmail Actions links'
- it { is_expected.to have_body_text /View Commit/ }
+ it { is_expected.to have_body_text('View Commit') }
end
shared_examples 'an unsubscribeable thread' do
it_behaves_like 'an unsubscribeable thread with incoming address without %{key}'
- it 'has a List-Unsubscribe header in the correct format' do
- is_expected.to have_header 'List-Unsubscribe', /unsubscribe/
- is_expected.to have_header 'List-Unsubscribe', /mailto/
- is_expected.to have_header 'List-Unsubscribe', /^<.+,.+>$/
+ it 'has a List-Unsubscribe header in the correct format, and a body link' do
+ aggregate_failures do
+ is_expected.to have_header('List-Unsubscribe', /unsubscribe/)
+ is_expected.to have_header('List-Unsubscribe', /mailto/)
+ is_expected.to have_header('List-Unsubscribe', /^<.+,.+>$/)
+ is_expected.to have_body_text('unsubscribe')
+ end
end
-
- it { is_expected.to have_body_text /unsubscribe/ }
end
shared_examples 'an unsubscribeable thread with incoming address without %{key}' do
include_context 'reply-by-email is enabled with incoming address without %{key}'
- it 'has a List-Unsubscribe header in the correct format' do
- is_expected.to have_header 'List-Unsubscribe', /unsubscribe/
- is_expected.not_to have_header 'List-Unsubscribe', /mailto/
- is_expected.to have_header 'List-Unsubscribe', /^<[^,]+>$/
+ it 'has a List-Unsubscribe header in the correct format, and a body link' do
+ aggregate_failures do
+ is_expected.to have_header('List-Unsubscribe', /unsubscribe/)
+ is_expected.not_to have_header('List-Unsubscribe', /mailto/)
+ is_expected.to have_header('List-Unsubscribe', /^<[^,]+>$/)
+ is_expected.to have_body_text('unsubscribe')
+ end
end
-
- it { is_expected.to have_body_text /unsubscribe/ }
end
shared_examples 'a user cannot unsubscribe through footer link' do
- it 'does not have a List-Unsubscribe header' do
- is_expected.not_to have_header 'List-Unsubscribe', /unsubscribe/
+ it 'does not have a List-Unsubscribe header or a body link' do
+ aggregate_failures do
+ is_expected.not_to have_header('List-Unsubscribe', /unsubscribe/)
+ is_expected.not_to have_body_text('unsubscribe')
+ end
end
-
- it { is_expected.not_to have_body_text /unsubscribe/ }
end
shared_examples 'an email with a labels subscriptions link in its footer' do
- it { is_expected.to have_body_text /label subscriptions/ }
+ it { is_expected.to have_body_text('label subscriptions') }
end
diff --git a/spec/support/prometheus_helpers.rb b/spec/support/prometheus_helpers.rb
index a52d8f37d14..4afdbd68304 100644
--- a/spec/support/prometheus_helpers.rb
+++ b/spec/support/prometheus_helpers.rb
@@ -1,10 +1,10 @@
module PrometheusHelpers
def prometheus_memory_query(environment_slug)
- %{sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})/1024/1024}
+ %{(sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"}) / count(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})) /1024/1024}
end
def prometheus_cpu_query(environment_slug)
- %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m]))}
+ %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m])) / count(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}) * 100}
end
def prometheus_query_url(prometheus_query)
diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb
index 704922b6cf4..b902fe90707 100644
--- a/spec/support/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/slack_mattermost_notifications_shared_examples.rb
@@ -324,5 +324,24 @@ RSpec.shared_examples 'slack or mattermost notifications' do
it_behaves_like 'call Slack/Mattermost API'
end
end
+
+ context 'only notify for the default branch' do
+ context 'when enabled' do
+ let(:pipeline) do
+ create(:ci_pipeline, project: project, status: 'failed', ref: 'not-the-default-branch')
+ end
+
+ before do
+ chat_service.notify_only_default_branch = true
+ end
+
+ it 'does not call the Slack/Mattermost API for pipeline events' do
+ data = Gitlab::DataBuilder::Pipeline.build(pipeline)
+ result = chat_service.execute(data)
+
+ expect(result).to be_falsy
+ end
+ end
+ end
end
end
diff --git a/spec/support/target_branch_helpers.rb b/spec/support/target_branch_helpers.rb
new file mode 100644
index 00000000000..3ee8f0f657e
--- /dev/null
+++ b/spec/support/target_branch_helpers.rb
@@ -0,0 +1,16 @@
+module TargetBranchHelpers
+ def select_branch(name)
+ first('button.js-target-branch').click
+ wait_for_ajax
+ all('a[data-group="Branches"]').find do |el|
+ el.text == name
+ end.click
+ end
+
+ def create_new_branch(name)
+ first('button.js-target-branch').click
+ click_link 'Create new branch'
+ fill_in 'new_branch_name', with: name
+ click_button 'Create'
+ end
+end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index f1d226b6ae3..648b0380f18 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -37,9 +37,10 @@ module TestEnv
'conflict-too-large' => '39fa04f',
'deleted-image-test' => '6c17798',
'wip' => 'b9238ee',
- 'csv' => '3dd0896'
+ 'csv' => '3dd0896',
+ 'v1.1.0' => 'b83d6e3'
}.freeze
-
+
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
# need to keep all the branches in sync.
# We currently only need a subset of the branches
diff --git a/spec/support/wait_for_requests.rb b/spec/support/wait_for_requests.rb
new file mode 100644
index 00000000000..0bfa7f72ff8
--- /dev/null
+++ b/spec/support/wait_for_requests.rb
@@ -0,0 +1,32 @@
+module WaitForRequests
+ extend self
+
+ # This is inspired by http://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests
+ def wait_for_requests_complete
+ Gitlab::Testing::RequestBlockerMiddleware.block_requests!
+ wait_for('pending AJAX requests complete') do
+ Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero?
+ end
+ ensure
+ Gitlab::Testing::RequestBlockerMiddleware.allow_requests!
+ end
+
+ # Waits until the passed block returns true
+ def wait_for(condition_name, max_wait_time: Capybara.default_max_wait_time, polling_interval: 0.01)
+ wait_until = Time.now + max_wait_time.seconds
+ loop do
+ break if yield
+ if Time.now > wait_until
+ raise "Condition not met: #{condition_name}"
+ else
+ sleep(polling_interval)
+ end
+ end
+ end
+end
+
+RSpec.configure do |config|
+ config.after(:each, :js) do
+ wait_for_requests_complete
+ end
+end
diff --git a/spec/support/wait_for_vue_resource.rb b/spec/support/wait_for_vue_resource.rb
index 1029f84716f..4a4e2e16ee7 100644
--- a/spec/support/wait_for_vue_resource.rb
+++ b/spec/support/wait_for_vue_resource.rb
@@ -1,7 +1,7 @@
module WaitForVueResource
def wait_for_vue_resource(spinner: true)
Timeout.timeout(Capybara.default_max_wait_time) do
- loop until page.evaluate_script('Vue.activeResources').zero?
+ loop until page.evaluate_script('window.activeVueResources').zero?
end
end
end
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
new file mode 100644
index 00000000000..d95baddf546
--- /dev/null
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -0,0 +1,78 @@
+require 'rake_helper'
+
+describe 'gitlab:gitaly namespace rake task' do
+ before :all do
+ Rake.application.rake_require 'tasks/gitlab/gitaly'
+ end
+
+ describe 'install' do
+ let(:repo) { 'https://gitlab.com/gitlab-org/gitaly.git' }
+ let(:clone_path) { Rails.root.join('tmp/tests/gitaly').to_s }
+ let(:tag) { "v#{File.read(Rails.root.join(Gitlab::GitalyClient::SERVER_VERSION_FILE)).chomp}" }
+
+ context 'no dir given' do
+ it 'aborts and display a help message' do
+ # avoid writing task output to spec progress
+ allow($stderr).to receive :write
+ expect { run_rake_task('gitlab:gitaly:install') }.to raise_error /Please specify the directory where you want to install gitaly/
+ end
+ end
+
+ context 'when an underlying Git command fail' do
+ it 'aborts and display a help message' do
+ expect_any_instance_of(Object).
+ to receive(:checkout_or_clone_tag).and_raise 'Git error'
+
+ expect { run_rake_task('gitlab:gitaly:install', clone_path) }.to raise_error 'Git error'
+ end
+ end
+
+ describe 'checkout or clone' do
+ before do
+ expect(Dir).to receive(:chdir).with(clone_path)
+ end
+
+ it 'calls checkout_or_clone_tag with the right arguments' do
+ expect_any_instance_of(Object).
+ to receive(:checkout_or_clone_tag).with(tag: tag, repo: repo, target_dir: clone_path)
+
+ run_rake_task('gitlab:gitaly:install', clone_path)
+ end
+ end
+
+ describe 'gmake/make' do
+ before do
+ FileUtils.mkdir_p(clone_path)
+ expect(Dir).to receive(:chdir).with(clone_path).and_call_original
+ end
+
+ context 'gmake is available' do
+ before do
+ expect_any_instance_of(Object).to receive(:checkout_or_clone_tag)
+ allow_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true)
+ end
+
+ it 'calls gmake in the gitaly directory' do
+ expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
+ expect_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true)
+
+ run_rake_task('gitlab:gitaly:install', clone_path)
+ end
+ end
+
+ context 'gmake is not available' do
+ before do
+ expect_any_instance_of(Object).to receive(:checkout_or_clone_tag)
+ allow_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true)
+ end
+
+ it 'calls make in the gitaly directory' do
+ expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42])
+ expect_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true)
+
+ run_rake_task('gitlab:gitaly:install', clone_path)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/workhorse_rake_spec.rb b/spec/tasks/gitlab/workhorse_rake_spec.rb
index 6de66c3cf07..8a66a4aa047 100644
--- a/spec/tasks/gitlab/workhorse_rake_spec.rb
+++ b/spec/tasks/gitlab/workhorse_rake_spec.rb
@@ -9,9 +9,6 @@ describe 'gitlab:workhorse namespace rake task' do
let(:repo) { 'https://gitlab.com/gitlab-org/gitlab-workhorse.git' }
let(:clone_path) { Rails.root.join('tmp/tests/gitlab-workhorse').to_s }
let(:tag) { "v#{File.read(Rails.root.join(Gitlab::Workhorse::VERSION_FILE)).chomp}" }
- before do
- allow(ENV).to receive(:[])
- end
context 'no dir given' do
it 'aborts and display a help message' do
diff --git a/spec/tasks/tokens_spec.rb b/spec/tasks/tokens_spec.rb
new file mode 100644
index 00000000000..19036c7677c
--- /dev/null
+++ b/spec/tasks/tokens_spec.rb
@@ -0,0 +1,21 @@
+require 'rake_helper'
+
+describe 'tokens rake tasks' do
+ let!(:user) { create(:user) }
+
+ before do
+ Rake.application.rake_require 'tasks/tokens'
+ end
+
+ describe 'reset_all task' do
+ it 'invokes create_hooks task' do
+ expect { run_rake_task('tokens:reset_all_auth') }.to change { user.reload.authentication_token }
+ end
+ end
+
+ describe 'reset_all_email task' do
+ it 'invokes create_hooks task' do
+ expect { run_rake_task('tokens:reset_all_email') }.to change { user.reload.incoming_email_token }
+ end
+ end
+end
diff --git a/spec/views/projects/pipelines/show.html.haml_spec.rb b/spec/views/projects/pipelines/show.html.haml_spec.rb
index c101f6f164d..e4aeaeca508 100644
--- a/spec/views/projects/pipelines/show.html.haml_spec.rb
+++ b/spec/views/projects/pipelines/show.html.haml_spec.rb
@@ -3,8 +3,9 @@ require 'spec_helper'
describe 'projects/pipelines/show' do
include Devise::Test::ControllerHelpers
+ let(:user) { create(:user) }
let(:project) { create(:project) }
- let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, user: user) }
before do
controller.prepend_view_path('app/views/projects')
@@ -21,6 +22,7 @@ describe 'projects/pipelines/show' do
assign(:project, project)
assign(:pipeline, pipeline)
+ assign(:commit, project.commit)
allow(view).to receive(:can?).and_return(true)
end
@@ -31,6 +33,12 @@ describe 'projects/pipelines/show' do
expect(rendered).to have_css('.js-pipeline-graph')
expect(rendered).to have_css('.js-grouped-pipeline-dropdown')
+ # header
+ expect(rendered).to have_text("##{pipeline.id}")
+ expect(rendered).to have_css('time', text: pipeline.created_at.strftime("%b %d, %Y"))
+ expect(rendered).to have_selector(%Q(img[alt$="#{pipeline.user.name}'s avatar"]))
+ expect(rendered).to have_link(pipeline.user.name, href: user_path(pipeline.user))
+
# stages
expect(rendered).to have_text('Build')
expect(rendered).to have_text('Test')