diff --git a/app/components/open_project/common/attribute_component.html.erb b/app/components/open_project/common/attribute_component.html.erb index 3db882cd538c..5fa17c497919 100644 --- a/app/components/open_project/common/attribute_component.html.erb +++ b/app/components/open_project/common/attribute_component.html.erb @@ -1,24 +1,46 @@
<%= render( Primer::Beta::Text.new(tag: :div, classes: ['op-long-text-attribute--text', PARAGRAPH_CSS_CLASS], color: text_color, + style: "max-height: #{max_height};", data: { 'attribute-target': "descriptionText" - })) { short_text } %> + })) { short_text } + %> + + <%= render( + Primer::Beta::Text.new(tag: :div, + display: display_expand_button_value, + classes: 'op-long-text-attribute--text-hider', + data: { 'attribute-target': 'textHider' })) + %> + + <%= render( + Primer::Alpha::HiddenTextExpander.new(inline: false, + "aria-label": I18n.t('label_attribute_expand_text', attribute: name), + display: display_expand_button_value, + data: { + 'attribute-target': 'expandButton', + 'test-selector': 'expand-button' + }, + button_arguments: { 'data-show-dialog-id': id }, + classes: 'op-long-text-attribute--text-expander' + )) + %> <%= render( Primer::Alpha::Dialog.new(id: id, + data: { + 'test-selector': 'attribute-dialog' + }, title: name, size: :large)) do |component| - component.with_show_button(scheme: :link, - display: display_expand_button_value, - ml: 1, - data: { 'attribute-target': 'expandButton' }) { I18n.t('js.label_expand') } component.with_body(mt: 2) { full_text } component.with_header(variant: :large) end diff --git a/app/components/open_project/common/attribute_component.rb b/app/components/open_project/common/attribute_component.rb index 1182707b70a7..993b893d5ed5 100644 --- a/app/components/open_project/common/attribute_component.rb +++ b/app/components/open_project/common/attribute_component.rb @@ -32,16 +32,20 @@ module Common class AttributeComponent < Primer::Component attr_reader :id, :name, - :description + :description, + :lines, + :background_reference_id PARAGRAPH_CSS_CLASS = "op-uc-p".freeze - def initialize(id, name, description, **args) + def initialize(id, name, description, lines: 1, background_reference_id: "content", **args) super @id = id @name = name @description = description @system_arguments = args + @lines = lines + @background_reference_id = background_reference_id end def short_text @@ -64,6 +68,10 @@ def text_color :muted if multi_type? end + def max_height + "#{lines * 1.6}em" + end + private def first_paragraph @@ -88,7 +96,7 @@ def body_children end def multi_type? - first_paragraph.include?("figure") || first_paragraph.include?("macro") + first_paragraph.include?("figure") || first_paragraph.include?("macro") || (body_children.any? && first_paragraph.blank?) end end end diff --git a/app/components/open_project/common/attribute_component.sass b/app/components/open_project/common/attribute_component.sass index ff6a4ab26583..126d434e06b9 100644 --- a/app/components/open_project/common/attribute_component.sass +++ b/app/components/open_project/common/attribute_component.sass @@ -1,7 +1,17 @@ .op-long-text-attribute - display: flex - align-items: center + position: relative + &--text - @include text-shortener + overflow: hidden margin: 0 - flex-grow: 1 + &--text-hider + position: absolute + bottom: 0 + right: 0 + height: 1.5em + width: 2em + &--text-expander + position: absolute + bottom: 1px + right: 0 + float: right diff --git a/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.html.erb b/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.html.erb index 8e538fb20a36..374bb4ed0642 100644 --- a/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.html.erb +++ b/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.html.erb @@ -1,15 +1,15 @@ <%= component_wrapper do flex_layout(align_items: :center, justify_content: :space_between, classes: 'op-project-custom-field', data: { - qa_selector: "project-custom-field-#{@project_custom_field.id}" + test_selector: "project-custom-field-#{@project_custom_field.id}" }) do |custom_field_container| custom_field_container.with_column(flex_layout: true) do |title_container| title_container.with_column(pt: 1, mr: 2) do - render(Primer::Beta::Text.new) do + render(Primer::Beta::Text.new) do @project_custom_field.name end end - title_container.with_column(pt: 1, mr: 2, data: { qa_selector: "custom-field-type" } ) do + title_container.with_column(pt: 1, mr: 2, data: { test_selector: "custom-field-type" } ) do render(Primer::Beta::Text.new(font_size: :small, color: :subtle)) do @project_custom_field.field_format.capitalize end @@ -37,7 +37,7 @@ ), csrf_token: form_authenticity_token, data: { 'turbo-method': :put, 'turbo-stream': true, - qa_selector: "toggle-project-custom-field-mapping-#{@project_custom_field.id}" }, + test_selector: "toggle-project-custom-field-mapping-#{@project_custom_field.id}" }, checked: active_in_project?, enabled: !@project_custom_field.required?, # required fields cannot be disabled size: :small, diff --git a/app/components/projects/settings/project_custom_field_sections/show_component.html.erb b/app/components/projects/settings/project_custom_field_sections/show_component.html.erb index 58f145a2f550..e80f89bca076 100644 --- a/app/components/projects/settings/project_custom_field_sections/show_component.html.erb +++ b/app/components/projects/settings/project_custom_field_sections/show_component.html.erb @@ -1,7 +1,7 @@ <%= component_wrapper do render(Primer::Beta::BorderBox.new(mt: 3, classes: 'op-project-custom-field-section', data: { - qa_selector: "project-custom-field-section-#{@project_custom_field_section.id}" + test_selector: "project-custom-field-section-#{@project_custom_field_section.id}" })) do |component| component.with_header(font_weight: :bold, py: 2) do flex_layout(justify_content: :space_between, align_items: :center) do |section_header_container| @@ -26,7 +26,7 @@ font_weight: :bold, color: :subtle, 'aria-label': t('projects.settings.project_custom_fields.actions.label_enable_all'), - data: { 'turbo-method': :put, 'turbo-stream': true, qa_selector: "enable-all-project-custom-field-mappings-#{@project_custom_field_section.id}" } + data: { 'turbo-method': :put, 'turbo-stream': true, test_selector: "enable-all-project-custom-field-mappings-#{@project_custom_field_section.id}" } )) do |button| button.with_leading_visual_icon(icon: 'check-circle', color: :subtle) t('projects.settings.project_custom_fields.actions.label_enable_all') @@ -45,7 +45,7 @@ font_weight: :bold, color: :subtle, 'aria-label': t('projects.settings.project_custom_fields.actions.label_disable_all'), - data: { 'turbo-method': :put, 'turbo-stream': true, qa_selector: "disable-all-project-custom-field-mappings-#{@project_custom_field_section.id}" } + data: { 'turbo-method': :put, 'turbo-stream': true, test_selector: "disable-all-project-custom-field-mappings-#{@project_custom_field_section.id}" } )) do |button| button.with_leading_visual_icon(icon: 'x-circle', color: :subtle) t('projects.settings.project_custom_fields.actions.label_disable_all') diff --git a/app/components/settings/project_custom_field_sections/custom_field_row_component.html.erb b/app/components/settings/project_custom_field_sections/custom_field_row_component.html.erb index bba637366eb8..7d87bb1dbc01 100644 --- a/app/components/settings/project_custom_field_sections/custom_field_row_component.html.erb +++ b/app/components/settings/project_custom_field_sections/custom_field_row_component.html.erb @@ -1,5 +1,5 @@ <%= - component_wrapper(class: "op-project-custom-field-container", data: { qa_selector: "project-custom-field-container-#{@project_custom_field.id}" }) do + component_wrapper(class: "op-project-custom-field-container", data: { test_selector: "project-custom-field-container-#{@project_custom_field.id}" }) do flex_layout(justify_content: :space_between, align_items: :center) do |main_container| main_container.with_column(flex_layout: true, align_items: :center) do |content_container| content_container.with_column(mr: 2) do @@ -36,7 +36,7 @@ end end main_container.with_column do - render(Primer::Alpha::ActionMenu.new(data: { qa_selector: "project-custom-field-action-menu" })) do |menu| + render(Primer::Alpha::ActionMenu.new(data: { test_selector: "project-custom-field-action-menu" })) do |menu| menu.with_show_button(icon: "kebab-horizontal", 'aria-label': t("settings.project_attributes.label_project_custom_field_actions"), scheme: :invisible) edit_action_item(menu) move_actions(menu) diff --git a/app/components/settings/project_custom_field_sections/custom_field_row_component.rb b/app/components/settings/project_custom_field_sections/custom_field_row_component.rb index 765ec50dc0db..b57eb096fff1 100644 --- a/app/components/settings/project_custom_field_sections/custom_field_row_component.rb +++ b/app/components/settings/project_custom_field_sections/custom_field_row_component.rb @@ -45,7 +45,7 @@ def initialize(project_custom_field:, first_and_last:) def edit_action_item(menu) menu.with_item(label: t("label_edit"), href: edit_admin_settings_project_custom_field_path(@project_custom_field), - data: { turbo: "false", qa_selector: "project-custom-field-edit" }) do |item| + data: { turbo: "false", test_selector: "project-custom-field-edit" }) do |item| item.with_leading_visual_icon(icon: :pencil) end end @@ -68,7 +68,7 @@ def move_action_item(menu, move_to, label_text, icon) menu.with_item(label: label_text, href: move_admin_settings_project_custom_field_path(@project_custom_field, move_to:), form_arguments: { - method: :put, data: { 'turbo-stream': true, qa_selector: "project-custom-field-move-#{move_to}" } + method: :put, data: { "turbo-stream": true, test_selector: "project-custom-field-move-#{move_to}" } }) do |item| item.with_leading_visual_icon(icon:) end @@ -79,8 +79,8 @@ def delete_action_item(menu) scheme: :danger, href: admin_settings_project_custom_field_path(@project_custom_field), form_arguments: { - method: :delete, data: { confirm: t("text_are_you_sure"), 'turbo-stream': true, - qa_selector: "project-custom-field-delete" } + method: :delete, data: { confirm: t("text_are_you_sure"), "turbo-stream": true, + test_selector: "project-custom-field-delete" } }) do |item| item.with_leading_visual_icon(icon: :trash) end diff --git a/app/components/settings/project_custom_field_sections/show_component.html.erb b/app/components/settings/project_custom_field_sections/show_component.html.erb index ea991c887388..c8db95da20e0 100644 --- a/app/components/settings/project_custom_field_sections/show_component.html.erb +++ b/app/components/settings/project_custom_field_sections/show_component.html.erb @@ -1,5 +1,5 @@ <%= - component_wrapper(class: "op-project-custom-field-section-container", data: { qa_selector: "project-custom-field-section-container-#{@project_custom_field_section.id}" }) do + component_wrapper(class: "op-project-custom-field-section-container", data: { test_selector: "project-custom-field-section-container-#{@project_custom_field_section.id}" }) do render(Primer::Beta::BorderBox.new(mt: 3, data: drag_and_drop_target_config)) do |component| component.with_header(font_weight: :bold) do flex_layout(justify_content: :space_between, align_items: :center) do |section_header_container| @@ -15,7 +15,7 @@ end section_header_container.with_column(flex_layout: true, justify_content: :flex_end) do |actions_container| actions_container.with_column do - render(Primer::Alpha::ActionMenu.new(data: { qa_selector: "project-custom-field-section-action-menu" })) do |menu| + render(Primer::Alpha::ActionMenu.new(data: { test_selector: "project-custom-field-section-action-menu" })) do |menu| menu.with_show_button(icon: "kebab-horizontal", 'aria-label': t("settings.project_attributes.label_section_actions"), scheme: :invisible) edit_action_item(menu) move_actions(menu) @@ -50,7 +50,7 @@ type: "ProjectCustomField", custom_field_section_id: @project_custom_field_section.id ), scheme: :secondary, - data: { turbo: "false", qa_selector: "new-project-custom-field-button" } + data: { turbo: "false", test_selector: "new-project-custom-field-button" } )) do |button| button.with_leading_visual_icon(icon: :plus) t('settings.project_attributes.label_new_attribute') diff --git a/app/components/settings/project_custom_field_sections/show_component.rb b/app/components/settings/project_custom_field_sections/show_component.rb index 76fa3c16a738..cc1af53fa9c5 100644 --- a/app/components/settings/project_custom_field_sections/show_component.rb +++ b/app/components/settings/project_custom_field_sections/show_component.rb @@ -49,18 +49,18 @@ def wrapper_uniq_by def drag_and_drop_target_config { - 'is-drag-and-drop-target': true, - 'target-container-accessor': '.Box > ul', # the accessor of the container that contains the drag and drop items - 'target-id': @project_custom_field_section.id, # the id of the target - 'target-allowed-drag-type': 'custom-field' # the type of dragged items which are allowed to be dropped in this target + "is-drag-and-drop-target": true, + "target-container-accessor": ".Box > ul", # the accessor of the container that contains the drag and drop items + "target-id": @project_custom_field_section.id, # the id of the target + "target-allowed-drag-type": "custom-field" # the type of dragged items which are allowed to be dropped in this target } end def draggable_item_config(project_custom_field) { - 'draggable-id': project_custom_field.id, - 'draggable-type': 'custom-field', - 'drop-url': drop_admin_settings_project_custom_field_path(project_custom_field) + "draggable-id": project_custom_field.id, + "draggable-type": "custom-field", + "drop-url": drop_admin_settings_project_custom_field_path(project_custom_field) } end @@ -82,7 +82,8 @@ def move_action_item(menu, move_to, label_text, icon) menu.with_item(label: label_text, href: move_admin_settings_project_custom_field_section_path(@project_custom_field_section, move_to:), form_arguments: { - method: :put, data: { 'turbo-stream': true, qa_selector: "project-custom-field-section-move-#{move_to}" } + method: :put, data: { "turbo-stream": true, + test_selector: "project-custom-field-section-move-#{move_to}" } }) do |item| item.with_leading_visual_icon(icon:) end @@ -99,8 +100,8 @@ def edit_action_item(menu) menu.with_item(label: t("settings.project_attributes.label_edit_section"), tag: :button, content_arguments: { - 'data-show-dialog-id': "project-custom-field-section-dialog#{@project_custom_field_section.id}", - 'data-qa-selector': "project-custom-field-section-edit" + "data-show-dialog-id": "project-custom-field-section-dialog#{@project_custom_field_section.id}", + "data-test-selector": "project-custom-field-section-edit" }, value: "") do |item| item.with_leading_visual_icon(icon: :pencil) @@ -112,8 +113,8 @@ def delete_action_item(menu) scheme: :danger, href: admin_settings_project_custom_field_section_path(@project_custom_field_section), form_arguments: { - method: :delete, data: { confirm: t("text_are_you_sure"), 'turbo-stream': true, - qa_selector: "project-custom-field-section-delete" } + method: :delete, data: { confirm: t("text_are_you_sure"), "turbo-stream": true, + test_selector: "project-custom-field-section-delete" } }) do |item| item.with_leading_visual_icon(icon: :trash) end diff --git a/app/components/settings/project_custom_fields/header_component.html.erb b/app/components/settings/project_custom_fields/header_component.html.erb index 0b1d0169aa7c..3045d6f46ed2 100644 --- a/app/components/settings/project_custom_fields/header_component.html.erb +++ b/app/components/settings/project_custom_fields/header_component.html.erb @@ -1,6 +1,6 @@ <%= - component_wrapper do + component_wrapper do render Primer::OpenProject::PageHeader.new do |header| header.with_title(variant: :default) { t('settings.project_attributes.heading') } header.with_description { t('settings.project_attributes.heading_description') } @@ -11,7 +11,7 @@ tag: :a, href: new_admin_settings_project_custom_field_path(type: "ProjectCustomField"), scheme: :primary, - data: { turbo: "false", qa_selector: "new-project-custom-field-button" } + data: { turbo: "false", test_selector: "new-project-custom-field-button" } )) do |button| button.with_leading_visual_icon(icon: :plus) t('settings.project_attributes.label_new_attribute') diff --git a/config/locales/en.yml b/config/locales/en.yml index 48074c49711a..40f0301584e6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1821,6 +1821,7 @@ Project attributes and sections are defined in the truncation_length - render_truncated_preview_and_dialog_for_rich_text_value(truncation_length) - else - render(Primer::Beta::Text.new) do - format_value(@project_custom_field_values.first&.value, @project_custom_field) - end - end - end - - def render_truncated_preview_and_dialog_for_rich_text_value(truncation_length) - flex_layout do |rich_text_preview_container| - rich_text_preview_container.with_row do - render(Primer::Beta::Text.new(classes: 'project-custom-fields-rich-text-preview')) do - format_value( - @project_custom_field_values.first&.value&.truncate(truncation_length), - @project_custom_field - ) - end + render_dialog - end - end - end - - def render_dialog - render(Primer::Alpha::Dialog.new(size: :medium_portrait, title: @project_custom_field.name)) do |dialog| - dialog.with_show_button(scheme: :link) { t(:label_expand) } - # TODO: remove inline style - dialog.with_body(style: "max-height: 500px;") do - format_value(@project_custom_field_values.first&.value, @project_custom_field) - end - end + def render_long_text + render OpenProject::Common::AttributeComponent.new("dialog-cf-#{@project_custom_field.id}", + @project_custom_field.name, + @project_custom_field_values&.first&.value, + lines: 3) end def render_user diff --git a/modules/overviews/app/components/project_custom_fields/sections/show_component.html.erb b/modules/overviews/app/components/project_custom_fields/sections/show_component.html.erb index b42340b6e217..2915d4781864 100644 --- a/modules/overviews/app/components/project_custom_fields/sections/show_component.html.erb +++ b/modules/overviews/app/components/project_custom_fields/sections/show_component.html.erb @@ -1,7 +1,7 @@ <%= component_wrapper do flex_layout(border: :bottom, pb: 2, classes: 'op-project-custom-field-section-container', data: { - qa_selector: "project-custom-field-section-#{@project_custom_field_section.id}" + test_selector: "project-custom-field-section-#{@project_custom_field_section.id}" }) do |details_container| details_container.with_row(mb: 2) do flex_layout(align_items: :center, justify_content: :space_between) do |heading| @@ -18,7 +18,7 @@ button_icon: :pencil, button_icon_label: t(:label_edit), button_attributes: { scheme: :invisible, data: { - qa_selector: "project-custom-field-section-edit-button" + test_selector: "project-custom-field-section-edit-button" } } )) end if allowed_to_edit? diff --git a/modules/overviews/app/components/project_custom_fields/sidebar_component.html.erb b/modules/overviews/app/components/project_custom_fields/sidebar_component.html.erb index 6f8f746c66ec..efdff6c6d02e 100644 --- a/modules/overviews/app/components/project_custom_fields/sidebar_component.html.erb +++ b/modules/overviews/app/components/project_custom_fields/sidebar_component.html.erb @@ -1,7 +1,7 @@ <%= component_wrapper do if available_project_custom_fields_grouped_by_section.any? - flex_layout(data: { qa_selector: "project-custom-fields-sidebar-async-content" }) do |sections_container| + flex_layout(data: { test_selector: "project-custom-fields-sidebar-async-content" }) do |sections_container| available_project_custom_fields_grouped_by_section.each do |project_custom_field_section, project_custom_fields| sections_container.with_row(mb: 3) do render(ProjectCustomFields::Sections::ShowComponent.new( diff --git a/spec/features/admin/project_custom_fields/list_spec.rb b/spec/features/admin/project_custom_fields/list_spec.rb index b01692b6606a..2c07ed5e272f 100644 --- a/spec/features/admin/project_custom_fields/list_spec.rb +++ b/spec/features/admin/project_custom_fields/list_spec.rb @@ -87,7 +87,8 @@ end end - expect(page).to have_no_css("[data-qa-selector='project-custom-field-section-container-#{section_for_multi_select_fields.id}']") + expect(page) + .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 @@ -188,7 +189,7 @@ end end - expect(page).to have_no_css("[data-qa-selector='project-custom-field-container-#{boolean_project_custom_field.id}']") + 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 @@ -208,14 +209,14 @@ end it 'redirects to the custom field new page via header menu button' do - page.find("[data-qa-selector='new-project-custom-field-button']").click + 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')) end 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-qa-selector='new-project-custom-field-button']") + expect(page).to have_no_css("[data-test-selector='new-project-custom-field-button']") end multi_list_project_custom_field.destroy @@ -225,7 +226,7 @@ visit admin_settings_project_custom_fields_path within_project_custom_field_section_container(section_for_multi_select_fields) do - page.find("[data-qa-selector='new-project-custom-field-button']").click + page.find("[data-test-selector='new-project-custom-field-button']").click end expect(page).to have_current_path(new_admin_settings_project_custom_field_path( @@ -239,12 +240,12 @@ # helper methods: def within_project_custom_field_section_container(section, &block) - within("[data-qa-selector='project-custom-field-section-container-#{section.id}']", &block) + within("[data-test-selector='project-custom-field-section-container-#{section.id}']", &block) end def within_project_custom_field_section_menu(section, &block) within_project_custom_field_section_container(section) do - page.find("[data-qa-selector='project-custom-field-section-action-menu']").click + page.find("[data-test-selector='project-custom-field-section-action-menu']").click within('anchored-position', &block) end end @@ -257,12 +258,12 @@ def perform_action_for_project_custom_field_section(section, action) end def within_project_custom_field_container(custom_field, &block) - within("[data-qa-selector='project-custom-field-container-#{custom_field.id}']", &block) + within("[data-test-selector='project-custom-field-container-#{custom_field.id}']", &block) end def within_project_custom_field_menu(section, &block) within_project_custom_field_container(section) do - page.find("[data-qa-selector='project-custom-field-action-menu']").click + page.find("[data-test-selector='project-custom-field-action-menu']").click within('anchored-position', &block) end end diff --git a/spec/features/projects/project_custom_fields/overview_page/dialog/permission_spec.rb b/spec/features/projects/project_custom_fields/overview_page/dialog/permission_spec.rb index 60c7414eaf94..aec9e99269a7 100644 --- a/spec/features/projects/project_custom_fields/overview_page/dialog/permission_spec.rb +++ b/spec/features/projects/project_custom_fields/overview_page/dialog/permission_spec.rb @@ -45,7 +45,7 @@ it 'does not show the edit buttons' do overview_page.within_async_loaded_sidebar do - expect(page).to have_no_css("[data-qa-selector='project-custom-field-section-edit-button']") + expect(page).to have_no_css("[data-test-selector='project-custom-field-section-edit-button']") end end end @@ -58,7 +58,7 @@ it 'shows the edit buttons' do overview_page.within_async_loaded_sidebar do - expect(page).to have_css("[data-qa-selector='project-custom-field-section-edit-button']", count: 3) + expect(page).to have_css("[data-test-selector='project-custom-field-section-edit-button']", count: 3) end end end diff --git a/spec/features/projects/project_custom_fields/overview_page/dialog/update_spec.rb b/spec/features/projects/project_custom_fields/overview_page/dialog/update_spec.rb index 15f61de8bbe9..1e7dbddd4dc3 100644 --- a/spec/features/projects/project_custom_fields/overview_page/dialog/update_spec.rb +++ b/spec/features/projects/project_custom_fields/overview_page/dialog/update_spec.rb @@ -269,9 +269,9 @@ describe "with text CF" do let(:custom_field) { text_project_custom_field } let(:field) { FormFields::Primerized::EditorFormField.new(custom_field) } - let(:expected_initial_value) { "Lorem\nipsum" } # TBD: why is the second newline missing? - let(:update_value) { "Dolor\n\nsit" } - let(:expected_updated_value) { "Dolor\nsit" } + let(:expected_initial_value) { "Lorem" } + let(:update_value) { "Dolor sit" } + let(:expected_updated_value) { "Dolor sit" } it_behaves_like "a rich text custom field input" end diff --git a/spec/features/projects/project_custom_fields/overview_page/sidebar_spec.rb b/spec/features/projects/project_custom_fields/overview_page/sidebar_spec.rb index 4eae8b7909e2..446a9d9d38aa 100644 --- a/spec/features/projects/project_custom_fields/overview_page/sidebar_spec.rb +++ b/spec/features/projects/project_custom_fields/overview_page/sidebar_spec.rb @@ -146,10 +146,6 @@ describe "with correct values" do describe "with boolean CF" do - # it_behaves_like 'a project custom field' do - # let(subject) { boolean_project_custom_field } - # end - describe "with value set by user" do it "shows the correct value for the project custom field if given" do overview_page.visit_page @@ -473,22 +469,30 @@ describe "with text CF" do describe "with value set by user" do - context "with a value that is shorter than 100 characters" do + context "with a value that does not have a line break and spans less than 3 lines" do + before do + text_project_custom_field.custom_values.where(customized: project).first.update!(value: "Lorem ipsum") + end + it "shows the correct value for the project custom field if given without truncation and dialog button" do overview_page.visit_page overview_page.within_async_loaded_sidebar do overview_page.within_custom_field_container(text_project_custom_field) do expect(page).to have_text "Text field" - expect(page).to have_text "Lorem\nipsum" + expect(page).to have_text "Lorem ipsum" end + + overview_page.expect_text_not_truncated(text_project_custom_field) end end end - context "with a value that is longer than 100 characters" do + context "with a value that spans more than three lines" do + let(:cf_value) { ("lorem " * 100).strip } + before do - text_project_custom_field.custom_values.where(customized: project).first.update!(value: "a" * 101) + text_project_custom_field.custom_values.where(customized: project).first.update!(value: cf_value) end it "shows the correct value for the project custom field if given with truncation and dialog button" do @@ -497,16 +501,14 @@ overview_page.within_async_loaded_sidebar do overview_page.within_custom_field_container(text_project_custom_field) do expect(page).to have_text "Text field" - expect(page).to have_text ("#{'a' * 97}...") - expect(page).to have_text "Expand" - - click_on "Expand" - - within "dialog" do - expect(page).to have_text "a" * 101 - end + expect(page).to have_text (("lorem " * 5).to_s) end + + overview_page.expect_text_truncated(text_project_custom_field) + overview_page.expand_text(text_project_custom_field) end + + overview_page.expect_full_text_in_dialog(cf_value) end end end @@ -524,6 +526,8 @@ expect(page).to have_text "Text field" expect(page).to have_text I18n.t("placeholders.default") end + + overview_page.expect_text_not_truncated(text_project_custom_field) end end end @@ -541,6 +545,8 @@ expect(page).to have_text "Text field" expect(page).to have_text I18n.t("placeholders.default") end + + overview_page.expect_text_not_truncated(text_project_custom_field) end end @@ -554,6 +560,8 @@ expect(page).to have_text "Text field" expect(page).to have_text I18n.t("placeholders.default") end + + overview_page.expect_text_not_truncated(text_project_custom_field) end end end @@ -718,14 +726,6 @@ end end end - - describe "with support for user groups" do - # TODO - end - - describe "with support for user placeholders" do - # TODO - end end describe "with multi list CF" do diff --git a/spec/features/projects/project_custom_fields/settings/mapping_spec.rb b/spec/features/projects/project_custom_fields/settings/mapping_spec.rb index 61db89df1e04..faf9ff175d9b 100644 --- a/spec/features/projects/project_custom_fields/settings/mapping_spec.rb +++ b/spec/features/projects/project_custom_fields/settings/mapping_spec.rb @@ -169,7 +169,9 @@ within_custom_field_container(boolean_project_custom_field) do expect_unchecked_state - page.find("[data-qa-selector='toggle-project-custom-field-mapping-#{boolean_project_custom_field.id}'] > button").click + page + .find("[data-test-selector='toggle-project-custom-field-mapping-#{boolean_project_custom_field.id}'] > button") + .click expect_checked_state # without reloading the page end @@ -194,7 +196,7 @@ visit project_settings_project_custom_fields_path(project) within_custom_field_section_container(section_for_input_fields) do - page.find("[data-qa-selector='enable-all-project-custom-field-mappings-#{section_for_input_fields.id}']").click + page.find("[data-test-selector='enable-all-project-custom-field-mappings-#{section_for_input_fields.id}']").click within_custom_field_container(boolean_project_custom_field) do expect_checked_state @@ -221,7 +223,7 @@ visit project_settings_project_custom_fields_path(project) within_custom_field_section_container(section_for_input_fields) do - page.find("[data-qa-selector='enable-all-project-custom-field-mappings-#{section_for_input_fields.id}']").click + page.find("[data-test-selector='enable-all-project-custom-field-mappings-#{section_for_input_fields.id}']").click within_custom_field_container(boolean_project_custom_field) do expect_checked_state @@ -244,7 +246,7 @@ end within_custom_field_section_container(section_for_input_fields) do - page.find("[data-qa-selector='disable-all-project-custom-field-mappings-#{section_for_input_fields.id}']").click + page.find("[data-test-selector='disable-all-project-custom-field-mappings-#{section_for_input_fields.id}']").click within_custom_field_container(boolean_project_custom_field) do expect_unchecked_state @@ -362,7 +364,9 @@ 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-qa-selector='disable-all-project-custom-field-mappings-#{section_with_invisible_fields.id}']").click + page + .find("[data-test-selector='disable-all-project-custom-field-mappings-#{section_with_invisible_fields.id}']") + .click within_custom_field_container(visible_project_custom_field) do expect_unchecked_state @@ -371,7 +375,9 @@ expect_unchecked_state end - page.find("[data-qa-selector='enable-all-project-custom-field-mappings-#{section_with_invisible_fields.id}']").click + page + .find("[data-test-selector='enable-all-project-custom-field-mappings-#{section_with_invisible_fields.id}']") + .click within_custom_field_container(visible_project_custom_field) do expect_checked_state @@ -398,7 +404,9 @@ 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-qa-selector='disable-all-project-custom-field-mappings-#{section_with_invisible_fields.id}']").click + page + .find("[data-test-selector='disable-all-project-custom-field-mappings-#{section_with_invisible_fields.id}']") + .click within_custom_field_container(visible_project_custom_field) do expect_unchecked_state @@ -410,7 +418,7 @@ # disable manually project.project_custom_field_project_mappings.find_by(custom_field_id: invisible_project_custom_field.id).destroy! - page.find("[data-qa-selector='enable-all-project-custom-field-mappings-#{section_with_invisible_fields.id}']").click + page.find("[data-test-selector='enable-all-project-custom-field-mappings-#{section_with_invisible_fields.id}']").click within_custom_field_container(visible_project_custom_field) do expect_checked_state @@ -425,7 +433,7 @@ end def expect_type(type) - within "[data-qa-selector='custom-field-type']" do + within "[data-test-selector='custom-field-type']" do expect(page).to have_content(type) end end @@ -439,10 +447,10 @@ def expect_unchecked_state end def within_custom_field_section_container(section, &) - within("[data-qa-selector='project-custom-field-section-#{section.id}']", &) + within("[data-test-selector='project-custom-field-section-#{section.id}']", &) end def within_custom_field_container(custom_field, &) - within("[data-qa-selector='project-custom-field-#{custom_field.id}']", &) + within("[data-test-selector='project-custom-field-#{custom_field.id}']", &) end end diff --git a/spec/features/projects/projects_index_spec.rb b/spec/features/projects/projects_index_spec.rb index ab09bd73b957..635c4844be92 100644 --- a/spec/features/projects/projects_index_spec.rb +++ b/spec/features/projects/projects_index_spec.rb @@ -26,38 +26,38 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'spec_helper' +require "spec_helper" -RSpec.describe 'Projects index page', +RSpec.describe "Projects index page", :js, :with_cuprite, with_settings: { login_required?: false } do shared_let(:admin) { create(:admin) } - shared_let(:manager) { create(:project_role, name: 'Manager') } - shared_let(:developer) { create(:project_role, name: 'Developer') } + shared_let(:manager) { create(:project_role, name: "Manager") } + shared_let(:developer) { create(:project_role, name: "Developer") } shared_let(:custom_field) { create(:text_project_custom_field) } shared_let(:invisible_custom_field) { create(:project_custom_field, visible: false) } shared_let(:project) do create(:project, - name: 'Plain project', - identifier: 'plain-project') + name: "Plain project", + identifier: "plain-project") end shared_let(:public_project) do project = create(:project, - name: 'Public project', - identifier: 'public-project', + name: "Public project", + identifier: "public-project", public: true) - project.custom_field_values = { invisible_custom_field.id => 'Secret CF' } + project.custom_field_values = { invisible_custom_field.id => "Secret CF" } project.save project end shared_let(:development_project) do create(:project, - name: 'Development project', - identifier: 'development-project') + name: "Development project", + identifier: "development-project") end let(:news) { create(:news, project:) } @@ -87,9 +87,9 @@ def expect_projects_in_order(*projects) end end - describe 'project visibility restriction' do - context 'for an anonymous user' do - specify 'only public projects shall be visible' do + describe "project visibility restriction" do + context "for an anonymous user" do + specify "only public projects shall be visible" do ProjectRole.anonymous visit projects_path @@ -97,20 +97,20 @@ def expect_projects_in_order(*projects) expect(page).to have_text(public_project.name) # Test that the 'More' menu stays invisible on hover - expect(page).to have_no_css('.icon-show-more-horizontal') + expect(page).to have_no_css(".icon-show-more-horizontal") end end - context 'for project members', with_ee: %i[custom_fields_in_projects_list] do + context "for project members", with_ee: %i[custom_fields_in_projects_list] do shared_let(:user) do create(:user, member_with_roles: { development_project => developer }, - login: 'nerd', - firstname: 'Alan', - lastname: 'Turing') + login: "nerd", + firstname: "Alan", + lastname: "Turing") end - specify 'only public projects or those the user is a member of shall be visible' do + specify "only public projects or those the user is a member of shall be visible" do ProjectRole.non_member login_as(user) visit projects_path @@ -121,28 +121,28 @@ def expect_projects_in_order(*projects) # Non-admin users shall not see invisible CFs. expect(page).to have_no_text(invisible_custom_field.name.upcase) - expect(page).to have_no_select('add_filter_select', with_options: [invisible_custom_field.name]) + expect(page).to have_no_select("add_filter_select", with_options: [invisible_custom_field.name]) end end - context 'for work package members', with_ee: %i[custom_fields_in_projects_list] do + context "for work package members", with_ee: %i[custom_fields_in_projects_list] do shared_let(:work_package) { create(:work_package, project: development_project) } shared_let(:user) do create(:user, member_with_permissions: { work_package => [:view_work_packages] }, - login: 'nerd', - firstname: 'Alan', - lastname: 'Turing') + login: "nerd", + firstname: "Alan", + lastname: "Turing") end - specify 'only public projects or those the user is member in a specific work package' do + specify "only public projects or those the user is member in a specific work package" do Setting.enabled_projects_columns += [custom_field.column_name] development_project.update( - description: 'I am a nice project', - status_explanation: 'We are on track', - status_code: 'on_track', - custom_field_values: { custom_field.id => 'This is a test value' } + description: "I am a nice project", + status_explanation: "We are on track", + status_code: "on_track", + custom_field_values: { custom_field.id => "This is a test value" } ) login_as(user) @@ -162,14 +162,14 @@ def expect_projects_in_order(*projects) end end - context 'for admins' do + context "for admins" do before do - project.update(created_at: 7.days.ago, description: 'I am a nice project') + project.update(created_at: 7.days.ago, description: "I am a nice project") news end - specify 'all projects are visible' do + specify "all projects are visible" do login_as(admin) visit projects_path @@ -177,130 +177,133 @@ def expect_projects_in_order(*projects) expect(page).to have_text(project.name) # Test visibility of 'more' menu list items - item = page.first('tbody tr .icon-show-more-horizontal', visible: :all) + item = page.first("tbody tr .icon-show-more-horizontal", visible: :all) item.hover item.click - menu = page.first('tbody tr .project-actions') - expect(menu).to have_text('Copy') - expect(menu).to have_text('Project settings') - expect(menu).to have_text('New subproject') - expect(menu).to have_text('Delete') - expect(menu).to have_text('Archive') + menu = page.first("tbody tr .project-actions") + expect(menu).to have_text("Copy") + expect(menu).to have_text("Project settings") + expect(menu).to have_text("New subproject") + expect(menu).to have_text("Delete") + expect(menu).to have_text("Archive") # Test visibility of admin only properties - within('#project-table') do + within("#project-table") do expect(page) - .to have_css('th', text: 'REQUIRED DISK STORAGE') + .to have_css("th", text: "REQUIRED DISK STORAGE") expect(page) - .to have_css('th', text: 'CREATED ON') + .to have_css("th", text: "CREATED ON") expect(page) - .to have_css('td', text: project.created_at.strftime('%m/%d/%Y')) + .to have_css("td", text: project.created_at.strftime("%m/%d/%Y")) expect(page) - .to have_css('th', text: 'LATEST ACTIVITY AT') + .to have_css("th", text: "LATEST ACTIVITY AT") expect(page) - .to have_css('td', text: news.created_at.strftime('%m/%d/%Y')) + .to have_css("td", text: news.created_at.strftime("%m/%d/%Y")) end end - specify 'flash sortBy is being escaped' do + specify "flash sortBy is being escaped" do login_as(admin) visit projects_path(sortBy: "[[\">\",\"\"]]") error_text = "Orders > is not set to one of the allowed values. and does not exist." error_html = "Orders ><script src='/foobar js'></script> is not set to one of the allowed values. and does not exist." - expect(page).to have_css('.op-toast.-error', text: error_text) + expect(page).to have_css(".op-toast.-error", text: error_text) - error_container = page.find('.op-toast.-error') - expect(error_container['innerHTML']).to include error_html + error_container = page.find(".op-toast.-error") + expect(error_container["innerHTML"]).to include error_html end end end - context 'without valid Enterprise token' do - specify 'CF columns and filters are not visible' do + context "without valid Enterprise token" do + specify "CF columns and filters are not visible" do load_and_open_filters admin # CF's columns are not present: expect(page).to have_no_text(custom_field.name.upcase) # CF's filters are not present: - expect(page).to have_no_select('add_filter_select', with_options: [custom_field.name]) + expect(page).to have_no_select("add_filter_select", with_options: [custom_field.name]) end end - context 'with valid Enterprise token', with_ee: %i[custom_fields_in_projects_list] do + context "with valid Enterprise token", with_ee: %i[custom_fields_in_projects_list] do shared_let(:long_text_custom_field) { create(:text_project_custom_field) } - specify 'CF columns and filters are not visible by default' do + specify "CF columns and filters are not visible by default" do load_and_open_filters admin # CF's columns are not shown due to setting expect(page).to have_no_text(custom_field.name.upcase) end - specify 'CF columns and filters are visible when added to settings' do + specify "CF columns and filters are visible when added to settings" do Setting.enabled_projects_columns += [custom_field.column_name, invisible_custom_field.column_name] load_and_open_filters admin # CF's column is present: expect(page).to have_text(custom_field.name.upcase) # CF's filter is present: - expect(page).to have_select('add_filter_select', with_options: [custom_field.name]) + expect(page).to have_select("add_filter_select", with_options: [custom_field.name]) # Admins shall be the only ones to see invisible CFs expect(page).to have_text(invisible_custom_field.name.upcase) - expect(page).to have_select('add_filter_select', with_options: [invisible_custom_field.name]) + expect(page).to have_select("add_filter_select", with_options: [invisible_custom_field.name]) end - specify 'long-text fields are truncated' do + specify "long-text fields are truncated" do development_project.update( - description: 'I am a nice project with a very long long long long long long long long long description', - status_explanation: '
I am a nice project status description with a figure
', - custom_field_values: { custom_field.id => 'This is a short value', - long_text_custom_field.id => 'This is a very long long long long long long long value' } + description: "I am a nice project with a very long long long long long long long long long description", + status_explanation: "
I am a nice project status description with a figure
", + custom_field_values: { custom_field.id => "This is a short value", + long_text_custom_field.id => "This is a very long long long long long long long value" } ) development_project.save! login_as(admin) - Setting.enabled_projects_columns += [custom_field.column_name, long_text_custom_field.column_name, 'description', - 'status_explanation'] + Setting.enabled_projects_columns += [custom_field.column_name, long_text_custom_field.column_name, "description", + "status_explanation"] projects_page.visit! - tr_project_development = page.first('tr', text: 'Development project') - # Check if the description is truncated and shows the Expand button correctly - td_project_development_description = tr_project_development.first('td.description') - expect(td_project_development_description).to have_css('button', text: 'Expand') - td_project_development_description.find('button', text: 'Expand').click - expect(page).to have_css('.Overlay-body', text: development_project.description) + projects_page.within_row(development_project) do + expect(page).to have_css('td.description [data-test-selector="expand-button"]') + page.find('td.description [data-test-selector="expand-button"]').click + end + + expect(page).to have_css(".Overlay-body", text: development_project.description) # Check if the status explanation with an html tag is truncated and shows the cell text and Expand button correctly - td_project_development_status_description = tr_project_development.first('td.status_explanation') - expect(td_project_development_status_description).to have_css('button', text: 'Expand') - expect(td_project_development_status_description).to have_text('Preview not available') + projects_page.within_row(development_project) do + expect(page).to have_css('td.status_explanation [data-test-selector="expand-button"]') + expect(page).to have_css("td.status_explanation", text: "Preview not available") + end # Check if a long-text custom field which has a short text as value is not truncated and there is no Expand button there - td_project_development_short_cf = tr_project_development.first('td', text: 'This is a short value') - expect(td_project_development_short_cf).to have_no_css('button', text: 'Expand') + projects_page.within_row(development_project) do + expect(page).to have_no_css("td.cf_#{custom_field.id} [data-test-selector=\"expand-button\"]") + expect(page).to have_css("td.cf_#{custom_field.id}", text: "This is a short value") + end # Check if a long-text custom field which has a long text as value is truncated and there is an Expand button there - td_project_development_long_cf = tr_project_development.first( - 'td', - text: 'This is a very long long long long long long long value' - ) - expect(td_project_development_long_cf).to have_css('button', text: 'Expand') + projects_page.within_row(development_project) do + expect(page).to have_css("td.cf_#{long_text_custom_field.id} [data-test-selector=\"expand-button\"]") + expect(page).to have_css("td.cf_#{long_text_custom_field.id}", + text: "This is a very long long long long long long long value") + end end end - context 'with a filter set' do - it 'only shows the matching projects and filters' do + context "with a filter set" do + it "only shows the matching projects and filters" do load_and_open_filters admin - projects_page.set_filter('name_and_identifier', - 'Name or identifier', - 'contains', - ['Plain']) + projects_page.set_filter("name_and_identifier", + "Name or identifier", + "contains", + ["Plain"]) - click_on 'Apply' + click_on "Apply" # Filter is applied: Only the project that contains the the word "Plain" gets listed projects_page.expect_projects_listed(project) projects_page.expect_projects_not_listed(public_project) @@ -309,54 +312,54 @@ def expect_projects_in_order(*projects) end end - context 'when paginating', with_settings: { enabled_projects_columns: %w[name project_status] } do + context "when paginating", with_settings: { enabled_projects_columns: %w[name project_status] } do before do allow(Setting).to receive(:per_page_options_array).and_return([1, 5]) end - it 'keeps applied filters, orders and columns' do + it "keeps applied filters, orders and columns" do load_and_open_filters admin - projects_page.set_filter('name_and_identifier', - 'Name or identifier', - 'doesn\'t contain', - ['Plain']) + projects_page.set_filter("name_and_identifier", + "Name or identifier", + "doesn't contain", + ["Plain"]) - click_on 'Apply' + click_on "Apply" wait_for_reload - projects_page.set_columns('Name') - projects_page.expect_columns('Name') + projects_page.set_columns("Name") + projects_page.expect_columns("Name") # Sorts ASC by name - projects_page.sort_by('Name') + projects_page.sort_by("Name") wait_for_reload # Results should be filtered and ordered ASC by name and only the selected columns should be present projects_page.expect_projects_listed(development_project) projects_page.expect_projects_not_listed(project, # as it is filtered out public_project) # as it is on the second page - projects_page.expect_columns('Name') - projects_page.expect_no_columns('Status') - expect(page).to have_text('Next') # as the result set is larger than 1 + projects_page.expect_columns("Name") + projects_page.expect_no_columns("Status") + expect(page).to have_text("Next") # as the result set is larger than 1 # Changing the page size to 5 and back to 1 should not change the filters (which we test later on the second page) projects_page.set_page_size(5) wait_for_reload projects_page.set_page_size(1) wait_for_reload - click_on '2' # Go to pagination page 2 + click_on "2" # Go to pagination page 2 # On page 2 you should see the second page of the filtered set ordered ASC by name and only the selected columns exist projects_page.expect_projects_listed(public_project) projects_page.expect_projects_not_listed(project, # Filtered out development_project) # Present on page 1 - projects_page.expect_columns('Name') - projects_page.expect_no_columns('Status') + projects_page.expect_columns("Name") + projects_page.expect_no_columns("Status") projects_page.expect_total_pages(2) # Filters kept active, so there is no third page. # Sorts DESC by name - projects_page.sort_by('Name') + projects_page.sort_by("Name") wait_for_reload # Clicking on sorting resets the page to the first one @@ -368,12 +371,12 @@ def expect_projects_in_order(*projects) development_project) # Present on page 2 projects_page.expect_total_pages(2) # Filters kept active, so there is no third page. - expect(page).to have_css('.sort.desc', text: 'NAME') - projects_page.expect_columns('Name') - projects_page.expect_no_columns('Status') + expect(page).to have_css(".sort.desc", text: "NAME") + projects_page.expect_columns("Name") + projects_page.expect_no_columns("Status") # Sending the filter form again what implies to compose the request freshly - click_on 'Apply' + click_on "Apply" wait_for_reload # We should see page 1, resetting pagination, as it is a new filter, but keeping the DESC order on the project @@ -382,53 +385,53 @@ def expect_projects_in_order(*projects) projects_page.expect_projects_not_listed(development_project, # as it is on the second page project) # as it filtered out projects_page.expect_total_pages(2) # as the result set is larger than 1 - expect(page).to have_css('.sort.desc', text: 'NAME') - projects_page.expect_columns('Name') - projects_page.expect_no_columns('Status') + expect(page).to have_css(".sort.desc", text: "NAME") + projects_page.expect_columns("Name") + projects_page.expect_no_columns("Status") end end - context 'when filter of type' do - specify 'Name and identifier gives results in both, name and identifier' do + context "when filter of type" do + specify "Name and identifier gives results in both, name and identifier" do load_and_open_filters admin # Filter on model attribute 'name' - projects_page.set_filter('name_and_identifier', - 'Name or identifier', - 'doesn\'t contain', - ['Plain']) + projects_page.set_filter("name_and_identifier", + "Name or identifier", + "doesn't contain", + ["Plain"]) - click_on 'Apply' + click_on "Apply" wait_for_reload projects_page.expect_projects_listed(development_project, public_project) projects_page.expect_projects_not_listed(project) # Filter on model attribute 'identifier' - remove_filter('name_and_identifier') + remove_filter("name_and_identifier") - projects_page.set_filter('name_and_identifier', - 'Name or identifier', - 'is', - ['plain-project']) + projects_page.set_filter("name_and_identifier", + "Name or identifier", + "is", + ["plain-project"]) - click_on 'Apply' + click_on "Apply" wait_for_reload projects_page.expect_projects_listed(project) projects_page.expect_projects_not_listed(development_project, public_project) end - describe 'Active or archived' do + describe "Active or archived" do shared_let(:parent_project) do create(:project, - name: 'Parent project', - identifier: 'parent-project') + name: "Parent project", + identifier: "parent-project") end shared_let(:child_project) do create(:project, - name: 'Child project', - identifier: 'child-project', + name: "Child project", + identifier: "child-project", parent: parent_project) end @@ -445,7 +448,7 @@ def expect_projects_in_order(*projects) public_project) accept_alert do - projects_page.click_menu_item_of('Archive', parent_project) + projects_page.click_menu_item_of("Archive", parent_project) end wait_for_reload @@ -463,20 +466,20 @@ def expect_projects_in_order(*projects) load_and_open_filters admin - projects_page.filter_by_active('no') + projects_page.filter_by_active("no") projects_page.expect_projects_listed(parent_project, child_project, archived: true) # Test visibility of 'more' menu list items projects_page.activate_menu_of(parent_project) do |menu| - expect(menu).to have_text('Unarchive') - expect(menu).to have_text('Delete') - expect(menu).to have_no_text('Archive') - expect(menu).to have_no_text('Copy') - expect(menu).to have_no_text('Settings') - expect(menu).to have_no_text('New subproject') - - click_link('Unarchive') + expect(menu).to have_text("Unarchive") + expect(menu).to have_text("Delete") + expect(menu).to have_no_text("Archive") + expect(menu).to have_no_text("Copy") + expect(menu).to have_no_text("Settings") + expect(menu).to have_no_text("New subproject") + + click_link_or_button("Unarchive") end # The child project does not get unarchived automatically @@ -488,7 +491,7 @@ def expect_projects_in_order(*projects) load_and_open_filters admin - projects_page.filter_by_active('yes') + projects_page.filter_by_active("yes") projects_page.expect_projects_listed(parent_project, project, @@ -498,7 +501,7 @@ def expect_projects_in_order(*projects) end end - describe 'I am member or not' do + describe "I am member or not" do shared_let(:member) { create(:user, member_with_permissions: { project => %i[view_work_packages edit_work_packages] }) } it "filters for projects I'm a member on and those where I'm not" do @@ -507,13 +510,13 @@ def expect_projects_in_order(*projects) projects_page.expect_projects_listed(project, public_project) - projects_page.filter_by_membership('yes') + projects_page.filter_by_membership("yes") wait_for_reload projects_page.expect_projects_listed(project) projects_page.expect_projects_not_listed(public_project, development_project) - projects_page.filter_by_membership('no') + projects_page.filter_by_membership("no") wait_for_reload projects_page.expect_projects_listed(public_project) @@ -521,75 +524,75 @@ def expect_projects_in_order(*projects) end end - describe 'project status filter' do + describe "project status filter" do shared_let(:no_status_project) do # A project that doesn't have a status code set create(:project, - name: 'No status project') + name: "No status project") end shared_let(:green_project) do # A project that has a status code set create(:project, - status_code: 'on_track', - name: 'Green project') + status_code: "on_track", + name: "Green project") end - it 'sorts and filters on project status' do + it "sorts and filters on project status" do login_as(admin) projects_page.visit! - click_link('Sort by "Status"') + click_link_or_button('Sort by "Status"') expect_project_at_place(green_project, 1) - expect(page).to have_text('(1 - 5/5)') + expect(page).to have_text("(1 - 5/5)") - click_link('Ascending sorted by "Status"') + click_link_or_button('Ascending sorted by "Status"') expect_project_at_place(green_project, 5) - expect(page).to have_text('(1 - 5/5)') + expect(page).to have_text("(1 - 5/5)") projects_page.open_filters - projects_page.set_filter('project_status_code', - 'Project status', - 'is (OR)', - ['On track']) + projects_page.set_filter("project_status_code", + "Project status", + "is (OR)", + ["On track"]) - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(green_project.name) expect(page).to have_no_text(no_status_project.name) - projects_page.set_filter('project_status_code', - 'Project status', - 'is not empty', + projects_page.set_filter("project_status_code", + "Project status", + "is not empty", []) - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(green_project.name) expect(page).to have_no_text(no_status_project.name) - projects_page.set_filter('project_status_code', - 'Project status', - 'is empty', + projects_page.set_filter("project_status_code", + "Project status", + "is empty", []) - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_no_text(green_project.name) expect(page).to have_text(no_status_project.name) - projects_page.set_filter('project_status_code', - 'Project status', - 'is not', - ['On track']) + projects_page.set_filter("project_status_code", + "Project status", + "is not", + ["On track"]) - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_no_text(green_project.name) @@ -597,7 +600,7 @@ def expect_projects_in_order(*projects) end end - describe 'other filter types', with_ee: %i[custom_fields_in_projects_list] do + describe "other filter types", with_ee: %i[custom_fields_in_projects_list] do include ActiveSupport::Testing::TimeHelpers shared_let(:list_custom_field) { create(:list_project_custom_field) } @@ -608,14 +611,14 @@ def expect_projects_in_order(*projects) date_of_this_week = today + ((today.wday % 7) > 2 ? -1 : 1) DateTime.parse("#{date_of_this_week}T11:11:11+00:00") end - shared_let(:fixed_datetime) { DateTime.parse('2017-11-11T11:11:11+00:00') } + shared_let(:fixed_datetime) { DateTime.parse("2017-11-11T11:11:11+00:00") } shared_let(:project_created_on_today) do freeze_time project = create(:project, - name: 'Created today project') + name: "Created today project") project.custom_field_values = { list_custom_field.id => list_custom_field.possible_values[2], - date_custom_field.id => '2011-11-11' } + date_custom_field.id => "2011-11-11" } project.save! project ensure @@ -624,21 +627,21 @@ def expect_projects_in_order(*projects) shared_let(:project_created_on_this_week) do travel_to(datetime_of_this_week) create(:project, - name: 'Created on this week project') + name: "Created on this week project") ensure travel_back end shared_let(:project_created_on_six_days_ago) do travel_to(DateTime.now - 6.days) create(:project, - name: 'Created on six days ago project') + name: "Created on six days ago project") ensure travel_back end shared_let(:project_created_on_fixed_date) do travel_to(fixed_datetime) create(:project, - name: 'Created on fixed date project') + name: "Created on fixed date project") ensure travel_back end @@ -654,13 +657,13 @@ def expect_projects_in_order(*projects) load_and_open_filters admin end - specify 'selecting operator' do + specify "selecting operator" do # created on 'today' shows projects that were created today - projects_page.set_filter('created_at', - 'Created on', - 'today') + projects_page.set_filter("created_at", + "Created on", + "today") - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(project_created_on_today.name) @@ -668,13 +671,13 @@ def expect_projects_in_order(*projects) expect(page).to have_no_text(project_created_on_fixed_date.name) # created on 'this week' shows projects that were created within the last seven days - remove_filter('created_at') + remove_filter("created_at") - projects_page.set_filter('created_at', - 'Created on', - 'this week') + projects_page.set_filter("created_at", + "Created on", + "this week") - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(project_created_on_today.name) @@ -682,14 +685,14 @@ def expect_projects_in_order(*projects) expect(page).to have_no_text(project_created_on_fixed_date.name) # created on 'on' shows projects that were created within the last seven days - remove_filter('created_at') + remove_filter("created_at") - projects_page.set_filter('created_at', - 'Created on', - 'on', - ['2017-11-11']) + projects_page.set_filter("created_at", + "Created on", + "on", + ["2017-11-11"]) - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(project_created_on_fixed_date.name) @@ -697,69 +700,69 @@ def expect_projects_in_order(*projects) expect(page).to have_no_text(project_created_on_this_week.name) # created on 'less than days ago' - remove_filter('created_at') + remove_filter("created_at") - projects_page.set_filter('created_at', - 'Created on', - 'less than days ago', - ['1']) + projects_page.set_filter("created_at", + "Created on", + "less than days ago", + ["1"]) - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(project_created_on_today.name) expect(page).to have_no_text(project_created_on_fixed_date.name) # created on 'more than days ago' - remove_filter('created_at') + remove_filter("created_at") - projects_page.set_filter('created_at', - 'Created on', - 'more than days ago', - ['1']) + projects_page.set_filter("created_at", + "Created on", + "more than days ago", + ["1"]) - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(project_created_on_fixed_date.name) expect(page).to have_no_text(project_created_on_today.name) # created on 'between' - remove_filter('created_at') + remove_filter("created_at") - projects_page.set_filter('created_at', - 'Created on', - 'between', - ['2017-11-10', '2017-11-12']) + projects_page.set_filter("created_at", + "Created on", + "between", + ["2017-11-10", "2017-11-12"]) - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(project_created_on_fixed_date.name) expect(page).to have_no_text(project_created_on_today.name) # Latest activity at 'today'. This spot check would fail if the data does not get collected from multiple tables - remove_filter('created_at') + remove_filter("created_at") - projects_page.set_filter('latest_activity_at', - 'Latest activity at', - 'today') + projects_page.set_filter("latest_activity_at", + "Latest activity at", + "today") - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(project_created_on_today.name) expect(page).to have_no_text(project_created_on_fixed_date.name) # CF List - remove_filter('latest_activity_at') + remove_filter("latest_activity_at") projects_page.set_filter(list_custom_field.column_name, list_custom_field.name, - 'is (OR)', + "is (OR)", [list_custom_field.possible_values[2].value]) - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(project_created_on_today.name) @@ -769,44 +772,44 @@ def expect_projects_in_order(*projects) cf_filter = page.find("li[filter-name='#{list_custom_field.column_name}']") within(cf_filter) do # Initial filter is a 'single select' - expect(cf_filter.find(:select, 'value')).not_to be_multiple - click_on 'Toggle multiselect' + expect(cf_filter.find(:select, "value")).not_to be_multiple + click_on "Toggle multiselect" # switching to multiselect keeps the current selection - expect(cf_filter.find(:select, 'value')).to be_multiple - expect(cf_filter).to have_select('value', selected: list_custom_field.possible_values[2].value) + expect(cf_filter.find(:select, "value")).to be_multiple + expect(cf_filter).to have_select("value", selected: list_custom_field.possible_values[2].value) - select list_custom_field.possible_values[3].value, from: 'value' + select list_custom_field.possible_values[3].value, from: "value" end - click_on 'Apply' + click_on "Apply" wait_for_reload cf_filter = page.find("li[filter-name='#{list_custom_field.column_name}']") within(cf_filter) do # Query has two values for that filter, so it should show a 'multi select'. - expect(cf_filter.find(:select, 'value')).to be_multiple + expect(cf_filter.find(:select, "value")).to be_multiple expect(cf_filter) - .to have_select('value', + .to have_select("value", selected: [list_custom_field.possible_values[2].value, list_custom_field.possible_values[3].value]) # switching to single select keeps the first selection - select list_custom_field.possible_values[1].value, from: 'value' - unselect list_custom_field.possible_values[2].value, from: 'value' + select list_custom_field.possible_values[1].value, from: "value" + unselect list_custom_field.possible_values[2].value, from: "value" - click_on 'Toggle multiselect' - expect(cf_filter.find(:select, 'value')).not_to be_multiple - expect(cf_filter).to have_select('value', selected: list_custom_field.possible_values[1].value) - expect(cf_filter).to have_no_select('value', selected: list_custom_field.possible_values[3].value) + click_on "Toggle multiselect" + expect(cf_filter.find(:select, "value")).not_to be_multiple + expect(cf_filter).to have_select("value", selected: list_custom_field.possible_values[1].value) + expect(cf_filter).to have_no_select("value", selected: list_custom_field.possible_values[3].value) end - click_on 'Apply' + click_on "Apply" wait_for_reload cf_filter = page.find("li[filter-name='#{list_custom_field.column_name}']") within(cf_filter) do # Query has one value for that filter, so it should show a 'single select'. - expect(cf_filter.find(:select, 'value')).not_to be_multiple + expect(cf_filter.find(:select, "value")).not_to be_multiple end # CF date filter work (at least for one operator) @@ -814,10 +817,10 @@ def expect_projects_in_order(*projects) projects_page.set_filter(date_custom_field.column_name, date_custom_field.name, - 'on', - ['2011-11-11']) + "on", + ["2011-11-11"]) - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_text(project_created_on_today.name) @@ -826,7 +829,7 @@ def expect_projects_in_order(*projects) # Disabling a CF in the project should remove the project from results project_created_on_today.project_custom_field_project_mappings.destroy_all - click_on 'Apply' + click_on "Apply" wait_for_reload expect(page).to have_no_text(project_created_on_today.name, wait: 1) @@ -836,13 +839,13 @@ def expect_projects_in_order(*projects) pending "NOT WORKING YET: Date vs. DateTime issue: Selecting same date for from and to value shows projects of that date" end - describe 'public filter' do + describe "public filter" do it 'filters on "public" status' do load_and_open_filters admin projects_page.expect_projects_listed(project, public_project) - projects_page.filter_by_public('no') + projects_page.filter_by_public("no") wait_for_reload projects_page.expect_projects_listed(project) @@ -850,7 +853,7 @@ def expect_projects_in_order(*projects) load_and_open_filters admin - projects_page.filter_by_public('yes') + projects_page.filter_by_public("yes") wait_for_reload projects_page.expect_projects_listed(public_project) @@ -859,18 +862,18 @@ def expect_projects_in_order(*projects) end end - context 'for non-admins with role with permission' do + context "for non-admins with role with permission" do shared_let(:can_copy_projects_role) do - create(:project_role, name: 'Can Copy Projects Role', permissions: [:copy_projects]) + create(:project_role, name: "Can Copy Projects Role", permissions: [:copy_projects]) end shared_let(:can_add_subprojects_role) do - create(:project_role, name: 'Can Add Subprojects Role', permissions: [:add_subprojects]) + create(:project_role, name: "Can Add Subprojects Role", permissions: [:add_subprojects]) end shared_let(:parent_project) do create(:project, - name: 'Parent project', - identifier: 'parent-project') + name: "Parent project", + identifier: "parent-project") end shared_let(:can_copy_projects_manager) do @@ -907,8 +910,8 @@ def expect_projects_in_order(*projects) expect(page).to have_text(parent_project.name) # 'More' does not become visible on hover - page.find('tbody tr').hover - expect(page).to have_no_css('.icon-show-more-horizontal') + page.find("tbody tr").hover + expect(page).to have_no_css(".icon-show-more-horizontal") # For a project member with :copy_projects privilege the 'More' menu is visible. login_as(can_copy_projects_manager) @@ -918,17 +921,17 @@ def expect_projects_in_order(*projects) # 'More' becomes visible on hover # because we use css opacity we can not test for the visibility changes - page.find('tbody tr').hover - expect(page).to have_css('.icon-show-more-horizontal') + page.find("tbody tr").hover + expect(page).to have_css(".icon-show-more-horizontal") # Test visibility of 'more' menu list items - page.find('tbody tr .icon-show-more-horizontal').click - menu = page.find('tbody tr .project-actions') - expect(menu).to have_text('Copy') - expect(menu).to have_no_text('New subproject') - expect(menu).to have_no_text('Delete') - expect(menu).to have_no_text('Archive') - expect(menu).to have_no_text('Unarchive') + page.find("tbody tr .icon-show-more-horizontal").click + menu = page.find("tbody tr .project-actions") + expect(menu).to have_text("Copy") + expect(menu).to have_no_text("New subproject") + expect(menu).to have_no_text("Delete") + expect(menu).to have_no_text("Archive") + expect(menu).to have_no_text("Unarchive") # For a project member with :add_subprojects privilege the 'More' menu is visible. login_as(can_add_subprojects_manager) @@ -936,35 +939,35 @@ def expect_projects_in_order(*projects) # 'More' becomes visible on hover # because we use css opacity we can not test for the visibility changes - page.find('tbody tr').hover - expect(page).to have_css('.icon-show-more-horizontal') + page.find("tbody tr").hover + expect(page).to have_css(".icon-show-more-horizontal") # Test visibility of 'more' menu list items - page.find('tbody tr .icon-show-more-horizontal').click - menu = page.find('tbody tr .project-actions') - expect(menu).to have_text('New subproject') - expect(menu).to have_no_text('Copy') - expect(menu).to have_no_text('Delete') - expect(menu).to have_no_text('Archive') - expect(menu).to have_no_text('Unrchive') + page.find("tbody tr .icon-show-more-horizontal").click + menu = page.find("tbody tr .project-actions") + expect(menu).to have_text("New subproject") + expect(menu).to have_no_text("Copy") + expect(menu).to have_no_text("Delete") + expect(menu).to have_no_text("Archive") + expect(menu).to have_no_text("Unrchive") # Test admin only properties are invisible - within('#project-table') do + within("#project-table") do expect(page) - .to have_no_css('th', text: 'REQUIRED DISK STORAGE') + .to have_no_css("th", text: "REQUIRED DISK STORAGE") expect(page) - .to have_no_css('th', text: 'CREATED ON') + .to have_no_css("th", text: "CREATED ON") expect(page) - .to have_no_css('td', text: project.created_at.strftime('%m/%d/%Y')) + .to have_no_css("td", text: project.created_at.strftime("%m/%d/%Y")) expect(page) - .to have_no_css('th', text: 'LATEST ACTIVITY AT') + .to have_no_css("th", text: "LATEST ACTIVITY AT") expect(page) - .to have_no_css('td', text: news.created_at.strftime('%m/%d/%Y')) + .to have_no_css("td", text: news.created_at.strftime("%m/%d/%Y")) end end end - describe 'order', with_ee: %i[custom_fields_in_projects_list] do + describe "order", with_ee: %i[custom_fields_in_projects_list] do shared_let(:integer_custom_field) { create(:integer_project_custom_field) } # order is important here as the implementation uses lft # first but then reorders in ruby @@ -1002,7 +1005,7 @@ def expect_projects_in_order(*projects) child_project_a.save! end - it 'allows to alter the order in which projects are displayed' do + it "allows to alter the order in which projects are displayed" do Setting.enabled_projects_columns += [integer_custom_field.column_name] # initially, ordered by name asc on each hierarchical level @@ -1013,7 +1016,7 @@ def expect_projects_in_order(*projects) child_project_z, public_project) - click_link('Name') + click_link_or_button("Name") wait_for_reload # Projects ordered by name asc @@ -1024,7 +1027,7 @@ def expect_projects_in_order(*projects) public_project, child_project_z) - click_link('Name') + click_link_or_button("Name") wait_for_reload # Projects ordered by name desc @@ -1035,7 +1038,7 @@ def expect_projects_in_order(*projects) development_project, child_project_a) - click_link(integer_custom_field.name) + click_link_or_button(integer_custom_field.name) wait_for_reload # Projects ordered by cf asc first then project name desc @@ -1046,7 +1049,7 @@ def expect_projects_in_order(*projects) child_project_m, child_project_a) - click_link('Sort by "Project hierarchy"') + click_link_or_button('Sort by "Project hierarchy"') wait_for_reload # again ordered by name asc on each hierarchical level @@ -1059,17 +1062,17 @@ def expect_projects_in_order(*projects) end end - describe 'blocked filter' do - it 'is not visible' do + describe "blocked filter" do + it "is not visible" do load_and_open_filters admin - expect(page).to have_no_select('add_filter_select', with_options: ["Principal"]) - expect(page).to have_no_select('add_filter_select', with_options: ["ID"]) - expect(page).to have_no_select('add_filter_select', with_options: ["Subproject of"]) + expect(page).to have_no_select("add_filter_select", with_options: ["Principal"]) + expect(page).to have_no_select("add_filter_select", with_options: ["ID"]) + expect(page).to have_no_select("add_filter_select", with_options: ["Subproject of"]) end end - describe 'column selection', + describe "column selection", with_ee: %i[custom_fields_in_projects_list], with_settings: { enabled_projects_columns: %w[name created_at] } do # Will still receive the :view_project permission shared_let(:user) do @@ -1096,54 +1099,54 @@ def expect_projects_in_order(*projects) development_project.at_risk! end - it 'allows to select columns to be displayed' do + it "allows to select columns to be displayed" do projects_page.visit! - projects_page.set_columns('Name', 'Status', integer_custom_field.name) + projects_page.set_columns("Name", "Status", integer_custom_field.name) - projects_page.expect_no_columns('Public', 'Description', 'Project status description') + projects_page.expect_no_columns("Public", "Description", "Project status description") projects_page.within_row(project) do expect(page) - .to have_css('.name', text: project.name) + .to have_css(".name", text: project.name) expect(page) .to have_css(".cf_#{integer_custom_field.id}", text: 2) expect(page) - .to have_css('.project_status', text: 'OFF TRACK') + .to have_css(".project_status", text: "OFF TRACK") expect(page) - .to have_no_css('.created_at ') + .to have_no_css(".created_at ") end projects_page.within_row(public_project) do expect(page) - .to have_css('.name', text: public_project.name) + .to have_css(".name", text: public_project.name) expect(page) .to have_css(".cf_#{integer_custom_field.id}", text: 1) expect(page) - .to have_css('.project_status', text: 'ON TRACK') + .to have_css(".project_status", text: "ON TRACK") expect(page) - .to have_no_css('.created_at ') + .to have_no_css(".created_at ") end projects_page.within_row(development_project) do expect(page) - .to have_css('.name', text: development_project.name) + .to have_css(".name", text: development_project.name) expect(page) .to have_css(".cf_#{integer_custom_field.id}", text: 3) expect(page) - .to have_css('.project_status', text: 'AT RISK') + .to have_css(".project_status", text: "AT RISK") expect(page) - .to have_no_css('.created_at ') + .to have_no_css(".created_at ") end end end - context 'with a multi-value custom field', with_ee: %i[custom_fields_in_projects_list] do + context "with a multi-value custom field", with_ee: %i[custom_fields_in_projects_list] do let!(:list_custom_field) { create(:list_project_custom_field, multi_value: true) } before do - project.custom_values << CustomValue.new(custom_field: list_custom_field, value: list_custom_field.value_of('A')) - project.custom_values << CustomValue.new(custom_field: list_custom_field, value: list_custom_field.value_of('B')) + project.custom_values << CustomValue.new(custom_field: list_custom_field, value: list_custom_field.value_of("A")) + project.custom_values << CustomValue.new(custom_field: list_custom_field, value: list_custom_field.value_of("B")) project.save! @@ -1155,7 +1158,7 @@ def expect_projects_in_order(*projects) visit projects_path end - it 'shows the multi selection' do + it "shows the multi selection" do expected_sort = list_custom_field .custom_options .where(value: %w[A B]) @@ -1165,10 +1168,10 @@ def expect_projects_in_order(*projects) end end - describe 'project activity menu item' do - context 'for projects with activity module enabled' do + describe "project activity menu item" do + context "for projects with activity module enabled" do shared_let(:project_with_activity_enabled) { project } - shared_let(:work_packages_viewer) { create(:project_role, name: 'Viewer', permissions: [:view_work_packages]) } + shared_let(:work_packages_viewer) { create(:project_role, name: "Viewer", permissions: [:view_work_packages]) } shared_let(:simple_member) do create(:user, member_with_roles: { project_with_activity_enabled => work_packages_viewer }) @@ -1180,7 +1183,7 @@ def expect_projects_in_order(*projects) project_with_activity_enabled.save end - it 'is displayed and redirects to project activity page with only project attributes visible' do + it "is displayed and redirects to project activity page with only project attributes visible" do login_as(simple_member) visit projects_path @@ -1188,13 +1191,13 @@ def expect_projects_in_order(*projects) # 'More' becomes visible on hover # because we use css opacity we can not test for the visibility changes - page.find('tbody tr', text: project.name).hover - expect(page).to have_css('.icon-show-more-horizontal') + page.find("tbody tr", text: project.name).hover + expect(page).to have_css(".icon-show-more-horizontal") # "Project activity" item should be displayed in the 'more' menu - page.find('tbody tr .icon-show-more-horizontal').click + page.find("tbody tr .icon-show-more-horizontal").click - menu = page.find('tbody tr .project-actions') + menu = page.find("tbody tr .project-actions") expect(menu).to have_text(I18n.t(:label_project_activity)) # Clicking the menu item should redirect to project activity page @@ -1202,8 +1205,8 @@ def expect_projects_in_order(*projects) menu.find_link(text: I18n.t(:label_project_activity)).click expect(page).to have_current_path(project_activity_index_path(project_with_activity_enabled), ignore_query: true) - expect(page).to have_checked_field(id: 'event_types_project_attributes') - expect(page).to have_unchecked_field(id: 'event_types_work_packages') + expect(page).to have_checked_field(id: "event_types_project_attributes") + expect(page).to have_unchecked_field(id: "event_types_work_packages") end end end diff --git a/spec/support/components/projects/project_custom_fields/edit_dialog.rb b/spec/support/components/projects/project_custom_fields/edit_dialog.rb index 3db7c11bbb03..3acdfad47f2c 100644 --- a/spec/support/components/projects/project_custom_fields/edit_dialog.rb +++ b/spec/support/components/projects/project_custom_fields/edit_dialog.rb @@ -50,7 +50,7 @@ def dialog_css_selector end def async_content_container_css_selector - "#{dialog_css_selector} [data-qa-selector='async-dialog-content']" + "#{dialog_css_selector} [data-test-selector='async-dialog-content']" end def within_dialog(&) @@ -77,7 +77,7 @@ def close_via_button def submit within(dialog_css_selector) do - page.find("[data-qa-selector='save-project-attributes-button']").click + page.find("[data-test-selector='save-project-attributes-button']").click end end @@ -104,7 +104,7 @@ def input_containers def within_custom_field_input_container(custom_field, &) # wrapping in `within_async_content` to make sure the container is properly loaded within_async_content do - within("[data-qa-selector='project-custom-field-input-container-#{custom_field.id}']", &) + within("[data-test-selector='project-custom-field-input-container-#{custom_field.id}']", &) end end end diff --git a/spec/support/pages/projects/show.rb b/spec/support/pages/projects/show.rb index 5ad63c542823..ce537a482669 100644 --- a/spec/support/pages/projects/show.rb +++ b/spec/support/pages/projects/show.rb @@ -26,7 +26,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require 'support/pages/page' +require "support/pages/page" module Pages module Projects @@ -44,7 +44,7 @@ def path end def within_sidebar(&) - within('#menu-sidebar', &) + within("#menu-sidebar", &) end def toast_type @@ -56,29 +56,54 @@ def visit_page end def within_async_loaded_sidebar(&) - within '#project-custom-fields-sidebar' do - expect(page).to have_css("[data-qa-selector='project-custom-fields-sidebar-async-content']") + within "#project-custom-fields-sidebar" do + expect(page).to have_css("[data-test-selector='project-custom-fields-sidebar-async-content']") yield end end def within_custom_field_section_container(section, &) - within("[data-qa-selector='project-custom-field-section-#{section.id}']", &) + within("[data-test-selector='project-custom-field-section-#{section.id}']", &) end def within_custom_field_container(custom_field, &) - within("[data-qa-selector='project-custom-field-#{custom_field.id}']", &) + within("[data-test-selector='project-custom-field-#{custom_field.id}']", &) end def open_edit_dialog_for_section(section) within_async_loaded_sidebar do - scroll_to_element(page.find("[data-qa-selector='project-custom-field-section-#{section.id}']")) + scroll_to_element(page.find("[data-test-selector='project-custom-field-section-#{section.id}']")) within_custom_field_section_container(section) do - page.find("[data-qa-selector='project-custom-field-section-edit-button']").click + page.find("[data-test-selector='project-custom-field-section-edit-button']").click end end - expect(page).to have_css("[data-qa-selector='async-dialog-content']", wait: 5) + expect(page).to have_css("[data-test-selector='async-dialog-content']", wait: 5) + end + + def expand_text(custom_field) + within_custom_field_container(custom_field) do + page.find('[data-test-selector="expand-button"]').click + end + end + + def expect_text_not_truncated(custom_field) + within_custom_field_container(custom_field) do + expect(page).to have_no_css('[data-test-selector="expand-button"]') + end + end + + def expect_text_truncated(custom_field) + within_custom_field_container(custom_field) do + expect(page).to have_css('[data-test-selector="expand-button"]') + end + end + + def expect_full_text_in_dialog(text) + within('[data-test-selector="attribute-dialog"]') do + expect(page) + .to have_content(text) + end end end end