summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-10-22 09:08:26 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-10-22 09:08:26 +0000
commit9dab4d7b6492628eb9222f14954fdd8889bd6e34 (patch)
treeae7723aff502efd04449be1692b4488cbd198de7
parentf0fa9f7acf4c23f9a9fb4903fd7c41678a20c028 (diff)
downloadgitlab-ce-9dab4d7b6492628eb9222f14954fdd8889bd6e34.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/design_management/pages/design/index.vue19
-rw-r--r--app/assets/javascripts/design_management/router/index.js17
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/modal_copy_button.vue33
-rw-r--r--app/controllers/projects/pipelines_controller.rb1
-rw-r--r--app/controllers/projects/static_site_editor_controller.rb2
-rw-r--r--changelogs/unreleased/sh-upgrade-workhorse-8-52-0.yml5
-rw-r--r--config/feature_flags/development/filter_pipelines_search.yml7
-rw-r--r--doc/user/analytics/value_stream_analytics.md4
-rw-r--r--lib/gitlab/error_tracking.rb7
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/controllers/projects/static_site_editor_controller_spec.rb7
-rw-r--r--spec/frontend/design_management/pages/design/index_spec.js110
-rw-r--r--spec/frontend/design_management/pages/index_spec.js8
-rw-r--r--spec/frontend/pipelines/pipelines_spec.js1
-rw-r--r--spec/frontend/vue_shared/components/modal_copy_button_spec.js11
-rw-r--r--spec/graphql/resolvers/projects_resolver_spec.rb4
-rw-r--r--spec/lib/gitlab/error_tracking_spec.rb98
22 files changed, 172 insertions, 181 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 12d13315ca4..5059b30b278 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-dac25a9c19af0a168a7927784295dd12fb5c8075
+19109e7090eb6dab2b8c0c168bbef76ceca79a31
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index f66efe38785..89a8ef8d8fc 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-8.51.0
+8.52.0
diff --git a/Gemfile b/Gemfile
index 9b683e20e9d..51f9d36cef9 100644
--- a/Gemfile
+++ b/Gemfile
@@ -307,9 +307,6 @@ gem 'rack-attack', '~> 6.3.0'
# Sentry integration
gem 'sentry-raven', '~> 3.0'
-# PostgreSQL query parsing
-gem 'pg_query', '~> 1.2'
-
gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
diff --git a/Gemfile.lock b/Gemfile.lock
index 241d21d4288..93670d22920 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -828,7 +828,6 @@ GEM
peek (1.1.0)
railties (>= 4.0.0)
pg (1.2.3)
- pg_query (1.2.0)
png_quantizator (0.2.1)
po_to_json (1.0.1)
json (>= 1.6.0)
@@ -1425,7 +1424,6 @@ DEPENDENCIES
parallel (~> 1.19)
peek (~> 1.1)
pg (~> 1.1)
- pg_query (~> 1.2)
png_quantizator (~> 0.2.1)
premailer-rails (~> 1.10.3)
prometheus-client-mmap (~> 0.12.0)
diff --git a/app/assets/javascripts/design_management/pages/design/index.vue b/app/assets/javascripts/design_management/pages/design/index.vue
index 6a96b06dcd8..642d51fbc7a 100644
--- a/app/assets/javascripts/design_management/pages/design/index.vue
+++ b/app/assets/javascripts/design_management/pages/design/index.vue
@@ -21,6 +21,7 @@ import {
updateImageDiffNoteOptimisticResponse,
toDiffNoteGid,
extractDesignNoteId,
+ getPageLayoutElement,
} from '../../utils/design_management_utils';
import {
updateStoreAfterAddImageDiffNote,
@@ -38,7 +39,7 @@ import {
} from '../../utils/error_messages';
import { trackDesignDetailView } from '../../utils/tracking';
import { DESIGNS_ROUTE_NAME } from '../../router/constants';
-import { ACTIVE_DISCUSSION_SOURCE_TYPES } from '../../constants';
+import { ACTIVE_DISCUSSION_SOURCE_TYPES, DESIGN_DETAIL_LAYOUT_CLASSLIST } from '../../constants';
const DEFAULT_SCALE = 1;
@@ -300,6 +301,22 @@ export default {
this.resolvedDiscussionsExpanded = !this.resolvedDiscussionsExpanded;
},
},
+ beforeRouteEnter(to, from, next) {
+ const pageEl = getPageLayoutElement();
+ if (pageEl) {
+ pageEl.classList.add(...DESIGN_DETAIL_LAYOUT_CLASSLIST);
+ }
+
+ next();
+ },
+ beforeRouteLeave(to, from, next) {
+ const pageEl = getPageLayoutElement();
+ if (pageEl) {
+ pageEl.classList.remove(...DESIGN_DETAIL_LAYOUT_CLASSLIST);
+ }
+
+ next();
+ },
createImageDiffNoteMutation,
DESIGNS_ROUTE_NAME,
};
diff --git a/app/assets/javascripts/design_management/router/index.js b/app/assets/javascripts/design_management/router/index.js
index cbeb2f7ce42..12692612bbc 100644
--- a/app/assets/javascripts/design_management/router/index.js
+++ b/app/assets/javascripts/design_management/router/index.js
@@ -1,9 +1,6 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
import routes from './routes';
-import { DESIGN_ROUTE_NAME } from './constants';
-import { getPageLayoutElement } from '~/design_management/utils/design_management_utils';
-import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '../constants';
Vue.use(VueRouter);
@@ -13,20 +10,6 @@ export default function createRouter(base) {
mode: 'history',
routes,
});
- const pageEl = getPageLayoutElement();
-
- router.beforeEach(({ name }, _, next) => {
- // apply a fullscreen layout style in Design View (a.k.a design detail)
- if (pageEl) {
- if (name === DESIGN_ROUTE_NAME) {
- pageEl.classList.add(...DESIGN_DETAIL_LAYOUT_CLASSLIST);
- } else {
- pageEl.classList.remove(...DESIGN_DETAIL_LAYOUT_CLASSLIST);
- }
- }
-
- next();
- });
return router;
}
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
index adba86d384b..00ccf59e37c 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
@@ -13,7 +13,6 @@ import CIPaginationMixin from '~/vue_shared/mixins/ci_pagination_api_mixin';
import PipelinesFilteredSearch from './pipelines_filtered_search.vue';
import { validateParams } from '../../utils';
import { ANY_TRIGGER_AUTHOR, RAW_TEXT_WARNING, FILTER_TAG_IDENTIFIER } from '../../constants';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
@@ -23,7 +22,7 @@ export default {
PipelinesFilteredSearch,
GlIcon,
},
- mixins: [pipelinesMixin, CIPaginationMixin, glFeatureFlagsMixin()],
+ mixins: [pipelinesMixin, CIPaginationMixin],
props: {
store: {
type: Object,
@@ -209,9 +208,6 @@ export default {
},
];
},
- canFilterPipelines() {
- return this.glFeatures.filterPipelinesSearch;
- },
validatedParams() {
return validateParams(this.params);
},
@@ -306,7 +302,6 @@ export default {
</div>
<pipelines-filtered-search
- v-if="canFilterPipelines"
:project-id="projectId"
:params="validatedParams"
@filterPipelines="filterPipelines"
diff --git a/app/assets/javascripts/vue_shared/components/modal_copy_button.vue b/app/assets/javascripts/vue_shared/components/modal_copy_button.vue
index cad4439ecea..de9c84dd157 100644
--- a/app/assets/javascripts/vue_shared/components/modal_copy_button.vue
+++ b/app/assets/javascripts/vue_shared/components/modal_copy_button.vue
@@ -1,8 +1,7 @@
<script>
-import $ from 'jquery';
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import Clipboard from 'clipboard';
-import { __ } from '~/locale';
+import { uniqueId } from 'lodash';
export default {
components: {
@@ -17,6 +16,11 @@ export default {
required: false,
default: '',
},
+ id: {
+ type: String,
+ required: false,
+ default: () => uniqueId('modal-copy-button-'),
+ },
container: {
type: String,
required: false,
@@ -52,7 +56,6 @@ export default {
default: null,
},
},
- copySuccessText: __('Copied'),
computed: {
modalDomId() {
return this.modalId ? `#${this.modalId}` : '';
@@ -68,11 +71,11 @@ export default {
});
this.clipboard
.on('success', e => {
- this.updateTooltip(e.trigger);
+ this.$root.$emit('bv::hide::tooltip', this.id);
this.$emit('success', e);
// Clear the selection and blur the trigger so it loses its border
e.clearSelection();
- $(e.trigger).blur();
+ e.trigger.blur();
})
.on('error', e => this.$emit('error', e));
});
@@ -82,29 +85,11 @@ export default {
this.clipboard.destroy();
}
},
- methods: {
- updateTooltip(target) {
- const $target = $(target);
- const originalTitle = $target.data('originalTitle');
-
- if ($target.tooltip) {
- /**
- * The original tooltip will continue staying there unless we remove it by hand.
- * $target.tooltip('hide') isn't working.
- */
- $('.tooltip').remove();
- $target.attr('title', this.$options.copySuccessText);
- $target.tooltip('_fixTitle');
- $target.tooltip('show');
- $target.attr('title', originalTitle);
- $target.tooltip('_fixTitle');
- }
- },
- },
};
</script>
<template>
<gl-button
+ :id="id"
v-gl-tooltip="{ placement: tooltipPlacement, container: tooltipContainer }"
:class="cssClasses"
:data-clipboard-target="target"
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 953dce4d63c..fa150412285 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -12,7 +12,6 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_create_pipeline!, only: [:new, :create, :config_variables]
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action do
- push_frontend_feature_flag(:filter_pipelines_search, project, default_enabled: true)
push_frontend_feature_flag(:dag_pipeline_tab, project, default_enabled: true)
push_frontend_feature_flag(:pipelines_security_report_summary, project)
push_frontend_feature_flag(:new_pipeline_form, project)
diff --git a/app/controllers/projects/static_site_editor_controller.rb b/app/controllers/projects/static_site_editor_controller.rb
index 7e2e32a843f..58cda84ebe7 100644
--- a/app/controllers/projects/static_site_editor_controller.rb
+++ b/app/controllers/projects/static_site_editor_controller.rb
@@ -47,6 +47,8 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
payload.transform_values do |value|
if value.is_a?(String) || value.is_a?(Integer)
value
+ elsif value.nil?
+ ''
else
value.to_json
end
diff --git a/changelogs/unreleased/sh-upgrade-workhorse-8-52-0.yml b/changelogs/unreleased/sh-upgrade-workhorse-8-52-0.yml
new file mode 100644
index 00000000000..0d5081113ad
--- /dev/null
+++ b/changelogs/unreleased/sh-upgrade-workhorse-8-52-0.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade Workhorse to v8.52.0
+merge_request: 45778
+author:
+type: fixed
diff --git a/config/feature_flags/development/filter_pipelines_search.yml b/config/feature_flags/development/filter_pipelines_search.yml
deleted file mode 100644
index 57bf61552d8..00000000000
--- a/config/feature_flags/development/filter_pipelines_search.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-name: filter_pipelines_search
-introduced_by_url:
-rollout_issue_url:
-group:
-type: development
-default_enabled: true
diff --git a/doc/user/analytics/value_stream_analytics.md b/doc/user/analytics/value_stream_analytics.md
index 86525587b30..5a498dea7bb 100644
--- a/doc/user/analytics/value_stream_analytics.md
+++ b/doc/user/analytics/value_stream_analytics.md
@@ -339,12 +339,14 @@ Feature.disable(:value_stream_analytics_create_multiple_value_streams)
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21631) in GitLab 12.6.
> - [Chart median line removed](https://gitlab.com/gitlab-org/gitlab/-/issues/235455) in GitLab 13.4.
-This chart visually depicts the total number of days it takes for cycles to be completed.
+This chart visually depicts the total number of days it takes for cycles to be completed. (Totals are being replaced with averages in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/262070).)
This chart uses the global page filters for displaying data based on the selected
group, projects, and timeframe. In addition, specific stages can be selected
from within the chart itself.
+The chart data is limited to the last 500 items.
+
### Disabling chart
This chart is enabled by default. If you have a self-managed instance, an
diff --git a/lib/gitlab/error_tracking.rb b/lib/gitlab/error_tracking.rb
index a5ace2be773..803acef9a40 100644
--- a/lib/gitlab/error_tracking.rb
+++ b/lib/gitlab/error_tracking.rb
@@ -123,7 +123,6 @@ module Gitlab
end
extra = sanitize_request_parameters(extra)
- inject_sql_query_into_extra(exception, extra)
if sentry && Raven.configuration.server
Raven.capture_exception(exception, tags: default_tags, extra: extra)
@@ -150,12 +149,6 @@ module Gitlab
filter.filter(parameters)
end
- def inject_sql_query_into_extra(exception, extra)
- return unless exception.is_a?(ActiveRecord::StatementInvalid)
-
- extra[:sql] = PgQuery.normalize(exception.sql.to_s)
- end
-
def sentry_dsn
return unless Rails.env.production? || Rails.env.development?
return unless Gitlab.config.sentry.enabled
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f6fdfb1706f..352ccdf4d1e 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8147,6 +8147,9 @@ msgstr ""
msgid "CycleAnalytics|The given date range is larger than 180 days"
msgstr ""
+msgid "CycleAnalytics|The total time spent in the selected stage for the items that were completed on each date. Data limited to the last 500 items."
+msgstr ""
+
msgid "CycleAnalytics|Total days to completion"
msgstr ""
diff --git a/spec/controllers/projects/static_site_editor_controller_spec.rb b/spec/controllers/projects/static_site_editor_controller_spec.rb
index 6ea730cbf27..867b2b51039 100644
--- a/spec/controllers/projects/static_site_editor_controller_spec.rb
+++ b/spec/controllers/projects/static_site_editor_controller_spec.rb
@@ -105,7 +105,8 @@ RSpec.describe Projects::StaticSiteEditorController do
foo: 'bar'
}
},
- a_boolean: true
+ a_boolean: true,
+ a_nil: nil
}
end
@@ -130,6 +131,10 @@ RSpec.describe Projects::StaticSiteEditorController do
it 'serializes data values which are hashes to JSON' do
expect(assigns_data[:a_hash]).to eq('{"a_deeper_hash":{"foo":"bar"}}')
end
+
+ it 'serializes data values which are nil to an empty string' do
+ expect(assigns_data[:a_nil]).to eq('')
+ end
end
end
end
diff --git a/spec/frontend/design_management/pages/design/index_spec.js b/spec/frontend/design_management/pages/design/index_spec.js
index d9f7146d258..d0c14c4c7eb 100644
--- a/spec/frontend/design_management/pages/design/index_spec.js
+++ b/spec/frontend/design_management/pages/design/index_spec.js
@@ -24,7 +24,13 @@ import mockAllVersions from '../../mock_data/all_versions';
jest.mock('~/flash');
const focusInput = jest.fn();
-
+const mutate = jest.fn().mockResolvedValue();
+const mockPageLayoutElement = {
+ classList: {
+ add: jest.fn(),
+ remove: jest.fn(),
+ },
+};
const DesignReplyForm = {
template: '<div><textarea ref="textarea"></textarea></div>',
methods: {
@@ -37,6 +43,32 @@ const mockDesignNoDiscussions = {
nodes: [],
},
};
+const newComment = 'new comment';
+const annotationCoordinates = {
+ x: 10,
+ y: 10,
+ width: 100,
+ height: 100,
+};
+const createDiscussionMutationVariables = {
+ mutation: createImageDiffNoteMutation,
+ update: expect.anything(),
+ variables: {
+ input: {
+ body: newComment,
+ noteableId: design.id,
+ position: {
+ headSha: 'headSha',
+ baseSha: 'baseSha',
+ startSha: 'startSha',
+ paths: {
+ newPath: 'full-design-path',
+ },
+ ...annotationCoordinates,
+ },
+ },
+ },
+};
const localVue = createLocalVue();
localVue.use(VueRouter);
@@ -45,35 +77,6 @@ describe('Design management design index page', () => {
let wrapper;
let router;
- const newComment = 'new comment';
- const annotationCoordinates = {
- x: 10,
- y: 10,
- width: 100,
- height: 100,
- };
- const createDiscussionMutationVariables = {
- mutation: createImageDiffNoteMutation,
- update: expect.anything(),
- variables: {
- input: {
- body: newComment,
- noteableId: design.id,
- position: {
- headSha: 'headSha',
- baseSha: 'baseSha',
- startSha: 'startSha',
- paths: {
- newPath: 'full-design-path',
- },
- ...annotationCoordinates,
- },
- },
- },
- };
-
- const mutate = jest.fn().mockResolvedValue();
-
const findDiscussionForm = () => wrapper.find(DesignReplyForm);
const findSidebar = () => wrapper.find(DesignSidebar);
const findDesignPresentation = () => wrapper.find(DesignPresentation);
@@ -122,19 +125,44 @@ describe('Design management design index page', () => {
wrapper.destroy();
});
- describe('when navigating', () => {
- it('applies fullscreen layout', () => {
- const mockEl = {
- classList: {
- add: jest.fn(),
- remove: jest.fn(),
- },
- };
- jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockEl);
+ describe('when navigating to component', () => {
+ it('applies fullscreen layout class', () => {
+ jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageLayoutElement);
createComponent({ loading: true });
- expect(mockEl.classList.add).toHaveBeenCalledTimes(1);
- expect(mockEl.classList.add).toHaveBeenCalledWith(...DESIGN_DETAIL_LAYOUT_CLASSLIST);
+ expect(mockPageLayoutElement.classList.add).toHaveBeenCalledTimes(1);
+ expect(mockPageLayoutElement.classList.add).toHaveBeenCalledWith(
+ ...DESIGN_DETAIL_LAYOUT_CLASSLIST,
+ );
+ });
+ });
+
+ describe('when navigating within the component', () => {
+ it('`scale` prop of DesignPresentation component is 1', async () => {
+ jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageLayoutElement);
+ createComponent({ loading: false }, { data: { design, scale: 2 } });
+
+ await wrapper.vm.$nextTick();
+ expect(findDesignPresentation().props('scale')).toBe(2);
+
+ DesignIndex.beforeRouteUpdate.call(wrapper.vm, {}, {}, jest.fn());
+ await wrapper.vm.$nextTick();
+
+ expect(findDesignPresentation().props('scale')).toBe(1);
+ });
+ });
+
+ describe('when navigating away from component', () => {
+ it('removes fullscreen layout class', async () => {
+ jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageLayoutElement);
+ createComponent({ loading: true });
+
+ wrapper.vm.$options.beforeRouteLeave[0].call(wrapper.vm, {}, {}, jest.fn());
+
+ expect(mockPageLayoutElement.classList.remove).toHaveBeenCalledTimes(1);
+ expect(mockPageLayoutElement.classList.remove).toHaveBeenCalledWith(
+ ...DESIGN_DETAIL_LAYOUT_CLASSLIST,
+ );
});
});
diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js
index 27a91b11448..279403789f1 100644
--- a/spec/frontend/design_management/pages/index_spec.js
+++ b/spec/frontend/design_management/pages/index_spec.js
@@ -19,7 +19,6 @@ import {
import { deprecatedCreateFlash as createFlash } from '~/flash';
import createRouter from '~/design_management/router';
import * as utils from '~/design_management/utils/design_management_utils';
-import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '~/design_management/constants';
import {
designListQueryResponse,
designUploadMutationCreatedResponse,
@@ -682,13 +681,6 @@ describe('Design management index page', () => {
});
describe('when navigating', () => {
- it('ensures fullscreen layout is not applied', () => {
- createComponent({ loading: true });
-
- expect(mockPageEl.classList.remove).toHaveBeenCalledTimes(1);
- expect(mockPageEl.classList.remove).toHaveBeenCalledWith(...DESIGN_DETAIL_LAYOUT_CLASSLIST);
- });
-
it('should trigger a scrollIntoView method if designs route is detected', () => {
router.replace({
path: '/designs',
diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js
index 1298a2a1524..5e04f9a6433 100644
--- a/spec/frontend/pipelines/pipelines_spec.js
+++ b/spec/frontend/pipelines/pipelines_spec.js
@@ -74,7 +74,6 @@ describe('Pipelines', () => {
const createComponent = (props = defaultProps, methods) => {
wrapper = mount(PipelinesComponent, {
- provide: { glFeatures: { filterPipelinesSearch: true } },
propsData: {
store: new Store(),
projectId: '21',
diff --git a/spec/frontend/vue_shared/components/modal_copy_button_spec.js b/spec/frontend/vue_shared/components/modal_copy_button_spec.js
index e5a8860f42e..ca9f8ff54d4 100644
--- a/spec/frontend/vue_shared/components/modal_copy_button_spec.js
+++ b/spec/frontend/vue_shared/components/modal_copy_button_spec.js
@@ -1,9 +1,7 @@
-import Vue from 'vue';
-import { shallowMount } from '@vue/test-utils';
-import modalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
+import { shallowMount, createWrapper } from '@vue/test-utils';
+import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
describe('modal copy button', () => {
- const Component = Vue.extend(modalCopyButton);
let wrapper;
afterEach(() => {
@@ -11,16 +9,18 @@ describe('modal copy button', () => {
});
beforeEach(() => {
- wrapper = shallowMount(Component, {
+ wrapper = shallowMount(ModalCopyButton, {
propsData: {
text: 'copy me',
title: 'Copy this value',
+ id: 'test-id',
},
});
});
describe('clipboard', () => {
it('should fire a `success` event on click', () => {
+ const root = createWrapper(wrapper.vm.$root);
document.execCommand = jest.fn(() => true);
window.getSelection = jest.fn(() => ({
toString: jest.fn(() => 'test'),
@@ -31,6 +31,7 @@ describe('modal copy button', () => {
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted().success).not.toBeEmpty();
expect(document.execCommand).toHaveBeenCalledWith('copy');
+ expect(root.emitted('bv::hide::tooltip')).toEqual([['test-id']]);
});
});
it("should propagate the clipboard error event if execCommand doesn't work", () => {
diff --git a/spec/graphql/resolvers/projects_resolver_spec.rb b/spec/graphql/resolvers/projects_resolver_spec.rb
index 83a26062957..3de54c7e410 100644
--- a/spec/graphql/resolvers/projects_resolver_spec.rb
+++ b/spec/graphql/resolvers/projects_resolver_spec.rb
@@ -134,8 +134,8 @@ RSpec.describe Resolvers::ProjectsResolver do
is_expected.to eq([named_project3, named_project1, named_project2])
end
- it 'returns projects not in order of similarity to search if flag is off' do
- is_expected.not_to eq([named_project3, named_project1, named_project2])
+ it 'returns projects in any order if flag is off' do
+ is_expected.to match_array([named_project3, named_project1, named_project2])
end
end
end
diff --git a/spec/lib/gitlab/error_tracking_spec.rb b/spec/lib/gitlab/error_tracking_spec.rb
index 68a46b11487..2cc9ff36c99 100644
--- a/spec/lib/gitlab/error_tracking_spec.rb
+++ b/spec/lib/gitlab/error_tracking_spec.rb
@@ -198,39 +198,47 @@ RSpec.describe Gitlab::ErrorTracking do
end
describe '.track_exception' do
- let(:extra) { { issue_url: issue_url, some_other_info: 'info' } }
-
- subject(:track_exception) { described_class.track_exception(exception, extra) }
-
- before do
- allow(Raven).to receive(:capture_exception).and_call_original
- allow(Gitlab::ErrorTracking::Logger).to receive(:error)
- end
-
it 'calls Raven.capture_exception' do
- track_exception
+ expected_extras = {
+ some_other_info: 'info',
+ issue_url: issue_url
+ }
- expect(Raven).to have_received(:capture_exception)
- .with(exception,
- tags: a_hash_including(correlation_id: 'cid'),
- extra: a_hash_including(some_other_info: 'info', issue_url: issue_url))
+ expected_tags = {
+ correlation_id: 'cid'
+ }
+
+ expect(Raven).to receive(:capture_exception)
+ .with(exception,
+ tags: a_hash_including(expected_tags),
+ extra: a_hash_including(expected_extras))
+
+ described_class.track_exception(
+ exception,
+ issue_url: issue_url,
+ some_other_info: 'info'
+ )
end
it 'calls Gitlab::ErrorTracking::Logger.error with formatted payload' do
- track_exception
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error)
+ .with(a_hash_including(*expected_payload_includes))
- expect(Gitlab::ErrorTracking::Logger).to have_received(:error)
- .with(a_hash_including(*expected_payload_includes))
+ described_class.track_exception(
+ exception,
+ issue_url: issue_url,
+ some_other_info: 'info'
+ )
end
context 'with filterable parameters' do
let(:extra) { { test: 1, my_token: 'test' } }
it 'filters parameters' do
- track_exception
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error).with(
+ hash_including({ 'extra.test' => 1, 'extra.my_token' => '[FILTERED]' }))
- expect(Gitlab::ErrorTracking::Logger).to have_received(:error)
- .with(hash_including({ 'extra.test' => 1, 'extra.my_token' => '[FILTERED]' }))
+ described_class.track_exception(exception, extra)
end
end
@@ -239,58 +247,44 @@ RSpec.describe Gitlab::ErrorTracking do
let(:exception) { double(message: 'bang!', sentry_extra_data: extra_info, backtrace: caller) }
it 'includes the extra data from the exception in the tracking information' do
- track_exception
+ expect(Raven).to receive(:capture_exception)
+ .with(exception, a_hash_including(extra: a_hash_including(extra_info)))
- expect(Raven).to have_received(:capture_exception)
- .with(exception, a_hash_including(extra: a_hash_including(extra_info)))
+ described_class.track_exception(exception)
end
end
context 'the exception implements :sentry_extra_data, which returns nil' do
let(:exception) { double(message: 'bang!', sentry_extra_data: nil, backtrace: caller) }
- let(:extra) { { issue_url: issue_url } }
it 'just includes the other extra info' do
- track_exception
+ extra_info = { issue_url: issue_url }
+ expect(Raven).to receive(:capture_exception)
+ .with(exception, a_hash_including(extra: a_hash_including(extra_info)))
- expect(Raven).to have_received(:capture_exception)
- .with(exception, a_hash_including(extra: a_hash_including(extra)))
+ described_class.track_exception(exception, extra_info)
end
end
context 'with sidekiq args' do
- context 'when the args does not have anything sensitive' do
- let(:extra) { { sidekiq: { 'class' => 'PostReceive', 'args' => [1, { 'id' => 2, 'name' => 'hello' }, 'some-value', 'another-value'] } } }
+ it 'ensures extra.sidekiq.args is a string' do
+ extra = { sidekiq: { 'class' => 'PostReceive', 'args' => [1, { 'id' => 2, 'name' => 'hello' }, 'some-value', 'another-value'] } }
- it 'ensures extra.sidekiq.args is a string' do
- track_exception
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error).with(
+ hash_including({ 'extra.sidekiq' => { 'class' => 'PostReceive', 'args' => ['1', '{"id"=>2, "name"=>"hello"}', 'some-value', 'another-value'] } }))
- expect(Gitlab::ErrorTracking::Logger).to have_received(:error).with(
- hash_including({ 'extra.sidekiq' => { 'class' => 'PostReceive', 'args' => ['1', '{"id"=>2, "name"=>"hello"}', 'some-value', 'another-value'] } }))
- end
+ described_class.track_exception(exception, extra)
end
- context 'when the args has sensitive information' do
- let(:extra) { { sidekiq: { 'class' => 'UnknownWorker', 'args' => ['sensitive string', 1, 2] } } }
-
- it 'filters sensitive arguments before sending' do
- track_exception
-
- expect(sentry_event.dig('extra', 'sidekiq', 'args')).to eq(['[FILTERED]', 1, 2])
- expect(Gitlab::ErrorTracking::Logger).to have_received(:error).with(
- hash_including('extra.sidekiq' => { 'class' => 'UnknownWorker', 'args' => ['[FILTERED]', '1', '2'] }))
- end
- end
- end
+ it 'filters sensitive arguments before sending' do
+ extra = { sidekiq: { 'class' => 'UnknownWorker', 'args' => ['sensitive string', 1, 2] } }
- context 'when the error is kind of an `ActiveRecord::StatementInvalid`' do
- let(:exception) { ActiveRecord::StatementInvalid.new(sql: 'SELECT "users".* FROM "users" WHERE "users"."id" = 1 AND "users"."foo" = $1') }
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error).with(
+ hash_including('extra.sidekiq' => { 'class' => 'UnknownWorker', 'args' => ['[FILTERED]', '1', '2'] }))
- it 'injects the normalized sql query into extra' do
- track_exception
+ described_class.track_exception(exception, extra)
- expect(Raven).to have_received(:capture_exception)
- .with(exception, a_hash_including(extra: a_hash_including(sql: 'SELECT "users".* FROM "users" WHERE "users"."id" = $2 AND "users"."foo" = $1')))
+ expect(sentry_event.dig('extra', 'sidekiq', 'args')).to eq(['[FILTERED]', 1, 2])
end
end
end