[#58426] add feature spec for custom fields of type hierarchy

- https://community.openproject.org/work_packages/58426
- add a couple of test selectors
- add page objects
This commit is contained in:
Eric Schubert
2024-10-18 14:02:17 +02:00
parent 503b2b114a
commit a8a6d112da
25 changed files with 374 additions and 55 deletions
@@ -28,7 +28,9 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<%=
render(Primer::Alpha::Dialog.new(id: DIALOG_ID, title: "Delete item")) do |dialog|
render(Primer::Alpha::Dialog.new(id: DIALOG_ID,
title: "Delete item",
data: { test_selector: TEST_SELECTOR })) do |dialog|
dialog.with_header(variant: :large)
dialog.with_body do
"Are you sure you want to delete this item from the current hierarchy level?"
@@ -35,6 +35,7 @@ module Admin
include OpTurbo::Streamable
DIALOG_ID = "op-hierarchy-item--deletion-confirmation"
TEST_SELECTOR = "op-custom-fields--delete-item-dialog"
def initialize(custom_field:, hierarchy_item:)
super
@@ -28,7 +28,7 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<%=
component_wrapper do
component_wrapper(data: { test_selector: "op-custom-fields--hierarchy-item" }) do
flex_layout(align_items: :center, justify_content: :space_between) do |item_container|
item_container.with_column(flex_layout: true) do |item_information|
item_information.with_column(mr: 2) do
@@ -32,7 +32,10 @@ See COPYRIGHT and LICENSE files for more details.
flex_layout do |container|
if items.empty? && !show_new_item_form?
container.with_row(mb: 3) do
render Primer::Beta::Blankslate.new(border: true) do |component|
render Primer::Beta::Blankslate.new(
border: true,
test_selector: "op-custom-fields--hierarchy-items-blankslate"
) do |component|
component.with_visual_icon(icon: "list-ordered")
component.with_heading(tag: :h3).with_content(I18n.t("custom_fields.admin.items.blankslate.title"))
component.with_description { I18n.t("custom_fields.admin.items.blankslate.description") }
@@ -31,6 +31,7 @@ See COPYRIGHT and LICENSE files for more details.
primer_form_with(
url: custom_field_items_path(@custom_field),
method: :post,
data: { test_selector: "op-custom-fields--new-item-form" }
) do |f|
render(CustomFields::Hierarchy::NewItemForm.new(f, custom_field: @custom_field, label: @label, short: @short))
end
@@ -1,9 +1,14 @@
<%=
component_wrapper do
flex_layout do |content|
content.with_row(mb: 3) do
render Primer::Alpha::Banner.new(scheme: :default, icon: :info, dismiss_scheme: :hide) do
I18n.t("custom_fields.admin.notice.remember_items_and_projects")
if has_no_items_or_projects?
content.with_row(mb: 3) do
render Primer::Alpha::Banner.new(scheme: :default,
icon: :info,
dismiss_scheme: :hide,
test_selector: "op-custom-fields--new-hierarchy-banner") do
I18n.t("custom_fields.admin.notice.remember_items_and_projects")
end
end
end
@@ -32,5 +32,13 @@ module CustomFields
class DetailsComponent < ApplicationComponent
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable
alias_method :custom_field, :model
def has_no_items_or_projects?
custom_field.field_format_hierarchy? &&
custom_field.hierarchy_root.children.empty? &&
custom_field.projects.empty?
end
end
end
+4 -1
View File
@@ -37,7 +37,10 @@ See COPYRIGHT and LICENSE files for more details.
data-admin--custom-fields-format-config-value="<%= OpenProject::CustomFieldFormatDependent.stimulus_config %>"
>
<div class="form--field -required" id="custom_field_name_attributes">
<%= f.text_field :name, required: true, container_class: '-middle' %>
<%= f.text_field :name,
required: true,
container_class: "-middle",
"data-test-selector": "op-custom-fields--new-custom-field-name" %>
</div>
<% if @custom_field.type == 'ProjectCustomField' %>
<div class="form--field -required">
+1 -1
View File
@@ -245,10 +245,10 @@ en:
heading: For all projects
description: This custom field is enabled in all projects since the "For all projects" option is checked. It cannot be deactivated for individual projects.
items:
actions: "Item actions"
blankslate:
title: "Your list of items is empty"
description: "Start by adding items to the custom field of type hierarchy. Each item can be used to create a hierarchy bellow it. To navigate and create sub-items inside a hierarchy click on the created item."
actions: "Item actions"
placeholder:
label: "Item label"
short: "Short name"
+2
View File
@@ -131,6 +131,8 @@ services:
environment:
PROXY_HOSTNAME: backend-test
CHROME_BIN: chromium
FE_PORT: 4200
FE_HOST: frontend-test
networks:
- testing
@@ -30,7 +30,7 @@ require "spec_helper"
RSpec.describe "Link custom fields edit", :js, :with_cuprite do
shared_let(:admin) { create(:admin) }
let(:cf_page) { Pages::CustomFields.new }
let(:cf_page) { Pages::CustomFields::IndexPage.new }
before do
login_as(admin)
@@ -30,7 +30,7 @@ require "spec_helper"
RSpec.describe "User custom fields edit", :js, :with_cuprite do
shared_let(:admin) { create(:admin) }
let(:cf_page) { Pages::CustomFields.new }
let(:cf_page) { Pages::CustomFields::IndexPage.new }
before do
login_as(admin)
@@ -27,11 +27,11 @@
#++
require "spec_helper"
require "support/pages/custom_fields"
require "support/pages/custom_fields/index_page"
RSpec.describe "custom fields", :js, :with_cuprite do
let(:user) { create(:admin) }
let(:cf_page) { Pages::CustomFields.new }
let(:cf_page) { Pages::CustomFields::IndexPage.new }
let(:for_all_cf) { create(:list_wp_custom_field, is_for_all: true) }
let(:project_specific_cf) { create(:integer_wp_custom_field) }
let(:work_package) do
@@ -1,9 +1,9 @@
require "spec_helper"
require "support/pages/custom_fields"
require "support/pages/custom_fields/index_page"
RSpec.describe "custom fields", :js, :with_cuprite do
let(:user) { create(:admin) }
let(:cf_page) { Pages::CustomFields.new }
let(:cf_page) { Pages::CustomFields::IndexPage.new }
before do
login_as user
@@ -1,9 +1,9 @@
require "spec_helper"
require "support/pages/custom_fields"
require "support/pages/custom_fields/index_page"
RSpec.describe "custom fields", :js, :with_cuprite do
let(:user) { create(:admin) }
let(:cf_page) { Pages::CustomFields.new }
let(:cf_page) { Pages::CustomFields::IndexPage.new }
before do
login_as user
@@ -1,9 +1,9 @@
require "spec_helper"
require "support/pages/custom_fields"
require "support/pages/custom_fields/index_page"
RSpec.describe "custom fields", :js, :with_cuprite do
let(:user) { create(:admin) }
let(:cf_page) { Pages::CustomFields.new }
let(:cf_page) { Pages::CustomFields::IndexPage.new }
before do
login_as user
@@ -1,9 +1,9 @@
require "spec_helper"
require "support/pages/custom_fields"
require "support/pages/custom_fields/index_page"
RSpec.describe "custom fields", :js, :with_cuprite do
let(:user) { create(:admin) }
let(:cf_page) { Pages::CustomFields.new }
let(:cf_page) { Pages::CustomFields::IndexPage.new }
before do
login_as user
@@ -1,9 +1,9 @@
require "spec_helper"
require "support/pages/custom_fields"
require "support/pages/custom_fields/index_page"
RSpec.describe "custom fields", :js do
let(:user) { create(:admin) }
let(:cf_page) { Pages::CustomFields.new }
let(:cf_page) { Pages::CustomFields::IndexPage.new }
let(:editor) { Components::WysiwygEditor.new "#custom_field_form" }
let(:type) { create(:type_task) }
let!(:project) { create(:project, enabled_module_names: %i[work_package_tracking], types: [type]) }
@@ -1,9 +1,9 @@
require "spec_helper"
require "support/pages/custom_fields"
require "support/pages/custom_fields/index_page"
RSpec.describe "custom fields", :js, :with_cuprite do
let(:user) { create(:admin) }
let(:cf_page) { Pages::CustomFields.new }
let(:cf_page) { Pages::CustomFields::IndexPage.new }
before do
login_as user
@@ -0,0 +1,131 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "spec_helper"
require "support/pages/custom_fields/hierarchy_page"
RSpec.describe "custom fields of type hierarchy", :js, :with_cuprite do
let(:user) { create(:admin) }
let(:custom_field_index_page) { Pages::CustomFields::IndexPage.new }
let(:new_custom_field_page) { Pages::CustomFields::NewPage.new }
let(:hierarchy_page) { Pages::CustomFields::HierarchyPage.new }
it "lets you create, update and delete a custom field of type hierarchy",
with_flag: { custom_field_of_type_hierarchy: true } do
login_as user
# First, we create a new custom field of type hierarchy
custom_field_index_page.visit!
click_on "New custom field"
new_custom_field_page.expect_current_path
hierarchy_name = "Stormtrooper Organisation"
fill_in "Name", with: hierarchy_name
select "Hierarchy", from: "Format"
click_on "Save"
custom_field_index_page.expect_current_path("tab=WorkPackageCustomField")
expect(page).to have_list_item(hierarchy_name)
# The next step is to enter the custom field and work on it
CustomField.find_by(name: hierarchy_name).tap do |custom_field|
hierarchy_page.add_custom_field_state(custom_field)
end
click_on hierarchy_name
hierarchy_page.expect_current_path
hierarchy_page.expect_empty_items_banner(visible: true)
hierarchy_page.expect_header_text(hierarchy_name)
# Changing the name is possible
hierarchy_name = "Imperial Organisation"
fill_in "Name", with: "", fill_options: { clear: :backspace }
fill_in "Name", with: hierarchy_name
click_on "Save"
hierarchy_page.expect_header_text(hierarchy_name)
# Now we want to create hierarchy items
hierarchy_page.switch_tab "Items"
hierarchy_page.expect_current_path
hierarchy_page.expect_blank_slate(visible: true)
click_on "Item"
hierarchy_page.expect_blank_slate(visible: false)
fill_in "Label", with: "Stormtroopers"
fill_in "Short", with: "ST"
click_on "Save"
hierarchy_page.expect_blank_slate(visible: false)
hierarchy_page.expect_items_count(1)
hierarchy_page.expect_hierarchy_item(label: "Stormtroopers", short: "(ST)")
# Is the form cancelable?
click_on "Item"
hierarchy_page.expect_inline_form(visible: true)
fill_in "Label", with: "Dark Troopers"
click_on "Cancel"
hierarchy_page.expect_inline_form(visible: false)
hierarchy_page.expect_items_count(1)
hierarchy_page.expect_hierarchy_item(label: "Dark Troopers", visible: false)
# What happens if I add a wrong item?
click_on "Item"
fill_in "Label", with: "Phoenix Squad"
click_on "Save"
hierarchy_page.expect_items_count(2)
hierarchy_page.expect_hierarchy_item(label: "Phoenix Squad", visible: true)
hierarchy_page.open_action_menu_for("Phoenix Squad")
click_on "Delete"
hierarchy_page.expect_deletion_dialog(visible: true)
click_on "Delete"
hierarchy_page.expect_deletion_dialog(visible: false)
hierarchy_page.expect_items_count(1)
hierarchy_page.expect_hierarchy_item(label: "Phoenix Squad", visible: false)
# Can I cancel the deletion?
hierarchy_page.open_action_menu_for("Stormtroopers")
click_on "Delete"
hierarchy_page.expect_deletion_dialog(visible: true)
click_on "Cancel"
hierarchy_page.expect_deletion_dialog(visible: false)
hierarchy_page.expect_hierarchy_item(label: "Stormtroopers", visible: true)
# And is the blue banner gone, now that I have added some items?
hierarchy_page.switch_tab "Details"
hierarchy_page.expect_empty_items_banner(visible: false)
# Finally, we delete the custom field ... I'm done with this ...
custom_field_index_page.visit!
expect(page).to have_list_item(hierarchy_name)
within("tr", text: hierarchy_name) { accept_prompt { click_on "Delete" } }
expect(page).to have_no_text(hierarchy_name)
end
end
@@ -1,5 +1,5 @@
require "spec_helper"
require "support/pages/custom_fields"
require "support/pages/custom_fields/index_page"
def get_possible_values(amount)
(1..amount).to_a.map { |x| "PREFIX #{x}" }
@@ -15,7 +15,7 @@ end
RSpec.describe "Reordering custom options of a list custom field", :js do
let(:user) { create(:admin) }
let(:cf_page) { Pages::CustomFields.new }
let(:cf_page) { Pages::CustomFields::IndexPage.new }
let!(:custom_field) do
create(
@@ -27,7 +27,7 @@
#++
require "spec_helper"
require "support/pages/custom_fields"
require "support/pages/custom_fields/index_page"
RSpec.describe "types", :js, :with_cuprite do
let(:user) do
@@ -0,0 +1,117 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "support/pages/page"
module Pages
module CustomFields
class HierarchyPage < Page
def path
case @tab
when "items"
"/custom_fields/#{@custom_field.id}/items"
when "projects"
"/custom_fields/#{@custom_field.id}/projects"
else
"/custom_fields/#{@custom_field.id}/edit"
end
end
def add_custom_field_state(custom_field)
@custom_field = custom_field
end
def switch_tab(tab)
@tab = tab.downcase
within_test_selector("custom_field_detail_header") do
click_on "Items"
end
end
def expect_empty_items_banner(visible:)
if visible
expect(page).to have_test_selector("op-custom-fields--new-hierarchy-banner")
else
expect(page).not_to have_test_selector("op-custom-fields--new-hierarchy-banner")
end
end
def expect_header_text(text)
expect(page).to have_css(".PageHeader-title", text: text)
end
def expect_blank_slate(visible:)
if visible
expect(page).to have_test_selector("op-custom-fields--hierarchy-items-blankslate")
else
expect(page).not_to have_test_selector("op-custom-fields--hierarchy-items-blankslate")
end
end
def expect_items_count(count)
expect(page).to have_test_selector("op-custom-fields--hierarchy-item", count:)
end
def expect_hierarchy_item(label:, short: nil, visible: true)
if visible
expect(page).to have_test_selector("op-custom-fields--hierarchy-item", text: label)
expect(page).to have_test_selector("op-custom-fields--hierarchy-item", text: short) unless short.nil?
else
expect(page).not_to have_test_selector("op-custom-fields--hierarchy-item", text: label)
end
end
def expect_inline_form(visible:)
if visible
expect(page).to have_test_selector("op-custom-fields--new-item-form")
else
expect(page).not_to have_test_selector("op-custom-fields--new-item-form")
end
end
def expect_deletion_dialog(visible:)
if visible
expect(page).to have_test_selector("op-custom-fields--delete-item-dialog")
else
expect(page).not_to have_test_selector("op-custom-fields--delete-item-dialog")
end
end
def open_action_menu_for(label)
within_test_selector("op-custom-fields--hierarchy-item", text: label) do
within_test_selector("op-hierarchy-item--action-menu") do
click_on
end
end
end
end
end
end
@@ -0,0 +1,66 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "support/pages/page"
module Pages
module CustomFields
class IndexPage < Page
def path
"/custom_fields"
end
def visit_tab(name)
visit!
within_test_selector("custom-fields--tab-nav") do
click_on name.to_s
end
end
def select_format(label)
select label, from: "custom_field_field_format"
end
def set_name(name)
find_by_id("custom_field_name").set name
end
def set_default_value(value)
fill_in "custom_field[default_value]", with: value
end
def set_all_projects(value)
find_by_id("custom_field_is_for_all").set value
end
def has_form_element?(name)
page.has_css? "label.form--label", text: name
end
end
end
end
@@ -29,36 +29,16 @@
require "support/pages/page"
module Pages
class CustomFields < Page
def path
"/custom_fields"
end
def visit_tab(name)
visit!
within_test_selector("custom-fields--tab-nav") do
click_link name.to_s
module CustomFields
class NewPage < Page
def path
"/custom_fields/new?type=#{@type}"
end
end
def select_format(label)
select label, from: "custom_field_field_format"
end
def set_name(name)
find_by_id("custom_field_name").set name
end
def set_default_value(value)
fill_in "custom_field[default_value]", with: value
end
def set_all_projects(value)
find_by_id("custom_field_is_for_all").set value
end
def has_form_element?(name)
page.has_css? "label.form--label", text: name
def initialize(type = "WorkPackageCustomField")
super()
@type = type
end
end
end
end