Browse Source

Feature: Mobile - User Detail View + Add/Edit functionality - End-to-end testing.

Dusan Vuckovic 2 years ago
parent
commit
a54a4726b6

+ 1 - 1
app/frontend/shared/graphql/types.ts

@@ -2036,7 +2036,7 @@ export type UserInput = {
   /** Additional custom attributes (names + values) */
   objectAttributeValues?: InputMaybe<Array<ObjectAttributeValueInput>>;
   /** The organization the user belongs to */
-  organizationId?: InputMaybe<Scalars['Int']>;
+  organizationId?: InputMaybe<Scalars['ID']>;
   /** The user password */
   password?: InputMaybe<Scalars['String']>;
   /** The user phone */

+ 1 - 1
app/frontend/shared/utils/testFlags.ts

@@ -27,7 +27,7 @@ class TestFlags {
     if (!VITE_TEST_MODE) return
     await this.mutex.runExclusive(() => {
       this.flags.set(flag, true)
-      if (import.meta.env.DEBUG_TEST_FLAGS) {
+      if (import.meta.env.VITE_DEBUG_TEST_FLAGS) {
         console.log('[testFlags] set flag "%s"', flag)
       }
     })

+ 1 - 1
app/graphql/gql/types/input/user_input_type.rb

@@ -10,7 +10,7 @@ module Gql::Types::Input
     argument :lastname, String, required: false, description: 'The user last name'
     argument :email, String, description: 'The user email'
     argument :password, String, required: false, description: 'The user password'
-    argument :organization_id, Integer, required: false, description: 'The organization the user belongs to'
+    argument :organization_id, GraphQL::Types::ID, required: false, description: 'The organization the user belongs to', loads: Gql::Types::OrganizationType
     argument :web, String, required: false, description: 'The user web'
     argument :phone, String, required: false, description: 'The user phone'
     argument :mobile, String, required: false, description: 'The user mobile'

+ 3 - 2
spec/support/capybara/form_helpers.rb

@@ -9,7 +9,7 @@ module FormHelpers
   # Returns the outer container element of the form field via its label.
   #   The returned object is always an instance of `Capybara::Node::Element``, with some added sugar on top.
   def find_outer(label, **find_options)
-    ZammadFormFieldCapybaraElementDelegator.new(find('.formkit-outer', text: label, **find_options), @form_context)
+    ZammadFormFieldCapybaraElementDelegator.new(find('.formkit-outer') { |element| element.has_css?('label', text: label, **find_options) }, @form_context)
   end
 
   # Usage:
@@ -89,8 +89,9 @@ class ZammadFormFieldCapybaraElementDelegator < SimpleDelegator
   end
 
   # Returns identifier of the form field.
-  def field_id # rubocop:disable Metrics/CyclomaticComplexity
+  def field_id # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
     return element.find('.formkit-input', visible: :all)['id'] if input? || type_date? || type_datetime?
+    return element.find('textarea')['id'] if type_textarea?
     return element.find('.formkit-fieldset')['id'] if type_radio?
     return element.find('[role="textbox"]')['id'] if type_editor?
     return element.find('input[type="checkbox"]', visible: :all)['id'] if type_toggle? || type_checkbox?

+ 93 - 0
spec/system/apps/mobile/tickets/ticket_information_customer_edit_spec.rb

@@ -0,0 +1,93 @@
+# Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
+
+require 'rails_helper'
+
+# TODO: Check why editing customer's secondary organizations is not working.
+
+RSpec.describe 'Mobile > Ticket > Information > Customer Edit', app: :mobile, authenticated_as: :authenticate, db_strategy: :reset, type: :system do
+  let(:primary_organization)    { create(:organization) }
+  let(:secondary_organizations) { create_list(:organization, 4) }
+  let(:customer)                { create(:customer, organization: primary_organization, organizations: secondary_organizations, address: 'Berlin') }
+  let(:group)                   { create(:group) }
+  let(:ticket)                  { create(:ticket, customer: customer, group: group) }
+  let(:agent)                   { create(:agent, groups: [group]) }
+  let(:closed_tickets)          { create_list(:ticket, 2, customer: customer, group: group, state: Ticket::State.find_by(name: 'closed')) }
+
+  def authenticate
+    closed_tickets
+    create(:object_manager_attribute_text, object_name: 'User', name: 'text_attribute', display: 'Text Attribute', screens: { edit: { '-all-' => { shown: true, required: false } }, view: { '-all-' => { shown: true, required: false } } })
+    ObjectManager::Attribute.migration_execute
+    agent
+  end
+
+  before do
+    visit "/tickets/#{ticket.id}"
+
+    wait_for_gql('apps/mobile/pages/ticket/graphql/queries/ticket.graphql')
+    wait_for_gql('shared/entities/object-attributes/graphql/queries/objectManagerFrontendAttributes.graphql')
+
+    # Switch to ticket information screen.
+    click '[data-test-id="title-content"]'
+
+    click_button('Customer')
+
+    wait_for_gql('apps/mobile/entities/user/graphql/queries/user.graphql')
+    wait_for_gql('shared/entities/object-attributes/graphql/queries/objectManagerFrontendAttributes.graphql', number: 2)
+  end
+
+  it 'shows customer data' do
+    expect(find("[role=\"img\"][aria-label=\"Avatar (#{customer.fullname})\"]")).to have_text(customer.firstname[0].upcase + customer.lastname[0].upcase)
+    expect(find('h2')).to have_text(customer.fullname)
+    expect(find('h3')).to have_text(primary_organization.name)
+    expect(find('section', text: 'Email')).to have_text(customer.email)
+    expect(find('section', text: 'Address')).to have_text('Berlin')
+    expect(page).to have_no_css('section', text: 'Text Attribute')
+
+    click_button('Show 1 more')
+
+    wait_for_gql('apps/mobile/entities/user/graphql/queries/user.graphql', number: 2)
+
+    secondary_organizations.each do |organization|
+      expect(page).to have_text(organization.name)
+    end
+
+    expect(find_all('[data-test-id="section-menu-item"]')[0]).to have_text("open\n1")
+    expect(find_all('[data-test-id="section-menu-item"]')[1]).to have_text("closed\n2")
+  end
+
+  it 'supports editing customer data' do
+    click_button('Edit Customer')
+
+    wait_for_form_to_settle('user-edit')
+
+    within_form(form_updater_gql_number: 2) do
+      find_input('Text Attribute').type('foobar')
+      find_input('First name').type('Foo')
+      find_input('Last name').type('Bar')
+      find_input('Address').type('München')
+      find_autocomplete('Organization').search_for_option(secondary_organizations.first.name)
+
+      # # Despite the name of the action, the following DESELECTS all secondary organizations for the customer.
+      # #   This works because all these values are already selected in the field.
+      # find_autocomplete('Secondary organizations').select_options(secondary_organizations.map { |organization| organization.name })
+    end
+
+    click_button('Save')
+
+    wait_for_gql('shared/graphql/subscriptions/userUpdates.graphql')
+
+    expect(find('[role="img"][aria-label="Avatar (Foo Bar)"]')).to have_text('FB')
+    expect(find('h2')).to have_text('Foo Bar')
+    expect(find('h3')).to have_text(secondary_organizations.first.name)
+    expect(find('section', text: 'Address')).to have_text('München')
+    expect(find('section', text: 'Text Attribute')).to have_text('foobar')
+
+    # expect(page).to have_no_text('Secondary organizations')
+
+    # secondary_organizations.each do |organization|
+    #   expect(page).to have_no_text(organization.name)
+    # end
+
+    expect(customer.reload).to have_attributes(firstname: 'Foo', lastname: 'Bar', text_attribute: 'foobar', address: 'München')
+  end
+end