govuk_content_models 6.0.2

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 (104) hide show
  1. data/.gitignore +17 -0
  2. data/.ruby-version +1 -0
  3. data/.travis.yml +14 -0
  4. data/CONTRIBUTING.md +22 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +20 -0
  7. data/README.md +5 -0
  8. data/Rakefile +46 -0
  9. data/app/models/action.rb +60 -0
  10. data/app/models/answer_edition.rb +13 -0
  11. data/app/models/artefact.rb +341 -0
  12. data/app/models/artefact_action.rb +27 -0
  13. data/app/models/artefact_external_link.rb +15 -0
  14. data/app/models/business_support/business_size.rb +14 -0
  15. data/app/models/business_support/business_type.rb +14 -0
  16. data/app/models/business_support/location.rb +14 -0
  17. data/app/models/business_support/purpose.rb +14 -0
  18. data/app/models/business_support/sector.rb +14 -0
  19. data/app/models/business_support/stage.rb +14 -0
  20. data/app/models/business_support/support_type.rb +14 -0
  21. data/app/models/business_support_edition.rb +69 -0
  22. data/app/models/campaign_edition.rb +72 -0
  23. data/app/models/completed_transaction_edition.rb +14 -0
  24. data/app/models/curated_list.rb +32 -0
  25. data/app/models/edition.rb +286 -0
  26. data/app/models/expectant.rb +21 -0
  27. data/app/models/expectation.rb +12 -0
  28. data/app/models/guide_edition.rb +19 -0
  29. data/app/models/help_page_edition.rb +13 -0
  30. data/app/models/licence_edition.rb +35 -0
  31. data/app/models/local_authority.rb +58 -0
  32. data/app/models/local_interaction.rb +20 -0
  33. data/app/models/local_service.rb +49 -0
  34. data/app/models/local_transaction_edition.rb +49 -0
  35. data/app/models/overview_dashboard.rb +25 -0
  36. data/app/models/part.rb +28 -0
  37. data/app/models/parted.rb +32 -0
  38. data/app/models/place_edition.rb +20 -0
  39. data/app/models/programme_edition.rb +26 -0
  40. data/app/models/simple_smart_answer_edition.rb +66 -0
  41. data/app/models/simple_smart_answer_edition/node.rb +40 -0
  42. data/app/models/simple_smart_answer_edition/node/option.rb +31 -0
  43. data/app/models/tag.rb +88 -0
  44. data/app/models/transaction_edition.rb +28 -0
  45. data/app/models/travel_advice_edition.rb +177 -0
  46. data/app/models/user.rb +54 -0
  47. data/app/models/video_edition.rb +24 -0
  48. data/app/models/workflow.rb +217 -0
  49. data/app/models/workflow_actor.rb +141 -0
  50. data/app/traits/attachable.rb +60 -0
  51. data/app/traits/govspeak_smart_quotes_fixer.rb +19 -0
  52. data/app/traits/taggable.rb +113 -0
  53. data/app/validators/safe_html.rb +33 -0
  54. data/app/validators/slug_validator.rb +53 -0
  55. data/config/mongoid.yml +5 -0
  56. data/govuk_content_models.gemspec +42 -0
  57. data/jenkins.sh +7 -0
  58. data/lib/fact_check_address.rb +36 -0
  59. data/lib/govuk_content_models.rb +12 -0
  60. data/lib/govuk_content_models/require_all.rb +14 -0
  61. data/lib/govuk_content_models/test_helpers/factories.rb +213 -0
  62. data/lib/govuk_content_models/test_helpers/local_services.rb +24 -0
  63. data/lib/govuk_content_models/version.rb +4 -0
  64. data/test/fixtures/contactotron_api_response.json +1 -0
  65. data/test/fixtures/uploads/image.jpg +0 -0
  66. data/test/models/artefact_action_test.rb +123 -0
  67. data/test/models/artefact_external_link_test.rb +32 -0
  68. data/test/models/artefact_tag_test.rb +52 -0
  69. data/test/models/artefact_test.rb +583 -0
  70. data/test/models/business_support/business_size_test.rb +25 -0
  71. data/test/models/business_support/business_type_test.rb +25 -0
  72. data/test/models/business_support/location_test.rb +25 -0
  73. data/test/models/business_support/purpose_test.rb +29 -0
  74. data/test/models/business_support/sector_test.rb +25 -0
  75. data/test/models/business_support/stage_test.rb +25 -0
  76. data/test/models/business_support/support_type_test.rb +25 -0
  77. data/test/models/business_support_edition_test.rb +186 -0
  78. data/test/models/campaign_edition_test.rb +90 -0
  79. data/test/models/curated_list_test.rb +32 -0
  80. data/test/models/edition_test.rb +826 -0
  81. data/test/models/fact_check_address_test.rb +36 -0
  82. data/test/models/help_page_edition_test.rb +38 -0
  83. data/test/models/licence_edition_test.rb +104 -0
  84. data/test/models/local_authority_test.rb +113 -0
  85. data/test/models/local_service_test.rb +199 -0
  86. data/test/models/local_transaction_edition_test.rb +78 -0
  87. data/test/models/overview_dashboard_test.rb +47 -0
  88. data/test/models/simple_smart_answer_edition_test.rb +169 -0
  89. data/test/models/simple_smart_answer_node_test.rb +134 -0
  90. data/test/models/simple_smart_answer_option_test.rb +90 -0
  91. data/test/models/tag_test.rb +92 -0
  92. data/test/models/time_zone_test.rb +48 -0
  93. data/test/models/transaction_edition_test.rb +20 -0
  94. data/test/models/travel_advice_edition_test.rb +480 -0
  95. data/test/models/user_test.rb +114 -0
  96. data/test/models/video_edition_test.rb +64 -0
  97. data/test/models/workflow_actor_test.rb +61 -0
  98. data/test/models/workflow_test.rb +307 -0
  99. data/test/test_helper.rb +47 -0
  100. data/test/traits/attachable_test.rb +143 -0
  101. data/test/traits/taggable_test.rb +114 -0
  102. data/test/validators/safe_html_validator_test.rb +86 -0
  103. data/test/validators/slug_validator_test.rb +42 -0
  104. metadata +511 -0
@@ -0,0 +1,32 @@
1
+ require "test_helper"
2
+
3
+ class CuratedListTest < ActiveSupport::TestCase
4
+ test "should validate format of slug" do
5
+ cl = CuratedList.new(slug: 'I am not a valid slug')
6
+ assert !cl.valid?
7
+ assert cl.errors[:slug].any?, "Doesn't have error on slug"
8
+ end
9
+
10
+ test "should include ability to have a section tag" do
11
+ cl = FactoryGirl.create(:curated_list)
12
+ tag = FactoryGirl.create(:tag, tag_id: 'batman', title: 'Batman', tag_type: 'section')
13
+
14
+ cl.sections = ['batman']
15
+ cl.save
16
+
17
+ assert_equal [tag], cl.sections
18
+ end
19
+
20
+ test "should return artefacts in relationship order, not their natural order" do
21
+ a = FactoryGirl.create(:artefact, name: "A")
22
+ b = FactoryGirl.create(:artefact, name: "B")
23
+ cl = FactoryGirl.create(:curated_list, artefact_ids: [b._id, a._id])
24
+ assert_equal ["B", "A"], cl.artefacts.map(&:name)
25
+ end
26
+
27
+ test "should translate string IDs to BSON::ObjectIds for artefacts set via artefact_ids" do
28
+ a = FactoryGirl.create(:artefact, name: "A")
29
+ cl = FactoryGirl.create(:curated_list, artefact_ids: [a.id.to_s])
30
+ assert cl.artefact_ids.first.is_a?(BSON::ObjectId)
31
+ end
32
+ end
@@ -0,0 +1,826 @@
1
+ require "test_helper"
2
+
3
+ class Edition
4
+ def update_in_search_index
5
+ end
6
+ end
7
+
8
+ class EditionTest < ActiveSupport::TestCase
9
+ def setup
10
+ @artefact = FactoryGirl.create(:artefact)
11
+ end
12
+
13
+ def template_answer(version_number = 1)
14
+ artefact = FactoryGirl.create(:artefact,
15
+ kind: "answer",
16
+ name: "Foo bar",
17
+ # primary_section: "test-section",
18
+ # sections: ["test-section"],
19
+ # department: "Test dept",
20
+ owning_app: "publisher")
21
+
22
+ AnswerEdition.create(state: "ready", slug: "childcare", panopticon_id: artefact.id,
23
+ title: "Child care stuff", body: "Lots of info", version_number: version_number)
24
+ end
25
+
26
+ def template_published_answer(version_number = 1)
27
+ answer = template_answer(version_number)
28
+ answer.publish
29
+ answer.save
30
+ answer
31
+ end
32
+
33
+ def template_transaction
34
+ artefact = FactoryGirl.create(:artefact)
35
+ TransactionEdition.create(title: "One", introduction: "introduction",
36
+ more_information: "more info", panopticon_id: @artefact.id, slug: "childcare")
37
+ end
38
+
39
+ def template_unpublished_answer(version_number = 1)
40
+ template_answer(version_number)
41
+ end
42
+
43
+ test "it must have a title" do
44
+ a = LocalTransactionEdition.new
45
+ refute a.valid?
46
+ assert a.errors[:title].any?
47
+ end
48
+
49
+ test "it should give a friendly (legacy supporting) description of its format" do
50
+ a = LocalTransactionEdition.new
51
+ assert_equal "LocalTransaction", a.format
52
+ end
53
+
54
+ test "it should be able to find its siblings" do
55
+ @artefact2 = FactoryGirl.create(:artefact)
56
+ g1 = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, version_number: 1)
57
+ g2 = FactoryGirl.create(:guide_edition, panopticon_id: @artefact2.id, version_number: 1)
58
+ g3 = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, version_number: 2)
59
+ assert_equal [], g2.siblings.to_a
60
+ assert_equal [g3], g1.siblings.to_a
61
+ end
62
+
63
+ test "it should be able to find its previous siblings" do
64
+ @artefact2 = FactoryGirl.create(:artefact)
65
+ g1 = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, version_number: 1)
66
+ g2 = FactoryGirl.create(:guide_edition, panopticon_id: @artefact2.id, version_number: 1)
67
+ g3 = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, version_number: 2)
68
+
69
+ assert_equal [], g1.previous_siblings.to_a
70
+ assert_equal [g1], g3.previous_siblings.to_a
71
+ end
72
+
73
+ test "A programme should have default parts" do
74
+ programme = FactoryGirl.create(:programme_edition, panopticon_id: @artefact.id)
75
+ assert_equal programme.parts.count, ProgrammeEdition::DEFAULT_PARTS.length
76
+ end
77
+
78
+ test "it should build a clone" do
79
+ edition = FactoryGirl.create(:guide_edition,
80
+ state: "published",
81
+ panopticon_id: @artefact.id,
82
+ version_number: 1,
83
+ department: "Test dept",
84
+ overview: "I am a test overview",
85
+ alternative_title: "Alternative test title")
86
+ clone_edition = edition.build_clone
87
+ assert_equal clone_edition.department, "Test dept"
88
+ assert_equal clone_edition.section, "test:subsection test"
89
+ assert_equal clone_edition.overview, "I am a test overview"
90
+ assert_equal clone_edition.alternative_title, "Alternative test title"
91
+ assert_equal clone_edition.version_number, 2
92
+ end
93
+
94
+ test "cloning can only occur from a published edition" do
95
+ edition = FactoryGirl.create(:guide_edition,
96
+ panopticon_id: @artefact.id,
97
+ version_number: 1)
98
+ assert_raise(RuntimeError) do
99
+ edition.build_clone
100
+ end
101
+ end
102
+
103
+ test "cloning can only occur from a published edition with no subsequent in progress siblings" do
104
+ edition = FactoryGirl.create(:guide_edition,
105
+ panopticon_id: @artefact.id,
106
+ state: "published",
107
+ version_number: 1)
108
+
109
+ FactoryGirl.create(:guide_edition,
110
+ panopticon_id: @artefact.id,
111
+ state: "draft",
112
+ version_number: 2)
113
+
114
+ assert_raise(RuntimeError) do
115
+ edition.build_clone
116
+ end
117
+ end
118
+
119
+ test "cloning from an earlier edition should give you a safe version number" do
120
+ edition = FactoryGirl.create(:guide_edition,
121
+ state: "published",
122
+ panopticon_id: @artefact.id,
123
+ version_number: 1)
124
+ edition_two = FactoryGirl.create(:guide_edition,
125
+ state: "published",
126
+ panopticon_id: @artefact.id,
127
+ version_number: 2)
128
+
129
+ clone1 = edition.build_clone
130
+ assert_equal clone1.version_number, 3
131
+ end
132
+
133
+ test "Cloning into a different edition type" do
134
+ edition = FactoryGirl.create(
135
+ :guide_edition,
136
+ state: "published",
137
+ panopticon_id: @artefact.id,
138
+ version_number: 1,
139
+ department: "Test dept",
140
+ overview: "I am a test overview",
141
+ alternative_title: "Alternative test title",
142
+ video_url: "http://www.youtube.com/watch?v=dQw4w9WgXcQ"
143
+ )
144
+ new_edition = edition.build_clone AnswerEdition
145
+
146
+ assert_equal new_edition.class, AnswerEdition
147
+ assert_equal new_edition.version_number, 2
148
+ assert_equal new_edition.panopticon_id, @artefact.id.to_s
149
+ assert_equal new_edition.state, "lined_up"
150
+ assert_equal new_edition.department, "Test dept"
151
+ assert_equal new_edition.overview, "I am a test overview"
152
+ assert_equal new_edition.alternative_title, "Alternative test title"
153
+ end
154
+
155
+ test "Cloning between types with parts" do
156
+ edition = FactoryGirl.create(:programme_edition,
157
+ panopticon_id: @artefact.id,
158
+ state: "published",
159
+ version_number: 1,
160
+ overview: "I am a shiny programme")
161
+ new_edition = edition.build_clone GuideEdition
162
+
163
+ assert_equal(new_edition.parts.map {|part| part.title },
164
+ edition.parts.map {|part| part.title })
165
+ end
166
+
167
+ test "edition finder should return the published edition when given an empty edition parameter" do
168
+ dummy_publication = template_published_answer
169
+ second_publication = template_unpublished_answer(2)
170
+
171
+ assert dummy_publication.published?
172
+ assert_equal dummy_publication, Edition.find_and_identify("childcare", "")
173
+ end
174
+
175
+ test "edition finder should return the latest edition when asked" do
176
+ dummy_publication = template_published_answer
177
+ second_publication = template_unpublished_answer(2)
178
+
179
+ assert_equal 2, Edition.where(slug: dummy_publication.slug).count
180
+ found_edition = Edition.find_and_identify("childcare", "latest")
181
+ assert_equal second_publication.version_number, found_edition.version_number
182
+ end
183
+
184
+ test "a publication should not have a video" do
185
+ dummy_publication = template_published_answer
186
+ assert !dummy_publication.has_video?
187
+ end
188
+
189
+ test "should create a publication based on data imported from panopticon" do
190
+ section = FactoryGirl.create(:tag, tag_id: "test-section", title: "Test section", tag_type: "section")
191
+ artefact = FactoryGirl.create(:artefact,
192
+ slug: "foo-bar",
193
+ kind: "answer",
194
+ name: "Foo bar",
195
+ department: "Test dept",
196
+ owning_app: "publisher",
197
+ )
198
+ artefact.primary_section = section.tag_id
199
+ artefact.save!
200
+
201
+ a = Artefact.find(artefact.id)
202
+
203
+ assert_equal section.tag_id, artefact.primary_section.tag_id
204
+ assert_equal section.title, artefact.primary_section.title
205
+ user = User.create
206
+
207
+ publication = Edition.find_or_create_from_panopticon_data(artefact.id, user, {})
208
+
209
+ assert_kind_of AnswerEdition, publication
210
+ assert_equal artefact.name, publication.title
211
+ assert_equal artefact.id.to_s, publication.panopticon_id.to_s
212
+ assert_equal section.title, publication.section
213
+ assert_equal artefact.department, publication.department
214
+ end
215
+
216
+ # TODO: come back and remove this one.
217
+ test "should not change edition name if published" do
218
+ FactoryGirl.create(:tag, tag_id: "test-section", title: "Test section", tag_type: "section")
219
+ artefact = FactoryGirl.create(:artefact,
220
+ slug: "foo-bar",
221
+ kind: "answer",
222
+ name: "Foo bar",
223
+ department: "Test dept",
224
+ owning_app: "publisher",
225
+ )
226
+
227
+ guide = FactoryGirl.create(:guide_edition,
228
+ panopticon_id: artefact.id,
229
+ title: "Original title",
230
+ slug: "original-title"
231
+ )
232
+ guide.state = "ready"
233
+ guide.save!
234
+ User.create(name: "Winston").publish(guide, comment: "testing")
235
+ artefact.name = "New title"
236
+ artefact.primary_section = "test-section"
237
+ artefact.save
238
+
239
+ assert_equal "Original title", guide.reload.title
240
+ assert_equal "Test section", guide.reload.section
241
+ end
242
+
243
+ test "should not change edition metadata if archived" do
244
+ FactoryGirl.create(:tag, tag_id: "test-section", title: "Test section", tag_type: "section")
245
+ artefact = FactoryGirl.create(:artefact,
246
+ slug: "foo-bar",
247
+ kind: "answer",
248
+ name: "Foo bar",
249
+ primary_section: "test-section",
250
+ sections: ["test-section"],
251
+ department: "Test dept",
252
+ owning_app: "publisher",
253
+ )
254
+
255
+ guide = FactoryGirl.create(:guide_edition,
256
+ panopticon_id: artefact.id,
257
+ title: "Original title",
258
+ slug: "original-title",
259
+ state: "archived"
260
+ )
261
+ artefact.slug = "new-slug"
262
+ artefact.save
263
+
264
+ assert_not_equal "new-slug", guide.reload.slug
265
+ end
266
+
267
+ test "should scope publications by assignee" do
268
+ stub_request(:get, %r{http://panopticon\.test\.gov\.uk/artefacts/.*\.js}).
269
+ to_return(status: 200, body: "{}", headers: {})
270
+
271
+ a, b = 2.times.map { FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id) }
272
+
273
+ alice, bob, charlie = %w[ alice bob charlie ].map { |s|
274
+ FactoryGirl.create(:user, name: s)
275
+ }
276
+ alice.assign(a, bob)
277
+ alice.assign(a, charlie)
278
+ alice.assign(b, bob)
279
+
280
+ assert_equal [b], Edition.assigned_to(bob).to_a
281
+ end
282
+
283
+ test "cannot delete a publication that has been published" do
284
+ dummy_answer = template_published_answer
285
+ loaded_answer = AnswerEdition.where(slug: "childcare").first
286
+
287
+ assert_equal loaded_answer, dummy_answer
288
+ assert ! dummy_answer.can_destroy?
289
+ assert_raise (Workflow::CannotDeletePublishedPublication) do
290
+ dummy_answer.destroy
291
+ end
292
+ end
293
+
294
+ test "cannot delete a published publication with a new draft edition" do
295
+ dummy_answer = template_published_answer
296
+
297
+ new_edition = dummy_answer.build_clone
298
+ new_edition.body = "Two"
299
+ dummy_answer.save
300
+
301
+ assert_raise (Edition::CannotDeletePublishedPublication) do
302
+ dummy_answer.destroy
303
+ end
304
+ end
305
+
306
+ test "can delete a publication that has not been published" do
307
+ dummy_answer = template_unpublished_answer
308
+ dummy_answer.destroy
309
+
310
+ loaded_answer = AnswerEdition.where(slug: dummy_answer.slug).first
311
+ assert_nil loaded_answer
312
+ end
313
+
314
+ test "should also delete associated artefact" do
315
+
316
+ FactoryGirl.create(:tag, tag_id: "test-section", title: "Test section", tag_type: "section")
317
+
318
+ user1 = FactoryGirl.create(:user)
319
+ edition = AnswerEdition.find_or_create_from_panopticon_data(@artefact.id, user1, {})
320
+
321
+ assert_difference "Artefact.count", -1 do
322
+ edition.destroy
323
+ end
324
+ end
325
+
326
+ test "should not delete associated artefact if there are other editions of this publication" do
327
+
328
+ FactoryGirl.create(:tag, tag_id: "test-section", title: "Test section", tag_type: "section")
329
+ user1 = FactoryGirl.create(:user)
330
+ edition = AnswerEdition.find_or_create_from_panopticon_data(@artefact.id, user1, {})
331
+ edition.update_attribute(:state, "published")
332
+
333
+ edition.reload
334
+ second_edition = edition.build_clone
335
+ second_edition.save!
336
+
337
+ assert_no_difference "Artefact.count" do
338
+ second_edition.destroy
339
+ end
340
+ end
341
+
342
+ test "should scope publications assigned to nobody" do
343
+ stub_request(:get, %r{http://panopticon\.test\.gov\.uk/artefacts/.*\.js}).
344
+ to_return(status: 200, body: "{}", headers: {})
345
+
346
+ a, b = 2.times.map { |i| GuideEdition.create!(panopticon_id: @artefact.id, title: "Guide #{i}", slug: "guide-#{i}") }
347
+
348
+ alice, bob, charlie = %w[ alice bob charlie ].map { |s|
349
+ FactoryGirl.create(:user, name: s)
350
+ }
351
+
352
+ alice.assign(a, bob)
353
+ a.reload
354
+ assert_equal bob, a.assigned_to
355
+
356
+ alice.assign(a, charlie)
357
+ a.reload
358
+ assert_equal charlie, a.assigned_to
359
+
360
+ assert_equal 2, Edition.count
361
+ assert_equal [b], Edition.assigned_to(nil).to_a
362
+ assert_equal [], Edition.assigned_to(bob).to_a
363
+ assert_equal [a], Edition.assigned_to(charlie).to_a
364
+ end
365
+
366
+ test "given multiple editions, can return the most recent published edition" do
367
+
368
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, slug: "hedgehog-topiary", state: "published")
369
+
370
+ second_edition = edition.build_clone
371
+ edition.update_attribute(:state, "archived")
372
+ second_edition.update_attribute(:state, "published")
373
+
374
+ third_edition = second_edition.build_clone
375
+ third_edition.update_attribute(:state, "draft")
376
+
377
+ assert_equal edition.published_edition, second_edition
378
+ end
379
+
380
+ test "editions, by default, return their title for use in the admin-interface lists of publications" do
381
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
382
+ assert_equal edition.title, edition.admin_list_title
383
+ end
384
+
385
+ test "editions can have notes stored for the history tab" do
386
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
387
+ user = User.new
388
+ assert edition.new_action(user, "note", comment: "Something important")
389
+ end
390
+
391
+ test "status should not be affected by notes" do
392
+ user = User.create(name: "bob")
393
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
394
+ edition.new_action(user, Action::APPROVE_REVIEW)
395
+ edition.new_action(user, Action::NOTE, comment: "Something important")
396
+
397
+ assert_equal Action::APPROVE_REVIEW, edition.latest_status_action.request_type
398
+ end
399
+
400
+ test "should have no assignee by default" do
401
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
402
+ assert_nil edition.assigned_to
403
+ end
404
+
405
+ test "should be assigned to the last assigned recipient" do
406
+ alice = User.create(name: "alice")
407
+ bob = User.create(name: "bob")
408
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
409
+ alice.assign(edition, bob)
410
+ assert_equal bob, edition.assigned_to
411
+ end
412
+
413
+ test "new edition should have an incremented version number" do
414
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "published")
415
+ new_edition = edition.build_clone
416
+ assert_equal edition.version_number + 1, new_edition.version_number
417
+ end
418
+
419
+ test "new edition should have an empty list of actions" do
420
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "published")
421
+ new_edition = edition.build_clone
422
+ assert_equal [], new_edition.actions
423
+ end
424
+
425
+ test "new editions should have the same text when created" do
426
+ edition = FactoryGirl.create(:guide_edition_with_two_parts, panopticon_id: @artefact.id, state: "published")
427
+ new_edition = edition.build_clone
428
+ original_text = edition.parts.map {|p| p.body }.join(" ")
429
+ new_text = new_edition.parts.map {|p| p.body }.join(" ")
430
+ assert_equal original_text, new_text
431
+ end
432
+
433
+ test "changing text in a new edition should not change text in old edition" do
434
+ edition = FactoryGirl.create(:guide_edition_with_two_parts, panopticon_id: @artefact.id, state: "published")
435
+ new_edition = edition.build_clone
436
+ new_edition.parts.first.body = "Some other version text"
437
+ original_text = edition.parts.map {|p| p.body }.join(" ")
438
+ new_text = new_edition.parts.map {|p| p.body }.join(" ")
439
+ assert_not_equal original_text, new_text
440
+ end
441
+
442
+ test "a new guide has no published edition" do
443
+ guide = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
444
+ assert_nil GuideEdition.where(state: "published", panopticon_id: guide.panopticon_id).first
445
+ end
446
+
447
+ test "an edition of a guide can be published" do
448
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
449
+ edition.publish
450
+ assert_not_nil GuideEdition.where(state: "published", panopticon_id: edition.panopticon_id).first
451
+ end
452
+
453
+ test "when an edition of a guide is published, all other published editions are archived" do
454
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
455
+
456
+ user = User.create name: "bob"
457
+ user.publish edition, comment: "First publication"
458
+
459
+ second_edition = edition.build_clone
460
+ second_edition.update_attribute(:state, "ready")
461
+ second_edition.save!
462
+ user.publish second_edition, comment: "Second publication"
463
+
464
+ third_edition = second_edition.build_clone
465
+ third_edition.update_attribute(:state, "ready")
466
+ third_edition.save!
467
+ user.publish third_edition, comment: "Third publication"
468
+
469
+ edition.reload
470
+ assert edition.archived?
471
+
472
+ second_edition.reload
473
+ assert second_edition.archived?
474
+
475
+ assert_equal 2, GuideEdition.where(panopticon_id: edition.panopticon_id, state: "archived").count
476
+ end
477
+
478
+ test "edition can return latest status action of a specified request type" do
479
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "draft")
480
+ user = User.create(name: "George")
481
+ user.request_review edition, comment: "Requesting review"
482
+
483
+ assert_equal edition.actions.size, 1
484
+ assert edition.latest_status_action(Action::REQUEST_REVIEW).present?
485
+ end
486
+
487
+ test "a published edition can't be edited" do
488
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "published")
489
+ edition.title = "My New Title"
490
+
491
+ assert ! edition.save
492
+ assert_equal ["Published editions can't be edited"], edition.errors[:base]
493
+ end
494
+
495
+ test "edition's publish history is recorded" do
496
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
497
+
498
+ user = User.create name: "bob"
499
+ user.publish edition, comment: "First publication"
500
+
501
+ second_edition = edition.build_clone
502
+ second_edition.update_attribute(:state, "ready")
503
+ second_edition.save!
504
+ user.publish second_edition, comment: "Second publication"
505
+
506
+ third_edition = second_edition.build_clone
507
+ third_edition.update_attribute(:state, "ready")
508
+ third_edition.save!
509
+ user.publish third_edition, comment: "Third publication"
510
+
511
+ edition.reload
512
+ assert edition.actions.where("request_type" => "publish")
513
+
514
+ second_edition.reload
515
+ assert second_edition.actions.where("request_type" => "publish")
516
+
517
+ third_edition.reload
518
+ assert third_edition.actions.where("request_type" => "publish")
519
+ assert third_edition.published?
520
+ end
521
+
522
+
523
+ test "a series with all editions published should not have siblings in progress" do
524
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
525
+
526
+ user = User.create name: "bob"
527
+ user.publish edition, comment: "First publication"
528
+
529
+ new_edition = edition.build_clone
530
+ new_edition.state = "ready"
531
+ new_edition.save!
532
+ user.publish new_edition, comment: "Second publication"
533
+
534
+ edition = edition.reload
535
+
536
+ assert_nil edition.sibling_in_progress
537
+ end
538
+
539
+ test "a series with one published and one draft edition should have a sibling in progress" do
540
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
541
+ edition.save!
542
+
543
+ user = User.create name: "bob"
544
+ user.publish edition, comment: "First publication"
545
+
546
+ new_edition = edition.build_clone
547
+ new_edition.save!
548
+
549
+ edition = edition.reload
550
+
551
+ assert_not_nil edition.sibling_in_progress
552
+ assert_equal new_edition.version_number, edition.sibling_in_progress
553
+ end
554
+
555
+ test "a new guide edition with multiple parts creates a full diff when published" do
556
+ user = User.create name: "Roland"
557
+
558
+ edition_one = GuideEdition.new(title: "One", slug: "one", panopticon_id: @artefact.id)
559
+ edition_one.parts.build title: "Part One", body:"Never gonna give you up", slug: "part-one"
560
+ edition_one.parts.build title: "Part Two", body:"NYAN NYAN NYAN NYAN", slug: "part-two"
561
+ edition_one.save!
562
+
563
+ edition_one.state = :ready
564
+ user.publish edition_one, comment: "First edition"
565
+
566
+ edition_two = edition_one.build_clone
567
+ edition_two.save!
568
+ edition_two.parts.first.update_attribute :title, "Changed Title"
569
+ edition_two.parts.first.update_attribute :body, "Never gonna let you down"
570
+
571
+ edition_two.state = :ready
572
+ user.publish edition_two, comment: "Second edition"
573
+
574
+ publish_action = edition_two.actions.where(request_type: "publish").last
575
+
576
+ assert_equal "{\"# Part One\" >> \"# Changed Title\"}\n\n{\"Never gonna give you up\" >> \"Never gonna let you down\"}\n\n# Part Two\n\nNYAN NYAN NYAN NYAN", publish_action.diff
577
+ end
578
+
579
+ test "a part's slug must be of the correct format" do
580
+ edition_one = GuideEdition.new(title: "One", slug: "one", panopticon_id: @artefact.id)
581
+ edition_one.parts.build title: "Part One", body:"Never gonna give you up", slug: "part-One-1"
582
+ edition_one.save!
583
+
584
+ edition_one.parts[0].slug = "part one"
585
+ assert_raise (Mongoid::Errors::Validations) do
586
+ edition_one.save!
587
+ end
588
+ end
589
+
590
+ test "parts can be sorted by the order field using a scope" do
591
+ edition = GuideEdition.new(title: "One", slug: "one", panopticon_id: @artefact.id)
592
+ edition.parts.build title: "Biscuits", body:"Never gonna give you up", slug: "biscuits", order: 2
593
+ edition.parts.build title: "Cookies", body:"NYAN NYAN NYAN NYAN", slug: "cookies", order: 1
594
+ edition.save!
595
+ edition.reload
596
+
597
+ assert_equal "Cookies", edition.parts.in_order.first.title
598
+ assert_equal "Biscuits", edition.parts.in_order.last.title
599
+ end
600
+
601
+ test "user should not be able to review an edition they requested review for" do
602
+ user = User.create(name: "Mary")
603
+
604
+ edition = ProgrammeEdition.new(title: "Childcare", slug: "childcare", panopticon_id: @artefact.id)
605
+ user.start_work(edition)
606
+ assert edition.can_request_review?
607
+ user.request_review(edition,{comment: "Review this programme please."})
608
+ assert ! user.request_amendments(edition, {comment: "Well Done, but work harder"})
609
+ end
610
+
611
+ test "a new programme edition with multiple parts creates a full diff when published" do
612
+ user = User.create name: "Mazz"
613
+
614
+ edition_one = ProgrammeEdition.new(title: "Childcare", slug: "childcare", panopticon_id: @artefact.id)
615
+ edition_one.parts.build title: "Part One", body:"Content for part one", slug: "part-one"
616
+ edition_one.parts.build title: "Part Two", body:"Content for part two", slug: "part-two"
617
+ edition_one.save!
618
+
619
+ edition_one.state = :ready
620
+ user.publish edition_one, comment: "First edition"
621
+
622
+ edition_two = edition_one.build_clone
623
+ edition_two.save!
624
+ edition_two.parts.first.update_attribute :body, "Some other content"
625
+ edition_two.state = :ready
626
+ user.publish edition_two, comment: "Second edition"
627
+
628
+ publish_action = edition_two.actions.where(request_type: "publish").last
629
+
630
+ assert_equal "# Part One\n\n{\"Content for part one\" >> \"Some other content\"}\n\n# Part Two\n\nContent for part two", publish_action.diff
631
+ end
632
+
633
+ test "a published publication with a draft edition is in progress" do
634
+ dummy_answer = template_published_answer
635
+ assert !dummy_answer.has_sibling_in_progress?
636
+
637
+ edition = dummy_answer.build_clone
638
+ edition.save
639
+
640
+ dummy_answer.reload
641
+ assert dummy_answer.has_sibling_in_progress?
642
+ end
643
+
644
+ test "a draft edition cannot be published" do
645
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "draft")
646
+ edition.start_work
647
+ refute edition.can_publish?
648
+ end
649
+
650
+ test "a draft edition can be emergency published" do
651
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "draft")
652
+ edition.start_work
653
+ assert edition.can_emergency_publish?
654
+ end
655
+
656
+
657
+ # test denormalisation
658
+
659
+ test "should denormalise an edition with an assigned user and action requesters" do
660
+ @user1 = FactoryGirl.create(:user, name: "Morwenna")
661
+ @user2 = FactoryGirl.create(:user, name: "John")
662
+ @user3 = FactoryGirl.create(:user, name: "Nick")
663
+
664
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "archived")
665
+
666
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "archived", assigned_to_id: @user1.id)
667
+ edition.actions.create request_type: Action::CREATE, requester: @user2
668
+ edition.actions.create request_type: Action::PUBLISH, requester: @user3
669
+ edition.actions.create request_type: Action::ARCHIVE, requester: @user1
670
+ edition.save! and edition.reload
671
+
672
+ assert_equal @user1.name, edition.assignee
673
+ assert_equal @user2.name, edition.creator
674
+ assert_equal @user3.name, edition.publisher
675
+ assert_equal @user1.name, edition.archiver
676
+ end
677
+
678
+ test "should denormalise an assignee's name when an edition is assigned" do
679
+ @user1 = FactoryGirl.create(:user)
680
+ @user2 = FactoryGirl.create(:user)
681
+
682
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "lined_up")
683
+ @user1.assign edition, @user2
684
+
685
+ assert_equal @user2, edition.assigned_to
686
+ assert_equal @user2.name, edition.assignee
687
+ end
688
+
689
+ test "should denormalise a creator's name when an edition is created" do
690
+ @user = FactoryGirl.create(:user)
691
+ FactoryGirl.create(:tag, tag_id: "test-section", title: "Test section", tag_type: "section")
692
+ artefact = FactoryGirl.create(:artefact,
693
+ slug: "foo-bar",
694
+ kind: "answer",
695
+ name: "Foo bar",
696
+ primary_section: "test-section",
697
+ sections: ["test-section"],
698
+ department: "Test dept",
699
+ owning_app: "publisher",
700
+ )
701
+
702
+ edition = AnswerEdition.find_or_create_from_panopticon_data(artefact.id, @user, {})
703
+
704
+ assert_equal @user.name, edition.creator
705
+ end
706
+
707
+ test "should denormalise a publishing user's name when an edition is published" do
708
+ @user = FactoryGirl.create(:user)
709
+
710
+ edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
711
+ @user.publish edition, { }
712
+
713
+ assert_equal @user.name, edition.publisher
714
+ end
715
+
716
+ test "should set siblings in progress to nil for new editions" do
717
+ @user = FactoryGirl.create(:user)
718
+ @edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
719
+ @published_edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "published")
720
+ assert_equal 1, @edition.version_number
721
+ assert_nil @edition.sibling_in_progress
722
+ end
723
+
724
+ test "should update previous editions when new edition is added" do
725
+ @user = FactoryGirl.create(:user)
726
+ @edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
727
+ @published_edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "published")
728
+ @new_edition = @published_edition.build_clone
729
+ @new_edition.save
730
+ @published_edition.reload
731
+
732
+ assert_equal 2, @new_edition.version_number
733
+ assert_equal 2, @published_edition.sibling_in_progress
734
+ end
735
+
736
+ test "should update previous editions when new edition is published" do
737
+ @user = FactoryGirl.create(:user)
738
+ @edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "ready")
739
+ @published_edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "published")
740
+ @new_edition = @published_edition.build_clone
741
+ @new_edition.save
742
+ @new_edition.update_attribute(:state, "ready")
743
+ @user.publish(@new_edition, comment: "Publishing this")
744
+ @published_edition.reload
745
+
746
+ assert_equal 2, @new_edition.version_number
747
+ assert_nil @new_edition.sibling_in_progress
748
+ assert_nil @published_edition.sibling_in_progress
749
+ end
750
+
751
+ test "all subclasses should provide a working whole_body method for diffing" do
752
+ Edition.subclasses.each do |klass|
753
+ assert klass.instance_methods.include?(:whole_body), "#{klass} doesn't provide a whole_body"
754
+ assert_nothing_raised do
755
+ klass.new.whole_body
756
+ end
757
+ end
758
+ end
759
+
760
+ test "should convert a GuideEdition to an AnswerEdition" do
761
+ guide_edition = FactoryGirl.create(:guide_edition, panopticon_id: @artefact.id, state: "published")
762
+ answer_edition = guide_edition.build_clone(AnswerEdition)
763
+
764
+ assert_equal guide_edition.whole_body, answer_edition.whole_body
765
+ end
766
+
767
+ test "should convert an AnswerEdition to a GuideEdition" do
768
+ answer_edition = template_published_answer
769
+ guide_edition = answer_edition.build_clone(GuideEdition)
770
+
771
+ expected = "# Part One\n\n" + answer_edition.whole_body
772
+
773
+ assert_equal expected, guide_edition.whole_body
774
+ end
775
+
776
+ test "should not allow any changes to an edition with an archived artefact" do
777
+ artefact = FactoryGirl.create(:artefact)
778
+ guide_edition = FactoryGirl.create(:guide_edition, state: 'draft', panopticon_id: artefact.id)
779
+ artefact.state = 'archived'
780
+ artefact.save
781
+
782
+ assert_raise(RuntimeError) do
783
+ guide_edition.title = "Error this"
784
+ guide_edition.save!
785
+ end
786
+ end
787
+
788
+ test "should return related artefact" do
789
+ assert_equal "Foo bar", template_published_answer.artefact.name
790
+ end
791
+
792
+ context "indexable_content" do
793
+ context "editions with a 'body'" do
794
+ should "include the body with markup removed" do
795
+ edition = FactoryGirl.create(:answer_edition, body: "## Title", panopticon_id: FactoryGirl.create(:artefact).id)
796
+ assert_equal "Title", edition.indexable_content
797
+ end
798
+ end
799
+
800
+ context "for a single part thing" do
801
+ should "have the normalised content of that part" do
802
+ edition = FactoryGirl.create(:guide_edition, :state => 'ready', :title => 'one part thing', :alternative_title => 'alternative one part thing', panopticon_id: FactoryGirl.create(:artefact).id)
803
+ edition.publish
804
+ assert_equal "alternative one part thing", edition.indexable_content
805
+ end
806
+ end
807
+
808
+ context "for a multi part thing" do
809
+ should "have the normalised content of all parts" do
810
+ edition = FactoryGirl.create(:guide_edition_with_two_parts, :state => 'ready', panopticon_id: FactoryGirl.create(:artefact).id)
811
+ edition.publish
812
+ assert_equal "PART ! This is some version text. PART !! This is some more version text.", edition.indexable_content
813
+ end
814
+ end
815
+
816
+ context "indexable_content would contain govspeak" do
817
+ should "convert it to plaintext" do
818
+ edition = FactoryGirl.create(:guide_edition_with_two_govspeak_parts, :state => 'ready', panopticon_id: FactoryGirl.create(:artefact).id)
819
+ edition.publish
820
+
821
+ expected = "Some Part Title! This is some version text. Another Part Title This is link text."
822
+ assert_equal expected, edition.indexable_content
823
+ end
824
+ end
825
+ end
826
+ end