summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-09-13 13:26:31 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-09-13 13:26:31 +0000
commitb7dfe2ae4054aa40e15182fd3c6cb7dd39f131db (patch)
tree5ab080ca9cadeb6cd9578bf301e4e9e8810bed9e /spec
parent25cb337cf12438169f1b14bc5dace8a06a7356e3 (diff)
downloadgitlab-ce-b7dfe2ae4054aa40e15182fd3c6cb7dd39f131db.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb8
-rw-r--r--spec/db/schema_spec.rb2
-rw-r--r--spec/factories/deployments.rb4
-rw-r--r--spec/features/projects/jobs_spec.rb25
-rw-r--r--spec/fixtures/api/schemas/cluster_basic.json16
-rw-r--r--spec/fixtures/api/schemas/deployment.json6
-rw-r--r--spec/fixtures/api/schemas/environment.json2
-rw-r--r--spec/frontend/clusters/stores/clusters_store_spec.js1
-rw-r--r--spec/helpers/issues_helper_spec.rb77
-rw-r--r--spec/helpers/onboarding_experiment_helper_spec.rb8
-rw-r--r--spec/javascripts/gl_dropdown_spec.js46
-rw-r--r--spec/javascripts/jobs/components/environments_block_spec.js78
-rw-r--r--spec/lib/gitlab/email/receiver_spec.rb9
-rw-r--r--spec/lib/gitlab/git_access_spec.rb2
-rw-r--r--spec/lib/gitlab/hook_data/issue_builder_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/url_blocker_spec.rb64
-rw-r--r--spec/lib/gitlab/url_blockers/url_whitelist_spec.rb72
-rw-r--r--spec/lib/gitlab_spec.rb55
-rw-r--r--spec/models/issue_spec.rb20
-rw-r--r--spec/models/merge_request_spec.rb2
-rw-r--r--spec/requests/git_http_spec.rb2
-rw-r--r--spec/rubocop/cop/graphql/authorize_types_spec.rb17
-rw-r--r--spec/rubocop/cop/graphql/descriptions_spec.rb75
-rw-r--r--spec/serializers/cluster_basic_entity_spec.rb35
-rw-r--r--spec/serializers/issue_entity_spec.rb40
-rw-r--r--spec/services/issues/duplicate_service_spec.rb6
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/db_cleaner.rb2
-rw-r--r--spec/support/helpers/graphql_helpers.rb25
-rw-r--r--spec/support/helpers/ldap_helpers.rb2
-rw-r--r--spec/support/helpers/license_helper.rb2
-rw-r--r--spec/support/helpers/login_helpers.rb2
-rw-r--r--spec/support/helpers/migrations_helpers.rb2
-rw-r--r--spec/support/helpers/stub_configuration.rb2
-rw-r--r--spec/support/helpers/stub_gitlab_calls.rb2
-rw-r--r--spec/support/helpers/stub_object_storage.rb2
-rw-r--r--spec/support/helpers/test_env.rb5
-rw-r--r--spec/support/helpers/workhorse_helpers.rb3
-rw-r--r--spec/support/matchers/markdown_matchers.rb2
-rw-r--r--spec/views/projects/issues/show.html.haml_spec.rb36
-rw-r--r--spec/views/shared/_label_row.html.haml_spec.rb1
43 files changed, 719 insertions, 46 deletions
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index bd3e66efd58..b455b55bd11 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -265,7 +265,8 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
let(:job) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) }
before do
- create(:deployment, :success, environment: environment, project: project)
+ create(:deployment, :success, :on_cluster, environment: environment, project: project)
+ project.add_maintainer(user) # Need to be a maintianer to view cluster.path
end
it 'exposes the deployment information' do
@@ -276,8 +277,9 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(json_response.dig('deployment_status', 'status')).to eq 'creating'
expect(json_response.dig('deployment_status', 'environment')).not_to be_nil
expect(json_response.dig('deployment_status', 'environment', 'last_deployment')).not_to be_nil
- expect(json_response.dig('deployment_status', 'environment', 'last_deployment'))
- .not_to include('commit')
+ expect(json_response.dig('deployment_status', 'environment', 'last_deployment')).not_to include('commit')
+ expect(json_response.dig('deployment_status', 'environment', 'last_deployment', 'cluster', 'name')).to eq('test-cluster')
+ expect(json_response.dig('deployment_status', 'environment', 'last_deployment', 'cluster', 'path')).to be_present
end
end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index 52af470efac..5216683bd36 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -4,6 +4,8 @@ require 'spec_helper'
require Rails.root.join('ee', 'spec', 'db', 'schema_support') if Gitlab.ee?
describe 'Database schema' do
+ prepend_if_ee('EE::DB::SchemaSupport')
+
let(:connection) { ActiveRecord::Base.connection }
let(:tables) { connection.tables }
diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb
index 89ff1c527df..50dc304a10e 100644
--- a/spec/factories/deployments.rb
+++ b/spec/factories/deployments.rb
@@ -17,6 +17,10 @@ FactoryBot.define do
unless deployment.project.repository_exists?
allow(deployment.project.repository).to receive(:create_ref)
end
+
+ if deployment.cluster && deployment.cluster.project_type? && deployment.cluster.project.nil?
+ deployment.cluster.projects << deployment.project
+ end
end
trait :review_app do
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index d1783de0330..cebca338f33 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -534,9 +534,32 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
it 'shows deployment message' do
- expect(page).to have_content 'This job is the most recent deployment'
+ expect(page).to have_content 'This job is the most recent deployment to production'
expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
end
+
+ context 'when there is a cluster used for the deployment' do
+ let(:cluster) { create(:cluster, name: 'the-cluster') }
+ let(:deployment) { create(:deployment, :success, cluster: cluster, environment: environment, project: environment.project) }
+ let(:user_access_level) { :maintainer }
+
+ it 'shows a link to the cluster' do
+ expect(page).to have_link 'the-cluster'
+ end
+
+ it 'shows the name of the cluster' do
+ expect(page).to have_content 'Cluster the-cluster was used'
+ end
+
+ context 'when the user is not able to view the cluster' do
+ let(:user_access_level) { :developer }
+
+ it 'includes only the name of the cluster without a link' do
+ expect(page).to have_content 'Cluster the-cluster was used'
+ expect(page).not_to have_link 'the-cluster'
+ end
+ end
+ end
end
context 'job is complete and not successful' do
diff --git a/spec/fixtures/api/schemas/cluster_basic.json b/spec/fixtures/api/schemas/cluster_basic.json
new file mode 100644
index 00000000000..6f0e77997f0
--- /dev/null
+++ b/spec/fixtures/api/schemas/cluster_basic.json
@@ -0,0 +1,16 @@
+{
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": { "type": "string" },
+ "path": {
+ "oneOf": [
+ { "type": "null" },
+ { "type": "string" }
+ ]
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/deployment.json b/spec/fixtures/api/schemas/deployment.json
index fe725b97c21..81c2d1ef5ab 100644
--- a/spec/fixtures/api/schemas/deployment.json
+++ b/spec/fixtures/api/schemas/deployment.json
@@ -47,6 +47,12 @@
{ "$ref": "job/job.json" }
]
},
+ "cluster": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "cluster_basic.json" }
+ ]
+ },
"manual_actions": {
"type": "array",
"items": { "$ref": "job/job.json" }
diff --git a/spec/fixtures/api/schemas/environment.json b/spec/fixtures/api/schemas/environment.json
index 5b1e3c049fa..e38b05ec008 100644
--- a/spec/fixtures/api/schemas/environment.json
+++ b/spec/fixtures/api/schemas/environment.json
@@ -28,6 +28,8 @@
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" },
"can_stop": { "type": "boolean" },
+ "cluster_type": { "type": "types/nullable_string.json" },
+ "terminal_path": { "type": "types/nullable_string.json" },
"last_deployment": {
"oneOf": [
{ "type": "null" },
diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js
index 98077498998..ee3b7d8aa90 100644
--- a/spec/frontend/clusters/stores/clusters_store_spec.js
+++ b/spec/frontend/clusters/stores/clusters_store_spec.js
@@ -152,6 +152,7 @@ describe('Clusters Store', () => {
},
},
environments: [],
+ fetchingEnvironments: false,
});
});
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 039143eb8d7..d15b5a4ab58 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -142,7 +142,7 @@ describe IssuesHelper do
expect(link_to_discussions_to_resolve(merge_request, nil)).to include(expected_path)
end
- it "containst the reference to the merge request" do
+ it "contains the reference to the merge request" do
expect(link_to_discussions_to_resolve(merge_request, nil)).to include(merge_request.to_reference)
end
end
@@ -185,4 +185,79 @@ describe IssuesHelper do
expect(helper.show_new_issue_link?(project)).to be_truthy
end
end
+
+ describe '#issue_closed_link' do
+ let(:new_issue) { create(:issue, project: project) }
+ let(:guest) { create(:user) }
+
+ before do
+ allow(helper).to receive(:can?) do |*args|
+ Ability.allowed?(*args)
+ end
+ end
+
+ shared_examples 'successfully displays link to issue and with css class' do |action|
+ it 'returns link' do
+ link = "<a class=\"#{css_class}\" href=\"/#{new_issue.project.full_path}/issues/#{new_issue.iid}\">(#{action})</a>"
+
+ expect(helper.issue_closed_link(issue, user, css_class: css_class)).to match(link)
+ end
+ end
+
+ shared_examples 'does not display link' do
+ it 'returns nil' do
+ expect(helper.issue_closed_link(issue, user)).to be_nil
+ end
+ end
+
+ context 'with linked issue' do
+ context 'with moved issue' do
+ before do
+ issue.update(moved_to: new_issue)
+ end
+
+ context 'when user has permission to see new issue' do
+ let(:user) { project.owner }
+ let(:css_class) { 'text-white text-underline' }
+
+ it_behaves_like 'successfully displays link to issue and with css class', 'moved'
+ end
+
+ context 'when user has no permission to see new issue' do
+ let(:user) { guest }
+
+ it_behaves_like 'does not display link'
+ end
+ end
+
+ context 'with duplicated issue' do
+ before do
+ issue.update(duplicated_to: new_issue)
+ end
+
+ context 'when user has permission to see new issue' do
+ let(:user) { project.owner }
+ let(:css_class) { 'text-white text-underline' }
+
+ it_behaves_like 'successfully displays link to issue and with css class', 'duplicated'
+ end
+
+ context 'when user has no permission to see new issue' do
+ let(:user) { guest }
+
+ it_behaves_like 'does not display link'
+ end
+ end
+ end
+
+ context 'without linked issue' do
+ let(:user) { project.owner }
+
+ before do
+ issue.update(moved_to: nil, duplicated_to: nil)
+ end
+
+ it_behaves_like 'does not display link'
+ end
+ end
end
diff --git a/spec/helpers/onboarding_experiment_helper_spec.rb b/spec/helpers/onboarding_experiment_helper_spec.rb
index 5b7d9b1c2e6..cada91bff3c 100644
--- a/spec/helpers/onboarding_experiment_helper_spec.rb
+++ b/spec/helpers/onboarding_experiment_helper_spec.rb
@@ -4,17 +4,17 @@ require 'spec_helper'
describe OnboardingExperimentHelper, type: :helper do
describe '.allow_access_to_onboarding?' do
- context "when we're not gitlab.com" do
+ context "when we're not gitlab.com or dev env" do
it 'returns false' do
- allow(::Gitlab).to receive(:com?).and_return(false)
+ allow(::Gitlab).to receive(:dev_env_or_com?).and_return(false)
expect(helper.allow_access_to_onboarding?).to be(false)
end
end
- context "when we're gitlab.com" do
+ context "when we're gitlab.com or dev env" do
before do
- allow(::Gitlab).to receive(:com?).and_return(true)
+ allow(::Gitlab).to receive(:dev_env_or_com?).and_return(true)
end
context 'and the :user_onboarding feature is not enabled' do
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index 8c7820ddb52..00bc552bd7d 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -243,14 +243,23 @@ describe('glDropdown', function describeDropdown() {
});
describe('renderItem', () => {
+ function dropdownWithOptions(options) {
+ const $dropdownDiv = $('<div />');
+
+ $dropdownDiv.glDropdown(options);
+
+ return $dropdownDiv.data('glDropdown');
+ }
+
+ function basicDropdown() {
+ return dropdownWithOptions({});
+ }
+
describe('without selected value', () => {
let dropdown;
beforeEach(() => {
- const dropdownOptions = {};
- const $dropdownDiv = $('<div />');
- $dropdownDiv.glDropdown(dropdownOptions);
- dropdown = $dropdownDiv.data('glDropdown');
+ dropdown = basicDropdown();
});
it('marks items without ID as active', () => {
@@ -275,6 +284,35 @@ describe('glDropdown', function describeDropdown() {
expect(link).not.toHaveClass('is-active');
});
});
+
+ it('should return an empty .separator li when when appropriate', () => {
+ const dropdown = basicDropdown();
+ const sep = { type: 'separator' };
+ const li = dropdown.renderItem(sep);
+
+ expect(li).toHaveClass('separator');
+ expect(li.childNodes.length).toEqual(0);
+ });
+
+ it('should return an empty .divider li when when appropriate', () => {
+ const dropdown = basicDropdown();
+ const div = { type: 'divider' };
+ const li = dropdown.renderItem(div);
+
+ expect(li).toHaveClass('divider');
+ expect(li.childNodes.length).toEqual(0);
+ });
+
+ it('should return a .dropdown-header li with the correct content when when appropriate', () => {
+ const dropdown = basicDropdown();
+ const text = 'My Header';
+ const header = { type: 'header', content: text };
+ const li = dropdown.renderItem(header);
+
+ expect(li).toHaveClass('dropdown-header');
+ expect(li.childNodes.length).toEqual(1);
+ expect(li.textContent).toEqual(text);
+ });
});
it('should keep selected item after selecting a second time', () => {
diff --git a/spec/javascripts/jobs/components/environments_block_spec.js b/spec/javascripts/jobs/components/environments_block_spec.js
index 0866ddd21d8..4bbc5f5a348 100644
--- a/spec/javascripts/jobs/components/environments_block_spec.js
+++ b/spec/javascripts/jobs/components/environments_block_spec.js
@@ -18,6 +18,8 @@ describe('Environments block', () => {
name: 'environment',
};
+ const lastDeployment = { iid: 'deployment', deployable: { build_path: 'bar' } };
+
afterEach(() => {
vm.$destroy();
});
@@ -45,7 +47,7 @@ describe('Environments block', () => {
deploymentStatus: {
status: 'out_of_date',
environment: Object.assign({}, environment, {
- last_deployment: { iid: 'deployment', deployable: { build_path: 'bar' } },
+ last_deployment: lastDeployment,
}),
},
iconStatus: status,
@@ -99,10 +101,7 @@ describe('Environments block', () => {
deploymentStatus: {
status: 'creating',
environment: Object.assign({}, environment, {
- last_deployment: {
- iid: 'deployment',
- deployable: { build_path: 'foo' },
- },
+ last_deployment: lastDeployment,
}),
},
iconStatus: status,
@@ -112,7 +111,7 @@ describe('Environments block', () => {
'This job is creating a deployment to environment and will overwrite the latest deployment.',
);
- expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('foo');
+ expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('bar');
});
});
@@ -146,4 +145,71 @@ describe('Environments block', () => {
});
});
});
+
+ describe('with a cluster', () => {
+ it('renders the cluster link', () => {
+ const cluster = {
+ name: 'the-cluster',
+ path: '/the-cluster-path',
+ };
+ vm = mountComponent(Component, {
+ deploymentStatus: {
+ status: 'last',
+ environment: Object.assign({}, environment, {
+ last_deployment: {
+ ...lastDeployment,
+ cluster,
+ },
+ }),
+ },
+ iconStatus: status,
+ });
+
+ expect(vm.$el.textContent.trim()).toContain('Cluster the-cluster was used.');
+
+ expect(vm.$el.querySelector('.js-job-cluster-link').getAttribute('href')).toEqual(
+ '/the-cluster-path',
+ );
+ });
+
+ describe('when the cluster is missing the path', () => {
+ it('renders the name without a link', () => {
+ const cluster = {
+ name: 'the-cluster',
+ };
+ vm = mountComponent(Component, {
+ deploymentStatus: {
+ status: 'last',
+ environment: Object.assign({}, environment, {
+ last_deployment: {
+ ...lastDeployment,
+ cluster,
+ },
+ }),
+ },
+ iconStatus: status,
+ });
+
+ expect(vm.$el.textContent.trim()).toContain('Cluster the-cluster was used.');
+
+ expect(vm.$el.querySelector('.js-job-cluster-link')).toBeNull();
+ });
+ });
+ });
+
+ describe('without a cluster', () => {
+ it('does not render a cluster link', () => {
+ vm = mountComponent(Component, {
+ deploymentStatus: {
+ status: 'last',
+ environment: Object.assign({}, environment, {
+ last_deployment: lastDeployment,
+ }),
+ },
+ iconStatus: status,
+ });
+
+ expect(vm.$el.querySelector('.js-job-cluster-link')).toBeNull();
+ });
+ });
});
diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb
index c9fde06cbae..6b5a355e598 100644
--- a/spec/lib/gitlab/email/receiver_spec.rb
+++ b/spec/lib/gitlab/email/receiver_spec.rb
@@ -14,6 +14,7 @@ describe Gitlab::Email::Receiver do
allow(handler).to receive(:execute)
allow(handler).to receive(:metrics_params)
+ allow(handler).to receive(:metrics_event)
end
it "finds the mail key" do
@@ -46,4 +47,12 @@ describe Gitlab::Email::Receiver do
expect { receiver.execute }.to raise_error(Gitlab::Email::AutoGeneratedEmailError)
end
end
+
+ it "requires all handlers to have a unique metric_event" do
+ events = Gitlab::Email::Handler.handlers.map do |handler|
+ handler.new(Mail::Message.new, 'gitlabhq/gitlabhq+auth_token').metrics_event
+ end
+
+ expect(events.uniq.count).to eq events.count
+ end
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index b9c21b3a7bd..d584cdbe280 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -943,7 +943,7 @@ describe Gitlab::GitAccess do
changes = ['6f6d7e7ed 570e7b2ab refs/heads/master', '6f6d7e7ed 570e7b2ab refs/heads/feature']
# There is still an N+1 query with protected branches
- expect { access.check('git-receive-pack', changes) }.not_to exceed_query_limit(control_count).with_threshold(1)
+ expect { access.check('git-receive-pack', changes) }.not_to exceed_query_limit(control_count).with_threshold(2)
end
it 'raises TimeoutError when #check_single_change_access raises a timeout error' do
diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
index b06d05c1c7f..6013fb78bc7 100644
--- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
@@ -23,6 +23,7 @@ describe Gitlab::HookData::IssueBuilder do
last_edited_by_id
milestone_id
moved_to_id
+ duplicated_to_id
project_id
relative_position
state
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index e496ab4cd35..6d573a4f39a 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -14,6 +14,7 @@ issues:
- todos
- user_agent_detail
- moved_to
+- duplicated_to
- events
- merge_requests_closing_issues
- metrics
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index e9750d23c53..2f178648838 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -19,6 +19,7 @@ Issue:
- closed_by_id
- due_date
- moved_to_id
+- duplicated_to_id
- lock_version
- milestone_id
- weight
diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb
index 6d1d7e48326..6ce002ad70e 100644
--- a/spec/lib/gitlab/url_blocker_spec.rb
+++ b/spec/lib/gitlab/url_blocker_spec.rb
@@ -30,8 +30,12 @@ describe Gitlab::UrlBlocker do
context 'when URI is internal' do
let(:import_url) { 'http://localhost' }
+ before do
+ stub_dns(import_url, ip_address: '127.0.0.1')
+ end
+
it_behaves_like 'validates URI and hostname' do
- let(:expected_uri) { 'http://[::1]' }
+ let(:expected_uri) { 'http://127.0.0.1' }
let(:expected_hostname) { 'localhost' }
end
end
@@ -347,6 +351,7 @@ describe Gitlab::UrlBlocker do
end
before do
+ allow(ApplicationSetting).to receive(:current).and_return(ApplicationSetting.new)
stub_application_setting(outbound_local_requests_whitelist: whitelist)
end
@@ -384,9 +389,15 @@ describe Gitlab::UrlBlocker do
it_behaves_like 'allows local requests', { allow_localhost: false, allow_local_network: false }
it 'whitelists IP when dns_rebind_protection is disabled' do
- stub_domain_resolv('example.com', '192.168.1.1') do
- expect(described_class).not_to be_blocked_url("http://example.com",
- url_blocker_attributes.merge(dns_rebind_protection: false))
+ url = "http://example.com"
+ attrs = url_blocker_attributes.merge(dns_rebind_protection: false)
+
+ stub_domain_resolv('example.com', '192.168.1.2') do
+ expect(described_class).not_to be_blocked_url(url, attrs)
+ end
+
+ stub_domain_resolv('example.com', '192.168.1.3') do
+ expect(described_class).to be_blocked_url(url, attrs)
end
end
end
@@ -437,6 +448,51 @@ describe Gitlab::UrlBlocker do
url_blocker_attributes)
end
end
+
+ shared_examples 'dns rebinding checks' do
+ shared_examples 'whitelists the domain' do
+ let(:whitelist) { [domain] }
+ let(:url) { "http://#{domain}" }
+
+ before do
+ stub_env('RSPEC_ALLOW_INVALID_URLS', 'false')
+ end
+
+ it do
+ expect(described_class).not_to be_blocked_url(url, dns_rebind_protection: dns_rebind_value)
+ end
+ end
+
+ context 'when dns_rebinding_setting is' do
+ context 'enabled' do
+ let(:dns_rebind_value) { true }
+
+ it_behaves_like 'whitelists the domain'
+ end
+
+ context 'disabled' do
+ let(:dns_rebind_value) { false }
+
+ it_behaves_like 'whitelists the domain'
+ end
+ end
+ end
+
+ context 'when the domain cannot be resolved' do
+ let(:domain) { 'foobar.x' }
+
+ it_behaves_like 'dns rebinding checks'
+ end
+
+ context 'when the domain can be resolved' do
+ let(:domain) { 'example.com' }
+
+ before do
+ stub_dns(url, ip_address: '93.184.216.34')
+ end
+
+ it_behaves_like 'dns rebinding checks'
+ end
end
context 'with ip ranges in whitelist' do
diff --git a/spec/lib/gitlab/url_blockers/url_whitelist_spec.rb b/spec/lib/gitlab/url_blockers/url_whitelist_spec.rb
new file mode 100644
index 00000000000..906e0f0ba3d
--- /dev/null
+++ b/spec/lib/gitlab/url_blockers/url_whitelist_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::UrlBlockers::UrlWhitelist do
+ include StubRequests
+
+ let(:whitelist) { [] }
+
+ before do
+ allow(ApplicationSetting).to receive(:current).and_return(ApplicationSetting.new)
+ stub_application_setting(outbound_local_requests_whitelist: whitelist)
+ end
+
+ describe '#domain_whitelisted?' do
+ let(:whitelist) do
+ [
+ 'www.example.com',
+ 'example.com'
+ ]
+ end
+
+ it 'returns true if domains present in whitelist' do
+ aggregate_failures do
+ whitelist.each do |domain|
+ expect(described_class).to be_domain_whitelisted(domain)
+ end
+
+ ['subdomain.example.com', 'example.org'].each do |domain|
+ expect(described_class).not_to be_domain_whitelisted(domain)
+ end
+ end
+ end
+
+ it 'returns false when domain is blank' do
+ expect(described_class).not_to be_domain_whitelisted(nil)
+ end
+ end
+
+ describe '#ip_whitelisted?' do
+ let(:whitelist) do
+ [
+ '0.0.0.0',
+ '127.0.0.1',
+ '192.168.1.1',
+ '0:0:0:0:0:ffff:192.168.1.2',
+ '::ffff:c0a8:102',
+ 'fc00:bf8b:e62c:abcd:abcd:aaaa:aaaa:aaaa',
+ '0:0:0:0:0:ffff:169.254.169.254',
+ '::ffff:a9fe:a9fe',
+ '::ffff:a9fe:a864',
+ 'fe80::c800:eff:fe74:8'
+ ]
+ end
+
+ it 'returns true if ips present in whitelist' do
+ aggregate_failures do
+ whitelist.each do |ip_address|
+ expect(described_class).to be_ip_whitelisted(ip_address)
+ end
+
+ ['172.16.2.2', '127.0.0.2', 'fe80::c800:eff:fe74:9'].each do |ip_address|
+ expect(described_class).not_to be_ip_whitelisted(ip_address)
+ end
+ end
+ end
+
+ it 'returns false when ip is blank' do
+ expect(described_class).not_to be_ip_whitelisted(nil)
+ end
+ end
+end
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index 589dac61528..ccb5cb3aa43 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -21,23 +21,23 @@ describe Gitlab do
context 'when a REVISION file exists' do
before do
expect(File).to receive(:exist?)
- .with(described_class.root.join('REVISION'))
- .and_return(true)
+ .with(described_class.root.join('REVISION'))
+ .and_return(true)
end
it 'returns the actual Git revision' do
expect(File).to receive(:read)
- .with(described_class.root.join('REVISION'))
- .and_return("abc123\n")
+ .with(described_class.root.join('REVISION'))
+ .and_return("abc123\n")
expect(described_class.revision).to eq('abc123')
end
it 'memoizes the revision' do
expect(File).to receive(:read)
- .once
- .with(described_class.root.join('REVISION'))
- .and_return("abc123\n")
+ .once
+ .with(described_class.root.join('REVISION'))
+ .and_return("abc123\n")
2.times { described_class.revision }
end
@@ -47,8 +47,8 @@ describe Gitlab do
context 'when the Git command succeeds' do
before do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with(cmd)
- .and_return(Gitlab::Popen::Result.new(cmd, 'abc123', '', double(success?: true)))
+ .with(cmd)
+ .and_return(Gitlab::Popen::Result.new(cmd, 'abc123', '', double(success?: true)))
end
it 'returns the actual Git revision' do
@@ -59,8 +59,8 @@ describe Gitlab do
context 'when the Git command fails' do
before do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with(cmd)
- .and_return(Gitlab::Popen::Result.new(cmd, '', 'fatal: Not a git repository', double('Process::Status', success?: false)))
+ .with(cmd)
+ .and_return(Gitlab::Popen::Result.new(cmd, '', 'fatal: Not a git repository', double('Process::Status', success?: false)))
end
it 'returns "Unknown"' do
@@ -123,6 +123,27 @@ describe Gitlab do
end
end
+ describe '.dev_env_or_com?' do
+ it 'is true when on .com' do
+ allow(described_class).to receive(:com?).and_return(true)
+
+ expect(described_class.dev_env_or_com?).to eq true
+ end
+
+ it 'is true when dev env' do
+ allow(described_class).to receive(:com?).and_return(false)
+ allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development'))
+
+ expect(described_class.dev_env_or_com?).to eq true
+ end
+
+ it 'is false when not dev or com' do
+ allow(described_class).to receive(:com?).and_return(false)
+
+ expect(described_class.dev_env_or_com?).to eq false
+ end
+ end
+
describe '.ee?' do
before do
described_class.instance_variable_set(:@is_ee, nil)
@@ -138,12 +159,12 @@ describe Gitlab do
allow(described_class)
.to receive(:root)
- .and_return(root)
+ .and_return(root)
allow(root)
.to receive(:join)
- .with('ee/app/models/license.rb')
- .and_return(license_path)
+ .with('ee/app/models/license.rb')
+ .and_return(license_path)
expect(described_class.ee?).to eq(true)
end
@@ -154,12 +175,12 @@ describe Gitlab do
allow(described_class)
.to receive(:root)
- .and_return(Pathname.new('dummy'))
+ .and_return(Pathname.new('dummy'))
allow(root)
.to receive(:join)
- .with('ee/app/models/license.rb')
- .and_return(license_path)
+ .with('ee/app/models/license.rb')
+ .and_return(license_path)
expect(described_class.ee?).to eq(false)
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 2e7d78d77a8..9c58d307c4c 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -7,6 +7,10 @@ describe Issue do
describe "Associations" do
it { is_expected.to belong_to(:milestone) }
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:moved_to).class_name('Issue') }
+ it { is_expected.to belong_to(:duplicated_to).class_name('Issue') }
+ it { is_expected.to belong_to(:closed_by).class_name('User') }
it { is_expected.to have_many(:assignees) }
end
@@ -310,6 +314,22 @@ describe Issue do
end
end
+ describe '#duplicated?' do
+ let(:issue) { create(:issue) }
+ subject { issue.duplicated? }
+
+ context 'issue not duplicated' do
+ it { is_expected.to eq false }
+ end
+
+ context 'issue already duplicated' do
+ let(:duplicated_to_issue) { create(:issue) }
+ let(:issue) { create(:issue, duplicated_to: duplicated_to_issue) }
+
+ it { is_expected.to eq true }
+ end
+ end
+
describe '#suggested_branch_name' do
let(:repository) { double }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 11234982dd4..fde1b096c76 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -195,7 +195,6 @@ describe MergeRequest do
with_them do
it "validates source_branch" do
subject = build(:merge_request, source_branch: branch_name, target_branch: 'master')
-
subject.valid?
expect(subject.errors.added?(:source_branch)).to eq(!valid)
@@ -203,7 +202,6 @@ describe MergeRequest do
it "validates target_branch" do
subject = build(:merge_request, source_branch: 'master', target_branch: branch_name)
-
subject.valid?
expect(subject.errors.added?(:target_branch)).to eq(!valid)
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 5c9a5b73ee5..e58f1b7d9dc 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -1,4 +1,4 @@
-require "spec_helper"
+require 'spec_helper'
describe 'Git HTTP requests' do
include ProjectForksHelper
diff --git a/spec/rubocop/cop/graphql/authorize_types_spec.rb b/spec/rubocop/cop/graphql/authorize_types_spec.rb
index eae3e176d64..af4315ecd34 100644
--- a/spec/rubocop/cop/graphql/authorize_types_spec.rb
+++ b/spec/rubocop/cop/graphql/authorize_types_spec.rb
@@ -11,6 +11,23 @@ describe RuboCop::Cop::Graphql::AuthorizeTypes do
subject(:cop) { described_class.new }
+ context 'when NOT in a type folder' do
+ before do
+ allow(cop).to receive(:in_type?).and_return(false)
+ end
+
+ it 'does not add an offense even though there is no authorize call' do
+ expect_no_offenses(<<~TYPE.strip)
+ module Types
+ class AType < BaseObject
+ field :a_thing
+ field :another_thing
+ end
+ end
+ TYPE
+ end
+ end
+
context 'when in a type folder' do
before do
allow(cop).to receive(:in_type?).and_return(true)
diff --git a/spec/rubocop/cop/graphql/descriptions_spec.rb b/spec/rubocop/cop/graphql/descriptions_spec.rb
new file mode 100644
index 00000000000..8cfdc05172d
--- /dev/null
+++ b/spec/rubocop/cop/graphql/descriptions_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/graphql/descriptions'
+
+describe RuboCop::Cop::Graphql::Descriptions do
+ include RuboCop::RSpec::ExpectOffense
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ context 'fields' do
+ it 'adds an offense when there is no field description' do
+ inspect_source(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::STRING_TYPE,
+ null: false
+ end
+ end
+ TYPE
+
+ expect(cop.offenses.size).to eq 1
+ end
+
+ it 'does not add an offense for fields with a description' do
+ expect_no_offenses(<<~TYPE.strip)
+ module Types
+ class FakeType < BaseObject
+ graphql_name 'FakeTypeName'
+
+ argument :a_thing,
+ GraphQL::STRING_TYPE,
+ null: false,
+ description: 'A descriptive description'
+ end
+ end
+ TYPE
+ end
+ end
+
+ context 'arguments' do
+ it 'adds an offense when there is no argument description' do
+ inspect_source(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ argument :a_thing,
+ GraphQL::STRING_TYPE,
+ null: false
+ end
+ end
+ TYPE
+
+ expect(cop.offenses.size).to eq 1
+ end
+
+ it 'does not add an offense for arguments with a description' do
+ expect_no_offenses(<<~TYPE.strip)
+ module Types
+ class FakeType < BaseObject
+ graphql_name 'FakeTypeName'
+
+ argument :a_thing,
+ GraphQL::STRING_TYPE,
+ null: false,
+ description: 'Behold! A description'
+ end
+ end
+ TYPE
+ end
+ end
+end
diff --git a/spec/serializers/cluster_basic_entity_spec.rb b/spec/serializers/cluster_basic_entity_spec.rb
new file mode 100644
index 00000000000..6762eb6ab3d
--- /dev/null
+++ b/spec/serializers/cluster_basic_entity_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe ClusterBasicEntity do
+ describe '#as_json' do
+ subject { described_class.new(cluster, request: request).as_json }
+ let(:maintainer) { create(:user) }
+ let(:developer) { create(:user) }
+ let(:current_user) { maintainer }
+ let(:request) { double(:request, current_user: current_user) }
+ let(:project) { create(:project) }
+ let(:cluster) { create(:cluster, name: 'the-cluster', projects: [project]) }
+
+ before do
+ project.add_maintainer(maintainer)
+ project.add_developer(developer)
+ end
+
+ it 'matches cluster_basic entity schema' do
+ expect(subject.as_json).to match_schema('cluster_basic')
+ end
+
+ it 'exposes the cluster details' do
+ expect(subject[:name]).to eq('the-cluster')
+ expect(subject[:path]).to eq("/#{project.full_path}/clusters/#{cluster.id}")
+ end
+
+ context 'when the user does not have permission to view the cluster' do
+ let(:current_user) { developer }
+
+ it 'does not include the path' do
+ expect(subject[:path]).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb
index 0e05b3c84f4..53278062de9 100644
--- a/spec/serializers/issue_entity_spec.rb
+++ b/spec/serializers/issue_entity_spec.rb
@@ -50,4 +50,44 @@ describe IssueEntity do
end
end
end
+
+ context 'when issue got duplicated' do
+ let(:private_project) { create(:project, :private) }
+ let(:member) { create(:user) }
+ let(:issue) { create(:issue, project: project) }
+ let(:new_issue) { create(:issue, project: private_project) }
+
+ before do
+ Issues::DuplicateService
+ .new(project, member)
+ .execute(issue, new_issue)
+ end
+
+ context 'when user cannot read new issue' do
+ let(:non_member) { create(:user) }
+
+ it 'does not return duplicated_to_id' do
+ request = double('request', current_user: non_member)
+
+ response = described_class.new(issue, request: request).as_json
+
+ expect(response[:duplicated_to_id]).to be_nil
+ end
+ end
+
+ context 'when user can read target project' do
+ before do
+ project.add_developer(member)
+ private_project.add_developer(member)
+ end
+
+ it 'returns duplicated duplicated_to_id' do
+ request = double('request', current_user: member)
+
+ response = described_class.new(issue, request: request).as_json
+
+ expect(response[:duplicated_to_id]).to eq(issue.duplicated_to_id)
+ end
+ end
+ end
end
diff --git a/spec/services/issues/duplicate_service_spec.rb b/spec/services/issues/duplicate_service_spec.rb
index 2c9581b993c..41a151b0ca1 100644
--- a/spec/services/issues/duplicate_service_spec.rb
+++ b/spec/services/issues/duplicate_service_spec.rb
@@ -77,6 +77,12 @@ describe Issues::DuplicateService do
subject.execute(duplicate_issue, canonical_issue)
end
+
+ it 'updates duplicate issue with canonical issue id' do
+ subject.execute(duplicate_issue, canonical_issue)
+
+ expect(duplicate_issue.reload.duplicated_to).to eq(canonical_issue)
+ end
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 47f09bf14d0..7d44b2340f3 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -32,6 +32,8 @@ end
require 'rainbow/ext/string'
Rainbow.enabled = false
+require_relative('../ee/spec/spec_helper') if Gitlab.ee?
+
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
# Requires helpers, and shared contexts/examples first since they're used in other support files
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 041ffa25535..5da707b11f9 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -13,3 +13,5 @@ module DbCleaner
DatabaseCleaner[:active_record, { connection: ActiveRecord::Base }]
end
end
+
+DbCleaner.prepend_if_ee('EE::DbCleaner')
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index beb346b2855..4d2ad165fd6 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -172,6 +172,31 @@ module GraphqlHelpers
post_graphql(mutation.query, current_user: current_user, variables: mutation.variables)
end
+ # this implements GraphQL multipart request v2
+ # https://github.com/jaydenseric/graphql-multipart-request-spec/tree/v2.0.0-alpha.2
+ # this is simplified and do not support file deduplication
+ def mutation_to_apollo_uploads_param(mutation, files: [])
+ operations = { 'query' => mutation.query, 'variables' => mutation.variables }
+ map = {}
+ extracted_files = {}
+
+ files.each_with_index do |file_path, idx|
+ apollo_idx = (idx + 1).to_s
+ parent_dig_path = file_path[0..-2]
+ file_key = file_path[-1]
+
+ parent = operations['variables']
+ parent = parent.dig(*parent_dig_path) unless parent_dig_path.empty?
+
+ extracted_files[apollo_idx] = parent[file_key]
+ parent[file_key] = nil
+
+ map[apollo_idx] = ["variables.#{file_path.join('.')}"]
+ end
+
+ { operations: operations.to_json, map: map.to_json }.merge(extracted_files)
+ end
+
# Raises an error if no data is found
def graphql_data
json_response['data'] || (raise NoData, graphql_errors)
diff --git a/spec/support/helpers/ldap_helpers.rb b/spec/support/helpers/ldap_helpers.rb
index dce8a3803f5..0549c56c753 100644
--- a/spec/support/helpers/ldap_helpers.rb
+++ b/spec/support/helpers/ldap_helpers.rb
@@ -70,3 +70,5 @@ module LdapHelpers
.to receive(:ldap_search).and_raise(Gitlab::Auth::LDAP::LDAPConnectionError)
end
end
+
+LdapHelpers.include_if_ee('EE::LdapHelpers')
diff --git a/spec/support/helpers/license_helper.rb b/spec/support/helpers/license_helper.rb
index 4aaad55a8ef..a1defba9ccb 100644
--- a/spec/support/helpers/license_helper.rb
+++ b/spec/support/helpers/license_helper.rb
@@ -6,3 +6,5 @@ module LicenseHelpers
# do nothing
end
end
+
+LicenseHelpers.prepend_if_ee('EE::LicenseHelpers')
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index 2b508ee6f2c..140ad173d38 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -211,3 +211,5 @@ module LoginHelpers
allow(Gitlab::Auth::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', external_groups: groups, args: {} } })
end
end
+
+LoginHelpers.prepend_if_ee('EE::LoginHelpers')
diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb
index 2727ab7fb1e..a3145ed91bd 100644
--- a/spec/support/helpers/migrations_helpers.rb
+++ b/spec/support/helpers/migrations_helpers.rb
@@ -133,3 +133,5 @@ module MigrationsHelpers
end
end
end
+
+MigrationsHelpers.prepend_if_ee('EE::MigrationsHelpers')
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index f364e4fd158..0dc6e851190 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -149,3 +149,5 @@ end
require_relative '../../../ee/spec/support/helpers/ee/stub_configuration' if
Dir.exist?("#{__dir__}/../../../ee")
+
+StubConfiguration.prepend_if_ee('EE::StubConfiguration')
diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb
index 7d10cffe920..e3dde888277 100644
--- a/spec/support/helpers/stub_gitlab_calls.rb
+++ b/spec/support/helpers/stub_gitlab_calls.rb
@@ -139,3 +139,5 @@ module StubGitlabCalls
JSON.parse f
end
end
+
+StubGitlabCalls.prepend_if_ee('EE::StubGitlabCalls')
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index e5b8bb712bb..3f7002b8768 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -78,3 +78,5 @@ end
require_relative '../../../ee/spec/support/helpers/ee/stub_object_storage' if
Dir.exist?("#{__dir__}/../../../ee")
+
+StubObjectStorage.prepend_if_ee('EE::StubObjectStorage')
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 8ca362ce2df..3274651ef19 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -418,3 +418,8 @@ module TestEnv
true
end
end
+
+require_relative('../../../ee/spec/support/helpers/ee/test_env') if Gitlab.ee?
+
+::TestEnv.prepend_if_ee('::EE::TestEnv')
+::TestEnv.extend_if_ee('::EE::TestEnv')
diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb
index fdbfe53fa39..40007a14b85 100644
--- a/spec/support/helpers/workhorse_helpers.rb
+++ b/spec/support/helpers/workhorse_helpers.rb
@@ -29,7 +29,8 @@ module WorkhorseHelpers
post(url,
params: workhorse_params,
- headers: workhorse_rewritten_fields_header('file' => file.path)
+ headers: workhorse_rewritten_fields_header(file_key => file.path),
+ env: { 'CONTENT_TYPE' => 'multipart/form-data' }
)
end
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index 12e8fa83a60..3245f8418b1 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -230,3 +230,5 @@ module RSpec::Matchers::DSL::Macros
end
end
end
+
+MarkdownMatchers.prepend_if_ee('EE::MarkdownMatchers')
diff --git a/spec/views/projects/issues/show.html.haml_spec.rb b/spec/views/projects/issues/show.html.haml_spec.rb
index 1ca9eaf8fdb..d734d0b4a20 100644
--- a/spec/views/projects/issues/show.html.haml_spec.rb
+++ b/spec/views/projects/issues/show.html.haml_spec.rb
@@ -56,7 +56,41 @@ describe 'projects/issues/show' do
end
end
- it 'shows "Closed" if an issue has not been moved' do
+ context 'when the issue was duplicated' do
+ let(:new_issue) { create(:issue, project: project, author: user) }
+
+ before do
+ issue.duplicated_to = new_issue
+ end
+
+ context 'when user can see the duplicated issue' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'shows "Closed (duplicated)" if an issue has been duplicated' do
+ render
+
+ expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (duplicated)')
+ end
+
+ it 'links "duplicated" to the new issue the original issue was duplicated to' do
+ render
+
+ expect(rendered).to have_selector("a[href=\"#{issue_path(new_issue)}\"]", text: 'duplicated')
+ end
+ end
+
+ context 'when user cannot see duplicated issue' do
+ it 'does not show duplicated issue link' do
+ render
+
+ expect(rendered).not_to have_selector("a[href=\"#{issue_path(new_issue)}\"]", text: 'duplicated')
+ end
+ end
+ end
+
+ it 'shows "Closed" if an issue has not been moved or duplicated' do
render
expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed')
diff --git a/spec/views/shared/_label_row.html.haml_spec.rb b/spec/views/shared/_label_row.html.haml_spec.rb
index 4cce13aa37c..0764f8480c8 100644
--- a/spec/views/shared/_label_row.html.haml_spec.rb
+++ b/spec/views/shared/_label_row.html.haml_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
require 'spec_helper'
describe 'shared/_label_row.html.haml' do