paper_trail 5.2.3 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. checksums.yaml +5 -5
  2. data/lib/generators/paper_trail/install/USAGE +3 -0
  3. data/lib/generators/paper_trail/install/install_generator.rb +75 -0
  4. data/lib/generators/paper_trail/{templates/add_object_changes_to_versions.rb → install/templates/add_object_changes_to_versions.rb.erb} +1 -1
  5. data/lib/generators/paper_trail/install/templates/create_versions.rb.erb +36 -0
  6. data/lib/generators/paper_trail/migration_generator.rb +38 -0
  7. data/lib/generators/paper_trail/update_item_subtype/USAGE +4 -0
  8. data/lib/generators/paper_trail/update_item_subtype/templates/update_versions_for_item_subtype.rb.erb +85 -0
  9. data/lib/generators/paper_trail/update_item_subtype/update_item_subtype_generator.rb +17 -0
  10. data/lib/paper_trail.rb +82 -130
  11. data/lib/paper_trail/attribute_serializers/attribute_serializer_factory.rb +27 -0
  12. data/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb +15 -44
  13. data/lib/paper_trail/attribute_serializers/object_attribute.rb +2 -0
  14. data/lib/paper_trail/attribute_serializers/object_changes_attribute.rb +2 -0
  15. data/lib/paper_trail/cleaner.rb +3 -1
  16. data/lib/paper_trail/compatibility.rb +51 -0
  17. data/lib/paper_trail/config.rb +11 -49
  18. data/lib/paper_trail/events/base.rb +323 -0
  19. data/lib/paper_trail/events/create.rb +32 -0
  20. data/lib/paper_trail/events/destroy.rb +42 -0
  21. data/lib/paper_trail/events/update.rb +60 -0
  22. data/lib/paper_trail/frameworks/active_record.rb +2 -1
  23. data/lib/paper_trail/frameworks/active_record/models/paper_trail/version.rb +8 -3
  24. data/lib/paper_trail/frameworks/cucumber.rb +5 -3
  25. data/lib/paper_trail/frameworks/rails.rb +2 -0
  26. data/lib/paper_trail/frameworks/rails/controller.rb +33 -43
  27. data/lib/paper_trail/frameworks/rails/engine.rb +34 -1
  28. data/lib/paper_trail/frameworks/rspec.rb +17 -4
  29. data/lib/paper_trail/frameworks/rspec/helpers.rb +2 -0
  30. data/lib/paper_trail/has_paper_trail.rb +22 -310
  31. data/lib/paper_trail/model_config.rb +157 -109
  32. data/lib/paper_trail/queries/versions/where_object.rb +65 -0
  33. data/lib/paper_trail/queries/versions/where_object_changes.rb +75 -0
  34. data/lib/paper_trail/record_history.rb +3 -9
  35. data/lib/paper_trail/record_trail.rb +169 -319
  36. data/lib/paper_trail/reifier.rb +53 -374
  37. data/lib/paper_trail/request.rb +166 -0
  38. data/lib/paper_trail/serializers/json.rb +9 -10
  39. data/lib/paper_trail/serializers/yaml.rb +15 -28
  40. data/lib/paper_trail/type_serializers/postgres_array_serializer.rb +48 -0
  41. data/lib/paper_trail/version_concern.rb +160 -155
  42. data/lib/paper_trail/version_number.rb +12 -4
  43. metadata +77 -372
  44. data/.github/CONTRIBUTING.md +0 -109
  45. data/.github/ISSUE_TEMPLATE.md +0 -13
  46. data/.gitignore +0 -23
  47. data/.rspec +0 -2
  48. data/.rubocop.yml +0 -99
  49. data/.rubocop_todo.yml +0 -22
  50. data/.travis.yml +0 -41
  51. data/Appraisals +0 -38
  52. data/CHANGELOG.md +0 -560
  53. data/Gemfile +0 -2
  54. data/MIT-LICENSE +0 -20
  55. data/README.md +0 -1654
  56. data/Rakefile +0 -30
  57. data/doc/bug_report_template.rb +0 -69
  58. data/doc/warning_about_not_setting_whodunnit.md +0 -32
  59. data/gemfiles/ar3.gemfile +0 -19
  60. data/gemfiles/ar4.gemfile +0 -8
  61. data/gemfiles/ar5.gemfile +0 -9
  62. data/lib/generators/paper_trail/USAGE +0 -2
  63. data/lib/generators/paper_trail/default_initializer.rb +0 -0
  64. data/lib/generators/paper_trail/install_generator.rb +0 -57
  65. data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +0 -13
  66. data/lib/generators/paper_trail/templates/create_version_associations.rb +0 -22
  67. data/lib/generators/paper_trail/templates/create_versions.rb +0 -80
  68. data/lib/paper_trail/attribute_serializers/legacy_active_record_shim.rb +0 -48
  69. data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +0 -11
  70. data/lib/paper_trail/frameworks/sinatra.rb +0 -40
  71. data/lib/paper_trail/version_association_concern.rb +0 -17
  72. data/paper_trail.gemspec +0 -56
  73. data/spec/generators/install_generator_spec.rb +0 -66
  74. data/spec/generators/paper_trail/templates/create_versions_spec.rb +0 -51
  75. data/spec/models/animal_spec.rb +0 -36
  76. data/spec/models/boolit_spec.rb +0 -48
  77. data/spec/models/callback_modifier_spec.rb +0 -96
  78. data/spec/models/car_spec.rb +0 -13
  79. data/spec/models/custom_primary_key_record_spec.rb +0 -18
  80. data/spec/models/fluxor_spec.rb +0 -17
  81. data/spec/models/gadget_spec.rb +0 -68
  82. data/spec/models/joined_version_spec.rb +0 -47
  83. data/spec/models/json_version_spec.rb +0 -102
  84. data/spec/models/kitchen/banana_spec.rb +0 -14
  85. data/spec/models/not_on_update_spec.rb +0 -22
  86. data/spec/models/post_with_status_spec.rb +0 -50
  87. data/spec/models/skipper_spec.rb +0 -46
  88. data/spec/models/thing_spec.rb +0 -11
  89. data/spec/models/truck_spec.rb +0 -5
  90. data/spec/models/vehicle_spec.rb +0 -5
  91. data/spec/models/version_spec.rb +0 -272
  92. data/spec/models/widget_spec.rb +0 -343
  93. data/spec/modules/paper_trail_spec.rb +0 -27
  94. data/spec/modules/version_concern_spec.rb +0 -31
  95. data/spec/modules/version_number_spec.rb +0 -43
  96. data/spec/paper_trail/config_spec.rb +0 -33
  97. data/spec/paper_trail_spec.rb +0 -79
  98. data/spec/rails_helper.rb +0 -34
  99. data/spec/requests/articles_spec.rb +0 -34
  100. data/spec/spec_helper.rb +0 -114
  101. data/spec/support/alt_db_init.rb +0 -54
  102. data/test/custom_json_serializer.rb +0 -13
  103. data/test/dummy/Rakefile +0 -7
  104. data/test/dummy/app/controllers/application_controller.rb +0 -33
  105. data/test/dummy/app/controllers/articles_controller.rb +0 -20
  106. data/test/dummy/app/controllers/test_controller.rb +0 -5
  107. data/test/dummy/app/controllers/widgets_controller.rb +0 -32
  108. data/test/dummy/app/helpers/application_helper.rb +0 -2
  109. data/test/dummy/app/models/animal.rb +0 -6
  110. data/test/dummy/app/models/article.rb +0 -24
  111. data/test/dummy/app/models/authorship.rb +0 -5
  112. data/test/dummy/app/models/bar_habtm.rb +0 -4
  113. data/test/dummy/app/models/book.rb +0 -9
  114. data/test/dummy/app/models/boolit.rb +0 -4
  115. data/test/dummy/app/models/callback_modifier.rb +0 -45
  116. data/test/dummy/app/models/car.rb +0 -3
  117. data/test/dummy/app/models/cat.rb +0 -2
  118. data/test/dummy/app/models/chapter.rb +0 -9
  119. data/test/dummy/app/models/citation.rb +0 -5
  120. data/test/dummy/app/models/custom_primary_key_record.rb +0 -13
  121. data/test/dummy/app/models/customer.rb +0 -4
  122. data/test/dummy/app/models/document.rb +0 -4
  123. data/test/dummy/app/models/dog.rb +0 -2
  124. data/test/dummy/app/models/editor.rb +0 -4
  125. data/test/dummy/app/models/editorship.rb +0 -5
  126. data/test/dummy/app/models/elephant.rb +0 -3
  127. data/test/dummy/app/models/fluxor.rb +0 -3
  128. data/test/dummy/app/models/foo_habtm.rb +0 -5
  129. data/test/dummy/app/models/foo_widget.rb +0 -2
  130. data/test/dummy/app/models/fruit.rb +0 -5
  131. data/test/dummy/app/models/gadget.rb +0 -3
  132. data/test/dummy/app/models/kitchen/banana.rb +0 -5
  133. data/test/dummy/app/models/legacy_widget.rb +0 -4
  134. data/test/dummy/app/models/line_item.rb +0 -4
  135. data/test/dummy/app/models/not_on_update.rb +0 -4
  136. data/test/dummy/app/models/order.rb +0 -5
  137. data/test/dummy/app/models/paragraph.rb +0 -5
  138. data/test/dummy/app/models/person.rb +0 -38
  139. data/test/dummy/app/models/post.rb +0 -3
  140. data/test/dummy/app/models/post_with_status.rb +0 -8
  141. data/test/dummy/app/models/protected_widget.rb +0 -3
  142. data/test/dummy/app/models/quotation.rb +0 -5
  143. data/test/dummy/app/models/section.rb +0 -6
  144. data/test/dummy/app/models/skipper.rb +0 -6
  145. data/test/dummy/app/models/song.rb +0 -41
  146. data/test/dummy/app/models/thing.rb +0 -3
  147. data/test/dummy/app/models/translation.rb +0 -4
  148. data/test/dummy/app/models/truck.rb +0 -4
  149. data/test/dummy/app/models/vehicle.rb +0 -4
  150. data/test/dummy/app/models/whatchamajigger.rb +0 -4
  151. data/test/dummy/app/models/widget.rb +0 -16
  152. data/test/dummy/app/models/wotsit.rb +0 -8
  153. data/test/dummy/app/versions/custom_primary_key_record_version.rb +0 -3
  154. data/test/dummy/app/versions/joined_version.rb +0 -6
  155. data/test/dummy/app/versions/json_version.rb +0 -3
  156. data/test/dummy/app/versions/kitchen/banana_version.rb +0 -5
  157. data/test/dummy/app/versions/post_version.rb +0 -3
  158. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  159. data/test/dummy/config.ru +0 -4
  160. data/test/dummy/config/application.rb +0 -80
  161. data/test/dummy/config/boot.rb +0 -10
  162. data/test/dummy/config/database.mysql.yml +0 -19
  163. data/test/dummy/config/database.postgres.yml +0 -15
  164. data/test/dummy/config/database.sqlite.yml +0 -15
  165. data/test/dummy/config/environment.rb +0 -5
  166. data/test/dummy/config/environments/development.rb +0 -41
  167. data/test/dummy/config/environments/production.rb +0 -74
  168. data/test/dummy/config/environments/test.rb +0 -51
  169. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -9
  170. data/test/dummy/config/initializers/inflections.rb +0 -10
  171. data/test/dummy/config/initializers/mime_types.rb +0 -5
  172. data/test/dummy/config/initializers/paper_trail.rb +0 -9
  173. data/test/dummy/config/initializers/secret_token.rb +0 -9
  174. data/test/dummy/config/initializers/session_store.rb +0 -8
  175. data/test/dummy/config/locales/en.yml +0 -5
  176. data/test/dummy/config/routes.rb +0 -4
  177. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +0 -361
  178. data/test/dummy/db/schema.rb +0 -288
  179. data/test/dummy/script/rails +0 -8
  180. data/test/functional/controller_test.rb +0 -90
  181. data/test/functional/enabled_for_controller_test.rb +0 -28
  182. data/test/functional/modular_sinatra_test.rb +0 -46
  183. data/test/functional/sinatra_test.rb +0 -51
  184. data/test/functional/thread_safety_test.rb +0 -46
  185. data/test/test_helper.rb +0 -127
  186. data/test/time_travel_helper.rb +0 -1
  187. data/test/unit/associations_test.rb +0 -1016
  188. data/test/unit/cleaner_test.rb +0 -188
  189. data/test/unit/inheritance_column_test.rb +0 -43
  190. data/test/unit/model_test.rb +0 -1489
  191. data/test/unit/protected_attrs_test.rb +0 -52
  192. data/test/unit/serializer_test.rb +0 -119
  193. data/test/unit/serializers/json_test.rb +0 -95
  194. data/test/unit/serializers/mixin_json_test.rb +0 -37
  195. data/test/unit/serializers/mixin_yaml_test.rb +0 -53
  196. data/test/unit/serializers/yaml_test.rb +0 -54
  197. data/test/unit/timestamp_test.rb +0 -41
  198. data/test/unit/version_test.rb +0 -119
@@ -1,188 +0,0 @@
1
- require "test_helper"
2
-
3
- class PaperTrailCleanerTest < ActiveSupport::TestCase
4
- def populate_db!
5
- @animals = [@animal = Animal.new, @dog = Dog.new, @cat = Cat.new]
6
- @animals.each do |animal|
7
- 3.times { animal.update_attribute(:name, FFaker::Name.name) }
8
- end
9
- end
10
-
11
- context "`clean_versions!` method" do
12
- setup { populate_db! }
13
-
14
- should "Baseline" do
15
- assert_equal 9, PaperTrail::Version.count
16
- @animals.each { |animal| assert_equal 3, animal.versions.size }
17
- end
18
-
19
- should "be extended by `PaperTrail` module" do
20
- assert_respond_to PaperTrail, :clean_versions!
21
- end
22
-
23
- context "No options provided" do
24
- should "removes extra versions for each item" do
25
- PaperTrail.clean_versions!
26
- assert_equal 3, PaperTrail::Version.count
27
- @animals.each { |animal| assert_equal 1, animal.versions.size }
28
- end
29
-
30
- should "removes the earliest version(s)" do
31
- before = @animals.map { |animal| animal.versions.last.reify.name }
32
- PaperTrail.clean_versions!
33
- after = @animals.map { |animal| animal.versions.last.reify.name }
34
- assert_equal before, after
35
- end
36
- end
37
-
38
- context "`:keeping` option" do
39
- should "modifies the number of versions ommitted from destruction" do
40
- PaperTrail.clean_versions!(keeping: 2)
41
- assert_equal 6, PaperTrail::Version.all.count
42
- @animals.each { |animal| assert_equal 2, animal.versions.size }
43
- end
44
- end
45
-
46
- context "`:date` option" do
47
- setup do
48
- @animal.versions.each { |ver| ver.update_attribute(:created_at, ver.created_at - 1.day) }
49
- @date = @animal.versions.first.created_at.to_date
50
- @animal.update_attribute(:name, FFaker::Name.name)
51
- end
52
-
53
- should "restrict the versions destroyed to those that were created on the date provided" do
54
- assert_equal 10, PaperTrail::Version.count
55
- assert_equal 4, @animal.versions.size
56
- assert_equal 3, @animal.paper_trail.versions_between(@date, @date + 1.day).size
57
- PaperTrail.clean_versions!(date: @date)
58
- assert_equal 8, PaperTrail::Version.count
59
- assert_equal 2, @animal.versions.reload.size
60
- assert_equal @date, @animal.versions.first.created_at.to_date
61
- assert_not_same @date, @animal.versions.last.created_at.to_date
62
- end
63
- end
64
-
65
- context "`:item_id` option" do
66
- context "single ID received" do
67
- should "restrict the versions destroyed to the versions for the Item with that ID" do
68
- PaperTrail.clean_versions!(item_id: @animal.id)
69
- assert_equal 1, @animal.versions.size
70
- assert_equal 7, PaperTrail::Version.count
71
- end
72
- end
73
-
74
- context "collection of ID's received" do
75
- should "restrict the versions destroyed to the versions for the Item with those ID's" do
76
- PaperTrail.clean_versions!(item_id: [@animal.id, @dog.id])
77
- assert_equal 1, @animal.versions.size
78
- assert_equal 1, @dog.versions.size
79
- assert_equal 5, PaperTrail::Version.count
80
- end
81
- end
82
- end
83
-
84
- context "options combinations" do # additional tests to cover combinations of options
85
- context "`:date`" do
86
- setup do
87
- [@animal, @dog].each do |animal|
88
- animal.versions.each { |ver| ver.update_attribute(:created_at, ver.created_at - 1.day) }
89
- animal.update_attribute(:name, FFaker::Name.name)
90
- end
91
- @date = @animal.versions.first.created_at.to_date
92
- end
93
-
94
- should "Baseline" do
95
- assert_equal 11, PaperTrail::Version.count
96
- [@animal, @dog].each do |animal|
97
- assert_equal 4, animal.versions.size
98
- assert_equal 3, animal.versions.between(@date, @date + 1.day).size
99
- end
100
- end
101
-
102
- context "and `:keeping`" do
103
- should "restrict cleaning properly" do
104
- PaperTrail.clean_versions!(date: @date, keeping: 2)
105
- [@animal, @dog].each do |animal|
106
- # reload the association to pick up the destructions made by the `Cleaner`
107
- animal.versions.reload
108
- assert_equal 3, animal.versions.size
109
- assert_equal 2, animal.versions.between(@date, @date + 1.day).size
110
- end
111
- # ensure that the versions for the `@cat` instance wasn't touched
112
- assert_equal 9, PaperTrail::Version.count
113
- end
114
- end
115
-
116
- context "and `:item_id`" do
117
- should "restrict cleaning properly" do
118
- PaperTrail.clean_versions!(date: @date, item_id: @dog.id)
119
- # reload the association to pick up the destructions made by the `Cleaner`
120
- @dog.versions.reload
121
- assert_equal 2, @dog.versions.size
122
- assert_equal 1, @dog.versions.between(@date, @date + 1.day).size
123
- # ensure the versions for other animals besides `@animal` weren't touched
124
- assert_equal 9, PaperTrail::Version.count
125
- end
126
- end
127
-
128
- context ", `:item_id`, and `:keeping`" do
129
- should "restrict cleaning properly" do
130
- PaperTrail.clean_versions!(date: @date, item_id: @dog.id, keeping: 2)
131
- # reload the association to pick up the destructions made by the `Cleaner`
132
- @dog.versions.reload
133
- assert_equal 3, @dog.versions.size
134
- assert_equal 2, @dog.versions.between(@date, @date + 1.day).size
135
- # ensure the versions for other animals besides `@animal` weren't touched
136
- assert_equal 10, PaperTrail::Version.count
137
- end
138
- end
139
- end
140
-
141
- context "`:keeping` and `:item_id`" do
142
- should "restrict cleaning properly" do
143
- PaperTrail.clean_versions!(keeping: 2, item_id: @animal.id)
144
- assert_equal 2, @animal.versions.size
145
- # ensure the versions for other animals besides `@animal` weren't touched
146
- assert_equal 8, PaperTrail::Version.count
147
- end
148
- end
149
- end
150
- end # clean_versions! method
151
-
152
- context "Custom timestamp field" do
153
- setup do
154
- change_schema
155
- populate_db!
156
- # now mess with the timestamps
157
- @animals.each do |animal|
158
- animal.versions.reverse.each_with_index do |version, index|
159
- version.update_attribute(:custom_created_at, Time.now.utc + index.days)
160
- end
161
- end
162
- PaperTrail.timestamp_field = :custom_created_at
163
- @animals.map { |a| a.versions.reload } # reload the `versions` association for each animal
164
- end
165
-
166
- teardown do
167
- PaperTrail.timestamp_field = :created_at
168
- restore_schema
169
- end
170
-
171
- should "Baseline" do
172
- assert_equal 9, PaperTrail::Version.count
173
- @animals.each do |animal|
174
- assert_equal 3, animal.versions.size
175
- animal.versions.each_cons(2) do |a, b|
176
- assert_equal a.created_at.to_date, b.created_at.to_date
177
- assert_not_equal a.custom_created_at.to_date, b.custom_created_at.to_date
178
- end
179
- end
180
- end
181
-
182
- should "group by `PaperTrail.timestamp_field` when seperating the versions by date to clean" do
183
- assert_equal 9, PaperTrail::Version.count
184
- PaperTrail.clean_versions!
185
- assert_equal 9, PaperTrail::Version.count
186
- end
187
- end
188
- end
@@ -1,43 +0,0 @@
1
- require "test_helper"
2
-
3
- class InheritanceColumnTest < ActiveSupport::TestCase
4
- context "STI models" do
5
- setup do
6
- @animal = Animal.create name: "Animal"
7
- @animal.update_attributes name: "Animal from the Muppets"
8
- @animal.update_attributes name: "Animal Muppet"
9
- @animal.destroy
10
-
11
- @dog = Dog.create name: "Snoopy"
12
- @dog.update_attributes name: "Scooby"
13
- @dog.update_attributes name: "Scooby Doo"
14
- @dog.destroy
15
-
16
- @cat = Cat.create name: "Garfield"
17
- @cat.update_attributes name: "Garfield (I hate Mondays)"
18
- @cat.update_attributes name: "Garfield The Cat"
19
- @cat.destroy
20
- end
21
-
22
- should "work with custom STI inheritance column" do
23
- assert_equal 12, PaperTrail::Version.count
24
- assert_equal 4, @animal.versions.count
25
- assert_nil @animal.versions.first.reify
26
- @animal.versions[1..-1].each { |v| assert_equal "Animal", v.reify.class.name }
27
-
28
- # For some reason `@dog.versions` doesn't include the final `destroy` version.
29
- # Neither do `@dog.versions.scoped` nor `@dog.versions(true)` nor `@dog.versions.reload`.
30
- dog_versions = PaperTrail::Version.where(item_id: @dog.id).
31
- order(PaperTrail.timestamp_field)
32
- assert_equal 4, dog_versions.count
33
- assert_nil dog_versions.first.reify
34
- assert_equal %w(NilClass Dog Dog Dog), dog_versions.map { |v| v.reify.class.name }
35
-
36
- cat_versions = PaperTrail::Version.where(item_id: @cat.id).
37
- order(PaperTrail.timestamp_field)
38
- assert_equal 4, cat_versions.count
39
- assert_nil cat_versions.first.reify
40
- assert_equal %w(NilClass Cat Cat Cat), cat_versions.map { |v| v.reify.class.name }
41
- end
42
- end
43
- end
@@ -1,1489 +0,0 @@
1
- require "test_helper"
2
- require "time_travel_helper"
3
-
4
- class HasPaperTrailModelTest < ActiveSupport::TestCase
5
- context "A record with defined 'only' and 'ignore' attributes" do
6
- setup { @article = Article.create }
7
-
8
- should "creation should change the number of versions" do
9
- assert_equal(1, PaperTrail::Version.count)
10
- end
11
-
12
- context "which updates an ignored column" do
13
- should "not change the number of versions" do
14
- @article.update_attributes title: "My first title"
15
- assert_equal(1, PaperTrail::Version.count)
16
- end
17
- end
18
-
19
- context "which updates an ignored column with truly Proc" do
20
- should "not change the number of versions" do
21
- @article.update_attributes abstract: "ignore abstract"
22
- assert_equal(1, PaperTrail::Version.count)
23
- end
24
- end
25
-
26
- context "which updates an ignored column with falsy Proc" do
27
- should "change the number of versions" do
28
- @article.update_attributes abstract: "do not ignore abstract!"
29
- assert_equal(2, PaperTrail::Version.count)
30
- end
31
- end
32
-
33
- context "which updates an ignored column, ignored with truly Proc and a selected column" do
34
- setup do
35
- @article.update_attributes(
36
- title: "My first title",
37
- content: "Some text here.",
38
- abstract: "ignore abstract"
39
- )
40
- end
41
-
42
- should "change the number of versions" do
43
- assert_equal(2, PaperTrail::Version.count)
44
- end
45
-
46
- should "show the new version in the model's `versions` association" do
47
- assert_equal(2, @article.versions.size)
48
- end
49
-
50
- should "have stored only non-ignored attributes" do
51
- expected = { "content" => [nil, "Some text here."] }
52
- assert_equal expected, @article.versions.last.changeset
53
- end
54
- end
55
-
56
- context "which updates an ignored column, ignored with falsy Proc and a selected column" do
57
- setup do
58
- @article.update_attributes(
59
- title: "My first title",
60
- content: "Some text here.",
61
- abstract: "do not ignore abstract"
62
- )
63
- end
64
-
65
- should "change the number of versions" do
66
- assert_equal(2, PaperTrail::Version.count)
67
- end
68
-
69
- should "show the new version in the model's `versions` association" do
70
- assert_equal(2, @article.versions.size)
71
- end
72
-
73
- should "have stored only non-ignored attributes" do
74
- expected = {
75
- "content" => [nil, "Some text here."],
76
- "abstract" => [nil, "do not ignore abstract"]
77
- }
78
- assert_equal expected, @article.versions.last.changeset
79
- end
80
- end
81
-
82
- context "which updates a selected column" do
83
- setup { @article.update_attributes content: "Some text here." }
84
- should "change the number of versions" do
85
- assert_equal(2, PaperTrail::Version.count)
86
- end
87
-
88
- should "show the new version in the model's `versions` association" do
89
- assert_equal(2, @article.versions.size)
90
- end
91
- end
92
-
93
- context "which updates a non-ignored and non-selected column" do
94
- should "not change the number of versions" do
95
- @article.update_attributes abstract: "Other abstract"
96
- assert_equal(1, PaperTrail::Version.count)
97
- end
98
- end
99
-
100
- context "which updates a skipped column" do
101
- should "not change the number of versions" do
102
- @article.update_attributes file_upload: "Your data goes here"
103
- assert_equal(1, PaperTrail::Version.count)
104
- end
105
- end
106
-
107
- context "which updates a skipped column and a selected column" do
108
- setup do
109
- @article.update_attributes(
110
- file_upload: "Your data goes here",
111
- content: "Some text here."
112
- )
113
- end
114
-
115
- should "change the number of versions" do
116
- assert_equal(2, PaperTrail::Version.count)
117
- end
118
-
119
- should "show the new version in the model's `versions` association" do
120
- assert_equal(2, @article.versions.size)
121
- end
122
-
123
- should "have stored only non-skipped attributes" do
124
- assert_equal ({ "content" => [nil, "Some text here."] }),
125
- @article.versions.last.changeset
126
- end
127
-
128
- context "and when updated again" do
129
- setup do
130
- @article.update_attributes(
131
- file_upload: "More data goes here",
132
- content: "More text here."
133
- )
134
- @old_article = @article.versions.last
135
- end
136
-
137
- should "have removed the skipped attributes when saving the previous version" do
138
- assert_equal nil, PaperTrail.serializer.load(@old_article.object)["file_upload"]
139
- end
140
-
141
- should "have kept the non-skipped attributes in the previous version" do
142
- assert_equal "Some text here.", PaperTrail.serializer.load(@old_article.object)["content"]
143
- end
144
- end
145
- end
146
-
147
- context "which gets destroyed" do
148
- setup { @article.destroy }
149
- should "change the number of versions" do assert_equal(2, PaperTrail::Version.count) end
150
-
151
- should "show the new version in the model's `versions` association" do
152
- assert_equal(2, @article.versions.size)
153
- end
154
- end
155
- end
156
-
157
- context "A record with defined 'ignore' attribute" do
158
- setup { @legacy_widget = LegacyWidget.create }
159
-
160
- context "which updates an ignored column" do
161
- setup { @legacy_widget.update_attributes version: 1 }
162
- should "not change the number of versions" do assert_equal(1, PaperTrail::Version.count) end
163
- end
164
- end
165
-
166
- context 'A record with defined "if" and "unless" attributes' do
167
- setup { @translation = Translation.new headline: "Headline" }
168
-
169
- context "for non-US translations" do
170
- setup { @translation.save }
171
- should "not change the number of versions" do assert_equal(0, PaperTrail::Version.count) end
172
-
173
- context "after update" do
174
- setup { @translation.update_attributes content: "Content" }
175
- should "not change the number of versions" do assert_equal(0, PaperTrail::Version.count) end
176
- end
177
-
178
- context "after destroy" do
179
- setup { @translation.destroy }
180
- should "not change the number of versions" do assert_equal(0, PaperTrail::Version.count) end
181
- end
182
- end
183
-
184
- context "for US translations" do
185
- setup { @translation.language_code = "US" }
186
-
187
- context "that are drafts" do
188
- setup do
189
- @translation.type = "DRAFT"
190
- @translation.save
191
- end
192
-
193
- should "not change the number of versions" do
194
- assert_equal(0, PaperTrail::Version.count)
195
- end
196
-
197
- context "after update" do
198
- setup { @translation.update_attributes content: "Content" }
199
- should "not change the number of versions" do
200
- assert_equal(0, PaperTrail::Version.count)
201
- end
202
- end
203
- end
204
-
205
- context "that are not drafts" do
206
- setup { @translation.save }
207
-
208
- should "change the number of versions" do
209
- assert_equal(1, PaperTrail::Version.count)
210
- end
211
-
212
- context "after update" do
213
- setup { @translation.update_attributes content: "Content" }
214
- should "change the number of versions" do
215
- assert_equal(2, PaperTrail::Version.count)
216
- end
217
-
218
- should "show the new version in the model's `versions` association" do
219
- assert_equal(2, @translation.versions.size)
220
- end
221
- end
222
-
223
- context "after destroy" do
224
- setup { @translation.destroy }
225
- should "change the number of versions" do
226
- assert_equal(2, PaperTrail::Version.count)
227
- end
228
-
229
- should "show the new version in the model's `versions` association" do
230
- assert_equal(2, @translation.versions.size)
231
- end
232
- end
233
- end
234
- end
235
- end
236
-
237
- context "A new record" do
238
- setup { @widget = Widget.new }
239
-
240
- should "not have any previous versions" do
241
- assert_equal [], @widget.versions
242
- end
243
-
244
- should "be live" do
245
- assert @widget.paper_trail.live?
246
- end
247
-
248
- context "which is then created" do
249
- setup { @widget.update_attributes name: "Henry", created_at: Time.now - 1.day }
250
-
251
- should "have one previous version" do
252
- assert_equal 1, @widget.versions.length
253
- end
254
-
255
- should "be nil in its previous version" do
256
- assert_nil @widget.versions.first.object
257
- assert_nil @widget.versions.first.reify
258
- end
259
-
260
- should "record the correct event" do
261
- assert_match(/create/i, @widget.versions.first.event)
262
- end
263
-
264
- should "be live" do
265
- assert @widget.paper_trail.live?
266
- end
267
-
268
- should "use the widget `updated_at` as the version's `created_at`" do
269
- assert_equal @widget.updated_at.to_i, @widget.versions.first.created_at.to_i
270
- end
271
-
272
- should "have changes" do
273
- # TODO: Postgres does not appear to pass back
274
- # ActiveSupport::TimeWithZone, so choosing the lowest common denominator
275
- # to test.
276
-
277
- changes = {
278
- "name" => [nil, "Henry"],
279
- "created_at" => [nil, @widget.created_at.to_time.utc],
280
- "updated_at" => [nil, @widget.updated_at.to_time.utc],
281
- "id" => [nil, @widget.id]
282
- }
283
-
284
- assert_kind_of Time, @widget.versions.last.changeset["updated_at"][1]
285
- assert_changes_equal changes, @widget.versions.last.changeset
286
- end
287
-
288
- context "and then updated without any changes" do
289
- setup { @widget.touch }
290
-
291
- should "not have a new version" do
292
- assert_equal 1, @widget.versions.length
293
- end
294
- end
295
-
296
- context "and then updated with changes" do
297
- setup { @widget.update_attributes name: "Harry" }
298
-
299
- should "have two previous versions" do
300
- assert_equal 2, @widget.versions.length
301
- end
302
-
303
- should "be available in its previous version" do
304
- assert_equal "Harry", @widget.name
305
- assert_not_nil @widget.versions.last.object
306
- widget = @widget.versions.last.reify
307
- assert_equal "Henry", widget.name
308
- assert_equal "Harry", @widget.name
309
- end
310
-
311
- should "have the same ID in its previous version" do
312
- assert_equal @widget.id, @widget.versions.last.reify.id
313
- end
314
-
315
- should "record the correct event" do
316
- assert_match(/update/i, @widget.versions.last.event)
317
- end
318
-
319
- should "have versions that are not live" do
320
- assert @widget.versions.map(&:reify).compact.all? { |w|
321
- !w.paper_trail.live?
322
- }
323
- end
324
-
325
- should "have stored changes" do
326
- # Behavior for ActiveRecord 4 is different than ActiveRecord 3;
327
- # AR4 includes the `updated_at` column in changes for updates, which
328
- # is why we reject it from the right side of this assertion.
329
- last_obj_changes = @widget.versions.last.object_changes
330
- actual = PaperTrail.serializer.load(last_obj_changes).reject { |k, _v|
331
- k.to_sym == :updated_at
332
- }
333
- assert_equal ({ "name" => %w(Henry Harry) }), actual
334
- actual = @widget.versions.last.changeset.reject { |k, _v|
335
- k.to_sym == :updated_at
336
- }
337
- assert_equal ({ "name" => %w(Henry Harry) }), actual
338
- end
339
-
340
- should "return changes with indifferent access" do
341
- assert_equal %w(Henry Harry), @widget.versions.last.changeset[:name]
342
- assert_equal %w(Henry Harry), @widget.versions.last.changeset["name"]
343
- end
344
-
345
- context "and has one associated object" do
346
- setup do
347
- @wotsit = @widget.create_wotsit name: "John"
348
- end
349
-
350
- should "not copy the has_one association by default when reifying" do
351
- reified_widget = @widget.versions.last.reify
352
- # association hasn't been affected by reifying
353
- assert_equal @wotsit, reified_widget.wotsit
354
- # confirm that the association is correct
355
- assert_equal @wotsit, @widget.reload.wotsit
356
- end
357
-
358
- should "copy the has_one association when reifying with :has_one => true" do
359
- reified_widget = @widget.versions.last.reify(has_one: true)
360
- # wotsit wasn't there at the last version
361
- assert_nil reified_widget.wotsit
362
- # wotsit should still exist on live object
363
- assert_equal @wotsit, @widget.reload.wotsit
364
- end
365
- end
366
-
367
- context "and has many associated objects" do
368
- setup do
369
- @f0 = @widget.fluxors.create name: "f-zero"
370
- @f1 = @widget.fluxors.create name: "f-one"
371
- @reified_widget = @widget.versions.last.reify
372
- end
373
-
374
- should "copy the has_many associations when reifying" do
375
- assert_equal @widget.fluxors.length, @reified_widget.fluxors.length
376
- assert_same_elements @widget.fluxors, @reified_widget.fluxors
377
-
378
- assert_equal @widget.versions.length, @reified_widget.versions.length
379
- assert_same_elements @widget.versions, @reified_widget.versions
380
- end
381
- end
382
-
383
- context "and has many associated polymorphic objects" do
384
- setup do
385
- @f0 = @widget.whatchamajiggers.create name: "f-zero"
386
- @f1 = @widget.whatchamajiggers.create name: "f-zero"
387
- @reified_widget = @widget.versions.last.reify
388
- end
389
-
390
- should "copy the has_many associations when reifying" do
391
- assert_equal @widget.whatchamajiggers.length, @reified_widget.whatchamajiggers.length
392
- assert_same_elements @widget.whatchamajiggers, @reified_widget.whatchamajiggers
393
-
394
- assert_equal @widget.versions.length, @reified_widget.versions.length
395
- assert_same_elements @widget.versions, @reified_widget.versions
396
- end
397
- end
398
-
399
- context "polymorphic objects by themselves" do
400
- setup do
401
- @widget = Whatchamajigger.new name: "f-zero"
402
- end
403
-
404
- should "not fail with a nil pointer on the polymorphic association" do
405
- @widget.save!
406
- end
407
- end
408
-
409
- context "and then destroyed" do
410
- setup do
411
- @fluxor = @widget.fluxors.create name: "flux"
412
- @widget.destroy
413
- @reified_widget = PaperTrail::Version.last.reify
414
- end
415
-
416
- should "record the correct event" do
417
- assert_match(/destroy/i, PaperTrail::Version.last.event)
418
- end
419
-
420
- should "have three previous versions" do
421
- assert_equal 3, PaperTrail::Version.with_item_keys("Widget", @widget.id).length
422
- end
423
-
424
- should "be available in its previous version" do
425
- assert_equal @widget.id, @reified_widget.id
426
- assert_attributes_equal @widget.attributes, @reified_widget.attributes
427
- end
428
-
429
- should "be re-creatable from its previous version" do
430
- assert @reified_widget.save
431
- end
432
-
433
- should "restore its associations on its previous version" do
434
- @reified_widget.save
435
- assert_equal 1, @reified_widget.fluxors.length
436
- end
437
-
438
- should "have nil item for last version" do
439
- assert_nil(@widget.versions.last.item)
440
- end
441
-
442
- should "not have changes" do
443
- assert_equal({}, @widget.versions.last.changeset)
444
- end
445
- end
446
- end
447
- end
448
- end
449
-
450
- # Test the serialisation and deserialisation.
451
- # TODO: binary
452
- context "A record's papertrail" do
453
- setup do
454
- @date_time = DateTime.now.utc
455
- @time = Time.now
456
- @date = Date.new 2009, 5, 29
457
- @widget = Widget.create(
458
- name: "Warble",
459
- a_text: "The quick brown fox",
460
- an_integer: 42,
461
- a_float: 153.01,
462
- a_decimal: 2.71828,
463
- a_datetime: @date_time,
464
- a_time: @time,
465
- a_date: @date,
466
- a_boolean: true
467
- )
468
- @widget.update_attributes(
469
- name: nil,
470
- a_text: nil,
471
- an_integer: nil,
472
- a_float: nil,
473
- a_decimal: nil,
474
- a_datetime: nil,
475
- a_time: nil,
476
- a_date: nil,
477
- a_boolean: false
478
- )
479
- @previous = @widget.versions.last.reify
480
- end
481
-
482
- should "handle strings" do
483
- assert_equal "Warble", @previous.name
484
- end
485
-
486
- should "handle text" do
487
- assert_equal "The quick brown fox", @previous.a_text
488
- end
489
-
490
- should "handle integers" do
491
- assert_equal 42, @previous.an_integer
492
- end
493
-
494
- should "handle floats" do
495
- assert_in_delta 153.01, @previous.a_float, 0.001
496
- end
497
-
498
- should "handle decimals" do
499
- assert_in_delta 2.7183, @previous.a_decimal, 0.0001
500
- end
501
-
502
- should "handle datetimes" do
503
- assert_equal @date_time.to_time.utc.to_i, @previous.a_datetime.to_time.utc.to_i
504
- end
505
-
506
- should "handle times" do
507
- assert_equal @time.utc.to_i, @previous.a_time.utc.to_i
508
- end
509
-
510
- should "handle dates" do
511
- assert_equal @date, @previous.a_date
512
- end
513
-
514
- should "handle booleans" do
515
- assert @previous.a_boolean
516
- end
517
-
518
- context "after a column is removed from the record's schema" do
519
- setup do
520
- change_schema
521
- Widget.connection.schema_cache.clear!
522
- Widget.reset_column_information
523
- @last = @widget.versions.last
524
- end
525
-
526
- teardown do
527
- restore_schema
528
- end
529
-
530
- should "reify previous version" do
531
- assert_kind_of Widget, @last.reify
532
- end
533
-
534
- should "restore all forward-compatible attributes" do
535
- assert_equal "Warble", @last.reify.name
536
- assert_equal "The quick brown fox", @last.reify.a_text
537
- assert_equal 42, @last.reify.an_integer
538
- assert_in_delta 153.01, @last.reify.a_float, 0.001
539
- assert_in_delta 2.7183, @last.reify.a_decimal, 0.0001
540
- assert_equal @date_time.to_time.utc.to_i, @last.reify.a_datetime.to_time.utc.to_i
541
- assert_equal @time.utc.to_i, @last.reify.a_time.utc.to_i
542
- assert_equal @date, @last.reify.a_date
543
- assert @last.reify.a_boolean
544
- end
545
- end
546
- end
547
-
548
- context "A record" do
549
- setup { @widget = Widget.create name: "Zaphod" }
550
-
551
- context "with PaperTrail globally disabled" do
552
- setup do
553
- PaperTrail.enabled = false
554
- @count = @widget.versions.length
555
- end
556
-
557
- teardown { PaperTrail.enabled = true }
558
-
559
- context "when updated" do
560
- setup { @widget.update_attributes name: "Beeblebrox" }
561
-
562
- should "not add to its trail" do
563
- assert_equal @count, @widget.versions.length
564
- end
565
- end
566
- end
567
-
568
- context "with its paper trail turned off" do
569
- setup do
570
- Widget.paper_trail.disable
571
- @count = @widget.versions.length
572
- end
573
-
574
- teardown { Widget.paper_trail.enable }
575
-
576
- context "when updated" do
577
- setup { @widget.update_attributes name: "Beeblebrox" }
578
-
579
- should "not add to its trail" do
580
- assert_equal @count, @widget.versions.length
581
- end
582
- end
583
-
584
- context 'when destroyed "without versioning"' do
585
- should "leave paper trail off after call" do
586
- @widget.paper_trail.without_versioning :destroy
587
- assert_equal false, Widget.paper_trail.enabled?
588
- end
589
- end
590
-
591
- context "and then its paper trail turned on" do
592
- setup { Widget.paper_trail.enable }
593
-
594
- context "when updated" do
595
- setup { @widget.update_attributes name: "Ford" }
596
-
597
- should "add to its trail" do
598
- assert_equal @count + 1, @widget.versions.length
599
- end
600
- end
601
-
602
- context 'when updated "without versioning"' do
603
- setup do
604
- @widget.paper_trail.without_versioning do
605
- @widget.update_attributes name: "Ford"
606
- end
607
- # The model instance should yield itself for convenience purposes
608
- @widget.paper_trail.without_versioning { |w| w.update_attributes name: "Nixon" }
609
- end
610
-
611
- should "not create new version" do
612
- assert_equal @count, @widget.versions.length
613
- end
614
-
615
- should "enable paper trail after call" do
616
- assert Widget.paper_trail.enabled?
617
- end
618
- end
619
-
620
- context "when receiving a method name as an argument" do
621
- setup { @widget.paper_trail.without_versioning(:touch_with_version) }
622
-
623
- should "not create new version" do
624
- assert_equal @count, @widget.versions.length
625
- end
626
-
627
- should "enable paper trail after call" do
628
- assert Widget.paper_trail.enabled?
629
- end
630
- end
631
- end
632
- end
633
- end
634
-
635
- context "A papertrail with somebody making changes" do
636
- setup do
637
- @widget = Widget.new name: "Fidget"
638
- end
639
-
640
- context "when a record is created" do
641
- setup do
642
- PaperTrail.whodunnit = "Alice"
643
- @widget.save
644
- @version = @widget.versions.last # only 1 version
645
- end
646
-
647
- should "track who made the change" do
648
- assert_equal "Alice", @version.whodunnit
649
- assert_nil @version.paper_trail_originator
650
- assert_equal "Alice", @version.terminator
651
- assert_equal "Alice", @widget.paper_trail.originator
652
- end
653
-
654
- context "when a record is updated" do
655
- setup do
656
- PaperTrail.whodunnit = "Bob"
657
- @widget.update_attributes name: "Rivet"
658
- @version = @widget.versions.last
659
- end
660
-
661
- should "track who made the change" do
662
- assert_equal "Bob", @version.whodunnit
663
- assert_equal "Alice", @version.paper_trail_originator
664
- assert_equal "Bob", @version.terminator
665
- assert_equal "Bob", @widget.paper_trail.originator
666
- end
667
-
668
- context "when a record is destroyed" do
669
- setup do
670
- PaperTrail.whodunnit = "Charlie"
671
- @widget.destroy
672
- @version = PaperTrail::Version.last
673
- end
674
-
675
- should "track who made the change" do
676
- assert_equal "Charlie", @version.whodunnit
677
- assert_equal "Bob", @version.paper_trail_originator
678
- assert_equal "Charlie", @version.terminator
679
- assert_equal "Charlie", @widget.paper_trail.originator
680
- end
681
- end
682
- end
683
- end
684
- end
685
-
686
- context "Timestamps" do
687
- setup do
688
- @wotsit = Wotsit.create! name: "wotsit"
689
- end
690
-
691
- should "record timestamps" do
692
- @wotsit.update_attributes! name: "changed"
693
- assert_not_nil @wotsit.versions.last.reify.created_at
694
- assert_not_nil @wotsit.versions.last.reify.updated_at
695
- end
696
-
697
- # Tests that it doesn't try to write created_on as an attribute just because
698
- # a created_on method exists.
699
- #
700
- # - Deprecation warning in Rails 3.2
701
- # - ActiveModel::MissingAttributeError in Rails 4
702
- #
703
- # In rails 5, `capture` is deprecated in favor of `capture_io`.
704
- #
705
- should "not generate warning" do
706
- assert_update_raises_nothing = lambda {
707
- assert_nothing_raised {
708
- @wotsit.update_attributes! name: "changed"
709
- }
710
- }
711
- warnings =
712
- if respond_to?(:capture_io)
713
- capture_io { assert_update_raises_nothing.call }.last
714
- else
715
- capture(:stderr) { assert_update_raises_nothing.call }
716
- end
717
- assert_equal "", warnings
718
- end
719
- end
720
-
721
- context "A subclass" do
722
- setup do
723
- @foo = FooWidget.create
724
- @foo.update_attributes! name: "Foo"
725
- end
726
-
727
- should "reify with the correct type" do
728
- # For some reason this test appears to be broken on AR4 in the test env.
729
- # Executing it manually in the Rails console seems to work.. not sure what
730
- # the issues is here.
731
- assert_kind_of FooWidget, @foo.versions.last.reify if ActiveRecord::VERSION::MAJOR < 4
732
- assert_equal @foo.versions.first, PaperTrail::Version.last.previous
733
- assert_nil PaperTrail::Version.last.next
734
- end
735
-
736
- should "should return the correct originator" do
737
- PaperTrail.whodunnit = "Ben"
738
- @foo.update_attribute(:name, "Geoffrey")
739
- assert_equal PaperTrail.whodunnit, @foo.paper_trail.originator
740
- end
741
-
742
- context "when destroyed" do
743
- setup { @foo.destroy }
744
-
745
- should "reify with the correct type" do
746
- assert_kind_of FooWidget, @foo.versions.last.reify
747
- assert_equal @foo.versions[1], PaperTrail::Version.last.previous
748
- assert_nil PaperTrail::Version.last.next
749
- end
750
- end
751
- end
752
-
753
- context "An item with versions" do
754
- setup do
755
- @widget = Widget.create name: "Widget"
756
- @widget.update_attributes name: "Fidget"
757
- @widget.update_attributes name: "Digit"
758
- end
759
-
760
- context "which were created over time" do
761
- setup do
762
- @created = 2.days.ago
763
- @first_update = 1.day.ago
764
- @second_update = 1.hour.ago
765
- @widget.versions[0].update_attributes created_at: @created
766
- @widget.versions[1].update_attributes created_at: @first_update
767
- @widget.versions[2].update_attributes created_at: @second_update
768
- @widget.update_attribute :updated_at, @second_update
769
- end
770
-
771
- should "return nil for version_at before it was created" do
772
- assert_nil @widget.paper_trail.version_at(@created - 1)
773
- end
774
-
775
- should "return how it looked when created for version_at its creation" do
776
- assert_equal "Widget", @widget.paper_trail.version_at(@created).name
777
- end
778
-
779
- should "return how it looked before its first update" do
780
- assert_equal "Widget", @widget.paper_trail.version_at(@first_update - 1).name
781
- end
782
-
783
- should "return how it looked after its first update" do
784
- assert_equal "Fidget", @widget.paper_trail.version_at(@first_update).name
785
- end
786
-
787
- should "return how it looked before its second update" do
788
- assert_equal "Fidget", @widget.paper_trail.version_at(@second_update - 1).name
789
- end
790
-
791
- should "return how it looked after its second update" do
792
- assert_equal "Digit", @widget.paper_trail.version_at(@second_update).name
793
- end
794
-
795
- should "return the current object for version_at after latest update" do
796
- assert_equal "Digit", @widget.paper_trail.version_at(1.day.from_now).name
797
- end
798
-
799
- context "passing in a string representation of a timestamp" do
800
- should "still return a widget when appropriate" do
801
- # need to add 1 second onto the timestamps before casting to a string,
802
- # since casting a Time to a string drops the microseconds
803
- assert_equal "Widget",
804
- @widget.paper_trail.version_at((@created + 1.second).to_s).name
805
- assert_equal "Fidget",
806
- @widget.paper_trail.version_at((@first_update + 1.second).to_s).name
807
- assert_equal "Digit",
808
- @widget.paper_trail.version_at((@second_update + 1.second).to_s).name
809
- end
810
- end
811
- end
812
-
813
- context ".versions_between" do
814
- setup do
815
- @created = 30.days.ago
816
- @first_update = 15.days.ago
817
- @second_update = 1.day.ago
818
- @widget.versions[0].update_attributes created_at: @created
819
- @widget.versions[1].update_attributes created_at: @first_update
820
- @widget.versions[2].update_attributes created_at: @second_update
821
- @widget.update_attribute :updated_at, @second_update
822
- end
823
-
824
- should "return versions in the time period" do
825
- assert_equal ["Fidget"],
826
- @widget.paper_trail.versions_between(20.days.ago, 10.days.ago).map(&:name)
827
- assert_equal %w(Widget Fidget),
828
- @widget.paper_trail.versions_between(45.days.ago, 10.days.ago).map(&:name)
829
- assert_equal %w(Fidget Digit Digit),
830
- @widget.paper_trail.versions_between(16.days.ago, 1.minute.ago).map(&:name)
831
- assert_equal [],
832
- @widget.paper_trail.versions_between(60.days.ago, 45.days.ago).map(&:name)
833
- end
834
- end
835
-
836
- context "on the first version" do
837
- setup { @version = @widget.versions.first }
838
-
839
- should "have a nil previous version" do
840
- assert_nil @version.previous
841
- end
842
-
843
- should "return the next version" do
844
- assert_equal @widget.versions[1], @version.next
845
- end
846
-
847
- should "return the correct index" do
848
- assert_equal 0, @version.index
849
- end
850
- end
851
-
852
- context "on the last version" do
853
- setup { @version = @widget.versions.last }
854
-
855
- should "return the previous version" do
856
- assert_equal @widget.versions[@widget.versions.length - 2], @version.previous
857
- end
858
-
859
- should "have a nil next version" do
860
- assert_nil @version.next
861
- end
862
-
863
- should "return the correct index" do
864
- assert_equal @widget.versions.length - 1, @version.index
865
- end
866
- end
867
- end
868
-
869
- context "An item" do
870
- setup do
871
- @initial_title = "Foobar"
872
- @article = Article.new title: @initial_title
873
- end
874
-
875
- context "which is created" do
876
- setup { @article.save }
877
-
878
- should "store fixed meta data" do
879
- assert_equal 42, @article.versions.last.answer
880
- end
881
-
882
- should "store dynamic meta data which is independent of the item" do
883
- assert_equal "31 + 11 = 42", @article.versions.last.question
884
- end
885
-
886
- should "store dynamic meta data which depends on the item" do
887
- assert_equal @article.id, @article.versions.last.article_id
888
- end
889
-
890
- should "store dynamic meta data based on a method of the item" do
891
- assert_equal @article.action_data_provider_method, @article.versions.last.action
892
- end
893
-
894
- should "store dynamic meta data based on an attribute of the item at creation" do
895
- assert_equal @initial_title, @article.versions.last.title
896
- end
897
-
898
- context "and updated" do
899
- setup do
900
- @article.update_attributes! content: "Better text.", title: "Rhubarb"
901
- end
902
-
903
- should "store fixed meta data" do
904
- assert_equal 42, @article.versions.last.answer
905
- end
906
-
907
- should "store dynamic meta data which is independent of the item" do
908
- assert_equal "31 + 11 = 42", @article.versions.last.question
909
- end
910
-
911
- should "store dynamic meta data which depends on the item" do
912
- assert_equal @article.id, @article.versions.last.article_id
913
- end
914
-
915
- should "store dynamic meta data based on an attribute of the item prior to the update" do
916
- assert_equal @initial_title, @article.versions.last.title
917
- end
918
- end
919
-
920
- context "and destroyed" do
921
- setup { @article.destroy }
922
-
923
- should "store fixed metadata" do
924
- assert_equal 42, @article.versions.last.answer
925
- end
926
-
927
- should "store dynamic metadata which is independent of the item" do
928
- assert_equal "31 + 11 = 42", @article.versions.last.question
929
- end
930
-
931
- should "store dynamic metadata which depends on the item" do
932
- assert_equal @article.id, @article.versions.last.article_id
933
- end
934
-
935
- should "store dynamic metadata based on attribute of item prior to destruction" do
936
- assert_equal @initial_title, @article.versions.last.title
937
- end
938
- end
939
- end
940
- end
941
-
942
- context "A reified item" do
943
- setup do
944
- widget = Widget.create name: "Bob"
945
- %w(Tom Dick Jane).each { |name| widget.update_attributes name: name }
946
- @version = widget.versions.last
947
- @widget = @version.reify
948
- end
949
-
950
- should "know which version it came from" do
951
- assert_equal @version, @widget.version
952
- end
953
-
954
- should "return its previous self" do
955
- assert_equal @widget.versions[-2].reify,
956
- @widget.paper_trail.previous_version
957
- end
958
- end
959
-
960
- context "A non-reified item" do
961
- setup { @widget = Widget.new }
962
-
963
- should "not have a previous version" do
964
- assert_nil @widget.paper_trail.previous_version
965
- end
966
-
967
- should "not have a next version" do
968
- assert_nil @widget.paper_trail.next_version
969
- end
970
-
971
- context "with versions" do
972
- setup do
973
- @widget.save
974
- %w(Tom Dick Jane).each { |name| @widget.update_attributes name: name }
975
- end
976
-
977
- should "have a previous version" do
978
- assert_equal @widget.versions.last.reify.name,
979
- @widget.paper_trail.previous_version.name
980
- end
981
-
982
- should "not have a next version" do
983
- assert_nil @widget.paper_trail.next_version
984
- end
985
- end
986
- end
987
-
988
- context "A reified item" do
989
- setup do
990
- @widget = Widget.create name: "Bob"
991
- %w(Tom Dick Jane).each { |name| @widget.update_attributes name: name }
992
- @second_widget = @widget.versions[1].reify # first widget is `nil`
993
- @last_widget = @widget.versions.last.reify
994
- end
995
-
996
- should "have a previous version" do
997
- # `create` events return `nil` for `reify`
998
- assert_nil @second_widget.paper_trail.previous_version
999
- assert_equal @widget.versions[-2].reify.name,
1000
- @last_widget.paper_trail.previous_version.name
1001
- end
1002
-
1003
- should "have a next version" do
1004
- assert_equal @widget.versions[2].reify.name, @second_widget.paper_trail.next_version.name
1005
- assert_equal @last_widget.paper_trail.next_version.name, @widget.name
1006
- end
1007
- end
1008
-
1009
- context ":has_many :through" do
1010
- setup do
1011
- @book = Book.create title: "War and Peace"
1012
- @dostoyevsky = Person.create name: "Dostoyevsky"
1013
- @solzhenitsyn = Person.create name: "Solzhenitsyn"
1014
- end
1015
-
1016
- should "store version on source <<" do
1017
- count = PaperTrail::Version.count
1018
- @book.authors << @dostoyevsky
1019
- assert_equal 1, PaperTrail::Version.count - count
1020
- assert_equal PaperTrail::Version.last, @book.authorships.first.versions.first
1021
- end
1022
-
1023
- should "store version on source create" do
1024
- count = PaperTrail::Version.count
1025
- @book.authors.create name: "Tolstoy"
1026
- assert_equal 2, PaperTrail::Version.count - count
1027
- actual = [
1028
- PaperTrail::Version.order(:id).to_a[-2].item,
1029
- PaperTrail::Version.last.item
1030
- ]
1031
- assert_same_elements [Person.last, Authorship.last], actual
1032
- end
1033
-
1034
- should "store version on join destroy" do
1035
- @book.authors << @dostoyevsky
1036
- count = PaperTrail::Version.count
1037
- @book.authorships.reload.last.destroy
1038
- assert_equal 1, PaperTrail::Version.count - count
1039
- assert_equal @book, PaperTrail::Version.last.reify.book
1040
- assert_equal @dostoyevsky, PaperTrail::Version.last.reify.author
1041
- end
1042
-
1043
- should "store version on join clear" do
1044
- @book.authors << @dostoyevsky
1045
- count = PaperTrail::Version.count
1046
- @book.authorships.reload.destroy_all
1047
- assert_equal 1, PaperTrail::Version.count - count
1048
- assert_equal @book, PaperTrail::Version.last.reify.book
1049
- assert_equal @dostoyevsky, PaperTrail::Version.last.reify.author
1050
- end
1051
- end
1052
-
1053
- context "When an attribute has a custom serializer" do
1054
- setup do
1055
- @person = Person.new(time_zone: "Samoa")
1056
- end
1057
-
1058
- should "be an instance of ActiveSupport::TimeZone" do
1059
- assert_equal ActiveSupport::TimeZone, @person.time_zone.class
1060
- end
1061
-
1062
- context "when the model is saved" do
1063
- setup do
1064
- @changes_before_save = @person.changes.dup
1065
- @person.save!
1066
- end
1067
-
1068
- # Test for serialization:
1069
- should "version.object_changes should store long serialization of TimeZone object" do
1070
- len = @person.versions.last.object_changes.length
1071
- assert len < 105, "object_changes length was #{len}"
1072
- end
1073
-
1074
- # It should store the serialized value.
1075
- should "version.object_changes attribute should have stored the value from serializer" do
1076
- as_stored_in_version = HashWithIndifferentAccess[
1077
- YAML.load(@person.versions.last.object_changes)
1078
- ]
1079
- assert_equal [nil, "Samoa"], as_stored_in_version[:time_zone]
1080
- serialized_value = Person::TimeZoneSerializer.dump(@person.time_zone)
1081
- assert_equal serialized_value, as_stored_in_version[:time_zone].last
1082
- end
1083
-
1084
- # Tests for unserialization:
1085
- should "version.changeset should convert attribute to original, unserialized value" do
1086
- unserialized_value = Person::TimeZoneSerializer.load(@person.time_zone)
1087
- assert_equal unserialized_value,
1088
- @person.versions.last.changeset[:time_zone].last
1089
- end
1090
-
1091
- should "record.changes (before save) returns the original, unserialized values" do
1092
- assert_equal [NilClass, ActiveSupport::TimeZone],
1093
- @changes_before_save[:time_zone].map(&:class)
1094
- end
1095
-
1096
- should "version.changeset should be the same as record.changes was before the save" do
1097
- actual = @person.versions.last.changeset.delete_if { |k, _v| k.to_sym == :id }
1098
- assert_equal @changes_before_save, actual
1099
- actual = @person.versions.last.changeset[:time_zone].map(&:class)
1100
- assert_equal [NilClass, ActiveSupport::TimeZone], actual
1101
- end
1102
-
1103
- context "when that attribute is updated" do
1104
- setup do
1105
- @attribute_value_before_change = @person.time_zone
1106
- @person.assign_attributes(time_zone: "Pacific Time (US & Canada)")
1107
- @changes_before_save = @person.changes.dup
1108
- @person.save!
1109
- end
1110
-
1111
- # Tests for serialization
1112
- # -----------------------
1113
- #
1114
- # Before the serialized attributes fix, the object/object_changes value
1115
- # that was stored was ridiculously long (58723).
1116
- #
1117
- # version.object should not have stored the default, ridiculously long
1118
- # (to_yaml) serialization of the TimeZone object.
1119
- should "object should not store long serialization of TimeZone object" do
1120
- len = @person.versions.last.object.length
1121
- assert len < 105, "object length was #{len}"
1122
- end
1123
-
1124
- # Need an additional clause to detect what version of ActiveRecord is
1125
- # being used for this test because AR4 injects the `updated_at` column
1126
- # into the changeset for updates to models.
1127
- #
1128
- # version.object_changes should not have stored the default,
1129
- # ridiculously long (to_yaml) serialization of the TimeZone object
1130
- should "object_changes should not store long serialization of TimeZone object" do
1131
- max_len = ActiveRecord::VERSION::MAJOR < 4 ? 105 : 118
1132
- len = @person.versions.last.object_changes.length
1133
- assert len < max_len, "object_changes length was #{len}"
1134
- end
1135
-
1136
- # But now it stores the short, serialized value.
1137
- should "version.object attribute should have stored value from serializer" do
1138
- as_stored_in_version = HashWithIndifferentAccess[
1139
- YAML.load(@person.versions.last.object)
1140
- ]
1141
- assert_equal "Samoa", as_stored_in_version[:time_zone]
1142
- serialized_value = Person::TimeZoneSerializer.dump(@attribute_value_before_change)
1143
- assert_equal serialized_value, as_stored_in_version[:time_zone]
1144
- end
1145
-
1146
- should "version.object_changes attribute should have stored value from serializer" do
1147
- as_stored_in_version = HashWithIndifferentAccess[
1148
- YAML.load(@person.versions.last.object_changes)
1149
- ]
1150
- assert_equal ["Samoa", "Pacific Time (US & Canada)"], as_stored_in_version[:time_zone]
1151
- serialized_value = Person::TimeZoneSerializer.dump(@person.time_zone)
1152
- assert_equal serialized_value, as_stored_in_version[:time_zone].last
1153
- end
1154
-
1155
- # Tests for unserialization:
1156
- should "version.reify should convert attribute to original, unserialized value" do
1157
- unserialized_value = Person::TimeZoneSerializer.load(@attribute_value_before_change)
1158
- assert_equal unserialized_value,
1159
- @person.versions.last.reify.time_zone
1160
- end
1161
-
1162
- should "version.changeset should convert attribute to original, unserialized value" do
1163
- unserialized_value = Person::TimeZoneSerializer.load(@person.time_zone)
1164
- assert_equal unserialized_value,
1165
- @person.versions.last.changeset[:time_zone].last
1166
- end
1167
-
1168
- should "record.changes (before save) returns the original, unserialized values" do
1169
- assert_equal [ActiveSupport::TimeZone, ActiveSupport::TimeZone],
1170
- @changes_before_save[:time_zone].map(&:class)
1171
- end
1172
-
1173
- should "version.changeset should be the same as record.changes was before the save" do
1174
- assert_equal @changes_before_save, @person.versions.last.changeset
1175
- assert_equal [ActiveSupport::TimeZone, ActiveSupport::TimeZone],
1176
- @person.versions.last.changeset[:time_zone].map(&:class)
1177
- end
1178
- end
1179
- end
1180
- end
1181
-
1182
- context "A new model instance which uses a custom PaperTrail::Version class" do
1183
- setup { @post = Post.new }
1184
-
1185
- context "which is then saved" do
1186
- setup { @post.save }
1187
- should "change the number of post versions" do assert_equal 1, PostVersion.count end
1188
- should "not change the number of versions" do assert_equal(0, PaperTrail::Version.count) end
1189
- end
1190
- end
1191
-
1192
- context "An existing model instance which uses a custom PaperTrail::Version class" do
1193
- setup { @post = Post.create }
1194
- should "have one post version" do assert_equal(1, PostVersion.count) end
1195
-
1196
- context "on the first version" do
1197
- setup { @version = @post.versions.first }
1198
-
1199
- should "have the correct index" do
1200
- assert_equal 0, @version.index
1201
- end
1202
- end
1203
-
1204
- should "have versions of the custom class" do
1205
- assert_equal "PostVersion", @post.versions.first.class.name
1206
- end
1207
-
1208
- context "which is modified" do
1209
- setup do
1210
- @post.update_attributes(content: "Some new content")
1211
- end
1212
-
1213
- should "change the number of post versions" do
1214
- assert_equal(2, PostVersion.count)
1215
- end
1216
-
1217
- should "not change the number of versions" do
1218
- assert_equal(0, PaperTrail::Version.count)
1219
- end
1220
-
1221
- should "not have stored changes when object_changes column doesn't exist" do
1222
- assert_nil @post.versions.last.changeset
1223
- end
1224
- end
1225
- end
1226
-
1227
- context "An overwritten default accessor" do
1228
- setup do
1229
- @song = Song.create length: 4
1230
- @song.update_attributes length: 5
1231
- end
1232
-
1233
- should 'return "overwritten" value on live instance' do
1234
- assert_equal 5, @song.length
1235
- end
1236
- should 'return "overwritten" value on reified instance' do
1237
- assert_equal 4, @song.versions.last.reify.length
1238
- end
1239
-
1240
- context "Has a virtual attribute injected into the ActiveModel::Dirty changes" do
1241
- setup do
1242
- @song.name = "Good Vibrations"
1243
- @song.save
1244
- @song.name = "Yellow Submarine"
1245
- end
1246
-
1247
- should "return persist the changes on the live instance properly" do
1248
- assert_equal "Yellow Submarine", @song.name
1249
- end
1250
- should 'return "overwritten" virtual attribute on the reified instance' do
1251
- assert_equal "Good Vibrations", @song.versions.last.reify.name
1252
- end
1253
- end
1254
- end
1255
-
1256
- context "An unsaved record" do
1257
- setup do
1258
- @widget = Widget.new
1259
- @widget.destroy
1260
- end
1261
- should "not have a version created on destroy" do
1262
- assert @widget.versions.empty?
1263
- end
1264
- end
1265
-
1266
- context "A model with a custom association" do
1267
- setup do
1268
- @doc = Document.create
1269
- @doc.update_attributes name: "Doc 1"
1270
- end
1271
-
1272
- should "not respond to versions method" do
1273
- assert !@doc.respond_to?(:versions)
1274
- end
1275
-
1276
- should "create a new version record" do
1277
- assert_equal 2, @doc.paper_trail_versions.length
1278
- end
1279
-
1280
- should "respond to `next_version` as normal" do
1281
- reified = @doc.paper_trail_versions.last.reify
1282
- assert_equal reified.paper_trail.next_version.name, @doc.name
1283
- end
1284
-
1285
- should "respond to `previous_version` as normal" do
1286
- @doc.update_attributes name: "Doc 2"
1287
- assert_equal 3, @doc.paper_trail_versions.length
1288
- assert_equal "Doc 1", @doc.paper_trail.previous_version.name
1289
- end
1290
- end
1291
-
1292
- context "The `on` option" do
1293
- context "on create" do
1294
- setup do
1295
- Fluxor.instance_eval <<-END
1296
- has_paper_trail :on => [:create]
1297
- END
1298
- @fluxor = Fluxor.create
1299
- @fluxor.update_attributes name: "blah"
1300
- @fluxor.destroy
1301
- end
1302
- should "only have a version for the create event" do
1303
- assert_equal 1, @fluxor.versions.length
1304
- assert_equal "create", @fluxor.versions.last.event
1305
- end
1306
- end
1307
- context "on update" do
1308
- setup do
1309
- Fluxor.reset_callbacks :create
1310
- Fluxor.reset_callbacks :update
1311
- Fluxor.reset_callbacks :destroy
1312
- Fluxor.instance_eval <<-END
1313
- has_paper_trail :on => [:update]
1314
- END
1315
- @fluxor = Fluxor.create
1316
- @fluxor.update_attributes name: "blah"
1317
- @fluxor.destroy
1318
- end
1319
- should "only have a version for the update event" do
1320
- assert_equal 1, @fluxor.versions.length
1321
- assert_equal "update", @fluxor.versions.last.event
1322
- end
1323
- end
1324
- context "on destroy" do
1325
- setup do
1326
- Fluxor.reset_callbacks :create
1327
- Fluxor.reset_callbacks :update
1328
- Fluxor.reset_callbacks :destroy
1329
- Fluxor.instance_eval <<-END
1330
- has_paper_trail :on => [:destroy]
1331
- END
1332
- @fluxor = Fluxor.create
1333
- @fluxor.update_attributes name: "blah"
1334
- @fluxor.destroy
1335
- end
1336
- should "only have a version for the destroy event" do
1337
- assert_equal 1, @fluxor.versions.length
1338
- assert_equal "destroy", @fluxor.versions.last.event
1339
- end
1340
- end
1341
- context "on []" do
1342
- setup do
1343
- Fluxor.reset_callbacks :create
1344
- Fluxor.reset_callbacks :update
1345
- Fluxor.reset_callbacks :destroy
1346
- Fluxor.instance_eval <<-END
1347
- has_paper_trail :on => []
1348
- END
1349
- @fluxor = Fluxor.create
1350
- @fluxor.update_attributes name: "blah"
1351
- end
1352
-
1353
- teardown do
1354
- @fluxor.destroy
1355
- end
1356
-
1357
- should "not have any versions" do
1358
- assert_equal 0, @fluxor.versions.length
1359
- end
1360
-
1361
- should "still respond to touch_with_version" do
1362
- @fluxor.paper_trail.touch_with_version
1363
- assert_equal 1, @fluxor.versions.length
1364
- end
1365
- end
1366
- context "allows a symbol to be passed" do
1367
- setup do
1368
- Fluxor.reset_callbacks :create
1369
- Fluxor.reset_callbacks :update
1370
- Fluxor.reset_callbacks :destroy
1371
- Fluxor.instance_eval <<-END
1372
- has_paper_trail :on => :create
1373
- END
1374
- @fluxor = Fluxor.create
1375
- @fluxor.update_attributes name: "blah"
1376
- @fluxor.destroy
1377
- end
1378
- should "only have a version for hte create event" do
1379
- assert_equal 1, @fluxor.versions.length
1380
- assert_equal "create", @fluxor.versions.last.event
1381
- end
1382
- end
1383
- end
1384
-
1385
- context "A model with column version and custom version_method" do
1386
- setup do
1387
- @legacy_widget = LegacyWidget.create(name: "foo", version: 2)
1388
- end
1389
-
1390
- should "set version on create" do
1391
- assert_equal 2, @legacy_widget.version
1392
- end
1393
-
1394
- should "allow version updates" do
1395
- @legacy_widget.update_attributes version: 3
1396
- assert_equal 3, @legacy_widget.version
1397
- end
1398
-
1399
- should "create a new version record" do
1400
- assert_equal 1, @legacy_widget.versions.size
1401
- end
1402
- end
1403
-
1404
- context "A reified item with a column -version- and custom version_method" do
1405
- setup do
1406
- widget = LegacyWidget.create(name: "foo", version: 2)
1407
- %w(bar baz).each { |name| widget.update_attributes name: name }
1408
- @version = widget.versions.last
1409
- @widget = @version.reify
1410
- end
1411
-
1412
- should "know which version it came from" do
1413
- assert_equal @version, @widget.custom_version
1414
- end
1415
-
1416
- should "return its previous self" do
1417
- assert_equal @widget.versions[-2].reify, @widget.paper_trail.previous_version
1418
- end
1419
- end
1420
-
1421
- context "custom events" do
1422
- context "on create" do
1423
- setup do
1424
- Fluxor.reset_callbacks :create
1425
- Fluxor.reset_callbacks :update
1426
- Fluxor.reset_callbacks :destroy
1427
- Fluxor.instance_eval <<-END
1428
- has_paper_trail :on => [:create]
1429
- END
1430
- @fluxor = Fluxor.new.tap { |model| model.paper_trail_event = "created" }
1431
- @fluxor.update_attributes name: "blah"
1432
- @fluxor.destroy
1433
- end
1434
- should "only have a version for the created event" do
1435
- assert_equal 1, @fluxor.versions.length
1436
- assert_equal "created", @fluxor.versions.last.event
1437
- end
1438
- end
1439
- context "on update" do
1440
- setup do
1441
- Fluxor.reset_callbacks :create
1442
- Fluxor.reset_callbacks :update
1443
- Fluxor.reset_callbacks :destroy
1444
- Fluxor.instance_eval <<-END
1445
- has_paper_trail :on => [:update]
1446
- END
1447
- @fluxor = Fluxor.create.tap { |model| model.paper_trail_event = "name_updated" }
1448
- @fluxor.update_attributes name: "blah"
1449
- @fluxor.destroy
1450
- end
1451
- should "only have a version for the name_updated event" do
1452
- assert_equal 1, @fluxor.versions.length
1453
- assert_equal "name_updated", @fluxor.versions.last.event
1454
- end
1455
- end
1456
- context "on destroy" do
1457
- setup do
1458
- Fluxor.reset_callbacks :create
1459
- Fluxor.reset_callbacks :update
1460
- Fluxor.reset_callbacks :destroy
1461
- Fluxor.instance_eval <<-END
1462
- has_paper_trail :on => [:destroy]
1463
- END
1464
- @fluxor = Fluxor.create.tap { |model| model.paper_trail_event = "destroyed" }
1465
- @fluxor.update_attributes name: "blah"
1466
- @fluxor.destroy
1467
- end
1468
- should "only have a version for the destroy event" do
1469
- assert_equal 1, @fluxor.versions.length
1470
- assert_equal "destroyed", @fluxor.versions.last.event
1471
- end
1472
- end
1473
- end
1474
-
1475
- context "`PaperTrail::Config.version_limit` set" do
1476
- setup do
1477
- PaperTrail.config.version_limit = 2
1478
- @widget = Widget.create! name: "Henry"
1479
- 6.times { @widget.update_attribute(:name, FFaker::Lorem.word) }
1480
- end
1481
-
1482
- teardown { PaperTrail.config.version_limit = nil }
1483
-
1484
- should "limit the number of versions to 3 (2 plus the created at event)" do
1485
- assert_equal "create", @widget.versions.first.event
1486
- assert_equal 3, @widget.versions.size
1487
- end
1488
- end
1489
- end