diff options
Diffstat (limited to 'spec')
44 files changed, 904 insertions, 110 deletions
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb index a95cfc5c6be..ebd2d0e092b 100644 --- a/spec/controllers/projects/commit_controller_spec.rb +++ b/spec/controllers/projects/commit_controller_spec.rb @@ -4,7 +4,6 @@ describe Projects::CommitController do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:commit) { project.commit("master") } - let(:pipeline) { create(:ci_pipeline, project: project, commit: commit) } let(:master_pickable_sha) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' } let(:master_pickable_commit) { project.commit(master_pickable_sha) } @@ -322,11 +321,26 @@ describe Projects::CommitController do end context 'when the commit exists' do - context 'when the commit has one or more pipelines' do - it 'shows pipelines' do - get_pipelines(id: commit.id) + context 'when the commit has pipelines' do + before do + create(:ci_pipeline, project: project, sha: commit.id) + end + + context 'when rendering a HTML format' do + it 'shows pipelines' do + get_pipelines(id: commit.id) + + expect(response).to be_ok + end + end - expect(response).to be_ok + context 'when rendering a JSON format' do + it 'responds with serialized pipelines' do + get_pipelines(id: commit.id, format: :json) + + expect(response).to be_ok + expect(JSON.parse(response.body)).not_to be_empty + end end end end diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index ec6cea5c0f4..3e0326dd47d 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -112,4 +112,49 @@ describe Projects::LabelsController do post :toggle_subscription, namespace_id: project.namespace.to_param, project_id: project.to_param, id: label.to_param end end + + describe 'POST #promote' do + let!(:promoted_label_name) { "Promoted Label" } + let!(:label_1) { create(:label, title: promoted_label_name, project: project) } + + context 'not group owner' do + it 'denies access' do + post :promote, namespace_id: project.namespace.to_param, project_id: project.to_param, id: label_1.to_param + + expect(response).to have_http_status(404) + end + end + + context 'group owner' do + before do + GroupMember.add_users_to_group(group, [user], :owner) + end + + it 'gives access' do + post :promote, namespace_id: project.namespace.to_param, project_id: project.to_param, id: label_1.to_param + + expect(response).to redirect_to(namespace_project_labels_path) + end + + it 'promotes the label' do + post :promote, namespace_id: project.namespace.to_param, project_id: project.to_param, id: label_1.to_param + + expect(Label.where(id: label_1.id)).to be_empty + expect(GroupLabel.find_by(title: promoted_label_name)).not_to be_nil + end + + context 'service raising InvalidRecord' do + before do + expect_any_instance_of(Labels::PromoteService).to receive(:execute) do |label| + raise ActiveRecord::RecordInvalid.new(label_1) + end + end + + it 'returns to label list' do + post :promote, namespace_id: project.namespace.to_param, project_id: project.to_param, id: label_1.to_param + expect(response).to redirect_to(namespace_project_labels_path) + end + end + end + end end diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb index 2ae635a1244..cae733f0cfb 100644 --- a/spec/controllers/projects/mattermosts_controller_spec.rb +++ b/spec/controllers/projects/mattermosts_controller_spec.rb @@ -13,13 +13,13 @@ describe Projects::MattermostsController do before do allow_any_instance_of(MattermostSlashCommandsService). to receive(:list_teams).and_return([]) + end + it 'accepts the request' do get(:new, namespace_id: project.namespace.to_param, project_id: project.to_param) - end - it 'accepts the request' do expect(response).to have_http_status(200) end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 7ea3ea4f376..e019541e74f 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Projects::MergeRequestsController do + include ApiHelpers + let(:project) { create(:project) } let(:user) { create(:user) } let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } @@ -455,7 +457,7 @@ describe Projects::MergeRequestsController do it 'renders the diffs template to a string' do expect(response).to render_template('projects/merge_requests/show/_diffs') - expect(JSON.parse(response.body)).to have_key('html') + expect(json_response).to have_key('html') end end @@ -494,7 +496,7 @@ describe Projects::MergeRequestsController do it 'renders the diffs template to a string' do expect(response).to render_template('projects/merge_requests/show/_diffs') - expect(JSON.parse(response.body)).to have_key('html') + expect(json_response).to have_key('html') end end end @@ -662,18 +664,45 @@ describe Projects::MergeRequestsController do go format: 'json' expect(response).to render_template('projects/merge_requests/show/_commits') - expect(JSON.parse(response.body)).to have_key('html') + expect(json_response).to have_key('html') end end end describe 'GET pipelines' do - it_behaves_like "loads labels", :pipelines + before do + create(:ci_pipeline, project: merge_request.source_project, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha) + end + + context 'when using HTML format' do + it_behaves_like "loads labels", :pipelines + end + + context 'when using JSON format' do + before do + get :pipelines, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: merge_request.iid, + format: :json + end + + it 'responds with a rendered HTML partial' do + expect(response) + .to render_template('projects/merge_requests/show/_pipelines') + expect(json_response).to have_key 'html' + end + + it 'responds with serialized pipelines' do + expect(json_response).to have_key 'pipelines' + expect(json_response['pipelines']).not_to be_empty + end + end end describe 'GET conflicts' do - let(:json_response) { JSON.parse(response.body) } - context 'when the conflicts cannot be resolved in the UI' do before do allow_any_instance_of(Gitlab::Conflict::Parser). @@ -770,8 +799,6 @@ describe Projects::MergeRequestsController do end describe 'GET conflict_for_path' do - let(:json_response) { JSON.parse(response.body) } - def conflict_for_path(path) get :conflict_for_path, namespace_id: merge_request_with_conflicts.project.namespace.to_param, @@ -826,7 +853,6 @@ describe Projects::MergeRequestsController do end context 'POST resolve_conflicts' do - let(:json_response) { JSON.parse(response.body) } let!(:original_head_sha) { merge_request_with_conflicts.diff_head_sha } def resolve_conflicts(files) @@ -1024,7 +1050,6 @@ describe Projects::MergeRequestsController do let!(:forked) { create(:project) } let!(:environment) { create(:environment, project: forked) } let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') } - let(:json_response) { JSON.parse(response.body) } let(:admin) { create(:admin) } let(:merge_request) do diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 188d33e8ef4..c28bb0dcdae 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -141,6 +141,36 @@ describe 'Issue Boards', feature: true, js: true do end end end + + it 'resets assignee dropdown' do + page.within(first('.board')) do + first('.card').click + end + + page.within('.assignee') do + click_link 'Edit' + + wait_for_ajax + + page.within('.dropdown-menu-user') do + click_link user.name + + wait_for_vue_resource + end + + expect(page).to have_content(user.name) + end + + page.within(first('.board')) do + find('.card:nth-child(2)').click + end + + page.within('.assignee') do + click_link 'Edit' + + expect(page).not_to have_selector('.is-active') + end + end end context 'milestone' do diff --git a/spec/features/issues/new_branch_button_spec.rb b/spec/features/issues/new_branch_button_spec.rb index a4d3053d10c..c0ab42c6822 100644 --- a/spec/features/issues/new_branch_button_spec.rb +++ b/spec/features/issues/new_branch_button_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Start new branch from an issue', feature: true do +feature 'Start new branch from an issue', feature: true, js: true do let!(:project) { create(:project) } let!(:issue) { create(:issue, project: project) } let!(:user) { create(:user)} @@ -11,7 +11,7 @@ feature 'Start new branch from an issue', feature: true do login_as(user) end - it 'shows the new branch button', js: true do + it 'shows the new branch button' do visit namespace_project_issue_path(project.namespace, project, issue) expect(page).to have_css('#new-branch .available') @@ -34,16 +34,26 @@ feature 'Start new branch from an issue', feature: true do visit namespace_project_issue_path(project.namespace, project, issue) end - it "hides the new branch button", js: true do + it "hides the new branch button" do expect(page).to have_css('#new-branch .unavailable') expect(page).not_to have_css('#new-branch .available') expect(page).to have_content /1 Related Merge Request/ end end + + context 'when issue is confidential' do + it 'hides the new branch button' do + issue = create(:issue, :confidential, project: project) + + visit namespace_project_issue_path(project.namespace, project, issue) + + expect(page).not_to have_css('#new-branch') + end + end end - context "for visiters" do - it 'shows no buttons', js: true do + context 'for visitors' do + it 'shows no buttons' do visit namespace_project_issue_path(project.namespace, project, issue) expect(page).not_to have_css('#new-branch') diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index 76bcfbe523a..ab7d89306db 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -25,6 +25,11 @@ feature 'Login', feature: true do expect(current_path).to eq root_path end + + it 'does not show flash messages when login page' do + visit root_path + expect(page).not_to have_content('You need to sign in or sign up before continuing.') + end end describe 'with two-factor authentication' do diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb index b13674b4db9..2582a540240 100644 --- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb +++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb @@ -11,7 +11,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do it_behaves_like 'issuable record that supports slash commands in its description and notes', :merge_request do let(:issuable) { create(:merge_request, source_project: project) } - let(:new_url_opts) { { merge_request: { source_branch: 'feature' } } } + let(:new_url_opts) { { merge_request: { source_branch: 'feature', target_branch: 'master' } } } end describe 'merge-request-only commands' do diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb new file mode 100644 index 00000000000..7d1805f5001 --- /dev/null +++ b/spec/features/merge_requests/widget_spec.rb @@ -0,0 +1,34 @@ +require 'rails_helper' + +describe 'Merge request', :feature, :js do + include WaitForAjax + + let(:project) { create(:project) } + let(:user) { create(:user) } + + before do + project.team << [user, :master] + login_as(user) + + visit new_namespace_project_merge_request_path( + project.namespace, + project, + merge_request: { + source_project_id: project.id, + target_project_id: project.id, + source_branch: 'feature', + target_branch: 'master' + } + ) + end + + it 'shows widget status after creating new merge request' do + click_button 'Submit merge request' + + expect(find('.mr-state-widget')).to have_content('Checking ability to merge automatically') + + wait_for_ajax + + expect(page).to have_selector('.accept_merge_request') + end +end diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb index 52d08982c7a..16dddb2a86b 100644 --- a/spec/features/projects/import_export/export_file_spec.rb +++ b/spec/features/projects/import_export/export_file_spec.rb @@ -74,6 +74,9 @@ feature 'Import/Export - project export integration test', feature: true, js: tr Otherwise, please add the exception to +safe_list+ in CURRENT_SPEC using #{sensitive_word} as the key and the correspondent hash or model as the value. + Also, if the attribute is a generated unique token, please add it to RelationFactory::TOKEN_RESET_MODELS if it needs to be + reset (to prevent duplicate column problems while importing to the same instance). + IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file} CURRENT_SPEC: #{__FILE__} MSG diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz Binary files differindex 7655c2b351f..20cdfbae24f 100644 --- a/spec/features/projects/import_export/test_project_export.tar.gz +++ b/spec/features/projects/import_export/test_project_export.tar.gz diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb index c9fa8315e79..97ce9cdfd87 100644 --- a/spec/features/projects/labels/update_prioritization_spec.rb +++ b/spec/features/projects/labels/update_prioritization_spec.rb @@ -20,7 +20,7 @@ feature 'Prioritize labels', feature: true do scenario 'user can prioritize a group label', js: true do visit namespace_project_labels_path(project.namespace, project) - expect(page).to have_content('No prioritized labels yet') + expect(page).to have_content('Star labels to start sorting by priority') page.within('.other-labels') do all('.js-toggle-priority')[1].click @@ -29,7 +29,7 @@ feature 'Prioritize labels', feature: true do end page.within('.prioritized-labels') do - expect(page).not_to have_content('No prioritized labels yet') + expect(page).not_to have_content('Star labels to start sorting by priority') expect(page).to have_content('feature') end end @@ -55,7 +55,7 @@ feature 'Prioritize labels', feature: true do scenario 'user can prioritize a project label', js: true do visit namespace_project_labels_path(project.namespace, project) - expect(page).to have_content('No prioritized labels yet') + expect(page).to have_content('Star labels to start sorting by priority') page.within('.other-labels') do first('.js-toggle-priority').click @@ -64,7 +64,7 @@ feature 'Prioritize labels', feature: true do end page.within('.prioritized-labels') do - expect(page).not_to have_content('No prioritized labels yet') + expect(page).not_to have_content('Star labels to start sorting by priority') expect(page).to have_content('bug') end end diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index abfc46601fb..b56e562b2b6 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -1,11 +1,13 @@ require "spec_helper" feature "New project", feature: true do - context "Visibility level selector" do - let(:user) { create(:admin) } + let(:user) { create(:admin) } - before { login_as(user) } + before do + login_as(user) + end + context "Visibility level selector" do Gitlab::VisibilityLevel.options.each do |key, level| it "sets selector to #{key}" do stub_application_setting(default_project_visibility: level) @@ -16,4 +18,16 @@ feature "New project", feature: true do end end end + + context 'Import project options' do + before do + visit new_project_path + end + + it 'does not autocomplete sensitive git repo URL' do + autocomplete = find('#project_import_url')['autocomplete'] + + expect(autocomplete).to eq('off') + end + end end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index e673ece37c3..917b545e98b 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -66,8 +66,8 @@ describe 'Pipeline', :feature, :js do context 'when pipeline has running builds' do it 'shows a running icon and a cancel action for the running build' do page.within('#ci-badge-deploy') do - expect(page).to have_selector('.ci-status-icon-running') - expect(page).to have_selector('.ci-action-icon-container .fa-ban') + expect(page).to have_selector('.js-ci-status-icon-running') + expect(page).to have_selector('.js-icon-action-cancel') expect(page).to have_content('deploy') end end @@ -82,12 +82,12 @@ describe 'Pipeline', :feature, :js do context 'when pipeline has successful builds' do it 'shows the success icon and a retry action for the successful build' do page.within('#ci-badge-build') do - expect(page).to have_selector('.ci-status-icon-success') + expect(page).to have_selector('.js-ci-status-icon-success') expect(page).to have_content('build') end page.within('#ci-badge-build .ci-action-icon-container') do - expect(page).to have_selector('.ci-action-icon-container .fa-refresh') + expect(page).to have_selector('.js-icon-action-retry') end end @@ -101,12 +101,12 @@ describe 'Pipeline', :feature, :js do context 'when pipeline has failed builds' do it 'shows the failed icon and a retry action for the failed build' do page.within('#ci-badge-test') do - expect(page).to have_selector('.ci-status-icon-failed') + expect(page).to have_selector('.js-ci-status-icon-failed') expect(page).to have_content('test') end page.within('#ci-badge-test .ci-action-icon-container') do - expect(page).to have_selector('.ci-action-icon-container .fa-refresh') + expect(page).to have_selector('.js-icon-action-retry') end end @@ -120,12 +120,12 @@ describe 'Pipeline', :feature, :js do context 'when pipeline has manual builds' do it 'shows the skipped icon and a play action for the manual build' do page.within('#ci-badge-manual-build') do - expect(page).to have_selector('.ci-status-icon-manual') + expect(page).to have_selector('.js-ci-status-icon-manual') expect(page).to have_content('manual') end page.within('#ci-badge-manual-build .ci-action-icon-container') do - expect(page).to have_selector('.ci-action-icon-container .fa-play') + expect(page).to have_selector('.js-icon-action-play') end end @@ -138,7 +138,7 @@ describe 'Pipeline', :feature, :js do context 'when pipeline has external build' do it 'shows the success icon and the generic comit status build' do - expect(page).to have_selector('.ci-status-icon-success') + expect(page).to have_selector('.js-ci-status-icon-success') expect(page).to have_content('jenkins') expect(page).to have_link('jenkins', href: 'http://gitlab.com/status') end diff --git a/spec/features/projects/services/mattermost_slash_command_spec.rb b/spec/features/projects/services/mattermost_slash_command_spec.rb index 86a07b2c679..042a1ccab51 100644 --- a/spec/features/projects/services/mattermost_slash_command_spec.rb +++ b/spec/features/projects/services/mattermost_slash_command_spec.rb @@ -99,6 +99,15 @@ feature 'Setup Mattermost slash commands', feature: true do expect(select_element.all('option').count).to eq(3) end + it 'shows an error alert with the error message if there is an error requesting teams' do + allow_any_instance_of(MattermostSlashCommandsService).to receive(:list_teams) { [[], 'test mattermost error message'] } + + click_link 'Add to Mattermost' + + expect(page).to have_selector('.alert') + expect(page).to have_content('test mattermost error message') + end + def stub_teams(count: 0) teams = create_teams(count) diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb index 3850e930b6d..1b352be9331 100644 --- a/spec/features/todos/todos_spec.rb +++ b/spec/features/todos/todos_spec.rb @@ -171,7 +171,7 @@ describe 'Dashboard Todos', feature: true do it 'links to the pipelines for the merge request' do href = pipelines_namespace_project_merge_request_path(project.namespace, project, todo.target) - expect(page).to have_link "merge request #{todo.target.to_reference}", href + expect(page).to have_link "merge request #{todo.target.to_reference(full: true)}", href end end end diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index a4f08dc4af0..df71680e44c 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -115,6 +115,46 @@ describe IssuablesHelper do end end + describe '#issuable_reference' do + context 'when show_full_reference truthy' do + it 'display issuable full reference' do + assign(:show_full_reference, true) + issue = build_stubbed(:issue) + + expect(helper.issuable_reference(issue)).to eql(issue.to_reference(full: true)) + end + end + + context 'when show_full_reference falsey' do + context 'when @group present' do + it 'display issuable reference to @group' do + project = build_stubbed(:project) + + assign(:show_full_reference, nil) + assign(:group, project.namespace) + + issue = build_stubbed(:issue) + + expect(helper.issuable_reference(issue)).to eql(issue.to_reference(project.namespace)) + end + end + + context 'when @project present' do + it 'display issuable reference to @project' do + project = build_stubbed(:project) + + assign(:show_full_reference, nil) + assign(:group, nil) + assign(:project, project) + + issue = build_stubbed(:issue) + + expect(helper.issuable_reference(issue)).to eql(issue.to_reference(project)) + end + end + end + end + describe '#issuable_filter_present?' do it 'returns true when any key is present' do allow(helper).to receive(:params).and_return( diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index e51720f10ed..b7e547dc1f5 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -41,6 +41,11 @@ describe SearchHelper do expect(search_autocomplete_opts("gro").size).to eq(1) end + it "includes nested group" do + create(:group, :nested, name: 'foo').add_owner(user) + expect(search_autocomplete_opts('foo').size).to eq(1) + end + it "includes the user's projects" do project = create(:empty_project, namespace: create(:namespace, owner: user)) expect(search_autocomplete_opts(project.name).size).to eq(1) diff --git a/spec/javascripts/gl_form_spec.js.es6 b/spec/javascripts/gl_form_spec.js.es6 new file mode 100644 index 00000000000..71d6e2a7e22 --- /dev/null +++ b/spec/javascripts/gl_form_spec.js.es6 @@ -0,0 +1,123 @@ +/* global autosize */ + +window.autosize = require('vendor/autosize'); +require('~/gl_form'); +require('~/lib/utils/text_utility'); +require('~/lib/utils/common_utils'); + +describe('GLForm', () => { + const global = window.gl || (window.gl = {}); + const GLForm = global.GLForm; + + it('should be defined in the global scope', () => { + expect(GLForm).toBeDefined(); + }); + + describe('when instantiated', function () { + beforeEach((done) => { + this.form = $('<form class="gfm-form"><textarea class="js-gfm-input"></form>'); + this.textarea = this.form.find('textarea'); + spyOn($.prototype, 'off').and.returnValue(this.textarea); + spyOn($.prototype, 'on').and.returnValue(this.textarea); + spyOn($.prototype, 'css'); + spyOn(window, 'autosize'); + + this.glForm = new GLForm(this.form); + setTimeout(() => { + $.prototype.off.calls.reset(); + $.prototype.on.calls.reset(); + $.prototype.css.calls.reset(); + autosize.calls.reset(); + done(); + }); + }); + + describe('.setupAutosize', () => { + beforeEach((done) => { + this.glForm.setupAutosize(); + setTimeout(() => { + done(); + }); + }); + + it('should register an autosize event handler on the textarea', () => { + expect($.prototype.off).toHaveBeenCalledWith('autosize:resized'); + expect($.prototype.on).toHaveBeenCalledWith('autosize:resized', jasmine.any(Function)); + }); + + it('should register a mouseup event handler on the textarea', () => { + expect($.prototype.off).toHaveBeenCalledWith('mouseup.autosize'); + expect($.prototype.on).toHaveBeenCalledWith('mouseup.autosize', jasmine.any(Function)); + }); + + it('should autosize the textarea', () => { + expect(autosize).toHaveBeenCalledWith(jasmine.any(Object)); + }); + + it('should set the resize css property to vertical', () => { + expect($.prototype.css).toHaveBeenCalledWith('resize', 'vertical'); + }); + }); + + describe('.setHeightData', () => { + beforeEach(() => { + spyOn($.prototype, 'data'); + spyOn($.prototype, 'outerHeight').and.returnValue(200); + this.glForm.setHeightData(); + }); + + it('should set the height data attribute', () => { + expect($.prototype.data).toHaveBeenCalledWith('height', 200); + }); + + it('should call outerHeight', () => { + expect($.prototype.outerHeight).toHaveBeenCalled(); + }); + }); + + describe('.destroyAutosize', () => { + describe('when called', () => { + beforeEach(() => { + spyOn($.prototype, 'data'); + spyOn($.prototype, 'outerHeight').and.returnValue(200); + spyOn(window, 'outerHeight').and.returnValue(400); + spyOn(autosize, 'destroy'); + + this.glForm.destroyAutosize(); + }); + + it('should call outerHeight', () => { + expect($.prototype.outerHeight).toHaveBeenCalled(); + }); + + it('should get data-height attribute', () => { + expect($.prototype.data).toHaveBeenCalledWith('height'); + }); + + it('should call autosize destroy', () => { + expect(autosize.destroy).toHaveBeenCalledWith(this.textarea); + }); + + it('should set the data-height attribute', () => { + expect($.prototype.data).toHaveBeenCalledWith('height', 200); + }); + + it('should set the outerHeight', () => { + expect($.prototype.outerHeight).toHaveBeenCalledWith(200); + }); + + it('should set the css', () => { + expect($.prototype.css).toHaveBeenCalledWith('max-height', window.outerHeight); + }); + }); + + it('should return undefined if the data-height equals the outerHeight', () => { + spyOn($.prototype, 'outerHeight').and.returnValue(200); + spyOn($.prototype, 'data').and.returnValue(200); + spyOn(autosize, 'destroy'); + expect(this.glForm.destroyAutosize()).toBeUndefined(); + expect(autosize.destroy).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb index b3c07347de1..8ad9b7cdf07 100644 --- a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb @@ -62,7 +62,7 @@ describe Gitlab::Ci::Status::Build::Cancelable do end describe '#action_icon' do - it { expect(subject.action_icon).to eq 'ban' } + it { expect(subject.action_icon).to eq 'icon_action_cancel' } end describe '#action_title' do diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb index f1b50a59ce6..f3e72ea1796 100644 --- a/spec/lib/gitlab/ci/status/build/play_spec.rb +++ b/spec/lib/gitlab/ci/status/build/play_spec.rb @@ -44,7 +44,7 @@ describe Gitlab::Ci::Status::Build::Play do end describe '#action_icon' do - it { expect(subject.action_icon).to eq 'play' } + it { expect(subject.action_icon).to eq 'icon_action_play' } end describe '#action_title' do diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb index 62036f8ec5d..2db0f8d29bd 100644 --- a/spec/lib/gitlab/ci/status/build/retryable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb @@ -62,7 +62,7 @@ describe Gitlab::Ci::Status::Build::Retryable do end describe '#action_icon' do - it { expect(subject.action_icon).to eq 'refresh' } + it { expect(subject.action_icon).to eq 'icon_action_retry' } end describe '#action_title' do diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb index 597e02e86e4..41c2b624774 100644 --- a/spec/lib/gitlab/ci/status/build/stop_spec.rb +++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb @@ -46,7 +46,7 @@ describe Gitlab::Ci::Status::Build::Stop do end describe '#action_icon' do - it { expect(subject.action_icon).to eq 'stop' } + it { expect(subject.action_icon).to eq 'icon_action_stop' } end describe '#action_title' do diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index 2c0750c3377..2e9f60432b4 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -6981,11 +6981,16 @@ ] } ], - "variables": [ - - ], "triggers": [ - + { + "id": 123, + "token": "cdbfasdf44a5958c83654733449e585", + "project_id": null, + "deleted_at": null, + "created_at": "2017-01-16T15:25:28.637Z", + "updated_at": "2017-01-16T15:25:28.637Z", + "gl_project_id": 123 + } ], "deploy_keys": [ diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 4b07fa53bf5..40d7d59f03b 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -197,6 +197,20 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do expect(restored_project_json).to be true end end + + context 'tokens are regenerated' do + before do + restored_project_json + end + + it 'has a new CI trigger token' do + expect(Ci::Trigger.where(token: 'cdbfasdf44a5958c83654733449e585')).to be_empty + end + + it 'has a new CI build token' do + expect(Ci::Build.where(token: 'abcd')).to be_empty + end + end end end end diff --git a/spec/lib/gitlab/import_export/relation_factory_spec.rb b/spec/lib/gitlab/import_export/relation_factory_spec.rb index db0084d6823..57e412b0cef 100644 --- a/spec/lib/gitlab/import_export/relation_factory_spec.rb +++ b/spec/lib/gitlab/import_export/relation_factory_spec.rb @@ -55,8 +55,8 @@ describe Gitlab::ImportExport::RelationFactory, lib: true do expect(created_object.project_id).to eq(project.id) end - it 'has a token' do - expect(created_object.token).to eq(token) + it 'has a nil token' do + expect(created_object.token).to eq(nil) end context 'original service exists' do @@ -178,4 +178,15 @@ describe Gitlab::ImportExport::RelationFactory, lib: true do expect(created_object.author).to eq(new_user) end end + + context 'encrypted attributes' do + let(:relation_sym) { 'Ci::Variable' } + let(:relation_hash) do + create(:ci_variable).as_json + end + + it 'has no value for the encrypted attribute' do + expect(created_object.value).to be_nil + end + end end diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index b2e06541e66..eba392044bf 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -288,6 +288,11 @@ describe Environment, models: true do "1-foo" => "env-1-foo" + SUFFIX, "1/foo" => "env-1-foo" + SUFFIX, "foo-" => "foo" + SUFFIX, + "foo--bar" => "foo-bar" + SUFFIX, + "foo**bar" => "foo-bar" + SUFFIX, + "*-foo" => "env-foo" + SUFFIX, + "staging-12345678-" => "staging-12345678" + SUFFIX, + "staging-12345678-01234567" => "staging-12345678" + SUFFIX, }.each do |name, matcher| it "returns a slug matching #{matcher}, given #{name}" do slug = described_class.new(name: name).generate_slug diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 40c0a75c364..bba9058f394 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -23,21 +23,74 @@ describe Issue, models: true do end describe '#to_reference' do - let(:project) { build(:empty_project, name: 'sample-project') } - let(:issue) { build(:issue, iid: 1, project: project) } + let(:namespace) { build(:namespace, path: 'sample-namespace') } + let(:project) { build(:empty_project, name: 'sample-project', namespace: namespace) } + let(:issue) { build(:issue, iid: 1, project: project) } + let(:group) { create(:group, name: 'Group', path: 'sample-group') } + + context 'when nil argument' do + it 'returns issue id' do + expect(issue.to_reference).to eq "#1" + end + end + + context 'when full is true' do + it 'returns complete path to the issue' do + expect(issue.to_reference(full: true)).to eq 'sample-namespace/sample-project#1' + expect(issue.to_reference(project, full: true)).to eq 'sample-namespace/sample-project#1' + expect(issue.to_reference(group, full: true)).to eq 'sample-namespace/sample-project#1' + end + end - it 'returns a String reference to the object' do - expect(issue.to_reference).to eq "#1" + context 'when same project argument' do + it 'returns issue id' do + expect(issue.to_reference(project)).to eq("#1") + end end - it 'returns a String reference with the full path' do - expect(issue.to_reference(full: true)).to eq(project.path_with_namespace + '#1') + context 'when cross namespace project argument' do + let(:another_namespace_project) { create(:empty_project, name: 'another-project') } + + it 'returns complete path to the issue' do + expect(issue.to_reference(another_namespace_project)).to eq 'sample-namespace/sample-project#1' + end end it 'supports a cross-project reference' do another_project = build(:empty_project, name: 'another-project', namespace: project.namespace) expect(issue.to_reference(another_project)).to eq "sample-project#1" end + + context 'when same namespace / cross-project argument' do + let(:another_project) { create(:empty_project, namespace: namespace) } + + it 'returns path to the issue with the project name' do + expect(issue.to_reference(another_project)).to eq 'sample-project#1' + end + end + + context 'when different namespace / cross-project argument' do + let(:another_namespace) { create(:namespace, path: 'another-namespace') } + let(:another_project) { create(:empty_project, path: 'another-project', namespace: another_namespace) } + + it 'returns full path to the issue' do + expect(issue.to_reference(another_project)).to eq 'sample-namespace/sample-project#1' + end + end + + context 'when argument is a namespace' do + context 'with same project path' do + it 'returns path to the issue with the project name' do + expect(issue.to_reference(namespace)).to eq 'sample-project#1' + end + end + + context 'with different project path' do + it 'returns full path to the issue' do + expect(issue.to_reference(group)).to eq 'sample-namespace/sample-project#1' + end + end + end end describe '#is_being_reassigned?' do diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb index c879edddfdd..98f3d420c8a 100644 --- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb +++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb @@ -113,10 +113,7 @@ describe MattermostSlashCommandsService, :models do end it 'shows error messages' do - teams, message = subject - - expect(teams).to be_empty - expect(message).to eq('Failed to get team list.') + expect(subject).to eq([[], "Failed to get team list."]) end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 646a1311462..48b085781e7 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -282,9 +282,10 @@ describe Project, models: true do end describe '#to_reference' do - let(:owner) { create(:user, name: 'Gitlab') } + let(:owner) { create(:user, name: 'Gitlab') } let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) } - let(:project) { create(:empty_project, path: 'sample-project', namespace: namespace) } + let(:project) { create(:empty_project, path: 'sample-project', namespace: namespace) } + let(:group) { create(:group, name: 'Group', path: 'sample-group', owner: owner) } context 'when nil argument' do it 'returns nil' do @@ -292,6 +293,14 @@ describe Project, models: true do end end + context 'when full is true' do + it 'returns complete path to the project' do + expect(project.to_reference(full: true)).to eq 'sample-namespace/sample-project' + expect(project.to_reference(project, full: true)).to eq 'sample-namespace/sample-project' + expect(project.to_reference(group, full: true)).to eq 'sample-namespace/sample-project' + end + end + context 'when same project argument' do it 'returns nil' do expect(project.to_reference(project)).to be_nil @@ -309,10 +318,33 @@ describe Project, models: true do context 'when same namespace / cross-project argument' do let(:another_project) { create(:empty_project, namespace: namespace) } - it 'returns complete path to the project' do + it 'returns path to the project' do expect(project.to_reference(another_project)).to eq 'sample-project' end end + + context 'when different namespace / cross-project argument' do + let(:another_namespace) { create(:namespace, path: 'another-namespace', owner: owner) } + let(:another_project) { create(:empty_project, path: 'another-project', namespace: another_namespace) } + + it 'returns full path to the project' do + expect(project.to_reference(another_project)).to eq 'sample-namespace/sample-project' + end + end + + context 'when argument is a namespace' do + context 'with same project path' do + it 'returns path to the project' do + expect(project.to_reference(namespace)).to eq 'sample-project' + end + end + + context 'with different project path' do + it 'returns full path to the project' do + expect(project.to_reference(group)).to eq 'sample-namespace/sample-project' + end + end + end end describe '#to_human_reference' do @@ -1801,6 +1833,14 @@ describe Project, models: true do end end + describe 'inside_path' do + let!(:project1) { create(:empty_project) } + let!(:project2) { create(:empty_project) } + let!(:path) { project1.namespace.path } + + it { expect(Project.inside_path(path)).to eq([project1]) } + end + def enable_lfs allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) end diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb index 8017d1c3324..581305ad39f 100644 --- a/spec/models/todo_spec.rb +++ b/spec/models/todo_spec.rb @@ -109,7 +109,7 @@ describe Todo, models: true do end describe '#target_reference' do - it 'returns the short commit id for commits' do + it 'returns commit full reference with short id' do project = create(:project, :repository) commit = project.commit @@ -117,12 +117,12 @@ describe Todo, models: true do subject.target_type = 'Commit' subject.commit_id = commit.id - expect(subject.target_reference).to eq commit.short_id + expect(subject.target_reference).to eq commit.reference_link_text(full: true) end - it 'returns reference for issuables' do + it 'returns full reference for issuables' do subject.target = issue - expect(subject.target_reference).to eq issue.to_reference + expect(subject.target_reference).to eq issue.to_reference(full: true) end end end diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb new file mode 100644 index 00000000000..0f280f32eac --- /dev/null +++ b/spec/policies/ci/build_policy_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe Ci::BuildPolicy, :models do + let(:user) { create(:user) } + let(:build) { create(:ci_build, pipeline: pipeline) } + let(:pipeline) { create(:ci_empty_pipeline, project: project) } + + let(:policies) do + described_class.abilities(user, build).to_set + end + + shared_context 'public pipelines disabled' do + before { project.update_attribute(:public_builds, false) } + end + + describe '#rules' do + context 'when user does not have access to the project' do + let(:project) { create(:empty_project, :private) } + + context 'when public builds are enabled' do + it 'does not include ability to read build' do + expect(policies).not_to include :read_build + end + end + + context 'when public builds are disabled' do + include_context 'public pipelines disabled' + + it 'does not include ability to read build' do + expect(policies).not_to include :read_build + end + end + end + + context 'when anonymous user has access to the project' do + let(:project) { create(:empty_project, :public) } + + context 'when public builds are enabled' do + it 'includes ability to read build' do + expect(policies).to include :read_build + end + end + + context 'when public builds are disabled' do + include_context 'public pipelines disabled' + + it 'does not include ability to read build' do + expect(policies).not_to include :read_build + end + end + end + + context 'when team member has access to the project' do + let(:project) { create(:empty_project, :public) } + + context 'team member is a guest' do + before { project.team << [user, :guest] } + + context 'when public builds are enabled' do + it 'includes ability to read build' do + expect(policies).to include :read_build + end + end + + context 'when public builds are disabled' do + include_context 'public pipelines disabled' + + it 'does not include ability to read build' do + expect(policies).not_to include :read_build + end + end + end + + context 'team member is a reporter' do + before { project.team << [user, :reporter] } + + context 'when public builds are enabled' do + it 'includes ability to read build' do + expect(policies).to include :read_build + end + end + + context 'when public builds are disabled' do + include_context 'public pipelines disabled' + + it 'does not include ability to read build' do + expect(policies).to include :read_build + end + end + end + end + end +end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index edbf0140583..1187d2e609d 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -176,6 +176,9 @@ describe API::Groups, api: true do expect(json_response['visibility_level']).to eq(group1.visibility_level) expect(json_response['avatar_url']).to eq(group1.avatar_url) expect(json_response['web_url']).to eq(group1.web_url) + expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled) + expect(json_response['full_name']).to eq(group1.full_name) + expect(json_response['full_path']).to eq(group1.full_path) expect(json_response['projects']).to be_an Array expect(json_response['projects'].length).to eq(2) expect(json_response['shared_projects']).to be_an Array diff --git a/spec/serializers/analytics_build_serializer_spec.rb b/spec/serializers/analytics_build_serializer_spec.rb index f0551c78671..e3b1dd93dc2 100644 --- a/spec/serializers/analytics_build_serializer_spec.rb +++ b/spec/serializers/analytics_build_serializer_spec.rb @@ -1,17 +1,13 @@ require 'spec_helper' describe AnalyticsBuildSerializer do - let(:serializer) do - described_class - .new.represent(resource) - end - - let(:json) { serializer.as_json } let(:resource) { create(:ci_build) } + subject { described_class.new.represent(resource) } + context 'when there is a single object provided' do it 'contains important elements of analyticsBuild' do - expect(json) + expect(subject) .to include(:name, :branch, :short_sha, :date, :total_time, :url, :author) end end diff --git a/spec/serializers/analytics_issue_serializer_spec.rb b/spec/serializers/analytics_issue_serializer_spec.rb index 6afbb2df35c..2f08958a783 100644 --- a/spec/serializers/analytics_issue_serializer_spec.rb +++ b/spec/serializers/analytics_issue_serializer_spec.rb @@ -1,14 +1,13 @@ require 'spec_helper' describe AnalyticsIssueSerializer do - let(:serializer) do + subject do described_class .new(project: project, entity: :merge_request) .represent(resource) end let(:user) { create(:user) } - let(:json) { serializer.as_json } let(:project) { create(:project) } let(:resource) do { @@ -23,7 +22,7 @@ describe AnalyticsIssueSerializer do context 'when there is a single object provided' do it 'contains important elements of the issue' do - expect(json).to include(:title, :iid, :created_at, :total_time, :url, :author) + expect(subject).to include(:title, :iid, :created_at, :total_time, :url, :author) end end end diff --git a/spec/serializers/analytics_merge_request_serializer_spec.rb b/spec/serializers/analytics_merge_request_serializer_spec.rb index cdfae27193f..62067cc0ef2 100644 --- a/spec/serializers/analytics_merge_request_serializer_spec.rb +++ b/spec/serializers/analytics_merge_request_serializer_spec.rb @@ -1,14 +1,13 @@ require 'spec_helper' describe AnalyticsMergeRequestSerializer do - let(:serializer) do + subject do described_class .new(project: project, entity: :merge_request) .represent(resource) end let(:user) { create(:user) } - let(:json) { serializer.as_json } let(:project) { create(:project) } let(:resource) do { @@ -24,7 +23,7 @@ describe AnalyticsMergeRequestSerializer do context 'when there is a single object provided' do it 'contains important elements of the merge request' do - expect(json).to include(:title, :iid, :created_at, :total_time, :url, :author, :state) + expect(subject).to include(:title, :iid, :created_at, :total_time, :url, :author, :state) end end end diff --git a/spec/serializers/analytics_stage_serializer_spec.rb b/spec/serializers/analytics_stage_serializer_spec.rb index f9951826683..be6aa7c65c3 100644 --- a/spec/serializers/analytics_stage_serializer_spec.rb +++ b/spec/serializers/analytics_stage_serializer_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' describe AnalyticsStageSerializer do - let(:serializer) do - described_class - .new.represent(resource) + subject do + described_class.new.represent(resource) end - let(:json) { serializer.as_json } - let(:resource) { Gitlab::CycleAnalytics::CodeStage.new(project: double, options: {}) } + let(:resource) do + Gitlab::CycleAnalytics::CodeStage.new(project: double, options: {}) + end before do allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(1.12) @@ -15,10 +15,10 @@ describe AnalyticsStageSerializer do end it 'it generates payload for single object' do - expect(json).to be_kind_of Hash + expect(subject).to be_kind_of Hash end it 'contains important elements of AnalyticsStage' do - expect(json).to include(:title, :description, :value) + expect(subject).to include(:title, :description, :value) end end diff --git a/spec/serializers/analytics_summary_serializer_spec.rb b/spec/serializers/analytics_summary_serializer_spec.rb index 7a84c8b0b40..5d7a94c2d02 100644 --- a/spec/serializers/analytics_summary_serializer_spec.rb +++ b/spec/serializers/analytics_summary_serializer_spec.rb @@ -1,29 +1,28 @@ require 'spec_helper' describe AnalyticsSummarySerializer do - let(:serializer) do - described_class - .new.represent(resource) + subject do + described_class.new.represent(resource) end - let(:json) { serializer.as_json } let(:project) { create(:empty_project) } let(:user) { create(:user) } + let(:resource) do - Gitlab::CycleAnalytics::Summary::Issue.new(project: double, - from: 1.day.ago, - current_user: user) + Gitlab::CycleAnalytics::Summary::Issue + .new(project: double, from: 1.day.ago, current_user: user) end before do - allow_any_instance_of(Gitlab::CycleAnalytics::Summary::Issue).to receive(:value).and_return(1.12) + allow_any_instance_of(Gitlab::CycleAnalytics::Summary::Issue) + .to receive(:value).and_return(1.12) end it 'it generates payload for single object' do - expect(json).to be_kind_of Hash + expect(subject).to be_kind_of Hash end it 'contains important elements of AnalyticsStage' do - expect(json).to include(:title, :value) + expect(subject).to include(:title, :value) end end diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb index b7ed4eb0239..3c37660885d 100644 --- a/spec/serializers/environment_serializer_spec.rb +++ b/spec/serializers/environment_serializer_spec.rb @@ -1,16 +1,15 @@ require 'spec_helper' describe EnvironmentSerializer do - let(:serializer) do + let(:user) { create(:user) } + let(:project) { create(:project) } + + let(:json) do described_class .new(user: user, project: project) .represent(resource) end - let(:json) { serializer.as_json } - let(:user) { create(:user) } - let(:project) { create(:project) } - context 'when there is a single object provided' do before do create(:ci_build, :manual, name: 'manual1', diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index 3a32cb394dd..7cbf131e41e 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -7,11 +7,7 @@ describe PipelineSerializer do described_class.new(user: user) end - let(:entity) do - serializer.represent(resource) - end - - subject { entity.as_json } + subject { serializer.represent(resource) } describe '#represent' do context 'when used without pagination' do diff --git a/spec/services/labels/promote_service_spec.rb b/spec/services/labels/promote_service_spec.rb new file mode 100644 index 00000000000..4b90ad19640 --- /dev/null +++ b/spec/services/labels/promote_service_spec.rb @@ -0,0 +1,187 @@ +require 'spec_helper' + +describe Labels::PromoteService, services: true do + describe '#execute' do + let!(:user) { create(:user) } + + context 'project without group' do + let!(:project_1) { create(:empty_project) } + + let!(:project_label_1_1) { create(:label, project: project_1) } + + subject(:service) { described_class.new(project_1, user) } + + it 'fails on project without group' do + expect(service.execute(project_label_1_1)).to be_falsey + end + end + + context 'project with group' do + let!(:promoted_label_name) { "Promoted Label" } + let!(:untouched_label_name) { "Untouched Label" } + let!(:promoted_description) { "Promoted Description" } + let!(:promoted_color) { "#0000FF" } + let!(:label_2_1_priority) { 1 } + let!(:label_3_1_priority) { 2 } + + let!(:group_1) { create(:group) } + let!(:group_2) { create(:group) } + + let!(:project_1) { create(:empty_project, namespace: group_1) } + let!(:project_2) { create(:empty_project, namespace: group_1) } + let!(:project_3) { create(:empty_project, namespace: group_1) } + let!(:project_4) { create(:empty_project, namespace: group_2) } + + # Labels/issues can't be lazily created so we might as well eager initialize + # all other objects too since we use them inside + let!(:project_label_1_1) { create(:label, project: project_1, name: promoted_label_name, color: promoted_color, description: promoted_description) } + let!(:project_label_1_2) { create(:label, project: project_1, name: untouched_label_name) } + let!(:project_label_2_1) { create(:label, project: project_2, priority: label_2_1_priority, name: promoted_label_name, color: "#FF0000") } + let!(:project_label_3_1) { create(:label, project: project_3, priority: label_3_1_priority, name: promoted_label_name) } + let!(:project_label_3_2) { create(:label, project: project_3, priority: 1, name: untouched_label_name) } + let!(:project_label_4_1) { create(:label, project: project_4, name: promoted_label_name) } + + let!(:issue_1_1) { create(:labeled_issue, project: project_1, labels: [project_label_1_1, project_label_1_2]) } + let!(:issue_1_2) { create(:labeled_issue, project: project_1, labels: [project_label_1_2]) } + let!(:issue_2_1) { create(:labeled_issue, project: project_2, labels: [project_label_2_1]) } + let!(:issue_4_1) { create(:labeled_issue, project: project_4, labels: [project_label_4_1]) } + + let!(:merge_3_1) { create(:labeled_merge_request, source_project: project_3, target_project: project_3, labels: [project_label_3_1, project_label_3_2]) } + + let!(:issue_board_2_1) { create(:board, project: project_2) } + let!(:issue_board_list_2_1) { create(:list, board: issue_board_2_1, label: project_label_2_1) } + + let(:new_label) { group_1.labels.find_by(title: promoted_label_name) } + + subject(:service) { described_class.new(project_1, user) } + + it 'fails on group label' do + group_label = create(:group_label, group: group_1) + + expect(service.execute(group_label)).to be_falsey + end + + it 'is truthy on success' do + expect(service.execute(project_label_1_1)).to be_truthy + end + + it 'recreates the label as a group label' do + expect { service.execute(project_label_1_1) }. + to change(project_1.labels, :count).by(-1). + and change(group_1.labels, :count).by(1) + expect(new_label).not_to be_nil + end + + it 'copies title, description and color' do + service.execute(project_label_1_1) + + expect(new_label.title).to eq(promoted_label_name) + expect(new_label.description).to eq(promoted_description) + expect(new_label.color).to eq(promoted_color) + end + + it 'merges labels with the same name in group' do + expect { service.execute(project_label_1_1) }.to change(project_2.labels, :count).by(-1).and \ + change(project_3.labels, :count).by(-1) + end + + it 'recreates priorities' do + service.execute(project_label_1_1) + + expect(new_label.priority(project_1)).to be_nil + expect(new_label.priority(project_2)).to eq(label_2_1_priority) + expect(new_label.priority(project_3)).to eq(label_3_1_priority) + end + + it 'does not touch project out of promoted group' do + service.execute(project_label_1_1) + project_4_new_label = project_4.labels.find_by(title: promoted_label_name) + + expect(project_4_new_label).not_to be_nil + expect(project_4_new_label.id).to eq(project_label_4_1.id) + end + + it 'does not touch out of group priority' do + service.execute(project_label_1_1) + + expect(new_label.priority(project_4)).to be_nil + end + + it 'relinks issue with the promoted label' do + service.execute(project_label_1_1) + issue_label = issue_1_1.labels.find_by(title: promoted_label_name) + + expect(issue_label).not_to be_nil + expect(issue_label.id).to eq(new_label.id) + end + + it 'does not remove untouched labels from issue' do + expect { service.execute(project_label_1_1) }.not_to change(issue_1_1.labels, :count) + end + + it 'does not relink untouched label in issue' do + service.execute(project_label_1_1) + issue_label = issue_1_2.labels.find_by(title: untouched_label_name) + + expect(issue_label).not_to be_nil + expect(issue_label.id).to eq(project_label_1_2.id) + end + + it 'relinks issues with merged labels' do + service.execute(project_label_1_1) + issue_label = issue_2_1.labels.find_by(title: promoted_label_name) + + expect(issue_label).not_to be_nil + expect(issue_label.id).to eq(new_label.id) + end + + it 'does not relink issues from other group' do + service.execute(project_label_1_1) + issue_label = issue_4_1.labels.find_by(title: promoted_label_name) + + expect(issue_label).not_to be_nil + expect(issue_label.id).to eq(project_label_4_1.id) + end + + it 'updates merge request' do + service.execute(project_label_1_1) + merge_label = merge_3_1.labels.find_by(title: promoted_label_name) + + expect(merge_label).not_to be_nil + expect(merge_label.id).to eq(new_label.id) + end + + it 'updates board lists' do + service.execute(project_label_1_1) + list = issue_board_2_1.lists.find_by(label: new_label) + + expect(list).not_to be_nil + end + + # In case someone adds a new relation to Label.rb and forgets to relink it + # and the database doesn't have foreign key constraints + it 'relinks all relations' do + service.execute(project_label_1_1) + + Label.reflect_on_all_associations.each do |association| + expect(project_label_1_1.send(association.name).any?).to be_falsey + end + end + + context 'with invalid group label' do + before do + allow(service).to receive(:clone_label_to_group_label).and_wrap_original do |m, *args| + label = m.call(*args) + allow(label).to receive(:valid?).and_return(false) + + label + end + end + + it 'raises an exception' do + expect { service.execute(project_label_1_1) }.to raise_error(ActiveRecord::RecordInvalid) + end + end + end + end +end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 309985a5d90..7cf2cd9968f 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -443,6 +443,8 @@ describe NotificationService, services: true do before do build_team(issue.project) + build_group(issue.project) + add_users_with_subscription(issue.project, issue) reset_delivered_emails! update_custom_notification(:new_issue, @u_guest_custom, project) @@ -459,6 +461,8 @@ describe NotificationService, services: true do should_email(@u_guest_custom) should_email(@u_custom_global) should_email(@u_participant_mentioned) + should_email(@g_global_watcher) + should_email(@g_watcher) should_not_email(@u_mentioned) should_not_email(@u_participating) should_not_email(@u_disabled) @@ -1218,6 +1222,22 @@ describe NotificationService, services: true do project.add_master(@u_custom_global) end + # Users in the project's group but not part of project's team + # with different notification settings + def build_group(project) + group = create(:group, :public) + project.group = group + + # Group member: global=disabled, group=watch + @g_watcher = create_user_with_notification(:watch, 'group_watcher', project.group) + @g_watcher.notification_settings_for(nil).disabled! + + # Group member: global=watch, group=global + @g_global_watcher = create_global_setting_for(create(:user), :watch) + group.add_users([@g_watcher, @g_global_watcher], :master) + group + end + def create_global_setting_for(user, level) setting = user.global_notification_setting setting.level = level @@ -1226,9 +1246,9 @@ describe NotificationService, services: true do user end - def create_user_with_notification(level, username) + def create_user_with_notification(level, username, resource = project) user = create(:user, username: username) - setting = user.notification_settings_for(project) + setting = user.notification_settings_for(resource) setting.level = level setting.save diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index bd89c4a7c11..bed1031e40a 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -41,6 +41,25 @@ describe 'Search::GlobalService', services: true do results = context.execute expect(results.objects('projects')).to match_array [found_project] end + + context 'nested group' do + let!(:nested_group) { create(:group, :nested) } + let!(:project) { create(:project, namespace: nested_group) } + + before { project.add_master(user) } + + 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] + end + + 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] + end + end end end end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 9f5a0ac4ec6..bd7269045e1 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -245,6 +245,8 @@ describe SystemNoteService, services: true do end describe '.change_title' do + let(:noteable) { create(:issue, project: project, title: 'Lorem ipsum') } + subject { described_class.change_title(noteable, project, author, 'Old title') } context 'when noteable responds to `title`' do @@ -252,7 +254,7 @@ describe SystemNoteService, services: true do it 'sets the note text' do expect(subject.note). - to eq "changed title from **{-Old title-}** to **{+#{noteable.title}+}**" + to eq "changed title from **{-Old title-}** to **{+Lorem ipsum+}**" end end end |