+
`;
@@ -214,7 +213,17 @@ export default class UserTabs {
AjaxCache.retrieve(calendarPath)
.then(data => UserTabs.renderActivityCalendar(data, $calendarWrap))
- .catch(() => flash(__('There was an error loading users activity calendar.')));
+ .catch(() => {
+ const cWrap = $calendarWrap[0];
+ cWrap.querySelector('.spinner').classList.add('invisible');
+ cWrap.querySelector('.user-calendar-error').classList.remove('invisible');
+ cWrap.querySelector('.user-calendar-error .js-retry-load').addEventListener('click', e => {
+ e.preventDefault();
+ cWrap.querySelector('.user-calendar-error').classList.add('invisible');
+ cWrap.querySelector('.spinner').classList.remove('invisible');
+ this.loadActivityCalendar();
+ });
+ });
}
static renderActivityCalendar(data, $calendarWrap) {
diff --git a/app/assets/javascripts/pipelines/components/dag/dag.vue b/app/assets/javascripts/pipelines/components/dag/dag.vue
index 6267b63328c..85171263f08 100644
--- a/app/assets/javascripts/pipelines/components/dag/dag.vue
+++ b/app/assets/javascripts/pipelines/components/dag/dag.vue
@@ -1,5 +1,5 @@
'}
+ ${'strong'} | ${'
highlighted'}
+ `('escapes `<$tag>` tags in text', ({ tag, text }) => {
+ createComponent(mockProps({ text }));
+
+ expect(
+ findLine()
+ .find(tag)
+ .exists(),
+ ).toBe(false);
+ expect(findLine().text()).toBe(text);
+ });
+ });
+
+ describe('when ci_job_line_links is enabled', () => {
+ beforeEach(() => {
+ window.gon.features = {
+ ciJobLineLinks: true,
+ };
+ });
+
+ it('renders an http link', () => {
+ createComponent(mockProps({ text: httpUrl }));
+
+ expect(findLink().text()).toBe(httpUrl);
+ expect(findLink().attributes().href).toBe(httpUrl);
+ });
+
+ it('renders an https link', () => {
+ createComponent(mockProps({ text: httpsUrl }));
+
+ expect(findLink().text()).toBe(httpsUrl);
+ expect(findLink().attributes().href).toBe(httpsUrl);
+ });
+
+ it('renders a multiple links surrounded by text', () => {
+ createComponent(mockProps({ text: `My HTTP url: ${httpUrl} and my HTTPS url: ${httpsUrl}` }));
+ expect(findLine().text()).toBe(
+ 'My HTTP url: http://example.com and my HTTPS url: https://example.com',
+ );
+ expect(findLinksAt(0).attributes().href).toBe(httpUrl);
+ expect(findLinksAt(1).attributes().href).toBe(httpsUrl);
+ });
+
+ it('renders a link with rel nofollow and noopener', () => {
+ createComponent(mockProps({ text: httpsUrl }));
+
+ expect(findLink().attributes().rel).toBe('nofollow noopener noreferrer');
+ });
+
+ it('render links surrounded by text', () => {
+ createComponent(
+ mockProps({ text: `My HTTP url: ${httpUrl} and my HTTPS url: ${httpsUrl} are here.` }),
+ );
+ expect(findLine().text()).toBe(
+ 'My HTTP url: http://example.com and my HTTPS url: https://example.com are here.',
+ );
+ expect(findLinksAt(0).attributes().href).toBe(httpUrl);
+ expect(findLinksAt(1).attributes().href).toBe(httpsUrl);
+ });
+
+ const jshref = 'javascript:doEvil();'; // eslint-disable-line no-script-url
+
+ test.each`
+ type | text
+ ${'js'} | ${jshref}
+ ${'file'} | ${'file:///a-file'}
+ ${'ftp'} | ${'ftp://example.com/file'}
+ ${'email'} | ${'email@example.com'}
+ ${'no scheme'} | ${'example.com/page'}
+ `('does not render a $type link', ({ text }) => {
+ createComponent(mockProps({ text }));
+ expect(findLink().exists()).toBe(false);
+ });
});
});
diff --git a/spec/frontend/notes/components/note_actions_spec.js b/spec/frontend/notes/components/note_actions_spec.js
index a79c3bbacb7..f01c6c6b84e 100644
--- a/spec/frontend/notes/components/note_actions_spec.js
+++ b/spec/frontend/notes/components/note_actions_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import { shallowMount, createLocalVue, createWrapper } from '@vue/test-utils';
+import { mount, createLocalVue, createWrapper } from '@vue/test-utils';
import { TEST_HOST } from 'spec/test_constants';
import AxiosMockAdapter from 'axios-mock-adapter';
import createStore from '~/notes/stores';
@@ -14,9 +14,9 @@ describe('noteActions', () => {
let actions;
let axiosMock;
- const shallowMountNoteActions = (propsData, computed) => {
+ const mountNoteActions = (propsData, computed) => {
const localVue = createLocalVue();
- return shallowMount(localVue.extend(noteActions), {
+ return mount(localVue.extend(noteActions), {
store,
propsData,
localVue,
@@ -61,7 +61,7 @@ describe('noteActions', () => {
beforeEach(() => {
store.dispatch('setUserData', userDataMock);
- wrapper = shallowMountNoteActions(props);
+ wrapper = mountNoteActions(props);
});
it('should render noteable author badge', () => {
@@ -178,7 +178,7 @@ describe('noteActions', () => {
};
beforeEach(() => {
- wrapper = shallowMountNoteActions(props, {
+ wrapper = mountNoteActions(props, {
targetType: () => 'issue',
});
store.state.noteableData = {
@@ -205,7 +205,7 @@ describe('noteActions', () => {
};
beforeEach(() => {
- wrapper = shallowMountNoteActions(props, {
+ wrapper = mountNoteActions(props, {
targetType: () => 'issue',
});
});
@@ -221,7 +221,7 @@ describe('noteActions', () => {
describe('user is not logged in', () => {
beforeEach(() => {
store.dispatch('setUserData', {});
- wrapper = shallowMountNoteActions({
+ wrapper = mountNoteActions({
...props,
canDelete: false,
canEdit: false,
@@ -241,7 +241,7 @@ describe('noteActions', () => {
describe('for showReply = true', () => {
beforeEach(() => {
- wrapper = shallowMountNoteActions({
+ wrapper = mountNoteActions({
...props,
showReply: true,
});
@@ -256,7 +256,7 @@ describe('noteActions', () => {
describe('for showReply = false', () => {
beforeEach(() => {
- wrapper = shallowMountNoteActions({
+ wrapper = mountNoteActions({
...props,
showReply: false,
});
@@ -273,7 +273,7 @@ describe('noteActions', () => {
beforeEach(() => {
store.dispatch('setUserData', userDataMock);
- wrapper = shallowMountNoteActions({ ...props, canResolve: true, isDraft: true });
+ wrapper = mountNoteActions({ ...props, canResolve: true, isDraft: true });
});
it('should render the right resolve button title', () => {
diff --git a/spec/graphql/resolvers/alert_management/integrations_resolver_spec.rb b/spec/graphql/resolvers/alert_management/integrations_resolver_spec.rb
index 910da77ad48..36e409e0677 100644
--- a/spec/graphql/resolvers/alert_management/integrations_resolver_spec.rb
+++ b/spec/graphql/resolvers/alert_management/integrations_resolver_spec.rb
@@ -28,14 +28,6 @@ RSpec.describe Resolvers::AlertManagement::IntegrationsResolver do
end
it { is_expected.to contain_exactly(active_http_integration, prometheus_integration) }
-
- context 'feature flag is not enabled' do
- before do
- stub_feature_flags(multiple_http_integrations: false)
- end
-
- it { is_expected.to be_empty }
- end
end
private
diff --git a/spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb b/spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb
new file mode 100644
index 00000000000..7fe82420364
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillJiraTrackerDeploymentType2, :migration, schema: 20201028182809 do
+ let_it_be(:jira_service_temp) { described_class::JiraServiceTemp }
+ let_it_be(:jira_tracker_data_temp) { described_class::JiraTrackerDataTemp }
+ let_it_be(:atlassian_host) { 'https://api.atlassian.net' }
+ let_it_be(:mixedcase_host) { 'https://api.AtlassiaN.nEt' }
+ let_it_be(:server_host) { 'https://my.server.net' }
+
+ let(:jira_service) { jira_service_temp.create!(type: 'JiraService', active: true, category: 'issue_tracker') }
+
+ subject { described_class.new }
+
+ def create_tracker_data(options = {})
+ jira_tracker_data_temp.create!({ service_id: jira_service.id }.merge(options))
+ end
+
+ describe '#perform' do
+ context do
+ it 'ignores if deployment already set' do
+ tracker_data = create_tracker_data(url: atlassian_host, deployment_type: 'server')
+
+ expect(subject).not_to receive(:collect_deployment_type)
+
+ subject.perform(tracker_data.id, tracker_data.id)
+
+ expect(tracker_data.reload.deployment_type).to eq 'server'
+ end
+
+ it 'ignores if no url is set' do
+ tracker_data = create_tracker_data(deployment_type: 'unknown')
+
+ expect(subject).to receive(:collect_deployment_type)
+
+ subject.perform(tracker_data.id, tracker_data.id)
+
+ expect(tracker_data.reload.deployment_type).to eq 'unknown'
+ end
+ end
+
+ context 'when tracker is valid' do
+ let!(:tracker_1) { create_tracker_data(url: atlassian_host, deployment_type: 0) }
+ let!(:tracker_2) { create_tracker_data(url: mixedcase_host, deployment_type: 0) }
+ let!(:tracker_3) { create_tracker_data(url: server_host, deployment_type: 0) }
+ let!(:tracker_4) { create_tracker_data(api_url: server_host, deployment_type: 0) }
+ let!(:tracker_nextbatch) { create_tracker_data(api_url: atlassian_host, deployment_type: 0) }
+
+ it 'sets the proper deployment_type', :aggregate_failures do
+ subject.perform(tracker_1.id, tracker_4.id)
+
+ expect(tracker_1.reload.deployment_cloud?).to be_truthy
+ expect(tracker_2.reload.deployment_cloud?).to be_truthy
+ expect(tracker_3.reload.deployment_server?).to be_truthy
+ expect(tracker_4.reload.deployment_server?).to be_truthy
+ expect(tracker_nextbatch.reload.deployment_unknown?).to be_truthy
+ end
+ end
+
+ it_behaves_like 'marks background migration job records' do
+ let(:arguments) { [1, 4] }
+ end
+ end
+end
diff --git a/spec/migrations/20201028182809_backfill_jira_tracker_deployment_type2_spec.rb b/spec/migrations/20201028182809_backfill_jira_tracker_deployment_type2_spec.rb
new file mode 100644
index 00000000000..658b26b1c49
--- /dev/null
+++ b/spec/migrations/20201028182809_backfill_jira_tracker_deployment_type2_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20201028182809_backfill_jira_tracker_deployment_type2.rb')
+
+RSpec.describe BackfillJiraTrackerDeploymentType2, :sidekiq, schema: 20201028182809 do
+ let(:services) { table(:services) }
+ let(:jira_tracker_data) { table(:jira_tracker_data) }
+ let(:migration) { described_class.new }
+ let(:batch_interval) { described_class::DELAY_INTERVAL }
+
+ describe '#up' do
+ before do
+ stub_const("#{described_class}::BATCH_SIZE", 2)
+
+ active_service = services.create!(type: 'JiraService', active: true)
+ inactive_service = services.create!(type: 'JiraService', active: false)
+
+ jira_tracker_data.create!(id: 1, service_id: active_service.id, deployment_type: 0)
+ jira_tracker_data.create!(id: 2, service_id: active_service.id, deployment_type: 1)
+ jira_tracker_data.create!(id: 3, service_id: inactive_service.id, deployment_type: 2)
+ jira_tracker_data.create!(id: 4, service_id: inactive_service.id, deployment_type: 0)
+ jira_tracker_data.create!(id: 5, service_id: active_service.id, deployment_type: 0)
+ end
+
+ it 'schedules BackfillJiraTrackerDeploymentType2 background jobs' do
+ Sidekiq::Testing.fake! do
+ freeze_time do
+ migration.up
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq(2)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(batch_interval, 1, 4)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(batch_interval * 2, 5, 5)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/alert_management/http_integrations/create_service_spec.rb b/spec/services/alert_management/http_integrations/create_service_spec.rb
index c83e6114b1e..ac5c62caf84 100644
--- a/spec/services/alert_management/http_integrations/create_service_spec.rb
+++ b/spec/services/alert_management/http_integrations/create_service_spec.rb
@@ -38,14 +38,6 @@ RSpec.describe AlertManagement::HttpIntegrations::CreateService do
it_behaves_like 'error response', 'You have insufficient permissions to create an HTTP integration for this project'
end
- context 'when feature flag is not enabled' do
- before do
- stub_feature_flags(multiple_http_integrations: false)
- end
-
- it_behaves_like 'error response', 'Multiple HTTP integrations are not supported for this project'
- end
-
context 'when an integration already exists' do
let_it_be(:existing_integration) { create(:alert_management_http_integration, project: project) }
diff --git a/spec/services/alert_management/http_integrations/destroy_service_spec.rb b/spec/services/alert_management/http_integrations/destroy_service_spec.rb
index 4e64ad9145b..cd949d728de 100644
--- a/spec/services/alert_management/http_integrations/destroy_service_spec.rb
+++ b/spec/services/alert_management/http_integrations/destroy_service_spec.rb
@@ -38,14 +38,6 @@ RSpec.describe AlertManagement::HttpIntegrations::DestroyService do
it_behaves_like 'error response', 'You have insufficient permissions to remove this HTTP integration'
end
- context 'when feature flag is not enabled' do
- before do
- stub_feature_flags(multiple_http_integrations: false)
- end
-
- it_behaves_like 'error response', 'Removing integrations is not supported for this project'
- end
-
context 'when an error occurs during removal' do
before do
allow(integration).to receive(:destroy).and_return(false)
diff --git a/spec/services/alert_management/http_integrations/update_service_spec.rb b/spec/services/alert_management/http_integrations/update_service_spec.rb
index 7917e32915a..94c34d9a29c 100644
--- a/spec/services/alert_management/http_integrations/update_service_spec.rb
+++ b/spec/services/alert_management/http_integrations/update_service_spec.rb
@@ -39,14 +39,6 @@ RSpec.describe AlertManagement::HttpIntegrations::UpdateService do
it_behaves_like 'error response', 'You have insufficient permissions to update this HTTP integration'
end
- context 'when feature flag is not enabled' do
- before do
- stub_feature_flags(multiple_http_integrations: false)
- end
-
- it_behaves_like 'error response', 'Multiple HTTP integrations are not supported for this project'
- end
-
context 'when an error occurs during update' do
let(:params) { { name: '' } }
diff --git a/spec/services/projects/alerting/notify_service_spec.rb b/spec/services/projects/alerting/notify_service_spec.rb
index 786280e8250..4674f614cf1 100644
--- a/spec/services/projects/alerting/notify_service_spec.rb
+++ b/spec/services/projects/alerting/notify_service_spec.rb
@@ -253,23 +253,6 @@ RSpec.describe Projects::Alerting::NotifyService do
end
end
- context 'with an Alerts Service' do
- let_it_be_with_reload(:integration) { create(:alerts_service, project: project) }
-
- it_behaves_like 'notifcations are handled correctly' do
- let(:source) { 'Generic Alert Endpoint' }
- end
-
- context 'with deactivated Alerts Service' do
- before do
- integration.update!(active: false)
- end
-
- it_behaves_like 'does not process incident issues due to error', http_status: :forbidden
- it_behaves_like 'does not an create alert management alert'
- end
- end
-
context 'with an HTTP Integration' do
let_it_be_with_reload(:integration) { create(:alert_management_http_integration, project: project) }
@@ -284,7 +267,7 @@ RSpec.describe Projects::Alerting::NotifyService do
integration.update!(active: false)
end
- it_behaves_like 'does not process incident issues due to error', http_status: :unauthorized
+ it_behaves_like 'does not process incident issues due to error', http_status: :forbidden
it_behaves_like 'does not an create alert management alert'
end
end
diff --git a/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb
new file mode 100644
index 00000000000..20f3270526e
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'marks background migration job records' do
+ it 'marks each job record as succeeded after processing' do
+ create(:background_migration_job, class_name: "::#{described_class.name}",
+ arguments: arguments)
+
+ expect(::Gitlab::Database::BackgroundMigrationJob).to receive(:mark_all_as_succeeded).and_call_original
+
+ expect do
+ subject.perform(*arguments)
+ end.to change { ::Gitlab::Database::BackgroundMigrationJob.succeeded.count }.from(0).to(1)
+ end
+
+ it 'returns the number of job records marked as succeeded' do
+ create(:background_migration_job, class_name: "::#{described_class.name}",
+ arguments: arguments)
+
+ jobs_updated = subject.perform(*arguments)
+
+ expect(jobs_updated).to eq(1)
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb
index c24e6050684..4d6442879d2 100644
--- a/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb
@@ -128,7 +128,6 @@ RSpec.shared_examples 'write access for a read-only GitLab instance' do
'LFS request to locks create' | '/root/rouge.git/info/lfs/locks'
'LFS request to locks unlock' | '/root/rouge.git/info/lfs/locks/1/unlock'
'request to git-upload-pack' | '/root/rouge.git/git-upload-pack'
- 'request to git-receive-pack' | '/root/rouge.git/git-receive-pack'
end
with_them do