diff options
author | Grzegorz Bizon <grzesiek.bizon@gmail.com> | 2017-04-03 10:36:09 +0300 |
---|---|---|
committer | Grzegorz Bizon <grzesiek.bizon@gmail.com> | 2017-04-03 10:36:09 +0300 |
commit | fc550b398be6cdc4584dad79411929305815ffaa (patch) | |
tree | 0995b6990fa3599dddfed5bd2720589731bcdf31 /spec | |
parent | 83d1fe9b5aeb947c1387666205ecaca81f2bf3a2 (diff) | |
parent | e7e9307219d1c81427f95444b36471c519dc06c2 (diff) |
Merge branch 'master' into feature/multi-level-container-registry-images
* master: (230 commits)
Fix N+1 query in loading pipelines in merge requests
Fix Spinach and Capybara dependencies
Prevent users from disconnecting gitlab account from CAS
30276 Move issue, mr, todos next to profile dropdown in top nav
Refactor SearchController#show
Properly eagerly-load the Capybara server for JS feature specs only
Updating documentation to include a missing step in the update procedure
Eager-load the Capybara server to prevent timeouts
Increase Capybara's timeout
Add metrics button to Environment Overview page
Fix link to Jira service documentation
Handle parsing OpenBSD ps output properly to display sidekiq infos on ...
Eliminate unnecessary queries that add ~500 ms of load time for a large issue
20914 Limits line length for project home page
Allow users to import GitHub projects to subgroups
Update dpl CI example
Fix the docs:check:links job
Don't clean up the gitlab-test-fork_bare repo
Make GitLab use Gitaly for commit_is_ancestor
Remove unnecessary ORDER BY clause from `forked_to_project_id` subquery
...
Diffstat (limited to 'spec')
207 files changed, 2880 insertions, 636 deletions
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb index 84a1ce773a1..5dd8f66343f 100644 --- a/spec/controllers/admin/application_settings_controller_spec.rb +++ b/spec/controllers/admin/application_settings_controller_spec.rb @@ -6,23 +6,34 @@ describe Admin::ApplicationSettingsController do let(:admin) { create(:admin) } before do - sign_in(admin) stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') end - describe 'PATCH #update' do + describe 'PUT #update' do + before do + sign_in(admin) + end + it 'updates the default_project_visibility for string value' do - patch :update, application_setting: { default_project_visibility: "20" } + put :update, application_setting: { default_project_visibility: "20" } + + expect(response).to redirect_to(admin_application_settings_path) + expect(ApplicationSetting.current.default_project_visibility).to eq(Gitlab::VisibilityLevel::PUBLIC) + end + + it 'update the restricted levels for string values' do + put :update, application_setting: { restricted_visibility_levels: %w[10 20] } expect(response).to redirect_to(admin_application_settings_path) - expect(ApplicationSetting.current.default_project_visibility).to eq Gitlab::VisibilityLevel::PUBLIC + expect(ApplicationSetting.current.restricted_visibility_levels).to eq([10, 20]) end - it 'falls back to default with default_project_visibility setting is omitted' do - patch :update, application_setting: {} + it 'falls back to defaults when settings are omitted' do + put :update, application_setting: {} expect(response).to redirect_to(admin_application_settings_path) - expect(ApplicationSetting.current.default_project_visibility).to eq Gitlab::VisibilityLevel::PRIVATE + expect(ApplicationSetting.current.default_project_visibility).to eq(Gitlab::VisibilityLevel::PRIVATE) + expect(ApplicationSetting.current.restricted_visibility_levels).to be_empty end end end diff --git a/spec/controllers/profiles/accounts_controller_spec.rb b/spec/controllers/profiles/accounts_controller_spec.rb index 18148acde3e..2f9d18e3a0e 100644 --- a/spec/controllers/profiles/accounts_controller_spec.rb +++ b/spec/controllers/profiles/accounts_controller_spec.rb @@ -1,25 +1,47 @@ require 'spec_helper' describe Profiles::AccountsController do - let(:user) { create(:omniauth_user, provider: 'saml') } + describe 'DELETE unlink' do + let(:user) { create(:omniauth_user) } - before do - sign_in(user) - end + before do + sign_in(user) + end - it 'does not allow to unlink SAML connected account' do - identity = user.identities.last - delete :unlink, provider: 'saml' - updated_user = User.find(user.id) + it 'renders 404 if someone tries to unlink a non existent provider' do + delete :unlink, provider: 'github' - expect(response).to have_http_status(302) - expect(updated_user.identities.size).to eq(1) - expect(updated_user.identities).to include(identity) - end + expect(response).to have_http_status(404) + end + + [:saml, :cas3].each do |provider| + describe "#{provider} provider" do + let(:user) { create(:omniauth_user, provider: provider.to_s) } + + it "does not allow to unlink connected account" do + identity = user.identities.last + + delete :unlink, provider: provider.to_s + + expect(response).to have_http_status(302) + expect(user.reload.identities).to include(identity) + end + end + end + + [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0].each do |provider| + describe "#{provider} provider" do + let(:user) { create(:omniauth_user, provider: provider.to_s) } + + it 'allows to unlink connected account' do + identity = user.identities.last - it 'does allow to delete other linked accounts' do - user.identities.create(provider: 'twitter', extern_uid: 'twitter_123') + delete :unlink, provider: provider.to_s - expect { delete :unlink, provider: 'twitter' }.to change(Identity.all, :size).by(-1) + expect(response).to have_http_status(302) + expect(user.reload.identities).not_to include(identity) + end + end + end end end diff --git a/spec/controllers/profiles/notifications_controller_spec.rb b/spec/controllers/profiles/notifications_controller_spec.rb new file mode 100644 index 00000000000..b97cdd4d489 --- /dev/null +++ b/spec/controllers/profiles/notifications_controller_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe Profiles::NotificationsController do + let(:user) do + create(:user) do |user| + user.emails.create(email: 'original@example.com') + user.emails.create(email: 'new@example.com') + user.notification_email = 'original@example.com' + user.save! + end + end + + describe 'GET show' do + it 'renders' do + sign_in(user) + + get :show + + expect(response).to render_template :show + end + end + + describe 'POST update' do + it 'updates only permitted attributes' do + sign_in(user) + + put :update, user: { notification_email: 'new@example.com', notified_of_own_activity: true, admin: true } + + user.reload + expect(user.notification_email).to eq('new@example.com') + expect(user.notified_of_own_activity).to eq(true) + expect(user.admin).to eq(false) + expect(controller).to set_flash[:notice].to('Notification settings saved') + end + + it 'shows an error message if the params are invalid' do + sign_in(user) + + put :update, user: { notification_email: '' } + + expect(user.reload.notification_email).to eq('original@example.com') + expect(controller).to set_flash[:alert].to('Failed to save new settings') + end + end +end diff --git a/spec/controllers/projects/builds_controller_spec.rb b/spec/controllers/projects/builds_controller_spec.rb new file mode 100644 index 00000000000..683667129e5 --- /dev/null +++ b/spec/controllers/projects/builds_controller_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Projects::BuildsController do + include ApiHelpers + + let(:user) { create(:user) } + let(:project) { create(:empty_project, :public) } + + before do + sign_in(user) + end + + describe 'GET status.json' do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:build) { create(:ci_build, pipeline: pipeline) } + let(:status) { build.detailed_status(double('user')) } + + before do + get :status, namespace_id: project.namespace, + project_id: project, + id: build.id, + format: :json + end + + it 'return a detailed build status in json' do + expect(response).to have_http_status(:ok) + expect(json_response['text']).to eq status.text + expect(json_response['label']).to eq status.label + expect(json_response['icon']).to eq status.icon + expect(json_response['favicon']).to eq status.favicon + end + end +end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index 83d80b376fb..5525fbd8130 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -81,6 +81,39 @@ describe Projects::EnvironmentsController do end end + describe 'GET folder' do + before do + create(:environment, project: project, + name: 'staging-1.0/review', + state: :available) + end + + context 'when using default format' do + it 'responds with HTML' do + get :folder, namespace_id: project.namespace, + project_id: project, + id: 'staging-1.0' + + expect(response).to be_ok + expect(response).to render_template 'folder' + end + end + + context 'when using JSON format' do + it 'responds with JSON' do + get :folder, namespace_id: project.namespace, + project_id: project, + id: 'staging-1.0', + format: :json + + expect(response).to be_ok + expect(response).not_to render_template 'folder' + expect(json_response['environments'][0]) + .to include('name' => 'staging-1.0/review') + end + end + end + describe 'GET show' do context 'with valid id' do it 'responds with a status code 200' do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index c310d830e81..72f41f7209a 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -1178,4 +1178,42 @@ describe Projects::MergeRequestsController do end end end + + describe 'GET pipeline_status.json' do + context 'when head_pipeline exists' do + let!(:pipeline) do + create(:ci_pipeline, project: merge_request.source_project, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha) + end + + let(:status) { pipeline.detailed_status(double('user')) } + + before { get_pipeline_status } + + it 'return a detailed head_pipeline status in json' do + expect(response).to have_http_status(:ok) + expect(json_response['text']).to eq status.text + expect(json_response['label']).to eq status.label + expect(json_response['icon']).to eq status.icon + expect(json_response['favicon']).to eq status.favicon + end + end + + context 'when head_pipeline does not exist' do + before { get_pipeline_status } + + it 'return empty' do + expect(response).to have_http_status(:ok) + expect(json_response).to be_empty + end + end + + def get_pipeline_status + get :pipeline_status, namespace_id: project.namespace, + project_id: project, + id: merge_request.iid, + format: :json + end + end end diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 04bb5cbbd59..d8f9bfd0d37 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -69,4 +69,24 @@ describe Projects::PipelinesController do format: :json end end + + describe 'GET status.json' do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:status) { pipeline.detailed_status(double('user')) } + + before do + get :status, namespace_id: project.namespace, + project_id: project, + id: pipeline.id, + format: :json + end + + it 'return a detailed pipeline status in json' do + expect(response).to have_http_status(:ok) + expect(json_response['text']).to eq status.text + expect(json_response['label']).to eq status.label + expect(json_response['icon']).to eq status.icon + expect(json_response['favicon']).to eq status.favicon + end + end end diff --git a/spec/factories/boards.rb b/spec/factories/boards.rb index a581725245a..4df9aef2846 100644 --- a/spec/factories/boards.rb +++ b/spec/factories/boards.rb @@ -3,7 +3,7 @@ FactoryGirl.define do project factory: :empty_project after(:create) do |board| - board.lists.create(list_type: :done) + board.lists.create(list_type: :closed) end end end diff --git a/spec/factories/lists.rb b/spec/factories/lists.rb index 2a2f3cca91c..f6a78811cbe 100644 --- a/spec/factories/lists.rb +++ b/spec/factories/lists.rb @@ -6,8 +6,8 @@ FactoryGirl.define do sequence(:position) end - factory :done_list, parent: :list do - list_type :done + factory :closed_list, parent: :list do + list_type :closed label nil position nil end diff --git a/spec/factories/system_note_metadata.rb b/spec/factories/system_note_metadata.rb new file mode 100644 index 00000000000..f487a2d7e4a --- /dev/null +++ b/spec/factories/system_note_metadata.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do + factory :system_note_metadata do + note + action 'merge' + end +end diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index f7e8b78b54d..e168585534d 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -42,7 +42,7 @@ describe 'Issue Boards', feature: true, js: true do end it 'creates default lists' do - lists = ['To Do', 'Doing', 'Done'] + lists = ['To Do', 'Doing', 'Closed'] page.within(find('.board-blank-state')) do click_button('Add default lists') @@ -65,7 +65,7 @@ describe 'Issue Boards', feature: true, js: true do let(:testing) { create(:label, project: project, name: 'Testing') } let(:bug) { create(:label, project: project, name: 'Bug') } let!(:backlog) { create(:label, project: project, name: 'Backlog') } - let!(:done) { create(:label, project: project, name: 'Done') } + let!(:closed) { create(:label, project: project, name: 'Closed') } let!(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') } let!(:list1) { create(:list, board: board, label: planning, position: 0) } @@ -114,7 +114,7 @@ describe 'Issue Boards', feature: true, js: true do end end - it 'search done list' do + it 'search closed list' do find('.filtered-search').set(issue8.title) find('.filtered-search').native.send_keys(:enter) @@ -186,13 +186,13 @@ describe 'Issue Boards', feature: true, js: true do end end - context 'done' do - it 'shows list of done issues' do + context 'closed' do + it 'shows list of closed issues' do wait_for_board_cards(3, 1) wait_for_ajax end - it 'moves issue to done' do + it 'moves issue to closed' do drag(list_from_index: 0, list_to_index: 2) wait_for_board_cards(1, 7) @@ -205,7 +205,7 @@ describe 'Issue Boards', feature: true, js: true do expect(find('.board:nth-child(3)')).not_to have_content(planning.title) end - it 'removes all of the same issue to done' do + it 'removes all of the same issue to closed' do drag(list_from_index: 0, list_to_index: 2) wait_for_board_cards(1, 7) @@ -252,7 +252,7 @@ describe 'Issue Boards', feature: true, js: true do expect(find('.board:nth-child(1)').all('.card').first).not_to have_content(planning.title) end - it 'issue moves from done' do + it 'issue moves from closed' do drag(list_from_index: 2, list_to_index: 1) expect(find('.board:nth-child(2)')).to have_content(issue8.title) @@ -308,12 +308,12 @@ describe 'Issue Boards', feature: true, js: true do expect(page).to have_selector('.board', count: 4) end - it 'creates new list for Done label' do + it 'creates new list for Closed label' do click_button 'Add list' wait_for_ajax page.within('.dropdown-menu-issues-board-new') do - click_link done.title + click_link closed.title end wait_for_vue_resource @@ -326,7 +326,7 @@ describe 'Issue Boards', feature: true, js: true do wait_for_ajax page.within('.dropdown-menu-issues-board-new') do - click_link done.title + click_link closed.title end wait_for_vue_resource diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index 6d14a8cf483..e6d7cf106d4 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -25,7 +25,7 @@ describe 'Issue Boards new issue', feature: true, js: true do expect(page).to have_selector('.board-issue-count-holder .btn', count: 1) end - it 'does not display new issue button in done list' do + it 'does not display new issue button in closed list' do page.within('.board:nth-child(2)') do expect(page).not_to have_selector('.board-issue-count-holder .btn') end diff --git a/spec/features/groups/group_name_toggle_spec.rb b/spec/features/groups/group_name_toggle_spec.rb index 8528718a2f7..8a1d415c4f1 100644 --- a/spec/features/groups/group_name_toggle_spec.rb +++ b/spec/features/groups/group_name_toggle_spec.rb @@ -6,39 +6,46 @@ feature 'Group name toggle', feature: true, js: true do let(:nested_group_2) { create(:group, parent: nested_group_1) } let(:nested_group_3) { create(:group, parent: nested_group_2) } + SMALL_SCREEN = 300 + before do login_as :user end - it 'is not present for less than 3 groups' do - visit group_path(group) - expect(page).not_to have_css('.group-name-toggle') + it 'is not present if enough horizontal space' do + visit group_path(nested_group_3) - visit group_path(nested_group_1) + container_width = page.evaluate_script("$('.title-container')[0].offsetWidth") + title_width = page.evaluate_script("$('.title')[0].offsetWidth") + + expect(container_width).to be > title_width expect(page).not_to have_css('.group-name-toggle') end - it 'is present for nested group of 3 or more in the namespace' do - visit group_path(nested_group_2) - expect(page).to have_css('.group-name-toggle') - + it 'is present if the title is longer than the container' do visit group_path(nested_group_3) - expect(page).to have_css('.group-name-toggle') + title_width = page.evaluate_script("$('.title')[0].offsetWidth") + + page_height = page.current_window.size[1] + page.current_window.resize_to(SMALL_SCREEN, page_height) + + find('.group-name-toggle') + container_width = page.evaluate_script("$('.title-container')[0].offsetWidth") + + expect(title_width).to be > container_width end - context 'for group with at least 3 groups' do - before do - visit group_path(nested_group_2) - end + it 'should show the full group namespace when toggled' do + page_height = page.current_window.size[1] + page.current_window.resize_to(SMALL_SCREEN, page_height) + visit group_path(nested_group_3) - it 'should show the full group namespace when toggled' do - expect(page).not_to have_content(group.name) - expect(page).to have_css('.group-path.hidable', visible: false) + expect(page).not_to have_content(group.name) + expect(page).to have_css('.group-path.hidable', visible: false) - click_button '...' + click_button '...' - expect(page).to have_content(group.name) - expect(page).to have_css('.group-path.hidable', visible: true) - end + expect(page).to have_content(group.name) + expect(page).to have_css('.group-path.hidable', visible: true) end end diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index d243f9478bb..c90cc06a8f5 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -46,7 +46,7 @@ feature 'Group', feature: true do describe 'Mattermost team creation' do before do - allow(Settings.mattermost).to receive_messages(enabled: mattermost_enabled) + stub_mattermost_setting(enabled: mattermost_enabled) visit new_group_path end @@ -100,6 +100,16 @@ feature 'Group', feature: true do end end + it 'checks permissions to avoid exposing groups by parent_id' do + group = create(:group, :private, path: 'secret-group') + + logout + login_as(:user) + visit new_group_path(parent_id: group.id) + + expect(page).not_to have_content('secret-group') + end + describe 'group edit' do let(:group) { create(:group) } let(:path) { edit_group_path(group) } diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb index 01b657bcada..bc8cbe30e66 100644 --- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb @@ -43,10 +43,10 @@ describe 'Dropdown hint', js: true, feature: true do end describe 'filtering' do - it 'does not filter `Keep typing and press Enter`' do + it 'does not filter `Press Enter or click to search`' do filtered_search.set('randomtext') - expect(page).to have_css(js_dropdown_hint, text: 'Keep typing and press Enter', visible: false) + expect(page).to have_css(js_dropdown_hint, text: 'Press Enter or click to search', visible: false) expect(dropdown_hint_size).to eq(0) end diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb index 41ff31d2b99..3fde85b0a5c 100644 --- a/spec/features/issues/todo_spec.rb +++ b/spec/features/issues/todo_spec.rb @@ -17,13 +17,13 @@ feature 'Manually create a todo item from issue', feature: true, js: true do expect(page).to have_content 'Mark done' end - page.within '.header-content .todos-pending-count' do + page.within '.header-content .todos-count' do expect(page).to have_content '1' end visit namespace_project_issue_path(project.namespace, project, issue) - page.within '.header-content .todos-pending-count' do + page.within '.header-content .todos-count' do expect(page).to have_content '1' end end @@ -34,10 +34,10 @@ feature 'Manually create a todo item from issue', feature: true, js: true do click_button 'Mark done' end - expect(page).to have_selector('.todos-pending-count', visible: false) + expect(page).to have_selector('.todos-count', visible: false) visit namespace_project_issue_path(project.namespace, project, issue) - expect(page).to have_selector('.todos-pending-count', visible: false) + expect(page).to have_selector('.todos-count', visible: false) end end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index a58aedc924e..7afceb88cf9 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -13,6 +13,13 @@ describe 'Issues', feature: true do user2 = create(:user) project.team << [[@user, user2], :developer] + + project.repository.create_file( + @user, + '.gitlab/issue_templates/bug.md', + 'this is a test "bug" template', + message: 'added issue template', + branch_name: 'master') end describe 'Edit issue' do @@ -600,6 +607,16 @@ describe 'Issues', feature: true do expect(page.find_field("issue_description").value).to match /\n\n$/ end end + + context 'form filled by URL parameters' do + before do + visit new_namespace_project_issue_path(project.namespace, project, issuable_template: 'bug') + end + + it 'fills in template' do + expect(find('.js-issuable-selector .dropdown-toggle-text')).to have_content('bug') + end + end end describe 'new issue by email' do diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb index d5e3d8e7eff..69164aabdb2 100644 --- a/spec/features/merge_requests/diff_notes_resolve_spec.rb +++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb @@ -296,7 +296,7 @@ feature 'Diff notes resolve', feature: true, js: true do it 'displays next discussion even if hidden' do page.all('.note-discussion').each do |discussion| page.within discussion do - click_link 'Toggle discussion' + click_button 'Toggle discussion' end end @@ -477,13 +477,13 @@ feature 'Diff notes resolve', feature: true, js: true do it 'shows resolved icon' do expect(page).to have_content '1/1 discussion resolved' - click_link 'Toggle discussion' + click_button 'Toggle discussion' expect(page).to have_selector('.line-resolve-btn.is-active') end it 'does not allow user to click resolve button' do expect(page).to have_selector('.line-resolve-btn.is-disabled') - click_link 'Toggle discussion' + click_button 'Toggle discussion' expect(page).to have_selector('.line-resolve-btn.is-disabled') end diff --git a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb index 3dbe26cddb0..1bc2a5548dd 100644 --- a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb +++ b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb @@ -41,7 +41,7 @@ feature 'Clicking toggle commit message link', feature: true, js: true do visit namespace_project_merge_request_path(project.namespace, project, merge_request) expect(textbox).not_to be_visible - click_link "Modify commit message" + click_button "Modify commit message" expect(textbox).to be_visible end diff --git a/spec/features/merge_requests/toggler_behavior_spec.rb b/spec/features/merge_requests/toggler_behavior_spec.rb index a2cf9b18bf2..3acd3f6a8b3 100644 --- a/spec/features/merge_requests/toggler_behavior_spec.rb +++ b/spec/features/merge_requests/toggler_behavior_spec.rb @@ -18,7 +18,7 @@ feature 'toggler_behavior', js: true, feature: true do it 'should be scrolled down to fragment' do page_height = page.current_window.size[1] page_scroll_y = page.evaluate_script("window.scrollY") - fragment_position_top = page.evaluate_script("$('#{fragment_id}').offset().top") + fragment_position_top = page.evaluate_script("Math.round($('#{fragment_id}').offset().top)") expect(find('.js-toggle-content').visible?).to eq true expect(find(fragment_id).visible?).to eq true expect(fragment_position_top).to be >= page_scroll_y diff --git a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb new file mode 100644 index 00000000000..e05fbb3715c --- /dev/null +++ b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +feature 'Profile > Notifications > User changes notified_of_own_activity setting', feature: true, js: true do + let(:user) { create(:user) } + + before do + login_as(user) + end + + scenario 'User opts into receiving notifications about their own activity' do + visit profile_notifications_path + + expect(page).not_to have_checked_field('user[notified_of_own_activity]') + + check 'user[notified_of_own_activity]' + + expect(page).to have_content('Notification settings saved') + expect(page).to have_checked_field('user[notified_of_own_activity]') + end + + scenario 'User opts out of receiving notifications about their own activity' do + user.update!(notified_of_own_activity: true) + visit profile_notifications_path + + expect(page).to have_checked_field('user[notified_of_own_activity]') + + uncheck 'user[notified_of_own_activity]' + + expect(page).to have_content('Notification settings saved') + expect(page).not_to have_checked_field('user[notified_of_own_activity]') + end +end diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index e2d16e0830a..acc3efe04e6 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -166,6 +166,25 @@ feature 'Environment', :feature do end end + feature 'environment folders', :js do + context 'when folder name contains special charaters' do + before do + create(:environment, project: project, + name: 'staging-1.0/review', + state: :available) + + visit folder_namespace_project_environments_path(project.namespace, + project, + id: 'staging-1.0') + end + + it 'renders a correct environment folder' do + expect(page).to have_http_status(:ok) + expect(page).to have_content('Environments / staging-1.0') + end + end + end + feature 'auto-close environment when branch is deleted' do given(:project) { create(:project) } diff --git a/spec/features/projects/group_links_spec.rb b/spec/features/projects/group_links_spec.rb index 4c28205da9b..c969acc9140 100644 --- a/spec/features/projects/group_links_spec.rb +++ b/spec/features/projects/group_links_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Project group links', feature: true, js: true do +feature 'Project group links', :feature, :js do include Select2Helper let(:master) { create(:user) } @@ -51,4 +51,24 @@ feature 'Project group links', feature: true, js: true do end end end + + describe 'the groups dropdown' do + before do + group_two = create(:group) + group.add_owner(master) + group_two.add_owner(master) + + visit namespace_project_settings_members_path(project.namespace, project) + execute_script 'GroupsSelect.PER_PAGE = 1;' + open_select2 '#link_group_id' + end + + it 'should infinitely scroll' do + expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 1) + + scroll_select2_to_bottom('.select2-drop .select2-results:visible') + + expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 2) + end + end end diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb index df229d0aa78..dab78fd3571 100644 --- a/spec/features/projects/milestones/milestone_spec.rb +++ b/spec/features/projects/milestones/milestone_spec.rb @@ -23,12 +23,14 @@ feature 'Project milestone', :feature do end it 'shows issues stats' do - expect(page).to have_content 'issues:' + expect(find('.milestone-sidebar')).to have_content 'Issues 0' end - it 'shows Browse Issues button' do - within('#content-body') do - expect(page).to have_link 'Browse Issues' + it 'shows link to browse and add issues' do + within('.milestone-sidebar') do + expect(page).to have_link 'New issue' + expect(page).to have_link 'Open: 0' + expect(page).to have_link 'Closed: 0' end end end @@ -48,12 +50,12 @@ feature 'Project milestone', :feature do end it 'hides issues stats' do - expect(page).to have_no_content 'issues:' + expect(find('.milestone-sidebar')).not_to have_content 'Issues 0' end - it 'hides Browse Issues button' do - within('#content-body') do - expect(page).not_to have_link 'Browse Issues' + it 'hides new issue button' do + within('.milestone-sidebar') do + expect(page).not_to have_link 'New issue' end end diff --git a/spec/features/projects/services/mattermost_slash_command_spec.rb b/spec/features/projects/services/mattermost_slash_command_spec.rb index 24d22a092d4..dc3854262e7 100644 --- a/spec/features/projects/services/mattermost_slash_command_spec.rb +++ b/spec/features/projects/services/mattermost_slash_command_spec.rb @@ -7,7 +7,7 @@ feature 'Setup Mattermost slash commands', :feature, :js do let(:mattermost_enabled) { true } before do - Settings.mattermost['enabled'] = mattermost_enabled + stub_mattermost_setting(enabled: mattermost_enabled) project.team << [user, :master] login_as(user) visit edit_namespace_project_service_path(project.namespace, project, service) diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb index 850020109d4..c270511c903 100644 --- a/spec/features/todos/todos_spec.rb +++ b/spec/features/todos/todos_spec.rb @@ -251,7 +251,7 @@ describe 'Dashboard Todos', feature: true do end it 'shows "All done" message' do - within('.todos-pending-count') { expect(page).to have_content '0' } + within('.todos-count') { expect(page).to have_content '0' } expect(page).to have_content 'To do 0' expect(page).to have_content 'Done 0' expect(page).to have_selector('.todos-all-done', count: 1) @@ -267,7 +267,7 @@ describe 'Dashboard Todos', feature: true do end it 'shows 99+ for count >= 100 in notification' do - expect(page).to have_selector('.todos-pending-count', text: '99+') + expect(page).to have_selector('.todos-count', text: '99+') end it 'shows exact number in To do tab' do @@ -277,7 +277,7 @@ describe 'Dashboard Todos', feature: true do it 'shows exact number for count < 100' do 3.times { first('.js-done-todo').click } - expect(page).to have_selector('.todos-pending-count', text: '98') + expect(page).to have_selector('.todos-count', text: '98') end end diff --git a/spec/features/user_callout_spec.rb b/spec/features/user_callout_spec.rb index 659cd7c7af7..848af5e3a4d 100644 --- a/spec/features/user_callout_spec.rb +++ b/spec/features/user_callout_spec.rb @@ -7,15 +7,27 @@ describe 'User Callouts', js: true do before do login_as(user) - project.team << [user, :master] + project.team << [user, :master] end - it 'takes you to the profile preferences when the link is clicked' do + it 'takes you to the profile preferences when the link is clicked' do visit dashboard_projects_path click_link 'Check it out' expect(current_path).to eq profile_preferences_path end + it 'does not show when cookie is set' do + visit dashboard_projects_path + + within('.user-callout') do + find('.close').click + end + + visit dashboard_projects_path + + expect(page).not_to have_selector('.user-callout') + end + describe 'user callout should appear in two routes' do it 'shows up on the user profile' do visit user_path(user) @@ -31,7 +43,7 @@ describe 'User Callouts', js: true do it 'hides the user callout when click on the dismiss icon' do visit user_path(user) within('.user-callout') do - find('.close-user-callout').click + find('.close').click end expect(page).not_to have_selector('.user-callout') end diff --git a/spec/fixtures/api/schemas/list.json b/spec/fixtures/api/schemas/list.json index 819287bf919..11a4caf6628 100644 --- a/spec/fixtures/api/schemas/list.json +++ b/spec/fixtures/api/schemas/list.json @@ -10,7 +10,7 @@ "id": { "type": "integer" }, "list_type": { "type": "string", - "enum": ["label", "done"] + "enum": ["label", "closed"] }, "label": { "type": ["object", "null"], diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb index cd3281d6f51..a0e1265efff 100644 --- a/spec/helpers/auth_helper_spec.rb +++ b/spec/helpers/auth_helper_spec.rb @@ -62,4 +62,18 @@ describe AuthHelper do end end end + + describe 'unlink_allowed?' do + [:saml, :cas3].each do |provider| + it "returns true if the provider is #{provider}" do + expect(helper.unlink_allowed?(provider)).to be false + end + end + + [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0].each do |provider| + it "returns false if the provider is #{provider}" do + expect(helper.unlink_allowed?(provider)).to be true + end + end + end end diff --git a/spec/helpers/sidekiq_helper_spec.rb b/spec/helpers/sidekiq_helper_spec.rb index f86e496740a..117abc9c556 100644 --- a/spec/helpers/sidekiq_helper_spec.rb +++ b/spec/helpers/sidekiq_helper_spec.rb @@ -53,6 +53,14 @@ describe SidekiqHelper do expect(parts).to eq(['17725', '1.0', '12.1', 'Ssl', '19:20:15', 'sidekiq 4.2.1 gitlab-rails [0 of 25 busy]']) end + it 'parses OpenBSD output' do + # OpenBSD 6.1 + line = '49258 0.5 2.3 R/0 Fri10PM ruby23: sidekiq 4.2.7 gitlab [0 of 25 busy] (ruby23)' + parts = helper.parse_sidekiq_ps(line) + + expect(parts).to eq(['49258', '0.5', '2.3', 'R/0', 'Fri10PM', 'ruby23: sidekiq 4.2.7 gitlab [0 of 25 busy] (ruby23)']) + end + it 'does fail gracefully on line not matching the format' do line = '55137 10.0 2.1 S+ 2:30pm something' parts = helper.parse_sidekiq_ps(line) diff --git a/spec/javascripts/blob/notebook/index_spec.js b/spec/javascripts/blob/notebook/index_spec.js new file mode 100644 index 00000000000..11f2a950678 --- /dev/null +++ b/spec/javascripts/blob/notebook/index_spec.js @@ -0,0 +1,159 @@ +import Vue from 'vue'; +import renderNotebook from '~/blob/notebook'; + +describe('iPython notebook renderer', () => { + preloadFixtures('static/notebook_viewer.html.raw'); + + beforeEach(() => { + loadFixtures('static/notebook_viewer.html.raw'); + }); + + it('shows loading icon', () => { + renderNotebook(); + + expect( + document.querySelector('.loading'), + ).not.toBeNull(); + }); + + describe('successful response', () => { + const response = (request, next) => { + next(request.respondWith(JSON.stringify({ + cells: [{ + cell_type: 'markdown', + source: ['# test'], + }, { + cell_type: 'code', + execution_count: 1, + source: [ + 'def test(str)', + ' return str', + ], + outputs: [], + }], + }), { + status: 200, + })); + }; + + beforeEach((done) => { + Vue.http.interceptors.push(response); + + renderNotebook(); + + setTimeout(() => { + done(); + }); + }); + + afterEach(() => { + Vue.http.interceptors = _.without( + Vue.http.interceptors, response, + ); + }); + + it('does not show loading icon', () => { + expect( + document.querySelector('.loading'), + ).toBeNull(); + }); + + it('renders the notebook', () => { + expect( + document.querySelector('.md'), + ).not.toBeNull(); + }); + + it('renders the markdown cell', () => { + expect( + document.querySelector('h1'), + ).not.toBeNull(); + + expect( + document.querySelector('h1').textContent.trim(), + ).toBe('test'); + }); + + it('highlights code', () => { + expect( + document.querySelector('.token'), + ).not.toBeNull(); + + expect( + document.querySelector('.language-python'), + ).not.toBeNull(); + }); + }); + + describe('error in JSON response', () => { + const response = (request, next) => { + next(request.respondWith('{ "cells": [{"cell_type": "markdown"} }', { + status: 200, + })); + }; + + beforeEach((done) => { + Vue.http.interceptors.push(response); + + renderNotebook(); + + setTimeout(() => { + done(); + }); + }); + + afterEach(() => { + Vue.http.interceptors = _.without( + Vue.http.interceptors, response, + ); + }); + + it('does not show loading icon', () => { + expect( + document.querySelector('.loading'), + ).toBeNull(); + }); + + it('shows error message', () => { + expect( + document.querySelector('.md').textContent.trim(), + ).toBe('An error occured whilst parsing the file.'); + }); + }); + + describe('error getting file', () => { + const response = (request, next) => { + next(request.respondWith('', { + status: 500, + })); + }; + + beforeEach((done) => { + Vue.http.interceptors.push(response); + + renderNotebook(); + + setTimeout(() => { + done(); + }); + }); + + afterEach(() => { + Vue.http.interceptors = _.without( + Vue.http.interceptors, response, + ); + }); + + it('does not show loading icon', () => { + expect( + document.querySelector('.loading'), + ).toBeNull(); + }); + + it('shows error message', () => { + expect( + document.querySelector('.md').textContent.trim(), + ).toBe('An error occured whilst loading the file. Please try again later.'); + }); + }); +}); diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js index 73d18458366..de072e7e470 100644 --- a/spec/javascripts/boards/board_card_spec.js +++ b/spec/javascripts/boards/board_card_spec.js @@ -1,10 +1,12 @@ /* global List */ +/* global ListUser */ /* global ListLabel */ /* global listObj */ /* global boardsMockInterceptor */ /* global BoardService */ import Vue from 'vue'; +import '~/boards/models/user'; require('~/boards/models/list'); require('~/boards/models/label'); @@ -130,6 +132,23 @@ describe('Issue card', () => { expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({}); }); + it('does not set detail issue if img is clicked', (done) => { + vm.issue.assignee = new ListUser({ + id: 1, + name: 'testing 123', + username: 'test', + avatar: 'test_image', + }); + + Vue.nextTick(() => { + triggerEvent('mouseup', vm.$el.querySelector('img')); + + expect(gl.issueBoards.BoardsStore.detail.issue).toEqual({}); + + done(); + }); + }); + it('does not set detail issue if showDetail is false after mouseup', () => { triggerEvent('mouseup'); diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js index e21f4ca2bc0..b55ff2f473a 100644 --- a/spec/javascripts/boards/boards_store_spec.js +++ b/spec/javascripts/boards/boards_store_spec.js @@ -50,9 +50,9 @@ describe('Store', () => { it('finds list by ID', () => { gl.issueBoards.BoardsStore.addList(listObj); - const list = gl.issueBoards.BoardsStore.findList('id', 1); + const list = gl.issueBoards.BoardsStore.findList('id', listObj.id); - expect(list.id).toBe(1); + expect(list.id).toBe(listObj.id); }); it('finds list by type', () => { @@ -64,7 +64,7 @@ describe('Store', () => { it('gets issue when new list added', (done) => { gl.issueBoards.BoardsStore.addList(listObj); - const list = gl.issueBoards.BoardsStore.findList('id', 1); + const list = gl.issueBoards.BoardsStore.findList('id', listObj.id); expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1); @@ -89,9 +89,9 @@ describe('Store', () => { expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1); setTimeout(() => { - const list = gl.issueBoards.BoardsStore.findList('id', 1); + const list = gl.issueBoards.BoardsStore.findList('id', listObj.id); expect(list).toBeDefined(); - expect(list.id).toBe(1); + expect(list.id).toBe(listObj.id); expect(list.position).toBe(0); done(); }, 0); @@ -106,9 +106,9 @@ describe('Store', () => { expect(gl.issueBoards.BoardsStore.shouldAddBlankState()).toBe(false); }); - it('check for blank state adding when done list exist', () => { + it('check for blank state adding when closed list exist', () => { gl.issueBoards.BoardsStore.addList({ - list_type: 'done' + list_type: 'closed' }); expect(gl.issueBoards.BoardsStore.shouldAddBlankState()).toBe(true); @@ -126,7 +126,7 @@ describe('Store', () => { expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1); - gl.issueBoards.BoardsStore.removeList(1, 'label'); + gl.issueBoards.BoardsStore.removeList(listObj.id, 'label'); expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(0); }); @@ -137,7 +137,7 @@ describe('Store', () => { expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(2); - gl.issueBoards.BoardsStore.moveList(listOne, ['2', '1']); + gl.issueBoards.BoardsStore.moveList(listOne, [listObjDuplicate.id, listObj.id]); expect(listOne.position).toBe(1); }); diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js index 66fc01fa1e5..a9d4c6ef76f 100644 --- a/spec/javascripts/boards/list_spec.js +++ b/spec/javascripts/boards/list_spec.js @@ -43,7 +43,7 @@ describe('List model', () => { list = new List({ title: 'test', label: { - id: 1, + id: _.random(10000), title: 'test', color: 'red' } @@ -51,7 +51,7 @@ describe('List model', () => { list.save(); setTimeout(() => { - expect(list.id).toBe(1); + expect(list.id).toBe(listObj.id); expect(list.type).toBe('label'); expect(list.position).toBe(0); done(); @@ -60,7 +60,7 @@ describe('List model', () => { it('destroys the list', (done) => { gl.issueBoards.BoardsStore.addList(listObj); - list = gl.issueBoards.BoardsStore.findList('id', 1); + list = gl.issueBoards.BoardsStore.findList('id', listObj.id); expect(gl.issueBoards.BoardsStore.state.lists.length).toBe(1); list.destroy(); @@ -92,7 +92,7 @@ describe('List model', () => { const listDup = new List(listObjDuplicate); const issue = new ListIssue({ title: 'Testing', - iid: 1, + iid: _.random(10000), confidential: false, labels: [list.label, listDup.label] }); @@ -102,7 +102,7 @@ describe('List model', () => { spyOn(gl.boardService, 'moveIssue').and.callThrough(); - listDup.updateIssueLabel(list, issue); + listDup.updateIssueLabel(issue, list); expect(gl.boardService.moveIssue) .toHaveBeenCalledWith(issue.id, list.id, listDup.id, undefined, undefined); diff --git a/spec/javascripts/boards/mock_data.js b/spec/javascripts/boards/mock_data.js index 7a399b307ad..a4fa694eebe 100644 --- a/spec/javascripts/boards/mock_data.js +++ b/spec/javascripts/boards/mock_data.js @@ -1,12 +1,12 @@ /* eslint-disable comma-dangle, no-unused-vars, quote-props */ const listObj = { - id: 1, + id: _.random(10000), position: 0, title: 'Test', list_type: 'label', label: { - id: 1, + id: _.random(10000), title: 'Testing', color: 'red', description: 'testing;' @@ -14,12 +14,12 @@ const listObj = { }; const listObjDuplicate = { - id: 2, + id: listObj.id, position: 1, title: 'Test', list_type: 'label', label: { - id: 2, + id: listObj.label.id, title: 'Testing', color: 'red', description: 'testing;' diff --git a/spec/javascripts/collapsed_sidebar_todo_spec.js b/spec/javascripts/collapsed_sidebar_todo_spec.js new file mode 100644 index 00000000000..974815fe939 --- /dev/null +++ b/spec/javascripts/collapsed_sidebar_todo_spec.js @@ -0,0 +1,123 @@ +/* global Sidebar */ +/* eslint-disable no-new */ +import _ from 'underscore'; +import '~/right_sidebar'; + +describe('Issuable right sidebar collapsed todo toggle', () => { + const fixtureName = 'issues/open-issue.html.raw'; + const jsonFixtureName = 'todos/todos.json'; + + preloadFixtures(fixtureName); + preloadFixtures(jsonFixtureName); + + beforeEach(() => { + const todoData = getJSONFixture(jsonFixtureName); + new Sidebar(); + loadFixtures(fixtureName); + + document.querySelector('.js-right-sidebar') + .classList.toggle('right-sidebar-expanded'); + document.querySelector('.js-right-sidebar') + .classList.toggle('right-sidebar-collapsed'); + + spyOn(jQuery, 'ajax').and.callFake((res) => { + const d = $.Deferred(); + const response = _.clone(todoData); + + if (res.type === 'DELETE') { + delete response.delete_path; + } + + d.resolve(response); + return d.promise(); + }); + }); + + it('shows add todo button', () => { + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon'), + ).not.toBeNull(); + + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .fa-plus-square'), + ).not.toBeNull(); + + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'), + ).toBeNull(); + }); + + it('sets default tooltip title', () => { + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('title'), + ).toBe('Add todo'); + }); + + it('toggle todo state', () => { + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); + + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'), + ).not.toBeNull(); + + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .fa-check-square'), + ).not.toBeNull(); + }); + + it('toggle todo state of expanded todo toggle', () => { + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); + + expect( + document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(), + ).toBe('Mark done'); + }); + + it('toggles todo button tooltip', () => { + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); + + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('data-original-title'), + ).toBe('Mark done'); + }); + + it('marks todo as done', () => { + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); + + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'), + ).not.toBeNull(); + + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); + + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'), + ).toBeNull(); + + expect( + document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(), + ).toBe('Add todo'); + }); + + it('updates aria-label to mark done', () => { + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); + + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'), + ).toBe('Mark done'); + }); + + it('updates aria-label to add todo', () => { + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); + + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'), + ).toBe('Mark done'); + + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); + + expect( + document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'), + ).toBe('Add todo'); + }); +}); diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js index bc2e092db65..8cac3cad232 100644 --- a/spec/javascripts/commit/pipelines/pipelines_spec.js +++ b/spec/javascripts/commit/pipelines/pipelines_spec.js @@ -9,7 +9,7 @@ describe('Pipelines table in Commits and Merge requests', () => { loadFixtures('static/pipelines_table.html.raw'); }); - describe('successfull request', () => { + describe('successful request', () => { describe('without pipelines', () => { const pipelinesEmptyResponse = (request, next) => { next(request.respondWith(JSON.stringify([]), { @@ -17,24 +17,25 @@ describe('Pipelines table in Commits and Merge requests', () => { })); }; - beforeEach(() => { + beforeEach(function () { Vue.http.interceptors.push(pipelinesEmptyResponse); + + this.component = new PipelinesTable({ + el: document.querySelector('#commit-pipeline-table-view'), + }); }); - afterEach(() => { + afterEach(function () { Vue.http.interceptors = _.without( Vue.http.interceptors, pipelinesEmptyResponse, ); + this.component.$destroy(); }); - it('should render the empty state', (done) => { - const component = new PipelinesTable({ - el: document.querySelector('#commit-pipeline-table-view'), - }); - + it('should render the empty state', function (done) { setTimeout(() => { - expect(component.$el.querySelector('.empty-state')).toBeDefined(); - expect(component.$el.querySelector('.realtime-loading')).toBe(null); + expect(this.component.$el.querySelector('.empty-state')).toBeDefined(); + expect(this.component.$el.querySelector('.realtime-loading')).toBe(null); done(); }, 1); }); @@ -49,22 +50,23 @@ describe('Pipelines table in Commits and Merge requests', () => { beforeEach(() => { Vue.http.interceptors.push(pipelinesResponse); + + this.component = new PipelinesTable({ + el: document.querySelector('#commit-pipeline-table-view'), + }); }); afterEach(() => { Vue.http.interceptors = _.without( Vue.http.interceptors, pipelinesResponse, ); + this.component.$destroy(); }); it('should render a table with the received pipelines', (done) => { - const component = new PipelinesTable({ - el: document.querySelector('#commit-pipeline-table-view'), - }); - setTimeout(() => { - expect(component.$el.querySelectorAll('table > tbody > tr').length).toEqual(1); - expect(component.$el.querySelector('.realtime-loading')).toBe(null); + expect(this.component.$el.querySelectorAll('table > tbody > tr').length).toEqual(1); + expect(this.component.$el.querySelector('.realtime-loading')).toBe(null); done(); }, 0); }); @@ -78,24 +80,25 @@ describe('Pipelines table in Commits and Merge requests', () => { })); }; - beforeEach(() => { + beforeEach(function () { Vue.http.interceptors.push(pipelinesErrorResponse); + + this.component = new PipelinesTable({ + el: document.querySelector('#commit-pipeline-table-view'), + }); }); - afterEach(() => { + afterEach(function () { Vue.http.interceptors = _.without( Vue.http.interceptors, pipelinesErrorResponse, ); + this.component.$destroy(); }); - it('should render empty state', (done) => { - const component = new PipelinesTable({ - el: document.querySelector('#commit-pipeline-table-view'), - }); - + it('should render empty state', function (done) { setTimeout(() => { - expect(component.$el.querySelector('.js-pipelines-error-state')).toBeDefined(); - expect(component.$el.querySelector('.realtime-loading')).toBe(null); + expect(this.component.$el.querySelector('.js-pipelines-error-state')).toBeDefined(); + expect(this.component.$el.querySelector('.realtime-loading')).toBe(null); done(); }, 0); }); diff --git a/spec/javascripts/environments/environment_actions_spec.js b/spec/javascripts/environments/environment_actions_spec.js index 85b73f1d4e2..13840b42bd6 100644 --- a/spec/javascripts/environments/environment_actions_spec.js +++ b/spec/javascripts/environments/environment_actions_spec.js @@ -32,7 +32,12 @@ describe('Actions Component', () => { }).$mount(); }); - it('should render a dropdown with the provided actions', () => { + it('should render a dropdown button with icon and title attribute', () => { + expect(component.$el.querySelector('.fa-caret-down')).toBeDefined(); + expect(component.$el.querySelector('.dropdown-new').getAttribute('title')).toEqual('Deploy to...'); + }); + + it('should render a dropdown with the provided list of actions', () => { expect( component.$el.querySelectorAll('.dropdown-menu li').length, ).toEqual(actionsMock.length); diff --git a/spec/javascripts/environments/environment_monitoring_spec.js b/spec/javascripts/environments/environment_monitoring_spec.js new file mode 100644 index 00000000000..fc451cce641 --- /dev/null +++ b/spec/javascripts/environments/environment_monitoring_spec.js @@ -0,0 +1,23 @@ +import Vue from 'vue'; +import monitoringComp from '~/environments/components/environment_monitoring'; + +describe('Monitoring Component', () => { + let MonitoringComponent; + + beforeEach(() => { + MonitoringComponent = Vue.extend(monitoringComp); + }); + + it('should render a link to environment monitoring page', () => { + const monitoringUrl = 'https://gitlab.com'; + const component = new MonitoringComponent({ + propsData: { + monitoringUrl, + }, + }).$mount(); + + expect(component.$el.getAttribute('href')).toEqual(monitoringUrl); + expect(component.$el.querySelector('.fa-area-chart')).toBeDefined(); + expect(component.$el.getAttribute('title')).toEqual('Monitoring'); + }); +}); diff --git a/spec/javascripts/environments/environment_stop_spec.js b/spec/javascripts/environments/environment_stop_spec.js index 8f79b88f3df..01055e3f255 100644 --- a/spec/javascripts/environments/environment_stop_spec.js +++ b/spec/javascripts/environments/environment_stop_spec.js @@ -24,7 +24,7 @@ describe('Stop Component', () => { it('should render a button to stop the environment', () => { expect(component.$el.tagName).toEqual('BUTTON'); - expect(component.$el.getAttribute('title')).toEqual('Stop Environment'); + expect(component.$el.getAttribute('title')).toEqual('Stop'); }); it('should call the service when an action is clicked', () => { diff --git a/spec/javascripts/environments/environment_terminal_button_spec.js b/spec/javascripts/environments/environment_terminal_button_spec.js index b07aa4e1745..be2289edc2b 100644 --- a/spec/javascripts/environments/environment_terminal_button_spec.js +++ b/spec/javascripts/environments/environment_terminal_button_spec.js @@ -18,7 +18,7 @@ describe('Stop Component', () => { it('should render a link to open a web terminal with the provided path', () => { expect(component.$el.tagName).toEqual('A'); - expect(component.$el.getAttribute('title')).toEqual('Open web terminal'); + expect(component.$el.getAttribute('title')).toEqual('Terminal'); expect(component.$el.getAttribute('href')).toEqual(terminalPath); }); }); diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js index 848c7656a8d..5f7c05e9014 100644 --- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js @@ -92,6 +92,20 @@ const FilteredSearchSpecHelper = require('../helpers/filtered_search_spec_helper manager.search(); }); + + it('removes duplicated tokens', (done) => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')} + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')} + `); + + spyOn(gl.utils, 'visitUrl').and.callFake((url) => { + expect(url).toEqual(`${defaultParams}&label_name[]=bug`); + done(); + }); + + manager.search(); + }); }); describe('handleInputPlaceholder', () => { diff --git a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js index a91801cfc89..cabbc694ec4 100644 --- a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js @@ -122,6 +122,14 @@ require('~/filtered_search/filtered_search_tokenizer'); expect(results.lastToken).toBe('std::includes'); expect(results.searchToken).toBe('std::includes'); }); + + it('removes duplicated values', () => { + const results = gl.FilteredSearchTokenizer.processTokens('label:~foo label:~foo'); + expect(results.tokens.length).toBe(1); + expect(results.tokens[0].key).toBe('label'); + expect(results.tokens[0].value).toBe('foo'); + expect(results.tokens[0].symbol).toBe('~'); + }); }); }); })(); diff --git a/spec/javascripts/fixtures/dashboard.rb b/spec/javascripts/fixtures/dashboard.rb new file mode 100644 index 00000000000..e83db8daaf2 --- /dev/null +++ b/spec/javascripts/fixtures/dashboard.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe Dashboard::ProjectsController, '(JavaScript fixtures)', type: :controller do + include JavaScriptFixturesHelpers + + let(:admin) { create(:admin) } + let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:project) { create(:project, namespace: namespace, path: 'builds-project') } + + render_views + + before(:all) do + clean_frontend_fixtures('dashboard/') + end + + before(:each) do + sign_in(admin) + end + + it 'dashboard/user-callout.html.raw' do |example| + rendered = render_template('shared/_user_callout') + store_frontend_fixture(rendered, example.description) + end + + private + + def render_template(template_file_name) + controller.prepend_view_path(JavaScriptFixturesHelpers::FIXTURE_PATH) + controller.render_to_string(template_file_name, layout: false) + end +end diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb index ee893b76c84..fddeaaf504d 100644 --- a/spec/javascripts/fixtures/merge_requests.rb +++ b/spec/javascripts/fixtures/merge_requests.rb @@ -6,6 +6,15 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont let(:admin) { create(:admin) } let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} let(:project) { create(:project, namespace: namespace, path: 'merge-requests-project') } + let(:merge_request) { create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') } + let(:pipeline) do + create( + :ci_pipeline, + project: merge_request.source_project, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha + ) + end render_views @@ -18,7 +27,8 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont end it 'merge_requests/merge_request_with_task_list.html.raw' do |example| - merge_request = create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') + create(:ci_build, :pending, pipeline: pipeline) + render_merge_request(example.description, merge_request) end diff --git a/spec/javascripts/fixtures/notebook_viewer.html.haml b/spec/javascripts/fixtures/notebook_viewer.html.haml new file mode 100644 index 00000000000..17a7a9d8f31 --- /dev/null +++ b/spec/javascripts/fixtures/notebook_viewer.html.haml @@ -0,0 +1 @@ +.file-content#js-notebook-viewer{ data: { endpoint: '/test' } } diff --git a/spec/javascripts/fixtures/user_callout.html.haml b/spec/javascripts/fixtures/user_callout.html.haml deleted file mode 100644 index 275359bde0a..00000000000 --- a/spec/javascripts/fixtures/user_callout.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -.user-callout{ 'callout-svg' => custom_icon('icon_customization') } - diff --git a/spec/javascripts/header_spec.js b/spec/javascripts/header_spec.js index 46a27b8c98f..b5dde5525e5 100644 --- a/spec/javascripts/header_spec.js +++ b/spec/javascripts/header_spec.js @@ -5,7 +5,7 @@ require('~/lib/utils/text_utility'); (function() { describe('Header', function() { - var todosPendingCount = '.todos-pending-count'; + var todosPendingCount = '.todos-count'; var fixtureTemplate = 'issues/open-issue.html.raw'; function isTodosCountHidden() { @@ -21,31 +21,31 @@ require('~/lib/utils/text_utility'); loadFixtures(fixtureTemplate); }); - it('should update todos-pending-count after receiving the todo:toggle event', function() { + it('should update todos-count after receiving the todo:toggle event', function() { triggerToggle(5); expect($(todosPendingCount).text()).toEqual('5'); }); - it('should hide todos-pending-count when it is 0', function() { + it('should hide todos-count when it is 0', function() { triggerToggle(0); expect(isTodosCountHidden()).toEqual(true); }); - it('should show todos-pending-count when it is more than 0', function() { + it('should show todos-count when it is more than 0', function() { triggerToggle(10); expect(isTodosCountHidden()).toEqual(false); }); - describe('when todos-pending-count is 1000', function() { + describe('when todos-count is 1000', function() { beforeEach(function() { triggerToggle(1000); }); - it('should show todos-pending-count', function() { + it('should show todos-count', function() { expect(isTodosCountHidden()).toEqual(false); }); - it('should show 99+ for todos-pending-count', function() { + it('should show 99+ for todos-count', function() { expect($(todosPendingCount).text()).toEqual('99+'); }); }); diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index d2e24eb7eb2..7cf39d37181 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -108,6 +108,37 @@ require('~/lib/utils/common_utils'); }); }); + describe('gl.utils.normalizeCRLFHeaders', () => { + beforeEach(function () { + this.CLRFHeaders = 'a-header: a-value\nAnother-Header: ANOTHER-VALUE\nLaSt-HeAdEr: last-VALUE'; + + spyOn(String.prototype, 'split').and.callThrough(); + spyOn(gl.utils, 'normalizeHeaders').and.callThrough(); + + this.normalizeCRLFHeaders = gl.utils.normalizeCRLFHeaders(this.CLRFHeaders); + }); + + it('should split by newline', function () { + expect(String.prototype.split).toHaveBeenCalledWith('\n'); + }); + + it('should split by colon+space for each header', function () { + expect(String.prototype.split.calls.allArgs().filter(args => args[0] === ': ').length).toBe(3); + }); + + it('should call gl.utils.normalizeHeaders with a parsed headers object', function () { + expect(gl.utils.normalizeHeaders).toHaveBeenCalledWith(jasmine.any(Object)); + }); + + it('should return a normalized headers object', function () { + expect(this.normalizeCRLFHeaders).toEqual({ + 'A-HEADER': 'a-value', + 'ANOTHER-HEADER': 'ANOTHER-VALUE', + 'LAST-HEADER': 'last-VALUE', + }); + }); + }); + describe('gl.utils.parseIntPagination', () => { it('should parse to integers all string values and return pagination object', () => { const pagination = { diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js index c794a632417..e3429c2a1cb 100644 --- a/spec/javascripts/lib/utils/poll_spec.js +++ b/spec/javascripts/lib/utils/poll_spec.js @@ -160,4 +160,44 @@ describe('Poll', () => { Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor); }); }); + + describe('restart', () => { + it('should restart polling when its 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(); + setTimeout(() => { + Polling.restart(); + }, 0); + }, + errorCallback: callbacks.error, + }); + + spyOn(Polling, 'stop').and.callThrough(); + + Polling.makeRequest(); + + setTimeout(() => { + expect(service.fetch.calls.count()).toEqual(2); + expect(service.fetch).toHaveBeenCalledWith({ page: 1 }); + expect(Polling.stop).toHaveBeenCalled(); + done(); + }, 10); + + Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor); + }); + }); }); diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index 7506e6ab49e..7b9632be84e 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -38,6 +38,10 @@ require('vendor/jquery.scrollTo'); } }); + afterEach(function () { + this.class.destroy(); + }); + describe('#activateTab', function () { beforeEach(function () { spyOn($, 'ajax').and.callFake(function () {}); @@ -200,6 +204,42 @@ require('vendor/jquery.scrollTo'); expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); }); }); + + describe('#tabShown', () => { + beforeEach(function () { + loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + }); + + describe('with "Side-by-side"/parallel diff view', () => { + beforeEach(function () { + this.class.diffViewType = () => 'parallel'; + }); + + it('maintains `container-limited` for pipelines tab', function (done) { + const asyncClick = function (selector) { + return new Promise((resolve) => { + setTimeout(() => { + document.querySelector(selector).click(); + resolve(); + }); + }); + }; + + asyncClick('.merge-request-tabs .pipelines-tab a') + .then(() => asyncClick('.merge-request-tabs .diffs-tab a')) + .then(() => asyncClick('.merge-request-tabs .pipelines-tab a')) + .then(() => { + const hasContainerLimitedClass = document.querySelector('.content-wrapper .container-fluid').classList.contains('container-limited'); + expect(hasContainerLimitedClass).toBe(true); + }) + .then(done) + .catch((err) => { + done.fail(`Something went wrong clicking MR tabs: ${err.message}\n${err.stack}`); + }); + }); + }); + }); + describe('#loadDiff', function () { it('requires an absolute pathname', function () { spyOn($, 'ajax').and.callFake(function (options) { diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js index 285b7940174..f2072a6f350 100644 --- a/spec/javascripts/right_sidebar_spec.js +++ b/spec/javascripts/right_sidebar_spec.js @@ -74,9 +74,15 @@ import '~/right_sidebar'; var todoToggleSpy = spyOnEvent(document, 'todo:toggle'); - $('.js-issuable-todo').click(); + $('.issuable-sidebar-header .js-issuable-todo').click(); expect(todoToggleSpy.calls.count()).toEqual(1); }); + + it('should not hide collapsed icons', () => { + [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => { + expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy(); + }); + }); }); }).call(window); diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index d658f680f97..b30c5da8822 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -37,14 +37,33 @@ if (process.env.BABEL_ENV === 'coverage') { const troubleMakers = [ './blob_edit/blob_bundle.js', './boards/boards_bundle.js', + './cycle_analytics/cycle_analytics_bundle.js', './cycle_analytics/components/stage_plan_component.js', './cycle_analytics/components/stage_staging_component.js', './cycle_analytics/components/stage_test_component.js', + './commit/pipelines/pipelines_bundle.js', + './diff_notes/diff_notes_bundle.js', './diff_notes/components/jump_to_discussion.js', './diff_notes/components/resolve_count.js', + './dispatcher.js', + './environments/environments_bundle.js', + './filtered_search/filtered_search_bundle.js', + './graphs/graphs_bundle.js', + './issuable/issuable_bundle.js', + './issuable/time_tracking/time_tracking_bundle.js', + './main.js', + './merge_conflicts/merge_conflicts_bundle.js', './merge_conflicts/components/inline_conflict_lines.js', './merge_conflicts/components/parallel_conflict_lines.js', + './merge_request_widget/ci_bundle.js', + './monitoring/monitoring_bundle.js', + './network/network_bundle.js', './network/branch_graph.js', + './profile/profile_bundle.js', + './protected_branches/protected_branches_bundle.js', + './snippet/snippet_bundle.js', + './terminal/terminal_bundle.js', + './users/users_bundle.js', ]; describe('Uncovered files', function () { diff --git a/spec/javascripts/user_callout_spec.js b/spec/javascripts/user_callout_spec.js index 2398149d3ad..c0375ebc61c 100644 --- a/spec/javascripts/user_callout_spec.js +++ b/spec/javascripts/user_callout_spec.js @@ -4,7 +4,7 @@ import UserCallout from '~/user_callout'; const USER_CALLOUT_COOKIE = 'user_callout_dismissed'; describe('UserCallout', function () { - const fixtureName = 'static/user_callout.html.raw'; + const fixtureName = 'dashboard/user-callout.html.raw'; preloadFixtures(fixtureName); beforeEach(() => { @@ -12,26 +12,22 @@ describe('UserCallout', function () { Cookies.remove(USER_CALLOUT_COOKIE); this.userCallout = new UserCallout(); - this.closeButton = $('.close-user-callout'); - this.userCalloutBtn = $('.user-callout-btn'); + this.closeButton = $('.js-close-callout.close'); + this.userCalloutBtn = $('.js-close-callout:not(.close)'); this.userCalloutContainer = $('.user-callout'); }); - it('does not show when cookie is set not defined', () => { - expect(Cookies.get(USER_CALLOUT_COOKIE)).toBeUndefined(); - expect(this.userCalloutContainer.is(':visible')).toBe(true); - }); - - it('shows when cookie is set to false', () => { - Cookies.set(USER_CALLOUT_COOKIE, 'false'); - - expect(Cookies.get(USER_CALLOUT_COOKIE)).toBeDefined(); - expect(this.userCalloutContainer.is(':visible')).toBe(true); - }); - - it('hides when user clicks on the dismiss-icon', () => { + it('hides when user clicks on the dismiss-icon', (done) => { this.closeButton.click(); expect(Cookies.get(USER_CALLOUT_COOKIE)).toBe('true'); + + setTimeout(() => { + expect( + document.querySelector('.user-callout'), + ).toBeNull(); + + done(); + }); }); it('hides when user clicks on the "check it out" button', () => { @@ -39,19 +35,3 @@ describe('UserCallout', function () { expect(Cookies.get(USER_CALLOUT_COOKIE)).toBe('true'); }); }); - -describe('UserCallout when cookie is present', function () { - const fixtureName = 'static/user_callout.html.raw'; - preloadFixtures(fixtureName); - - beforeEach(() => { - loadFixtures(fixtureName); - Cookies.set(USER_CALLOUT_COOKIE, 'true'); - this.userCallout = new UserCallout(); - this.userCalloutContainer = $('.user-callout'); - }); - - it('removes the DOM element', () => { - expect(this.userCalloutContainer.length).toBe(0); - }); -}); diff --git a/spec/javascripts/vue_shared/components/pipelines_table_spec.js b/spec/javascripts/vue_shared/components/pipelines_table_spec.js index b0b1df5a753..4d3ced944d7 100644 --- a/spec/javascripts/vue_shared/components/pipelines_table_spec.js +++ b/spec/javascripts/vue_shared/components/pipelines_table_spec.js @@ -21,6 +21,10 @@ describe('Pipelines Table', () => { }).$mount(); }); + afterEach(() => { + component.$destroy(); + }); + it('should render a table', () => { expect(component.$el).toEqual('TABLE'); }); diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js index a5c3870b3ac..d1640ffed99 100644 --- a/spec/javascripts/vue_shared/components/table_pagination_spec.js +++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js @@ -83,7 +83,7 @@ describe('Pagination component', () => { }, }).$mount(); - component.changePage({ target: { innerText: 'Last >>' } }); + component.changePage({ target: { innerText: 'Last »' } }); expect(changeChanges.one).toEqual(10); }); @@ -100,7 +100,7 @@ describe('Pagination component', () => { }, }).$mount(); - component.changePage({ target: { innerText: '<< First' } }); + component.changePage({ target: { innerText: '« First' } }); expect(changeChanges.one).toEqual(1); }); diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb index 11607d4fb26..f1082495fcc 100644 --- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb @@ -21,6 +21,19 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do end end + describe 'performance' do + let(:another_issue) { create(:issue, project: project) } + + it 'does not have a N+1 query problem' do + single_reference = "Issue #{issue.to_reference}" + multiple_references = "Issues #{issue.to_reference} and #{another_issue.to_reference}" + + control_count = ActiveRecord::QueryRecorder.new { reference_filter(single_reference).to_html }.count + + expect { reference_filter(multiple_references).to_html }.not_to exceed_query_limit(control_count) + end + end + context 'internal reference' do it_behaves_like 'a reference containing an element node' diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb index 3d3d36061f4..40232f6e426 100644 --- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb @@ -17,6 +17,19 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do end end + describe 'performance' do + let(:another_merge) { create(:merge_request, source_project: project, source_branch: 'fix') } + + it 'does not have a N+1 query problem' do + single_reference = "Merge request #{merge.to_reference}" + multiple_references = "Merge requests #{merge.to_reference} and #{another_merge.to_reference}" + + control_count = ActiveRecord::QueryRecorder.new { reference_filter(single_reference).to_html }.count + + expect { reference_filter(multiple_references).to_html }.not_to exceed_query_limit(control_count) + end + end + context 'internal reference' do let(:reference) { merge.to_reference } diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb index 9873774909e..63b23dac7ed 100644 --- a/spec/lib/banzai/filter/user_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb @@ -83,6 +83,14 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do expect(doc.css('a').length).to eq 1 end + it 'links to a User with different case-sensitivity' do + user = create(:user, username: 'RescueRanger') + + doc = reference_filter("Hey #{user.to_reference.upcase}") + expect(doc.css('a').length).to eq 1 + expect(doc.css('a').text).to eq(user.to_reference) + end + it 'includes a data-user attribute' do doc = reference_filter("Hey #{reference}") link = doc.css('a').first diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb index 8b3bd08cf13..e648a3ac3a2 100644 --- a/spec/lib/gitlab/ci/status/build/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb @@ -27,6 +27,7 @@ describe Gitlab::Ci::Status::Build::Factory do it 'fabricates status with correct details' do expect(status.text).to eq 'passed' expect(status.icon).to eq 'icon_status_success' + expect(status.favicon).to eq 'favicon_status_success' expect(status.label).to eq 'passed' expect(status).to have_details expect(status).to have_action @@ -53,6 +54,7 @@ describe Gitlab::Ci::Status::Build::Factory do it 'fabricates status with correct details' do expect(status.text).to eq 'failed' expect(status.icon).to eq 'icon_status_failed' + expect(status.favicon).to eq 'favicon_status_failed' expect(status.label).to eq 'failed' expect(status).to have_details expect(status).to have_action @@ -79,6 +81,7 @@ describe Gitlab::Ci::Status::Build::Factory do it 'fabricates status with correct details' do expect(status.text).to eq 'failed' expect(status.icon).to eq 'icon_status_warning' + expect(status.favicon).to eq 'favicon_status_failed' expect(status.label).to eq 'failed (allowed to fail)' expect(status).to have_details expect(status).to have_action @@ -107,6 +110,7 @@ describe Gitlab::Ci::Status::Build::Factory do it 'fabricates status with correct details' do expect(status.text).to eq 'canceled' expect(status.icon).to eq 'icon_status_canceled' + expect(status.favicon).to eq 'favicon_status_canceled' expect(status.label).to eq 'canceled' expect(status).to have_details expect(status).to have_action @@ -132,6 +136,7 @@ describe Gitlab::Ci::Status::Build::Factory do it 'fabricates status with correct details' do expect(status.text).to eq 'running' expect(status.icon).to eq 'icon_status_running' + expect(status.favicon).to eq 'favicon_status_running' expect(status.label).to eq 'running' expect(status).to have_details expect(status).to have_action @@ -157,6 +162,7 @@ describe Gitlab::Ci::Status::Build::Factory do it 'fabricates status with correct details' do expect(status.text).to eq 'pending' expect(status.icon).to eq 'icon_status_pending' + expect(status.favicon).to eq 'favicon_status_pending' expect(status.label).to eq 'pending' expect(status).to have_details expect(status).to have_action @@ -181,6 +187,7 @@ describe Gitlab::Ci::Status::Build::Factory do it 'fabricates status with correct details' do expect(status.text).to eq 'skipped' expect(status.icon).to eq 'icon_status_skipped' + expect(status.favicon).to eq 'favicon_status_skipped' expect(status.label).to eq 'skipped' expect(status).to have_details expect(status).not_to have_action @@ -208,6 +215,7 @@ describe Gitlab::Ci::Status::Build::Factory do expect(status.text).to eq 'manual' expect(status.group).to eq 'manual' expect(status.icon).to eq 'icon_status_manual' + expect(status.favicon).to eq 'favicon_status_manual' expect(status.label).to eq 'manual play action' expect(status).to have_details expect(status).to have_action @@ -235,6 +243,7 @@ describe Gitlab::Ci::Status::Build::Factory do expect(status.text).to eq 'manual' expect(status.group).to eq 'manual' expect(status.icon).to eq 'icon_status_manual' + expect(status.favicon).to eq 'favicon_status_manual' expect(status.label).to eq 'manual stop action' expect(status).to have_details expect(status).to have_action diff --git a/spec/lib/gitlab/ci/status/canceled_spec.rb b/spec/lib/gitlab/ci/status/canceled_spec.rb index 768f8926f1d..530639a5897 100644 --- a/spec/lib/gitlab/ci/status/canceled_spec.rb +++ b/spec/lib/gitlab/ci/status/canceled_spec.rb @@ -17,6 +17,10 @@ describe Gitlab::Ci::Status::Canceled do it { expect(subject.icon).to eq 'icon_status_canceled' } end + describe '#favicon' do + it { expect(subject.favicon).to eq 'favicon_status_canceled' } + end + describe '#group' do it { expect(subject.group).to eq 'canceled' } end diff --git a/spec/lib/gitlab/ci/status/created_spec.rb b/spec/lib/gitlab/ci/status/created_spec.rb index e96c13aede3..aef982e17f1 100644 --- a/spec/lib/gitlab/ci/status/created_spec.rb +++ b/spec/lib/gitlab/ci/status/created_spec.rb @@ -17,6 +17,10 @@ describe Gitlab::Ci::Status::Created do it { expect(subject.icon).to eq 'icon_status_created' } end + describe '#favicon' do + it { expect(subject.favicon).to eq 'favicon_status_created' } + end + describe '#group' do it { expect(subject.group).to eq 'created' } end diff --git a/spec/lib/gitlab/ci/status/failed_spec.rb b/spec/lib/gitlab/ci/status/failed_spec.rb index e5da0a91159..9a25743885c 100644 --- a/spec/lib/gitlab/ci/status/failed_spec.rb +++ b/spec/lib/gitlab/ci/status/failed_spec.rb @@ -17,6 +17,10 @@ describe Gitlab::Ci::Status::Failed do it { expect(subject.icon).to eq 'icon_status_failed' } end + describe '#favicon' do + it { expect(subject.favicon).to eq 'favicon_status_failed' } + end + describe '#group' do it { expect(subject.group).to eq 'failed' } end diff --git a/spec/lib/gitlab/ci/status/manual_spec.rb b/spec/lib/gitlab/ci/status/manual_spec.rb index 3fd3727b92d..6fdc3801d71 100644 --- a/spec/lib/gitlab/ci/status/manual_spec.rb +++ b/spec/lib/gitlab/ci/status/manual_spec.rb @@ -17,6 +17,10 @@ describe Gitlab::Ci::Status::Manual do it { expect(subject.icon).to eq 'icon_status_manual' } end + describe '#favicon' do + it { expect(subject.favicon).to eq 'favicon_status_manual' } + end + describe '#group' do it { expect(subject.group).to eq 'manual' } end diff --git a/spec/lib/gitlab/ci/status/pending_spec.rb b/spec/lib/gitlab/ci/status/pending_spec.rb index 8d09cf2a05a..ffc53f0506b 100644 --- a/spec/lib/gitlab/ci/status/pending_spec.rb +++ b/spec/lib/gitlab/ci/status/pending_spec.rb @@ -17,6 +17,10 @@ describe Gitlab::Ci::Status::Pending do it { expect(subject.icon).to eq 'icon_status_pending' } end + describe '#favicon' do + it { expect(subject.favicon).to eq 'favicon_status_pending' } + end + describe '#group' do it { expect(subject.group).to eq 'pending' } end diff --git a/spec/lib/gitlab/ci/status/running_spec.rb b/spec/lib/gitlab/ci/status/running_spec.rb index 10d3bf749c1..0babf1fb54e 100644 --- a/spec/lib/gitlab/ci/status/running_spec.rb +++ b/spec/lib/gitlab/ci/status/running_spec.rb @@ -17,6 +17,10 @@ describe Gitlab::Ci::Status::Running do it { expect(subject.icon).to eq 'icon_status_running' } end + describe '#favicon' do + it { expect(subject.favicon).to eq 'favicon_status_running' } + end + describe '#group' do it { expect(subject.group).to eq 'running' } end diff --git a/spec/lib/gitlab/ci/status/skipped_spec.rb b/spec/lib/gitlab/ci/status/skipped_spec.rb index 10db93d3802..670747c9f0b 100644 --- a/spec/lib/gitlab/ci/status/skipped_spec.rb +++ b/spec/lib/gitlab/ci/status/skipped_spec.rb @@ -17,6 +17,10 @@ describe Gitlab::Ci::Status::Skipped do it { expect(subject.icon).to eq 'icon_status_skipped' } end + describe '#favicon' do + it { expect(subject.favicon).to eq 'favicon_status_skipped' } + end + describe '#group' do it { expect(subject.group).to eq 'skipped' } end diff --git a/spec/lib/gitlab/ci/status/success_spec.rb b/spec/lib/gitlab/ci/status/success_spec.rb index 230f24b94a4..ff65b074808 100644 --- a/spec/lib/gitlab/ci/status/success_spec.rb +++ b/spec/lib/gitlab/ci/status/success_spec.rb @@ -17,6 +17,10 @@ describe Gitlab::Ci::Status::Success do it { expect(subject.icon).to eq 'icon_status_success' } end + describe '#favicon' do + it { expect(subject.favicon).to eq 'favicon_status_success' } + end + describe '#group' do it { expect(subject.group).to eq 'success' } end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 9ed43da1116..d4b7684adfd 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -771,8 +771,8 @@ describe Gitlab::Git::Repository, seed_helper: true do commits = repository.log(options) expect(commits.size).to be > 0 - satisfy do - commits.all? { |commit| commit.created_at >= options[:after] } + expect(commits).to satisfy do |commits| + commits.all? { |commit| commit.time >= options[:after] } end end end @@ -784,8 +784,27 @@ describe Gitlab::Git::Repository, seed_helper: true do commits = repository.log(options) expect(commits.size).to be > 0 - satisfy do - commits.all? { |commit| commit.created_at <= options[:before] } + expect(commits).to satisfy do |commits| + commits.all? { |commit| commit.time <= options[:before] } + end + end + end + + context 'when multiple paths are provided' do + let(:options) { { ref: 'master', path: ['PROCESS.md', 'README.md'] } } + + def commit_files(commit) + commit.diff(commit.parent_ids.first).deltas.flat_map do |delta| + [delta.old_file[:path], delta.new_file[:path]].uniq.compact + end + end + + it 'only returns commits matching at least one path' do + commits = repository.log(options) + + expect(commits.size).to be > 0 + expect(commits).to satisfy do |commits| + commits.none? { |commit| (commit_files(commit) & options[:path]).empty? } end end end diff --git a/spec/lib/gitlab/gitaly_client/notifications_spec.rb b/spec/lib/gitlab/gitaly_client/notifications_spec.rb index a6252c99aa1..bb5d93994ad 100644 --- a/spec/lib/gitlab/gitaly_client/notifications_spec.rb +++ b/spec/lib/gitlab/gitaly_client/notifications_spec.rb @@ -1,20 +1,13 @@ require 'spec_helper' describe Gitlab::GitalyClient::Notifications do - let(:client) { Gitlab::GitalyClient::Notifications.new } - - before do - allow(Gitlab.config.gitaly).to receive(:socket_path).and_return('path/to/gitaly.socket') - end - describe '#post_receive' do - let(:repo_path) { '/path/to/my_repo.git' } - it 'sends a post_receive message' do + repo_path = create(:empty_project).repository.path_to_repo expect_any_instance_of(Gitaly::Notifications::Stub). to receive(:post_receive).with(post_receive_request_with_repo_path(repo_path)) - client.post_receive(repo_path) + described_class.new(repo_path).post_receive end end end diff --git a/spec/lib/gitlab/github_import/label_formatter_spec.rb b/spec/lib/gitlab/github_import/label_formatter_spec.rb index 10449ef5fcb..565435824fd 100644 --- a/spec/lib/gitlab/github_import/label_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/label_formatter_spec.rb @@ -25,7 +25,7 @@ describe Gitlab::GithubImport::LabelFormatter, lib: true do context 'when label exists' do it 'does not create a new label' do - project.labels.create(name: raw.name) + Labels::CreateService.new(name: raw.name).execute(project: project) expect { subject.create! }.not_to change(Label, :count) end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index e5bba29d85b..d1902766b99 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -29,6 +29,7 @@ notes: - resolved_by - todos - events +- system_note_metadata label_links: - target - label diff --git a/spec/lib/gitlab/repo_path_spec.rb b/spec/lib/gitlab/repo_path_spec.rb new file mode 100644 index 00000000000..0fb5d7646f2 --- /dev/null +++ b/spec/lib/gitlab/repo_path_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe ::Gitlab::RepoPath do + describe '.strip_storage_path' do + before do + allow(Gitlab.config.repositories).to receive(:storages).and_return({ + 'storage1' => { 'path' => '/foo' }, + 'storage2' => { 'path' => '/bar' }, + }) + end + + it 'strips the storage path' do + expect(described_class.strip_storage_path('/bar/foo/qux/baz.git')).to eq('foo/qux/baz.git') + end + + it 'raises NotFoundError if no storage matches the path' do + expect { described_class.strip_storage_path('/doesnotexist/foo.git') }.to raise_error( + described_class::NotFoundError + ) + end + end +end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 8e5e8288c49..535c96eeee9 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -184,18 +184,14 @@ describe Gitlab::Workhorse, lib: true do it { expect(subject).to eq({ GL_ID: "user-#{user.id}", RepoPath: repository.path_to_repo }) } - context 'when Gitaly socket path is present' do - let(:gitaly_socket_path) { '/tmp/gitaly.sock' } - + context 'when Gitaly is enabled' do before do - allow(Gitlab.config.gitaly).to receive(:socket_path).and_return(gitaly_socket_path) + allow(Gitlab.config.gitaly).to receive(:enabled).and_return(true) end it 'includes Gitaly params in the returned value' do - expect(subject).to include({ - GitalyResourcePath: "/projects/#{repository.project.id}/git-http/info-refs", - GitalySocketPath: gitaly_socket_path, - }) + gitaly_socket_path = URI(Gitlab::GitalyClient.get_address('default')).path + expect(subject).to include({ GitalySocketPath: gitaly_socket_path }) end end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 4b72eb2eaa3..f60c5ffb32a 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -184,6 +184,7 @@ describe Notify do end context 'for merge requests' do + let(:project) { create(:project, :repository) } let(:merge_author) { create(:user) } let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) } let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: FFaker::Lorem.sentence) } @@ -334,7 +335,7 @@ describe Notify do end describe 'project was moved' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:user) { create(:user) } subject { Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab") } @@ -460,7 +461,7 @@ describe Notify do end describe 'project invitation' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:master) { create(:user).tap { |u| project.team << [u, :master] } } let(:project_member) { invite_to_project(project, inviter: master) } @@ -480,7 +481,7 @@ describe Notify do end describe 'project invitation accepted' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:invited_user) { create(:user, name: 'invited user') } let(:master) { create(:user).tap { |u| project.team << [u, :master] } } let(:project_member) do @@ -505,7 +506,7 @@ describe Notify do end describe 'project invitation declined' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:master) { create(:user).tap { |u| project.team << [u, :master] } } let(:project_member) do invitee = invite_to_project(project, inviter: master) @@ -569,6 +570,7 @@ describe Notify do end describe 'on a commit' do + let(:project) { create(:project, :repository) } let(:commit) { project.commit } before(:each) { allow(note).to receive(:noteable).and_return(commit) } @@ -636,6 +638,7 @@ describe Notify do end context 'items that are noteable, emails for a note on a diff' do + let(:project) { create(:project, :repository) } let(:note_author) { create(:user, name: 'author_name') } before :each do @@ -977,6 +980,7 @@ describe Notify do end describe 'email on push with multiple commits' do + let(:project) { create(:project, :repository) } let(:example_site_path) { root_path } let(:user) { create(:user) } let(:raw_compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_image_commit.id, sample_commit.id) } @@ -1070,6 +1074,7 @@ describe Notify do end describe 'email on push with a single commit' do + let(:project) { create(:project, :repository) } let(:example_site_path) { root_path } let(:user) { create(:user) } let(:raw_compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_commit.parent_id, sample_commit.id) } @@ -1102,7 +1107,7 @@ describe Notify do end describe 'HTML emails setting' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:multipart_mail) { Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab") } diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb index b6d678bac18..3db57595fa6 100644 --- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb +++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_worker_jobs.rb') describe MigrateProcessCommitWorkerJobs do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:commit) { project.commit.raw.raw_commit } diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb index 94c25a454aa..552229e9b07 100644 --- a/spec/models/blob_spec.rb +++ b/spec/models/blob_spec.rb @@ -53,6 +53,20 @@ describe Blob do end end + describe '#ipython_notebook?' do + it 'is falsey when language is not Jupyter Notebook' do + git_blob = double(text?: true, language: double(name: 'JSON')) + + expect(described_class.decorate(git_blob)).not_to be_ipython_notebook + end + + it 'is truthy when language is Jupyter Notebook' do + git_blob = double(text?: true, language: double(name: 'Jupyter Notebook')) + + expect(described_class.decorate(git_blob)).to be_ipython_notebook + end + end + describe '#video?' do it 'is falsey with image extension' do git_blob = Gitlab::Git::Blob.new(name: 'image.png') @@ -116,6 +130,11 @@ describe Blob do blob = stubbed_blob expect(blob.to_partial_path(project)).to eq 'download' end + + it 'handles iPython notebooks' do + blob = stubbed_blob(text?: true, ipython_notebook?: true) + expect(blob.to_partial_path(project)).to eq 'notebook' + end end describe '#size_within_svg_limits?' do diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index ea5e4e21039..7343b735a74 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -297,4 +297,40 @@ describe CommitStatus, :models do end end end + + describe '#locking_enabled?' do + before do + commit_status.lock_version = 100 + end + + subject { commit_status.locking_enabled? } + + context "when changing status" do + before do + commit_status.status = "running" + end + + it "lock" do + is_expected.to be true + end + + it "raise exception when trying to update" do + expect{ commit_status.save }.to raise_error(ActiveRecord::StaleObjectError) + end + end + + context "when changing description" do + before do + commit_status.description = "test" + end + + it "do not lock" do + is_expected.to be false + end + + it "save correctly" do + expect(commit_status.save).to be true + end + end + end end diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb index e6ca4853873..db2c2619968 100644 --- a/spec/models/list_spec.rb +++ b/spec/models/list_spec.rb @@ -19,8 +19,8 @@ describe List do expect(subject).to validate_uniqueness_of(:label_id).scoped_to(:board_id) end - context 'when list_type is set to done' do - subject { described_class.new(list_type: :done) } + context 'when list_type is set to closed' do + subject { described_class.new(list_type: :closed) } it { is_expected.not_to validate_presence_of(:label) } it { is_expected.not_to validate_presence_of(:position) } @@ -34,8 +34,8 @@ describe List do expect(subject.destroy).to be_truthy end - it 'can not be destroyed when when list_type is set to done' do - subject = create(:done_list) + it 'can not be destroyed when when list_type is set to closed' do + subject = create(:closed_list) expect(subject.destroy).to be_falsey end @@ -48,8 +48,8 @@ describe List do expect(subject).to be_destroyable end - it 'returns false when list_type is set to done' do - subject.list_type = :done + it 'returns false when list_type is set to closed' do + subject.list_type = :closed expect(subject).not_to be_destroyable end @@ -62,8 +62,8 @@ describe List do expect(subject).to be_movable end - it 'returns false when list_type is set to done' do - subject.list_type = :done + it 'returns false when list_type is set to closed' do + subject.list_type = :closed expect(subject).not_to be_movable end @@ -77,10 +77,10 @@ describe List do expect(subject.title).to eq 'Development' end - it 'returns Done when list_type is set to done' do - subject.list_type = :done + it 'returns Closed when list_type is set to closed' do + subject.list_type = :closed - expect(subject.title).to eq 'Done' + expect(subject.title).to eq 'Closed' end end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 95f4785060c..e1bfd20a6aa 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -129,10 +129,10 @@ describe Namespace, models: true do end end - describe '#move_dir' do + describe '#move_dir', repository: true do before do @namespace = create :namespace - @project = create(:empty_project, namespace: @namespace) + @project = create(:project_empty_repo, namespace: @namespace) allow(@namespace).to receive(:path_changed?).and_return(true) end @@ -141,9 +141,9 @@ describe Namespace, models: true do end it "moves dir if path changed" do - new_path = @namespace.path + "_new" - allow(@namespace).to receive(:path_was).and_return(@namespace.path) - allow(@namespace).to receive(:path).and_return(new_path) + new_path = @namespace.full_path + "_new" + allow(@namespace).to receive(:full_path_was).and_return(@namespace.full_path) + allow(@namespace).to receive(:full_path).and_return(new_path) expect(@namespace).to receive(:remove_exports!) expect(@namespace.move_dir).to be_truthy end @@ -163,16 +163,75 @@ describe Namespace, models: true do it { expect { @namespace.move_dir }.to raise_error('Namespace cannot be moved, because at least one project has images in container registry') } end + + context 'renaming a sub-group' do + let(:parent) { create(:group, name: 'parent', path: 'parent') } + let(:child) { create(:group, name: 'child', path: 'child', parent: parent) } + let!(:project) { create(:project_empty_repo, path: 'the-project', namespace: child) } + let(:uploads_dir) { File.join(CarrierWave.root, 'uploads', 'parent') } + let(:pages_dir) { File.join(TestEnv.pages_path, 'parent') } + + before do + FileUtils.mkdir_p(File.join(uploads_dir, 'child', 'the-project')) + FileUtils.mkdir_p(File.join(pages_dir, 'child', 'the-project')) + end + + it 'correctly moves the repository, uploads and pages' do + expected_repository_path = File.join(TestEnv.repos_path, 'parent', 'renamed', 'the-project.git') + expected_upload_path = File.join(uploads_dir, 'renamed', 'the-project') + expected_pages_path = File.join(pages_dir, 'renamed', 'the-project') + + child.update_attributes!(path: 'renamed') + + expect(File.directory?(expected_repository_path)).to be(true) + expect(File.directory?(expected_upload_path)).to be(true) + expect(File.directory?(expected_pages_path)).to be(true) + end + end end - 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) } + describe '#rm_dir', 'callback', repository: true do + let!(:project) { create(:project_empty_repo, namespace: namespace) } + let(:repository_storage_path) { Gitlab.config.repositories.storages.default['path'] } + let(:path_in_dir) { File.join(repository_storage_path, namespace.full_path) } + let(:deleted_path) { namespace.full_path.gsub(namespace.path, "#{namespace.full_path}+#{namespace.id}+deleted") } + let(:deleted_path_in_dir) { File.join(repository_storage_path, deleted_path) } + + it 'renames its dirs when deleted' do + allow(GitlabShellWorker).to receive(:perform_in) - it "removes its dirs when deleted" do namespace.destroy - expect(File.exist?(path)).to be(false) + expect(File.exist?(deleted_path_in_dir)).to be(true) + end + + it 'schedules the namespace for deletion' do + expect(GitlabShellWorker).to receive(:perform_in).with(5.minutes, :rm_namespace, repository_storage_path, deleted_path) + + namespace.destroy + end + + context 'in sub-groups' do + let(:parent) { create(:namespace, path: 'parent') } + let(:child) { create(:namespace, parent: parent, path: 'child') } + let!(:project) { create(:project_empty_repo, namespace: child) } + let(:path_in_dir) { File.join(repository_storage_path, 'parent', 'child') } + let(:deleted_path) { File.join('parent', "child+#{child.id}+deleted") } + let(:deleted_path_in_dir) { File.join(repository_storage_path, deleted_path) } + + it 'renames its dirs when deleted' do + allow(GitlabShellWorker).to receive(:perform_in) + + child.destroy + + expect(File.exist?(deleted_path_in_dir)).to be(true) + end + + it 'schedules the namespace for deletion' do + expect(GitlabShellWorker).to receive(:perform_in).with(5.minutes, :rm_namespace, repository_storage_path, deleted_path) + + child.destroy + end end it 'removes the exports folder' do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 585b87b828d..df742ee8084 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1851,4 +1851,17 @@ describe Repository, models: true do end end end + + describe '#is_ancestor?' do + context 'Gitaly is_ancestor feature enabled' do + it 'asks Gitaly server if it\'s an ancestor' do + commit = repository.commit + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(true) + expect(Gitlab::GitalyClient::Commit).to receive(:is_ancestor). + with(repository.raw_repository, commit.id, commit.id).and_return(true) + + expect(repository.is_ancestor?(commit.id, commit.id)).to be true + end + end + end end diff --git a/spec/models/system_note_metadata_spec.rb b/spec/models/system_note_metadata_spec.rb new file mode 100644 index 00000000000..d238e28209a --- /dev/null +++ b/spec/models/system_note_metadata_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe SystemNoteMetadata, models: true do + describe 'associations' do + it { is_expected.to belong_to(:note) } + end + + describe 'validation' do + it { is_expected.to validate_presence_of(:note) } + + context 'when action type is invalid' do + subject do + build(:system_note_metadata, note: build(:note), action: 'invalid_type' ) + end + + it { is_expected.to be_invalid } + end + + context 'when action type is valid' do + subject do + build(:system_note_metadata, note: build(:note), action: 'merge' ) + end + + it { is_expected.to be_valid } + end + end +end diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb index 7591bfd1471..2905d5b26a5 100644 --- a/spec/policies/issue_policy_spec.rb +++ b/spec/policies/issue_policy_spec.rb @@ -5,7 +5,7 @@ describe IssuePolicy, models: true do describe '#rules' do context 'using a regular issue' do - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:issue) { create(:issue, project: project) } let(:policies) { described_class.abilities(user, issue).to_set } diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 0a5edf35f59..064847ee3dc 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -74,7 +74,7 @@ describe ProjectPolicy, models: true do end it 'does not include the read_issue permission when the issue author is not a member of the private project' do - project = create(:project, :private) + project = create(:empty_project, :private) issue = create(:issue, project: project) user = issue.author diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index 63ec00cdf04..eed45d37444 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -424,12 +424,12 @@ describe API::Internal, api: true do end before do - allow(Gitlab.config.gitaly).to receive(:socket_path).and_return('path/to/gitaly.socket') + allow(Gitlab.config.gitaly).to receive(:enabled).and_return(true) end it "calls the Gitaly client if it's enabled" do expect_any_instance_of(Gitlab::GitalyClient::Notifications). - to receive(:post_receive).with(project.repository.path) + to receive(:post_receive) post api("/internal/notify_post_receive"), valid_params @@ -438,7 +438,7 @@ describe API::Internal, api: true do it "returns 500 if the gitaly call fails" do expect_any_instance_of(Gitlab::GitalyClient::Notifications). - to receive(:post_receive).with(project.repository.path).and_raise(GRPC::Unavailable) + to receive(:post_receive).and_raise(GRPC::Unavailable) post api("/internal/notify_post_receive"), valid_params diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 52f68fed2cc..91d6fb83c0b 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -333,8 +333,16 @@ describe API::Issues, api: true do end let(:base_url) { "/groups/#{group.id}/issues" } + it 'returns all group issues (including opened and closed)' do + get api(base_url, admin) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(3) + end + it 'returns group issues without confidential issues for non project members' do - get api(base_url, non_member) + get api("#{base_url}?state=opened", non_member) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -344,7 +352,7 @@ describe API::Issues, api: true do end it 'returns group confidential issues for author' do - get api(base_url, author) + get api("#{base_url}?state=opened", author) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -353,7 +361,7 @@ describe API::Issues, api: true do end it 'returns group confidential issues for assignee' do - get api(base_url, assignee) + get api("#{base_url}?state=opened", assignee) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -362,7 +370,7 @@ describe API::Issues, api: true do end it 'returns group issues with confidential issues for project members' do - get api(base_url, user) + get api("#{base_url}?state=opened", user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -371,7 +379,7 @@ describe API::Issues, api: true do end it 'returns group confidential issues for admin' do - get api(base_url, admin) + get api("#{base_url}?state=opened", admin) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -460,7 +468,7 @@ describe API::Issues, api: true do end it 'returns an array of issues in given milestone' do - get api("#{base_url}?milestone=#{group_milestone.title}", user) + get api("#{base_url}?state=opened&milestone=#{group_milestone.title}", user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 9aba1d75612..61d965e8974 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -623,64 +623,6 @@ describe API::MergeRequests, api: true do end end - describe "POST /projects/:id/merge_requests/:merge_request_iid/comments" do - it "returns comment" do - original_count = merge_request.notes.size - - post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/comments", user), note: "My comment" - - expect(response).to have_http_status(201) - expect(json_response['note']).to eq('My comment') - expect(json_response['author']['name']).to eq(user.name) - expect(json_response['author']['username']).to eq(user.username) - expect(merge_request.reload.notes.size).to eq(original_count + 1) - end - - it "returns 400 if note is missing" do - post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/comments", user) - expect(response).to have_http_status(400) - end - - it "returns 404 if merge request iid is invalid" do - post api("/projects/#{project.id}/merge_requests/404/comments", user), - note: 'My comment' - expect(response).to have_http_status(404) - end - - it "returns 404 if merge request id is used instead of iid" do - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user), - note: 'My comment' - expect(response).to have_http_status(404) - end - end - - describe "GET :id/merge_requests/:merge_request_iid/comments" do - let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") } - let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") } - - it "returns merge_request comments ordered by created_at" do - get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/comments", user) - - expect(response).to have_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(2) - expect(json_response.first['note']).to eq("a comment on a MR") - expect(json_response.first['author']['id']).to eq(user.id) - expect(json_response.last['note']).to eq("another comment on a MR") - end - - it "returns a 404 error if merge_request_iid is invalid" do - get api("/projects/#{project.id}/merge_requests/999/comments", user) - expect(response).to have_http_status(404) - end - - it "returns a 404 error if merge_request id is used instead of iid" do - get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user) - expect(response).to have_http_status(404) - end - end - describe 'GET :id/merge_requests/:merge_request_iid/closes_issues' do it 'returns the issue that will be closed on merge' do issue = create(:issue, project: project) diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index 347f8f6fa3b..d8eb8ce921e 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -34,7 +34,7 @@ describe API::Notes, api: true do describe "GET /projects/:id/noteable/:noteable_id/notes" do context "when noteable is an Issue" do it "returns an array of issue notes" do - get api("/projects/#{project.id}/issues/#{issue.id}/notes", user) + get api("/projects/#{project.id}/issues/#{issue.iid}/notes", user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -50,7 +50,7 @@ describe API::Notes, api: true do context "and current user cannot view the notes" do it "returns an empty array" do - get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) + get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -62,7 +62,7 @@ describe API::Notes, api: true do before { ext_issue.update_attributes(confidential: true) } it "returns 404" do - get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) + get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user) expect(response).to have_http_status(404) end @@ -70,7 +70,7 @@ describe API::Notes, api: true do context "and current user can view the note" do it "returns an empty array" do - get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user) + get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", private_user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -106,7 +106,7 @@ describe API::Notes, api: true do context "when noteable is a Merge Request" do it "returns an array of merge_requests notes" do - get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user) + get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/notes", user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -131,21 +131,21 @@ describe API::Notes, api: true do describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do context "when noteable is an Issue" do it "returns an issue note by id" do - get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user) + get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", user) expect(response).to have_http_status(200) expect(json_response['body']).to eq(issue_note.note) end it "returns a 404 error if issue note not found" do - get api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) + get api("/projects/#{project.id}/issues/#{issue.iid}/notes/12345", user) expect(response).to have_http_status(404) end context "and current user cannot view the note" do it "returns a 404 error" do - get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", user) + get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes/#{cross_reference_note.id}", user) expect(response).to have_http_status(404) end @@ -154,7 +154,7 @@ describe API::Notes, api: true do before { issue.update_attributes(confidential: true) } it "returns 404" do - get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", private_user) + get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", private_user) expect(response).to have_http_status(404) end @@ -162,7 +162,7 @@ describe API::Notes, api: true do context "and current user can view the note" do it "returns an issue note by id" do - get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", private_user) + get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes/#{cross_reference_note.id}", private_user) expect(response).to have_http_status(200) expect(json_response['body']).to eq(cross_reference_note.note) @@ -190,7 +190,7 @@ describe API::Notes, api: true do describe "POST /projects/:id/noteable/:noteable_id/notes" do context "when noteable is an Issue" do it "creates a new issue note" do - post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' + post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: 'hi!' expect(response).to have_http_status(201) expect(json_response['body']).to eq('hi!') @@ -198,13 +198,13 @@ describe API::Notes, api: true do end it "returns a 400 bad request error if body not given" do - post api("/projects/#{project.id}/issues/#{issue.id}/notes", user) + post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user) expect(response).to have_http_status(400) end it "returns a 401 unauthorized error if user not authenticated" do - post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!' + post api("/projects/#{project.id}/issues/#{issue.iid}/notes"), body: 'hi!' expect(response).to have_http_status(401) end @@ -212,7 +212,7 @@ describe API::Notes, api: true do context 'when an admin or owner makes the request' do it 'accepts the creation date to be set' do creation_time = 2.weeks.ago - post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), + post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: 'hi!', created_at: creation_time expect(response).to have_http_status(201) @@ -226,7 +226,7 @@ describe API::Notes, api: true do let(:issue2) { create(:issue, project: project) } it 'creates a new issue note' do - post api("/projects/#{project.id}/issues/#{issue2.id}/notes", user), body: ':+1:' + post api("/projects/#{project.id}/issues/#{issue2.iid}/notes", user), body: ':+1:' expect(response).to have_http_status(201) expect(json_response['body']).to eq(':+1:') @@ -235,7 +235,7 @@ describe API::Notes, api: true do context 'when the user is posting an award emoji on his/her own issue' do it 'creates a new issue note' do - post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: ':+1:' + post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: ':+1:' expect(response).to have_http_status(201) expect(json_response['body']).to eq(':+1:') @@ -270,7 +270,7 @@ describe API::Notes, api: true do project = create(:empty_project, :private) { |p| p.add_guest(user) } issue = create(:issue, :confidential, project: project) - post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), + post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: 'Foo' expect(response).to have_http_status(404) @@ -285,7 +285,7 @@ describe API::Notes, api: true do # from a different project, see #15577 # before do - post api("/projects/#{project.id}/issues/#{private_issue.id}/notes", user), + post api("/projects/#{private_issue.project.id}/issues/#{private_issue.iid}/notes", user), body: 'Hi!' end @@ -303,14 +303,14 @@ describe API::Notes, api: true do it "creates an activity event when an issue note is created" do expect(Event).to receive(:create) - post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' + post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: 'hi!' end end describe 'PUT /projects/:id/noteable/:noteable_id/notes/:note_id' do context 'when noteable is an Issue' do it 'returns modified note' do - put api("/projects/#{project.id}/issues/#{issue.id}/"\ + put api("/projects/#{project.id}/issues/#{issue.iid}/"\ "notes/#{issue_note.id}", user), body: 'Hello!' expect(response).to have_http_status(200) @@ -318,14 +318,14 @@ describe API::Notes, api: true do end it 'returns a 404 error when note id not found' do - put api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user), + put api("/projects/#{project.id}/issues/#{issue.iid}/notes/12345", user), body: 'Hello!' expect(response).to have_http_status(404) end it 'returns a 400 bad request error if body not given' do - put api("/projects/#{project.id}/issues/#{issue.id}/"\ + put api("/projects/#{project.id}/issues/#{issue.iid}/"\ "notes/#{issue_note.id}", user) expect(response).to have_http_status(400) @@ -351,7 +351,7 @@ describe API::Notes, api: true do context 'when noteable is a Merge Request' do it 'returns modified note' do - put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ + put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/"\ "notes/#{merge_request_note.id}", user), body: 'Hello!' expect(response).to have_http_status(200) @@ -359,7 +359,7 @@ describe API::Notes, api: true do end it 'returns a 404 error when note id not found' do - put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ + put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/"\ "notes/12345", user), body: "Hello!" expect(response).to have_http_status(404) @@ -370,18 +370,18 @@ describe API::Notes, api: true do describe 'DELETE /projects/:id/noteable/:noteable_id/notes/:note_id' do context 'when noteable is an Issue' do it 'deletes a note' do - delete api("/projects/#{project.id}/issues/#{issue.id}/"\ + delete api("/projects/#{project.id}/issues/#{issue.iid}/"\ "notes/#{issue_note.id}", user) expect(response).to have_http_status(204) # Check if note is really deleted - delete api("/projects/#{project.id}/issues/#{issue.id}/"\ + delete api("/projects/#{project.id}/issues/#{issue.iid}/"\ "notes/#{issue_note.id}", user) expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do - delete api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) + delete api("/projects/#{project.id}/issues/#{issue.iid}/notes/12345", user) expect(response).to have_http_status(404) end @@ -410,18 +410,18 @@ describe API::Notes, api: true do context 'when noteable is a Merge Request' do it 'deletes a note' do delete api("/projects/#{project.id}/merge_requests/"\ - "#{merge_request.id}/notes/#{merge_request_note.id}", user) + "#{merge_request.iid}/notes/#{merge_request_note.id}", user) expect(response).to have_http_status(204) # Check if note is really deleted delete api("/projects/#{project.id}/merge_requests/"\ - "#{merge_request.id}/notes/#{merge_request_note.id}", user) + "#{merge_request.iid}/notes/#{merge_request_note.id}", user) expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/merge_requests/"\ - "#{merge_request.id}/notes/12345", user) + "#{merge_request.iid}/notes/12345", user) expect(response).to have_http_status(404) end diff --git a/spec/requests/api/v3/issues_spec.rb b/spec/requests/api/v3/issues_spec.rb index 51021eec63c..383871d5c38 100644 --- a/spec/requests/api/v3/issues_spec.rb +++ b/spec/requests/api/v3/issues_spec.rb @@ -285,8 +285,16 @@ describe API::V3::Issues, api: true do end let(:base_url) { "/groups/#{group.id}/issues" } + it 'returns all group issues (including opened and closed)' do + get v3_api(base_url, admin) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(3) + end + it 'returns group issues without confidential issues for non project members' do - get v3_api(base_url, non_member) + get v3_api("#{base_url}?state=opened", non_member) expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -295,7 +303,7 @@ describe API::V3::Issues, api: true do end it 'returns group confidential issues for author' do - get v3_api(base_url, author) + get v3_api("#{base_url}?state=opened", author) expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -303,7 +311,7 @@ describe API::V3::Issues, api: true do end it 'returns group confidential issues for assignee' do - get v3_api(base_url, assignee) + get v3_api("#{base_url}?state=opened", assignee) expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -311,7 +319,7 @@ describe API::V3::Issues, api: true do end it 'returns group issues with confidential issues for project members' do - get v3_api(base_url, user) + get v3_api("#{base_url}?state=opened", user) expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -319,7 +327,7 @@ describe API::V3::Issues, api: true do end it 'returns group confidential issues for admin' do - get v3_api(base_url, admin) + get v3_api("#{base_url}?state=opened", admin) expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -368,7 +376,7 @@ describe API::V3::Issues, api: true do end it 'returns an array of issues in given milestone' do - get v3_api("#{base_url}?milestone=#{group_milestone.title}", user) + get v3_api("#{base_url}?state=opened&milestone=#{group_milestone.title}", user) expect(response).to have_http_status(200) expect(json_response).to be_an Array diff --git a/spec/routing/environments_spec.rb b/spec/routing/environments_spec.rb new file mode 100644 index 00000000000..ba124de70bb --- /dev/null +++ b/spec/routing/environments_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe Projects::EnvironmentsController, :routing do + let(:project) { create(:empty_project) } + + let(:environment) do + create(:environment, project: project, + name: 'staging-1.0/review') + end + + let(:environments_route) do + "#{project.namespace.name}/#{project.name}/environments/" + end + + describe 'routing environment folders' do + context 'when using JSON format' do + it 'correctly matches environment name and JSON format' do + expect(get_folder('staging-1.0.json')) + .to route_to(*folder_action(id: 'staging-1.0', format: 'json')) + end + end + + context 'when using HTML format' do + it 'correctly matches environment name and HTML format' do + expect(get_folder('staging-1.0.html')) + .to route_to(*folder_action(id: 'staging-1.0', format: 'html')) + end + end + + context 'when using implicit format' do + it 'correctly matches environment name' do + expect(get_folder('staging-1.0')) + .to route_to(*folder_action(id: 'staging-1.0')) + end + end + end + + def get_folder(folder) + get("#{project.namespace.name}/#{project.name}/" \ + "environments/folders/#{folder}") + end + + def folder_action(**opts) + options = { namespace_id: project.namespace.name, + project_id: project.name } + + ['projects/environments#folder', options.merge(opts)] + end +end diff --git a/spec/serializers/analytics_issue_serializer_spec.rb b/spec/serializers/analytics_issue_serializer_spec.rb index 2f08958a783..ba24cf8e481 100644 --- a/spec/serializers/analytics_issue_serializer_spec.rb +++ b/spec/serializers/analytics_issue_serializer_spec.rb @@ -8,7 +8,7 @@ describe AnalyticsIssueSerializer do end let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:resource) do { total_time: "172802.724419", diff --git a/spec/serializers/analytics_merge_request_serializer_spec.rb b/spec/serializers/analytics_merge_request_serializer_spec.rb index 62067cc0ef2..56cb08acfc6 100644 --- a/spec/serializers/analytics_merge_request_serializer_spec.rb +++ b/spec/serializers/analytics_merge_request_serializer_spec.rb @@ -8,7 +8,7 @@ describe AnalyticsMergeRequestSerializer do end let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:resource) do { total_time: "172802.724419", diff --git a/spec/serializers/build_entity_spec.rb b/spec/serializers/build_entity_spec.rb index 60c9642ee2c..7dcdf54fd93 100644 --- a/spec/serializers/build_entity_spec.rb +++ b/spec/serializers/build_entity_spec.rb @@ -1,10 +1,16 @@ require 'spec_helper' describe BuildEntity do + let(:user) { create(:user) } let(:build) { create(:ci_build) } + let(:request) { double('request') } + + before do + allow(request).to receive(:user).and_return(user) + end let(:entity) do - described_class.new(build, request: double) + described_class.new(build, request: request) end subject { entity.as_json } @@ -22,6 +28,11 @@ describe BuildEntity do expect(subject).to include(:created_at, :updated_at) end + it 'contains details' do + expect(subject).to include :status + expect(subject[:status]).to include :icon, :favicon, :text, :label + end + context 'when build is a regular job' do it 'does not contain path to play action' do expect(subject).not_to include(:play_path) diff --git a/spec/serializers/build_serializer_spec.rb b/spec/serializers/build_serializer_spec.rb new file mode 100644 index 00000000000..3cc791bca50 --- /dev/null +++ b/spec/serializers/build_serializer_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe BuildSerializer do + let(:user) { create(:user) } + + let(:serializer) do + described_class.new(user: user) + end + + subject { serializer.represent(resource) } + + describe '#represent' do + context 'when a single object is being serialized' do + let(:resource) { create(:ci_build) } + + it 'serializers the pipeline object' do + expect(subject[:id]).to eq resource.id + end + end + + context 'when multiple objects are being serialized' do + let(:resource) { create_list(:ci_build, 2) } + + it 'serializers the array of pipelines' do + expect(subject).not_to be_empty + end + end + end + + describe '#represent_status' do + context 'when represents only status' do + let(:resource) { create(:ci_build) } + let(:status) { resource.detailed_status(double('user')) } + + subject { serializer.represent_status(resource) } + + it 'serializes only status' do + expect(subject[:text]).to eq(status.text) + expect(subject[:label]).to eq(status.label) + expect(subject[:icon]).to eq(status.icon) + expect(subject[:favicon]).to eq(status.favicon) + end + end + end +end diff --git a/spec/serializers/commit_entity_spec.rb b/spec/serializers/commit_entity_spec.rb index 0333d73b5b5..04247c78549 100644 --- a/spec/serializers/commit_entity_spec.rb +++ b/spec/serializers/commit_entity_spec.rb @@ -6,7 +6,7 @@ describe CommitEntity do end let(:request) { double('request') } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:commit) { project.commit } subject { entity.as_json } diff --git a/spec/serializers/deployment_entity_spec.rb b/spec/serializers/deployment_entity_spec.rb index ea87771e2a2..95eca5463eb 100644 --- a/spec/serializers/deployment_entity_spec.rb +++ b/spec/serializers/deployment_entity_spec.rb @@ -1,8 +1,15 @@ require 'spec_helper' describe DeploymentEntity do + let(:user) { create(:user) } + let(:request) { double('request') } + + before do + allow(request).to receive(:user).and_return(user) + end + let(:entity) do - described_class.new(deployment, request: double) + described_class.new(deployment, request: request) end let(:deployment) { create(:deployment) } diff --git a/spec/serializers/environment_entity_spec.rb b/spec/serializers/environment_entity_spec.rb index 57728ce3181..979d9921941 100644 --- a/spec/serializers/environment_entity_spec.rb +++ b/spec/serializers/environment_entity_spec.rb @@ -15,4 +15,24 @@ describe EnvironmentEntity do it 'exposes core elements of environment' do expect(subject).to include(:id, :name, :state, :environment_path) end + + context 'metrics disabled' do + before do + allow(environment).to receive(:has_metrics?).and_return(false) + end + + it "doesn't expose metrics path" do + expect(subject).not_to include(:metrics_path) + end + end + + context 'metrics enabled' do + before do + allow(environment).to receive(:has_metrics?).and_return(true) + end + + it 'exposes metrics path' do + expect(subject).to include(:metrics_path) + end + end end diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb index 6a6df377b35..1909e6385b5 100644 --- a/spec/serializers/environment_serializer_spec.rb +++ b/spec/serializers/environment_serializer_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe EnvironmentSerializer do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:json) do described_class @@ -11,21 +11,20 @@ describe EnvironmentSerializer do end context 'when there is a single object provided' do - before do - create(:ci_build, :manual, name: 'manual1', - pipeline: deployable.pipeline) - end - + let(:project) { create(:project, :repository) } + let(:deployable) { create(:ci_build) } let(:deployment) do create(:deployment, deployable: deployable, user: user, project: project, sha: project.commit.id) end - - let(:deployable) { create(:ci_build) } let(:resource) { deployment.environment } + before do + create(:ci_build, :manual, name: 'manual1', pipeline: deployable.pipeline) + end + it 'contains important elements of environment' do expect(json) .to include(:name, :external_url, :environment_path, :last_deployment) diff --git a/spec/serializers/pipeline_entity_spec.rb b/spec/serializers/pipeline_entity_spec.rb index ccb72973f9c..93d5a21419d 100644 --- a/spec/serializers/pipeline_entity_spec.rb +++ b/spec/serializers/pipeline_entity_spec.rb @@ -30,7 +30,7 @@ describe PipelineEntity do .to include :duration, :finished_at expect(subject[:details]) .to include :stages, :artifacts, :manual_actions - expect(subject[:details][:status]).to include :icon, :text, :label + expect(subject[:details][:status]).to include :icon, :favicon, :text, :label end it 'contains flags' do diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index 2aaef03cb93..8642b803844 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -94,4 +94,20 @@ describe PipelineSerializer do end end end + + describe '#represent_status' do + context 'when represents only status' do + let(:resource) { create(:ci_pipeline) } + let(:status) { resource.detailed_status(double('user')) } + + subject { serializer.represent_status(resource) } + + it 'serializes only status' do + expect(subject[:text]).to eq(status.text) + expect(subject[:label]).to eq(status.label) + expect(subject[:icon]).to eq(status.icon) + expect(subject[:favicon]).to eq(status.favicon) + end + end + end end diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb index 89428b4216e..c94902dbab8 100644 --- a/spec/serializers/status_entity_spec.rb +++ b/spec/serializers/status_entity_spec.rb @@ -16,7 +16,7 @@ describe StatusEntity do subject { entity.as_json } it 'contains status details' do - expect(subject).to include :text, :icon, :label, :group + expect(subject).to include :text, :icon, :favicon, :label, :group expect(subject).to include :has_details, :details_path end end diff --git a/spec/services/after_branch_delete_service_spec.rb b/spec/services/after_branch_delete_service_spec.rb index d29e0addb53..77ca17bc82c 100644 --- a/spec/services/after_branch_delete_service_spec.rb +++ b/spec/services/after_branch_delete_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe AfterBranchDeleteService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:service) { described_class.new(project, user) } diff --git a/spec/services/boards/create_service_spec.rb b/spec/services/boards/create_service_spec.rb index 7b29b043296..a8555f5b4a0 100644 --- a/spec/services/boards/create_service_spec.rb +++ b/spec/services/boards/create_service_spec.rb @@ -15,7 +15,7 @@ describe Boards::CreateService, services: true do board = service.execute expect(board.lists.size).to eq 1 - expect(board.lists.first).to be_done + expect(board.lists.first).to be_closed end end diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb index d841bdaa292..c982031c791 100644 --- a/spec/services/boards/issues/list_service_spec.rb +++ b/spec/services/boards/issues/list_service_spec.rb @@ -15,7 +15,7 @@ describe Boards::Issues::ListService, services: true do let!(:list1) { create(:list, board: board, label: development, position: 0) } let!(:list2) { create(:list, board: board, label: testing, position: 1) } - let!(:done) { create(:done_list, board: board) } + let!(:closed) { create(:closed_list, board: board) } let!(:opened_issue1) { create(:labeled_issue, project: project, labels: [bug]) } let!(:opened_issue2) { create(:labeled_issue, project: project, labels: [p2]) } @@ -53,8 +53,8 @@ describe Boards::Issues::ListService, services: true do expect(issues).to eq [opened_issue2, reopened_issue1, opened_issue1] end - it 'returns closed issues when listing issues from Done' do - params = { board_id: board.id, id: done.id } + it 'returns closed issues when listing issues from Closed' do + params = { board_id: board.id, id: closed.id } issues = described_class.new(project, user, params).execute diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb index 727ea04ea5c..4ff7ac6bb2f 100644 --- a/spec/services/boards/issues/move_service_spec.rb +++ b/spec/services/boards/issues/move_service_spec.rb @@ -12,7 +12,7 @@ describe Boards::Issues::MoveService, services: true do let!(:list1) { create(:list, board: board1, label: development, position: 0) } let!(:list2) { create(:list, board: board1, label: testing, position: 1) } - let!(:done) { create(:done_list, board: board1) } + let!(:closed) { create(:closed_list, board: board1) } before do project.team << [user, :developer] @@ -35,13 +35,13 @@ describe Boards::Issues::MoveService, services: true do end end - context 'when moving to done' do + context 'when moving to closed' do let(:board2) { create(:board, project: project) } let(:regression) { create(:label, project: project, name: 'Regression') } let!(:list3) { create(:list, board: board2, label: regression, position: 1) } let(:issue) { create(:labeled_issue, project: project, labels: [bug, development, testing, regression]) } - let(:params) { { board_id: board1.id, from_list_id: list2.id, to_list_id: done.id } } + let(:params) { { board_id: board1.id, from_list_id: list2.id, to_list_id: closed.id } } it 'delegates the close proceedings to Issues::CloseService' do expect_any_instance_of(Issues::CloseService).to receive(:execute).with(issue).once @@ -58,9 +58,9 @@ describe Boards::Issues::MoveService, services: true do end end - context 'when moving from done' do + context 'when moving from closed' do let(:issue) { create(:labeled_issue, :closed, project: project, labels: [bug]) } - let(:params) { { board_id: board1.id, from_list_id: done.id, to_list_id: list2.id } } + let(:params) { { board_id: board1.id, from_list_id: closed.id, to_list_id: list2.id } } it 'delegates the re-open proceedings to Issues::ReopenService' do expect_any_instance_of(Issues::ReopenService).to receive(:execute).with(issue).once diff --git a/spec/services/boards/lists/destroy_service_spec.rb b/spec/services/boards/lists/destroy_service_spec.rb index a30860f828a..af2d7c784bb 100644 --- a/spec/services/boards/lists/destroy_service_spec.rb +++ b/spec/services/boards/lists/destroy_service_spec.rb @@ -18,18 +18,18 @@ describe Boards::Lists::DestroyService, services: true do development = create(:list, board: board, position: 0) review = create(:list, board: board, position: 1) staging = create(:list, board: board, position: 2) - done = board.done_list + closed = board.closed_list described_class.new(project, user).execute(development) expect(review.reload.position).to eq 0 expect(staging.reload.position).to eq 1 - expect(done.reload.position).to be_nil + expect(closed.reload.position).to be_nil end end - it 'does not remove list from board when list type is done' do - list = board.done_list + it 'does not remove list from board when list type is closed' do + list = board.closed_list service = described_class.new(project, user) expect { service.execute(list) }.not_to change(board.lists, :count) diff --git a/spec/services/boards/lists/list_service_spec.rb b/spec/services/boards/lists/list_service_spec.rb index 2dffc62b215..ab9fb1bc914 100644 --- a/spec/services/boards/lists/list_service_spec.rb +++ b/spec/services/boards/lists/list_service_spec.rb @@ -10,7 +10,7 @@ describe Boards::Lists::ListService, services: true do service = described_class.new(project, double) - expect(service.execute(board)).to eq [list, board.done_list] + expect(service.execute(board)).to eq [list, board.closed_list] end end end diff --git a/spec/services/boards/lists/move_service_spec.rb b/spec/services/boards/lists/move_service_spec.rb index 3786dc82bf0..4b3bdd133f2 100644 --- a/spec/services/boards/lists/move_service_spec.rb +++ b/spec/services/boards/lists/move_service_spec.rb @@ -10,7 +10,7 @@ describe Boards::Lists::MoveService, services: true do let!(:development) { create(:list, board: board, position: 1) } let!(:review) { create(:list, board: board, position: 2) } let!(:staging) { create(:list, board: board, position: 3) } - let!(:done) { create(:done_list, board: board) } + let!(:closed) { create(:closed_list, board: board) } context 'when list type is set to label' do it 'keeps position of lists when new position is nil' do @@ -86,10 +86,10 @@ describe Boards::Lists::MoveService, services: true do end end - it 'keeps position of lists when list type is done' do + it 'keeps position of lists when list type is closed' do service = described_class.new(project, user, position: 2) - service.execute(done) + service.execute(closed) expect(current_list_positions).to eq [0, 1, 2, 3] end diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb index 51441e8f3be..0dc96521fa8 100644 --- a/spec/services/chat_names/find_user_service_spec.rb +++ b/spec/services/chat_names/find_user_service_spec.rb @@ -18,9 +18,16 @@ describe ChatNames::FindUserService, services: true do end it 'updates when last time chat name was used' do + expect(chat_name.last_used_at).to be_nil + subject - expect(chat_name.reload.last_used_at).to be_like_time(Time.now) + initial_last_used = chat_name.reload.last_used_at + expect(initial_last_used).to be_present + + Timecop.travel(2.days.from_now) { described_class.new(service, params).execute } + + expect(chat_name.reload.last_used_at).to be > initial_last_used end end diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index a969829a63e..d2f0337c260 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Ci::CreatePipelineService, services: true do - let(:project) { FactoryGirl.create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:admin) } before do diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index 5e68343784d..5a20102872a 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Ci::CreateTriggerRequestService, services: true do let(:service) { described_class.new } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:trigger) { create(:ci_trigger, project: project) } before do diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index 5445b65f4e8..f1b2d3a4798 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -9,6 +9,19 @@ describe Ci::RetryPipelineService, '#execute', :services do context 'when user has ability to modify pipeline' do let(:user) { create(:admin) } + context 'when there are already retried jobs present' do + before do + create_build('rspec', :canceled, 0) + create_build('rspec', :failed, 0) + end + + it 'does not retry jobs that has already been retried' do + expect(statuses.first).to be_retried + expect { service.execute(pipeline) } + .to change { CommitStatus.count }.by(1) + end + end + context 'when there are failed builds in the last stage' do before do create_build('rspec 1', :success, 0) diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb index 560f83d94f7..32c72a9cf5e 100644 --- a/spec/services/ci/stop_environments_service_spec.rb +++ b/spec/services/ci/stop_environments_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Ci::StopEnvironmentsService, services: true do - let(:project) { create(:project, :private) } + let(:project) { create(:project, :private, :repository) } let(:user) { create(:user) } let(:service) { described_class.new(project, user) } diff --git a/spec/services/ci/update_build_queue_service_spec.rb b/spec/services/ci/update_build_queue_service_spec.rb index f01a388b895..c44e6b2a48b 100644 --- a/spec/services/ci/update_build_queue_service_spec.rb +++ b/spec/services/ci/update_build_queue_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Ci::UpdateBuildQueueService, :services do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:build) { create(:ci_build, pipeline: pipeline) } let(:pipeline) { create(:ci_pipeline, project: project) } diff --git a/spec/services/compare_service_spec.rb b/spec/services/compare_service_spec.rb index 0a7fc58523f..bea7c965233 100644 --- a/spec/services/compare_service_spec.rb +++ b/spec/services/compare_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe CompareService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:service) { described_class.new(project, 'feature') } diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb index 61e5ae72f51..271ccfe7968 100644 --- a/spec/services/create_release_service_spec.rb +++ b/spec/services/create_release_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe CreateReleaseService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:tag_name) { project.repository.tag_names.first } let(:description) { 'Awesome release!' } diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb index 336f5dafb5b..c4685c9aa31 100644 --- a/spec/services/delete_branch_service_spec.rb +++ b/spec/services/delete_branch_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe DeleteBranchService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:user) { create(:user) } let(:service) { described_class.new(project, user) } diff --git a/spec/services/delete_merged_branches_service_spec.rb b/spec/services/delete_merged_branches_service_spec.rb index 181488e89c7..a41a421fa6e 100644 --- a/spec/services/delete_merged_branches_service_spec.rb +++ b/spec/services/delete_merged_branches_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe DeleteMergedBranchesService, services: true do subject(:service) { described_class.new(project, project.owner) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } context '#execute' do context 'unprotected branches' do diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb index 35e6e139238..26aa5b432d4 100644 --- a/spec/services/files/update_service_spec.rb +++ b/spec/services/files/update_service_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" describe Files::UpdateService do subject { described_class.new(project, user, commit_params) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:file_path) { 'files/ruby/popen.rb' } let(:new_contents) { 'New Content' } diff --git a/spec/services/git_hooks_service_spec.rb b/spec/services/git_hooks_service_spec.rb index 3318dfb22b6..ac7ccfbaab0 100644 --- a/spec/services/git_hooks_service_spec.rb +++ b/spec/services/git_hooks_service_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe GitHooksService, services: true do include RepoHelpers - let(:user) { create :user } - let(:project) { create :project } + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } let(:service) { GitHooksService.new } before do diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index bd71618e6f4..0477cac6677 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe GitPushService, services: true do include RepoHelpers - let(:user) { create :user } - let(:project) { create :project } + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } before do project.team << [user, :master] diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index bd074b9bd71..b73beb3f6fc 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe GitTagPushService, services: true do include RepoHelpers - let(:user) { create :user } - let(:project) { create :project } + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } let(:service) { GitTagPushService.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) } let(:oldrev) { Gitlab::Git::BLANK_SHA } diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index ec89b540e6a..bcb62429275 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -44,7 +44,7 @@ describe Groups::CreateService, '#execute', services: true do let!(:service) { described_class.new(user, params) } before do - Settings.mattermost['enabled'] = true + stub_mattermost_setting(enabled: true) end it 'create the chat team with the group' do diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb index 98c560ffb26..2ee11fc8b4c 100644 --- a/spec/services/groups/destroy_service_spec.rb +++ b/spec/services/groups/destroy_service_spec.rb @@ -6,7 +6,7 @@ describe Groups::DestroyService, services: true do let!(:user) { create(:user) } let!(:group) { create(:group) } let!(:nested_group) { create(:group, parent: group) } - let!(:project) { create(:project, namespace: group) } + let!(:project) { create(:empty_project, namespace: group) } let!(:gitlab_shell) { Gitlab::Shell.new } let!(:remove_path) { group.path + "+#{group.id}+deleted" } diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index 7c0fccb9d41..f6ad5cebd2c 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -13,7 +13,7 @@ describe Groups::UpdateService, services: true do before do public_group.add_user(user, Gitlab::Access::MASTER) - create(:project, :public, group: public_group) + create(:empty_project, :public, group: public_group) end it "does not change permission level" do @@ -27,7 +27,7 @@ describe Groups::UpdateService, services: true do before do internal_group.add_user(user, Gitlab::Access::MASTER) - create(:project, :internal, group: internal_group) + create(:empty_project, :internal, group: internal_group) end it "does not change permission level" do @@ -36,6 +36,20 @@ describe Groups::UpdateService, services: true do end end end + + context "with parent_id user doesn't have permissions for" do + let(:service) { described_class.new(public_group, user, parent_id: private_group.id) } + + before do + service.execute + end + + it 'does not update parent_id' do + updated_group = public_group.reload + + expect(updated_group.parent_id).to be_nil + end + end end context "unauthorized visibility_level validation" do @@ -55,7 +69,7 @@ describe Groups::UpdateService, services: true do before do internal_group.add_user(user, Gitlab::Access::MASTER) - create(:project, :internal, group: internal_group) + create(:empty_project, :internal, group: internal_group) end it 'returns true' do diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb index 1dd53236fbd..17990f41b3b 100644 --- a/spec/services/issues/build_service_spec.rb +++ b/spec/services/issues/build_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper.rb' describe Issues::BuildService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index db196ed5751..9f8346d52bb 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -5,8 +5,8 @@ describe Issues::MoveService, services: true do let(:author) { create(:user) } let(:title) { 'Some issue' } let(:description) { 'Some issue description' } - let(:old_project) { create(:project) } - let(:new_project) { create(:project) } + let(:old_project) { create(:empty_project) } + let(:new_project) { create(:empty_project) } let(:milestone1) { create(:milestone, project_id: old_project.id, title: 'v9.0') } let(:old_issue) do diff --git a/spec/services/issues/resolve_discussions_spec.rb b/spec/services/issues/resolve_discussions_spec.rb index 6cc738aec08..3a72f92383c 100644 --- a/spec/services/issues/resolve_discussions_spec.rb +++ b/spec/services/issues/resolve_discussions_spec.rb @@ -10,7 +10,7 @@ class DummyService < Issues::BaseService end describe DummyService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index fa472f3e2c3..5b324f3c706 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -13,6 +13,7 @@ describe Issues::UpdateService, services: true do let(:issue) do create(:issue, title: 'Old title', + description: "for #{user2.to_reference}", assignee_id: user3.id, project: project) end @@ -182,16 +183,24 @@ describe Issues::UpdateService, services: true do it 'marks pending todos as done' do expect(todo.reload.done?).to eq true end + + it 'does not create any new todos' do + expect(Todo.count).to eq(1) + end end context 'when the description change' do before do - update_issue(description: 'Also please fix') + update_issue(description: "Also please fix #{user2.to_reference} #{user3.to_reference}") end it 'marks todos as done' do expect(todo.reload.done?).to eq true end + + it 'creates only 1 new todo' do + expect(Todo.count).to eq(2) + end end context 'when is reassigned' do diff --git a/spec/services/labels/create_service_spec.rb b/spec/services/labels/create_service_spec.rb new file mode 100644 index 00000000000..0ecab0c3475 --- /dev/null +++ b/spec/services/labels/create_service_spec.rb @@ -0,0 +1,186 @@ +require 'spec_helper' + +describe Labels::CreateService, services: true do + describe '#execute' do + let(:project) { create(:project) } + let(:group) { create(:group) } + + let(:hex_color) { '#FF0000' } + let(:named_color) { 'red' } + let(:upcase_color) { 'RED' } + let(:spaced_color) { ' red ' } + let(:unknown_color) { 'unknown' } + let(:no_color) { '' } + + let(:expected_saved_color) { hex_color } + + context 'in a project' do + context 'with color in hex-code' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(hex_color)).execute(project: project) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with color in allowed name' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(named_color)).execute(project: project) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with color in up-case allowed name' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(upcase_color)).execute(project: project) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with color surrounded by spaces' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(spaced_color)).execute(project: project) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with unknown color' do + it 'doesn\'t create a label' do + label = Labels::CreateService.new(params_with(unknown_color)).execute(project: project) + + expect(label).not_to be_persisted + end + end + + context 'with no color' do + it 'doesn\'t create a label' do + label = Labels::CreateService.new(params_with(no_color)).execute(project: project) + + expect(label).not_to be_persisted + end + end + end + + context 'in a group' do + context 'with color in hex-code' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(hex_color)).execute(group: group) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with color in allowed name' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(named_color)).execute(group: group) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with color in up-case allowed name' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(upcase_color)).execute(group: group) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with color surrounded by spaces' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(spaced_color)).execute(group: group) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with unknown color' do + it 'doesn\'t create a label' do + label = Labels::CreateService.new(params_with(unknown_color)).execute(group: group) + + expect(label).not_to be_persisted + end + end + + context 'with no color' do + it 'doesn\'t create a label' do + label = Labels::CreateService.new(params_with(no_color)).execute(group: group) + + expect(label).not_to be_persisted + end + end + end + + context 'in admin area' do + context 'with color in hex-code' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(hex_color)).execute(template: true) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with color in allowed name' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(named_color)).execute(template: true) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with color in up-case allowed name' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(upcase_color)).execute(template: true) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with color surrounded by spaces' do + it 'creates a label' do + label = Labels::CreateService.new(params_with(spaced_color)).execute(template: true) + + expect(label).to be_persisted + expect(label.color).to eq expected_saved_color + end + end + + context 'with unknown color' do + it 'doesn\'t create a label' do + label = Labels::CreateService.new(params_with(unknown_color)).execute(template: true) + + expect(label).not_to be_persisted + end + end + + context 'with no color' do + it 'doesn\'t create a label' do + label = Labels::CreateService.new(params_with(no_color)).execute(template: true) + + expect(label).not_to be_persisted + end + end + end + end + + def params_with(color) + { + title: 'A Label', + color: color + } + end +end diff --git a/spec/services/labels/find_or_create_service_spec.rb b/spec/services/labels/find_or_create_service_spec.rb index 7a9b34f9f96..de8f1827cce 100644 --- a/spec/services/labels/find_or_create_service_spec.rb +++ b/spec/services/labels/find_or_create_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Labels::FindOrCreateService, services: true do describe '#execute' do let(:group) { create(:group) } - let(:project) { create(:project, namespace: group) } + let(:project) { create(:empty_project, namespace: group) } let(:params) do { diff --git a/spec/services/labels/transfer_service_spec.rb b/spec/services/labels/transfer_service_spec.rb index 13654a0881c..11d5f1cad5e 100644 --- a/spec/services/labels/transfer_service_spec.rb +++ b/spec/services/labels/transfer_service_spec.rb @@ -6,8 +6,8 @@ describe Labels::TransferService, services: true do let(:group_1) { create(:group) } let(:group_2) { create(:group) } let(:group_3) { create(:group) } - let(:project_1) { create(:project, namespace: group_2) } - let(:project_2) { create(:project, namespace: group_3) } + let(:project_1) { create(:empty_project, namespace: group_2) } + let(:project_2) { create(:empty_project, namespace: group_3) } let(:group_label_1) { create(:group_label, group: group_1, name: 'Group Label 1') } let(:group_label_2) { create(:group_label, group: group_1, name: 'Group Label 2') } diff --git a/spec/services/labels/update_service_spec.rb b/spec/services/labels/update_service_spec.rb new file mode 100644 index 00000000000..f2498ea6990 --- /dev/null +++ b/spec/services/labels/update_service_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Labels::UpdateService, services: true do + describe '#execute' do + let(:project) { create(:project) } + + let(:hex_color) { '#FF0000' } + let(:named_color) { 'red' } + let(:upcase_color) { 'RED' } + let(:spaced_color) { ' red ' } + let(:unknown_color) { 'unknown' } + let(:no_color) { '' } + + let(:expected_saved_color) { hex_color } + + before(:each) do + @label = Labels::CreateService.new(title: 'Initial', color: '#000000').execute(project: project) + expect(@label).to be_persisted + end + + context 'with color in hex-code' do + it 'updates the label' do + label = Labels::UpdateService.new(params_with(hex_color)).execute(@label) + + expect(label).to be_valid + expect(label.reload.color).to eq expected_saved_color + end + end + + context 'with color in allowed name' do + it 'updates the label' do + label = Labels::UpdateService.new(params_with(named_color)).execute(@label) + + expect(label).to be_valid + expect(label.reload.color).to eq expected_saved_color + end + end + + context 'with color in up-case allowed name' do + it 'updates the label' do + label = Labels::UpdateService.new(params_with(upcase_color)).execute(@label) + + expect(label).to be_valid + expect(label.reload.color).to eq expected_saved_color + end + end + + context 'with color surrounded by spaces' do + it 'updates the label' do + label = Labels::UpdateService.new(params_with(spaced_color)).execute(@label) + + expect(label).to be_valid + expect(label.reload.color).to eq expected_saved_color + end + end + + context 'with unknown color' do + it 'doesn\'t update the label' do + label = Labels::UpdateService.new(params_with(unknown_color)).execute(@label) + + expect(label).not_to be_valid + end + end + + context 'with no color' do + it 'doesn\'t update the label' do + label = Labels::UpdateService.new(params_with(no_color)).execute(@label) + + expect(label).not_to be_valid + end + end + end + + def params_with(color) + { + title: 'A Label', + color: color + } + end +end diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb index 574df6e0f42..41450c67d7e 100644 --- a/spec/services/members/destroy_service_spec.rb +++ b/spec/services/members/destroy_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Members::DestroyService, services: true do let(:user) { create(:user) } let(:member_user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:group) { create(:group, :public) } shared_examples 'a service raising ActiveRecord::RecordNotFound' do diff --git a/spec/services/members/request_access_service_spec.rb b/spec/services/members/request_access_service_spec.rb index 853c125dadb..814c17b9ad0 100644 --- a/spec/services/members/request_access_service_spec.rb +++ b/spec/services/members/request_access_service_spec.rb @@ -29,7 +29,7 @@ describe Members::RequestAccessService, services: true do end context 'when current user cannot request access to the project' do - %i[project group].each do |source_type| + %i[empty_project group].each do |source_type| it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do let(:source) { create(source_type, :private) } end @@ -37,7 +37,7 @@ describe Members::RequestAccessService, services: true do end context 'when access requests are disabled' do - %i[project group].each do |source_type| + %i[empty_project group].each do |source_type| it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do let(:source) { create(source_type, :public) } end @@ -45,7 +45,7 @@ describe Members::RequestAccessService, services: true do end context 'when current user can request access to the project' do - %i[project group].each do |source_type| + %i[empty_project group].each do |source_type| it_behaves_like 'a service creating a access request' do let(:source) { create(source_type, :public, :access_requestable) } end diff --git a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb index d80fb8a1af1..af0a214c00f 100644 --- a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb +++ b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe MergeRequests::AddTodoWhenBuildFailsService do let(:user) { create(:user) } let(:merge_request) { create(:merge_request) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:sha) { '1234567890abcdef1234567890abcdef12345678' } let(:ref) { merge_request.source_branch } diff --git a/spec/services/merge_requests/assign_issues_service_spec.rb b/spec/services/merge_requests/assign_issues_service_spec.rb index 5034b6ef33f..fe75757dd29 100644 --- a/spec/services/merge_requests/assign_issues_service_spec.rb +++ b/spec/services/merge_requests/assign_issues_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe MergeRequests::AssignIssuesService, services: true do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, :simple, source_project: project, author: user, description: "fixes #{issue.to_reference}") } let(:service) { described_class.new(project, user, merge_request: merge_request) } diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index adfa75a524f..c8bd4d4601a 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe MergeRequests::BuildService, services: true do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:issue_confidential) { false } let(:issue) { create(:issue, project: project, title: 'A bug', confidential: issue_confidential) } diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index 673c0bd6c9c..0e16c7cc94b 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe MergeRequests::CreateService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:assignee) { create(:user) } diff --git a/spec/services/merge_requests/get_urls_service_spec.rb b/spec/services/merge_requests/get_urls_service_spec.rb index b7a05907208..290e00ea1ba 100644 --- a/spec/services/merge_requests/get_urls_service_spec.rb +++ b/spec/services/merge_requests/get_urls_service_spec.rb @@ -1,7 +1,7 @@ require "spec_helper" describe MergeRequests::GetUrlsService do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:service) { MergeRequests::GetUrlsService.new(project) } let(:source_branch) { "my_branch" } let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{source_branch}" } diff --git a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb index c2f205c389d..769b3193275 100644 --- a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb +++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe MergeRequests::MergeWhenPipelineSucceedsService do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:mr_merge_if_green_enabled) do create(:merge_request, merge_when_pipeline_succeeds: true, merge_user: user, diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index 92729f68e5f..c22d145ca5d 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe MergeRequests::RefreshService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:service) { MergeRequests::RefreshService } @@ -11,7 +11,7 @@ describe MergeRequests::RefreshService, services: true do group = create(:group) group.add_owner(@user) - @project = create(:project, namespace: group) + @project = create(:project, :repository, namespace: group) @fork_project = Projects::ForkService.new(@project, @user).execute @merge_request = create(:merge_request, source_project: @project, @@ -252,7 +252,7 @@ describe MergeRequests::RefreshService, services: true do context 'when the merge request is sourced from a different project' do it 'creates a `MergeRequestsClosingIssues` record for each issue closed by a commit' do - forked_project = create(:project) + forked_project = create(:project, :repository) create(:forked_project_link, forked_to_project: forked_project, forked_from_project: @project) merge_request = create(:merge_request, diff --git a/spec/services/merge_requests/resolve_service_spec.rb b/spec/services/merge_requests/resolve_service_spec.rb index d33535d22af..eaf7785e549 100644 --- a/spec/services/merge_requests/resolve_service_spec.rb +++ b/spec/services/merge_requests/resolve_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe MergeRequests::ResolveService do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:fork_project) do create(:forked_project_with_submodules) do |fork_project| diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 7d73c0ea5d0..f2ca1e6fcbd 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe MergeRequests::UpdateService, services: true do include EmailHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } @@ -12,6 +12,7 @@ describe MergeRequests::UpdateService, services: true do let(:merge_request) do create(:merge_request, :simple, title: 'Old title', + description: "FYI #{user2.to_reference}", assignee_id: user3.id, source_project: project) end @@ -225,16 +226,24 @@ describe MergeRequests::UpdateService, services: true do it 'marks pending todos as done' do expect(pending_todo.reload).to be_done end + + it 'does not create any new todos' do + expect(Todo.count).to eq(1) + end end context 'when the description change' do before do - update_merge_request({ description: 'Also please fix' }) + update_merge_request({ description: "Also please fix #{user2.to_reference} #{user3.to_reference}" }) end it 'marks pending todos as done' do expect(pending_todo.reload).to be_done end + + it 'creates only 1 new todo' do + expect(Todo.count).to eq(2) + end end context 'when is reassigned' do diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb index fe6a19e97ea..d581b94ff43 100644 --- a/spec/services/milestones/close_service_spec.rb +++ b/spec/services/milestones/close_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Milestones::CloseService, services: true do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) } before do diff --git a/spec/services/note_summary_spec.rb b/spec/services/note_summary_spec.rb new file mode 100644 index 00000000000..39f06f8f8e7 --- /dev/null +++ b/spec/services/note_summary_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe NoteSummary, services: true do + let(:project) { build(:empty_project) } + let(:noteable) { build(:issue) } + let(:user) { build(:user) } + + def create_note_summary + described_class.new(noteable, project, user, 'note', action: 'icon', commit_count: 5) + end + + describe '#metadata?' do + it 'returns true when metadata present' do + expect(create_note_summary.metadata?).to be_truthy + end + + it 'returns false when metadata not present' do + expect(described_class.new(noteable, project, user, 'note').metadata?).to be_falsey + end + end + + describe '#note' do + it 'returns note hash' do + expect(create_note_summary.note).to eq(noteable: noteable, project: project, author: user, note: 'note') + end + + context 'when noteable is a commit' do + let(:noteable) { build(:commit) } + + it 'returns note hash specific to commit' do + expect(create_note_summary.note).to eq( + noteable: nil, project: project, author: user, note: 'note', + noteable_type: 'Commit', commit_id: noteable.id + ) + end + end + end + + describe '#metadata' do + it 'returns metadata hash' do + expect(create_note_summary.metadata).to eq(action: 'icon', commit_count: 5) + end + end +end diff --git a/spec/services/notes/diff_position_update_service_spec.rb b/spec/services/notes/diff_position_update_service_spec.rb index 110efb54fa0..d73ae51fbc3 100644 --- a/spec/services/notes/diff_position_update_service_spec.rb +++ b/spec/services/notes/diff_position_update_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Notes::DiffPositionUpdateService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:create_commit) { project.commit("913c66a37b4a45b9769037c55c2d238bd0942d2e") } let(:modify_commit) { project.commit("874797c3a73b60d2187ed6e2fcabd289ff75171e") } let(:edit_commit) { project.commit("570e7b2abdd848b95f2f578043fc23bd6f6fd24d") } diff --git a/spec/services/notes/update_service_spec.rb b/spec/services/notes/update_service_spec.rb index dde4bde7dc2..905e2f46bde 100644 --- a/spec/services/notes/update_service_spec.rb +++ b/spec/services/notes/update_service_spec.rb @@ -4,12 +4,14 @@ describe Notes::UpdateService, services: true do let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:user2) { create(:user) } + let(:user3) { create(:user) } let(:issue) { create(:issue, project: project) } - let(:note) { create(:note, project: project, noteable: issue, author: user, note: 'Old note') } + let(:note) { create(:note, project: project, noteable: issue, author: user, note: "Old note #{user2.to_reference}") } before do project.team << [user, :master] project.team << [user2, :developer] + project.team << [user3, :developer] end describe '#execute' do @@ -23,22 +25,30 @@ describe Notes::UpdateService, services: true do context 'when the note change' do before do - update_note({ note: 'New note' }) + update_note({ note: "New note #{user2.to_reference} #{user3.to_reference}" }) end it 'marks todos as done' do expect(todo.reload).to be_done end + + it 'creates only 1 new todo' do + expect(Todo.count).to eq(2) + end end context 'when the note does not change' do before do - update_note({ note: 'Old note' }) + update_note({ note: "Old note #{user2.to_reference}" }) end it 'keep todos' do expect(todo.reload).to be_pending end + + it 'does not create any new todos' do + expect(Todo.count).to eq(1) + end end end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index f7240969588..5c841843b40 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -146,6 +146,16 @@ describe NotificationService, services: true do should_not_email(@u_lazy_participant) end + it "emails the note author if they've opted into notifications about their activity" do + add_users_with_subscription(note.project, issue) + note.author.notified_of_own_activity = true + reset_delivered_emails! + + notification.new_note(note) + + should_email(note.author) + end + it 'filters out "mentioned in" notes' do mentioned_note = SystemNoteService.cross_reference(mentioned_issue, issue, issue.author) @@ -362,7 +372,7 @@ describe NotificationService, services: true do end context 'commit note' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:note) { create(:note_on_commit, project: project) } before do @@ -411,7 +421,7 @@ describe NotificationService, services: true do end context "merge request diff note" do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:merge_request) { create(:merge_request, source_project: project, assignee: user) } let(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) } @@ -476,6 +486,20 @@ describe NotificationService, services: true do should_not_email(issue.assignee) end + it "emails the author if they've opted into notifications about their activity" do + issue.author.notified_of_own_activity = true + + notification.new_issue(issue, issue.author) + + should_email(issue.author) + end + + it "doesn't email the author if they haven't opted into notifications about their activity" do + notification.new_issue(issue, issue.author) + + should_not_email(issue.author) + end + it "emails subscribers of the issue's labels" do user_1 = create(:user) user_2 = create(:user) @@ -665,6 +689,19 @@ describe NotificationService, services: true do should_email(subscriber_to_label_2) end + it "emails the current user if they've opted into notifications about their activity" do + subscriber_to_label_2.notified_of_own_activity = true + notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2) + + should_email(subscriber_to_label_2) + end + + it "doesn't email the current user if they haven't opted into notifications about their activity" do + notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2) + + should_not_email(subscriber_to_label_2) + end + it "doesn't send email to anyone but subscribers of the given labels" do notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled) @@ -812,7 +849,7 @@ describe NotificationService, services: true do describe 'Merge Requests' do let(:group) { create(:group) } - let(:project) { create(:project, :public, namespace: group) } + let(:project) { create(:project, :public, :repository, namespace: group) } let(:another_project) { create(:empty_project, :public, namespace: group) } let(:merge_request) { create :merge_request, source_project: project, assignee: create(:user), description: 'cc @participant' } @@ -845,6 +882,20 @@ describe NotificationService, services: true do should_not_email(@u_lazy_participant) end + it "emails the author if they've opted into notifications about their activity" do + merge_request.author.notified_of_own_activity = true + + notification.new_merge_request(merge_request, merge_request.author) + + should_email(merge_request.author) + end + + it "doesn't email the author if they haven't opted into notifications about their activity" do + notification.new_merge_request(merge_request, merge_request.author) + + should_not_email(merge_request.author) + end + it "emails subscribers of the merge request's labels" do user_1 = create(:user) user_2 = create(:user) @@ -1040,6 +1091,14 @@ describe NotificationService, services: true do should_not_email(@u_watcher) end + it "notifies the merger when the pipeline succeeds is false but they've opted into notifications about their activity" do + merge_request.merge_when_pipeline_succeeds = false + @u_watcher.notified_of_own_activity = true + notification.merge_mr(merge_request, @u_watcher) + + should_email(@u_watcher) + end + it_behaves_like 'participating notifications' do let(:participant) { create(:user, username: 'user-participant') } let(:issuable) { merge_request } @@ -1102,7 +1161,7 @@ describe NotificationService, services: true do end describe 'Projects' do - let(:project) { create :project } + let(:project) { create(:empty_project) } before do build_team(project) @@ -1147,7 +1206,7 @@ describe NotificationService, services: true do describe 'ProjectMember' do describe '#decline_group_invite' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:member) { create(:user) } before(:each) do @@ -1221,7 +1280,7 @@ describe NotificationService, services: true do describe 'Pipelines' do describe '#pipeline_finished' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:current_user) { create(:user) } let(:u_member) { create(:user) } let(:u_other) { create(:user) } diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index 193ccd17282..cf1f90becfd 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Projects::DestroyService, services: true do let!(:user) { create(:user) } - let!(:project) { create(:project, namespace: user.namespace) } + let!(:project) { create(:project, :repository, namespace: user.namespace) } let!(:path) { project.repository.path_to_repo } let!(:remove_path) { path.sub(/\.git\Z/, "+#{project.id}+deleted.git") } let!(:async) { false } # execute or async_execute diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb index 122a7cea2a1..33b267c069c 100644 --- a/spec/services/projects/download_service_spec.rb +++ b/spec/services/projects/download_service_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe Projects::DownloadService, services: true do describe 'File service' do before do - @user = create :user - @project = create :project, creator_id: @user.id, namespace: @user.namespace + @user = create(:user) + @project = create(:empty_project, creator_id: @user.id, namespace: @user.namespace) end context 'for a URL that is not on whitelist' do diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index e3be1989c93..f8eb34f2ef4 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -7,6 +7,7 @@ describe Projects::ForkService, services: true do @from_user = create(:user, namespace: @from_namespace ) avatar = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") @from_project = create(:project, + :repository, creator_id: @from_user.id, namespace: @from_namespace, star_count: 107, @@ -54,7 +55,7 @@ describe Projects::ForkService, services: true do context 'project already exists' do it "fails due to validation, not transaction failure" do - @existing_project = create(:project, creator_id: @to_user.id, name: @from_project.name, namespace: @to_namespace) + @existing_project = create(:project, :repository, creator_id: @to_user.id, name: @from_project.name, namespace: @to_namespace) @to_project = fork_project(@from_project, @to_user) expect(@existing_project).to be_persisted @@ -104,9 +105,10 @@ describe Projects::ForkService, services: true do before do @group_owner = create(:user) @developer = create(:user) - @project = create(:project, creator_id: @group_owner.id, - star_count: 777, - description: 'Wow, such a cool project!') + @project = create(:project, :repository, + creator_id: @group_owner.id, + star_count: 777, + description: 'Wow, such a cool project!') @group = create(:group) @group.add_user(@group_owner, GroupMember::OWNER) @group.add_user(@developer, GroupMember::DEVELOPER) @@ -139,8 +141,9 @@ describe Projects::ForkService, services: true do context 'project already exists in group' do it 'fails due to validation, not transaction failure' do - existing_project = create(:project, name: @project.name, - namespace: @group) + existing_project = create(:project, :repository, + name: @project.name, + namespace: @group) to_project = fork_project(@project, @group_owner, @opts) expect(existing_project.persisted?).to be_truthy expect(to_project.errors[:name]).to eq(['has already been taken']) diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb index 57a5aa5cedc..eaf63457b32 100644 --- a/spec/services/projects/housekeeping_service_spec.rb +++ b/spec/services/projects/housekeeping_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Projects::HousekeepingService do subject { Projects::HousekeepingService.new(project) } - let(:project) { create :project } + let(:project) { create(:project, :repository) } before do project.reset_pushes_since_gc diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index a3babaf1e0b..81e15f9dba6 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Projects::TransferService, services: true do let(:user) { create(:user) } let(:group) { create(:group) } - let(:project) { create(:project, namespace: user.namespace) } + let(:project) { create(:project, :repository, namespace: user.namespace) } context 'namespace -> namespace' do before do @@ -61,7 +61,7 @@ describe Projects::TransferService, services: true do before { internal_group.add_owner(user) } context 'when namespace visibility level < project visibility level' do - let(:public_project) { create(:project, :public, namespace: user.namespace) } + let(:public_project) { create(:project, :public, :repository, namespace: user.namespace) } before { transfer_project(public_project, user, internal_group) } @@ -69,7 +69,7 @@ describe Projects::TransferService, services: true do end context 'when namespace visibility level > project visibility level' do - let(:private_project) { create(:project, :private, namespace: user.namespace) } + let(:private_project) { create(:project, :private, :repository, namespace: user.namespace) } before { transfer_project(private_project, user, internal_group) } diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index f75fdd9e03f..fc0a17296f3 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -1,9 +1,9 @@ require "spec_helper" describe Projects::UpdatePagesService do - let(:project) { create :project } - let(:pipeline) { create :ci_pipeline, project: project, sha: project.commit('HEAD').sha } - let(:build) { create :ci_build, pipeline: pipeline, ref: 'HEAD' } + let(:project) { create(:project, :repository) } + let(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) } + let(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD') } let(:invalid_file) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png') } subject { described_class.new(project, build) } diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index caa23962519..05b18fef061 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Projects::UpdateService, services: true do let(:user) { create(:user) } let(:admin) { create(:admin) } - let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } + let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) } describe 'update_by_user' do context 'when visibility_level is INTERNAL' do @@ -56,7 +56,7 @@ describe Projects::UpdateService, services: true do end describe 'visibility_level' do - let(:project) { create(:project, :internal) } + let(:project) { create(:empty_project, :internal) } let(:forked_project) { create(:forked_project_with_submodules, :internal) } before do diff --git a/spec/services/projects/upload_service_spec.rb b/spec/services/projects/upload_service_spec.rb index 150c8ccaef7..d2cefa46bfa 100644 --- a/spec/services/projects/upload_service_spec.rb +++ b/spec/services/projects/upload_service_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe Projects::UploadService, services: true do describe 'File service' do before do - @user = create :user - @project = create :project, creator_id: @user.id, namespace: @user.namespace + @user = create(:user) + @project = create(:empty_project, creator_id: @user.id, namespace: @user.namespace) end context 'for valid gif file' do diff --git a/spec/services/search/global_service_spec.rb b/spec/services/search/global_service_spec.rb new file mode 100644 index 00000000000..2531607acad --- /dev/null +++ b/spec/services/search/global_service_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Search::GlobalService, services: true do + let(:user) { create(:user) } + let(:internal_user) { create(:user) } + + let!(:found_project) { create(:empty_project, :private, name: 'searchable_project') } + let!(:unfound_project) { create(:empty_project, :private, name: 'unfound_project') } + let!(:internal_project) { create(:empty_project, :internal, name: 'searchable_internal_project') } + let!(:public_project) { create(:empty_project, :public, name: 'searchable_public_project') } + + before do + found_project.add_master(user) + end + + describe '#execute' do + context 'unauthenticated' do + it 'returns public projects only' do + results = Search::GlobalService.new(nil, search: "searchable").execute + + expect(results.objects('projects')).to match_array [public_project] + end + end + + context 'authenticated' do + it 'returns public, internal and private projects' do + results = Search::GlobalService.new(user, search: "searchable").execute + + expect(results.objects('projects')).to match_array [public_project, found_project, internal_project] + end + + it 'returns only public & internal projects' do + results = Search::GlobalService.new(internal_user, search: "searchable").execute + + expect(results.objects('projects')).to match_array [internal_project, public_project] + end + + it 'namespace name is searchable' do + results = Search::GlobalService.new(user, search: found_project.namespace.path).execute + + expect(results.objects('projects')).to match_array [found_project] + end + + context 'nested group' do + let!(:nested_group) { create(:group, :nested) } + let!(:project) { create(:empty_project, namespace: nested_group) } + + before do + project.add_master(user) + end + + it 'returns result from nested group' do + results = Search::GlobalService.new(user, search: project.path).execute + + expect(results.objects('projects')).to match_array [project] + end + + it 'returns result from descendants when search inside group' do + results = Search::GlobalService.new(user, search: project.path, group_id: nested_group.parent).execute + + expect(results.objects('projects')).to match_array [project] + end + end + end + end +end diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index bed1031e40a..2112f1cf9ea 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -1,65 +1,286 @@ require 'spec_helper' -describe 'Search::GlobalService', services: true do +describe SearchService, services: true do let(:user) { create(:user) } - let(:public_user) { create(:user) } - let(:internal_user) { create(:user) } - let!(:found_project) { create(:empty_project, :private, name: 'searchable_project') } - let!(:unfound_project) { create(:empty_project, :private, name: 'unfound_project') } - let!(:internal_project) { create(:empty_project, :internal, name: 'searchable_internal_project') } - let!(:public_project) { create(:empty_project, :public, name: 'searchable_public_project') } + let(:accessible_group) { create(:group, :private) } + let(:inaccessible_group) { create(:group, :private) } + let!(:group_member) { create(:group_member, group: accessible_group, user: user) } + + let!(:accessible_project) { create(:empty_project, :private, name: 'accessible_project') } + let!(:inaccessible_project) { create(:empty_project, :private, name: 'inaccessible_project') } + let(:note) { create(:note_on_issue, project: accessible_project) } + + let(:snippet) { create(:snippet, author: user) } + let(:group_project) { create(:empty_project, group: accessible_group, name: 'group_project') } + let(:public_project) { create(:empty_project, :public, name: 'public_project') } before do - found_project.team << [user, :master] + accessible_project.add_master(user) + end + + describe '#project' do + context 'when the project is accessible' do + it 'returns the project' do + project = SearchService.new(user, project_id: accessible_project.id).project + + expect(project).to eq accessible_project + end + end + + context 'when the project is not accessible' do + it 'returns nil' do + project = SearchService.new(user, project_id: inaccessible_project.id).project + + expect(project).to be_nil + end + end + + context 'when there is no project_id' do + it 'returns nil' do + project = SearchService.new(user).project + + expect(project).to be_nil + end + end end - describe '#execute' do - context 'unauthenticated' do - it 'returns public projects only' do - context = Search::GlobalService.new(nil, search: "searchable") - results = context.execute - expect(results.objects('projects')).to match_array [public_project] + describe '#group' do + context 'when the group is accessible' do + it 'returns the group' do + group = SearchService.new(user, group_id: accessible_group.id).group + + expect(group).to eq accessible_group end end - context 'authenticated' do - it 'returns public, internal and private projects' do - context = Search::GlobalService.new(user, search: "searchable") - results = context.execute - expect(results.objects('projects')).to match_array [public_project, found_project, internal_project] + context 'when the group is not accessible' do + it 'returns nil' do + group = SearchService.new(user, group_id: inaccessible_group.id).group + + expect(group).to be_nil end + end + + context 'when there is no group_id' do + it 'returns nil' do + group = SearchService.new(user).group - it 'returns only public & internal projects' do - context = Search::GlobalService.new(internal_user, search: "searchable") - results = context.execute - expect(results.objects('projects')).to match_array [internal_project, public_project] + expect(group).to be_nil end + end + end + + describe '#show_snippets?' do + context 'when :snippets is \'true\'' do + it 'returns true' do + show_snippets = SearchService.new(user, snippets: 'true').show_snippets? - it 'namespace name is searchable' do - context = Search::GlobalService.new(user, search: found_project.namespace.path) - results = context.execute - expect(results.objects('projects')).to match_array [found_project] + expect(show_snippets).to be_truthy end + end - context 'nested group' do - let!(:nested_group) { create(:group, :nested) } - let!(:project) { create(:project, namespace: nested_group) } + context 'when :snippets is not \'true\'' do + it 'returns false' do + show_snippets = SearchService.new(user, snippets: 'tru').show_snippets? + + expect(show_snippets).to be_falsey + end + end - before { project.add_master(user) } + context 'when :snippets is missing' do + it 'returns false' do + show_snippets = SearchService.new(user).show_snippets? - it 'returns result from nested group' do - context = Search::GlobalService.new(user, search: project.path) - results = context.execute - expect(results.objects('projects')).to match_array [project] + expect(show_snippets).to be_falsey + end + end + end + + describe '#scope' do + context 'with accessible project_id' do + context 'and allowed scope' do + it 'returns the specified scope' do + scope = SearchService.new(user, project_id: accessible_project.id, scope: 'notes').scope + + expect(scope).to eq 'notes' end + end + + context 'and disallowed scope' do + it 'returns the default scope' do + scope = SearchService.new(user, project_id: accessible_project.id, scope: 'projects').scope - it 'returns result from descendants when search inside group' do - context = Search::GlobalService.new(user, search: project.path, group_id: nested_group.parent) - results = context.execute - expect(results.objects('projects')).to match_array [project] + expect(scope).to eq 'blobs' end end + + context 'and no scope' do + it 'returns the default scope' do + scope = SearchService.new(user, project_id: accessible_project.id).scope + + expect(scope).to eq 'blobs' + end + end + end + + context 'with \'true\' snippets' do + context 'and allowed scope' do + it 'returns the specified scope' do + scope = SearchService.new(user, snippets: 'true', scope: 'snippet_titles').scope + + expect(scope).to eq 'snippet_titles' + end + end + + context 'and disallowed scope' do + it 'returns the default scope' do + scope = SearchService.new(user, snippets: 'true', scope: 'projects').scope + + expect(scope).to eq 'snippet_blobs' + end + end + + context 'and no scope' do + it 'returns the default scope' do + scope = SearchService.new(user, snippets: 'true').scope + + expect(scope).to eq 'snippet_blobs' + end + end + end + + context 'with no project_id, no snippets' do + context 'and allowed scope' do + it 'returns the specified scope' do + scope = SearchService.new(user, scope: 'issues').scope + + expect(scope).to eq 'issues' + end + end + + context 'and disallowed scope' do + it 'returns the default scope' do + scope = SearchService.new(user, scope: 'blobs').scope + + expect(scope).to eq 'projects' + end + end + + context 'and no scope' do + it 'returns the default scope' do + scope = SearchService.new(user).scope + + expect(scope).to eq 'projects' + end + end + end + end + + describe '#search_results' do + context 'with accessible project_id' do + it 'returns an instance of Gitlab::ProjectSearchResults' do + search_results = SearchService.new( + user, + project_id: accessible_project.id, + scope: 'notes', + search: note.note).search_results + + expect(search_results).to be_a Gitlab::ProjectSearchResults + end + end + + context 'with accessible project_id and \'true\' snippets' do + it 'returns an instance of Gitlab::ProjectSearchResults' do + search_results = SearchService.new( + user, + project_id: accessible_project.id, + snippets: 'true', + scope: 'notes', + search: note.note).search_results + + expect(search_results).to be_a Gitlab::ProjectSearchResults + end + end + + context 'with \'true\' snippets' do + it 'returns an instance of Gitlab::SnippetSearchResults' do + search_results = SearchService.new( + user, + snippets: 'true', + search: snippet.content).search_results + + expect(search_results).to be_a Gitlab::SnippetSearchResults + end + end + + context 'with no project_id and no snippets' do + it 'returns an instance of Gitlab::SearchResults' do + search_results = SearchService.new( + user, + search: public_project.name).search_results + + expect(search_results).to be_a Gitlab::SearchResults + end + end + end + + describe '#search_objects' do + context 'with accessible project_id' do + it 'returns objects in the project' do + search_objects = SearchService.new( + user, + project_id: accessible_project.id, + scope: 'notes', + search: note.note).search_objects + + expect(search_objects.first).to eq note + end + end + + context 'with accessible project_id and \'true\' snippets' do + it 'returns objects in the project' do + search_objects = SearchService.new( + user, + project_id: accessible_project.id, + snippets: 'true', + scope: 'notes', + search: note.note).search_objects + + expect(search_objects.first).to eq note + end + end + + context 'with \'true\' snippets' do + it 'returns objects in snippets' do + search_objects = SearchService.new( + user, + snippets: 'true', + search: snippet.content).search_objects + + expect(search_objects.first).to eq snippet + end + end + + context 'with accessible group_id' do + it 'returns objects in the group' do + search_objects = SearchService.new( + user, + group_id: accessible_group.id, + search: group_project.name).search_objects + + expect(search_objects.first).to eq group_project + end + end + + context 'with no project_id, group_id or snippets' do + it 'returns objects in global' do + search_objects = SearchService.new( + user, + search: public_project.name).search_objects + + expect(search_objects.first).to eq public_project + end end end end diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb index 52e8678cb9d..a63281f0eab 100644 --- a/spec/services/slash_commands/interpret_service_spec.rb +++ b/spec/services/slash_commands/interpret_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe SlashCommands::InterpretService, services: true do - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:developer) { create(:user) } let(:issue) { create(:issue, project: project) } let(:milestone) { create(:milestone, project: project, title: '9.10') } @@ -260,6 +260,8 @@ describe SlashCommands::InterpretService, services: true do end shared_examples 'merge command' do + let(:project) { create(:project, :repository) } + it 'runs merge command if content contains /merge' do _, updates = service.execute(content, issuable) @@ -322,6 +324,7 @@ describe SlashCommands::InterpretService, services: true do end context 'when sha is missing' do + let(:project) { create(:project, :repository) } let(:service) { described_class.new(project, developer, {}) } it 'precheck passes and returns merge command' do @@ -694,7 +697,7 @@ describe SlashCommands::InterpretService, services: true do end context '/target_branch command' do - let(:non_empty_project) { create(:project) } + let(:non_empty_project) { create(:project, :repository) } let(:another_merge_request) { create(:merge_request, author: developer, source_project: non_empty_project) } let(:service) { described_class.new(non_empty_project, developer)} diff --git a/spec/services/spam_service_spec.rb b/spec/services/spam_service_spec.rb index e09c05ccf32..74cba8c014b 100644 --- a/spec/services/spam_service_spec.rb +++ b/spec/services/spam_service_spec.rb @@ -15,7 +15,7 @@ describe SpamService, services: true do end context 'when recaptcha was not verified' do - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:issue) { create(:issue, project: project) } let(:request) { double(:request, env: {}) } diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 11037a4917b..667059f230c 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' describe SystemHooksService, services: true do - let(:user) { create :user } - let(:project) { create :project } - let(:project_member) { create :project_member } - let(:key) { create(:key, user: user) } - let(:deploy_key) { create(:key) } - let(:group) { create(:group) } - let(:group_member) { create(:group_member) } + let(:user) { create(:user) } + let(:project) { create(:empty_project) } + let(:project_member) { create(:project_member) } + let(:key) { create(:key, user: user) } + let(:deploy_key) { create(:key) } + let(:group) { create(:group) } + let(:group_member) { create(:group_member) } context 'event data' do it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) } diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 36a17a3bf2e..90cde705b85 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -3,17 +3,20 @@ require 'spec_helper' describe SystemNoteService, services: true do include Gitlab::Routing.url_helpers - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:author) { create(:user) } let(:noteable) { create(:issue, project: project) } shared_examples_for 'a system note' do + let(:expected_noteable) { noteable } + let(:commit_count) { nil } + it 'is valid' do expect(subject).to be_valid end it 'sets the noteable model' do - expect(subject.noteable).to eq noteable + expect(subject.noteable).to eq expected_noteable end it 'sets the project' do @@ -27,17 +30,34 @@ describe SystemNoteService, services: true do it 'is a system note' do expect(subject).to be_system end + + context 'metadata' do + it 'creates a new system note metadata record' do + expect { subject }.to change{ SystemNoteMetadata.count }.from(0).to(1) + end + + it 'creates a record correctly' do + metadata = subject.system_note_metadata + + expect(metadata.commit_count).to eq(commit_count) + expect(metadata.action).to eq(action) + end + end end describe '.add_commits' do subject { described_class.add_commits(noteable, project, author, new_commits, old_commits, oldrev) } + let(:project) { create(:project, :repository) } let(:noteable) { create(:merge_request, source_project: project) } let(:new_commits) { noteable.commits } let(:old_commits) { [] } let(:oldrev) { nil } - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:commit_count) { new_commits.size } + let(:action) { 'commit' } + end describe 'note body' do let(:note_lines) { subject.note.split("\n").reject(&:blank?) } @@ -116,7 +136,9 @@ describe SystemNoteService, services: true do let(:assignee) { create(:user) } - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'assignee' } + end context 'when assignee added' do it 'sets the note text' do @@ -140,7 +162,9 @@ describe SystemNoteService, services: true do let(:added) { [] } let(:removed) { [] } - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'label' } + end context 'with added labels' do let(:added) { labels } @@ -175,7 +199,9 @@ describe SystemNoteService, services: true do let(:milestone) { create(:milestone, project: project) } - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'milestone' } + end context 'when milestone added' do it 'sets the note text' do @@ -198,7 +224,9 @@ describe SystemNoteService, services: true do let(:status) { 'new_status' } let(:source) { nil } - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'status' } + end context 'with a source' do let(:source) { double('commit', gfm_reference: 'commit 123456') } @@ -216,6 +244,7 @@ describe SystemNoteService, services: true do end describe '.merge_when_pipeline_succeeds' do + let(:project) { create(:project, :repository) } let(:pipeline) { build(:ci_pipeline_without_jobs )} let(:noteable) do create(:merge_request, source_project: project, target_project: project) @@ -223,21 +252,26 @@ describe SystemNoteService, services: true do subject { described_class.merge_when_pipeline_succeeds(noteable, project, author, noteable.diff_head_commit) } - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'merge' } + end it "posts the 'merge when pipeline succeeds' system note" do - expect(subject.note).to match /enabled an automatic merge when the pipeline for (\w+\/\w+@)?\h{40} succeeds/ + expect(subject.note).to match(/enabled an automatic merge when the pipeline for (\w+\/\w+@)?\h{40} succeeds/) end end describe '.cancel_merge_when_pipeline_succeeds' do + let(:project) { create(:project, :repository) } let(:noteable) do create(:merge_request, source_project: project, target_project: project) end subject { described_class.cancel_merge_when_pipeline_succeeds(noteable, project, author) } - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'merge' } + end it "posts the 'merge when pipeline succeeds' system note" do expect(subject.note).to eq "canceled the automatic merge" @@ -250,7 +284,9 @@ describe SystemNoteService, services: true do subject { described_class.change_title(noteable, project, author, 'Old title') } context 'when noteable responds to `title`' do - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'title' } + end it 'sets the note text' do expect(subject.note). @@ -263,7 +299,9 @@ describe SystemNoteService, services: true do subject { described_class.change_issue_confidentiality(noteable, project, author) } context 'when noteable responds to `confidential`' do - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'confidentiality' } + end it 'sets the note text' do expect(subject.note).to eq 'made the issue visible to everyone' @@ -273,10 +311,14 @@ describe SystemNoteService, services: true do describe '.change_branch' do subject { described_class.change_branch(noteable, project, author, 'target', old_branch, new_branch) } + + let(:project) { create(:project, :repository) } let(:old_branch) { 'old_branch'} let(:new_branch) { 'new_branch'} - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'branch' } + end context 'when target branch name changed' do it 'sets the note text' do @@ -288,7 +330,11 @@ describe SystemNoteService, services: true do describe '.change_branch_presence' do subject { described_class.change_branch_presence(noteable, project, author, :source, 'feature', :delete) } - it_behaves_like 'a system note' + let(:project) { create(:project, :repository) } + + it_behaves_like 'a system note' do + let(:action) { 'branch' } + end context 'when source branch deleted' do it 'sets the note text' do @@ -300,11 +346,15 @@ describe SystemNoteService, services: true do describe '.new_issue_branch' do subject { described_class.new_issue_branch(noteable, project, author, "1-mepmep") } - it_behaves_like 'a system note' + let(:project) { create(:project, :repository) } + + it_behaves_like 'a system note' do + let(:action) { 'branch' } + end context 'when a branch is created from the new branch button' do it 'sets the note text' do - expect(subject.note).to match /\Acreated branch [`1-mepmep`]/ + expect(subject.note).to start_with("created branch [`1-mepmep`]") end end end @@ -314,7 +364,9 @@ describe SystemNoteService, services: true do let(:mentioner) { create(:issue, project: project) } - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'cross_reference' } + end context 'when cross-reference disallowed' do before do @@ -324,6 +376,10 @@ describe SystemNoteService, services: true do it 'returns nil' do expect(subject).to be_nil end + + it 'does not create a system note metadata record' do + expect { subject }.not_to change{ SystemNoteMetadata.count } + end end context 'when cross-reference allowed' do @@ -331,9 +387,13 @@ describe SystemNoteService, services: true do expect(described_class).to receive(:cross_reference_disallowed?).and_return(false) end + it_behaves_like 'a system note' do + let(:action) { 'cross_reference' } + end + describe 'note_body' do context 'cross-project' do - let(:project2) { create(:project) } + let(:project2) { create(:project, :repository) } let(:mentioner) { create(:issue, project: project2) } context 'from Commit' do @@ -353,6 +413,7 @@ describe SystemNoteService, services: true do context 'within the same project' do context 'from Commit' do + let(:project) { create(:project, :repository) } let(:mentioner) { project.repository.commit } it 'references the mentioning commit' do @@ -394,6 +455,7 @@ describe SystemNoteService, services: true do end context 'when mentioner is a MergeRequest' do + let(:project) { create(:project, :repository) } let(:mentioner) { create(:merge_request, :simple, source_project: project) } let(:noteable) { project.commit } @@ -421,6 +483,7 @@ describe SystemNoteService, services: true do end describe '.cross_reference_exists?' do + let(:project) { create(:project, :repository) } let(:commit0) { project.commit } let(:commit1) { project.commit('HEAD~2') } @@ -513,7 +576,7 @@ describe SystemNoteService, services: true do end describe '.noteable_moved' do - let(:new_project) { create(:project) } + let(:new_project) { create(:empty_project) } let(:new_noteable) { create(:issue, project: new_project) } subject do @@ -540,9 +603,12 @@ describe SystemNoteService, services: true do let(:direction) { :to } it_behaves_like 'cross project mentionable' + it_behaves_like 'a system note' do + let(:action) { 'moved' } + end it 'notifies about noteable being moved to' do - expect(subject.note).to match /moved to/ + expect(subject.note).to match('moved to') end end @@ -550,9 +616,12 @@ describe SystemNoteService, services: true do let(:direction) { :from } it_behaves_like 'cross project mentionable' + it_behaves_like 'a system note' do + let(:action) { 'moved' } + end it 'notifies about noteable being moved from' do - expect(subject.note).to match /moved from/ + expect(subject.note).to match('moved from') end end @@ -574,13 +643,13 @@ describe SystemNoteService, services: true do end end - include JiraServiceHelper - describe 'JIRA integration' do + include JiraServiceHelper + let(:project) { create(:jira_project) } let(:author) { create(:user) } let(:issue) { create(:issue, project: project) } - let(:merge_request) { create(:merge_request, :simple, target_project: project, source_project: project) } + let(:merge_request) { create(:merge_request, :simple, target_project: project, source_project: project) } let(:jira_issue) { ExternalIssue.new("JIRA-1", project)} let(:jira_tracker) { project.jira_service } let(:commit) { project.commit } @@ -720,33 +789,34 @@ describe SystemNoteService, services: true do let(:merge_request) { discussion.noteable } let(:project) { merge_request.source_project } let(:issue) { create(:issue, project: project) } - let(:user) { create(:user) } def reloaded_merge_request MergeRequest.find(merge_request.id) end - before do - project.team << [user, :developer] + subject { described_class.discussion_continued_in_issue(discussion, project, author, issue) } + + it_behaves_like 'a system note' do + let(:expected_noteable) { discussion.first_note.noteable } + let(:action) { 'discussion' } end it 'creates a new note in the discussion' do # we need to completely rebuild the merge request object, or the `@discussions` on the merge request are not reloaded. - expect { SystemNoteService.discussion_continued_in_issue(discussion, project, user, issue) }. - to change { reloaded_merge_request.discussions.first.notes.size }.by(1) + expect { subject }.to change { reloaded_merge_request.discussions.first.notes.size }.by(1) end it 'mentions the created issue in the system note' do - note = SystemNoteService.discussion_continued_in_issue(discussion, project, user, issue) - - expect(note.note).to include(issue.to_reference) + expect(subject.note).to include(issue.to_reference) end end describe '.change_time_estimate' do subject { described_class.change_time_estimate(noteable, project, author) } - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'time_tracking' } + end context 'with a time estimate' do it 'sets the note text' do @@ -776,7 +846,9 @@ describe SystemNoteService, services: true do described_class.change_time_spent(noteable, project, author) end - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'time_tracking' } + end context 'when time was added' do it 'sets the note text' do @@ -808,7 +880,36 @@ describe SystemNoteService, services: true do end end + describe '.remove_merge_request_wip' do + let(:noteable) { create(:issue, project: project, title: 'WIP: Lorem ipsum') } + + subject { described_class.remove_merge_request_wip(noteable, project, author) } + + it_behaves_like 'a system note' do + let(:action) { 'title' } + end + + it 'sets the note text' do + expect(subject.note).to eq 'unmarked as a **Work In Progress**' + end + end + + describe '.add_merge_request_wip' do + let(:noteable) { create(:issue, project: project, title: 'Lorem ipsum') } + + subject { described_class.add_merge_request_wip(noteable, project, author) } + + it_behaves_like 'a system note' do + let(:action) { 'title' } + end + + it 'sets the note text' do + expect(subject.note).to eq 'marked as a **Work In Progress**' + end + end + describe '.add_merge_request_wip_from_commit' do + let(:project) { create(:project, :repository) } let(:noteable) do create(:merge_request, source_project: project, target_project: project) end @@ -822,7 +923,9 @@ describe SystemNoteService, services: true do ) end - it_behaves_like 'a system note' + it_behaves_like 'a system note' do + let(:action) { 'title' } + end it "posts the 'marked as a Work In Progress from commit' system note" do expect(subject.note).to match( @@ -830,4 +933,33 @@ describe SystemNoteService, services: true do ) end end + + describe '.change_task_status' do + let(:noteable) { create(:issue, project: project) } + let(:task) { double(:task, complete?: true, source: 'task') } + + subject { described_class.change_task_status(noteable, project, author, task) } + + it_behaves_like 'a system note' do + let(:action) { 'task' } + end + + it "posts the 'marked as a Work In Progress from commit' system note" do + expect(subject.note).to eq("marked the task **task** as completed") + end + end + + describe '.resolve_all_discussions' do + let(:noteable) { create(:merge_request, source_project: project, target_project: project) } + + subject { described_class.resolve_all_discussions(noteable, project, author) } + + it_behaves_like 'a system note' do + let(:action) { 'discussion' } + end + + it 'sets the note text' do + expect(subject.note).to eq 'resolved all discussions' + end + end end diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb index 5478b8c9ec0..b9121b1de49 100644 --- a/spec/services/tags/create_service_spec.rb +++ b/spec/services/tags/create_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Tags::CreateService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:user) { create(:user) } let(:service) { described_class.new(project, user) } diff --git a/spec/services/tags/destroy_service_spec.rb b/spec/services/tags/destroy_service_spec.rb index a388c93379a..28396fc3658 100644 --- a/spec/services/tags/destroy_service_spec.rb +++ b/spec/services/tags/destroy_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Tags::DestroyService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:user) { create(:user) } let(:service) { described_class.new(project, user) } diff --git a/spec/services/test_hook_service_spec.rb b/spec/services/test_hook_service_spec.rb index 4f6dd8c6d3f..f99fd8434c2 100644 --- a/spec/services/test_hook_service_spec.rb +++ b/spec/services/test_hook_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' describe TestHookService, services: true do - let(:user) { create :user } - let(:project) { create :project } - let(:hook) { create :project_hook, project: project } + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:hook) { create(:project_hook, project: project) } describe '#execute' do it "executes successfully" do diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index 3645b73b039..89b3b6aad10 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -8,10 +8,12 @@ describe TodoService, services: true do let(:guest) { create(:user) } let(:admin) { create(:admin) } let(:john_doe) { create(:user) } - let(:project) { create(:project) } - let(:mentions) { 'FYI: ' + [author, assignee, john_doe, member, guest, non_member, admin].map(&:to_reference).join(' ') } - let(:directly_addressed) { [author, assignee, john_doe, member, guest, non_member, admin].map(&:to_reference).join(' ') } - let(:directly_addressed_and_mentioned) { member.to_reference + ", what do you think? cc: " + [guest, admin].map(&:to_reference).join(' ') } + let(:skipped) { create(:user) } + let(:skip_users) { [skipped] } + let(:project) { create(:empty_project) } + let(:mentions) { 'FYI: ' + [author, assignee, john_doe, member, guest, non_member, admin, skipped].map(&:to_reference).join(' ') } + let(:directly_addressed) { [author, assignee, john_doe, member, guest, non_member, admin, skipped].map(&:to_reference).join(' ') } + let(:directly_addressed_and_mentioned) { member.to_reference + ", what do you think? cc: " + [guest, admin, skipped].map(&:to_reference).join(' ') } let(:service) { described_class.new } before do @@ -19,6 +21,7 @@ describe TodoService, services: true do project.team << [author, :developer] project.team << [member, :developer] project.team << [john_doe, :developer] + project.team << [skipped, :developer] end describe 'Issues' do @@ -99,9 +102,9 @@ describe TodoService, services: true do end context 'when a private group is mentioned' do - let(:group) { create :group, :private } - let(:project) { create :project, :private, group: group } - let(:issue) { create :issue, author: author, project: project, description: group.to_reference } + let(:group) { create(:group, :private) } + let(:project) { create(:empty_project, :private, group: group) } + let(:issue) { create(:issue, author: author, project: project, description: group.to_reference) } before do group.add_owner(author) @@ -119,46 +122,61 @@ describe TodoService, services: true do end describe '#update_issue' do - it 'creates a todo for each valid mentioned user' do - service.update_issue(issue, author) + it 'creates a todo for each valid mentioned user not included in skip_users' do + service.update_issue(issue, author, skip_users) should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_create_todo(user: guest, target: issue, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED) should_create_todo(user: author, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED) + should_not_create_todo(user: skipped, target: issue, action: Todo::MENTIONED) end - it 'creates a todo for each valid user based on the type of mention' do + it 'creates a todo for each valid user not included in skip_users based on the type of mention' do issue.update(description: directly_addressed_and_mentioned) - service.update_issue(issue, author) + service.update_issue(issue, author, skip_users) should_create_todo(user: member, target: issue, action: Todo::DIRECTLY_ADDRESSED) should_create_todo(user: guest, target: issue, action: Todo::MENTIONED) should_create_todo(user: admin, target: issue, action: Todo::MENTIONED) + should_not_create_todo(user: skipped, target: issue) end - it 'creates a directly addressed todo for each valid addressed user' do - service.update_issue(addressed_issue, author) + it 'creates a directly addressed todo for each valid addressed user not included in skip_users' do + service.update_issue(addressed_issue, author, skip_users) should_create_todo(user: member, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED) should_create_todo(user: guest, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED) should_create_todo(user: john_doe, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED) should_create_todo(user: author, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED) should_not_create_todo(user: non_member, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED) + should_not_create_todo(user: skipped, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED) end - it 'does not create a todo if user was already mentioned' do + it 'does not create a todo if user was already mentioned and todo is pending' do create(:todo, :mentioned, user: member, project: project, target: issue, author: author) - expect { service.update_issue(issue, author) }.not_to change(member.todos, :count) + expect { service.update_issue(issue, author, skip_users) }.not_to change(member.todos, :count) + end + + it 'does not create a todo if user was already mentioned and todo is done' do + create(:todo, :mentioned, :done, user: skipped, project: project, target: issue, author: author) + + expect { service.update_issue(issue, author, skip_users) }.not_to change(skipped.todos, :count) end - it 'does not create a directly addressed todo if user was already mentioned or addressed' do + it 'does not create a directly addressed todo if user was already mentioned or addressed and todo is pending' do create(:todo, :directly_addressed, user: member, project: project, target: addressed_issue, author: author) - expect { service.update_issue(addressed_issue, author) }.not_to change(member.todos, :count) + expect { service.update_issue(addressed_issue, author, skip_users) }.not_to change(member.todos, :count) + end + + it 'does not create a directly addressed todo if user was already mentioned or addressed and todo is done' do + create(:todo, :directly_addressed, :done, user: skipped, project: project, target: addressed_issue, author: author) + + expect { service.update_issue(addressed_issue, author, skip_users) }.not_to change(skipped.todos, :count) end it 'does not create todo if user can not see the issue when issue is confidential' do @@ -422,22 +440,26 @@ describe TodoService, services: true do should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue) end - it 'creates a todo for each valid mentioned user when leaving a note on commit' do - service.new_note(note_on_commit, john_doe) + context 'on commit' do + let(:project) { create(:project, :repository) } - should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) - should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) - should_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) - should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) - end + it 'creates a todo for each valid mentioned user when leaving a note on commit' do + service.new_note(note_on_commit, john_doe) + + should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) + should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) + should_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) + should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) + end - it 'creates a directly addressed todo for each valid mentioned user when leaving a note on commit' do - service.new_note(addressed_note_on_commit, john_doe) + it 'creates a directly addressed todo for each valid mentioned user when leaving a note on commit' do + service.new_note(addressed_note_on_commit, john_doe) - should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit) - should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit) - should_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit) - should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit) + should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit) + should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit) + should_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit) + should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit) + end end it 'does not create todo when leaving a note on snippet' do @@ -517,47 +539,62 @@ describe TodoService, services: true do end describe '#update_merge_request' do - it 'creates a todo for each valid mentioned user' do - service.update_merge_request(mr_assigned, author) + it 'creates a todo for each valid mentioned user not included in skip_users' do + service.update_merge_request(mr_assigned, author, skip_users) should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED) + should_not_create_todo(user: skipped, target: mr_assigned, action: Todo::MENTIONED) end - it 'creates a todo for each valid user based on the type of mention' do + it 'creates a todo for each valid user not included in skip_users based on the type of mention' do mr_assigned.update(description: directly_addressed_and_mentioned) - service.update_merge_request(mr_assigned, author) + service.update_merge_request(mr_assigned, author, skip_users) should_create_todo(user: member, target: mr_assigned, action: Todo::DIRECTLY_ADDRESSED) should_create_todo(user: admin, target: mr_assigned, action: Todo::MENTIONED) + should_not_create_todo(user: skipped, target: mr_assigned) end - it 'creates a directly addressed todo for each valid addressed user' do - service.update_merge_request(addressed_mr_assigned, author) + it 'creates a directly addressed todo for each valid addressed user not included in skip_users' do + service.update_merge_request(addressed_mr_assigned, author, skip_users) should_create_todo(user: member, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED) should_not_create_todo(user: guest, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED) should_create_todo(user: john_doe, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED) should_create_todo(user: author, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED) should_not_create_todo(user: non_member, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED) + should_not_create_todo(user: skipped, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED) end - it 'does not create a todo if user was already mentioned' do + it 'does not create a todo if user was already mentioned and todo is pending' do create(:todo, :mentioned, user: member, project: project, target: mr_assigned, author: author) expect { service.update_merge_request(mr_assigned, author) }.not_to change(member.todos, :count) end - it 'does not create a directly addressed todo if user was already mentioned or addressed' do + it 'does not create a todo if user was already mentioned and todo is done' do + create(:todo, :mentioned, :done, user: skipped, project: project, target: mr_assigned, author: author) + + expect { service.update_merge_request(mr_assigned, author, skip_users) }.not_to change(skipped.todos, :count) + end + + it 'does not create a directly addressed todo if user was already mentioned or addressed and todo is pending' do create(:todo, :directly_addressed, user: member, project: project, target: addressed_mr_assigned, author: author) expect{ service.update_merge_request(addressed_mr_assigned, author) }.not_to change(member.todos, :count) end + it 'does not create a directly addressed todo if user was already mentioned or addressed and todo is done' do + create(:todo, :directly_addressed, user: skipped, project: project, target: addressed_mr_assigned, author: author) + + expect{ service.update_merge_request(addressed_mr_assigned, author, skip_users) }.not_to change(skipped.todos, :count) + end + context 'with a task list' do it 'does not create todo when tasks are marked as completed' do mr_assigned.update(description: "- [x] Task 1\n- [X] Task 2 #{mentions}") @@ -720,6 +757,7 @@ describe TodoService, services: true do end describe '#new_note' do + let(:project) { create(:project, :repository) } let(:mention) { john_doe.to_reference } let(:diff_note_on_merge_request) { create(:diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "Hey #{mention}") } let(:addressed_diff_note_on_merge_request) { create(:diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "#{mention}, hey!") } @@ -752,6 +790,69 @@ describe TodoService, services: true do end end + describe '#update_note' do + let(:noteable) { create(:issue, project: project) } + let(:note) { create(:note, project: project, note: mentions, noteable: noteable) } + let(:addressed_note) { create(:note, project: project, note: "#{directly_addressed}", noteable: noteable) } + + it 'creates a todo for each valid mentioned user not included in skip_users' do + service.update_note(note, author, skip_users) + + should_create_todo(user: member, target: noteable, action: Todo::MENTIONED) + should_create_todo(user: guest, target: noteable, action: Todo::MENTIONED) + should_create_todo(user: john_doe, target: noteable, action: Todo::MENTIONED) + should_create_todo(user: author, target: noteable, action: Todo::MENTIONED) + should_not_create_todo(user: non_member, target: noteable, action: Todo::MENTIONED) + should_not_create_todo(user: skipped, target: noteable, action: Todo::MENTIONED) + end + + it 'creates a todo for each valid user not included in skip_users based on the type of mention' do + note.update(note: directly_addressed_and_mentioned) + + service.update_note(note, author, skip_users) + + should_create_todo(user: member, target: noteable, action: Todo::DIRECTLY_ADDRESSED) + should_create_todo(user: guest, target: noteable, action: Todo::MENTIONED) + should_create_todo(user: admin, target: noteable, action: Todo::MENTIONED) + should_not_create_todo(user: skipped, target: noteable) + end + + it 'creates a directly addressed todo for each valid addressed user not included in skip_users' do + service.update_note(addressed_note, author, skip_users) + + should_create_todo(user: member, target: noteable, action: Todo::DIRECTLY_ADDRESSED) + should_create_todo(user: guest, target: noteable, action: Todo::DIRECTLY_ADDRESSED) + should_create_todo(user: john_doe, target: noteable, action: Todo::DIRECTLY_ADDRESSED) + should_create_todo(user: author, target: noteable, action: Todo::DIRECTLY_ADDRESSED) + should_not_create_todo(user: non_member, target: noteable, action: Todo::DIRECTLY_ADDRESSED) + should_not_create_todo(user: skipped, target: noteable, action: Todo::DIRECTLY_ADDRESSED) + end + + it 'does not create a todo if user was already mentioned and todo is pending' do + create(:todo, :mentioned, user: member, project: project, target: noteable, author: author) + + expect { service.update_note(note, author, skip_users) }.not_to change(member.todos, :count) + end + + it 'does not create a todo if user was already mentioned and todo is done' do + create(:todo, :mentioned, :done, user: skipped, project: project, target: noteable, author: author) + + expect { service.update_note(note, author, skip_users) }.not_to change(skipped.todos, :count) + end + + it 'does not create a directly addressed todo if user was already mentioned or addressed and todo is pending' do + create(:todo, :directly_addressed, user: member, project: project, target: noteable, author: author) + + expect { service.update_note(addressed_note, author, skip_users) }.not_to change(member.todos, :count) + end + + it 'does not create a directly addressed todo if user was already mentioned or addressed and todo is done' do + create(:todo, :directly_addressed, :done, user: skipped, project: project, target: noteable, author: author) + + expect { service.update_note(addressed_note, author, skip_users) }.not_to change(skipped.todos, :count) + end + end + it 'updates cached counts when a todo is created' do issue = create(:issue, project: project, assignee: john_doe, author: author, description: mentions) diff --git a/spec/services/update_release_service_spec.rb b/spec/services/update_release_service_spec.rb index bba211089a8..69ed8de9c31 100644 --- a/spec/services/update_release_service_spec.rb +++ b/spec/services/update_release_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe UpdateReleaseService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:tag_name) { project.repository.tag_names.first } let(:description) { 'Awesome release!' } diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb index 5f79203701a..66f68650f81 100644 --- a/spec/services/users/create_service_spec.rb +++ b/spec/services/users/create_service_spec.rb @@ -61,6 +61,23 @@ describe Users::CreateService, services: true do ) end + context 'when the current_user is not persisted' do + let(:admin_user) { build(:admin) } + + it 'persists the given attributes and sets created_by_id to nil' 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 + ) + end + end + it 'user is not confirmed if skip_confirmation param is not present' do expect(service.execute).not_to be_confirmed end diff --git a/spec/services/users/destroy_spec.rb b/spec/services/users/destroy_spec.rb index 922e82445d0..9a28c03d968 100644 --- a/spec/services/users/destroy_spec.rb +++ b/spec/services/users/destroy_spec.rb @@ -5,7 +5,7 @@ describe Users::DestroyService, services: true do let!(:user) { create(:user) } let!(:admin) { create(:admin) } let!(:namespace) { create(:namespace, owner: user) } - let!(:project) { create(:project, namespace: namespace) } + let!(:project) { create(:empty_project, namespace: namespace) } let(:service) { described_class.new(admin) } context 'no options are given' do @@ -25,7 +25,7 @@ describe Users::DestroyService, services: true do end context "a deleted user's issues" do - let(:project) { create :project } + let(:project) { create(:project) } before do project.add_developer(user) diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb index 08733d6dcf1..b19374ef1a2 100644 --- a/spec/services/users/refresh_authorized_projects_service_spec.rb +++ b/spec/services/users/refresh_authorized_projects_service_spec.rb @@ -152,7 +152,7 @@ describe Users::RefreshAuthorizedProjectsService do context 'projects of groups the user is a member of' do let(:group) { create(:group) } - let!(:other_project) { create(:project, group: group) } + let!(:other_project) { create(:empty_project, group: group) } before do group.add_owner(user) @@ -166,7 +166,7 @@ describe Users::RefreshAuthorizedProjectsService do context 'projects of subgroups of groups the user is a member of' do let(:group) { create(:group) } let(:nested_group) { create(:group, parent: group) } - let!(:other_project) { create(:project, group: nested_group) } + let!(:other_project) { create(:empty_project, group: nested_group) } before do group.add_master(user) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5ab8f0d981a..4eb5b150af5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,7 +9,8 @@ require 'rspec/rails' require 'shoulda/matchers' require 'rspec/retry' -if ENV['RSPEC_PROFILING_POSTGRES_URL'] || ENV['RSPEC_PROFILING'] +if (ENV['RSPEC_PROFILING_POSTGRES_URL'] || ENV['RSPEC_PROFILING']) && + (!ENV.has_key?('CI') || ENV['CI_COMMIT_REF_NAME'] == 'master') require 'rspec_profiling/rspec' end diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index aa14709bc9c..b8ca8f22a3d 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -1,10 +1,11 @@ +# rubocop:disable Style/GlobalVars require 'capybara/rails' require 'capybara/rspec' require 'capybara/poltergeist' require 'capybara-screenshot/rspec' # Give CI some extra time -timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 30 : 10 +timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 60 : 30 Capybara.javascript_driver = :poltergeist Capybara.register_driver :poltergeist do |app| @@ -26,7 +27,10 @@ Capybara.ignore_hidden_elements = true Capybara::Screenshot.prune_strategy = :keep_last_run RSpec.configure do |config| - config.before(:suite) do - TestEnv.warm_asset_cache + config.before(:context, :js) do + next if $capybara_server_already_started + + TestEnv.eager_load_driver_server + $capybara_server_already_started = true end end diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb index d0fd2d52004..51f1015f43c 100644 --- a/spec/support/controllers/githubish_import_controller_shared_examples.rb +++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb @@ -228,5 +228,19 @@ shared_examples 'a GitHub-ish import controller: POST create' do post :create, { new_name: test_name, format: :js } end end + + context 'user has chosen a nested namespace and name for the project' do + let(:parent_namespace) { create(:namespace, name: 'foo', owner: user) } + let(:nested_namespace) { create(:namespace, name: 'bar', parent: parent_namespace, owner: user) } + let(:test_name) { 'test_name' } + + it 'takes the selected namespace and name' do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, test_name, nested_namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js } + end + end end end diff --git a/spec/support/notify_shared_examples.rb b/spec/support/notify_shared_examples.rb index 16a425f2ca2..76411065265 100644 --- a/spec/support/notify_shared_examples.rb +++ b/spec/support/notify_shared_examples.rb @@ -3,7 +3,7 @@ shared_context 'gitlab email notification' do let(:gitlab_sender) { Gitlab.config.gitlab.email_from } let(:gitlab_sender_reply_to) { Gitlab.config.gitlab.email_reply_to } let(:recipient) { create(:user, email: 'recipient@example.com') } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:new_user_address) { 'newguy@example.com' } before do diff --git a/spec/support/prometheus_helpers.rb b/spec/support/prometheus_helpers.rb index 4afdbd68304..cc79b11616a 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}"}) / count(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})) /1024/1024} + %{(sum(container_memory_usage_bytes{container_name!="POD",environment="#{environment_slug}"}) / count(container_memory_usage_bytes{container_name!="POD",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])) / count(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}) * 100} + %{sum(rate(container_cpu_usage_seconds_total{container_name!="POD",environment="#{environment_slug}"}[2m])) / count(container_cpu_usage_seconds_total{container_name!="POD",environment="#{environment_slug}"}) * 100} end def prometheus_query_url(prometheus_query) diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb index 0d526045012..6b1853c2364 100644 --- a/spec/support/select2_helper.rb +++ b/spec/support/select2_helper.rb @@ -22,4 +22,12 @@ module Select2Helper execute_script("$('#{selector}').select2('val', '#{value}').trigger('change');") end end + + def open_select2(selector) + execute_script("$('#{selector}').select2('open');") + end + + def scroll_select2_to_bottom(selector) + evaluate_script "$('#{selector}').scrollTop($('#{selector}')[0].scrollHeight); $('#{selector}');" + end end diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb index 81d06dc2a3d..ee492daee30 100644 --- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb +++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb @@ -2,7 +2,7 @@ # It can take a `default_params`. shared_examples 'new issuable record that supports slash commands' do - let!(:project) { create(:project) } + let!(:project) { create(:project, :repository) } let(:user) { create(:user).tap { |u| project.team << [u, :master] } } let(:assignee) { create(:user) } let!(:milestone) { create(:milestone, project: project) } diff --git a/spec/support/stored_repositories.rb b/spec/support/stored_repositories.rb new file mode 100644 index 00000000000..df18926d58c --- /dev/null +++ b/spec/support/stored_repositories.rb @@ -0,0 +1,5 @@ +RSpec.configure do |config| + config.before(:each, :repository) do + TestEnv.clean_test_path + end +end diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb index f40ee862df8..444adcc1906 100644 --- a/spec/support/stub_configuration.rb +++ b/spec/support/stub_configuration.rb @@ -21,6 +21,10 @@ module StubConfiguration allow(Gitlab.config.incoming_email).to receive_messages(messages) end + def stub_mattermost_setting(messages) + allow(Gitlab.config.mattermost).to receive_messages(messages) + end + private # Modifies stubbed messages to also stub possible predicate versions diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 648b0380f18..a19a35c2c0d 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -40,7 +40,7 @@ module TestEnv '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 @@ -61,9 +61,6 @@ module TestEnv clean_test_path - FileUtils.mkdir_p(repos_path) - FileUtils.mkdir_p(backup_path) - # Setup GitLab shell for test instance setup_gitlab_shell @@ -95,10 +92,14 @@ module TestEnv tmp_test_path = Rails.root.join('tmp', 'tests', '**') Dir[tmp_test_path].each do |entry| - unless File.basename(entry) =~ /\Agitlab-(shell|test|test-fork)\z/ + unless File.basename(entry) =~ /\Agitlab-(shell|test|test_bare|test-fork|test-fork_bare)\z/ FileUtils.rm_rf(entry) end end + + FileUtils.mkdir_p(repos_path) + FileUtils.mkdir_p(backup_path) + FileUtils.mkdir_p(pages_path) end def setup_gitlab_shell @@ -151,6 +152,10 @@ module TestEnv Gitlab.config.backup.path end + def pages_path + Gitlab.config.pages.path + end + def copy_forked_repo_with_submodules(project) base_repo_path = File.expand_path(forked_repo_path_bare) target_repo_path = File.expand_path(project.repository_storage_path + "/#{project.full_path}.git") @@ -164,16 +169,11 @@ module TestEnv # # Otherwise they'd be created by the first test, often timing out and # causing a transient test failure - def warm_asset_cache - return if warm_asset_cache? + def eager_load_driver_server return unless defined?(Capybara) - Capybara.current_session.driver.visit '/' - end - - def warm_asset_cache? - cache = Rails.root.join(*%w(tmp cache assets test)) - Dir.exist?(cache) && Dir.entries(cache).length > 2 + puts "Starting the Capybara driver server..." + Capybara.current_session.visit '/' end private diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 10458966cb9..daea0c6bb37 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -81,6 +81,10 @@ describe 'gitlab:app namespace rake task' do end # backup_restore task describe 'backup' do + before(:all) do + ENV['force'] = 'yes' + end + def tars_glob Dir.glob(File.join(Gitlab.config.backup.path, '*_gitlab_backup.tar')) end @@ -88,6 +92,9 @@ describe 'gitlab:app namespace rake task' do def create_backup FileUtils.rm tars_glob + # This reconnect makes our project fixture disappear, breaking the restore. Stub it out. + allow(ActiveRecord::Base.connection).to receive(:reconnect!) + # Redirect STDOUT and run the rake task orig_stdout = $stdout $stdout = StringIO.new @@ -109,7 +116,7 @@ describe 'gitlab:app namespace rake task' do end describe 'backup creation and deletion using custom_hooks' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user_backup_path) { "repositories/#{project.path_with_namespace}" } before(:each) do @@ -119,9 +126,6 @@ describe 'gitlab:app namespace rake task' do FileUtils.mkdir_p(path) FileUtils.touch(File.join(path, "dummy.txt")) - # We need to use the full path instead of the relative one - allow(Gitlab.config.gitlab_shell).to receive(:path).and_return(File.expand_path(Gitlab.config.gitlab_shell.path, Rails.root.to_s)) - ENV["SKIP"] = "db" create_backup end @@ -220,15 +224,15 @@ describe 'gitlab:app namespace rake task' do end context 'multiple repository storages' do - let(:project_a) { create(:project, repository_storage: 'default') } - let(:project_b) { create(:project, repository_storage: 'custom') } + let(:project_a) { create(:project, :repository, repository_storage: 'default') } + let(:project_b) { create(:project, :repository, repository_storage: 'custom') } before do FileUtils.mkdir('tmp/tests/default_storage') FileUtils.mkdir('tmp/tests/custom_storage') storages = { - 'default' => { 'path' => 'tmp/tests/default_storage' }, - 'custom' => { 'path' => 'tmp/tests/custom_storage' } + 'default' => { 'path' => Settings.absolute('tmp/tests/default_storage') }, + 'custom' => { 'path' => Settings.absolute('tmp/tests/custom_storage') } } allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) diff --git a/spec/views/projects/builds/_build.html.haml_spec.rb b/spec/views/projects/builds/_build.html.haml_spec.rb index e141a117731..751482cac42 100644 --- a/spec/views/projects/builds/_build.html.haml_spec.rb +++ b/spec/views/projects/builds/_build.html.haml_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'projects/ci/builds/_build' do include Devise::Test::ControllerHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_empty_pipeline, id: 1337, project: project, sha: project.commit.id) } let(:build) { create(:ci_build, pipeline: pipeline, stage: 'test', stage_idx: 1, name: 'rspec 0:2', status: :pending) } diff --git a/spec/views/projects/builds/_generic_commit_status.html.haml_spec.rb b/spec/views/projects/builds/_generic_commit_status.html.haml_spec.rb index 49b20e5b36b..dc2ffc9dc47 100644 --- a/spec/views/projects/builds/_generic_commit_status.html.haml_spec.rb +++ b/spec/views/projects/builds/_generic_commit_status.html.haml_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'projects/generic_commit_statuses/_generic_commit_status.html.haml' do include Devise::Test::ControllerHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_empty_pipeline, id: 1337, project: project, sha: project.commit.id) } let(:generic_commit_status) { create(:generic_commit_status, pipeline: pipeline, stage: 'external', name: 'jenkins', stage_idx: 3) } diff --git a/spec/views/projects/builds/show.html.haml_spec.rb b/spec/views/projects/builds/show.html.haml_spec.rb index ec78ac30593..55b64808fb3 100644 --- a/spec/views/projects/builds/show.html.haml_spec.rb +++ b/spec/views/projects/builds/show.html.haml_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe 'projects/builds/show', :view do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:build) { create(:ci_build, pipeline: pipeline) } let(:pipeline) do diff --git a/spec/views/projects/commit/_commit_box.html.haml_spec.rb b/spec/views/projects/commit/_commit_box.html.haml_spec.rb index 8bc344bfbf6..cec87dcecc8 100644 --- a/spec/views/projects/commit/_commit_box.html.haml_spec.rb +++ b/spec/views/projects/commit/_commit_box.html.haml_spec.rb @@ -4,7 +4,7 @@ describe 'projects/commit/_commit_box.html.haml' do include Devise::Test::ControllerHelpers let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do assign(:project, project) diff --git a/spec/views/projects/issues/_related_branches.html.haml_spec.rb b/spec/views/projects/issues/_related_branches.html.haml_spec.rb index 889d9a38887..8c845251765 100644 --- a/spec/views/projects/issues/_related_branches.html.haml_spec.rb +++ b/spec/views/projects/issues/_related_branches.html.haml_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'projects/issues/_related_branches' do include Devise::Test::ControllerHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:branch) { project.repository.find_branch('feature') } let!(:pipeline) { create(:ci_pipeline, project: project, sha: branch.dereferenced_target.id, ref: 'feature') } diff --git a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb index 6f70b3daf8e..4052dbf8df3 100644 --- a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb +++ b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb @@ -4,11 +4,8 @@ describe 'projects/merge_requests/show/_commits.html.haml' do include Devise::Test::ControllerHelpers let(:user) { create(:user) } - let(:target_project) { create(:project) } - - let(:source_project) do - create(:project, forked_from_project: target_project) - end + let(:target_project) { create(:project, :repository) } + let(:source_project) { create(:project, :repository, forked_from_project: target_project) } let(:merge_request) do create(:merge_request, :simple, diff --git a/spec/views/projects/merge_requests/edit.html.haml_spec.rb b/spec/views/projects/merge_requests/edit.html.haml_spec.rb index 3650b22c389..69c7d0cbf28 100644 --- a/spec/views/projects/merge_requests/edit.html.haml_spec.rb +++ b/spec/views/projects/merge_requests/edit.html.haml_spec.rb @@ -4,8 +4,8 @@ describe 'projects/merge_requests/edit.html.haml' do include Devise::Test::ControllerHelpers let(:user) { create(:user) } - let(:project) { create(:project) } - let(:fork_project) { create(:project, forked_from_project: project) } + let(:project) { create(:project, :repository) } + let(:fork_project) { create(:project, :repository, forked_from_project: project) } let(:unlink_project) { Projects::UnlinkForkService.new(fork_project, user) } let(:milestone) { create(:milestone, project: project) } diff --git a/spec/views/projects/merge_requests/show.html.haml_spec.rb b/spec/views/projects/merge_requests/show.html.haml_spec.rb index 7f123b15194..dc2fcc3e715 100644 --- a/spec/views/projects/merge_requests/show.html.haml_spec.rb +++ b/spec/views/projects/merge_requests/show.html.haml_spec.rb @@ -4,8 +4,8 @@ describe 'projects/merge_requests/show.html.haml' do include Devise::Test::ControllerHelpers let(:user) { create(:user) } - let(:project) { create(:project) } - let(:fork_project) { create(:project, forked_from_project: project) } + let(:project) { create(:project, :repository) } + let(:fork_project) { create(:project, :repository, forked_from_project: project) } let(:unlink_project) { Projects::UnlinkForkService.new(fork_project, user) } let(:note) { create(:note_on_merge_request, project: project, noteable: closed_merge_request) } diff --git a/spec/views/projects/pipelines/_stage.html.haml_spec.rb b/spec/views/projects/pipelines/_stage.html.haml_spec.rb index 65f9d0125e6..10095ad7694 100644 --- a/spec/views/projects/pipelines/_stage.html.haml_spec.rb +++ b/spec/views/projects/pipelines/_stage.html.haml_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe 'projects/pipelines/_stage', :view do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_pipeline, project: project) } let(:stage) { build(:ci_stage, pipeline: pipeline) } diff --git a/spec/views/projects/pipelines/show.html.haml_spec.rb b/spec/views/projects/pipelines/show.html.haml_spec.rb index e4aeaeca508..dca78dec6df 100644 --- a/spec/views/projects/pipelines/show.html.haml_spec.rb +++ b/spec/views/projects/pipelines/show.html.haml_spec.rb @@ -4,7 +4,7 @@ describe 'projects/pipelines/show' do include Devise::Test::ControllerHelpers let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, user: user) } before do diff --git a/spec/views/projects/tree/show.html.haml_spec.rb b/spec/views/projects/tree/show.html.haml_spec.rb index c381b1a86df..900f8d4732f 100644 --- a/spec/views/projects/tree/show.html.haml_spec.rb +++ b/spec/views/projects/tree/show.html.haml_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'projects/tree/show' do include Devise::Test::ControllerHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:repository) { project.repository } before do diff --git a/spec/workers/delete_merged_branches_worker_spec.rb b/spec/workers/delete_merged_branches_worker_spec.rb index d9497bd486c..39009d9e4b2 100644 --- a/spec/workers/delete_merged_branches_worker_spec.rb +++ b/spec/workers/delete_merged_branches_worker_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe DeleteMergedBranchesWorker do subject(:worker) { described_class.new } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } describe "#perform" do it "calls DeleteMergedBranchesService" do diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb index f27e413f7b8..8cf2b888f9a 100644 --- a/spec/workers/emails_on_push_worker_spec.rb +++ b/spec/workers/emails_on_push_worker_spec.rb @@ -5,7 +5,7 @@ describe EmailsOnPushWorker do include EmailHelpers include EmailSpec::Matchers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:data) { Gitlab::DataBuilder::Push.build_sample(project, user) } let(:recipients) { user.email } diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index a60af574a08..029f35512e0 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -3,7 +3,7 @@ require 'fileutils' require 'spec_helper' describe GitGarbageCollectWorker do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:shell) { Gitlab::Shell.new } subject { GitGarbageCollectWorker.new } diff --git a/spec/workers/group_destroy_worker_spec.rb b/spec/workers/group_destroy_worker_spec.rb index 4e4eaf9b2f7..1ff5a3b9034 100644 --- a/spec/workers/group_destroy_worker_spec.rb +++ b/spec/workers/group_destroy_worker_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe GroupDestroyWorker do let(:group) { create(:group) } let(:user) { create(:admin) } - let!(:project) { create(:project, namespace: group) } + let!(:project) { create(:empty_project, namespace: group) } subject { GroupDestroyWorker.new } diff --git a/spec/workers/pipeline_metrics_worker_spec.rb b/spec/workers/pipeline_metrics_worker_spec.rb index 2d47d93acec..5dbc0da95c2 100644 --- a/spec/workers/pipeline_metrics_worker_spec.rb +++ b/spec/workers/pipeline_metrics_worker_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe PipelineMetricsWorker do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) } let(:pipeline) do diff --git a/spec/workers/pipeline_notification_worker_spec.rb b/spec/workers/pipeline_notification_worker_spec.rb index 603ae52ed1e..5a7ce2e08c4 100644 --- a/spec/workers/pipeline_notification_worker_spec.rb +++ b/spec/workers/pipeline_notification_worker_spec.rb @@ -11,7 +11,7 @@ describe PipelineNotificationWorker do status: status) end - let(:project) { create(:project, public_builds: false) } + let(:project) { create(:project, :repository, public_builds: false) } let(:user) { create(:user) } let(:pusher) { user } let(:watcher) { pusher } diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index 7bcb5521202..a2a559a2369 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -4,7 +4,7 @@ describe PostReceive do let(:changes) { "123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag" } let(:wrongly_encoded_changes) { changes.encode("ISO-8859-1").force_encoding("UTF-8") } let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:key) { create(:key, user: project.owner) } let(:key_id) { key.shell_id } diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb index 75c7fc1efd2..1c383d0514d 100644 --- a/spec/workers/process_commit_worker_spec.rb +++ b/spec/workers/process_commit_worker_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe ProcessCommitWorker do let(:worker) { described_class.new } let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:issue) { create(:issue, project: project, author: user) } let(:commit) { project.commit } diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb index f4f63b57a5f..c23ffdf99c0 100644 --- a/spec/workers/project_cache_worker_spec.rb +++ b/spec/workers/project_cache_worker_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe ProjectCacheWorker do let(:worker) { described_class.new } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:statistics) { project.statistics } describe '#perform' do diff --git a/spec/workers/project_destroy_worker_spec.rb b/spec/workers/project_destroy_worker_spec.rb index 1f4c39eb64a..0ab42f99510 100644 --- a/spec/workers/project_destroy_worker_spec.rb +++ b/spec/workers/project_destroy_worker_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe ProjectDestroyWorker do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:path) { project.repository.path_to_repo } subject { ProjectDestroyWorker.new } diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb index 27727d6abf9..bcd97a4f6ef 100644 --- a/spec/workers/repository_check/batch_worker_spec.rb +++ b/spec/workers/repository_check/batch_worker_spec.rb @@ -4,7 +4,7 @@ describe RepositoryCheck::BatchWorker do subject { described_class.new } it 'prefers projects that have never been checked' do - projects = create_list(:project, 3, created_at: 1.week.ago) + projects = create_list(:empty_project, 3, created_at: 1.week.ago) projects[0].update_column(:last_repository_check_at, 4.months.ago) projects[2].update_column(:last_repository_check_at, 3.months.ago) @@ -12,7 +12,7 @@ describe RepositoryCheck::BatchWorker do end it 'sorts projects by last_repository_check_at' do - projects = create_list(:project, 3, created_at: 1.week.ago) + projects = create_list(:empty_project, 3, created_at: 1.week.ago) projects[0].update_column(:last_repository_check_at, 2.months.ago) projects[1].update_column(:last_repository_check_at, 4.months.ago) projects[2].update_column(:last_repository_check_at, 3.months.ago) @@ -21,7 +21,7 @@ describe RepositoryCheck::BatchWorker do end it 'excludes projects that were checked recently' do - projects = create_list(:project, 3, created_at: 1.week.ago) + projects = create_list(:empty_project, 3, created_at: 1.week.ago) projects[0].update_column(:last_repository_check_at, 2.days.ago) projects[1].update_column(:last_repository_check_at, 2.months.ago) projects[2].update_column(:last_repository_check_at, 3.days.ago) @@ -40,7 +40,7 @@ describe RepositoryCheck::BatchWorker do it 'skips projects created less than 24 hours ago' do project = create(:empty_project) project.update_column(:created_at, 23.hours.ago) - + expect(subject.perform).to eq([]) end end diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index 87521ae408e..7d6a2db2972 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe RepositoryForkWorker do - let(:project) { create(:project) } - let(:fork_project) { create(:project, forked_from_project: project) } + let(:project) { create(:project, :repository) } + let(:fork_project) { create(:project, :repository, forked_from_project: project) } let(:shell) { Gitlab::Shell.new } subject { RepositoryForkWorker.new } diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb index c42f3147b7a..fbb22439f33 100644 --- a/spec/workers/repository_import_worker_spec.rb +++ b/spec/workers/repository_import_worker_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe RepositoryImportWorker do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } subject { described_class.new } diff --git a/spec/workers/update_merge_requests_worker_spec.rb b/spec/workers/update_merge_requests_worker_spec.rb index 262d6e5a9ab..558ff9109ec 100644 --- a/spec/workers/update_merge_requests_worker_spec.rb +++ b/spec/workers/update_merge_requests_worker_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe UpdateMergeRequestsWorker do include RepoHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } subject { described_class.new } |