From 4f01ac5ba0bf72427ed4fef9b229d056dbb60e89 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 13 Nov 2019 09:06:41 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../diffs/components/diff_content.vue | 2 + app/assets/javascripts/lib/graphql.js | 7 +- .../monitoring/components/dashboard.vue | 23 ++++- .../settings/operations/show/index.js | 4 +- .../components/diff_viewer/diff_viewer.vue | 12 +++ .../viewers/image_diff/two_up_viewer.vue | 12 +++ .../diff_viewer/viewers/image_diff_viewer.vue | 10 +++ app/controllers/application_controller.rb | 10 +-- .../concerns/confirm_email_warning.rb | 7 +- app/controllers/concerns/uploads_actions.rb | 17 ++++ .../projects/grafana_api_controller.rb | 6 -- app/workers/expire_build_artifacts_worker.rb | 19 ---- ...diting-metric-dashboard-using-yml-file.yml | 5 ++ ...-trial-header-to-include-the-tier-gold.yml | 5 ++ .../unreleased/defect-diff-file-size.yml | 5 ++ .../unreleased/tr-remove-grafana-ff.yml | 5 ++ .../filter/inline_grafana_metrics_filter.rb | 2 - lib/gitlab/gon_helper.rb | 3 - locale/gitlab.pot | 8 +- package.json | 2 +- .../application_controller_spec.rb | 14 ++- .../projects/grafana_api_controller_spec.rb | 12 --- spec/controllers/uploads_controller_spec.rb | 24 ++--- .../settings/operations_settings_spec.rb | 12 +-- .../viewers/image_viewer_spec.js | 45 ++++++++++ .../monitoring/components/dashboard_spec.js | 43 +++++++++ spec/javascripts/monitoring/mock_data.js | 19 +++- .../diff_viewer/diff_viewer_spec.js | 74 +++++++-------- .../viewers/image_diff_viewer_spec.js | 45 +++++----- .../inline_grafana_metrics_filter_spec.rb | 10 --- spec/requests/user_avatar_spec.rb | 36 ++++++++ .../expire_build_artifacts_worker_spec.rb | 51 ----------- yarn.lock | 89 +++++-------------- 33 files changed, 359 insertions(+), 279 deletions(-) create mode 100644 changelogs/unreleased/34779-editing-metric-dashboard-using-yml-file.yml create mode 100644 changelogs/unreleased/36142-update-saas-trial-header-to-include-the-tier-gold.yml create mode 100644 changelogs/unreleased/defect-diff-file-size.yml create mode 100644 changelogs/unreleased/tr-remove-grafana-ff.yml create mode 100644 spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js create mode 100644 spec/requests/user_avatar_spec.rb diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue index 9a1e59ec045..a5ffa84e3fb 100644 --- a/app/assets/javascripts/diffs/components/diff_content.vue +++ b/app/assets/javascripts/diffs/components/diff_content.vue @@ -124,8 +124,10 @@ export default { :diff-viewer-mode="diffViewerMode" :new-path="diffFile.new_path" :new-sha="diffFile.diff_refs.head_sha" + :new-size="diffFile.new_size" :old-path="diffFile.old_path" :old-sha="diffFile.diff_refs.base_sha" + :old-size="diffFile.old_size" :file-hash="diffFileHash" :project-path="projectPath" :a-mode="diffFile.a_mode" diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js index ca797cde913..2c5278d16ae 100644 --- a/app/assets/javascripts/lib/graphql.js +++ b/app/assets/javascripts/lib/graphql.js @@ -26,8 +26,11 @@ export default (resolvers = {}, config = {}) => { createUploadLink(httpOptions), new BatchHttpLink(httpOptions), ), - cache: new InMemoryCache({ ...config.cacheConfig, freezeResults: true }), + cache: new InMemoryCache({ + ...config.cacheConfig, + freezeResults: config.assumeImmutableResults, + }), resolvers, - assumeImmutableResults: true, + assumeImmutableResults: config.assumeImmutableResults, }); }; diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 94c7bf1cee4..705044565a0 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -187,8 +187,11 @@ export default { firstDashboard() { return this.allDashboards[0] || {}; }, + selectedDashboard() { + return this.allDashboards.find(d => d.path === this.currentDashboard) || this.firstDashboard; + }, selectedDashboardText() { - return this.currentDashboard || this.firstDashboard.display_name; + return this.selectedDashboard.display_name; }, showRearrangePanelsBtn() { return !this.showEmptyState && this.rearrangePanelsAvailable; @@ -199,6 +202,14 @@ export default { alertWidgetAvailable() { return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint; }, + hasHeaderButtons() { + return ( + this.addingMetricsAvailable || + this.showRearrangePanelsBtn || + this.selectedDashboard.can_edit || + this.externalDashboardUrl.length + ); + }, }, created() { this.setEndpoints({ @@ -390,7 +401,7 @@ export default { @@ -437,6 +448,14 @@ export default { + + {{ __('Edit dashboard') }} + + { mountErrorTrackingForm(); mountOperationSettings(); - if (gon.features.gfmGrafanaIntegration) { - mountGrafanaIntegration(); - } + mountGrafanaIntegration(); initSettingsPanels(); }); diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue index ebb253ff422..b874bedab36 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue @@ -23,6 +23,11 @@ export default { type: String, required: true, }, + newSize: { + type: Number, + required: false, + default: 0, + }, oldPath: { type: String, required: true, @@ -31,6 +36,11 @@ export default { type: String, required: true, }, + oldSize: { + type: Number, + required: false, + default: 0, + }, projectPath: { type: String, required: false, @@ -85,6 +95,8 @@ export default { :diff-mode="diffMode" :new-path="fullNewPath" :old-path="fullOldPath" + :old-size="oldSize" + :new-size="newSize" :project-path="projectPath" :a-mode="aMode" :b-mode="bMode" diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue index a17fc022195..4dbfdb6d79c 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue @@ -14,6 +14,16 @@ export default { type: String, required: true, }, + newSize: { + type: Number, + required: false, + default: 0, + }, + oldSize: { + type: Number, + required: false, + default: 0, + }, }, }; @@ -22,12 +32,14 @@ export default {
{ Feature.enabled?(:soft_email_confirmation) } + before_action :set_confirm_warning, if: :show_confirm_warning? end protected + def show_confirm_warning? + html_request? && request.get? && Feature.enabled?(:soft_email_confirmation) + end + def set_confirm_warning return unless current_user return if current_user.confirmed? - return if peek_request? || json_request? || !request.get? email = current_user.unconfirmed_email || current_user.email diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index b87779c22d3..023c41821da 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -1,11 +1,16 @@ # frozen_string_literal: true module UploadsActions + extend ActiveSupport::Concern include Gitlab::Utils::StrongMemoize include SendFileUpload UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo favicon).freeze + included do + prepend_before_action :set_request_format_from_path_extension + end + def create uploader = UploadService.new(model, params[:file], uploader_class).execute @@ -64,6 +69,18 @@ module UploadsActions private + # From ActionDispatch::Http::MimeNegotiation. We have an initializer that + # monkey-patches this method out (so that repository paths don't guess a + # format based on extension), but we do want this behaviour when serving + # uploads. + def set_request_format_from_path_extension + path = request.headers['action_dispatch.original_path'] || request.headers['PATH_INFO'] + + if match = path&.match(/\.(\w+)\z/) + request.format = match.captures.first + end + end + def uploader_class raise NotImplementedError end diff --git a/app/controllers/projects/grafana_api_controller.rb b/app/controllers/projects/grafana_api_controller.rb index 85f47b92e58..380a18818ab 100644 --- a/app/controllers/projects/grafana_api_controller.rb +++ b/app/controllers/projects/grafana_api_controller.rb @@ -4,8 +4,6 @@ class Projects::GrafanaApiController < Projects::ApplicationController include RenderServiceResults include MetricsDashboard - before_action :validate_feature_enabled!, only: [:metrics_dashboard] - def proxy result = ::Grafana::ProxyService.new( project, @@ -26,10 +24,6 @@ class Projects::GrafanaApiController < Projects::ApplicationController params.permit(:embedded, :grafana_url) end - def validate_feature_enabled! - render_403 unless Feature.enabled?(:gfm_grafana_integration) - end - def query_params params.permit(:query, :start, :end, :step) end diff --git a/app/workers/expire_build_artifacts_worker.rb b/app/workers/expire_build_artifacts_worker.rb index 9545227fa31..383fd30e098 100644 --- a/app/workers/expire_build_artifacts_worker.rb +++ b/app/workers/expire_build_artifacts_worker.rb @@ -7,25 +7,6 @@ class ExpireBuildArtifactsWorker feature_category :continuous_integration def perform - if Feature.enabled?(:ci_new_expire_job_artifacts_service, default_enabled: true) - perform_efficient_artifacts_removal - else - perform_legacy_artifacts_removal - end - end - - def perform_efficient_artifacts_removal Ci::DestroyExpiredJobArtifactsService.new.execute end - - # rubocop: disable CodeReuse/ActiveRecord - def perform_legacy_artifacts_removal - Rails.logger.info 'Scheduling removal of build artifacts' # rubocop:disable Gitlab/RailsLogger - - build_ids = Ci::Build.with_expired_artifacts.pluck(:id) - build_ids = build_ids.map { |build_id| [build_id] } - - ExpireBuildInstanceArtifactsWorker.bulk_perform_async(build_ids) - end - # rubocop: enable CodeReuse/ActiveRecord end diff --git a/changelogs/unreleased/34779-editing-metric-dashboard-using-yml-file.yml b/changelogs/unreleased/34779-editing-metric-dashboard-using-yml-file.yml new file mode 100644 index 00000000000..f4a4d67175e --- /dev/null +++ b/changelogs/unreleased/34779-editing-metric-dashboard-using-yml-file.yml @@ -0,0 +1,5 @@ +--- +title: Add edit button to metrics dashboard +merge_request: 19279 +author: +type: added diff --git a/changelogs/unreleased/36142-update-saas-trial-header-to-include-the-tier-gold.yml b/changelogs/unreleased/36142-update-saas-trial-header-to-include-the-tier-gold.yml new file mode 100644 index 00000000000..d68b5b1f4b9 --- /dev/null +++ b/changelogs/unreleased/36142-update-saas-trial-header-to-include-the-tier-gold.yml @@ -0,0 +1,5 @@ +--- +title: Update SaaS trial header to include the tier Gold +merge_request: 19970 +author: +type: changed diff --git a/changelogs/unreleased/defect-diff-file-size.yml b/changelogs/unreleased/defect-diff-file-size.yml new file mode 100644 index 00000000000..9bc24cadf6b --- /dev/null +++ b/changelogs/unreleased/defect-diff-file-size.yml @@ -0,0 +1,5 @@ +--- +title: Re-add missing file sizes in 2-Up diff file viewer +merge_request: 19710 +author: +type: fixed diff --git a/changelogs/unreleased/tr-remove-grafana-ff.yml b/changelogs/unreleased/tr-remove-grafana-ff.yml new file mode 100644 index 00000000000..2b62f57872a --- /dev/null +++ b/changelogs/unreleased/tr-remove-grafana-ff.yml @@ -0,0 +1,5 @@ +--- +title: Allow Grafana charts to be embedded in Gitlab Flavored Markdown +merge_request: 18486 +author: +type: added diff --git a/lib/banzai/filter/inline_grafana_metrics_filter.rb b/lib/banzai/filter/inline_grafana_metrics_filter.rb index 34d93a59f48..321580b532f 100644 --- a/lib/banzai/filter/inline_grafana_metrics_filter.rb +++ b/lib/banzai/filter/inline_grafana_metrics_filter.rb @@ -18,8 +18,6 @@ module Banzai end def embed_params(node) - return unless Feature.enabled?(:gfm_grafana_integration) - query_params = Gitlab::Metrics::Dashboard::Url.parse_query(node['href']) return unless [:panelId, :from, :to].all? do |param| query_params.include?(param) diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index f1e31a615a4..2616a19fdaa 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -42,9 +42,6 @@ module Gitlab # Initialize gon.features with any flags that should be # made globally available to the frontend push_frontend_feature_flag(:suppress_ajax_navigation_errors, default_enabled: true) - - # Flag controls a GFM feature used across many routes. - push_frontend_feature_flag(:gfm_grafana_integration) end # Exposes the state of a feature flag to the frontend code. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6390b11e838..1f536157c85 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6022,6 +6022,9 @@ msgstr "" msgid "Edit comment" msgstr "" +msgid "Edit dashboard" +msgstr "" + msgid "Edit description" msgstr "" @@ -6442,6 +6445,9 @@ msgstr "" msgid "Environments|No deployments yet" msgstr "" +msgid "Environments|No pods to display" +msgstr "" + msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file." msgstr "" @@ -16145,7 +16151,7 @@ msgstr "" msgid "Start a %{new_merge_request} with these changes" msgstr "" -msgid "Start a Free Trial" +msgid "Start a Free Gold Trial" msgstr "" msgid "Start a new discussion..." diff --git a/package.json b/package.json index a816863f499..c1ddb7be2bf 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@gitlab/ui": "7.5.0", "@gitlab/visual-review-tools": "1.0.3", "@sentry/browser": "^5.7.1", - "apollo-cache-inmemory": "^1.5.1", + "apollo-cache-inmemory": "^1.6.3", "apollo-client": "^2.6.4", "apollo-link": "^1.2.11", "apollo-link-batch-http": "^1.2.11", diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 53896c7f5c7..ca39f5dd9f2 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -90,14 +90,6 @@ describe ApplicationController do let(:format) { :html } it_behaves_like 'setting gon variables' - - context 'for peek requests' do - before do - request.path = '/-/peek' - end - - it_behaves_like 'not setting gon variables' - end end context 'with json format' do @@ -105,6 +97,12 @@ describe ApplicationController do it_behaves_like 'not setting gon variables' end + + context 'with atom format' do + let(:format) { :atom } + + it_behaves_like 'not setting gon variables' + end end describe 'session expiration' do diff --git a/spec/controllers/projects/grafana_api_controller_spec.rb b/spec/controllers/projects/grafana_api_controller_spec.rb index 96a12efdcfc..0ef96514961 100644 --- a/spec/controllers/projects/grafana_api_controller_spec.rb +++ b/spec/controllers/projects/grafana_api_controller_spec.rb @@ -164,17 +164,5 @@ describe Projects::GrafanaApiController do it_behaves_like 'error response', :bad_request end end - - context 'when grafana embeds are not enabled' do - before do - stub_feature_flags(gfm_grafana_integration: false) - end - - it 'returns 403 immediately' do - get :metrics_dashboard, params: params - - expect(response).to have_gitlab_http_status(:forbidden) - end - end end end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 1bcf3bb106b..f35babc1b56 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -228,10 +228,10 @@ describe UploadsController do user.block end - it "redirects to the sign in page" do + it "responds with status 401" do get :show, params: { model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png" } - expect(response).to redirect_to(new_user_session_path) + expect(response).to have_gitlab_http_status(401) end end @@ -320,10 +320,10 @@ describe UploadsController do end context "when not signed in" do - it "redirects to the sign in page" do + it "responds with status 401" do get :show, params: { model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" } - expect(response).to redirect_to(new_user_session_path) + expect(response).to have_gitlab_http_status(401) end end @@ -343,10 +343,10 @@ describe UploadsController do project.add_maintainer(user) end - it "redirects to the sign in page" do + it "responds with status 401" do get :show, params: { model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" } - expect(response).to redirect_to(new_user_session_path) + expect(response).to have_gitlab_http_status(401) end end @@ -439,10 +439,10 @@ describe UploadsController do user.block end - it "redirects to the sign in page" do + it "responds with status 401" do get :show, params: { model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" } - expect(response).to redirect_to(new_user_session_path) + expect(response).to have_gitlab_http_status(401) end end @@ -526,10 +526,10 @@ describe UploadsController do end context "when not signed in" do - it "redirects to the sign in page" do + it "responds with status 401" do get :show, params: { model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" } - expect(response).to redirect_to(new_user_session_path) + expect(response).to have_gitlab_http_status(401) end end @@ -549,10 +549,10 @@ describe UploadsController do project.add_maintainer(user) end - it "redirects to the sign in page" do + it "responds with status 401" do get :show, params: { model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" } - expect(response).to redirect_to(new_user_session_path) + expect(response).to have_gitlab_http_status(401) end end diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb index ca937651af8..9bbeb0eb260 100644 --- a/spec/features/projects/settings/operations_settings_spec.rb +++ b/spec/features/projects/settings/operations_settings_spec.rb @@ -104,17 +104,7 @@ describe 'Projects > Settings > For a forked project', :js do end context 'grafana integration settings form' do - it 'is not present when the feature flag is disabled' do - stub_feature_flags(gfm_grafana_integration: false) - - visit project_settings_operations_path(project) - - wait_for_requests - - expect(page).to have_no_css('.js-grafana-integration') - end - - it 'is present when the feature flag is enabled' do + it 'successfully fills and completes the form' do visit project_settings_operations_path(project) wait_for_requests diff --git a/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js new file mode 100644 index 00000000000..3ad8f3aec7c --- /dev/null +++ b/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js @@ -0,0 +1,45 @@ +import { shallowMount } from '@vue/test-utils'; + +import ImageViewer from '~/vue_shared/components/content_viewer/viewers/image_viewer.vue'; +import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants'; + +describe('Image Viewer', () => { + const requiredProps = { + path: GREEN_BOX_IMAGE_URL, + renderInfo: true, + }; + let wrapper; + let imageInfo; + + function createElement({ props, includeRequired = true } = {}) { + const data = includeRequired ? { ...requiredProps, ...props } : { ...props }; + + wrapper = shallowMount(ImageViewer, { + propsData: data, + }); + imageInfo = wrapper.find('.image-info'); + } + + describe('file sizes', () => { + it('should show the humanized file size when `renderInfo` is true and there is size info', () => { + createElement({ props: { fileSize: 1024 } }); + + expect(imageInfo.text()).toContain('1.00 KiB'); + }); + + it('should not show the humanized file size when `renderInfo` is true and there is no size', () => { + const FILESIZE_RE = /\d+(\.\d+)?\s*([KMGTP]i)*B/; + + createElement({ props: { fileSize: 0 } }); + + // It shouldn't show any filesize info + expect(imageInfo.text()).not.toMatch(FILESIZE_RE); + }); + + it('should not show any image information when `renderInfo` is false', () => { + createElement({ props: { renderInfo: false } }); + + expect(imageInfo.exists()).toBe(false); + }); + }); +}); diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/javascripts/monitoring/components/dashboard_spec.js index 4070de4cc35..35a7d8e2f2d 100644 --- a/spec/javascripts/monitoring/components/dashboard_spec.js +++ b/spec/javascripts/monitoring/components/dashboard_spec.js @@ -623,6 +623,49 @@ describe('Dashboard', () => { }); }); + describe('dashboard edit link', () => { + let wrapper; + const findEditLink = () => wrapper.find('.js-edit-link'); + + beforeEach(done => { + mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); + + wrapper = shallowMount(DashboardComponent, { + localVue, + sync: false, + attachToDocument: true, + propsData: { ...propsData, hasMetrics: true }, + store, + }); + + wrapper.vm.$store.commit( + `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, + dashboardGitResponse, + ); + wrapper.vm.$nextTick(done); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('is not present for the default dashboard', () => { + expect(findEditLink().exists()).toBe(false); + }); + + it('is present for a custom dashboard, and links to its edit_path', done => { + const dashboard = dashboardGitResponse[1]; // non-default dashboard + const currentDashboard = dashboard.path; + + wrapper.setProps({ currentDashboard }); + wrapper.vm.$nextTick(() => { + expect(findEditLink().exists()).toBe(true); + expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path); + done(); + }); + }); + }); + describe('external dashboard link', () => { beforeEach(() => { mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js index 4d63f91f658..9e20eb34ee6 100644 --- a/spec/javascripts/monitoring/mock_data.js +++ b/spec/javascripts/monitoring/mock_data.js @@ -931,14 +931,25 @@ export const metricsDashboardResponse = { export const dashboardGitResponse = [ { - path: 'config/prometheus/common_metrics.yml', - display_name: 'Common Metrics', default: true, + display_name: 'Default', + can_edit: false, + project_blob_path: null, + path: 'config/prometheus/common_metrics.yml', }, { - path: '.gitlab/dashboards/super.yml', - display_name: 'Custom Dashboard 1', default: false, + display_name: 'Custom Dashboard 1', + can_edit: true, + project_blob_path: `${mockProjectPath}/blob/master/dashboards/.gitlab/dashboards/dashboard_1.yml`, + path: '.gitlab/dashboards/dashboard_1.yml', + }, + { + default: false, + display_name: 'Custom Dashboard 2', + can_edit: true, + project_blob_path: `${mockProjectPath}/blob/master/dashboards/.gitlab/dashboards/dashboard_2.yml`, + path: '.gitlab/dashboards/dashboard_2.yml', }, ]; diff --git a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js index 660eaddf01f..1acd6b3ebe7 100644 --- a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js @@ -1,13 +1,23 @@ import Vue from 'vue'; + import diffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants'; describe('DiffViewer', () => { + const requiredProps = { + diffMode: 'replaced', + diffViewerMode: 'image', + newPath: GREEN_BOX_IMAGE_URL, + newSha: 'ABC', + oldPath: RED_BOX_IMAGE_URL, + oldSha: 'DEF', + }; let vm; function createComponent(props) { const DiffViewer = Vue.extend(diffViewer); + vm = mountComponent(DiffViewer, props); } @@ -20,15 +30,11 @@ describe('DiffViewer', () => { relative_url_root: '', }; - createComponent({ - diffMode: 'replaced', - diffViewerMode: 'image', - newPath: GREEN_BOX_IMAGE_URL, - newSha: 'ABC', - oldPath: RED_BOX_IMAGE_URL, - oldSha: 'DEF', - projectPath: '', - }); + createComponent( + Object.assign({}, requiredProps, { + projectPath: '', + }), + ); setTimeout(() => { expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe( @@ -44,14 +50,13 @@ describe('DiffViewer', () => { }); it('renders fallback download diff display', done => { - createComponent({ - diffMode: 'replaced', - diffViewerMode: 'added', - newPath: 'test.abc', - newSha: 'ABC', - oldPath: 'testold.abc', - oldSha: 'DEF', - }); + createComponent( + Object.assign({}, requiredProps, { + diffViewerMode: 'added', + newPath: 'test.abc', + oldPath: 'testold.abc', + }), + ); setTimeout(() => { expect(vm.$el.querySelector('.deleted .file-info').textContent.trim()).toContain( @@ -72,29 +77,28 @@ describe('DiffViewer', () => { }); it('renders renamed component', () => { - createComponent({ - diffMode: 'renamed', - diffViewerMode: 'renamed', - newPath: 'test.abc', - newSha: 'ABC', - oldPath: 'testold.abc', - oldSha: 'DEF', - }); + createComponent( + Object.assign({}, requiredProps, { + diffMode: 'renamed', + diffViewerMode: 'renamed', + newPath: 'test.abc', + oldPath: 'testold.abc', + }), + ); expect(vm.$el.textContent).toContain('File moved'); }); it('renders mode changed component', () => { - createComponent({ - diffMode: 'mode_changed', - diffViewerMode: 'image', - newPath: 'test.abc', - newSha: 'ABC', - oldPath: 'testold.abc', - oldSha: 'DEF', - aMode: '123', - bMode: '321', - }); + createComponent( + Object.assign({}, requiredProps, { + diffMode: 'mode_changed', + newPath: 'test.abc', + oldPath: 'testold.abc', + aMode: '123', + bMode: '321', + }), + ); expect(vm.$el.textContent).toContain('File mode changed from 123 to 321'); }); diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js index 97c870f27d9..0cb26d5000b 100644 --- a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js @@ -4,6 +4,11 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper'; import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants'; describe('ImageDiffViewer', () => { + const requiredProps = { + diffMode: 'replaced', + newPath: GREEN_BOX_IMAGE_URL, + oldPath: RED_BOX_IMAGE_URL, + }; let vm; function createComponent(props) { @@ -45,11 +50,7 @@ describe('ImageDiffViewer', () => { }); it('renders image diff for replaced', done => { - createComponent({ - diffMode: 'replaced', - newPath: GREEN_BOX_IMAGE_URL, - oldPath: RED_BOX_IMAGE_URL, - }); + createComponent(requiredProps); setTimeout(() => { expect(vm.$el.querySelector('.added img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL); @@ -70,11 +71,12 @@ describe('ImageDiffViewer', () => { }); it('renders image diff for new', done => { - createComponent({ - diffMode: 'new', - newPath: GREEN_BOX_IMAGE_URL, - oldPath: '', - }); + createComponent( + Object.assign({}, requiredProps, { + diffMode: 'new', + oldPath: '', + }), + ); setTimeout(() => { expect(vm.$el.querySelector('.added img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL); @@ -84,11 +86,12 @@ describe('ImageDiffViewer', () => { }); it('renders image diff for deleted', done => { - createComponent({ - diffMode: 'deleted', - newPath: '', - oldPath: RED_BOX_IMAGE_URL, - }); + createComponent( + Object.assign({}, requiredProps, { + diffMode: 'deleted', + newPath: '', + }), + ); setTimeout(() => { expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe(RED_BOX_IMAGE_URL); @@ -119,11 +122,7 @@ describe('ImageDiffViewer', () => { describe('swipeMode', () => { beforeEach(done => { - createComponent({ - diffMode: 'replaced', - newPath: GREEN_BOX_IMAGE_URL, - oldPath: RED_BOX_IMAGE_URL, - }); + createComponent(requiredProps); setTimeout(() => { done(); @@ -142,11 +141,7 @@ describe('ImageDiffViewer', () => { describe('onionSkin', () => { beforeEach(done => { - createComponent({ - diffMode: 'replaced', - newPath: GREEN_BOX_IMAGE_URL, - oldPath: RED_BOX_IMAGE_URL, - }); + createComponent(requiredProps); setTimeout(() => { done(); diff --git a/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb b/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb index 81053469e7a..fd6f8816b63 100644 --- a/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb +++ b/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb @@ -18,16 +18,6 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do '&var-instance=All&panelId=14' end - context 'when feature flag is disabled' do - before do - stub_feature_flags(gfm_grafana_integration: false) - end - - it 'leaves the markdown unchanged' do - expect(unescape(doc.to_s)).to eq(input) - end - end - it 'appends a metrics charts placeholder with dashboard url after metrics links' do node = doc.at_css('.js-render-metrics') expect(node).to be_present diff --git a/spec/requests/user_avatar_spec.rb b/spec/requests/user_avatar_spec.rb new file mode 100644 index 00000000000..9451674161c --- /dev/null +++ b/spec/requests/user_avatar_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Loading a user avatar' do + let(:user) { create(:user, :with_avatar) } + + context 'when logged in' do + # The exact query count will vary depending on the 2FA settings of the + # instance, group, and user. Removing those extra 2FA queries in this case + # may not be a good idea, so we just set up the ideal case. + before do + stub_application_setting(require_two_factor_authentication: true) + + login_as(create(:user, :two_factor)) + end + + # One each for: current user, avatar user, and upload record + it 'only performs three SQL queries' do + get user.avatar_url # Skip queries on first application load + + expect(response).to have_gitlab_http_status(200) + expect { get user.avatar_url }.not_to exceed_query_limit(3) + end + end + + context 'when logged out' do + # One each for avatar user and upload record + it 'only performs two SQL queries' do + get user.avatar_url # Skip queries on first application load + + expect(response).to have_gitlab_http_status(200) + expect { get user.avatar_url }.not_to exceed_query_limit(2) + end + end +end diff --git a/spec/workers/expire_build_artifacts_worker_spec.rb b/spec/workers/expire_build_artifacts_worker_spec.rb index 74d6b5605d1..0a0aea838d2 100644 --- a/spec/workers/expire_build_artifacts_worker_spec.rb +++ b/spec/workers/expire_build_artifacts_worker_spec.rb @@ -3,62 +3,11 @@ require 'spec_helper' describe ExpireBuildArtifactsWorker do - include RepoHelpers - let(:worker) { described_class.new } - before do - Sidekiq::Worker.clear_all - end - describe '#perform' do - before do - stub_feature_flags(ci_new_expire_job_artifacts_service: false) - build - end - - subject! do - Sidekiq::Testing.fake! { worker.perform } - end - - context 'with expired artifacts' do - let(:build) { create(:ci_build, :artifacts, artifacts_expire_at: Time.now - 7.days) } - - it 'enqueues that build' do - expect(jobs_enqueued.size).to eq(1) - expect(jobs_enqueued[0]["args"]).to eq([build.id]) - end - end - - context 'with not yet expired artifacts' do - let(:build) { create(:ci_build, :artifacts, artifacts_expire_at: Time.now + 7.days) } - - it 'does not enqueue that build' do - expect(jobs_enqueued.size).to eq(0) - end - end - - context 'without expire date' do - let(:build) { create(:ci_build, :artifacts) } - - it 'does not enqueue that build' do - expect(jobs_enqueued.size).to eq(0) - end - end - - def jobs_enqueued - Sidekiq::Queues.jobs_by_worker['ExpireBuildInstanceArtifactsWorker'] - end - end - - describe '#perform with ci_new_expire_job_artifacts_service feature flag' do - before do - stub_feature_flags(ci_new_expire_job_artifacts_service: true) - end - it 'executes a service' do expect_any_instance_of(Ci::DestroyExpiredJobArtifactsService).to receive(:execute) - expect(ExpireBuildInstanceArtifactsWorker).not_to receive(:bulk_perform_async) worker.perform end diff --git a/yarn.lock b/yarn.lock index 7802d5fb01c..df69f48f972 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1052,7 +1052,7 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/node@*", "@types/node@^10.11.7": +"@types/node@*", "@types/node@>=6", "@types/node@^10.11.7": version "10.12.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.9.tgz#a07bfa74331471e1dc22a47eb72026843f7b95c8" integrity sha512-eajkMXG812/w3w4a1OcBlaTwsFPO5F7fJ/amy+tieQxEMWBlbV1JGSjkFM+zkHNf81Cad+dfIRA+IBkvmvdAeA== @@ -1310,6 +1310,14 @@ "@webassemblyjs/wast-parser" "1.8.5" "@xtuc/long" "4.2.2" +"@wry/context@^0.4.0": + version "0.4.4" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.4.4.tgz#e50f5fa1d6cfaabf2977d1fda5ae91717f8815f8" + integrity sha512-LrKVLove/zw6h2Md/KZyWxIkFM6AoyKp71OqpH9Hiip1csjPVoD3tPxlbQUNxEnHENks3UGgNpSBCAfq9KWuag== + dependencies: + "@types/node" ">=6" + tslib "^1.9.3" + "@wry/equality@^0.1.2": version "0.1.9" resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.9.tgz#b13e18b7a8053c6858aa6c85b54911fb31e3a909" @@ -1473,18 +1481,18 @@ anymatch@^3.0.1: normalize-path "^3.0.0" picomatch "^2.0.4" -apollo-cache-inmemory@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.5.1.tgz#265d1ee67b0bf0aca9c37629d410bfae44e62953" - integrity sha512-D3bdpPmWfaKQkWy8lfwUg+K8OBITo3sx0BHLs1B/9vIdOIZ7JNCKq3EUcAgAfInomJUdN0QG1yOfi8M8hxkN1g== +apollo-cache-inmemory@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.3.tgz#826861d20baca4abc45f7ca7a874105905b8525d" + integrity sha512-S4B/zQNSuYc0M/1Wq8dJDTIO9yRgU0ZwDGnmlqxGGmFombOZb9mLjylewSfQKmjNpciZ7iUIBbJ0mHlPJTzdXg== dependencies: - apollo-cache "^1.2.1" - apollo-utilities "^1.2.1" - optimism "^0.6.9" - ts-invariant "^0.2.1" + apollo-cache "^1.3.2" + apollo-utilities "^1.3.2" + optimism "^0.10.0" + ts-invariant "^0.4.0" tslib "^1.9.3" -apollo-cache@1.3.2, apollo-cache@^1.2.1: +apollo-cache@1.3.2, apollo-cache@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.3.2.tgz#df4dce56240d6c95c613510d7e409f7214e6d26a" integrity sha512-+KA685AV5ETEJfjZuviRTEImGA11uNBp/MJGnaCvkgr+BYRrGLruVKBv6WvyFod27WEB2sp7SsG8cNBKANhGLg== @@ -3475,7 +3483,7 @@ d3@^4.13.0: d3-voronoi "1.1.2" d3-zoom "1.7.1" -d3@^5.12: +d3@^5.12, d3@^5.7.0: version "5.12.0" resolved "https://registry.yarnpkg.com/d3/-/d3-5.12.0.tgz#0ddeac879c28c882317cd439b495290acd59ab61" integrity sha512-flYVMoVuhPFHd9zVCe2BxIszUWqBcd5fvQGMNRmSiBrgdnh6Vlruh60RJQTouAK9xPbOB0plxMvBm4MoyODXNg== @@ -3512,43 +3520,6 @@ d3@^5.12: d3-voronoi "1" d3-zoom "1" -d3@^5.7.0: - version "5.9.2" - resolved "https://registry.yarnpkg.com/d3/-/d3-5.9.2.tgz#64e8a7e9c3d96d9e6e4999d2c8a2c829767e67f5" - integrity sha512-ydrPot6Lm3nTWH+gJ/Cxf3FcwuvesYQ5uk+j/kXEH/xbuYWYWTMAHTJQkyeuG8Y5WM5RSEYB41EctUrXQQytRQ== - dependencies: - d3-array "1" - d3-axis "1" - d3-brush "1" - d3-chord "1" - d3-collection "1" - d3-color "1" - d3-contour "1" - d3-dispatch "1" - d3-drag "1" - d3-dsv "1" - d3-ease "1" - d3-fetch "1" - d3-force "1" - d3-format "1" - d3-geo "1" - d3-hierarchy "1" - d3-interpolate "1" - d3-path "1" - d3-polygon "1" - d3-quadtree "1" - d3-random "1" - d3-scale "2" - d3-scale-chromatic "1" - d3-selection "1" - d3-shape "1" - d3-time "1" - d3-time-format "2" - d3-timer "1" - d3-transition "1" - d3-voronoi "1" - d3-zoom "1" - dagre-d3@dagrejs/dagre-d3: version "0.6.4-pre" resolved "https://codeload.github.com/dagrejs/dagre-d3/tar.gz/e1a00e5cb518f5d2304a35647e024f31d178e55b" @@ -5682,11 +5653,6 @@ immediate@~3.0.5: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= -immutable-tuple@^0.4.9: - version "0.4.9" - resolved "https://registry.yarnpkg.com/immutable-tuple/-/immutable-tuple-0.4.9.tgz#473ebdd6c169c461913a454bf87ef8f601a20ff0" - integrity sha512-LWbJPZnidF8eczu7XmcnLBsumuyRBkpwIRPCZxlojouhBo5jEBO4toj6n7hMy6IxHU/c+MqDSWkvaTpPlMQcyA== - import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -8282,12 +8248,12 @@ opn@^5.5.0: dependencies: is-wsl "^1.1.0" -optimism@^0.6.9: - version "0.6.9" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.6.9.tgz#19258ff8b3be0cea29ac35f06bff818e026e30bb" - integrity sha512-xoQm2lvXbCA9Kd7SCx6y713Y7sZ6fUc5R6VYpoL5M6svKJbTuvtNopexK8sO8K4s0EOUYHuPN2+yAEsNyRggkQ== +optimism@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.10.3.tgz#163268fdc741dea2fb50f300bedda80356445fd7" + integrity sha512-9A5pqGoQk49H6Vhjb9kPgAeeECfUDF6aIICbMDL23kDLStBn1MWk3YvcZ4xWF9CsSf6XEgvRLkXy4xof/56vVw== dependencies: - immutable-tuple "^0.4.9" + "@wry/context" "^0.4.0" optimist@^0.6.1: version "0.6.1" @@ -11111,13 +11077,6 @@ tryer@^1.0.0: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7" integrity sha1-Antp+oIyJeVRys4+8DsR9qs3wdc= -ts-invariant@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.2.1.tgz#3d587f9d6e3bded97bf9ec17951dd9814d5a9d3f" - integrity sha512-Z/JSxzVmhTo50I+LKagEISFJW3pvPCqsMWLamCTX8Kr3N5aMrnGOqcflbe5hLUzwjvgPfnLzQtHZv0yWQ+FIHg== - dependencies: - tslib "^1.9.3" - ts-invariant@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.3.2.tgz#89a2ffeb70879b777258df1df1c59383c35209b0"