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,78 @@
1
+ require "test_helper"
2
+ require "govuk_content_models/test_helpers/local_services"
3
+
4
+ class LocalTransactionEditionTest < ActiveSupport::TestCase
5
+ include LocalServicesHelper
6
+ def setup
7
+ @artefact = FactoryGirl.create(:artefact)
8
+ end
9
+
10
+ test "should report that an authority provides a service" do
11
+ bins_transaction = LocalTransactionEdition.new(
12
+ lgsl_code: "bins",
13
+ title: "Transaction",
14
+ slug: "slug",
15
+ panopticon_id: @artefact.id
16
+ )
17
+ county_council = make_authority_providing("bins")
18
+ assert bins_transaction.service_provided_by?(county_council.snac)
19
+ end
20
+
21
+ test "should report that an authority does not provide a service" do
22
+ bins_transaction = LocalTransactionEdition.new(
23
+ lgsl_code: "bins",
24
+ title: "Transaction",
25
+ slug: "slug",
26
+ panopticon_id: @artefact.id
27
+ )
28
+ county_council = make_authority_providing("housing-benefit")
29
+ refute bins_transaction.service_provided_by?(county_council.snac)
30
+ end
31
+
32
+ test "should be a transaction search format" do
33
+ bins_transaction = LocalTransactionEdition.new(
34
+ lgsl_code: "bins",
35
+ title: "Transaction",
36
+ slug: "slug",
37
+ panopticon_id: @artefact.id
38
+ )
39
+ assert_equal "transaction", bins_transaction.search_format
40
+ end
41
+
42
+
43
+ test "should validate on save that a LocalService exists for that lgsl_code" do
44
+ s = LocalService.create!(lgsl_code: "bins", providing_tier: %w{county unitary})
45
+
46
+ lt = LocalTransactionEdition.new(lgsl_code: "nonexistent", title: "Foo", slug: "foo", panopticon_id: @artefact.id)
47
+ lt.save
48
+ assert !lt.valid?
49
+
50
+ lt = LocalTransactionEdition.new(lgsl_code: s.lgsl_code, title: "Bar", slug: "bar", panopticon_id: @artefact.id)
51
+ lt.save
52
+ assert lt.valid?
53
+ assert lt.persisted?
54
+ end
55
+
56
+ test "should create a diff between the versions when publishing a new version" do
57
+ make_service(149, %w{county unitary})
58
+ edition_one = LocalTransactionEdition.new(title: "Transaction", slug: "transaction", lgsl_code: "149", panopticon_id: @artefact.id)
59
+ user = User.create name: "Thomas"
60
+
61
+ edition_one.introduction = "Test"
62
+ edition_one.state = :ready
63
+ edition_one.save!
64
+
65
+ user.publish edition_one, comment: "First edition"
66
+
67
+ edition_two = edition_one.build_clone
68
+ edition_two.introduction = "Testing"
69
+ edition_two.state = :ready
70
+ edition_two.save!
71
+
72
+ user.publish edition_two, comment: "Second edition"
73
+
74
+ publish_action = edition_two.actions.where(request_type: "publish").last
75
+
76
+ assert_equal "{\"Test\" >> \"Testing\"}", publish_action.diff
77
+ end
78
+ end
@@ -0,0 +1,47 @@
1
+ require "test_helper"
2
+
3
+ class OverviewDashboardTest < ActiveSupport::TestCase
4
+
5
+ test "Can create and retrieve dashboard overview objects" do
6
+ overview = create_test_overview
7
+
8
+ assert_equal overview.dashboard_type, "Format"
9
+ assert_equal overview.result_group, "Guide"
10
+ check_status_equal overview, row_status
11
+ end
12
+
13
+ private
14
+ def create_test_overview
15
+ overview = OverviewDashboard.create dashboard_type: "Format", result_group: "Guide"
16
+
17
+ row_status.each do |k, v|
18
+ overview[k] = v
19
+ end
20
+
21
+ overview.save
22
+
23
+ found_overviews = OverviewDashboard.where(dashboard_type: "Format")
24
+ assert_equal found_overviews.size, 1
25
+ found_overviews.first
26
+ end
27
+
28
+ def row_status
29
+ {
30
+ lined_up: 1,
31
+ draft: 1,
32
+ ammends_needed: 1,
33
+ in_review: 1,
34
+ ready: 1,
35
+ fact_check_recieved: 1,
36
+ fact_check: 1,
37
+ published: 1,
38
+ archived: 1
39
+ }
40
+ end
41
+
42
+ def check_status_equal(actual_object, expected_hash)
43
+ expected_hash.each do |k, v|
44
+ assert_equal actual_object[k], expected_hash[k]
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,169 @@
1
+ require "test_helper"
2
+
3
+ class SimpleSmartAnswerEditionTest < ActiveSupport::TestCase
4
+ setup do
5
+ @artefact = FactoryGirl.create(:artefact)
6
+ end
7
+
8
+ should "be created with valid nodes" do
9
+ edition = FactoryGirl.build(:simple_smart_answer_edition, panopticon_id: @artefact.id)
10
+ edition.body = "This is a simple smart answer."
11
+
12
+ edition.nodes.build(:slug => "question1", :title => "You approach two locked doors. Which do you choose?", :kind => "question", :order => 1)
13
+ edition.nodes.build(:slug => "left", :title => "As you open the door, a lion bursts out and mauls you to death.", :order => 2, :kind => "outcome")
14
+ edition.nodes.build(:slug => "right", :title => "As you open the door, a tiger bursts out and mauls you to death.", :order => 3, :kind => "outcome")
15
+ edition.save!
16
+
17
+ edition = SimpleSmartAnswerEdition.first
18
+
19
+ assert_equal "This is a simple smart answer.", edition.body
20
+ assert_equal 3, edition.nodes.count
21
+ assert_equal ["question1", "left", "right"], edition.nodes.all.map(&:slug)
22
+ end
23
+
24
+ should "copy the body and nodes when cloning an edition" do
25
+ edition = FactoryGirl.create(:simple_smart_answer_edition,
26
+ panopticon_id: @artefact.id,
27
+ body: "This smart answer is somewhat unique and calls for a different kind of introduction",
28
+ state: "published"
29
+ )
30
+ edition.nodes.build(:slug => "question1", :title => "You approach two open doors. Which do you choose?", :kind => "question", :order => 1)
31
+ edition.nodes.build(:slug => "left", :title => "As you wander through the door, it slams shut behind you, as a lion starts pacing towards you...", :order => 2, :kind => "outcome")
32
+ edition.nodes.build(:slug => "right", :title => "As you wander through the door, it slams shut behind you, as a tiger starts pacing towards you...", :order => 3, :kind => "outcome")
33
+ edition.save!
34
+
35
+ cloned_edition = edition.build_clone
36
+ cloned_edition.save!
37
+
38
+ old_edition = SimpleSmartAnswerEdition.find(edition.id)
39
+ assert_equal ["question", "outcome", "outcome"], old_edition.nodes.all.map(&:kind)
40
+ assert_equal ["question1", "left", "right"], old_edition.nodes.all.map(&:slug)
41
+
42
+ new_edition = SimpleSmartAnswerEdition.find(cloned_edition.id)
43
+ assert_equal edition.body, new_edition.body
44
+ assert_equal ["question", "outcome", "outcome"], new_edition.nodes.all.map(&:kind)
45
+ assert_equal ["question1", "left", "right"], new_edition.nodes.all.map(&:slug)
46
+ end
47
+
48
+ should "not copy nodes when new edition is not a smart answer" do
49
+ edition = FactoryGirl.create(:simple_smart_answer_edition,
50
+ panopticon_id: @artefact.id,
51
+ body: "This smart answer is somewhat unique and calls for a different kind of introduction",
52
+ state: "published"
53
+ )
54
+ edition.nodes.build(:slug => "question1", :title => "You approach two open doors. Which do you choose?", :kind => "question", :order => 1)
55
+ edition.save!
56
+
57
+ new_edition = edition.build_clone(AnswerEdition)
58
+
59
+ assert_equal edition.body, new_edition.body
60
+
61
+ assert new_edition.is_a?(AnswerEdition)
62
+ assert ! new_edition.respond_to?(:nodes)
63
+ end
64
+
65
+ should "select the first node as the starting node" do
66
+ edition = FactoryGirl.create(:simple_smart_answer_edition)
67
+ edition.nodes.build(:slug => "question1", :title => "Question 1", :kind => "question", :order => 1)
68
+ edition.nodes.build(:slug => "question2", :title => "Question 2", :kind => "question", :order => 2)
69
+ edition.nodes.build(:slug => "foo", :title => "Outcome 1.", :order => 3, :kind => "outcome")
70
+ edition.nodes.build(:slug => "bar", :title => "Outcome 2", :order => 4, :kind => "outcome")
71
+
72
+ assert_equal "question1", edition.initial_node.slug
73
+ end
74
+
75
+ should "create nodes with nested attributes" do
76
+ edition = FactoryGirl.create(:simple_smart_answer_edition, :nodes_attributes => [
77
+ { slug: "question1", title: "Question 1", kind: "question", order: 1},
78
+ { slug: "foo", title: "Outcome 1", kind: "outcome", order: 2 },
79
+ ])
80
+
81
+ assert_equal 2, edition.nodes.size
82
+ assert_equal ["question1", "foo"], edition.nodes.all.map(&:slug)
83
+ end
84
+
85
+ should "destroy nodes using nested attributes" do
86
+ edition = FactoryGirl.create(:simple_smart_answer_edition)
87
+ edition.nodes.build(:slug => "question1", :title => "Question 1", :kind => "question", :order => 1)
88
+ edition.nodes.build(:slug => "question2", :title => "Question 2", :kind => "question", :order => 1)
89
+ edition.save!
90
+
91
+ assert_equal 2, edition.nodes.size
92
+
93
+ edition.update_attributes({
94
+ :nodes_attributes => {
95
+ "1" => { "id" => edition.nodes.first.id, "_destroy" => "1" }
96
+ }
97
+ })
98
+ edition.reload
99
+
100
+ assert_equal 1, edition.nodes.size
101
+ end
102
+
103
+ context "update_attributes method" do
104
+ setup do
105
+ @edition = FactoryGirl.create(:simple_smart_answer_edition)
106
+ @edition.nodes.build(:slug => "question1", :title => "Question 1", :kind => "question", :order => 1)
107
+ @edition.nodes.build(:slug => "question2", :title => "Question 2", :kind => "question", :order => 1)
108
+ @edition.nodes.first.options.build(
109
+ :label => "Option 1", :next_node => "question2", :order => 1)
110
+ @edition.save!
111
+ end
112
+
113
+ should "update edition and nested node and option attributes" do
114
+ @edition.update_attributes(:title => "Smarter than the average answer",
115
+ :body => "No developers were involved in the changing of this copy",
116
+ :nodes_attributes => {
117
+ "0" => { "id" => @edition.nodes.first.id, "title" => "Question the first", "options_attributes" => {
118
+ "0" => { "id" => @edition.nodes.first.options.first.id, "label" => "Option the first" }
119
+ }
120
+ }
121
+ })
122
+
123
+ @edition.reload
124
+
125
+ assert_equal "Smarter than the average answer", @edition.title
126
+ assert_equal "No developers were involved in the changing of this copy", @edition.body
127
+ assert_equal "Question the first", @edition.nodes.first.title
128
+ assert_equal "Option the first", @edition.nodes.first.options.first.label
129
+ end
130
+
131
+ should "create and destroy nodes and options using nested attributes" do
132
+ @edition.update_attributes({
133
+ :nodes_attributes => {
134
+ "0" => { "id" => @edition.nodes.first.id, "options_attributes" => {
135
+ "0" => { "id" => @edition.nodes.first.options.first.id, "_destroy" => "1" }
136
+ }
137
+ },
138
+ "1" => { "id" => @edition.nodes.second.id, "_destroy" => "1" },
139
+ "2" => { "kind" => "question", "title" => "Question 3", "slug" => "question3", "options_attributes" => {
140
+ "0" => { "label" => "Goes to outcome 1", "next_node" => "outcome1" }
141
+ } },
142
+ "3" => { "kind" => "outcome", "title" => "Outcome 1", "slug" => "outcome1" }
143
+ }
144
+ })
145
+
146
+ @edition.reload
147
+
148
+ assert_equal 3, @edition.nodes.size
149
+ assert_equal 0, @edition.nodes.first.options.size
150
+ assert_equal "Question 3", @edition.nodes.second.title
151
+ assert_equal 1, @edition.nodes.second.options.size
152
+ assert_equal "outcome1", @edition.nodes.second.options.first.next_node
153
+ assert_equal "Outcome 1", @edition.nodes.third.title
154
+ end
155
+
156
+ should "ignore new nodes if they are to be destroyed" do
157
+ @edition.update_attributes({
158
+ :nodes_attributes => {
159
+ "0" => { "id" => @edition.nodes.first.id, "title" => "Question the first" },
160
+ "1" => { "title" => "", "slug" => "", "kind" => "outcome", "_destroy" => "1" }
161
+ }
162
+ })
163
+ @edition.reload
164
+
165
+ assert_equal "Question the first", @edition.nodes.first.title
166
+ assert_equal 2, @edition.nodes.size
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,134 @@
1
+ require "test_helper"
2
+
3
+ class SimpleSmartAnswerNodeTest < ActiveSupport::TestCase
4
+
5
+ context "given a smart answer exists" do
6
+ setup do
7
+ @edition = FactoryGirl.create(:simple_smart_answer_edition)
8
+
9
+ @atts = {
10
+ title: "How much wood could a woodchuck chuck if a woodchuck could chuck wood?",
11
+ slug: "how-much-wood-could-a-woodchuck-chuck-if-a-woodchuck-could-chuck-wood",
12
+ body: "This is a serious question.",
13
+ kind: "question"
14
+ }
15
+ end
16
+
17
+ should "be able to create a valid node" do
18
+ @node = @edition.nodes.build(@atts)
19
+
20
+ assert @node.save!
21
+
22
+ @edition.reload
23
+
24
+ assert_equal "how-much-wood-could-a-woodchuck-chuck-if-a-woodchuck-could-chuck-wood", @edition.nodes.first.slug
25
+ assert_equal "How much wood could a woodchuck chuck if a woodchuck could chuck wood?", @edition.nodes.first.title
26
+ assert_equal "This is a serious question.", @edition.nodes.first.body
27
+ end
28
+
29
+ should "not be valid without a slug" do
30
+ @node = @edition.nodes.build( @atts.merge(slug: "") )
31
+
32
+ assert ! @node.valid?
33
+ assert_equal [:slug], @node.errors.keys
34
+ end
35
+
36
+ should "not be valid with an invalid slug" do
37
+ @node = @edition.nodes.build(@atts)
38
+
39
+ [
40
+ 'under_score',
41
+ 'space space',
42
+ 'punct.u&ation',
43
+ ].each do |slug|
44
+ @node.slug = slug
45
+ refute @node.valid?
46
+ end
47
+ end
48
+
49
+ should "not be valid without a title" do
50
+ @node = @edition.nodes.build( @atts.merge(title: "") )
51
+
52
+ assert ! @node.valid?
53
+ assert_equal [:title], @node.errors.keys
54
+ end
55
+
56
+ should "not be valid without a kind" do
57
+ @node = @edition.nodes.build(@atts.merge(:kind => nil))
58
+ assert ! @node.valid?
59
+
60
+ assert_equal [:kind], @node.errors.keys
61
+ end
62
+
63
+ should "not be valid with a kind other than 'question' or 'outcome'" do
64
+ @node = @edition.nodes.build(@atts.merge(:kind => 'blah'))
65
+ assert ! @node.valid?
66
+
67
+ assert_equal [:kind], @node.errors.keys
68
+ end
69
+
70
+ should "create options using nested attributes" do
71
+ @node = @edition.nodes.create!(@atts.merge(:options_attributes => [
72
+ { :label => "Yes", :next_node => "yes" },
73
+ { :label => "No", :next_node => "no" }
74
+ ]))
75
+
76
+ @node.reload
77
+ assert_equal 2, @node.options.count
78
+ assert_equal ["Yes", "No"], @node.options.all.map(&:label)
79
+ assert_equal ["yes", "no"], @node.options.all.map(&:next_node)
80
+ end
81
+
82
+ should "destroy options using nested attributes" do
83
+ @node = @edition.nodes.create!(@atts.merge(:options_attributes => [
84
+ { :label => "Yes", :next_node => "yes" },
85
+ { :label => "No", :next_node => "no" }
86
+ ]))
87
+ assert_equal 2, @node.options.count
88
+
89
+ @node.update_attributes!(:options_attributes => {
90
+ "1" => { "id" => @node.options.first.id, "_destroy" => "1" }
91
+ })
92
+ @node.reload
93
+
94
+ assert_equal 1, @node.options.count
95
+ assert_equal ["No"], @node.options.all.map(&:label)
96
+ assert_equal ["no"], @node.options.all.map(&:next_node)
97
+ end
98
+
99
+ should "not be valid if an outcome has options" do
100
+ @node = @edition.nodes.build(@atts.merge(:kind => 'outcome', options_attributes: [
101
+ { :label => "Yes", :next_node => "yes" },
102
+ { :label => "No", :next_node => "no" }
103
+ ]))
104
+ assert ! @node.valid?
105
+
106
+ assert_equal [:options], @node.errors.keys
107
+ end
108
+
109
+ should "be able to create an outcome without options" do
110
+ @node = @edition.nodes.build(@atts.merge(:kind => 'outcome', :options_attributes => [] ))
111
+
112
+ assert @node.valid?
113
+ assert @node.save!
114
+ end
115
+
116
+ should "be returned in order" do
117
+ @nodes = [
118
+ @edition.nodes.create(@atts.merge(:title => "Third", :order => 3)),
119
+ @edition.nodes.create(@atts.merge(:title => "First", :order => 1)),
120
+ @edition.nodes.create(@atts.merge(:title => "Second", :order => 2)),
121
+ ]
122
+
123
+ assert_equal ["First","Second","Third"], @edition.nodes.all.map(&:title)
124
+ end
125
+
126
+ should "expose the simple smart answer edition" do
127
+ @node = @edition.nodes.build(@atts)
128
+
129
+ assert_equal @node.edition, @edition
130
+ end
131
+
132
+ end
133
+
134
+ end
@@ -0,0 +1,90 @@
1
+ require "test_helper"
2
+
3
+ class SimpleSmartAnswerOptionTest < ActiveSupport::TestCase
4
+
5
+ context "given a smart answer exists with a node" do
6
+ setup do
7
+ @node = SimpleSmartAnswerEdition::Node.new(:slug => "question1", :title => "Question One?", :kind => "question")
8
+ @edition = FactoryGirl.create(:simple_smart_answer_edition, :nodes => [
9
+ @node,
10
+ SimpleSmartAnswerEdition::Node.new(:slug => "outcome1", :title => "Outcome One", :kind => "outcome")
11
+ ])
12
+
13
+ @atts = {
14
+ label: "Yes",
15
+ next_node: "yes"
16
+ }
17
+ end
18
+
19
+ should "be able to create a valid option" do
20
+ @option = @node.options.build(@atts)
21
+
22
+ assert @option.save!
23
+ @node.reload
24
+
25
+ assert_equal "Yes", @node.options.first.label
26
+ assert_equal "yes", @node.options.first.next_node
27
+ end
28
+
29
+ should "not be valid without a label" do
30
+ @option = @node.options.build(@atts.merge(label: nil))
31
+
32
+ assert !@option.valid?
33
+ assert @option.errors.keys.include?(:label)
34
+ end
35
+
36
+ should "not be valid without the next node" do
37
+ @option = @node.options.build(@atts.merge(next_node: nil))
38
+
39
+ assert !@option.valid?
40
+ assert @option.errors.keys.include?(:next_node)
41
+ end
42
+
43
+ should "expose the node" do
44
+ @option = @node.options.create(@atts)
45
+ @option.reload
46
+
47
+ assert_equal @node, @option.node
48
+ end
49
+
50
+ should "return in order" do
51
+ @options = [
52
+ @node.options.create(@atts.merge(:label => "Third", :next_node => "baz", :order => 3)),
53
+ @node.options.create(@atts.merge(:label => "First", :next_node => "foo", :order => 1)),
54
+ @node.options.create(@atts.merge(:label => "Second", :next_node => "bar", :order => 2)),
55
+ ]
56
+
57
+ assert_equal ["First","Second","Third"], @node.options.all.map(&:label)
58
+ assert_equal ["foo","bar","baz"], @node.options.all.map(&:next_node)
59
+ end
60
+
61
+ context "slug" do
62
+ should "generate a slug from the label if blank" do
63
+ @option = @node.options.build(@atts)
64
+
65
+ assert @option.valid?
66
+ assert_equal "yes", @option.slug
67
+ end
68
+
69
+ should "not overwrite a given slug" do
70
+ @option = @node.options.build(@atts.merge(:slug => "fooey"))
71
+
72
+ assert @option.valid?
73
+ assert_equal "fooey", @option.slug
74
+ end
75
+
76
+ should "not be valid with an invalid slug" do
77
+ @option = @node.options.build(@atts)
78
+
79
+ [
80
+ 'under_score',
81
+ 'space space',
82
+ 'punct.u&ation',
83
+ ].each do |slug|
84
+ @option.slug = slug
85
+ refute @option.valid?
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end