From e7eee7ba721f21a1cb0ab65f82f6d027f2ffd161 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 11 Jan 2024 11:04:43 +0100 Subject: [PATCH] spec(move_and_morph): add some spec and lint with rubocop/haml/eslint --- .../block_component/block_component.html.haml | 2 +- .../types_de_champ_editor/champ_component.rb | 3 +- .../select_champ_position_component.html.haml | 4 +- ...hamp_template_position_component.html.haml | 2 +- ...lect_champ_position_template_controller.ts | 1 + .../champ_component_spec.rb | 2 +- .../administrateurs/types_de_champ_spec.rb | 132 ++++++++++++++---- 7 files changed, 112 insertions(+), 34 deletions(-) diff --git a/app/components/types_de_champ_editor/block_component/block_component.html.haml b/app/components/types_de_champ_editor/block_component/block_component.html.haml index c2c8d9d7a..109234df4 100644 --- a/app/components/types_de_champ_editor/block_component/block_component.html.haml +++ b/app/components/types_de_champ_editor/block_component/block_component.html.haml @@ -1,6 +1,6 @@ - c = TypesDeChampEditor::SelectChampTemplatePositionComponent.new(block: @block, coordinates: @coordinates) %ul.types-de-champ-block{ id: block_id, data: sortable_options.merge(controller: 'select-champ-position-template', 'select-champ-position-template-template-id-value': c.block_id ) } - %li.hidden= render c - @coordinates.each do |coordinate| = render TypesDeChampEditor::ChampComponent.new(coordinate:, upper_coordinates: coordinate.upper_coordinates) + %li.hidden= render c diff --git a/app/components/types_de_champ_editor/champ_component.rb b/app/components/types_de_champ_editor/champ_component.rb index 3ec6c35c7..3b8eca491 100644 --- a/app/components/types_de_champ_editor/champ_component.rb +++ b/app/components/types_de_champ_editor/champ_component.rb @@ -30,8 +30,7 @@ class TypesDeChampEditor::ChampComponent < ApplicationComponent controller: 'type-de-champ-editor', type_de_champ_editor_move_url_value: move_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id), type_de_champ_editor_move_up_url_value: move_up_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id), - type_de_champ_editor_move_down_url_value: move_down_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id), - type_de_champ_editor_move_and_morph_url_value: move_and_morph_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id) + type_de_champ_editor_move_down_url_value: move_down_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id) } } end diff --git a/app/components/types_de_champ_editor/select_champ_position_component/select_champ_position_component.html.haml b/app/components/types_de_champ_editor/select_champ_position_component/select_champ_position_component.html.haml index 35c2a7e35..b74c6e002 100644 --- a/app/components/types_de_champ_editor/select_champ_position_component/select_champ_position_component.html.haml +++ b/app/components/types_de_champ_editor/select_champ_position_component/select_champ_position_component.html.haml @@ -1,3 +1,3 @@ = form_with(url: move_and_morph_admin_procedure_type_de_champ_path(@coordinate.revision.procedure, @coordinate.type_de_champ.stable_id), class: 'fr-ml-3w flex', method: :patch, data: { turbo: true }) do |f| - = label_tag :target_stable_id, "Déplacer le champ", for: describedby_id, class: 'flex align-center flex-no-shrink fr-mr-3w' - = select_tag :target_stable_id, options_for_select(options), id: describedby_id, class: 'fr-select', aria: { discribedby: describedby_id }, data: { 'select-champ-position-template-target': 'select', selected: @coordinate.stable_id } \ No newline at end of file + = label_tag :target_stable_id, "Déplacer le champ", for: describedby_id, class: 'flex align-center flex-no-shrink fr-mr-3w' + = select_tag :target_stable_id, options_for_select(options), id: describedby_id, class: 'fr-select', aria: { discribedby: describedby_id }, data: { 'select-champ-position-template-target': 'select', selected: @coordinate.stable_id } diff --git a/app/components/types_de_champ_editor/select_champ_template_position_component/select_champ_template_position_component.html.haml b/app/components/types_de_champ_editor/select_champ_template_position_component/select_champ_template_position_component.html.haml index c66ad1fa9..ddd30c44e 100644 --- a/app/components/types_de_champ_editor/select_champ_template_position_component/select_champ_template_position_component.html.haml +++ b/app/components/types_de_champ_editor/select_champ_template_position_component/select_champ_template_position_component.html.haml @@ -2,4 +2,4 @@ %select %option{ disabled: :disabled } Selectionner une option - @coordinates.each do |coordinate| - %option{ value: coordinate.stable_id }= "#{coordinate.position} #{coordinate.libelle}" \ No newline at end of file + %option{ value: coordinate.stable_id }= "#{coordinate.position} #{coordinate.libelle}" diff --git a/app/javascript/controllers/select_champ_position_template_controller.ts b/app/javascript/controllers/select_champ_position_template_controller.ts index cd0523137..ebcbdd23e 100644 --- a/app/javascript/controllers/select_champ_position_template_controller.ts +++ b/app/javascript/controllers/select_champ_position_template_controller.ts @@ -61,6 +61,7 @@ export class SelectChampPositionTemplateController extends ApplicationController if (stableIdDidChange) { changedSelectTarget.form?.requestSubmit(); } + event.stopImmediatePropagation(); } private getStableIdForSelect(select: HTMLSelectElement): string | null { diff --git a/spec/components/types_de_champ_editor/champ_component_spec.rb b/spec/components/types_de_champ_editor/champ_component_spec.rb index e5a9a6410..1e368f1ba 100644 --- a/spec/components/types_de_champ_editor/champ_component_spec.rb +++ b/spec/components/types_de_champ_editor/champ_component_spec.rb @@ -65,7 +65,7 @@ describe TypesDeChampEditor::ChampComponent, type: :component do let(:coordinate) { procedure.draft_revision.revision_types_de_champ_public.first } let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :text, libelle: 'a' }]) } it 'does not have select to move champs' do - expect(page).not_to have_css("select##{ActionView::RecordIdentifier.dom_id(coordinate, :move_and_morph)}") + expect(page).to have_css("select##{ActionView::RecordIdentifier.dom_id(coordinate, :move_and_morph)}") end end end diff --git a/spec/system/administrateurs/types_de_champ_spec.rb b/spec/system/administrateurs/types_de_champ_spec.rb index e86b0b775..bcbafd039 100644 --- a/spec/system/administrateurs/types_de_champ_spec.rb +++ b/spec/system/administrateurs/types_de_champ_spec.rb @@ -234,37 +234,115 @@ describe 'As an administrateur I can edit types de champ', js: true do end end - context 'move and morph champs' do - let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :text, libelle: 'first_tdc' }, { type: :text, libelle: 'middle_tdc' }, { type: :text, libelle: 'last_tdc' }]) } - let!(:initial_first_coordinate) { procedure.draft_revision.revision_types_de_champ[0] } - let!(:initial_second_coordinate) { procedure.draft_revision.revision_types_de_champ[1] } - let!(:initial_third_coordinate) { procedure.draft_revision.revision_types_de_champ[2] } - # TODO: check no select when 1 champs - # TODO: check empty select when 1 champs - # TODO: check select is seeding on focus - # TODO: check select.change move champ and keep order - # TODO: select options are segmented by block - scenario 'root champs' do - initial_order = [initial_first_coordinate, initial_second_coordinate, initial_third_coordinate].map(&:stable_id) - initial_first_coordinate_selector = "##{ActionView::RecordIdentifier.dom_id(initial_first_coordinate, :move_and_morph)}" - # at first, select only contains the current coordinate - expect(page).to have_selector("#{initial_first_coordinate_selector} option", count: 1) - expect(page.find(initial_first_coordinate_selector).all("option").first.value.to_s).to eq(initial_first_coordinate.stable_id.to_s) + context 'move and morph' do + let(:procedure) { create(:procedure, types_de_champ_public: tdcs) } + let!(:initial_first_coordinate) { procedure.draft_revision.revision_types_de_champ_public[0] } + let!(:initial_second_coordinate) { procedure.draft_revision.revision_types_de_champ_public[1] } + let!(:initial_third_coordinate) { procedure.draft_revision.revision_types_de_champ_public[2] } - # once clicked, the select is updated other options - page.find(initial_first_coordinate_selector).click - expect(page).to have_selector("#{initial_first_coordinate_selector} option", count: 4) - # also we re-hydrate the selected value - expect(page.find(initial_first_coordinate_selector).find("option[selected]").value.to_s).to eq(initial_first_coordinate.stable_id.to_s) - page.find(initial_first_coordinate_selector).select(initial_third_coordinate.libelle) - wait_until do - procedure.reload.draft_revision.revision_types_de_champ.last.type_de_champ.libelle == initial_first_coordinate.type_de_champ.libelle + context 'with root champs' do + let(:tdcs) do + [ + { type: :text, libelle: 'first_tdc' }, + { type: :text, libelle: 'middle_tdc' }, + { type: :text, libelle: 'last_tdc' } + ] + end + let(:initial_first_coordinate_selector) { "##{ActionView::RecordIdentifier.dom_id(initial_first_coordinate, :move_and_morph)}" } + + scenario 'root select is empty by default' do + # at first, select only contains the current coordinate + expect(page).to have_selector("#{initial_first_coordinate_selector} option", count: 1) + expect(page.find(initial_first_coordinate_selector).all("option").first.value.to_s).to eq(initial_first_coordinate.stable_id.to_s) + end + + scenario 'when select is focused, it seeds its options' do + # once clicked, the select is updated with root champs options only, preselected on coordinates and have nice libelles + page.find(initial_first_coordinate_selector).click + expect(page).to have_selector("#{initial_first_coordinate_selector} option", count: 4) + expect(page.find(initial_first_coordinate_selector).find("option[selected]").value.to_s).to eq(initial_first_coordinate.stable_id.to_s) + expect(page.find(initial_first_coordinate_selector).all("option").map(&:text)).to match_array(["Selectionner une option", '0 first_tdc', '1 middle_tdc', '2 last_tdc']) + + # renaming a tdc renames it's option + within "##{dom_id(initial_first_coordinate, :type_de_champ_editor)}" do + fill_in 'Libellé du champ', with: 'renamed' + end + wait_until { initial_first_coordinate.reload.libelle == 'renamed' } + page.find(initial_first_coordinate_selector).click + expect(page.find(initial_first_coordinate_selector).all("option").map(&:text)).to match_array(["Selectionner une option", '0 renamed', '1 middle_tdc', '2 last_tdc']) + end + + scenario 'when select is changed, it move the coordinates' do + page.find(initial_first_coordinate_selector).click # seeds + page.find(initial_first_coordinate_selector).select(initial_third_coordinate.libelle) + wait_until do + procedure.reload.draft_revision.revision_types_de_champ.last.type_de_champ.libelle == initial_first_coordinate.type_de_champ.libelle + end + # wait until turbo response + expect(page).to have_text('Formulaire enregistré') + + # check reorder worked on backend + reordered_coordinates = [initial_second_coordinate, initial_third_coordinate, initial_first_coordinate] + expect(procedure.reload.draft_revision.revision_types_de_champ.map(&:stable_id)).to eq(reordered_coordinates.map(&:stable_id)) + + # check reorder rerendered champ component between target->destination + reordered_coordinates.map(&:reload).map do |coordinate| + expect(page).to have_selector("##{ActionView::RecordIdentifier.dom_id(coordinate, :type_de_champ_editor)} .position", text: "##{coordinate.position}") + end end - expect(procedure.reload.draft_revision.revision_types_de_champ.map(&:stable_id)) - .to eq([initial_second_coordinate, initial_third_coordinate, initial_first_coordinate].map(&:stable_id)) end - scenario 'repetition champs' do + context 'with repetition champs' do + let(:tdcs) do + [ + { type: :text, libelle: 'root_first_tdc' }, + { + type: :repetition, + libelle: 'root_second_tdc', + children: [ + { type: :text, libelle: 'child_first_tdc' }, + { type: :text, libelle: 'child_second_tdc' } + ] + }, + { type: :text, libelle: 'root_thrid_tdc' } + ] + end + let(:children_coordinates) { procedure.draft_revision.revision_types_de_champ.filter { _1.parent.present? } } + let(:first_child_coordinate_selector) { "##{ActionView::RecordIdentifier.dom_id(children_coordinates.first, :move_and_morph)}" } + + scenario 'first child of repetition select is empty by default' do + expect(page).to have_selector("#{first_child_coordinate_selector} option", count: 1) + expect(page.find(first_child_coordinate_selector).all("option").first.value.to_s).to eq(children_coordinates.first.stable_id.to_s) + end + + scenario 'when first child select is focused, seed with repetition only tdcs' do + page.find(first_child_coordinate_selector).click + expect(page).to have_selector("#{first_child_coordinate_selector} option", count: 3) + + opts = page.find(first_child_coordinate_selector).all("option").map(&:text) + expect(opts).to match_array(["Selectionner une option"] + children_coordinates.map { "#{_1.position} #{_1.libelle}" }) + end + + scenario 'when first child select is changed, move champ in repetition' do + page.find(first_child_coordinate_selector).click + expect(children_coordinates.first.position).to eq(0) + page.find(first_child_coordinate_selector).select(children_coordinates.last.libelle) + # check reorder works on backend + wait_until do + children_coordinates.first.reload.position == 1 + end + # wait until turbo response + expect(page).to have_text('Formulaire enregistré') + + # check reorder worked on backend + reordered_coordinates = children_coordinates.reverse + expect(procedure.reload.draft_revision.revision_types_de_champ.filter { _1.parent.present? }.sort_by(&:position).map(&:stable_id)).to eq(reordered_coordinates.map(&:stable_id)) + + # check reorder rerendered champ component between target->destination + reordered_coordinates.map(&:reload).map do |coordinate| + expect(page).to have_selector("##{ActionView::RecordIdentifier.dom_id(coordinate, :type_de_champ_editor)} .position", text: coordinate.position) + end + end end end end