mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-08-01 16:04:19 +00:00
Update from merge request
This commit is contained in:
@ -219,7 +219,7 @@
|
||||
{"name":"gitaly","version":"18.2.0","platform":"ruby","checksum":"229010b9e8a9e8de213591989795df17a3bdcc01957903bd2a2f1474bbff5578"},
|
||||
{"name":"gitlab","version":"4.19.0","platform":"ruby","checksum":"3f645e3e195dbc24f0834fbf83e8ccfb2056d8e9712b01a640aad418a6949679"},
|
||||
{"name":"gitlab-chronic","version":"0.10.6","platform":"ruby","checksum":"a244d11a1396d2aac6ae9b2f326adf1605ec1ad20c29f06e8b672047d415a9ac"},
|
||||
{"name":"gitlab-cloud-connector","version":"1.22.0","platform":"ruby","checksum":"5c9cffd0a24b7004fa7a16d0a5ef378c7192ceb11b38b8147f18c268720b2a86"},
|
||||
{"name":"gitlab-cloud-connector","version":"1.23.0","platform":"ruby","checksum":"8d0765432340a03a65bce48e16ebea8062841b0fc9001b84aedbce70a468385d"},
|
||||
{"name":"gitlab-crystalball","version":"1.1.1","platform":"ruby","checksum":"0464a113b0809e0e9fa7c0100bb6634fe38465af95aa04efa49541d64250b8ed"},
|
||||
{"name":"gitlab-dangerfiles","version":"4.9.2","platform":"ruby","checksum":"d5c050f685d8720f6e70191a7d1216854d860dbdea5b455f87abe7542e005798"},
|
||||
{"name":"gitlab-experiment","version":"0.9.1","platform":"ruby","checksum":"f230ee742154805a755d5f2539dc44d93cdff08c5bbbb7656018d61f93d01f48"},
|
||||
|
@ -732,9 +732,9 @@ GEM
|
||||
terminal-table (>= 1.5.1)
|
||||
gitlab-chronic (0.10.6)
|
||||
numerizer (~> 0.2)
|
||||
gitlab-cloud-connector (1.22.0)
|
||||
gitlab-cloud-connector (1.23.0)
|
||||
activesupport (~> 7.0)
|
||||
jwt (~> 2.9.3)
|
||||
jwt (~> 2.9)
|
||||
gitlab-crystalball (1.1.1)
|
||||
git (< 4)
|
||||
ostruct (< 1)
|
||||
|
@ -219,7 +219,7 @@
|
||||
{"name":"gitaly","version":"18.2.0","platform":"ruby","checksum":"229010b9e8a9e8de213591989795df17a3bdcc01957903bd2a2f1474bbff5578"},
|
||||
{"name":"gitlab","version":"4.19.0","platform":"ruby","checksum":"3f645e3e195dbc24f0834fbf83e8ccfb2056d8e9712b01a640aad418a6949679"},
|
||||
{"name":"gitlab-chronic","version":"0.10.6","platform":"ruby","checksum":"a244d11a1396d2aac6ae9b2f326adf1605ec1ad20c29f06e8b672047d415a9ac"},
|
||||
{"name":"gitlab-cloud-connector","version":"1.22.0","platform":"ruby","checksum":"5c9cffd0a24b7004fa7a16d0a5ef378c7192ceb11b38b8147f18c268720b2a86"},
|
||||
{"name":"gitlab-cloud-connector","version":"1.23.0","platform":"ruby","checksum":"8d0765432340a03a65bce48e16ebea8062841b0fc9001b84aedbce70a468385d"},
|
||||
{"name":"gitlab-crystalball","version":"1.1.1","platform":"ruby","checksum":"0464a113b0809e0e9fa7c0100bb6634fe38465af95aa04efa49541d64250b8ed"},
|
||||
{"name":"gitlab-dangerfiles","version":"4.9.2","platform":"ruby","checksum":"d5c050f685d8720f6e70191a7d1216854d860dbdea5b455f87abe7542e005798"},
|
||||
{"name":"gitlab-experiment","version":"0.9.1","platform":"ruby","checksum":"f230ee742154805a755d5f2539dc44d93cdff08c5bbbb7656018d61f93d01f48"},
|
||||
|
@ -726,9 +726,9 @@ GEM
|
||||
terminal-table (>= 1.5.1)
|
||||
gitlab-chronic (0.10.6)
|
||||
numerizer (~> 0.2)
|
||||
gitlab-cloud-connector (1.22.0)
|
||||
gitlab-cloud-connector (1.23.0)
|
||||
activesupport (~> 7.0)
|
||||
jwt (~> 2.9.3)
|
||||
jwt (~> 2.9)
|
||||
gitlab-crystalball (1.1.1)
|
||||
git (< 4)
|
||||
ostruct (< 1)
|
||||
|
@ -1,16 +1,18 @@
|
||||
<script>
|
||||
import { GlTooltipDirective, GlFormCheckbox, GlLink } from '@gitlab/ui';
|
||||
import HelpIcon from '~/vue_shared/components/help_icon/help_icon.vue';
|
||||
import HelpPopover from '~/vue_shared/components/help_popover.vue';
|
||||
import SafeHtml from '~/vue_shared/directives/safe_html';
|
||||
import { SQUASH_BEFORE_MERGE } from '../../i18n';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlFormCheckbox,
|
||||
GlLink,
|
||||
HelpIcon,
|
||||
HelpPopover,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
SafeHtml,
|
||||
},
|
||||
i18n: {
|
||||
...SQUASH_BEFORE_MERGE,
|
||||
@ -35,6 +37,9 @@ export default {
|
||||
tooltipTitle() {
|
||||
return this.isDisabled ? this.$options.i18n.tooltipTitle : null;
|
||||
},
|
||||
popoverOptions() {
|
||||
return this.$options.i18n.popoverOptions;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -53,15 +58,26 @@ export default {
|
||||
>
|
||||
{{ $options.i18n.checkboxLabel }}
|
||||
</gl-form-checkbox>
|
||||
<gl-link
|
||||
<help-popover
|
||||
v-if="helpPath"
|
||||
v-gl-tooltip
|
||||
:href="helpPath"
|
||||
:title="$options.i18n.helpLabel"
|
||||
class="gl-leading-1"
|
||||
target="_blank"
|
||||
class="gl-flex gl-items-start"
|
||||
:options="popoverOptions"
|
||||
:aria-label="$options.i18n.helpLabel"
|
||||
>
|
||||
<help-icon :aria-label="$options.i18n.helpLabel" />
|
||||
</gl-link>
|
||||
<template v-if="popoverOptions.content">
|
||||
<p
|
||||
v-if="popoverOptions.content.text"
|
||||
v-safe-html="popoverOptions.content.text"
|
||||
class="gl-mb-0"
|
||||
></p>
|
||||
<gl-link
|
||||
v-if="popoverOptions.content.learnMorePath"
|
||||
:href="popoverOptions.content.learnMorePath"
|
||||
target="_blank"
|
||||
class="gl-text-sm"
|
||||
>{{ $options.i18n.learnMore }}</gl-link
|
||||
>
|
||||
</template>
|
||||
</help-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { __, s__ } from '~/locale';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
|
||||
export const MR_WIDGET_PREPARING_ASYNCHRONOUSLY = s__(
|
||||
'mrWidget|Your merge request is almost ready!',
|
||||
@ -18,6 +19,16 @@ export const SQUASH_BEFORE_MERGE = {
|
||||
tooltipTitle: __('Required in this project.'),
|
||||
checkboxLabel: __('Squash commits'),
|
||||
helpLabel: __('What is squashing?'),
|
||||
popoverOptions: {
|
||||
title: __('What is squashing?'),
|
||||
content: {
|
||||
text: __(
|
||||
'Squashing combines multiple commits into a single commit on merge. This keeps your repository history clean and makes it easier to revert changes.',
|
||||
),
|
||||
learnMorePath: helpPagePath('user/project/merge_requests/squash_and_merge'),
|
||||
},
|
||||
},
|
||||
learnMore: __('Learn more'),
|
||||
};
|
||||
|
||||
export const I18N_SHA_MISMATCH = {
|
||||
|
@ -13,10 +13,8 @@ module WorkItems
|
||||
end
|
||||
|
||||
def execute
|
||||
if cleanup_data_source_work_item_data?
|
||||
cleanup_work_item_widgets_data
|
||||
cleanup_work_item_non_widgets_data
|
||||
end
|
||||
cleanup_work_item_non_widgets_data
|
||||
cleanup_work_item_widgets_data
|
||||
|
||||
cleanup_work_item
|
||||
end
|
||||
@ -32,6 +30,10 @@ module WorkItems
|
||||
sync_data_callback_class = widget.class.sync_data_callback_class
|
||||
next if sync_data_callback_class.nil?
|
||||
|
||||
unless cleanup_data_source_work_item_data? || sync_data_callback_class.class.name.demodulize == 'Hierarchy'
|
||||
next
|
||||
end
|
||||
|
||||
data_handler = sync_data_callback_class.new(
|
||||
work_item: work_item,
|
||||
target_work_item: nil,
|
||||
@ -46,6 +48,7 @@ module WorkItems
|
||||
WorkItem.non_widgets.filter_map do |association_name|
|
||||
sync_callback_class = WorkItem.sync_callback_class(association_name)
|
||||
next if sync_callback_class.nil?
|
||||
next unless cleanup_data_source_work_item_data?
|
||||
|
||||
data_handler = sync_callback_class.new(
|
||||
work_item: work_item,
|
||||
|
@ -22,6 +22,10 @@ module WorkItems
|
||||
# Has to be implemented in the specific widget class or it can be an empty implementation if it does not need to
|
||||
# cleanup any data on the original work item
|
||||
def post_move_cleanup; end
|
||||
|
||||
def cleanup_data_source_work_item_data?
|
||||
Feature.enabled?(:cleanup_data_source_work_item_data, work_item.resource_parent)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -18,13 +18,15 @@ module WorkItems
|
||||
end
|
||||
|
||||
def post_move_cleanup
|
||||
return unless cleanup_data_source_work_item_data?
|
||||
|
||||
# Cleanup children linked to moved item when that is an issue because we are currently creating those
|
||||
# child items in the destination namespace anyway. If we decide to relink child items for Issue WIT
|
||||
# then we should not be deleting them here.
|
||||
work_item.child_links.each { |child_link| child_link.work_item.destroy! } if work_item.work_item_type.issue?
|
||||
|
||||
# cleanup parent link
|
||||
work_item.parent_link&.destroy!
|
||||
remove_parent_link_from_work_item
|
||||
end
|
||||
|
||||
private
|
||||
@ -81,6 +83,14 @@ module WorkItems
|
||||
).execute
|
||||
end
|
||||
end
|
||||
|
||||
def remove_parent_link_from_work_item
|
||||
return unless work_item.parent_link
|
||||
|
||||
::WorkItems::ParentLinks::DestroyService
|
||||
.new(work_item.parent_link, current_user, skip_policy_check: true)
|
||||
.execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -5,7 +5,7 @@ module WorkItems
|
||||
class DestroyService < IssuableLinks::DestroyService
|
||||
extend ::Gitlab::Utils::Override
|
||||
|
||||
attr_reader :link, :current_user, :parent, :child
|
||||
attr_reader :link, :current_user, :parent, :child, :params
|
||||
|
||||
def initialize(link, user, params = {})
|
||||
@link = link
|
||||
@ -34,6 +34,9 @@ module WorkItems
|
||||
end
|
||||
|
||||
def permission_to_remove_relation?
|
||||
# we can skip policy check if we check policy in the service that calls this
|
||||
return true if skip_policy_check?
|
||||
|
||||
can?(current_user, :admin_parent_link, child) && can?(current_user, :admin_parent_link, parent)
|
||||
end
|
||||
|
||||
@ -43,6 +46,10 @@ module WorkItems
|
||||
|
||||
GraphqlTriggers.work_item_updated(@link.work_item_parent)
|
||||
end
|
||||
|
||||
def skip_policy_check?
|
||||
params.fetch(:skip_policy_check, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,14 @@
|
||||
- merge_request = local_assigns.fetch(:issuable)
|
||||
|
||||
- popover_title = s_("What is squashing?")
|
||||
- popover_content = capture do
|
||||
%p.gl-mb-0
|
||||
= s_("Squashing combines multiple commits into a single commit on merge. This keeps your repository history clean and makes it easier to revert changes.")
|
||||
|
||||
= link_to s_("Learn more"), help_page_path('user/project/merge_requests/squash_and_merge.md'), target: '_blank', rel: 'noopener noreferrer'
|
||||
|
||||
- popover_data = { container: 'body', toggle: 'popover', placement: "top", html: 'true', triggers: 'hover focus', title: popover_title, content: popover_content }
|
||||
|
||||
- return if !merge_request.is_a?(MergeRequest) || merge_request.closed_or_merged_without_fork?
|
||||
|
||||
.form-group.row.gl-mb-7
|
||||
@ -28,4 +37,4 @@
|
||||
= render Pajamas::CheckboxTagComponent.new(name: 'merge_request[squash]', checked: merge_request_squash_option?(merge_request), value: '1', checkbox_options: { class: 'js-form-update' }) do |c|
|
||||
- c.with_label do
|
||||
= _("Squash commits when merge request is accepted.")
|
||||
= link_to sprite_icon('question-o'), help_page_path('user/project/merge_requests/squash_and_merge.md'), target: '_blank', rel: 'noopener noreferrer'
|
||||
= render Pajamas::ButtonComponent.new(variant: :link, icon: 'question-o', button_options: { class: '-gl-mt-1', data: popover_data })
|
||||
|
@ -60909,6 +60909,9 @@ msgstr ""
|
||||
msgid "SquashTmIntegration|Update Squash TM requirements when GitLab issues are modified."
|
||||
msgstr ""
|
||||
|
||||
msgid "Squashing combines multiple commits into a single commit on merge. This keeps your repository history clean and makes it easier to revert changes."
|
||||
msgstr ""
|
||||
|
||||
msgid "Stack trace"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { GlFormCheckbox, GlLink } from '@gitlab/ui';
|
||||
import { GlFormCheckbox } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import HelpPopover from '~/vue_shared/components/help_popover.vue';
|
||||
import SquashBeforeMerge from '~/vue_merge_request_widget/components/states/squash_before_merge.vue';
|
||||
import { SQUASH_BEFORE_MERGE } from '~/vue_merge_request_widget/i18n';
|
||||
|
||||
@ -64,37 +65,48 @@ describe('Squash before merge component', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('about link', () => {
|
||||
describe('help popover', () => {
|
||||
it('is not rendered if no help path is passed', () => {
|
||||
createComponent({
|
||||
value: false,
|
||||
});
|
||||
|
||||
const aboutLink = wrapper.findComponent(GlLink);
|
||||
const helpPopover = wrapper.findComponent(HelpPopover);
|
||||
|
||||
expect(aboutLink.exists()).toBe(false);
|
||||
expect(helpPopover.exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('is rendered if help path is passed', () => {
|
||||
it('is rendered if help path is passed', () => {
|
||||
createComponent({
|
||||
value: false,
|
||||
helpPath: 'test-path',
|
||||
});
|
||||
|
||||
const aboutLink = wrapper.findComponent(GlLink);
|
||||
const helpPopover = wrapper.findComponent(HelpPopover);
|
||||
|
||||
expect(aboutLink.exists()).toBe(true);
|
||||
expect(helpPopover.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should have a correct help path if passed', () => {
|
||||
it('should have correct popover options', () => {
|
||||
createComponent({
|
||||
value: false,
|
||||
helpPath: 'test-path',
|
||||
});
|
||||
|
||||
const aboutLink = wrapper.findComponent(GlLink);
|
||||
const helpPopover = wrapper.findComponent(HelpPopover);
|
||||
|
||||
expect(aboutLink.attributes('href')).toEqual('test-path');
|
||||
expect(helpPopover.props('options')).toEqual(SQUASH_BEFORE_MERGE.popoverOptions);
|
||||
});
|
||||
|
||||
it('should have correct aria-label', () => {
|
||||
createComponent({
|
||||
value: false,
|
||||
helpPath: 'test-path',
|
||||
});
|
||||
|
||||
const helpPopover = wrapper.findComponent(HelpPopover);
|
||||
|
||||
expect(helpPopover.props('ariaLabel')).toBe(SQUASH_BEFORE_MERGE.helpLabel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -129,12 +129,32 @@ RSpec.describe WorkItems::DataSync::Widgets::Hierarchy, feature_category: :team_
|
||||
describe '#post_move_cleanup' do
|
||||
let_it_be(:parent_link) { create(:parent_link, work_item: work_item, work_item_parent: parent) }
|
||||
|
||||
it "clears the parent for the original work_item" do
|
||||
expect { callback.post_move_cleanup }.to change { work_item.reload.work_item_parent }.from(parent).to(nil)
|
||||
context 'when cleanup_data_source_work_item_data feature is enabled' do
|
||||
before do
|
||||
stub_feature_flags(cleanup_data_source_work_item_data: true)
|
||||
end
|
||||
|
||||
it "clears the parent for the original work_item" do
|
||||
expect { callback.post_move_cleanup }.to change { work_item.reload.work_item_parent }.from(parent).to(nil)
|
||||
end
|
||||
|
||||
it "deletes a work_item_parent_link record" do
|
||||
expect { callback.post_move_cleanup }.to change { WorkItems::ParentLink.count }.by(-1)
|
||||
end
|
||||
end
|
||||
|
||||
it "deletes a work_item_parent_link record" do
|
||||
expect { callback.post_move_cleanup }.to change { WorkItems::ParentLink.count }.by(-1)
|
||||
context 'when cleanup_data_source_work_item_data feature is disabled' do
|
||||
before do
|
||||
stub_feature_flags(cleanup_data_source_work_item_data: false)
|
||||
end
|
||||
|
||||
it "does not clear the parent for the original work_item" do
|
||||
expect { callback.post_move_cleanup }.not_to change { work_item.reload.work_item_parent }
|
||||
end
|
||||
|
||||
it "does not clear a work_item_parent_link record" do
|
||||
expect { callback.post_move_cleanup }.not_to change { WorkItems::ParentLink.count }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -8,7 +8,7 @@ RSpec.describe WorkItems::ParentLinks::DestroyService, feature_category: :team_p
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:work_item) { create(:work_item, project: project) }
|
||||
let_it_be(:task) { create(:work_item, :task, project: project) }
|
||||
let_it_be(:parent_link) { create(:parent_link, work_item: task, work_item_parent: work_item) }
|
||||
let!(:parent_link) { create(:parent_link, work_item: task, work_item_parent: work_item) }
|
||||
|
||||
let(:parent_link_class) { WorkItems::ParentLink }
|
||||
|
||||
@ -88,6 +88,16 @@ RSpec.describe WorkItems::ParentLinks::DestroyService, feature_category: :team_p
|
||||
.and not_change(WorkItems::ResourceLinkEvent, :count)
|
||||
expect(SystemNoteService).not_to receive(:unrelate_work_item)
|
||||
end
|
||||
|
||||
context 'when skip_policy_check is true' do
|
||||
it 'removes relation' do
|
||||
expect(SystemNoteService).to receive(:unrelate_work_item)
|
||||
|
||||
expect { described_class.new(parent_link, user, skip_policy_check: true).execute }
|
||||
.to change { WorkItems::ParentLink.count }.by(-1)
|
||||
.and change { WorkItems::ResourceLinkEvent.count }.by(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -662,6 +662,23 @@ RSpec.shared_examples 'work items iteration' do
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'work items time tracking' do
|
||||
def add_estimate(estimate)
|
||||
click_button 'estimate'
|
||||
within_testid 'set-time-estimate-modal' do
|
||||
fill_in 'Estimate', with: estimate
|
||||
click_button 'Save'
|
||||
end
|
||||
end
|
||||
|
||||
def add_time_entry(time, summary = '')
|
||||
click_button 'Add time entry'
|
||||
within_testid 'create-timelog-modal' do
|
||||
fill_in 'Time spent', with: time
|
||||
fill_in 'Summary', with: summary
|
||||
click_button 'Save'
|
||||
end
|
||||
end
|
||||
|
||||
it 'passes axe automated accessibility testing for estimate and time spent modals', :aggregate_failures do
|
||||
click_button 'estimate'
|
||||
|
||||
@ -676,11 +693,7 @@ RSpec.shared_examples 'work items time tracking' do
|
||||
end
|
||||
|
||||
it 'adds and removes an estimate', :aggregate_failures do
|
||||
click_button 'estimate'
|
||||
within_testid 'set-time-estimate-modal' do
|
||||
fill_in 'Estimate', with: '5d'
|
||||
click_button 'Save'
|
||||
end
|
||||
add_estimate('5d')
|
||||
|
||||
expect(page).to have_text 'Estimate 5d'
|
||||
expect(page).to have_button '5d'
|
||||
@ -697,21 +710,9 @@ RSpec.shared_examples 'work items time tracking' do
|
||||
end
|
||||
|
||||
it 'adds and deletes time entries and view report', :aggregate_failures do
|
||||
click_button 'Add time entry'
|
||||
add_time_entry('1d', 'First summary')
|
||||
|
||||
within_testid 'create-timelog-modal' do
|
||||
fill_in 'Time spent', with: '1d'
|
||||
fill_in 'Summary', with: 'First summary'
|
||||
click_button 'Save'
|
||||
end
|
||||
|
||||
click_button 'Add time entry'
|
||||
|
||||
within_testid 'create-timelog-modal' do
|
||||
fill_in 'Time spent', with: '2d'
|
||||
fill_in 'Summary', with: 'Second summary'
|
||||
click_button 'Save'
|
||||
end
|
||||
add_time_entry('2d', 'Second summary')
|
||||
|
||||
expect(page).to have_text 'Spent 3d'
|
||||
expect(page).to have_button '3d'
|
||||
@ -734,6 +735,52 @@ RSpec.shared_examples 'work items time tracking' do
|
||||
expect(page).to have_text 'Spent 1d'
|
||||
expect(page).to have_button '1d'
|
||||
end
|
||||
|
||||
it 'checks for progess bar with both time entries and estimate', :aggregate_failures do
|
||||
add_estimate('5d')
|
||||
|
||||
expect(page).to have_text 'Estimate 5d'
|
||||
expect(page).to have_button '5d'
|
||||
expect(page).not_to have_button 'estimate'
|
||||
|
||||
add_time_entry('1d')
|
||||
|
||||
expect(page).to have_text 'Spent 1d'
|
||||
expect(page).to have_button '1d'
|
||||
|
||||
within_testid 'time-tracking-body' do
|
||||
expect(page).to have_selector('[role="progressbar"][aria-valuenow="20"]')
|
||||
end
|
||||
end
|
||||
|
||||
it 'using quick actions', :aggregate_failures do
|
||||
add_estimate('5d')
|
||||
|
||||
expect(page).to have_text 'Estimate 5d'
|
||||
expect(page).to have_button '5d'
|
||||
expect(page).not_to have_button 'estimate'
|
||||
|
||||
add_time_entry('1d')
|
||||
|
||||
expect(page).to have_text 'Spent 1d'
|
||||
expect(page).to have_button '1d'
|
||||
|
||||
fill_in _('Add a reply'), with: '/estimate 4d'
|
||||
click_button "Comment"
|
||||
|
||||
fill_in _('Add a reply'), with: '/spend 1d'
|
||||
click_button "Comment"
|
||||
|
||||
expect(page).to have_text 'Estimate 4d'
|
||||
expect(page).to have_button '4d'
|
||||
|
||||
expect(page).to have_text 'Spent 2d'
|
||||
expect(page).to have_button '2d'
|
||||
|
||||
within_testid 'time-tracking-body' do
|
||||
expect(page).to have_selector('[role="progressbar"][aria-valuenow="50"]')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'work items crm contacts' do
|
||||
|
@ -73,11 +73,12 @@ RSpec.describe ProcessCommitWorker, feature_category: :source_code_management do
|
||||
end
|
||||
|
||||
context 'when commit is not a merge request merge commit' do
|
||||
context 'when commit has work_item reference' do
|
||||
context 'when commit has work_item reference', :clean_gitlab_redis_cache do
|
||||
let(:work_item) { create(:work_item, :task, project: project) }
|
||||
let(:work_item_url) { Gitlab::UrlBuilder.build(work_item) }
|
||||
|
||||
before do
|
||||
# markdown cache from CacheMarkdownField needs to be cleared otherwise cached references are used
|
||||
allow(commit).to receive_messages(
|
||||
safe_message: "Ref #{work_item_url}",
|
||||
author: author
|
||||
|
@ -36,7 +36,6 @@ func Handler(rails *api.API) http.Handler {
|
||||
fail.Request(w, r, fmt.Errorf("failed to execute a Duo Workflow: %v", err))
|
||||
return
|
||||
}
|
||||
defer func() { _ = wf.CloseSend() }()
|
||||
|
||||
runner := &runner{
|
||||
rails: rails,
|
||||
@ -45,6 +44,7 @@ func Handler(rails *api.API) http.Handler {
|
||||
conn: conn,
|
||||
wf: wf,
|
||||
}
|
||||
defer func() { _ = runner.threadSafeCloseSend() }()
|
||||
|
||||
if err := runner.Execute(r.Context()); err != nil {
|
||||
log.WithRequest(r).WithError(err).Error()
|
||||
|
@ -32,6 +32,7 @@ type websocketConn interface {
|
||||
type workflowStream interface {
|
||||
Send(*pb.ClientEvent) error
|
||||
Recv() (*pb.Action, error)
|
||||
CloseSend() error
|
||||
}
|
||||
|
||||
type runner struct {
|
||||
@ -137,3 +138,9 @@ func (r *runner) threadSafeSend(event *pb.ClientEvent) error {
|
||||
defer r.sendMu.Unlock()
|
||||
return r.wf.Send(event)
|
||||
}
|
||||
|
||||
func (r *runner) threadSafeCloseSend() error {
|
||||
r.sendMu.Lock()
|
||||
defer r.sendMu.Unlock()
|
||||
return r.wf.CloseSend()
|
||||
}
|
||||
|
@ -84,6 +84,10 @@ func (m *mockWorkflowStream) Recv() (*pb.Action, error) {
|
||||
return action, nil
|
||||
}
|
||||
|
||||
func (m *mockWorkflowStream) CloseSend() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRunner_Execute(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
Reference in New Issue
Block a user