diff options
Diffstat (limited to 'spec')
-rw-r--r-- | spec/factories/customer_relations/contacts.rb | 14 | ||||
-rw-r--r-- | spec/features/atom/issues_spec.rb | 84 | ||||
-rw-r--r-- | spec/features/atom/merge_requests_spec.rb | 88 | ||||
-rw-r--r-- | spec/features/issues/rss_spec.rb | 39 | ||||
-rw-r--r-- | spec/features/merge_requests/rss_spec.rb | 46 | ||||
-rw-r--r-- | spec/frontend/lib/utils/text_markdown_spec.js | 19 | ||||
-rw-r--r-- | spec/lib/error_tracking/collector/dsn_spec.rb | 26 | ||||
-rw-r--r-- | spec/models/ci/pipeline_spec.rb | 45 | ||||
-rw-r--r-- | spec/models/customer_relations/contact_spec.rb | 37 | ||||
-rw-r--r-- | spec/models/customer_relations/organization_spec.rb | 2 | ||||
-rw-r--r-- | spec/requests/jira_connect/installations_controller_spec.rb | 95 | ||||
-rw-r--r-- | spec/support/shared_examples/features/atom/issuable_shared_examples.rb | 12 | ||||
-rw-r--r-- | spec/support/shared_examples/features/rss_shared_examples.rb | 20 |
13 files changed, 418 insertions, 109 deletions
diff --git a/spec/factories/customer_relations/contacts.rb b/spec/factories/customer_relations/contacts.rb new file mode 100644 index 00000000000..437f8feea48 --- /dev/null +++ b/spec/factories/customer_relations/contacts.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :contact, class: 'CustomerRelations::Contact' do + group + + first_name { generate(:name) } + last_name { generate(:name) } + + trait :with_organization do + organization + end + end +end diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb index 13798a94fe9..e6a862ddccc 100644 --- a/spec/features/atom/issues_spec.rb +++ b/spec/features/atom/issues_spec.rb @@ -4,61 +4,75 @@ require 'spec_helper' RSpec.describe 'Issues Feed' do describe 'GET /issues' do - let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } - let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } - let!(:group) { create(:group) } - let!(:project) { create(:project) } - let!(:issue) { create(:issue, author: user, assignees: [assignee], project: project) } - - before do + let_it_be_with_reload(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } + let_it_be(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project) } + let_it_be(:issue) { create(:issue, author: user, assignees: [assignee], project: project, due_date: Date.today) } + let_it_be(:issuable) { issue } # "alias" for shared examples + + before_all do project.add_developer(user) group.add_developer(user) end + RSpec.shared_examples 'an authenticated issue atom feed' do + it 'renders atom feed with additional issue information' do + expect(body).to have_selector('title', text: "#{project.name} issues") + expect(body).to have_selector('due_date', text: issue.due_date) + end + end + context 'when authenticated' do - it 'renders atom feed' do + before do sign_in user visit project_issues_path(project, :atom) - - expect(response_headers['Content-Type']) - .to have_content('application/atom+xml') - expect(body).to have_selector('title', text: "#{project.name} issues") - expect(body).to have_selector('author email', text: issue.author_public_email) - expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('entry summary', text: issue.title) end + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated issue atom feed' end context 'when authenticated via personal access token' do - it 'renders atom feed' do + before do personal_access_token = create(:personal_access_token, user: user) visit project_issues_path(project, :atom, - private_token: personal_access_token.token) - - expect(response_headers['Content-Type']) - .to have_content('application/atom+xml') - expect(body).to have_selector('title', text: "#{project.name} issues") - expect(body).to have_selector('author email', text: issue.author_public_email) - expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('entry summary', text: issue.title) + private_token: personal_access_token.token) end + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated issue atom feed' end context 'when authenticated via feed token' do - it 'renders atom feed' do + before do visit project_issues_path(project, :atom, - feed_token: user.feed_token) + feed_token: user.feed_token) + end - expect(response_headers['Content-Type']) - .to have_content('application/atom+xml') - expect(body).to have_selector('title', text: "#{project.name} issues") - expect(body).to have_selector('author email', text: issue.author_public_email) - expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('entry summary', text: issue.title) + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated issue atom feed' + end + + context 'when not authenticated' do + before do + visit project_issues_path(project, :atom) + end + + context 'and the project is private' do + it 'redirects to login page' do + expect(page).to have_current_path(new_user_session_path) + end + end + + context 'and the project is public' do + let_it_be(:project) { create(:project, :public) } + let_it_be(:issue) { create(:issue, author: user, assignees: [assignee], project: project, due_date: Date.today) } + let_it_be(:issuable) { issue } # "alias" for shared examples + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated issue atom feed' end end diff --git a/spec/features/atom/merge_requests_spec.rb b/spec/features/atom/merge_requests_spec.rb new file mode 100644 index 00000000000..29f0b4660c9 --- /dev/null +++ b/spec/features/atom/merge_requests_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Merge Requests Feed' do + describe 'GET /merge_requests' do + let_it_be_with_reload(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } + let_it_be(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository) } + let_it_be(:merge_request) { create(:merge_request, source_project: project, assignees: [assignee]) } + let_it_be(:issuable) { merge_request } # "alias" for shared examples + + before_all do + project.add_developer(user) + group.add_developer(user) + end + + RSpec.shared_examples 'an authenticated merge request atom feed' do + it 'renders atom feed with additional merge request information' do + expect(body).to have_selector('title', text: "#{project.name} merge requests") + end + end + + context 'when authenticated' do + before do + sign_in user + visit project_merge_requests_path(project, :atom) + end + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated merge request atom feed' + + context 'but the use can not see the project' do + let_it_be(:other_project) { create(:project) } + + it 'renders 404 page' do + visit project_issues_path(other_project, :atom) + + expect(page).to have_gitlab_http_status(:not_found) + end + end + end + + context 'when authenticated via personal access token' do + before do + personal_access_token = create(:personal_access_token, user: user) + + visit project_merge_requests_path(project, :atom, + private_token: personal_access_token.token) + end + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated merge request atom feed' + end + + context 'when authenticated via feed token' do + before do + visit project_merge_requests_path(project, :atom, + feed_token: user.feed_token) + end + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated merge request atom feed' + end + + context 'when not authenticated' do + before do + visit project_merge_requests_path(project, :atom) + end + + context 'and the project is private' do + it 'redirects to login page' do + expect(page).to have_current_path(new_user_session_path) + end + end + + context 'and the project is public' do + let_it_be(:project) { create(:project, :public, :repository) } + let_it_be(:merge_request) { create(:merge_request, source_project: project, assignees: [assignee]) } + let_it_be(:issuable) { merge_request } # "alias" for shared examples + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated merge request atom feed' + end + end + end +end diff --git a/spec/features/issues/rss_spec.rb b/spec/features/issues/rss_spec.rb index 6c4498ea711..b20502ecc25 100644 --- a/spec/features/issues/rss_spec.rb +++ b/spec/features/issues/rss_spec.rb @@ -3,21 +3,24 @@ require 'spec_helper' RSpec.describe 'Project Issues RSS' do - let!(:user) { create(:user) } - let(:group) { create(:group) } - let(:project) { create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } - let(:path) { project_issues_path(project) } + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let_it_be(:path) { project_issues_path(project) } + let_it_be(:issue) { create(:issue, project: project, assignees: [user]) } - before do - create(:issue, project: project, assignees: [user]) + before_all do group.add_developer(user) end context 'when signed in' do - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } - before do + before_all do project.add_developer(user) + end + + before do sign_in(user) visit path end @@ -36,26 +39,6 @@ RSpec.describe 'Project Issues RSS' do end describe 'feeds' do - shared_examples 'updates atom feed link' do |type| - it "for #{type}" do - sign_in(user) - visit path - - link = find_link('Subscribe to RSS feed') - params = CGI.parse(URI.parse(link[:href]).query) - auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) - auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) - - expected = { - 'feed_token' => [user.feed_token], - 'assignee_id' => [user.id.to_s] - } - - expect(params).to include(expected) - expect(auto_discovery_params).to include(expected) - end - end - it_behaves_like 'updates atom feed link', :project do let(:path) { project_issues_path(project, assignee_id: user.id) } end diff --git a/spec/features/merge_requests/rss_spec.rb b/spec/features/merge_requests/rss_spec.rb new file mode 100644 index 00000000000..9fc3d3d6ae1 --- /dev/null +++ b/spec/features/merge_requests/rss_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project Merge Requests RSS' do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let_it_be(:merge_request) { create(:merge_request, source_project: project, assignees: [user]) } + let_it_be(:path) { project_merge_requests_path(project) } + + before_all do + group.add_developer(user) + end + + context 'when signed in' do + let_it_be(:user) { create(:user) } + + before_all do + project.add_developer(user) + end + + before do + sign_in(user) + visit path + end + + it_behaves_like "it has an RSS button with current_user's feed token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" + end + + context 'when signed out' do + before do + visit path + end + + it_behaves_like "it has an RSS button without a feed token" + it_behaves_like "an autodiscoverable RSS feed without a feed token" + end + + describe 'feeds' do + it_behaves_like 'updates atom feed link', :project do + let(:path) { project_merge_requests_path(project, assignee_id: user.id) } + end + end +end diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js index beedb9b2eba..9fe2780fde2 100644 --- a/spec/frontend/lib/utils/text_markdown_spec.js +++ b/spec/frontend/lib/utils/text_markdown_spec.js @@ -70,6 +70,25 @@ describe('init markdown', () => { expect(textArea.value).toContain('# Does not parse the `$` currently.'); }); + it('inserts a new line correctly', () => { + const initialValue = ''; + + textArea.value = initialValue; + textArea.selectionStart = 0; + textArea.selectionEnd = 0; + + insertMarkdownText({ + textArea, + text: textArea.value, + tag: '```suggestion:-0+0\n{text}\n```', + blockTag: true, + selected: '# Does not parse the \\n currently.', + wrap: false, + }); + + expect(textArea.value).toContain('# Does not parse the \\n currently.'); + }); + it('inserts the tag on a new line if the current one is not empty', () => { const initialValue = 'some text'; diff --git a/spec/lib/error_tracking/collector/dsn_spec.rb b/spec/lib/error_tracking/collector/dsn_spec.rb new file mode 100644 index 00000000000..af55e6f20ec --- /dev/null +++ b/spec/lib/error_tracking/collector/dsn_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ErrorTracking::Collector::Dsn do + describe '.build__url' do + let(:gitlab) do + double( + protocol: 'https', + https: true, + host: 'gitlab.example.com', + port: '4567', + relative_url_root: nil + ) + end + + subject { described_class.build_url('abcdef1234567890', 778) } + + it 'returns a valid URL' do + allow(Settings).to receive(:gitlab).and_return(gitlab) + allow(Settings).to receive(:gitlab_on_standard_port?).and_return(false) + + is_expected.to eq('https://abcdef1234567890@gitlab.example.com:4567/api/v4/error_tracking/collector/778') + end + end +end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index da89eccc3b2..597d532f8f1 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -4524,51 +4524,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do subject(:reset_bridge) { pipeline.reset_source_bridge!(project.owner) } - # This whole block will be removed by https://gitlab.com/gitlab-org/gitlab/-/issues/329194 - # It contains some duplicate checks. - context 'when the FF ci_reset_bridge_with_subsequent_jobs is disabled' do - before do - stub_feature_flags(ci_reset_bridge_with_subsequent_jobs: false) - end - - context 'when the pipeline is a child pipeline and the bridge is depended' do - let!(:parent_pipeline) { create(:ci_pipeline) } - let!(:bridge) { create_bridge(parent_pipeline, pipeline, true) } - - it 'marks source bridge as pending' do - reset_bridge - - expect(bridge.reload).to be_pending - end - - context 'when the parent pipeline has subsequent jobs after the bridge' do - let!(:after_bridge_job) { create(:ci_build, :skipped, pipeline: parent_pipeline, stage_idx: bridge.stage_idx + 1) } - - it 'does not touch subsequent jobs of the bridge' do - reset_bridge - - expect(after_bridge_job.reload).to be_skipped - end - end - - context 'when the parent pipeline has a dependent upstream pipeline' do - let(:upstream_pipeline) { create(:ci_pipeline, project: create(:project)) } - let!(:upstream_bridge) { create_bridge(upstream_pipeline, parent_pipeline, true) } - - let(:upstream_upstream_pipeline) { create(:ci_pipeline, project: create(:project)) } - let!(:upstream_upstream_bridge) { create_bridge(upstream_upstream_pipeline, upstream_pipeline, true) } - - it 'marks all source bridges as pending' do - reset_bridge - - expect(bridge.reload).to be_pending - expect(upstream_bridge.reload).to be_pending - expect(upstream_upstream_bridge.reload).to be_pending - end - end - end - end - context 'when the pipeline is a child pipeline and the bridge is depended' do let!(:parent_pipeline) { create(:ci_pipeline) } let!(:bridge) { create_bridge(parent_pipeline, pipeline, true) } diff --git a/spec/models/customer_relations/contact_spec.rb b/spec/models/customer_relations/contact_spec.rb new file mode 100644 index 00000000000..b19554dd67e --- /dev/null +++ b/spec/models/customer_relations/contact_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe CustomerRelations::Contact, type: :model do + describe 'associations' do + it { is_expected.to belong_to(:group) } + it { is_expected.to belong_to(:organization).optional } + end + + describe 'validations' do + subject { build(:contact) } + + it { is_expected.to validate_presence_of(:group) } + it { is_expected.to validate_presence_of(:first_name) } + it { is_expected.to validate_presence_of(:last_name) } + + it { is_expected.to validate_length_of(:phone).is_at_most(32) } + it { is_expected.to validate_length_of(:first_name).is_at_most(255) } + it { is_expected.to validate_length_of(:last_name).is_at_most(255) } + it { is_expected.to validate_length_of(:email).is_at_most(255) } + it { is_expected.to validate_length_of(:description).is_at_most(1024) } + + it_behaves_like 'an object with RFC3696 compliant email-formatted attributes', :email + end + + describe '#before_validation' do + it 'strips leading and trailing whitespace' do + contact = described_class.new(first_name: ' First ', last_name: ' Last ', phone: ' 123456 ') + contact.valid? + + expect(contact.first_name).to eq('First') + expect(contact.last_name).to eq('Last') + expect(contact.phone).to eq('123456') + end + end +end diff --git a/spec/models/customer_relations/organization_spec.rb b/spec/models/customer_relations/organization_spec.rb index b79b5748156..71b455ae8c8 100644 --- a/spec/models/customer_relations/organization_spec.rb +++ b/spec/models/customer_relations/organization_spec.rb @@ -8,7 +8,7 @@ RSpec.describe CustomerRelations::Organization, type: :model do end describe 'validations' do - subject { create(:organization) } + subject { build(:organization) } it { is_expected.to validate_presence_of(:group) } it { is_expected.to validate_presence_of(:name) } diff --git a/spec/requests/jira_connect/installations_controller_spec.rb b/spec/requests/jira_connect/installations_controller_spec.rb new file mode 100644 index 00000000000..6315c66a41a --- /dev/null +++ b/spec/requests/jira_connect/installations_controller_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe JiraConnect::InstallationsController do + let_it_be(:installation) { create(:jira_connect_installation) } + + describe 'GET /-/jira_connect/installations' do + before do + get '/-/jira_connect/installations', params: { jwt: jwt } + end + + context 'without JWT' do + let(:jwt) { nil } + + it 'returns 403' do + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'with valid JWT' do + let(:qsh) { Atlassian::Jwt.create_query_string_hash('https://gitlab.test/installations', 'GET', 'https://gitlab.test') } + let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) } + + it 'returns status ok' do + expect(response).to have_gitlab_http_status(:ok) + end + + it 'returns the installation as json' do + expect(json_response).to eq({ + 'gitlab_com' => true, + 'instance_url' => nil + }) + end + + context 'with instance_url' do + let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'https://example.com') } + + it 'returns the installation as json' do + expect(json_response).to eq({ + 'gitlab_com' => false, + 'instance_url' => 'https://example.com' + }) + end + end + end + end + + describe 'PUT /-/jira_connect/installations' do + before do + put '/-/jira_connect/installations', params: { jwt: jwt, installation: { instance_url: update_instance_url } } + end + + let(:update_instance_url) { 'https://example.com' } + + context 'without JWT' do + let(:jwt) { nil } + + it 'returns 403' do + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'with valid JWT' do + let(:qsh) { Atlassian::Jwt.create_query_string_hash('https://gitlab.test/subscriptions', 'GET', 'https://gitlab.test') } + let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) } + + it 'returns 200' do + expect(response).to have_gitlab_http_status(:ok) + end + + it 'updates the instance_url' do + expect(json_response).to eq({ + 'gitlab_com' => false, + 'instance_url' => 'https://example.com' + }) + end + + context 'invalid URL' do + let(:update_instance_url) { 'invalid url' } + + it 'returns 422 and errors', :aggregate_failures do + expect(response).to have_gitlab_http_status(:unprocessable_entity) + expect(json_response).to eq({ + 'errors' => { + 'instance_url' => [ + 'is blocked: Only allowed schemes are http, https' + ] + } + }) + end + end + end + end +end diff --git a/spec/support/shared_examples/features/atom/issuable_shared_examples.rb b/spec/support/shared_examples/features/atom/issuable_shared_examples.rb new file mode 100644 index 00000000000..17993830f37 --- /dev/null +++ b/spec/support/shared_examples/features/atom/issuable_shared_examples.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +RSpec.shared_examples "an authenticated issuable atom feed" do + it "renders atom feed with common issuable information" do + expect(response_headers['Content-Type']) + .to have_content('application/atom+xml') + expect(body).to have_selector('author email', text: issuable.author_public_email) + expect(body).to have_selector('assignees assignee email', text: issuable.assignees.first.public_email) + expect(body).to have_selector('assignee email', text: issuable.assignees.first.public_email) + expect(body).to have_selector('entry summary', text: issuable.title) + end +end diff --git a/spec/support/shared_examples/features/rss_shared_examples.rb b/spec/support/shared_examples/features/rss_shared_examples.rb index c7c2aeea358..0991de21d8d 100644 --- a/spec/support/shared_examples/features/rss_shared_examples.rb +++ b/spec/support/shared_examples/features/rss_shared_examples.rb @@ -25,3 +25,23 @@ RSpec.shared_examples "it has an RSS button without a feed token" do .to have_css("a:has(.qa-rss-icon):not([href*='feed_token'])") # rubocop:disable QA/SelectorUsage end end + +RSpec.shared_examples "updates atom feed link" do |type| + it "for #{type}" do + sign_in(user) + visit path + + link = find_link('Subscribe to RSS feed') + params = CGI.parse(URI.parse(link[:href]).query) + auto_discovery_link = find("link[type='application/atom+xml']", visible: false) + auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) + + expected = { + 'feed_token' => [user.feed_token], + 'assignee_id' => [user.id.to_s] + } + + expect(params).to include(expected) + expect(auto_discovery_params).to include(expected) + end +end |