Commit 52bcd7af authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents e91fd7f9 6ab0df04
...@@ -47,6 +47,7 @@ export default { ...@@ -47,6 +47,7 @@ export default {
{ variant: 'success' }, { variant: 'success' },
{ category: 'primary' }, { category: 'primary' },
{ 'data-testid': 'submit-commit' }, { 'data-testid': 'submit-commit' },
{ 'data-qa-selector': 'submit_commit_button' },
], ],
}, },
actionCancel: { actionCancel: {
......
<script> <script>
import { GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { referenceQueries } from '~/sidebar/constants'; import { referenceQueries } from '~/sidebar/constants';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import CopyableField from '~/vue_shared/components/sidebar/copyable_field.vue';
export default { export default {
i18n: {
copyReference: __('Copy reference'),
text: __('Reference'),
},
components: { components: {
ClipboardButton, CopyableField,
GlLoadingIcon,
}, },
inject: ['fullPath', 'iid'], inject: ['fullPath', 'iid'],
props: { props: {
...@@ -56,31 +50,10 @@ export default { ...@@ -56,31 +50,10 @@ export default {
</script> </script>
<template> <template>
<div class="sub-block"> <copyable-field
<clipboard-button class="sub-block"
v-if="!isLoading" :is-loading="isLoading"
:title="$options.i18n.copyReference" :name="__('Reference')"
:text="reference" :value="reference"
category="tertiary"
css-class="sidebar-collapsed-icon dont-change-state"
tooltip-placement="left"
/>
<div
class="gl-display-flex gl-align-items-center gl-justify-content-space-between gl-mb-2 hide-collapsed"
>
<span class="gl-overflow-hidden gl-text-overflow-ellipsis gl-white-space-nowrap">
{{ $options.i18n.text }}: {{ reference }}
<gl-loading-icon v-if="isLoading" inline :label="$options.i18n.text" />
</span>
<clipboard-button
v-if="!isLoading"
:title="$options.i18n.copyReference"
:text="reference"
size="small"
category="tertiary"
css-class="gl-mr-1"
tooltip-placement="left"
/> />
</div>
</div>
</template> </template>
...@@ -151,6 +151,7 @@ export default { ...@@ -151,6 +151,7 @@ export default {
v-gl-tooltip.hover v-gl-tooltip.hover
:title="cherryPickTitle" :title="cherryPickTitle"
size="small" size="small"
data-qa-selector="cherry_pick_button"
@click="openCherryPickModal" @click="openCherryPickModal"
> >
{{ cherryPickLabel }} {{ cherryPickLabel }}
......
...@@ -67,7 +67,7 @@ export default { ...@@ -67,7 +67,7 @@ export default {
<div> <div>
<clipboard-button <clipboard-button
v-if="!isLoading" v-if="!isLoading"
css-class="sidebar-collapsed-icon dont-change-state" css-class="sidebar-collapsed-icon dont-change-state gl-rounded-0! gl-hover-bg-transparent"
v-bind="clipboardProps" v-bind="clipboardProps"
/> />
......
...@@ -24,7 +24,7 @@ module Ci ...@@ -24,7 +24,7 @@ module Ci
def execute(params = {}) def execute(params = {})
@metrics.increment_queue_operation(:queue_attempt) @metrics.increment_queue_operation(:queue_attempt)
@metrics.observe_queue_time(:process) do @metrics.observe_queue_time(:process, @runner.runner_type) do
process_queue(params) process_queue(params)
end end
end end
...@@ -128,7 +128,7 @@ module Ci ...@@ -128,7 +128,7 @@ module Ci
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def retrieve_queue(queue_query_proc) def retrieve_queue(queue_query_proc)
@metrics.observe_queue_time(:retrieve) do @metrics.observe_queue_time(:retrieve, @runner.runner_type) do
queue_query_proc.call queue_query_proc.call
end end
end end
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
Project: <%= @project.name %> ( <%= project_url(@project) %> ) Project: <%= @project.name %> ( <%= project_url(@project) %> )
Branch: <%= @pipeline.source_ref %> ( <%= commits_url(@pipeline) %> ) Branch: <%= @pipeline.source_ref %> ( <%= commits_url(@pipeline) %> )
<% if @merge_request -%> <% if @merge_request -%>
Merge Request: <%= @merge_request.to_reference %> ( <%= merge_request_url(@merge_request) %> ) Merge request: <%= @merge_request.to_reference %> ( <%= merge_request_url(@merge_request) %> )
<% end -%> <% end -%>
Commit: <%= @pipeline.short_sha %> ( <%= commit_url(@pipeline) %> ) Commit: <%= @pipeline.short_sha %> ( <%= commit_url(@pipeline) %> )
......
%p %p
Merge Request #{merge_request_reference_link(@merge_request)} Merge request #{merge_request_reference_link(@merge_request)}
was closed by #{sanitize_name(@updated_by.name)} was closed by #{sanitize_name(@updated_by.name)}
Merge Request #{@merge_request.to_reference} was closed by #{sanitize_name(@updated_by.name)} Merge request #{@merge_request.to_reference} was closed by #{sanitize_name(@updated_by.name)}
Merge Request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
= merge_path_description(@merge_request, 'to') = merge_path_description(@merge_request, 'to')
......
%p %p
Merge Request #{merge_request_reference_link(@merge_request)} Merge request #{merge_request_reference_link(@merge_request)}
was #{@mr_status} by #{sanitize_name(@updated_by.name)} was #{@mr_status} by #{sanitize_name(@updated_by.name)}
Merge Request #{@merge_request.to_reference} was #{@mr_status} by #{sanitize_name(@updated_by.name)} Merge request #{@merge_request.to_reference} was #{@mr_status} by #{sanitize_name(@updated_by.name)}
Merge Request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
= merge_path_description(@merge_request, 'to') = merge_path_description(@merge_request, 'to')
......
%p %p
Merge Request #{merge_request_reference_link(@merge_request)} can no longer be merged due to conflict. Merge request #{merge_request_reference_link(@merge_request)} can no longer be merged due to conflict.
Merge Request #{@merge_request.to_reference} can no longer be merged due to conflict. Merge request #{@merge_request.to_reference} can no longer be merged due to conflict.
Merge Request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
= merge_path_description(@merge_request, 'to') = merge_path_description(@merge_request, 'to')
......
Merge Request #{@merge_request.to_reference} was scheduled to merge after pipeline succeeds by #{sanitize_name(@mwps_set_by.name)} Merge request #{@merge_request.to_reference} was scheduled to merge after pipeline succeeds by #{sanitize_name(@mwps_set_by.name)}
Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
= merge_path_description(@merge_request, 'to') = merge_path_description(@merge_request, 'to')
......
%p %p
Merge Request #{merge_request_reference_link(@merge_request)} was merged Merge request #{merge_request_reference_link(@merge_request)} was merged
Merge Request #{@merge_request.to_reference} was merged Merge request #{@merge_request.to_reference} was merged
Merge Request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
= merge_path_description(@merge_request, 'to') = merge_path_description(@merge_request, 'to')
......
%p %p
You have been mentioned in Merge Request #{merge_request_reference_link(@merge_request)} You have been mentioned in merge request #{merge_request_reference_link(@merge_request)}
= render template: 'notify/new_merge_request_email' = render template: 'notify/new_merge_request_email'
You have been mentioned in Merge Request <%= @merge_request.to_reference %> You have been mentioned in merge request <%= @merge_request.to_reference %>
<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
......
...@@ -3,7 +3,7 @@ Pipeline #<%= @pipeline.id %> has failed! ...@@ -3,7 +3,7 @@ Pipeline #<%= @pipeline.id %> has failed!
Project: <%= @project.name %> ( <%= project_url(@project) %> ) Project: <%= @project.name %> ( <%= project_url(@project) %> )
Branch: <%= @pipeline.source_ref %> ( <%= commits_url(@pipeline) %> ) Branch: <%= @pipeline.source_ref %> ( <%= commits_url(@pipeline) %> )
<% if @merge_request -%> <% if @merge_request -%>
Merge Request: <%= @merge_request.to_reference %> ( <%= merge_request_url(@merge_request) %> ) Merge request: <%= @merge_request.to_reference %> ( <%= merge_request_url(@merge_request) %> )
<% end -%> <% end -%>
Commit: <%= @pipeline.short_sha %> ( <%= commit_url(@pipeline) %> ) Commit: <%= @pipeline.short_sha %> ( <%= commit_url(@pipeline) %> )
......
#{sanitize_name(@updated_by_user.name)} pushed new commits to merge request #{@merge_request.to_reference} #{sanitize_name(@updated_by_user.name)} pushed new commits to merge request #{@merge_request.to_reference}
Merge Request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
\ \
- if @existing_commits.any? - if @existing_commits.any?
- count = @existing_commits.size - count = @existing_commits.size
......
Reassigned Merge Request <%= @merge_request.iid %> Reassigned merge request <%= @merge_request.iid %>
<%= url_for([@merge_request.project, @merge_request, { only_path: false }]) %> <%= url_for([@merge_request.project, @merge_request, { only_path: false }]) %>
......
%p %p
All discussions on Merge Request #{merge_request_reference_link(@merge_request)} All discussions on merge request #{merge_request_reference_link(@merge_request)}
were resolved by #{sanitize_name(@resolved_by.name)} were resolved by #{sanitize_name(@resolved_by.name)}
All discussions on Merge Request <%= @merge_request.to_reference %> were resolved by <%= sanitize_name(@resolved_by.name) %> All discussions on merge request <%= @merge_request.to_reference %> were resolved by <%= sanitize_name(@resolved_by.name) %>
<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
...@@ -18,14 +18,14 @@ ...@@ -18,14 +18,14 @@
- elsif @project && can?(current_user, :read_issue, @project) - elsif @project && can?(current_user, :read_issue, @project)
- issues_link = link_to s_('WikiEmptyIssueMessage|issue tracker'), project_issues_path(@project) - issues_link = link_to s_('WikiEmptyIssueMessage|issue tracker'), project_issues_path(@project)
- new_issue_link = link_to s_('WikiEmpty|Suggest wiki improvement'), new_project_issue_path(@project), class: 'btn gl-button btn-confirm', title: s_('WikiEmptyIssueMessage|Suggest wiki improvement')
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do = render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
%h4 %h4
= messages.dig(:issuable, :title) = messages.dig(:issuable, :title)
%p.text-left %p.text-left
= messages.dig(:issuable, :body).html_safe % { issues_link: issues_link } = messages.dig(:issuable, :body).html_safe % { issues_link: issues_link }
= new_issue_link - if show_new_issue_link?(@project)
= link_to s_('WikiEmpty|Suggest wiki improvement'), new_project_issue_path(@project), class: 'btn gl-button btn-confirm', title: s_('WikiEmptyIssueMessage|Suggest wiki improvement')
- else - else
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do = render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
= form_tag [:bulk_update, @project, type], method: :post, class: "bulk-update" do = form_tag [:bulk_update, @project, type], method: :post, class: "bulk-update" do
.block.issuable-sidebar-header .block.issuable-sidebar-header
.filter-item.inline.update-issues-btn.float-left .filter-item.inline.update-issues-btn.float-left
= button_tag _('Update all'), class: "gl-button btn update-selected-issues btn-info", disabled: true = button_tag _('Update all'), class: "gl-button btn update-selected-issues btn-confirm", disabled: true
= button_tag _('Cancel'), class: "gl-button btn btn-default js-bulk-update-menu-hide float-right" = button_tag _('Cancel'), class: "gl-button btn btn-default js-bulk-update-menu-hide float-right"
- if params[:state] != 'merged' - if params[:state] != 'merged'
.block .block
......
---
title: Remove deprecated info button from issue list view
merge_request: 57762
author:
type: other
---
redirect_to: 'index.md'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-06-30. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
...@@ -10,7 +10,7 @@ export const returnToPreviousPageFactory = ({ ...@@ -10,7 +10,7 @@ export const returnToPreviousPageFactory = ({
onDemandScansPath, onDemandScansPath,
profilesLibraryPath, profilesLibraryPath,
urlParamKey, urlParamKey,
}) => (gid) => { }) => ({ id } = {}) => {
// when previous page is not On-demand scans page // when previous page is not On-demand scans page
// redirect user to profiles library page // redirect user to profiles library page
if (!document.referrer?.includes(onDemandScansPath)) { if (!document.referrer?.includes(onDemandScansPath)) {
...@@ -20,9 +20,9 @@ export const returnToPreviousPageFactory = ({ ...@@ -20,9 +20,9 @@ export const returnToPreviousPageFactory = ({
// Otherwise, redirect them back to On-demand scans page // Otherwise, redirect them back to On-demand scans page
// with corresponding profile id, if available // with corresponding profile id, if available
// for example, /on_demand_scans?site_profile_id=35 // for example, /on_demand_scans?site_profile_id=35
const previousPagePath = gid const previousPagePath = id
? setUrlParams( ? setUrlParams(
{ [urlParamKey]: getIdFromGraphQLId(gid) }, { [urlParamKey]: getIdFromGraphQLId(id) },
relativePathToAbsolute(onDemandScansPath, getBaseURL()), relativePathToAbsolute(onDemandScansPath, getBaseURL()),
) )
: onDemandScansPath; : onDemandScansPath;
......
...@@ -11,7 +11,6 @@ import { ...@@ -11,7 +11,6 @@ import {
} from '@gitlab/ui'; } from '@gitlab/ui';
import * as Sentry from '@sentry/browser'; import * as Sentry from '@sentry/browser';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { returnToPreviousPageFactory } from 'ee/security_configuration/dast_profiles/redirect';
import { initFormField } from 'ee/security_configuration/utils'; import { initFormField } from 'ee/security_configuration/utils';
import { serializeFormObject } from '~/lib/utils/forms'; import { serializeFormObject } from '~/lib/utils/forms';
import { __, s__, n__, sprintf } from '~/locale'; import { __, s__, n__, sprintf } from '~/locale';
...@@ -50,19 +49,16 @@ export default { ...@@ -50,19 +49,16 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
profilesLibraryPath: {
type: String,
required: true,
},
onDemandScansPath: {
type: String,
required: true,
},
siteProfile: { siteProfile: {
type: Object, type: Object,
required: false, required: false,
default: null, default: null,
}, },
showHeader: {
type: Boolean,
required: false,
default: true,
},
}, },
data() { data() {
const { name = '', targetUrl = '', excludedUrls = [], auth = {} } = this.siteProfile || {}; const { name = '', targetUrl = '', excludedUrls = [], auth = {} } = this.siteProfile || {};
...@@ -96,11 +92,6 @@ export default { ...@@ -96,11 +92,6 @@ export default {
token: null, token: null,
errorMessage: '', errorMessage: '',
errors: [], errors: [],
returnToPreviousPage: returnToPreviousPageFactory({
onDemandScansPath: this.onDemandScansPath,
profilesLibraryPath: this.profilesLibraryPath,
urlParamKey: 'site_profile_id',
}),
}; };
}, },
computed: { computed: {
...@@ -216,7 +207,9 @@ export default { ...@@ -216,7 +207,9 @@ export default {
this.showErrors({ message: errorMessage, errors }); this.showErrors({ message: errorMessage, errors });
this.isLoading = false; this.isLoading = false;
} else { } else {
this.returnToPreviousPage(id); this.$emit('success', {
id,
});
} }
}, },
) )
...@@ -234,7 +227,7 @@ export default { ...@@ -234,7 +227,7 @@ export default {
} }
}, },
discard() { discard() {
this.returnToPreviousPage(); this.$emit('cancel');
}, },
captureException(exception) { captureException(exception) {
Sentry.captureException(exception); Sentry.captureException(exception);
...@@ -265,7 +258,7 @@ export default { ...@@ -265,7 +258,7 @@ export default {
<template> <template>
<gl-form novalidate @submit.prevent="onSubmit"> <gl-form novalidate @submit.prevent="onSubmit">
<h2 class="gl-mb-6"> <h2 v-if="showHeader" class="gl-mb-6">
{{ i18n.title }} {{ i18n.title }}
</h2> </h2>
......
import Vue from 'vue'; import Vue from 'vue';
import { returnToPreviousPageFactory } from 'ee/security_configuration/dast_profiles/redirect';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import DastSiteProfileForm from './components/dast_site_profile_form.vue'; import DastSiteProfileForm from './components/dast_site_profile_form.vue';
import apolloProvider from './graphql/provider'; import apolloProvider from './graphql/provider';
...@@ -13,14 +14,18 @@ export default () => { ...@@ -13,14 +14,18 @@ export default () => {
const props = { const props = {
fullPath, fullPath,
profilesLibraryPath,
onDemandScansPath,
}; };
if (el.dataset.siteProfile) { if (el.dataset.siteProfile) {
props.siteProfile = convertObjectPropsToCamelCase(JSON.parse(el.dataset.siteProfile)); props.siteProfile = convertObjectPropsToCamelCase(JSON.parse(el.dataset.siteProfile));
} }
const factoryParams = {
onDemandScansPath,
profilesLibraryPath,
urlParamKey: 'site_profile_id',
};
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new Vue({ new Vue({
el, el,
...@@ -28,6 +33,10 @@ export default () => { ...@@ -28,6 +33,10 @@ export default () => {
render(h) { render(h) {
return h(DastSiteProfileForm, { return h(DastSiteProfileForm, {
props, props,
on: {
success: returnToPreviousPageFactory(factoryParams),
cancel: returnToPreviousPageFactory(factoryParams),
},
}); });
}, },
}); });
......
---
title: Hide "Suggest wiki improvement" button from Auditors on project wiki page
merge_request: 56897
author:
type: fixed
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'User sees Scanner profile' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let(:profile_form_path) {new_project_security_configuration_dast_scans_dast_site_profile_path(project)}
let(:profile_library_path) { project_security_configuration_dast_scans_path(project) }
before_all do
project.add_developer(user)
end
before do
sign_in(user)
end
context 'when feature is available' do
before do
stub_licensed_features(security_on_demand_scans: true)
visit(profile_form_path)
end
it 'shows the form' do
expect(page).to have_gitlab_http_status(:ok)
expect(page).to have_content("New site profile")
end
it 'on submit', :js do
fill_in_profile_form
expect(current_path).to eq(profile_library_path)
end
it 'on cancel', :js do
click_button 'Cancel'
expect(current_path).to eq(profile_library_path)
end
end
context 'when feature is not available' do
before do
visit(profile_form_path)
end
it 'renders a 404' do
expect(page).to have_gitlab_http_status(:not_found)
end
end
def fill_in_profile_form
fill_in 'profileName', with: "hello"
fill_in 'targetUrl', with: "https://example.com"
click_button 'Save profile'
wait_for_requests
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Project > User views empty wiki' do
let_it_be(:auditor) { create(:user, auditor: true) }
let_it_be(:project) { create(:project, :private) }
let_it_be(:wiki) { create(:project_wiki, project: project) }
it_behaves_like 'User views empty wiki' do
context 'when signed in user is an Auditor' do
before do
sign_in(auditor)
end
context 'when user is not a member of the project' do
it_behaves_like 'empty wiki message', issuable: true, expect_button: false
end
context 'when user is a member of the project' do
before do
project.add_guest(auditor)
end
it_behaves_like 'empty wiki message', issuable: true, expect_button: true
end
end
end
end
...@@ -53,7 +53,7 @@ describe('DAST Profiles redirector', () => { ...@@ -53,7 +53,7 @@ describe('DAST Profiles redirector', () => {
}); });
it('redirects to previous page with id', () => { it('redirects to previous page with id', () => {
factory(2); factory({ id: 2 });
expect(urlUtility.redirectTo).toHaveBeenCalledWith( expect(urlUtility.redirectTo).toHaveBeenCalledWith(
`${onDemandScansPath}?site_profile_id=2`, `${onDemandScansPath}?site_profile_id=2`,
); );
......
...@@ -13,7 +13,6 @@ import * as responses from 'ee_jest/security_configuration/dast_site_profiles_fo ...@@ -13,7 +13,6 @@ import * as responses from 'ee_jest/security_configuration/dast_site_profiles_fo
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import * as urlUtility from '~/lib/utils/url_utility';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueApollo); localVue.use(VueApollo);
...@@ -134,7 +133,18 @@ describe('DastSiteProfileForm', () => { ...@@ -134,7 +133,18 @@ describe('DastSiteProfileForm', () => {
it('renders properly', () => { it('renders properly', () => {
createComponent(); createComponent();
expect(wrapper.html()).not.toBe(''); expect(findForm().exists()).toBe(true);
expect(findForm().text()).toContain('New site profile');
});
it('when showHeader prop is disabled', () => {
createComponent({
propsData: {
...defaultProps,
showHeader: false,
},
});
expect(findForm().text()).not.toContain('New site profile');
}); });
describe('target URL input', () => { describe('target URL input', () => {
...@@ -215,8 +225,6 @@ describe('DastSiteProfileForm', () => { ...@@ -215,8 +225,6 @@ describe('DastSiteProfileForm', () => {
siteProfile, siteProfile,
}, },
}); });
jest.spyOn(urlUtility, 'redirectTo').mockImplementation();
}); });
it('sets the correct title', () => { it('sets the correct title', () => {
...@@ -260,8 +268,10 @@ describe('DastSiteProfileForm', () => { ...@@ -260,8 +268,10 @@ describe('DastSiteProfileForm', () => {
}); });
}); });
it('redirects to the profiles library', () => { it('emits success event with correct params', () => {
expect(urlUtility.redirectTo).toHaveBeenCalledWith(profilesLibraryPath); expect(wrapper.emitted('success')).toBeTruthy();
expect(wrapper.emitted('success')).toHaveLength(1);
expect(wrapper.emitted('success')[0]).toStrictEqual([{ id: '3083' }]);
}); });
it('does not show an alert', () => { it('does not show an alert', () => {
...@@ -319,9 +329,9 @@ describe('DastSiteProfileForm', () => { ...@@ -319,9 +329,9 @@ describe('DastSiteProfileForm', () => {
describe('cancellation', () => { describe('cancellation', () => {
describe('form unchanged', () => { describe('form unchanged', () => {
it('redirects to the profiles library', () => { it('emits cancel event', () => {
findCancelButton().vm.$emit('click'); findCancelButton().vm.$emit('click');
expect(urlUtility.redirectTo).toHaveBeenCalledWith(profilesLibraryPath); expect(wrapper.emitted('cancel')).toBeTruthy();
}); });
}); });
...@@ -337,9 +347,9 @@ describe('DastSiteProfileForm', () => { ...@@ -337,9 +347,9 @@ describe('DastSiteProfileForm', () => {
expect(findCancelModal().vm.show).toHaveBeenCalled(); expect(findCancelModal().vm.show).toHaveBeenCalled();
}); });
it('redirects to the profiles library if confirmed', () => { it('emits cancel event', () => {
findCancelModal().vm.$emit('ok'); findCancelModal().vm.$emit('ok');
expect(urlUtility.redirectTo).toHaveBeenCalledWith(profilesLibraryPath); expect(wrapper.emitted('cancel')).toBeTruthy();
}); });
}); });
}); });
......
...@@ -100,7 +100,7 @@ module Gitlab ...@@ -100,7 +100,7 @@ module Gitlab
self.class.queue_size_total.observe({ runner_type: runner_type }, size_proc.call.to_f) self.class.queue_size_total.observe({ runner_type: runner_type }, size_proc.call.to_f)
end end
def observe_queue_time(metric) def observe_queue_time(metric, runner_type)
start_time = ::Gitlab::Metrics::System.monotonic_time start_time = ::Gitlab::Metrics::System.monotonic_time
result = yield result = yield
...@@ -111,9 +111,9 @@ module Gitlab ...@@ -111,9 +111,9 @@ module Gitlab
case metric case metric
when :process when :process
self.class.queue_iteration_duration_seconds.observe({}, seconds.to_f) self.class.queue_iteration_duration_seconds.observe({ runner_type: runner_type }, seconds.to_f)
when :retrieve when :retrieve
self.class.queue_retrieval_duration_seconds.observe({}, seconds.to_f) self.class.queue_retrieval_duration_seconds.observe({ runner_type: runner_type }, seconds.to_f)
else else
raise ArgumentError unless Rails.env.production? raise ArgumentError unless Rails.env.production?
end end
......
...@@ -500,6 +500,7 @@ module QA ...@@ -500,6 +500,7 @@ module QA
autoload :Wiki, 'qa/page/component/wiki' autoload :Wiki, 'qa/page/component/wiki'
autoload :WikiSidebar, 'qa/page/component/wiki_sidebar' autoload :WikiSidebar, 'qa/page/component/wiki_sidebar'
autoload :WikiPageForm, 'qa/page/component/wiki_page_form' autoload :WikiPageForm, 'qa/page/component/wiki_page_form'
autoload :CommitModal, 'qa/page/component/commit_modal'
module Issuable module Issuable
autoload :Common, 'qa/page/component/issuable/common' autoload :Common, 'qa/page/component/issuable/common'
......
# frozen_string_literal: true
module QA
module Page
module Component
class CommitModal < Page::Base
view 'app/assets/javascripts/projects/commit/components/form_modal.vue' do
element :submit_commit_button, required: true
end
end
end
end
end
...@@ -107,6 +107,10 @@ module QA ...@@ -107,6 +107,10 @@ module QA
element :suggestion_button element :suggestion_button
end end
view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue' do
element :cherry_pick_button
end
def start_review def start_review
click_element(:start_review_button) click_element(:start_review_button)
...@@ -359,6 +363,11 @@ module QA ...@@ -359,6 +363,11 @@ module QA
def apply_suggestions_batch def apply_suggestions_batch
all_elements(:apply_suggestions_batch_button, minimum: 1).first.click all_elements(:apply_suggestions_batch_button, minimum: 1).first.click
end end
def cherry_pick!
click_element(:cherry_pick_button, Page::Component::CommitModal)
click_element(:submit_commit_button)
end
end end
end end
end end
......
...@@ -35,7 +35,7 @@ module QA ...@@ -35,7 +35,7 @@ module QA
attribute :target do attribute :target do
Repository::ProjectPush.fabricate! do |resource| Repository::ProjectPush.fabricate! do |resource|
resource.project = project resource.project = project
resource.branch_name = project.default_branch resource.branch_name = target_branch
resource.new_branch = @target_new_branch resource.new_branch = @target_new_branch
resource.remote_branch = target_branch resource.remote_branch = target_branch
end end
...@@ -62,6 +62,7 @@ module QA ...@@ -62,6 +62,7 @@ module QA
@labels = [] @labels = []
@file_name = "added_file-#{SecureRandom.hex(8)}.txt" @file_name = "added_file-#{SecureRandom.hex(8)}.txt"
@file_content = "File Added" @file_content = "File Added"
@target_branch = "master"
@target_new_branch = true @target_new_branch = true
@no_preparation = false @no_preparation = false
@wait_for_merge = true @wait_for_merge = true
......
# frozen_string_literal: true
module QA
RSpec.describe 'Create' do
describe 'Cherry picking from a merge request' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project'
project.initialize_with_readme = true
end
end
let(:feature_mr) do
Resource::MergeRequest.fabricate_via_api! do |merge_request|
merge_request.project = project
merge_request.target_branch = 'development'
merge_request.target_new_branch = true
end
end
before do
Flow::Login.sign_in
end
it 'cherry picks a basic merge request', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1616' do
feature_mr.visit!
Page::MergeRequest::Show.perform do |merge_request|
merge_request.merge!
merge_request.cherry_pick!
end
Page::MergeRequest::New.perform do |merge_request|
expect(merge_request).to have_content('The merge request has been successfully cherry-picked')
end
end
end
end
end
import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
...@@ -8,18 +7,21 @@ import { IssuableType } from '~/issue_show/constants'; ...@@ -8,18 +7,21 @@ import { IssuableType } from '~/issue_show/constants';
import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue'; import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue';
import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql'; import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql';
import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql'; import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import CopyableField from '~/vue_shared/components/sidebar/copyable_field.vue';
import { issueReferenceResponse } from '../../mock_data'; import { issueReferenceResponse } from '../../mock_data';
describe('Sidebar Reference Widget', () => { describe('Sidebar Reference Widget', () => {
let wrapper; let wrapper;
let fakeApollo; let fakeApollo;
const referenceText = 'reference';
const mockReferenceValue = 'reference-1234';
const findCopyableField = () => wrapper.findComponent(CopyableField);
const createComponent = ({ const createComponent = ({
issuableType, issuableType = IssuableType.Issue,
referenceQuery = issueReferenceQuery, referenceQuery = issueReferenceQuery,
referenceQueryHandler = jest.fn().mockResolvedValue(issueReferenceResponse(referenceText)), referenceQueryHandler = jest.fn().mockResolvedValue(issueReferenceResponse(mockReferenceValue)),
} = {}) => { } = {}) => {
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -39,14 +41,20 @@ describe('Sidebar Reference Widget', () => { ...@@ -39,14 +41,20 @@ describe('Sidebar Reference Widget', () => {
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
wrapper = null; });
describe('when reference is loading', () => {
it('sets CopyableField `is-loading` prop to `true`', () => {
createComponent({ referenceQueryHandler: jest.fn().mockReturnValue(new Promise(() => {})) });
expect(findCopyableField().props('isLoading')).toBe(true);
});
}); });
describe.each([ describe.each([
[IssuableType.Issue, issueReferenceQuery], [IssuableType.Issue, issueReferenceQuery],
[IssuableType.MergeRequest, mergeRequestReferenceQuery], [IssuableType.MergeRequest, mergeRequestReferenceQuery],
])('when issuableType is %s', (issuableType, referenceQuery) => { ])('when issuableType is %s', (issuableType, referenceQuery) => {
it('displays the reference text', async () => { it('sets CopyableField `value` prop to reference value', async () => {
createComponent({ createComponent({
issuableType, issuableType,
referenceQuery, referenceQuery,
...@@ -54,19 +62,10 @@ describe('Sidebar Reference Widget', () => { ...@@ -54,19 +62,10 @@ describe('Sidebar Reference Widget', () => {
await waitForPromises(); await waitForPromises();
expect(wrapper.text()).toContain(referenceText); expect(findCopyableField().props('value')).toBe(mockReferenceValue);
});
it('displays loading icon while fetching and hides clipboard icon', async () => {
createComponent({
issuableType,
referenceQuery,
});
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
expect(wrapper.find(ClipboardButton).exists()).toBe(false);
}); });
describe('when error occurs', () => {
it('calls createFlash with correct parameters', async () => { it('calls createFlash with correct parameters', async () => {
const mockError = new Error('mayday'); const mockError = new Error('mayday');
...@@ -90,4 +89,5 @@ describe('Sidebar Reference Widget', () => { ...@@ -90,4 +89,5 @@ describe('Sidebar Reference Widget', () => {
expect(networkError).toEqual(mockError); expect(networkError).toEqual(mockError);
}); });
}); });
});
}); });
...@@ -31,7 +31,7 @@ RSpec.describe Emails::MergeRequests do ...@@ -31,7 +31,7 @@ RSpec.describe Emails::MergeRequests do
aggregate_failures do aggregate_failures do
is_expected.to have_referable_subject(merge_request, reply: true) is_expected.to have_referable_subject(merge_request, reply: true)
is_expected.to have_body_text(project_merge_request_path(project, merge_request)) is_expected.to have_body_text(project_merge_request_path(project, merge_request))
is_expected.to have_body_text('You have been mentioned in Merge Request') is_expected.to have_body_text('You have been mentioned in merge request')
is_expected.to have_link(merge_request.to_reference, href: project_merge_request_url(merge_request.target_project, merge_request)) is_expected.to have_link(merge_request.to_reference, href: project_merge_request_url(merge_request.target_project, merge_request))
is_expected.to have_text_part_content(assignee.name) is_expected.to have_text_part_content(assignee.name)
is_expected.to have_text_part_content(reviewer.name) is_expected.to have_text_part_content(reviewer.name)
......
...@@ -615,13 +615,25 @@ module Ci ...@@ -615,13 +615,25 @@ module Ci
create(:ci_build, pipeline: pipeline, tag_list: %w[non-matching]) create(:ci_build, pipeline: pipeline, tag_list: %w[non-matching])
end end
it "observes queue size of only matching jobs" do it 'observes queue size of only matching jobs' do
# pending_job + 2 x matching ones # pending_job + 2 x matching ones
expect(Gitlab::Ci::Queue::Metrics.queue_size_total).to receive(:observe) expect(Gitlab::Ci::Queue::Metrics.queue_size_total).to receive(:observe)
.with({ runner_type: specific_runner.runner_type }, 3) .with({ runner_type: specific_runner.runner_type }, 3)
expect(execute(specific_runner)).to eq(pending_job) expect(execute(specific_runner)).to eq(pending_job)
end end
it 'observes queue processing time by the runner type' do
expect(Gitlab::Ci::Queue::Metrics.queue_iteration_duration_seconds)
.to receive(:observe)
.with({ runner_type: specific_runner.runner_type }, anything)
expect(Gitlab::Ci::Queue::Metrics.queue_retrieval_duration_seconds)
.to receive(:observe)
.with({ runner_type: specific_runner.runner_type }, anything)
expect(execute(specific_runner)).to eq(pending_job)
end
end end
context 'when ci_register_job_temporary_lock is enabled' do context 'when ci_register_job_temporary_lock is enabled' do
......
...@@ -20,7 +20,7 @@ RSpec.shared_examples 'User views empty wiki' do ...@@ -20,7 +20,7 @@ RSpec.shared_examples 'User views empty wiki' do
end end
end end
shared_examples 'empty wiki message' do |writable: false, issuable: false, confluence: false| shared_examples 'empty wiki message' do |writable: false, issuable: false, confluence: false, expect_button: true|
# This mirrors the logic in: # This mirrors the logic in:
# - app/views/shared/empty_states/_wikis.html.haml # - app/views/shared/empty_states/_wikis.html.haml
# - WikiHelper#wiki_empty_state_messages # - WikiHelper#wiki_empty_state_messages
...@@ -37,7 +37,7 @@ RSpec.shared_examples 'User views empty wiki' do ...@@ -37,7 +37,7 @@ RSpec.shared_examples 'User views empty wiki' do
if issuable && !writable if issuable && !writable
expect(element).to have_content("improve the wiki for this #{container_name}") expect(element).to have_content("improve the wiki for this #{container_name}")
expect(element).to have_link("issue tracker", href: project_issues_path(project)) expect(element).to have_link("issue tracker", href: project_issues_path(project))
expect(element).to have_link("Suggest wiki improvement", href: new_project_issue_path(project)) expect(element.has_link?("Suggest wiki improvement", href: new_project_issue_path(project))).to be(expect_button)
else else
expect(element).not_to have_content("improve the wiki for this #{container_name}") expect(element).not_to have_content("improve the wiki for this #{container_name}")
expect(element).not_to have_link("issue tracker") expect(element).not_to have_link("issue tracker")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment