Merge pull request #10869 from demarches-simplifiees/add_id_to_column_second_part

Tech: utilise les objets `SortedColumn`
This commit is contained in:
LeSim 2024-10-09 13:05:50 +00:00 committed by GitHub
commit 163fa42007
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 409 additions and 267 deletions

View file

@ -886,11 +886,11 @@ describe Instructeurs::ProceduresController, type: :controller do
end
it 'can change order' do
column_id = procedure.find_column(label: "Nom").id
expect { get :update_sort, params: { procedure_id: procedure.id, column_id:, order: 'asc' } }
.to change { procedure_presentation.sort }
.from({ "column" => "notifications", "order" => "desc", "table" => "notifications" })
.to({ "column" => "nom", "order" => "asc", "table" => "individual" })
column = procedure.find_column(label: "Nom")
expect { get :update_sort, params: { procedure_id: procedure.id, sorted_column: { id: column.id, order: 'asc' } } }
.to change { procedure_presentation.sorted_column }
.from(procedure.default_sorted_column)
.to(SortedColumn.new(column:, order: 'asc'))
end
end

View file

@ -47,24 +47,4 @@ describe '20201001161931_migrate_filters_to_use_stable_id' do
expect(procedure_presentation_without_migration.filters['suivis']).to be_present
end
end
context 'when the procedure presentation is invalid' do
before do
procedure_presentation_with_migration.update_column(
:sort,
{ table: 'invalid-table', column: 'invalid-column', order: 'invalid-order' }
)
end
it 'removes the "migrated" key properly' do
run_task
expect(procedure_presentation_with_migration).not_to be_valid
expect(procedure_presentation_with_migration.filters).not_to have_key('migrated')
end
it 'leaves the other keys unchanged' do
run_task
expect(procedure_presentation_without_migration.filters['suivis']).to be_present
end
end
end

View file

@ -45,10 +45,10 @@ describe '20240920130741_migrate_procedure_presentation_to_columns.rake' do
order, column_id = procedure_presentation
.sorted_column
.then { |sorted| [sorted['order'], sorted['id']] }
.then { |sorted| [sorted.order, sorted.column.h_id] }
expect(order).to eq('desc')
expect(column_id).to eq("procedure_id" => procedure_id, "column_id" => "self/en_construction_at")
expect(column_id).to eq(procedure_id: procedure_id, column_id: "self/en_construction_at")
expect(procedure_presentation.tous_filters).to eq([])

View file

@ -1,6 +1,22 @@
# frozen_string_literal: true
describe ColumnsConcern do
describe '#find_column' do
let(:procedure) { build(:procedure) }
let(:notifications_column) { procedure.notifications_column }
it do
label = notifications_column.label
expect(procedure.find_column(label:)).to eq(notifications_column)
h_id = notifications_column.h_id
expect(procedure.find_column(h_id:)).to eq(notifications_column)
unknwon = 'unknown'
expect { procedure.find_column(h_id: unknwon) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
describe "#columns" do
subject { procedure.columns }

View file

@ -41,13 +41,6 @@ describe ProcedurePresentation do
it { expect(build(:procedure_presentation, displayed_fields: [{ table: "user", column: "reset_password_token", "order" => "asc" }])).to be_invalid }
end
context 'of sort' do
it { expect(build(:procedure_presentation, sort: { table: "notifications", column: "notifications", "order" => "asc" })).to be_valid }
it { expect(build(:procedure_presentation, sort: { table: "self", column: "id", "order" => "asc" })).to be_valid }
it { expect(build(:procedure_presentation, sort: { table: "self", column: "state", "order" => "asc" })).to be_valid }
it { expect(build(:procedure_presentation, sort: { table: "user", column: "reset_password_token", "order" => "asc" })).to be_invalid }
end
context 'of filters' do
it { expect(build(:procedure_presentation, filters: { "suivis" => [{ table: "user", column: "reset_password_token", "order" => "asc" }] })).to be_invalid }
it { expect(build(:procedure_presentation, filters: { "suivis" => [{ table: "user", column: "email", "value" => "exceedingly long filter value" * 10 }] })).to be_invalid }
@ -61,19 +54,18 @@ describe ProcedurePresentation do
describe '#sorted_ids' do
let(:instructeur) { create(:instructeur) }
let(:assign_to) { create(:assign_to, procedure: procedure, instructeur: instructeur) }
let(:sort) { { 'table' => table, 'column' => column, 'order' => order } }
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to, sort: sort) }
let(:assign_to) { create(:assign_to, procedure:, instructeur:) }
let(:sorted_column) { SortedColumn.new(column:, order:) }
let(:procedure_presentation) { create(:procedure_presentation, assign_to:, sorted_column:) }
subject { procedure_presentation.send(:sorted_ids, procedure.dossiers, procedure.dossiers.count) }
context 'for notifications table' do
let(:table) { 'notifications' }
let(:column) { 'notifications' }
let(:column) { procedure.notifications_column }
let!(:notified_dossier) { create(:dossier, :en_construction, procedure: procedure) }
let!(:recent_dossier) { create(:dossier, :en_construction, procedure: procedure) }
let!(:older_dossier) { create(:dossier, :en_construction, procedure: procedure) }
let!(:notified_dossier) { create(:dossier, :en_construction, procedure:) }
let!(:recent_dossier) { create(:dossier, :en_construction, procedure:) }
let!(:older_dossier) { create(:dossier, :en_construction, procedure:) }
before do
notified_dossier.update!(last_champ_updated_at: Time.zone.local(2018, 9, 20))
@ -96,7 +88,7 @@ describe ProcedurePresentation do
end
context 'with a dossier terminé' do
let!(:notified_dossier) { create(:dossier, :accepte, procedure: procedure) }
let!(:notified_dossier) { create(:dossier, :accepte, procedure:) }
let(:order) { 'desc' }
it { is_expected.to eq([notified_dossier, recent_dossier, older_dossier].map(&:id)) }
@ -104,11 +96,10 @@ describe ProcedurePresentation do
end
context 'for self table' do
let(:table) { 'self' }
let(:order) { 'asc' } # Desc works the same, no extra test required
context 'for created_at column' do
let(:column) { 'created_at' }
let!(:column) { procedure.find_column(label: 'Créé le') }
let!(:recent_dossier) { Timecop.freeze(Time.zone.local(2018, 10, 17)) { create(:dossier, procedure: procedure) } }
let!(:older_dossier) { Timecop.freeze(Time.zone.local(2003, 11, 11)) { create(:dossier, procedure: procedure) } }
@ -116,7 +107,7 @@ describe ProcedurePresentation do
end
context 'for en_construction_at column' do
let(:column) { 'en_construction_at' }
let!(:column) { procedure.find_column(label: 'En construction le') }
let!(:recent_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2018, 10, 17)) }
let!(:older_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2013, 1, 1)) }
@ -124,7 +115,7 @@ describe ProcedurePresentation do
end
context 'for updated_at column' do
let(:column) { 'updated_at' }
let(:column) { procedure.find_column(label: 'Mis à jour le') }
let(:recent_dossier) { create(:dossier, procedure: procedure) }
let(:older_dossier) { create(:dossier, procedure: procedure) }
@ -140,10 +131,10 @@ describe ProcedurePresentation do
context 'for type_de_champ table' do
context 'with no revisions' do
let(:table) { 'type_de_champ' }
let(:column) { procedure.active_revision.types_de_champ_public.first.stable_id.to_s }
let(:column) { procedure.find_column(label: first_type_de_champ.libelle) }
let(:beurre_dossier) { create(:dossier, procedure: procedure) }
let(:tartine_dossier) { create(:dossier, procedure: procedure) }
let(:beurre_dossier) { create(:dossier, procedure:) }
let(:tartine_dossier) { create(:dossier, procedure:) }
before do
beurre_dossier.project_champs_public.first.update(value: 'beurre')
@ -165,12 +156,11 @@ describe ProcedurePresentation do
context 'with a revision adding a new type_de_champ' do
let!(:tdc) { { type_champ: :text, libelle: 'nouveau champ' } }
let(:table) { 'type_de_champ' }
let(:column) { procedure.active_revision.types_de_champ_public.last.stable_id.to_s }
let(:column) { procedure.find_column(label: 'nouveau champ') }
let(:nothing_dossier) { create(:dossier, procedure: procedure) }
let(:beurre_dossier) { create(:dossier, procedure: procedure) }
let(:tartine_dossier) { create(:dossier, procedure: procedure) }
let!(:nothing_dossier) { create(:dossier, procedure:) }
let!(:beurre_dossier) { create(:dossier, procedure:) }
let!(:tartine_dossier) { create(:dossier, procedure:) }
before do
nothing_dossier
@ -182,20 +172,19 @@ describe ProcedurePresentation do
context 'asc' do
let(:order) { 'asc' }
it { is_expected.to eq([beurre_dossier, tartine_dossier, nothing_dossier].map(&:id)) }
it { is_expected.to eq([nothing_dossier, beurre_dossier, tartine_dossier].map(&:id)) }
end
context 'desc' do
let(:order) { 'desc' }
it { is_expected.to eq([nothing_dossier, tartine_dossier, beurre_dossier].map(&:id)) }
it { is_expected.to eq([tartine_dossier, beurre_dossier, nothing_dossier].map(&:id)) }
end
end
end
context 'for type_de_champ_private table' do
context 'with no revisions' do
let(:table) { 'type_de_champ' }
let(:column) { procedure.active_revision.types_de_champ_private.first.stable_id.to_s }
let(:column) { procedure.find_column(label: procedure.active_revision.types_de_champ_private.first.libelle) }
let(:biere_dossier) { create(:dossier, procedure: procedure) }
let(:vin_dossier) { create(:dossier, procedure: procedure) }
@ -217,38 +206,9 @@ describe ProcedurePresentation do
it { is_expected.to eq([vin_dossier, biere_dossier].map(&:id)) }
end
end
context 'with a revision adding a new type_de_champ' do
let!(:tdc) { { type_champ: :text, private: true, libelle: 'nouveau champ' } }
let(:table) { 'type_de_champ' }
let(:column) { procedure.active_revision.types_de_champ_private.last.stable_id.to_s }
let(:nothing_dossier) { create(:dossier, procedure: procedure) }
let(:biere_dossier) { create(:dossier, procedure: procedure) }
let(:vin_dossier) { create(:dossier, procedure: procedure) }
before do
nothing_dossier
procedure.draft_revision.add_type_de_champ(tdc)
procedure.publish_revision!
biere_dossier.project_champs_private.last.update(value: 'biere')
vin_dossier.project_champs_private.last.update(value: 'vin')
end
context 'asc' do
let(:order) { 'asc' }
it { is_expected.to eq([biere_dossier, vin_dossier, nothing_dossier].map(&:id)) }
end
context 'desc' do
let(:order) { 'desc' }
it { is_expected.to eq([nothing_dossier, vin_dossier, biere_dossier].map(&:id)) }
end
end
end
context 'for individual table' do
let(:table) { 'individual' }
let(:order) { 'asc' } # Desc works the same, no extra test required
let(:procedure) { create(:procedure, :for_individual) }
@ -257,26 +217,25 @@ describe ProcedurePresentation do
let!(:last_dossier) { create(:dossier, procedure: procedure, individual: build(:individual, gender: 'Mme', prenom: 'Zora', nom: 'Zemmour')) }
context 'for gender column' do
let(:column) { 'gender' }
let(:column) { procedure.find_column(label: 'Civilité') }
it { is_expected.to eq([first_dossier, last_dossier].map(&:id)) }
end
context 'for prenom column' do
let(:column) { 'prenom' }
let(:column) { procedure.find_column(label: 'Prénom') }
it { is_expected.to eq([first_dossier, last_dossier].map(&:id)) }
end
context 'for nom column' do
let(:column) { 'nom' }
let(:column) { procedure.find_column(label: 'Nom') }
it { is_expected.to eq([first_dossier, last_dossier].map(&:id)) }
end
end
context 'for followers_instructeurs table' do
let(:table) { 'followers_instructeurs' }
let(:order) { 'asc' } # Desc works the same, no extra test required
let!(:dossier_z) { create(:dossier, :en_construction, procedure: procedure) }
@ -290,15 +249,14 @@ describe ProcedurePresentation do
end
context 'for email column' do
let(:column) { 'email' }
let(:column) { procedure.find_column(label: 'Email instructeur') }
it { is_expected.to eq([dossier_a, dossier_z, dossier_without_instructeur].map(&:id)) }
end
end
context 'for avis table' do
let(:table) { 'avis' }
let(:column) { 'question_answer' }
let(:column) { procedure.find_column(label: 'Avis oui/non') }
let(:order) { 'asc' }
let!(:dossier_yes) { create(:dossier, procedure:) }
@ -315,8 +273,7 @@ describe ProcedurePresentation do
context 'for other tables' do
# All other columns and tables work the same so its ok to test only one
let(:table) { 'etablissement' }
let(:column) { 'code_postal' }
let(:column) { procedure.find_column(label: 'Code postal') }
let(:order) { 'asc' } # Desc works the same, no extra test required
let!(:huitieme_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, code_postal: '75008')) }
@ -971,7 +928,7 @@ describe ProcedurePresentation do
describe '#update_displayed_fields' do
let(:procedure_presentation) do
create(:procedure_presentation, assign_to:).tap do |pp|
pp.update_sort(procedure.find_column(label: 'Demandeur').id, 'desc')
pp.update(sorted_column: SortedColumn.new(column: procedure.find_column(label: 'Demandeur'), order: 'desc'))
end
end
@ -992,26 +949,8 @@ describe ProcedurePresentation do
{ "column_id" => "self/updated_at", "procedure_id" => procedure.id }
])
expect(procedure_presentation.sorted_column['id']).to eq("column_id" => "self/id", "procedure_id" => procedure.id)
expect(procedure_presentation.sorted_column['order']).to eq('desc')
end
end
describe '#update_sort' do
let(:procedure_presentation) { create(:procedure_presentation, assign_to:) }
subject do
column_id = procedure.find_column(label: 'En construction le').id
procedure_presentation.update_sort(column_id, 'asc')
end
it 'should update sort and order' do
expect(procedure_presentation.sorted_column).to be_nil
subject
expect(procedure_presentation.sorted_column['id']).to eq("column_id" => "self/en_construction_at", "procedure_id" => procedure.id)
expect(procedure_presentation.sorted_column['order']).to eq('asc')
expect(procedure_presentation.sorted_column).to eq(procedure.default_sorted_column)
expect(procedure_presentation.sorted_column.order).to eq('desc')
end
end
end

View file

@ -0,0 +1,28 @@
# frozen_string_literal: true
describe SortedColumn do
let(:column) { Column.new(procedure_id: 1, table: 'table', column: 'column') }
let(:sorted_column) { SortedColumn.new(column: column, order: 'asc') }
describe '==' do
it 'returns true for the same sorted column' do
other = SortedColumn.new(column: column, order: 'asc')
expect(sorted_column == other).to eq(true)
end
it 'returns false if the order is different' do
other = SortedColumn.new(column: column, order: 'desc')
expect(sorted_column == other).to eq(false)
end
it 'returns false if the column is different' do
other_column = Column.new(procedure_id: 1, table: 'table', column: 'other')
other = SortedColumn.new(column: other_column, order: 'asc')
expect(sorted_column == other).to eq(false)
end
it 'returns false if the other is nil' do
expect(sorted_column == nil).to eq(false)
end
end
end

View file

@ -0,0 +1,71 @@
# frozen_string_literal: true
describe ColumnType do
let(:type) { ColumnType.new }
describe 'cast' do
it 'from Column' do
column = Column.new(procedure_id: 1, table: 'table', column: 'column')
expect(type.cast(column)).to eq(column)
end
it 'from nil' do
expect(type.cast(nil)).to eq(nil)
end
describe 'from form' do
it 'with valid column id' do
column = Column.new(procedure_id: 1, table: 'table', column: 'column')
expect(Column).to receive(:find).with(column.h_id).and_return(column)
expect(type.cast(column.id)).to eq(column)
end
it 'with invalid column id' do
expect { type.cast('invalid') }.to raise_error(JSON::ParserError)
id = { procedure_id: 'invalid', column_id: 'nop' }.to_json
expect { type.cast(id) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
describe 'from db' do
it 'with valid column id' do
column = Column.new(procedure_id: 1, table: 'table', column: 'column')
expect(Column).to receive(:find).with(column.h_id).and_return(column)
expect(type.cast(column.h_id)).to eq(column)
end
it 'with invalid column id' do
h_id = { procedure_id: 'invalid', column_id: 'nop' }
expect { type.cast(h_id) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
describe 'deserialize' do
context 'with valid value' do
it 'works' do
column = Column.new(procedure_id: 1, table: 'table', column: 'column')
expect(Column).to receive(:find).with(column.h_id).and_return(column)
expect(type.deserialize(column.h_id)).to eq(column)
end
end
end
describe 'serialize' do
it 'with SortedColumn' do
column = Column.new(procedure_id: 1, table: 'table', column: 'column')
expect(type.serialize(column)).to eq(column.h_id.to_json)
end
it 'with nil' do
expect(type.serialize(nil)).to eq(nil)
end
it 'with invalid value' do
expect { type.serialize('invalid') }.to raise_error(ArgumentError)
end
end
end

View file

@ -0,0 +1,71 @@
# frozen_string_literal: true
describe SortedColumnType do
let(:type) { SortedColumnType.new }
describe 'cast' do
it 'from SortedColumn' do
column = Column.new(procedure_id: 1, table: 'table', column: 'column')
sorted_column = SortedColumn.new(column:, order: 'asc')
expect(type.cast(sorted_column)).to eq(sorted_column)
end
it 'from nil' do
expect(type.cast(nil)).to eq(nil)
end
describe 'from form' do
it 'with valid column id' do
column = Column.new(procedure_id: 1, table: 'table', column: 'column')
h = { order: 'asc', id: column.id }
expect(Column).to receive(:find).with(column.h_id).and_return(column)
expect(type.cast(h)).to eq(SortedColumn.new(column: column, order: 'asc'))
end
it 'with invalid column id' do
h = { order: 'asc', id: 'invalid' }
expect { type.cast(h) }.to raise_error(JSON::ParserError)
h = { order: 'asc', id: { procedure_id: 'invalid', column_id: 'nop' }.to_json }
expect { type.cast(h) }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'with invalid order' do
column = Column.new(procedure_id: 1, table: 'table', column: 'column')
h = { order: 'invalid', id: column.id }
expect { type.cast(h) }.to raise_error(NoMatchingPatternError)
end
end
end
describe 'deserialize' do
context 'with valid value' do
it 'works' do
column = Column.new(procedure_id: 1, table: 'table', column: 'column')
expect(Column).to receive(:find).with(column.h_id).and_return(column)
expect(type.deserialize({ id: column.h_id, order: 'asc' }.to_json)).to eq(SortedColumn.new(column: column, order: 'asc'))
end
end
context 'with nil' do
it { expect(type.deserialize(nil)).to eq(nil) }
end
end
describe 'serialize' do
it 'with SortedColumn' do
column = Column.new(procedure_id: 1, table: 'table', column: 'column')
sorted_column = SortedColumn.new(column: column, order: 'asc')
expect(type.serialize(sorted_column)).to eq({ id: column.h_id, order: 'asc' }.to_json)
end
it 'with nil' do
expect(type.serialize(nil)).to eq(nil)
end
it 'with invalid value' do
expect { type.serialize('invalid') }.to raise_error(ArgumentError)
end
end
end