govuk_content_models 6.0.2

Sign up to get free protection for your applications and to get access to all the features.
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