提交 79ddf163 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 ae69a88c
......@@ -4,6 +4,7 @@ import Dashboard from '~/monitoring/components/dashboard.vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import { getParameterValues } from '~/lib/utils/url_utility';
import store from './stores';
import { promCustomVariablesFromUrl } from './utils';
Vue.use(GlToast);
......@@ -13,6 +14,8 @@ export default (props = {}) => {
if (el && el.dataset) {
const [currentDashboard] = getParameterValues('dashboard');
store.dispatch('monitoringDashboard/setVariables', promCustomVariablesFromUrl());
// eslint-disable-next-line no-new
new Vue({
el,
......
......@@ -80,6 +80,10 @@ export const setTimeRange = ({ commit }, timeRange) => {
commit(types.SET_TIME_RANGE, timeRange);
};
export const setVariables = ({ commit }, variables) => {
commit(types.SET_PROM_QUERY_VARIABLES, variables);
};
export const filterEnvironments = ({ commit, dispatch }, searchTerm) => {
commit(types.SET_ENVIRONMENTS_FILTER, searchTerm);
dispatch('fetchEnvironmentsData');
......@@ -218,12 +222,16 @@ export const fetchDashboardData = ({ state, dispatch, getters }) => {
*
* @param {metric} metric
*/
export const fetchPrometheusMetric = ({ commit }, { metric, defaultQueryParams }) => {
export const fetchPrometheusMetric = ({ commit, state }, { metric, defaultQueryParams }) => {
const queryParams = { ...defaultQueryParams };
if (metric.step) {
queryParams.step = metric.step;
}
if (state.promVariables.length > 0) {
queryParams.variables = state.promVariables;
}
commit(types.REQUEST_METRIC_RESULT, { metricId: metric.metricId });
return getPrometheusMetricResult(metric.prometheusEndpointPath, queryParams)
......
// Dashboard "skeleton", groups, panels and metrics
// Dashboard "skeleton", groups, panels, metrics, query variables
export const REQUEST_METRICS_DASHBOARD = 'REQUEST_METRICS_DASHBOARD';
export const RECEIVE_METRICS_DASHBOARD_SUCCESS = 'RECEIVE_METRICS_DASHBOARD_SUCCESS';
export const RECEIVE_METRICS_DASHBOARD_FAILURE = 'RECEIVE_METRICS_DASHBOARD_FAILURE';
export const SET_PROM_QUERY_VARIABLES = 'SET_PROM_QUERY_VARIABLES';
// Annotations
export const RECEIVE_ANNOTATIONS_SUCCESS = 'RECEIVE_ANNOTATIONS_SUCCESS';
......
......@@ -51,6 +51,18 @@ const emptyStateFromError = error => {
return metricStates.UNKNOWN_ERROR;
};
/**
* Maps an variables object to an array
* @returns {Array} The custom variables array to be send to the API
* in the format of [variable1, variable1_value]
* @param {Object} variables - Custom variables provided by the user
*/
const transformVariablesObjectArray = variables =>
Object.entries(variables)
.flat()
.map(encodeURIComponent);
export default {
/**
* Dashboard panels structure and global state
......@@ -169,4 +181,7 @@ export default {
state.expandedPanel.group = group;
state.expandedPanel.panel = panel;
},
[types.SET_PROM_QUERY_VARIABLES](state, variables) {
state.promVariables = transformVariablesObjectArray(variables);
},
};
......@@ -33,6 +33,7 @@ export default () => ({
panel: null,
},
allDashboards: [],
promVariables: [],
// Other project data
annotations: [],
......
import { omit } from 'lodash';
import { queryToObject, mergeUrlParams, removeParams } from '~/lib/utils/url_utility';
import {
timeRangeParamNames,
......@@ -5,6 +6,13 @@ import {
timeRangeToParams,
} from '~/lib/utils/datetime_range';
/**
* List of non time range url parameters
* This will be removed once we add support for free text variables
* via the dashboard yaml files in https://gitlab.com/gitlab-org/gitlab/-/issues/215689
*/
export const dashboardParams = ['dashboard', 'group', 'title', 'y_label'];
/**
* This method is used to validate if the graph data format for a chart component
* that needs a time series as a response from a prometheus query (queryRange) is
......@@ -113,6 +121,21 @@ export const timeRangeFromUrl = (search = window.location.search) => {
return timeRangeFromParams(params);
};
/**
* Returns an array with user defined variables from the URL
*
* @returns {Array} The custom variables defined by the
* user in the URL
* @param {String} New URL
*/
export const promCustomVariablesFromUrl = (search = window.location.search) => {
const params = queryToObject(search);
const paramsToRemove = timeRangeParamNames.concat(dashboardParams);
return omit(params, paramsToRemove);
};
/**
* Returns a URL with no time range based on the current URL.
*
......
......@@ -3,7 +3,15 @@ import $ from 'jquery';
import { mapActions, mapGetters, mapState } from 'vuex';
import { isEmpty } from 'lodash';
import Autosize from 'autosize';
import { GlAlert, GlIntersperse, GlLink, GlSprintf } from '@gitlab/ui';
import {
GlAlert,
GlFormCheckbox,
GlIcon,
GlIntersperse,
GlLink,
GlSprintf,
GlTooltipDirective,
} from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import Flash from '../../flash';
......@@ -24,6 +32,7 @@ import loadingButton from '../../vue_shared/components/loading_button.vue';
import noteSignedOutWidget from './note_signed_out_widget.vue';
import discussionLockedWidget from './discussion_locked_widget.vue';
import issuableStateMixin from '../mixins/issuable_state';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'CommentForm',
......@@ -36,11 +45,16 @@ export default {
loadingButton,
TimelineEntryItem,
GlAlert,
GlFormCheckbox,
GlIcon,
GlIntersperse,
GlLink,
GlSprintf,
},
mixins: [issuableStateMixin],
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [issuableStateMixin, glFeatureFlagsMixin()],
props: {
noteableType: {
type: String,
......@@ -51,6 +65,7 @@ export default {
return {
note: '',
noteType: constants.COMMENT,
noteIsConfidential: false,
isSubmitting: false,
isSubmitButtonDisabled: true,
};
......@@ -138,6 +153,9 @@ export default {
trackingLabel() {
return slugifyWithUnderscore(`${this.commentButtonTitle} button`);
},
confidentialNotesEnabled() {
return Boolean(this.glFeatures.confidentialNotes);
},
},
watch: {
note(newNote) {
......@@ -185,6 +203,7 @@ export default {
note: {
noteable_type: this.noteableType,
noteable_id: this.getNoteableData.id,
confidential: this.noteIsConfidential,
note: this.note,
},
merge_request_diff_head_sha: this.getNoteableData.diff_head_sha,
......@@ -285,6 +304,7 @@ export default {
if (shouldClear) {
this.note = '';
this.noteIsConfidential = false;
this.resizeTextarea();
this.$refs.markdownField.previewMarkdown = false;
}
......@@ -411,6 +431,19 @@ js-gfm-input js-autosize markdown-area js-vue-textarea qa-comment-input"
</p>
</gl-alert>
<div class="note-form-actions">
<div v-if="confidentialNotesEnabled" class="js-confidential-note-toggle mb-4">
<gl-form-checkbox v-model="noteIsConfidential">
<gl-icon name="eye-slash" :size="12" />
{{ __('Mark this comment as private') }}
<gl-icon
v-gl-tooltip:tooltipcontainer.bottom
name="question"
:size="12"
:title="__('Private comments are accessible by internal staff only')"
class="gl-text-gray-800"
/>
</gl-form-checkbox>
</div>
<div
class="float-left btn-group
append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
......
<script>
import { mapActions } from 'vuex';
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import GitlabTeamMemberBadge from '~/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue';
......@@ -7,6 +8,10 @@ export default {
components: {
timeAgoTooltip,
GitlabTeamMemberBadge,
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
author: {
......@@ -44,6 +49,11 @@ export default {
required: false,
default: true,
},
isConfidential: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
......@@ -160,7 +170,7 @@ export default {
</span>
</template>
<span v-else>{{ __('A deleted user') }}</span>
<span class="note-headline-light note-headline-meta">
<span class="note-headline-light note-headline-meta d-inline-flex align-items-center">
<span class="system-note-message"> <slot></slot> </span>
<template v-if="createdAt">
<span ref="actionText" class="system-note-separator">
......@@ -177,6 +187,15 @@ export default {
</a>
<time-ago-tooltip v-else ref="noteTimestamp" :time="createdAt" tooltip-placement="bottom" />
</template>
<gl-icon
v-if="isConfidential"
ref="confidentialIndicator"
v-gl-tooltip:tooltipcontainer.bottom
name="eye-slash"
:size="14"
:title="__('Private comments are accessible by internal staff only')"
class="ml-1 gl-text-gray-800"
/>
<slot name="extra-controls"></slot>
<i
v-if="showSpinner"
......
......@@ -255,10 +255,16 @@ export default {
</div>
<div class="timeline-content">
<div class="note-header">
<note-header v-once :author="author" :created-at="note.created_at" :note-id="note.id">
<note-header
v-once
:author="author"
:created-at="note.created_at"
:note-id="note.id"
:is-confidential="note.confidential"
>
<slot slot="note-header-info" name="note-header-info"></slot>
<span v-if="commit" v-html="actionText"></span>
<span v-else class="d-none d-sm-inline">&middot;</span>
<span v-else class="d-none d-sm-inline mr-1">&middot;</span>
</note-header>
<note-actions
:author-id="author.id"
......
......@@ -4,6 +4,7 @@ import query from '~/issuable_sidebar/queries/issue_sidebar.query.graphql';
import actionCable from '~/actioncable_consumer';
export default {
subscription: null,
name: 'AssigneesRealtime',
props: {
mediator: {
......@@ -36,6 +37,9 @@ export default {
mounted() {
this.initActionCablePolling();
},
beforeDestroy() {
this.$options.subscription.unsubscribe();
},
methods: {
received(data) {
if (data.event === 'updated') {
......@@ -43,7 +47,7 @@ export default {
}
},
initActionCablePolling() {
actionCable.subscriptions.create(
this.$options.subscription = actionCable.subscriptions.create(
{
channel: 'IssuesChannel',
project_path: this.projectPath,
......
......@@ -660,10 +660,6 @@ $note-form-margin-left: 72px;
padding-bottom: 0;
}
.note-headline-light {
display: inline;
}
.note-headline-light,
.discussion-headline-light {
color: $gl-text-color-secondary;
......
......@@ -52,6 +52,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action only: :show do
push_frontend_feature_flag(:real_time_issue_sidebar, @project)
push_frontend_feature_flag(:confidential_notes, @project)
end
around_action :allow_gitaly_ref_name_caching, only: [:discussions]
......
......@@ -7,6 +7,8 @@ class Projects::PagesDomainsController < Projects::ApplicationController
before_action :authorize_update_pages!
before_action :domain, except: [:new, :create]
helper_method :domain_presenter
def show
end
......@@ -27,7 +29,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController
end
def retry_auto_ssl
PagesDomains::RetryAcmeOrderService.new(@domain.pages_domain).execute
PagesDomains::RetryAcmeOrderService.new(@domain).execute
redirect_to project_pages_domain_path(@project, @domain)
end
......@@ -88,6 +90,10 @@ class Projects::PagesDomainsController < Projects::ApplicationController
end
def domain
@domain ||= @project.pages_domains.find_by_domain!(params[:id].to_s).present(current_user: current_user)
@domain ||= @project.pages_domains.find_by_domain!(params[:id].to_s)
end
def domain_presenter
@domain_presenter ||= domain.present(current_user: current_user)
end
end
- auto_ssl_available = ::Gitlab::LetsEncrypt.enabled?
- auto_ssl_enabled = @domain.auto_ssl_enabled?
- auto_ssl_enabled = domain_presenter.auto_ssl_enabled?
- auto_ssl_available_and_enabled = auto_ssl_available && auto_ssl_enabled
- has_user_defined_certificate = @domain.certificate && @domain.certificate_user_provided?
- has_user_defined_certificate = domain_presenter.certificate && domain_presenter.certificate_user_provided?
- if auto_ssl_available
.form-group.border-section
......@@ -36,9 +36,9 @@
= _('Certificate')
.d-flex.justify-content-between.align-items-center.p-3
%span
= @domain.pages_domain.subject || _('missing')
= domain_presenter.pages_domain.subject || _('missing')
= link_to _('Remove'),
clean_certificate_project_pages_domain_path(@project, @domain),
clean_certificate_project_pages_domain_path(@project, domain_presenter),
data: { confirm: _('Are you sure?') },
class: 'btn btn-remove btn-sm',
method: :delete
......
- verification_enabled = Gitlab::CurrentSettings.pages_domain_verification_enabled?
- dns_record = "#{@domain.domain} CNAME #{@domain.project.pages_subdomain}.#{Settings.pages.host}."
- dns_record = "#{domain_presenter.domain} CNAME #{domain_presenter.project.pages_subdomain}.#{Settings.pages.host}."
.form-group.border-section
.row
......@@ -13,17 +13,17 @@
%p.form-text.text-muted
= _("To access this domain create a new DNS record")
- if verification_enabled
- verification_record = "#{@domain.verification_domain} TXT #{@domain.keyed_verification_code}"
- verification_record = "#{domain_presenter.verification_domain} TXT #{domain_presenter.keyed_verification_code}"
.form-group.border-section
.row
.col-sm-2
= _("Verification status")
.col-sm-10
.status-badge
- text, status = @domain.unverified? ? [_('Unverified'), 'badge-danger'] : [_('Verified'), 'badge-success']
- text, status = domain_presenter.unverified? ? [_('Unverified'), 'badge-danger'] : [_('Verified'), 'badge-success']
.badge{ class: status }
= text
= link_to sprite_icon("redo"), verify_project_pages_domain_path(@project, @domain), method: :post, class: "btn has-tooltip", title: _("Retry verification")
= link_to sprite_icon("redo"), verify_project_pages_domain_path(@project, domain_presenter), method: :post, class: "btn has-tooltip", title: _("Retry verification")
.input-group
= text_field_tag :domain_verification, verification_record, class: "monospace js-select-on-focus form-control", readonly: true
.input-group-append
......
- if @domain.errors.any?
- if domain_presenter.errors.any?
.alert.alert-danger
- @domain.errors.full_messages.each do |msg|
- domain_presenter.errors.full_messages.each do |msg|
= msg
.form-group.border-section
.row
- if @domain.persisted?
- if domain_presenter.persisted?
.col-sm-2
= _("Domain")
.col-sm-10
= external_link(@domain.url, @domain.url)
= external_link(domain_presenter.url, domain_presenter.url)
- else
.col-sm-2
= f.label :domain, _("Domain")
......@@ -17,7 +17,7 @@
.input-group
= f.text_field :domain, required: true, autocomplete: "off", class: "form-control"
- if @domain.persisted?
- if domain_presenter.persisted?
= render 'dns'
- if Gitlab.config.pages.external_https
......
- if @domain.enabled?
- if @domain.auto_ssl_enabled
- if @domain.show_auto_ssl_failed_warning?
- if domain_presenter.enabled?
- if domain_presenter.auto_ssl_enabled
- if domain_presenter.show_auto_ssl_failed_warning?
.form-group.border-section.js-shown-if-auto-ssl{ class: ("d-none" unless auto_ssl_available_and_enabled) }
.row
.col-sm-10.offset-sm-2
......@@ -9,8 +9,8 @@
= icon('warning', class: 'mr-2')
= _("Something went wrong while obtaining the Let's Encrypt certificate.")
.row.mx-0.mt-3
= link_to s_('GitLabPagesDomains|Retry'), retry_auto_ssl_project_pages_domain_path(@project, @domain), class: "btn btn-sm btn-grouped btn-warning", method: :post
- elsif !@domain.certificate_gitlab_provided?
= link_to s_('GitLabPagesDomains|Retry'), retry_auto_ssl_project_pages_domain_path(@project, domain_presenter), class: "btn btn-sm btn-grouped btn-warning", method: :post
- elsif !domain_presenter.certificate_gitlab_provided?
.form-group.border-section.js-shown-if-auto-ssl{ class: ("d-none" unless auto_ssl_available_and_enabled) }
.row
.col-sm-10.offset-sm-2
......
......@@ -4,7 +4,7 @@
= _("New Pages Domain")
= render 'projects/pages_domains/helper_text'
%div
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'fieldset-form' } do |f|
= form_for [@project.namespace.becomes(Namespace), @project, domain_presenter], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f }
.form-actions
= f.submit _('Create New Domain'), class: "btn btn-success"
......
- add_to_breadcrumbs _("Pages"), project_pages_path(@project)
- breadcrumb_title @domain.domain
- page_title @domain.domain
- breadcrumb_title domain_presenter.domain
- page_title domain_presenter.domain
- verification_enabled = Gitlab::CurrentSettings.pages_domain_verification_enabled?
- if verification_enabled && @domain.unverified?
- if verification_enabled && domain_presenter.unverified?
= content_for :flash_message do
.alert.alert-warning
.container-fluid.container-limited
......@@ -14,7 +14,7 @@
= _('Pages Domain')
= render 'projects/pages_domains/helper_text'
%div
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'fieldset-form' } do |f|
= form_for [@project.namespace.becomes(Namespace), @project, domain_presenter], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f }
.form-actions.d-flex.justify-content-between
= f.submit _('Save Changes'), class: "btn btn-success"
......
---
title: Add confidential status support for new comments
merge_request: 30570
author:
type: added
---
title: Fix 500 on creating an invalid domains and verification
merge_request: 31190
author:
type: fixed
---
title: In metrics dashboard use custom variables from URL in queries
merge_request: 30560
author:
type: added
......@@ -9146,9 +9146,15 @@ msgstr ""
msgid "FeatureFlags|Feature flags allow you to configure your code into different flavors by dynamically toggling certain functionality."
msgstr ""
msgid "FeatureFlags|Flag becomes read only soon"
msgstr ""
msgid "FeatureFlags|Get started with feature flags"
msgstr ""
msgid "FeatureFlags|GitLab is moving to a new way of managing feature flags, and in 13.4, this feature flag will become read-only. Please create a new feature flag."
msgstr ""
msgid "FeatureFlags|ID"
msgstr ""
......@@ -12794,6 +12800,9 @@ msgstr ""
msgid "Mark comment as resolved"
msgstr ""
msgid "Mark this comment as private"
msgstr ""
msgid "Mark this issue as a duplicate of another issue"
msgstr ""
......@@ -15586,6 +15595,9 @@ msgstr ""
msgid "Private - The group and its projects can only be viewed by members."
msgstr ""
msgid "Private comments are accessible by internal staff only"
msgstr ""
msgid "Private group(s)"
msgstr ""
......
......@@ -148,16 +148,10 @@ describe Projects::PagesDomainsController do
describe 'POST verify' do
let(:params) { request_params.merge(id: pages_domain.domain) }
def stub_service
service = double(:service)
expect(VerifyPagesDomainService).to receive(:new) { service }
service
end
it 'handles verification success' do
expect(stub_service).to receive(:execute).and_return(status: :success)
expect_next_instance_of(VerifyPagesDomainService, pages_domain) do |service|
expect(service).to receive(:execute).and_return(status: :success)
end
post :verify, params: params
......@@ -166,7 +160,9 @@ describe Projects::PagesDomainsController do
end
it 'handles verification failure' do
expect(stub_service).to receive(:execute).and_return(status: :failed)
expect_next_instance_of(VerifyPagesDomainService, pages_domain) do |service|
expect(service).to receive(:execute).and_return(status: :failed)
end
post :verify, params: params
......
......@@ -158,6 +158,17 @@ shared_examples 'pages settings editing' do
expect(page).to have_content('my.test.domain.com')
end
it 'shows validation error if domain is duplicated' do
project.pages_domains.create!(domain: 'my.test.domain.com')
visit new_project_pages_domain_path(project)
fill_in 'Domain', with: 'my.test.domain.com'
click_button 'Create New Domain'
expect(page).to have_content('Domain has already been taken')
end
describe 'with dns verification enabled' do
before do
stub_application_setting(pages_domain_verification_enabled: true)
......
......@@ -25,6 +25,7 @@ import {
clearExpandedPanel,
setGettingStartedEmptyState,
duplicateSystemDashboard,
setVariables,
} from '~/monitoring/stores/actions';
import {
gqClient,
......@@ -392,6 +393,29 @@ describe('Monitoring store actions', () => {
);
});
});
describe('setVariables', () => {
let mockedState;
beforeEach(() => {
mockedState = storeState();
});
it('should commit SET_PROM_QUERY_VARIABLES mutation', done => {
testAction(
setVariables,
{ pod: 'POD' },
mockedState,
[
{
type: types.SET_PROM_QUERY_VARIABLES,
payload: { pod: 'POD' },
},
],
[],
done,
);
});
});
describe('fetchDashboard', () => {
let dispatch;
let state;
......
......@@ -364,4 +364,18 @@ describe('Monitoring mutations', () => {
expect(stateCopy.expandedPanel.panel).toEqual(null);
});
});
describe('SET_PROM_QUERY_VARIABLES', () => {
it('stores an empty variables array when no custom variables are given', () => {
mutations[types.SET_PROM_QUERY_VARIABLES](stateCopy, {});
expect(stateCopy.promVariables).toEqual([]);
});
it('stores variables in the key key_value format in the array', () => {
mutations[types.SET_PROM_QUERY_VARIABLES](stateCopy, { pod: 'POD', stage: 'main ops' });
expect(stateCopy.promVariables).toEqual(['pod', 'POD', 'stage', 'main%20ops']);
});
});
});
......@@ -169,6 +169,43 @@ describe('monitoring/utils', () => {
});
});
describe('promCustomVariablesFromUrl', () => {
const { promCustomVariablesFromUrl } = monitoringUtils;
beforeEach(() => {
jest.spyOn(urlUtils, 'queryToObject');
});
afterEach(() => {
urlUtils.queryToObject.mockRestore();
});
it('returns an object with only the custom variables', () => {
urlUtils.queryToObject.mockReturnValueOnce({
dashboard: '.gitlab/dashboards/custom_dashboard.yml',
y_label: 'memory usage',
group: 'kubernetes',
title: 'Kubernetes memory total',
start: '2020-05-06',
end: '2020-05-07',
duration_seconds: '86400',
direction: 'left',
anchor: 'top',
pod: 'POD',
});
expect(promCustomVariablesFromUrl()).toEqual(expect.objectContaining({ pod: 'POD' }));
});
it('returns an empty object when no custom variables are present', () => {
urlUtils.queryToObject.mockReturnValueOnce({
dashboard: '.gitlab/dashboards/custom_dashboard.yml',
});
expect(promCustomVariablesFromUrl()).toStrictEqual({});
});
});
describe('removeTimeRangeParams', () => {
const { removeTimeRangeParams } = monitoringUtils;
......
......@@ -24,6 +24,7 @@ describe('issue_comment_form component', () => {
let store;
let wrapper;
let axiosMock;
let features = {};
const setupStore = (userData, noteableData) => {
store.dispatch('setUserData', userData);
......@@ -37,12 +38,16 @@ describe('issue_comment_form component', () => {
noteableType,
},
store,
provide: {
glFeatures: features,
},
});
};
beforeEach(() => {
axiosMock = new MockAdapter(axios);
store = createStore();
features = {};
});
afterEach(() => {
......@@ -298,6 +303,32 @@ describe('issue_comment_form component', () => {
});
});
});
describe('when note can be confidential', () => {
it('appends confidential status to note payload when saving', () => {
jest.spyOn(wrapper.vm, 'saveNote').mockReturnValue(new Promise(() => {}));
wrapper.vm.note = 'confidential note';
return wrapper.vm.$nextTick().then(() => {
wrapper.find('.js-comment-submit-button').trigger('click');
const [providedData] = wrapper.vm.saveNote.mock.calls[0];
expect(providedData.data.note.confidential).toBe(false);
});
});
it('should render confidential toggle as false', () => {
features = { confidentialNotes: true };
mountComponent();
const input = wrapper.find('.js-confidential-note-toggle .form-check-input');
expect(input.exists()).toBe(true);
expect(input.attributes('checked')).toBeFalsy();
});
});
});
describe('issue is confidential', () => {
......
......@@ -19,6 +19,7 @@ describe('NoteHeader component', () => {
const findActionText = () => wrapper.find({ ref: 'actionText' });
const findTimestampLink = () => wrapper.find({ ref: 'noteTimestampLink' });
const findTimestamp = () => wrapper.find({ ref: 'noteTimestamp' });
const findConfidentialIndicator = () => wrapper.find({ ref: 'confidentialIndicator' });
const findSpinner = () => wrapper.find({ ref: 'spinner' });
const author = {
......@@ -246,4 +247,15 @@ describe('NoteHeader component', () => {
});
});
});
describe('with confidentiality indicator', () => {
it.each`
status | condition
${true} | ${'shows'}
${false} | ${'hides'}
`('$condition icon indicator when isConfidential is $status', ({ status }) => {
createComponent({ isConfidential: status });
expect(findConfidentialIndicator().exists()).toBe(status);
});
});
});
......@@ -6,7 +6,9 @@ import Mock from './mock_data';
import query from '~/issuable_sidebar/queries/issue_sidebar.query.graphql';
jest.mock('@rails/actioncable', () => {
const mockConsumer = { subscriptions: { create: jest.fn() } };
const mockConsumer = {
subscriptions: { create: jest.fn().mockReturnValue({ unsubscribe: jest.fn() }) },
};
return {
createConsumer: jest.fn().mockReturnValue(mockConsumer),
};
......
......@@ -7,7 +7,7 @@ describe 'projects/pages_domains/show' do
before do
assign(:project, project)
assign(:domain, domain.present)
allow(view).to receive(:domain_presenter).and_return(domain.present)
stub_pages_setting(external_https: true)
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册