diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-23 21:10:40 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-23 21:10:40 +0300 |
commit | c37dd28c4afd33fee46cff8ddfdada8a3f54564c (patch) | |
tree | c4fb2a3f93338991784cf89b3b1547ab23c1b5e1 /spec | |
parent | 5ff5047fdc2c614f347de5c388424b50a5460165 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
33 files changed, 924 insertions, 547 deletions
diff --git a/spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb b/spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb index 81318b49cd9..3c4376909f8 100644 --- a/spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb +++ b/spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb @@ -4,29 +4,25 @@ require 'spec_helper' RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do describe 'GET index' do - let(:project) { create(:project, :public, :repository) } - let(:ref_path) { 'refs/heads/master' } - let(:param_type) { 'coverage' } - let(:start_date) { '2019-12-10' } - let(:end_date) { '2020-03-09' } - let(:allowed_to_read) { true } - let(:user) { create(:user) } - let(:feature_enabled?) { true } + let_it_be(:project) { create(:project, :public, :repository) } + let_it_be(:ref_path) { 'refs/heads/master' } + let_it_be(:param_type) { 'coverage' } + let_it_be(:start_date) { '2019-12-10' } + let_it_be(:end_date) { '2020-03-09' } + let_it_be(:allowed_to_read) { true } + let_it_be(:user) { create(:user) } + let_it_be(:rspec_coverage_1) { create_daily_coverage('rspec', 79.0, '2020-03-09') } + let_it_be(:rspec_coverage_2) { create_daily_coverage('rspec', 77.0, '2020-03-08') } + let_it_be(:karma_coverage) { create_daily_coverage('karma', 81.0, '2019-12-10') } + let_it_be(:minitest_coverage) { create_daily_coverage('minitest', 67.0, '2019-12-09') } + let_it_be(:mocha_coverage) { create_daily_coverage('mocha', 71.0, '2019-12-09') } before do - create_daily_coverage('rspec', 79.0, '2020-03-09') - create_daily_coverage('rspec', 77.0, '2020-03-08') - create_daily_coverage('karma', 81.0, '2019-12-10') - create_daily_coverage('minitest', 67.0, '2019-12-09') - create_daily_coverage('mocha', 71.0, '2019-12-09') - sign_in(user) allow(Ability).to receive(:allowed?).and_call_original allow(Ability).to receive(:allowed?).with(user, :read_build_report_results, project).and_return(allowed_to_read) - stub_feature_flags(coverage_data_new_finder: feature_enabled?) - get :index, params: { namespace_id: project.namespace, project_id: project, @@ -140,33 +136,13 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do context 'when format is JSON' do let(:format) { :json } - context 'when coverage_data_new_finder flag is enabled' do - let(:feature_enabled?) { true } - - it_behaves_like 'JSON results' - end - - context 'when coverage_data_new_finder flag is disabled' do - let(:feature_enabled?) { false } - - it_behaves_like 'JSON results' - end + it_behaves_like 'JSON results' end context 'when format is CSV' do let(:format) { :csv } - context 'when coverage_data_new_finder flag is enabled' do - let(:feature_enabled?) { true } - - it_behaves_like 'CSV results' - end - - context 'when coverage_data_new_finder flag is disabled' do - let(:feature_enabled?) { false } - - it_behaves_like 'CSV results' - end + it_behaves_like 'CSV results' end end diff --git a/spec/features/projects/services/user_activates_jira_spec.rb b/spec/features/projects/services/user_activates_jira_spec.rb index 3b8032e1d0d..85afc54be48 100644 --- a/spec/features/projects/services/user_activates_jira_spec.rb +++ b/spec/features/projects/services/user_activates_jira_spec.rb @@ -6,14 +6,12 @@ RSpec.describe 'User activates Jira', :js do include_context 'project service activation' include_context 'project service Jira context' - before do - server_info = { key: 'value' }.to_json - stub_request(:get, test_url).to_return(body: server_info) - end - describe 'user tests Jira Service' do context 'when Jira connection test succeeds' do before do + server_info = { key: 'value' }.to_json + stub_request(:get, test_url).with(basic_auth: %w(username password)).to_return(body: server_info) + visit_project_integration('Jira') fill_form click_test_then_save_integration(expect_test_to_fail: false) @@ -83,40 +81,4 @@ RSpec.describe 'User activates Jira', :js do end end end - - describe 'issue transition settings' do - it 'shows validation errors' do - visit_project_integration('Jira') - - expect(page).to have_field('Move to Done', checked: true) - - fill_form - choose 'Use custom transitions' - click_save_integration - - within '[data-testid="issue-transition-settings"]' do - expect(page).to have_content('This field is required.') - end - - fill_in 'service[jira_issue_transition_id]', with: '1, 2, 3' - click_save_integration - - expect(page).to have_content('Jira settings saved and active.') - expect(project.reload.jira_service.jira_issue_transition_id).to eq('1, 2, 3') - end - - it 'clears the transition IDs when using automatic transitions' do - create(:jira_service, project: project, jira_issue_transition_id: '1, 2, 3') - visit_project_integration('Jira') - - expect(page).to have_field('Use custom transitions', checked: true) - expect(page).to have_field('service[jira_issue_transition_id]', with: '1, 2, 3') - - choose 'Move to Done' - click_save_integration - - expect(page).to have_content('Jira settings saved and active.') - expect(project.reload.jira_service.jira_issue_transition_id).to eq('') - end - end end diff --git a/spec/finders/ci/daily_build_group_report_results_finder_spec.rb b/spec/finders/ci/daily_build_group_report_results_finder_spec.rb index 2a6e44673e3..ecfa66b07dc 100644 --- a/spec/finders/ci/daily_build_group_report_results_finder_spec.rb +++ b/spec/finders/ci/daily_build_group_report_results_finder_spec.rb @@ -5,10 +5,14 @@ require 'spec_helper' RSpec.describe Ci::DailyBuildGroupReportResultsFinder do describe '#execute' do let_it_be(:project) { create(:project, :private) } - let_it_be(:current_user) { project.owner } + let(:user_without_permission) { create(:user) } + let_it_be(:user_with_permission) { project.owner } let_it_be(:ref_path) { 'refs/heads/master' } let(:limit) { nil } let_it_be(:default_branch) { false } + let(:start_date) { '2020-03-09' } + let(:end_date) { '2020-03-10' } + let(:sort) { true } let_it_be(:rspec_coverage_1) { create_daily_coverage('rspec', 79.0, '2020-03-09') } let_it_be(:karma_coverage_1) { create_daily_coverage('karma', 89.0, '2020-03-09') } @@ -17,24 +21,35 @@ RSpec.describe Ci::DailyBuildGroupReportResultsFinder do let_it_be(:rspec_coverage_3) { create_daily_coverage('rspec', 97.0, '2020-03-11') } let_it_be(:karma_coverage_3) { create_daily_coverage('karma', 99.0, '2020-03-11') } - let(:attributes) do + let(:finder) { described_class.new(params: params, current_user: current_user) } + + let(:params) do { - current_user: current_user, project: project, + coverage: true, ref_path: ref_path, - start_date: '2020-03-09', - end_date: '2020-03-10', - limit: limit + start_date: start_date, + end_date: end_date, + limit: limit, + sort: sort } end - subject(:coverages) do - described_class.new(**attributes).execute - end + subject(:coverages) { finder.execute } + + context 'when params are provided' do + context 'when current user is not allowed to read data' do + let(:current_user) { user_without_permission } + + it 'returns an empty collection' do + expect(coverages).to be_empty + end + end - context 'when ref_path is present' do - context 'when current user is allowed to read build report results' do - it 'returns all matching results within the given date range' do + context 'when current user is allowed to read data' do + let(:current_user) { user_with_permission } + + it 'returns matching coverages within the given date range' do expect(coverages).to match_array([ karma_coverage_2, rspec_coverage_2, @@ -43,10 +58,21 @@ RSpec.describe Ci::DailyBuildGroupReportResultsFinder do ]) end - context 'and limit is specified' do + context 'when ref_path is nil' do + let(:default_branch) { true } + let(:ref_path) { nil } + + it 'returns coverages for the default branch' do + rspec_coverage_4 = create_daily_coverage('rspec', 66.0, '2020-03-10') + + expect(coverages).to contain_exactly(rspec_coverage_4) + end + end + + context 'when limit is specified' do let(:limit) { 2 } - it 'returns limited number of matching results within the given date range' do + it 'returns limited number of matching coverages within the given date range' do expect(coverages).to match_array([ karma_coverage_2, rspec_coverage_2 @@ -54,28 +80,6 @@ RSpec.describe Ci::DailyBuildGroupReportResultsFinder do end end end - - context 'when current user is not allowed to read build report results' do - let(:current_user) { create(:user) } - - it 'returns an empty result' do - expect(coverages).to be_empty - end - end - end - - context 'when ref_path query parameter is not present' do - let(:ref_path) { nil } - - context 'when records with cover data from the default branch exist' do - let(:default_branch) { true } - - it 'returns records with default_branch:true, irrespective of ref_path' do - rspec_coverage_4 = create_daily_coverage('rspec', 66.0, '2020-03-10') - - expect(coverages).to contain_exactly(rspec_coverage_4) - end - end end end diff --git a/spec/finders/ci/testing/daily_build_group_report_results_finder_spec.rb b/spec/finders/ci/testing/daily_build_group_report_results_finder_spec.rb deleted file mode 100644 index a703f3b800c..00000000000 --- a/spec/finders/ci/testing/daily_build_group_report_results_finder_spec.rb +++ /dev/null @@ -1,99 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Ci::Testing::DailyBuildGroupReportResultsFinder do - describe '#execute' do - let_it_be(:project) { create(:project, :private) } - let(:user_without_permission) { create(:user) } - let_it_be(:user_with_permission) { project.owner } - let_it_be(:ref_path) { 'refs/heads/master' } - let(:limit) { nil } - let_it_be(:default_branch) { false } - let(:start_date) { '2020-03-09' } - let(:end_date) { '2020-03-10' } - let(:sort) { true } - - let_it_be(:rspec_coverage_1) { create_daily_coverage('rspec', 79.0, '2020-03-09') } - let_it_be(:karma_coverage_1) { create_daily_coverage('karma', 89.0, '2020-03-09') } - let_it_be(:rspec_coverage_2) { create_daily_coverage('rspec', 95.0, '2020-03-10') } - let_it_be(:karma_coverage_2) { create_daily_coverage('karma', 92.0, '2020-03-10') } - let_it_be(:rspec_coverage_3) { create_daily_coverage('rspec', 97.0, '2020-03-11') } - let_it_be(:karma_coverage_3) { create_daily_coverage('karma', 99.0, '2020-03-11') } - - let(:finder) { described_class.new(params: params, current_user: current_user) } - - let(:params) do - { - project: project, - coverage: true, - ref_path: ref_path, - start_date: start_date, - end_date: end_date, - limit: limit, - sort: sort - } - end - - subject(:coverages) { finder.execute } - - context 'when params are provided' do - context 'when current user is not allowed to read data' do - let(:current_user) { user_without_permission } - - it 'returns an empty collection' do - expect(coverages).to be_empty - end - end - - context 'when current user is allowed to read data' do - let(:current_user) { user_with_permission } - - it 'returns matching coverages within the given date range' do - expect(coverages).to match_array([ - karma_coverage_2, - rspec_coverage_2, - karma_coverage_1, - rspec_coverage_1 - ]) - end - - context 'when ref_path is nil' do - let(:default_branch) { true } - let(:ref_path) { nil } - - it 'returns coverages for the default branch' do - rspec_coverage_4 = create_daily_coverage('rspec', 66.0, '2020-03-10') - - expect(coverages).to contain_exactly(rspec_coverage_4) - end - end - - context 'when limit is specified' do - let(:limit) { 2 } - - it 'returns limited number of matching coverages within the given date range' do - expect(coverages).to match_array([ - karma_coverage_2, - rspec_coverage_2 - ]) - end - end - end - end - end - - private - - def create_daily_coverage(group_name, coverage, date) - create( - :ci_daily_build_group_report_result, - project: project, - ref_path: ref_path || 'feature-branch', - group_name: group_name, - data: { 'coverage' => coverage }, - date: date, - default_branch: default_branch - ) - end -end diff --git a/spec/finders/repositories/previous_tag_finder_spec.rb b/spec/finders/repositories/previous_tag_finder_spec.rb index 7cc33d11baf..e07eab7ec5f 100644 --- a/spec/finders/repositories/previous_tag_finder_spec.rb +++ b/spec/finders/repositories/previous_tag_finder_spec.rb @@ -12,16 +12,19 @@ RSpec.describe Repositories::PreviousTagFinder do tag1 = double(:tag1, name: 'v1.0.0') tag2 = double(:tag2, name: 'v1.1.0') tag3 = double(:tag3, name: 'v2.0.0') - tag4 = double(:tag4, name: '1.0.0') + tag4 = double(:tag4, name: '0.9.0') + tag5 = double(:tag4, name: 'v0.8.0-pre1') allow(project.repository) .to receive(:tags) - .and_return([tag1, tag3, tag2, tag4]) + .and_return([tag1, tag3, tag2, tag4, tag5]) expect(finder.execute('2.1.0')).to eq(tag3) expect(finder.execute('2.0.0')).to eq(tag2) expect(finder.execute('1.5.0')).to eq(tag2) expect(finder.execute('1.0.1')).to eq(tag1) + expect(finder.execute('1.0.0')).to eq(tag4) + expect(finder.execute('0.9.0')).to eq(tag5) end end diff --git a/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js b/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js index 6a8ab02a69a..c6e7ee44355 100644 --- a/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js +++ b/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js @@ -30,21 +30,14 @@ describe('JiraTriggerFields', () => { const findCommentSettings = () => wrapper.find('[data-testid="comment-settings"]'); const findCommentDetail = () => wrapper.find('[data-testid="comment-detail"]'); const findCommentSettingsCheckbox = () => findCommentSettings().find(GlFormCheckbox); - const findIssueTransitionSettings = () => - wrapper.find('[data-testid="issue-transition-settings"]'); - const findIssueTransitionModeRadios = () => - findIssueTransitionSettings().findAll('input[type="radio"]'); - const findIssueTransitionIdsField = () => - wrapper.find('input[type="text"][name="service[jira_issue_transition_id]"]'); describe('template', () => { describe('initialTriggerCommit and initialTriggerMergeRequest are false', () => { - it('does not show trigger settings', () => { + it('does not show comment settings', () => { createComponent(); expect(findCommentSettings().isVisible()).toBe(false); expect(findCommentDetail().isVisible()).toBe(false); - expect(findIssueTransitionSettings().isVisible()).toBe(false); }); }); @@ -55,10 +48,9 @@ describe('JiraTriggerFields', () => { }); }); - it('shows trigger settings', () => { + it('shows comment settings', () => { expect(findCommentSettings().isVisible()).toBe(true); expect(findCommentDetail().isVisible()).toBe(false); - expect(findIssueTransitionSettings().isVisible()).toBe(true); }); // As per https://vuejs.org/v2/guide/forms.html#Checkbox-1, @@ -81,14 +73,13 @@ describe('JiraTriggerFields', () => { }); describe('initialTriggerMergeRequest is true', () => { - it('shows trigger settings', () => { + it('shows comment settings', () => { createComponent({ initialTriggerMergeRequest: true, }); expect(findCommentSettings().isVisible()).toBe(true); expect(findCommentDetail().isVisible()).toBe(false); - expect(findIssueTransitionSettings().isVisible()).toBe(true); }); }); @@ -104,41 +95,7 @@ describe('JiraTriggerFields', () => { }); }); - describe('initialJiraIssueTransitionId is not set', () => { - it('uses automatic transitions', () => { - createComponent({ - initialTriggerCommit: true, - }); - - const [radio1, radio2] = findIssueTransitionModeRadios().wrappers; - expect(radio1.element.checked).toBe(true); - expect(radio2.element.checked).toBe(false); - - expect(findIssueTransitionIdsField().exists()).toBe(false); - }); - }); - - describe('initialJiraIssueTransitionId is set', () => { - it('uses custom transitions', () => { - createComponent({ - initialJiraIssueTransitionId: '1, 2, 3', - initialTriggerCommit: true, - }); - - const [radio1, radio2] = findIssueTransitionModeRadios().wrappers; - expect(radio1.element.checked).toBe(false); - expect(radio2.element.checked).toBe(true); - - const field = findIssueTransitionIdsField(); - expect(field.isVisible()).toBe(true); - expect(field.element).toMatchObject({ - type: 'text', - value: '1, 2, 3', - }); - }); - }); - - it('disables input fields if inheriting', () => { + it('disables checkboxes and radios if inheriting', () => { createComponent( { initialTriggerCommit: true, @@ -147,8 +104,12 @@ describe('JiraTriggerFields', () => { true, ); - wrapper.findAll('[type=text], [type=checkbox], [type=radio]').wrappers.forEach((input) => { - expect(input.attributes('disabled')).toBe('disabled'); + wrapper.findAll('[type=checkbox]').wrappers.forEach((checkbox) => { + expect(checkbox.attributes('disabled')).toBe('disabled'); + }); + + wrapper.findAll('[type=radio]').wrappers.forEach((radio) => { + expect(radio.attributes('disabled')).toBe('disabled'); }); }); }); diff --git a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap index c9141d13a46..1c1327e7a4e 100644 --- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap +++ b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap @@ -4,7 +4,7 @@ exports[`Learn GitLab Design A should render the loading state 1`] = ` <ul> <li> <span> - Create a repository + Create or import a repository </span> </li> <li> @@ -14,7 +14,11 @@ exports[`Learn GitLab Design A should render the loading state 1`] = ` </li> <li> <span> - Set-up CI/CD + <gl-link-stub + href="http://example.com/" + > + Set up CI/CD + </gl-link-stub> </span> </li> <li> @@ -22,7 +26,7 @@ exports[`Learn GitLab Design A should render the loading state 1`] = ` <gl-link-stub href="http://example.com/" > - Start a free trial of GitLab Gold + Start a free Ultimate trial </gl-link-stub> </span> </li> @@ -40,7 +44,7 @@ exports[`Learn GitLab Design A should render the loading state 1`] = ` <gl-link-stub href="http://example.com/" > - Enable require merge approvals + Add merge request approval </gl-link-stub> </span> </li> @@ -49,7 +53,7 @@ exports[`Learn GitLab Design A should render the loading state 1`] = ` <gl-link-stub href="http://example.com/" > - Submit a merge request (MR) + Submit a merge request </gl-link-stub> </span> </li> @@ -58,7 +62,7 @@ exports[`Learn GitLab Design A should render the loading state 1`] = ` <gl-link-stub href="http://example.com/" > - Run a Security scan using CI/CD + Run a security scan </gl-link-stub> </span> </li> diff --git a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap index 85e3b675e5b..dd899b93302 100644 --- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap +++ b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap @@ -1,66 +1,519 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Learn GitLab Design B should render the loading state 1`] = ` -<ul> - <li> - <span> - Create a repository - </span> - </li> - <li> - <span> - Invite your colleagues - </span> - </li> - <li> - <span> - Set-up CI/CD - </span> - </li> - <li> - <span> - <gl-link-stub - href="http://example.com/" +exports[`Learn GitLab Design B renders correctly 1`] = ` +<div> + <div + class="row" + > + <div + class="gl-mb-7 col-md-8 col-lg-7" + > + <h1 + class="gl-font-size-h1" > - Start a free trial of GitLab Gold - </gl-link-stub> - </span> - </li> - <li> - <span> - <gl-link-stub - href="http://example.com/" + Learn GitLab + </h1> + + <p + class="gl-text-gray-700 gl-mb-0" > - Add code owners - </gl-link-stub> - </span> - </li> - <li> - <span> - <gl-link-stub - href="http://example.com/" + Ready to get started with GitLab? Follow these steps to set up your workspace, plan and commit changes, and deploy your project. + </p> + </div> + </div> + + <div + class="gl-mb-3" + > + <p + class="gl-text-gray-500 gl-mb-2" + data-testid="completion-percentage" + > + 25% completed + </p> + + <div + class="progress" + max="8" + value="2" + > + <div + aria-valuemax="8" + aria-valuemin="0" + aria-valuenow="2" + class="progress-bar" + role="progressbar" + style="width: 25%;" > - Enable require merge approvals - </gl-link-stub> - </span> - </li> - <li> - <span> - <gl-link-stub - href="http://example.com/" + <!----> + </div> + </div> + </div> + + <h2 + class="gl-font-lg gl-mb-3" + > + Set up your workspace + </h2> + + <p + class="gl-text-gray-700 gl-mb-6" + > + Complete these tasks first so you can enjoy GitLab's features to their fullest: + </p> + + <div + class="row row-cols-2 row-cols-md-3 row-cols-lg-4" + > + <div + class="col gl-mb-6" + > + <div + class="gl-card gl-pt-0" > - Submit a merge request (MR) - </gl-link-stub> - </span> - </li> - <li> - <span> - <gl-link-stub - href="http://example.com/" + <!----> + + <div + class="gl-card-body" + > + <div + class="gl-text-right gl-h-5" + > + <svg + aria-hidden="true" + class="gl-text-green-500 gl-icon s16" + data-testid="completed-icon" + > + <use + href="#check-circle-filled" + /> + </svg> + </div> + + <div + class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content" + > + <img + src="http://example.com/images/illustration.svg" + /> + + <h6> + Invite your colleagues + </h6> + + <p + class="gl-font-sm gl-text-gray-700" + > + GitLab works best as a team. Invite your colleague to enjoy all features. + </p> + + <a + class="gl-link" + href="http://example.com/" + rel="noopener noreferrer" + target="_blank" + > + Invite your colleagues + </a> + </div> + </div> + + <!----> + </div> + </div> + + <div + class="col gl-mb-6" + > + <div + class="gl-card gl-pt-0" > - Run a Security scan using CI/CD - </gl-link-stub> - </span> - </li> -</ul> + <!----> + + <div + class="gl-card-body" + > + <div + class="gl-text-right gl-h-5" + > + <svg + aria-hidden="true" + class="gl-text-green-500 gl-icon s16" + data-testid="completed-icon" + > + <use + href="#check-circle-filled" + /> + </svg> + </div> + + <div + class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content" + > + <img + src="http://example.com/images/illustration.svg" + /> + + <h6> + Create or import a repository + </h6> + + <p + class="gl-font-sm gl-text-gray-700" + > + Create or import your first repository into your new project. + </p> + + <a + class="gl-link" + href="http://example.com/" + rel="noopener noreferrer" + target="_blank" + > + Create or import a repository + </a> + </div> + </div> + + <!----> + </div> + </div> + + <div + class="col gl-mb-6" + > + <div + class="gl-card gl-pt-0" + > + <!----> + + <div + class="gl-card-body" + > + <div + class="gl-text-right gl-h-5" + > + <!----> + </div> + + <div + class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content" + > + <img + src="http://example.com/images/illustration.svg" + /> + + <h6> + Set up CI/CD + </h6> + + <p + class="gl-font-sm gl-text-gray-700" + > + Save time by automating your integration and deployment tasks. + </p> + + <a + class="gl-link" + href="http://example.com/" + rel="noopener noreferrer" + target="_blank" + > + Set-up CI/CD + </a> + </div> + </div> + + <!----> + </div> + </div> + + <div + class="col gl-mb-6" + > + <div + class="gl-card gl-pt-0" + > + <!----> + + <div + class="gl-card-body" + > + <div + class="gl-text-right gl-h-5" + > + <!----> + </div> + + <div + class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content" + > + <img + src="http://example.com/images/illustration.svg" + /> + + <h6> + Start a free Ultimate trial + </h6> + + <p + class="gl-font-sm gl-text-gray-700" + > + Try all GitLab features for 30 days, no credit card required. + </p> + + <a + class="gl-link" + href="http://example.com/" + rel="noopener noreferrer" + target="_blank" + > + Try GitLab Ultimate for free + </a> + </div> + </div> + + <!----> + </div> + </div> + + <div + class="col gl-mb-6" + > + <div + class="gl-card gl-pt-0" + > + <!----> + + <div + class="gl-card-body" + > + <div + class="gl-text-right gl-h-5" + > + <span + class="gl-text-gray-500 gl-font-sm gl-font-style-italic" + data-testid="trial-only" + > + Trial only + </span> + </div> + + <div + class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content" + > + <img + src="http://example.com/images/illustration.svg" + /> + + <h6> + Add code owners + </h6> + + <p + class="gl-font-sm gl-text-gray-700" + > + Prevent unexpected changes to important assets by assigning ownership of files and paths. + </p> + + <a + class="gl-link" + href="http://example.com/" + rel="noopener noreferrer" + target="_blank" + > + Add code owners + </a> + </div> + </div> + + <!----> + </div> + </div> + + <div + class="col gl-mb-6" + > + <div + class="gl-card gl-pt-0" + > + <!----> + + <div + class="gl-card-body" + > + <div + class="gl-text-right gl-h-5" + > + <span + class="gl-text-gray-500 gl-font-sm gl-font-style-italic" + data-testid="trial-only" + > + Trial only + </span> + </div> + + <div + class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content" + > + <img + src="http://example.com/images/illustration.svg" + /> + + <h6> + Add merge request approval + </h6> + + <p + class="gl-font-sm gl-text-gray-700" + > + Route code reviews to the right reviewers, every time. + </p> + + <a + class="gl-link" + href="http://example.com/" + rel="noopener noreferrer" + target="_blank" + > + Enable require merge approvals + </a> + </div> + </div> + + <!----> + </div> + </div> + </div> + + <h2 + class="gl-font-lg gl-mb-3" + > + Plan and execute + </h2> + + <p + class="gl-text-gray-700 gl-mb-6" + > + Create a workflow for your new workspace, and learn how GitLab features work together: + </p> + + <div + class="row row-cols-2 row-cols-md-3 row-cols-lg-4" + > + <div + class="col gl-mb-6" + > + <div + class="gl-card gl-pt-0" + > + <!----> + + <div + class="gl-card-body" + > + <div + class="gl-text-right gl-h-5" + > + <!----> + </div> + + <div + class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content" + > + <img + src="http://example.com/images/illustration.svg" + /> + + <h6> + Submit a merge request + </h6> + + <p + class="gl-font-sm gl-text-gray-700" + > + Review and edit proposed changes to source code. + </p> + + <a + class="gl-link" + href="http://example.com/" + rel="noopener noreferrer" + target="_blank" + > + Submit a merge request (MR) + </a> + </div> + </div> + + <!----> + </div> + </div> + </div> + + <h2 + class="gl-font-lg gl-mb-3" + > + Deploy + </h2> + + <p + class="gl-text-gray-700 gl-mb-6" + > + Use your new GitLab workflow to deploy your application, monitor its health, and keep it secure: + </p> + + <div + class="row row-cols-2 row-cols-lg-4 g-2 g-lg-3" + > + <div + class="col gl-mb-6" + > + <div + class="gl-card gl-pt-0" + > + <!----> + + <div + class="gl-card-body" + > + <div + class="gl-text-right gl-h-5" + > + <!----> + </div> + + <div + class="gl-text-center gl-display-flex gl-justify-content-center gl-align-items-center gl-flex-direction-column learn-gitlab-info-card-content" + > + <img + src="http://example.com/images/illustration.svg" + /> + + <h6> + Run a security scan + </h6> + + <p + class="gl-font-sm gl-text-gray-700" + > + Scan your code to uncover vulnerabilities before deploying. + </p> + + <a + class="gl-link" + href="http://example.com/" + rel="noopener noreferrer" + target="_blank" + > + Run a Security scan + </a> + </div> + </div> + + <!----> + </div> + </div> + </div> +</div> `; diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_a_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_a_spec.js index ddc5339e7e0..2154358de51 100644 --- a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_a_spec.js +++ b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_a_spec.js @@ -1,41 +1,6 @@ import { shallowMount } from '@vue/test-utils'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import LearnGitlabA from '~/pages/projects/learn_gitlab/components/learn_gitlab_a.vue'; - -const TEST_ACTIONS = { - gitWrite: { - url: 'http://example.com/', - completed: true, - }, - userAdded: { - url: 'http://example.com/', - completed: true, - }, - pipelineCreated: { - url: 'http://example.com/', - completed: true, - }, - trialStarted: { - url: 'http://example.com/', - completed: false, - }, - codeOwnersEnabled: { - url: 'http://example.com/', - completed: false, - }, - requiredMrApprovalsEnabled: { - url: 'http://example.com/', - completed: false, - }, - mergeRequestCreated: { - url: 'http://example.com/', - completed: false, - }, - securityScanEnabled: { - url: 'http://example.com/', - completed: false, - }, -}; +import { testActions } from './mock_data'; describe('Learn GitLab Design A', () => { let wrapper; @@ -46,13 +11,7 @@ describe('Learn GitLab Design A', () => { }); const createWrapper = () => { - wrapper = extendedWrapper( - shallowMount(LearnGitlabA, { - propsData: { - actions: TEST_ACTIONS, - }, - }), - ); + wrapper = shallowMount(LearnGitlabA, { propsData: { actions: testActions } }); }; it('should render the loading state', () => { diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_b_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_b_spec.js index be4f5768402..fbb989fae32 100644 --- a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_b_spec.js +++ b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_b_spec.js @@ -1,63 +1,38 @@ -import { shallowMount } from '@vue/test-utils'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import LearnGitlabA from '~/pages/projects/learn_gitlab/components/learn_gitlab_a.vue'; - -const TEST_ACTIONS = { - gitWrite: { - url: 'http://example.com/', - completed: true, - }, - userAdded: { - url: 'http://example.com/', - completed: true, - }, - pipelineCreated: { - url: 'http://example.com/', - completed: true, - }, - trialStarted: { - url: 'http://example.com/', - completed: false, - }, - codeOwnersEnabled: { - url: 'http://example.com/', - completed: false, - }, - requiredMrApprovalsEnabled: { - url: 'http://example.com/', - completed: false, - }, - mergeRequestCreated: { - url: 'http://example.com/', - completed: false, - }, - securityScanEnabled: { - url: 'http://example.com/', - completed: false, - }, -}; +import { GlProgressBar } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import LearnGitlabB from '~/pages/projects/learn_gitlab/components/learn_gitlab_b.vue'; +import { testActions } from './mock_data'; describe('Learn GitLab Design B', () => { let wrapper; + const createWrapper = () => { + wrapper = mount(LearnGitlabB, { propsData: { actions: testActions } }); + }; + + beforeEach(() => { + createWrapper(); + }); + afterEach(() => { wrapper.destroy(); wrapper = null; }); - const createWrapper = () => { - wrapper = extendedWrapper( - shallowMount(LearnGitlabA, { - propsData: { - actions: TEST_ACTIONS, - }, - }), - ); - }; + it('renders correctly', () => { + expect(wrapper.element).toMatchSnapshot(); + }); - it('should render the loading state', () => { - createWrapper(); + it('renders the progress percentage', () => { + const text = wrapper.find('[data-testid="completion-percentage"]').text(); - expect(wrapper.element).toMatchSnapshot(); + expect(text).toEqual('25% completed'); + }); + + it('renders the progress bar with correct values', () => { + const progressBar = wrapper.find(GlProgressBar); + + expect(progressBar.attributes('value')).toBe('2'); + expect(progressBar.attributes('max')).toBe('8'); }); }); diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_info_card_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_info_card_spec.js new file mode 100644 index 00000000000..ad4bc826a9d --- /dev/null +++ b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_info_card_spec.js @@ -0,0 +1,57 @@ +import { shallowMount } from '@vue/test-utils'; +import LearnGitlabInfoCard from '~/pages/projects/learn_gitlab/components/learn_gitlab_info_card.vue'; + +const defaultProps = { + title: 'Create Repository', + description: 'Some description', + actionLabel: 'Create Repository now', + url: 'https://example.com', + completed: false, + svg: 'https://example.com/illustration.svg', +}; + +describe('Learn GitLab Info Card', () => { + let wrapper; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const createWrapper = (props = {}) => { + wrapper = shallowMount(LearnGitlabInfoCard, { + propsData: { ...defaultProps, ...props }, + }); + }; + + it('renders no icon when not completed', () => { + createWrapper({ completed: false }); + + expect(wrapper.find('[data-testid="completed-icon"]').exists()).toBe(false); + }); + + it('renders the completion icon when completed', () => { + createWrapper({ completed: true }); + + expect(wrapper.find('[data-testid="completed-icon"]').exists()).toBe(true); + }); + + it('renders no trial only when it is not required', () => { + createWrapper(); + + expect(wrapper.find('[data-testid="trial-only"]').exists()).toBe(false); + }); + + it('renders trial only when trial is required', () => { + createWrapper({ trialRequired: true }); + + expect(wrapper.find('[data-testid="trial-only"]').exists()).toBe(true); + }); + + it('renders completion icon when completed a trial-only feature', () => { + createWrapper({ trialRequired: true, completed: true }); + + expect(wrapper.find('[data-testid="trial-only"]').exists()).toBe(false); + expect(wrapper.find('[data-testid="completed-icon"]').exists()).toBe(true); + }); +}); diff --git a/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js b/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js new file mode 100644 index 00000000000..caac667e2b1 --- /dev/null +++ b/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js @@ -0,0 +1,42 @@ +export const testActions = { + gitWrite: { + url: 'http://example.com/', + completed: true, + svg: 'http://example.com/images/illustration.svg', + }, + userAdded: { + url: 'http://example.com/', + completed: true, + svg: 'http://example.com/images/illustration.svg', + }, + pipelineCreated: { + url: 'http://example.com/', + completed: false, + svg: 'http://example.com/images/illustration.svg', + }, + trialStarted: { + url: 'http://example.com/', + completed: false, + svg: 'http://example.com/images/illustration.svg', + }, + codeOwnersEnabled: { + url: 'http://example.com/', + completed: false, + svg: 'http://example.com/images/illustration.svg', + }, + requiredMrApprovalsEnabled: { + url: 'http://example.com/', + completed: false, + svg: 'http://example.com/images/illustration.svg', + }, + mergeRequestCreated: { + url: 'http://example.com/', + completed: false, + svg: 'http://example.com/images/illustration.svg', + }, + securityScanEnabled: { + url: 'http://example.com/', + completed: false, + svg: 'http://example.com/images/illustration.svg', + }, +}; diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js index 46d0452f437..b44ba0e4543 100644 --- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js +++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js @@ -193,6 +193,7 @@ describe('Pipeline editor app component', () => { describe('and the commit mutation succeeds', () => { beforeEach(() => { + window.scrollTo = jest.fn(); createComponent(); findEditorHome().vm.$emit('commit', { type: COMMIT_SUCCESS }); @@ -201,11 +202,16 @@ describe('Pipeline editor app component', () => { it('shows a confirmation message', () => { expect(findAlert().text()).toBe(wrapper.vm.$options.successTexts[COMMIT_SUCCESS]); }); + + it('scrolls to the top of the page to bring attention to the confirmation message', () => { + expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' }); + }); }); describe('and the commit mutation fails', () => { const commitFailedReasons = ['Commit failed']; beforeEach(() => { + window.scrollTo = jest.fn(); createComponent(); findEditorHome().vm.$emit('showError', { @@ -219,11 +225,17 @@ describe('Pipeline editor app component', () => { `${updateFailureMessage} ${commitFailedReasons[0]}`, ); }); + + it('scrolls to the top of the page to bring attention to the error message', () => { + expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' }); + }); }); + describe('when an unknown error occurs', () => { const unknownReasons = ['Commit failed']; beforeEach(() => { + window.scrollTo = jest.fn(); createComponent(); findEditorHome().vm.$emit('showError', { @@ -237,6 +249,10 @@ describe('Pipeline editor app component', () => { `${updateFailureMessage} ${unknownReasons[0]}`, ); }); + + it('scrolls to the top of the page to bring attention to the error message', () => { + expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' }); + }); }); }); }); diff --git a/spec/helpers/learn_gitlab_helper_spec.rb b/spec/helpers/learn_gitlab_helper_spec.rb index f789eb9d940..6cee8a9191c 100644 --- a/spec/helpers/learn_gitlab_helper_spec.rb +++ b/spec/helpers/learn_gitlab_helper_spec.rb @@ -41,11 +41,13 @@ RSpec.describe LearnGitlabHelper do it 'sets correct path and completion status' do expect(onboarding_actions_data[:git_write]).to eq({ url: project_issue_url(project, LearnGitlabHelper::ACTION_ISSUE_IDS[:git_write]), - completed: true + completed: true, + svg: helper.image_path("learn_gitlab/git_write.svg") }) expect(onboarding_actions_data[:pipeline_created]).to eq({ url: project_issue_url(project, LearnGitlabHelper::ACTION_ISSUE_IDS[:pipeline_created]), - completed: false + completed: false, + svg: helper.image_path("learn_gitlab/pipeline_created.svg") }) end end diff --git a/spec/helpers/services_helper_spec.rb b/spec/helpers/services_helper_spec.rb index 3b08393ebdf..1726a8362a7 100644 --- a/spec/helpers/services_helper_spec.rb +++ b/spec/helpers/services_helper_spec.rb @@ -27,26 +27,17 @@ RSpec.describe ServicesHelper do ] end - let(:jira_fields) { %i[jira_issue_transition_id] } - subject { helper.integration_form_data(integration) } context 'Slack service' do let(:integration) { build(:slack_service) } it { is_expected.to include(*fields) } - it { is_expected.not_to include(*jira_fields) } specify do expect(subject[:reset_path]).to eq(helper.scoped_reset_integration_path(integration)) end end - - context 'Jira service' do - let(:integration) { build(:jira_service) } - - it { is_expected.to include(*fields, *jira_fields) } - end end describe '#scoped_reset_integration_path' do diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb index 2e43f22830a..42fbb88cceb 100644 --- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb @@ -32,6 +32,7 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do it 'saves given value' do expect(subject[:key]).to eq variable_key expect(subject[:value]).to eq expected_value + expect(subject.value).to eq expected_value end end diff --git a/spec/lib/gitlab/ci/variables/collection_spec.rb b/spec/lib/gitlab/ci/variables/collection_spec.rb index 670d836b4e8..7a275b69fd8 100644 --- a/spec/lib/gitlab/ci/variables/collection_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection_spec.rb @@ -98,6 +98,50 @@ RSpec.describe Gitlab::Ci::Variables::Collection do end end + describe '#[]' do + variable = { key: 'VAR', value: 'value', public: true, masked: false } + + collection = described_class.new([variable]) + + it 'returns nil for a non-existent variable name' do + expect(collection['UNKNOWN_VAR']).to be_nil + end + + it 'returns Item for an existent variable name' do + expect(collection['VAR']).to be_an_instance_of(Gitlab::Ci::Variables::Collection::Item) + expect(collection['VAR'].to_runner_variable).to eq(variable) + end + end + + describe '#size' do + it 'returns zero for empty collection' do + collection = described_class.new([]) + + expect(collection.size).to eq(0) + end + + it 'returns 2 for collection with 2 variables' do + collection = described_class.new( + [ + { key: 'VAR1', value: 'value', public: true, masked: false }, + { key: 'VAR2', value: 'value', public: true, masked: false } + ]) + + expect(collection.size).to eq(2) + end + + it 'returns 3 for collection with 2 duplicate variables' do + collection = described_class.new( + [ + { key: 'VAR1', value: 'value', public: true, masked: false }, + { key: 'VAR2', value: 'value', public: true, masked: false }, + { key: 'VAR1', value: 'value', public: true, masked: false } + ]) + + expect(collection.size).to eq(3) + end + end + describe '#to_runner_variables' do it 'creates an array of hashes in a runner-compatible format' do collection = described_class.new([{ key: 'TEST', value: '1' }]) diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb index a4b046a9ed5..1cd9411f475 100644 --- a/spec/lib/gitlab/experimentation_spec.rb +++ b/spec/lib/gitlab/experimentation_spec.rb @@ -7,7 +7,6 @@ require 'spec_helper' RSpec.describe Gitlab::Experimentation::EXPERIMENTS do it 'temporarily ensures we know what experiments exist for backwards compatibility' do expected_experiment_keys = [ - :ci_notification_dot, :upgrade_link_in_user_menu_a, :invite_members_version_a, :invite_members_version_b, diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb index 0a2b04f1a7c..9e65635da91 100644 --- a/spec/models/pages/lookup_path_spec.rb +++ b/spec/models/pages/lookup_path_spec.rb @@ -117,14 +117,6 @@ RSpec.describe Pages::LookupPath do end end - context 'when pages_serve_from_deployments feature flag is disabled' do - before do - stub_feature_flags(pages_serve_from_deployments: false) - end - - include_examples 'uses disk storage' - end - context 'when deployment were created during migration' do before do allow(deployment).to receive(:migrated?).and_return(true) diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index ef0bc930152..3fc39fd3266 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -82,8 +82,11 @@ RSpec.describe JiraService do subject(:fields) { service.fields } - it 'returns custom fields' do - expect(fields.pluck(:name)).to eq(%w[url api_url username password]) + it 'includes transition help link' do + transition_id_field = fields.find { |field| field[:name] == 'jira_issue_transition_id' } + + expect(transition_id_field[:title]).to eq('Jira workflow transition IDs') + expect(transition_id_field[:help]).to include('/help/user/project/integrations/jira') end end @@ -457,10 +460,10 @@ RSpec.describe JiraService do end context 'with options' do - let(:issue_url) { "#{url}/rest/api/2/issue/#{issue_key}?expand=renderedFields,transitions" } + let(:issue_url) { "#{url}/rest/api/2/issue/#{issue_key}?expand=renderedFields" } it 'calls the Jira API with the options to get the issue' do - jira_service.find_issue(issue_key, rendered_fields: true, transitions: true) + jira_service.find_issue(issue_key, rendered_fields: true) expect(WebMock).to have_requested(:get, issue_url) end @@ -502,7 +505,7 @@ RSpec.describe JiraService do allow(closed_issue).to receive(:resolution).and_return(true) allow(JIRA::Resource::Issue).to receive(:find).and_return(open_issue, closed_issue) - allow_any_instance_of(JIRA::Resource::Issue).to receive(:key).and_return(issue_key) + allow_any_instance_of(JIRA::Resource::Issue).to receive(:key).and_return('JIRA-123') allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) WebMock.stub_request(:get, issue_url).with(basic_auth: %w(jira-username jira-password)) @@ -661,49 +664,6 @@ RSpec.describe JiraService do ).once end - context 'when using automatic issue transitions' do - let(:transitions) do - [ - { id: '1' }, - { id: '2', to: { statusCategory: { key: 'new' } } }, - { id: '3', to: { statusCategory: { key: 'done' } } }, - { id: '4', to: { statusCategory: { key: 'done' } } } - ] - end - - before do - allow(jira_service).to receive_messages(jira_issue_transition_id: '') - - close_issue - end - - it 'uses the next transition with a status category of done' do - expect(WebMock).to have_requested(:post, transitions_url).with( - body: /"id":"3"/ - ).once - end - - context 'when no done transition is available' do - let(:transitions) do - [ - { id: '1', to: { statusCategory: { key: 'new' } } } - ] - end - - it 'does not attempt to transition' do - expect(WebMock).not_to have_requested(:post, transitions_url) - end - end - - context 'when no valid transitions are returned' do - let(:transitions) { 'foo' } - - it 'does not attempt to transition' do - expect(WebMock).not_to have_requested(:post, transitions_url) - end - end - end - context 'when using multiple transition ids' do before do allow(jira_service).to receive_messages(jira_issue_transition_id: '1,2,3') diff --git a/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb index 7dd897f6466..b5aaf304812 100644 --- a/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb @@ -9,8 +9,9 @@ RSpec.describe 'Adding a DiffNote' do let(:noteable) { create(:merge_request, source_project: project, target_project: project) } let(:project) { create(:project, :repository) } let(:diff_refs) { noteable.diff_refs } - let(:mutation) do - variables = { + + let(:base_variables) do + { noteable_id: GitlabSchema.id_from_object(noteable).to_s, body: 'Body text', position: { @@ -18,16 +19,16 @@ RSpec.describe 'Adding a DiffNote' do old_path: 'files/ruby/popen.rb', new_path: 'files/ruby/popen2.rb' }, - new_line: 14, base_sha: diff_refs.base_sha, head_sha: diff_refs.head_sha, start_sha: diff_refs.start_sha } } - - graphql_mutation(:create_diff_note, variables) end + let(:variables) { base_variables.deep_merge({ position: { new_line: 14 } }) } + let(:mutation) { graphql_mutation(:create_diff_note, variables) } + def mutation_response graphql_mutation_response(:create_diff_note) end @@ -41,6 +42,18 @@ RSpec.describe 'Adding a DiffNote' do it_behaves_like 'a Note mutation that creates a Note' + context 'add comment to old line' do + let(:variables) { base_variables.deep_merge({ position: { old_line: 14 } }) } + + it_behaves_like 'a Note mutation that creates a Note' + end + + context 'add a comment with a position without lines' do + let(:variables) { base_variables } + + it_behaves_like 'a Note mutation that does not create a Note' + end + it_behaves_like 'a Note mutation when there are active record validation errors', model: DiffNote it_behaves_like 'a Note mutation when there are rate limit validation errors' diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index d70a8bd692d..44b7a38ed2b 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -3044,18 +3044,6 @@ RSpec.describe API::Users do expect(response).to have_gitlab_http_status(:bad_request) end - - context 'when the clear_status_with_quick_options feature flag is disabled' do - before do - stub_feature_flags(clear_status_with_quick_options: false) - end - - it 'does not persist clear_status_at' do - put api('/user/status', user), params: { emoji: 'smirk', message: 'hello world', clear_status_after: '3_hours' } - - expect(user.status.reload.clear_status_at).to be_nil - end - end end end diff --git a/spec/services/alert_management/process_prometheus_alert_service_spec.rb b/spec/services/alert_management/process_prometheus_alert_service_spec.rb index 288a33b71cd..9bd71ea6f64 100644 --- a/spec/services/alert_management/process_prometheus_alert_service_spec.rb +++ b/spec/services/alert_management/process_prometheus_alert_service_spec.rb @@ -11,6 +11,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do describe '#execute' do let(:service) { described_class.new(project, payload) } + let(:source) { 'Prometheus' } let(:auto_close_incident) { true } let(:create_issue) { true } let(:send_email) { true } @@ -31,7 +32,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do subject(:execute) { service.execute } context 'when alert payload is valid' do - let(:parsed_payload) { Gitlab::AlertManagement::Payload.parse(project, payload, monitoring_tool: 'Prometheus') } + let(:parsed_payload) { Gitlab::AlertManagement::Payload.parse(project, payload, monitoring_tool: source) } let(:fingerprint) { parsed_payload.gitlab_fingerprint } let(:payload) do { @@ -112,9 +113,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do it_behaves_like 'Alert Notification Service sends notification email' it_behaves_like 'processes incident issues' - it 'creates a system note corresponding to alert creation' do - expect { subject }.to change(Note, :count).by(1) - end + it_behaves_like 'creates single system note based on the source of the alert' context 'when auto-alert creation is disabled' do let(:create_issue) { false } @@ -158,17 +157,20 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do context 'when Prometheus alert status is resolved' do let(:status) { 'resolved' } - let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint) } + let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint, monitoring_tool: source) } context 'when auto_resolve_incident set to true' do context 'when status can be changed' do it_behaves_like 'Alert Notification Service sends notification email' it_behaves_like 'does not process incident issues' - it 'resolves an existing alert' do + it 'resolves an existing alert without error' do + expect(Gitlab::AppLogger).not_to receive(:warn) expect { execute }.to change { alert.reload.resolved? }.to(true) end + it_behaves_like 'creates status-change system note for an auto-resolved alert' + context 'existing issue' do let!(:alert) { create(:alert_management_alert, :with_issue, project: project, fingerprint: fingerprint) } @@ -215,6 +217,8 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do it 'does not resolve an existing alert' do expect { execute }.not_to change { alert.reload.resolved? } end + + it_behaves_like 'creates single system note based on the source of the alert' end context 'when emails are disabled' do diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index 88770c8095b..d45d6120cf6 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -590,22 +590,14 @@ module Ci before do allow(Time).to receive(:now).and_return(current_time) - - # Stub defaults for any metrics other than the ones we're testing - allow(Gitlab::Metrics).to receive(:counter) - .with(any_args) - .and_return(Gitlab::Metrics::NullMetric.instance) - allow(Gitlab::Metrics).to receive(:histogram) - .with(any_args) - .and_return(Gitlab::Metrics::NullMetric.instance) - # Stub tested metrics - allow(Gitlab::Metrics).to receive(:counter) - .with(:job_register_attempts_total, anything) - .and_return(attempt_counter) - allow(Gitlab::Metrics).to receive(:histogram) - .with(:job_queue_duration_seconds, anything, anything, anything) - .and_return(job_queue_duration_seconds) + allow(Gitlab::Ci::Queue::Metrics) + .to receive(:attempt_counter) + .and_return(attempt_counter) + + allow(Gitlab::Ci::Queue::Metrics) + .to receive(:job_queue_duration_seconds) + .and_return(job_queue_duration_seconds) project.update!(shared_runners_enabled: true) pending_job.update!(created_at: current_time - 3600, queued_at: current_time - 1800) @@ -655,7 +647,7 @@ module Ci context 'when shared runner is used' do let(:runner) { create(:ci_runner, :instance, tag_list: %w(tag1 tag2)) } let(:expected_shared_runner) { true } - let(:expected_shard) { Ci::RegisterJobService::DEFAULT_METRICS_SHARD } + let(:expected_shard) { ::Gitlab::Ci::Queue::Metrics::DEFAULT_METRICS_SHARD } let(:expected_jobs_running_for_project_first_job) { 0 } let(:expected_jobs_running_for_project_third_job) { 2 } @@ -694,7 +686,7 @@ module Ci context 'when specific runner is used' do let(:runner) { create(:ci_runner, :project, projects: [project], tag_list: %w(tag1 metrics_shard::shard_tag tag2)) } let(:expected_shared_runner) { false } - let(:expected_shard) { Ci::RegisterJobService::DEFAULT_METRICS_SHARD } + let(:expected_shard) { ::Gitlab::Ci::Queue::Metrics::DEFAULT_METRICS_SHARD } let(:expected_jobs_running_for_project_first_job) { '+Inf' } let(:expected_jobs_running_for_project_third_job) { '+Inf' } diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 80de453cd8b..611f12c8146 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -166,6 +166,20 @@ RSpec.describe MergeRequests::MergeService do service.execute(merge_request) end + context 'when jira_issue_transition_id is not present' do + before do + allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(nil) + end + + it 'does not close issue' do + jira_tracker.update!(jira_issue_transition_id: nil) + + expect_any_instance_of(JiraService).not_to receive(:transition_issue) + + service.execute(merge_request) + end + end + context 'wrong issue markdown' do it 'does not close issues on Jira issue tracker' do jira_issue = ExternalIssue.new('#JIRA-123', project) diff --git a/spec/services/projects/alerting/notify_service_spec.rb b/spec/services/projects/alerting/notify_service_spec.rb index 4e366fce0d9..c272ce13132 100644 --- a/spec/services/projects/alerting/notify_service_spec.rb +++ b/spec/services/projects/alerting/notify_service_spec.rb @@ -119,6 +119,7 @@ RSpec.describe Projects::Alerting::NotifyService do end it_behaves_like 'does not an create alert management alert' + it_behaves_like 'creates single system note based on the source of the alert' context 'auto_close_enabled setting enabled' do let(:auto_close_enabled) { true } @@ -131,6 +132,8 @@ RSpec.describe Projects::Alerting::NotifyService do expect(alert.ended_at).to eql(ended_at) end + it_behaves_like 'creates status-change system note for an auto-resolved alert' + context 'related issue exists' do let(:alert) { create(:alert_management_alert, :with_issue, project: project, fingerprint: fingerprint_sha) } let(:issue) { alert.issue } @@ -209,10 +212,7 @@ RSpec.describe Projects::Alerting::NotifyService do ) end - it 'creates a system note corresponding to alert creation' do - expect { subject }.to change(Note, :count).by(1) - expect(Note.last.note).to include(source) - end + it_behaves_like 'creates single system note based on the source of the alert' end end diff --git a/spec/services/projects/update_pages_configuration_service_spec.rb b/spec/services/projects/update_pages_configuration_service_spec.rb index 294de813e02..9ef66a10f0d 100644 --- a/spec/services/projects/update_pages_configuration_service_spec.rb +++ b/spec/services/projects/update_pages_configuration_service_spec.rb @@ -26,11 +26,18 @@ RSpec.describe Projects::UpdatePagesConfigurationService do context 'when configuration changes' do it 'updates the config and reloads the daemon' do - allow(service).to receive(:update_file).and_call_original - expect(service).to receive(:update_file).with(file.path, an_instance_of(String)) .and_call_original - expect(service).to receive(:reload_daemon).and_call_original + allow(service).to receive(:update_file).with(File.join(::Settings.pages.path, '.update'), + an_instance_of(String)).and_call_original + + expect(subject).to include(status: :success) + end + + it "doesn't update configuration files if updates on legacy storage are disabled" do + stub_feature_flags(pages_update_legacy_storage: false) + + expect(service).not_to receive(:update_file) expect(subject).to include(status: :success) end @@ -42,8 +49,8 @@ RSpec.describe Projects::UpdatePagesConfigurationService do service.execute end - it 'does not update the .update file' do - expect(service).not_to receive(:reload_daemon) + it 'does not update anything' do + expect(service).not_to receive(:update_file) expect(subject).to include(status: :success) end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index df4880dfa13..54cef164f1c 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -779,4 +779,17 @@ RSpec.describe SystemNoteService do described_class.change_incident_severity(incident, author) end end + + describe '.log_resolving_alert' do + let(:alert) { build(:alert_management_alert) } + let(:monitoring_tool) { 'Prometheus' } + + it 'calls AlertManagementService' do + expect_next_instance_of(SystemNotes::AlertManagementService) do |service| + expect(service).to receive(:log_resolving_alert).with(monitoring_tool) + end + + described_class.log_resolving_alert(alert, monitoring_tool) + end + end end diff --git a/spec/services/system_notes/alert_management_service_spec.rb b/spec/services/system_notes/alert_management_service_spec.rb index 4ebaa54534c..fc71799d8c5 100644 --- a/spec/services/system_notes/alert_management_service_spec.rb +++ b/spec/services/system_notes/alert_management_service_spec.rb @@ -59,4 +59,17 @@ RSpec.describe ::SystemNotes::AlertManagementService do expect(subject.note).to eq("changed the status to **Resolved** by closing issue #{issue.to_reference(project)}") end end + + describe '#log_resolving_alert' do + subject { described_class.new(noteable: noteable, project: project).log_resolving_alert('Some Service') } + + it_behaves_like 'a system note' do + let(:author) { User.alert_bot } + let(:action) { 'new_alert_added' } + end + + it 'has the appropriate message' do + expect(subject.note).to eq('logged a resolving alert from **Some Service**') + end + end end diff --git a/spec/support/shared_contexts/project_service_jira_context.rb b/spec/support/shared_contexts/project_service_jira_context.rb index 54bb9fd108e..8e01de70846 100644 --- a/spec/support/shared_contexts/project_service_jira_context.rb +++ b/spec/support/shared_contexts/project_service_jira_context.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true RSpec.shared_context 'project service Jira context' do - let(:url) { 'https://jira.example.com' } - let(:test_url) { 'https://jira.example.com/rest/api/2/serverInfo' } + let(:url) { 'http://jira.example.com' } + let(:test_url) { 'http://jira.example.com/rest/api/2/serverInfo' } def fill_form(disable: false) click_active_checkbox if disable @@ -10,5 +10,6 @@ RSpec.shared_context 'project service Jira context' do fill_in 'service_url', with: url fill_in 'service_username', with: 'username' fill_in 'service_password', with: 'password' + fill_in 'service_jira_issue_transition_id', with: '25' end end diff --git a/spec/support/shared_contexts/project_service_shared_context.rb b/spec/support/shared_contexts/project_service_shared_context.rb index a8e75c624e8..b4b9ab456e0 100644 --- a/spec/support/shared_contexts/project_service_shared_context.rb +++ b/spec/support/shared_contexts/project_service_shared_context.rb @@ -15,10 +15,7 @@ RSpec.shared_context 'project service activation' do def visit_project_integration(name) visit_project_integrations - - within('#content-body') do - click_link(name) - end + click_link(name) end def click_active_checkbox diff --git a/spec/support/shared_examples/alert_notification_service_shared_examples.rb b/spec/support/shared_examples/alert_notification_service_shared_examples.rb index 7bd6df8c608..fc935effe0e 100644 --- a/spec/support/shared_examples/alert_notification_service_shared_examples.rb +++ b/spec/support/shared_examples/alert_notification_service_shared_examples.rb @@ -27,3 +27,18 @@ RSpec.shared_examples 'Alert Notification Service sends no notifications' do |ht end end end + +RSpec.shared_examples 'creates status-change system note for an auto-resolved alert' do + it 'has 2 new system notes' do + expect { subject }.to change(Note, :count).by(2) + expect(Note.last.note).to include('Resolved') + end +end + +# Requires `source` to be defined +RSpec.shared_examples 'creates single system note based on the source of the alert' do + it 'has one new system note' do + expect { subject }.to change(Note, :count).by(1) + expect(Note.last.note).to include(source) + end +end diff --git a/spec/workers/pages_update_configuration_worker_spec.rb b/spec/workers/pages_update_configuration_worker_spec.rb index 87bbff1a28b..ff3727646c7 100644 --- a/spec/workers/pages_update_configuration_worker_spec.rb +++ b/spec/workers/pages_update_configuration_worker_spec.rb @@ -2,9 +2,9 @@ require "spec_helper" RSpec.describe PagesUpdateConfigurationWorker do - describe "#perform" do - let_it_be(:project) { create(:project) } + let_it_be(:project) { create(:project) } + describe "#perform" do it "does not break if the project doesn't exist" do expect { subject.perform(-1) }.not_to raise_error end @@ -42,4 +42,22 @@ RSpec.describe PagesUpdateConfigurationWorker do end end end + + describe '#perform_async' do + it "calls the correct service", :sidekiq_inline do + expect_next_instance_of(Projects::UpdatePagesConfigurationService, project) do |service| + expect(service).to receive(:execute).and_return(status: :success) + end + + described_class.perform_async(project.id) + end + + it "doesn't schedule a worker if updates on legacy storage are disabled", :sidekiq_inline do + stub_feature_flags(pages_update_legacy_storage: false) + + expect(Projects::UpdatePagesConfigurationService).not_to receive(:new) + + described_class.perform_async(project.id) + end + end end |