Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
213ce78058
commit
4f01ac5ba0
33 changed files with 359 additions and 279 deletions
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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 {
|
|||
</template>
|
||||
|
||||
<gl-form-group
|
||||
v-if="addingMetricsAvailable || showRearrangePanelsBtn || externalDashboardUrl.length"
|
||||
v-if="hasHeaderButtons"
|
||||
label-for="prometheus-graphs-dropdown-buttons"
|
||||
class="dropdown-buttons col-md d-md-flex col-lg d-lg-flex align-items-end"
|
||||
>
|
||||
|
@ -437,6 +448,14 @@ export default {
|
|||
</div>
|
||||
</gl-modal>
|
||||
|
||||
<gl-button
|
||||
v-if="selectedDashboard.can_edit"
|
||||
class="mt-1 js-edit-link"
|
||||
:href="selectedDashboard.project_blob_path"
|
||||
>
|
||||
{{ __('Edit dashboard') }}
|
||||
</gl-button>
|
||||
|
||||
<gl-button
|
||||
v-if="externalDashboardUrl.length"
|
||||
class="mt-1 js-external-dashboard-link"
|
||||
|
|
|
@ -6,8 +6,6 @@ import initSettingsPanels from '~/settings_panels';
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
mountErrorTrackingForm();
|
||||
mountOperationSettings();
|
||||
if (gon.features.gfmGrafanaIntegration) {
|
||||
mountGrafanaIntegration();
|
||||
}
|
||||
initSettingsPanels();
|
||||
});
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -14,6 +14,16 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
newSize: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
oldSize: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -22,12 +32,14 @@ export default {
|
|||
<div class="two-up view d-flex">
|
||||
<image-viewer
|
||||
:path="oldPath"
|
||||
:file-size="oldSize"
|
||||
:render-info="true"
|
||||
inner-css-classes="frame deleted"
|
||||
class="wrap w-50"
|
||||
/>
|
||||
<image-viewer
|
||||
:path="newPath"
|
||||
:file-size="newSize"
|
||||
:render-info="true"
|
||||
:inner-css-classes="['frame', 'added']"
|
||||
class="wrap w-50"
|
||||
|
|
|
@ -22,6 +22,16 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
newSize: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
oldSize: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -20,11 +20,11 @@ class ApplicationController < ActionController::Base
|
|||
before_action :authenticate_user!, except: [:route_not_found]
|
||||
before_action :enforce_terms!, if: :should_enforce_terms?
|
||||
before_action :validate_user_service_ticket!
|
||||
before_action :check_password_expiration
|
||||
before_action :check_password_expiration, if: :html_request?
|
||||
before_action :ldap_security_check
|
||||
before_action :sentry_context
|
||||
before_action :default_headers
|
||||
before_action :add_gon_variables, unless: [:peek_request?, :json_request?]
|
||||
before_action :add_gon_variables, if: :html_request?
|
||||
before_action :configure_permitted_parameters, if: :devise_controller?
|
||||
before_action :require_email, unless: :devise_controller?
|
||||
before_action :active_user_check, unless: :devise_controller?
|
||||
|
@ -455,8 +455,8 @@ class ApplicationController < ActionController::Base
|
|||
response.headers['Page-Title'] = URI.escape(page_title('GitLab'))
|
||||
end
|
||||
|
||||
def peek_request?
|
||||
request.path.start_with?('/-/peek')
|
||||
def html_request?
|
||||
request.format.html?
|
||||
end
|
||||
|
||||
def json_request?
|
||||
|
@ -466,7 +466,7 @@ class ApplicationController < ActionController::Base
|
|||
def should_enforce_terms?
|
||||
return false unless Gitlab::CurrentSettings.current_application_settings.enforce_terms
|
||||
|
||||
!(peek_request? || devise_controller?)
|
||||
html_request? && !devise_controller?
|
||||
end
|
||||
|
||||
def set_usage_stats_consent_flag
|
||||
|
|
|
@ -4,15 +4,18 @@ module ConfirmEmailWarning
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
before_action :set_confirm_warning, if: -> { 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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add edit button to metrics dashboard
|
||||
merge_request: 19279
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update SaaS trial header to include the tier Gold
|
||||
merge_request: 19970
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/defect-diff-file-size.yml
Normal file
5
changelogs/unreleased/defect-diff-file-size.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Re-add missing file sizes in 2-Up diff file viewer
|
||||
merge_request: 19710
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/tr-remove-grafana-ff.yml
Normal file
5
changelogs/unreleased/tr-remove-grafana-ff.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow Grafana charts to be embedded in Gitlab Flavored Markdown
|
||||
merge_request: 18486
|
||||
author:
|
||||
type: added
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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..."
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -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',
|
||||
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',
|
||||
createComponent(
|
||||
Object.assign({}, requiredProps, {
|
||||
diffViewerMode: 'added',
|
||||
newPath: 'test.abc',
|
||||
newSha: 'ABC',
|
||||
oldPath: 'testold.abc',
|
||||
oldSha: 'DEF',
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.deleted .file-info').textContent.trim()).toContain(
|
||||
|
@ -72,29 +77,28 @@ describe('DiffViewer', () => {
|
|||
});
|
||||
|
||||
it('renders renamed component', () => {
|
||||
createComponent({
|
||||
createComponent(
|
||||
Object.assign({}, requiredProps, {
|
||||
diffMode: 'renamed',
|
||||
diffViewerMode: 'renamed',
|
||||
newPath: 'test.abc',
|
||||
newSha: 'ABC',
|
||||
oldPath: 'testold.abc',
|
||||
oldSha: 'DEF',
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
expect(vm.$el.textContent).toContain('File moved');
|
||||
});
|
||||
|
||||
it('renders mode changed component', () => {
|
||||
createComponent({
|
||||
createComponent(
|
||||
Object.assign({}, requiredProps, {
|
||||
diffMode: 'mode_changed',
|
||||
diffViewerMode: 'image',
|
||||
newPath: 'test.abc',
|
||||
newSha: 'ABC',
|
||||
oldPath: 'testold.abc',
|
||||
oldSha: 'DEF',
|
||||
aMode: '123',
|
||||
bMode: '321',
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
expect(vm.$el.textContent).toContain('File mode changed from 123 to 321');
|
||||
});
|
||||
|
|
|
@ -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({
|
||||
createComponent(
|
||||
Object.assign({}, requiredProps, {
|
||||
diffMode: 'new',
|
||||
newPath: GREEN_BOX_IMAGE_URL,
|
||||
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({
|
||||
createComponent(
|
||||
Object.assign({}, requiredProps, {
|
||||
diffMode: 'deleted',
|
||||
newPath: '',
|
||||
oldPath: RED_BOX_IMAGE_URL,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
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();
|
||||
|
|
|
@ -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
|
||||
|
|
36
spec/requests/user_avatar_spec.rb
Normal file
36
spec/requests/user_avatar_spec.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
89
yarn.lock
89
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"
|
||||
|
|
Loading…
Reference in a new issue