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,4 @@
1
+ module GovukContentModels
2
+ # Changing this causes Jenkins to tag and release the gem into the wild
3
+ VERSION = "6.0.2"
4
+ end
@@ -0,0 +1 @@
1
+ {"created_at":"2011-12-02T12:02:43Z","email_address":"contact@example.com","id":189,"name":"Contact Name","opening_hours":null,"postal_address":"1 High Street\nTown","updated_at":"2011-12-02T12:02:43Z","website_url":"http://www.example.com/","phone_numbers":[{"contact_id":189,"created_at":"2011-12-02T12:02:43Z","id":447,"kind":"phone","label":null,"updated_at":"2011-12-02T12:02:43Z","value":"0113 496 0123"},{"contact_id":189,"created_at":"2011-12-02T12:02:43Z","id":448,"kind":"fax","label":null,"updated_at":"2011-12-02T12:02:43Z","value":"028 9018 1337"}]}
Binary file
@@ -0,0 +1,123 @@
1
+ require "test_helper"
2
+
3
+ def merge_attributes(original, *update_hashes)
4
+ # Merge multiple attribute hashes: this also differs from Hash#merge in that
5
+ # it converts symbolic keys to strings
6
+ if update_hashes.empty?
7
+ return original
8
+ else
9
+ first_update, *other_updates = update_hashes
10
+ updated = first_update.reduce(original) do |old, pair|
11
+ key, value = pair
12
+ old.merge(key.to_s => value)
13
+ end
14
+ merge_attributes(updated, *other_updates)
15
+ end
16
+ end
17
+
18
+ class ArtefactActionTest < ActiveSupport::TestCase
19
+
20
+ DEFAULTS = {
21
+ "business_proposition" => false,
22
+ "active" => false,
23
+ "tag_ids" => [],
24
+ "state" => "draft",
25
+ "related_artefact_ids" => [],
26
+ "paths" => [],
27
+ "prefixes" => [],
28
+ "language" => "en",
29
+ "need_extended_font" => false
30
+ }
31
+
32
+ def base_fields
33
+ {
34
+ slug: "an-artefact",
35
+ name: "An artefact",
36
+ kind: "answer",
37
+ owning_app: "publisher"
38
+ }
39
+ end
40
+
41
+ test "a new artefact should have a create action" do
42
+ a = Artefact.create!(base_fields)
43
+ a.reload
44
+
45
+ assert_equal 1, a.actions.size
46
+ action = a.actions.first
47
+ assert_equal "create", action[:action_type]
48
+ assert_equal merge_attributes(DEFAULTS, base_fields), action.snapshot
49
+ assert action.created_at, "Action has no creation timestamp"
50
+ end
51
+
52
+ test "an updated artefact should have two actions" do
53
+ a = Artefact.create!(base_fields)
54
+ a.description = "An artefact of shining wonderment."
55
+ a.save!
56
+ a.reload
57
+
58
+ assert_equal 2, a.actions.size
59
+ assert_equal ["create", "update"], a.actions.map(&:action_type)
60
+ create_snapshot = merge_attributes(DEFAULTS, base_fields)
61
+ update_snapshot = create_snapshot.merge("description" => a.description)
62
+ assert_equal create_snapshot, a.actions[0].snapshot
63
+ assert_equal update_snapshot, a.actions[1].snapshot
64
+ a.actions.each do |action|
65
+ assert action.created_at, "Action has no creation timestamp"
66
+ end
67
+ end
68
+
69
+ test "saving with no tracked changes will not create a new snapshot" do
70
+ a = Artefact.create!(base_fields)
71
+ a.updated_at = Time.zone.now + 5.minutes
72
+ a.save!
73
+ assert_equal 1, a.actions.size
74
+ end
75
+
76
+ test "updating attributes as a user should record a user action" do
77
+ a = Artefact.create!(base_fields)
78
+ user = FactoryGirl.create :user
79
+ updates = {description: "Shiny shiny description"}
80
+ a.update_attributes_as user, updates
81
+ a.reload
82
+
83
+ assert_equal "Shiny shiny description", a.description
84
+ assert_equal 2, a.actions.size
85
+ assert_equal ["create", "update"], a.actions.map(&:action_type)
86
+ assert_equal user, a.actions.last.user
87
+ assert_equal(
88
+ merge_attributes(DEFAULTS, base_fields, updates),
89
+ a.actions.last.snapshot
90
+ )
91
+ end
92
+
93
+ test "saving as a user should record a user action" do
94
+ a = Artefact.create!(base_fields)
95
+ user = FactoryGirl.create :user
96
+ updates = {description: "Shiny shiny description"}
97
+ a.description = updates[:description]
98
+ a.save_as user
99
+ a.reload
100
+
101
+ assert_equal "Shiny shiny description", a.description
102
+ assert_equal 2, a.actions.size
103
+ assert_equal ["create", "update"], a.actions.map(&:action_type)
104
+ assert_equal user, a.actions.last.user
105
+ assert_equal(
106
+ merge_attributes(DEFAULTS, base_fields, updates),
107
+ a.actions.last.snapshot
108
+ )
109
+ end
110
+
111
+ test "saving as a user with an action type" do
112
+ a = Artefact.create!(base_fields)
113
+ user = FactoryGirl.create :user
114
+ updates = {description: "Shiny shiny description"}
115
+ a.description = updates[:description]
116
+ a.save_as user, action_type: "awesome"
117
+ a.reload
118
+
119
+ assert_equal user, a.actions.last.user
120
+ assert_equal "awesome", a.actions.last.action_type
121
+ end
122
+
123
+ end
@@ -0,0 +1,32 @@
1
+ require "test_helper"
2
+
3
+ class ArtefactExternalLinkTest < ActiveSupport::TestCase
4
+ context "validating a link" do
5
+ should "not be valid without a title or URL" do
6
+ refute ArtefactExternalLink.new.valid?
7
+ end
8
+
9
+ should "not be valid with URL missing" do
10
+ refute ArtefactExternalLink.new(:title => "Foo").valid?
11
+ end
12
+
13
+ should "not be valid with title missing" do
14
+ refute ArtefactExternalLink.new(:url => "http://bar.com").valid?
15
+ end
16
+
17
+ should "be valid with both fields supplied" do
18
+ link = ArtefactExternalLink.new(:title => "Foo", :url => "http://bar.com")
19
+ assert link.valid?
20
+ end
21
+
22
+ should "only be valid if the URL is valid" do
23
+ link = ArtefactExternalLink.new(:title => "Foo", :url => "notreal://foo.com")
24
+ refute link.valid?
25
+ end
26
+
27
+ should "be valid with an https URL" do
28
+ link = ArtefactExternalLink.new(:title => "Foo", :url => "https://bar.com")
29
+ assert link.valid?
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,52 @@
1
+ require "test_helper"
2
+
3
+ class ArtefactTagTest < ActiveSupport::TestCase
4
+
5
+ TEST_SECTIONS = [
6
+ ['crime', 'Crime'], ['crime/the-police', 'The Police'], ['crime/batman', 'Batman']
7
+ ]
8
+ TEST_KEYWORDS = [['cheese', 'Cheese'], ['bacon', 'Bacon']]
9
+
10
+ setup do
11
+ TEST_SECTIONS.each do |tag_id, title|
12
+ FactoryGirl.create(:tag, :tag_id => tag_id, :tag_type => 'section', :title => title)
13
+ end
14
+ TEST_KEYWORDS.each do |tag_id, title|
15
+ FactoryGirl.create(:tag, :tag_id => tag_id, :tag_type => 'keyword', :title => title)
16
+ end
17
+ end
18
+
19
+ test "return primary section title when asked for its section" do
20
+ a = FactoryGirl.create(:artefact)
21
+ a.sections = ['crime', 'crime/the-police']
22
+ a.primary_section = 'crime'
23
+ a.reconcile_tag_ids
24
+
25
+ assert_equal 'Crime', a.section
26
+ end
27
+
28
+ test "returns title of parent and child tags when its primary section has a parent" do
29
+ parent = Tag.by_tag_id('crime', 'section')
30
+ child = Tag.by_tag_id('crime/batman', 'section')
31
+ child.update_attributes parent_id: parent.tag_id
32
+
33
+ a = FactoryGirl.create(:artefact)
34
+ a.primary_section = child.tag_id
35
+ a.reconcile_tag_ids
36
+
37
+ assert_equal "#{parent.title}:#{child.title}", a.section
38
+ end
39
+
40
+ test "has legacy_sources tag collection" do
41
+ ls1 = FactoryGirl.create(:tag, :tag_id => 'businesslink', :tag_type => 'legacy_source', :title => 'Business Link')
42
+ ls2 = FactoryGirl.create(:tag, :tag_id => 'directgov', :tag_type => 'legacy_source', :title => 'Directgov')
43
+ ls3 = FactoryGirl.create(:tag, :tag_id => 'dvla', :tag_type => 'legacy_source', :title => 'DVLA')
44
+
45
+ a = FactoryGirl.build(:artefact)
46
+ a.legacy_sources = ['businesslink', 'dvla']
47
+ a.save
48
+
49
+ a = Artefact.first
50
+ assert_equal [ls1, ls3], a.legacy_sources
51
+ end
52
+ end
@@ -0,0 +1,583 @@
1
+ require "test_helper"
2
+
3
+ class ArtefactTest < ActiveSupport::TestCase
4
+ context "validating slug" do
5
+ should "allow nice clean slugs" do
6
+ a = FactoryGirl.build(:artefact, slug: "its-a-nice-day")
7
+ assert a.valid?
8
+ end
9
+
10
+ should "not allow apostrophes in slugs" do
11
+ a = FactoryGirl.build(:artefact, slug: "it's-a-nice-day")
12
+ refute a.valid?
13
+ assert a.errors[:slug].any?
14
+ end
15
+
16
+ should "not allow spaces in slugs" do
17
+ a = FactoryGirl.build(:artefact, slug: "it is-a-nice-day")
18
+ refute a.valid?
19
+ assert a.errors[:slug].any?
20
+ end
21
+
22
+ should "allow slashes in slugs when the namespace is 'done'" do
23
+ a = FactoryGirl.build(:artefact, slug: "done/its-a-nice-day")
24
+ assert a.valid?
25
+ end
26
+
27
+ should "not allow slashes in slugs when the namespace is not 'done'" do
28
+ a = FactoryGirl.build(:artefact, slug: "something-else/its-a-nice-day")
29
+ refute a.valid?
30
+ assert a.errors[:slug].any?
31
+ end
32
+
33
+ should "allow travel-advice to have a slug prefixed with 'foreign-travel-advice/'" do
34
+ a = FactoryGirl.build(:artefact, slug: "foreign-travel-advice/aruba", kind: "travel-advice")
35
+ assert a.valid?
36
+ end
37
+
38
+ should "not allow multiple slashes in travel-advice artefacts" do
39
+ a = FactoryGirl.build(:artefact, slug: "foreign-travel-advice/aruba/foo", kind: "travel-advice")
40
+ refute a.valid?
41
+ assert a.errors[:slug].any?
42
+ end
43
+
44
+ should "not allow a foreign-travel-advice prefix for non-travel-advice artefacts" do
45
+ a = FactoryGirl.build(:artefact, slug: "foreign-travel-advice/aruba", kind: "answer")
46
+ refute a.valid?
47
+ assert a.errors[:slug].any?
48
+ end
49
+
50
+ should "allow a government prefix for Inside Government artefacts" do
51
+ a = FactoryGirl.build(:artefact, slug: "government/slug", kind: "case_study")
52
+ assert a.valid?
53
+ end
54
+
55
+ should "allow a government prefix and multiple path parts for Inside Government artefacts" do
56
+ a = FactoryGirl.build(:artefact, slug: "government/something/somewhere/somehow/slug", kind: "case_study")
57
+ assert a.valid?
58
+ end
59
+
60
+ should "not allow a government prefix with invalid path parts" do
61
+ a = FactoryGirl.build(:artefact, slug: "government/SomeThing/some.where/somehow/slug", kind: "case_study")
62
+ refute a.valid?
63
+ end
64
+
65
+ should "require a government prefix for Inside Government artefacts" do
66
+ a = FactoryGirl.build(:artefact, slug: "slug", kind: "case_study")
67
+ refute a.valid?
68
+ end
69
+
70
+ should "not require a government prefix for Detailed Guides" do
71
+ a = FactoryGirl.build(:artefact, slug: "slug", kind: "detailed_guide")
72
+ assert a.valid?
73
+ end
74
+
75
+ context "help page special case" do
76
+ should "allow a help page to have a help/ prefix on the slug" do
77
+ a = FactoryGirl.build(:artefact, :slug => "help/foo", :kind => "help_page")
78
+ assert a.valid?
79
+ end
80
+
81
+ should "require a help page to have a help/ prefix on the slug" do
82
+ a = FactoryGirl.build(:artefact, :slug => "foo", :kind => "help_page")
83
+ refute a.valid?
84
+ assert_equal 1, a.errors[:slug].count
85
+ end
86
+
87
+ should "not allow other kinds to have a help/ prefix" do
88
+ a = FactoryGirl.build(:artefact, :slug => "help/foo", :kind => "answer")
89
+ refute a.valid?
90
+ assert_equal 1, a.errors[:slug].count
91
+ end
92
+ end
93
+ end
94
+
95
+ context "validating paths and prefixes" do
96
+ setup do
97
+ @a = FactoryGirl.build(:artefact)
98
+ end
99
+
100
+ should "be valid when empty" do
101
+ @a.paths = []
102
+ @a.prefixes = []
103
+ assert @a.valid?
104
+
105
+ @a.paths = nil
106
+ @a.prefixes = nil
107
+ assert @a.valid?
108
+ end
109
+
110
+ should "be valid when set to array of absolute URL paths" do
111
+ @a.paths = ["/foo.json"]
112
+ @a.prefixes = ["/foo", "/bar"]
113
+ assert @a.valid?
114
+ end
115
+
116
+ should "be invalid if an entry is not a valid absolute URL path" do
117
+ [
118
+ "not a URL path",
119
+ "http://foo.example.com/bar",
120
+ "bar/baz",
121
+ "/foo/bar?baz=qux",
122
+ ].each do |path|
123
+ @a.paths = ["/foo.json", path]
124
+ @a.prefixes = ["/foo", path]
125
+ refute @a.valid?
126
+ assert_equal 1, @a.errors[:paths].count
127
+ assert_equal 1, @a.errors[:prefixes].count
128
+ end
129
+ end
130
+
131
+ should "be invalid with consecutive or trailing slashes" do
132
+ [
133
+ "/foo//bar",
134
+ "/foo/bar///",
135
+ "//bar/baz",
136
+ "//",
137
+ "/foo/bar/",
138
+ ].each do |path|
139
+ @a.paths = ["/foo.json", path]
140
+ @a.prefixes = ["/foo", path]
141
+ refute @a.valid?
142
+ assert_equal 1, @a.errors[:paths].count
143
+ assert_equal 1, @a.errors[:prefixes].count
144
+ end
145
+ end
146
+
147
+ should "skip validating these if they haven't changed" do
148
+ # This validation can be expensive, so skip it where unnecessary.
149
+ @a.paths = ["foo"]
150
+ @a.prefixes = ["bar"]
151
+ @a.save :validate => false
152
+
153
+ assert @a.valid?
154
+ end
155
+ end
156
+
157
+ test "should translate kind into internally normalised form" do
158
+ a = Artefact.new(kind: "benefit / scheme")
159
+ a.normalise
160
+ assert_equal "programme", a.kind
161
+ end
162
+
163
+ test "should not translate unknown kinds" do
164
+ a = Artefact.new(kind: "other")
165
+ a.normalise
166
+ assert_equal "other", a.kind
167
+ end
168
+
169
+ test "should store and return related artefacts in order" do
170
+ a = Artefact.create!(slug: "a", name: "a", kind: "place", need_id: 1, owning_app: "x")
171
+ b = Artefact.create!(slug: "b", name: "b", kind: "place", need_id: 2, owning_app: "x")
172
+ c = Artefact.create!(slug: "c", name: "c", kind: "place", need_id: 3, owning_app: "x")
173
+
174
+ a.related_artefacts = [b, c]
175
+ a.save!
176
+ a.reload
177
+
178
+ assert_equal [b, c], a.ordered_related_artefacts
179
+ end
180
+
181
+ test "should store and return related artefacts in order, even when not in natural order" do
182
+ a = Artefact.create!(slug: "a", name: "a", kind: "place", need_id: 1, owning_app: "x")
183
+ b = Artefact.create!(slug: "b", name: "b", kind: "place", need_id: 2, owning_app: "x")
184
+ c = Artefact.create!(slug: "c", name: "c", kind: "place", need_id: 3, owning_app: "x")
185
+
186
+ a.related_artefacts = [c, b]
187
+ a.save!
188
+ a.reload
189
+
190
+ assert_equal [c, b], a.ordered_related_artefacts
191
+ end
192
+
193
+ test "should store and return related artefacts in order, with a scope" do
194
+ a = Artefact.create!(slug: "a", name: "a", kind: "place", need_id: 1, owning_app: "x")
195
+ b = Artefact.create!(state: "live", slug: "b", name: "b", kind: "place", need_id: 2, owning_app: "x")
196
+ c = Artefact.create!(slug: "c", name: "c", kind: "place", need_id: 3, owning_app: "x")
197
+ d = Artefact.create!(state: "live", slug: "d", name: "d", kind: "place", need_id: 3, owning_app: "x")
198
+
199
+ a.related_artefacts = [d, c, b]
200
+ a.save!
201
+ a.reload
202
+
203
+ assert_equal [d, b], a.ordered_related_artefacts(a.related_artefacts.where(state: "live"))
204
+ end
205
+
206
+ test "published_related_artefacts should return all non-publisher artefacts, but only published publisher artefacts" do
207
+ # because currently only publisher has an idea of "published"
208
+
209
+ parent = Artefact.create!(slug: "parent", name: "Parent", kind: "guide", owning_app: "x")
210
+
211
+ a = Artefact.create!(slug: "a", name: "has no published editions", kind: "guide", owning_app: "publisher")
212
+ Edition.create!(panopticon_id: a.id, title: "Unpublished", state: "draft")
213
+ parent.related_artefacts << a
214
+
215
+ b = Artefact.create!(slug: "b", name: "has a published edition", kind: "guide", owning_app: "publisher")
216
+ Edition.create!(panopticon_id: b.id, title: "Published", state: "published")
217
+ parent.related_artefacts << b
218
+
219
+ c = Artefact.create!(slug: "c", name: "not a publisher artefact", kind: "place", owning_app: "x")
220
+ parent.related_artefacts << c
221
+ parent.save!
222
+
223
+ assert_equal [b.slug, c.slug], parent.published_related_artefacts.map(&:slug)
224
+ end
225
+
226
+ test "should raise a not found exception if the slug doesn't match" do
227
+ assert_raise Mongoid::Errors::DocumentNotFound do
228
+ Artefact.from_param("something-fake")
229
+ end
230
+ end
231
+
232
+ test "on save update metadata with associated publication" do
233
+ FactoryGirl.create(:tag, tag_id: "test-section", title: "Test section", tag_type: "section")
234
+ artefact = FactoryGirl.create(:artefact,
235
+ slug: "foo-bar",
236
+ kind: "answer",
237
+ name: "Foo bar",
238
+ primary_section: "test-section",
239
+ sections: ["test-section"],
240
+ department: "Test dept",
241
+ owning_app: "publisher",
242
+ )
243
+
244
+ user1 = FactoryGirl.create(:user)
245
+ edition = AnswerEdition.find_or_create_from_panopticon_data(artefact.id, user1, {})
246
+
247
+ assert_equal artefact.name, edition.title
248
+ assert_equal artefact.section, edition.section
249
+
250
+ artefact.name = "Babar"
251
+ artefact.save
252
+
253
+ edition.reload
254
+ assert_equal artefact.name, edition.title
255
+ end
256
+
257
+ test "should not let you edit the slug if the artefact is live" do
258
+ artefact = FactoryGirl.create(:artefact,
259
+ slug: "too-late-to-edit",
260
+ kind: "answer",
261
+ name: "Foo bar",
262
+ owning_app: "publisher",
263
+ state: "live"
264
+ )
265
+
266
+ artefact.slug = "belated-correction"
267
+ refute artefact.save
268
+
269
+ assert_equal "too-late-to-edit", artefact.reload.slug
270
+ end
271
+
272
+ # should continue to work in the way it has been:
273
+ # i.e. you can edit everything but the name/title for published content in panop
274
+ test "on save title should not be applied to already published content" do
275
+ FactoryGirl.create(:tag, tag_id: "test-section", title: "Test section", tag_type: "section")
276
+ artefact = FactoryGirl.create(:artefact,
277
+ slug: "foo-bar",
278
+ kind: "answer",
279
+ name: "Foo bar",
280
+ primary_section: "test-section",
281
+ sections: ["test-section"],
282
+ department: "Test dept",
283
+ owning_app: "publisher",
284
+ )
285
+
286
+ user1 = FactoryGirl.create(:user)
287
+ edition = AnswerEdition.find_or_create_from_panopticon_data(artefact.id, user1, {})
288
+ edition.state = "published"
289
+ edition.save!
290
+
291
+ assert_equal artefact.name, edition.title
292
+ assert_equal artefact.section, edition.section
293
+
294
+ artefact.name = "Babar"
295
+ artefact.save
296
+
297
+ edition.reload
298
+ assert_not_equal artefact.name, edition.title
299
+ end
300
+
301
+ test "should indicate when any editions have been published for this artefact" do
302
+ artefact = FactoryGirl.create(:artefact,
303
+ slug: "foo-bar",
304
+ kind: "answer",
305
+ name: "Foo bar",
306
+ owning_app: "publisher",
307
+ )
308
+ user1 = FactoryGirl.create(:user)
309
+ edition = AnswerEdition.find_or_create_from_panopticon_data(artefact.id, user1, {})
310
+
311
+ refute artefact.any_editions_published?
312
+
313
+ edition.state = "published"
314
+ edition.save!
315
+
316
+ assert artefact.any_editions_published?
317
+ end
318
+
319
+ test "should have a specialist_body field present for markdown content" do
320
+ artefact = Artefact.create!(slug: "parent", name: "Harry Potter", kind: "guide", owning_app: "x")
321
+ refute_includes artefact.attributes, "specialist_body"
322
+
323
+ artefact.specialist_body = "Something wicked this way comes"
324
+ assert_includes artefact.attributes, "specialist_body"
325
+ assert_equal "Something wicked this way comes", artefact.specialist_body
326
+ end
327
+
328
+ test "should have 'video' as a supported FORMAT" do
329
+ assert_includes Artefact::FORMATS, "video"
330
+ end
331
+
332
+ test "should find the default owning_app for a format" do
333
+ assert_equal "publisher", Artefact.default_app_for_format("guide")
334
+ end
335
+
336
+ test "should allow creation of artefacts with 'video' as the kind" do
337
+ artefact = Artefact.create!(slug: "omlette-du-fromage", name: "Omlette du fromage", kind: "video", owning_app: "Dexter's Lab")
338
+
339
+ refute artefact.nil?
340
+ assert_equal "video", artefact.kind
341
+ end
342
+
343
+ test "should archive all editions when archived" do
344
+ artefact = FactoryGirl.create(:artefact, state: "live")
345
+ editions = ["draft", "ready", "published", "archived"].map { |state|
346
+ FactoryGirl.create(:programme_edition, panopticon_id: artefact.id, state: state)
347
+ }
348
+ user1 = FactoryGirl.create(:user)
349
+
350
+ artefact.update_attributes_as(user1, state: "archived")
351
+ artefact.save!
352
+
353
+ editions.each &:reload
354
+ editions.each do |edition|
355
+ assert_equal "archived", edition.state
356
+ end
357
+ # remove the previously already archived edition, as no note will have been added
358
+ editions.pop
359
+ editions.each do |edition|
360
+ assert_equal "Artefact has been archived. Archiving this edition.", edition.actions.first.comment
361
+ end
362
+ end
363
+
364
+ test "should restrict what attributes can be updated on an edition that has an archived artefact" do
365
+ artefact = FactoryGirl.create(:artefact, state: "live")
366
+ edition = FactoryGirl.create(:programme_edition, panopticon_id: artefact.id, state: "published")
367
+ artefact.state = "archived"
368
+ artefact.save
369
+ assert_raise RuntimeError do
370
+ edition.update_attributes({state: "archived", title: "Shabba", slug: "do-not-allow"})
371
+ end
372
+ end
373
+
374
+ context "artefact language" do
375
+ should "return english by default" do
376
+ a = FactoryGirl.create(:artefact)
377
+
378
+ assert_equal 'en', a.language
379
+ end
380
+
381
+ should "accept welsh language" do
382
+ a = FactoryGirl.build(:artefact)
383
+ a.language = 'cy'
384
+ a.save
385
+
386
+ a = Artefact.first
387
+ assert_equal 'cy', a.language
388
+ end
389
+
390
+ should "reject a language which is not english or welsh" do
391
+ a = FactoryGirl.build(:artefact)
392
+ a.language = 'pirate'
393
+
394
+ assert ! a.valid?
395
+ end
396
+
397
+ should "has has_extended_chars field set to false by default" do
398
+ a = Artefact.new
399
+ assert_equal false, a.need_extended_font
400
+ end
401
+
402
+ should "allow has_extended_chars to be set" do
403
+ a = FactoryGirl.build(:artefact)
404
+ a.need_extended_font = true
405
+ a.save
406
+
407
+ a = Artefact.first
408
+ assert_equal true, a.need_extended_font
409
+ end
410
+ end
411
+
412
+ context "returning json representation" do
413
+ context "returning tags" do
414
+ setup do
415
+ FactoryGirl.create(:tag, :tag_type => 'section', :tag_id => 'crime', :title => 'Crime')
416
+ FactoryGirl.create(:tag, :tag_type => 'section', :tag_id => 'justice', :title => 'Justice', :description => "All about justice")
417
+ FactoryGirl.create(:tag, :tag_type => 'legacy_source', :tag_id => 'directgov', :title => 'Directgov')
418
+ FactoryGirl.create(:tag, :tag_type => 'legacy_source', :tag_id => 'businesslink', :title => 'Business Link')
419
+
420
+ @a = FactoryGirl.create(:artefact, :slug => 'fooey')
421
+ end
422
+
423
+ should "return empty array of tags and tag_ids" do
424
+ hash = @a.as_json
425
+
426
+ assert_equal [], hash['tag_ids']
427
+ assert_equal [], hash['tags']
428
+ end
429
+
430
+ context "for an artefact with tags" do
431
+ setup do
432
+ @a.sections = ['justice']
433
+ @a.legacy_sources = ['businesslink']
434
+ @a.save!
435
+ end
436
+
437
+ should "return an array of tag_id strings in tag_ids" do
438
+ hash = @a.as_json
439
+
440
+ assert_equal ['justice', 'businesslink'], hash['tag_ids']
441
+ end
442
+
443
+ should "return an array of tag objects in tags" do
444
+ hash = @a.as_json
445
+
446
+ expected = [
447
+ {
448
+ :id => 'justice',
449
+ :title => 'Justice',
450
+ :type => 'section',
451
+ :description => 'All about justice',
452
+ :short_description => nil
453
+ },
454
+ {
455
+ :id => 'businesslink',
456
+ :title => 'Business Link',
457
+ :type => 'legacy_source',
458
+ :description => nil,
459
+ :short_description => nil
460
+ }
461
+ ]
462
+ assert_equal expected, hash['tags']
463
+ end
464
+ end
465
+ end
466
+ end
467
+
468
+ context "artefact related external links" do
469
+ should "have none by default" do
470
+ artefact = FactoryGirl.create(:artefact)
471
+ assert_equal 0, artefact.external_links.length
472
+ end
473
+
474
+ should "contain the title and URL of the link" do
475
+ artefact = FactoryGirl.create(:artefact)
476
+ artefact.external_links << ArtefactExternalLink.new(:title => "Foo", :url => "http://bar.com")
477
+ assert_equal 1, artefact.external_links.length
478
+ assert_equal "Foo", artefact.external_links.first.title
479
+ end
480
+ end
481
+
482
+ should "have an archived? helper method" do
483
+ published_artefact = FactoryGirl.create(:artefact, :slug => "scooby", :state => "live")
484
+ archived_artefact = FactoryGirl.create(:artefact, :slug => "doo", :state => "archived")
485
+
486
+ refute published_artefact.archived?
487
+ assert archived_artefact.archived?
488
+ end
489
+
490
+ should "have a related_items method which discards artefacts that are archived or completed transactions" do
491
+ generic = FactoryGirl.create(:artefact, slug: "generic")
492
+ archived = FactoryGirl.create(:artefact, :slug => "archived", :state => "archived")
493
+ completed = FactoryGirl.create(:artefact, slug: "completed-transaction", kind: "completed_transaction")
494
+
495
+ assert_equal [generic], Artefact.relatable_items
496
+ end
497
+
498
+ context "related artefacts grouped by section tags" do
499
+ setup do
500
+ FactoryGirl.create(:tag, :tag_id => "fruit", :tag_type => 'section', :title => "Fruit")
501
+ FactoryGirl.create(:tag, :tag_id => "fruit/simple", :tag_type => 'section', :title => "Simple fruits", :parent_id => "fruit")
502
+ FactoryGirl.create(:tag, :tag_id => "fruit/aggregate", :tag_type => 'section', :title => "Aggregrate fruits", :parent_id => "fruit")
503
+ FactoryGirl.create(:tag, :tag_id => "vegetables", :tag_type => 'section', :title => "Vegetables")
504
+
505
+ @artefact = Artefact.create!(slug: "apple", name: "Apple", sections: [], kind: "guide", need_id: 1, owning_app: "x")
506
+ end
507
+
508
+ context "when related items are present in all groups" do
509
+ setup do
510
+ @artefact.sections = ["fruit/simple"]
511
+
512
+ @artefact.related_artefacts = [
513
+ Artefact.create!(slug: "pear", name: "Pear", kind: "guide", sections: ["fruit/simple"], need_id: 4, owning_app: "x"),
514
+ Artefact.create!(slug: "pineapple", name: "Pineapple", kind: "guide", sections: ["fruit/aggregate"], need_id: 2, owning_app: "x"),
515
+ Artefact.create!(slug: "broccoli", name: "Broccoli", kind: "guide", sections: ["vegetables"], need_id: 3, owning_app: "x")
516
+ ]
517
+ @artefact.save!
518
+ @artefact.reload
519
+ end
520
+
521
+ should "return a hash of artefacts in the same subsection" do
522
+ artefacts = @artefact.related_artefacts_grouped_by_distance
523
+ assert_equal ["pear"], artefacts['subsection'].map(&:slug)
524
+ end
525
+
526
+ should "return a hash of other artefacts in the same parent section" do
527
+ artefacts = @artefact.related_artefacts_grouped_by_distance
528
+ assert_equal ["pineapple"], artefacts['section'].map(&:slug)
529
+ end
530
+
531
+ should "return a hash of artefacts in other sections" do
532
+ artefacts = @artefact.related_artefacts_grouped_by_distance
533
+ assert_equal ["broccoli"], artefacts['other'].map(&:slug)
534
+ end
535
+
536
+ should "return related artefacts in order, with a scope" do
537
+ a = Artefact.create!(state: "live", slug: "a", name: "a", kind: "place", need_id: 1, owning_app: "x")
538
+ b = Artefact.create!(slug: "b", name: "b", kind: "place", need_id: 2, owning_app: "x")
539
+ c = Artefact.create!(state: "live", slug: "c", name: "c", kind: "place", need_id: 3, owning_app: "x")
540
+
541
+ @artefact.related_artefacts = [c,b,a]
542
+ @artefact.save!
543
+ @artefact.reload
544
+
545
+ assert_equal [c, a], @artefact.related_artefacts_grouped_by_distance(@artefact.related_artefacts.where(state: "live"))["other"]
546
+ end
547
+ end
548
+
549
+ should "return an empty array for a group with no related artefacts" do
550
+ # @artefact with no related items created in setup block
551
+
552
+ assert_equal [], @artefact.related_artefacts_grouped_by_distance["subsection"]
553
+ assert_equal [], @artefact.related_artefacts_grouped_by_distance["section"]
554
+ assert_equal [], @artefact.related_artefacts_grouped_by_distance["other"]
555
+ end
556
+
557
+ should "return all related artefacts in 'other' when an artefact has no sections" do
558
+ @artefact.related_artefacts = [
559
+ Artefact.create!(slug: "pear", name: "Pear", kind: "guide", sections: ["fruit/simple"], need_id: 4, owning_app: "x"),
560
+ Artefact.create!(slug: "banana", name: "Banana", kind: "guide", sections: ["fruit/simple"], need_id: 6, owning_app: "x")
561
+ ]
562
+
563
+ assert_equal [], @artefact.related_artefacts_grouped_by_distance["subsection"]
564
+ assert_equal [], @artefact.related_artefacts_grouped_by_distance["section"]
565
+ assert_equal ["pear", "banana"], @artefact.related_artefacts_grouped_by_distance["other"].map(&:slug)
566
+ end
567
+
568
+ should "return no section level related artefacts if the primary section has no parent_id" do
569
+ FactoryGirl.create(:tag, :tag_id => "fruit/multiple", :tag_type => 'section', :title => "Multiple fruits", :parent_id => nil)
570
+
571
+ @artefact.primary_section = "fruit/multiple"
572
+ @artefact.related_artefacts = [
573
+ Artefact.create!(slug: "fig", name: "Fig", kind: "guide", sections: ["fruit/multiple"], need_id: 4, owning_app: "x"),
574
+ Artefact.create!(slug: "strawberry", name: "Strawberry", kind: "guide", sections: ["fruit/simple"], need_id: 6, owning_app: "x")
575
+ ]
576
+ @artefact.save!
577
+
578
+ assert_equal ["fig"], @artefact.related_artefacts_grouped_by_distance["subsection"].map(&:slug)
579
+ assert_equal [], @artefact.related_artefacts_grouped_by_distance["section"]
580
+ assert_equal ["strawberry"], @artefact.related_artefacts_grouped_by_distance["other"].map(&:slug)
581
+ end
582
+ end
583
+ end