From 10e45aead55fcc9e728af2a29d0ff134f501f82b Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Thu, 16 Oct 2025 09:55:52 +0200 Subject: [PATCH] [64823] Improve usability of Workflow tables (#20581) * Split workflow tables into multiple tabs Co-authored-by: Behrokh Satarnejad * make header and first column sticky in work flow tables * calculate the height of the table * Update the tabs individually * calculate the height of the table * Add tests for separated Workflow update process * add a new style sheet for the workflows page and handle vertical and horizontal scroll in it * set a class for page header in workflows page * set page header class for other pages like summary and copy as well * make header and first column sticky in summary page * make the button sticky while scrolling horizontally * redirect to the current tab in update method --------- Co-authored-by: Behrokh Satarnejad --- .../edit_page_header_component.html.erb | 63 +++++++++++++ .../workflows/edit_page_header_component.rb | 51 ++++++++++ .../workflows/page_header_component.html.erb | 2 +- .../workflows/page_header_component.rb | 15 +-- app/controllers/workflows_controller.rb | 5 +- app/helpers/workflow_helper.rb | 54 +++++++++++ app/services/workflows/bulk_update_service.rb | 23 +++-- app/views/workflows/_form.html.erb | 16 +++- app/views/workflows/edit.html.erb | 37 ++++---- app/views/workflows/show.html.erb | 8 +- config/locales/en.yml | 5 + .../src/global_styles/content/_index.sass | 1 + .../src/global_styles/content/_tables.sass | 46 ---------- .../content/work_packages/_workflows.sass | 86 +++++++++++++++++ spec/controllers/workflows_controller_spec.rb | 6 +- spec/features/workflows/edit_spec.rb | 92 +++++++++++++++++++ .../bulk_update_service_integration_spec.rb | 36 +++++--- 17 files changed, 443 insertions(+), 103 deletions(-) create mode 100644 app/components/workflows/edit_page_header_component.html.erb create mode 100644 app/components/workflows/edit_page_header_component.rb create mode 100644 app/helpers/workflow_helper.rb create mode 100644 frontend/src/global_styles/content/work_packages/_workflows.sass diff --git a/app/components/workflows/edit_page_header_component.html.erb b/app/components/workflows/edit_page_header_component.html.erb new file mode 100644 index 00000000000..8eb13bec966 --- /dev/null +++ b/app/components/workflows/edit_page_header_component.html.erb @@ -0,0 +1,63 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + render Primer::OpenProject::PageHeader.new(classes: "workflows-page-header") do |header| + header.with_title { title } + header.with_breadcrumbs(breadcrumb_items) + + header.with_action_button( + tag: :a, + mobile_icon: :copy, + mobile_label: t(:button_copy), + size: :medium, + href: copy_workflows_path, + aria: { label: I18n.t(:button_copy) }, + title: I18n.t(:button_copy) + ) do |button| + button.with_leading_visual_icon(icon: :copy) + t(:button_copy) + end + + header.with_action_button( + tag: :a, + mobile_icon: :info, + mobile_label: t(:label_workflow_summary), + size: :medium, + href: workflows_path, + aria: { label: I18n.t(:label_workflow_summary) }, + title: I18n.t(:label_workflow_summary) + ) do |button| + button.with_leading_visual_icon(icon: :info) + t(:label_workflow_summary) + end + + helpers.render_tab_header_nav(header, @tabs) + end +%> diff --git a/app/components/workflows/edit_page_header_component.rb b/app/components/workflows/edit_page_header_component.rb new file mode 100644 index 00000000000..da8af2c35e4 --- /dev/null +++ b/app/components/workflows/edit_page_header_component.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +# -- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +# ++ + +module Workflows + class EditPageHeaderComponent < ApplicationComponent + include OpPrimer::ComponentHelpers + include ApplicationHelper + + def initialize(tabs:) + super + @tabs = tabs + end + + def breadcrumb_items + [{ href: admin_index_path, text: t("label_administration") }, + { href: admin_settings_work_packages_general_path, text: t(:label_work_package_plural) }, + title] + end + + def title + Workflow.model_name.human + end + end +end diff --git a/app/components/workflows/page_header_component.html.erb b/app/components/workflows/page_header_component.html.erb index 0c989933946..ca339b28049 100644 --- a/app/components/workflows/page_header_component.html.erb +++ b/app/components/workflows/page_header_component.html.erb @@ -28,7 +28,7 @@ See COPYRIGHT and LICENSE files for more details. ++#%> <%= - render Primer::OpenProject::PageHeader.new do |header| + render Primer::OpenProject::PageHeader.new(classes: "workflows-page-header") do |header| header.with_title { title } header.with_breadcrumbs(breadcrumb_items) diff --git a/app/components/workflows/page_header_component.rb b/app/components/workflows/page_header_component.rb index 0df058b70a1..83551dc6865 100644 --- a/app/components/workflows/page_header_component.rb +++ b/app/components/workflows/page_header_component.rb @@ -39,15 +39,10 @@ module Workflows end def breadcrumb_items - base_items = [{ href: admin_index_path, text: t("label_administration") }, - { href: admin_settings_work_packages_general_path, text: t(:label_work_package_plural) }, - title] - - if @state == :edit - base_items - else - base_items.insert(2, { href: edit_workflows_path, text: t(:label_workflow) }) - end + [{ href: admin_index_path, text: t("label_administration") }, + { href: admin_settings_work_packages_general_path, text: t(:label_work_package_plural) }, + { href: edit_workflows_path, text: t(:label_workflow) }, + title] end def title @@ -56,8 +51,6 @@ module Workflows t(:label_workflow_summary) when :copy t(:label_workflow_copy) - when :edit - Workflow.model_name.human else t(:label_workflow_plural) end diff --git a/app/controllers/workflows_controller.rb b/app/controllers/workflows_controller.rb index 3c04a2957c5..178900db361 100644 --- a/app/controllers/workflows_controller.rb +++ b/app/controllers/workflows_controller.rb @@ -57,13 +57,14 @@ class WorkflowsController < ApplicationController end def update + tab = params[:tab] || "always" call = Workflows::BulkUpdateService - .new(role: @role, type: @type) + .new(role: @role, type: @type, tab:) .call(permitted_status_params) if call.success? flash[:notice] = I18n.t(:notice_successful_update) - redirect_to action: "edit", role_id: @role, type_id: @type + redirect_to action: "edit", role_id: @role, type_id: @type, tab: end end diff --git a/app/helpers/workflow_helper.rb b/app/helpers/workflow_helper.rb new file mode 100644 index 00000000000..98de547190e --- /dev/null +++ b/app/helpers/workflow_helper.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module WorkflowHelper + def workflow_tabs + [ + { + name: "always", + partial: "workflows/form", + path: edit_workflows_path({ tab: :always }.merge(params.permit(:role_id, :type_id, :used_statuses_only))), + label: I18n.t(:"admin.workflows.tabs.default_transitions") + }, + { + name: "author", + partial: "workflows/form", + path: edit_workflows_path({ tab: :author }.merge(params.permit(:role_id, :type_id, :used_statuses_only))), + label: I18n.t(:"admin.workflows.tabs.user_author") + }, + { + name: "assignee", + partial: "workflows/form", + path: edit_workflows_path({ tab: :assignee }.merge(params.permit(:role_id, :type_id, :used_statuses_only))), + label: I18n.t(:"admin.workflows.tabs.user_assignee") + } + ] + end +end diff --git a/app/services/workflows/bulk_update_service.rb b/app/services/workflows/bulk_update_service.rb index 6e111012b1b..81d6121a7d1 100644 --- a/app/services/workflows/bulk_update_service.rb +++ b/app/services/workflows/bulk_update_service.rb @@ -29,9 +29,10 @@ #++ class Workflows::BulkUpdateService < BaseServices::Update - def initialize(role:, type:) + def initialize(role:, type:, tab:) @role = role @type = type + @tab = tab end def call(status_transitions) @@ -64,8 +65,8 @@ class Workflows::BulkUpdateService < BaseServices::Update role:, old_status: status_map[status_id.to_i], new_status: status_map[new_status_id.to_i], - author: options_include(options, "author"), - assignee: options_include(options, "assignee")) + author: author?, + assignee: assignee?) end end @@ -73,7 +74,13 @@ class Workflows::BulkUpdateService < BaseServices::Update end def delete_current - Workflow.where(role_id: role.id, type_id: type.id).delete_all + if author? + Workflow.where(role_id: role.id, type_id: type.id, author: true).delete_all + elsif assignee? + Workflow.where(role_id: role.id, type_id: type.id, assignee: true).delete_all + else + Workflow.where(role_id: role.id, type_id: type.id, assignee: false, author: false).delete_all + end end def bulk_insert(workflows) @@ -89,7 +96,11 @@ class Workflows::BulkUpdateService < BaseServices::Update @status_map ||= Status.all.group_by(&:id).transform_values(&:first) end - def options_include(options, string) - options.is_a?(Array) && options.include?(string) && !options.include?("always") + def author? + @tab == "author" + end + + def assignee? + @tab == "assignee" end end diff --git a/app/views/workflows/_form.html.erb b/app/views/workflows/_form.html.erb index 007d07da4a5..07b5c7ae66d 100644 --- a/app/views/workflows/_form.html.erb +++ b/app/views/workflows/_form.html.erb @@ -26,10 +26,22 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. See COPYRIGHT and LICENSE files for more details. ++#%> + +<% name = tab[:name] %> +<% workflows = @workflows[name] %> + +<%= + if name == "assignee" + render(Primer::OpenProject::Heading.new(tag: :h3, my: 3)) { t(:label_additional_workflow_transitions_for_assignee) } + elsif name == "author" + render(Primer::OpenProject::Heading.new(tag: :h3, my: 3)) { t(:label_additional_workflow_transitions_for_author) } + end +%> +
- +
@@ -39,7 +51,7 @@ See COPYRIGHT and LICENSE files for more details. <%= t(:label_new_statuses_allowed) %> - (<%= check_all_links "workflow_form_" + name %>) + (<%= check_all_links "workflow_form_#{name}" %>) diff --git a/app/views/workflows/edit.html.erb b/app/views/workflows/edit.html.erb index f04cce47057..8dfaa9e0f63 100644 --- a/app/views/workflows/edit.html.erb +++ b/app/views/workflows/edit.html.erb @@ -28,9 +28,11 @@ See COPYRIGHT and LICENSE files for more details. ++#%> <% html_title t(:label_administration), t(:label_workflow_plural) -%> -<%= render Workflows::PageHeaderComponent.new(state: :edit) %> +<%= render Workflows::EditPageHeaderComponent.new(tabs: workflow_tabs) %> <%= styled_form_tag({}, method: "get") do %> + <%= hidden_field_tag "tab", params[:tab] || "always" %> +
<%= t(:text_workflow_edit) %>
    @@ -57,31 +59,32 @@ See COPYRIGHT and LICENSE files for more details.
  • - <%= submit_tag t(:button_edit), name: nil, accesskey: accesskey(:edit), class: "button -small -primary" %> + <%= render( + Primer::Beta::Button.new( + scheme: :primary, + type: :submit, + name: nil, + accesskey: accesskey(:edit), + size: :small + ) + ) { t(:button_edit) } %>
  • <% end %> + <% if @type && @role && @statuses.any? %> <%= form_tag({ action: :update }, id: "workflow_form", method: :patch) do %> <%= hidden_field_tag "type_id", @type.id %> <%= hidden_field_tag "role_id", @role.id %> + <%= hidden_field_tag "tab", params[:tab] || "always" %> - <%= render partial: "form", - locals: { name: "always", workflows: @workflows["always"] } %> + <%= render_tabs workflow_tabs %> - <%= augmented_collapsible_section initiallyExpanded: @workflows["author"].present?, - title: t(:label_additional_workflow_transitions_for_author) do %> - <%= render partial: "form", locals: { name: "author", workflows: @workflows["author"] } %> - <% end %> - - <%= augmented_collapsible_section initiallyExpanded: @workflows["assignee"].present?, - title: t(:label_additional_workflow_transitions_for_assignee) do %> - <%= render partial: "form", locals: { name: "assignee", workflows: @workflows["assignee"] } %> - <% end %> - - <%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> + <%= + render Primer::Beta::Button.new(scheme: :primary, type: :submit, id: "work-flow-save-button") do + t(:button_save) + end + %> <% end %> <% end %> - -<% html_title(Workflow.model_name.human) -%> diff --git a/app/views/workflows/show.html.erb b/app/views/workflows/show.html.erb index f852a27ba13..95493e1dddc 100644 --- a/app/views/workflows/show.html.erb +++ b/app/views/workflows/show.html.erb @@ -31,10 +31,10 @@ See COPYRIGHT and LICENSE files for more details. <%= render Workflows::PageHeaderComponent.new(state: :show) %> <% if @workflow_counts.any? %> -
    +
    - +
    @@ -44,7 +44,7 @@ See COPYRIGHT and LICENSE files for more details. - + <% @workflow_counts.first.last.each do |role, count| %> @@ -74,7 +74,7 @@ See COPYRIGHT and LICENSE files for more details. - + <% else %> <%= no_results_box %> <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 2ff39354e4a..2298859339b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -171,6 +171,11 @@ en: expired: "Expired on %{date}" revoked: "Revoked on %{date}" title: "Access token table" + workflows: + tabs: + default_transitions: "Default transitions" + user_author: "User is author" + user_assignee: "User is assignee" authentication: login_and_registration: "Login and registration" diff --git a/frontend/src/global_styles/content/_index.sass b/frontend/src/global_styles/content/_index.sass index 3d6c4d37060..1a63d7ddf8f 100644 --- a/frontend/src/global_styles/content/_index.sass +++ b/frontend/src/global_styles/content/_index.sass @@ -84,3 +84,4 @@ @import user-content/index @import bim/index @import reporting/index +@import "work_packages/workflows" diff --git a/frontend/src/global_styles/content/_tables.sass b/frontend/src/global_styles/content/_tables.sass index a151b2402d4..f2816daae31 100644 --- a/frontend/src/global_styles/content/_tables.sass +++ b/frontend/src/global_styles/content/_tables.sass @@ -55,52 +55,6 @@ table font-style: normal font-weight: var(--base-text-weight-bold) background-color: #EEEEEE - -#workflow_form - .generic-table--results-container - position: relative - - .workflow-table.generic-table - // Let space for the turned header - margin-left: 30px - width: calc(100% - 30px) - - .workflow-table--current-status - font-weight: var(--base-text-weight-bold) - text-transform: uppercase - font-size: 0.875rem - - tbody - span.workflow-table--turned-header - white-space: nowrap - transform: rotate(270deg) - position: absolute - top: 235px - left: 0px - transform-origin: 0 0 - text-transform: uppercase - font-weight: var(--base-text-weight-bold) - max-width: 220px - @include text-shortener - - thead - th - padding: 0 6px - .workflow-table--header - text-align: right - display: flex - span - flex-basis: 50% - .workflow-table--check-all - font-size: 12px - font-style: italic - text-transform: none - a:hover - text-decoration: underline - - .generic-table--sort-header-outer:hover - background: none - tr div.expander cursor: pointer diff --git a/frontend/src/global_styles/content/work_packages/_workflows.sass b/frontend/src/global_styles/content/work_packages/_workflows.sass new file mode 100644 index 00000000000..1ca0b121457 --- /dev/null +++ b/frontend/src/global_styles/content/work_packages/_workflows.sass @@ -0,0 +1,86 @@ +.controller-workflows + #content-body + display: grid + grid-area: auto + padding-top: 0 + overflow-x: scroll + + .workflows-page-header + padding-top: 1rem + +#workflow_form + .generic-table--results-container + position: relative + overflow: visible + + .generic-table--container + overflow: visible + + .workflow-table.generic-table + // Let space for the turned header + margin-left: 30px + width: calc(100% - 30px) + + .workflow-table--current-status + font-weight: var(--base-text-weight-bold) + text-transform: uppercase + font-size: 0.875rem + + td:first-child:not(:has(.workflow-table--turned-header)), + th:first-child + position: sticky + left: -1rem + background: var(--body-background) + z-index: 2 + box-shadow: 0 2px 4px rgba(0,0,0,0.08) + padding-top: 16px + + th:first-child + z-index: 3 + + tbody + span.workflow-table--turned-header + white-space: nowrap + transform: rotate(270deg) + position: absolute + top: 235px + left: 0px + transform-origin: 0 0 + text-transform: uppercase + font-weight: var(--base-text-weight-bold) + max-width: 220px + @include text-shortener + + thead + th + padding: 0 6px + .workflow-table--header + text-align: right + display: flex + span + flex-basis: 50% + .workflow-table--check-all + font-size: 12px + font-style: italic + text-transform: none + a:hover + text-decoration: underline + + .generic-table--sort-header-outer:hover + background: none + +#workflow_summary + td:first-child:not(:has(.workflow-table--turned-header)), + th:first-child + position: sticky + left: -1rem + background: var(--body-background) + z-index: 2 + box-shadow: 0 2px 4px rgba(0,0,0,0.08) + + th:first-child + z-index: 3 + +#work-flow-save-button + position: sticky + left: 0 diff --git a/spec/controllers/workflows_controller_spec.rb b/spec/controllers/workflows_controller_spec.rb index 2480143c9bf..b408ccacaea 100644 --- a/spec/controllers/workflows_controller_spec.rb +++ b/spec/controllers/workflows_controller_spec.rb @@ -253,8 +253,8 @@ RSpec.describe WorkflowsController do allow(Workflows::BulkUpdateService) .to receive(:new) - .with(role:, type:) - .and_return(service) + .with(role: role, type: type, tab: "always") + .and_return(service) service end @@ -279,7 +279,7 @@ RSpec.describe WorkflowsController do it "redirects to edit" do expect(response) - .to redirect_to edit_workflows_path(role_id: role.id, type_id: type.id) + .to redirect_to edit_workflows_path(role_id: role.id, type_id: type.id, tab: "always") end end diff --git a/spec/features/workflows/edit_spec.rb b/spec/features/workflows/edit_spec.rb index 8bb7d660611..9c32a20f34a 100644 --- a/spec/features/workflows/edit_spec.rb +++ b/spec/features/workflows/edit_spec.rb @@ -77,6 +77,98 @@ RSpec.describe "Workflow edit" do .to have_field "status_#{statuses[2].id}_#{statuses[0].id}_", checked: false expect(page) .to have_field "status_#{statuses[2].id}_#{statuses[1].id}_", checked: false + + expect(Workflow.where(type_id: type.id, role_id: role.id).count).to be 2 + + w = Workflow.where(role_id: role.id, type_id: type.id, old_status_id: statuses[0].id, new_status_id: statuses[1].id).first + assert !w.author + assert !w.assignee + + w = Workflow.where(role_id: role.id, type_id: type.id, old_status_id: statuses[1].id, new_status_id: statuses[2].id).first + assert !w.author + assert !w.assignee + end + end + + it "allows editing the workflow when the user is author" do + click_link "User is author" + click_button "Edit" + + within "#workflow_form_author" do + check "status_#{statuses[2].id}_#{statuses[1].id}_" + end + + click_button "Save" + + expect_flash(message: "Successful update.") + + within "#workflow_form_author" do + expect(page) + .to have_field "status_#{statuses[2].id}_#{statuses[1].id}_", checked: true + + expect(page) + .to have_field "status_#{statuses[0].id}_#{statuses[1].id}_", checked: false + expect(page) + .to have_field "status_#{statuses[1].id}_#{statuses[2].id}_", checked: false + expect(page) + .to have_field "status_#{statuses[0].id}_#{statuses[2].id}_", checked: false + expect(page) + .to have_field "status_#{statuses[1].id}_#{statuses[0].id}_", checked: false + expect(page) + .to have_field "status_#{statuses[2].id}_#{statuses[0].id}_", checked: false + + expect(Workflow.where(type_id: type.id, role_id: role.id).count).to be 2 + + # the newly added Workflow + w = Workflow.where(role_id: role.id, type_id: type.id, old_status_id: statuses[2].id, new_status_id: statuses[1].id).first + assert w.author + assert !w.assignee + + # The already existing Workflow + w = Workflow.where(role_id: role.id, type_id: type.id, old_status_id: statuses[0].id, new_status_id: statuses[1].id).first + assert !w.author + assert !w.assignee + end + end + + it "allows editing the workflow when the user is assignee" do + click_link "User is assignee" + click_button "Edit" + + within "#workflow_form_assignee" do + check "status_#{statuses[2].id}_#{statuses[0].id}_" + end + + click_button "Save" + + expect_flash(message: "Successful update.") + + within "#workflow_form_assignee" do + expect(page) + .to have_field "status_#{statuses[2].id}_#{statuses[0].id}_", checked: true + + expect(page) + .to have_field "status_#{statuses[0].id}_#{statuses[1].id}_", checked: false + expect(page) + .to have_field "status_#{statuses[1].id}_#{statuses[2].id}_", checked: false + expect(page) + .to have_field "status_#{statuses[0].id}_#{statuses[2].id}_", checked: false + expect(page) + .to have_field "status_#{statuses[1].id}_#{statuses[0].id}_", checked: false + expect(page) + .to have_field "status_#{statuses[2].id}_#{statuses[1].id}_", checked: false + + expect(Workflow.where(type_id: type.id, role_id: role.id).count).to be 2 + + # the newly added Workflow + w = Workflow.where(role_id: role.id, type_id: type.id, old_status_id: statuses[2].id, new_status_id: statuses[0].id).first + assert !w.author + assert w.assignee + + # The already existing Workflow + w = Workflow.where(role_id: role.id, type_id: type.id, old_status_id: statuses[0].id, new_status_id: statuses[1].id).first + assert !w.author + assert !w.assignee end end end diff --git a/spec/services/workflows/bulk_update_service_integration_spec.rb b/spec/services/workflows/bulk_update_service_integration_spec.rb index d26cfd10157..c5b973425a7 100644 --- a/spec/services/workflows/bulk_update_service_integration_spec.rb +++ b/spec/services/workflows/bulk_update_service_integration_spec.rb @@ -54,7 +54,7 @@ RSpec.describe Workflows::BulkUpdateService, "integration", type: :model do end let(:instance) do - described_class.new(role:, type:) + described_class.new(role:, type:, tab:) end describe "#call" do @@ -64,6 +64,7 @@ RSpec.describe Workflows::BulkUpdateService, "integration", type: :model do end context "with status transitions for everybody" do + let(:tab) { "always" } let(:params) do { status4.id => { status5.id => ["always"] }, @@ -88,11 +89,11 @@ RSpec.describe Workflows::BulkUpdateService, "integration", type: :model do end end - context "with additional transitions" do + context "with additional author transitions" do + let(:tab) { "author" } let(:params) do { - status4.id => { status5.id => ["always"] }, - status3.id => { status1.id => ["author"], status2.id => ["assignee"], status4.id => %w(author assignee) } + status3.id => { status1.id => ["author"] } } end @@ -100,24 +101,36 @@ RSpec.describe Workflows::BulkUpdateService, "integration", type: :model do subject expect(Workflow.where(type_id: type.id, role_id: role.id).count) - .to be 4 + .to be 1 - w = Workflow.where(role_id: role.id, type_id: type.id, old_status_id: status4.id, new_status_id: status5.id).first - assert !w.author - assert !w.assignee w = Workflow.where(role_id: role.id, type_id: type.id, old_status_id: status3.id, new_status_id: status1.id).first assert w.author assert !w.assignee + end + end + + context "with additional assignee transitions" do + let(:tab) { "assignee" } + let(:params) do + { + status3.id => { status2.id => ["assignee"] } + } + end + + it "sets the workflows" do + subject + + expect(Workflow.where(type_id: type.id, role_id: role.id).count) + .to be 1 + w = Workflow.where(role_id: role.id, type_id: type.id, old_status_id: status3.id, new_status_id: status2.id).first assert !w.author assert w.assignee - w = Workflow.where(role_id: role.id, type_id: type.id, old_status_id: status3.id, new_status_id: status4.id).first - assert w.author - assert w.assignee end end context "without transitions" do + let(:tab) { "always" } let(:params) do {} end @@ -135,6 +148,7 @@ RSpec.describe Workflows::BulkUpdateService, "integration", type: :model do end context "with no params" do + let(:tab) { "always" } let(:params) do nil end