diff options
Diffstat (limited to 'spec/services/work_items')
11 files changed, 464 insertions, 80 deletions
diff --git a/spec/services/work_items/create_and_link_service_spec.rb b/spec/services/work_items/create_and_link_service_spec.rb index 81be15f9e2f..e259a22d388 100644 --- a/spec/services/work_items/create_and_link_service_spec.rb +++ b/spec/services/work_items/create_and_link_service_spec.rb @@ -6,7 +6,7 @@ RSpec.describe WorkItems::CreateAndLinkService do let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, group: group) } let_it_be(:user) { create(:user) } - let_it_be(:related_work_item) { create(:work_item, project: project) } + let_it_be(:related_work_item, refind: true) { create(:work_item, project: project) } let_it_be(:invalid_parent) { create(:work_item, :task, project: project) } let(:spam_params) { double } @@ -24,6 +24,26 @@ RSpec.describe WorkItems::CreateAndLinkService do project.add_developer(user) end + shared_examples 'successful work item and link creator' do + it 'creates a work item successfully with links' do + expect do + service_result + end.to change(WorkItem, :count).by(1).and( + change(WorkItems::ParentLink, :count).by(1) + ) + end + + it 'copies confidential status from the parent' do + expect do + service_result + end.to change(WorkItem, :count).by(1) + + created_task = WorkItem.last + + expect(created_task.confidential).to eq(related_work_item.confidential) + end + end + describe '#execute' do subject(:service_result) { described_class.new(project: project, current_user: user, params: params, spam_params: spam_params, link_params: link_params).execute } @@ -42,15 +62,21 @@ RSpec.describe WorkItems::CreateAndLinkService do ) end + it_behaves_like 'title with extra spaces' + context 'when link params are valid' do let(:link_params) { { parent_work_item: related_work_item } } - it 'creates a work item successfully with links' do - expect do - service_result - end.to change(WorkItem, :count).by(1).and( - change(WorkItems::ParentLink, :count).by(1) - ) + context 'when parent is not confidential' do + it_behaves_like 'successful work item and link creator' + end + + context 'when parent is confidential' do + before do + related_work_item.update!(confidential: true) + end + + it_behaves_like 'successful work item and link creator' end end diff --git a/spec/services/work_items/create_from_task_service_spec.rb b/spec/services/work_items/create_from_task_service_spec.rb index 7d2dab228b1..7c5430f038c 100644 --- a/spec/services/work_items/create_from_task_service_spec.rb +++ b/spec/services/work_items/create_from_task_service_spec.rb @@ -64,6 +64,8 @@ RSpec.describe WorkItems::CreateFromTaskService do expect(list_work_item.description).to eq("- [ ] #{created_work_item.to_reference}+") end + + it_behaves_like 'title with extra spaces' end context 'when last operation fails' do diff --git a/spec/services/work_items/create_service_spec.rb b/spec/services/work_items/create_service_spec.rb index 4009c85bacd..c0bcf9b606d 100644 --- a/spec/services/work_items/create_service_spec.rb +++ b/spec/services/work_items/create_service_spec.rb @@ -65,6 +65,12 @@ RSpec.describe WorkItems::CreateService do expect(work_item.description).to eq('please fix') expect(work_item.work_item_type.base_type).to eq('issue') end + + it 'calls NewIssueWorker with correct arguments' do + expect(NewIssueWorker).to receive(:perform_async).with(Integer, current_user.id, 'WorkItem') + + service_result + end end context 'when params are invalid' do @@ -170,7 +176,7 @@ RSpec.describe WorkItems::CreateService do let_it_be(:parent) { create(:work_item, :task, project: project) } it_behaves_like 'fails creating work item and returns errors' do - let(:error_message) { 'only Issue and Incident can be parent of Task.'} + let(:error_message) { 'only Issue and Incident can be parent of Task.' } end end @@ -197,7 +203,7 @@ RSpec.describe WorkItems::CreateService do end it_behaves_like 'fails creating work item and returns errors' do - let(:error_message) { 'No matching task found. Make sure that you are adding a valid task ID.'} + let(:error_message) { 'No matching task found. Make sure that you are adding a valid task ID.' } end end end diff --git a/spec/services/work_items/parent_links/create_service_spec.rb b/spec/services/work_items/parent_links/create_service_spec.rb index 85b0ee040cd..0ba41373544 100644 --- a/spec/services/work_items/parent_links/create_service_spec.rb +++ b/spec/services/work_items/parent_links/create_service_spec.rb @@ -12,10 +12,10 @@ RSpec.describe WorkItems::ParentLinks::CreateService do let_it_be(:task1) { create(:work_item, :task, project: project) } let_it_be(:task2) { create(:work_item, :task, project: project) } let_it_be(:guest_task) { create(:work_item, :task) } - let_it_be(:invalid_task) { build_stubbed(:work_item, :task, id: non_existing_record_id)} + let_it_be(:invalid_task) { build_stubbed(:work_item, :task, id: non_existing_record_id) } let_it_be(:another_project) { (create :project) } let_it_be(:other_project_task) { create(:work_item, :task, iid: 100, project: another_project) } - let_it_be(:existing_parent_link) { create(:parent_link, work_item: task, work_item_parent: work_item)} + let_it_be(:existing_parent_link) { create(:parent_link, work_item: task, work_item_parent: work_item) } let(:parent_link_class) { WorkItems::ParentLink } let(:issuable_type) { :task } @@ -84,13 +84,26 @@ RSpec.describe WorkItems::ParentLinks::CreateService do expect(subject[:created_references].map(&:work_item_id)).to match_array([task1.id, task2.id]) end + it 'creates notes', :aggregate_failures do + subject + + work_item_notes = work_item.notes.last(2) + expect(work_item_notes.first.note).to eq("added #{task1.to_reference} as child task") + expect(work_item_notes.last.note).to eq("added #{task2.to_reference} as child task") + expect(task1.notes.last.note).to eq("added #{work_item.to_reference} as parent issue") + expect(task2.notes.last.note).to eq("added #{work_item.to_reference} as parent issue") + end + context 'when task is already assigned' do let(:params) { { issuable_references: [task, task2] } } - it 'creates links only for non related tasks' do + it 'creates links only for non related tasks', :aggregate_failures do expect { subject }.to change(parent_link_class, :count).by(1) expect(subject[:created_references].map(&:work_item_id)).to match_array([task2.id]) + expect(work_item.notes.last.note).to eq("added #{task2.to_reference} as child task") + expect(task2.notes.last.note).to eq("added #{work_item.to_reference} as parent issue") + expect(task.notes).to be_empty end end @@ -109,6 +122,15 @@ RSpec.describe WorkItems::ParentLinks::CreateService do is_expected.to eq(service_error(error, http_status: 422)) end + + it 'creates notes for valid links' do + subject + + expect(work_item.notes.last.note).to eq("added #{task1.to_reference} as child task") + expect(task1.notes.last.note).to eq("added #{work_item.to_reference} as parent issue") + expect(issue.notes).to be_empty + expect(other_project_task.notes).to be_empty + end end context 'when parent type is invalid' do diff --git a/spec/services/work_items/parent_links/destroy_service_spec.rb b/spec/services/work_items/parent_links/destroy_service_spec.rb index 574b70af397..654a03ef6f7 100644 --- a/spec/services/work_items/parent_links/destroy_service_spec.rb +++ b/spec/services/work_items/parent_links/destroy_service_spec.rb @@ -9,7 +9,7 @@ RSpec.describe WorkItems::ParentLinks::DestroyService do 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_it_be(:parent_link) { create(:parent_link, work_item: task, work_item_parent: work_item) } let(:parent_link_class) { WorkItems::ParentLink } @@ -23,8 +23,11 @@ RSpec.describe WorkItems::ParentLinks::DestroyService do context 'when user has permissions to update work items' do let(:user) { reporter } - it 'removes relation' do + it 'removes relation and creates notes', :aggregate_failures do expect { subject }.to change(parent_link_class, :count).by(-1) + + expect(work_item.notes.last.note).to eq("removed child task #{task.to_reference}") + expect(task.notes.last.note).to eq("removed parent issue #{work_item.to_reference}") end it 'returns success message' do @@ -35,8 +38,10 @@ RSpec.describe WorkItems::ParentLinks::DestroyService do context 'when user has insufficient permissions' do let(:user) { guest } - it 'does not remove relation' do + it 'does not remove relation', :aggregate_failures do expect { subject }.not_to change(parent_link_class, :count).from(1) + + expect(SystemNoteService).not_to receive(:unrelate_work_item) end it 'returns error message' do diff --git a/spec/services/work_items/update_service_spec.rb b/spec/services/work_items/update_service_spec.rb index b17c9ffb4fb..2e0b0051495 100644 --- a/spec/services/work_items/update_service_spec.rb +++ b/spec/services/work_items/update_service_spec.rb @@ -4,7 +4,8 @@ require 'spec_helper' RSpec.describe WorkItems::UpdateService do let_it_be(:developer) { create(:user) } - let_it_be(:project) { create(:project).tap { |proj| proj.add_developer(developer) } } + let_it_be(:guest) { create(:user) } + let_it_be(:project) { create(:project) } let_it_be(:parent) { create(:work_item, project: project) } let_it_be_with_reload(:work_item) { create(:work_item, project: project, assignees: [developer]) } @@ -13,21 +14,36 @@ RSpec.describe WorkItems::UpdateService do let(:opts) { {} } let(:current_user) { developer } + before do + project.add_developer(developer) + project.add_guest(guest) + end + describe '#execute' do - subject(:update_work_item) do + let(:service) do described_class.new( project: project, current_user: current_user, params: opts, spam_params: spam_params, widget_params: widget_params - ).execute(work_item) + ) end + subject(:update_work_item) { service.execute(work_item) } + before do stub_spam_services end + shared_examples 'update service that triggers graphql dates updated subscription' do + it 'triggers graphql subscription issueableDatesUpdated' do + expect(GraphqlTriggers).to receive(:issuable_dates_updated).with(work_item).and_call_original + + update_work_item + end + end + context 'when title is changed' do let(:opts) { { title: 'changed' } } @@ -50,6 +66,16 @@ RSpec.describe WorkItems::UpdateService do end end + context 'when dates are changed' do + let(:opts) { { start_date: Date.today } } + + it 'tracks users updating work item dates' do + expect(Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter).to receive(:track_work_item_date_changed_action).with(author: current_user) + + update_work_item + end + end + context 'when updating state_event' do context 'when state_event is close' do let(:opts) { { state_event: 'close' } } @@ -82,8 +108,7 @@ RSpec.describe WorkItems::UpdateService do let(:widget_params) do { hierarchy_widget: { parent: parent }, - description_widget: { description: 'foo' }, - weight_widget: { weight: 1 } + description_widget: { description: 'foo' } } end @@ -101,8 +126,7 @@ RSpec.describe WorkItems::UpdateService do let(:supported_widgets) do [ - { klass: WorkItems::Widgets::DescriptionService::UpdateService, callback: :update, params: { description: 'foo' } }, - { klass: WorkItems::Widgets::WeightService::UpdateService, callback: :update, params: { weight: 1 } }, + { klass: WorkItems::Widgets::DescriptionService::UpdateService, callback: :before_update_callback, params: { description: 'foo' } }, { klass: WorkItems::Widgets::HierarchyService::UpdateService, callback: :before_update_in_transaction, params: { parent: parent } } ] end @@ -126,7 +150,7 @@ RSpec.describe WorkItems::UpdateService do before do allow_next_instance_of(widget_service_class) do |instance| allow(instance) - .to receive(:update) + .to receive(:before_update_callback) .with(params: { description: 'changed' }).and_return(nil) end end @@ -142,6 +166,69 @@ RSpec.describe WorkItems::UpdateService do expect(work_item.description).to eq('changed') end + + context 'with mentions', :mailer, :sidekiq_might_not_need_inline do + shared_examples 'creates the todo and sends email' do |attribute| + it 'creates a todo and sends email' do + expect { perform_enqueued_jobs { update_work_item } }.to change(Todo, :count).by(1) + expect(work_item.reload.attributes[attribute.to_s]).to eq("mention #{guest.to_reference}") + should_email(guest) + end + end + + context 'when description contains a user mention' do + let(:widget_params) { { description_widget: { description: "mention #{guest.to_reference}" } } } + + it_behaves_like 'creates the todo and sends email', :description + end + + context 'when title contains a user mention' do + let(:opts) { { title: "mention #{guest.to_reference}" } } + + it_behaves_like 'creates the todo and sends email', :title + end + end + + context 'when work item validation fails' do + let(:opts) { { title: '' } } + + it 'returns validation errors' do + expect(update_work_item[:message]).to contain_exactly("Title can't be blank") + end + + it 'does not execute after-update widgets', :aggregate_failures do + expect(service).to receive(:update).and_call_original + expect(service).not_to receive(:execute_widgets).with(callback: :update, widget_params: widget_params) + + expect { update_work_item }.not_to change(work_item, :description) + end + end + end + + context 'for start and due date widget' do + let(:updated_date) { 1.week.from_now.to_date } + + context 'when due_date is updated' do + let(:widget_params) { { start_and_due_date_widget: { due_date: updated_date } } } + + it_behaves_like 'update service that triggers graphql dates updated subscription' + end + + context 'when start_date is updated' do + let(:widget_params) { { start_and_due_date_widget: { start_date: updated_date } } } + + it_behaves_like 'update service that triggers graphql dates updated subscription' + end + + context 'when no date param is updated' do + let(:opts) { { title: 'should not trigger' } } + + it 'does not trigger date updated subscription' do + expect(GraphqlTriggers).not_to receive(:issuable_dates_updated) + + update_work_item + end + end end context 'for the hierarchy widget' do @@ -175,6 +262,22 @@ RSpec.describe WorkItems::UpdateService do end.to not_change(WorkItems::ParentLink, :count).and(not_change(work_item, :title)) end end + + context 'when work item validation fails' do + let(:opts) { { title: '' } } + + it 'returns validation errors' do + expect(update_work_item[:message]).to contain_exactly("Title can't be blank") + end + + it 'does not execute after-update widgets', :aggregate_failures do + expect(service).to receive(:update).and_call_original + expect(service).not_to receive(:execute_widgets).with(callback: :before_update_in_transaction, widget_params: widget_params) + expect(work_item.work_item_children).not_to include(child_work_item) + + update_work_item + end + end end end end diff --git a/spec/services/work_items/widgets/assignees_service/update_service_spec.rb b/spec/services/work_items/widgets/assignees_service/update_service_spec.rb new file mode 100644 index 00000000000..0ab2c85f078 --- /dev/null +++ b/spec/services/work_items/widgets/assignees_service/update_service_spec.rb @@ -0,0 +1,116 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe WorkItems::Widgets::AssigneesService::UpdateService, :freeze_time do + let_it_be(:reporter) { create(:user) } + let_it_be(:project) { create(:project, :private) } + let_it_be(:new_assignee) { create(:user) } + + let(:work_item) do + create(:work_item, project: project, updated_at: 1.day.ago) + end + + let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Assignees) } } + let(:current_user) { reporter } + let(:params) { { assignee_ids: [new_assignee.id] } } + + before_all do + project.add_reporter(reporter) + project.add_guest(new_assignee) + end + + describe '#before_update_in_transaction' do + subject do + described_class.new(widget: widget, current_user: current_user) + .before_update_in_transaction(params: params) + end + + it 'updates the assignees and sets updated_at to the current time' do + subject + + expect(work_item.assignee_ids).to contain_exactly(new_assignee.id) + expect(work_item.updated_at).to be_like_time(Time.current) + end + + context 'when passing an empty array' do + let(:params) { { assignee_ids: [] } } + + before do + work_item.assignee_ids = [reporter.id] + end + + it 'removes existing assignees' do + subject + + expect(work_item.assignee_ids).to be_empty + expect(work_item.updated_at).to be_like_time(Time.current) + end + end + + context 'when user does not have access' do + let(:current_user) { create(:user) } + + it 'does not update the assignees' do + subject + + expect(work_item.assignee_ids).to be_empty + expect(work_item.updated_at).to be_like_time(1.day.ago) + end + end + + context 'when multiple assignees are given' do + let(:params) { { assignee_ids: [new_assignee.id, reporter.id] } } + + context 'when work item allows multiple assignees' do + before do + allow(work_item).to receive(:allows_multiple_assignees?).and_return(true) + end + + it 'sets all the given assignees' do + subject + + expect(work_item.assignee_ids).to contain_exactly(new_assignee.id, reporter.id) + expect(work_item.updated_at).to be_like_time(Time.current) + end + end + + context 'when work item does not allow multiple assignees' do + before do + allow(work_item).to receive(:allows_multiple_assignees?).and_return(false) + end + + it 'only sets the first assignee' do + subject + + expect(work_item.assignee_ids).to contain_exactly(new_assignee.id) + expect(work_item.updated_at).to be_like_time(Time.current) + end + end + end + + context 'when assignee does not have access to the work item' do + let(:params) { { assignee_ids: [create(:user).id] } } + + it 'does not set the assignee' do + subject + + expect(work_item.assignee_ids).to be_empty + expect(work_item.updated_at).to be_like_time(1.day.ago) + end + end + + context 'when assignee ids are the same as the existing ones' do + before do + work_item.assignee_ids = [new_assignee.id] + end + + it 'does not touch updated_at' do + subject + + expect(work_item.assignee_ids).to contain_exactly(new_assignee.id) + expect(work_item.updated_at).to be_like_time(1.day.ago) + end + end + end +end diff --git a/spec/services/work_items/widgets/description_service/update_service_spec.rb b/spec/services/work_items/widgets/description_service/update_service_spec.rb index a2eceb97f09..582d9dc85f7 100644 --- a/spec/services/work_items/widgets/description_service/update_service_spec.rb +++ b/spec/services/work_items/widgets/description_service/update_service_spec.rb @@ -3,32 +3,102 @@ require 'spec_helper' RSpec.describe WorkItems::Widgets::DescriptionService::UpdateService do - let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project) } - let_it_be_with_reload(:work_item) { create(:work_item, project: project, description: 'old description') } + let_it_be(:random_user) { create(:user) } + let_it_be(:author) { create(:user) } + let_it_be(:guest) { create(:user) } + let_it_be(:reporter) { create(:user) } + let_it_be(:project) { create(:project, :public) } - let(:widget) { work_item.widgets.find {|widget| widget.is_a?(WorkItems::Widgets::Description) } } + let(:params) { { description: 'updated description' } } + let(:current_user) { author } + let(:work_item) do + create(:work_item, author: author, project: project, description: 'old description', + last_edited_at: Date.yesterday, last_edited_by: random_user + ) + end - describe '#update' do - subject { described_class.new(widget: widget, current_user: user).update(params: params) } # rubocop:disable Rails/SaveBang + let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Description) } } - context 'when description param is present' do - let(:params) { { description: 'updated description' } } + describe '#update' do + subject { described_class.new(widget: widget, current_user: current_user).before_update_callback(params: params) } + shared_examples 'sets work item description' do it 'correctly sets work item description value' do subject - expect(work_item.description).to eq('updated description') + expect(work_item.description).to eq(params[:description]) + expect(work_item.last_edited_by).to eq(current_user) + expect(work_item.last_edited_at).to be_within(2.seconds).of(Time.current) end end - context 'when description param is not present' do - let(:params) { {} } - + shared_examples 'does not set work item description' do it 'does not change work item description value' do subject expect(work_item.description).to eq('old description') + expect(work_item.last_edited_by).to eq(random_user) + expect(work_item.last_edited_at).to eq(Date.yesterday) + end + end + + context 'when user has permission to update description' do + context 'when user is work item author' do + let(:current_user) { author } + + it_behaves_like 'sets work item description' + end + + context 'when user is a project reporter' do + let(:current_user) { reporter } + + before do + project.add_reporter(reporter) + end + + it_behaves_like 'sets work item description' + end + + context 'when description is nil' do + let(:current_user) { author } + let(:params) { { description: nil } } + + it_behaves_like 'sets work item description' + end + + context 'when description is empty' do + let(:current_user) { author } + let(:params) { { description: '' } } + + it_behaves_like 'sets work item description' + end + + context 'when description param is not present' do + let(:params) { {} } + + it_behaves_like 'does not set work item description' + end + end + + context 'when user does not have permission to update description' do + context 'when user is a project guest' do + let(:current_user) { guest } + + before do + project.add_guest(guest) + end + + it_behaves_like 'does not set work item description' + end + + context 'with private project' do + let_it_be(:project) { create(:project) } + + context 'when user is work item author' do + let(:current_user) { author } + + it_behaves_like 'does not set work item description' + end end end end diff --git a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb index 4f6ff1b8676..9a425d5308c 100644 --- a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb +++ b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb @@ -11,7 +11,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do let_it_be(:child_work_item) { create(:work_item, :task, project: project) } let_it_be(:existing_link) { create(:parent_link, work_item: child_work_item, work_item_parent: work_item) } - let(:widget) { work_item.widgets.find {|widget| widget.is_a?(WorkItems::Widgets::Hierarchy) } } + let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Hierarchy) } } let(:not_found_error) { 'No matching task found. Make sure that you are adding a valid task ID.' } shared_examples 'raises a WidgetError' do @@ -29,13 +29,21 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do end end + context 'when invalid params are present' do + let(:params) { { other_parent: parent_work_item } } + + it_behaves_like 'raises a WidgetError' do + let(:message) { 'One or more arguments are invalid: other_parent.' } + end + end + context 'when updating children' do let_it_be(:child_work_item2) { create(:work_item, :task, project: project) } let_it_be(:child_work_item3) { create(:work_item, :task, project: project) } let_it_be(:child_work_item4) { create(:work_item, :task, project: project) } context 'when work_items_hierarchy feature flag is disabled' do - let(:params) { { children: [child_work_item4] }} + let(:params) { { children: [child_work_item4] } } before do stub_feature_flags(work_items_hierarchy: false) @@ -47,7 +55,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do end context 'when user has insufficient permissions to link work items' do - let(:params) { { children: [child_work_item4] }} + let(:params) { { children: [child_work_item4] } } it_behaves_like 'raises a WidgetError' do let(:message) { not_found_error } @@ -60,7 +68,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do end context 'with valid params' do - let(:params) { { children: [child_work_item2, child_work_item3] }} + let(:params) { { children: [child_work_item2, child_work_item3] } } it 'correctly sets work item parent' do subject @@ -71,7 +79,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do end context 'when child is already assigned' do - let(:params) { { children: [child_work_item] }} + let(:params) { { children: [child_work_item] } } it_behaves_like 'raises a WidgetError' do let(:message) { 'Task(s) already assigned' } @@ -81,7 +89,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do context 'when child type is invalid' do let_it_be(:child_issue) { create(:work_item, project: project) } - let(:params) { { children: [child_issue] }} + let(:params) { { children: [child_issue] } } it_behaves_like 'raises a WidgetError' do let(:message) do @@ -95,7 +103,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do context 'when updating parent' do let_it_be(:work_item) { create(:work_item, :task, project: project) } - let(:params) {{ parent: parent_work_item } } + let(:params) { { parent: parent_work_item } } context 'when work_items_hierarchy feature flag is disabled' do before do @@ -144,9 +152,9 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do end context 'when type is invalid' do - let_it_be(:parent_task) { create(:work_item, :task, project: project)} + let_it_be(:parent_task) { create(:work_item, :task, project: project) } - let(:params) {{ parent: parent_task } } + let(:params) { { parent: parent_task } } it_behaves_like 'raises a WidgetError' do let(:message) do diff --git a/spec/services/work_items/widgets/start_and_due_date_service/update_service_spec.rb b/spec/services/work_items/widgets/start_and_due_date_service/update_service_spec.rb new file mode 100644 index 00000000000..d328c541fc7 --- /dev/null +++ b/spec/services/work_items/widgets/start_and_due_date_service/update_service_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe WorkItems::Widgets::StartAndDueDateService::UpdateService do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be_with_reload(:work_item) { create(:work_item, project: project) } + + let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::StartAndDueDate) } } + + describe '#before_update_callback' do + let(:start_date) { Date.today } + let(:due_date) { 1.week.from_now.to_date } + + subject(:update_params) do + described_class.new(widget: widget, current_user: user).before_update_callback(params: params) + end + + context 'when start and due date params are present' do + let(:params) { { start_date: Date.today, due_date: 1.week.from_now.to_date } } + + it 'correctly sets date values' do + expect do + update_params + end.to change(work_item, :start_date).from(nil).to(start_date).and( + change(work_item, :due_date).from(nil).to(due_date) + ) + end + end + + context 'when date params are not present' do + let(:params) { {} } + + it 'does not change work item date values' do + expect do + update_params + end.to not_change(work_item, :start_date).from(nil).and( + not_change(work_item, :due_date).from(nil) + ) + end + end + + context 'when work item had both date values already set' do + before do + work_item.update!(start_date: start_date, due_date: due_date) + end + + context 'when one of the two params is null' do + let(:params) { { start_date: nil } } + + it 'sets only one date to null' do + expect do + update_params + end.to change(work_item, :start_date).from(start_date).to(nil).and( + not_change(work_item, :due_date).from(due_date) + ) + end + end + end + end +end diff --git a/spec/services/work_items/widgets/weight_service/update_service_spec.rb b/spec/services/work_items/widgets/weight_service/update_service_spec.rb deleted file mode 100644 index 97e17f1c526..00000000000 --- a/spec/services/work_items/widgets/weight_service/update_service_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe WorkItems::Widgets::WeightService::UpdateService do - let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project) } - let_it_be_with_reload(:work_item) { create(:work_item, project: project, weight: 1) } - - let(:widget) { work_item.widgets.find {|widget| widget.is_a?(WorkItems::Widgets::Weight) } } - - describe '#update' do - subject { described_class.new(widget: widget, current_user: user).update(params: params) } # rubocop:disable Rails/SaveBang - - context 'when weight param is present' do - let(:params) { { weight: 2 } } - - it 'correctly sets work item weight value' do - subject - - expect(work_item.weight).to eq(2) - end - end - - context 'when weight param is not present' do - let(:params) { {} } - - it 'does not change work item weight value', :aggregate_failures do - expect { subject } - .to not_change { work_item.weight } - - expect(work_item.weight).to eq(1) - end - end - end -end |