mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
rubocop autocorrect
This commit is contained in:
@@ -31,7 +31,8 @@
|
||||
module Admin
|
||||
class AttachmentsSettingsHeaderComponent < ApplicationComponent
|
||||
def initialize(title:, selected:)
|
||||
raise 'selected must 1, 2 or 3' if [1, 2, 3].exclude?(selected)
|
||||
raise "selected must 1, 2 or 3" if [1, 2, 3].exclude?(selected)
|
||||
|
||||
@title = title
|
||||
@selected = selected
|
||||
end
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
#++
|
||||
|
||||
module FrontendAssetHelper
|
||||
CLI_DEFAULT_PROXY = 'http://localhost:4200'.freeze
|
||||
CLI_DEFAULT_PROXY = "http://localhost:4200".freeze
|
||||
|
||||
def self.assets_proxied?
|
||||
ENV['OPENPROJECT_DISABLE_DEV_ASSET_PROXY'].blank? && !Rails.env.production? && cli_proxy.present?
|
||||
ENV["OPENPROJECT_DISABLE_DEV_ASSET_PROXY"].blank? && !Rails.env.production? && cli_proxy.present?
|
||||
end
|
||||
|
||||
def self.cli_proxy
|
||||
ENV.fetch('OPENPROJECT_CLI_PROXY', CLI_DEFAULT_PROXY)
|
||||
ENV.fetch("OPENPROJECT_CLI_PROXY", CLI_DEFAULT_PROXY)
|
||||
end
|
||||
|
||||
##
|
||||
|
||||
@@ -40,7 +40,6 @@ class Project < ApplicationRecord
|
||||
|
||||
include ::Scopes::Scoped
|
||||
|
||||
|
||||
# Maximum length for project identifiers
|
||||
IDENTIFIER_MAX_LENGTH = 100
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ class Setting < ApplicationRecord
|
||||
private
|
||||
|
||||
def accessor_base_name(name)
|
||||
name.to_s.sub(/(_writable\?)|(\?)|=\z/, '')
|
||||
name.to_s.sub(/(_writable\?)|(\?)|=\z/, "")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -337,7 +337,7 @@ class Setting < ApplicationRecord
|
||||
|
||||
if definition.serialized? && value.is_a?(String)
|
||||
deserialize_hash(value)
|
||||
elsif value != ''.freeze && !value.nil?
|
||||
elsif value != "".freeze && !value.nil?
|
||||
read_formatted_setting(value, definition.format)
|
||||
else
|
||||
definition.format == :string ? value : nil
|
||||
|
||||
@@ -77,16 +77,16 @@ module WorkPackage::Exports
|
||||
elsif model_s == "project"
|
||||
resolve_project_match(id || work_package.project.id, type, attribute, user)
|
||||
else
|
||||
msg_macro_error I18n.t('export.macro.model_not_found', model: model_s)
|
||||
msg_macro_error I18n.t("export.macro.model_not_found", model: model_s)
|
||||
end
|
||||
end
|
||||
|
||||
def self.msg_macro_error(message)
|
||||
msg_inline I18n.t('export.macro.error', message:)
|
||||
msg_inline I18n.t("export.macro.error", message:)
|
||||
end
|
||||
|
||||
def self.msg_macro_error_rich_text
|
||||
msg_inline I18n.t('export.macro.rich_text_unsupported')
|
||||
msg_inline I18n.t("export.macro.rich_text_unsupported")
|
||||
end
|
||||
|
||||
def self.msg_inline(message)
|
||||
@@ -109,11 +109,11 @@ module WorkPackage::Exports
|
||||
|
||||
def self.resolve_work_package_match(id, type, attribute, user)
|
||||
return resolve_label_work_package(attribute) if type == "label"
|
||||
return msg_macro_error(I18n.t('export.macro.model_not_found', model: type)) unless type == "value"
|
||||
return msg_macro_error(I18n.t("export.macro.model_not_found", model: type)) unless type == "value"
|
||||
|
||||
work_package = WorkPackage.visible(user).find_by(id:)
|
||||
if work_package.nil?
|
||||
return msg_macro_error(I18n.t('export.macro.resource_not_found', resource: "#{WorkPackage.name} #{id}"))
|
||||
return msg_macro_error(I18n.t("export.macro.resource_not_found", resource: "#{WorkPackage.name} #{id}"))
|
||||
end
|
||||
|
||||
resolve_value_work_package(work_package, attribute)
|
||||
|
||||
@@ -99,8 +99,9 @@ module Projects
|
||||
end
|
||||
|
||||
def copy_activated_custom_fields(call)
|
||||
call.result.project_custom_field_ids = source.project_custom_field_ids
|
||||
call.result.project_custom_field_ids = source.project_custom_field_ids
|
||||
end
|
||||
|
||||
def contract_options
|
||||
{ copy_source: source, validate_model: true }
|
||||
end
|
||||
|
||||
@@ -65,7 +65,7 @@ module Users
|
||||
|
||||
def invalidate_tokens
|
||||
::Users::DropTokensService
|
||||
.new(current_user: current_user)
|
||||
.new(current_user:)
|
||||
.call!
|
||||
end
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@ module Users
|
||||
private
|
||||
|
||||
def invalidate_recovery_tokens
|
||||
Token::Recovery.where(user: user).delete_all
|
||||
Token::Recovery.where(user:).delete_all
|
||||
end
|
||||
|
||||
def invalidate_invitation_tokens
|
||||
Token::Invitation.where(user: user).delete_all
|
||||
Token::Invitation.where(user:).delete_all
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -42,7 +42,7 @@ class AddLdapTlsOptions < ActiveRecord::Migration[7.0]
|
||||
# Current LDAP library default is to not verify the certificate
|
||||
MigratingAuthSource.reset_column_information
|
||||
|
||||
ldap_settings = Setting.find_by(name: 'ldap_tls_options')&.value
|
||||
ldap_settings = Setting.find_by(name: "ldap_tls_options")&.value
|
||||
migrate_ldap_settings(ldap_settings)
|
||||
end
|
||||
end
|
||||
@@ -54,7 +54,7 @@ class AddLdapTlsOptions < ActiveRecord::Migration[7.0]
|
||||
return if ldap_settings.blank?
|
||||
|
||||
parsed = Setting.deserialize_hash(ldap_settings)
|
||||
verify_peer = parsed['verify_mode'] == OpenSSL::SSL::VERIFY_PEER
|
||||
verify_peer = parsed["verify_mode"] == OpenSSL::SSL::VERIFY_PEER
|
||||
|
||||
MigratingAuthSource.update_all(verify_peer:)
|
||||
rescue StandardError => e
|
||||
|
||||
@@ -3,6 +3,6 @@ class RenameDelayToLag < ActiveRecord::Migration[7.1]
|
||||
rename_column :relations, :delay, :lag
|
||||
|
||||
# TODO remove after 14.0
|
||||
add_column :relations, :delay, :virtual, type: :integer, as: 'lag', stored: true
|
||||
add_column :relations, :delay, :virtual, type: :integer, as: "lag", stored: true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class RemoveVirtualDelayColumn < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
remove_column :relations, :delay, :virtual, type: :integer, as: 'lag', stored: true
|
||||
remove_column :relations, :delay, :virtual, type: :integer, as: "lag", stored: true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@ module Primer
|
||||
options.reverse_merge!(
|
||||
component: "opce-project-autocompleter",
|
||||
defaultData: false,
|
||||
filters: [{ name: 'active', operator: '=', values: ['t'] }],
|
||||
filters: [{ name: "active", operator: "=", values: ["t"] }]
|
||||
)
|
||||
|
||||
if options[:disabledProjects]
|
||||
|
||||
@@ -42,7 +42,7 @@ module Acts::Journalized
|
||||
get_association_changes(original, changed, *)
|
||||
end
|
||||
|
||||
def association_changes_multiple_attributes(original, changed, association, association_name, key, values)
|
||||
def association_changes_multiple_attributes(original, changed, association, association_name, key, values)
|
||||
list = {}
|
||||
values.each do |value|
|
||||
list.store(value, get_association_changes(original, changed, association, association_name, key, value))
|
||||
@@ -69,7 +69,7 @@ module Acts::Journalized
|
||||
|
||||
def no_nil_to_empty_strings?(normalized_old_data, attribute, new_value)
|
||||
old_value = normalized_old_data[attribute]
|
||||
new_value != old_value && ([new_value, old_value] - ['', nil]).present?
|
||||
new_value != old_value && ([new_value, old_value] - ["", nil]).present?
|
||||
end
|
||||
|
||||
def journaled_attributes(object)
|
||||
@@ -133,7 +133,7 @@ module Acts::Journalized
|
||||
if selected_journals.empty?
|
||||
nil
|
||||
else
|
||||
selected_journals.sort.join(',')
|
||||
selected_journals.sort.join(",")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,14 +45,14 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
require 'journal_changes'
|
||||
require 'journal_formatter'
|
||||
require 'cause_of_change'
|
||||
require "journal_changes"
|
||||
require "journal_formatter"
|
||||
require "cause_of_change"
|
||||
|
||||
module Acts
|
||||
end
|
||||
|
||||
Dir[File.expand_path('acts/journalized/*.rb', __dir__)].sort.each { |f| require f }
|
||||
Dir[File.expand_path("acts/journalized/*.rb", __dir__)].sort.each { |f| require f }
|
||||
|
||||
module Acts
|
||||
module Journalized
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
require_relative 'cause_of_change/base'
|
||||
require_relative 'cause_of_change/no_cause'
|
||||
require_relative "cause_of_change/base"
|
||||
require_relative "cause_of_change/no_cause"
|
||||
|
||||
module CauseOfChange
|
||||
end
|
||||
|
||||
@@ -65,15 +65,14 @@ RSpec.describe "Calendar Widget", :js, :with_cuprite, with_settings: { start_of_
|
||||
overview_page.expect_and_dismiss_toaster message: I18n.t("js.notice_successful_update")
|
||||
end
|
||||
|
||||
it 'shows the meeting' do
|
||||
expect(page).to have_css('.fc-event', text: "Weekly", visible: :all)
|
||||
page.find('.fc-event', text: "Weekly", visible: :all).click
|
||||
it "shows the meeting" do
|
||||
expect(page).to have_css(".fc-event", text: "Weekly", visible: :all)
|
||||
page.find(".fc-event", text: "Weekly", visible: :all).click
|
||||
|
||||
expect(page).to have_current_path /meetings\/#{meeting.id}/
|
||||
end
|
||||
|
||||
it "opens the work package full view when clicking a calendar entry" do
|
||||
|
||||
# Clicking the calendar entry goes to work package full screen
|
||||
page.find(".fc-event-title", text: work_package.subject).click
|
||||
wp_full_view.ensure_page_loaded
|
||||
|
||||
@@ -47,7 +47,7 @@ class Meeting::Title < ApplicationForm
|
||||
scheme: :secondary,
|
||||
label: I18n.t(:button_cancel),
|
||||
tag: :a,
|
||||
data: { 'turbo-stream': true },
|
||||
data: { "turbo-stream": true },
|
||||
href: OpenProject::StaticRouting::StaticUrlHelpers.new.cancel_edit_meeting_path(@meeting)
|
||||
)
|
||||
end
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#++
|
||||
|
||||
class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
|
||||
activity_provider_for type: 'meetings',
|
||||
activity_provider_for type: "meetings",
|
||||
activities: %i[meeting meeting_content],
|
||||
permission: :view_meetings
|
||||
|
||||
@@ -35,7 +35,7 @@ class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
|
||||
case activity
|
||||
when :meeting_content
|
||||
query.join(meetings_table).on(activity_journals_table[:meeting_id].eq(meetings_table[:id]))
|
||||
join_cond = journals_table[:journable_type].eq('MeetingContent')
|
||||
join_cond = journals_table[:journable_type].eq("MeetingContent")
|
||||
query.join(meeting_contents_table).on(journals_table[:journable_id].eq(meeting_contents_table[:id]).and(join_cond))
|
||||
else
|
||||
super
|
||||
@@ -46,17 +46,17 @@ class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
|
||||
case activity
|
||||
when :meeting
|
||||
[
|
||||
activity_journal_projection_statement(:title, 'meeting_title'),
|
||||
activity_journal_projection_statement(:start_time, 'meeting_start_time'),
|
||||
activity_journal_projection_statement(:duration, 'meeting_duration'),
|
||||
activity_journal_projection_statement(:project_id, 'project_id')
|
||||
activity_journal_projection_statement(:title, "meeting_title"),
|
||||
activity_journal_projection_statement(:start_time, "meeting_start_time"),
|
||||
activity_journal_projection_statement(:duration, "meeting_duration"),
|
||||
activity_journal_projection_statement(:project_id, "project_id")
|
||||
]
|
||||
else
|
||||
[
|
||||
projection_statement(meeting_contents_table, :type, 'meeting_content_type'),
|
||||
projection_statement(meetings_table, :id, 'meeting_id'),
|
||||
projection_statement(meetings_table, :title, 'meeting_title'),
|
||||
projection_statement(meetings_table, :project_id, 'project_id')
|
||||
projection_statement(meeting_contents_table, :type, "meeting_content_type"),
|
||||
projection_statement(meetings_table, :id, "meeting_id"),
|
||||
projection_statement(meetings_table, :title, "meeting_title"),
|
||||
projection_statement(meetings_table, :project_id, "project_id")
|
||||
]
|
||||
end
|
||||
end
|
||||
@@ -86,13 +86,13 @@ class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
|
||||
protected
|
||||
|
||||
def event_name(event)
|
||||
case event['event_description']
|
||||
when 'Agenda closed'
|
||||
I18n.t('meeting_agenda_closed', scope: 'events')
|
||||
when 'Agenda opened'
|
||||
I18n.t('meeting_agenda_opened', scope: 'events')
|
||||
when 'Minutes created'
|
||||
I18n.t('meeting_minutes_created', scope: 'events')
|
||||
case event["event_description"]
|
||||
when "Agenda closed"
|
||||
I18n.t("meeting_agenda_closed", scope: "events")
|
||||
when "Agenda opened"
|
||||
I18n.t("meeting_agenda_opened", scope: "events")
|
||||
when "Minutes created"
|
||||
I18n.t("meeting_minutes_created", scope: "events")
|
||||
else
|
||||
super
|
||||
end
|
||||
@@ -101,12 +101,12 @@ class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
|
||||
def event_title(event)
|
||||
case activity
|
||||
when :meeting
|
||||
start_time = if event['meeting_start_time'].is_a?(String)
|
||||
DateTime.parse(event['meeting_start_time'])
|
||||
start_time = if event["meeting_start_time"].is_a?(String)
|
||||
DateTime.parse(event["meeting_start_time"])
|
||||
else
|
||||
event['meeting_start_time']
|
||||
event["meeting_start_time"]
|
||||
end
|
||||
end_time = start_time + event['meeting_duration'].to_f.hours
|
||||
end_time = start_time + event["meeting_duration"].to_f.hours
|
||||
|
||||
fstart_with = format_date start_time
|
||||
fstart_without = format_time start_time, false
|
||||
@@ -121,9 +121,9 @@ class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
|
||||
def event_type(event)
|
||||
case activity
|
||||
when :meeting
|
||||
'meeting'
|
||||
"meeting"
|
||||
else
|
||||
event['meeting_content_type'].include?('Agenda') ? 'meeting-agenda' : 'meeting-minutes'
|
||||
event["meeting_content_type"].include?("Agenda") ? "meeting-agenda" : "meeting-minutes"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -164,6 +164,6 @@ class Activities::MeetingActivityProvider < Activities::BaseActivityProvider
|
||||
end
|
||||
|
||||
def activity_id(event)
|
||||
activity == :meeting ? event['journable_id'] : event['meeting_id']
|
||||
activity == :meeting ? event["journable_id"] : event["meeting_id"]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,7 +48,7 @@ RSpec.describe Meetings::CopyService, "integration", type: :model do
|
||||
expect(copy.start_time).to eq(meeting.start_time + 1.week)
|
||||
end
|
||||
|
||||
context 'when the meeting is closed' do
|
||||
context "when the meeting is closed" do
|
||||
it "reopens the meeting" do
|
||||
meeting.update! state: "closed"
|
||||
expect(service_result).to be_success
|
||||
|
||||
@@ -31,7 +31,7 @@ class Widget::CostTypes < Widget::Base
|
||||
@cost_types = options.delete(:cost_types)
|
||||
@selected_type_id = options.delete(:selected_type_id)
|
||||
|
||||
super(options, &)
|
||||
super
|
||||
end
|
||||
|
||||
def render
|
||||
|
||||
@@ -40,7 +40,7 @@ class Widget::ReportingWidget < ActionView::Base
|
||||
attr_accessor :output_buffer, :controller, :config, :_content_for, :_routes, :subject
|
||||
|
||||
def self.new(subject)
|
||||
super(subject).tap do |o|
|
||||
super.tap do |o|
|
||||
o.subject = subject
|
||||
end
|
||||
end
|
||||
|
||||
@@ -33,7 +33,7 @@ require_module_spec_helper
|
||||
|
||||
# Test if the deletion of a ProjectStorage actually deletes related FileLink
|
||||
# objects.
|
||||
RSpec.describe "Delete ProjectStorage with FileLinks", :js, :with_cuprite, :webmock do
|
||||
RSpec.describe "Delete ProjectStorage with FileLinks", :js, :webmock, :with_cuprite do
|
||||
let(:user) { create(:user) }
|
||||
let(:role) { create(:project_role, permissions: [:manage_files_in_project]) }
|
||||
let(:project) do
|
||||
@@ -69,7 +69,7 @@ RSpec.describe "Delete ProjectStorage with FileLinks", :js, :with_cuprite, :webm
|
||||
visit external_file_storages_project_settings_project_storages_path(project)
|
||||
|
||||
# The list of enabled file storages should now contain Storage 1
|
||||
expect(page).to have_selector('h1', text: 'Files')
|
||||
expect(page).to have_css("h1", text: "Files")
|
||||
expect(page).to have_text("Storage 1")
|
||||
|
||||
# Press Delete icon to remove the storage from the project
|
||||
|
||||
@@ -74,7 +74,7 @@ RSpec.describe "Hide attachments", :js, :with_cuprite do
|
||||
context "if Setting.show_work_package_attachments is false", with_settings: { show_work_package_attachments: false } do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
it 'renders the toggle as off for project with not set deactivate_work_package_attachments' do
|
||||
it "renders the toggle as off for project with not set deactivate_work_package_attachments" do
|
||||
expect(project.deactivate_work_package_attachments).to be_nil
|
||||
|
||||
login_as current_user
|
||||
@@ -89,7 +89,7 @@ RSpec.describe "Hide attachments", :js, :with_cuprite do
|
||||
context "if Setting.show_work_package_attachments is true", with_settings: { show_work_package_attachments: true } do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
it 'renders the toggle as on for project with not set deactivate_work_package_attachments' do
|
||||
it "renders the toggle as on for project with not set deactivate_work_package_attachments" do
|
||||
expect(project.deactivate_work_package_attachments).to be_nil
|
||||
|
||||
login_as current_user
|
||||
|
||||
@@ -38,10 +38,7 @@ require_module_spec_helper
|
||||
# We decrease the notification polling interval because some portions of the JS code rely on something triggering
|
||||
# the Angular change detection. This is usually done by the notification polling, but we don't want to wait
|
||||
RSpec.describe("Activation of storages in projects",
|
||||
:js,
|
||||
:with_cuprite,
|
||||
:webmock,
|
||||
with_settings: { notifications_polling_interval: 1_000 }) do
|
||||
:js, :webmock, :with_cuprite, with_settings: { notifications_polling_interval: 1_000 }) do
|
||||
let(:user) { create(:user) }
|
||||
# The first page is the Project -> Settings -> General page, so we need
|
||||
# to provide the user with the edit_project permission in the role.
|
||||
@@ -112,7 +109,7 @@ RSpec.describe("Activation of storages in projects",
|
||||
expect(page).to have_title("Files")
|
||||
expect(page).to have_current_path external_file_storages_project_settings_project_storages_path(project)
|
||||
expect(page).to have_text(I18n.t("storages.no_results"))
|
||||
page.first(:link, 'New storage').click
|
||||
page.first(:link, "New storage").click
|
||||
|
||||
# Can cancel the creation of a new file storage
|
||||
expect(page).to have_current_path new_project_settings_project_storage_path(project_id: project)
|
||||
@@ -121,7 +118,7 @@ RSpec.describe("Activation of storages in projects",
|
||||
expect(page).to have_current_path external_file_storages_project_settings_project_storages_path(project)
|
||||
|
||||
# Enable one file storage together with a project folder mode
|
||||
page.first(:link, 'New storage').click
|
||||
page.first(:link, "New storage").click
|
||||
expect(page).to have_current_path new_project_settings_project_storage_path(project_id: project)
|
||||
expect(page).to have_text("Add a file storage")
|
||||
expect(page).to have_select("storages_project_storage_storage_id",
|
||||
@@ -149,14 +146,14 @@ RSpec.describe("Activation of storages in projects",
|
||||
page.click_button("Add")
|
||||
|
||||
# The list of enabled file storages should now contain Storage 1
|
||||
expect(page).to have_selector('h1', text: 'Files')
|
||||
expect(page).to have_css("h1", text: "Files")
|
||||
expect(page).to have_text(storage.name)
|
||||
|
||||
# Press Edit icon to change the project folder mode to inactive
|
||||
page.find(".icon.icon-edit").click
|
||||
expect(page).to have_current_path edit_project_settings_project_storage_path(project_id: project,
|
||||
id: Storages::ProjectStorage.last,
|
||||
storages_project_storage: {project_folder_mode: "manual"})
|
||||
storages_project_storage: { project_folder_mode: "manual" })
|
||||
expect(page).to have_text("Edit the file storage to this project")
|
||||
expect(page).to have_no_select("storages_project_storage_storage_id")
|
||||
expect(page).to have_text(storage.name)
|
||||
@@ -169,14 +166,14 @@ RSpec.describe("Activation of storages in projects",
|
||||
page.click_button("Save")
|
||||
|
||||
# The list of enabled file storages should still contain Storage 1
|
||||
expect(page).to have_selector('h1', text: 'Files')
|
||||
expect(page).to have_css("h1", text: "Files")
|
||||
expect(page).to have_text(storage.name)
|
||||
|
||||
# Click Edit icon again but cancel the edit
|
||||
page.find(".icon.icon-edit").click
|
||||
expect(page).to have_current_path edit_project_settings_project_storage_path(project_id: project,
|
||||
id: Storages::ProjectStorage.last,
|
||||
storages_project_storage: {project_folder_mode: "inactive"})
|
||||
storages_project_storage: { project_folder_mode: "inactive" })
|
||||
expect(page).to have_text("Edit the file storage to this project")
|
||||
page.click_link("Cancel")
|
||||
expect(page).to have_current_path external_file_storages_project_settings_project_storages_path(project)
|
||||
@@ -237,7 +234,7 @@ RSpec.describe("Activation of storages in projects",
|
||||
it "excludes storages that are not configured correctly" do
|
||||
visit external_file_storages_project_settings_project_storages_path(project)
|
||||
|
||||
page.first(:link, 'New storage').click
|
||||
page.first(:link, "New storage").click
|
||||
|
||||
aggregate_failures "select field options" do
|
||||
expect(page).to have_select("storages_project_storage_storage_id",
|
||||
|
||||
@@ -75,7 +75,7 @@ RSpec.describe "OAuth Access Grant Nudge upon adding a storage to a project",
|
||||
expect(page).to have_checked_field("New folder with automatically managed permissions")
|
||||
click_on("Add")
|
||||
|
||||
expect(page).to have_selector('h1', text: 'Files')
|
||||
expect(page).to have_css("h1", text: "Files")
|
||||
expect(page).to have_text(storage.name)
|
||||
|
||||
within_test_selector("oauth-access-grant-nudge-modal") do
|
||||
|
||||
@@ -79,7 +79,7 @@ RSpec.describe Members::RoleFormComponent, type: :component do
|
||||
|
||||
expect(form).to have_css "input[name='member[user_ids][]']", visible: :hidden # rubocop:disable Capybara/SpecificMatcher
|
||||
|
||||
expect(form.first("input[name='member[user_ids][]']", visible: :hidden).value).to eq '42'
|
||||
expect(form.first("input[name='member[user_ids][]']", visible: :hidden).value).to eq "42"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe ApplicationController, "enforcement of authorization" do # rubocop:disable RSpec/FilePath, RSpec/SpecFilePathFormat
|
||||
RSpec.describe ApplicationController, "enforcement of authorization" do # rubocop:disable RSpec/RSpec/SpecFilePathFormat
|
||||
shared_let(:user) { create(:user) }
|
||||
|
||||
controller_setup = Module.new do
|
||||
|
||||
@@ -53,7 +53,7 @@ RSpec.describe "Link custom fields edit", :js, :with_cuprite do
|
||||
# Expect field to be created
|
||||
cf = CustomField.last
|
||||
expect(cf.name).to eq("My Link CF")
|
||||
expect(cf.field_format).to eq 'link'
|
||||
expect(cf.field_format).to eq "link"
|
||||
|
||||
# Edit again
|
||||
find("a", text: "My Link CF").click
|
||||
|
||||
@@ -26,29 +26,29 @@
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
require 'spec_helper'
|
||||
require_relative 'shared_context'
|
||||
require "spec_helper"
|
||||
require_relative "shared_context"
|
||||
|
||||
RSpec.describe 'List project custom fields', :js do
|
||||
include_context 'with seeded project custom fields'
|
||||
RSpec.describe "List project custom fields", :js do
|
||||
include_context "with seeded project custom fields"
|
||||
|
||||
context 'with unsufficient permissions' do
|
||||
it 'is not accessible' do
|
||||
context "with unsufficient permissions" do
|
||||
it "is not accessible" do
|
||||
login_as(non_admin)
|
||||
visit admin_settings_project_custom_fields_path
|
||||
|
||||
expect(page).to have_text('You are not authorized to access this page.')
|
||||
expect(page).to have_text("You are not authorized to access this page.")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with sufficient permissions' do
|
||||
context "with sufficient permissions" do
|
||||
before do
|
||||
login_as(admin)
|
||||
visit admin_settings_project_custom_fields_path
|
||||
end
|
||||
|
||||
it 'shows all sections in the correct order and allows reordering via menu or drag and drop' do
|
||||
containers = page.all('.op-project-custom-field-section-container')
|
||||
it "shows all sections in the correct order and allows reordering via menu or drag and drop" do
|
||||
containers = page.all(".op-project-custom-field-section-container")
|
||||
|
||||
expect(containers[0].text).to include(section_for_input_fields.name)
|
||||
expect(containers[1].text).to include(section_for_select_fields.name)
|
||||
@@ -58,7 +58,7 @@ RSpec.describe 'List project custom fields', :js do
|
||||
|
||||
visit admin_settings_project_custom_fields_path
|
||||
|
||||
containers = page.all('.op-project-custom-field-section-container')
|
||||
containers = page.all(".op-project-custom-field-section-container")
|
||||
|
||||
expect(containers[0].text).to include(section_for_input_fields.name)
|
||||
expect(containers[1].text).to include(section_for_multi_select_fields.name)
|
||||
@@ -67,9 +67,9 @@ RSpec.describe 'List project custom fields', :js do
|
||||
# TODO: Add drag and drop test
|
||||
end
|
||||
|
||||
it 'allows to delete a section only if no project custom fields are assigned to it' do
|
||||
it "allows to delete a section only if no project custom fields are assigned to it" do
|
||||
within_project_custom_field_section_menu(section_for_multi_select_fields) do
|
||||
expect(page).to have_css("button[aria-disabled='true']", text: 'Delete')
|
||||
expect(page).to have_css("button[aria-disabled='true']", text: "Delete")
|
||||
end
|
||||
|
||||
multi_list_project_custom_field.destroy
|
||||
@@ -79,11 +79,11 @@ RSpec.describe 'List project custom fields', :js do
|
||||
visit admin_settings_project_custom_fields_path
|
||||
|
||||
within_project_custom_field_section_menu(section_for_multi_select_fields) do
|
||||
expect(page).to have_no_css("button[aria-disabled='true']", text: 'Delete')
|
||||
expect(page).to have_button('Delete')
|
||||
expect(page).to have_no_css("button[aria-disabled='true']", text: "Delete")
|
||||
expect(page).to have_button("Delete")
|
||||
|
||||
accept_confirm do
|
||||
click_on('Delete')
|
||||
click_on("Delete")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -91,42 +91,42 @@ RSpec.describe 'List project custom fields', :js do
|
||||
.to have_no_css("[data-test-selector='project-custom-field-section-container-#{section_for_multi_select_fields.id}']")
|
||||
end
|
||||
|
||||
it 'allows to edit a section' do
|
||||
it "allows to edit a section" do
|
||||
within_project_custom_field_section_menu(section_for_input_fields) do
|
||||
click_on('Edit title')
|
||||
click_on("Edit title")
|
||||
end
|
||||
|
||||
fill_in('project_custom_field_section_name', with: 'Updated section name')
|
||||
fill_in("project_custom_field_section_name", with: "Updated section name")
|
||||
|
||||
click_on('Save')
|
||||
click_on("Save")
|
||||
|
||||
expect(page).to have_no_text(section_for_input_fields.name)
|
||||
expect(page).to have_text('Updated section name')
|
||||
expect(page).to have_text("Updated section name")
|
||||
end
|
||||
|
||||
it 'allows to create a new section' do
|
||||
within '#settings-project-custom-fields-header-component' do
|
||||
click_on('dialog-show-project-custom-field-section-dialog')
|
||||
it "allows to create a new section" do
|
||||
within "#settings-project-custom-fields-header-component" do
|
||||
click_on("dialog-show-project-custom-field-section-dialog")
|
||||
end
|
||||
|
||||
fill_in('project_custom_field_section_name', with: 'New section name')
|
||||
fill_in("project_custom_field_section_name", with: "New section name")
|
||||
|
||||
click_on('Save')
|
||||
click_on("Save")
|
||||
|
||||
expect(page).to have_text('New section name')
|
||||
expect(page).to have_text("New section name")
|
||||
|
||||
containers = page.all('.op-project-custom-field-section-container')
|
||||
containers = page.all(".op-project-custom-field-section-container")
|
||||
|
||||
expect(containers[0].text).to include('New section name')
|
||||
expect(containers[0].text).to include("New section name")
|
||||
expect(containers[1].text).to include(section_for_input_fields.name)
|
||||
expect(containers[2].text).to include(section_for_select_fields.name)
|
||||
expect(containers[3].text).to include(section_for_multi_select_fields.name)
|
||||
end
|
||||
|
||||
describe 'managing project custom fields' do
|
||||
it 'shows all custom fields in the correct order within their section and allows reordering via menu or drag and drop' do
|
||||
describe "managing project custom fields" do
|
||||
it "shows all custom fields in the correct order within their section and allows reordering via menu or drag and drop" do
|
||||
within_project_custom_field_section_container(section_for_input_fields) do
|
||||
containers = page.all('.op-project-custom-field-container')
|
||||
containers = page.all(".op-project-custom-field-container")
|
||||
|
||||
expect(containers[0].text).to include(boolean_project_custom_field.name)
|
||||
expect(containers[1].text).to include(string_project_custom_field.name)
|
||||
@@ -137,7 +137,7 @@ RSpec.describe 'List project custom fields', :js do
|
||||
end
|
||||
|
||||
within_project_custom_field_section_container(section_for_select_fields) do
|
||||
containers = page.all('.op-project-custom-field-container')
|
||||
containers = page.all(".op-project-custom-field-container")
|
||||
|
||||
expect(containers[0].text).to include(list_project_custom_field.name)
|
||||
expect(containers[1].text).to include(version_project_custom_field.name)
|
||||
@@ -145,7 +145,7 @@ RSpec.describe 'List project custom fields', :js do
|
||||
end
|
||||
|
||||
within_project_custom_field_section_container(section_for_multi_select_fields) do
|
||||
containers = page.all('.op-project-custom-field-container')
|
||||
containers = page.all(".op-project-custom-field-container")
|
||||
|
||||
expect(containers[0].text).to include(multi_list_project_custom_field.name)
|
||||
expect(containers[1].text).to include(multi_version_project_custom_field.name)
|
||||
@@ -157,7 +157,7 @@ RSpec.describe 'List project custom fields', :js do
|
||||
visit admin_settings_project_custom_fields_path
|
||||
|
||||
within_project_custom_field_section_container(section_for_multi_select_fields) do
|
||||
containers = page.all('.op-project-custom-field-container')
|
||||
containers = page.all(".op-project-custom-field-container")
|
||||
|
||||
expect(containers[0].text).to include(multi_list_project_custom_field.name)
|
||||
expect(containers[1].text).to include(multi_user_project_custom_field.name)
|
||||
@@ -167,9 +167,9 @@ RSpec.describe 'List project custom fields', :js do
|
||||
# TODO: Add drag and drop test
|
||||
end
|
||||
|
||||
it 'shows the number of projects using a custom field' do
|
||||
it "shows the number of projects using a custom field" do
|
||||
within_project_custom_field_container(boolean_project_custom_field) do
|
||||
expect(page).to have_text('0 Projects')
|
||||
expect(page).to have_text("0 Projects")
|
||||
end
|
||||
|
||||
project = create(:project)
|
||||
@@ -178,29 +178,29 @@ RSpec.describe 'List project custom fields', :js do
|
||||
visit admin_settings_project_custom_fields_path
|
||||
|
||||
within_project_custom_field_container(boolean_project_custom_field) do
|
||||
expect(page).to have_text('1 Project')
|
||||
expect(page).to have_text("1 Project")
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows to delete a custom field' do
|
||||
it "allows to delete a custom field" do
|
||||
within_project_custom_field_menu(boolean_project_custom_field) do
|
||||
accept_confirm do
|
||||
click_on('Delete')
|
||||
click_on("Delete")
|
||||
end
|
||||
end
|
||||
|
||||
expect(page).to have_no_css("[data-test-selector='project-custom-field-container-#{boolean_project_custom_field.id}']")
|
||||
end
|
||||
|
||||
it 'redirects to the custom field edit page via menu item' do
|
||||
it "redirects to the custom field edit page via menu item" do
|
||||
within_project_custom_field_menu(boolean_project_custom_field) do
|
||||
click_on('Edit')
|
||||
click_on("Edit")
|
||||
end
|
||||
|
||||
expect(page).to have_current_path(edit_admin_settings_project_custom_field_path(boolean_project_custom_field))
|
||||
end
|
||||
|
||||
it 'redirects to the custom field edit page via click on the name of the custom field' do
|
||||
it "redirects to the custom field edit page via click on the name of the custom field" do
|
||||
within_project_custom_field_container(boolean_project_custom_field) do
|
||||
click_on(boolean_project_custom_field.name)
|
||||
end
|
||||
@@ -208,13 +208,13 @@ RSpec.describe 'List project custom fields', :js do
|
||||
expect(page).to have_current_path(edit_admin_settings_project_custom_field_path(boolean_project_custom_field))
|
||||
end
|
||||
|
||||
it 'redirects to the custom field new page via header menu button' do
|
||||
it "redirects to the custom field new page via header menu button" do
|
||||
page.find("[data-test-selector='new-project-custom-field-button']").click
|
||||
|
||||
expect(page).to have_current_path(new_admin_settings_project_custom_field_path(type: 'ProjectCustomField'))
|
||||
expect(page).to have_current_path(new_admin_settings_project_custom_field_path(type: "ProjectCustomField"))
|
||||
end
|
||||
|
||||
it 'redirects to the custom field new page via button in empty sections' do
|
||||
it "redirects to the custom field new page via button in empty sections" do
|
||||
within_project_custom_field_section_container(section_for_multi_select_fields) do
|
||||
expect(page).to have_no_css("[data-test-selector='new-project-custom-field-button']")
|
||||
end
|
||||
@@ -230,7 +230,7 @@ RSpec.describe 'List project custom fields', :js do
|
||||
end
|
||||
|
||||
expect(page).to have_current_path(new_admin_settings_project_custom_field_path(
|
||||
type: 'ProjectCustomField',
|
||||
type: "ProjectCustomField",
|
||||
custom_field_section_id: section_for_multi_select_fields.id
|
||||
))
|
||||
end
|
||||
@@ -246,7 +246,7 @@ RSpec.describe 'List project custom fields', :js do
|
||||
def within_project_custom_field_section_menu(section, &block)
|
||||
within_project_custom_field_section_container(section) do
|
||||
page.find("[data-test-selector='project-custom-field-section-action-menu']").click
|
||||
within('anchored-position', &block)
|
||||
within("anchored-position", &block)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -264,7 +264,7 @@ RSpec.describe 'List project custom fields', :js do
|
||||
def within_project_custom_field_menu(section, &block)
|
||||
within_project_custom_field_container(section) do
|
||||
page.find("[data-test-selector='project-custom-field-action-menu']").click
|
||||
within('anchored-position', &block)
|
||||
within("anchored-position", &block)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ RSpec.describe "Favorite projects", :js do
|
||||
let(:my_page) do
|
||||
Pages::My::Page.new
|
||||
end
|
||||
|
||||
context "as a user" do
|
||||
before do
|
||||
login_as user
|
||||
|
||||
+8
-8
@@ -26,15 +26,15 @@
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
require 'spec_helper'
|
||||
require_relative '../shared_context'
|
||||
require "spec_helper"
|
||||
require_relative "../shared_context"
|
||||
|
||||
RSpec.describe 'Edit project custom fields on project overview page', :js do
|
||||
include_context 'with seeded projects, members and project custom fields'
|
||||
RSpec.describe "Edit project custom fields on project overview page", :js do
|
||||
include_context "with seeded projects, members and project custom fields"
|
||||
|
||||
let(:overview_page) { Pages::Projects::Show.new(project) }
|
||||
|
||||
describe 'with insufficient permissions' do
|
||||
describe "with insufficient permissions" do
|
||||
# turboframe sidebar request is covered by a controller spec checking for 403
|
||||
# async dialog content request is be covered by a controller spec checking for 403
|
||||
# via spec/permissions/manage_project_custom_values_spec.rb
|
||||
@@ -43,20 +43,20 @@ RSpec.describe 'Edit project custom fields on project overview page', :js do
|
||||
overview_page.visit_page
|
||||
end
|
||||
|
||||
it 'does not show the edit buttons' do
|
||||
it "does not show the edit buttons" do
|
||||
overview_page.within_async_loaded_sidebar do
|
||||
expect(page).to have_no_css("[data-test-selector='project-custom-field-section-edit-button']")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with sufficient permissions' do
|
||||
describe "with sufficient permissions" do
|
||||
before do
|
||||
login_as member_with_project_edit_permissions
|
||||
overview_page.visit_page
|
||||
end
|
||||
|
||||
it 'shows the edit buttons' do
|
||||
it "shows the edit buttons" do
|
||||
overview_page.within_async_loaded_sidebar do
|
||||
expect(page).to have_css("[data-test-selector='project-custom-field-section-edit-button']", count: 3)
|
||||
end
|
||||
|
||||
@@ -26,16 +26,16 @@
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
require 'spec_helper'
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe 'Projects custom fields mapping via project settings', :js, :with_cuprite do
|
||||
let(:project) { create(:project, name: 'Foo project', identifier: 'foo-project') }
|
||||
let(:other_project) { create(:project, name: 'Bar project', identifier: 'bar-project') }
|
||||
RSpec.describe "Projects custom fields mapping via project settings", :js, :with_cuprite do
|
||||
let(:project) { create(:project, name: "Foo project", identifier: "foo-project") }
|
||||
let(:other_project) { create(:project, name: "Bar project", identifier: "bar-project") }
|
||||
|
||||
let!(:user_with_sufficient_permissions) do
|
||||
create(:user,
|
||||
firstname: 'Project',
|
||||
lastname: 'Admin',
|
||||
firstname: "Project",
|
||||
lastname: "Admin",
|
||||
member_with_permissions: {
|
||||
project => %w[
|
||||
view_work_packages
|
||||
@@ -52,8 +52,8 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
|
||||
let!(:member_in_project) do
|
||||
create(:user,
|
||||
firstname: 'Member 1',
|
||||
lastname: 'In Project',
|
||||
firstname: "Member 1",
|
||||
lastname: "In Project",
|
||||
member_with_permissions: { project => %w[
|
||||
edit_project
|
||||
view_work_packages
|
||||
@@ -62,69 +62,69 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
|
||||
let!(:another_member_in_project) do
|
||||
create(:user,
|
||||
firstname: 'Member 2',
|
||||
lastname: 'In Project',
|
||||
firstname: "Member 2",
|
||||
lastname: "In Project",
|
||||
member_with_permissions: { project => %w[
|
||||
view_work_packages
|
||||
] })
|
||||
end
|
||||
|
||||
let!(:section_for_input_fields) { create(:project_custom_field_section, name: 'Input fields') }
|
||||
let!(:section_for_select_fields) { create(:project_custom_field_section, name: 'Select fields') }
|
||||
let!(:section_for_multi_select_fields) { create(:project_custom_field_section, name: 'Multi select fields') }
|
||||
let!(:section_for_input_fields) { create(:project_custom_field_section, name: "Input fields") }
|
||||
let!(:section_for_select_fields) { create(:project_custom_field_section, name: "Select fields") }
|
||||
let!(:section_for_multi_select_fields) { create(:project_custom_field_section, name: "Multi select fields") }
|
||||
|
||||
let!(:boolean_project_custom_field) do
|
||||
create(:boolean_project_custom_field, name: 'Boolean field',
|
||||
create(:boolean_project_custom_field, name: "Boolean field",
|
||||
project_custom_field_section: section_for_input_fields)
|
||||
end
|
||||
|
||||
let!(:string_project_custom_field) do
|
||||
create(:string_project_custom_field, name: 'String field',
|
||||
create(:string_project_custom_field, name: "String field",
|
||||
project_custom_field_section: section_for_input_fields)
|
||||
end
|
||||
|
||||
let!(:list_project_custom_field) do
|
||||
create(:list_project_custom_field, name: 'List field',
|
||||
create(:list_project_custom_field, name: "List field",
|
||||
project_custom_field_section: section_for_select_fields,
|
||||
possible_values: ['Option 1', 'Option 2', 'Option 3'])
|
||||
possible_values: ["Option 1", "Option 2", "Option 3"])
|
||||
end
|
||||
|
||||
let!(:multi_list_project_custom_field) do
|
||||
create(:list_project_custom_field, name: 'Multi list field',
|
||||
create(:list_project_custom_field, name: "Multi list field",
|
||||
project_custom_field_section: section_for_multi_select_fields,
|
||||
possible_values: ['Option 1', 'Option 2', 'Option 3'],
|
||||
possible_values: ["Option 1", "Option 2", "Option 3"],
|
||||
multi_value: true)
|
||||
end
|
||||
|
||||
describe 'with insufficient permissions' do
|
||||
describe "with insufficient permissions" do
|
||||
before do
|
||||
login_as member_in_project # can edit project but is not allowed to select project custom fields
|
||||
end
|
||||
|
||||
it 'does not show the menu entry in the project settings menu' do
|
||||
it "does not show the menu entry in the project settings menu" do
|
||||
visit project_settings_general_path(project)
|
||||
|
||||
within '#menu-sidebar' do
|
||||
within "#menu-sidebar" do
|
||||
expect(page).to have_no_css("li[data-name='settings_project_custom_fields']")
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not show the project custom fields page' do
|
||||
it "does not show the project custom fields page" do
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
|
||||
expect(page).to have_content('You are not authorized to access this page.')
|
||||
expect(page).to have_content("You are not authorized to access this page.")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with sufficient permissions' do
|
||||
describe "with sufficient permissions" do
|
||||
before do
|
||||
login_as user_with_sufficient_permissions
|
||||
end
|
||||
|
||||
it 'does show the menu entry in the project settings menu' do
|
||||
it "does show the menu entry in the project settings menu" do
|
||||
visit project_settings_general_path(project)
|
||||
|
||||
within '#menu-sidebar' do
|
||||
within "#menu-sidebar" do
|
||||
expect(page).to have_css("li[data-name='settings_project_custom_fields']")
|
||||
end
|
||||
end
|
||||
@@ -151,17 +151,17 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
expect(page).to have_content("Multi list field")
|
||||
end
|
||||
|
||||
it 'shows all available project custom fields with their correct mapping state' do
|
||||
it "shows all available project custom fields with their correct mapping state" do
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
|
||||
within_custom_field_section_container(section_for_input_fields) do
|
||||
within_custom_field_container(boolean_project_custom_field) do
|
||||
expect(page).to have_content('Boolean field')
|
||||
expect(page).to have_content("Boolean field")
|
||||
expect_type("Bool")
|
||||
expect_unchecked_state
|
||||
end
|
||||
within_custom_field_container(string_project_custom_field) do
|
||||
expect(page).to have_content('String field')
|
||||
expect(page).to have_content("String field")
|
||||
expect_type("String")
|
||||
expect_unchecked_state
|
||||
end
|
||||
@@ -169,7 +169,7 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
|
||||
within_custom_field_section_container(section_for_select_fields) do
|
||||
within_custom_field_container(list_project_custom_field) do
|
||||
expect(page).to have_content('List field')
|
||||
expect(page).to have_content("List field")
|
||||
expect_type("List")
|
||||
expect_unchecked_state
|
||||
end
|
||||
@@ -177,14 +177,14 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
|
||||
within_custom_field_section_container(section_for_multi_select_fields) do
|
||||
within_custom_field_container(multi_list_project_custom_field) do
|
||||
expect(page).to have_content('Multi list field')
|
||||
expect(page).to have_content("Multi list field")
|
||||
expect_type("List")
|
||||
expect_unchecked_state
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'toggles the mapping state of a project custom field for a specific project when clicked' do
|
||||
it "toggles the mapping state of a project custom field for a specific project when clicked" do
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
|
||||
within_custom_field_section_container(section_for_input_fields) do
|
||||
@@ -214,7 +214,7 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
end
|
||||
end
|
||||
|
||||
it 'enables all mapping states of a section for a specific project when bulk action button clicked' do
|
||||
it "enables all mapping states of a section for a specific project when bulk action button clicked" do
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
|
||||
within_custom_field_section_container(section_for_input_fields) do
|
||||
@@ -241,7 +241,7 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
end
|
||||
end
|
||||
|
||||
it 'disables all mapping states of a section for a specific project when bulk action button clicked' do
|
||||
it "disables all mapping states of a section for a specific project when bulk action button clicked" do
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
|
||||
within_custom_field_section_container(section_for_input_fields) do
|
||||
@@ -279,59 +279,59 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
end
|
||||
end
|
||||
|
||||
it 'filters the project custom fields by name with given user input' do
|
||||
it "filters the project custom fields by name with given user input" do
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
|
||||
fill_in 'project-custom-fields-mapping-filter', with: 'Boolean'
|
||||
fill_in "project-custom-fields-mapping-filter", with: "Boolean"
|
||||
|
||||
within_custom_field_section_container(section_for_input_fields) do
|
||||
expect(page).to have_content('Boolean field')
|
||||
expect(page).to have_no_content('String field')
|
||||
expect(page).to have_content("Boolean field")
|
||||
expect(page).to have_no_content("String field")
|
||||
end
|
||||
|
||||
within_custom_field_section_container(section_for_select_fields) do
|
||||
expect(page).to have_no_content('List field')
|
||||
expect(page).to have_no_content("List field")
|
||||
end
|
||||
|
||||
within_custom_field_section_container(section_for_multi_select_fields) do
|
||||
expect(page).to have_no_content('Multi list field')
|
||||
expect(page).to have_no_content("Multi list field")
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows the project custom field sections in the correct order' do
|
||||
it "shows the project custom field sections in the correct order" do
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
|
||||
sections = page.all('.op-project-custom-field-section')
|
||||
sections = page.all(".op-project-custom-field-section")
|
||||
|
||||
expect(sections.size).to eq(3)
|
||||
|
||||
expect(sections[0].text).to include('Input fields')
|
||||
expect(sections[1].text).to include('Select fields')
|
||||
expect(sections[2].text).to include('Multi select fields')
|
||||
expect(sections[0].text).to include("Input fields")
|
||||
expect(sections[1].text).to include("Select fields")
|
||||
expect(sections[2].text).to include("Multi select fields")
|
||||
|
||||
section_for_input_fields.move_to_bottom
|
||||
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
|
||||
sections = page.all('.op-project-custom-field-section')
|
||||
sections = page.all(".op-project-custom-field-section")
|
||||
|
||||
expect(sections.size).to eq(3)
|
||||
|
||||
expect(sections[0].text).to include('Select fields')
|
||||
expect(sections[1].text).to include('Multi select fields')
|
||||
expect(sections[2].text).to include('Input fields')
|
||||
expect(sections[0].text).to include("Select fields")
|
||||
expect(sections[1].text).to include("Multi select fields")
|
||||
expect(sections[2].text).to include("Input fields")
|
||||
end
|
||||
|
||||
it 'shows the project custom fields in the correct order within the sections' do
|
||||
it "shows the project custom fields in the correct order within the sections" do
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
|
||||
within_custom_field_section_container(section_for_input_fields) do
|
||||
custom_fields = page.all('.op-project-custom-field')
|
||||
custom_fields = page.all(".op-project-custom-field")
|
||||
|
||||
expect(custom_fields.size).to eq(2)
|
||||
|
||||
expect(custom_fields[0].text).to include('Boolean field')
|
||||
expect(custom_fields[1].text).to include('String field')
|
||||
expect(custom_fields[0].text).to include("Boolean field")
|
||||
expect(custom_fields[1].text).to include("String field")
|
||||
end
|
||||
|
||||
boolean_project_custom_field.move_to_bottom
|
||||
@@ -339,21 +339,21 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
|
||||
within_custom_field_section_container(section_for_input_fields) do
|
||||
custom_fields = page.all('.op-project-custom-field')
|
||||
custom_fields = page.all(".op-project-custom-field")
|
||||
|
||||
expect(custom_fields.size).to eq(2)
|
||||
|
||||
expect(custom_fields[0].text).to include('String field')
|
||||
expect(custom_fields[1].text).to include('Boolean field')
|
||||
expect(custom_fields[0].text).to include("String field")
|
||||
expect(custom_fields[1].text).to include("Boolean field")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with visibility of project custom fields' do
|
||||
let!(:section_with_invisible_fields) { create(:project_custom_field_section, name: 'Section with invisible fields') }
|
||||
context "with visibility of project custom fields" do
|
||||
let!(:section_with_invisible_fields) { create(:project_custom_field_section, name: "Section with invisible fields") }
|
||||
|
||||
let!(:visible_project_custom_field) do
|
||||
create(:project_custom_field,
|
||||
name: 'Normal field',
|
||||
name: "Normal field",
|
||||
visible: true,
|
||||
projects: [project],
|
||||
project_custom_field_section: section_with_invisible_fields)
|
||||
@@ -361,13 +361,13 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
|
||||
let!(:invisible_project_custom_field) do
|
||||
create(:project_custom_field,
|
||||
name: 'Admin only field',
|
||||
name: "Admin only field",
|
||||
visible: false,
|
||||
projects: [project],
|
||||
project_custom_field_section: section_with_invisible_fields)
|
||||
end
|
||||
|
||||
context 'with admin permissions' do
|
||||
context "with admin permissions" do
|
||||
let!(:admin) do
|
||||
create(:admin)
|
||||
end
|
||||
@@ -377,14 +377,14 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
end
|
||||
|
||||
it 'shows the invisible project custom fields' do
|
||||
it "shows the invisible project custom fields" do
|
||||
within_custom_field_section_container(section_with_invisible_fields) do
|
||||
expect(page).to have_content('Normal field')
|
||||
expect(page).to have_content('Admin only field')
|
||||
expect(page).to have_content("Normal field")
|
||||
expect(page).to have_content("Admin only field")
|
||||
end
|
||||
end
|
||||
|
||||
it 'includeds the invisible project custom fields in the bulk actions' do
|
||||
it "includeds the invisible project custom fields in the bulk actions" do
|
||||
within_custom_field_section_container(section_with_invisible_fields) do
|
||||
page
|
||||
.find("[data-test-selector='disable-all-project-custom-field-mappings-#{section_with_invisible_fields.id}']")
|
||||
@@ -411,20 +411,20 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
end
|
||||
end
|
||||
|
||||
context 'with non-admin permissions' do
|
||||
context "with non-admin permissions" do
|
||||
before do
|
||||
login_as user_with_sufficient_permissions
|
||||
visit project_settings_project_custom_fields_path(project)
|
||||
end
|
||||
|
||||
it 'does not show the invisible project custom fields' do
|
||||
it "does not show the invisible project custom fields" do
|
||||
within_custom_field_section_container(section_with_invisible_fields) do
|
||||
expect(page).to have_content('Normal field')
|
||||
expect(page).to have_no_content('Admin only field')
|
||||
expect(page).to have_content("Normal field")
|
||||
expect(page).to have_no_content("Admin only field")
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not include the invisible project custom fields in the bulk actions' do
|
||||
it "does not include the invisible project custom fields in the bulk actions" do
|
||||
within_custom_field_section_container(section_with_invisible_fields) do
|
||||
page
|
||||
.find("[data-test-selector='disable-all-project-custom-field-mappings-#{section_with_invisible_fields.id}']")
|
||||
@@ -461,11 +461,11 @@ RSpec.describe 'Projects custom fields mapping via project settings', :js, :with
|
||||
end
|
||||
|
||||
def expect_checked_state
|
||||
expect(page).to have_css('.ToggleSwitch-statusOn')
|
||||
expect(page).to have_css(".ToggleSwitch-statusOn")
|
||||
end
|
||||
|
||||
def expect_unchecked_state
|
||||
expect(page).to have_css('.ToggleSwitch-statusOff')
|
||||
expect(page).to have_css(".ToggleSwitch-statusOff")
|
||||
end
|
||||
|
||||
def within_custom_field_section_container(section, &)
|
||||
|
||||
@@ -234,15 +234,16 @@ RSpec.describe "work package export" do
|
||||
allow_any_instance_of(WorkPackage::PDFExport::WorkPackageListToPdf)
|
||||
.to receive(:export!)
|
||||
.and_return(
|
||||
::Exports::Result.new format: :pdf,
|
||||
title: 'foo',
|
||||
content: Tempfile.new("something"),
|
||||
mime_type: "application/pdf"
|
||||
Exports::Result.new(format: :pdf,
|
||||
title: "foo",
|
||||
content: Tempfile.new("something"),
|
||||
mime_type: "application/pdf")
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "table" do
|
||||
let(:export_type) { I18n.t("export.format.pdf_overview_table") }
|
||||
|
||||
context "with many columns" do
|
||||
before do
|
||||
# Despite attempts to provoke the error by having a lot of columns, the pdf
|
||||
@@ -292,7 +293,7 @@ RSpec.describe "work package export" do
|
||||
wp_table.visit_query query
|
||||
work_packages_page.ensure_loaded
|
||||
settings_menu.open_and_choose "Export"
|
||||
expect(page).not_to have_content(export_type)
|
||||
expect(page).to have_no_content(export_type)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -303,7 +304,7 @@ RSpec.describe "work package export" do
|
||||
it "has no gantt export" do
|
||||
wp_table.visit_query query_tl
|
||||
settings_menu.open_and_choose "Export"
|
||||
expect(page).not_to have_content(export_type)
|
||||
expect(page).to have_no_content(export_type)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -31,34 +31,7 @@ require "spec_helper"
|
||||
RSpec.describe "Wysiwyg attribute macros", :js do
|
||||
shared_let(:admin) { create(:admin) }
|
||||
let(:user) { admin }
|
||||
|
||||
shared_let(:type_milestone) { create(:type_milestone) }
|
||||
shared_let(:type_task) { create(:type_task) }
|
||||
|
||||
shared_let(:project) do
|
||||
create(:project,
|
||||
identifier: "some-project",
|
||||
types: [type_milestone, type_task],
|
||||
enabled_module_names: %w[wiki work_package_tracking])
|
||||
end
|
||||
shared_let(:work_package) do
|
||||
create(:work_package,
|
||||
subject: "Foo Bar",
|
||||
project:,
|
||||
start_date: '2023-01-01',
|
||||
due_date: '2023-01-05',
|
||||
type: type_task)
|
||||
end
|
||||
shared_let(:milestone) do
|
||||
create(:work_package,
|
||||
subject: "Milestone",
|
||||
project:,
|
||||
due_date: '2023-01-10',
|
||||
type: type_milestone)
|
||||
end
|
||||
|
||||
let(:editor) { Components::WysiwygEditor.new }
|
||||
|
||||
let(:markdown) do
|
||||
<<~MD
|
||||
# My headline
|
||||
@@ -100,6 +73,31 @@ RSpec.describe "Wysiwyg attribute macros", :js do
|
||||
MD
|
||||
end
|
||||
|
||||
shared_let(:type_milestone) { create(:type_milestone) }
|
||||
shared_let(:type_task) { create(:type_task) }
|
||||
|
||||
shared_let(:project) do
|
||||
create(:project,
|
||||
identifier: "some-project",
|
||||
types: [type_milestone, type_task],
|
||||
enabled_module_names: %w[wiki work_package_tracking])
|
||||
end
|
||||
shared_let(:work_package) do
|
||||
create(:work_package,
|
||||
subject: "Foo Bar",
|
||||
project:,
|
||||
start_date: "2023-01-01",
|
||||
due_date: "2023-01-05",
|
||||
type: type_task)
|
||||
end
|
||||
shared_let(:milestone) do
|
||||
create(:work_package,
|
||||
subject: "Milestone",
|
||||
project:,
|
||||
due_date: "2023-01-10",
|
||||
type: type_milestone)
|
||||
end
|
||||
|
||||
before do
|
||||
login_as(user)
|
||||
end
|
||||
|
||||
+9
-9
@@ -1,16 +1,16 @@
|
||||
require 'webrick'
|
||||
require 'httpx'
|
||||
require "webrick"
|
||||
require "httpx"
|
||||
|
||||
RSpec.describe 'HTTPX' do
|
||||
describe 'persistent connections' do
|
||||
it 'does not hang forever when used to request HTTP 1.1 server' do
|
||||
server = WEBrick::HTTPServer.new(:Port => 8543)
|
||||
server.mount_proc '/' do |req, res|
|
||||
res.body = 'Response Body'
|
||||
RSpec.describe "HTTPX" do
|
||||
describe "persistent connections" do
|
||||
it "does not hang forever when used to request HTTP 1.1 server" do
|
||||
server = WEBrick::HTTPServer.new(Port: 8543)
|
||||
server.mount_proc "/" do |_req, res|
|
||||
res.body = "Response Body"
|
||||
end
|
||||
|
||||
Thread.new { server.start }
|
||||
session = HTTPX .plugin(:persistent).with(timeout: {keep_alive_timeout: 2})
|
||||
session = HTTPX.plugin(:persistent).with(timeout: { keep_alive_timeout: 2 })
|
||||
number_of_requests_made = 0
|
||||
begin
|
||||
Timeout.timeout(10) do
|
||||
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'i18n/tasks'
|
||||
require "i18n/tasks"
|
||||
|
||||
RSpec.describe I18n do
|
||||
let(:i18n) { I18n::Tasks::BaseTask.new }
|
||||
@@ -8,19 +8,19 @@ RSpec.describe I18n do
|
||||
let(:unused_keys) { i18n.unused_keys }
|
||||
let(:inconsistent_interpolations) { i18n.inconsistent_interpolations }
|
||||
|
||||
it 'does not have missing keys' do
|
||||
pending 'Enable when i18n-tasks is enabled across the project, otherwise this spec will report false positives'
|
||||
it "does not have missing keys" do
|
||||
pending "Enable when i18n-tasks is enabled across the project, otherwise this spec will report false positives"
|
||||
|
||||
expect(missing_keys).to be_empty,
|
||||
"Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
|
||||
end
|
||||
|
||||
it 'does not have unused keys' do
|
||||
it "does not have unused keys" do
|
||||
expect(unused_keys).to be_empty,
|
||||
"#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
|
||||
end
|
||||
|
||||
it 'files are normalized' do
|
||||
it "files are normalized" do
|
||||
non_normalized = i18n.non_normalized_paths
|
||||
error_message = "The following files need to be normalized:\n" \
|
||||
"#{non_normalized.map { |path| " #{path}" }.join("\n")}\n" \
|
||||
@@ -28,12 +28,12 @@ RSpec.describe I18n do
|
||||
expect(non_normalized).to be_empty, error_message
|
||||
end
|
||||
|
||||
context 'for all i18n files' do
|
||||
let(:root_dir) { Pathname.new(__FILE__).dirname.join('..') }
|
||||
let(:config_file) { root_dir.join('config/i18n-tasks-all-files.yml') }
|
||||
context "for all i18n files" do
|
||||
let(:root_dir) { Pathname.new(__FILE__).dirname.join("..") }
|
||||
let(:config_file) { root_dir.join("config/i18n-tasks-all-files.yml") }
|
||||
let(:i18n) { I18n::Tasks::BaseTask.new(config_file:) }
|
||||
|
||||
it 'does not have inconsistent interpolations' do
|
||||
it "does not have inconsistent interpolations" do
|
||||
config_file_relative_path = config_file.relative_path_from(Dir.pwd)
|
||||
error_message = "#{inconsistent_interpolations.leaves.count} i18n keys have inconsistent interpolations.\n" \
|
||||
"Run 'i18n-tasks check-consistent-interpolations --config #{config_file_relative_path}' to show them"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe Queries::Projects::ProjectQuery, "#allowed to" do # rubocop:disable RSpec/FilePath,RSpec/SpecFilePathFormat
|
||||
RSpec.describe Queries::Projects::ProjectQuery, "#allowed to" do # rubocop:disable RSpec/RSpec/SpecFilePathFormat
|
||||
shared_let(:user) { create(:user) }
|
||||
shared_let(:other_user) { create(:user) }
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
require 'spec_helper'
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe WorkPackage do
|
||||
describe '#journal' do
|
||||
describe "#journal" do
|
||||
let(:type) { create(:type) }
|
||||
let(:project) do
|
||||
create(:project,
|
||||
@@ -42,7 +42,7 @@ RSpec.describe WorkPackage do
|
||||
create(:work_package,
|
||||
project_id: project.id,
|
||||
type:,
|
||||
description: 'Description',
|
||||
description: "Description",
|
||||
priority:,
|
||||
status:,
|
||||
duration: 1)
|
||||
@@ -52,53 +52,53 @@ RSpec.describe WorkPackage do
|
||||
|
||||
current_user { create(:user) }
|
||||
|
||||
context 'for work package creation' do
|
||||
context "for work package creation" do
|
||||
it { expect(Journal.for_work_package.count).to eq(1) }
|
||||
|
||||
it 'has a journal entry' do
|
||||
it "has a journal entry" do
|
||||
expect(Journal.for_work_package.first.journable).to eq(work_package)
|
||||
end
|
||||
|
||||
it 'notes the changes to subject' do
|
||||
it "notes the changes to subject" do
|
||||
expect(work_package.last_journal.details[:subject])
|
||||
.to contain_exactly(nil, work_package.subject)
|
||||
end
|
||||
|
||||
it 'notes the changes to project' do
|
||||
it "notes the changes to project" do
|
||||
expect(work_package.last_journal.details[:project_id])
|
||||
.to contain_exactly(nil, work_package.project_id)
|
||||
end
|
||||
|
||||
it 'notes the description' do
|
||||
it "notes the description" do
|
||||
expect(work_package.last_journal.details[:description])
|
||||
.to contain_exactly(nil, work_package.description)
|
||||
end
|
||||
|
||||
it 'notes the scheduling mode' do
|
||||
it "notes the scheduling mode" do
|
||||
expect(work_package.last_journal.details[:schedule_manually])
|
||||
.to contain_exactly(nil, false)
|
||||
end
|
||||
|
||||
it 'has the timestamp of the work package update time for created_at' do
|
||||
it "has the timestamp of the work package update time for created_at" do
|
||||
expect(work_package.last_journal.created_at)
|
||||
.to eql(work_package.reload.updated_at)
|
||||
end
|
||||
|
||||
it 'has the updated_at of the work package as the lower bound for validity_period and no upper bound' do
|
||||
it "has the updated_at of the work package as the lower bound for validity_period and no upper bound" do
|
||||
expect(work_package.last_journal.validity_period)
|
||||
.to eql(work_package.reload.updated_at...)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when nothing is changed' do
|
||||
context "when nothing is changed" do
|
||||
it { expect { work_package.save! }.not_to change(Journal, :count) }
|
||||
|
||||
it 'does not update the updated_at time of the work package' do
|
||||
it "does not update the updated_at time of the work package" do
|
||||
expect { work_package.save! }.not_to change(work_package, :updated_at)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for different newlines', with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
context "for different newlines", with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
let(:description) { "Description\n\nwith newlines\n\nembedded" }
|
||||
let(:changed_description) { description.gsub("\n", "\r\n") }
|
||||
let!(:work_package1) do
|
||||
@@ -113,19 +113,19 @@ RSpec.describe WorkPackage do
|
||||
work_package1.description = changed_description
|
||||
end
|
||||
|
||||
context 'when a new journal is created tracking a simultaneously applied change' do
|
||||
context "when a new journal is created tracking a simultaneously applied change" do
|
||||
before do
|
||||
work_package1.subject += 'changed'
|
||||
work_package1.subject += "changed"
|
||||
work_package1.save!
|
||||
end
|
||||
|
||||
describe 'does not track the changed newline characters' do
|
||||
describe "does not track the changed newline characters" do
|
||||
subject { work_package1.last_journal.data.description }
|
||||
|
||||
it { is_expected.to eq(description) }
|
||||
end
|
||||
|
||||
describe 'tracks only the other change' do
|
||||
describe "tracks only the other change" do
|
||||
subject { work_package1.last_journal.details }
|
||||
|
||||
it { is_expected.to have_key :subject }
|
||||
@@ -133,7 +133,7 @@ RSpec.describe WorkPackage do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a legacy journal containing non-escaped newlines' do
|
||||
context "when there is a legacy journal containing non-escaped newlines" do
|
||||
let!(:work_package1) do
|
||||
create(:work_package,
|
||||
journals: {
|
||||
@@ -142,13 +142,13 @@ RSpec.describe WorkPackage do
|
||||
})
|
||||
end
|
||||
|
||||
it 'does not track the change to the newline characters' do
|
||||
it "does not track the change to the newline characters" do
|
||||
expect(work_package1.reload.last_journal.details).not_to have_key :description
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'on work package change without aggregation', with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
describe "on work package change without aggregation", with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
let(:parent_work_package) do
|
||||
create(:work_package,
|
||||
project_id: project.id,
|
||||
@@ -162,8 +162,8 @@ RSpec.describe WorkPackage do
|
||||
before do
|
||||
project.types << type2
|
||||
|
||||
work_package.subject = 'changed'
|
||||
work_package.description = 'changed'
|
||||
work_package.subject = "changed"
|
||||
work_package.description = "changed"
|
||||
work_package.type = type2
|
||||
work_package.status = status2
|
||||
work_package.priority = priority2
|
||||
@@ -179,10 +179,10 @@ RSpec.describe WorkPackage do
|
||||
work_package.save!
|
||||
end
|
||||
|
||||
context 'for last created journal' do
|
||||
context "for last created journal" do
|
||||
subject { work_package.last_journal.details }
|
||||
|
||||
it 'contains all changes' do
|
||||
it "contains all changes" do
|
||||
%i(subject description type_id status_id priority_id
|
||||
start_date due_date estimated_hours assigned_to_id
|
||||
responsible_id parent_id schedule_manually duration).each do |a|
|
||||
@@ -191,106 +191,106 @@ RSpec.describe WorkPackage do
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'old value' do
|
||||
shared_examples_for "old value" do
|
||||
subject { work_package.last_journal.old_value_for(property) }
|
||||
|
||||
it { is_expected.to eq(expected_value) }
|
||||
end
|
||||
|
||||
shared_examples_for 'new value' do
|
||||
shared_examples_for "new value" do
|
||||
subject { work_package.last_journal.new_value_for(property) }
|
||||
|
||||
it { is_expected.to eq(expected_value) }
|
||||
end
|
||||
|
||||
describe 'journaled value for' do
|
||||
describe 'description' do
|
||||
let(:property) { 'description' }
|
||||
describe "journaled value for" do
|
||||
describe "description" do
|
||||
let(:property) { "description" }
|
||||
|
||||
context 'for old value' do
|
||||
let(:expected_value) { 'Description' }
|
||||
context "for old value" do
|
||||
let(:expected_value) { "Description" }
|
||||
|
||||
it_behaves_like 'old value'
|
||||
it_behaves_like "old value"
|
||||
end
|
||||
|
||||
context 'for new value' do
|
||||
let(:expected_value) { 'changed' }
|
||||
context "for new value" do
|
||||
let(:expected_value) { "changed" }
|
||||
|
||||
it_behaves_like 'new value'
|
||||
it_behaves_like "new value"
|
||||
end
|
||||
end
|
||||
|
||||
describe 'schedule_manually' do
|
||||
let(:property) { 'schedule_manually' }
|
||||
describe "schedule_manually" do
|
||||
let(:property) { "schedule_manually" }
|
||||
|
||||
context 'for old value' do
|
||||
context "for old value" do
|
||||
let(:expected_value) { false }
|
||||
|
||||
it_behaves_like 'old value'
|
||||
it_behaves_like "old value"
|
||||
end
|
||||
|
||||
context 'for new value' do
|
||||
context "for new value" do
|
||||
let(:expected_value) { true }
|
||||
|
||||
it_behaves_like 'new value'
|
||||
it_behaves_like "new value"
|
||||
end
|
||||
end
|
||||
|
||||
describe 'duration' do
|
||||
let(:property) { 'duration' }
|
||||
describe "duration" do
|
||||
let(:property) { "duration" }
|
||||
|
||||
context 'for old value' do
|
||||
context "for old value" do
|
||||
let(:expected_value) { 1 }
|
||||
|
||||
it_behaves_like 'old value'
|
||||
it_behaves_like "old value"
|
||||
end
|
||||
|
||||
context 'for new value' do
|
||||
context "for new value" do
|
||||
let(:expected_value) { 8 }
|
||||
|
||||
it_behaves_like 'new value'
|
||||
it_behaves_like "new value"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'adding journal with a missing journal and an existing journal' do
|
||||
describe "adding journal with a missing journal and an existing journal" do
|
||||
before do
|
||||
allow(WorkPackages::UpdateContract).to receive(:new).and_return(NoopContract.new)
|
||||
service = WorkPackages::UpdateService.new(user: current_user, model: work_package)
|
||||
service.call(journal_notes: 'note to be deleted', send_notifications: false)
|
||||
service.call(journal_notes: "note to be deleted", send_notifications: false)
|
||||
work_package.reload
|
||||
service.call(description: 'description v2', send_notifications: false)
|
||||
service.call(description: "description v2", send_notifications: false)
|
||||
work_package.reload
|
||||
work_package.journals.reload.find_by(notes: 'note to be deleted').delete
|
||||
work_package.journals.reload.find_by(notes: "note to be deleted").delete
|
||||
|
||||
service.call(description: 'description v4', send_notifications: false)
|
||||
service.call(description: "description v4", send_notifications: false)
|
||||
end
|
||||
|
||||
it 'creates a journal for the last change' do
|
||||
it "creates a journal for the last change" do
|
||||
last_journal = work_package.last_journal
|
||||
|
||||
expect(last_journal.data.description).to eql('description v4')
|
||||
expect(last_journal.data.description).to eql("description v4")
|
||||
end
|
||||
end
|
||||
|
||||
it 'has the timestamp of the work package update time for created_at' do
|
||||
it "has the timestamp of the work package update time for created_at" do
|
||||
expect(work_package.last_journal.created_at)
|
||||
.to eql(work_package.reload.updated_at)
|
||||
end
|
||||
|
||||
it 'has the updated_at of the work package as the lower bound for validity_period and no upper bound' do
|
||||
it "has the updated_at of the work package as the lower bound for validity_period and no upper bound" do
|
||||
expect(work_package.last_journal.validity_period)
|
||||
.to eql(work_package.reload.updated_at...)
|
||||
end
|
||||
|
||||
it 'sets the upper bound of the preceeding journal to be the created_at time of the newly created journal' do
|
||||
it "sets the upper bound of the preceeding journal to be the created_at time of the newly created journal" do
|
||||
former_last_journal = work_package.journals[-2]
|
||||
expect(former_last_journal.validity_period)
|
||||
.to eql(former_last_journal.created_at...work_package.last_journal.created_at)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'attachments', with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
describe "attachments", with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
let(:attachment) { build(:attachment) }
|
||||
let(:attachment_id) { "attachments_#{attachment.id}" }
|
||||
|
||||
@@ -299,7 +299,7 @@ RSpec.describe WorkPackage do
|
||||
work_package.save!
|
||||
end
|
||||
|
||||
context 'for new attachment' do
|
||||
context "for new attachment" do
|
||||
subject { work_package.last_journal.details }
|
||||
|
||||
it { is_expected.to have_key attachment_id }
|
||||
@@ -307,22 +307,22 @@ RSpec.describe WorkPackage do
|
||||
it { expect(subject[attachment_id]).to eq([nil, attachment.filename]) }
|
||||
end
|
||||
|
||||
context 'when attachment saved w/o change' do
|
||||
context "when attachment saved w/o change" do
|
||||
it { expect { attachment.save! }.not_to change(Journal, :count) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'custom values', with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
describe "custom values", with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
let(:custom_field) { create(:work_package_custom_field) }
|
||||
let(:custom_value) do
|
||||
build(:custom_value,
|
||||
value: 'false',
|
||||
value: "false",
|
||||
custom_field:)
|
||||
end
|
||||
|
||||
let(:custom_field_id) { "custom_fields_#{custom_value.custom_field_id}" }
|
||||
|
||||
shared_context 'for work package with custom value' do
|
||||
shared_context "for work package with custom value" do
|
||||
before do
|
||||
project.work_package_custom_fields << custom_field
|
||||
type.custom_fields << custom_field
|
||||
@@ -332,8 +332,8 @@ RSpec.describe WorkPackage do
|
||||
end
|
||||
end
|
||||
|
||||
context 'for new custom value' do
|
||||
include_context 'for work package with custom value'
|
||||
context "for new custom value" do
|
||||
include_context "for work package with custom value"
|
||||
|
||||
subject { work_package.last_journal.details }
|
||||
|
||||
@@ -342,12 +342,12 @@ RSpec.describe WorkPackage do
|
||||
it { expect(subject[custom_field_id]).to eq([nil, custom_value.value]) }
|
||||
end
|
||||
|
||||
context 'for custom value modified' do
|
||||
include_context 'for work package with custom value'
|
||||
context "for custom value modified" do
|
||||
include_context "for work package with custom value"
|
||||
|
||||
let(:modified_custom_value) do
|
||||
create(:work_package_custom_value,
|
||||
value: 'true',
|
||||
value: "true",
|
||||
custom_field:)
|
||||
end
|
||||
|
||||
@@ -363,12 +363,12 @@ RSpec.describe WorkPackage do
|
||||
it { expect(subject[custom_field_id]).to eq([custom_value.value.to_s, modified_custom_value.value.to_s]) }
|
||||
end
|
||||
|
||||
context 'when work package saved w/o change' do
|
||||
include_context 'for work package with custom value'
|
||||
context "when work package saved w/o change" do
|
||||
include_context "for work package with custom value"
|
||||
|
||||
let(:unmodified_custom_value) do
|
||||
create(:work_package_custom_value,
|
||||
value: 'false',
|
||||
value: "false",
|
||||
custom_field:)
|
||||
end
|
||||
|
||||
@@ -378,15 +378,15 @@ RSpec.describe WorkPackage do
|
||||
|
||||
it { expect { work_package.save! }.not_to change(Journal, :count) }
|
||||
|
||||
it 'does not set an upper bound to the already existing journal' do
|
||||
it "does not set an upper bound to the already existing journal" do
|
||||
work_package.save
|
||||
expect(work_package.last_journal.validity_period.end)
|
||||
.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom value removed' do
|
||||
include_context 'for work package with custom value'
|
||||
context "when custom value removed" do
|
||||
include_context "for work package with custom value"
|
||||
|
||||
before do
|
||||
work_package.custom_values.delete(custom_value)
|
||||
@@ -400,35 +400,35 @@ RSpec.describe WorkPackage do
|
||||
it { expect(subject[custom_field_id]).to eq([custom_value.value, nil]) }
|
||||
end
|
||||
|
||||
context 'when custom value did not exist before' do
|
||||
context "when custom value did not exist before" do
|
||||
let(:custom_field) do
|
||||
create(:work_package_custom_field,
|
||||
is_required: false,
|
||||
field_format: 'list',
|
||||
possible_values: ['', '1', '2', '3', '4', '5', '6', '7'])
|
||||
field_format: "list",
|
||||
possible_values: ["", "1", "2", "3", "4", "5", "6", "7"])
|
||||
end
|
||||
let(:custom_value) do
|
||||
create(:custom_value,
|
||||
value: '',
|
||||
value: "",
|
||||
customized: work_package,
|
||||
custom_field:)
|
||||
end
|
||||
|
||||
describe 'empty values are recognized as unchanged' do
|
||||
include_context 'for work package with custom value'
|
||||
describe "empty values are recognized as unchanged" do
|
||||
include_context "for work package with custom value"
|
||||
|
||||
it { expect(work_package.last_journal.customizable_journals).to be_empty }
|
||||
end
|
||||
|
||||
describe 'empty values handled as non existing' do
|
||||
include_context 'for work package with custom value'
|
||||
describe "empty values handled as non existing" do
|
||||
include_context "for work package with custom value"
|
||||
|
||||
it { expect(work_package.last_journal.customizable_journals.count).to eq(0) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'file_links', with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
describe "file_links", with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
let(:file_link) { build(:file_link) }
|
||||
let(:file_link_id) { "file_links_#{file_link.id}" }
|
||||
|
||||
@@ -437,18 +437,18 @@ RSpec.describe WorkPackage do
|
||||
work_package.save!
|
||||
end
|
||||
|
||||
context 'for the new file link' do
|
||||
context "for the new file link" do
|
||||
subject(:journal_details) { work_package.last_journal.details }
|
||||
|
||||
it { is_expected.to have_key file_link_id }
|
||||
|
||||
it {
|
||||
expect(journal_details[file_link_id])
|
||||
.to eq([nil, { 'link_name' => file_link.origin_name, 'storage_name' => nil }])
|
||||
.to eq([nil, { "link_name" => file_link.origin_name, "storage_name" => nil }])
|
||||
}
|
||||
end
|
||||
|
||||
context 'when file link saved w/o change' do
|
||||
context "when file link saved w/o change" do
|
||||
it {
|
||||
expect do
|
||||
file_link.save
|
||||
@@ -458,56 +458,56 @@ RSpec.describe WorkPackage do
|
||||
end
|
||||
end
|
||||
|
||||
context 'for only journal notes adding' do
|
||||
context "for only journal notes adding" do
|
||||
subject do
|
||||
work_package.add_journal(user: User.current, notes: 'some notes')
|
||||
work_package.add_journal(user: User.current, notes: "some notes")
|
||||
work_package.save
|
||||
work_package
|
||||
end
|
||||
|
||||
it 'does not create a new journal entry' do
|
||||
it "does not create a new journal entry" do
|
||||
expect { subject }.not_to change(work_package, :last_journal)
|
||||
end
|
||||
|
||||
it 'has the timestamp of the work package update time for updated_at' do
|
||||
it "has the timestamp of the work package update time for updated_at" do
|
||||
expect(subject.last_journal.updated_at).to eql(work_package.reload.updated_at)
|
||||
end
|
||||
|
||||
it 'updates the updated_at time of the work package' do
|
||||
it "updates the updated_at time of the work package" do
|
||||
expect { subject.reload }.to change(work_package, :updated_at)
|
||||
end
|
||||
|
||||
it 'stores the note with the existing journal entry' do
|
||||
expect { subject }.to change { work_package.last_journal.notes }.from('').to('some notes')
|
||||
it "stores the note with the existing journal entry" do
|
||||
expect { subject }.to change { work_package.last_journal.notes }.from("").to("some notes")
|
||||
end
|
||||
end
|
||||
|
||||
context 'for mixed journal notes and attribute adding' do
|
||||
context "for mixed journal notes and attribute adding" do
|
||||
subject do
|
||||
work_package.add_journal(user: User.current, notes: 'some notes')
|
||||
work_package.subject = 'blubs'
|
||||
work_package.add_journal(user: User.current, notes: "some notes")
|
||||
work_package.subject = "blubs"
|
||||
work_package.save
|
||||
work_package
|
||||
end
|
||||
|
||||
it 'does not create a new journal entry' do
|
||||
it "does not create a new journal entry" do
|
||||
expect { subject }.not_to change(work_package, :last_journal)
|
||||
end
|
||||
|
||||
it 'has the timestamp of the work package update time for updated_at' do
|
||||
it "has the timestamp of the work package update time for updated_at" do
|
||||
expect(subject.last_journal.updated_at).to eql(work_package.reload.updated_at)
|
||||
end
|
||||
|
||||
it 'updates the updated_at time of the work package' do
|
||||
it "updates the updated_at time of the work package" do
|
||||
expect { subject.reload }.to change(work_package, :updated_at)
|
||||
end
|
||||
|
||||
it 'stores the note with the existing journal entry' do
|
||||
expect { subject }.to change { work_package.last_journal.notes }.from('').to('some notes')
|
||||
it "stores the note with the existing journal entry" do
|
||||
expect { subject }.to change { work_package.last_journal.notes }.from("").to("some notes")
|
||||
end
|
||||
end
|
||||
|
||||
context 'for only journal cause adding' do
|
||||
context "for only journal cause adding" do
|
||||
subject do
|
||||
work_package.add_journal(
|
||||
user: User.current,
|
||||
@@ -517,62 +517,62 @@ RSpec.describe WorkPackage do
|
||||
work_package
|
||||
end
|
||||
|
||||
it 'has the cause logged in the last journal' do
|
||||
it "has the cause logged in the last journal" do
|
||||
expect(subject.last_journal.cause).to eql({
|
||||
'type' => 'work_package_predecessor_changed_times',
|
||||
'work_package_id' => other_work_package.id
|
||||
"type" => "work_package_predecessor_changed_times",
|
||||
"work_package_id" => other_work_package.id
|
||||
})
|
||||
end
|
||||
|
||||
it 'has the timestamp of the work package update time for created_at' do
|
||||
it "has the timestamp of the work package update time for created_at" do
|
||||
expect(subject.last_journal.updated_at).to eql(work_package.reload.updated_at)
|
||||
end
|
||||
|
||||
it 'updates the updated_at time of the work package' do
|
||||
it "updates the updated_at time of the work package" do
|
||||
expect { subject.reload }.to change(work_package, :updated_at)
|
||||
end
|
||||
|
||||
it 'does create a new journal entry' do
|
||||
it "does create a new journal entry" do
|
||||
expect { subject }.to change(work_package, :last_journal)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for mixed journal cause, notes and attribute adding' do
|
||||
context "for mixed journal cause, notes and attribute adding" do
|
||||
subject do
|
||||
work_package.add_journal(
|
||||
user: User.current,
|
||||
notes: 'some notes',
|
||||
notes: "some notes",
|
||||
cause: Journal::CausedByWorkPackagePredecessorChange.new(other_work_package)
|
||||
)
|
||||
work_package.subject = 'blubs'
|
||||
work_package.subject = "blubs"
|
||||
work_package.save
|
||||
work_package
|
||||
end
|
||||
|
||||
it 'has the timestamp of the work package update time for created_at' do
|
||||
it "has the timestamp of the work package update time for created_at" do
|
||||
expect(work_package.last_journal.updated_at).to eql(work_package.reload.updated_at)
|
||||
end
|
||||
|
||||
it 'does create a new journal entry' do
|
||||
it "does create a new journal entry" do
|
||||
expect { subject }.to change(work_package, :last_journal)
|
||||
end
|
||||
|
||||
it 'updates the updated_at time of the work package' do
|
||||
it "updates the updated_at time of the work package" do
|
||||
updated_at_before = work_package.updated_at
|
||||
|
||||
expect(subject.reload.updated_at).not_to eql(updated_at_before)
|
||||
end
|
||||
|
||||
it 'stores the cause and note with the existing journal entry' do
|
||||
it "stores the cause and note with the existing journal entry" do
|
||||
subject
|
||||
|
||||
expect(work_package.last_journal.notes).to eq('some notes')
|
||||
expect(work_package.last_journal.cause_type).to eq('work_package_predecessor_changed_times')
|
||||
expect(work_package.last_journal.notes).to eq("some notes")
|
||||
expect(work_package.last_journal.cause_type).to eq("work_package_predecessor_changed_times")
|
||||
expect(work_package.last_journal.cause_work_package_id).to eq(other_work_package.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when 2 updates with the same cause occur' do
|
||||
context "when 2 updates with the same cause occur" do
|
||||
before do
|
||||
work_package.add_journal(
|
||||
user: User.current,
|
||||
@@ -591,20 +591,20 @@ RSpec.describe WorkPackage do
|
||||
work_package.save
|
||||
end
|
||||
|
||||
it 'does not create a new journal entry' do
|
||||
it "does not create a new journal entry" do
|
||||
expect { subject }.not_to change(work_package, :last_journal)
|
||||
end
|
||||
|
||||
it 'stores the last update only' do
|
||||
it "stores the last update only" do
|
||||
subject
|
||||
|
||||
expect(work_package.last_journal.new_value_for(:subject)).to eq('new subject 2')
|
||||
expect(work_package.last_journal.cause_type).to eq('work_package_predecessor_changed_times')
|
||||
expect(work_package.last_journal.new_value_for(:subject)).to eq("new subject 2")
|
||||
expect(work_package.last_journal.cause_type).to eq("work_package_predecessor_changed_times")
|
||||
expect(work_package.last_journal.cause_work_package_id).to eq(other_work_package.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when updated within aggregation time' do
|
||||
context "when updated within aggregation time" do
|
||||
subject(:journals) { work_package.journals }
|
||||
|
||||
current_user { user1 }
|
||||
@@ -627,18 +627,18 @@ RSpec.describe WorkPackage do
|
||||
work_package.save!
|
||||
end
|
||||
|
||||
context 'as author of last change' do
|
||||
context "as author of last change" do
|
||||
let(:new_author) { user1 }
|
||||
|
||||
it 'leads to a single journal' do
|
||||
it "leads to a single journal" do
|
||||
expect(subject.count).to be 1
|
||||
end
|
||||
|
||||
it 'is the initial journal' do
|
||||
it "is the initial journal" do
|
||||
expect(subject.first).to be_initial
|
||||
end
|
||||
|
||||
it 'contains the changes of both updates with the later overwriting the former' do
|
||||
it "contains the changes of both updates with the later overwriting the former" do
|
||||
expect(subject.first.data.status_id)
|
||||
.to eql changes[:status].id
|
||||
|
||||
@@ -646,51 +646,51 @@ RSpec.describe WorkPackage do
|
||||
.to eql work_package.type_id
|
||||
end
|
||||
|
||||
context 'with a comment' do
|
||||
let(:notes) { 'This is why I changed it.' }
|
||||
context "with a comment" do
|
||||
let(:notes) { "This is why I changed it." }
|
||||
|
||||
it 'leads to a single journal with the comment' do
|
||||
it "leads to a single journal with the comment" do
|
||||
expect(subject.count).to be 1
|
||||
expect(subject.first.notes)
|
||||
.to eql notes
|
||||
end
|
||||
|
||||
context 'when adding a second comment' do
|
||||
let(:second_notes) { 'Another comment, unrelated to the first one.' }
|
||||
context "when adding a second comment" do
|
||||
let(:second_notes) { "Another comment, unrelated to the first one." }
|
||||
|
||||
before do
|
||||
work_package.add_journal(user: new_author, notes: second_notes)
|
||||
work_package.save!
|
||||
end
|
||||
|
||||
it 'returns two journals' do
|
||||
it "returns two journals" do
|
||||
expect(subject.count).to be 2
|
||||
expect(subject.first.notes).to eql notes
|
||||
expect(subject.second.notes).to eql second_notes
|
||||
end
|
||||
|
||||
it 'has one initial journal and one non-initial journal' do
|
||||
it "has one initial journal and one non-initial journal" do
|
||||
expect(subject.first).to be_initial
|
||||
expect(subject.second).not_to be_initial
|
||||
end
|
||||
end
|
||||
|
||||
context 'when adding another change without comment' do
|
||||
context "when adding another change without comment" do
|
||||
before do
|
||||
work_package.reload # need to update the lock_version, avoiding StaleObjectError
|
||||
work_package.subject = 'foo'
|
||||
work_package.subject = "foo"
|
||||
work_package.assigned_to = current_user
|
||||
work_package.save!
|
||||
end
|
||||
|
||||
it 'leads to a single journal with the comment of the replaced journal and the state both combined' do
|
||||
it "leads to a single journal with the comment of the replaced journal and the state both combined" do
|
||||
expect(subject.count).to eq 1
|
||||
|
||||
expect(subject.first.notes)
|
||||
.to eql notes
|
||||
|
||||
expect(subject.first.data.subject)
|
||||
.to eql 'foo'
|
||||
.to eql "foo"
|
||||
|
||||
expect(subject.first.data.assigned_to)
|
||||
.to eql current_user
|
||||
@@ -700,16 +700,16 @@ RSpec.describe WorkPackage do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when adding another change with a customized work package' do
|
||||
context "when adding another change with a customized work package" do
|
||||
let(:custom_field) do
|
||||
create(:work_package_custom_field,
|
||||
is_required: false,
|
||||
field_format: 'list',
|
||||
possible_values: ['', '1', '2', '3', '4', '5', '6', '7'])
|
||||
field_format: "list",
|
||||
possible_values: ["", "1", "2", "3", "4", "5", "6", "7"])
|
||||
end
|
||||
let(:custom_value) do
|
||||
create(:custom_value,
|
||||
value: custom_field.custom_options.find { |co| co.value == '1' }.try(:id),
|
||||
value: custom_field.custom_options.find { |co| co.value == "1" }.try(:id),
|
||||
customized: work_package,
|
||||
custom_field:)
|
||||
end
|
||||
@@ -717,66 +717,66 @@ RSpec.describe WorkPackage do
|
||||
before do
|
||||
custom_value
|
||||
work_package.reload # need to update the lock_version, avoiding StaleObjectError
|
||||
work_package.subject = 'foo'
|
||||
work_package.subject = "foo"
|
||||
work_package.save!
|
||||
end
|
||||
|
||||
it 'leads to a single journal with only one customizable journal' do
|
||||
it "leads to a single journal with only one customizable journal" do
|
||||
expect(subject.count).to eq 1
|
||||
|
||||
expect(subject.first.notes)
|
||||
.to eql notes
|
||||
|
||||
expect(subject.first.data.subject)
|
||||
.to eql 'foo'
|
||||
.to eql "foo"
|
||||
|
||||
expect(subject.first.customizable_journals.count).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'has the journal\'s creation time as the lower and no upper bound for validity_period' do
|
||||
it "has the journal's creation time as the lower and no upper bound for validity_period" do
|
||||
expect(work_package.last_journal.validity_period)
|
||||
.to eql(work_package.last_journal.created_at...)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a different author' do
|
||||
context "with a different author" do
|
||||
let(:new_author) { user2 }
|
||||
|
||||
it 'leads to two journals' do
|
||||
it "leads to two journals" do
|
||||
expect(subject.count).to be 2
|
||||
end
|
||||
|
||||
it 'has the initial user as the author of the first journal' do
|
||||
it "has the initial user as the author of the first journal" do
|
||||
expect(subject.first.user)
|
||||
.to eql current_user
|
||||
end
|
||||
|
||||
it 'has the second user as he author of the second journal' do
|
||||
it "has the second user as he author of the second journal" do
|
||||
expect(subject.second.user)
|
||||
.to eql new_author
|
||||
end
|
||||
|
||||
it 'has the changes (compared to the initial state) in the second journal' do
|
||||
it "has the changes (compared to the initial state) in the second journal" do
|
||||
expect(subject.second.get_changes)
|
||||
.to eql("status_id" => [status.id, new_status.id])
|
||||
end
|
||||
|
||||
it 'has the first journal\'s creation time as the lower and the second journal\'s creation time ' \
|
||||
'as the upper bound for validity_period of the first journal' do
|
||||
it "has the first journal's creation time as the lower and the second journal's creation time " \
|
||||
"as the upper bound for validity_period of the first journal" do
|
||||
expect(subject.first.validity_period)
|
||||
.to eql(subject.first.created_at...subject.second.created_at)
|
||||
end
|
||||
|
||||
it 'has the second journal\'s creation time as the lower and no upper bound for validity_period of the second journal' do
|
||||
it "has the second journal's creation time as the lower and no upper bound for validity_period of the second journal" do
|
||||
expect(subject.second.validity_period)
|
||||
.to eql(subject.second.created_at...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when updated after aggregation timeout expired', with_settings: { journal_aggregation_time_minutes: 1 } do
|
||||
context "when updated after aggregation timeout expired", with_settings: { journal_aggregation_time_minutes: 1 } do
|
||||
let(:last_update_time) { 2.minutes.ago }
|
||||
|
||||
subject(:journals) { work_package.journals }
|
||||
@@ -792,38 +792,38 @@ RSpec.describe WorkPackage do
|
||||
work_package.save!
|
||||
end
|
||||
|
||||
it 'creates a new journal' do
|
||||
it "creates a new journal" do
|
||||
expect(journals.count).to be 2
|
||||
end
|
||||
|
||||
it 'has the first journal\'s creation time as the lower and the second journal\'s creation time ' \
|
||||
'as the upper bound for validity_period of the first journal' do
|
||||
it "has the first journal's creation time as the lower and the second journal's creation time " \
|
||||
"as the upper bound for validity_period of the first journal" do
|
||||
expect(subject.first.validity_period)
|
||||
.to eql(subject.first.created_at...subject.second.created_at)
|
||||
end
|
||||
|
||||
it 'has the second journal\'s creation time as the lower and no upper bound for validity_period of the second journal' do
|
||||
it "has the second journal's creation time as the lower and no upper bound for validity_period of the second journal" do
|
||||
expect(subject.second.validity_period)
|
||||
.to eql(subject.second.created_at...)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when updating with aggregation disabled', with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
context "when updating with aggregation disabled", with_settings: { journal_aggregation_time_minutes: 0 } do
|
||||
subject(:journals) { work_package.journals }
|
||||
|
||||
context 'when WP updated within milliseconds' do
|
||||
context "when WP updated within milliseconds" do
|
||||
before do
|
||||
work_package.status = build(:status)
|
||||
work_package.save!
|
||||
end
|
||||
|
||||
it 'creates a new journal' do
|
||||
it "creates a new journal" do
|
||||
expect(journals.count).to be 2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when aggregation leads to an empty change (changing back and forth)',
|
||||
context "when aggregation leads to an empty change (changing back and forth)",
|
||||
with_settings: { journal_aggregation_time_minutes: 1 } do
|
||||
let!(:work_package) do
|
||||
User.execute_as current_user do
|
||||
@@ -832,7 +832,7 @@ RSpec.describe WorkPackage do
|
||||
created_at: 5.minutes.ago,
|
||||
project_id: project.id,
|
||||
type:,
|
||||
description: 'Description',
|
||||
description: "Description",
|
||||
priority:,
|
||||
status:,
|
||||
duration: 1)
|
||||
@@ -848,17 +848,17 @@ RSpec.describe WorkPackage do
|
||||
work_package.save!
|
||||
end
|
||||
|
||||
it 'creates a new journal' do
|
||||
it "creates a new journal" do
|
||||
expect(work_package.journals.count).to be 2
|
||||
end
|
||||
|
||||
it 'has the old state in the last journal`s data' do
|
||||
it "has the old state in the last journal`s data" do
|
||||
expect(work_package.journals.last.data.status_id).to be status.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy' do
|
||||
describe "#destroy" do
|
||||
let(:project) { create(:project) }
|
||||
let(:type) { create(:type) }
|
||||
let(:custom_field) do
|
||||
@@ -887,27 +887,27 @@ RSpec.describe WorkPackage do
|
||||
work_package.destroy
|
||||
end
|
||||
|
||||
it 'removes the journal' do
|
||||
it "removes the journal" do
|
||||
expect(Journal.find_by(id: journal.id))
|
||||
.to be_nil
|
||||
end
|
||||
|
||||
it 'removes the journal data' do
|
||||
it "removes the journal data" do
|
||||
expect(Journal::WorkPackageJournal.find_by(id: journal.data_id))
|
||||
.to be_nil
|
||||
end
|
||||
|
||||
it 'removes the customizable journals' do
|
||||
it "removes the customizable journals" do
|
||||
expect(Journal::CustomizableJournal.find_by(id: customizable_journals.map(&:id)))
|
||||
.to be_nil
|
||||
end
|
||||
|
||||
it 'removes the attachable journals' do
|
||||
it "removes the attachable journals" do
|
||||
expect(Journal::AttachableJournal.find_by(id: attachable_journals.map(&:id)))
|
||||
.to be_nil
|
||||
end
|
||||
|
||||
it 'removes the storable journals' do
|
||||
it "removes the storable journals" do
|
||||
expect(Journal::StorableJournal.find_by(id: attachable_journals.map(&:id)))
|
||||
.to be_nil
|
||||
end
|
||||
|
||||
@@ -188,9 +188,9 @@ RSpec.describe(
|
||||
end
|
||||
end
|
||||
|
||||
context 'with disabled project custom fields with default value' do
|
||||
it 'is still disabled in the copy' do
|
||||
create(:text_project_custom_field, default_value: 'default value')
|
||||
context "with disabled project custom fields with default value" do
|
||||
it "is still disabled in the copy" do
|
||||
create(:text_project_custom_field, default_value: "default value")
|
||||
|
||||
expect(subject).to be_success
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ RSpec.describe Users::DropTokensService, type: :model do
|
||||
shared_let(:other_user) { create(:user) }
|
||||
|
||||
let(:instance) { described_class.new(current_user: input_user) }
|
||||
|
||||
subject { instance.call! }
|
||||
|
||||
describe "Invitation token" do
|
||||
|
||||
@@ -26,14 +26,14 @@
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
require 'spec_helper'
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe WorkPackages::SetScheduleService do
|
||||
create_shared_association_defaults_for_work_package_factory
|
||||
|
||||
let(:work_package) do
|
||||
create(:work_package,
|
||||
subject: 'subject',
|
||||
subject: "subject",
|
||||
start_date: work_package_start_date,
|
||||
due_date: work_package_due_date)
|
||||
end
|
||||
@@ -123,17 +123,17 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
|
||||
subject { instance.call(attributes) }
|
||||
|
||||
shared_examples_for 'reschedules' do
|
||||
shared_examples_for "reschedules" do
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
it 'is success' do
|
||||
it "is success" do
|
||||
expect(subject)
|
||||
.to be_success
|
||||
end
|
||||
|
||||
it 'updates the following work packages' do
|
||||
it "updates the following work packages" do
|
||||
expected.each do |wp, (start_date, due_date)|
|
||||
expected_cause_type = "work_package_related_changed_times"
|
||||
result = subject.all_results.find { |result_wp| result_wp.id == wp.id }
|
||||
@@ -168,87 +168,87 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns only the original and the changed work packages' do
|
||||
it "returns only the original and the changed work packages" do
|
||||
expect(subject.all_results)
|
||||
.to match_array expected.keys + [work_package]
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'does not reschedule' do
|
||||
shared_examples_for "does not reschedule" do
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
it 'is success' do
|
||||
it "is success" do
|
||||
expect(subject)
|
||||
.to be_success
|
||||
end
|
||||
|
||||
it 'does not change any other work packages' do
|
||||
it "does not change any other work packages" do
|
||||
expect(subject.all_results)
|
||||
.to contain_exactly(work_package)
|
||||
end
|
||||
|
||||
it 'does not assign a journal cause' do
|
||||
it "does not assign a journal cause" do
|
||||
subject.all_results.each do |work_package|
|
||||
expect(work_package.journal_cause).to be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without relation' do
|
||||
it 'is success' do
|
||||
context "without relation" do
|
||||
it "is success" do
|
||||
expect(subject)
|
||||
.to be_success
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a single successor' do
|
||||
context "with a single successor" do
|
||||
let!(:following) do
|
||||
[following_work_package1]
|
||||
end
|
||||
|
||||
context 'when moving forward' do
|
||||
context "when moving forward" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 8.days] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving forward with the follower having no due date' do
|
||||
context "when moving forward with the follower having no due date" do
|
||||
let(:follower1_due_date) { nil }
|
||||
|
||||
before do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, nil] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving forward with the follower having no start date' do
|
||||
context "when moving forward with the follower having no start date" do
|
||||
let(:follower1_start_date) { nil }
|
||||
|
||||
before do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 6.days] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving forward with the follower having some space left' do
|
||||
context "when moving forward with the follower having some space left" do
|
||||
let(:follower1_start_date) { Time.zone.today + 3.days }
|
||||
let(:follower1_due_date) { Time.zone.today + 5.days }
|
||||
|
||||
@@ -256,14 +256,14 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 8.days] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving forward with the follower having enough space left to not be moved at all' do
|
||||
context "when moving forward with the follower having enough space left to not be moved at all" do
|
||||
let(:follower1_start_date) { Time.zone.today + 10.days }
|
||||
let(:follower1_due_date) { Time.zone.today + 12.days }
|
||||
|
||||
@@ -271,10 +271,10 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
|
||||
context 'when moving forward with the follower having some space left and a lag' do
|
||||
context "when moving forward with the follower having some space left and a lag" do
|
||||
let(:follower1_start_date) { Time.zone.today + 5.days }
|
||||
let(:follower1_due_date) { Time.zone.today + 7.days }
|
||||
let(:follower1_lag) { 3 }
|
||||
@@ -283,14 +283,14 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 9.days, Time.zone.today + 11.days] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving forward with the follower not needing to be moved' do
|
||||
context "when moving forward with the follower not needing to be moved" do
|
||||
let(:follower1_start_date) { Time.zone.today + 6.days }
|
||||
let(:follower1_due_date) { Time.zone.today + 8.days }
|
||||
|
||||
@@ -298,18 +298,18 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
|
||||
context 'when moving backwards' do
|
||||
context "when moving backwards" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today - 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
|
||||
context 'when moving backwards with space between' do
|
||||
context "when moving backwards with space between" do
|
||||
let(:follower1_start_date) { Time.zone.today + 3.days }
|
||||
let(:follower1_due_date) { Time.zone.today + 5.days }
|
||||
|
||||
@@ -317,7 +317,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
work_package.due_date = Time.zone.today - 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
|
||||
context 'when moving backwards with the follower having no start date (which should not happen) \
|
||||
@@ -328,7 +328,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
work_package.due_date = Time.zone.today - 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today - 4.days, follower1_due_date] }
|
||||
end
|
||||
@@ -343,82 +343,82 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
work_package.due_date = follower1_due_date + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [follower1_due_date + 6.days, follower1_due_date + 6.days] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when removing the dates on the predecessor' do
|
||||
context "when removing the dates on the predecessor" do
|
||||
before do
|
||||
work_package.start_date = work_package.due_date = nil
|
||||
end
|
||||
|
||||
# The follower will keep its dates
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
|
||||
context 'when the follower has no start date but a due date' do
|
||||
context "when the follower has no start date but a due date" do
|
||||
let(:follower1_start_date) { nil }
|
||||
let(:follower1_due_date) { Time.zone.today + 15.days }
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not moving and the successor not having start & due date (e.g. creating relation)' do
|
||||
context "when not moving and the successor not having start & due date (e.g. creating relation)" do
|
||||
let(:follower1_start_date) { nil }
|
||||
let(:follower1_due_date) { nil }
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [work_package.due_date + 1.day, nil] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not moving and the successor having due before predecessor due date (e.g. creating relation)' do
|
||||
context "when not moving and the successor having due before predecessor due date (e.g. creating relation)" do
|
||||
let(:follower1_start_date) { nil }
|
||||
let(:follower1_due_date) { work_package_due_date - 5.days }
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [work_package.due_date + 1.day, work_package.due_date + 1.day] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not moving and the successor having start before predecessor due date (e.g. creating relation)' do
|
||||
context "when not moving and the successor having start before predecessor due date (e.g. creating relation)" do
|
||||
let(:follower1_start_date) { work_package_due_date - 5.days }
|
||||
let(:follower1_due_date) { nil }
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [work_package.due_date + 1.day, nil] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not moving and the successor having start and due before predecessor due date (e.g. creating relation)' do
|
||||
context "when not moving and the successor having start and due before predecessor due date (e.g. creating relation)" do
|
||||
let(:follower1_start_date) { work_package_due_date - 5.days }
|
||||
let(:follower1_due_date) { work_package_due_date - 2.days }
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [work_package.due_date + 1.day, work_package.due_date + 4.days] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not having dates and the successor not having start & due date (e.g. creating relation)' do
|
||||
context "when not having dates and the successor not having start & due date (e.g. creating relation)" do
|
||||
let(:work_package_due_date) { nil }
|
||||
let(:follower1_start_date) { nil }
|
||||
let(:follower1_due_date) { nil }
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
|
||||
context 'with the successor having another predecessor which has no dates' do
|
||||
context "with the successor having another predecessor which has no dates" do
|
||||
let(:following_work_package1) do
|
||||
create_follower(follower1_start_date,
|
||||
follower1_due_date,
|
||||
@@ -431,29 +431,29 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
due_date: nil)
|
||||
end
|
||||
|
||||
context 'when moving forward' do
|
||||
context "when moving forward" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 8.days] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving backwards' do
|
||||
context "when moving backwards" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today - 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with only a parent' do
|
||||
context "with only a parent" do
|
||||
let!(:parent_work_package) do
|
||||
create(:work_package).tap do |parent|
|
||||
work_package.parent = parent
|
||||
@@ -462,14 +462,14 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
let(:work_package_start_date) { Time.zone.today - 5.days }
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ parent_work_package => [work_package_start_date, work_package_due_date] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a parent having a follower' do
|
||||
context "with a parent having a follower" do
|
||||
let(:work_package_start_date) { Time.zone.today }
|
||||
let(:work_package_due_date) { Time.zone.today + 5.days }
|
||||
let!(:parent_work_package) do
|
||||
@@ -487,7 +487,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
{ parent_work_package => 0 })
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ parent_work_package => [work_package_start_date, work_package_due_date],
|
||||
follower_of_parent_work_package => [work_package_due_date + 1.day, work_package_due_date + 3.days] }
|
||||
@@ -514,7 +514,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
#
|
||||
# That's why the WorkPackage.for_scheduling call is mocked to customize
|
||||
# the order of the returned work_packages to reproduce this bug.
|
||||
context 'with also a sibling follower with same parent' do
|
||||
context "with also a sibling follower with same parent" do
|
||||
let!(:sibling_follower_of_work_package) do
|
||||
create_follower(Time.zone.today + 2.days,
|
||||
Time.zone.today + 3.days,
|
||||
@@ -531,7 +531,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ sibling_follower_of_work_package => [work_package_due_date + 1.day, work_package_due_date + 2.days],
|
||||
parent_work_package => [work_package_start_date, work_package_due_date + 2.days],
|
||||
@@ -541,18 +541,18 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a single successor having a parent' do
|
||||
context "with a single successor having a parent" do
|
||||
let!(:following) do
|
||||
[following_work_package1,
|
||||
parent_following_work_package1]
|
||||
end
|
||||
|
||||
context 'when moving forward' do
|
||||
context "when moving forward" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 8.days],
|
||||
parent_following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 8.days] }
|
||||
@@ -560,7 +560,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving forward with the parent having another child not being moved' do
|
||||
context "when moving forward with the parent having another child not being moved" do
|
||||
let(:parent_follower1_start_date) { follower1_start_date }
|
||||
let(:parent_follower1_due_date) { follower1_due_date + 4.days }
|
||||
|
||||
@@ -574,7 +574,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 8.days],
|
||||
parent_following_work_package1 => [Time.zone.today + 5.days, Time.zone.today + 8.days] }
|
||||
@@ -582,16 +582,16 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving backwards' do
|
||||
context "when moving backwards" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today - 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a single successor having a child' do
|
||||
context "with a single successor having a child" do
|
||||
let(:child_start_date) { follower1_start_date }
|
||||
let(:child_due_date) { follower1_due_date }
|
||||
|
||||
@@ -602,12 +602,12 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
child_work_package]
|
||||
end
|
||||
|
||||
context 'when moving forward' do
|
||||
context "when moving forward" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 8.days],
|
||||
child_work_package => [Time.zone.today + 6.days, Time.zone.today + 8.days] }
|
||||
@@ -616,7 +616,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a single successor having two children' do
|
||||
context "with a single successor having two children" do
|
||||
let(:follower1_start_date) { work_package_due_date + 1.day }
|
||||
let(:follower1_due_date) { work_package_due_date + 10.days }
|
||||
let(:child1_start_date) { follower1_start_date }
|
||||
@@ -633,11 +633,11 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
child2_work_package]
|
||||
end
|
||||
|
||||
context 'with unchanged dates (e.g. when creating a follows relation) and successor starting 1 day after scheduled' do
|
||||
it_behaves_like 'does not reschedule'
|
||||
context "with unchanged dates (e.g. when creating a follows relation) and successor starting 1 day after scheduled" do
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
|
||||
context 'with unchanged dates (e.g. when creating a follows relation) and successor starting 3 days after scheduled' do
|
||||
context "with unchanged dates (e.g. when creating a follows relation) and successor starting 3 days after scheduled" do
|
||||
let(:follower1_start_date) { work_package_due_date + 3.days }
|
||||
let(:follower1_due_date) { follower1_start_date + 10.days }
|
||||
let(:child1_start_date) { follower1_start_date }
|
||||
@@ -645,10 +645,10 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
let(:child2_start_date) { follower1_start_date + 8.days }
|
||||
let(:child2_due_date) { follower1_due_date }
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
|
||||
context 'with unchanged dates (e.g. when creating a follows relation) and successor\'s first child needs to be rescheduled' do
|
||||
context "with unchanged dates (e.g. when creating a follows relation) and successor's first child needs to be rescheduled" do
|
||||
let(:follower1_start_date) { work_package_due_date - 3.days }
|
||||
let(:follower1_due_date) { work_package_due_date + 10.days }
|
||||
let(:child1_start_date) { follower1_start_date }
|
||||
@@ -657,7 +657,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
let(:child2_due_date) { follower1_due_date }
|
||||
|
||||
# following parent is reduced in length as the children allow to be executed at the same time
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [work_package_due_date + 1.day, follower1_due_date],
|
||||
child1_work_package => [work_package_due_date + 1.day, follower1_start_date + 10.days] }
|
||||
@@ -674,7 +674,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
let(:child2_due_date) { follower1_due_date }
|
||||
|
||||
# following parent is reduced in length and children are rescheduled
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [work_package_due_date + 1.day, follower1_start_date + 21.days],
|
||||
child1_work_package => [work_package_due_date + 1.day, child1_due_date + 9.days],
|
||||
@@ -684,7 +684,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a chain of successors' do
|
||||
context "with a chain of successors" do
|
||||
let(:follower1_start_date) { Time.zone.today + 1.day }
|
||||
let(:follower1_due_date) { Time.zone.today + 3.days }
|
||||
let(:follower2_start_date) { Time.zone.today + 4.days }
|
||||
@@ -698,12 +698,12 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
following_work_package3]
|
||||
end
|
||||
|
||||
context 'when moving forward' do
|
||||
context "when moving forward" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 8.days],
|
||||
following_work_package2 => [Time.zone.today + 9.days, Time.zone.today + 13.days],
|
||||
@@ -712,7 +712,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving forward with some space between the followers' do
|
||||
context "when moving forward with some space between the followers" do
|
||||
let(:follower1_start_date) { Time.zone.today + 1.day }
|
||||
let(:follower1_due_date) { Time.zone.today + 3.days }
|
||||
let(:follower2_start_date) { Time.zone.today + 7.days }
|
||||
@@ -724,7 +724,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 8.days],
|
||||
following_work_package2 => [Time.zone.today + 9.days, Time.zone.today + 12.days] }
|
||||
@@ -732,16 +732,16 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving backwards' do
|
||||
context "when moving backwards" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today - 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a chain of successors with two paths leading to the same work package in the end' do
|
||||
context "with a chain of successors with two paths leading to the same work package in the end" do
|
||||
let(:follower3_start_date) { Time.zone.today + 4.days }
|
||||
let(:follower3_due_date) { Time.zone.today + 7.days }
|
||||
let(:follower3_lag) { 0 }
|
||||
@@ -766,12 +766,12 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
following_work_package4]
|
||||
end
|
||||
|
||||
context 'when moving forward' do
|
||||
context "when moving forward" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'reschedules' do
|
||||
it_behaves_like "reschedules" do
|
||||
let(:expected) do
|
||||
{ following_work_package1 => [Time.zone.today + 6.days, Time.zone.today + 8.days],
|
||||
following_work_package2 => [Time.zone.today + 9.days, Time.zone.today + 13.days],
|
||||
@@ -781,16 +781,16 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when moving backwards' do
|
||||
context "when moving backwards" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today - 5.days
|
||||
end
|
||||
|
||||
it_behaves_like 'does not reschedule'
|
||||
it_behaves_like "does not reschedule"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when setting the parent' do
|
||||
context "when setting the parent" do
|
||||
let(:new_parent_work_package) { create(:work_package) }
|
||||
let(:attributes) { [:parent] }
|
||||
|
||||
@@ -806,14 +806,14 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
context "with the parent being restricted in its ability to be moved" do
|
||||
let(:soonest_date) { Time.zone.today + 3.days }
|
||||
|
||||
it 'sets the start date and due date to the earliest possible date' do
|
||||
it "sets the start date and due date to the earliest possible date" do
|
||||
subject
|
||||
|
||||
expect(work_package.start_date).to eql(Time.zone.today + 3.days)
|
||||
expect(work_package.due_date).to eql(Time.zone.today + 3.days)
|
||||
end
|
||||
|
||||
it 'does not change the due date if after the newly set start date' do
|
||||
it "does not change the due date if after the newly set start date" do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
subject
|
||||
|
||||
@@ -822,7 +822,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with the parent being restricted but work package already having dates set' do
|
||||
context "with the parent being restricted but work package already having dates set" do
|
||||
let(:soonest_date) { Time.zone.today + 3.days }
|
||||
|
||||
before do
|
||||
@@ -830,7 +830,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
work_package.due_date = Time.zone.today + 5.days
|
||||
end
|
||||
|
||||
it 'sets the dates to provided dates' do
|
||||
it "sets the dates to provided dates" do
|
||||
subject
|
||||
|
||||
expect(work_package.start_date).to eql(Time.zone.today + 4.days)
|
||||
@@ -838,7 +838,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with the parent being restricted but the attributes define an earlier date' do
|
||||
context "with the parent being restricted but the attributes define an earlier date" do
|
||||
let(:soonest_date) { Time.zone.today + 3.days }
|
||||
|
||||
before do
|
||||
@@ -848,7 +848,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
|
||||
# This would be invalid but the dates should be set nevertheless
|
||||
# so we can have a correct error handling.
|
||||
it 'sets the dates to provided dates' do
|
||||
it "sets the dates to provided dates" do
|
||||
subject
|
||||
|
||||
expect(work_package.start_date).to eql(Time.zone.today + 1.day)
|
||||
@@ -857,7 +857,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with deep hierarchy of work packages' do
|
||||
context "with deep hierarchy of work packages" do
|
||||
before do
|
||||
work_package.due_date = Time.zone.today - 5.days
|
||||
end
|
||||
@@ -872,7 +872,7 @@ RSpec.describe WorkPackages::SetScheduleService do
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not fail with a SystemStackError (regression #43894)' do
|
||||
it "does not fail with a SystemStackError (regression #43894)" do
|
||||
parent = create(:work_package, start_date: Date.current, due_date: Date.current)
|
||||
hierarchy = [1, 1, 1, 1, 2, 4, 4, 4]
|
||||
create_hierarchy(parent, hierarchy)
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
# ++
|
||||
|
||||
require 'support/components/common/modal'
|
||||
require 'support/components/autocompleter/ng_select_autocomplete_helpers'
|
||||
require "support/components/common/modal"
|
||||
require "support/components/autocompleter/ng_select_autocomplete_helpers"
|
||||
|
||||
module Components
|
||||
module Projects
|
||||
@@ -71,7 +71,7 @@ module Components
|
||||
|
||||
def close_via_button
|
||||
within(dialog_css_selector) do
|
||||
click_link_or_button 'Cancel'
|
||||
click_link_or_button "Cancel"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -96,8 +96,8 @@ module Components
|
||||
###
|
||||
|
||||
def input_containers
|
||||
within '#project-section-edit-form > .FormControl-spacingWrapper' do
|
||||
page.all('.FormControl-spacingWrapper')
|
||||
within "#project-section-edit-form > .FormControl-spacingWrapper" do
|
||||
page.all(".FormControl-spacingWrapper")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ module Pages
|
||||
end
|
||||
|
||||
def mark_all_read
|
||||
click_button 'Mark all as read'
|
||||
click_button "Mark all as read"
|
||||
end
|
||||
|
||||
def mark_notification_as_read(notification)
|
||||
@@ -50,13 +50,13 @@ module Pages
|
||||
end
|
||||
|
||||
def show_all
|
||||
click_button 'All'
|
||||
click_button "All"
|
||||
end
|
||||
|
||||
def item_title(notification)
|
||||
text = notification.resource.is_a?(WorkPackage) ? notification.resource.subject : notification.subject
|
||||
within_item(notification) do
|
||||
page.find('span', text:, exact_text: true)
|
||||
page.find("span", text:, exact_text: true)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -111,15 +111,15 @@ module Pages
|
||||
end
|
||||
|
||||
def expect_closed
|
||||
expect(page).to have_no_css('op-in-app-notification-center')
|
||||
expect(page).to have_no_css("op-in-app-notification-center")
|
||||
end
|
||||
|
||||
def expect_open
|
||||
expect(page).to have_css('op-in-app-notification-center')
|
||||
expect(page).to have_css("op-in-app-notification-center")
|
||||
end
|
||||
|
||||
def expect_empty
|
||||
expect(page).to have_text 'New notifications will appear here when there is activity that concerns you'
|
||||
expect(page).to have_text "New notifications will appear here when there is activity that concerns you"
|
||||
end
|
||||
|
||||
def expect_number_of_notifications(count)
|
||||
@@ -143,15 +143,15 @@ module Pages
|
||||
end
|
||||
|
||||
def expect_no_toaster
|
||||
expect(page).to have_no_css('.op-toast.-info', wait: 10)
|
||||
expect(page).to have_no_css(".op-toast.-info", wait: 10)
|
||||
end
|
||||
|
||||
def expect_toast
|
||||
expect(page).to have_css('.op-toast.-info', wait: 10)
|
||||
expect(page).to have_css(".op-toast.-info", wait: 10)
|
||||
end
|
||||
|
||||
def update_via_toaster
|
||||
page.find('.op-toast.-info a', wait: 10).click
|
||||
page.find(".op-toast.-info a", wait: 10).click
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user