can_assets_examples.rb 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. RSpec.shared_examples 'ApplicationModel::CanAssets' do |associations: [], selectors: [], own_attributes: true|
  2. subject { create(described_class.name.underscore, updated_by_id: admin.id) }
  3. let(:admin) { create(:admin_user) }
  4. describe '#assets (for supplying model data to front-end framework)' do
  5. shared_examples 'own asset attributes' do
  6. it 'returns a hash with own asset attributes' do
  7. expect(subject.assets({}).dig(described_class.to_app_model))
  8. .to include(subject.id => hash_including(subject.attributes_with_association_ids))
  9. end
  10. end
  11. include_examples 'own asset attributes' if own_attributes
  12. describe 'for created_by & updated_by users' do
  13. let(:users) { User.where(id: subject.attributes.slice('created_by_id', 'updated_by_id').values) }
  14. let(:users_assets) { users.reduce({}) { |assets_hash, user| user.assets(assets_hash) } }
  15. it 'returns a hash with their asset attributes' do
  16. expect(subject.assets({})[:User]).to include(users_assets[:User])
  17. end
  18. end
  19. context 'when given a non-empty hash' do
  20. let(:hash) { { described_class.to_app_model => { foo: 'bar' } } }
  21. it 'deep-merges assets into it, in place' do
  22. expect { subject.assets(hash) }
  23. .to change { hash }
  24. .to(hash.deep_merge(subject.assets({})))
  25. end
  26. end
  27. Array(associations).each do |association|
  28. describe "for ##{association} association" do
  29. let(:reflection) { described_class.reflect_on_association(association) }
  30. shared_examples 'single association' do
  31. subject { create(described_class.name.underscore, association => single) }
  32. let(:single) { create(reflection.class_name.underscore) }
  33. it 'returns a hash with its asset attributes' do
  34. expect(subject.assets({})).to include(single.assets({}))
  35. end
  36. context 'after association has been modified' do
  37. it 'does not use a cached value' do
  38. subject.assets({})
  39. single.update(updated_by_id: User.last.id)
  40. expect(subject.assets({}).dig(reflection.klass.to_app_model, single.id))
  41. .to include(single.attributes_with_association_ids)
  42. end
  43. end
  44. end
  45. shared_examples 'collection association' do
  46. subject { create(described_class.name.underscore, association => collection) }
  47. let(:collection) { create_list(reflection.class_name.underscore, 5) }
  48. let(:collection_assets) { collection.reduce({}) { |assets_hash, single| single.assets(assets_hash) } }
  49. it 'returns a hash with their asset attributes' do
  50. expect(subject.assets({})).to include(collection_assets)
  51. end
  52. context 'after association has been modified' do
  53. it 'does not use a cached value' do
  54. subject.assets({})
  55. collection.first.update(updated_by_id: User.last.id)
  56. expect(subject.assets({}).dig(reflection.klass.to_app_model, collection.first.id))
  57. .to include(collection.first.attributes_with_association_ids)
  58. end
  59. end
  60. end
  61. if described_class.reflect_on_association(association).macro.in?(%i[has_one belongs_to])
  62. include_examples 'single association'
  63. else
  64. include_examples 'collection association'
  65. end
  66. end
  67. end
  68. Array(selectors).each do |s|
  69. subject { create(described_class.name.underscore, s => selector) }
  70. let(:selector) { { 'ticket.priority_id' => { operator: 'is', value: [1, 2] } } }
  71. let(:priorities_assets) { Ticket::Priority.first(2).reduce({}) { |asset_hash, priority| priority.assets(asset_hash) } }
  72. describe "for objects referenced in ##{s}" do
  73. it 'returns a hash with their asset attributes' do
  74. expect(subject.assets({})).to include(priorities_assets)
  75. end
  76. end
  77. end
  78. end
  79. end